@@ -139,8 +139,9 @@ There are three types of specifiers:
139
139
* _ Absolute specifiers_ like ` 'file:///opt/nodejs/config.js' ` . They refer
140
140
directly and explicitly to a full path.
141
141
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
144
145
the standard relative [ URL] [ ] resolution semantics.
145
146
146
147
Like in CommonJS, module files within packages can be accessed by appending a
@@ -1007,28 +1008,6 @@ and there is no security.
1007
1008
// https-loader.mjs
1008
1009
import { get } from 'node:https';
1009
1010
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
-
1032
1011
export function load(url, context, nextLoad) {
1033
1012
// For JavaScript to be loaded over the network, we need to fetch and
1034
1013
// return it.
@@ -1069,9 +1048,7 @@ prints the current version of CoffeeScript per the module at the URL in
1069
1048
#### Transpiler loader
1070
1049
1071
1050
Sources 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].
1075
1052
1076
1053
This is less performant than transpiling source files before running
1077
1054
Node.js; a transpiler loader should only be used for development and testing
@@ -1087,25 +1064,6 @@ import CoffeeScript from 'coffeescript';
1087
1064
1088
1065
const baseURL = pathToFileURL(`${cwd()}/`).href;
1089
1066
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
-
1109
1067
export async function load(url, context, nextLoad) {
1110
1068
if (extensionsRegex.test(url)) {
1111
1069
// 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`,
1198
1156
` .litcoffee ` or ` .coffee .md ` files referenced via ` import ` statements of any
1199
1157
loaded file.
1200
1158
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
1202
1207
1203
1208
### Features
1204
1209
1205
- The resolver has the following properties:
1210
+ The default resolver has the following properties:
1206
1211
1207
1212
* FileURL-based resolution as is used by ES modules
1208
- * Support for builtin module loading
1209
1213
* Relative and absolute URL resolution
1210
1214
* No default extensions
1211
1215
* No folder mains
1212
1216
* 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
1213
1221
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
1215
1230
1216
1231
The algorithm to load an ES module specifier is given through the
1217
1232
**ESM\_ RESOLVE** method below. It returns the resolved URL for a
1218
1233
module specifier relative to a parentURL.
1219
1234
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
+
1220
1250
The 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
1222
1252
format for any file. The _"module"_ format is returned for an ECMAScript
1223
1253
Module, while the _"commonjs"_ format is used to indicate loading through the
1224
1254
legacy CommonJS loader. Additional formats such as _"addon"_ can be extended in
@@ -1245,7 +1275,7 @@ The resolver can throw the following errors:
1245
1275
* _Unsupported Directory Import_: The resolved path corresponds to a directory,
1246
1276
which is not a supported target for module imports.
1247
1277
1248
- ### Resolver Algorithm Specification
1278
+ ### Resolution Algorithm Specification
1249
1279
1250
1280
**ESM\_ RESOLVE**(_specifier_, _parentURL_)
1251
1281
@@ -1279,7 +1309,7 @@ The resolver can throw the following errors:
1279
1309
> 8. Otherwise,
1280
1310
> 1. Set _format_ the module format of the content type associated with the
1281
1311
> URL _resolved_.
1282
- > 9. Load _resolved_ as module format, _format_.
1312
+ > 9. Return _format_ and _resolved_ to the loading phase
1283
1313
1284
1314
**PACKAGE\_ RESOLVE**(_packageSpecifier_, _parentURL_)
1285
1315
@@ -1484,9 +1514,9 @@ _isImports_, _conditions_)
1484
1514
> 7. If _pjson?.type_ exists and is _"module"_, then
1485
1515
> 1. If _url_ ends in _".js"_, then
1486
1516
> 1. Return _"module"_.
1487
- > 2. Throw an _Unsupported File Extension_ error .
1517
+ > 2. Return **undefined** .
1488
1518
> 8. Otherwise,
1489
- > 1. Throw an _Unsupported File Extension_ error .
1519
+ > 1. Return **undefined** .
1490
1520
1491
1521
**LOOKUP\_ PACKAGE\_ SCOPE**(_url_)
1492
1522
@@ -1552,7 +1582,7 @@ success!
1552
1582
[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
1553
1583
[JSON modules]: #json-modules
1554
1584
[Loaders API]: #loaders
1555
- [Node.js Module Resolution Algorithm]: #resolver -algorithm-specification
1585
+ [Node.js Module Resolution And Loading Algorithm]: #resolution -algorithm-specification
1556
1586
[Terminology]: #terminology
1557
1587
[URL]: https://url.spec.whatwg.org/
1558
1588
[` " exports" ` ]: packages.md#exports
@@ -1580,7 +1610,6 @@ success!
1580
1610
[custom https loader]: #https-loader
1581
1611
[load hook]: #loadurl-context-nextload
1582
1612
[percent-encoded]: url.md#percent-encoding-in-urls
1583
- [resolve hook]: #resolvespecifier-context-nextresolve
1584
1613
[special scheme]: https://url.spec.whatwg.org/#special-scheme
1585
1614
[status code]: process.md#exit-codes
1586
1615
[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
0 commit comments