条件导出
条件导出提供了一种根据特定条件映射到不同路径的方法。 CommonJS 和 ES 模块导入都支持它们。
比如,包想要为 require()
和 import
提供不同的 ES 模块导出可以这样写:
// package.json
{
"main": "./main-require.cjs",
"exports": {
"import": "./main-module.js",
"require": "./main-require.cjs"
},
"type": "module"
}
Node.js 实现以下条件:
"import"
- 当包通过import
或import()
,或者通过 ECMAScript 模块加载器的任何顶层导入或解析操作加载时匹配。 无论目标文件的模块格式如何,都适用。 始终与"require"
互斥。"require"
- 当包通过require()
加载时匹配。 引用的文件应该可以用require()
加载,尽管无论目标文件的模块格式如何,条件都匹配。 预期的格式包括 CommonJS、JSON 和原生插件,但不包括 ES 模块,因为require()
不支持它们。 始终与"import"
互斥。"node"
- 匹配任何 Node.js 环境。 可以是 CommonJS 或 ES 模块文件。 此条件应始终在"import"
或"require"
之后。"node-addons"
- 类似于"node"
并匹配任何 Node.js 环境。 此条件可用于提供使用原生 C++ 插件的入口点,而不是更通用且不依赖原生插件的入口点。 可以通过--no-addons
标志禁用此条件。"default"
- 始终匹配的通用后备。 可以是 CommonJS 或 ES 模块文件。 此条件应始终放在最后。
在 "exports"
对象中,键顺序很重要。
在条件匹配过程中,较早的条目具有更高的优先级并优先于较晚的条目。
一般规则是条件应该按照对象顺序从最具体到最不具体。
使用 "import"
和 "require"
条件会导致一些危害,在双 CommonJS/ES 模块包章节中有进一步的解释。
条件导出也可以扩展为导出子路径,例如:
{
"main": "./main.js",
"exports": {
".": "./main.js",
"./feature": {
"node": "./feature-node.js",
"default": "./feature.js"
}
}
}
定义了一个包,其中 require('pkg/feature')
和 import 'pkg/feature'
可以在 Node.js 和其他 JS 环境之间提供不同的实现。
当使用环境分支时,总是尽可能包含 "default"
条件。
提供 "default"
条件可确保任何未知的 JS 环境都能够使用此通用实现,这有助于避免这些 JS 环境必须伪装成现有环境以支持具有条件导出的包。
出于这个原因,使用 "node"
和 "default"
条件分支通常比使用 "node"
和 "browser"
条件分支更可取。
Conditional exports provide a way to map to different paths depending on certain conditions. They are supported for both CommonJS and ES module imports.
For example, a package that wants to provide different ES module exports for
require()
and import
can be written:
// package.json
{
"main": "./main-require.cjs",
"exports": {
"import": "./main-module.js",
"require": "./main-require.cjs"
},
"type": "module"
}
Node.js implements the following conditions:
"import"
- matches when the package is loaded viaimport
orimport()
, or via any top-level import or resolve operation by the ECMAScript module loader. Applies regardless of the module format of the target file. Always mutually exclusive with"require"
."require"
- matches when the package is loaded viarequire()
. The referenced file should be loadable withrequire()
although the condition matches regardless of the module format of the target file. Expected formats include CommonJS, JSON, and native addons but not ES modules asrequire()
doesn't support them. Always mutually exclusive with"import"
."node"
- matches for any Node.js environment. Can be a CommonJS or ES module file. This condition should always come after"import"
or"require"
."node-addons"
- similar to"node"
and matches for any Node.js environment. This condition can be used to provide an entry point which uses native C++ addons as opposed to an entry point which is more universal and doesn't rely on native addons. This condition can be disabled via the--no-addons
flag."default"
- the generic fallback that always matches. Can be a CommonJS or ES module file. This condition should always come last.
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.
Using the "import"
and "require"
conditions can lead to some hazards,
which are further explained in the dual CommonJS/ES module packages section.
Conditional exports can also be extended to exports subpaths, for example:
{
"main": "./main.js",
"exports": {
".": "./main.js",
"./feature": {
"node": "./feature-node.js",
"default": "./feature.js"
}
}
}
Defines a package where require('pkg/feature')
and import 'pkg/feature'
could provide different implementations between Node.js and other JS
environments.
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.