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