diff --git a/lib/internal/main/check_syntax.js b/lib/internal/main/check_syntax.js index 1b32a4d569f494..467a740363f772 100644 --- a/lib/internal/main/check_syntax.js +++ b/lib/internal/main/check_syntax.js @@ -52,7 +52,8 @@ function loadESMIfNeeded(cb) { const hasModulePreImport = getOptionValue('--import').length > 0; if (hasModulePreImport) { - const { loadESM } = require('internal/process/esm_loader'); + const { loadESM, init } = require('internal/process/esm_loader'); + init(); loadESM(cb); return; } diff --git a/lib/internal/main/eval_stdin.js b/lib/internal/main/eval_stdin.js index d947af49a6a942..435cb029cb0e95 100644 --- a/lib/internal/main/eval_stdin.js +++ b/lib/internal/main/eval_stdin.js @@ -18,6 +18,7 @@ const { prepareMainThreadExecution(); markBootstrapComplete(); + readStdin((code) => { // This is necessary for fork() and CJS module compilation. // TODO(joyeecheung): pass this with something really internal. @@ -25,12 +26,15 @@ readStdin((code) => { const print = getOptionValue('--print'); const loadESM = getOptionValue('--import').length > 0; - if (getOptionValue('--input-type') === 'module') + if (getOptionValue('--input-type') === 'module') { + require('internal/process/esm_loader').init(); evalModule(code, print); - else + } else { evalScript('[stdin]', code, getOptionValue('--inspect-brk'), print, loadESM); + require('internal/process/esm_loader').initIfNeeded(); + } }); diff --git a/lib/internal/main/eval_string.js b/lib/internal/main/eval_string.js index ec6a2d51af5450..4495f18d0aa75f 100644 --- a/lib/internal/main/eval_string.js +++ b/lib/internal/main/eval_string.js @@ -25,9 +25,11 @@ markBootstrapComplete(); const source = getOptionValue('--eval'); const print = getOptionValue('--print'); const loadESM = getOptionValue('--import').length > 0; -if (getOptionValue('--input-type') === 'module') +const esmLoader = require('internal/process/esm_loader'); +if (getOptionValue('--input-type') === 'module') { + esmLoader.init(); evalModule(source, print); -else { +} else { // For backward compatibility, we want the identifier crypto to be the // `node:crypto` module rather than WebCrypto. const isUsingCryptoIdentifier = @@ -54,4 +56,5 @@ else { getOptionValue('--inspect-brk'), print, loadESM); + esmLoader.initIfNeeded(); } diff --git a/lib/internal/main/inspect.js b/lib/internal/main/inspect.js index a60e4aa40b9605..df621e7505704a 100644 --- a/lib/internal/main/inspect.js +++ b/lib/internal/main/inspect.js @@ -11,6 +11,7 @@ prepareMainThreadExecution(); markBootstrapComplete(); +require('internal/process/esm_loader').init(); // Start the debugger agent. process.nextTick(() => { diff --git a/lib/internal/main/repl.js b/lib/internal/main/repl.js index da1764a9c80d95..97cb1b8ac61c9e 100644 --- a/lib/internal/main/repl.js +++ b/lib/internal/main/repl.js @@ -36,6 +36,7 @@ if (process.env.NODE_REPL_EXTERNAL_MODULE) { } const esmLoader = require('internal/process/esm_loader'); + esmLoader.init(); esmLoader.loadESM(() => { console.log(`Welcome to Node.js ${process.version}.\n` + 'Type ".help" for more information.'); diff --git a/lib/internal/main/run_main_module.js b/lib/internal/main/run_main_module.js index 51331270a2161f..b754e0784bfbc8 100644 --- a/lib/internal/main/run_main_module.js +++ b/lib/internal/main/run_main_module.js @@ -11,6 +11,8 @@ prepareMainThreadExecution(true); markBootstrapComplete(); +require('internal/process/esm_loader').init(); + // Necessary to reset RegExp statics before user code runs. RegExpPrototypeExec(/^/, ''); diff --git a/lib/internal/main/test_runner.js b/lib/internal/main/test_runner.js index 5ce9e51e4b6af6..6f0a02353b3936 100644 --- a/lib/internal/main/test_runner.js +++ b/lib/internal/main/test_runner.js @@ -22,6 +22,8 @@ if (isUsingInspector()) { inspectPort = process.debugPort; } +require('internal/process/esm_loader').init(); + run({ concurrency, inspectPort, watch: getOptionValue('--watch'), setup: setupTestReporters }) .once('test:fail', () => { process.exitCode = kGenericUserError; diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js index 4d8938e58bdf33..84f82e8d9f8556 100644 --- a/lib/internal/main/worker_thread.js +++ b/lib/internal/main/worker_thread.js @@ -162,11 +162,13 @@ port.on('message', (message) => { }); ArrayPrototypeSplice(process.argv, 1, 0, name); evalScript(name, filename); + require('internal/process/esm_loader').initIfNeeded(); break; } case 'module': { const { evalModule } = require('internal/process/execution'); + require('internal/process/esm_loader').init(); PromisePrototypeThen(evalModule(filename), undefined, (e) => { workerOnGlobalUncaughtException(e, true); }); @@ -179,6 +181,7 @@ port.on('message', (message) => { // XXX: the monkey-patchability here should probably be deprecated. ArrayPrototypeSplice(process.argv, 1, 0, filename); const CJSLoader = require('internal/modules/cjs/loader'); + require('internal/process/esm_loader').initIfNeeded(); CJSLoader.Module.runMain(filename); break; } diff --git a/lib/internal/modules/esm/utils.js b/lib/internal/modules/esm/utils.js index f80f1fbdac0c2e..12d00970d480e2 100644 --- a/lib/internal/modules/esm/utils.js +++ b/lib/internal/modules/esm/utils.js @@ -2,10 +2,7 @@ const { ArrayIsArray, - ArrayPrototypePush, PromisePrototypeThen, - ReflectApply, - SafeMap, SafeSet, SafeWeakMap, ObjectFreeze, @@ -17,7 +14,7 @@ const { } = require('internal/errors').codes; const { getOptionValue } = require('internal/options'); const { pathToFileURL } = require('internal/url'); -const { kEmptyObject, createDeferredPromise } = require('internal/util'); +const { kEmptyObject } = require('internal/util'); const { setImportModuleDynamicallyCallback, setInitializeImportMetaObjectCallback, @@ -95,14 +92,6 @@ async function importModuleDynamicallyCallback(wrap, specifier, assertions) { } function initializeESM() { - const { setESMLoader } = require('internal/process/esm_loader'); - // Minimal mock for letting `--require` work before we have any actual ESM loader. - setESMLoader({ __proto__: null, cjsCache: new SafeMap(), import() { - const { promise, resolve, reject } = createDeferredPromise(); - ArrayPrototypePush(this.importRequests, { arguments: arguments, resolve, reject }); - return promise; - }, importRequests: [] }); - initializeDefaultConditions(); // Setup per-isolate callbacks that locate data or callbacks that we keep // track of for different ESM modules. @@ -126,22 +115,10 @@ async function initializeHooks() { const hooks = new Hooks(); const { DefaultModuleLoader } = require('internal/modules/esm/loader'); - const { esmLoader, setESMLoader } = require('internal/process/esm_loader'); + const { init } = require('internal/process/esm_loader'); class ModuleLoader extends DefaultModuleLoader { - constructor() { - super(); - for (const { 0: key, 1: value } of esmLoader.cjsCache) { - // Getting back the values from the mocked loader. - this.cjsCache.set(key, value); - } - for (let i = 0; i < esmLoader.importRequests.length; i++) { - PromisePrototypeThen( - ReflectApply(this.import, this, esmLoader.importRequests[i].arguments), - esmLoader.importRequests[i].resolve, - esmLoader.importRequests[i].reject, - ); - } - } + // eslint-disable-next-line no-useless-constructor + constructor() { super(); } loaderType = 'internal'; async #getModuleJob(specifier, parentURL, importAssertions) { @@ -166,8 +143,6 @@ async function initializeHooks() { } const privateModuleLoader = new ModuleLoader(); - setESMLoader(privateModuleLoader); - const parentURL = pathToFileURL(cwd).href; for (let i = 0; i < customLoaderPaths.length; i++) { @@ -185,6 +160,8 @@ async function initializeHooks() { const preloadScripts = hooks.initializeGlobalPreload(); + init(privateModuleLoader); + return { __proto__: null, hooks, preloadScripts }; } diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index 21a71b0d8a5a11..0bfe7b11241416 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -47,12 +47,9 @@ function shouldUseESMLoader(mainPath) { } function runMainESM(mainPath) { - const { loadESM, setESMLoader } = require('internal/process/esm_loader'); - const { createModuleLoader } = require('internal/modules/esm/loader'); + const { loadESM } = require('internal/process/esm_loader'); const { pathToFileURL } = require('internal/url'); - setESMLoader(createModuleLoader(true)); - handleMainPromise(loadESM((esmLoader) => { const main = path.isAbsolute(mainPath) ? pathToFileURL(mainPath).href : mainPath; diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js index 477cf7b7ae7f73..f34e39763482bc 100644 --- a/lib/internal/process/esm_loader.js +++ b/lib/internal/process/esm_loader.js @@ -1,16 +1,56 @@ 'use strict'; +const { + ArrayPrototypePush, + PromisePrototypeThen, + ReflectApply, + SafeMap, +} = primordials; + +const assert = require('internal/assert'); +const { createModuleLoader } = require('internal/modules/esm/loader'); const { getOptionValue } = require('internal/options'); const { hasUncaughtExceptionCaptureCallback, } = require('internal/process/execution'); const { pathToFileURL } = require('internal/url'); -const { kEmptyObject } = require('internal/util'); +const { kEmptyObject, createDeferredPromise } = require('internal/util'); + +let esmLoader; +let init = false; module.exports = { - esmLoader: undefined, - setESMLoader(loader) { - module.exports.esmLoader = loader; + get esmLoader() { + return esmLoader ??= { __proto__: null, cjsCache: new SafeMap(), import() { + const { promise, resolve, reject } = createDeferredPromise(); + ArrayPrototypePush(this.importRequests, { arguments: arguments, resolve, reject }); + return promise; + }, importRequests: [] }; + }, + initIfNeeded() { + // TODO: we could try to avoid loading ESM loader on CJS-only codebase + return module.exports.init(); + }, + init(loader = undefined) { + assert(!init); + init = true; + + loader ??= createModuleLoader(true); + + if (esmLoader != null) { + for (const { 0: key, 1: value } of esmLoader.cjsCache) { + // Getting back the values from the mocked loader. + loader.cjsCache.set(key, value); + } + for (let i = 0; i < esmLoader.importRequests.length; i++) { + PromisePrototypeThen( + ReflectApply(loader.import, loader, esmLoader.importRequests[i].arguments), + esmLoader.importRequests[i].resolve, + esmLoader.importRequests[i].reject, + ); + } + } + esmLoader = loader; }, async loadESM(callback) { const { esmLoader } = module.exports; diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js index 941a6b03fb2a87..afe2ba2c2c977b 100644 --- a/lib/internal/process/execution.js +++ b/lib/internal/process/execution.js @@ -45,12 +45,8 @@ function evalModule(source, print) { if (print) { throw new ERR_EVAL_ESM_CANNOT_PRINT(); } - const { loadESM, setESMLoader } = require('internal/process/esm_loader'); - const { createModuleLoader } = require('internal/modules/esm/loader'); + const { loadESM } = require('internal/process/esm_loader'); const { handleMainPromise } = require('internal/modules/run_main'); - - setESMLoader(createModuleLoader(true)); - RegExpPrototypeExec(/^/, ''); // Necessary to reset RegExp statics before user code runs. return handleMainPromise(loadESM((loader) => loader.eval(source))); } @@ -67,8 +63,10 @@ function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) { module.filename = path.join(cwd, name); module.paths = CJSModule._nodeModulePaths(cwd); + const { handleMainPromise } = require('internal/modules/run_main'); const asyncESM = require('internal/process/esm_loader'); const baseUrl = pathToFileURL(module.filename).href; + const { loadESM } = asyncESM; const runScript = () => { // Create wrapper for cache entry @@ -101,11 +99,6 @@ function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) { }; if (shouldLoadESM) { - const { loadESM, setESMLoader } = asyncESM; - const { createModuleLoader } = require('internal/modules/esm/loader'); - const { handleMainPromise } = require('internal/modules/run_main'); - - setESMLoader(createModuleLoader(true)); return handleMainPromise(loadESM(runScript)); } return runScript(); diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 53cda5737f07c0..1f360e536b9b4d 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -120,8 +120,8 @@ function prepareExecution(options) { } function setupUserModules() { - initializeCJSLoader(); initializeESMLoader(); + initializeCJSLoader(); const CJSLoader = require('internal/modules/cjs/loader'); assert(!CJSLoader.hasLoadedAnyUserCJSModule); loadPreloadModules();