Rolldown 的 shimMissingExports 配置详解为缺失导出自动生成 Shim 变量【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown导读在 JavaScript/TypeScript 打包中导入了一个并不存在的导出是常见的隐患。默认情况下 Rolldown 会直接抛出构建错误而开启shimMissingExports: true后Rolldown 会为缺失的导出自动生成值为undefined的 shim 变量让构建继续推进。本文以 shim-missing-exports.md 为核心骨架结合 Rolldown 源码中的实现链路说明该选项的配置方式、典型场景、底层工作原理及其边界限制帮助你决定何时开启、开启后代码会发生什么。选项概览定义、类型与默认值shimMissingExports是 Rolldown 构建输入Input Options中的一个布尔开关其官方定义位于 TypeScript 侧的类型声明中定义位置input-options.ts类型shimMissingExports?: boolean语义When true, creates shim variables for missing exports instead of throwing an error.当为true时为缺失导出创建 shim 变量而不是抛出错误默认值false关闭从类型角度看它属于可选布尔值一旦经过规范化Normalize在 normalized-input-options.ts 中会被收敛为确定的boolean供后续构建流程统一读取。快速上手如何开启 shimming直接在 Rolldown 配置文件中加入一行即可export default { shimMissingExports: true, };上述示例来自官方文档 shim-missing-exports.md也是该选项最标准的用法。配置入口支持多种形式既可以在rolldown.config.js中按上图书写也可以在使用 Node API 时作为InputOptions的一部分传入import { rolldown } from rolldown; const bundle await rolldown({ input: src/index.js, shimMissingExports: true, });典型场景还原导出不存在的重导出官方文档给出了一个非常典型、容易踩坑的场景——从一个模块重导出re-export一个它并不存在的名称。module-a.jsexport { nonExistent } from ./module-b.js;module-b.js// nonExistent is not actually exported here export const something value;在上述代码中module-a.js声明要重导出nonExistent但module-b.js实际只导出了something。此时shimMissingExports: false默认值Rolldown 在链接linking阶段检测到该导入在目标模块中无法匹配到任何导出会抛出构建错误整个打包失败shimMissingExports: trueRolldown 不会报错而是自动创建一个 shim 变量使打包正常完成。官方文档给出简化后的产物示意// Bundled output (simplified) const nonExistent undefined; export { nonExistent, something };可以看到shim 变量会被声明并赋值为undefined同时被并入导出集合从而保证导入方拿到的确实是一个已声明的标识符运行时行为与真实导出缺失的语义一致访问即为undefined而不是直接触发 ReferenceError 之外的构建中断。关闭时的默认行为MissingExport 构建错误当选项保持默认值false时上述场景会触发 Rolldown 的 MissingExport 错误。该诊断事件定义在 missing_export.rs其错误消息格式为nonExistent is not exported by module-b.js, imported by module-a.js.该错误属于MissingExportError事件类型Rolldown 会在诊断中定位到导入方的源文件与imported_specifier的精确 spanimported_specifier_span用 Missing export 标签标注出错位置支持附带可选的note说明信息。在 packages/rolldown/tests/fixtures/verify-options/_config.ts 的配置校验测试中也能印证默认值语义——测试断言规范化后的option.shimMissingExports为false。开启时的工作原理链接阶段的 shim 注入理解为什么开启后就不报错了需要进入 Rust 核心实现的链接link阶段。关键逻辑位于 bind_imports_and_exports.rsif let Module::Normal(importee) self.index_modules[tracker.importee] { if (self.options.shim_missing_exports || matches!(importee.module_type, ModuleType::Empty)) matches!(ret, MatchImportKind::NoMatch) { match tracker.imported { Specifier::Star unreachable!(star should always exist, no need to shim), Specifier::Literal(imported) { let shimmed_symbol_ref self.metas[tracker.importee] .shimmed_missing_exports .entry(imported.clone()) .or_insert_with(|| { self.symbol_db.create_facade_root_symbol_ref(tracker.importee, imported.as_str()) }); return MatchImportKind::Normal(MatchImportKindNormal { symbol: *shimmed_symbol_ref, reexports: vec![], }); } } } }这段代码揭示了几个关键实现事实判定条件仅当导入在目标模块中匹配结果为NoMatch没有任何可绑定符号时才考虑 shim 兜底按需创建、幂等缓存shim 符号通过shimmed_missing_exports这个FxHashMapCompactStr, SymbolRef缓存定义见 linking_metadata.rs同一模块中同名缺失导出的 shim 只会创建一次entry(...).or_insert_with(...)shim 的本质是一个门面根符号facade root symbol ref它并不指向任何真实存在的声明而是由create_facade_root_symbol_ref动态创建一个占位符号之后由后续阶段为其生成实际的变量声明。从占位符号到真实声明shim 符号创建后链接阶段在 create_exports_for_ecma_modules.rs 中为每个 shim 生成一个 facadeStmtInfo声明该符号的语句信息。注释明确说明了目的Create facade StmtInfo that declares variables based on the missing exports, so they can participate in the symbol de-conflict and tree-shaking process.为缺失导出创建声明变量的 facade StmtInfo使其能参与符号去冲突与 tree-shaking 流程。这意味着 shim 变量并非简单地硬塞进产物而是会参与标识符重命名/去冲突de-conflict避免与真实变量命名碰撞参与 tree-shaking 的引用分析只有被实际引用时才保留。最终模块终结阶段module finalizer在 impl_visit_mut.rs 中消费shimmed_missing_exports集合把 shim 声明写入生成代码最终呈现为文档中展示的const nonExistent undefined;形态。另一个触发条件空模块ModuleType::Empty导入值得注意的一个细节是shim 兜底并不只由shimMissingExports选项触发。在 bind_imports_and_exports.rs 的判定条件中if (self.options.shim_missing_exports || matches!(importee.module_type, ModuleType::Empty))即当被导入模块的类型是ModuleType::Empty空模块没有任何导出内容时Rolldown 同样会为命名导入生成 shim而无需显式开启选项。这是从源码结构中可以直接确认的实现行为空模块场景下默认容忍缺失导出避免对无导出文件做命名导入时直接构建失败。配置传递链路从 TS 选项到 Rust 内部选项shimMissingExports从 JavaScript 侧到 Rust 核心的完整传递路径如下便于排查为什么我的配置没生效TS 类型声明input-options.ts 声明shimMissingExports?: boolean运行时校验validator.ts 用 schema 校验其类型描述为 Create shim variables for missing exportsBinding 转换bindingify-input-options.ts 将输入选项绑定到 Native 侧Rust 侧原始选项inner_bundler_options/mod.rs 中以Optionbool承接默认值收敛prepare_build_context.rs 中raw_options.shim_missing_exports.unwrap_or(false)将None归一为false规范化选项normalized_bundler_options.rs 中变为确定的bool字段供链接阶段等消费。CLI 支持情况除了配置文件与 Node APIRolldown 的 CLI 也暴露了对应的命令行开关。在 CLI 帮助输出快照 cli-e2e.test.ts.snap 中可以看到--shimMissingExports Create shim variables for missing exports.因此在命令行场景下可以直接通过--shimMissingExports开启该行为而不必编写配置文件。边界与注意事项结合源码实现使用该选项时有几点需要明确命名空间导入不受影响import * as ns from ./module-b.js这类星号导入走的是命名空间路径源码中以unreachable!(star should always exist, no need to shim)注释直接说明星号导入总是存在无需 shim。shim 仅针对具名导入Specifier::Literal。shim 值是undefinedshim 变量被声明为undefined后续任何对它的真实访问都会得到undefined。它只解决构建期报错问题不解决运行期语义错误——如果代码逻辑本身依赖该导出有值运行期仍会得到undefined。建议仅在兼容性迁移场景使用默认值false的报错行为其实是一种保护机制能尽早暴露导入不存在这类拼写错误或模块版本不一致问题。开启 shim 后这类错误会被静默掩盖建议仅在迁移遗留代码、第三方依赖存在已知缺失导出且无法修改时启用并在构建后通过产物检查确认缺失导出确实无业务影响。对模块包装的影响shimmed 导出也会参与模块包装逻辑的判断例如 order_wrapping.rs 中会对存在shimmed_missing_exports的模块做特殊处理如避免被判定为可省略包装以确保 shim 声明在产物中正确就位。小结shimMissingExports是 Rolldown 中一个小而关键的容错开关默认关闭时任何缺失导出都会在链接阶段以 MissingExport 错误阻断构建开启后Rolldown 会为缺失的具名导出自动创建值为undefined的 shim 变量并让 shim 完整参与符号去冲突、tree-shaking 与最终代码生成。理解其底层实现链路NoMatch判定 → 门面根符号创建 → facade StmtInfo 生成 → 模块终结阶段落盘有助于你在迁移旧代码与调试疑难构建时做出准确的取舍。【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
