双包的危害
当应用程序使用提供 CommonJS 和 ES 模块源的包时,如果包的两个版本都被加载,则存在某些错误的风险。
此潜力来自于 const pkgInstance = require('pkg')
创建的 pkgInstance
与 import pkgInstance from 'pkg'
创建的 pkgInstance
(或像 'pkg/module'
这样的替代主路径)不同的事实。
这是“双包风险”,同一包的两个版本可以在同一个运行时环境中加载。
虽然应用程序或包不太可能有意直接加载两个版本,但应用程序加载一个版本而应用程序的依赖项加载另一个版本是很常见的。
这种危险可能发生,因为 Node.js 支持混合 CommonJS 和 ES 模块,并可能导致意外行为。
如果包主导出是一个构造函数,两个版本创建的实例的 instanceof
比较返回 false
,如果导出是一个对象,添加到一个的属性(如 pkgInstance.foo = 3
)在另一个上不存在。
这与 import
和 require
语句分别在全 CommonJS 或全 ES 模块环境中的工作方式不同,因此令用户感到惊讶。
它也不同于用户在通过 Babel 或 esm
等工具使用转译时所熟悉的行为。
When an application is using a package that provides both CommonJS and ES module
sources, there is a risk of certain bugs if both versions of the package get
loaded. This potential comes from the fact that the pkgInstance
created by
const pkgInstance = require('pkg')
is not the same as the pkgInstance
created by import pkgInstance from 'pkg'
(or an alternative main path like
'pkg/module'
). This is the “dual package hazard,” where two versions of the
same package can be loaded within the same runtime environment. While it is
unlikely that an application or package would intentionally load both versions
directly, it is common for an application to load one version while a dependency
of the application loads the other version. This hazard can happen because
Node.js supports intermixing CommonJS and ES modules, and can lead to unexpected
behavior.
If the package main export is a constructor, an instanceof
comparison of
instances created by the two versions returns false
, and if the export is an
object, properties added to one (like pkgInstance.foo = 3
) are not present on
the other. This differs from how import
and require
statements work in
all-CommonJS or all-ES module environments, respectively, and therefore is
surprising to users. It also differs from the behavior users are familiar with
when using transpilation via tools like Babel or esm
.