diff --git a/doc/api/errors.md b/doc/api/errors.md index 80b5f44bc59cbe..c322ddb1b5a51b 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1570,14 +1570,6 @@ strict compliance with the API specification (which in some cases may accept `func(undefined)` and `func()` are treated identically, and the [`ERR_INVALID_ARG_TYPE`][] error code may be used instead. - -### `ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK` - -> Stability: 1 - Experimental - -An [ES Module][] loader hook specified `format: 'dynamic'` but did not provide -a `dynamicInstantiate` hook. - ### `ERR_MISSING_OPTION` @@ -2519,12 +2511,6 @@ while trying to read and parse it. The `--entry-type=...` flag is not compatible with the Node.js REPL. - -#### `ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK` - -Used when an [ES Module][] loader hook specifies `format: 'dynamic'` but does -not provide a `dynamicInstantiate` hook. - #### `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` diff --git a/doc/api/esm.md b/doc/api/esm.md index f8a7595a721873..38885d7a0b243e 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -1204,7 +1204,6 @@ of the following: | --- | --- | --- | | `'builtin'` | Load a Node.js builtin module | Not applicable | | `'commonjs'` | Load a Node.js CommonJS module | Not applicable | -| `'dynamic'` | Use a [dynamic instantiate hook][] | Not applicable | | `'json'` | Load a JSON file | { [ArrayBuffer][], [string][], [TypedArray][] } | | `'module'` | Load an ES module | { [ArrayBuffer][], [string][], [TypedArray][] } | | `'wasm'` | Load a WebAssembly module | { [ArrayBuffer][], [string][], [TypedArray][] } | @@ -1345,38 +1344,6 @@ const require = createRequire(process.cwd() + '/'); } ``` -#### dynamicInstantiate hook - -> Note: The loaders API is being redesigned. This hook may disappear or its -> signature may change. Do not rely on the API described below. - -To create a custom dynamic module that doesn't correspond to one of the -existing `format` interpretations, the `dynamicInstantiate` hook can be used. -This hook is called only for modules that return `format: 'dynamic'` from -the [`getFormat` hook][]. - -```js -/** - * @param {string} url - * @returns {object} response - * @returns {array} response.exports - * @returns {function} response.execute - */ -export async function dynamicInstantiate(url) { - return { - exports: ['customExportName'], - execute: (exports) => { - // Get and set functions provided for pre-allocated export names - exports.customExportName.set('value'); - } - }; -} -``` - -With the list of module exports provided upfront, the `execute` function will -then be called at the exact point of module evaluation order for that module -in the import tree. - ### Examples The various loader hooks can be used together to accomplish wide-ranging @@ -1846,7 +1813,6 @@ success! [`data:` URLs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs [`esm`]: https://github.com/standard-things/esm#readme [`export`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export -[`getFormat` hook]: #esm_code_getformat_code_hook [`import()`]: #esm_import_expressions [`import.meta.url`]: #esm_import_meta [`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import @@ -1859,7 +1825,6 @@ success! [TypedArray]: http://www.ecma-international.org/ecma-262/6.0/#sec-typedarray-objects [Uint8Array]: http://www.ecma-international.org/ecma-262/6.0/#sec-uint8array [`util.TextDecoder`]: util.html#util_class_util_textdecoder -[dynamic instantiate hook]: #esm_code_dynamicinstantiate_code_hook [import an ES or CommonJS module for its side effects only]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Import_a_module_for_its_side_effects_only [special scheme]: https://url.spec.whatwg.org/#special-scheme [the full specifier path]: #esm_mandatory_file_extensions diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 8bd39eaaad082f..b1c434e25c3f1d 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1258,9 +1258,6 @@ E('ERR_MISSING_ARGS', } return `${msg} must be specified`; }, TypeError); -E('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK', - 'The ES Module loader may not return a format of \'dynamic\' when no ' + - 'dynamicInstantiate function was provided', Error); E('ERR_MISSING_OPTION', '%s is required', TypeError); E('ERR_MODULE_NOT_FOUND', (path, base, type = 'package') => { return `Cannot find ${type} '${path}' imported from ${base}`; diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 135c541813d49c..de08845a820d77 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -11,7 +11,6 @@ const { ERR_INVALID_RETURN_PROPERTY, ERR_INVALID_RETURN_PROPERTY_VALUE, ERR_INVALID_RETURN_VALUE, - ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK, ERR_UNKNOWN_MODULE_FORMAT } = require('internal/errors').codes; const { URL, pathToFileURL } = require('internal/url'); @@ -28,14 +27,10 @@ const { defaultGetSource } = require( 'internal/modules/esm/get_source'); const { defaultTransformSource } = require( 'internal/modules/esm/transform_source'); -const createDynamicModule = require( - 'internal/modules/esm/create_dynamic_module'); const { translators } = require( 'internal/modules/esm/translators'); const { getOptionValue } = require('internal/options'); -const debug = require('internal/util/debuglog').debuglog('esm'); - /* A Loader instance is used as the main entry point for loading ES modules. * Currently, this is a singleton -- there is only one used for loading * the main module and everything in its dependency graph. */ @@ -68,8 +63,6 @@ class Loader { // This hook is called after the module is resolved but before a translator // is chosen to load it; the format returned by this function is the name // of a translator. - // If `.format` on the returned value is 'dynamic', .dynamicInstantiate - // will be used as described below. this._getFormat = defaultGetFormat; // This hook is called just before the source code of an ES module file // is loaded. @@ -77,14 +70,6 @@ class Loader { // This hook is called just after the source code of an ES module file // is loaded, but before anything is done with the string. this._transformSource = defaultTransformSource; - // This hook is only called when getFormat is 'dynamic' and - // has the signature - // (url : string) -> Promise<{ exports: { ... }, execute: function }> - // Where `exports` is an object whose property names define the exported - // names of the generated module. `execute` is a function that receives - // an object with the same keys as `exports`, whose values are get/set - // functions for the actual exported values. - this._dynamicInstantiate = undefined; // The index for assigning unique URLs to anonymous module evaluation this.evalIndex = 0; } @@ -138,7 +123,6 @@ class Loader { } if (this._resolve === defaultResolve && - format !== 'dynamic' && !url.startsWith('file:') && !url.startsWith('data:') ) { @@ -193,8 +177,8 @@ class Loader { if (resolve !== undefined) this._resolve = FunctionPrototypeBind(resolve, null); if (dynamicInstantiate !== undefined) { - this._dynamicInstantiate = - FunctionPrototypeBind(dynamicInstantiate, null); + process.emitWarning( + 'The dynamicInstantiate loader hook has been removed.'); } if (getFormat !== undefined) { this._getFormat = FunctionPrototypeBind(getFormat, null); @@ -248,25 +232,10 @@ class Loader { if (job !== undefined) return job; - let loaderInstance; - if (format === 'dynamic') { - if (typeof this._dynamicInstantiate !== 'function') - throw new ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK(); - - loaderInstance = async (url) => { - debug(`Translating dynamic ${url}`); - const { exports, execute } = await this._dynamicInstantiate(url); - return createDynamicModule([], exports, url, (reflect) => { - debug(`Loading dynamic ${url}`); - execute(reflect.exports); - }).module; - }; - } else { - if (!translators.has(format)) - throw new ERR_UNKNOWN_MODULE_FORMAT(format); + if (!translators.has(format)) + throw new ERR_UNKNOWN_MODULE_FORMAT(format); - loaderInstance = translators.get(format); - } + const loaderInstance = translators.get(format); const inspectBrk = parentURL === undefined && format === 'module' && getOptionValue('--inspect-brk'); diff --git a/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs b/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs deleted file mode 100644 index 62781c37d48240..00000000000000 --- a/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs +++ /dev/null @@ -1,8 +0,0 @@ -// Flags: --experimental-loader ./test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs -import { expectsError } from '../common/index.mjs'; - -import('test').catch(expectsError({ - code: 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK', - message: 'The ES Module loader may not return a format of \'dynamic\' ' + - 'when no dynamicInstantiate function was provided' -})); diff --git a/test/es-module/test-esm-named-exports.mjs b/test/es-module/test-esm-named-exports.mjs index 7d8d1080082401..ce8599e68b1bf5 100644 --- a/test/es-module/test-esm-named-exports.mjs +++ b/test/es-module/test-esm-named-exports.mjs @@ -1,8 +1,9 @@ // Flags: --experimental-loader ./test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs import '../common/index.mjs'; -import { readFile } from 'fs'; +import { readFile, __fromLoader } from 'fs'; import assert from 'assert'; import ok from '../fixtures/es-modules/test-esm-ok.mjs'; assert(ok); assert(readFile); +assert(__fromLoader); diff --git a/test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs b/test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs index 9f1bc24560b87a..f476c676cdea5b 100644 --- a/test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs +++ b/test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs @@ -1,23 +1,62 @@ import module from 'module'; -export function getFormat(url, context, defaultGetFormat) { - if (module.builtinModules.includes(url)) { +const GET_BUILTIN = `$__get_builtin_hole_${Date.now()}`; + +export function getGlobalPreloadCode() { + return `Object.defineProperty(globalThis, ${JSON.stringify(GET_BUILTIN)}, { + value: (builtinName) => { + return getBuiltin(builtinName); + }, + enumerable: false, + configurable: false, +}); +`; +} + +export function resolve(specifier, context, defaultResolve) { + const def = defaultResolve(specifier, context); + if (def.url.startsWith('nodejs:')) { + return { + url: `custom-${def.url}`, + }; + } + return def; +} + +export function getSource(url, context, defaultGetSource) { + if (url.startsWith('custom-nodejs:')) { + const urlObj = new URL(url); return { - format: 'dynamic' + source: generateBuiltinModule(urlObj.pathname), + format: 'module', }; } + return defaultGetSource(url, context); +} + +export function getFormat(url, context, defaultGetFormat) { + if (url.startsWith('custom-nodejs:')) { + return { format: 'module' }; + } return defaultGetFormat(url, context, defaultGetFormat); } -export function dynamicInstantiate(url) { - const builtinInstance = module._load(url); - const builtinExports = ['default', ...Object.keys(builtinInstance)]; - return { - exports: builtinExports, - execute: exports => { - for (let name of builtinExports) - exports[name].set(builtinInstance[name]); - exports.default.set(builtinInstance); - } - }; +function generateBuiltinModule(builtinName) { + const builtinInstance = module._load(builtinName); + const builtinExports = [ + ...Object.keys(builtinInstance), + ]; + return `\ +const $builtinInstance = ${GET_BUILTIN}(${JSON.stringify(builtinName)}); + +export const __fromLoader = true; + +export default $builtinInstance; + +${ + builtinExports + .map(name => `export const ${name} = $builtinInstance.${name};`) + .join('\n') +} +`; }