diff --git a/lib/internal/modules/esm/get_source.js b/lib/internal/modules/esm/get_source.js index 54dbd029fcfc22..4c559b6126fe74 100644 --- a/lib/internal/modules/esm/get_source.js +++ b/lib/internal/modules/esm/get_source.js @@ -11,6 +11,7 @@ const { Buffer } = require('buffer'); const fs = require('fs'); const { URL } = require('url'); const { promisify } = require('internal/util'); +const { defaultGetFormat } = require('internal/modules/esm/get_format'); const { ERR_INVALID_URL, ERR_INVALID_URL_SCHEME, @@ -19,7 +20,13 @@ const readFileAsync = promisify(fs.readFile); const DATA_URL_PATTERN = /^[^/]+\/[^,;]+(?:[^,]*?)(;base64)?,([\s\S]*)$/; -async function defaultGetSource(url, { format } = {}, defaultGetSource) { +async function defaultGetSource(url, context, defaultGetSource) { + const { format } = defaultGetFormat(url); + + if (format === 'builtin' || format === 'commonjs') { + return { format, source: null }; + } + const parsed = new URL(url); let source; if (parsed.protocol === 'file:') { @@ -37,6 +44,6 @@ async function defaultGetSource(url, { format } = {}, defaultGetSource) { if (policy?.manifest) { policy.manifest.assertIntegrity(parsed, source); } - return { source }; + return { format, source }; } exports.defaultGetSource = defaultGetSource; diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 110464cbdb1da3..2308db9fe1e80f 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -25,13 +25,8 @@ const { defaultResolve, DEFAULT_CONDITIONS, } = require('internal/modules/esm/resolve'); -const { defaultGetFormat } = require('internal/modules/esm/get_format'); -const { defaultGetSource } = require( - 'internal/modules/esm/get_source'); -const { defaultTransformSource } = require( - 'internal/modules/esm/transform_source'); -const { translators } = require( - 'internal/modules/esm/translators'); +const { defaultGetSource } = require('internal/modules/esm/get_source'); +const { translators } = require('internal/modules/esm/translators'); const { getOptionValue } = require('internal/options'); /* A Loader instance is used as the main entry point for loading ES modules. @@ -57,22 +52,10 @@ class Loader { // running any preload code. // The preload code runs as soon as the hook module has finished evaluating. this._getGlobalPreloadCode = null; - // The resolver has the signature - // (specifier : string, parentURL : string, defaultResolve) - // -> Promise<{ url : string }> - // where defaultResolve is ModuleRequest.resolve (having the same - // signature itself). - this._resolve = defaultResolve; - // 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. - this._getFormat = defaultGetFormat; - // This hook is called just before the source code of an ES module file - // is loaded. - this._getSource = defaultGetSource; - // 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._resolveToURL = defaultResolve; + this._loadFromURL = defaultGetSource; + // The index for assigning unique URLs to anonymous module evaluation this.evalIndex = 0; } @@ -82,7 +65,7 @@ class Loader { if (!isMain) validateString(parentURL, 'parentURL'); - const resolveResponse = await this._resolve( + const resolveResponse = await this._resolveToURL( specifier, { parentURL, conditions: DEFAULT_CONDITIONS }, defaultResolve); if (typeof resolveResponse !== 'object') { throw new ERR_INVALID_RETURN_VALUE( @@ -97,46 +80,6 @@ class Loader { return url; } - async getFormat(url) { - const getFormatResponse = await this._getFormat( - url, {}, defaultGetFormat); - if (typeof getFormatResponse !== 'object') { - throw new ERR_INVALID_RETURN_VALUE( - 'object', 'loader getFormat', getFormatResponse); - } - - const { format } = getFormatResponse; - if (typeof format !== 'string') { - throw new ERR_INVALID_RETURN_PROPERTY_VALUE( - 'string', 'loader getFormat', 'format', format); - } - - if (format === 'builtin') { - return format; - } - - if (this._resolve !== defaultResolve) { - try { - new URL(url); - } catch { - throw new ERR_INVALID_RETURN_PROPERTY( - 'url', 'loader resolve', 'url', url - ); - } - } - - if (this._resolve === defaultResolve && - !url.startsWith('file:') && - !url.startsWith('data:') - ) { - throw new ERR_INVALID_RETURN_PROPERTY( - 'file: or data: url', 'loader resolve', 'url', url - ); - } - - return format; - } - async eval( source, url = pathToFileURL(`${process.cwd()}/[eval${++this.evalIndex}]`).href @@ -168,29 +111,24 @@ class Loader { hook(hooks) { const { - resolve, - dynamicInstantiate, - getFormat, - getSource, - transformSource, + resolveToURL, + loadFromURL, getGlobalPreloadCode, + + // previous hooks: + dynamicInstantiate, } = hooks; - // Use .bind() to avoid giving access to the Loader instance when called. - if (resolve !== undefined) - this._resolve = FunctionPrototypeBind(resolve, null); if (dynamicInstantiate !== undefined) { process.emitWarning( 'The dynamicInstantiate loader hook has been removed.'); } - if (getFormat !== undefined) { - this._getFormat = FunctionPrototypeBind(getFormat, null); - } - if (getSource !== undefined) { - this._getSource = FunctionPrototypeBind(getSource, null); - } - if (transformSource !== undefined) { - this._transformSource = FunctionPrototypeBind(transformSource, null); + + // Use .bind() to avoid giving access to the Loader instance when called. + if (resolveToURL !== undefined) + this._resolveToURL = FunctionPrototypeBind(resolveToURL, null); + if (loadFromURL !== undefined) { + this._loadFromURL = FunctionPrototypeBind(loadFromURL, null); } if (getGlobalPreloadCode !== undefined) { this._getGlobalPreloadCode = @@ -227,7 +165,6 @@ class Loader { async getModuleJob(specifier, parentURL) { const url = await this.resolve(specifier, parentURL); - const format = await this.getFormat(url); let job = this.moduleMap.get(url); // CommonJS will set functions for lazy job evaluation. if (typeof job === 'function') @@ -235,14 +172,16 @@ class Loader { if (job !== undefined) return job; - if (!translators.has(format)) - throw new ERR_UNKNOWN_MODULE_FORMAT(format); - - const loaderInstance = translators.get(format); + const moduleProvider = async (url, isMain) => { + const { source, format } = await this._loadFromURL(url, {}, defaultGetSource); + if (!translators.has(format)) + throw new ERR_UNKNOWN_MODULE_FORMAT(format); + const translator = translators.get(format); + return translator.call(this, url, source, isMain); + } - const inspectBrk = parentURL === undefined && - format === 'module' && getOptionValue('--inspect-brk'); - job = new ModuleJob(this, url, loaderInstance, parentURL === undefined, + const inspectBrk = parentURL === undefined && getOptionValue('--inspect-brk'); + job = new ModuleJob(this, url, moduleProvider, parentURL === undefined, inspectBrk); this.moduleMap.set(url, job); return job; diff --git a/lib/internal/modules/esm/transform_source.js b/lib/internal/modules/esm/transform_source.js index 2d07dd3607fb66..82b794c95bf6d1 100644 --- a/lib/internal/modules/esm/transform_source.js +++ b/lib/internal/modules/esm/transform_source.js @@ -1,7 +1 @@ -'use strict'; - -function defaultTransformSource(source, { url, format } = {}, - defaultTransformSource) { - return { source }; -} -exports.defaultTransformSource = defaultTransformSource; +// no longer needed. diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index f9a76d599e880a..5474bc8788b248 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -118,12 +118,8 @@ function initializeImportMeta(meta, { url }) { } // Strategy for loading a standard JavaScript module. -translators.set('module', async function moduleStrategy(url) { - let { source } = await this._getSource( - url, { format: 'module' }, defaultGetSource); +translators.set('module', async function moduleStrategy(url, source) { assertBufferSource(source, true, 'getSource'); - ({ source } = await this._transformSource( - source, { url, format: 'module' }, defaultTransformSource)); source = stringify(source); maybeCacheSourceMap(url, source); debug(`Translating StandardModule ${url}`); @@ -157,7 +153,7 @@ function enrichCJSError(err) { // Strategy for loading a node-style CommonJS module const isWindows = process.platform === 'win32'; const winSepRegEx = /\//g; -translators.set('commonjs', async function commonjsStrategy(url, isMain) { +translators.set('commonjs', async function commonjsStrategy(url, source, isMain) { debug(`Translating CJSModule ${url}`); let filename = internalURLModule.fileURLToPath(new URL(url)); @@ -270,7 +266,7 @@ translators.set('builtin', async function builtinStrategy(url) { }); // Strategy for loading a JSON file -translators.set('json', async function jsonStrategy(url) { +translators.set('json', async function jsonStrategy(url, source) { emitExperimentalWarning('Importing JSON modules'); debug(`Translating JSONModule ${url}`); debug(`Loading JSONModule ${url}`); @@ -288,11 +284,7 @@ translators.set('json', async function jsonStrategy(url) { }); } } - let { source } = await this._getSource( - url, { format: 'json' }, defaultGetSource); assertBufferSource(source, true, 'getSource'); - ({ source } = await this._transformSource( - source, { url, format: 'json' }, defaultTransformSource)); source = stringify(source); if (pathname) { // A require call could have been called on the same file during loading and @@ -330,13 +322,8 @@ translators.set('json', async function jsonStrategy(url) { }); // Strategy for loading a wasm module -translators.set('wasm', async function(url) { +translators.set('wasm', async function(url, source) { emitExperimentalWarning('Importing Web Assembly modules'); - let { source } = await this._getSource( - url, { format: 'wasm' }, defaultGetSource); - assertBufferSource(source, false, 'getSource'); - ({ source } = await this._transformSource( - source, { url, format: 'wasm' }, defaultTransformSource)); assertBufferSource(source, false, 'transformSource'); debug(`Translating WASMModule ${url}`); let compiled; diff --git a/test/fixtures/es-module-loaders/get-source.mjs b/test/fixtures/es-module-loaders/get-source.mjs index e5a9c65201aa28..9702c092c86f47 100644 --- a/test/fixtures/es-module-loaders/get-source.mjs +++ b/test/fixtures/es-module-loaders/get-source.mjs @@ -1,10 +1,11 @@ -export async function getSource(url, { format }, defaultGetSource) { +export async function loadFromURL(url, context, defaultLoadFromURL) { if (url.endsWith('fixtures/es-modules/message.mjs')) { // Oh, I’ve got that one in my cache! return { + format: 'module', source: `export const message = 'Woohoo!'.toUpperCase();` } } else { - return defaultGetSource(url, {format}, defaultGetSource); + return defaultLoadFromURL(url, context, defaultLoadFromURL); } } diff --git a/test/fixtures/es-module-loaders/transform-source.mjs b/test/fixtures/es-module-loaders/transform-source.mjs index 25d983b64e62ca..73e130a83eed80 100644 --- a/test/fixtures/es-module-loaders/transform-source.mjs +++ b/test/fixtures/es-module-loaders/transform-source.mjs @@ -1,14 +1,14 @@ -export async function transformSource( - source, { url, format }, defaultTransformSource) { +export async function loadFromURL(url, context, defaultLoadFromURL) { + let {format, source} = await defaultLoadFromURL(url, context); if (format === 'module') { if (typeof source !== 'string') { source = new TextDecoder().decode(source); } return { + format, source: source.replace(`'A message';`, `'A message'.toUpperCase();`) }; } else { // source could be a buffer, e.g. for WASM - return defaultTransformSource( - source, {url, format}, defaultTransformSource); + return {format, source}; } }