条件导出


条件导出提供了一种根据特定条件映射到不同路径的方法。 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" - 当包通过 importimport(),或者通过 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 via import or import(), 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 via require(). The referenced file should be loadable with require() 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 as require() 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.