- 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 压缩
- 其他版本
模块:包#>
【Modules: Packages】
介绍#>
【Introduction】
一个包是由 package.json 文件描述的文件夹树。该包由包含 package.json 文件的文件夹及其所有子文件夹组成,直到遇到下一个包含另一个 package.json 文件的文件夹,或者一个名为 node_modules 的文件夹。
【A package is a folder tree described by a package.json file. The package
consists of the folder containing the package.json file and all subfolders
until the next folder containing another package.json file, or a folder
named node_modules.】
本页面为编写 package.json 文件的包作者提供指导,同时提供 Node.js 定义的 package.json 字段参考。
【This page provides guidance for package authors writing package.json files
along with a reference for the package.json fields defined by Node.js.】
确定模块系统#>
【Determining module system】
介绍#>
【Introduction】
当作为初始输入传递给 node,或通过 import 语句或 import() 表达式引用时,Node.js 将把以下内容视为 ES 模块:
【Node.js will treat the following as ES modules when passed to node as the
initial input, or when referenced by import statements or import()
expressions:】
- 具有
.mjs扩展名的文件。 - 当最近的父级
package.json文件包含顶层"type"字段且其值为"module"时,具有.js扩展名的文件。 - 作为
--eval参数传入的字符串,或通过STDIN管道传递给node,并使用标志--input-type=module。 - 包含仅成功解析为ES 模块的语法的代码,如“import”或“export”语句或“import.meta”,且没有明确的解释标记。显式标记包括“.mjs”或“.cjs”扩展,“package.json”“类型”字段,且带有“module”或“commonjs”值,或“--input-type”或“--experimental-default-type”标志。 动态“import()”表达式在 CommonJS 或 ES 模块中都支持,且不会强制文件被当作 ES 模块处理。参见语法检测。
当作为初始输入传递给 node,或通过 import 语句或 import() 表达式引用时,Node.js 将把以下内容视为 CommonJS:
【Node.js will treat the following as CommonJS when passed to node as the
initial input, or when referenced by import statements or import()
expressions:】
- 具有
.cjs扩展名的文件。 - 当最近的父文件“package.json”文件包含顶层字段
"type"且值为“commonjs”时,文件扩展名为“.js”。 - 以参数形式传入
--eval或--print的字符串,或通过STDIN管道传给node,并使用标志--input-type=commonjs。
除了这些明确的情况外,还有其他一些情况,Node.js 会根据 --experimental-default-type 标志的值默认使用某一种模块系统:
【Aside from these explicit cases, there are other cases where Node.js defaults to
one module system or the other based on the value of the
--experimental-default-type flag:】
- 以
.js结尾的文件或没有扩展名的文件,如果在同一文件夹或任何父文件夹中没有package.json文件存在。 - 以
.js结尾的文件或没有扩展名的文件,如果最近的父级package.json字段缺少"type"字段;除非该文件夹位于node_modules文件夹内。(当package.json文件缺少"type"字段时,node_modules下的包作用域始终被视为 CommonJS,无论--experimental-default-type如何,以保证向后兼容性。) - 作为
--eval参数传入或通过STDIN管道传递给node的字符串,当未指定--input-type时。
此标志当前默认为 "commonjs",但将来可能会更改为默认 "module"。因此,最好在可能的情况下明确指定;特别是,包作者应始终在其 package.json 文件中包含 "type" 字段,即使在所有源都是 CommonJS 的包中也是如此。明确包的 type 可以为包提供未来兼容性,以防 Node.js 的默认类型发生变化,同时也会让构建工具和加载器更容易确定包中的文件应如何解释。
【This flag currently defaults to "commonjs", but it may change in the future to
default to "module". For this reason it is best to be explicit wherever
possible; in particular, package authors should always include the "type"
field in their package.json files, even in packages where all sources are
CommonJS. Being explicit about the type of the package will future-proof the
package in case the default type of Node.js ever changes, and it will also make
things easier for build tools and loaders to determine how the files in the
package should be interpreted.】
语法检测#>
【Syntax detection】
Node.js 将检查模糊输入的源代码,以确定其是否包含 ES 模块语法;如果检测到此类语法,该输入将被视为 ES 模块。
【Node.js will inspect the source code of ambiguous input to determine whether it contains ES module syntax; if such syntax is detected, the input will be treated as an ES module.】
不明确的输入定义为:
【Ambiguous input is defined as:】
- 扩展名为
.js的文件或没有扩展名的文件;并且没有控制性的package.json文件,或者有但缺少type字段;并且未指定--experimental-default-type。 - 当未指定
--input-type或--experimental-default-type时,字符串输入(--eval或 STDIN)。
ES 模块语法被定义为在作为 CommonJS 评估时会抛出错误的语法。这包括以下内容:
【ES module syntax is defined as syntax that would throw when evaluated as CommonJS. This includes the following:】
import语句(但不包括import()表达式,后者在 CommonJS 中是有效的)。export语句。import.meta引用。- 在模块的顶层使用
await。 - CommonJS 封装器变量的词法重新声明(
require、module、exports、__dirname、__filename)。
模块加载器#>
【Modules loaders】
Node.js 有两个系统用于解析说明符和加载模块。
【Node.js has two systems for resolving a specifier and loading modules.】
有 CommonJS 模块加载器:
【There is the CommonJS module loader:】
- 它是完全同步的。
- 它负责处理
require()调用。 - 它可以被动态修改。
- 它支持将文件夹作为模块。
- 在解析指定符时,如果没有找到完全匹配项,它会尝试添加扩展名(
.js、.json,最后是.node),然后尝试解析 将文件夹作为模块。 - 它将
.json视为 JSON 文本文件。 .node文件被解释为通过process.dlopen()加载的已编译的插件模块。- 它将所有缺少
.json或.node扩展名的文件视为 JavaScript 文本文件。 - 只有在模块图是同步的(不包含顶层
await)时,它才能用于 从 CommonJS 模块加载 ECMAScript 模块。当用于加载不是 ECMAScript 模块的 JavaScript 文本文件时,该文件将作为 CommonJS 模块加载。
有 ECMAScript 模块加载器:
【There is the ECMAScript module loader:】
- 它是异步的,除非它被用来加载
require()的模块。 - 它负责处理
import语句和import()表达式。 - 它不能被猴子补丁,但可以使用 加载器钩子 进行自定义。
- 它不支持将文件夹作为模块,目录索引(例如
'./startup/index.js')必须完整指定。 - 它不进行扩展名搜索。当指定的文件 URL 是相对或绝对路径时,必须提供文件扩展名。
- 它可以加载 JSON 模块,但需要导入断言。
- 它只接受
.js、.mjs和.cjs扩展名的 JavaScript 文本文件。 - 它可以用于加载 JavaScript CommonJS 模块。此类模块会通过
cjs-module-lexer进行处理,以尝试识别命名导出,如果可以通过静态分析确定,则可以使用这些导出。导入的 CommonJS 模块会将其 URL 转换为绝对路径,然后通过 CommonJS 模块加载器进行加载。
package.json 和文件扩展名#>
【package.json and file extensions】
在一个包中,package.json "type" 字段定义了 Node.js 应该如何解释 .js 文件。如果 package.json 文件没有 "type" 字段,.js 文件将被视为 CommonJS。
【Within a package, the package.json "type" field defines how
Node.js should interpret .js files. If a package.json file does not have a
"type" field, .js files are treated as CommonJS.】
'package.json' '类型''的'“模块”值告诉Node.js解释“.js” 该包内的文件使用ES 模块语法。
【A package.json "type" value of "module" tells Node.js to interpret .js
files within that package as using ES module syntax.】
"type" 字段不仅适用于初始入口点 (node my-app.js),还适用于通过 import 语句和 import() 表达式引用的文件。
【The "type" field applies not only to initial entry points (node my-app.js)
but also to files referenced by import statements and import() expressions.】
// my-app.js, treated as an ES module because there is a package.json
// file in the same folder with "type": "module".
import './startup/init.js';
// Loaded as ES module since ./startup contains no package.json file,
// and therefore inherits the "type" value from one level up.
import 'commonjs-package';
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
// lacks a "type" field or contains "type": "commonjs".
import './node_modules/commonjs-package/index.js';
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
// lacks a "type" field or contains "type": "commonjs".
以 .mjs 结尾的文件总是作为 ES 模块 加载,无论最近的父级 package.json 是什么。
【Files ending with .mjs are always loaded as ES modules regardless of
the nearest parent package.json.】
以 .cjs 结尾的文件总是作为 CommonJS 加载,无论最近的父级 package.json 是什么。
【Files ending with .cjs are always loaded as CommonJS regardless of the
nearest parent package.json.】
import './legacy-file.cjs';
// Loaded as CommonJS since .cjs is always loaded as CommonJS.
import 'commonjs-package/src/index.mjs';
// Loaded as ES module since .mjs is always loaded as ES module.
.mjs 和 .cjs 扩展名可以在同一个包中混合使用不同类型:
【The .mjs and .cjs extensions can be used to mix types within the same
package:】
- 在“type”: “module”包中,Node.js可以通过命名为“.cjs”扩展名来解释某个文件为CommonJS(因为“.js”和“.mjs”文件在“module”包中被视为ES模块)。
- 在一个
"type": "commonjs"的包中,可以通过将特定文件命名为.mjs扩展名来指示 Node.js 将其解释为 ES 模块(因为在"commonjs"包中,.js和.cjs文件都被视为 CommonJS)。
--input-type 标志#>
【--input-type flag】
当设置了 --input-type=module 标志时,作为 --eval(或 -e)参数传入的字符串,或通过 STDIN 管道传入 node 的字符串,将被视为 ES 模块。
【Strings passed in as an argument to --eval (or -e), or piped to node via
STDIN, are treated as ES modules when the --input-type=module flag
is set.】
node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"
echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module
为了完整性,还有 --input-type=commonjs,用于将字符串输入明确地作为 CommonJS 运行。如果未指定 --input-type,这是默认行为。
【For completeness there is also --input-type=commonjs, for explicitly running
string input as CommonJS. This is the default behavior if --input-type is
unspecified.】
确定包管理器#>
【Determining package manager】
虽然所有的 Node.js 项目在发布后都应该可以通过所有包管理器安装,但它们的开发团队通常被要求使用某一个特定的包管理器。为了简化这个过程,Node.js 提供了一个名为 Corepack 的工具,它旨在让所有包管理器在你的环境中透明可用——前提是你已经安装了 Node.js。
【While all Node.js projects are expected to be installable by all package managers once published, their development teams are often required to use one specific package manager. To make this process easier, Node.js ships with a tool called Corepack that aims to make all package managers transparently available in your environment - provided you have Node.js installed.】
默认情况下,Corepack 不会强制使用任何特定的包管理器,而是使用与每个 Node.js 版本关联的通用“最后已知良好”版本,但你可以通过在项目的 package.json 中设置 "packageManager" 字段来改善这一体验。
【By default Corepack won't enforce any specific package manager and will use
the generic "Last Known Good" versions associated with each Node.js release,
but you can improve this experience by setting the "packageManager" field
in your project's package.json.】
包入口点#>
【Package entry points】
在一个包的 package.json 文件中,有两个字段可以定义包的入口点:"main" 和 "exports"。两个字段都适用于 ES 模块和 CommonJS 模块的入口点。
【In a package's package.json file, two fields can define entry points for a
package: "main" and "exports". Both fields apply to both ES module
and CommonJS module entry points.】
"main" 字段在所有版本的 Node.js 中都受到支持,但其功能有限:它仅定义了包的主要入口点。
【The "main" field is supported in all versions of Node.js, but its
capabilities are limited: it only defines the main entry point of the package.】
"exports" 提供了 "main" 的现代替代方案,允许定义多个入口点,支持在不同环境之间进行条件入口解析,并且防止使用除 "exports" 中定义的入口点之外的任何其他入口点。这种封装使模块作者能够清晰地定义他们包的公共接口。
【The "exports" provides a modern alternative to "main" allowing
multiple entry points to be defined, conditional entry resolution support
between environments, and preventing any other entry points besides those
defined in "exports". This encapsulation allows module authors to
clearly define the public interface for their package.】
对于面向当前支持的 Node.js 版本的新软件包,建议使用 "exports" 字段。对于支持 Node.js 10 及以下版本的软件包,必须使用 "main" 字段。如果同时定义了 "exports" 和 "main" 字段,则在支持的 Node.js 版本中,"exports" 字段优先于 "main" 字段。
【For new packages targeting the currently supported versions of Node.js, the
"exports" field is recommended. For packages supporting Node.js 10 and
below, the "main" field is required. If both "exports" and
"main" are defined, the "exports" field takes precedence over
"main" in supported versions of Node.js.】
条件导出 可以在 "exports" 中使用,以定义每个环境的不同包入口点,包括该包是通过 require 还是通过 import 引用。有关在单个包中同时支持 CommonJS 和 ES 模块的更多信息,请参阅 双重 CommonJS/ES 模块包部分。
引入 "exports" 字段的现有包将阻止包的使用者使用任何未定义的入口点,包括 package.json(例如 require('your-package/package.json'))。这很可能会是一个重大更改。
【Existing packages introducing the "exports" field will prevent consumers
of the package from using any entry points that are not defined, including the
package.json (e.g. require('your-package/package.json')). This will
likely be a breaking change.】
为了使 "exports" 的引入保持向后兼容,请确保导出之前支持的每个入口点。最好明确指定入口点,以便包的公共 API 定义清晰。例如,一个之前导出 main、lib、feature 和 package.json 的项目可以使用以下 package.exports:
【To make the introduction of "exports" non-breaking, ensure that every
previously supported entry point is exported. It is best to explicitly specify
entry points so that the package's public API is well-defined. For example,
a project that previously exported main, lib,
feature, and the package.json could use the following package.exports:】
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/index": "./lib/index.js",
"./lib/index.js": "./lib/index.js",
"./feature": "./feature/index.js",
"./feature/index": "./feature/index.js",
"./feature/index.js": "./feature/index.js",
"./package.json": "./package.json"
}
}
或者,一个项目可以选择使用导出模式导出整个文件夹,同时包含或不包含带扩展名的子路径:
【Alternatively a project could choose to export entire folders both with and without extensioned subpaths using export patterns:】
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/*": "./lib/*.js",
"./lib/*.js": "./lib/*.js",
"./feature": "./feature/index.js",
"./feature/*": "./feature/*.js",
"./feature/*.js": "./feature/*.js",
"./package.json": "./package.json"
}
}
通过上述为任何次要版本提供向后兼容性,包的未来重大更改就可以适当地将导出限制为仅公开的特定功能导出:
【With the above providing backwards-compatibility for any minor package versions, a future major change for the package can then properly restrict the exports to only the specific feature exports exposed:】
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./feature/*.js": "./feature/*.js",
"./feature/internal/*": null
}
}
主入口点导出#>
【Main entry point export】
在编写新软件包时,建议使用 "exports" 字段:
【When writing a new package, it is recommended to use the "exports" field:】
{
"exports": "./index.js"
}
当定义了 "exports" 字段时,包的所有子路径都会被封装,导入者将无法再访问。例如,require('pkg/subpath.js') 会抛出 ERR_PACKAGE_PATH_NOT_EXPORTED 错误。
【When the "exports" field is defined, all subpaths of the package are
encapsulated and no longer available to importers. For example,
require('pkg/subpath.js') throws an ERR_PACKAGE_PATH_NOT_EXPORTED
error.】
这种对导出的封装为工具以及在处理包的语义化版本升级时提供了更可靠的包接口保证。这不是一种强封装,因为对包的任何绝对子路径的直接 require,例如 require('/path/to/node_modules/pkg/subpath.js'),仍然会加载 subpath.js。
【This encapsulation of exports provides more reliable guarantees
about package interfaces for tools and when handling semver upgrades for a
package. It is not a strong encapsulation since a direct require of any
absolute subpath of the package such as
require('/path/to/node_modules/pkg/subpath.js') will still load subpath.js.】
所有目前支持的 Node.js 版本和现代构建工具都支持 "exports" 字段。对于使用较旧版本 Node.js 或相关构建工具的项目,可以通过在 "exports" 字段旁边包含 "main" 字段并指向同一个模块来实现兼容性:
【All currently supported versions of Node.js and modern build tools support the
"exports" field. For projects using an older version of Node.js or a related
build tool, compatibility can be achieved by including the "main" field
alongside "exports" pointing to the same module:】
{
"main": "./index.js",
"exports": "./index.js"
}
子路径导出#>
【Subpath exports】
在使用 "exports" 字段时,可以定义自定义子路径以及主入口点,通过将主入口点视为 "." 子路径来实现:
【When using the "exports" field, custom subpaths can be defined along
with the main entry point by treating the main entry point as the
"." subpath:】
{
"exports": {
".": "./index.js",
"./submodule.js": "./src/submodule.js"
}
}
现在只有在 "exports" 中定义的子路径可以被使用者导入:
【Now only the defined subpath in "exports" can be imported by a consumer:】
import submodule from 'es-module-package/submodule.js';
// Loads ./node_modules/es-module-package/src/submodule.js
而其他子路径会出错:
【While other subpaths will error:】
import submodule from 'es-module-package/private-module.js';
// Throws ERR_PACKAGE_PATH_NOT_EXPORTED
子路径中的扩展#>
【Extensions in subpaths】
包的作者应该在其导出中提供带扩展名(import 'pkg/subpath.js')或不带扩展名(import 'pkg/subpath')的子路径。这确保每个导出模块只有一个子路径,从而使所有依赖方导入相同的一致的标识符,保持包的契约对使用者清晰,并简化包子路径的补全。
【Package authors should provide either extensioned (import 'pkg/subpath.js') or
extensionless (import 'pkg/subpath') subpaths in their exports. This ensures
that there is only one subpath for each exported module so that all dependents
import the same consistent specifier, keeping the package contract clear for
consumers and simplifying package subpath completions.】
传统上,包通常倾向于使用无扩展名的风格,这种风格具有可读性强以及隐藏包内文件真实路径的优点。
【Traditionally, packages tended to use the extensionless style, which has the benefits of readability and of masking the true path of the file within the package.】
随着 导入地图 现在为浏览器和其他 JavaScript 运行时提供了包解析的标准,使用无扩展名的风格可能会导致导入映射定义膨胀。显式的文件扩展名可以避免这个问题,因为它使导入映射能够利用 包文件夹映射 尽可能地映射多个子路径,而不必为每个包子路径导出创建单独的映射条目。这也反映了在相对和绝对导入说明符中使用 完整限定路径 的要求。
【With import maps now providing a standard for package resolution in browsers and other JavaScript runtimes, using the extensionless style can result in bloated import map definitions. Explicit file extensions can avoid this issue by enabling the import map to utilize a packages folder mapping to map multiple subpaths where possible instead of a separate map entry per package subpath export. This also mirrors the requirement of using the full specifier path in relative and absolute import specifiers.】
导出糖#>
【Exports sugar】
如果是'“。”出口是唯一的出口,"exports"田提供糖
对于此情况,则是直接的"exports"场值。
【If the "." export is the only export, the "exports" field provides sugar
for this case being the direct "exports" field value.】
{
"exports": {
".": "./index.js"
}
}
可以写成:
【can be written:】
{
"exports": "./index.js"
}
子路径导入#>
【Subpath imports】
除了"exports"字段外,还有一个包“导入”字段
创建仅适用于从
封装本身。
【In addition to the "exports" field, there is a package "imports" field
to create private mappings that only apply to import specifiers from within the
package itself.】
"imports" 字段中的条目必须始终以 # 开头,以确保它们与外部包说明符区分开来。
【Entries in the "imports" field must always start with # to ensure they are
disambiguated from external package specifiers.】
例如,imports 字段可以用于为内部模块获得条件导出的好处:
【For example, the imports field can be used to gain the benefits of conditional exports for internal modules:】
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
当 import '#dep' 无法解析外部包 dep-node-native(包括其导出内容)时,它会在其他环境中获取相对于该包的本地文件 ./dep-polyfill.js。
【where import '#dep' does not get the resolution of the external package
dep-node-native (including its exports in turn), and instead gets the local
file ./dep-polyfill.js relative to the package in other environments.】
与 "exports" 字段不同,"imports" 字段允许映射到外部包。
【Unlike the "exports" field, the "imports" field permits mapping to external
packages.】
imports 字段的解析规则在其他方面与 exports 字段类似。
【The resolution rules for the imports field are otherwise analogous to the exports field.】
子路径模式#>
【Subpath patterns】
对于导出或导入数量较少的包,我们建议显式列出每个 exports 子路径条目。但对于子路径数量较多的包,这可能会导致 package.json 文件变大并带来维护问题。
【For packages with a small number of exports or imports, we recommend
explicitly listing each exports subpath entry. But for packages that have
large numbers of subpaths, this might cause package.json bloat and
maintenance issues.】
对于这些用例,可以使用子路径导出模式:
【For these use cases, subpath export patterns can be used instead:】
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js"
},
"imports": {
"#internal/*.js": "./src/internal/*.js"
}
}
* 映射仅作为字符串替换语法暴露嵌套子路径。
右边所有的 * 实例都将被此值替换,包括其中包含任何 / 分隔符的情况。
【All instances of * on the right hand side will then be replaced with this
value, including if it contains any / separators.】
import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js
import featureY from 'es-module-package/features/y/y.js';
// Loads ./node_modules/es-module-package/src/features/y/y.js
import internalZ from '#internal/z.js';
// Loads ./node_modules/es-module-package/src/internal/z.js
这是一个直接的静态匹配和替换,不对文件扩展名进行任何特殊处理。在映射的两侧都包含 "*.js" 会将暴露的包导出限制为仅 JS 文件。
【This is a direct static matching and replacement without any special handling
for file extensions. Including the "*.js" on both sides of the mapping
restricts the exposed package exports to only JS files.】
通过导出模式,导出具有静态可枚举性的特性得以保持,因为可以通过将右侧的目标模式视为针对包内文件列表的 ** 通配符来确定包的各个导出。由于在导出目标中禁止使用 node_modules 路径,这种展开仅依赖于包自身的文件。
【The property of exports being statically enumerable is maintained with exports
patterns since the individual exports for a package can be determined by
treating the right hand side target pattern as a ** glob against the list of
files within the package. Because node_modules paths are forbidden in exports
targets, this expansion is dependent on only the files of the package itself.】
要从模式中排除私有子文件夹,可以使用 null 目标:
【To exclude private subfolders from patterns, null targets can be used:】
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js",
"./features/private-internal/*": null
}
}
import featureInternal from 'es-module-package/features/private-internal/m.js';
// Throws: ERR_PACKAGE_PATH_NOT_EXPORTED
import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js
条件导出#>
【Conditional exports】
条件导出提供了一种根据特定条件映射到不同路径的方式。它们同时支持 CommonJS 和 ES 模块导入。
【Conditional exports provide a way to map to different paths depending on certain conditions. They are supported for both CommonJS and ES module imports.】
例如,一个希望为 require() 和 import 提供不同 ES 模块导出的包可以这样编写:
【For example, a package that wants to provide different ES module exports for
require() and import can be written:】
// package.json
{
"exports": {
"import": "./index-module.js",
"require": "./index-require.cjs"
},
"type": "module"
}
Node.js 实现了以下条件,按从最具体到最不具体的顺序列出,因为条件应如此定义:
【Node.js implements the following conditions, listed in order from most specific to least specific as conditions should be defined:】
"node-addons"- 类似于"node",适用于任何 Node.js 环境。该条件可用于提供使用本地 C++ 插件的入口点,而不是更通用且不依赖本地插件的入口点。此条件可以通过--no-addons标志 禁用。node- 匹配任何 Node.js 环境。可以是 CommonJS 或 ES 模块文件。在大多数情况下,不需要明确指定 Node.js 平台。"import"- 当通过import或import()加载包,或通过 ECMAScript 模块加载器的任何顶层导入或解析操作加载包时匹配。无论目标文件的模块格式如何都适用。始终与"require"互斥."require"- 当包通过require()加载时匹配。被引用的文件应该能够通过require()加载,尽管该条件与目标文件的模块格式无关。预期格式包括 CommonJS、JSON、原生插件和 ES 模块。始终与"import"互斥。"module-sync"- 无论包是通过import、import()还是require()加载,都可以匹配。期望的格式是 ES 模块,其模块图中不包含顶层 await——如果包含,当该模块被require()时将抛出ERR_REQUIRE_ASYNC_MODULE。"default"- 通用的回退选项,总是匹配。可以是 CommonJS 或 ES 模块文件。此条件应始终放在最后。
在 "exports" 对象中,键的顺序是重要的。在条件匹配过程中,较早的条目具有更高的优先级,优先于较晚的条目。一般规则是,条件应按照从最具体到最不具体的顺序排列在对象中。
【Within the "exports" object, key order is significant. During condition
matching, earlier entries have higher priority and take precedence over later
entries. The general rule is that conditions should be from most specific to
least specific in object order.】
使用 "import" 和 "require" 条件可能会导致一些风险,这些风险在 双重 CommonJS/ES 模块包部分 中有进一步解释。
【Using the "import" and "require" conditions can lead to some hazards,
which are further explained in the dual CommonJS/ES module packages section.】
"node-addons" 条件可以用来提供一个使用本地 C++ 插件的入口点。然而,该条件可以通过 --no-addons 标志 被禁用。在使用 "node-addons" 时,建议将 "default" 视为一种增强,提供一个更通用的入口点,例如使用 WebAssembly 代替本地插件。
【The "node-addons" condition can be used to provide an entry point which
uses native C++ addons. However, this condition can be disabled via the
--no-addons flag. When using "node-addons", it's recommended to treat
"default" as an enhancement that provides a more universal entry point, e.g.
using WebAssembly instead of a native addon.】
条件导出也可以扩展为导出子路径,例如:
【Conditional exports can also be extended to exports subpaths, for example:】
{
"exports": {
".": "./index.js",
"./feature.js": {
"node": "./feature-node.js",
"default": "./feature.js"
}
}
}
定义一个包,在该包中 require('pkg/feature.js') 和 import 'pkg/feature.js' 在 Node.js 与其他 JS 环境之间可能提供不同的实现。
【Defines a package where require('pkg/feature.js') and
import 'pkg/feature.js' could provide different implementations between
Node.js and other JS environments.】
在使用环境分支时,尽可能始终包含一个 "default" 条件。提供 "default" 条件可以确保任何未知的 JS 环境能够使用这个通用实现,这有助于避免这些 JS 环境为了支持具有条件导出的包而不得不假装成现有环境。因此,使用 "node" 和 "default" 条件分支通常比使用 "node" 和 "browser" 条件分支更可取。
【When using environment branches, always include a "default" condition where
possible. Providing a "default" condition ensures that any unknown JS
environments are able to use this universal implementation, which helps avoid
these JS environments from having to pretend to be existing environments in
order to support packages with conditional exports. For this reason, using
"node" and "default" condition branches is usually preferable to using
"node" and "browser" condition branches.】
嵌套条件#>
【Nested conditions】
除了直接映射,Node.js 还支持嵌套条件对象。
【In addition to direct mappings, Node.js also supports nested condition objects.】
例如,要定义一个只在 Node.js 中使用而不在浏览器中使用的具有双模式入口点的包:
【For example, to define a package that only has dual mode entry points for use in Node.js but not the browser:】
{
"exports": {
"node": {
"import": "./feature-node.mjs",
"require": "./feature-node.cjs"
},
"default": "./feature.mjs"
}
}
条件会按顺序继续匹配,就像平铺的条件一样。如果嵌套条件没有任何映射,它将继续检查父条件的其余条件。通过这种方式,嵌套条件的行为类似于嵌套的 JavaScript if 语句。
【Conditions continue to be matched in order as with flat conditions. If
a nested condition does not have any mapping it will continue checking
the remaining conditions of the parent condition. In this way nested
conditions behave analogously to nested JavaScript if statements.】
解析用户条件#>
【Resolving user conditions】
在运行 Node.js 时,可以使用 --conditions 标志添加自定义用户条件:
【When running Node.js, custom user conditions can be added with the
--conditions flag:】
node --conditions=development index.js
这将会解决包导入和导出中的“development”条件,同时适当地解决现有的“node”、“node-addons”、“default”、“import”和“require”条件。
【which would then resolve the "development" condition in package imports and
exports, while resolving the existing "node", "node-addons", "default",
"import", and "require" conditions as appropriate.】
可以使用重复标志设置任意数量的自定义条件。
【Any number of custom conditions can be set with repeat flags.】
典型条件应仅包含字母数字字符,如果有必要,可以使用“:”、“-”或“=”作为分隔符。其他任何内容可能在节点外运行时出现兼容性问题。
【Typical conditions should only contain alphanumerical characters, using ":", "-", or "=" as separators if necessary. Anything else may run into compability issues outside of node.】
在节点中,条件几乎没有限制,但具体包括:
【In node, conditions have very few restrictions, but specifically these include:】
- 它们必须至少包含一个字符。
- 它们不能以“.”开头,因为它们可能出现在也允许相对路径的位置。
- 它们不能包含 ",",因为某些命令行工具可能会将其解析为以逗号分隔的列表。
- 它们不能像“10”那样是整数属性键,因为这可能对 JavaScript 对象的属性键排序产生意想不到的影响。
社区条件定义#>
【Community Conditions Definitions】
除“import”、“require”、“node”、“module-sync”、“node-addons”和“default”条件外,默认情况下会忽略Node.js 核心实现的其他条件字符串。
【Condition strings other than the "import", "require", "node", "module-sync",
"node-addons" and "default" conditions
implemented in Node.js core are ignored by default.】
其他平台可能会实现其他条件,并且可以通过 --conditions / -C 标志 在 Node.js 中启用用户条件。
【Other platforms may implement other conditions and user conditions can be
enabled in Node.js via the --conditions / -C flag.】
由于定制包的条件需要明确的定义以确保正确使用,以下提供了一份常见已知包条件及其严格定义的列表,以协助生态系统协调。
【Since custom package conditions require clear definitions to ensure correct usage, a list of common known package conditions and their strict definitions is provided below to assist with ecosystem coordination.】
"types"- 可以被类型系统用于解析给定导出的类型文件。此条件应始终首先包含。"browser"- 任何网页浏览器环境。"development"- 可用于定义仅限开发的环境入口,例如在开发模式下提供额外的调试信息,如更好的错误提示。必须始终与"production"互斥。"production"- 可用于定义生产环境的入口点。必须始终与"development"互斥。
对于其他运行时,由 运行时密钥 提案规范中的 冬季CG 维护平台特定的条件键定义。
【For other runtimes, platform-specific condition key definitions are maintained by the WinterCG in the Runtime Keys proposal specification.】
可以通过向 本节的 Node.js 文档 创建拉取请求将新的条件定义添加到此列表中。在此列出新条件定义的要求是:
【New conditions definitions may be added to this list by creating a pull request to the Node.js documentation for this section. The requirements for listing a new condition definition here are that:】
- 定义应当清晰明确,以便所有实现者都能理解。
- 需要明确说明为什么需要该条件的使用情况。
- 应该存在足够的现有实现使用。
- 条件名称不应与其他条件定义或广泛使用的条件冲突。
- 条件定义的列表应该为生态系统提供一种协调效益,而这是在其他情况下无法实现的。例如,公司特定或应用特定的条件不一定能够实现这种效果。
- 条件应该是符合 Node.js 用户在 Node.js 核心文档中所期望的。
"types"条件是一个很好的例子:它实际上并不属于 运行时密钥 提案,但在 Node.js 文档中非常合适。
上述定义可能会在适当的时候移至专门的条件登记处。
【The above definitions may be moved to a dedicated conditions registry in due course.】
使用名称自引用包#>
【Self-referencing a package using its name】
在一个包中,可以通过包的名称引用在包的 package.json 的 "exports" 字段中定义的值。例如,假设 package.json 如下:
【Within a package, the values defined in the package's
package.json "exports" field can be referenced via the package's name.
For example, assuming the package.json is:】
// package.json
{
"name": "a-package",
"exports": {
".": "./index.mjs",
"./foo.js": "./foo.js"
}
}
那么该包中的任何模块都可以引用包本身中的导出内容:
【Then any module in that package can reference an export in the package itself:】
// ./a-module.mjs
import { something } from 'a-package'; // Imports "something" from ./index.mjs.
只有在 package.json 中有 "exports" 时才可以使用自引用,并且只允许导入该 "exports"(在 package.json 中)允许的内容。因此,以下代码在使用之前的包时,将会产生运行时错误:
【Self-referencing is available only if package.json has "exports", and
will allow importing only what that "exports" (in the package.json)
allows. So the code below, given the previous package, will generate a runtime
error:】
// ./another-module.mjs
// Imports "another" from ./m.mjs. Fails because
// the "package.json" "exports" field
// does not provide an export named "./m.mjs".
import { another } from 'a-package/m.mjs';
在使用 require 时,也可以进行自我引用,无论是在 ES 模块中,还是在 CommonJS 模块中。例如,这段代码也可以正常运行:
【Self-referencing is also available when using require, both in an ES module,
and in a CommonJS one. For example, this code will also work:】
// ./a-module.js
const { something } = require('a-package/foo.js'); // Loads from ./foo.js.
最后,自引用也适用于有作用域的包。例如,这段代码也可以正常工作:
【Finally, self-referencing also works with scoped packages. For example, this code will also work:】
// package.json
{
"name": "@my/package",
"exports": "./index.js"
}
// ./index.js
module.exports = 42;
// ./other.js
console.log(require('@my/package'));
$ node other.js
42
双 CommonJS/ES 模块包#>
【Dual CommonJS/ES module packages】
详情请参见 软件包示例仓库。
【See the package examples repository for details.】
Node.js package.json 字段定义#>
【Node.js package.json field definitions】
本节描述了 Node.js 运行时使用的字段。其他工具(例如 npm)使用额外的字段,这些字段在 Node.js 中会被忽略,并且在此处未作记录。
【This section describes the fields used by the Node.js runtime. Other tools (such as npm) use additional fields which are ignored by Node.js and not documented here.】
package.json 文件中的以下字段在 Node.js 中使用:
【The following fields in package.json files are used in Node.js:】
"name"- 在包中使用具名导入时相关。也被包管理器用作包的名称。"main"- 加载包时的默认模块,如果未指定 exports,并且在 Node.js 引入 exports 之前的版本中。"packageManager"- 在为该软件包贡献时推荐的包管理器。由 Corepack 插件使用。"type"- 包类型决定是否将.js文件作为 CommonJS 或 ES 模块加载。"exports"- 包导出和条件导出。当存在时,限制可以从包内部加载的子模块。"imports"- 包导入,供包内模块自身使用。
"name"#>
- 类型:<string>
{
"name": "package-name"
}
"name" 字段定义了你的包的名称。发布到 npm 注册表需要一个满足 某些要求 的名称。
【The "name" field defines your package's name. Publishing to the
npm registry requires a name that satisfies
certain requirements.】
"name" 字段可以与 "exports" 字段一起使用,通过包名来 自我指涉 一个包。
【The "name" field can be used in addition to the "exports" field to
self-reference a package using its name.】
"main"#>
- 类型:<string>
{
"main": "./index.js"
}
"main" 字段定义了通过 node_modules 查找按名称导入时包的入口点。它的值是一个路径。
【The "main" field defines the entry point of a package when imported by name
via a node_modules lookup. Its value is a path.】
当一个包有 "exports" 字段时,在按名称导入该包时,这将优先于 "main" 字段。
【When a package has an "exports" field, this will take precedence over the
"main" field when importing the package by name.】
它还定义了在通过 require() 加载包目录时使用的脚本。
【It also defines the script that is used when the package directory is loaded
via require().】
// This resolves to ./path/to/directory/index.js.
require('./path/to/directory');
"packageManager"#>
- 类型:<string>
{
"packageManager": "<package manager name>@<version>"
}
"packageManager" 字段定义了在当前项目中预期使用的包管理器。它可以设置为任何 支持的包管理器,并且可以确保你的团队使用完全相同的包管理器版本,而无需安装除 Node.js 之外的其他任何东西。
【The "packageManager" field defines which package manager is expected to be
used when working on the current project. It can be set to any of the
supported package managers, and will ensure that your teams use the exact
same package manager versions without having to install anything else other than
Node.js.】
该字段目前处于实验阶段,需要选择加入;有关此操作的详细信息,请查看 Corepack 页面。
【This field is currently experimental and needs to be opted-in; check the Corepack page for details about the procedure.】
"type"#>
- 类型:<string>
"type" 字段定义了 Node.js 用于所有以该 package.json 文件作为最近父级的 .js 文件的模块格式。
【The "type" field defines the module format that Node.js uses for all
.js files that have that package.json file as their nearest parent.】
当最近的上级 package.json 文件包含顶层字段 "type" 且值为 "module" 时,以 .js 结尾的文件将作为 ES 模块加载。
【Files ending with .js are loaded as ES modules when the nearest parent
package.json file contains a top-level field "type" with a value of
"module".】
最近的父级 package.json 定义为在当前文件夹、该文件夹的父文件夹以及依次向上搜索时找到的第一个 package.json,直到遇到 node_modules 文件夹或卷根目录为止。
【The nearest parent package.json is defined as the first package.json found
when searching in the current folder, that folder's parent, and so on up
until a node_modules folder or the volume root is reached.】
// package.json
{
"type": "module"
}
# In same folder as preceding package.json
node my-app.js # Runs as ES module
如果最近的父级 package.json 缺少 "type" 字段,或者包含 "type": "commonjs",则 .js 文件被视为 CommonJS。如果到达卷根目录仍未找到 package.json,则 .js 文件被视为 CommonJS。
【If the nearest parent package.json lacks a "type" field, or contains
"type": "commonjs", .js files are treated as CommonJS. If the volume
root is reached and no package.json is found, .js files are treated as
CommonJS.】
如果最近的父级 package.json 包含 "type": "module",则 .js 文件的 import 语句会被视为 ES 模块。
// my-app.js, part of the same example as above
import './startup.js'; // Loaded as ES module because of package.json
无论 "type" 字段的值如何,.mjs 文件始终被视为 ES 模块,而 .cjs 文件始终被视为 CommonJS。
【Regardless of the value of the "type" field, .mjs files are always treated
as ES modules and .cjs files are always treated as CommonJS.】
"exports"#>
- 类型:<Object> | <string> | <string[]>
{
"exports": "./index.js"
}
“exports” 字段允许定义在通过名称导入包时的 入口点,无论是通过 node_modules 查找加载,还是通过 自我指涉 加载到其自身名称。它在 Node.js 12 及以上版本中受到支持,作为 "main" 的替代方案,可以支持定义 子路径导出 和 条件导出,同时封装内部未导出的模块。
【The "exports" field allows defining the entry points of a package when
imported by name loaded either via a node_modules lookup or a
self-reference to its own name. It is supported in Node.js 12+ as an
alternative to the "main" that can support defining subpath exports
and conditional exports while encapsulating internal unexported modules.】
条件性出口 也可以在 "exports" 中使用,以根据不同环境定义不同的包入口点,包括该包是通过 require 还是通过 import 引用。
exports 中定义的所有路径必须是以 ./ 开头的相对文件 URL。
【All paths defined in the "exports" must be relative file URLs starting with
./.】
"imports"#>
- 类型: <Object>
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
imports 字段中的条目必须是以 # 开头的字符串。
【Entries in the imports field must be strings starting with #.】
包导入允许映射到外部包。
【Package imports permit mapping to external packages.】
此字段定义了当前包的 子路径导入。
【This field defines subpath imports for the current package.】