双包的危害


当应用程序使用提供 CommonJS 和 ES 模块源的包时,如果包的两个版本都被加载,则存在某些错误的风险。 此潜力来自于 const pkgInstance = require('pkg') 创建的 pkgInstanceimport pkgInstance from 'pkg' 创建的 pkgInstance(或像 'pkg/module' 这样的替代主路径)不同的事实。 这是“双包风险”,同一包的两个版本可以在同一个运行时环境中加载。 虽然应用程序或包不太可能有意直接加载两个版本,但应用程序加载一个版本而应用程序的依赖项加载另一个版本是很常见的。 这种危险可能发生,因为 Node.js 支持混合 CommonJS 和 ES 模块,并可能导致意外行为。

如果包主导出是一个构造函数,两个版本创建的实例的 instanceof 比较返回 false,如果导出是一个对象,添加到一个的属性(如 pkgInstance.foo = 3)在另一个上不存在。 这与 importrequire 语句分别在全 CommonJS 或全 ES 模块环境中的工作方式不同,因此令用户感到惊讶。 它也不同于用户在通过 Babelesm 等工具使用转译时所熟悉的行为。

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.