mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
PR-URL: https://github.com/nodejs/node/pull/49158 Reviewed-By: Debadree Chatterjee <debadree333@gmail.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
1735 lines
63 KiB
Markdown
1735 lines
63 KiB
Markdown
# Modules: ECMAScript modules
|
|
|
|
<!--introduced_in=v8.5.0-->
|
|
|
|
<!-- type=misc -->
|
|
|
|
<!-- YAML
|
|
added: v8.5.0
|
|
changes:
|
|
- version: v20.0.0
|
|
pr-url: https://github.com/nodejs/node/pull/44710
|
|
description: Loader hooks are executed off the main thread.
|
|
- version:
|
|
- v18.6.0
|
|
- v16.17.0
|
|
pr-url: https://github.com/nodejs/node/pull/42623
|
|
description: Add support for chaining loaders.
|
|
- version:
|
|
- v17.1.0
|
|
- v16.14.0
|
|
pr-url: https://github.com/nodejs/node/pull/40250
|
|
description: Add support for import assertions.
|
|
- version:
|
|
- v17.0.0
|
|
- v16.12.0
|
|
pr-url: https://github.com/nodejs/node/pull/37468
|
|
description:
|
|
Consolidate loader hooks, removed `getFormat`, `getSource`,
|
|
`transformSource`, and `getGlobalPreloadCode` hooks
|
|
added `load` and `globalPreload` hooks
|
|
allowed returning `format` from either `resolve` or `load` hooks.
|
|
- version:
|
|
- v15.3.0
|
|
- v14.17.0
|
|
- v12.22.0
|
|
pr-url: https://github.com/nodejs/node/pull/35781
|
|
description: Stabilize modules implementation.
|
|
- version:
|
|
- v14.13.0
|
|
- v12.20.0
|
|
pr-url: https://github.com/nodejs/node/pull/35249
|
|
description: Support for detection of CommonJS named exports.
|
|
- version: v14.8.0
|
|
pr-url: https://github.com/nodejs/node/pull/34558
|
|
description: Unflag Top-Level Await.
|
|
- version:
|
|
- v14.0.0
|
|
- v13.14.0
|
|
- v12.20.0
|
|
pr-url: https://github.com/nodejs/node/pull/31974
|
|
description: Remove experimental modules warning.
|
|
- version:
|
|
- v13.2.0
|
|
- v12.17.0
|
|
pr-url: https://github.com/nodejs/node/pull/29866
|
|
description: Loading ECMAScript modules no longer requires a command-line flag.
|
|
- version: v12.0.0
|
|
pr-url: https://github.com/nodejs/node/pull/26745
|
|
description:
|
|
Add support for ES modules using `.js` file extension via `package.json`
|
|
`"type"` field.
|
|
-->
|
|
|
|
> Stability: 2 - Stable
|
|
|
|
## Introduction
|
|
|
|
<!--name=esm-->
|
|
|
|
ECMAScript modules are [the official standard format][] to package JavaScript
|
|
code for reuse. Modules are defined using a variety of [`import`][] and
|
|
[`export`][] statements.
|
|
|
|
The following example of an ES module exports a function:
|
|
|
|
```js
|
|
// addTwo.mjs
|
|
function addTwo(num) {
|
|
return num + 2;
|
|
}
|
|
|
|
export { addTwo };
|
|
```
|
|
|
|
The following example of an ES module imports the function from `addTwo.mjs`:
|
|
|
|
```js
|
|
// app.mjs
|
|
import { addTwo } from './addTwo.mjs';
|
|
|
|
// Prints: 6
|
|
console.log(addTwo(4));
|
|
```
|
|
|
|
Node.js fully supports ECMAScript modules as they are currently specified and
|
|
provides interoperability between them and its original module format,
|
|
[CommonJS][].
|
|
|
|
<!-- Anchors to make sure old links find a target -->
|
|
|
|
<i id="esm_package_json_type_field"></i><i id="esm_package_scope_and_file_extensions"></i><i id="esm_input_type_flag"></i>
|
|
|
|
## Enabling
|
|
|
|
<!-- type=misc -->
|
|
|
|
Node.js has two module systems: [CommonJS][] modules and ECMAScript modules.
|
|
|
|
Authors can tell Node.js to use the ECMAScript modules loader
|
|
via the `.mjs` file extension, the `package.json` [`"type"`][] field, or the
|
|
[`--input-type`][] flag. Outside of those cases, Node.js will use the CommonJS
|
|
module loader. See [Determining module system][] for more details.
|
|
|
|
<!-- Anchors to make sure old links find a target -->
|
|
|
|
<i id="esm_package_entry_points"></i><i id="esm_main_entry_point_export"></i><i id="esm_subpath_exports"></i><i id="esm_package_exports_fallbacks"></i><i id="esm_exports_sugar"></i><i id="esm_conditional_exports"></i><i id="esm_nested_conditions"></i><i id="esm_self_referencing_a_package_using_its_name"></i><i id="esm_internal_package_imports"></i><i id="esm_dual_commonjs_es_module_packages"></i><i id="esm_dual_package_hazard"></i><i id="esm_writing_dual_packages_while_avoiding_or_minimizing_hazards"></i><i id="esm_approach_1_use_an_es_module_wrapper"></i><i id="esm_approach_2_isolate_state"></i>
|
|
|
|
## Packages
|
|
|
|
This section was moved to [Modules: Packages](packages.md).
|
|
|
|
## `import` Specifiers
|
|
|
|
### Terminology
|
|
|
|
The _specifier_ of an `import` statement is the string after the `from` keyword,
|
|
e.g. `'node:path'` in `import { sep } from 'node:path'`. Specifiers are also
|
|
used in `export from` statements, and as the argument to an `import()`
|
|
expression.
|
|
|
|
There are three types of specifiers:
|
|
|
|
* _Relative specifiers_ like `'./startup.js'` or `'../config.mjs'`. They refer
|
|
to a path relative to the location of the importing file. _The file extension
|
|
is always necessary for these._
|
|
|
|
* _Bare specifiers_ like `'some-package'` or `'some-package/shuffle'`. They can
|
|
refer to the main entry point of a package by the package name, or a
|
|
specific feature module within a package prefixed by the package name as per
|
|
the examples respectively. _Including the file extension is only necessary
|
|
for packages without an [`"exports"`][] field._
|
|
|
|
* _Absolute specifiers_ like `'file:///opt/nodejs/config.js'`. They refer
|
|
directly and explicitly to a full path.
|
|
|
|
Bare specifier resolutions are handled by the [Node.js module
|
|
resolution and loading algorithm][].
|
|
All other specifier resolutions are always only resolved with
|
|
the standard relative [URL][] resolution semantics.
|
|
|
|
Like in CommonJS, module files within packages can be accessed by appending a
|
|
path to the package name unless the package's [`package.json`][] contains an
|
|
[`"exports"`][] field, in which case files within packages can only be accessed
|
|
via the paths defined in [`"exports"`][].
|
|
|
|
For details on these package resolution rules that apply to bare specifiers in
|
|
the Node.js module resolution, see the [packages documentation](packages.md).
|
|
|
|
### Mandatory file extensions
|
|
|
|
A file extension must be provided when using the `import` keyword to resolve
|
|
relative or absolute specifiers. Directory indexes (e.g. `'./startup/index.js'`)
|
|
must also be fully specified.
|
|
|
|
This behavior matches how `import` behaves in browser environments, assuming a
|
|
typically configured server.
|
|
|
|
### URLs
|
|
|
|
ES modules are resolved and cached as URLs. This means that special characters
|
|
must be [percent-encoded][], such as `#` with `%23` and `?` with `%3F`.
|
|
|
|
`file:`, `node:`, and `data:` URL schemes are supported. A specifier like
|
|
`'https://example.com/app.js'` is not supported natively in Node.js unless using
|
|
a [custom HTTPS loader][].
|
|
|
|
#### `file:` URLs
|
|
|
|
Modules are loaded multiple times if the `import` specifier used to resolve
|
|
them has a different query or fragment.
|
|
|
|
```js
|
|
import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1"
|
|
import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2"
|
|
```
|
|
|
|
The volume root may be referenced via `/`, `//`, or `file:///`. Given the
|
|
differences between [URL][] and path resolution (such as percent encoding
|
|
details), it is recommended to use [url.pathToFileURL][] when importing a path.
|
|
|
|
#### `data:` imports
|
|
|
|
<!-- YAML
|
|
added: v12.10.0
|
|
-->
|
|
|
|
[`data:` URLs][] are supported for importing with the following MIME types:
|
|
|
|
* `text/javascript` for ES modules
|
|
* `application/json` for JSON
|
|
* `application/wasm` for Wasm
|
|
|
|
```js
|
|
import 'data:text/javascript,console.log("hello!");';
|
|
import _ from 'data:application/json,"world!"' assert { type: 'json' };
|
|
```
|
|
|
|
`data:` URLs only resolve [bare specifiers][Terminology] for builtin modules
|
|
and [absolute specifiers][Terminology]. Resolving
|
|
[relative specifiers][Terminology] does not work because `data:` is not a
|
|
[special scheme][]. For example, attempting to load `./foo`
|
|
from `data:text/javascript,import "./foo";` fails to resolve because there
|
|
is no concept of relative resolution for `data:` URLs.
|
|
|
|
#### `node:` imports
|
|
|
|
<!-- YAML
|
|
added:
|
|
- v14.13.1
|
|
- v12.20.0
|
|
changes:
|
|
- version:
|
|
- v16.0.0
|
|
- v14.18.0
|
|
pr-url: https://github.com/nodejs/node/pull/37246
|
|
description: Added `node:` import support to `require(...)`.
|
|
-->
|
|
|
|
`node:` URLs are supported as an alternative means to load Node.js builtin
|
|
modules. This URL scheme allows for builtin modules to be referenced by valid
|
|
absolute URL strings.
|
|
|
|
```js
|
|
import fs from 'node:fs/promises';
|
|
```
|
|
|
|
## Import assertions
|
|
|
|
<!-- YAML
|
|
added:
|
|
- v17.1.0
|
|
- v16.14.0
|
|
-->
|
|
|
|
> Stability: 1 - Experimental
|
|
|
|
The [Import Assertions proposal][] adds an inline syntax for module import
|
|
statements to pass on more information alongside the module specifier.
|
|
|
|
```js
|
|
import fooData from './foo.json' assert { type: 'json' };
|
|
|
|
const { default: barData } =
|
|
await import('./bar.json', { assert: { type: 'json' } });
|
|
```
|
|
|
|
Node.js supports the following `type` values, for which the assertion is
|
|
mandatory:
|
|
|
|
| Assertion `type` | Needed for |
|
|
| ---------------- | ---------------- |
|
|
| `'json'` | [JSON modules][] |
|
|
|
|
## Builtin modules
|
|
|
|
[Core modules][] provide named exports of their public API. A
|
|
default export is also provided which is the value of the CommonJS exports.
|
|
The default export can be used for, among other things, modifying the named
|
|
exports. Named exports of builtin modules are updated only by calling
|
|
[`module.syncBuiltinESMExports()`][].
|
|
|
|
```js
|
|
import EventEmitter from 'node:events';
|
|
const e = new EventEmitter();
|
|
```
|
|
|
|
```js
|
|
import { readFile } from 'node:fs';
|
|
readFile('./foo.txt', (err, source) => {
|
|
if (err) {
|
|
console.error(err);
|
|
} else {
|
|
console.log(source);
|
|
}
|
|
});
|
|
```
|
|
|
|
```js
|
|
import fs, { readFileSync } from 'node:fs';
|
|
import { syncBuiltinESMExports } from 'node:module';
|
|
import { Buffer } from 'node:buffer';
|
|
|
|
fs.readFileSync = () => Buffer.from('Hello, ESM');
|
|
syncBuiltinESMExports();
|
|
|
|
fs.readFileSync === readFileSync;
|
|
```
|
|
|
|
## `import()` expressions
|
|
|
|
[Dynamic `import()`][] is supported in both CommonJS and ES modules. In CommonJS
|
|
modules it can be used to load ES modules.
|
|
|
|
## `import.meta`
|
|
|
|
* {Object}
|
|
|
|
The `import.meta` meta property is an `Object` that contains the following
|
|
properties.
|
|
|
|
### `import.meta.url`
|
|
|
|
* {string} The absolute `file:` URL of the module.
|
|
|
|
This is defined exactly the same as it is in browsers providing the URL of the
|
|
current module file.
|
|
|
|
This enables useful patterns such as relative file loading:
|
|
|
|
```js
|
|
import { readFileSync } from 'node:fs';
|
|
const buffer = readFileSync(new URL('./data.proto', import.meta.url));
|
|
```
|
|
|
|
### `import.meta.resolve(specifier)`
|
|
|
|
<!--
|
|
added:
|
|
- v13.9.0
|
|
- v12.16.2
|
|
changes:
|
|
- version: v20.0.0
|
|
pr-url: https://github.com/nodejs/node/pull/44710
|
|
description: This API now returns a string synchronously instead of a Promise.
|
|
- version:
|
|
- v16.2.0
|
|
- v14.18.0
|
|
pr-url: https://github.com/nodejs/node/pull/38587
|
|
description: Add support for WHATWG `URL` object to `parentURL` parameter.
|
|
- version:
|
|
- REPLACEME
|
|
pr-url: https://github.com/nodejs/node/pull/49028
|
|
description: Unflag import.meta.resolve, with `parentURL` parameter still
|
|
flagged.
|
|
-->
|
|
|
|
> Stability: 1.2 - Release candidate
|
|
|
|
* `specifier` {string} The module specifier to resolve relative to the
|
|
current module.
|
|
* Returns: {string} The absolute (`file:`) URL string for the resolved module.
|
|
|
|
[`import.meta.resolve`][] is a module-relative resolution function scoped to
|
|
each module, returning the URL string.
|
|
|
|
```js
|
|
const dependencyAsset = import.meta.resolve('component-lib/asset.css');
|
|
// file:///app/node_modules/component-lib/asset.css
|
|
```
|
|
|
|
All features of the Node.js module resolution are supported. Dependency
|
|
resolutions are subject to the permitted exports resolutions within the package.
|
|
|
|
```js
|
|
import.meta.resolve('./dep', import.meta.url);
|
|
// file:///app/dep
|
|
```
|
|
|
|
> **Caveat** This can result in synchronous file-system operations, which
|
|
> can impact performance similarly to `require.resolve`.
|
|
|
|
Previously, Node.js implemented an asynchronous resolver which also permitted
|
|
a second contextual argument. The implementation has since been updated to be
|
|
synchronous, with the second contextual `parent` argument still accessible
|
|
behind the `--experimental-import-meta-resolve` flag:
|
|
|
|
* `parent` {string|URL} An optional absolute parent module URL to resolve from.
|
|
|
|
## Interoperability with CommonJS
|
|
|
|
### `import` statements
|
|
|
|
An `import` statement can reference an ES module or a CommonJS module.
|
|
`import` statements are permitted only in ES modules, but dynamic [`import()`][]
|
|
expressions are supported in CommonJS for loading ES modules.
|
|
|
|
When importing [CommonJS modules](#commonjs-namespaces), the
|
|
`module.exports` object is provided as the default export. Named exports may be
|
|
available, provided by static analysis as a convenience for better ecosystem
|
|
compatibility.
|
|
|
|
### `require`
|
|
|
|
The CommonJS module `require` always treats the files it references as CommonJS.
|
|
|
|
Using `require` to load an ES module is not supported because ES modules have
|
|
asynchronous execution. Instead, use [`import()`][] to load an ES module
|
|
from a CommonJS module.
|
|
|
|
### CommonJS Namespaces
|
|
|
|
CommonJS modules consist of a `module.exports` object which can be of any type.
|
|
|
|
When importing a CommonJS module, it can be reliably imported using the ES
|
|
module default import or its corresponding sugar syntax:
|
|
|
|
<!-- eslint-disable no-duplicate-imports -->
|
|
|
|
```js
|
|
import { default as cjs } from 'cjs';
|
|
|
|
// The following import statement is "syntax sugar" (equivalent but sweeter)
|
|
// for `{ default as cjsSugar }` in the above import statement:
|
|
import cjsSugar from 'cjs';
|
|
|
|
console.log(cjs);
|
|
console.log(cjs === cjsSugar);
|
|
// Prints:
|
|
// <module.exports>
|
|
// true
|
|
```
|
|
|
|
The ECMAScript Module Namespace representation of a CommonJS module is always
|
|
a namespace with a `default` export key pointing to the CommonJS
|
|
`module.exports` value.
|
|
|
|
This Module Namespace Exotic Object can be directly observed either when using
|
|
`import * as m from 'cjs'` or a dynamic import:
|
|
|
|
<!-- eslint-skip -->
|
|
|
|
```js
|
|
import * as m from 'cjs';
|
|
console.log(m);
|
|
console.log(m === await import('cjs'));
|
|
// Prints:
|
|
// [Module] { default: <module.exports> }
|
|
// true
|
|
```
|
|
|
|
For better compatibility with existing usage in the JS ecosystem, Node.js
|
|
in addition attempts to determine the CommonJS named exports of every imported
|
|
CommonJS module to provide them as separate ES module exports using a static
|
|
analysis process.
|
|
|
|
For example, consider a CommonJS module written:
|
|
|
|
```cjs
|
|
// cjs.cjs
|
|
exports.name = 'exported';
|
|
```
|
|
|
|
The preceding module supports named imports in ES modules:
|
|
|
|
<!-- eslint-disable no-duplicate-imports -->
|
|
|
|
```js
|
|
import { name } from './cjs.cjs';
|
|
console.log(name);
|
|
// Prints: 'exported'
|
|
|
|
import cjs from './cjs.cjs';
|
|
console.log(cjs);
|
|
// Prints: { name: 'exported' }
|
|
|
|
import * as m from './cjs.cjs';
|
|
console.log(m);
|
|
// Prints: [Module] { default: { name: 'exported' }, name: 'exported' }
|
|
```
|
|
|
|
As can be seen from the last example of the Module Namespace Exotic Object being
|
|
logged, the `name` export is copied off of the `module.exports` object and set
|
|
directly on the ES module namespace when the module is imported.
|
|
|
|
Live binding updates or new exports added to `module.exports` are not detected
|
|
for these named exports.
|
|
|
|
The detection of named exports is based on common syntax patterns but does not
|
|
always correctly detect named exports. In these cases, using the default
|
|
import form described above can be a better option.
|
|
|
|
Named exports detection covers many common export patterns, reexport patterns
|
|
and build tool and transpiler outputs. See [cjs-module-lexer][] for the exact
|
|
semantics implemented.
|
|
|
|
### Differences between ES modules and CommonJS
|
|
|
|
#### No `require`, `exports`, or `module.exports`
|
|
|
|
In most cases, the ES module `import` can be used to load CommonJS modules.
|
|
|
|
If needed, a `require` function can be constructed within an ES module using
|
|
[`module.createRequire()`][].
|
|
|
|
#### No `__filename` or `__dirname`
|
|
|
|
These CommonJS variables are not available in ES modules.
|
|
|
|
`__filename` and `__dirname` use cases can be replicated via
|
|
[`import.meta.url`][].
|
|
|
|
#### No Addon Loading
|
|
|
|
[Addons][] are not currently supported with ES module imports.
|
|
|
|
They can instead be loaded with [`module.createRequire()`][] or
|
|
[`process.dlopen`][].
|
|
|
|
#### No `require.resolve`
|
|
|
|
Relative resolution can be handled via `new URL('./local', import.meta.url)`.
|
|
|
|
For a complete `require.resolve` replacement, there is the
|
|
[import.meta.resolve][] API.
|
|
|
|
Alternatively `module.createRequire()` can be used.
|
|
|
|
#### No `NODE_PATH`
|
|
|
|
`NODE_PATH` is not part of resolving `import` specifiers. Please use symlinks
|
|
if this behavior is desired.
|
|
|
|
#### No `require.extensions`
|
|
|
|
`require.extensions` is not used by `import`. The expectation is that loader
|
|
hooks can provide this workflow in the future.
|
|
|
|
#### No `require.cache`
|
|
|
|
`require.cache` is not used by `import` as the ES module loader has its own
|
|
separate cache.
|
|
|
|
<i id="esm_experimental_json_modules"></i>
|
|
|
|
## JSON modules
|
|
|
|
> Stability: 1 - Experimental
|
|
|
|
JSON files can be referenced by `import`:
|
|
|
|
```js
|
|
import packageConfig from './package.json' assert { type: 'json' };
|
|
```
|
|
|
|
The `assert { type: 'json' }` syntax is mandatory; see [Import Assertions][].
|
|
|
|
The imported JSON only exposes a `default` export. There is no support for named
|
|
exports. A cache entry is created in the CommonJS cache to avoid duplication.
|
|
The same object is returned in CommonJS if the JSON module has already been
|
|
imported from the same path.
|
|
|
|
<i id="esm_experimental_wasm_modules"></i>
|
|
|
|
## Wasm modules
|
|
|
|
> Stability: 1 - Experimental
|
|
|
|
Importing WebAssembly modules is supported under the
|
|
`--experimental-wasm-modules` flag, allowing any `.wasm` files to be
|
|
imported as normal modules while also supporting their module imports.
|
|
|
|
This integration is in line with the
|
|
[ES Module Integration Proposal for WebAssembly][].
|
|
|
|
For example, an `index.mjs` containing:
|
|
|
|
```js
|
|
import * as M from './module.wasm';
|
|
console.log(M);
|
|
```
|
|
|
|
executed under:
|
|
|
|
```bash
|
|
node --experimental-wasm-modules index.mjs
|
|
```
|
|
|
|
would provide the exports interface for the instantiation of `module.wasm`.
|
|
|
|
<i id="esm_experimental_top_level_await"></i>
|
|
|
|
## Top-level `await`
|
|
|
|
<!-- YAML
|
|
added: v14.8.0
|
|
-->
|
|
|
|
The `await` keyword may be used in the top level body of an ECMAScript module.
|
|
|
|
Assuming an `a.mjs` with
|
|
|
|
```js
|
|
export const five = await Promise.resolve(5);
|
|
```
|
|
|
|
And a `b.mjs` with
|
|
|
|
```js
|
|
import { five } from './a.mjs';
|
|
|
|
console.log(five); // Logs `5`
|
|
```
|
|
|
|
```bash
|
|
node b.mjs # works
|
|
```
|
|
|
|
If a top level `await` expression never resolves, the `node` process will exit
|
|
with a `13` [status code][].
|
|
|
|
```js
|
|
import { spawn } from 'node:child_process';
|
|
import { execPath } from 'node:process';
|
|
|
|
spawn(execPath, [
|
|
'--input-type=module',
|
|
'--eval',
|
|
// Never-resolving Promise:
|
|
'await new Promise(() => {})',
|
|
]).once('exit', (code) => {
|
|
console.log(code); // Logs `13`
|
|
});
|
|
```
|
|
|
|
## HTTPS and HTTP imports
|
|
|
|
> Stability: 1 - Experimental
|
|
|
|
Importing network based modules using `https:` and `http:` is supported under
|
|
the `--experimental-network-imports` flag. This allows web browser-like imports
|
|
to work in Node.js with a few differences due to application stability and
|
|
security concerns that are different when running in a privileged environment
|
|
instead of a browser sandbox.
|
|
|
|
### Imports are limited to HTTP/1
|
|
|
|
Automatic protocol negotiation for HTTP/2 and HTTP/3 is not yet supported.
|
|
|
|
### HTTP is limited to loopback addresses
|
|
|
|
`http:` is vulnerable to man-in-the-middle attacks and is not allowed to be
|
|
used for addresses outside of the IPv4 address `127.0.0.0/8` (`127.0.0.1` to
|
|
`127.255.255.255`) and the IPv6 address `::1`. Support for `http:` is intended
|
|
to be used for local development.
|
|
|
|
### Authentication is never sent to the destination server.
|
|
|
|
`Authorization`, `Cookie`, and `Proxy-Authorization` headers are not sent to the
|
|
server. Avoid including user info in parts of imported URLs. A security model
|
|
for safely using these on the server is being worked on.
|
|
|
|
### CORS is never checked on the destination server
|
|
|
|
CORS is designed to allow a server to limit the consumers of an API to a
|
|
specific set of hosts. This is not supported as it does not make sense for a
|
|
server-based implementation.
|
|
|
|
### Cannot load non-network dependencies
|
|
|
|
These modules cannot access other modules that are not over `http:` or `https:`.
|
|
To still access local modules while avoiding the security concern, pass in
|
|
references to the local dependencies:
|
|
|
|
```mjs
|
|
// file.mjs
|
|
import worker_threads from 'node:worker_threads';
|
|
import { configure, resize } from 'https://example.com/imagelib.mjs';
|
|
configure({ worker_threads });
|
|
```
|
|
|
|
```mjs
|
|
// https://example.com/imagelib.mjs
|
|
let worker_threads;
|
|
export function configure(opts) {
|
|
worker_threads = opts.worker_threads;
|
|
}
|
|
export function resize(img, size) {
|
|
// Perform resizing in worker_thread to avoid main thread blocking
|
|
}
|
|
```
|
|
|
|
### Network-based loading is not enabled by default
|
|
|
|
For now, the `--experimental-network-imports` flag is required to enable loading
|
|
resources over `http:` or `https:`. In the future, a different mechanism will be
|
|
used to enforce this. Opt-in is required to prevent transitive dependencies
|
|
inadvertently using potentially mutable state that could affect reliability
|
|
of Node.js applications.
|
|
|
|
<i id="esm_experimental_loaders"></i>
|
|
|
|
## Loaders
|
|
|
|
<!-- YAML
|
|
added: v8.8.0
|
|
changes:
|
|
- version: REPLACEME
|
|
pr-url: https://github.com/nodejs/node/pull/48842
|
|
description: Added `initialize` hook to replace `globalPreload`.
|
|
- version:
|
|
- v18.6.0
|
|
- v16.17.0
|
|
pr-url: https://github.com/nodejs/node/pull/42623
|
|
description: Add support for chaining loaders.
|
|
- version: v16.12.0
|
|
pr-url: https://github.com/nodejs/node/pull/37468
|
|
description: Removed `getFormat`, `getSource`, `transformSource`, and
|
|
`globalPreload`; added `load` hook and `getGlobalPreload` hook.
|
|
-->
|
|
|
|
> Stability: 1 - Experimental
|
|
|
|
> This API is currently being redesigned and will still change.
|
|
|
|
<!-- type=misc -->
|
|
|
|
To customize the default module resolution, loader hooks can optionally be
|
|
provided via a `--experimental-loader ./loader-name.mjs` argument to Node.js.
|
|
|
|
When hooks are used they apply to each subsequent loader, the entry point, and
|
|
all `import` calls. They won't apply to `require` calls; those still follow
|
|
[CommonJS][] rules.
|
|
|
|
Loaders follow the pattern of `--require`:
|
|
|
|
```bash
|
|
node \
|
|
--experimental-loader unpkg \
|
|
--experimental-loader http-to-https \
|
|
--experimental-loader cache-buster
|
|
```
|
|
|
|
These are called in the following sequence: `cache-buster` calls
|
|
`http-to-https` which calls `unpkg`.
|
|
|
|
### Hooks
|
|
|
|
Hooks are part of a chain, even if that chain consists of only one custom
|
|
(user-provided) hook and the default hook, which is always present. Hook
|
|
functions nest: each one must always return a plain object, and chaining happens
|
|
as a result of each function calling `next<hookName>()`, which is a reference
|
|
to the subsequent loader's hook.
|
|
|
|
A hook that returns a value lacking a required property triggers an exception.
|
|
A hook that returns without calling `next<hookName>()` _and_ without returning
|
|
`shortCircuit: true` also triggers an exception. These errors are to help
|
|
prevent unintentional breaks in the chain.
|
|
|
|
Hooks are run in a separate thread, isolated from the main. That means it is a
|
|
different [realm](https://tc39.es/ecma262/#realm). The hooks thread may be
|
|
terminated by the main thread at any time, so do not depend on asynchronous
|
|
operations (like `console.log`) to complete.
|
|
|
|
#### `initialize()`
|
|
|
|
<!-- YAML
|
|
added: REPLACEME
|
|
-->
|
|
|
|
> The loaders API is being redesigned. This hook may disappear or its
|
|
> signature may change. Do not rely on the API described below.
|
|
|
|
* `data` {any} The data from `register(loader, import.meta.url, { data })`.
|
|
* Returns: {any} The data to be returned to the caller of `register`.
|
|
|
|
The `initialize` hook provides a way to define a custom function that runs
|
|
in the loader's thread when the loader is initialized. Initialization happens
|
|
when the loader is registered via [`register`][] or registered via the
|
|
`--experimental-loader` command line option.
|
|
|
|
This hook can send and receive data from a [`register`][] invocation, including
|
|
ports and other transferrable objects. The return value of `initialize` must be
|
|
either:
|
|
|
|
* `undefined`,
|
|
* something that can be posted as a message between threads (e.g. the input to
|
|
[`port.postMessage`][]),
|
|
* a `Promise` resolving to one of the aforementioned values.
|
|
|
|
Loader code:
|
|
|
|
```js
|
|
// In the below example this file is referenced as
|
|
// '/path-to-my-loader.js'
|
|
|
|
export async function initialize({ number, port }) {
|
|
port.postMessage(`increment: ${number + 1}`);
|
|
return 'ok';
|
|
}
|
|
```
|
|
|
|
Caller code:
|
|
|
|
```js
|
|
import assert from 'node:assert';
|
|
import { register } from 'node:module';
|
|
import { MessageChannel } from 'node:worker_threads';
|
|
|
|
// This example showcases how a message channel can be used to
|
|
// communicate between the main (application) thread and the loader
|
|
// running on the loaders thread, by sending `port2` to the loader.
|
|
const { port1, port2 } = new MessageChannel();
|
|
|
|
port1.on('message', (msg) => {
|
|
assert.strictEqual(msg, 'increment: 2');
|
|
});
|
|
|
|
const result = register('/path-to-my-loader.js', {
|
|
parentURL: import.meta.url,
|
|
data: { number: 1, port: port2 },
|
|
transferList: [port2],
|
|
});
|
|
|
|
assert.strictEqual(result, 'ok');
|
|
```
|
|
|
|
#### `resolve(specifier, context, nextResolve)`
|
|
|
|
<!-- YAML
|
|
changes:
|
|
- version:
|
|
- v18.6.0
|
|
- v16.17.0
|
|
pr-url: https://github.com/nodejs/node/pull/42623
|
|
description: Add support for chaining resolve hooks. Each hook must either
|
|
call `nextResolve()` or include a `shortCircuit` property set to `true`
|
|
in its return.
|
|
- version:
|
|
- v17.1.0
|
|
- v16.14.0
|
|
pr-url: https://github.com/nodejs/node/pull/40250
|
|
description: Add support for import assertions.
|
|
-->
|
|
|
|
> The loaders API is being redesigned. This hook may disappear or its
|
|
> signature may change. Do not rely on the API described below.
|
|
|
|
* `specifier` {string}
|
|
* `context` {Object}
|
|
* `conditions` {string\[]} Export conditions of the relevant `package.json`
|
|
* `importAssertions` {Object} An object whose key-value pairs represent the
|
|
assertions for the module to import
|
|
* `parentURL` {string|undefined} The module importing this one, or undefined
|
|
if this is the Node.js entry point
|
|
* `nextResolve` {Function} The subsequent `resolve` hook in the chain, or the
|
|
Node.js default `resolve` hook after the last user-supplied `resolve` hook
|
|
* `specifier` {string}
|
|
* `context` {Object}
|
|
* Returns: {Object|Promise}
|
|
* `format` {string|null|undefined} A hint to the load hook (it might be
|
|
ignored)
|
|
`'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'`
|
|
* `importAssertions` {Object|undefined} The import assertions to use when
|
|
caching the module (optional; if excluded the input will be used)
|
|
* `shortCircuit` {undefined|boolean} A signal that this hook intends to
|
|
terminate the chain of `resolve` hooks. **Default:** `false`
|
|
* `url` {string} The absolute URL to which this input resolves
|
|
|
|
> **Caveat** Despite support for returning promises and async functions, calls
|
|
> to `resolve` may block the main thread which can impact performance.
|
|
|
|
The `resolve` hook chain is responsible for telling Node.js where to find and
|
|
how to cache a given `import` statement or expression. It can optionally return
|
|
its format (such as `'module'`) as a hint to the `load` hook. If a format is
|
|
specified, the `load` hook is ultimately responsible for providing the final
|
|
`format` value (and it is free to ignore the hint provided by `resolve`); if
|
|
`resolve` provides a `format`, a custom `load` hook is required even if only to
|
|
pass the value to the Node.js default `load` hook.
|
|
|
|
Import type assertions are part of the cache key for saving loaded modules into
|
|
the internal module cache. The `resolve` hook is responsible for
|
|
returning an `importAssertions` object if the module should be cached with
|
|
different assertions than were present in the source code.
|
|
|
|
The `conditions` property in `context` is an array of conditions for
|
|
[package exports conditions][Conditional Exports] that apply to this resolution
|
|
request. They can be used for looking up conditional mappings elsewhere or to
|
|
modify the list when calling the default resolution logic.
|
|
|
|
The current [package exports conditions][Conditional Exports] are always in
|
|
the `context.conditions` array passed into the hook. To guarantee _default
|
|
Node.js module specifier resolution behavior_ when calling `defaultResolve`, the
|
|
`context.conditions` array passed to it _must_ include _all_ elements of the
|
|
`context.conditions` array originally passed into the `resolve` hook.
|
|
|
|
```js
|
|
export function resolve(specifier, context, nextResolve) {
|
|
const { parentURL = null } = context;
|
|
|
|
if (Math.random() > 0.5) { // Some condition.
|
|
// For some or all specifiers, do some custom logic for resolving.
|
|
// Always return an object of the form {url: <string>}.
|
|
return {
|
|
shortCircuit: true,
|
|
url: parentURL ?
|
|
new URL(specifier, parentURL).href :
|
|
new URL(specifier).href,
|
|
};
|
|
}
|
|
|
|
if (Math.random() < 0.5) { // Another condition.
|
|
// When calling `defaultResolve`, the arguments can be modified. In this
|
|
// case it's adding another value for matching conditional exports.
|
|
return nextResolve(specifier, {
|
|
...context,
|
|
conditions: [...context.conditions, 'another-condition'],
|
|
});
|
|
}
|
|
|
|
// Defer to the next hook in the chain, which would be the
|
|
// Node.js default resolve if this is the last user-specified loader.
|
|
return nextResolve(specifier);
|
|
}
|
|
```
|
|
|
|
#### `load(url, context, nextLoad)`
|
|
|
|
<!-- YAML
|
|
changes:
|
|
- version: REPLACEME
|
|
pr-url: https://github.com/nodejs/node/pull/47999
|
|
description: Add support for `source` with format `commonjs`.
|
|
- version:
|
|
- v18.6.0
|
|
- v16.17.0
|
|
pr-url: https://github.com/nodejs/node/pull/42623
|
|
description: Add support for chaining load hooks. Each hook must either
|
|
call `nextLoad()` or include a `shortCircuit` property set to `true` in
|
|
its return.
|
|
-->
|
|
|
|
> The loaders API is being redesigned. This hook may disappear or its
|
|
> signature may change. Do not rely on the API described below.
|
|
|
|
> In a previous version of this API, this was split across 3 separate, now
|
|
> deprecated, hooks (`getFormat`, `getSource`, and `transformSource`).
|
|
|
|
* `url` {string} The URL returned by the `resolve` chain
|
|
* `context` {Object}
|
|
* `conditions` {string\[]} Export conditions of the relevant `package.json`
|
|
* `format` {string|null|undefined} The format optionally supplied by the
|
|
`resolve` hook chain
|
|
* `importAssertions` {Object}
|
|
* `nextLoad` {Function} The subsequent `load` hook in the chain, or the
|
|
Node.js default `load` hook after the last user-supplied `load` hook
|
|
* `specifier` {string}
|
|
* `context` {Object}
|
|
* Returns: {Object}
|
|
* `format` {string}
|
|
* `shortCircuit` {undefined|boolean} A signal that this hook intends to
|
|
terminate the chain of `resolve` hooks. **Default:** `false`
|
|
* `source` {string|ArrayBuffer|TypedArray} The source for Node.js to evaluate
|
|
|
|
The `load` hook provides a way to define a custom method of determining how
|
|
a URL should be interpreted, retrieved, and parsed. It is also in charge of
|
|
validating the import assertion.
|
|
|
|
The final value of `format` must be one of the following:
|
|
|
|
| `format` | Description | Acceptable types for `source` returned by `load` |
|
|
| ------------ | ------------------------------ | -------------------------------------------------------------------------- |
|
|
| `'builtin'` | Load a Node.js builtin module | Not applicable |
|
|
| `'commonjs'` | Load a Node.js CommonJS module | { [`string`][], [`ArrayBuffer`][], [`TypedArray`][], `null`, `undefined` } |
|
|
| `'json'` | Load a JSON file | { [`string`][], [`ArrayBuffer`][], [`TypedArray`][] } |
|
|
| `'module'` | Load an ES module | { [`string`][], [`ArrayBuffer`][], [`TypedArray`][] } |
|
|
| `'wasm'` | Load a WebAssembly module | { [`ArrayBuffer`][], [`TypedArray`][] } |
|
|
|
|
The value of `source` is ignored for type `'builtin'` because currently it is
|
|
not possible to replace the value of a Node.js builtin (core) module.
|
|
|
|
The value of `source` can be omitted for type `'commonjs'`. When a `source` is
|
|
provided, all `require` calls from this module will be processed by the ESM
|
|
loader with registered `resolve` and `load` hooks; all `require.resolve` calls
|
|
from this module will be processed by the ESM loader with registered `resolve`
|
|
hooks; `require.extensions` and monkey-patching on the CommonJS module loader
|
|
will not apply. If `source` is undefined or `null`, it will be handled by the
|
|
CommonJS module loader and `require`/`require.resolve` calls will not go through
|
|
the registered hooks. This behavior for nullish `source` is temporary — in the
|
|
future, nullish `source` will not be supported.
|
|
|
|
The Node.js own `load` implementation, which is the value of `next` for the last
|
|
loader in the `load` chain, returns `null` for `source` when `format` is
|
|
`'commonjs'` for backward compatibility. Here is an example loader that would
|
|
opt-in to using the non-default behavior:
|
|
|
|
```js
|
|
import { readFile } from 'node:fs/promises';
|
|
|
|
export async function load(url, context, nextLoad) {
|
|
const result = await nextLoad(url, context);
|
|
if (result.format === 'commonjs') {
|
|
result.source ??= await readFile(new URL(result.responseURL ?? url));
|
|
}
|
|
return result;
|
|
}
|
|
```
|
|
|
|
> **Caveat**: The ESM `load` hook and namespaced exports from CommonJS modules
|
|
> are incompatible. Attempting to use them together will result in an empty
|
|
> object from the import. This may be addressed in the future.
|
|
|
|
> These types all correspond to classes defined in ECMAScript.
|
|
|
|
* The specific [`ArrayBuffer`][] object is a [`SharedArrayBuffer`][].
|
|
* The specific [`TypedArray`][] object is a [`Uint8Array`][].
|
|
|
|
If the source value of a text-based format (i.e., `'json'`, `'module'`)
|
|
is not a string, it is converted to a string using [`util.TextDecoder`][].
|
|
|
|
The `load` hook provides a way to define a custom method for retrieving the
|
|
source code of an ES module specifier. This would allow a loader to potentially
|
|
avoid reading files from disk. It could also be used to map an unrecognized
|
|
format to a supported one, for example `yaml` to `module`.
|
|
|
|
```js
|
|
export async function load(url, context, nextLoad) {
|
|
const { format } = context;
|
|
|
|
if (Math.random() > 0.5) { // Some condition
|
|
/*
|
|
For some or all URLs, do some custom logic for retrieving the source.
|
|
Always return an object of the form {
|
|
format: <string>,
|
|
source: <string|buffer>,
|
|
}.
|
|
*/
|
|
return {
|
|
format,
|
|
shortCircuit: true,
|
|
source: '...',
|
|
};
|
|
}
|
|
|
|
// Defer to the next hook in the chain.
|
|
return nextLoad(url);
|
|
}
|
|
```
|
|
|
|
In a more advanced scenario, this can also be used to transform an unsupported
|
|
source to a supported one (see [Examples](#examples) below).
|
|
|
|
#### `globalPreload()`
|
|
|
|
<!-- YAML
|
|
changes:
|
|
- version:
|
|
- v18.6.0
|
|
- v16.17.0
|
|
pr-url: https://github.com/nodejs/node/pull/42623
|
|
description: Add support for chaining globalPreload hooks.
|
|
-->
|
|
|
|
> This hook will be removed in a future version. Use [`initialize`][] instead.
|
|
> When a loader has an `initialize` export, `globalPreload` will be ignored.
|
|
|
|
> In a previous version of this API, this hook was named
|
|
> `getGlobalPreloadCode`.
|
|
|
|
* `context` {Object} Information to assist the preload code
|
|
* `port` {MessagePort}
|
|
* Returns: {string} Code to run before application startup
|
|
|
|
Sometimes it might be necessary to run some code inside of the same global
|
|
scope that the application runs in. This hook allows the return of a string
|
|
that is run as a sloppy-mode script on startup.
|
|
|
|
Similar to how CommonJS wrappers work, the code runs in an implicit function
|
|
scope. The only argument is a `require`-like function that can be used to load
|
|
builtins like "fs": `getBuiltin(request: string)`.
|
|
|
|
If the code needs more advanced `require` features, it has to construct
|
|
its own `require` using `module.createRequire()`.
|
|
|
|
```js
|
|
export function globalPreload(context) {
|
|
return `\
|
|
globalThis.someInjectedProperty = 42;
|
|
console.log('I just set some globals!');
|
|
|
|
const { createRequire } = getBuiltin('module');
|
|
const { cwd } = getBuiltin('process');
|
|
|
|
const require = createRequire(cwd() + '/<preload>');
|
|
// [...]
|
|
`;
|
|
}
|
|
```
|
|
|
|
In order to allow communication between the application and the loader, another
|
|
argument is provided to the preload code: `port`. This is available as a
|
|
parameter to the loader hook and inside of the source text returned by the hook.
|
|
Some care must be taken in order to properly call [`port.ref()`][] and
|
|
[`port.unref()`][] to prevent a process from being in a state where it won't
|
|
close normally.
|
|
|
|
```js
|
|
/**
|
|
* This example has the application context send a message to the loader
|
|
* and sends the message back to the application context
|
|
*/
|
|
export function globalPreload({ port }) {
|
|
port.onmessage = (evt) => {
|
|
port.postMessage(evt.data);
|
|
};
|
|
return `\
|
|
port.postMessage('console.log("I went to the Loader and back");');
|
|
port.onmessage = (evt) => {
|
|
eval(evt.data);
|
|
};
|
|
`;
|
|
}
|
|
```
|
|
|
|
### Examples
|
|
|
|
The various loader hooks can be used together to accomplish wide-ranging
|
|
customizations of the Node.js code loading and evaluation behaviors.
|
|
|
|
#### HTTPS loader
|
|
|
|
In current Node.js, specifiers starting with `https://` are experimental (see
|
|
[HTTPS and HTTP imports][]).
|
|
|
|
The loader below registers hooks to enable rudimentary support for such
|
|
specifiers. While this may seem like a significant improvement to Node.js core
|
|
functionality, there are substantial downsides to actually using this loader:
|
|
performance is much slower than loading files from disk, there is no caching,
|
|
and there is no security.
|
|
|
|
```js
|
|
// https-loader.mjs
|
|
import { get } from 'node:https';
|
|
|
|
export function load(url, context, nextLoad) {
|
|
// For JavaScript to be loaded over the network, we need to fetch and
|
|
// return it.
|
|
if (url.startsWith('https://')) {
|
|
return new Promise((resolve, reject) => {
|
|
get(url, (res) => {
|
|
let data = '';
|
|
res.setEncoding('utf8');
|
|
res.on('data', (chunk) => data += chunk);
|
|
res.on('end', () => resolve({
|
|
// This example assumes all network-provided JavaScript is ES module
|
|
// code.
|
|
format: 'module',
|
|
shortCircuit: true,
|
|
source: data,
|
|
}));
|
|
}).on('error', (err) => reject(err));
|
|
});
|
|
}
|
|
|
|
// Let Node.js handle all other URLs.
|
|
return nextLoad(url);
|
|
}
|
|
```
|
|
|
|
```js
|
|
// main.mjs
|
|
import { VERSION } from 'https://coffeescript.org/browser-compiler-modern/coffeescript.js';
|
|
|
|
console.log(VERSION);
|
|
```
|
|
|
|
With the preceding loader, running
|
|
`node --experimental-loader ./https-loader.mjs ./main.mjs`
|
|
prints the current version of CoffeeScript per the module at the URL in
|
|
`main.mjs`.
|
|
|
|
#### Transpiler loader
|
|
|
|
Sources that are in formats Node.js doesn't understand can be converted into
|
|
JavaScript using the [`load` hook][load hook].
|
|
|
|
This is less performant than transpiling source files before running
|
|
Node.js; a transpiler loader should only be used for development and testing
|
|
purposes.
|
|
|
|
```js
|
|
// coffeescript-loader.mjs
|
|
import { readFile } from 'node:fs/promises';
|
|
import { dirname, extname, resolve as resolvePath } from 'node:path';
|
|
import { cwd } from 'node:process';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
import CoffeeScript from 'coffeescript';
|
|
|
|
const baseURL = pathToFileURL(`${cwd()}/`).href;
|
|
|
|
export async function load(url, context, nextLoad) {
|
|
if (extensionsRegex.test(url)) {
|
|
// Now that we patched resolve to let CoffeeScript URLs through, we need to
|
|
// tell Node.js what format such URLs should be interpreted as. Because
|
|
// CoffeeScript transpiles into JavaScript, it should be one of the two
|
|
// JavaScript formats: 'commonjs' or 'module'.
|
|
|
|
// CoffeeScript files can be either CommonJS or ES modules, so we want any
|
|
// CoffeeScript file to be treated by Node.js the same as a .js file at the
|
|
// same location. To determine how Node.js would interpret an arbitrary .js
|
|
// file, search up the file system for the nearest parent package.json file
|
|
// and read its "type" field.
|
|
const format = await getPackageType(url);
|
|
// When a hook returns a format of 'commonjs', `source` is ignored.
|
|
// To handle CommonJS files, a handler needs to be registered with
|
|
// `require.extensions` in order to process the files with the CommonJS
|
|
// loader. Avoiding the need for a separate CommonJS handler is a future
|
|
// enhancement planned for ES module loaders.
|
|
if (format === 'commonjs') {
|
|
return {
|
|
format,
|
|
shortCircuit: true,
|
|
};
|
|
}
|
|
|
|
const { source: rawSource } = await nextLoad(url, { ...context, format });
|
|
// This hook converts CoffeeScript source code into JavaScript source code
|
|
// for all imported CoffeeScript files.
|
|
const transformedSource = coffeeCompile(rawSource.toString(), url);
|
|
|
|
return {
|
|
format,
|
|
shortCircuit: true,
|
|
source: transformedSource,
|
|
};
|
|
}
|
|
|
|
// Let Node.js handle all other URLs.
|
|
return nextLoad(url);
|
|
}
|
|
|
|
async function getPackageType(url) {
|
|
// `url` is only a file path during the first iteration when passed the
|
|
// resolved url from the load() hook
|
|
// an actual file path from load() will contain a file extension as it's
|
|
// required by the spec
|
|
// this simple truthy check for whether `url` contains a file extension will
|
|
// work for most projects but does not cover some edge-cases (such as
|
|
// extensionless files or a url ending in a trailing space)
|
|
const isFilePath = !!extname(url);
|
|
// If it is a file path, get the directory it's in
|
|
const dir = isFilePath ?
|
|
dirname(fileURLToPath(url)) :
|
|
url;
|
|
// Compose a file path to a package.json in the same directory,
|
|
// which may or may not exist
|
|
const packagePath = resolvePath(dir, 'package.json');
|
|
// Try to read the possibly nonexistent package.json
|
|
const type = await readFile(packagePath, { encoding: 'utf8' })
|
|
.then((filestring) => JSON.parse(filestring).type)
|
|
.catch((err) => {
|
|
if (err?.code !== 'ENOENT') console.error(err);
|
|
});
|
|
// Ff package.json existed and contained a `type` field with a value, voila
|
|
if (type) return type;
|
|
// Otherwise, (if not at the root) continue checking the next directory up
|
|
// If at the root, stop and return false
|
|
return dir.length > 1 && getPackageType(resolvePath(dir, '..'));
|
|
}
|
|
```
|
|
|
|
```coffee
|
|
# main.coffee
|
|
import { scream } from './scream.coffee'
|
|
console.log scream 'hello, world'
|
|
|
|
import { version } from 'node:process'
|
|
console.log "Brought to you by Node.js version #{version}"
|
|
```
|
|
|
|
```coffee
|
|
# scream.coffee
|
|
export scream = (str) -> str.toUpperCase()
|
|
```
|
|
|
|
With the preceding loader, running
|
|
`node --experimental-loader ./coffeescript-loader.mjs main.coffee`
|
|
causes `main.coffee` to be turned into JavaScript after its source code is
|
|
loaded from disk but before Node.js executes it; and so on for any `.coffee`,
|
|
`.litcoffee` or `.coffee.md` files referenced via `import` statements of any
|
|
loaded file.
|
|
|
|
#### "import map" loader
|
|
|
|
The previous two loaders defined `load` hooks. This is an example of a loader
|
|
that does its work via the `resolve` hook. This loader reads an
|
|
`import-map.json` file that specifies which specifiers to override to another
|
|
URL (this is a very simplistic implemenation of a small subset of the
|
|
"import maps" specification).
|
|
|
|
```js
|
|
// import-map-loader.js
|
|
import fs from 'node:fs/promises';
|
|
|
|
const { imports } = JSON.parse(await fs.readFile('import-map.json'));
|
|
|
|
export async function resolve(specifier, context, nextResolve) {
|
|
if (Object.hasOwn(imports, specifier)) {
|
|
return nextResolve(imports[specifier], context);
|
|
}
|
|
|
|
return nextResolve(specifier, context);
|
|
}
|
|
```
|
|
|
|
Let's assume we have these files:
|
|
|
|
```js
|
|
// main.js
|
|
import 'a-module';
|
|
```
|
|
|
|
```json
|
|
// import-map.json
|
|
{
|
|
"imports": {
|
|
"a-module": "./some-module.js"
|
|
}
|
|
}
|
|
```
|
|
|
|
```js
|
|
// some-module.js
|
|
console.log('some module!');
|
|
```
|
|
|
|
If you run `node --experimental-loader ./import-map-loader.js main.js`
|
|
the output will be `some module!`.
|
|
|
|
### Register loaders programmatically
|
|
|
|
<!-- YAML
|
|
added: REPLACEME
|
|
-->
|
|
|
|
In addition to using the `--experimental-loader` option in the CLI,
|
|
loaders can also be registered programmatically. You can find
|
|
detailed information about this process in the documentation page
|
|
for [`module.register()`][].
|
|
|
|
## Resolution and loading algorithm
|
|
|
|
### Features
|
|
|
|
The default resolver has the following properties:
|
|
|
|
* FileURL-based resolution as is used by ES modules
|
|
* Relative and absolute URL resolution
|
|
* No default extensions
|
|
* No folder mains
|
|
* Bare specifier package resolution lookup through node\_modules
|
|
* Does not fail on unknown extensions or protocols
|
|
* Can optionally provide a hint of the format to the loading phase
|
|
|
|
The default loader has the following properties
|
|
|
|
* Support for builtin module loading via `node:` URLs
|
|
* Support for "inline" module loading via `data:` URLs
|
|
* Support for `file:` module loading
|
|
* Fails on any other URL protocol
|
|
* Fails on unknown extensions for `file:` loading
|
|
(supports only `.cjs`, `.js`, and `.mjs`)
|
|
|
|
### Resolution algorithm
|
|
|
|
The algorithm to load an ES module specifier is given through the
|
|
**ESM\_RESOLVE** method below. It returns the resolved URL for a
|
|
module specifier relative to a parentURL.
|
|
|
|
The resolution algorithm determines the full resolved URL for a module
|
|
load, along with its suggested module format. The resolution algorithm
|
|
does not determine whether the resolved URL protocol can be loaded,
|
|
or whether the file extensions are permitted, instead these validations
|
|
are applied by Node.js during the load phase
|
|
(for example, if it was asked to load a URL that has a protocol that is
|
|
not `file:`, `data:`, `node:`, or if `--experimental-network-imports`
|
|
is enabled, `https:`).
|
|
|
|
The algorithm also tries to determine the format of the file based
|
|
on the extension (see `ESM_FILE_FORMAT` algorithm below). If it does
|
|
not recognize the file extension (eg if it is not `.mjs`, `.cjs`, or
|
|
`.json`), then a format of `undefined` is returned,
|
|
which will throw during the load phase.
|
|
|
|
The algorithm to determine the module format of a resolved URL is
|
|
provided by **ESM\_FILE\_FORMAT**, which returns the unique module
|
|
format for any file. The _"module"_ format is returned for an ECMAScript
|
|
Module, while the _"commonjs"_ format is used to indicate loading through the
|
|
legacy CommonJS loader. Additional formats such as _"addon"_ can be extended in
|
|
future updates.
|
|
|
|
In the following algorithms, all subroutine errors are propagated as errors
|
|
of these top-level routines unless stated otherwise.
|
|
|
|
_defaultConditions_ is the conditional environment name array,
|
|
`["node", "import"]`.
|
|
|
|
The resolver can throw the following errors:
|
|
|
|
* _Invalid Module Specifier_: Module specifier is an invalid URL, package name
|
|
or package subpath specifier.
|
|
* _Invalid Package Configuration_: package.json configuration is invalid or
|
|
contains an invalid configuration.
|
|
* _Invalid Package Target_: Package exports or imports define a target module
|
|
for the package that is an invalid type or string target.
|
|
* _Package Path Not Exported_: Package exports do not define or permit a target
|
|
subpath in the package for the given module.
|
|
* _Package Import Not Defined_: Package imports do not define the specifier.
|
|
* _Module Not Found_: The package or module requested does not exist.
|
|
* _Unsupported Directory Import_: The resolved path corresponds to a directory,
|
|
which is not a supported target for module imports.
|
|
|
|
### Resolution Algorithm Specification
|
|
|
|
**ESM\_RESOLVE**(_specifier_, _parentURL_)
|
|
|
|
> 1. Let _resolved_ be **undefined**.
|
|
> 2. If _specifier_ is a valid URL, then
|
|
> 1. Set _resolved_ to the result of parsing and reserializing
|
|
> _specifier_ as a URL.
|
|
> 3. Otherwise, if _specifier_ starts with _"/"_, _"./"_, or _"../"_, then
|
|
> 1. Set _resolved_ to the URL resolution of _specifier_ relative to
|
|
> _parentURL_.
|
|
> 4. Otherwise, if _specifier_ starts with _"#"_, then
|
|
> 1. Set _resolved_ to the result of
|
|
> **PACKAGE\_IMPORTS\_RESOLVE**(_specifier_,
|
|
> _parentURL_, _defaultConditions_).
|
|
> 5. Otherwise,
|
|
> 1. Note: _specifier_ is now a bare specifier.
|
|
> 2. Set _resolved_ the result of
|
|
> **PACKAGE\_RESOLVE**(_specifier_, _parentURL_).
|
|
> 6. Let _format_ be **undefined**.
|
|
> 7. If _resolved_ is a _"file:"_ URL, then
|
|
> 1. If _resolved_ contains any percent encodings of _"/"_ or _"\\"_ (_"%2F"_
|
|
> and _"%5C"_ respectively), then
|
|
> 1. Throw an _Invalid Module Specifier_ error.
|
|
> 2. If the file at _resolved_ is a directory, then
|
|
> 1. Throw an _Unsupported Directory Import_ error.
|
|
> 3. If the file at _resolved_ does not exist, then
|
|
> 1. Throw a _Module Not Found_ error.
|
|
> 4. Set _resolved_ to the real path of _resolved_, maintaining the
|
|
> same URL querystring and fragment components.
|
|
> 5. Set _format_ to the result of **ESM\_FILE\_FORMAT**(_resolved_).
|
|
> 8. Otherwise,
|
|
> 1. Set _format_ the module format of the content type associated with the
|
|
> URL _resolved_.
|
|
> 9. Return _format_ and _resolved_ to the loading phase
|
|
|
|
**PACKAGE\_RESOLVE**(_packageSpecifier_, _parentURL_)
|
|
|
|
> 1. Let _packageName_ be **undefined**.
|
|
> 2. If _packageSpecifier_ is an empty string, then
|
|
> 1. Throw an _Invalid Module Specifier_ error.
|
|
> 3. If _packageSpecifier_ is a Node.js builtin module name, then
|
|
> 1. Return the string _"node:"_ concatenated with _packageSpecifier_.
|
|
> 4. If _packageSpecifier_ does not start with _"@"_, then
|
|
> 1. Set _packageName_ to the substring of _packageSpecifier_ until the first
|
|
> _"/"_ separator or the end of the string.
|
|
> 5. Otherwise,
|
|
> 1. If _packageSpecifier_ does not contain a _"/"_ separator, then
|
|
> 1. Throw an _Invalid Module Specifier_ error.
|
|
> 2. Set _packageName_ to the substring of _packageSpecifier_
|
|
> until the second _"/"_ separator or the end of the string.
|
|
> 6. If _packageName_ starts with _"."_ or contains _"\\"_ or _"%"_, then
|
|
> 1. Throw an _Invalid Module Specifier_ error.
|
|
> 7. Let _packageSubpath_ be _"."_ concatenated with the substring of
|
|
> _packageSpecifier_ from the position at the length of _packageName_.
|
|
> 8. If _packageSubpath_ ends in _"/"_, then
|
|
> 1. Throw an _Invalid Module Specifier_ error.
|
|
> 9. Let _selfUrl_ be the result of
|
|
> **PACKAGE\_SELF\_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_).
|
|
> 10. If _selfUrl_ is not **undefined**, return _selfUrl_.
|
|
> 11. While _parentURL_ is not the file system root,
|
|
> 1. Let _packageURL_ be the URL resolution of _"node\_modules/"_
|
|
> concatenated with _packageSpecifier_, relative to _parentURL_.
|
|
> 2. Set _parentURL_ to the parent folder URL of _parentURL_.
|
|
> 3. If the folder at _packageURL_ does not exist, then
|
|
> 1. Continue the next loop iteration.
|
|
> 4. Let _pjson_ be the result of **READ\_PACKAGE\_JSON**(_packageURL_).
|
|
> 5. If _pjson_ is not **null** and _pjson_._exports_ is not **null** or
|
|
> **undefined**, then
|
|
> 1. Return the result of **PACKAGE\_EXPORTS\_RESOLVE**(_packageURL_,
|
|
> _packageSubpath_, _pjson.exports_, _defaultConditions_).
|
|
> 6. Otherwise, if _packageSubpath_ is equal to _"."_, then
|
|
> 1. If _pjson.main_ is a string, then
|
|
> 1. Return the URL resolution of _main_ in _packageURL_.
|
|
> 7. Otherwise,
|
|
> 1. Return the URL resolution of _packageSubpath_ in _packageURL_.
|
|
> 12. Throw a _Module Not Found_ error.
|
|
|
|
**PACKAGE\_SELF\_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_)
|
|
|
|
> 1. Let _packageURL_ be the result of **LOOKUP\_PACKAGE\_SCOPE**(_parentURL_).
|
|
> 2. If _packageURL_ is **null**, then
|
|
> 1. Return **undefined**.
|
|
> 3. Let _pjson_ be the result of **READ\_PACKAGE\_JSON**(_packageURL_).
|
|
> 4. If _pjson_ is **null** or if _pjson_._exports_ is **null** or
|
|
> **undefined**, then
|
|
> 1. Return **undefined**.
|
|
> 5. If _pjson.name_ is equal to _packageName_, then
|
|
> 1. Return the result of **PACKAGE\_EXPORTS\_RESOLVE**(_packageURL_,
|
|
> _packageSubpath_, _pjson.exports_, _defaultConditions_).
|
|
> 6. Otherwise, return **undefined**.
|
|
|
|
**PACKAGE\_EXPORTS\_RESOLVE**(_packageURL_, _subpath_, _exports_, _conditions_)
|
|
|
|
> 1. If _exports_ is an Object with both a key starting with _"."_ and a key not
|
|
> starting with _"."_, throw an _Invalid Package Configuration_ error.
|
|
> 2. If _subpath_ is equal to _"."_, then
|
|
> 1. Let _mainExport_ be **undefined**.
|
|
> 2. If _exports_ is a String or Array, or an Object containing no keys
|
|
> starting with _"."_, then
|
|
> 1. Set _mainExport_ to _exports_.
|
|
> 3. Otherwise if _exports_ is an Object containing a _"."_ property, then
|
|
> 1. Set _mainExport_ to _exports_\[_"."_].
|
|
> 4. If _mainExport_ is not **undefined**, then
|
|
> 1. Let _resolved_ be the result of **PACKAGE\_TARGET\_RESOLVE**(
|
|
> _packageURL_, _mainExport_, **null**, **false**, _conditions_).
|
|
> 2. If _resolved_ is not **null** or **undefined**, return _resolved_.
|
|
> 3. Otherwise, if _exports_ is an Object and all keys of _exports_ start with
|
|
> _"."_, then
|
|
> 1. Let _matchKey_ be the string _"./"_ concatenated with _subpath_.
|
|
> 2. Let _resolved_ be the result of **PACKAGE\_IMPORTS\_EXPORTS\_RESOLVE**(
|
|
> _matchKey_, _exports_, _packageURL_, **false**, _conditions_).
|
|
> 3. If _resolved_ is not **null** or **undefined**, return _resolved_.
|
|
> 4. Throw a _Package Path Not Exported_ error.
|
|
|
|
**PACKAGE\_IMPORTS\_RESOLVE**(_specifier_, _parentURL_, _conditions_)
|
|
|
|
> 1. Assert: _specifier_ begins with _"#"_.
|
|
> 2. If _specifier_ is exactly equal to _"#"_ or starts with _"#/"_, then
|
|
> 1. Throw an _Invalid Module Specifier_ error.
|
|
> 3. Let _packageURL_ be the result of **LOOKUP\_PACKAGE\_SCOPE**(_parentURL_).
|
|
> 4. If _packageURL_ is not **null**, then
|
|
> 1. Let _pjson_ be the result of **READ\_PACKAGE\_JSON**(_packageURL_).
|
|
> 2. If _pjson.imports_ is a non-null Object, then
|
|
> 1. Let _resolved_ be the result of
|
|
> **PACKAGE\_IMPORTS\_EXPORTS\_RESOLVE**(
|
|
> _specifier_, _pjson.imports_, _packageURL_, **true**, _conditions_).
|
|
> 2. If _resolved_ is not **null** or **undefined**, return _resolved_.
|
|
> 5. Throw a _Package Import Not Defined_ error.
|
|
|
|
**PACKAGE\_IMPORTS\_EXPORTS\_RESOLVE**(_matchKey_, _matchObj_, _packageURL_,
|
|
_isImports_, _conditions_)
|
|
|
|
> 1. If _matchKey_ is a key of _matchObj_ and does not contain _"\*"_, then
|
|
> 1. Let _target_ be the value of _matchObj_\[_matchKey_].
|
|
> 2. Return the result of **PACKAGE\_TARGET\_RESOLVE**(_packageURL_,
|
|
> _target_, **null**, _isImports_, _conditions_).
|
|
> 2. Let _expansionKeys_ be the list of keys of _matchObj_ containing only a
|
|
> single _"\*"_, sorted by the sorting function **PATTERN\_KEY\_COMPARE**
|
|
> which orders in descending order of specificity.
|
|
> 3. For each key _expansionKey_ in _expansionKeys_, do
|
|
> 1. Let _patternBase_ be the substring of _expansionKey_ up to but excluding
|
|
> the first _"\*"_ character.
|
|
> 2. If _matchKey_ starts with but is not equal to _patternBase_, then
|
|
> 1. Let _patternTrailer_ be the substring of _expansionKey_ from the
|
|
> index after the first _"\*"_ character.
|
|
> 2. If _patternTrailer_ has zero length, or if _matchKey_ ends with
|
|
> _patternTrailer_ and the length of _matchKey_ is greater than or
|
|
> equal to the length of _expansionKey_, then
|
|
> 1. Let _target_ be the value of _matchObj_\[_expansionKey_].
|
|
> 2. Let _patternMatch_ be the substring of _matchKey_ starting at the
|
|
> index of the length of _patternBase_ up to the length of
|
|
> _matchKey_ minus the length of _patternTrailer_.
|
|
> 3. Return the result of **PACKAGE\_TARGET\_RESOLVE**(_packageURL_,
|
|
> _target_, _patternMatch_, _isImports_, _conditions_).
|
|
> 4. Return **null**.
|
|
|
|
**PATTERN\_KEY\_COMPARE**(_keyA_, _keyB_)
|
|
|
|
> 1. Assert: _keyA_ ends with _"/"_ or contains only a single _"\*"_.
|
|
> 2. Assert: _keyB_ ends with _"/"_ or contains only a single _"\*"_.
|
|
> 3. Let _baseLengthA_ be the index of _"\*"_ in _keyA_ plus one, if _keyA_
|
|
> contains _"\*"_, or the length of _keyA_ otherwise.
|
|
> 4. Let _baseLengthB_ be the index of _"\*"_ in _keyB_ plus one, if _keyB_
|
|
> contains _"\*"_, or the length of _keyB_ otherwise.
|
|
> 5. If _baseLengthA_ is greater than _baseLengthB_, return -1.
|
|
> 6. If _baseLengthB_ is greater than _baseLengthA_, return 1.
|
|
> 7. If _keyA_ does not contain _"\*"_, return 1.
|
|
> 8. If _keyB_ does not contain _"\*"_, return -1.
|
|
> 9. If the length of _keyA_ is greater than the length of _keyB_, return -1.
|
|
> 10. If the length of _keyB_ is greater than the length of _keyA_, return 1.
|
|
> 11. Return 0.
|
|
|
|
**PACKAGE\_TARGET\_RESOLVE**(_packageURL_, _target_, _patternMatch_,
|
|
_isImports_, _conditions_)
|
|
|
|
> 1. If _target_ is a String, then
|
|
> 1. If _target_ does not start with _"./"_, then
|
|
> 1. If _isImports_ is **false**, or if _target_ starts with _"../"_ or
|
|
> _"/"_, or if _target_ is a valid URL, then
|
|
> 1. Throw an _Invalid Package Target_ error.
|
|
> 2. If _patternMatch_ is a String, then
|
|
> 1. Return **PACKAGE\_RESOLVE**(_target_ with every instance of _"\*"_
|
|
> replaced by _patternMatch_, _packageURL_ + _"/"_).
|
|
> 3. Return **PACKAGE\_RESOLVE**(_target_, _packageURL_ + _"/"_).
|
|
> 2. If _target_ split on _"/"_ or _"\\"_ contains any _""_, _"."_, _".."_,
|
|
> or _"node\_modules"_ segments after the first _"."_ segment, case
|
|
> insensitive and including percent encoded variants, throw an _Invalid
|
|
> Package Target_ error.
|
|
> 3. Let _resolvedTarget_ be the URL resolution of the concatenation of
|
|
> _packageURL_ and _target_.
|
|
> 4. Assert: _resolvedTarget_ is contained in _packageURL_.
|
|
> 5. If _patternMatch_ is **null**, then
|
|
> 1. Return _resolvedTarget_.
|
|
> 6. If _patternMatch_ split on _"/"_ or _"\\"_ contains any _""_, _"."_,
|
|
> _".."_, or _"node\_modules"_ segments, case insensitive and including
|
|
> percent encoded variants, throw an _Invalid Module Specifier_ error.
|
|
> 7. Return the URL resolution of _resolvedTarget_ with every instance of
|
|
> _"\*"_ replaced with _patternMatch_.
|
|
> 2. Otherwise, if _target_ is a non-null Object, then
|
|
> 1. If _exports_ contains any index property keys, as defined in ECMA-262
|
|
> [6.1.7 Array Index][], throw an _Invalid Package Configuration_ error.
|
|
> 2. For each property _p_ of _target_, in object insertion order as,
|
|
> 1. If _p_ equals _"default"_ or _conditions_ contains an entry for _p_,
|
|
> then
|
|
> 1. Let _targetValue_ be the value of the _p_ property in _target_.
|
|
> 2. Let _resolved_ be the result of **PACKAGE\_TARGET\_RESOLVE**(
|
|
> _packageURL_, _targetValue_, _patternMatch_, _isImports_,
|
|
> _conditions_).
|
|
> 3. If _resolved_ is equal to **undefined**, continue the loop.
|
|
> 4. Return _resolved_.
|
|
> 3. Return **undefined**.
|
|
> 3. Otherwise, if _target_ is an Array, then
|
|
> 1. If \_target.length is zero, return **null**.
|
|
> 2. For each item _targetValue_ in _target_, do
|
|
> 1. Let _resolved_ be the result of **PACKAGE\_TARGET\_RESOLVE**(
|
|
> _packageURL_, _targetValue_, _patternMatch_, _isImports_,
|
|
> _conditions_), continuing the loop on any _Invalid Package Target_
|
|
> error.
|
|
> 2. If _resolved_ is **undefined**, continue the loop.
|
|
> 3. Return _resolved_.
|
|
> 3. Return or throw the last fallback resolution **null** return or error.
|
|
> 4. Otherwise, if _target_ is _null_, return **null**.
|
|
> 5. Otherwise throw an _Invalid Package Target_ error.
|
|
|
|
**ESM\_FILE\_FORMAT**(_url_)
|
|
|
|
> 1. Assert: _url_ corresponds to an existing file.
|
|
> 2. If _url_ ends in _".mjs"_, then
|
|
> 1. Return _"module"_.
|
|
> 3. If _url_ ends in _".cjs"_, then
|
|
> 1. Return _"commonjs"_.
|
|
> 4. If _url_ ends in _".json"_, then
|
|
> 1. Return _"json"_.
|
|
> 5. Let _packageURL_ be the result of **LOOKUP\_PACKAGE\_SCOPE**(_url_).
|
|
> 6. Let _pjson_ be the result of **READ\_PACKAGE\_JSON**(_packageURL_).
|
|
> 7. If _pjson?.type_ exists and is _"module"_, then
|
|
> 1. If _url_ ends in _".js"_, then
|
|
> 1. Return _"module"_.
|
|
> 2. Return **undefined**.
|
|
> 8. Otherwise,
|
|
> 1. Return **undefined**.
|
|
|
|
**LOOKUP\_PACKAGE\_SCOPE**(_url_)
|
|
|
|
> 1. Let _scopeURL_ be _url_.
|
|
> 2. While _scopeURL_ is not the file system root,
|
|
> 1. Set _scopeURL_ to the parent URL of _scopeURL_.
|
|
> 2. If _scopeURL_ ends in a _"node\_modules"_ path segment, return **null**.
|
|
> 3. Let _pjsonURL_ be the resolution of _"package.json"_ within
|
|
> _scopeURL_.
|
|
> 4. if the file at _pjsonURL_ exists, then
|
|
> 1. Return _scopeURL_.
|
|
> 3. Return **null**.
|
|
|
|
**READ\_PACKAGE\_JSON**(_packageURL_)
|
|
|
|
> 1. Let _pjsonURL_ be the resolution of _"package.json"_ within _packageURL_.
|
|
> 2. If the file at _pjsonURL_ does not exist, then
|
|
> 1. Return **null**.
|
|
> 3. If the file at _packageURL_ does not parse as valid JSON, then
|
|
> 1. Throw an _Invalid Package Configuration_ error.
|
|
> 4. Return the parsed JSON source of the file at _pjsonURL_.
|
|
|
|
### Customizing ESM specifier resolution algorithm
|
|
|
|
The [Loaders API][] provides a mechanism for customizing the ESM specifier
|
|
resolution algorithm. An example loader that provides CommonJS-style resolution
|
|
for ESM specifiers is [commonjs-extension-resolution-loader][].
|
|
|
|
<!-- Note: The cjs-module-lexer link should be kept in-sync with the deps version -->
|
|
|
|
[6.1.7 Array Index]: https://tc39.es/ecma262/#integer-index
|
|
[Addons]: addons.md
|
|
[CommonJS]: modules.md
|
|
[Conditional exports]: packages.md#conditional-exports
|
|
[Core modules]: modules.md#core-modules
|
|
[Determining module system]: packages.md#determining-module-system
|
|
[Dynamic `import()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
|
|
[ES Module Integration Proposal for WebAssembly]: https://github.com/webassembly/esm-integration
|
|
[HTTPS and HTTP imports]: #https-and-http-imports
|
|
[Import Assertions]: #import-assertions
|
|
[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
|
|
[JSON modules]: #json-modules
|
|
[Loaders API]: #loaders
|
|
[Node.js Module Resolution And Loading Algorithm]: #resolution-algorithm-specification
|
|
[Terminology]: #terminology
|
|
[URL]: https://url.spec.whatwg.org/
|
|
[`"exports"`]: packages.md#exports
|
|
[`"type"`]: packages.md#type
|
|
[`--input-type`]: cli.md#--input-typetype
|
|
[`ArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
|
|
[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
|
|
[`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
|
|
[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
|
|
[`data:` URLs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
|
|
[`export`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
|
|
[`import()`]: #import-expressions
|
|
[`import.meta.resolve`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve
|
|
[`import.meta.url`]: #importmetaurl
|
|
[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
|
[`initialize`]: #initialize
|
|
[`module.createRequire()`]: module.md#modulecreaterequirefilename
|
|
[`module.register()`]: module.md#moduleregister
|
|
[`module.syncBuiltinESMExports()`]: module.md#modulesyncbuiltinesmexports
|
|
[`package.json`]: packages.md#nodejs-packagejson-field-definitions
|
|
[`port.postMessage`]: worker_threads.md#portpostmessagevalue-transferlist
|
|
[`port.ref()`]: https://nodejs.org/dist/latest-v17.x/docs/api/worker_threads.html#portref
|
|
[`port.unref()`]: https://nodejs.org/dist/latest-v17.x/docs/api/worker_threads.html#portunref
|
|
[`process.dlopen`]: process.md#processdlopenmodule-filename-flags
|
|
[`register`]: module.md#moduleregister
|
|
[`string`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
|
|
[`util.TextDecoder`]: util.md#class-utiltextdecoder
|
|
[cjs-module-lexer]: https://github.com/nodejs/cjs-module-lexer/tree/1.2.2
|
|
[commonjs-extension-resolution-loader]: https://github.com/nodejs/loaders-test/tree/main/commonjs-extension-resolution-loader
|
|
[custom https loader]: #https-loader
|
|
[import.meta.resolve]: #importmetaresolvespecifier
|
|
[load hook]: #loadurl-context-nextload
|
|
[percent-encoded]: url.md#percent-encoding-in-urls
|
|
[special scheme]: https://url.spec.whatwg.org/#special-scheme
|
|
[status code]: process.md#exit-codes
|
|
[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
|
|
[url.pathToFileURL]: url.md#urlpathtofileurlpath
|