Node.js v24.0.0 文档
- Node.js v24.0.0
- 目录
-
索引
- 断言测试
- 异步上下文追踪
- 异步钩子
- Buffer
- C++ 插件
- 使用 Node-API 的 C/C++ 插件
- C++ 嵌入器 API
- 子进程
- 集群
- 命令行选项
- Console
- Crypto
- 调试器
- 已弃用的 API
- 诊断通道
- DNS
- Domain
- 错误
- 事件
- 文件系统
- 全局变量
- HTTP
- HTTP/2
- HTTPS
- Inspector
- 国际化
- 模块:CommonJS 模块
- 模块:ECMAScript 模块
- 模块:
node:module
API - 模块:包
- 模块:TypeScript
- Net
- OS
- Path
- 性能钩子
- 权限
- Process
- Punycode
- 查询字符串
- Readline
- REPL
- 报告
- 单个可执行应用程序
- SQLite
- Stream
- 字符串解码器
- 测试运行器
- 定时器
- TLS/SSL
- 跟踪事件
- TTY
- UDP/数据报
- URL
- Utilities
- V8
- VM
- WASI
- Web Crypto API
- Web Streams API
- Worker 线程
- Zlib
- 其他版本
- 选项
模块:ECMAScript 模块#
简介#
ECMAScript 模块是 官方标准格式,用于打包 JavaScript 代码以供重用。模块使用各种 import
和 export
语句定义。
以下 ES 模块示例导出一个函数
// addTwo.mjs
function addTwo(num) {
return num + 2;
}
export { addTwo };
以下 ES 模块示例从 addTwo.mjs
导入函数
// app.mjs
import { addTwo } from './addTwo.mjs';
// Prints: 6
console.log(addTwo(4));
Node.js 完全支持当前指定的 ECMAScript 模块,并提供它们与其原始模块格式 CommonJS 之间的互操作性。
启用#
Node.js 有两个模块系统:CommonJS 模块和 ECMAScript 模块。
作者可以告诉 Node.js 将 JavaScript 解释为 ES 模块,通过 .mjs
文件扩展名,package.json
"type"
字段,值为 "module"
,或者 --input-type
标志,值为 "module"
。 这些是代码打算作为 ES 模块运行的显式标记。
相反,作者可以显式地告诉 Node.js 将 JavaScript 解释为 CommonJS,通过 .cjs
文件扩展名,package.json
"type"
字段,值为 "commonjs"
,或者 --input-type
标志,值为 "commonjs"
。
当代码缺少任何模块系统的显式标记时,Node.js 将检查模块的源代码以查找 ES 模块语法。如果找到这样的语法,Node.js 将把代码作为 ES 模块运行;否则它将把模块作为 CommonJS 运行。有关更多详细信息,请参见 确定模块系统。
包#
此部分已移至 模块:包。
import
标识符#
术语#
import
语句的标识符是 from
关键字之后的字符串,例如 import { sep } from 'node:path'
中的 'node:path'
。 标识符也用于 export from
语句中,并作为 import()
表达式的参数。
有三种类型的标识符
-
相对标识符,例如
'./startup.js'
或'../config.mjs'
。 它们引用相对于导入文件位置的路径。 文件扩展名对于这些标识符始终是必需的。 -
裸标识符,例如
'some-package'
或'some-package/shuffle'
。 它们可以通过包名称引用包的主入口点,或者引用包内的特定功能模块,如示例中所示。 仅当包没有"exports"
字段时,才需要包含文件扩展名。 -
绝对标识符,例如
'file:///opt/nodejs/config.js'
。 它们直接和明确地引用完整路径。
裸标识符解析由 Node.js 模块解析和加载算法处理。 所有其他标识符解析始终仅使用标准相对 URL 解析语义进行解析。
与 CommonJS 中一样,除非包的 package.json
包含 "exports"
字段,否则可以通过将路径附加到包名称来访问包中的模块文件,在这种情况下,只能通过 "exports"
中定义的路径访问包中的文件。
有关适用于 Node.js 模块解析中裸标识符的这些包解析规则的详细信息,请参阅包文档。
强制文件扩展名#
使用 import
关键字解析相对或绝对标识符时,必须提供文件扩展名。 目录索引(例如 './startup/index.js'
)也必须完全指定。
此行为与 import
在浏览器环境中的行为方式相匹配,假设服务器已进行典型配置。
URL#
ES 模块被解析并缓存为 URL。 这意味着特殊字符必须 进行百分比编码,例如 #
用 %23
和 ?
用 %3F
。
支持 file:
、node:
和 data:
URL 方案。 除非使用 自定义 HTTPS 加载器,否则 Node.js 本机不支持像 'https://example.com/app.js'
这样的标识符。
file:
URL#
如果用于解析它们的 import
标识符具有不同的查询或片段,则模块将被多次加载。
import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1"
import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2"
卷根目录可以通过 /
、//
或 file:///
引用。 鉴于 URL 和路径解析之间的差异(例如百分比编码细节),建议在导入路径时使用 url.pathToFileURL。
data:
导入#
data:
URL 支持使用以下 MIME 类型进行导入
text/javascript
用于 ES 模块application/json
用于 JSONapplication/wasm
用于 Wasm
import 'data:text/javascript,console.log("hello!");';
import _ from 'data:application/json,"world!"' with { type: 'json' };
data:
URL 仅解析内置模块的裸标识符和绝对标识符。解析相对标识符不起作用,因为 data:
不是特殊方案。例如,尝试从 data:text/javascript,import "./foo";
加载 ./foo
无法解析,因为 data:
URL 没有相对解析的概念。
node:
导入#
支持使用 node:
URL 作为加载 Node.js 内置模块的替代方法。 此 URL 方案允许通过有效的绝对 URL 字符串引用内置模块。
import fs from 'node:fs/promises';
导入属性#
Import attributes (导入属性) 是一种模块导入语句的内联语法,用于传递模块标识符之外的更多信息。
import fooData from './foo.json' with { type: 'json' };
const { default: barData } =
await import('./bar.json', { with: { type: 'json' } });
Node.js 仅支持 type
属性,并支持以下值:
属性 type | 所需用于 |
---|---|
'json' | JSON 模块 |
导入 JSON 模块时,必须使用 type: 'json'
属性。
内置模块#
内置模块提供其公共 API 的命名导出。 同时也提供一个默认导出,它是 CommonJS exports 的值。 默认导出可用于(除其他外)修改命名导出。 内置模块的命名导出仅通过调用 module.syncBuiltinESMExports()
来更新。
import EventEmitter from 'node:events';
const e = new EventEmitter();
import { readFile } from 'node:fs';
readFile('./foo.txt', (err, source) => {
if (err) {
console.error(err);
} else {
console.log(source);
}
});
import fs, { readFileSync } from 'node:fs';
import { syncBuiltinESMExports } from 'node:module';
import { Buffer } from 'node:buffer';
fs.readFileSync = () => Buffer.from('Hello, ESM');
syncBuiltinESMExports();
fs.readFileSync === readFileSync;
import()
表达式#
CommonJS 和 ES 模块均支持 动态 import()
。 在 CommonJS 模块中,它可用于加载 ES 模块。
import.meta
#
import.meta
元属性是一个 Object
,其中包含以下属性。 它仅在 ES 模块中受支持。
import.meta.dirname
#
- <string> 当前模块的目录名。
这与 import.meta.filename
的 path.dirname()
相同。
注意:仅在
file:
模块上显示。
import.meta.filename
#
- <string> 当前模块的完整绝对路径和文件名,并解析符号链接。
这与 import.meta.url
的 url.fileURLToPath()
相同。
注意 只有本地模块支持此属性。 不使用
file:
协议的模块将不提供它。
import.meta.url
#
- <string> 模块的绝对
file:
URL。
这与浏览器中完全相同,提供当前模块文件的 URL。
这支持有用的模式,例如相对文件加载
import { readFileSync } from 'node:fs';
const buffer = readFileSync(new URL('./data.proto', import.meta.url));
import.meta.resolve(specifier)
#
import.meta.resolve
是一个模块相关的解析函数,其作用域限定于每个模块,并返回 URL 字符串。
const dependencyAsset = import.meta.resolve('component-lib/asset.css');
// file:///app/node_modules/component-lib/asset.css
import.meta.resolve('./dep.js');
// file:///app/dep.js
支持 Node.js 模块解析的所有功能。 依赖关系解析受包中允许的导出解析的约束。
注意事项:
- 这可能导致同步文件系统操作,这会像
require.resolve
一样影响性能。 - 此功能在自定义加载器中不可用(它会创建一个死锁)。
非标准 API:
使用 --experimental-import-meta-resolve
标志时,该函数接受第二个参数
与 CommonJS 的互操作性#
import
语句#
import
语句可以引用 ES 模块或 CommonJS 模块。 import
语句仅在 ES 模块中允许,但动态 import()
表达式在 CommonJS 中受支持,用于加载 ES 模块。
导入 CommonJS 模块时,module.exports
对象作为默认导出提供。 可以提供命名导出,通过静态分析作为一种便利,以实现更好的生态系统兼容性。
require
#
CommonJS 模块 require
目前仅支持加载同步 ES 模块(即不使用顶层 await
的 ES 模块)。
有关详细信息,请参见使用 require()
加载 ECMAScript 模块。
CommonJS 命名空间#
CommonJS 模块由一个 module.exports
对象组成,该对象可以是任何类型。
为了支持这一点,当从 ECMAScript 模块导入 CommonJS 时,将构造 CommonJS 模块的命名空间包装器,该包装器始终提供指向 CommonJS module.exports
值的 default
导出键。
此外,针对 CommonJS 模块的源文本执行启发式静态分析,以获得一个尽力而为的静态导出列表,以便从 module.exports
上的值在命名空间上提供。 这是必要的,因为必须在评估 CJS 模块之前构造这些命名空间。
这些 CommonJS 命名空间对象还将 default
导出作为 'module.exports'
命名导出提供,以便明确地指示它们在 CommonJS 中的表示形式使用此值,而不是命名空间值。 这反映了在 require(esm)
互操作支持中处理 'module.exports'
导出名称的语义。
导入 CommonJS 模块时,可以使用 ES 模块默认导入或其相应的语法糖可靠地导入它
import { default as cjs } from 'cjs';
// Identical to the above
import cjsSugar from 'cjs';
console.log(cjs);
console.log(cjs === cjsSugar);
// Prints:
// <module.exports>
// true
当使用 import * as m from 'cjs'
或动态导入时,可以直接观察到此模块命名空间异构对象
import * as m from 'cjs';
console.log(m);
console.log(m === await import('cjs'));
// Prints:
// [Module] { default: <module.exports>, 'module.exports': <module.exports> }
// true
为了与 JS 生态系统中现有用法更好地兼容,Node.js 还会尝试确定每个导入的 CommonJS 模块的 CommonJS 命名导出,以使用静态分析过程将它们作为单独的 ES 模块导出提供。
例如,考虑一个编写的 CommonJS 模块
// cjs.cjs
exports.name = 'exported';
前面的模块支持 ES 模块中的命名导入
import { name } from './cjs.cjs';
console.log(name);
// Prints: 'exported'
import cjs from './cjs.cjs';
console.log(cjs);
// Prints: { name: 'exported' }
import * as m from './cjs.cjs';
console.log(m);
// Prints:
// [Module] {
// default: { name: 'exported' },
// 'module.exports': { name: 'exported' },
// name: 'exported'
// }
从记录的模块命名空间异构对象的最后一个示例中可以看出,当导入模块时,name
导出是从 module.exports
对象中复制出来的,并直接设置在 ES 模块命名空间上。
对于这些命名导出,不会检测到实时绑定更新或添加到 module.exports
的新导出。
命名导出的检测基于常见的语法模式,但并非总是正确检测命名导出。 在这些情况下,使用上述默认导入形式可能是一个更好的选择。
命名导出检测涵盖了许多常见的导出模式、重新导出模式以及构建工具和转换编译器的输出。 有关已实现的准确语义,请参阅 cjs-module-lexer。
ES 模块和 CommonJS 之间的差异#
没有 require
、exports
或 module.exports
#
在大多数情况下,ES 模块 import
可用于加载 CommonJS 模块。
如果需要,可以使用 module.createRequire()
在 ES 模块中构造 require
函数。
没有 __filename
或 __dirname
#
这些 CommonJS 变量在 ES 模块中不可用。
__filename
和 __dirname
用例可以通过 import.meta.filename
和 import.meta.dirname
复制。
没有 require.resolve
#
可以通过 new URL('./local', import.meta.url)
处理相对解析。
对于完整的 require.resolve
替换,有 import.meta.resolve API。
或者,可以使用 module.createRequire()
。
没有 NODE_PATH
#
NODE_PATH
不是解析 import
标识符的一部分。 如果需要此行为,请使用符号链接。
没有 require.extensions
#
require.extensions
不被 import
使用。 模块自定义钩子可以提供替换。
JSON 模块#
JSON 文件可以通过 import
引用
import packageConfig from './package.json' with { type: 'json' };
with { type: 'json' }
语法是强制性的;参见 导入属性。
导入的 JSON 只公开一个 default
导出。不支持具名导出。在 CommonJS 缓存中创建一个缓存条目以避免重复。如果 JSON 模块已从同一路径导入,则在 CommonJS 中返回相同的对象。
Wasm 模块#
在 --experimental-wasm-modules
标志下,支持导入 WebAssembly 模块实例和 WebAssembly 源码阶段导入。
这两个集成都符合 WebAssembly 的 ES 模块集成提案。
实例导入允许将任何 .wasm
文件作为普通模块导入,进而支持它们的模块导入。
例如,一个包含以下内容的 index.js
:
import * as M from './library.wasm';
console.log(M);
在以下情况下执行:
node --experimental-wasm-modules index.mjs
将为 library.wasm
的实例化提供导出接口。
Wasm 源码阶段导入#
源码阶段导入提案允许使用 import source
关键字组合直接导入一个 WebAssembly.Module
对象,而不是获取一个已经用其依赖项实例化的模块实例。
当需要为 Wasm 进行自定义实例化,同时仍然通过 ES 模块集成解析和加载它时,这非常有用。
例如,要创建模块的多个实例,或将自定义导入传递到 library.wasm
的新实例中:
import source libraryModule from './library.wasm';
const instance1 = await WebAssembly.instantiate(libraryModule, {
custom: import1,
});
const instance2 = await WebAssembly.instantiate(libraryModule, {
custom: import2,
});
顶层 await
#
await
关键字可以在 ECMAScript 模块的顶层主体中使用。
假设一个 a.mjs
包含:
export const five = await Promise.resolve(5);
以及一个 b.mjs
包含:
import { five } from './a.mjs';
console.log(five); // Logs `5`
node b.mjs # works
如果顶层 await
表达式永远不解析,则 node
进程将以 13
状态码退出。
import { spawn } from 'node:child_process';
import { execPath } from 'node:process';
spawn(execPath, [
'--input-type=module',
'--eval',
// Never-resolving Promise:
'await new Promise(() => {})',
]).once('exit', (code) => {
console.log(code); // Logs `13`
});
加载器#
以前的加载器文档现在位于 模块: 自定义钩子。
解析和加载算法#
特性#
默认的解析器具有以下属性
- 基于 FileURL 的解析,就像 ES 模块使用的那样
- 相对和绝对 URL 解析
- 没有默认的扩展名
- 没有文件夹 mains
- 通过 node_modules 查找的裸说明符包解析
- 不会因未知的扩展名或协议而失败
- 可以选择提供格式提示给加载阶段
默认加载器具有以下属性
- 支持通过
node:
URL 加载内置模块 - 支持通过
data:
URL 加载 "inline" 模块 - 支持
file:
模块加载 - 对于任何其他 URL 协议都会失败
- 对于
file:
加载,对于未知的扩展名都会失败(仅支持.cjs
、.js
和.mjs
)
解析算法#
加载 ES 模块说明符的算法通过下面的 ESM_RESOLVE 方法给出。它返回相对于 parentURL 的模块说明符的解析 URL。
解析算法确定模块加载的完整解析 URL,以及其建议的模块格式。解析算法不确定解析的 URL 协议是否可以加载,或者文件扩展名是否被允许,相反,这些验证由 Node.js 在加载阶段应用(例如,如果它被要求加载一个具有不是 file:
、data:
或 node:
的协议的 URL)。
该算法还尝试根据扩展名确定文件的格式(参见下面的 ESM_FILE_FORMAT
算法)。如果它不识别文件扩展名(例如,如果它不是 .mjs
、.cjs
或 .json
),则返回 undefined
的格式,这将在加载阶段抛出错误。
确定解析 URL 的模块格式的算法由 ESM_FILE_FORMAT 提供,它返回任何文件的唯一模块格式。"module" 格式返回用于 ECMAScript 模块,而 "commonjs" 格式用于指示通过旧版 CommonJS 加载器加载。其他格式,如 "addon",可以在未来的更新中扩展。
在以下算法中,所有子例程错误都作为这些顶级例程的错误传播,除非另有说明。
defaultConditions 是条件环境名称数组,["node", "import"]
。
解析器可以抛出以下错误
- 无效模块说明符:模块说明符是无效的 URL、包名或包子路径说明符。
- 无效包配置:package.json 配置无效或包含无效配置。
- 无效包目标:包导出或导入为包定义了一个目标模块,该模块是一个无效类型或字符串目标。
- 未导出包路径:包导出不定义或允许给定模块的包中的目标子路径。
- 未定义包导入:包导入不定义说明符。
- 未找到模块:请求的包或模块不存在。
- 不支持的目录导入:解析的路径对应于一个目录,该目录不是模块导入支持的目标。
解析算法规范#
ESM_RESOLVE(specifier, parentURL)
- 令 resolved 为 undefined。
- 如果 specifier 是一个有效的 URL,则
- 将 resolved 设置为将 specifier 解析并重新序列化为 URL 的结果。
- 否则,如果 specifier 以 "/"、"./" 或 "../" 开头,则
- 将 resolved 设置为相对于 parentURL 的 specifier 的 URL 解析。
- 否则,如果 specifier 以 "#" 开头,则
- 将 resolved 设置为 PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, defaultConditions) 的结果。
- 否则,
- 注意:specifier 现在是一个裸说明符。
- 将 resolved 设置为 PACKAGE_RESOLVE(specifier, parentURL) 的结果。
- 令 format 为 undefined。
- 如果 resolved 是一个 "file:" URL,则
- 如果 resolved 包含 "/" 或 "\" (分别为 "%2F" 和 "%5C") 的任何百分比编码,则
- 抛出一个 无效模块说明符 错误。
- 如果 resolved 的文件是一个目录,则
- 抛出一个 不支持的目录导入 错误。
- 如果 resolved 的文件不存在,则
- 抛出一个 未找到模块 错误。
- 将 resolved 设置为 resolved 的真实路径,保持相同的 URL 查询字符串和片段组件。
- 将 format 设置为 ESM_FILE_FORMAT(resolved) 的结果。
- 否则,
- 将 format 设置为与 URL resolved 关联的内容类型的模块格式。
- 将 format 和 resolved 返回到加载阶段
PACKAGE_RESOLVE(packageSpecifier, parentURL)
- 令 packageName 为 undefined。
- 如果 packageSpecifier 是一个空字符串,则
- 抛出一个 无效模块说明符 错误。
- 如果 packageSpecifier 是一个 Node.js 内置模块名称,则
- 返回字符串 "node:" 与 packageSpecifier 的连接。
- 如果 packageSpecifier 不以 "@" 开头,则
- 将 packageName 设置为 packageSpecifier 的子字符串,直到第一个 "/" 分隔符或字符串结尾。
- 否则,
- 如果 packageSpecifier 不包含 "/" 分隔符,则
- 抛出一个 无效模块说明符 错误。
- 将 packageName 设置为 packageSpecifier 的子字符串,直到第二个 "/" 分隔符或字符串结尾。
- 如果 packageName 以 "." 开头或包含 "\" 或 "%",则
- 抛出一个 无效模块说明符 错误。
- 令 packageSubpath 为 "." 与 packageSpecifier 的子字符串的连接,从长度为 packageName 的位置开始。
- 令 selfUrl 为 PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL) 的结果。
- 如果 selfUrl 不是 undefined,则返回 selfUrl。
- 当 parentURL 不是文件系统根目录时,
- 令 packageURL 为相对于 parentURL 的 "node_modules/" 与 packageName 连接的 URL 解析。
- 将 parentURL 设置为 parentURL 的父文件夹 URL。
- 如果 packageURL 的文件夹不存在,则
- 继续下一次循环迭代。
- 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 如果 pjson 不是 null 并且 pjson.exports 不是 null 或 undefined,则
- 返回 PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions) 的结果。
- 否则,如果 packageSubpath 等于 ".",则
- 如果 pjson.main 是一个字符串,则
- 返回 packageURL 中 main 的 URL 解析。
- 否则,
- 返回 packageURL 中 packageSubpath 的 URL 解析。
- 抛出一个 未找到模块 错误。
PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL)
- 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(parentURL) 的结果。
- 如果 packageURL 是 null,则
- 返回 undefined。
- 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 如果 pjson 是 null,或者如果 pjson.exports 是 null 或 undefined,则
- 返回 undefined。
- 如果 pjson.name 等于 packageName,则
- 返回 PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions) 的结果。
- 否则,返回 undefined。
PACKAGE_EXPORTS_RESOLVE(packageURL, subpath, exports, conditions)
注意:此函数由 CommonJS 解析算法直接调用。
- 如果 exports 是一个对象,其中既有以 "." 开头的键,也有不以 "." 开头的键,则抛出一个 无效包配置 错误。
- 如果 subpath 等于 ".",则
- 令 mainExport 为 undefined。
- 如果 exports 是一个字符串或数组,或者是一个不包含以 "." 开头的键的对象,则
- 将 mainExport 设置为 exports。
- 否则,如果 exports 是一个包含 "." 属性的对象,那么
- 将 mainExport 设置为 exports["."]。
- 如果 mainExport 不是 undefined,那么
- 令 resolved 为 PACKAGE_TARGET_RESOLVE(packageURL, mainExport, null, false, conditions) 的结果。
- 如果 resolved 不是 null 或 undefined,则返回 resolved。
- 否则,如果 exports 是一个对象且 exports 的所有键都以 "." 开头,那么
- 断言:subpath 以 "./" 开头。
- 令 resolved 为 PACKAGE_IMPORTS_EXPORTS_RESOLVE(subpath, exports, packageURL, false, conditions) 的结果。
- 如果 resolved 不是 null 或 undefined,则返回 resolved。
- 抛出一个 Package Path Not Exported 错误。
PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, conditions)
注意:此函数由 CommonJS 解析算法直接调用。
- 断言:specifier 以 "#" 开头。
- 如果 specifier 完全等于 "#" 或以 "#/" 开头,那么
- 抛出一个 无效模块说明符 错误。
- 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(parentURL) 的结果。
- 如果 packageURL 不是 null,那么
- 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 如果 pjson.imports 是一个非空的 Object,那么
- 令 resolved 为 PACKAGE_IMPORTS_EXPORTS_RESOLVE(specifier, pjson.imports, packageURL, true, conditions) 的结果。
- 如果 resolved 不是 null 或 undefined,则返回 resolved。
- 抛出一个 Package Import Not Defined 错误。
PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)
- 如果 matchKey 以 "/" 结尾,那么
- 抛出一个 无效模块说明符 错误。
- 如果 matchKey 是 matchObj 的一个键,并且不包含 "*",那么
- 令 target 为 matchObj[matchKey] 的值。
- 返回 PACKAGE_TARGET_RESOLVE(packageURL, target, null, isImports, conditions) 的结果。
- 令 expansionKeys 为 matchObj 的键的列表,其中只包含一个 "*",并按排序函数 PATTERN_KEY_COMPARE 排序,该函数按特异性降序排列。
- 对于 expansionKeys 中的每个键 expansionKey,执行
- 令 patternBase 为 expansionKey 的子字符串,直到但不包括第一个 "*" 字符。
- 如果 matchKey 以 patternBase 开头但不等于 patternBase,那么
- 令 patternTrailer 为 expansionKey 的子字符串,从第一个 "*" 字符之后的索引开始。
- 如果 patternTrailer 的长度为零,或者如果 matchKey 以 patternTrailer 结尾,并且 matchKey 的长度大于或等于 expansionKey 的长度,那么
- 令 target 为 matchObj[expansionKey] 的值。
- 令 patternMatch 为 matchKey 的子字符串,从 patternBase 长度的索引开始,直到 matchKey 的长度减去 patternTrailer 的长度。
- 返回 PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions) 的结果。
- 返回 null。
PATTERN_KEY_COMPARE(keyA, keyB)
- 断言:keyA 只包含一个 "*"。
- 断言:keyB 只包含一个 "*"。
- 令 baseLengthA 为 keyA 中 "*" 的索引。
- 令 baseLengthB 为 keyB 中 "*" 的索引。
- 如果 baseLengthA 大于 baseLengthB,返回 -1。
- 如果 baseLengthB 大于 baseLengthA,返回 1。
- 如果 keyA 的长度大于 keyB 的长度,返回 -1。
- 如果 keyB 的长度大于 keyA 的长度,返回 1。
- 返回 0。
PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions)
- 如果 target 是一个字符串,那么
- 如果 target 不是以 "./" 开头,那么
- 如果 isImports 是 false,或者如果 target 以 "../" 或 "/" 开头,或者如果 target 是一个有效的 URL,那么
- 抛出一个 Invalid Package Target 错误。
- 如果 patternMatch 是一个字符串,那么
- 返回 PACKAGE_RESOLVE(target,其中每个 "*" 的实例都替换为 patternMatch, packageURL + "/")。
- 返回 PACKAGE_RESOLVE(target, packageURL + "/")。
- 如果 target 在 "/" 或 "\" 上拆分后,在第一个 "." 段之后包含任何 ""、"."、".." 或 "node_modules" 段,不区分大小写,包括百分比编码的变体,则抛出一个 Invalid Package Target 错误。
- 令 resolvedTarget 为 packageURL 和 target 的串联的 URL 解析。
- 断言:packageURL 包含在 resolvedTarget 中。
- 如果 patternMatch 是 null,那么
- 返回 resolvedTarget。
- 如果 patternMatch 在 "/" 或 "\" 上拆分后,包含任何 ""、"."、".." 或 "node_modules" 段,不区分大小写,包括百分比编码的变体,则抛出一个 Invalid Module Specifier 错误。
- 返回 resolvedTarget 的 URL 解析,其中每个 "*" 的实例都替换为 patternMatch。
- 否则,如果 target 是一个非空的 Object,那么
- 如果 target 包含任何索引属性键,如 ECMA-262 6.1.7 数组索引 中定义的那样,则抛出一个 Invalid Package Configuration 错误。
- 对于 target 的每个属性 p,按照对象插入顺序,执行
- 如果 p 等于 "default" 或 conditions 包含 p 的条目,那么
- 令 targetValue 为 target 中 p 属性的值。
- 令 resolved 为 PACKAGE_TARGET_RESOLVE(packageURL, targetValue, patternMatch, isImports, conditions) 的结果。
- 如果 resolved 等于 undefined,则继续循环。
- 返回 resolved。
- 返回 undefined。
- 否则,如果 target 是一个数组,那么
- 如果 _target.length 为零,则返回 null。
- 对于 target 中的每个项目 targetValue,执行
- 令 resolved 为 PACKAGE_TARGET_RESOLVE(packageURL, targetValue, patternMatch, isImports, conditions) 的结果,并在任何 Invalid Package Target 错误时继续循环。
- 如果 resolved 是 undefined,则继续循环。
- 返回 resolved。
- 返回或抛出最后一个回退解析的 null 返回或错误。
- 否则,如果 target 是 null,则返回 null。
- 否则抛出一个 Invalid Package Target 错误。
ESM_FILE_FORMAT(url)
- 断言:url 对应于现有文件。
- 如果 url 以 ".mjs" 结尾,那么
- 返回 "module"。
- 如果 url 以 ".cjs" 结尾,那么
- 返回 "commonjs"。
- 如果 url 以 ".json" 结尾,那么
- 返回 "json"。
- 如果启用了
--experimental-wasm-modules
并且 url 以 ".wasm" 结尾,那么
- 返回 "wasm"。
- 如果启用了
--experimental-addon-modules
并且 url 以 ".node" 结尾,那么
- 返回 "addon"。
- 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(url) 的结果。
- 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 令 packageType 为 null。
- 如果 pjson?.type 为 "module" 或 "commonjs",那么
- 将 packageType 设置为 pjson.type。
- 如果 url 以 ".js" 结尾,那么
- 如果 packageType 不是 null,那么
- 返回 packageType。
- 如果 DETECT_MODULE_SYNTAX(source) 的结果为 true,那么
- 返回 "module"。
- 返回 "commonjs"。
- 如果 url 没有任何扩展名,那么
- 如果 packageType 是 "module" 并且启用了
--experimental-wasm-modules
并且 url 处的文件包含 WebAssembly 模块的标头,那么
- 返回 "wasm"。
- 如果 packageType 不是 null,那么
- 返回 packageType。
- 如果 DETECT_MODULE_SYNTAX(source) 的结果为 true,那么
- 返回 "module"。
- 返回 "commonjs"。
- 返回 undefined (将在加载阶段抛出)。
LOOKUP_PACKAGE_SCOPE(url)
- 令 scopeURL 为 url。
- 当 scopeURL 不是文件系统根时,
- 将 scopeURL 设置为 scopeURL 的父 URL。
- 如果 scopeURL 以 "node_modules" 路径段结尾,则返回 null。
- 令 pjsonURL 为 scopeURL 中 "package.json" 的解析。
- 如果 pjsonURL 处的文件存在,那么
- 返回 scopeURL。
- 返回 null。
READ_PACKAGE_JSON(packageURL)
- 令 pjsonURL 为 packageURL 中 "package.json" 的解析。
- 如果 pjsonURL 处的文件不存在,那么
- 返回 null。
- 如果 packageURL 处的文件无法解析为有效的 JSON,那么
- 抛出一个 Invalid Package Configuration 错误。
- 返回 pjsonURL 处文件的解析后的 JSON 源代码。
DETECT_MODULE_SYNTAX(source)
- 将 source 解析为 ECMAScript 模块。
- 如果解析成功,那么
- 如果 source 包含顶级
await
、静态import
或export
语句,或import.meta
,则返回 true。- 如果 source 包含任何 CommonJS 包装器变量 (
require
、exports
、module
、__filename
或__dirname
) 的顶级词法声明 (const
、let
或class
),则返回 true。- 否则返回 false。
自定义 ESM 说明符解析算法#
模块定制钩子提供了一种定制 ESM 说明符解析算法的机制。一个为 ESM 说明符提供 CommonJS 风格解析的示例是 commonjs-extension-resolution-loader。