输出与模块格式
为什么 format 是核心维度
Rslib 支持的 format 不只是文件后缀差异。它决定了产物运行方式、外部依赖类型、chunk 加载方式、library 声明方式、runtime chunk 策略和默认 target。
当前 format 包括:
维护 format 逻辑时,不能把某个 format 当成只是 output.library.type 的变化。每个 format 都有一套运行时假设。
ESM
ESM 是 Rslib 的默认 format。核心输出特征:
output.module = true。- Rspack
library.type = "modern-module"。 - chunk loading 使用
import。 - worker chunk loading 使用
import。 filenameHash默认 false。- splitChunks 只允许 async chunks,避免 sync chunk 影响 entry inline。
avoidEntryIife = true。concatenateModules = false。
ESM 的难点主要在 external 和 Node 兼容。Rslib 使用 externalsType = "module-import",但如果源码里从 CommonJS issuer external 了某个 request,可能会让最终 ESM import type 不符合用户预期。因此有 composeExternalsWarnConfig 提示用户显式设置 external type。
ESM target 为 node 时,还会涉及 Node builtins、__dirname、__filename、require shim。默认不启用这些 CJS 全局 shim,但用户可通过 shims.esm 打开。
CJS
CJS 产物核心特征:
output.module = false。- Rspack
library.type = "commonjs-static"。 - chunk loading 使用
require。 - worker chunk loading 使用
async-node。 filenameHash默认 false。- splitChunks 只允许 async chunks。
CJS 的兼容重点是 import.meta.*。源码可能使用 import.meta.url、import.meta.dirname、import.meta.filename,但 CJS 运行时没有这些语义。Rslib 默认为 CJS 启用这些 shim:
import.meta.urlimport.meta.dirnameimport.meta.filename
这些 shim 由 pluginCjsShims 和 EntryChunkPlugin 共同参与。修改 CJS 输出时,必须检查 shebang、"use strict" 和 shim 插入顺序,避免破坏可执行脚本或 strict directive。
UMD
UMD 适合需要兼容 AMD、CommonJS 和浏览器 global 的场景。Rslib 对 UMD 有强约束:
- 必须
bundle: true。 - 关闭 async chunks。
- 关闭 splitChunks。
library.type = "umd"。- 用户可通过
umdName指定 library name。
UMD 不支持 bundleless 是合理的,因为 UMD 目标是一个可直接分发的最终 bundle。保留源码结构和多个相对 import 不符合 UMD 的运行模型。
维护 UMD 时,重点关注:
- externalsType 为
umd。 nodeEnv使用process.env.NODE_ENV。- 不要引入需要 ESM runtime 的配置。
- 多入口或 chunk 拆分是否会破坏单 bundle 假设。
IIFE
IIFE 适合浏览器中直接执行。Rslib 对 IIFE 也要求 bundle: true,并设置:
output.iife = true。library.type = "module"。chunkFormat = false。chunkLoading = "import"。asyncChunks = false。splitChunks = false。globalObject = "globalThis"。
IIFE 的 external type 使用 global。原因是如果对包名如 @pkg 使用 var,可能生成非法变量名;如果使用 umd,又可能受环境中 define 等变量影响。global 是更稳妥的折中。
Module Federation
MF format 和其他 format 的区别最大。它更像应用构建而不是库构建:
- 必须
bundle: true。 - 默认 target 是 web。
dev.writeToDisk = true。- 需要用户使用 Module Federation 插件。
- Rslib 会设置 Rspack output
uniqueName = pkgJson.name。 nodeEnv在 development 和 production 中使用对应字符串。
core 会通过 checkMFPlugin 检查 MF 插件是否存在,避免用户只写 format: "mf" 却没有配置 federation 插件。
MF dev server 只选择 MF environment。普通 ESM/CJS library environment 不会进入 mf-dev。
Target 与 format 的关系
Rslib 的 target 默认规则:
format: "mf"默认web。- 其他 format 默认
node。
这只是默认值,用户仍可配置 output.target。target 会影响:
- Rspack target。
- Node builtins external。
- moduleIds 策略。
- syntax 到 browserslist 的默认推导。
- exe 是否允许生成。
Node target 下,Rslib 会把 Node builtins external 掉。这对库构建很重要,因为把 node:fs、path 等内置模块打进库产物通常不是用户想要的结果。
文件扩展名
format 与扩展名的关系由 getDefaultExtension 处理。维护时应理解三个输入:
format- package.json
type autoExtension
常见目标是:
- ESM 在合适场景输出
.mjs或.js。 - CJS 在合适场景输出
.cjs或.js。 - dts 对应
.d.mts、.d.cts或.d.ts。
文件扩展名不是纯展示问题。Node ESM loader 不做扩展名搜索;bundleless 输出中如果 import 没有扩展名,运行时可能无法解析。因此 bundleless redirect 会主动补扩展名。
Runtime chunk
Rslib 对 runtime chunk 的处理也和 format、bundle、entry 数量有关:
- bundleless 模式使用固定
rslib-runtime。 - bundle 单入口通常不需要单独 runtime chunk。
- bundle 多入口需要 runtime chunk,避免重复 runtime 或入口无法正确共享。
- 多 compiler 时,runtime chunk 名会带 compiler index,避免多个 environment 输出冲突。
Runtime chunk 修改需要特别小心,因为它不一定在简单单入口测试中暴露问题。多入口、多 format 和 bundleless 测试更能覆盖这类风险。
Minify 策略
Rslib 默认的 minify 不是追求最小体积,而是面向库构建的保守优化:
- 开启 JS minify 管线。
- 不 mangle。
- 默认不做 aggressive minify。
- 主要做 unused 和 dead code。
- 保留部分注释和 annotations。
MF 是例外。MF 资源通过网络加载,项目侧不一定再压缩 remoteEntry 和相关资源,因此 MF 会更积极地 minify。
外部依赖类型
不同 format 的 external type 不同:
这意味着同一个 output.externals 在不同 format 下会生成不同运行时引用。维护 externals 时必须同时考虑 format 和 target,而不是只看依赖名。
新增 format 的成本
新增 format 不是加一个字符串。至少要检查:
Format类型。composeFormatConfig。composeExternalsConfig的 externalsType 和 globalObject。composeTargetConfig默认 target 是否特殊。getDefaultExtension。- autoExternal 默认值。
- dts extension。
- runtime chunk。
- CLI 参数说明。
- integration tests 和文档。
如果新增 format 不能明确回答这些问题,就不应进入 core。
排查输出格式问题
输出格式问题通常表现为:
- Node import 失败。
- 浏览器全局变量不存在。
- chunk 加载路径错误。
- external 依赖引用方式不对。
.d.ts扩展名与 JS 不匹配。- bundleless import 指向源码扩展名。
排查建议:
- 先运行
rslib inspect看最终 Rspack output。 - 查看 dist 内实际文件名和 chunk 名。
- 搜索产物内部 import 或 require。
- 对照 format 的 externalsType。
- 检查 package.json
type。 - 检查
autoExtension和redirect。 - 用最小 integration case 断言真实运行结果。