@@ -143,8 +143,9 @@ There are three types of specifiers:
143
143
* _ Absolute specifiers_ like ` 'file:///opt/nodejs/config.js' ` . They refer
144
144
directly and explicitly to a full path.
145
145
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
148
149
the standard relative [ URL] [ ] resolution semantics.
149
150
150
151
Like in CommonJS, module files within packages can be accessed by appending a
@@ -1029,28 +1030,6 @@ and there is no security.
1029
1030
// https-loader.mjs
1030
1031
import { get } from 'node:https';
1031
1032
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
-
1054
1033
export function load(url, context, nextLoad) {
1055
1034
// For JavaScript to be loaded over the network, we need to fetch and
1056
1035
// return it.
@@ -1091,9 +1070,7 @@ prints the current version of CoffeeScript per the module at the URL in
1091
1070
#### Transpiler loader
1092
1071
1093
1072
Sources 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].
1097
1074
1098
1075
This is less performant than transpiling source files before running
1099
1076
Node.js; a transpiler loader should only be used for development and testing
@@ -1109,25 +1086,6 @@ import CoffeeScript from 'coffeescript';
1109
1086
1110
1087
const baseURL = pathToFileURL(`${cwd()}/`).href;
1111
1088
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
-
1131
1089
export async function load(url, context, nextLoad) {
1132
1090
if (extensionsRegex.test(url)) {
1133
1091
// 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`,
1220
1178
` .litcoffee ` or ` .coffee .md ` files referenced via ` import ` statements of any
1221
1179
loaded file.
1222
1180
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
1224
1229
1225
1230
### Features
1226
1231
1227
- The resolver has the following properties:
1232
+ The default resolver has the following properties:
1228
1233
1229
1234
* FileURL-based resolution as is used by ES modules
1230
- * Support for builtin module loading
1231
1235
* Relative and absolute URL resolution
1232
1236
* No default extensions
1233
1237
* No folder mains
1234
1238
* 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
1235
1243
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
1237
1252
1238
1253
The algorithm to load an ES module specifier is given through the
1239
1254
**ESM\_ RESOLVE** method below. It returns the resolved URL for a
1240
1255
module specifier relative to a parentURL.
1241
1256
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
+
1242
1272
The 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
1244
1274
format for any file. The _"module"_ format is returned for an ECMAScript
1245
1275
Module, while the _"commonjs"_ format is used to indicate loading through the
1246
1276
legacy CommonJS loader. Additional formats such as _"addon"_ can be extended in
@@ -1267,7 +1297,7 @@ The resolver can throw the following errors:
1267
1297
* _Unsupported Directory Import_: The resolved path corresponds to a directory,
1268
1298
which is not a supported target for module imports.
1269
1299
1270
- ### Resolver Algorithm Specification
1300
+ ### Resolution Algorithm Specification
1271
1301
1272
1302
**ESM\_ RESOLVE**(_specifier_, _parentURL_)
1273
1303
@@ -1301,7 +1331,7 @@ The resolver can throw the following errors:
1301
1331
> 8. Otherwise,
1302
1332
> 1. Set _format_ the module format of the content type associated with the
1303
1333
> URL _resolved_.
1304
- > 9. Load _resolved_ as module format, _format_.
1334
+ > 9. Return _format_ and _resolved_ to the loading phase
1305
1335
1306
1336
**PACKAGE\_ RESOLVE**(_packageSpecifier_, _parentURL_)
1307
1337
@@ -1506,9 +1536,9 @@ _isImports_, _conditions_)
1506
1536
> 7. If _pjson?.type_ exists and is _"module"_, then
1507
1537
> 1. If _url_ ends in _".js"_, then
1508
1538
> 1. Return _"module"_.
1509
- > 2. Throw an _Unsupported File Extension_ error .
1539
+ > 2. Return **undefined** .
1510
1540
> 8. Otherwise,
1511
- > 1. Throw an _Unsupported File Extension_ error .
1541
+ > 1. Return **undefined** .
1512
1542
1513
1543
**LOOKUP\_ PACKAGE\_ SCOPE**(_url_)
1514
1544
@@ -1552,7 +1582,7 @@ for ESM specifiers is [commonjs-extension-resolution-loader][].
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
@@ -1581,7 +1611,6 @@ for ESM specifiers is [commonjs-extension-resolution-loader][].
1581
1611
[custom https loader]: #https-loader
1582
1612
[load hook]: #loadurl-context-nextload
1583
1613
[percent-encoded]: url.md#percent-encoding-in-urls
1584
- [resolve hook]: #resolvespecifier-context-nextresolve
1585
1614
[special scheme]: https://url.spec.whatwg.org/#special-scheme
1586
1615
[status code]: process.md#exit-codes
1587
1616
[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
0 commit comments