Rolldown 未支持特性测试清单全解读:从 Rollup 兼容性测试窥探实现边界
2026/9/15 19:26:53 网站建设 项目流程

Rolldown 未支持特性测试清单全解读:从 Rollup 兼容性测试窥探实现边界

【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown

本篇文章聚焦 Rolldown 仓库中一份特殊的“豁免台账”——ignored-by-unsupported-features.md。它记录了 Rolldown 在运行 Rollup 官方测试套件时,因尚未支持或未完全兼容的插件能力、输出选项与语言特性而被跳过的全部测试用例。读完本文,你将理解这份清单的加载与执行机制、测试 ID 的编码规则、321 条被豁免用例背后的三大类兼容性差距,以及如何用它追踪 Rolldown 与 Rollup 行为对齐的进度。

这份清单的定位:兼容性测试的“未支持特性”豁免台账

Rolldown 的目标是与 Rollup 保持行为对齐,其验证手段并非另起炉灶,而是直接运行 Rollup 自己的测试套件。仓库中的 packages/rollup-tests/README.md 明确写道:packages/rollup-tests/test下的每个测试用例都会代理到项目根目录rollupgit submodule 中的对应测试,从而在 Rolldown 上复跑 Rollup 的全部测试。

然而,Rollup 与 Rolldown 的差距并非一蹴而就,因此测试需要分层豁免。在packages/rollup-tests/src目录下,共存在四种“跳过”机制,各自针对不同的差距类型:

豁免文件含义
ignored-tests.js已迁移到其他位置、依赖第三方插件、测试基础设施差异,或属于“预期行为差异”的用例
ignored-by-unsupported-features.md(本文主题)因 Rolldown 尚未支持/未完全兼容某项特性而被跳过的用例
ignored-treeshaking-tests.js与 tree-shaking 相关、暂不参与对齐的用例
ignored-passed-snapshot-different-tests.js行为已通过、但输出快照与 Rollup 不同的用例

从当前 status.md 的统计可以看到这套体系的全貌:

状态分类数量
failed0
skipFailed296
ignored106
ignored(unsupported features)321
ignored(treeshaking)327
ignored(behavior passed, snapshot different)163
passed1214

也就是说,在总计约 2400 余条 Rollup 测试中,已有 1214 条完全通过,而“未支持特性”豁免占据了 321 条——这是仅次于 tree-shaking 豁免的第二大差距来源,也是理解 Rolldown 当前实现边界最直接的入口。

清单如何被加载与执行:从 Markdown 到测试跳过

这份 Markdown 并不是给人看的静态说明,它同时是机器可读的豁免规则文件。核心解析逻辑位于 packages/rollup-tests/src/intercept/utils.js:

function loadUnsupportedFeaturesIgnoredTests() { const unsupportedIgnoredTests = [] const content = fs.readFileSync(path.join(__dirname, '../ignored-by-unsupported-features.md'), 'utf-8') const matches = content.match(/- (.*)/g) if (matches) { for (const id of matches) { unsupportedIgnoredTests.push(id.replace('- ', '').trim()) } } return unsupportedIgnoredTests }

解析规则非常朴素:凡是文件中以-开头的行,其内容即被视为一个测试 ID。随后的shouldIgnoredTest(同文件 L67-L69)会判断某个测试 ID 是否命中四种豁免集合之一:

function shouldIgnoredTest(id) { return ignoredTests.has(id) || ignoredSnapshotDifferentTests.has(id) || ignoredTreeshakingTests.has(id) || unsupportedFeaturesIgnoredTests.find((test) => test.includes(id)) }

实际跳过动作发生在测试运行器的钩子中。update-test-status.js 的beforeEach会在每个用例执行前检查shouldIgnoredTest(id),命中则直接this.currentTest?.skip()afterEach则统计通过/失败数量,最终将结果写入status.jsonstatus.md,并在after钩子中强制process.exit(0)以避免残留的 Rust 进程阻塞测试结束。运行与更新状态的命令来自 justfile:just test-node-rollup运行测试,just test-node-rollup --update运行并刷新状态文件(注意--update--grep互斥)。

测试 ID 的编码规则

要读懂清单中的每一行,必须先理解测试 ID 的生成方式。见 utils.js:

function calcTestId(test) { const paths = test.titlePath() return paths.join('@') }

测试 ID 由 Mocha 的test.titlePath()数组以@拼接而成,因此形如rollup@function@preload-module: allows pre-loading modules via this.load,含义为:测试套件根(rollup)@ 测试目录(function)@ 用例名: 用例描述。常见的一级目录包括:

  • function:构建行为与运行期行为测试(对应 test/function/index.js 代理逻辑)
  • form:输出格式/渲染快照测试
  • hooks:插件钩子执行语义测试
  • sourcemaps:sourcemap 相关测试
  • chunking form:代码分割下的输出格式测试
  • cliwatchmisctypescript等其他套件

部分 ID 末尾带@generates es(或cjssystem)后缀,表示该用例是同一目录在不同输出格式下的变体,例如rollup@form@compact: supports compact output with compact: true@generates es

插件相关未支持特性(Plugin related)

清单第一部分记录的是插件体系中的兼容性缺口,共 17 组。这些特性主要涉及PluginContext能力、插件钩子返回值契约与并行插件机制,是生态插件能否无缝迁移的关键。

钩子参数与返回值契约的差异

  • TheNormalizedOptionsat hooks is not compatible with rollup:options/outputOptions 钩子中拿到的是未完全规范化(Normalized)的选项对象,影响options-hookoutput-options-hook两个用例——它们依赖在钩子中读取并修改规范化后的完整选项。
  • Theloadhook returnastis not supportedload钩子返回自定义 AST(ast字段)目前不被支持,豁免了uses-supplied-astcustom-ast
  • TheresolveIdhookresolvedByis not supportedresolveId返回对象中的resolvedBy字段尚未实现,豁免validate-resolved-by-logic
  • TheshouldTransformCachedModulehook is not supported:缓存模块变换钩子缺失,豁免plugin-error-should-transform
  • TheresolveDynamicImporthookspecifier: AstNodenot supported:动态导入解析钩子无法接收原始 AST 节点,豁免dynamic-import-unresolvabledynamic-import-expression
  • TherenderDynamicImport/resolveImportMeta/shouldTransformCachedModulehooks not supported:输出期动态导入渲染与 import.meta 解析钩子缺失,豁免enforce-plugin-order

插件编排与并行语义

  • The pluginsequentialis not supportedsequential: true声明(保证并行钩子的顺序执行)尚未实现,豁免enforce-sequential-plugin-order(function 套件)与 watch 模式下的同类用例。
  • TherenderDynamicImporthook not supported:自定义动态导入处理钩子缺失,豁免custom-dynamic-import-no-interop@generates es)。

PluginContext 能力缺口

  • ThePluginContext.parsedoes not supportallowReturnOutsideFunctionoption:上下文解析不支持在函数外返回语句,豁免parse-return-outside-function
  • ThePluginContext.cacheis not supported:插件级缓存 API 完全缺失,共豁免 7 条用例:匿名插件对 cache 的 delete/get/has/set(各 1 条)、重名插件无 cache key 时访问缓存、以及 2 条与 transform 缓存交互的 hooks 用例。
  • ThePluginContext.loadis not fully supportedthis.load预加载能力不完整,豁免preload-cyclic-modulepreload-modulemodule-side-effects@writablemodify-meta——后两者涉及在resolveId阶段加载入口模块并修改ModuleInfo的语义。
  • ThemaxParallelFileOpsis not supported:并行文件操作数上限选项缺失,豁免max-parallel-file-operations的 default/error/infinity/set/with-plugin 五条用例。
  • ThePluginContext.emitFileemit chunk is only supported partially:通过emitFile发射 chunk 仅部分支持,主要缺口集中在隐式依赖(implicit dependencies)校验与文件名可用性时序,共豁免 17 条:implicit-dependencies@*系列(7 条)、emit-file@set-asset-source-chunkemit-file@modules-loadedemit-file@invalid-chunk-id、三个chunk-filename-not-available*(buildEnd/renderStart/常规)、file-references-in-bundlehooks@caches chunk emission in transform hook
  • ThePluginContext.emitFileemit prebuilt chunk is not supported:发射预构建 chunk(直接提供 code/filename)缺失,豁免prebuilt-chunkinvalid-prebuilt-chunk-filenameinvalid-prebuit-chunk-code
  • ThePluginContext.setAssetSourceis not supported:资源源码设置 API 缺失,豁免 9 条用例,包括空资源源码校验(invalid2/3/4)、transform 钩子中设置、outputOptions 钩子中设置、重复设置(twice/twice2)、非法 id,以及hooks@keeps emitted ids stable between runs
  • originalFileName/originalFileNamesis not supported properly:发射资源时回传原始文件名到其他钩子的能力不完善,豁免deprecated@emit-file@original-file-nameoriginal-file-nameoriginal-file-names三条。
  • import.meta.ROLLUP_FILE_URL_OBJ_*is not supported:以 URL 对象形式引用发射资源的占位符缺失,豁免resolve-file-url-obj@generates cjs@generates es两条)。

选项相关未支持特性(Options related)

第二部分针对的是输入/输出选项层面的差距,共 18 组。这些选项大多属于构建产物形态控制能力,对追求“开箱即用替代 Rollup”的使用者影响最直接。

输出格式与代码形态选项

  • Theoutput.formatsystemjs is not supported:SystemJS 输出格式整体缺失,是单组豁免量最大的选项类缺口,共 20 条用例,覆盖system-commentssystem-default-commentssystem-export-declarationssystem-export-destructuring-declarationsystem-export-rendering(-compact)system-module-reservedsystem-multiple-export-bindingssystem-null-setterssystem-reexportssystem-semicolonsystem-uninitializedimport-namespace-systemjsmodify-export-semi等全部渲染细节。
  • Theformat: amdnot supported:AMD 格式的选项校验缺失,豁免amd-auto-id-idamd-base-path-idamd-base-path三条(均涉及amd.autoId/amd.basePath/amd.id的组合冲突校验)。
  • Theoutput.compactis not supported:紧凑输出模式缺失,豁免inlined-dynamic-namespace-compactcompactcompact-multiple-imports@generates es)、form@compact@generates es)。
  • Theoutput.validateis not supported:输出代码语法校验选项缺失,豁免validate-output
  • Theoutput.interopis not supported:CJS/外部模块 interop 策略完全未实现,是第二大的选项类豁免组,共 16 条,覆盖interop-auto-live-bindingsinterop-auto-no-live-bindingsinterop-default-conflictinterop-default-only*系列、interop-defaultinterop-esmoduleinvalid-interopdeconflicts-interopinterop-per-dependency*interop-per-reexported-dependency
  • Theoutput.generatedCodeis not supported:产物代码风格选项缺失,豁免 arrow-functions、const-bindings、object-shorthand、reserved-names-as-props 四个子项的 true/false 全组合(function/form 双套件)及unknown-generated-code-value
  • Theoutput.generatedCode.presetis not supported:代码风格预设缺失,豁免generated-code-presets@es2015@es5preset-with-overrideunknown-generated-code-preset。注意在测试代理 test/function/index.js 中,Rolldown 默认使用generatedCode.preset: 'es2015',而测试侧统一强制为es5以保证对比基准一致。
  • Theoutput.generatedCode.symbolsis not supported properlySymbol.toStringTag相关产物符号生成不完善,豁免name-conflict-symbolnamespace-tostring@*系列(dynamic-import-default-mode/dynamic-import/external-namespaces/property-descriptor)共 4 条。
  • Theoutput.sourcemapBaseUrlis not compatible yet:sourcemap 基础 URL 前缀选项不兼容,豁免sourcemap-base-url-invalidsourcemap-base-url-without-trailing-slash@generates es)、sourcemap-base-url@generates es)。

模块组织与分块选项

  • Theoutput.preserveModulesis not compatible yet:保留模块结构的输出模式尚不兼容,豁免 12 条用例:preserve-modules-default-mode-namespacecircular-preserve-modulesmissing-export-preserve-modulespreserve-modules-circular-orderpreserve-modules@*校验系列(invalid-default-export-mode/invalid-no-preserve-entry-signatures/invalid-none-export-mode/manual-chunks/mixed-exports)、synthetic-named-exports@preserve-modulescircular-namespace-reexport-preserve-modules
  • Theoutput.manualChunksis not compatible:手动分块能力不兼容,豁免 8 条:manual-chunks-conflictmanual-chunks-include-external-modules(-3)manual-chunks-infocircular-namespace-reexport-manual-chunksemit-chunk-manual-asset-sourceemit-chunk-manualmanual-chunks-order
  • Theoutput.treeshake.presetis not supported:tree-shaking 预设缺失,豁免unknown-treeshake-preset
  • Theoutput.treeshake.moduleSideEffectis not compatible with rollupmoduleSideEffects与插件resolveId的交互语义不兼容,豁免module-side-effects@resolve-id-externalmodule-side-effects@resolve-id

输入侧选项与产物 API

  • Theinput.perfandbundle.getTimings()is not supported:性能计时选项与getTimings()API 缺失,豁免adds-timings-to-bundle-when-codesplittingadds-timings-to-bundle
  • Theinput.moduleContextis not supported:模块级this上下文定制选项缺失,豁免custom-module-context-functioncustom-module-context@generates es)。
  • TheBundle.cacheis not supportedbundle对象的cache/modules数组信息缺失,豁免module-tree(#903)、has-modules-array
  • TheModuleInfois not compatible with rollup:插件可见的模块信息对象不兼容,豁免 9 条:plugin-module-information(-no-cache)module-parsed-hookhas-default-exportcontext-resolvecheck-exports-exportedBindings-as-a-supplementary-testload-resolve-dependenciesimportedIdResolutions)、resolve-relative-external-id
  • The chunk information is not compatible with rollup:chunk 级信息不兼容,豁免form@addon-functions@generates es)与hooks@supports generateBundle hook including reporting rendered exports and source length(涉及modules.dep.renderedExports/removedExports)。

功能特性相关未支持(Features)

第三部分记录的是语言特性与运行期语义层面的差距,共 14 组,是三类中粒度最细、最接近 ECMAScript 规范的部分。

  • ThesyntheticNamedExportsis not supported:合成具名导出特性整体缺失,是全文最大的单组豁免,共 28 条,覆盖入口/动态导入/命名空间/循环依赖/跨 chunk 去冲突/回退导出(fallback)/in运算符优化等全部语义。
  • Import Assertions is not supportedassert形式的导入断言缺失,豁免 6 条:plugin-assertions-this-resolve(两条)、warn-assertion-conflictswarn-unresolvable-assertionsdeprecated@removes-dynamic-assertionsdeprecated@removes-static-attributes
  • Import attributes is not supportedwith形式的导入属性缺失,共 21 条,覆盖静态/动态导入属性的保留与移除、插件在resolveId/resolveDynamicImport中读写属性、resolveFileUrl/resolveImportMeta附加属性、configure-file-url、以及 deprecated 的load/transform返回属性行为。
  • Source phase import is not supportedimport source阶段导入缺失,豁免 5 条:source-phase-imports-externalsource-phase-dynamic-import-error(-resolved)source-phase-format-unsupportedsource-phase-import-error
  • watch behavior is not compatible yet:watch 模式行为不兼容,豁免hooks@allows to enforce plugin hook order in watch mode
  • escaping external id is not supported:外部模块 ID 的引号转义处理缺失,豁免form@quote-id@generates es)。
  • removeuse strictfrom function body:从函数体中移除use strict指令的行为缺失,豁免function-use-strict-directive-removed
  • The namespace object is not compatible with rollup:命名空间对象语义不兼容(null 原型、冻结、arguments占位、动态导入默认导出模式、外部实时绑定等),共 13 条,包括namespaces-have-null-prototypenamespaces-are-frozennamespace-overrideescape-argumentsdynamic-import-only-defaultdynamic-import-default-mode-facadechunking-duplicate-reexportnamespace-tostring@interop-property-descriptorexternal-dynamic-import-live-binding(-compact)no-external-live-bindings(-compact)
  • hasOwnPropertyexport is not handled properly:导出名为hasOwnProperty时的语义处理不完善,豁免re-export-own
  • __proto__export is not properly handled:导出名为__proto__时与 CJS 转译器的交互不完善,豁免cjs-transpiler-re-exports-1cjs-transpiler-re-exports(均@generates cjs,涉及output.externalLiveBindingsoutput.reExportProtoFromExternal)。
  • source map combine logic does not support coarse sourcemap well enough:粗粒度 sourcemap 的合并逻辑不足,豁免combined-sourcemap-3@generates es)。
  • strictDeprecationsoption is not supported:严格弃用告警选项缺失,豁免deprecations@*系列 7 条(externalImportAssertionsasset-filename-nameasset-filename-originalfilenameasset-name-in-bundleasset-originalfilename-in-bundleasset-render-chunk-originalfilename-in-bundleasset-render-chunk-name-in-bundle)。
  • The error/warning information is not compatible with rollup:错误/警告的信息载体不兼容(错误码、cause属性、日志钩子语义),豁免 8 条,例如banner-and-footer(期望ADDON_ERROR实得PLUGIN_ERROR)、conflicting-reexports@named-import(期望AMBIGUOUS_EXTERNAL_NAMESPACES实得MISSING_EXPORT)、logging@handle-logs-in-pluginssupports renderError hook等。
  • The error/warning not implement:错误/警告类型本身尚未实现,是清单中条目最多的单组(约 55 条),几乎每一行都标注了“期望的 Rollup 错误/警告码 vs Rolldown 实际产物”,例如INVALID_OPTIONvsGenericFailureVALIDATION_ERRORvsInvalidArgMISSING_EXPORT应为警告而非错误、EMPTY_BUNDLE/NAMESPACE_CONFLICT/THIS_IS_UNDEFINED/SOURCEMAP_ERROR/INVALID_ANNOTATION等告警的缺失或错位。部分条目还标注了上游来源,如ast-validations@redeclare-import-var关联 oxc-project/oxc issue #15961。

从清单到实现:兼容性差距的观测与演进

清单是"活文档",也是 roadmap

这份清单不是一次性产物。任何开发者都可以在修复某项特性后,从文件中删除对应的-行,使相关测试重新进入执行队列;反向地,新增未支持特性时也应把对应测试 ID 追加进来。由于解析逻辑完全基于-前缀的行匹配,维护时只需遵守“一行一个测试 ID”的格式约定即可,无需改动 intercept/utils.js 的代码。

测试代理与格式变体

被豁免的用例大多仍由 test/function/index.js 等代理文件按 Rollup 原样加载(其测试样本目录指向../../../../rollup/test/function/samples,即根目录 git submodule)。代理层还会为对比公平性注入一些默认值,例如禁用inlineConst优化、默认dynamicImportInCjs: true、强制generatedCode.preset: 'es5'等,因此清单中@generates es/cjs/system后缀标识的变体正是同一用例在多格式参数化下的独立 ID。

如何自行验证当前状态

在项目根目录执行just test-node-rollup即可复现测试;使用just test-node-rollup --update会在结束后刷新 status.json 与 status.md。运行期间,update-test-status.js会为每个用例打印其 ID(console.log(id)),便于对照清单逐条核验:某个用例若从“ignored(unsupported features)”转为通过,通常意味着对应特性已落地。

结语

ignored-by-unsupported-features.md表面上是一份被跳过的测试列表,实际上是一张经过机器验证的兼容性差距地图:插件体系(PluginContext、emitFile、缓存、钩子契约)、选项体系(SystemJS/AMD 格式、interop、generatedCode、preserveModules/manualChunks)、语言特性(syntheticNamedExports、import attributes、source phase import、namespace 语义)三大维度的边界被 321 条用例精确锚定。对于使用 Rolldown 的开发者,它是排查“为什么这个 Rollup 选项/插件行为不一致”的权威索引;对于贡献者,它则是按图索骥、逐项消除差距的路线图——每删掉一行-开头的内容,就意味着 Rolldown 与 Rollup 的行为对齐又前进了一步。

【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询