diff --git a/lib/internal/modules/esm/create_dynamic_module.js b/lib/internal/modules/esm/create_dynamic_module.js index f7c20083b6c918..1abb909f58526b 100644 --- a/lib/internal/modules/esm/create_dynamic_module.js +++ b/lib/internal/modules/esm/create_dynamic_module.js @@ -35,7 +35,7 @@ ${ArrayPrototypeJoin(ArrayPrototypeMap(imports, createImport), '\n')} ${ArrayPrototypeJoin(ArrayPrototypeMap(exports, createExport), '\n')} import.meta.done(); `; - const { ModuleWrap, callbackMap } = internalBinding('module_wrap'); + const { ModuleWrap } = internalBinding('module_wrap'); const m = new ModuleWrap(`${url}`, undefined, source, 0, 0); const readyfns = new SafeSet(); @@ -46,8 +46,8 @@ import.meta.done(); if (imports.length) reflect.imports = ObjectCreate(null); - - callbackMap.set(m, { + const { setCallbackForWrap } = require('internal/modules/esm/utils'); + setCallbackForWrap(m, { initializeImportMeta: (meta, wrap) => { meta.exports = reflect.exports; if (reflect.imports) diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index c30cc13756cdf4..9a422e8f70f1ce 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -47,9 +47,12 @@ function newModuleMap() { const { defaultResolve, - DEFAULT_CONDITIONS, } = require('internal/modules/esm/resolve'); +const { + getDefaultConditions, +} = require('internal/modules/esm/utils'); + function getTranslators() { const { translators } = require('internal/modules/esm/translators'); return translators; @@ -363,9 +366,10 @@ class ESMLoader { url = pathToFileURL(`${process.cwd()}/[eval${++this.evalIndex}]`).href ) { const evalInstance = (url) => { - const { ModuleWrap, callbackMap } = internalBinding('module_wrap'); + const { ModuleWrap } = internalBinding('module_wrap'); + const { setCallbackForWrap } = require('internal/modules/esm/utils'); const module = new ModuleWrap(url, undefined, source, 0, 0); - callbackMap.set(module, { + setCallbackForWrap(module, { importModuleDynamically: (specifier, { url }, importAssertions) => { return this.import(specifier, url, importAssertions); } @@ -798,7 +802,7 @@ class ESMLoader { } const chain = this.#hooks.resolve; const context = { - conditions: DEFAULT_CONDITIONS, + conditions: getDefaultConditions(), importAssertions, parentURL, }; diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index e4ad685a6938e7..aa979eec36fe8b 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -6,7 +6,6 @@ const { ArrayPrototypeJoin, ArrayPrototypeShift, JSONStringify, - ObjectFreeze, ObjectGetOwnPropertyNames, ObjectPrototypeHasOwnProperty, RegExp, @@ -45,7 +44,6 @@ const typeFlag = getOptionValue('--input-type'); const { URL, pathToFileURL, fileURLToPath } = require('internal/url'); const { ERR_INPUT_TYPE_NOT_ALLOWED, - ERR_INVALID_ARG_VALUE, ERR_INVALID_MODULE_SPECIFIER, ERR_INVALID_PACKAGE_CONFIG, ERR_INVALID_PACKAGE_TARGET, @@ -60,25 +58,13 @@ const { const { Module: CJSModule } = require('internal/modules/cjs/loader'); const { getPackageConfig, getPackageScopeConfig } = require('internal/modules/esm/package_config'); +const { getConditionsSet } = require('internal/modules/esm/utils'); /** * @typedef {import('internal/modules/esm/package_config.js').PackageConfig} PackageConfig */ -const userConditions = getOptionValue('--conditions'); -const noAddons = getOptionValue('--no-addons'); -const addonConditions = noAddons ? [] : ['node-addons']; - -const DEFAULT_CONDITIONS = ObjectFreeze([ - 'node', - 'import', - ...addonConditions, - ...userConditions, -]); - -const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS); - const emittedPackageWarnings = new SafeSet(); function emitTrailingSlashPatternDeprecation(match, pjsonUrl, base) { @@ -147,21 +133,6 @@ function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) { ); } -/** - * @param {string[]} [conditions] - * @returns {Set} - */ -function getConditionsSet(conditions) { - if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) { - if (!ArrayIsArray(conditions)) { - throw new ERR_INVALID_ARG_VALUE('conditions', conditions, - 'expected an array'); - } - return new SafeSet(conditions); - } - return DEFAULT_CONDITIONS_SET; -} - const realpathCache = new SafeMap(); /** @@ -1125,7 +1096,6 @@ async function defaultResolve(specifier, context = {}) { } module.exports = { - DEFAULT_CONDITIONS, defaultResolve, encodedSepRegEx, getPackageScopeConfig, diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 2007b8d5ab063d..b21c3c18cf7383 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -115,7 +115,8 @@ translators.set('module', async function moduleStrategy(url, source, isMain) { maybeCacheSourceMap(url, source); debug(`Translating StandardModule ${url}`); const module = new ModuleWrap(url, undefined, source, 0, 0); - moduleWrap.callbackMap.set(module, { + const { setCallbackForWrap } = require('internal/modules/esm/utils'); + setCallbackForWrap(module, { initializeImportMeta: (meta, wrap) => this.importMetaInitialize(meta, { url }), importModuleDynamically, }); diff --git a/lib/internal/modules/esm/utils.js b/lib/internal/modules/esm/utils.js new file mode 100644 index 00000000000000..2927326097a152 --- /dev/null +++ b/lib/internal/modules/esm/utils.js @@ -0,0 +1,105 @@ +'use strict'; +const { + ArrayIsArray, + SafeSet, + SafeWeakMap, + ObjectFreeze, +} = primordials; + +const { + ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING, + ERR_INVALID_ARG_VALUE, +} = require('internal/errors').codes; + +const { getOptionValue } = require('internal/options'); + +const { + setImportModuleDynamicallyCallback, + setInitializeImportMetaObjectCallback +} = internalBinding('module_wrap'); +const { + getModuleFromWrap, +} = require('internal/vm/module'); +const assert = require('internal/assert'); + +const callbackMap = new SafeWeakMap(); +function setCallbackForWrap(wrap, data) { + callbackMap.set(wrap, data); +} + +let defaultConditions; +function getDefaultConditions() { + assert(defaultConditions !== undefined); + return defaultConditions; +} + +let defaultConditionsSet; +function getDefaultConditionsSet() { + assert(defaultConditionsSet !== undefined); + return defaultConditionsSet; +} + +// This function is called during pre-execution, before any user code is run. +function initializeDefaultConditions() { + const userConditions = getOptionValue('--conditions'); + const noAddons = getOptionValue('--no-addons'); + const addonConditions = noAddons ? [] : ['node-addons']; + + defaultConditions = ObjectFreeze([ + 'node', + 'import', + ...addonConditions, + ...userConditions, + ]); + defaultConditionsSet = new SafeSet(defaultConditions); +} + +/** + * @param {string[]} [conditions] + * @returns {Set} + */ +function getConditionsSet(conditions) { + if (conditions !== undefined && conditions !== getDefaultConditions()) { + if (!ArrayIsArray(conditions)) { + throw new ERR_INVALID_ARG_VALUE('conditions', conditions, + 'expected an array'); + } + return new SafeSet(conditions); + } + return getDefaultConditionsSet(); +} + +function initializeImportMetaObject(wrap, meta) { + if (callbackMap.has(wrap)) { + const { initializeImportMeta } = callbackMap.get(wrap); + if (initializeImportMeta !== undefined) { + initializeImportMeta(meta, getModuleFromWrap(wrap) || wrap); + } + } +} + +async function importModuleDynamicallyCallback(wrap, specifier, assertions) { + if (callbackMap.has(wrap)) { + const { importModuleDynamically } = callbackMap.get(wrap); + if (importModuleDynamically !== undefined) { + return importModuleDynamically( + specifier, getModuleFromWrap(wrap) || wrap, assertions); + } + } + throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING(); +} + +function initializeESM() { + initializeDefaultConditions(); + // Setup per-isolate callbacks that locate data or callbacks that we keep + // track of for different ESM modules. + setInitializeImportMetaObjectCallback(initializeImportMetaObject); + setImportModuleDynamicallyCallback(importModuleDynamicallyCallback); +} + +module.exports = { + setCallbackForWrap, + initializeESM, + getDefaultConditions, + getConditionsSet, +}; diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js index 473cea48cafe64..25a2b7191cb5d4 100644 --- a/lib/internal/process/esm_loader.js +++ b/lib/internal/process/esm_loader.js @@ -5,40 +5,11 @@ const { ObjectCreate, } = primordials; -const { - ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING, -} = require('internal/errors').codes; const { ESMLoader } = require('internal/modules/esm/loader'); const { hasUncaughtExceptionCaptureCallback, } = require('internal/process/execution'); const { pathToFileURL } = require('internal/url'); -const { - getModuleFromWrap, -} = require('internal/vm/module'); - -exports.initializeImportMetaObject = function(wrap, meta) { - const { callbackMap } = internalBinding('module_wrap'); - if (callbackMap.has(wrap)) { - const { initializeImportMeta } = callbackMap.get(wrap); - if (initializeImportMeta !== undefined) { - initializeImportMeta(meta, getModuleFromWrap(wrap) || wrap); - } - } -}; - -exports.importModuleDynamicallyCallback = -async function importModuleDynamicallyCallback(wrap, specifier, assertions) { - const { callbackMap } = internalBinding('module_wrap'); - if (callbackMap.has(wrap)) { - const { importModuleDynamically } = callbackMap.get(wrap); - if (importModuleDynamically !== undefined) { - return importModuleDynamically( - specifier, getModuleFromWrap(wrap) || wrap, assertions); - } - } - throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING(); -}; const esmLoader = new ESMLoader(); exports.esmLoader = esmLoader; diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index d808f65424c2de..899a69a6f3bfff 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -6,7 +6,6 @@ const { ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, SafeMap, - SafeWeakMap, StringPrototypeStartsWith, globalThis, } = primordials; @@ -571,20 +570,10 @@ function initializeCJSLoader() { } function initializeESMLoader() { - // Create this WeakMap in js-land because V8 has no C++ API for WeakMap. - internalBinding('module_wrap').callbackMap = new SafeWeakMap(); - if (getEmbedderOptions().shouldNotRegisterESMLoader) return; - const { - setImportModuleDynamicallyCallback, - setInitializeImportMetaObjectCallback - } = internalBinding('module_wrap'); - const esm = require('internal/process/esm_loader'); - // Setup per-isolate callbacks that locate data or callbacks that we keep - // track of for different ESM modules. - setInitializeImportMetaObjectCallback(esm.initializeImportMetaObject); - setImportModuleDynamicallyCallback(esm.importModuleDynamicallyCallback); + const { initializeESM } = require('internal/modules/esm/utils'); + initializeESM(); // Patch the vm module when --experimental-vm-modules is on. // Please update the comments in vm.js when this block changes. diff --git a/lib/internal/vm.js b/lib/internal/vm.js index 32bcdf06234b67..a813c55d9e3aad 100644 --- a/lib/internal/vm.js +++ b/lib/internal/vm.js @@ -94,12 +94,11 @@ function internalCompileFunction(code, params, options) { if (importModuleDynamically !== undefined) { validateFunction(importModuleDynamically, 'options.importModuleDynamically'); - const { importModuleDynamicallyWrap } = - require('internal/vm/module'); - const { callbackMap } = internalBinding('module_wrap'); + const { importModuleDynamicallyWrap } = require('internal/vm/module'); const wrapped = importModuleDynamicallyWrap(importModuleDynamically); const func = result.function; - callbackMap.set(result.cacheKey, { + const { setCallbackForWrap } = require('internal/modules/esm/utils'); + setCallbackForWrap(result.cacheKey, { importModuleDynamically: (s, _k, i) => wrapped(s, func, i), }); } diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index 7e0131c7db2872..91d489fc4c7cdd 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -125,8 +125,8 @@ class Module { this[kWrap] = new ModuleWrap(identifier, context, sourceText, options.lineOffset, options.columnOffset, options.cachedData); - - binding.callbackMap.set(this[kWrap], { + const { setCallbackForWrap } = require('internal/modules/esm/utils'); + setCallbackForWrap(this[kWrap], { initializeImportMeta: options.initializeImportMeta, importModuleDynamically: options.importModuleDynamically ? importModuleDynamicallyWrap(options.importModuleDynamically) : diff --git a/lib/vm.js b/lib/vm.js index ec91c8a2651775..dea012156628b6 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -111,10 +111,9 @@ class Script extends ContextifyScript { if (importModuleDynamically !== undefined) { validateFunction(importModuleDynamically, 'options.importModuleDynamically'); - const { importModuleDynamicallyWrap } = - require('internal/vm/module'); - const { callbackMap } = internalBinding('module_wrap'); - callbackMap.set(this, { + const { importModuleDynamicallyWrap } = require('internal/vm/module'); + const { setCallbackForWrap } = require('internal/modules/esm/utils'); + setCallbackForWrap(this, { importModuleDynamically: importModuleDynamicallyWrap(importModuleDynamically), }); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 064a208eb7b56f..3ecc7f91a64813 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -65,6 +65,7 @@ const expectedModules = new Set([ 'NativeModule internal/modules/esm/package_config', 'NativeModule internal/modules/esm/resolve', 'NativeModule internal/modules/esm/translators', + 'NativeModule internal/modules/esm/utils', 'NativeModule internal/modules/package_json_reader', 'NativeModule internal/modules/run_main', 'NativeModule internal/net',