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');
5556const {
5657 urlToFilename,
@@ -62,7 +63,8 @@ const {
6263loadWithHooks: loadWithSyncHooks,
6364 validateLoadSloppy,
6465} = require('internal/modules/customization_hooks');
65-let defaultResolve, defaultLoadSync, importMetaInitializer;
66+67+let defaultResolve, defaultLoadSync;
66686769const { tracingChannel } = require('diagnostics_channel');
6870const onImport = tracingChannel('module.import');
@@ -183,13 +185,6 @@ class ModuleLoader {
183185 */
184186translators = 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) {
235231this.#asyncLoaderHooks = asyncLoaderHooks;
236232if (asyncLoaderHooks) {
237-this.allowImportMetaResolve = asyncLoaderHooks.allowImportMetaResolve;
238233this.isForAsyncLoaderHookWorker = asyncLoaderHooks.isForAsyncLoaderHookWorker;
239234} else {
240-this.allowImportMetaResolve = true;
241235this.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) {
883868return 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 */
900924function 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+}
902935return cascadedLoader;
903936}
904937