77<!-- YAML
88added: v8.5.0
99changes:
10+ - version: REPLACEME
11+ pr-url: https://github.com/nodejs/node/pull/44710
12+ description: Loader hooks are executed off the main thread.
1013 - version:
1114 - v18.6.0
1215 pr-url: https://github.com/nodejs/node/pull/42623
@@ -325,6 +328,9 @@ added:
325328 - v13.9.0
326329 - v12.16.2
327330changes:
331+ - version: REPLACEME
332+ pr-url: https://github.com/nodejs/node/pull/44710
333+ description: This API now returns a string synchronously instead of a Promise.
328334 - version:
329335 - v16.2.0
330336 - v14.18.0
@@ -340,29 +346,26 @@ command flag enabled.
340346* ` specifier` {string} The module specifier to resolve relative to ` parent` .
341347* ` parent ` {string|URL} The absolute parent module URL to resolve from. If none
342348 is specified, the value of ` import.meta.url` is used as the default.
343- * Returns: {Promise }
349+ * Returns: {string }
344350
345351Provides a module-relative resolution function scoped to each module, returning
346- the URL string.
352+ the URL string. In alignment with browser behavior, this now returns
353+ synchronously.
347354
348- <!-- eslint-skip -->
355+ > **Caveat** This can result in synchronous file-system operations, which
356+ > can impact performance similarly to ` require.resolve` .
349357
350358` ` ` js
351- const dependencyAsset = await import .meta.resolve(' component-lib/asset.css' );
359+ const dependencyAsset = import .meta.resolve(' component-lib/asset.css' );
352360` ` `
353361
354362` import .meta.resolve` also accepts a second argument which is the parent module
355- from which to resolve from:
356-
357- <!-- eslint-skip -->
363+ from which to resolve:
358364
359365` ` ` js
360- await import .meta.resolve(' ./dep' , import.meta.url);
366+ import .meta.resolve(' ./dep' , import.meta.url);
361367` ` `
362368
363- This function is asynchronous because the ES module resolver in Node.js is
364- allowed to be asynchronous.
365-
366369## Interoperability with CommonJS
367370
368371### ` import ` statements
@@ -729,6 +732,11 @@ A hook that returns without calling `next<hookName>()` _and_ without returning
729732` shortCircuit: true ` also triggers an exception. These errors are to help
730733prevent unintentional breaks in the chain.
731734
735+ Hooks are run in a separate thread, isolated from the main. That means it is a
736+ different [realm](https://tc39.es/ecma262/#realm). The hooks thread may be
737+ terminated by the main thread at any time, so do not depend on asynchronous
738+ operations to (like ` console .log ` ) complete.
739+
732740#### ` resolve (specifier, context, nextResolve)`
733741
734742<!-- YAML
@@ -759,7 +767,7 @@ changes:
759767 Node.js default ` resolve` hook after the last user-supplied ` resolve` hook
760768 * ` specifier` {string}
761769 * ` context` {Object}
762- * Returns: {Object}
770+ * Returns: {Object|Promise }
763771 * ` format` {string|null|undefined} A hint to the load hook (it might be
764772 ignored)
765773 ` ' builtin' | ' commonjs' | ' json' | ' module' | ' wasm' `
@@ -769,6 +777,9 @@ changes:
769777 terminate the chain of ` resolve` hooks. **Default:** ` false `
770778 * ` url` {string} The absolute URL to which this input resolves
771779
780+ > **Caveat** Despite support for returning promises and async functions, calls
781+ > to ` resolve` may block the main thread which can impact performance.
782+
772783The ` resolve` hook chain is responsible for telling Node.js where to find and
773784how to cache a given ` import ` statement or expression. It can optionally return
774785its format (such as ` ' module' ` ) as a hint to the ` load` hook. If a format is
@@ -794,7 +805,7 @@ Node.js module specifier resolution behavior_ when calling `defaultResolve`, the
794805` context.conditions` array originally passed into the ` resolve` hook.
795806
796807` ` ` js
797- export async function resolve (specifier , context , nextResolve ) {
808+ export function resolve (specifier , context , nextResolve ) {
798809 const { parentURL = null } = context;
799810
800811 if (Math .random () > 0.5 ) { // Some condition.
@@ -1067,6 +1078,25 @@ import CoffeeScript from 'coffeescript';
10671078
10681079const baseURL = pathToFileURL(`${cwd()}/`).href;
10691080
1081+ // CoffeeScript files end in .coffee, .litcoffee, or .coffee.md.
1082+ const extensionsRegex = /\. coffee$|\. litcoffee$|\. coffee\. md$/;
1083+
1084+ export function resolve(specifier, context, nextResolve) {
1085+ if (extensionsRegex.test(specifier)) {
1086+ const { parentURL = baseURL } = context;
1087+
1088+ // Node.js normally errors on unknown file extensions, so return a URL for
1089+ // specifiers ending in the CoffeeScript file extensions.
1090+ return {
1091+ shortCircuit: true,
1092+ url: new URL(specifier, parentURL).href,
1093+ };
1094+ }
1095+
1096+ // Let Node.js handle all other specifiers.
1097+ return nextResolve(specifier);
1098+ }
1099+
10701100export async function load(url, context, nextLoad) {
10711101 if (extensionsRegex.test(url)) {
10721102 // Now that we patched resolve to let CoffeeScript URLs through, we need to
0 commit comments