Auto external 与 externals type
问题背景
库构建默认不应该把所有依赖都打进产物。对于 npm library,很多依赖应该留给消费者安装和解析。Rslib 的 autoExternal 就是把 package.json 中的依赖自动转成 bundler external。
但 external 不是只有“包名是否 external”这一维。它还有:
- 哪类 dependency 默认 external。
- 用户 external 与 autoExternal 的优先级。
- subpath import 如何匹配。
- 不同 format 用什么 externalsType。
- ESM 下 commonjs issuer external 是否需要 warning。
这些细节不理解,产物很容易“构建成功但发布后坏”。
默认 external 哪些依赖
默认策略:
原因:
dependencies是发布后消费者会安装的运行时依赖。peerDependencies通常由消费者提供,例如 React、Vue。optionalDependencies也属于运行时依赖,只是可选安装。devDependencies默认只用于构建或测试,不应假设消费者环境有它。
如果用户确实希望 external devDependency,可以显式配置 autoExternal.devDependencies 或 output.externals。
哪些 format 默认启用 autoExternal
Rslib 中 getAutoExternalDefaultValue 根据 format 判断默认值。中间库产物 format 更倾向 external,最终 bundle format 不一定 external。
通常:
- ESM/CJS 这类 library intermediate format 默认 external。
- UMD/IIFE/MF 这类最终运行产物默认更倾向 bundle,除非用户显式 external。
这是因为 ESM/CJS 常发布到 npm,被下游 bundler 或 Node 消费;UMD/IIFE 常直接给浏览器消费,默认把依赖打进去更符合预期。
subpath import
Auto external 不只匹配包根:
如果 react 被 external,react/jsx-runtime 也应该 external。Rslib 为每个依赖生成:
- 字符串 external:
react - 正则 external:
^react($|/|\\)
这样 subpath import 能一起匹配。
用户 externals 优先
用户显式配置 output.externals 时,优先级高于 autoExternal。典型例子:
如果 autoExternal 仍然再生成 react,就会有冲突。Rslib 在用户 externals 是 object 时会把这些 key 从 autoExternal 里排除。
externalsType 按 format 变化
同样 external 一个 react,不同 format 的输出语义不同:
所以 external 不只是包名列表,还要配合 format。
ESM 下 commonjs issuer warning
Rslib 有一个看起来很特殊的 warning:当 ESM format 中,CommonJS issuer external 了某个 request,可能提示用户设置 external type。
背景是,源码可能包含:
但输出是 ESM format,external type 是 module-import。这会让 CommonJS request 以 ESM external 方式处理,可能不符合用户预期。
Rslib 不直接禁止,而是 warning,提示用户如果需要其他 external type,就显式设置 output.externals。
Phantom dependency 风险
Bundleless external 中有一段注释提到 phantom dependency。场景是:
如果 react 能从本地 node_modules resolve,但 package.json 没有声明它,构建时可能看起来成功。发布后消费者安装你的包,不一定有 react,产物就坏了。
Rslib 在 resolver 失败时会保留原 request 并 debug 提示用户把 npm 包加到 dependencies 或 peerDependencies。这是“发布后正确性”的防护。
修改风险
改 autoExternal 时必须检查:
- dependencies、peerDependencies、optionalDependencies、devDependencies。
- subpath import。
- 用户 externals object/string/array。
- ESM/CJS output。
- bundleless 与 bundle。
- dts bundle 的 bundledPackages。
尤其要注意:JS external 和 dts external 应保持语义一致,否则 JS 产物和类型产物边界不同。