@@ -143,8 +143,9 @@ There are three types of specifiers:
143143* _ Absolute specifiers_ like ` 'file:///opt/nodejs/config.js' ` . They refer
144144 directly and explicitly to a full path.
145145
146- Bare specifier resolutions are handled by the [ Node.js module resolution
147- algorithm] [ ] . All other specifier resolutions are always only resolved with
146+ Bare specifier resolutions are handled by the [ Node.js module
147+ resolution and loading algorithm] [ ] .
148+ All other specifier resolutions are always only resolved with
148149the standard relative [ URL] [ ] resolution semantics.
149150
150151Like in CommonJS, module files within packages can be accessed by appending a
@@ -1029,28 +1030,6 @@ and there is no security.
10291030// https-loader.mjs
10301031import { get } from 'node:https';
10311032
1032- export function resolve(specifier, context, nextResolve) {
1033- const { parentURL = null } = context;
1034-
1035- // Normally Node.js would error on specifiers starting with 'https://', so
1036- // this hook intercepts them and converts them into absolute URLs to be
1037- // passed along to the later hooks below.
1038- if (specifier.startsWith('https://')) {
1039- return {
1040- shortCircuit: true,
1041- url: specifier,
1042- };
1043- } else if (parentURL && parentURL.startsWith('https://')) {
1044- return {
1045- shortCircuit: true,
1046- url: new URL(specifier, parentURL).href,
1047- };
1048- }
1049-
1050- // Let Node.js handle all other specifiers.
1051- return nextResolve(specifier);
1052- }
1053-
10541033export function load(url, context, nextLoad) {
10551034 // For JavaScript to be loaded over the network, we need to fetch and
10561035 // return it.
@@ -1091,9 +1070,7 @@ prints the current version of CoffeeScript per the module at the URL in
10911070#### Transpiler loader
10921071
10931072Sources that are in formats Node .js doesn' t understand can be converted into
1094- JavaScript using the [`load` hook][load hook]. Before that hook gets called,
1095- however, a [`resolve` hook][resolve hook] needs to tell Node.js not to
1096- throw an error on unknown file types.
1073+ JavaScript using the [`load` hook][load hook].
10971074
10981075This is less performant than transpiling source files before running
10991076Node.js; a transpiler loader should only be used for development and testing
@@ -1109,25 +1086,6 @@ import CoffeeScript from 'coffeescript';
11091086
11101087const baseURL = pathToFileURL(`${cwd()}/`).href;
11111088
1112- // CoffeeScript files end in .coffee, .litcoffee, or .coffee.md.
1113- const extensionsRegex = /\. coffee$|\. litcoffee$|\. coffee\. md$/;
1114-
1115- export function resolve(specifier, context, nextResolve) {
1116- if (extensionsRegex.test(specifier)) {
1117- const { parentURL = baseURL } = context;
1118-
1119- // Node.js normally errors on unknown file extensions, so return a URL for
1120- // specifiers ending in the CoffeeScript file extensions.
1121- return {
1122- shortCircuit: true,
1123- url: new URL(specifier, parentURL).href,
1124- };
1125- }
1126-
1127- // Let Node.js handle all other specifiers.
1128- return nextResolve(specifier);
1129- }
1130-
11311089export async function load(url, context, nextLoad) {
11321090 if (extensionsRegex.test(url)) {
11331091 // Now that we patched resolve to let CoffeeScript URLs through, we need to
@@ -1220,27 +1178,99 @@ loaded from disk but before Node.js executes it; and so on for any `.coffee`,
12201178` .litcoffee ` or ` .coffee .md ` files referenced via ` import ` statements of any
12211179loaded file.
12221180
1223- ## Resolution algorithm
1181+ #### "import map" loader
1182+
1183+ The previous two loaders defined ` load` hooks. This is an example of a loader
1184+ that does its work via the ` resolve` hook. This loader reads an
1185+ ` import-map.json` file that specifies which specifiers to override to another
1186+ URL (this is a very simplistic implemenation of a small subset of the
1187+ "import maps" specification).
1188+
1189+ ` ` ` js
1190+ // import-map-loader.js
1191+ import fs from ' node:fs/promises' ;
1192+
1193+ const { imports } = JSON .parse (await fs .readFile (' import-map.json' ));
1194+
1195+ export async function resolve (specifier , context , nextResolve ) {
1196+ if (Object .hasOwn (imports, specifier)) {
1197+ return nextResolve (imports[specifier], context);
1198+ }
1199+
1200+ return nextResolve (specifier, context);
1201+ }
1202+ ` ` `
1203+
1204+ Let's assume we have these files:
1205+
1206+ ` ` ` js
1207+ // main.js
1208+ import ' a-module' ;
1209+ ` ` `
1210+
1211+ ` ` ` json
1212+ // import-map.json
1213+ {
1214+ " imports" : {
1215+ " a-module" : " ./some-module.js"
1216+ }
1217+ }
1218+ ` ` `
1219+
1220+ ` ` ` js
1221+ // some-module.js
1222+ console .log (' some module!' );
1223+ ` ` `
1224+
1225+ If you run ` node -- experimental- loader ./ import -map-loader.js main.js`
1226+ the output will be ` some module!` .
1227+
1228+ ## Resolution and loading algorithm
12241229
12251230### Features
12261231
1227- The resolver has the following properties:
1232+ The default resolver has the following properties:
12281233
12291234* FileURL-based resolution as is used by ES modules
1230- * Support for builtin module loading
12311235* Relative and absolute URL resolution
12321236* No default extensions
12331237* No folder mains
12341238* Bare specifier package resolution lookup through node\_ modules
1239+ * Does not fail on unknown extensions or protocols
1240+ * Can optionally provide a hint of the format to the loading phase
1241+
1242+ The default loader has the following properties
12351243
1236- ### Resolver algorithm
1244+ * Support for builtin module loading via ` node:` URLs
1245+ * Support for "inline" module loading via ` data:` URLs
1246+ * Support for ` file:` module loading
1247+ * Fails on any other URL protocol
1248+ * Fails on unknown extensions for ` file:` loading
1249+ (supports only ` .cjs` , ` .js` , and ` .mjs` )
1250+
1251+ ### Resolution algorithm
12371252
12381253The algorithm to load an ES module specifier is given through the
12391254**ESM\_ RESOLVE** method below. It returns the resolved URL for a
12401255module specifier relative to a parentURL.
12411256
1257+ The resolution algorithm determines the full resolved URL for a module
1258+ load, along with its suggested module format. The resolution algorithm
1259+ does not determine whether the resolved URL protocol can be loaded,
1260+ or whether the file extensions are permitted, instead these validations
1261+ are applied by Node.js during the load phase
1262+ (for example, if it was asked to load a URL that has a protocol that is
1263+ not ` file:` , ` data:` , ` node:` , or if ` --experimental-network-imports`
1264+ is enabled, ` https:` ).
1265+
1266+ The algorithm also tries to determine the format of the file based
1267+ on the extension (see ` ESM_FILE_FORMAT` algorithm below). If it does
1268+ not recognize the file extension (eg if it is not ` .mjs` , ` .cjs` , or
1269+ ` .json` ), then a format of ` undefined` is returned,
1270+ which will throw during the load phase.
1271+
12421272The algorithm to determine the module format of a resolved URL is
1243- provided by **ESM\_ FORMAT**, which returns the unique module
1273+ provided by **ESM\_ FILE \ _ FORMAT**, which returns the unique module
12441274format for any file. The _"module"_ format is returned for an ECMAScript
12451275Module, while the _"commonjs"_ format is used to indicate loading through the
12461276legacy CommonJS loader. Additional formats such as _"addon"_ can be extended in
@@ -1267,7 +1297,7 @@ The resolver can throw the following errors:
12671297* _Unsupported Directory Import_: The resolved path corresponds to a directory,
12681298 which is not a supported target for module imports.
12691299
1270- ### Resolver Algorithm Specification
1300+ ### Resolution Algorithm Specification
12711301
12721302**ESM\_ RESOLVE**(_specifier_, _parentURL_)
12731303
@@ -1301,7 +1331,7 @@ The resolver can throw the following errors:
13011331> 8. Otherwise,
13021332> 1. Set _format_ the module format of the content type associated with the
13031333> URL _resolved_.
1304- > 9. Load _resolved_ as module format, _format_.
1334+ > 9. Return _format_ and _resolved_ to the loading phase
13051335
13061336**PACKAGE\_ RESOLVE**(_packageSpecifier_, _parentURL_)
13071337
@@ -1506,9 +1536,9 @@ _isImports_, _conditions_)
15061536> 7. If _pjson?.type_ exists and is _"module"_, then
15071537> 1. If _url_ ends in _".js"_, then
15081538> 1. Return _"module"_.
1509- > 2. Throw an _Unsupported File Extension_ error .
1539+ > 2. Return **undefined** .
15101540> 8. Otherwise,
1511- > 1. Throw an _Unsupported File Extension_ error .
1541+ > 1. Return **undefined** .
15121542
15131543**LOOKUP\_ PACKAGE\_ SCOPE**(_url_)
15141544
@@ -1552,7 +1582,7 @@ for ESM specifiers is [commonjs-extension-resolution-loader][].
15521582[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
15531583[JSON modules]: #json-modules
15541584[Loaders API]: #loaders
1555- [Node.js Module Resolution Algorithm]: #resolver -algorithm-specification
1585+ [Node.js Module Resolution And Loading Algorithm]: #resolution -algorithm-specification
15561586[Terminology]: #terminology
15571587[URL]: https://url.spec.whatwg.org/
15581588[` " exports" ` ]: packages.md#exports
@@ -1581,7 +1611,6 @@ for ESM specifiers is [commonjs-extension-resolution-loader][].
15811611[custom https loader]: #https-loader
15821612[load hook]: #loadurl-context-nextload
15831613[percent-encoded]: url.md#percent-encoding-in-urls
1584- [resolve hook]: #resolvespecifier-context-nextresolve
15851614[special scheme]: https://url.spec.whatwg.org/#special-scheme
15861615[status code]: process.md#exit-codes
15871616[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
0 commit comments