mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
module: implement register utility
PR-URL: https://github.com/nodejs/node/pull/46826 Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
parent
d2d4a310f1
commit
a40a6c890a
|
|
@ -1267,6 +1267,23 @@ provided.
|
|||
Encoding provided to `TextDecoder()` API was not one of the
|
||||
[WHATWG Supported Encodings][].
|
||||
|
||||
<a id="ERR_ESM_LOADER_REGISTRATION_UNAVAILABLE"></a>
|
||||
|
||||
### `ERR_ESM_LOADER_REGISTRATION_UNAVAILABLE`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Programmatically registering custom ESM loaders
|
||||
currently requires at least one custom loader to have been
|
||||
registered via the `--experimental-loader` flag. A no-op
|
||||
loader registered via CLI is sufficient
|
||||
(for example: `--experimental-loader data:text/javascript,`;
|
||||
do not omit the necessary trailing comma).
|
||||
A future version of Node.js will support the programmatic
|
||||
registration of loaders without needing to also use the flag.
|
||||
|
||||
<a id="ERR_EVAL_ESM_CANNOT_PRINT"></a>
|
||||
|
||||
### `ERR_EVAL_ESM_CANNOT_PRINT`
|
||||
|
|
|
|||
|
|
@ -1225,6 +1225,17 @@ console.log('some module!');
|
|||
If you run `node --experimental-loader ./import-map-loader.js main.js`
|
||||
the output will be `some module!`.
|
||||
|
||||
### Register loaders programmatically
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
In addition to using the `--experimental-loader` option in the CLI,
|
||||
loaders can also be registered programmatically. You can find
|
||||
detailed information about this process in the documentation page
|
||||
for [`module.register()`][].
|
||||
|
||||
## Resolution and loading algorithm
|
||||
|
||||
### Features
|
||||
|
|
@ -1599,6 +1610,7 @@ for ESM specifiers is [commonjs-extension-resolution-loader][].
|
|||
[`import.meta.url`]: #importmetaurl
|
||||
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
||||
[`module.createRequire()`]: module.md#modulecreaterequirefilename
|
||||
[`module.register()`]: module.md#moduleregister
|
||||
[`module.syncBuiltinESMExports()`]: module.md#modulesyncbuiltinesmexports
|
||||
[`package.json`]: packages.md#nodejs-packagejson-field-definitions
|
||||
[`port.ref()`]: https://nodejs.org/dist/latest-v17.x/docs/api/worker_threads.html#portref
|
||||
|
|
|
|||
|
|
@ -80,6 +80,101 @@ isBuiltin('fs'); // true
|
|||
isBuiltin('wss'); // false
|
||||
```
|
||||
|
||||
### `module.register()`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
In addition to using the `--experimental-loader` option in the CLI,
|
||||
loaders can be registered programmatically using the
|
||||
`module.register()` method.
|
||||
|
||||
```mjs
|
||||
import { register } from 'node:module';
|
||||
|
||||
register('http-to-https', import.meta.url);
|
||||
|
||||
// Because this is a dynamic `import()`, the `http-to-https` hooks will run
|
||||
// before importing `./my-app.mjs`.
|
||||
await import('./my-app.mjs');
|
||||
```
|
||||
|
||||
In the example above, we are registering the `http-to-https` loader,
|
||||
but it will only be available for subsequently imported modules—in
|
||||
this case, `my-app.mjs`. If the `await import('./my-app.mjs')` had
|
||||
instead been a static `import './my-app.mjs'`, _the app would already
|
||||
have been loaded_ before the `http-to-https` hooks were
|
||||
registered. This is part of the design of ES modules, where static
|
||||
imports are evaluated from the leaves of the tree first back to the
|
||||
trunk. There can be static imports _within_ `my-app.mjs`, which
|
||||
will not be evaluated until `my-app.mjs` is when it's dynamically
|
||||
imported.
|
||||
|
||||
The `--experimental-loader` flag of the CLI can be used together
|
||||
with the `register` function; the loaders registered with the
|
||||
function will follow the same evaluation chain of loaders registered
|
||||
within the CLI:
|
||||
|
||||
```console
|
||||
node \
|
||||
--experimental-loader unpkg \
|
||||
--experimental-loader http-to-https \
|
||||
--experimental-loader cache-buster \
|
||||
entrypoint.mjs
|
||||
```
|
||||
|
||||
```mjs
|
||||
// entrypoint.mjs
|
||||
import { URL } from 'node:url';
|
||||
import { register } from 'node:module';
|
||||
|
||||
const loaderURL = new URL('./my-programmatically-loader.mjs', import.meta.url);
|
||||
|
||||
register(loaderURL);
|
||||
await import('./my-app.mjs');
|
||||
```
|
||||
|
||||
The `my-programmatic-loader.mjs` can leverage `unpkg`,
|
||||
`http-to-https`, and `cache-buster` loaders.
|
||||
|
||||
It's also possible to use `register` more than once:
|
||||
|
||||
```mjs
|
||||
// entrypoint.mjs
|
||||
import { URL } from 'node:url';
|
||||
import { register } from 'node:module';
|
||||
|
||||
register(new URL('./first-loader.mjs', import.meta.url));
|
||||
register('./second-loader.mjs', import.meta.url);
|
||||
await import('./my-app.mjs');
|
||||
```
|
||||
|
||||
Both loaders (`first-loader.mjs` and `second-loader.mjs`) can use
|
||||
all the resources provided by the loaders registered in the CLI. But
|
||||
remember that they will only be available in the next imported
|
||||
module (`my-app.mjs`). The evaluation order of the hooks when
|
||||
importing `my-app.mjs` and consecutive modules in the example above
|
||||
will be:
|
||||
|
||||
```console
|
||||
resolve: second-loader.mjs
|
||||
resolve: first-loader.mjs
|
||||
resolve: cache-buster
|
||||
resolve: http-to-https
|
||||
resolve: unpkg
|
||||
load: second-loader.mjs
|
||||
load: first-loader.mjs
|
||||
load: cache-buster
|
||||
load: http-to-https
|
||||
load: unpkg
|
||||
globalPreload: second-loader.mjs
|
||||
globalPreload: first-loader.mjs
|
||||
globalPreload: cache-buster
|
||||
globalPreload: http-to-https
|
||||
globalPreload: unpkg
|
||||
```
|
||||
|
||||
### `module.syncBuiltinESMExports()`
|
||||
|
||||
<!-- YAML
|
||||
|
|
|
|||
|
|
@ -1036,6 +1036,11 @@ E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) {
|
|||
}, TypeError);
|
||||
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported',
|
||||
RangeError);
|
||||
E('ERR_ESM_LOADER_REGISTRATION_UNAVAILABLE', 'Programmatically registering custom ESM loaders ' +
|
||||
'currently requires at least one custom loader to have been registered via the --experimental-loader ' +
|
||||
'flag. A no-op loader registered via CLI is sufficient (for example: `--experimental-loader ' +
|
||||
'"data:text/javascript,"` with the necessary trailing comma). A future version of Node.js ' +
|
||||
'will remove this requirement.', Error);
|
||||
E('ERR_EVAL_ESM_CANNOT_PRINT', '--print cannot be used with ESM input', Error);
|
||||
E('ERR_EVENT_RECURSION', 'The event "%s" is already being dispatched', Error);
|
||||
E('ERR_FALSY_VALUE_REJECTION', function(reason) {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ const {
|
|||
validateString,
|
||||
} = require('internal/validators');
|
||||
|
||||
const { kEmptyObject } = require('internal/util');
|
||||
|
||||
const {
|
||||
defaultResolve,
|
||||
throwIfInvalidParentURL,
|
||||
|
|
@ -117,6 +119,23 @@ class Hooks {
|
|||
// Cache URLs we've already validated to avoid repeated validation
|
||||
#validatedUrls = new SafeSet();
|
||||
|
||||
/**
|
||||
* Import and register custom/user-defined module loader hook(s).
|
||||
* @param {string} urlOrSpecifier
|
||||
* @param {string} parentURL
|
||||
*/
|
||||
async register(urlOrSpecifier, parentURL) {
|
||||
const moduleLoader = require('internal/process/esm_loader').esmLoader;
|
||||
|
||||
const keyedExports = await moduleLoader.import(
|
||||
urlOrSpecifier,
|
||||
parentURL,
|
||||
kEmptyObject,
|
||||
);
|
||||
|
||||
this.addCustomLoader(urlOrSpecifier, keyedExports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect custom/user-defined module loader hook(s).
|
||||
* After all hooks have been collected, the global preload hook(s) must be initialized.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const {
|
|||
} = primordials;
|
||||
|
||||
const {
|
||||
ERR_ESM_LOADER_REGISTRATION_UNAVAILABLE,
|
||||
ERR_UNKNOWN_MODULE_FORMAT,
|
||||
} = require('internal/errors').codes;
|
||||
const { getOptionValue } = require('internal/options');
|
||||
|
|
@ -287,12 +288,19 @@ class CustomizedModuleLoader extends DefaultModuleLoader {
|
|||
constructor() {
|
||||
super();
|
||||
|
||||
if (hooksProxy) {
|
||||
// The worker proxy is shared across all instances, so don't recreate it if it already exists.
|
||||
return;
|
||||
getHooksProxy();
|
||||
}
|
||||
const { HooksProxy } = require('internal/modules/esm/hooks');
|
||||
hooksProxy = new HooksProxy(); // The user's custom hooks are loaded within the worker as part of its startup.
|
||||
|
||||
/**
|
||||
* Register some loader specifier.
|
||||
* @param {string} originalSpecifier The specified URL path of the loader to
|
||||
* be registered.
|
||||
* @param {string} parentURL The parent URL from where the loader will be
|
||||
* registered if using it package name as specifier
|
||||
* @returns {{ format: string, url: URL['href'] }}
|
||||
*/
|
||||
register(originalSpecifier, parentURL) {
|
||||
return hooksProxy.makeSyncRequest('register', originalSpecifier, parentURL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -370,7 +378,46 @@ function createModuleLoader(useCustomLoadersIfPresent = true) {
|
|||
return new DefaultModuleLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HooksProxy instance. If it is not defined, then create a new one.
|
||||
* @returns {HooksProxy}
|
||||
*/
|
||||
function getHooksProxy() {
|
||||
if (!hooksProxy) {
|
||||
const { HooksProxy } = require('internal/modules/esm/hooks');
|
||||
hooksProxy = new HooksProxy();
|
||||
}
|
||||
|
||||
return hooksProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a single loader programmatically.
|
||||
* @param {string} specifier
|
||||
* @param {string} [parentURL]
|
||||
* @returns {void}
|
||||
* @example
|
||||
* ```js
|
||||
* register('./myLoader.js');
|
||||
* register('ts-node/esm', import.meta.url);
|
||||
* register('./myLoader.js', import.meta.url);
|
||||
* register(new URL('./myLoader.js', import.meta.url));
|
||||
* ```
|
||||
*/
|
||||
function register(specifier, parentURL = 'data:') {
|
||||
// TODO: Remove this limitation in a follow-up before `register` is released publicly
|
||||
if (getOptionValue('--experimental-loader').length < 1) {
|
||||
throw new ERR_ESM_LOADER_REGISTRATION_UNAVAILABLE();
|
||||
}
|
||||
|
||||
const moduleLoader = require('internal/process/esm_loader').esmLoader;
|
||||
|
||||
moduleLoader.register(`${specifier}`, parentURL);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DefaultModuleLoader,
|
||||
createModuleLoader,
|
||||
getHooksProxy,
|
||||
register,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -146,9 +146,9 @@ async function initializeHooks() {
|
|||
load(url, context) { return hooks.load(url, context); }
|
||||
}
|
||||
const privateModuleLoader = new ModuleLoader();
|
||||
|
||||
const parentURL = pathToFileURL(cwd).href;
|
||||
|
||||
// TODO(jlenon7): reuse the `Hooks.register()` method for registering loaders.
|
||||
for (let i = 0; i < customLoaderURLs.length; i++) {
|
||||
const customLoaderURL = customLoaderURLs[i];
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
const { findSourceMap } = require('internal/source_map/source_map_cache');
|
||||
const { Module } = require('internal/modules/cjs/loader');
|
||||
const { register } = require('internal/modules/esm/loader');
|
||||
const { SourceMap } = require('internal/source_map/source_map');
|
||||
|
||||
Module.findSourceMap = findSourceMap;
|
||||
Module.register = register;
|
||||
Module.SourceMap = SourceMap;
|
||||
module.exports = Module;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,28 @@ describe('Loader hooks', { concurrency: true }, () => {
|
|||
assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/);
|
||||
});
|
||||
|
||||
it('are called with all expected arguments using register function', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader=data:text/javascript,',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
`register(${JSON.stringify(fixtures.fileURL('/es-module-loaders/hooks-input.mjs'))});` +
|
||||
`await import(${JSON.stringify(fixtures.fileURL('/es-modules/json-modules.mjs'))});`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/);
|
||||
assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/);
|
||||
});
|
||||
|
||||
describe('should handle never-settling hooks in ESM files', { concurrency: true }, () => {
|
||||
it('top-level await of a never-settling resolve', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
|
|
|
|||
236
test/es-module/test-esm-loader-programmatically.mjs
Normal file
236
test/es-module/test-esm-loader-programmatically.mjs
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
import { spawnPromisified } from '../common/index.mjs';
|
||||
import * as fixtures from '../common/fixtures.js';
|
||||
import assert from 'node:assert';
|
||||
import { execPath } from 'node:process';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
// This test ensures that the register function can register loaders
|
||||
// programmatically.
|
||||
|
||||
const commonArgs = [
|
||||
'--no-warnings',
|
||||
'--input-type=module',
|
||||
'--loader=data:text/javascript,',
|
||||
];
|
||||
|
||||
const commonEvals = {
|
||||
import: (module) => `await import(${JSON.stringify(module)});`,
|
||||
register: (loader, parentURL = 'file:///') => `register(${JSON.stringify(loader)}, ${JSON.stringify(parentURL)});`,
|
||||
dynamicImport: (module) => `await import(${JSON.stringify(`data:text/javascript,${encodeURIComponent(module)}`)});`,
|
||||
staticImport: (module) => `import ${JSON.stringify(`data:text/javascript,${encodeURIComponent(module)}`)};`,
|
||||
};
|
||||
|
||||
describe('ESM: programmatically register loaders', { concurrency: true }, () => {
|
||||
it('works with only a dummy CLI argument', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs')) +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
|
||||
assert.match(lines[0], /resolve passthru/);
|
||||
assert.match(lines[1], /load passthru/);
|
||||
assert.match(lines[2], /Hello from dynamic import/);
|
||||
|
||||
assert.strictEqual(lines[3], '');
|
||||
});
|
||||
|
||||
describe('registering via --import', { concurrency: true }, () => {
|
||||
for (const moduleType of ['mjs', 'cjs']) {
|
||||
it(`should programmatically register a loader from a ${moduleType.toUpperCase()} file`, async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--import', fixtures.fileURL('es-module-loaders', `register-loader.${moduleType}`).href,
|
||||
'--eval', commonEvals.staticImport('console.log("entry point")'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const [
|
||||
passthruStdout,
|
||||
entryPointStdout,
|
||||
] = stdout.split('\n');
|
||||
|
||||
assert.match(passthruStdout, /resolve passthru/);
|
||||
assert.match(entryPointStdout, /entry point/);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('programmatically registered loaders are appended to an existing chaining', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--loader',
|
||||
fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs'),
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
|
||||
assert.match(lines[0], /resolve passthru/);
|
||||
assert.match(lines[1], /resolve passthru/);
|
||||
assert.match(lines[2], /load passthru/);
|
||||
assert.match(lines[3], /Hello from dynamic import/);
|
||||
|
||||
assert.strictEqual(lines[4], '');
|
||||
});
|
||||
|
||||
it('works registering loaders across files', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--eval',
|
||||
commonEvals.import(fixtures.fileURL('es-module-loaders', 'register-programmatically-loader-load.mjs')) +
|
||||
commonEvals.import(fixtures.fileURL('es-module-loaders', 'register-programmatically-loader-resolve.mjs')) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
|
||||
assert.match(lines[0], /resolve passthru/);
|
||||
assert.match(lines[1], /load passthru/);
|
||||
assert.match(lines[2], /Hello from dynamic import/);
|
||||
|
||||
assert.strictEqual(lines[3], '');
|
||||
});
|
||||
|
||||
it('works registering loaders across virtual files', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--eval',
|
||||
commonEvals.import(fixtures.fileURL('es-module-loaders', 'register-programmatically-loader-load.mjs')) +
|
||||
commonEvals.dynamicImport(
|
||||
commonEvals.import(fixtures.fileURL('es-module-loaders', 'register-programmatically-loader-resolve.mjs')) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
|
||||
assert.match(lines[0], /resolve passthru/);
|
||||
assert.match(lines[1], /load passthru/);
|
||||
assert.match(lines[2], /Hello from dynamic import/);
|
||||
|
||||
assert.strictEqual(lines[3], '');
|
||||
});
|
||||
|
||||
it('works registering the same loaders more them once', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs')) +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs')) +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
|
||||
assert.match(lines[0], /resolve passthru/);
|
||||
assert.match(lines[1], /resolve passthru/);
|
||||
assert.match(lines[2], /load passthru/);
|
||||
assert.match(lines[3], /load passthru/);
|
||||
assert.match(lines[4], /Hello from dynamic import/);
|
||||
|
||||
assert.strictEqual(lines[5], '');
|
||||
});
|
||||
|
||||
it('works registering loaders as package name', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
commonEvals.register('resolve', fixtures.fileURL('es-module-loaders', 'package.json')) +
|
||||
commonEvals.register('load', fixtures.fileURL('es-module-loaders', 'package.json')) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
|
||||
assert.match(lines[0], /resolve passthru/);
|
||||
assert.match(lines[1], /load passthru/);
|
||||
assert.match(lines[2], /Hello from dynamic import/);
|
||||
|
||||
assert.strictEqual(lines[3], '');
|
||||
});
|
||||
|
||||
it('does not work without dummy CLI loader', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
assert.match(stderr, /ERR_ESM_LOADER_REGISTRATION_UNAVAILABLE/);
|
||||
});
|
||||
|
||||
it('does not work with a loader specifier that does not exist', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
commonEvals.register('./not-found.mjs', import.meta.url) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
assert.match(stderr, /ERR_MODULE_NOT_FOUND/);
|
||||
});
|
||||
|
||||
it('does not work with a loader that got syntax errors', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
...commonArgs,
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
commonEvals.register(fixtures.fileURL('es-module-loaders', 'syntax-error.mjs')) +
|
||||
commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
assert.match(stderr, /SyntaxError/);
|
||||
});
|
||||
});
|
||||
|
|
@ -17,7 +17,11 @@ export async function resolve(specifier, context, next) {
|
|||
if (resolveCalls === 1) {
|
||||
url = new URL(specifier).href;
|
||||
assert.match(specifier, /json-modules\.mjs$/);
|
||||
|
||||
if (!(/\[eval\d*\]$/).test(context.parentURL)) {
|
||||
assert.strictEqual(context.parentURL, undefined);
|
||||
}
|
||||
|
||||
assert.deepStrictEqual(context.importAssertions, {});
|
||||
} else if (resolveCalls === 2) {
|
||||
url = new URL(specifier, context.parentURL).href;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { writeSync } from 'node:fs';
|
||||
|
||||
|
||||
export async function load(url, context, next) {
|
||||
// This check is needed to make sure that we don't prevent the
|
||||
// resolution from follow-up loaders. It wouldn't be a problem
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { writeSync } from 'node:fs';
|
||||
|
||||
|
||||
export async function resolve(specifier, context, next) {
|
||||
// This check is needed to make sure that we don't prevent the
|
||||
// resolution from follow-up loaders. It wouldn't be a problem
|
||||
|
|
|
|||
1
test/fixtures/es-module-loaders/node_modules/load/index.mjs
generated
vendored
Normal file
1
test/fixtures/es-module-loaders/node_modules/load/index.mjs
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from '../../loader-load-passthru.mjs'
|
||||
3
test/fixtures/es-module-loaders/node_modules/load/package.json
generated
vendored
Normal file
3
test/fixtures/es-module-loaders/node_modules/load/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"exports": "./index.mjs"
|
||||
}
|
||||
1
test/fixtures/es-module-loaders/node_modules/resolve/index.mjs
generated
vendored
Normal file
1
test/fixtures/es-module-loaders/node_modules/resolve/index.mjs
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from '../../loader-resolve-passthru.mjs'
|
||||
3
test/fixtures/es-module-loaders/node_modules/resolve/package.json
generated
vendored
Normal file
3
test/fixtures/es-module-loaders/node_modules/resolve/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"exports": "./index.mjs"
|
||||
}
|
||||
4
test/fixtures/es-module-loaders/register-loader.cjs
vendored
Normal file
4
test/fixtures/es-module-loaders/register-loader.cjs
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
const { register } = require('node:module');
|
||||
const fixtures = require('../../common/fixtures.js');
|
||||
|
||||
register(fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs'));
|
||||
4
test/fixtures/es-module-loaders/register-loader.mjs
vendored
Normal file
4
test/fixtures/es-module-loaders/register-loader.mjs
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { register } from 'node:module';
|
||||
import fixtures from '../../common/fixtures.js';
|
||||
|
||||
register(fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs'));
|
||||
4
test/fixtures/es-module-loaders/register-programmatically-loader-load.mjs
vendored
Normal file
4
test/fixtures/es-module-loaders/register-programmatically-loader-load.mjs
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import * as fixtures from '../../common/fixtures.mjs';
|
||||
import { register } from 'node:module';
|
||||
|
||||
register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs'));
|
||||
3
test/fixtures/es-module-loaders/register-programmatically-loader-resolve.mjs
vendored
Normal file
3
test/fixtures/es-module-loaders/register-programmatically-loader-resolve.mjs
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { register } from 'node:module';
|
||||
|
||||
register('./loader-resolve-passthru.mjs', import.meta.url);
|
||||
Loading…
Reference in New Issue
Block a user