- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- Corepack 核心包
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
Node.js v20.20.6 文档
- Node.js v20.20.6
- 目录
-
导航
- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- Corepack 核心包
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- 其他版本
模块:node:module API#>
【Modules: node:module API】
Module 对象#>
【The Module object】
在与 Module 实例交互时提供通用实用方法,这是在 CommonJS 模块中经常看到的 module 变量。可以通过 import 'node:module' 或 require('node:module') 访问。
【Provides general utility methods when interacting with instances of
Module, the module variable often seen in CommonJS modules. Accessed
via import 'node:module' or require('node:module').】
module.builtinModules#>
Node.js 提供的所有模块名称列表。可用于验证某个模块是否由第三方维护。
【A list of the names of all modules provided by Node.js. Can be used to verify if a module is maintained by a third party or not.】
注意:列表中不包含像 node:test 这样的 仅前缀模块。
【Note: the list doesn't contain prefix-only modules like node:test.】
在此上下文中,module 并不是 模块封装 提供的同一个对象。要访问它,需要引入 Module 模块:
// module.mjs
// In an ECMAScript module
import { builtinModules as builtin } from 'node:module';// module.cjs
// In a CommonJS module
const builtin = require('node:module').builtinModules;
module.createRequire(filename)#>
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');
module.isBuiltin(moduleName)#>
import { isBuiltin } from 'node:module';
isBuiltin('node:fs'); // true
isBuiltin('fs'); // true
isBuiltin('wss'); // false
module.register(specifier[, parentURL][, options])#>
specifier<string> | <URL> 要注册的自定义钩子;这应该与传递给import()的字符串相同,除非它是相对路径,相对路径将相对于parentURL解析。parentURL<string> | <URL> 如果你想要相对于一个基 URL(例如import.meta.url)解析specifier,你可以在这里传入该 URL。默认值:'data:'options<Object>parentURL<string> | <URL> 如果你想基于某个基础 URL(例如import.meta.url)解析specifier,可以在此处传递该 URL。如果parentURL作为第二个参数提供,则会忽略此属性。默认值:'data:'data<any> 任何可任意克隆的 JavaScript 值,用于传入initialize钩子。transferList<Object[]> 可转移对象 将传入initialize钩子。
注册一个模块,该模块导出 钩子,用于自定义 Node.js 模块的解析和加载行为。参见 自定义钩子。
【Register a module that exports hooks that customize Node.js module resolution and loading behavior. See Customization hooks.】
如果与 权限模型 一起使用此功能,则需要 --allow-worker。
【This feature requires --allow-worker if used with the Permission Model.】
module.syncBuiltinESMExports()#>
module.syncBuiltinESMExports() 方法会更新内置 ES 模块 的所有实时绑定,以匹配 CommonJS 导出的属性。它不会添加或删除 ES 模块 的导出名称。
【The module.syncBuiltinESMExports() method updates all the live bindings for
builtin ES Modules to match the properties of the CommonJS exports. It
does not add or remove exported names from the ES Modules.】
const fs = require('node:fs');
const assert = require('node:assert');
const { syncBuiltinESMExports } = require('node:module');
fs.readFile = newAPI;
delete fs.readFileSync;
function newAPI() {
// ...
}
fs.newAPI = newAPI;
syncBuiltinESMExports();
import('node:fs').then((esmFS) => {
// It syncs the existing readFile property with the new value
assert.strictEqual(esmFS.readFile, newAPI);
// readFileSync has been deleted from the required fs
assert.strictEqual('readFileSync' in fs, false);
// syncBuiltinESMExports() does not remove readFileSync from esmFS
assert.strictEqual('readFileSync' in esmFS, true);
// syncBuiltinESMExports() does not add names
assert.strictEqual(esmFS.newAPI, undefined);
});
定制钩子#>
【Customization Hooks】
启用#>
【Enabling】
模块解析和加载可以通过注册一个导出一组钩子的文件来自定义。这可以通过使用 node:module 中的 register 方法来完成,你可以在使用 --import 标志运行应用代码之前执行该方法:
【Module resolution and loading can be customized by registering a file which
exports a set of hooks. This can be done using the register method
from node:module, which you can run before your application code by
using the --import flag:】
node --import ./register-hooks.js ./my-app.js
// register-hooks.js
import { register } from 'node:module';
register('./hooks.mjs', import.meta.url);// register-hooks.js
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
register('./hooks.mjs', pathToFileURL(__filename));
传递给 --import 的文件也可以是依赖的导出文件:
【The file passed to --import can also be an export from a dependency:】
node --import some-package/register ./my-app.js
some-package 包含一个 "exports" 字段,用于定义 /register 导出,以映射到调用 register() 的文件,例如以下 register-hooks.js 示例。
【Where some-package has an "exports" field defining the /register
export to map to a file that calls register(), like the following register-hooks.js
example.】
使用 --import 可以确保在导入任何应用文件(包括应用的入口点)之前注册钩子。或者,可以从入口点调用 register,但对于任何在钩子注册后应运行的代码,必须使用动态 import():
【Using --import ensures that the hooks are registered before any application
files are imported, including the entry point of the application. Alternatively,
register can be called from the entry point, but dynamic import() must be
used for any code that should be run after the hooks are registered:】
import { register } from 'node:module';
register('http-to-https', import.meta.url);
// Because this is a dynamic `import()`, the `http-to-https` hooks will run
// to handle `./my-app.js` and any other files it imports or requires.
await import('./my-app.js');const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
register('http-to-https', pathToFileURL(__filename));
// Because this is a dynamic `import()`, the `http-to-https` hooks will run
// to handle `./my-app.js` and any other files it imports or requires.
import('./my-app.js');
在这个例子中,我们正在注册 http-to-https 钩子,但它们只会对随后导入的模块可用——在本例中是 my-app.js 以及它通过 import(可选地通过 require)引用的任何内容。如果 import('./my-app.js') 替换为静态的 import './my-app.js',那么应用将在 http-to-https 钩子注册之前就已 加载。这是由于 ES 模块规范,静态导入会先从树的叶节点开始求值,然后回到主干。在 my-app.js 中可能存在静态导入,这些导入在 my-app.js 动态导入之前不会被求值。
【In this example, we are registering the http-to-https hooks, but they will
only be available for subsequently imported modules—in this case, my-app.js
and anything it references via import (and optionally require). If the
import('./my-app.js') had instead been a static import './my-app.js', the
app would have already been loaded before the http-to-https hooks were
registered. This due to the ES modules specification, where static imports are
evaluated from the leaves of the tree first, then back to the trunk. There can
be static imports within my-app.js, which will not be evaluated until
my-app.js is dynamically imported.】
my-app.js 也可以是 CommonJS 模块。自定义钩子会针对它通过 import(可选的 require)引用的任何模块执行。
最后,如果你只是想在应用运行前注册钩子,并且不想为此创建一个单独的文件,你可以将一个 data: URL 传递给 --import:
【Finally, if all you want to do is register hooks before your app runs and you
don't want to create a separate file for that purpose, you can pass a data:
URL to --import:】
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("http-to-https", pathToFileURL("./"));' ./my-app.js
链接#>
【Chaining】
可以多次调用 register:
【It's possible to call register more than once:】
// entrypoint.mjs
import { register } from 'node:module';
register('./foo.mjs', import.meta.url);
register('./bar.mjs', import.meta.url);
await import('./my-app.mjs');// entrypoint.cjs
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const parentURL = pathToFileURL(__filename);
register('./foo.mjs', parentURL);
register('./bar.mjs', parentURL);
import('./my-app.mjs');
在这个例子中,注册的钩子将形成链。这些链以后进先出(LIFO)的顺序运行。如果 foo.mjs 和 bar.mjs 都定义了 resolve 钩子,它们将按如下方式调用(注意从右到左):
node 默认 ← ./foo.mjs ← ./bar.mjs
(从 ./bar.mjs 开始,然后是 ./foo.mjs,最后是 Node.js 默认)。
其他所有钩子也同样适用。
【In this example, the registered hooks will form chains. These chains run
last-in, first out (LIFO). If both foo.mjs and bar.mjs define a resolve
hook, they will be called like so (note the right-to-left):
node's default ← ./foo.mjs ← ./bar.mjs
(starting with ./bar.mjs, then ./foo.mjs, then the Node.js default).
The same applies to all the other hooks.】
已注册的钩子也会影响 register 本身。在这个例子中,bar.mjs 将通过 foo.mjs 注册的钩子来解析和加载(因为 foo 的钩子已经被添加到链中)。这允许像使用非 JavaScript 语言编写钩子这样的操作,只要先前注册的钩子能够转译成 JavaScript。
【The registered hooks also affect register itself. In this example,
bar.mjs will be resolved and loaded via the hooks registered by foo.mjs
(because foo's hooks will have already been added to the chain). This allows
for things like writing hooks in non-JavaScript languages, so long as
earlier registered hooks transpile into JavaScript.】
register 方法不能在定义钩子的模块内部调用。
【The register method cannot be called from within the module that defines the
hooks.】
与模块定制钩子通信#>
【Communication with module customization hooks】
模块自定义钩子在专用线程上运行,独立于运行应用代码的主线程。这意味着修改全局变量不会影响其他线程,必须使用消息通道在各线程之间进行通信。
【Module customization hooks run on a dedicated thread, separate from the main thread that runs application code. This means mutating global variables won't affect the other thread(s), and message channels must be used to communicate between the threads.】
register 方法可以用来向 initialize 钩子传递数据。传递给钩子的数据可能包括可转移的对象,例如端口。
【The register method can be used to pass data to an initialize hook. The
data passed to the hook may include transferrable objects like ports.】
import { register } from 'node:module';
import { MessageChannel } from 'node:worker_threads';
// This example demonstrates how a message channel can be used to
// communicate with the hooks, by sending `port2` to the hooks.
const { port1, port2 } = new MessageChannel();
port1.on('message', (msg) => {
console.log(msg);
});
port1.unref();
register('./my-hooks.mjs', {
parentURL: import.meta.url,
data: { number: 1, port: port2 },
transferList: [port2],
});const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const { MessageChannel } = require('node:worker_threads');
// This example showcases how a message channel can be used to
// communicate with the hooks, by sending `port2` to the hooks.
const { port1, port2 } = new MessageChannel();
port1.on('message', (msg) => {
console.log(msg);
});
port1.unref();
register('./my-hooks.mjs', {
parentURL: pathToFileURL(__filename),
data: { number: 1, port: port2 },
transferList: [port2],
});
钩子#>
【Hooks】
register 方法可用于注册导出一组钩子的模块。钩子是由 Node.js 调用的函数,用于自定义模块解析和加载过程。导出的函数必须具有特定的名称和签名,并且必须以命名导出的形式导出。
【The register method can be used to register a module that exports a set of
hooks. The hooks are functions that are called by Node.js to customize the
module resolution and loading process. The exported functions must have specific
names and signatures, and they must be exported as named exports.】
export async function initialize({ number, port }) {
// Receives data from `register`.
}
export async function resolve(specifier, context, nextResolve) {
// Take an `import` or `require` specifier and resolve it to a URL.
}
export async function load(url, context, nextLoad) {
// Take a resolved URL and return the source code to be evaluated.
}
Hooks 是 链条 的一部分,即使该链只包含一个自定义(用户提供的)hook 和始终存在的默认 hook。Hook 函数是嵌套的:每个函数必须总是返回一个普通对象,而链式调用的发生是因为每个函数调用了 next<hookName>(),它是对后续加载器的 hook 的引用(按后进先出顺序)。
返回缺少必需属性的值的钩子会触发异常。一个钩子如果没有调用 next<hookName>() 并且也没有返回 shortCircuit: true,同样会触发异常。这些错误是为了帮助防止链条意外中断。从钩子返回 shortCircuit: true 表示链条在你的钩子处故意结束。
Hooks 在单独的线程中运行,与运行应用代码的主线程隔离。这意味着它是一个不同的 字段。主线程可以在任何时候终止 hooks 线程,所以不要依赖异步操作(如 console.log)来完成。
【Hooks are run in a separate thread, isolated from the main thread where
application code runs. That means it is a different 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()#>
data<any> 来自register(loader, import.meta.url, { data })的数据。
initialize 钩子提供了一种方式,可以定义一个自定义函数,当 hooks 模块初始化时,该函数将在 hooks 线程中运行。当通过 register 注册 hooks 模块时,初始化就会发生。
【The initialize hook provides a way to define a custom function that runs in
the hooks thread when the hooks module is initialized. Initialization happens
when the hooks module is registered via register.】
此钩子可以接收来自 register 调用的数据,包括端口和其他可传输对象。initialize 的返回值可以是一个 <Promise>,在这种情况下,会在主应用线程执行恢复之前等待它完成。
【This hook can receive data from a register invocation, including
ports and other transferrable objects. The return value of initialize can be a
<Promise>, in which case it will be awaited before the main application thread
execution resumes.】
模块定制代码:
【Module customization code:】
// path-to-my-hooks.js
export async function initialize({ number, port }) {
port.postMessage(`increment: ${number + 1}`);
}
调用者代码:
【Caller code:】
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 hooks running on the hooks
// thread, by sending `port2` to the `initialize` hook.
const { port1, port2 } = new MessageChannel();
port1.on('message', (msg) => {
assert.strictEqual(msg, 'increment: 2');
});
port1.unref();
register('./path-to-my-hooks.js', {
parentURL: import.meta.url,
data: { number: 1, port: port2 },
transferList: [port2],
});const assert = require('node:assert');
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const { MessageChannel } = require('node:worker_threads');
// This example showcases how a message channel can be used to communicate
// between the main (application) thread and the hooks running on the hooks
// thread, by sending `port2` to the `initialize` hook.
const { port1, port2 } = new MessageChannel();
port1.on('message', (msg) => {
assert.strictEqual(msg, 'increment: 2');
});
port1.unref();
register('./path-to-my-hooks.js', {
parentURL: pathToFileURL(__filename),
data: { number: 1, port: port2 },
transferList: [port2],
});
resolve(specifier, context, nextResolve)#>
specifier<string>context<Object>conditions<string[]> 导出相关package.json的条件importAttributes<Object> 一个对象,其键值对表示要导入模块的属性parentURL<string> | <undefined> 导入此模块的模块,或者如果这是 Node.js 的入口点,则为 undefined
nextResolve<Function> 链中后续的resolve钩子,或在最后一个用户提供的resolve钩子之后的 Node.js 默认resolve钩子- 返回:<Object> | <Promise>
format<string> | <null> | <undefined> 对加载钩子的提示(可能会被忽略)'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'importAttributes<Object> | <undefined> 缓存模块时使用的导入属性(可选;如果省略,则使用输入)shortCircuit<undefined> | <boolean> 一个信号,表示此钩子打算终止resolve钩子的链条。默认值:falseurl<string> 此输入解析后的绝对 URL
警告 尽管支持返回 Promise 和异步函数,但调用
resolve可能会阻塞主线程,从而影响性能。
resolve 钩子链负责告诉 Node.js 在哪里查找以及如何缓存给定的 import 语句或表达式,或 require 调用。它可以选择性地返回一个格式(例如 'module')作为 load 钩子的提示。如果指定了格式,load 钩子最终负责提供最终的 format 值(并且可以忽略 resolve 提供的提示);如果 resolve 提供了 format,即使只是为了将该值传递给 Node.js 的默认 load 钩子,也需要自定义 load 钩子。
【The resolve hook chain is responsible for telling Node.js where to find and
how to cache a given import statement or expression, or require call. It can
optionally return a 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.】
导入类型属性是将加载的模块保存到内部模块缓存中的缓存键的一部分。如果模块应以不同于源代码中存在的属性进行缓存,resolve 钩子负责返回一个 importAttributes 对象。
【Import type attributes are part of the cache key for saving loaded modules into
the internal module cache. The resolve hook is responsible for returning an
importAttributes object if the module should be cached with different
attributes than were present in the source code.】
context 中的 conditions 属性是一个适用于此解析请求的 包导出条件 条件数组。它们可以用于在其他地方查找条件映射,或者在调用默认解析逻辑时修改列表。
【The conditions property in context is an array of conditions for
package exports conditions 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.】
当前的 包导出条件 总是包含在传入 hook 的 context.conditions 数组中。为了在调用 defaultResolve 时保证 默认的 Node.js 模块指定符解析行为,传递给它的 context.conditions 数组 必须 包含最初传入 resolve hook 的 context.conditions 数组的 所有 元素。
【The current package exports conditions 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.】
export async 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)#>
url<string>resolve链返回的 URLcontext<Object>conditions<string[]> 导出相关package.json的条件format<string> | <null> | <undefined> 由resolve钩子链可选提供的格式importAttributes<Object>
nextLoad<Function> 链中后续的load钩子,或在最后一个用户提供的load钩子之后的 Node.js 默认load钩子- 返回:<Object>
format<string>shortCircuit<undefined> | <boolean> 一个信号,表示此钩子打算终止load钩子的链条。默认值:falsesource<string> | <ArrayBuffer> | <TypedArray> Node.js 用于评估的源代码
load 钩子提供了一种定义自定义方法的方法,用于确定 URL 应该如何被解释、获取和解析。它还负责验证导入断言。
【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.】
format 的最终值必须是以下之一:
【The final value of format must be one of the following:】
format | 描述 | load 返回的 source 可接受类型 |
|---|---|---|
'builtin' | 加载 Node.js 内置模块 | 不适用 |
'commonjs' | 加载 Node.js CommonJS 模块 | { string, ArrayBuffer, TypedArray, null, undefined } |
'json' | 加载 JSON 文件 | { string, ArrayBuffer, TypedArray } |
'module' | 加载 ES 模块 | { string, ArrayBuffer, TypedArray } |
'wasm' | 加载 WebAssembly 模块 | { ArrayBuffer, TypedArray } |
source 的值会被忽略,因为对于 'builtin' 类型,目前无法替换 Node.js 内置(核心)模块的值。
【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.】
省略与提供 'commonjs' 的 source 会产生非常不同的效果:
【Omitting vs providing a source for 'commonjs' has very different effects:】
- 当提供
source时,该模块中的所有require调用将由 ESM 加载器处理,并使用已注册的resolve和load钩子;该模块中的所有require.resolve调用将由 ESM 加载器处理,并使用已注册的resolve钩子;只有部分 CommonJS API 可用(例如没有require.extensions、没有require.cache、没有require.resolve.paths),对 CommonJS 模块加载器的猴子补丁将不生效。 - 如果
source是未定义或null,它将由 CommonJS 模块加载器处理,并且require/require.resolve调用不会经过注册的钩子。对于 null 值的source,这种行为是暂时的——将来将不再支持 null 值的source。
当使用 --experimental-default-type=commonjs 运行 node 时,Node.js 内部的 load 实现(即 load 链中最后一个 hook 的 next 值)在 format 为 'commonjs' 时会返回 null 作为 source,以保持向后兼容。下面是一个示例 hook,它会选择使用非默认行为:
【When node is run with --experimental-default-type=commonjs, the Node.js
internal load implementation, which is the value of next for the
last hook in the load chain, returns null for source when format is
'commonjs' for backward compatibility. Here is an example hook that would
opt-in to using the non-default behavior:】
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;
}
警告:ESM 的
load钩子与来自 CommonJS 模块的命名导出不兼容。尝试将它们一起使用将导致从导入中得到一个空对象。未来可能会解决这个问题。
这些类型都对应于 ECMAScript 中定义的类。
- 特定的
ArrayBuffer对象是一个SharedArrayBuffer。 - 特定的
TypedArray对象是一个Uint8Array。
如果基于文本的格式(即 'json'、'module')的源值不是字符串,它将使用 util.TextDecoder 转换为字符串。
【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.】
load 钩子提供了一种方式来定义用于获取已解析 URL 源代码的自定义方法。这允许加载器在潜在情况下避免从磁盘读取文件。它也可以用于将无法识别的格式映射为受支持的格式,例如将 yaml 映射为 module。
【The load hook provides a way to define a custom method for retrieving the
source code of a resolved URL. 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.】
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 below).】
globalPreload()#>
警告: 此钩子将在未来版本中被移除。请使用
initialize代替。当 hooks 模块包含initialize导出时,globalPreload将被忽略。
context<Object> 用于辅助预加载代码的信息port<MessagePort>
- 返回:<string> 应用启动前要运行的代码
有时可能需要在应用运行的同一全局作用域中运行一些代码。这个钩子允许返回一个字符串,该字符串将在启动时作为宽松模式脚本运行。
【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.】
与 CommonJS 封装器的工作方式类似,代码在一个隐式函数作用域中运行。唯一的参数是一个类似 require 的函数,可用于加载内置模块,如 “fs”:getBuiltin(request: string)。
【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).】
如果代码需要更高级的 require 功能,它必须使用 module.createRequire() 自行构建 require。
【If the code needs more advanced require features, it has to construct
its own require using module.createRequire().】
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>');
// [...]
`;
}
另一个参数传递给预加载代码:port。这个参数可以作为钩子的参数使用,并且可以在钩子返回的源代码中使用。此功能已被移至 initialize 钩子。
【Another argument is provided to the preload code: port. This is available as a
parameter to the hook and inside of the source text returned by the hook. This
functionality has been moved to the initialize hook.】
必须小心,以正确调用 port.ref() 和 port.unref(),防止进程处于无法正常关闭的状态。
【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.】
/**
* This example has the application context send a message to the hook
* 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 hook and back");');
port.onmessage = (evt) => {
eval(evt.data);
};
`;
}
示例#>
【Examples】
各种模块自定义钩子可以结合使用,以实现对 Node.js 代码加载和执行行为的广泛定制。
【The various module customization hooks can be used together to accomplish wide-ranging customizations of the Node.js code loading and evaluation behaviors.】
从 HTTPS 导入#>
【Import from HTTPS】
在当前的 Node.js 中,以 https:// 开头的说明符是实验性的(参见 HTTPS 和 HTTP 导入)。
【In current Node.js, specifiers starting with https:// are experimental (see
HTTPS and HTTP imports).】
下面的钩子注册了钩子以启用对这种说明符的基本支持。虽然这看起来像是对 Node.js 核心功能的重大改进,但实际上使用这些钩子有很大的缺点:性能比从磁盘加载文件慢得多,没有缓存,也没有安全性。
【The hook 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 these hooks: performance is much slower than loading files from disk, there is no caching, and there is no security.】
// https-hooks.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);
}
// main.mjs
import { VERSION } from 'https://coffeescript.org/browser-compiler-modern/coffeescript.js';
console.log(VERSION);
使用前面的 hooks 模块,运行
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./https-hooks.mjs"));' ./main.mjs
会打印 main.mjs 中 URL 模块的当前 CoffeeScript 版本。
【With the preceding hooks module, running
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./https-hooks.mjs"));' ./main.mjs
prints the current version of CoffeeScript per the module at the URL in
main.mjs.】
转换#>
【Transpilation】
Node.js 无法理解的格式的源文件可以使用 load 钩子 转换为 JavaScript。
【Sources that are in formats Node.js doesn't understand can be converted into
JavaScript using the load hook.】
这比在运行 Node.js 之前转译源文件的性能要低;转译器钩子应该仅用于开发和测试目的。
【This is less performant than transpiling source files before running Node.js; transpiler hooks should only be used for development and testing purposes.】
// coffeescript-hooks.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 extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/;
export async function load(url, context, nextLoad) {
if (extensionsRegex.test(url)) {
// 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);
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 = coffeescript.compile(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);
});
// If package.json existed and contained a `type` field with a value, voilà
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, '..'));
}
# 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}"
# scream.coffee
export scream = (str) -> str.toUpperCase()
使用前面的 hooks 模块,运行
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.mjs"));' ./main.coffee
会导致 main.coffee 在从磁盘加载其源代码后但在 Node.js 执行之前被转换为 JavaScript;通过任何已加载文件的 import 语句引用的任何 .coffee、.litcoffee 或 .coffee.md 文件也会依此类推。
【With the preceding hooks module, running
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.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 maps】
前两个例子定义了 load 钩子。下面是一个 resolve 钩子的例子。这个钩子模块会读取一个 import-map.json 文件,该文件定义了哪些标识符需要重写为其他 URL(这是对“导入映射”规范中一个小子集的非常简单的实现)。
【The previous two examples defined load hooks. This is an example of a
resolve hook. This hooks module reads an import-map.json file that defines
which specifiers to override to other URLs (this is a very simplistic
implementation of a small subset of the "import maps" specification).】
// import-map-hooks.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);
}
有了这些文件:
【With these files:】
// main.js
import 'a-module';
// import-map.json
{
"imports": {
"a-module": "./some-module.js"
}
}
// some-module.js
console.log('some module!');
运行 node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./import-map-hooks.js"));' main.js 应该会打印 some module!。
【Running node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./import-map-hooks.js"));' main.js
should print some module!.】
源映射 v3 支持#>
【Source map v3 support】
用于与源映射缓存交互的辅助工具。当启用源映射解析并且在模块的页脚中找到 源映射包含指令 时,此缓存会被填充。
【Helpers for interacting with the source map cache. This cache is populated when source map parsing is enabled and source map include directives are found in a modules' footer.】
要启用源映射解析,必须使用标志 --enable-source-maps 运行 Node.js,或者通过设置 NODE_V8_COVERAGE=dir 启用代码覆盖率。
【To enable source map parsing, Node.js must be run with the flag
--enable-source-maps, or with code coverage enabled by setting
NODE_V8_COVERAGE=dir.】
// module.mjs
// In an ECMAScript module
import { findSourceMap, SourceMap } from 'node:module';// module.cjs
// In a CommonJS module
const { findSourceMap, SourceMap } = require('node:module');
module.findSourceMap(path)#>
path<string>- 返回值: <module.SourceMap> | <undefined> 如果找到源映射,则返回
module.SourceMap,否则返回undefined。
path 是文件的解析路径,应为该文件获取相应的源映射。
类:module.SourceMap#>
【Class: module.SourceMap】
new SourceMap(payload[, { lineLengths }])#>
payload<Object>lineLengths数字[]
创建一个新的 sourceMap 实例。
【Creates a new sourceMap instance.】
payload 是一个对象,其键与 源映射 v3 格式 匹配:
file: <string>version: <number>- “来源”:<string[]>
sourcesContent: 字符串数组[]- '名字':<string[]>
mappings: <string>sourceRoot: <string>
lineLengths 是一个可选数组,用于表示生成代码中每一行的长度。
sourceMap.payload#>
- 返回:<Object>
用于构造 SourceMap 实例的有效负载的获取方法。
【Getter for the payload used to construct the SourceMap instance.】
sourceMap.findEntry(lineOffset, columnOffset)#>
给定生成源文件中的行偏移和列偏移,如果找到,则返回表示原始文件中 SourceMap 范围的对象;如果未找到,则返回空对象。
【Given a line offset and column offset in the generated source file, returns an object representing the SourceMap range in the original file if found, or an empty object if not.】
返回的对象包含以下键:
【The object returned contains the following keys:】
- generatedLine: <number> 生成源中范围起始行的行偏移
- generatedColumn: <number> 在生成的源代码中范围起始位置的列偏移量
- originalSource: <string> 原始来源的文件名,如 SourceMap 中所示
- originalLine: <number> 原始源中范围起始行的行偏移
- originalColumn: <number> 原始源中范围起始的列偏移
- 名称: <string>
返回值表示在 SourceMap 中出现的原始范围,基于零索引偏移,而不是在错误信息和 CallSite 对象中显示的从 1 开始的行号和列号。
【The returned value represents the raw range as it appears in the SourceMap, based on zero-indexed offsets, not 1-indexed line and column numbers as they appear in Error messages and CallSite objects.】
要从 Error 堆栈和 CallSite 对象报告的 lineNumber 和 columnNumber 获取对应的从 1 开始的行号和列号,请使用 sourceMap.findOrigin(lineNumber, columnNumber)
【To get the corresponding 1-indexed line and column numbers from a
lineNumber and columnNumber as they are reported by Error stacks
and CallSite objects, use sourceMap.findOrigin(lineNumber, columnNumber)】
sourceMap.findOrigin(lineNumber, columnNumber)#>
给定生成源代码中调用位置的从 1 开始索引的 lineNumber 和 columnNumber,找到原始源代码中对应的调用位置。
【Given a 1-indexed lineNumber and columnNumber from a call site in
the generated source, find the corresponding call site location
in the original source.】
如果提供的 lineNumber 和 columnNumber 在任何源映射中都未找到,则返回一个空对象。否则,返回的对象包含以下键:
【If the lineNumber and columnNumber provided are not found in any
source map, then an empty object is returned. Otherwise, the
returned object contains the following keys:】
- 名称:<string> | <undefined> 源地图中范围的名称(如果提供了的话)
- fileName: <string> 原始来源的文件名,如 SourceMap 中所报告的
- lineNumber: <number> 原始源代码中对应调用位置的以1为起始的行号
- columnNumber: <number> 原始源代码中对应调用位置的以1为起始的列号