- 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 v18.20.0 文档
- Node.js v18.20.0
- 目录
-
导航
- 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
的实例交互时提供通用实用方法,module
变量经常出现在 CommonJS 模块中。通过 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.
此上下文中的 module
与 模块封装器 提供的对象不同。要访问它,需要 Module
模块:
¥module
in this context isn't the same object that's provided
by the module wrapper. To access it, require the 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)
#
-
filename
<string> | <URL> 用于构造 require 函数的文件名。必须是文件网址对象、文件网址字符串、或绝对路径字符串。¥
filename
<string> | <URL> Filename to be used to construct the require function. Must be a file URL object, file URL string, or absolute path string. -
返回:<require> require 函数
¥Returns: <require> Require function
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)
#
-
moduleName
<string> 模块名称¥
moduleName
<string> name of the module -
返回:<boolean> 如果模块是内置的,则返回 true,否则返回 false
¥Returns: <boolean> returns true if the module is builtin else returns false
import { isBuiltin } from 'node:module';
isBuiltin('node:fs'); // true
isBuiltin('fs'); // true
isBuiltin('wss'); // false
module.register(specifier[, parentURL][, options])
#
¥Stability: 1.1 - Active development
-
specifier
<string> | <URL> 需要注册的定制钩子;这应该与传递给import()
的字符串相同,但如果它是相对的,则它是相对于parentURL
解析的。¥
specifier
<string> | <URL> Customization hooks to be registered; this should be the same string that would be passed toimport()
, except that if it is relative, it is resolved relative toparentURL
. -
parentURL
<string> | <URL> 如果你想要相对于基本 URL(例如import.meta.url
)解析specifier
,你可以在此处传递该 URL。默认值:'data:'
¥
parentURL
<string> | <URL> If you want to resolvespecifier
relative to a base URL, such asimport.meta.url
, you can pass that URL here. Default:'data:'
-
options
<Object>-
data
<any> 传递到initialize
钩子的任何任意的、可克隆的 JavaScript 值。¥
data
<any> Any arbitrary, cloneable JavaScript value to pass into theinitialize
hook. -
transferList
<Object[]> 可转换对象 要传递到initialize
钩子中。¥
transferList
<Object[]> transferrable objects to be passed into theinitialize
hook.
-
注册一个导出 钩子 的模块,用于自定义 Node.js 模块解析和加载行为。参见 定制钩子。
¥Register a module that exports hooks that customize Node.js module resolution and loading behavior. See Customization hooks.
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
¥Stability: 1.1 - Active development
启用#
¥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
)引用的任何模块运行。
¥my-app.js
can also be CommonJS. Customization hooks will run for any
modules that it references via import
(and optionally 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('./first.mjs', import.meta.url);
register('./second.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('./first.mjs', parentURL);
register('./second.mjs', parentURL);
import('./my-app.mjs');
在这个例子中,注册的钩子将形成链。如果 first.mjs
和 second.mjs
都定义了 resolve
钩子,则两者都将按照它们注册的顺序被调用。这同样适用于所有其他钩子。
¥In this example, the registered hooks will form chains. If both first.mjs
and
second.mjs
define a resolve
hook, both will be called, in the order they
were registered. The same applies to all the other hooks.
注册的钩子也会影响 register
本身。在此示例中,second.mjs
将根据 first.mjs
注册的钩子进行解析和加载。这允许用非 JavaScript 语言编写钩子之类的事情,只要早期注册的加载器是转换为 JavaScript 的加载器即可。
¥The registered hooks also affect register
itself. In this example,
second.mjs
will be resolved and loaded per the hooks registered by
first.mjs
. This allows for things like writing hooks in non-JavaScript
languages, so long as an earlier registered loader is one that transpiles 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);
});
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);
});
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.
}
钩子是链的一部分,即使该链仅由一个自定义(用户提供的)钩子和始终存在的默认钩子组成。钩子函数嵌套:每个都必须始终返回一个普通对象,并且链接是由于每个函数调用 next<hookName>()
而发生的,next<hookName>()
是对后续加载程序钩子的引用。
¥Hooks are part of a chain, even if that chain consists of only one custom
(user-provided) hook and the default hook, which is always present. Hook
functions nest: each one must always return a plain object, and chaining happens
as a result of each function calling next<hookName>()
, which is a reference to
the subsequent loader's hook.
返回缺少必需属性的值的钩子会触发异常。没有调用 next<hookName>()
和没有返回 shortCircuit: true
就返回的钩子也会触发异常。这些错误有助于防止链中的意外中断。从钩子返回 shortCircuit: true
表示该链有意在你的钩子处结束。
¥A hook that returns a value lacking a required property triggers an exception. A
hook that returns without calling next<hookName>()
and without returning
shortCircuit: true
also triggers an exception. These errors are to help
prevent unintentional breaks in the chain. Return shortCircuit: true
from a
hook to signal that the chain is intentionally ending at your hook.
钩子在单独的线程中运行,与运行应用代码的主线程隔离。这意味着它是一个不同的 realm。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()
#
¥Stability: 1.1 - Active development
-
data
<any> 数据来自register(loader, import.meta.url, { data })
。¥
data
<any> The data fromregister(loader, import.meta.url, { data })
.
initialize
hook 提供了一种定义自定义函数的方法,该函数在 hooks 模块初始化时在 hooks 线程中运行。当 hooks 模块通过 register
注册时,就会进行初始化。
¥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');
});
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');
});
register('./path-to-my-hooks.js', {
parentURL: pathToFileURL(__filename),
data: { number: 1, port: port2 },
transferList: [port2],
});
resolve(specifier, context, nextResolve)
#
¥Stability: 1.2 - Release candidate
-
specifier
<string> -
context
<Object>-
conditions
<string[]> 相关package.json
的导出条件¥
conditions
<string[]> Export conditions of the relevantpackage.json
-
importAttributes
<Object> 一个对象,其键值对表示要导入的模块的属性¥
importAttributes
<Object> An object whose key-value pairs represent the attributes for the module to import -
parentURL
<string> | <undefined> 导入此模块的模块,如果这是 Node.js 入口点,则为未定义¥
parentURL
<string> | <undefined> The module importing this one, or undefined if this is the Node.js entry point
-
-
nextResolve
<Function> 链中后续的resolve
钩子,或者用户提供的最后一个resolve
钩子之后的 Node.js 默认resolve
钩子¥
nextResolve
<Function> The subsequentresolve
hook in the chain, or the Node.js defaultresolve
hook after the last user-suppliedresolve
hook -
¥Returns: <Object> | <Promise>
-
format
<string> | <null> | <undefined> 加载钩子的提示(可能会被忽略)'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'
¥
format
<string> | <null> | <undefined> A hint to the load hook (it might be ignored)'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'
-
importAttributes
<Object> | <undefined> 缓存模块时要使用的导入属性(可选;如果排除,将使用输入)¥
importAttributes
<Object> | <undefined> The import attributes to use when caching the module (optional; if excluded the input will be used) -
shortCircuit
<undefined> | <boolean> 此钩子打算终止resolve
钩子链的信号。默认值:false
¥
shortCircuit
<undefined> | <boolean> A signal that this hook intends to terminate the chain ofresolve
hooks. Default:false
-
url
<string> 此输入解析到的绝对 URL¥
url
<string> The absolute URL to which this input resolves
-
警告 尽管支持返回 Promise 和异步函数,但对
resolve
的调用可能会阻塞主线程,从而影响性能。¥Despite support for returning promises and async functions, calls to
resolve
may block the main thread which can impact performance.
resolve
钩子链负责告诉 Node.js 在哪里查找以及如何缓存给定的 import
语句或表达式,或 require
调用。它可以选择返回一种格式(例如 'module'
)作为 load
钩子的提示。如果指定了格式,load
钩子最终负责提供最终的 format
值(并且可以随意忽略 resolve
提供的提示);如果 resolve
提供 format
,则需要自定义 load
钩子,即使只是将值传递给 Node.js 默认的 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.
当前的 包导出条件 总是在传入钩子的 context.conditions
数组中。为了保证在调用 defaultResolve
时默认的 Node.js 模块说明符解析行为,传递给它的 context.conditions
数组必须包括最初传递到 resolve
钩子的 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)
#
¥Stability: 1.2 - Release candidate
-
url
<string>resolve
链返回的 URL¥
url
<string> The URL returned by theresolve
chain -
context
<Object>-
conditions
<string[]> 相关package.json
的导出条件¥
conditions
<string[]> Export conditions of the relevantpackage.json
-
format
<string> | <null> | <undefined>resolve
钩子链可选提供的格式¥
format
<string> | <null> | <undefined> The format optionally supplied by theresolve
hook chain -
importAttributes
<Object>
-
-
nextLoad
<Function> 链中后续的load
钩子,或者用户提供的最后一个load
钩子之后的 Node.js 默认load
钩子¥
nextLoad
<Function> The subsequentload
hook in the chain, or the Node.js defaultload
hook after the last user-suppliedload
hook -
返回:<Object>
¥Returns: <Object>
-
format
<string> -
shortCircuit
<undefined> | <boolean> 此钩子打算终止resolve
钩子链的信号。默认值:false
¥
shortCircuit
<undefined> | <boolean> A signal that this hook intends to terminate the chain ofresolve
hooks. Default:false
-
source
<string> | <ArrayBuffer> | <TypedArray> Node.js 评估的来源¥
source
<string> | <ArrayBuffer> | <TypedArray> The source for Node.js to evaluate
-
load
钩子提供了一种方式来定义确定网址应如何解释、检索、以及解析的自定义方法。它还负责验证导入断言。
¥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 模块 | 不适用 |
'json' | 加载 JSON 文件 | { string , ArrayBuffer , TypedArray } |
'module' | 加载 ES 模块 | { string , ArrayBuffer , TypedArray } |
'wasm' | 加载 WebAssembly 模块 | { ArrayBuffer , TypedArray } |
source
的值对于类型 'builtin'
被忽略,因为目前无法替换 Node.js 内置(核心)模块的值。对于类型 'commonjs'
,source
的值被忽略,因为 CommonJS 模块加载器没有为 ES 模块加载器提供覆盖 CommonJS 模块返回值 的机制。这个限制将来可能会被克服。
¥The value of source
is ignored for type 'builtin'
because currently it is
not possible to replace the value of a Node.js builtin (core) module. The value
of source
is ignored for type 'commonjs'
because the CommonJS module loader
does not provide a mechanism for the ES module loader to override the
CommonJS module return value. This limitation
might be overcome in the future.
警告:ESM
load
钩子和来自 CommonJS 模块的命名空间导出不兼容。尝试将它们一起使用将导致导入的对象为空。这可能会在未来得到解决。¥Warning: The ESM
load
hook and namespaced exports from CommonJS modules are incompatible. Attempting to use them together will result in an empty object from the import. This may be addressed in the future.
这些类型都对应于 ECMAScript 中定义的类。
¥These types all correspond to classes defined in ECMAScript.
-
特定的
ArrayBuffer
对象是SharedArrayBuffer
。¥The specific
ArrayBuffer
object is aSharedArrayBuffer
. -
特定的
TypedArray
对象是Uint8Array
。¥The specific
TypedArray
object is aUint8Array
.
如果基于文本的格式(即 '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()
#
¥Stability: 1.0 - Early development
警告:该钩子将在未来版本中删除。改用
initialize
。当 hooks 模块具有initialize
导出时,globalPreload
将被忽略。¥Warning: This hook will be removed in a future version. Use
initialize
instead. When a hooks module has aninitialize
export,globalPreload
will be ignored.
-
context
<Object> 辅助预加载代码的信息¥
context
<Object> Information to assist the preload codeport
<MessagePort>
-
返回:<string> 应用启动前运行的代码
¥Returns: <string> Code to run before application startup
有时可能需要在应用运行所在的同一全局作用域内运行一些代码。此钩子允许返回在启动时作为宽松模式脚本运行的字符串。
¥Sometimes it might be necessary to run some code inside of the same global scope that the application runs in. This hook allows the return of a string that is run as a sloppy-mode script on startup.
类似于 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.on('message', (msg) => {
port.postMessage(msg);
});
return `\
port.postMessage('console.log("I went to the hook and back");');
port.on('message', (msg) => {
eval(msg);
});
`;
}
示例#
¥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
可以使用 load
钩子 将 Node.js 无法理解的格式的源代码转换为 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);
});
// Ff package.json existed and contained a `type` field with a value, voila
if (type) return type;
// Otherwise, (if not at the root) continue checking the next directory up
// If at the root, stop and return false
return dir.length > 1 && getPackageType(resolvePath(dir, '..'));
}
# 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
¥Stability: 1 - Experimental
与源映射缓存交互的助手。当启用源映射解析并且在模块的页脚中找到 源映射包含指令 时,将填充此缓存。
¥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.
要启用源映射解析,则 Node.js 必须使用标志 --enable-source-maps
运行、或者通过设置 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
。¥Returns: <module.SourceMap> | <undefined> Returns
module.SourceMap
if a source map is found,undefined
otherwise.
path
是文件的解析路径,应为其获取相应的源映射。
¥path
is the resolved path for the file for which a corresponding source map
should be fetched.
类:module.SourceMap
#
¥Class: module.SourceMap
new SourceMap(payload)
#
payload
<Object>
创建新的 sourceMap
实例。
¥Creates a new sourceMap
instance.
payload
是一个对象,其键与 源映射 v3 格式 匹配:
¥payload
is an object with keys matching the Source map v3 format:
-
file
:<string> -
version
:<number> -
sources
:<string[]> -
sourcesContent
:<string[]> -
names
:<string[]> -
mappings
:<string> -
sourceRoot
:<string>
sourceMap.payload
#
用于构造 SourceMap
实例的有效负载的获取器。
¥Getter for the payload used to construct the SourceMap
instance.
sourceMap.findEntry(lineOffset, columnOffset)
#
-
lineOffset
<number> 生成的源中的零索引行号偏移量¥
lineOffset
<number> The zero-indexed line number offset in the generated source -
columnOffset
<number> 生成的源中的零索引列号偏移量¥
columnOffset
<number> The zero-indexed column number offset in the generated source -
返回:<Object>
¥Returns: <Object>
给定生成的源文件中的行偏移量和列偏移量,如果找到,则返回表示原始文件中的 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:
-
生成行:<number> 生成的源中范围开始的行偏移量
¥generatedLine: <number> The line offset of the start of the range in the generated source
-
生成的列:<number> 生成的源中范围开始的列偏移量
¥generatedColumn: <number> The column offset of start of the range in the generated source
-
原文出处:<string> 原始源的文件名,如 SourceMap 中报告的那样
¥originalSource: <string> The file name of the original source, as reported in the SourceMap
-
原行:<number> 原始源中范围开始的行偏移量
¥originalLine: <number> The line offset of the start of the range in the original source
-
原文栏目:<number> 原始源中范围开始的列偏移量
¥originalColumn: <number> The column offset of start of the range in the original source
-
名称:<string>
¥name: <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.
要从错误堆栈和 CallSite 对象报告的行号和列号中获取相应的 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)
#
-
lineNumber
<number> 生成的源中调用站点的 1 索引行号¥
lineNumber
<number> The 1-indexed line number of the call site in the generated source -
columnOffset
<number> 生成源中调用站点的 1 索引列号¥
columnOffset
<number> The 1-indexed column number of the call site in the generated source -
返回:<Object>
¥Returns: <Object>
给定生成源中调用站点的 1 索引行号和列号,在原始源中查找相应的调用站点位置。
¥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.
如果在任何源映射中都找不到提供的行号和列号,则返回空对象。否则,返回的对象包含以下键:
¥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> 源映射中范围的名称(如果提供)
¥name: <string> | <undefined> The name of the range in the source map, if one was provided
-
文件名:<string> 原始源的文件名,如 SourceMap 中报告的那样
¥fileName: <string> The file name of the original source, as reported in the SourceMap
-
调用号码:<number> 原始源中相应调用站点的 1 索引行编号
¥lineNumber: <number> The 1-indexed lineNumber of the corresponding call site in the original source
-
栏目编号:<number> 原始源中相应调用站点的 1 索引列编号
¥columnNumber: <number> The 1-indexed columnNumber of the corresponding call site in the original source