src: move import.meta initializer to native land · nodejs/node@e9b68e6

@@ -51,6 +51,7 @@ const {

5151

kErrored,

5252

kSourcePhase,

5353

throwIfPromiseRejected,

54+

setImportMetaResolveInitializer,

5455

} = internalBinding('module_wrap');

5556

const {

5657

urlToFilename,

@@ -62,7 +63,8 @@ const {

6263

loadWithHooks: loadWithSyncHooks,

6364

validateLoadSloppy,

6465

} = require('internal/modules/customization_hooks');

65-

let defaultResolve, defaultLoadSync, importMetaInitializer;

66+67+

let defaultResolve, defaultLoadSync;

66686769

const { tracingChannel } = require('diagnostics_channel');

6870

const onImport = tracingChannel('module.import');

@@ -183,13 +185,6 @@ class ModuleLoader {

183185

*/

184186

translators = getTranslators();

185187186-

/**

187-

* Truthy to allow the use of `import.meta.resolve`. This is needed

188-

* currently because the `Hooks` class does not have `resolveSync`

189-

* implemented and `import.meta.resolve` requires it.

190-

*/

191-

allowImportMetaResolve;

192-193188

/**

194189

* @see {AsyncLoaderHooks.isForAsyncLoaderHookWorker}

195190

* Shortcut to this.#asyncLoaderHooks.isForAsyncLoaderHookWorker.

@@ -200,9 +195,10 @@ class ModuleLoader {

200195

* Asynchronous loader hooks to pass requests to.

201196

*

202197

* Note that this value _MUST_ be set with `#setAsyncLoaderHooks`

203-

* because it needs to copy `#asyncLoaderHooks.allowImportMetaResolve`

204-

* to this property and failure to do so will cause undefined

205-

* behavior when invoking `import.meta.resolve`.

198+

* because it needs to copy `#asyncLoaderHooks.isForAsyncLoaderHookWorker`

199+

* to this property.

200+

* TODO(joyeecheung): this was a legacy of the previous setup of import.meta.resolve

201+

* configuration; put this information in the environment directly instead.

206202

*

207203

* When the ModuleLoader is created on a loader hook thread, this is

208204

* {@link AsyncLoaderHooksOnLoaderHookWorker}, and its methods directly call out

@@ -234,10 +230,8 @@ class ModuleLoader {

234230

#setAsyncLoaderHooks(asyncLoaderHooks) {

235231

this.#asyncLoaderHooks = asyncLoaderHooks;

236232

if (asyncLoaderHooks) {

237-

this.allowImportMetaResolve = asyncLoaderHooks.allowImportMetaResolve;

238233

this.isForAsyncLoaderHookWorker = asyncLoaderHooks.isForAsyncLoaderHookWorker;

239234

} else {

240-

this.allowImportMetaResolve = true;

241235

this.isForAsyncLoaderHookWorker = false;

242236

}

243237

}

@@ -821,15 +815,6 @@ class ModuleLoader {

821815

}

822816

}

823817824-

importMetaInitialize(meta, context) {

825-

if (this.#asyncLoaderHooks) {

826-

return this.#asyncLoaderHooks.importMetaInitialize(meta, context, this);

827-

}

828-

importMetaInitializer ??= require('internal/modules/esm/initialize_import_meta').initializeImportMeta;

829-

meta = importMetaInitializer(meta, context, this);

830-

return meta;

831-

}

832-833818

/**

834819

* Block until the async loader hooks have been initialized.

835820

*

@@ -883,8 +868,47 @@ function createModuleLoader(asyncLoaderHooks) {

883868

return new ModuleLoader(asyncLoaderHooks);

884869

}

885870886-

let cascadedLoader;

871+

let allowImportMetaResolveParentURL;

872+

/**

873+

* This is only called from the native ImportMetaObjectInitialize function to set up import.meta.resolve

874+

* when import.meta.resolve is accessed for the first time in a module.

875+

* @param {ModuleLoader} loader The cascaded loader to use. Bound when this function gets passed to native land.

876+

* @param {string} moduleURL URL of the module accessing import.meta

877+

* @returns {function(string, URL['href']=): string} The import.meta.resolve function

878+

*/

879+

function createImportMetaResolve(loader, moduleURL) {

880+

/**

881+

* @param {string} specifier The module specifier to resolve.

882+

* @param {URL['href']} [parentURL] Optional parent URL to resolve against. Ignored unless

883+

* `--experimental-import-meta-resolve` is enabled.

884+

* @returns {string}

885+

*/

886+

return function resolve(specifier, parentURL) {

887+

// The second argument is ignored unless --experimental-import-meta-resolve is enabled.

888+

// Even then, if it's not provided, parentURL defaults to the url of the module accessing

889+

// import.meta.resolve.

890+

allowImportMetaResolveParentURL ??= getOptionValue('--experimental-import-meta-resolve');

891+

parentURL = allowImportMetaResolveParentURL ? (parentURL ?? moduleURL) : moduleURL;

892+893+

let url;

894+

try {

895+

({ url } = loader.resolveSync(parentURL, { specifier, __proto__: null }));

896+

return url;

897+

} catch (error) {

898+

switch (error?.code) {

899+

case 'ERR_UNSUPPORTED_DIR_IMPORT':

900+

case 'ERR_MODULE_NOT_FOUND':

901+

({ url } = error);

902+

if (url) {

903+

return url;

904+

}

905+

}

906+

throw error;

907+

}

908+

};

909+

}

887910911+

let cascadedLoader;

888912

/**

889913

* This is a singleton ESM loader that integrates the loader hooks, if any.

890914

* It it used by other internal built-ins when they need to load user-land ESM code

@@ -898,7 +922,16 @@ let cascadedLoader;

898922

* @returns {ModuleLoader}

899923

*/

900924

function getOrInitializeCascadedLoader(asyncLoaderHooks) {

901-

cascadedLoader ??= createModuleLoader(asyncLoaderHooks);

925+

if (!cascadedLoader) {

926+

cascadedLoader = createModuleLoader(asyncLoaderHooks);

927+

// import.meta.resolve is not allowed in the async loader hook worker thread.

928+

// So only set up the import.meta.resolve initializer when we are initializing

929+

// the non-loader-hook-thread cascaded loader. When the native land doesn't see it,

930+

// it knows the loader is running on the loader hook thread.

931+

if (!(asyncLoaderHooks?.isForAsyncLoaderHookWorker)) {

932+

setImportMetaResolveInitializer(createImportMetaResolve.bind(null, cascadedLoader));

933+

}

934+

}

902935

return cascadedLoader;

903936

}

904937