🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

unloader

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

unloader - npm Package Compare versions

Comparing version
0.8.3
to
0.9.0
+332
dist/api-B_siCmJt.mjs
import module from "node:module";
import process from "node:process";
import path from "node:path";
import remapping from "@jridgewell/remapping";
import picomatch from "picomatch";
import { loadConfigSync } from "unconfig";
import { Buffer } from "node:buffer";
import { fileURLToPath, pathToFileURL } from "node:url";
import { createDebug } from "obug";
//#region src/plugin.ts
function normalizePluginHook(plugin, key) {
const hook = plugin?.[key];
if (!hook) return {};
if (typeof hook === "function") return { handler: hook };
return hook;
}
//#endregion
//#region node_modules/.pnpm/@antfu+utils@9.3.0/node_modules/@antfu/utils/dist/index.mjs
function toArray(array) {
array = array ?? [];
return Array.isArray(array) ? array : [array];
}
//#endregion
//#region src/plugin-filter.ts
function getMatcherString(glob, cwd) {
if (glob.startsWith("**") || path.isAbsolute(glob)) return path.normalize(glob);
const resolved = path.resolve(cwd, glob);
return path.normalize(resolved);
}
function patternToIdFilter(pattern) {
if (pattern instanceof RegExp) return (id) => {
const normalizedId = path.normalize(id);
const result = pattern.test(normalizedId);
pattern.lastIndex = 0;
return result;
};
const matcher = picomatch(getMatcherString(pattern, process.cwd()), { dot: true });
return (id) => {
return matcher(path.normalize(id));
};
}
function patternToCodeFilter(pattern) {
if (pattern instanceof RegExp) return (code) => {
const result = pattern.test(code);
pattern.lastIndex = 0;
return result;
};
return (code) => code.includes(pattern);
}
function createFilter(exclude, include) {
if (!exclude && !include) return;
return (input) => {
if (exclude?.some((filter) => filter(input))) return false;
if (include?.some((filter) => filter(input))) return true;
return !include || include.length <= 0;
};
}
function normalizeFilter(filter) {
if (typeof filter === "string" || filter instanceof RegExp) return { include: [filter] };
if (Array.isArray(filter)) return { include: filter };
return {
exclude: filter.exclude ? toArray(filter.exclude) : void 0,
include: filter.include ? toArray(filter.include) : void 0
};
}
function createIdFilter(filter) {
if (!filter) return;
const { exclude, include } = normalizeFilter(filter);
const excludeFilter = exclude?.map(patternToIdFilter);
const includeFilter = include?.map(patternToIdFilter);
return createFilter(excludeFilter, includeFilter);
}
function createCodeFilter(filter) {
if (!filter) return;
const { exclude, include } = normalizeFilter(filter);
const excludeFilter = exclude?.map(patternToCodeFilter);
const includeFilter = include?.map(patternToCodeFilter);
return createFilter(excludeFilter, includeFilter);
}
function createFilterForId(filter) {
const filterFunction = createIdFilter(filter);
return filterFunction ? (id) => !!filterFunction(id) : void 0;
}
function createFilterForTransform(idFilter, codeFilter) {
if (!idFilter && !codeFilter) return;
const idFilterFunction = createIdFilter(idFilter);
const codeFilterFunction = createCodeFilter(codeFilter);
return (id, code) => {
let fallback = true;
if (idFilterFunction) fallback &&= idFilterFunction(id);
if (!fallback) return false;
if (codeFilterFunction) fallback &&= codeFilterFunction(code);
return fallback;
};
}
//#endregion
//#region src/utils/config.ts
function loadConfig() {
const { config } = loadConfigSync({
sources: [{
files: "unloader.config",
extensions: [
"ts",
"mts",
"cts",
"js",
"mjs",
"cjs",
"json",
""
]
}],
cwd: process.cwd(),
defaults: {}
});
return config;
}
//#endregion
//#region src/utils/map.ts
function attachSourceMap(map, code) {
if (map) {
if (!map.sourcesContent || map.sourcesContent.length === 0) map.sourcesContent = [code];
code += `\n//# sourceMappingURL=${toUrl(map)}`;
}
return code;
}
function toUrl(map) {
return `data:application/json;charset=utf-8;base64,${Buffer.from(map.toString()).toString("base64")}`;
}
//#endregion
//#region src/utils/url.ts
function urlToPath(url) {
if (!url) return url;
return url.startsWith("file://") ? fileURLToPath(url) : url;
}
function pathToUrl(isRequire, path) {
if (isRequire || path.startsWith("file://") || path.startsWith("data://") || path.startsWith("node:")) return path;
return pathToFileURL(path).href;
}
//#endregion
//#region src/hooks.ts
function createHooks() {
let config;
let deactivated = false;
let pluginContext;
function init(context, inlineConfig) {
pluginContext = context;
config = inlineConfig || loadConfig();
for (const plugin of config.plugins || []) {
const { handler } = normalizePluginHook(plugin, "options");
const result = handler?.call(context, config);
assertSync(result, plugin.name, "options");
config = result || config;
}
for (const plugin of config.plugins || []) {
const { handler } = normalizePluginHook(plugin, "buildStart");
const result = handler?.call(context);
assertSync(result, plugin.name, "buildStart");
context.debug(`loaded plugin: %s`, plugin.name);
}
return config;
}
function resolve(specifier, context, nextResolve) {
if (deactivated) return nextResolve(specifier, context);
const isRequire = !Array.isArray(context.conditions) || context.conditions[0] === "require";
pluginContext?.debug(`resolving %s with context %o`, specifier, context);
if (config?.plugins && pluginContext) for (const plugin of config.plugins) {
const resolveFn = createResolve(nextResolve, isRequire, pluginContext.debug);
const { handler, filter } = normalizePluginHook(plugin, "resolveId");
const filterFn = createFilterForId(filter?.id);
const id = urlToPath(specifier);
if (filterFn && !filterFn(id)) continue;
const result = handler?.call({
resolve: resolveFn,
...pluginContext
}, id, urlToPath(context.parentURL), {
conditions: context.conditions,
attributes: context.importAttributes
});
assertSync(result, plugin.name, "resolveId");
if (result) {
let output;
if (typeof result === "string") output = {
url: pathToUrl(isRequire, result),
importAttributes: context.importAttributes,
shortCircuit: true
};
else output = {
url: pathToUrl(isRequire, result.id),
format: result.format,
importAttributes: result.attributes || context.importAttributes,
shortCircuit: true
};
pluginContext.debug(`resolved %s to %s with format %s`, specifier, output.url, output.format);
return output;
}
}
return nextResolve(specifier, context);
}
function load(url, context, nextLoad) {
if (deactivated || !config?.plugins) return nextLoad(url, context);
pluginContext?.debug(`load %s with context %o`, url, context);
let result;
const defaultFormat = context.format || "module";
const maps = [];
for (const plugin of config.plugins) {
const { handler, filter } = normalizePluginHook(plugin, "load");
const filterFn = createFilterForId(filter?.id);
const id = urlToPath(url);
if (filterFn && !filterFn(id)) continue;
const loadResult = handler?.call(pluginContext, id, {
format: context.format,
conditions: context.conditions,
attributes: context.importAttributes
});
assertSync(loadResult, plugin.name, "load");
if (loadResult) {
if (isModuleSource(loadResult)) result = {
source: loadResult,
format: defaultFormat,
shortCircuit: true
};
else {
if (loadResult.map) maps.unshift(loadResult.map);
result = {
source: loadResult.code,
format: loadResult.format || defaultFormat,
shortCircuit: true
};
}
break;
}
}
result ||= nextLoad(url, context);
for (const plugin of config.plugins) {
const { handler, filter } = normalizePluginHook(plugin, "transform");
const filterFn = createFilterForTransform(filter?.id, filter?.code);
const code = result.source || "";
const id = urlToPath(url);
if (filterFn && (typeof code !== "string" || !filterFn(id, code || ""))) continue;
const transformResult = handler?.call(pluginContext, code, id, {
format: result.format,
conditions: context.conditions,
attributes: context.importAttributes
});
assertSync(transformResult, plugin.name, "transform");
if (transformResult) if (isModuleSource(transformResult)) result = {
...result,
source: transformResult
};
else {
if (transformResult.map) maps.unshift(transformResult.map);
result = {
...result,
source: transformResult.code,
format: transformResult.format || result.format
};
}
}
if (maps.length && typeof result.source === "string") {
const code = attachSourceMap(remapping(maps, () => null), result.source);
result.source = code;
}
return result;
}
const deactivate = () => {
deactivated = true;
};
return {
init,
resolve,
load,
deactivate
};
}
function createResolve(nextResolve, isRequire, debug) {
return (source, importer, options) => {
if (!path.isAbsolute(source) && importer) source = path.resolve(importer, "..", source);
try {
const resolved = nextResolve(pathToUrl(isRequire, source), {
parentURL: importer,
conditions: options?.conditions,
importAttributes: options?.attributes
});
debug("resolved %s to %s with format %s", source, resolved.url, resolved.format);
return {
id: urlToPath(resolved.url),
attributes: resolved.importAttributes,
format: resolved.format
};
} catch (error) {
debug("error resolving %s: %o", source, error);
return null;
}
};
}
function isModuleSource(v) {
return typeof v === "string" || ArrayBuffer.isView(v) || v instanceof ArrayBuffer;
}
function assertSync(value, plugin, hook) {
if (value != null && typeof value === "object" && typeof value.then === "function") throw new Error(`Plugin "${plugin}" returned a Promise from "${hook}" hook. unloader only supports synchronous hooks.`);
}
//#endregion
//#region src/utils/context.ts
const sharedPluginContext = {
error: (message) => {
throw typeof message === "string" ? new Error(message) : message;
},
meta: { unloaderVersion: "0.9.0" }
};
//#endregion
//#region src/utils/debug.ts
const debug = createDebug("unloader");
//#endregion
//#region src/api.ts
function register(inlineConfig) {
const registerHooks = module.registerHooks;
if (!registerHooks) throw new Error(`This version of Node.js (${process.version}) does not support module.registerHooks(). Please upgrade to Node v22.15 or v23.5 and above.`);
const { init, resolve, load, deactivate } = createHooks();
if (init({
...sharedPluginContext,
log: (message) => console.info(message),
debug
}, inlineConfig).sourcemap && !process.sourceMapsEnabled) process.setSourceMapsEnabled(true);
registerHooks({
resolve,
load
});
return deactivate;
}
//#endregion
export { loadConfig as n, normalizePluginHook as r, register as t };
+31
-31
import { ImportAttributes, ModuleFormat, ModuleSource } from "node:module";
import { QuansyncAwaitableGenerator } from "quansync";
import { QuansyncFn } from "quansync/macro";
import { Arrayable } from "@antfu/utils";
import { MessagePort, Transferable } from "node:worker_threads";
//#region node_modules/.pnpm/@antfu+utils@9.3.0/node_modules/@antfu/utils/dist/index.d.mts
/**
* Array, or not yet
*/
type Arrayable<T> = T | Array<T>;
/**
* Function
*/
//#endregion
//#region src/plugin-filter.d.ts

@@ -15,3 +20,2 @@ type StringOrRegExp = string | RegExp;

//#region src/plugin.d.ts
type Awaitable<T> = T | Promise<T>;
type FalsyValue = null | undefined | false | void;

@@ -48,4 +52,3 @@ interface ResolveMeta {

interface PluginContext {
port?: MessagePort;
log: (message: any, transferList?: Transferable[]) => void;
log: (message: any) => void;
debug: (...args: any[]) => void;

@@ -57,5 +60,4 @@ error: (message: string | Error) => never;

}
type ConditionalAwaitable<C, T> = (C extends true ? T : Awaitable<T>) | QuansyncAwaitableGenerator<T>;
type ResolveFn<Sync = false> = (source: string, importer?: string, options?: ResolveMeta) => ConditionalAwaitable<Sync, ResolvedId | null>;
interface Plugin<Sync = false> extends Partial<PluginHooks<Sync>> {
type ResolveFn = (source: string, importer?: string, options?: ResolveMeta) => ResolvedId | null;
interface Plugin extends Partial<PluginHooks> {
name: string;

@@ -67,7 +69,7 @@ }

}
type HookFilterExtension<K$1 extends keyof FunctionPluginHooks<false>> = K$1 extends "transform" ? {
type HookFilterExtension<K extends keyof FunctionPluginHooks> = K extends "transform" ? {
filter?: HookFilter | undefined;
} : K$1 extends "load" ? {
} : K extends "load" ? {
filter?: Pick<HookFilter, "id"> | undefined;
} : K$1 extends "resolveId" ? {
} : K extends "resolveId" ? {
filter?: {

@@ -77,14 +79,14 @@ id?: StringFilter<RegExp> | undefined;

} | undefined : {};
interface FunctionPluginHooks<Sync> {
options: (this: PluginContext, config: UnloaderConfig<Sync>) => UnloaderConfig<Sync> | FalsyValue;
buildStart: (this: PluginContext) => ConditionalAwaitable<Sync, void>;
interface FunctionPluginHooks {
options: (this: PluginContext, config: UnloaderConfig) => UnloaderConfig | FalsyValue;
buildStart: (this: PluginContext) => void;
resolveId: (this: PluginContext & {
resolve: ResolveFn<Sync>;
}, source: string, importer: string | undefined, options: ResolveMeta) => ConditionalAwaitable<Sync, string | ResolvedId | FalsyValue>;
resolve: ResolveFn;
}, source: string, importer: string | undefined, options: ResolveMeta) => string | ResolvedId | FalsyValue;
load: (this: PluginContext, id: string, options: ResolveMeta & {
format: ModuleFormat | (string & {}) | null | undefined;
}) => ConditionalAwaitable<Sync, ModuleSource | LoadResult | FalsyValue>;
}) => ModuleSource | LoadResult | FalsyValue;
transform: (this: PluginContext, code: ModuleSource, id: string, options: ResolveMeta & {
format: string | null | undefined;
}) => ConditionalAwaitable<Sync, ModuleSource | LoadResult | FalsyValue>;
}) => ModuleSource | LoadResult | FalsyValue;
}

@@ -94,22 +96,20 @@ type ObjectHook<T, O = {}> = T | ({

} & O);
type PluginHooks<Sync> = { [K in keyof FunctionPluginHooks<Sync>]: ObjectHook<FunctionPluginHooks<Sync>[K], HookFilterExtension<K>> };
declare function normalizePluginHook<K$1 extends keyof PluginHooks<false>>(plugin: Plugin<false>, key: K$1): Partial<{
handler?: FunctionPluginHooks<false>[K$1];
} & HookFilterExtension<K$1>>;
type PluginHooks = { [K in keyof FunctionPluginHooks]: ObjectHook<FunctionPluginHooks[K], HookFilterExtension<K>> };
declare function normalizePluginHook<K extends keyof PluginHooks>(plugin: Plugin, key: K): Partial<{
handler?: FunctionPluginHooks[K];
} & HookFilterExtension<K>>;
//#endregion
//#region src/utils/config.d.ts
interface UnloaderConfig<Sync = false> {
interface UnloaderConfig {
sourcemap?: boolean;
plugins?: Plugin<Sync>[];
plugins?: Plugin[];
}
declare const loadConfig: QuansyncFn<UnloaderConfig, []>;
declare function loadConfig(): UnloaderConfig;
//#endregion
//#region src/api.d.ts
declare function register(inlineConfig?: string): Promise<() => Promise<void>>;
declare function registerSync(inlineConfig?: UnloaderConfig<true>): () => void;
declare function register(inlineConfig?: UnloaderConfig): () => void;
//#endregion
//#region src/index.d.ts
declare function defineConfig(config: UnloaderConfig): UnloaderConfig;
declare function defineSyncConfig(config: UnloaderConfig<true>): UnloaderConfig<true>;
//#endregion
export { Awaitable, ConditionalAwaitable, FalsyValue, FunctionPluginHooks, HookFilter, HookFilterExtension, LoadResult, ObjectHook, Plugin, PluginContext, PluginHooks, ResolveFn, ResolveMeta, ResolvedId, UnloaderConfig, defineConfig, defineSyncConfig, loadConfig, normalizePluginHook, register, registerSync };
export { FalsyValue, FunctionPluginHooks, HookFilter, HookFilterExtension, LoadResult, ObjectHook, Plugin, PluginContext, PluginHooks, ResolveFn, ResolveMeta, ResolvedId, UnloaderConfig, defineConfig, loadConfig, normalizePluginHook, register };

@@ -1,4 +0,2 @@

import { i as normalizePluginHook, r as loadConfig } from "./context-DRCo_iRa.mjs";
import { n as registerSync, t as register } from "./api-CXxT_bvN.mjs";
import { n as loadConfig, r as normalizePluginHook, t as register } from "./api-B_siCmJt.mjs";
//#region src/index.ts

@@ -8,7 +6,3 @@ function defineConfig(config) {

}
function defineSyncConfig(config) {
return config;
}
//#endregion
export { defineConfig, defineSyncConfig, loadConfig, normalizePluginHook, register, registerSync };
export { defineConfig, loadConfig, normalizePluginHook, register };

@@ -1,8 +0,5 @@

import "./context-DRCo_iRa.mjs";
import { t as register } from "./api-CXxT_bvN.mjs";
import { t as register } from "./api-B_siCmJt.mjs";
//#region src/register.ts
await register();
register();
//#endregion
export { };
export {};
{
"name": "unloader",
"type": "module",
"version": "0.8.3",
"version": "0.9.0",
"description": "Node.js loader with a Rollup-like interface.",

@@ -20,9 +20,4 @@ "author": "Kevin Deng <sxzz@sxzz.moe>",

"./register": "./dist/register.mjs",
"./register-sync": "./dist/register-sync.mjs",
"./worker": "./dist/worker.mjs",
"./package.json": "./package.json"
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"files": [

@@ -39,24 +34,24 @@ "dist"

"@jridgewell/remapping": "^2.3.5",
"birpc": "^4.0.0",
"obug": "^2.1.1",
"picomatch": "^4.0.3",
"quansync": "^1.0.0",
"unconfig": "^7.4.2"
"picomatch": "^4.0.4",
"unconfig": "^7.5.0"
},
"inlinedDependencies": {
"@antfu/utils": "9.3.0"
},
"devDependencies": {
"@antfu/utils": "^9.3.0",
"@quansync/fs": "^1.0.0",
"@sxzz/eslint-config": "^7.4.3",
"@sxzz/prettier-config": "^2.2.6",
"@types/node": "^24.10.4",
"@types/picomatch": "^4.0.2",
"@typescript/native-preview": "7.0.0-dev.20251219.1",
"bumpp": "^10.3.2",
"eslint": "^9.39.2",
"@sxzz/eslint-config": "^7.8.4",
"@sxzz/prettier-config": "^2.3.1",
"@types/node": "^25.6.0",
"@types/picomatch": "^4.0.3",
"@typescript/native-preview": "7.0.0-dev.20260415.1",
"bumpp": "^11.0.1",
"eslint": "^10.2.0",
"magic-string": "^0.30.21",
"prettier": "^3.7.4",
"tsdown": "^0.18.1",
"prettier": "^3.8.3",
"tsdown": "^0.21.8",
"tsdown-preset-sxzz": "^0.5.0",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"unplugin-quansync": "^0.5.1"
"typescript": "^6.0.2"
},

@@ -63,0 +58,0 @@ "prettier": "@sxzz/prettier-config",

@@ -33,4 +33,3 @@ # unloader

```bash
node --import unloader/register ... # For ESM only, support both sync and async hooks
node --require unloader/register-sync ... # For both ESM and CJS, only support sync hooks
node --import unloader/register ...
```

@@ -51,20 +50,9 @@

unloader supports both ESM and CJS, however, async hooks are only supported in
ESM. To support both ESM and CJS, please make sure all hooks are synchronous, or
use [quansync](https://github.com/quansync-dev/quansync).
unloader supports both ESM and CJS. All hooks are synchronous.
Here is an example of using sync hooks and quansync:
<details>
<summary>Show code</summary>
```ts
import { readFileSync } from 'node:fs'
import { readFile } from '@quansync/fs'
import { quansync } from 'quansync'
import type { Plugin } from 'unloader'
// sync usage
const pluginSync: Plugin<true> = {
const plugin: Plugin = {
name: 'my-plugin',

@@ -84,23 +72,4 @@ resolveId(source, importer, options) {

}
// quansync usage
const pluginQuansync: Plugin = {
name: 'my-plugin',
resolveId: quansync(function* (source, importer, options) {
const result = yield this.resolve(`${source}.js`, importer, options)
if (result) {
console.log(result)
return result
}
}),
load: quansync(function* (id) {
const contents = yield readFile(id, 'utf8')
console.log(contents)
return contents
}),
}
```
</details>
### Example

@@ -125,3 +94,3 @@

},
async resolveId(source, importer, options) {
resolveId(source, importer, options) {
if (source.startsWith('node:')) return

@@ -135,3 +104,3 @@

// Feature: try resolve with different extensions
const result = await this.resolve(`${source}.js`, importer, options)
const result = this.resolve(`${source}.js`, importer, options)
if (result) return result

@@ -138,0 +107,0 @@ },

import { n as createHooks, t as sharedPluginContext } from "./context-DRCo_iRa.mjs";
import module from "node:module";
import process from "node:process";
import { createBirpc } from "birpc";
import { createDebug } from "obug";
//#region src/utils/debug.ts
const debug = createDebug("unloader");
//#endregion
//#region src/rpc.ts
const mainFunctions = {
log(...messages) {
console.info(...messages);
},
debug(...args) {
debug(...args);
}
};
function createRpc(port) {
return createBirpc(mainFunctions, {
post: (data) => port.postMessage(data),
on: (fn) => port.on("message", fn)
});
}
//#endregion
//#region src/api.ts
async function register(inlineConfig) {
if (!module.register) throw new Error(`This version of Node.js (${process.version}) does not support module.register(). Please upgrade to Node v20.6 and above.`);
const { port1, port2 } = new MessageChannel();
const rpc = createRpc(port1);
port1.unref();
const data = {
port: port2,
inlineConfig
};
const transferList = [port2];
module.register("./worker.mjs", {
parentURL: import.meta.url,
data,
transferList
});
if (await rpc.ping()) process.setSourceMapsEnabled(true);
return () => rpc.deactivate();
}
function registerSync(inlineConfig) {
const registerHooks = module.registerHooks;
if (!registerHooks) throw new Error(`This version of Node.js (${process.version}) does not support module.registerHooks(). Please upgrade to Node v22.15 or v23.5 and above.`);
const { init, resolve, load, deactivate } = createHooks();
const context = {
...sharedPluginContext,
log: (message) => console.info(message),
debug
};
if (init.sync(context, inlineConfig).sourcemap && !process.sourceMapsEnabled) process.setSourceMapsEnabled(true);
registerHooks({
resolve: resolve.sync,
load: load.sync
});
return deactivate;
}
//#endregion
export { registerSync as n, register as t };
import process from "node:process";
import path from "node:path";
import remapping from "@jridgewell/remapping";
import { getIsAsync } from "quansync";
import { quansync } from "quansync/macro";
import picomatch from "picomatch";
import { loadConfig } from "unconfig";
import { Buffer } from "node:buffer";
import { fileURLToPath, pathToFileURL } from "node:url";
//#region src/plugin.ts
function normalizePluginHook(plugin, key) {
const hook = plugin?.[key];
if (!hook) return {};
if (typeof hook === "function") return { handler: hook };
return hook;
}
//#endregion
//#region node_modules/.pnpm/@antfu+utils@9.3.0/node_modules/@antfu/utils/dist/index.mjs
function toArray(array) {
array = array ?? [];
return Array.isArray(array) ? array : [array];
}
//#endregion
//#region src/plugin-filter.ts
function getMatcherString(glob, cwd) {
if (glob.startsWith("**") || path.isAbsolute(glob)) return path.normalize(glob);
const resolved = path.resolve(cwd, glob);
return path.normalize(resolved);
}
function patternToIdFilter(pattern) {
if (pattern instanceof RegExp) return (id) => {
const normalizedId = path.normalize(id);
const result = pattern.test(normalizedId);
pattern.lastIndex = 0;
return result;
};
const matcher = picomatch(getMatcherString(pattern, process.cwd()), { dot: true });
return (id) => {
return matcher(path.normalize(id));
};
}
function patternToCodeFilter(pattern) {
if (pattern instanceof RegExp) return (code) => {
const result = pattern.test(code);
pattern.lastIndex = 0;
return result;
};
return (code) => code.includes(pattern);
}
function createFilter(exclude, include) {
if (!exclude && !include) return;
return (input) => {
if (exclude?.some((filter) => filter(input))) return false;
if (include?.some((filter) => filter(input))) return true;
return !include || include.length <= 0;
};
}
function normalizeFilter(filter) {
if (typeof filter === "string" || filter instanceof RegExp) return { include: [filter] };
if (Array.isArray(filter)) return { include: filter };
return {
exclude: filter.exclude ? toArray(filter.exclude) : void 0,
include: filter.include ? toArray(filter.include) : void 0
};
}
function createIdFilter(filter) {
if (!filter) return;
const { exclude, include } = normalizeFilter(filter);
const excludeFilter = exclude?.map(patternToIdFilter);
const includeFilter = include?.map(patternToIdFilter);
return createFilter(excludeFilter, includeFilter);
}
function createCodeFilter(filter) {
if (!filter) return;
const { exclude, include } = normalizeFilter(filter);
const excludeFilter = exclude?.map(patternToCodeFilter);
const includeFilter = include?.map(patternToCodeFilter);
return createFilter(excludeFilter, includeFilter);
}
function createFilterForId(filter) {
const filterFunction = createIdFilter(filter);
return filterFunction ? (id) => !!filterFunction(id) : void 0;
}
function createFilterForTransform(idFilter, codeFilter) {
if (!idFilter && !codeFilter) return;
const idFilterFunction = createIdFilter(idFilter);
const codeFilterFunction = createCodeFilter(codeFilter);
return (id, code) => {
let fallback = true;
if (idFilterFunction) fallback &&= idFilterFunction(id);
if (!fallback) return false;
if (codeFilterFunction) fallback &&= codeFilterFunction(code);
return fallback;
};
}
//#endregion
//#region src/utils/config.ts
const loadConfig$1 = quansync(function* () {
const { config } = yield loadConfig({
sources: [{
files: "unloader.config",
extensions: [
"ts",
"mts",
"cts",
"js",
"mjs",
"cjs",
"json",
""
]
}],
cwd: process.cwd(),
defaults: {}
});
return config;
});
//#endregion
//#region src/utils/map.ts
function attachSourceMap(map, code) {
if (map) {
if (!map.sourcesContent || map.sourcesContent.length === 0) map.sourcesContent = [code];
code += `\n//# sourceMappingURL=${toUrl(map)}`;
}
return code;
}
function toUrl(map) {
return `data:application/json;charset=utf-8;base64,${Buffer.from(map.toString()).toString("base64")}`;
}
//#endregion
//#region src/utils/url.ts
function urlToPath(url) {
if (!url) return url;
return url.startsWith("file://") ? fileURLToPath(url) : url;
}
function pathToUrl(isRequire, path$1) {
if (isRequire || path$1.startsWith("file://") || path$1.startsWith("data://") || path$1.startsWith("node:")) return path$1;
return pathToFileURL(path$1).href;
}
//#endregion
//#region src/hooks.ts
function createHooks() {
let config;
let deactivated = false;
let pluginContext;
const init = quansync(function* (context, inlineConfig) {
pluginContext = context;
config = inlineConfig || (yield loadConfig$1());
for (const plugin of config.plugins || []) {
const { handler } = normalizePluginHook(plugin, "options");
config = handler?.call(context, config) || config;
}
for (const plugin of config.plugins || []) {
const { handler } = normalizePluginHook(plugin, "buildStart");
yield handler?.call(context);
context.debug(`loaded plugin: %s`, plugin.name);
}
return config;
});
const resolve = quansync(function* (specifier, context, nextResolve) {
if (deactivated) return nextResolve(specifier, context);
const isRequire = !Array.isArray(context.conditions) || context.conditions[0] === "require";
pluginContext?.debug(`resolving %s with context %o`, specifier, context);
if (config?.plugins && pluginContext) for (const plugin of config.plugins) {
const resolve$1 = createResolve(nextResolve, isRequire, pluginContext.debug);
const isAsync = yield getIsAsync();
const { handler, filter } = normalizePluginHook(plugin, "resolveId");
const filterFn = createFilterForId(filter?.id);
const id = urlToPath(specifier);
if (filterFn && !filterFn(id)) continue;
const result = yield handler?.call({
resolve: isAsync ? resolve$1 : resolve$1.sync,
...pluginContext
}, id, urlToPath(context.parentURL), {
conditions: context.conditions,
attributes: context.importAttributes
});
if (result) {
let output;
if (typeof result === "string") output = {
url: pathToUrl(isRequire, result),
importAttributes: context.importAttributes,
shortCircuit: true
};
else output = {
url: pathToUrl(isRequire, result.id),
format: result.format,
importAttributes: result.attributes || context.importAttributes,
shortCircuit: true
};
pluginContext.debug(`resolved %s to %s with format %s`, specifier, output.url, output.format);
return output;
}
}
return nextResolve(specifier, context);
});
const load = quansync(function* (url, context, nextLoad) {
if (deactivated || !config?.plugins) return nextLoad(url, context);
pluginContext?.debug(`load %s with context %o`, url, context);
let result;
const defaultFormat = context.format || "module";
const maps = [];
for (const plugin of config.plugins) {
const { handler, filter } = normalizePluginHook(plugin, "load");
const filterFn = createFilterForId(filter?.id);
const id = urlToPath(url);
if (filterFn && !filterFn(id)) continue;
const loadResult = yield handler?.call(pluginContext, id, {
format: context.format,
conditions: context.conditions,
attributes: context.importAttributes
});
if (loadResult) {
if (isModuleSource(loadResult)) result = {
source: loadResult,
format: defaultFormat,
shortCircuit: true
};
else {
if (loadResult.map) maps.unshift(loadResult.map);
result = {
source: loadResult.code,
format: loadResult.format || defaultFormat,
shortCircuit: true
};
}
break;
}
}
result ||= yield nextLoad(url, context);
for (const plugin of config.plugins) {
const { handler, filter } = normalizePluginHook(plugin, "transform");
const filterFn = createFilterForTransform(filter?.id, filter?.code);
const code = result.source || "";
const id = urlToPath(url);
if (filterFn && (typeof code !== "string" || !filterFn(id, code || ""))) continue;
const transformResult = yield handler?.call(pluginContext, code, id, {
format: result.format,
conditions: context.conditions,
attributes: context.importAttributes
});
if (transformResult) if (isModuleSource(transformResult)) result = {
...result,
source: transformResult
};
else {
if (transformResult.map) maps.unshift(transformResult.map);
result = {
...result,
source: transformResult.code,
format: transformResult.format || result.format
};
}
}
if (maps.length && typeof result.source === "string") {
const code = attachSourceMap(remapping(maps, () => null), result.source);
result.source = code;
}
return result;
});
const deactivate = () => {
deactivated = true;
};
return {
init,
resolve,
load,
deactivate
};
}
function createResolve(nextResolve, isRequire, debug) {
return quansync(function* (source, importer, options) {
if (!path.isAbsolute(source) && importer) source = path.resolve(importer, "..", source);
try {
const resolved = yield nextResolve(pathToUrl(isRequire, source), {
parentURL: importer,
conditions: options?.conditions,
importAttributes: options?.attributes
});
debug("resolved %s to %s with format %s", source, resolved.url, resolved.format);
return {
id: urlToPath(resolved.url),
attributes: resolved.importAttributes,
format: resolved.format
};
} catch (error) {
debug("error resolving %s: %o", source, error);
return null;
}
});
}
function isModuleSource(v) {
return typeof v === "string" || ArrayBuffer.isView(v) || v instanceof ArrayBuffer;
}
//#endregion
//#region package.json
var version = "0.8.3";
//#endregion
//#region src/utils/context.ts
const sharedPluginContext = {
error: (message) => {
throw typeof message === "string" ? new Error(message) : message;
},
meta: { unloaderVersion: version }
};
//#endregion
export { normalizePluginHook as i, createHooks as n, loadConfig$1 as r, sharedPluginContext as t };
import "./context-DRCo_iRa.mjs";
import { n as registerSync } from "./api-CXxT_bvN.mjs";
//#region src/register-sync.ts
registerSync();
//#endregion
export { };
import { InitializeHook, LoadHook, ResolveHook } from "node:module";
import { MessagePort } from "node:worker_threads";
//#region src/worker.d.ts
interface Data {
port: MessagePort;
inlineConfig?: string;
}
declare const threadFunctions: {
deactivate(): void;
ping(): Promise<boolean>;
};
type ThreadFunctions = typeof threadFunctions;
declare const initialize: InitializeHook;
declare const resolve: ResolveHook;
declare const load: LoadHook;
//#endregion
export { Data, ThreadFunctions, initialize, load, resolve };
import { n as createHooks, t as sharedPluginContext } from "./context-DRCo_iRa.mjs";
import process from "node:process";
import { createBirpc } from "birpc";
//#region src/worker.ts
let data;
const { promise: promisePong, resolve: pong } = Promise.withResolvers();
const hooks = createHooks();
const threadFunctions = {
deactivate() {
hooks.deactivate();
},
ping() {
return promisePong;
}
};
let rpc;
const initialize = async (_data) => {
data = _data;
const { port } = data;
rpc = createBirpc(threadFunctions, {
post: (data$1) => port.postMessage(data$1),
on: (fn) => port.on("message", fn)
});
const context = {
...sharedPluginContext,
port,
log: (...args) => rpc.log(...args),
debug: (...args) => rpc.debug(...args)
};
let inlineConfig;
if (data.inlineConfig) inlineConfig = (await import(data.inlineConfig)).default;
pong(!!((await hooks.init(context, inlineConfig)).sourcemap && !process.sourceMapsEnabled));
};
const resolve = hooks.resolve.async;
const load = hooks.load.async;
//#endregion
export { initialize, load, resolve };