xref: /third_party/node/doc/api/module.md (revision 1cb0ef41)
1# Modules: `node:module` API
2
3<!--introduced_in=v12.20.0-->
4
5<!-- YAML
6added: v0.3.7
7-->
8
9## The `Module` object
10
11* {Object}
12
13Provides general utility methods when interacting with instances of
14`Module`, the [`module`][] variable often seen in [CommonJS][] modules. Accessed
15via `import 'node:module'` or `require('node:module')`.
16
17### `module.builtinModules`
18
19<!-- YAML
20added:
21  - v9.3.0
22  - v8.10.0
23  - v6.13.0
24-->
25
26* {string\[]}
27
28A list of the names of all modules provided by Node.js. Can be used to verify
29if a module is maintained by a third party or not.
30
31`module` in this context isn't the same object that's provided
32by the [module wrapper][]. To access it, require the `Module` module:
33
34```mjs
35// module.mjs
36// In an ECMAScript module
37import { builtinModules as builtin } from 'node:module';
38```
39
40```cjs
41// module.cjs
42// In a CommonJS module
43const builtin = require('node:module').builtinModules;
44```
45
46### `module.createRequire(filename)`
47
48<!-- YAML
49added: v12.2.0
50-->
51
52* `filename` {string|URL} Filename to be used to construct the require
53  function. Must be a file URL object, file URL string, or absolute path
54  string.
55* Returns: {require} Require function
56
57```mjs
58import { createRequire } from 'node:module';
59const require = createRequire(import.meta.url);
60
61// sibling-module.js is a CommonJS module.
62const siblingModule = require('./sibling-module');
63```
64
65### `module.isBuiltin(moduleName)`
66
67<!-- YAML
68added: v18.6.0
69-->
70
71* `moduleName` {string} name of the module
72* Returns: {boolean} returns true if the module is builtin else returns false
73
74```mjs
75import { isBuiltin } from 'node:module';
76isBuiltin('node:fs'); // true
77isBuiltin('fs'); // true
78isBuiltin('wss'); // false
79```
80
81### `module.register(specifier[, parentURL][, options])`
82
83<!-- YAML
84added: v18.19.0
85changes:
86  - version: v18.19.0
87    pr-url: https://github.com/nodejs/node/pull/49655
88    description: Add support for WHATWG URL instances.
89-->
90
91> Stability: 1.1 - Active development
92
93* `specifier` {string|URL} Customization hooks to be registered; this should be
94  the same string that would be passed to `import()`, except that if it is
95  relative, it is resolved relative to `parentURL`.
96* `parentURL` {string|URL} If you want to resolve `specifier` relative to a base
97  URL, such as `import.meta.url`, you can pass that URL here. **Default:**
98  `'data:'`
99* `options` {Object}
100  * `data` {any} Any arbitrary, cloneable JavaScript value to pass into the
101    [`initialize`][] hook.
102  * `transferList` {Object\[]} [transferrable objects][] to be passed into the
103    `initialize` hook.
104
105Register a module that exports [hooks][] that customize Node.js module
106resolution and loading behavior. See [Customization hooks][].
107
108### `module.syncBuiltinESMExports()`
109
110<!-- YAML
111added: v12.12.0
112-->
113
114The `module.syncBuiltinESMExports()` method updates all the live bindings for
115builtin [ES Modules][] to match the properties of the [CommonJS][] exports. It
116does not add or remove exported names from the [ES Modules][].
117
118```js
119const fs = require('node:fs');
120const assert = require('node:assert');
121const { syncBuiltinESMExports } = require('node:module');
122
123fs.readFile = newAPI;
124
125delete fs.readFileSync;
126
127function newAPI() {
128  // ...
129}
130
131fs.newAPI = newAPI;
132
133syncBuiltinESMExports();
134
135import('node:fs').then((esmFS) => {
136  // It syncs the existing readFile property with the new value
137  assert.strictEqual(esmFS.readFile, newAPI);
138  // readFileSync has been deleted from the required fs
139  assert.strictEqual('readFileSync' in fs, false);
140  // syncBuiltinESMExports() does not remove readFileSync from esmFS
141  assert.strictEqual('readFileSync' in esmFS, true);
142  // syncBuiltinESMExports() does not add names
143  assert.strictEqual(esmFS.newAPI, undefined);
144});
145```
146
147<i id="module_customization_hooks"></i>
148
149## Customization Hooks
150
151<!-- YAML
152added: v8.8.0
153changes:
154  - version: v18.19.0
155    pr-url: https://github.com/nodejs/node/pull/48842
156    description: Added `initialize` hook to replace `globalPreload`.
157  - version:
158    - v18.6.0
159    - v16.17.0
160    pr-url: https://github.com/nodejs/node/pull/42623
161    description: Add support for chaining loaders.
162  - version: v16.12.0
163    pr-url: https://github.com/nodejs/node/pull/37468
164    description: Removed `getFormat`, `getSource`, `transformSource`, and
165                 `globalPreload`; added `load` hook and `getGlobalPreload` hook.
166-->
167
168> Stability: 1.1 - Active development
169
170<!-- type=misc -->
171
172<i id="enabling_module_customization_hooks"></i>
173
174### Enabling
175
176Module resolution and loading can be customized by registering a file which
177exports a set of hooks. This can be done using the [`register`][] method
178from `node:module`, which you can run before your application code by
179using the `--import` flag:
180
181```bash
182node --import ./register-hooks.js ./my-app.js
183```
184
185```mjs
186// register-hooks.js
187import { register } from 'node:module';
188
189register('./hooks.mjs', import.meta.url);
190```
191
192```cjs
193// register-hooks.js
194const { register } = require('node:module');
195const { pathToFileURL } = require('node:url');
196
197register('./hooks.mjs', pathToFileURL(__filename));
198```
199
200The file passed to `--import` can also be an export from a dependency:
201
202```bash
203node --import some-package/register ./my-app.js
204```
205
206Where `some-package` has an [`"exports"`][] field defining the `/register`
207export to map to a file that calls `register()`, like the following `register-hooks.js`
208example.
209
210Using `--import` ensures that the hooks are registered before any application
211files are imported, including the entry point of the application. Alternatively,
212`register` can be called from the entry point, but dynamic `import()` must be
213used for any code that should be run after the hooks are registered:
214
215```mjs
216import { register } from 'node:module';
217
218register('http-to-https', import.meta.url);
219
220// Because this is a dynamic `import()`, the `http-to-https` hooks will run
221// to handle `./my-app.js` and any other files it imports or requires.
222await import('./my-app.js');
223```
224
225```cjs
226const { register } = require('node:module');
227const { pathToFileURL } = require('node:url');
228
229register('http-to-https', pathToFileURL(__filename));
230
231// Because this is a dynamic `import()`, the `http-to-https` hooks will run
232// to handle `./my-app.js` and any other files it imports or requires.
233import('./my-app.js');
234```
235
236In this example, we are registering the `http-to-https` hooks, but they will
237only be available for subsequently imported modules—in this case, `my-app.js`
238and anything it references via `import` (and optionally `require`). If the
239`import('./my-app.js')` had instead been a static `import './my-app.js'`, the
240app would have _already_ been loaded **before** the `http-to-https` hooks were
241registered. This due to the ES modules specification, where static imports are
242evaluated from the leaves of the tree first, then back to the trunk. There can
243be static imports _within_ `my-app.js`, which will not be evaluated until
244`my-app.js` is dynamically imported.
245
246`my-app.js` can also be CommonJS. Customization hooks will run for any
247modules that it references via `import` (and optionally `require`).
248
249Finally, if all you want to do is register hooks before your app runs and you
250don't want to create a separate file for that purpose, you can pass a `data:`
251URL to `--import`:
252
253```bash
254node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("http-to-https", pathToFileURL("./"));' ./my-app.js
255```
256
257### Chaining
258
259It's possible to call `register` more than once:
260
261```mjs
262// entrypoint.mjs
263import { register } from 'node:module';
264
265register('./first.mjs', import.meta.url);
266register('./second.mjs', import.meta.url);
267await import('./my-app.mjs');
268```
269
270```cjs
271// entrypoint.cjs
272const { register } = require('node:module');
273const { pathToFileURL } = require('node:url');
274
275const parentURL = pathToFileURL(__filename);
276register('./first.mjs', parentURL);
277register('./second.mjs', parentURL);
278import('./my-app.mjs');
279```
280
281In this example, the registered hooks will form chains. If both `first.mjs` and
282`second.mjs` define a `resolve` hook, both will be called, in the order they
283were registered. The same applies to all the other hooks.
284
285The registered hooks also affect `register` itself. In this example,
286`second.mjs` will be resolved and loaded per the hooks registered by
287`first.mjs`. This allows for things like writing hooks in non-JavaScript
288languages, so long as an earlier registered loader is one that transpiles into
289JavaScript.
290
291The `register` method cannot be called from within the module that defines the
292hooks.
293
294### Communication with module customization hooks
295
296Module customization hooks run on a dedicated thread, separate from the main
297thread that runs application code. This means mutating global variables won't
298affect the other thread(s), and message channels must be used to communicate
299between the threads.
300
301The `register` method can be used to pass data to an [`initialize`][] hook. The
302data passed to the hook may include transferrable objects like ports.
303
304```mjs
305import { register } from 'node:module';
306import { MessageChannel } from 'node:worker_threads';
307
308// This example demonstrates how a message channel can be used to
309// communicate with the hooks, by sending `port2` to the hooks.
310const { port1, port2 } = new MessageChannel();
311
312port1.on('message', (msg) => {
313  console.log(msg);
314});
315
316register('./my-hooks.mjs', {
317  parentURL: import.meta.url,
318  data: { number: 1, port: port2 },
319  transferList: [port2],
320});
321```
322
323```cjs
324const { register } = require('node:module');
325const { pathToFileURL } = require('node:url');
326const { MessageChannel } = require('node:worker_threads');
327
328// This example showcases how a message channel can be used to
329// communicate with the hooks, by sending `port2` to the hooks.
330const { port1, port2 } = new MessageChannel();
331
332port1.on('message', (msg) => {
333  console.log(msg);
334});
335
336register('./my-hooks.mjs', {
337  parentURL: pathToFileURL(__filename),
338  data: { number: 1, port: port2 },
339  transferList: [port2],
340});
341```
342
343### Hooks
344
345The [`register`][] method can be used to register a module that exports a set of
346hooks. The hooks are functions that are called by Node.js to customize the
347module resolution and loading process. The exported functions must have specific
348names and signatures, and they must be exported as named exports.
349
350```mjs
351export async function initialize({ number, port }) {
352  // Receives data from `register`.
353}
354
355export async function resolve(specifier, context, nextResolve) {
356  // Take an `import` or `require` specifier and resolve it to a URL.
357}
358
359export async function load(url, context, nextLoad) {
360  // Take a resolved URL and return the source code to be evaluated.
361}
362```
363
364Hooks are part of a chain, even if that chain consists of only one custom
365(user-provided) hook and the default hook, which is always present. Hook
366functions nest: each one must always return a plain object, and chaining happens
367as a result of each function calling `next<hookName>()`, which is a reference to
368the subsequent loader's hook.
369
370A hook that returns a value lacking a required property triggers an exception. A
371hook that returns without calling `next<hookName>()` _and_ without returning
372`shortCircuit: true` also triggers an exception. These errors are to help
373prevent unintentional breaks in the chain. Return `shortCircuit: true` from a
374hook to signal that the chain is intentionally ending at your hook.
375
376Hooks are run in a separate thread, isolated from the main thread where
377application code runs. That means it is a different [realm][]. The hooks thread
378may be terminated by the main thread at any time, so do not depend on
379asynchronous operations (like `console.log`) to complete.
380
381#### `initialize()`
382
383<!-- YAML
384added: v18.19.0
385-->
386
387> Stability: 1.1 - Active development
388
389* `data` {any} The data from `register(loader, import.meta.url, { data })`.
390
391The `initialize` hook provides a way to define a custom function that runs in
392the hooks thread when the hooks module is initialized. Initialization happens
393when the hooks module is registered via [`register`][].
394
395This hook can receive data from a [`register`][] invocation, including
396ports and other transferrable objects. The return value of `initialize` can be a
397{Promise}, in which case it will be awaited before the main application thread
398execution resumes.
399
400Module customization code:
401
402```mjs
403// path-to-my-hooks.js
404
405export async function initialize({ number, port }) {
406  port.postMessage(`increment: ${number + 1}`);
407}
408```
409
410Caller code:
411
412```mjs
413import assert from 'node:assert';
414import { register } from 'node:module';
415import { MessageChannel } from 'node:worker_threads';
416
417// This example showcases how a message channel can be used to communicate
418// between the main (application) thread and the hooks running on the hooks
419// thread, by sending `port2` to the `initialize` hook.
420const { port1, port2 } = new MessageChannel();
421
422port1.on('message', (msg) => {
423  assert.strictEqual(msg, 'increment: 2');
424});
425
426register('./path-to-my-hooks.js', {
427  parentURL: import.meta.url,
428  data: { number: 1, port: port2 },
429  transferList: [port2],
430});
431```
432
433```cjs
434const assert = require('node:assert');
435const { register } = require('node:module');
436const { pathToFileURL } = require('node:url');
437const { MessageChannel } = require('node:worker_threads');
438
439// This example showcases how a message channel can be used to communicate
440// between the main (application) thread and the hooks running on the hooks
441// thread, by sending `port2` to the `initialize` hook.
442const { port1, port2 } = new MessageChannel();
443
444port1.on('message', (msg) => {
445  assert.strictEqual(msg, 'increment: 2');
446});
447
448register('./path-to-my-hooks.js', {
449  parentURL: pathToFileURL(__filename),
450  data: { number: 1, port: port2 },
451  transferList: [port2],
452});
453```
454
455#### `resolve(specifier, context, nextResolve)`
456
457<!-- YAML
458changes:
459  - version: v18.19.0
460    pr-url: https://github.com/nodejs/node/pull/50140
461    description: The property `context.importAssertions` is replaced with
462                 `context.importAttributes`. Using the old name is still
463                 supported and will emit an experimental warning.
464  - version:
465    - v18.6.0
466    - v16.17.0
467    pr-url: https://github.com/nodejs/node/pull/42623
468    description: Add support for chaining resolve hooks. Each hook must either
469      call `nextResolve()` or include a `shortCircuit` property set to `true`
470      in its return.
471  - version:
472    - v17.1.0
473    - v16.14.0
474    pr-url: https://github.com/nodejs/node/pull/40250
475    description: Add support for import assertions.
476-->
477
478> Stability: 1.2 - Release candidate
479
480* `specifier` {string}
481* `context` {Object}
482  * `conditions` {string\[]} Export conditions of the relevant `package.json`
483  * `importAttributes` {Object} An object whose key-value pairs represent the
484    attributes for the module to import
485  * `parentURL` {string|undefined} The module importing this one, or undefined
486    if this is the Node.js entry point
487* `nextResolve` {Function} The subsequent `resolve` hook in the chain, or the
488  Node.js default `resolve` hook after the last user-supplied `resolve` hook
489  * `specifier` {string}
490  * `context` {Object}
491* Returns: {Object|Promise}
492  * `format` {string|null|undefined} A hint to the load hook (it might be
493    ignored)
494    `'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'`
495  * `importAttributes` {Object|undefined} The import attributes to use when
496    caching the module (optional; if excluded the input will be used)
497  * `shortCircuit` {undefined|boolean} A signal that this hook intends to
498    terminate the chain of `resolve` hooks. **Default:** `false`
499  * `url` {string} The absolute URL to which this input resolves
500
501> **Warning** Despite support for returning promises and async functions, calls
502> to `resolve` may block the main thread which can impact performance.
503
504The `resolve` hook chain is responsible for telling Node.js where to find and
505how to cache a given `import` statement or expression, or `require` call. It can
506optionally return a format (such as `'module'`) as a hint to the `load` hook. If
507a format is specified, the `load` hook is ultimately responsible for providing
508the final `format` value (and it is free to ignore the hint provided by
509`resolve`); if `resolve` provides a `format`, a custom `load` hook is required
510even if only to pass the value to the Node.js default `load` hook.
511
512Import type attributes are part of the cache key for saving loaded modules into
513the internal module cache. The `resolve` hook is responsible for returning an
514`importAttributes` object if the module should be cached with different
515attributes than were present in the source code.
516
517The `conditions` property in `context` is an array of conditions for
518[package exports conditions][Conditional exports] that apply to this resolution
519request. They can be used for looking up conditional mappings elsewhere or to
520modify the list when calling the default resolution logic.
521
522The current [package exports conditions][Conditional exports] are always in
523the `context.conditions` array passed into the hook. To guarantee _default
524Node.js module specifier resolution behavior_ when calling `defaultResolve`, the
525`context.conditions` array passed to it _must_ include _all_ elements of the
526`context.conditions` array originally passed into the `resolve` hook.
527
528```mjs
529export async function resolve(specifier, context, nextResolve) {
530  const { parentURL = null } = context;
531
532  if (Math.random() > 0.5) { // Some condition.
533    // For some or all specifiers, do some custom logic for resolving.
534    // Always return an object of the form {url: <string>}.
535    return {
536      shortCircuit: true,
537      url: parentURL ?
538        new URL(specifier, parentURL).href :
539        new URL(specifier).href,
540    };
541  }
542
543  if (Math.random() < 0.5) { // Another condition.
544    // When calling `defaultResolve`, the arguments can be modified. In this
545    // case it's adding another value for matching conditional exports.
546    return nextResolve(specifier, {
547      ...context,
548      conditions: [...context.conditions, 'another-condition'],
549    });
550  }
551
552  // Defer to the next hook in the chain, which would be the
553  // Node.js default resolve if this is the last user-specified loader.
554  return nextResolve(specifier);
555}
556```
557
558#### `load(url, context, nextLoad)`
559
560<!-- YAML
561changes:
562  - version:
563    - v18.6.0
564    - v16.17.0
565    pr-url: https://github.com/nodejs/node/pull/42623
566    description: Add support for chaining load hooks. Each hook must either
567      call `nextLoad()` or include a `shortCircuit` property set to `true` in
568      its return.
569-->
570
571> Stability: 1.2 - Release candidate
572
573* `url` {string} The URL returned by the `resolve` chain
574* `context` {Object}
575  * `conditions` {string\[]} Export conditions of the relevant `package.json`
576  * `format` {string|null|undefined} The format optionally supplied by the
577    `resolve` hook chain
578  * `importAttributes` {Object}
579* `nextLoad` {Function} The subsequent `load` hook in the chain, or the
580  Node.js default `load` hook after the last user-supplied `load` hook
581  * `specifier` {string}
582  * `context` {Object}
583* Returns: {Object}
584  * `format` {string}
585  * `shortCircuit` {undefined|boolean} A signal that this hook intends to
586    terminate the chain of `resolve` hooks. **Default:** `false`
587  * `source` {string|ArrayBuffer|TypedArray} The source for Node.js to evaluate
588
589The `load` hook provides a way to define a custom method of determining how a
590URL should be interpreted, retrieved, and parsed. It is also in charge of
591validating the import assertion.
592
593The final value of `format` must be one of the following:
594
595| `format`     | Description                    | Acceptable types for `source` returned by `load`      |
596| ------------ | ------------------------------ | ----------------------------------------------------- |
597| `'builtin'`  | Load a Node.js builtin module  | Not applicable                                        |
598| `'commonjs'` | Load a Node.js CommonJS module | Not applicable                                        |
599| `'json'`     | Load a JSON file               | { [`string`][], [`ArrayBuffer`][], [`TypedArray`][] } |
600| `'module'`   | Load an ES module              | { [`string`][], [`ArrayBuffer`][], [`TypedArray`][] } |
601| `'wasm'`     | Load a WebAssembly module      | { [`ArrayBuffer`][], [`TypedArray`][] }               |
602
603The value of `source` is ignored for type `'builtin'` because currently it is
604not possible to replace the value of a Node.js builtin (core) module. The value
605of `source` is ignored for type `'commonjs'` because the CommonJS module loader
606does not provide a mechanism for the ES module loader to override the
607[CommonJS module return value](esm.md#commonjs-namespaces). This limitation
608might be overcome in the future.
609
610> **Warning**: The ESM `load` hook and namespaced exports from CommonJS modules
611> are incompatible. Attempting to use them together will result in an empty
612> object from the import. This may be addressed in the future.
613
614> These types all correspond to classes defined in ECMAScript.
615
616* The specific [`ArrayBuffer`][] object is a [`SharedArrayBuffer`][].
617* The specific [`TypedArray`][] object is a [`Uint8Array`][].
618
619If the source value of a text-based format (i.e., `'json'`, `'module'`)
620is not a string, it is converted to a string using [`util.TextDecoder`][].
621
622The `load` hook provides a way to define a custom method for retrieving the
623source code of a resolved URL. This would allow a loader to potentially avoid
624reading files from disk. It could also be used to map an unrecognized format to
625a supported one, for example `yaml` to `module`.
626
627```mjs
628export async function load(url, context, nextLoad) {
629  const { format } = context;
630
631  if (Math.random() > 0.5) { // Some condition
632    /*
633      For some or all URLs, do some custom logic for retrieving the source.
634      Always return an object of the form {
635        format: <string>,
636        source: <string|buffer>,
637      }.
638    */
639    return {
640      format,
641      shortCircuit: true,
642      source: '...',
643    };
644  }
645
646  // Defer to the next hook in the chain.
647  return nextLoad(url);
648}
649```
650
651In a more advanced scenario, this can also be used to transform an unsupported
652source to a supported one (see [Examples](#examples) below).
653
654#### `globalPreload()`
655
656<!-- YAML
657changes:
658  - version:
659    - v18.6.0
660    - v16.17.0
661    pr-url: https://github.com/nodejs/node/pull/42623
662    description: Add support for chaining globalPreload hooks.
663-->
664
665> Stability: 1.0 - Early development
666
667> **Warning:** This hook will be removed in a future version. Use
668> [`initialize`][] instead. When a hooks module has an `initialize` export,
669> `globalPreload` will be ignored.
670
671* `context` {Object} Information to assist the preload code
672  * `port` {MessagePort}
673* Returns: {string} Code to run before application startup
674
675Sometimes it might be necessary to run some code inside of the same global
676scope that the application runs in. This hook allows the return of a string
677that is run as a sloppy-mode script on startup.
678
679Similar to how CommonJS wrappers work, the code runs in an implicit function
680scope. The only argument is a `require`-like function that can be used to load
681builtins like "fs": `getBuiltin(request: string)`.
682
683If the code needs more advanced `require` features, it has to construct
684its own `require` using  `module.createRequire()`.
685
686```mjs
687export function globalPreload(context) {
688  return `\
689globalThis.someInjectedProperty = 42;
690console.log('I just set some globals!');
691
692const { createRequire } = getBuiltin('module');
693const { cwd } = getBuiltin('process');
694
695const require = createRequire(cwd() + '/<preload>');
696// [...]
697`;
698}
699```
700
701Another argument is provided to the preload code: `port`. This is available as a
702parameter to the hook and inside of the source text returned by the hook. This
703functionality has been moved to the `initialize` hook.
704
705Care must be taken in order to properly call [`port.ref()`][] and
706[`port.unref()`][] to prevent a process from being in a state where it won't
707close normally.
708
709```mjs
710/**
711 * This example has the application context send a message to the hook
712 * and sends the message back to the application context
713 */
714export function globalPreload({ port }) {
715  port.on('message', (msg) => {
716    port.postMessage(msg);
717  });
718  return `\
719    port.postMessage('console.log("I went to the hook and back");');
720    port.on('message', (msg) => {
721      eval(msg);
722    });
723  `;
724}
725```
726
727### Examples
728
729The various module customization hooks can be used together to accomplish
730wide-ranging customizations of the Node.js code loading and evaluation
731behaviors.
732
733#### Import from HTTPS
734
735In current Node.js, specifiers starting with `https://` are experimental (see
736[HTTPS and HTTP imports][]).
737
738The hook below registers hooks to enable rudimentary support for such
739specifiers. While this may seem like a significant improvement to Node.js core
740functionality, there are substantial downsides to actually using these hooks:
741performance is much slower than loading files from disk, there is no caching,
742and there is no security.
743
744```mjs
745// https-hooks.mjs
746import { get } from 'node:https';
747
748export function load(url, context, nextLoad) {
749  // For JavaScript to be loaded over the network, we need to fetch and
750  // return it.
751  if (url.startsWith('https://')) {
752    return new Promise((resolve, reject) => {
753      get(url, (res) => {
754        let data = '';
755        res.setEncoding('utf8');
756        res.on('data', (chunk) => data += chunk);
757        res.on('end', () => resolve({
758          // This example assumes all network-provided JavaScript is ES module
759          // code.
760          format: 'module',
761          shortCircuit: true,
762          source: data,
763        }));
764      }).on('error', (err) => reject(err));
765    });
766  }
767
768  // Let Node.js handle all other URLs.
769  return nextLoad(url);
770}
771```
772
773```mjs
774// main.mjs
775import { VERSION } from 'https://coffeescript.org/browser-compiler-modern/coffeescript.js';
776
777console.log(VERSION);
778```
779
780With the preceding hooks module, running
781`node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./https-hooks.mjs"));' ./main.mjs`
782prints the current version of CoffeeScript per the module at the URL in
783`main.mjs`.
784
785#### Transpilation
786
787Sources that are in formats Node.js doesn't understand can be converted into
788JavaScript using the [`load` hook][load hook].
789
790This is less performant than transpiling source files before running Node.js;
791transpiler hooks should only be used for development and testing purposes.
792
793```mjs
794// coffeescript-hooks.mjs
795import { readFile } from 'node:fs/promises';
796import { dirname, extname, resolve as resolvePath } from 'node:path';
797import { cwd } from 'node:process';
798import { fileURLToPath, pathToFileURL } from 'node:url';
799import coffeescript from 'coffeescript';
800
801const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/;
802
803export async function load(url, context, nextLoad) {
804  if (extensionsRegex.test(url)) {
805    // CoffeeScript files can be either CommonJS or ES modules, so we want any
806    // CoffeeScript file to be treated by Node.js the same as a .js file at the
807    // same location. To determine how Node.js would interpret an arbitrary .js
808    // file, search up the file system for the nearest parent package.json file
809    // and read its "type" field.
810    const format = await getPackageType(url);
811
812    const { source: rawSource } = await nextLoad(url, { ...context, format });
813    // This hook converts CoffeeScript source code into JavaScript source code
814    // for all imported CoffeeScript files.
815    const transformedSource = coffeescript.compile(rawSource.toString(), url);
816
817    return {
818      format,
819      shortCircuit: true,
820      source: transformedSource,
821    };
822  }
823
824  // Let Node.js handle all other URLs.
825  return nextLoad(url);
826}
827
828async function getPackageType(url) {
829  // `url` is only a file path during the first iteration when passed the
830  // resolved url from the load() hook
831  // an actual file path from load() will contain a file extension as it's
832  // required by the spec
833  // this simple truthy check for whether `url` contains a file extension will
834  // work for most projects but does not cover some edge-cases (such as
835  // extensionless files or a url ending in a trailing space)
836  const isFilePath = !!extname(url);
837  // If it is a file path, get the directory it's in
838  const dir = isFilePath ?
839    dirname(fileURLToPath(url)) :
840    url;
841  // Compose a file path to a package.json in the same directory,
842  // which may or may not exist
843  const packagePath = resolvePath(dir, 'package.json');
844  // Try to read the possibly nonexistent package.json
845  const type = await readFile(packagePath, { encoding: 'utf8' })
846    .then((filestring) => JSON.parse(filestring).type)
847    .catch((err) => {
848      if (err?.code !== 'ENOENT') console.error(err);
849    });
850  // Ff package.json existed and contained a `type` field with a value, voila
851  if (type) return type;
852  // Otherwise, (if not at the root) continue checking the next directory up
853  // If at the root, stop and return false
854  return dir.length > 1 && getPackageType(resolvePath(dir, '..'));
855}
856```
857
858```coffee
859# main.coffee
860import { scream } from './scream.coffee'
861console.log scream 'hello, world'
862
863import { version } from 'node:process'
864console.log "Brought to you by Node.js version #{version}"
865```
866
867```coffee
868# scream.coffee
869export scream = (str) -> str.toUpperCase()
870```
871
872With the preceding hooks module, running
873`node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.mjs"));' ./main.coffee`
874causes `main.coffee` to be turned into JavaScript after its source code is
875loaded from disk but before Node.js executes it; and so on for any `.coffee`,
876`.litcoffee` or `.coffee.md` files referenced via `import` statements of any
877loaded file.
878
879#### Import maps
880
881The previous two examples defined `load` hooks. This is an example of a
882`resolve` hook. This hooks module reads an `import-map.json` file that defines
883which specifiers to override to other URLs (this is a very simplistic
884implementation of a small subset of the "import maps" specification).
885
886```mjs
887// import-map-hooks.js
888import fs from 'node:fs/promises';
889
890const { imports } = JSON.parse(await fs.readFile('import-map.json'));
891
892export async function resolve(specifier, context, nextResolve) {
893  if (Object.hasOwn(imports, specifier)) {
894    return nextResolve(imports[specifier], context);
895  }
896
897  return nextResolve(specifier, context);
898}
899```
900
901With these files:
902
903```mjs
904// main.js
905import 'a-module';
906```
907
908```json
909// import-map.json
910{
911  "imports": {
912    "a-module": "./some-module.js"
913  }
914}
915```
916
917```mjs
918// some-module.js
919console.log('some module!');
920```
921
922Running `node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./import-map-hooks.js"));' main.js`
923should print `some module!`.
924
925## Source map v3 support
926
927<!-- YAML
928added:
929 - v13.7.0
930 - v12.17.0
931-->
932
933> Stability: 1 - Experimental
934
935Helpers for interacting with the source map cache. This cache is
936populated when source map parsing is enabled and
937[source map include directives][] are found in a modules' footer.
938
939To enable source map parsing, Node.js must be run with the flag
940[`--enable-source-maps`][], or with code coverage enabled by setting
941[`NODE_V8_COVERAGE=dir`][].
942
943```mjs
944// module.mjs
945// In an ECMAScript module
946import { findSourceMap, SourceMap } from 'node:module';
947```
948
949```cjs
950// module.cjs
951// In a CommonJS module
952const { findSourceMap, SourceMap } = require('node:module');
953```
954
955<!-- Anchors to make sure old links find a target -->
956
957<a id="module_module_findsourcemap_path_error"></a>
958
959### `module.findSourceMap(path)`
960
961<!-- YAML
962added:
963 - v13.7.0
964 - v12.17.0
965-->
966
967* `path` {string}
968* Returns: {module.SourceMap|undefined} Returns `module.SourceMap` if a source
969  map is found, `undefined` otherwise.
970
971`path` is the resolved path for the file for which a corresponding source map
972should be fetched.
973
974### Class: `module.SourceMap`
975
976<!-- YAML
977added:
978 - v13.7.0
979 - v12.17.0
980-->
981
982#### `new SourceMap(payload)`
983
984* `payload` {Object}
985
986Creates a new `sourceMap` instance.
987
988`payload` is an object with keys matching the [Source map v3 format][]:
989
990* `file`: {string}
991* `version`: {number}
992* `sources`: {string\[]}
993* `sourcesContent`: {string\[]}
994* `names`: {string\[]}
995* `mappings`: {string}
996* `sourceRoot`: {string}
997
998#### `sourceMap.payload`
999
1000* Returns: {Object}
1001
1002Getter for the payload used to construct the [`SourceMap`][] instance.
1003
1004#### `sourceMap.findEntry(lineOffset, columnOffset)`
1005
1006* `lineOffset` {number} The zero-indexed line number offset in
1007  the generated source
1008* `columnOffset` {number} The zero-indexed column number offset
1009  in the generated source
1010* Returns: {Object}
1011
1012Given a line offset and column offset in the generated source
1013file, returns an object representing the SourceMap range in the
1014original file if found, or an empty object if not.
1015
1016The object returned contains the following keys:
1017
1018* generatedLine: {number} The line offset of the start of the
1019  range in the generated source
1020* generatedColumn: {number} The column offset of start of the
1021  range in the generated source
1022* originalSource: {string} The file name of the original source,
1023  as reported in the SourceMap
1024* originalLine: {number} The line offset of the start of the
1025  range in the original source
1026* originalColumn: {number} The column offset of start of the
1027  range in the original source
1028* name: {string}
1029
1030The returned value represents the raw range as it appears in the
1031SourceMap, based on zero-indexed offsets, _not_ 1-indexed line and
1032column numbers as they appear in Error messages and CallSite
1033objects.
1034
1035To get the corresponding 1-indexed line and column numbers from a
1036lineNumber and columnNumber as they are reported by Error stacks
1037and CallSite objects, use `sourceMap.findOrigin(lineNumber,
1038columnNumber)`
1039
1040#### `sourceMap.findOrigin(lineNumber, columnNumber)`
1041
1042* `lineNumber` {number} The 1-indexed line number of the call
1043  site in the generated source
1044* `columnOffset` {number} The 1-indexed column number
1045  of the call site in the generated source
1046* Returns: {Object}
1047
1048Given a 1-indexed lineNumber and columnNumber from a call site in
1049the generated source, find the corresponding call site location
1050in the original source.
1051
1052If the lineNumber and columnNumber provided are not found in any
1053source map, then an empty object is returned.  Otherwise, the
1054returned object contains the following keys:
1055
1056* name: {string | undefined} The name of the range in the
1057  source map, if one was provided
1058* fileName: {string} The file name of the original source, as
1059  reported in the SourceMap
1060* lineNumber: {number} The 1-indexed lineNumber of the
1061  corresponding call site in the original source
1062* columnNumber: {number} The 1-indexed columnNumber of the
1063  corresponding call site in the original source
1064
1065[CommonJS]: modules.md
1066[Conditional exports]: packages.md#conditional-exports
1067[Customization hooks]: #customization-hooks
1068[ES Modules]: esm.md
1069[HTTPS and HTTP imports]: esm.md#https-and-http-imports
1070[Source map v3 format]: https://sourcemaps.info/spec.html#h.mofvlxcwqzej
1071[`"exports"`]: packages.md#exports
1072[`--enable-source-maps`]: cli.md#--enable-source-maps
1073[`ArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
1074[`NODE_V8_COVERAGE=dir`]: cli.md#node_v8_coveragedir
1075[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
1076[`SourceMap`]: #class-modulesourcemap
1077[`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
1078[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
1079[`initialize`]: #initialize
1080[`module`]: modules.md#the-module-object
1081[`port.ref()`]: worker_threads.md#portref
1082[`port.unref()`]: worker_threads.md#portunref
1083[`register`]: #moduleregisterspecifier-parenturl-options
1084[`string`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
1085[`util.TextDecoder`]: util.md#class-utiltextdecoder
1086[hooks]: #customization-hooks
1087[load hook]: #loadurl-context-nextload
1088[module wrapper]: modules.md#the-module-wrapper
1089[realm]: https://tc39.es/ecma262/#realm
1090[source map include directives]: https://sourcemaps.info/spec.html#h.lmz475t4mvbx
1091[transferrable objects]: worker_threads.md#portpostmessagevalue-transferlist
1092