feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+16
View File
@@ -0,0 +1,16 @@
import { createJiti } from 'jiti';
async function loader(info, options) {
const jiti = createJiti(info.parentPath, {
...info.cache === false ? {
cache: false,
requireCache: false
} : {},
...options.loaderOptions?.jiti
});
const mod = await jiti.import(info.specifier);
info.dependencies = Object.values(jiti.cache || {}).map((i) => i.filename).filter(Boolean);
return mod;
}
export { loader };
+34
View File
@@ -0,0 +1,34 @@
import { register, tsImport } from 'tsx/esm/api';
const apiESM = /* @__PURE__ */ new Map();
async function loader(info, options) {
const dependencies = [];
info.dependencies = dependencies;
if (info.cache === true) {
if (!apiESM.get(info.fullPath)) {
apiESM.set(info.fullPath, register({
onImport(url) {
dependencies.push(url);
},
namespace: `importx_${Math.random().toString(36).slice(2)}`,
...options.loaderOptions?.tsx
}));
}
return await apiESM.get(info.fullPath).import(
info.specifier,
info.parentPath
);
}
return await tsImport(
info.specifier,
{
onImport(url) {
dependencies.push(url);
},
parentURL: info.parentPath,
...options.loaderOptions?.tsx
}
);
}
export { loader };
+189
View File
@@ -0,0 +1,189 @@
import * as bundle_require from 'bundle-require';
import * as jiti from 'jiti';
import * as tsx_esm_api from 'tsx/esm/api';
type ArgumentTypes<T> = T extends (...args: infer U) => any ? U : never;
type SupportedLoader = 'tsx' | 'jiti' | 'bundle-require' | 'native';
interface FeaturesOptions {
/**
* Whether to cache the imported module.
*
* Setting to `null` means it doesn't matter for you.
*
* By the spec of ESM, modules are always cached.
*
* Meaning that if you want to re-import a module without cache,
* you can't use native ESM import.
*
* `cache: false` does not compatible with following loaders:
* - `native`
*
* `cache: true` does not compatible with following loaders:
* - `bundle-require`
*
* Affects `auto` resolution:
* - When set to `false`, `native` will be fallback to `tsx` or `jiti`
*
* @default null
*/
cache?: boolean | null;
/**
* List dependencies of the module.
*
* Only available for `tsx` and `bundle-require` loader.
*
* Affects `auto` resolution:
* - When set to `true`, `jiti` will be fallback to `bundle-require`
*
* Setting to `null` means it doesn't matter for you.
*
* @default null
*/
listDependencies?: boolean | null;
/**
* Whether module resolution and evaluation type
*
* Setting to `null` means it doesn't matter for you.
*
* @default null
*/
type?: 'module' | 'commonjs' | null;
/**
* Exclude loaders from being used.
*
* Only affects `auto` resolution.
*/
excludeLoaders?: SupportedLoader[];
}
interface ImportxOptions extends FeaturesOptions {
/**
* Loader to use for importing the file.
* @default 'auto'
*/
loader?: SupportedLoader | 'auto';
/**
* Fallback loaders used to import the module.
*
* @default ['jiti']
*/
fallbackLoaders?: SupportedLoader[] | false;
/**
* Options for each loader
* Only the loader that is used will be applied.
*/
loaderOptions?: {
/**
* Options for `tsx` loader.
*
* @see https://tsx.is/node/ts-import
*/
tsx?: Omit<Partial<Exclude<ArgumentTypes<typeof tsx_esm_api.tsImport>['1'], string>>, 'parentURL'>;
/**
* Options for `jiti` loader.
*
* @see https://github.com/unjs/jiti#options
*/
jiti?: jiti.JitiOptions;
/**
* Options for `bundle-require` loader.
*
* @see https://github.com/egoist/bundle-require
* @see https://www.jsdocs.io/package/bundle-require#Options
*/
bundleRequire?: Omit<Partial<bundle_require.Options>, 'filepath' | 'cwd'>;
};
/**
* Bypass the `importx` options validation and import anyway.
*
* The final behavior is determined by the loader and might not always work as your configuration.
*
* @default false
*/
ignoreImportxWarning?: boolean;
/**
* The URL of the parent module.
* Usually you pass `import.meta.url` or `__filename` of the module you are doing the importing.
*/
parentURL: string | URL;
/**
* The `with` option for native `import()` call.
*
* @see https://github.com/tc39/proposal-import-attributes#dynamic-import
*/
with?: ImportCallOptions['with'];
}
/**
* Additional information about the imported module from `importx`.
*/
interface ImportxModuleInfo {
/**
* Resolved loader used to import the module.
*/
loader: SupportedLoader;
/**
* User passed import specifier.
*/
specifier: string;
/**
* Resolved full path the specifier points to.
*/
fullPath: string;
/**
* Enable module cache or not
*/
cache: boolean | null;
/**
* Parent URL, normalized to file URL.
*/
parentURL: URL;
/**
* Parent path, normalized to file path.
*/
parentPath: string;
/**
* Timestamp when import is initialized.
*/
timestampInit: number;
/**
* Timestamp when import is completed.
*/
timestampLoad: number;
/**
* List of dependencies of the module, presented as full file URLs.
* Only available for `tsx` and `bundle-require` loader.
* Will be `undefined` for other loaders.
*/
dependencies?: string[];
/**
* If an single module instance is imported multiple times, this will be the previous import info.
*/
previousImportInfo?: ImportxModuleInfo;
}
/**
* Import a tiny TypeScript module to verify if native TypeScript import is supported.
*/
declare function isNativeTsImportSupported(): Promise<boolean>;
declare function isTypeScriptFile(path: string): boolean;
/**
* Get the importx module info from a module instance.
* Returns `undefined` if the module is not imported by `importx`.
*/
declare function getModuleInfo(mod: any): ImportxModuleInfo | undefined;
/**
* Import a TypeScript module at runtime.
*
* @param specifier The path to the file to import.
* @param parentURL The URL of the parent module, usually `import.meta.url` or `__filename`.
*/
declare function importx<T = any>(specifier: string | URL, parentURL: string | URL): Promise<T>;
/**
* Import a TypeScript module at runtime.
*
* @param specifier The path to the file to import.
* @param options Options
*/
declare function importx<T = any>(specifier: string | URL, options: ImportxOptions): Promise<T>;
export { type FeaturesOptions, type ImportxModuleInfo, type ImportxOptions, type SupportedLoader, getModuleInfo, importx as import, importx, isNativeTsImportSupported, isTypeScriptFile };
+189
View File
@@ -0,0 +1,189 @@
import * as bundle_require from 'bundle-require';
import * as jiti from 'jiti';
import * as tsx_esm_api from 'tsx/esm/api';
type ArgumentTypes<T> = T extends (...args: infer U) => any ? U : never;
type SupportedLoader = 'tsx' | 'jiti' | 'bundle-require' | 'native';
interface FeaturesOptions {
/**
* Whether to cache the imported module.
*
* Setting to `null` means it doesn't matter for you.
*
* By the spec of ESM, modules are always cached.
*
* Meaning that if you want to re-import a module without cache,
* you can't use native ESM import.
*
* `cache: false` does not compatible with following loaders:
* - `native`
*
* `cache: true` does not compatible with following loaders:
* - `bundle-require`
*
* Affects `auto` resolution:
* - When set to `false`, `native` will be fallback to `tsx` or `jiti`
*
* @default null
*/
cache?: boolean | null;
/**
* List dependencies of the module.
*
* Only available for `tsx` and `bundle-require` loader.
*
* Affects `auto` resolution:
* - When set to `true`, `jiti` will be fallback to `bundle-require`
*
* Setting to `null` means it doesn't matter for you.
*
* @default null
*/
listDependencies?: boolean | null;
/**
* Whether module resolution and evaluation type
*
* Setting to `null` means it doesn't matter for you.
*
* @default null
*/
type?: 'module' | 'commonjs' | null;
/**
* Exclude loaders from being used.
*
* Only affects `auto` resolution.
*/
excludeLoaders?: SupportedLoader[];
}
interface ImportxOptions extends FeaturesOptions {
/**
* Loader to use for importing the file.
* @default 'auto'
*/
loader?: SupportedLoader | 'auto';
/**
* Fallback loaders used to import the module.
*
* @default ['jiti']
*/
fallbackLoaders?: SupportedLoader[] | false;
/**
* Options for each loader
* Only the loader that is used will be applied.
*/
loaderOptions?: {
/**
* Options for `tsx` loader.
*
* @see https://tsx.is/node/ts-import
*/
tsx?: Omit<Partial<Exclude<ArgumentTypes<typeof tsx_esm_api.tsImport>['1'], string>>, 'parentURL'>;
/**
* Options for `jiti` loader.
*
* @see https://github.com/unjs/jiti#options
*/
jiti?: jiti.JitiOptions;
/**
* Options for `bundle-require` loader.
*
* @see https://github.com/egoist/bundle-require
* @see https://www.jsdocs.io/package/bundle-require#Options
*/
bundleRequire?: Omit<Partial<bundle_require.Options>, 'filepath' | 'cwd'>;
};
/**
* Bypass the `importx` options validation and import anyway.
*
* The final behavior is determined by the loader and might not always work as your configuration.
*
* @default false
*/
ignoreImportxWarning?: boolean;
/**
* The URL of the parent module.
* Usually you pass `import.meta.url` or `__filename` of the module you are doing the importing.
*/
parentURL: string | URL;
/**
* The `with` option for native `import()` call.
*
* @see https://github.com/tc39/proposal-import-attributes#dynamic-import
*/
with?: ImportCallOptions['with'];
}
/**
* Additional information about the imported module from `importx`.
*/
interface ImportxModuleInfo {
/**
* Resolved loader used to import the module.
*/
loader: SupportedLoader;
/**
* User passed import specifier.
*/
specifier: string;
/**
* Resolved full path the specifier points to.
*/
fullPath: string;
/**
* Enable module cache or not
*/
cache: boolean | null;
/**
* Parent URL, normalized to file URL.
*/
parentURL: URL;
/**
* Parent path, normalized to file path.
*/
parentPath: string;
/**
* Timestamp when import is initialized.
*/
timestampInit: number;
/**
* Timestamp when import is completed.
*/
timestampLoad: number;
/**
* List of dependencies of the module, presented as full file URLs.
* Only available for `tsx` and `bundle-require` loader.
* Will be `undefined` for other loaders.
*/
dependencies?: string[];
/**
* If an single module instance is imported multiple times, this will be the previous import info.
*/
previousImportInfo?: ImportxModuleInfo;
}
/**
* Import a tiny TypeScript module to verify if native TypeScript import is supported.
*/
declare function isNativeTsImportSupported(): Promise<boolean>;
declare function isTypeScriptFile(path: string): boolean;
/**
* Get the importx module info from a module instance.
* Returns `undefined` if the module is not imported by `importx`.
*/
declare function getModuleInfo(mod: any): ImportxModuleInfo | undefined;
/**
* Import a TypeScript module at runtime.
*
* @param specifier The path to the file to import.
* @param parentURL The URL of the parent module, usually `import.meta.url` or `__filename`.
*/
declare function importx<T = any>(specifier: string | URL, parentURL: string | URL): Promise<T>;
/**
* Import a TypeScript module at runtime.
*
* @param specifier The path to the file to import.
* @param options Options
*/
declare function importx<T = any>(specifier: string | URL, options: ImportxOptions): Promise<T>;
export { type FeaturesOptions, type ImportxModuleInfo, type ImportxOptions, type SupportedLoader, getModuleInfo, importx as import, importx, isNativeTsImportSupported, isTypeScriptFile };
+200
View File
@@ -0,0 +1,200 @@
import { fileURLToPath, pathToFileURL } from 'node:url';
import Debug from 'debug';
import { dirname, join } from 'pathe';
let _loaderMatrix;
async function _createLoaderMatrix(options) {
const matrix = [
{
name: "native",
cache: [true],
listDependencies: [false],
type: ["module", "commonjs"],
importTS: options.isNativeTsImportSupported ? [true, false] : [false]
},
{
name: "tsx",
supported: options.isRuntimeSupportsTsx,
type: ["module", "commonjs"],
cache: [true, false],
listDependencies: [true, false],
importTS: [true, false]
},
{
name: "jiti",
type: ["commonjs"],
cache: [true, false],
listDependencies: [true, false],
importTS: [true, false]
},
{
name: "bundle-require",
type: ["module", "commonjs"],
cache: [false],
listDependencies: [true],
importTS: [true, false]
}
];
return matrix.filter((i) => i.supported !== false);
}
async function getLoaderMatrix() {
if (_loaderMatrix)
return _loaderMatrix;
_loaderMatrix = await _createLoaderMatrix({
isNativeTsImportSupported: await isNativeTsImportSupported(),
isRuntimeSupportsTsx: await isRuntimeSupportsTsx()
});
return _loaderMatrix;
}
let _isNativeTsImportSupported;
async function isNativeTsImportSupported() {
if (_isNativeTsImportSupported === undefined) {
if (typeof process !== "undefined" && process.features?.typescript) {
return _isNativeTsImportSupported = true;
}
try {
const modName = "dummy.mts";
const mod = await import(`../runtime-fixtures/${modName}`);
_isNativeTsImportSupported = mod.default === "dummy";
} catch {
_isNativeTsImportSupported = false;
}
}
return _isNativeTsImportSupported;
}
const nodeVersionNumbers = globalThis?.process?.versions?.node?.split(".").map(Number);
async function detectLoader(context, matrix) {
matrix = matrix || await getLoaderMatrix();
for (const loader of matrix) {
if (context.excludeLoaders?.includes(loader.name))
continue;
if ((context.cache === null || loader.cache.includes(context.cache)) && (context.listDependencies === null || loader.listDependencies.includes(context.listDependencies)) && (context.type === null || loader.type.includes(context.type)) && loader.importTS.includes(context.isTs)) {
return loader.name;
}
}
return null;
}
async function isRuntimeSupportsTsx() {
if (!nodeVersionNumbers || nodeVersionNumbers[0] < 18 || nodeVersionNumbers[0] === 18 && nodeVersionNumbers[1] < 19 || nodeVersionNumbers[0] === 20 && nodeVersionNumbers[1] < 8) {
return false;
}
const msBuildVersion = typeof process !== "undefined" && process.versions["microsoft-build"];
if (msBuildVersion && Number(msBuildVersion) < 10629634) {
return false;
}
return true;
}
const reIsTypeScriptFile = /\.[mc]?tsx?$/;
function isTypeScriptFile(path) {
return reIsTypeScriptFile.test(path);
}
const debug = Debug("importx");
const _moduleInfoMap = /* @__PURE__ */ new WeakMap();
function getModuleInfo(mod) {
return _moduleInfoMap.get(mod);
}
async function importx(_specifier, _options) {
const options = typeof _options === "string" || _options instanceof URL ? { parentURL: _options } : _options;
const {
loaderOptions = {},
parentURL: inputUserUrl,
cache = null,
type = null,
excludeLoaders = [],
listDependencies = null,
ignoreImportxWarning = false,
fallbackLoaders = ["jiti"],
...otherOptions
} = options;
let specifier = _specifier instanceof URL ? fileURLToPath(_specifier) : _specifier;
specifier = specifier.match(/^[a-z]:/i) ? pathToFileURL(specifier).href : specifier;
let loader = options.loader || getLoaderFromEnv() || "auto";
if (loader === "auto") {
const context = {
cache,
listDependencies,
type,
isTs: isTypeScriptFile(specifier),
excludeLoaders
};
const _loader = await detectLoader(context);
if (!_loader)
throw new Error(`[importx] Cannot find a suitable loader for given requirements ${JSON.stringify(context)}`);
loader = _loader;
}
const parentPath = typeof inputUserUrl === "string" && !inputUserUrl.includes("://") ? inputUserUrl : fileURLToPath(inputUserUrl);
const parentURL = pathToFileURL(parentPath);
const fullPath = specifier[0] === "." ? fileURLToPath(new URL(specifier, parentURL)) : specifier;
const info = {
loader,
cache,
specifier,
fullPath,
parentURL,
parentPath,
timestampInit: Date.now(),
timestampLoad: -1
};
async function run(loader2) {
info.loader = loader2;
debug(`[${loader2}]`, "Importing", fullPath, "from", parentPath);
switch (loader2) {
case "native": {
if (cache === false && !ignoreImportxWarning)
throw new Error("`cache: false` is not compatible with `native` loader");
return import(fullPath, otherOptions);
}
case "tsx": {
return import('./chunks/tsx.mjs').then((r) => r.loader(info, options));
}
case "jiti": {
return import('./chunks/jiti.mjs').then((r) => r.loader(info, options));
}
case "bundle-require": {
if (cache === true && !ignoreImportxWarning)
throw new Error("`cache: true` is not compatible with `native` loader");
const cwd = dirname(parentPath);
return import('bundle-require').then((r) => r.bundleRequire({
format: type === "commonjs" ? "cjs" : "esm",
...loaderOptions.bundleRequire,
filepath: fullPath,
cwd
})).then((r) => {
info.dependencies = r.dependencies.map((d) => pathToFileURL(join(cwd, d)).href);
return r.mod;
});
}
default: {
throw new Error(`Unknown loader: ${loader2}`);
}
}
}
const loaders = /* @__PURE__ */ new Set([
loader,
...fallbackLoaders || []
]);
let error;
for (const loader2 of loaders) {
try {
const mod = await run(loader2);
info.timestampLoad = Date.now();
const previous = _moduleInfoMap.get(mod);
if (previous)
info.previousImportInfo = previous;
_moduleInfoMap.set(mod, info);
return mod;
} catch (err) {
error = err;
debug(`[${loader2}]`, err);
}
}
throw error;
}
function getLoaderFromEnv() {
if (typeof process !== "undefined" && process.env)
return process.env.IMPORTX_LOADER || undefined;
return undefined;
}
export { getModuleInfo, importx as import, importx, isNativeTsImportSupported, isTypeScriptFile };