@@ -139,8 +139,9 @@ There are three types of specifiers:
139139* _ Absolute specifiers_ like ` 'file:///opt/nodejs/config.js' ` . They refer
140140 directly and explicitly to a full path.
141141
142- Bare specifier resolutions are handled by the [ Node.js module resolution
143- algorithm] [ ] . All other specifier resolutions are always only resolved with
142+ Bare specifier resolutions are handled by the [ Node.js module
143+ resolution and loading algorithm] [ ] .
144+ All other specifier resolutions are always only resolved with
144145the standard relative [ URL] [ ] resolution semantics.
145146
146147Like in CommonJS, module files within packages can be accessed by appending a
@@ -1007,28 +1008,6 @@ and there is no security.
10071008// https-loader.mjs
10081009import { get } from 'node:https';
10091010
1010- export function resolve(specifier, context, nextResolve) {
1011- const { parentURL = null } = context;
1012-
1013- // Normally Node.js would error on specifiers starting with 'https://', so
1014- // this hook intercepts them and converts them into absolute URLs to be
1015- // passed along to the later hooks below.
1016- if (specifier.startsWith('https://')) {
1017- return {
1018- shortCircuit: true,
1019- url: specifier,
1020- };
1021- } else if (parentURL && parentURL.startsWith('https://')) {
1022- return {
1023- shortCircuit: true,
1024- url: new URL(specifier, parentURL).href,
1025- };
1026- }
1027-
1028- // Let Node.js handle all other specifiers.
1029- return nextResolve(specifier);
1030- }
1031-
10321011export function load(url, context, nextLoad) {
10331012 // For JavaScript to be loaded over the network, we need to fetch and
10341013 // return it.
@@ -1069,9 +1048,7 @@ prints the current version of CoffeeScript per the module at the URL in
10691048#### Transpiler loader
10701049
10711050Sources that are in formats Node .js doesn' t understand can be converted into
1072- JavaScript using the [`load` hook][load hook]. Before that hook gets called,
1073- however, a [`resolve` hook][resolve hook] needs to tell Node.js not to
1074- throw an error on unknown file types.
1051+ JavaScript using the [`load` hook][load hook].
10751052
10761053This is less performant than transpiling source files before running
10771054Node.js; a transpiler loader should only be used for development and testing
@@ -1087,25 +1064,6 @@ import CoffeeScript from 'coffeescript';
10871064
10881065const baseURL = pathToFileURL(`${cwd()}/`).href;
10891066
1090- // CoffeeScript files end in .coffee, .litcoffee, or .coffee.md.
1091- const extensionsRegex = /\. coffee$|\. litcoffee$|\. coffee\. md$/;
1092-
1093- export async function resolve(specifier, context, nextResolve) {
1094- if (extensionsRegex.test(specifier)) {
1095- const { parentURL = baseURL } = context;
1096-
1097- // Node.js normally errors on unknown file extensions, so return a URL for
1098- // specifiers ending in the CoffeeScript file extensions.
1099- return {
1100- shortCircuit: true,
1101- url: new URL(specifier, parentURL).href,
1102- };
1103- }
1104-
1105- // Let Node.js handle all other specifiers.
1106- return nextResolve(specifier);
1107- }
1108-
11091067export async function load(url, context, nextLoad) {
11101068 if (extensionsRegex.test(url)) {
11111069 // Now that we patched resolve to let CoffeeScript URLs through, we need to
@@ -1198,27 +1156,99 @@ loaded from disk but before Node.js executes it; and so on for any `.coffee`,
11981156` .litcoffee ` or ` .coffee .md ` files referenced via ` import ` statements of any
11991157loaded file.
12001158
1201- ## Resolution algorithm
1159+ #### "import map" loader
1160+
1161+ The previous two loaders defined ` load` hooks. This is an example of a loader
1162+ that does its work via the ` resolve` hook. This loader reads an
1163+ ` import-map.json` file that specifies which specifiers to override to another
1164+ URL (this is a very simplistic implemenation of a small subset of the
1165+ "import maps" specification).
1166+
1167+ ` ` ` js
1168+ // import-map-loader.js
1169+ import fs from ' node:fs/promises' ;
1170+
1171+ const { imports } = JSON .parse (await fs .readFile (' import-map.json' ));
1172+
1173+ export async function resolve (specifier , context , nextResolve ) {
1174+ if (Object .hasOwn (imports, specifier)) {
1175+ return nextResolve (imports[specifier], context);
1176+ }
1177+
1178+ return nextResolve (specifier, context);
1179+ }
1180+ ` ` `
1181+
1182+ Let's assume we have these files:
1183+
1184+ ` ` ` js
1185+ // main.js
1186+ import ' a-module' ;
1187+ ` ` `
1188+
1189+ ` ` ` json
1190+ // import-map.json
1191+ {
1192+ " imports" : {
1193+ " a-module" : " ./some-module.js"
1194+ }
1195+ }
1196+ ` ` `
1197+
1198+ ` ` ` js
1199+ // some-module.js
1200+ console .log (' some module!' );
1201+ ` ` `
1202+
1203+ If you run ` node -- experimental- loader ./ import -map-loader.js main.js`
1204+ the output will be ` some module!` .
1205+
1206+ ## Resolution and loading algorithm
12021207
12031208### Features
12041209
1205- The resolver has the following properties:
1210+ The default resolver has the following properties:
12061211
12071212* FileURL-based resolution as is used by ES modules
1208- * Support for builtin module loading
12091213* Relative and absolute URL resolution
12101214* No default extensions
12111215* No folder mains
12121216* Bare specifier package resolution lookup through node\_ modules
1217+ * Does not fail on unknown extensions or protocols
1218+ * Can optionally provide a hint of the format to the loading phase
1219+
1220+ The default loader has the following properties
12131221
1214- ### Resolver algorithm
1222+ * Support for builtin module loading via ` node:` URLs
1223+ * Support for "inline" module loading via ` data:` URLs
1224+ * Support for ` file:` module loading
1225+ * Fails on any other URL protocol
1226+ * Fails on unknown extensions for ` file:` loading
1227+ (supports only ` .cjs` , ` .js` , and ` .mjs` )
1228+
1229+ ### Resolution algorithm
12151230
12161231The algorithm to load an ES module specifier is given through the
12171232**ESM\_ RESOLVE** method below. It returns the resolved URL for a
12181233module specifier relative to a parentURL.
12191234
1235+ The resolution algorithm determines the full resolved URL for a module
1236+ load, along with its suggested module format. The resolution algorithm
1237+ does not determine whether the resolved URL protocol can be loaded,
1238+ or whether the file extensions are permitted, instead these validations
1239+ are applied by Node.js during the load phase
1240+ (for example, if it was asked to load a URL that has a protocol that is
1241+ not ` file:` , ` data:` , ` node:` , or if ` --experimental-network-imports`
1242+ is enabled, ` https:` ).
1243+
1244+ The algorithm also tries to determine the format of the file based
1245+ on the extension (see ` ESM_FILE_FORMAT` algorithm below). If it does
1246+ not recognize the file extension (eg if it is not ` .mjs` , ` .cjs` , or
1247+ ` .json` ), then a format of ` undefined` is returned,
1248+ which will throw during the load phase.
1249+
12201250The algorithm to determine the module format of a resolved URL is
1221- provided by **ESM\_ FORMAT**, which returns the unique module
1251+ provided by **ESM\_ FILE \ _ FORMAT**, which returns the unique module
12221252format for any file. The _"module"_ format is returned for an ECMAScript
12231253Module, while the _"commonjs"_ format is used to indicate loading through the
12241254legacy CommonJS loader. Additional formats such as _"addon"_ can be extended in
@@ -1245,7 +1275,7 @@ The resolver can throw the following errors:
12451275* _Unsupported Directory Import_: The resolved path corresponds to a directory,
12461276 which is not a supported target for module imports.
12471277
1248- ### Resolver Algorithm Specification
1278+ ### Resolution Algorithm Specification
12491279
12501280**ESM\_ RESOLVE**(_specifier_, _parentURL_)
12511281
@@ -1279,7 +1309,7 @@ The resolver can throw the following errors:
12791309> 8. Otherwise,
12801310> 1. Set _format_ the module format of the content type associated with the
12811311> URL _resolved_.
1282- > 9. Load _resolved_ as module format, _format_.
1312+ > 9. Return _format_ and _resolved_ to the loading phase
12831313
12841314**PACKAGE\_ RESOLVE**(_packageSpecifier_, _parentURL_)
12851315
@@ -1484,9 +1514,9 @@ _isImports_, _conditions_)
14841514> 7. If _pjson?.type_ exists and is _"module"_, then
14851515> 1. If _url_ ends in _".js"_, then
14861516> 1. Return _"module"_.
1487- > 2. Throw an _Unsupported File Extension_ error .
1517+ > 2. Return **undefined** .
14881518> 8. Otherwise,
1489- > 1. Throw an _Unsupported File Extension_ error .
1519+ > 1. Return **undefined** .
14901520
14911521**LOOKUP\_ PACKAGE\_ SCOPE**(_url_)
14921522
@@ -1552,7 +1582,7 @@ success!
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
@@ -1580,7 +1610,6 @@ success!
15801610[custom https loader]: #https-loader
15811611[load hook]: #loadurl-context-nextload
15821612[percent-encoded]: url.md#percent-encoding-in-urls
1583- [resolve hook]: #resolvespecifier-context-nextresolve
15841613[special scheme]: https://url.spec.whatwg.org/#special-scheme
15851614[status code]: process.md#exit-codes
15861615[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
0 commit comments