ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Rolldown 插件源码转换与 Sourcemap 生成实践:transform / renderChunk / generateBundle 全流程解析

Rolldown 插件源码转换与 Sourcemap 生成实践:transform / renderChunk / generateBundle 全流程解析 Rolldown 插件源码转换与 Sourcemap 生成实践transform / renderChunk / generateBundle 全流程解析【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown本篇指南聚焦 RolldownRust 实现的 JavaScript/TypeScript 打包器插件体系中的**源码转换Source Code Transformations**与 Sourcemap 协作机制插件对源码或 chunk 做改写后如何正确返回map何时返回空 map、何时返回null以及为什么官方不鼓励在generateBundle中做转换。读完本文你将掌握在 Rolldown 插件中编写转换不出错、断点可定位、产物哈希一致的代码转换逻辑并理解其背后的源码级实现依据。一、为什么插件转换源码时必须携带 SourcemapRolldown 的构建管线中transform与renderChunk这类钩子允许插件改写代码。一旦代码被改写行号与列号就会与原始模块产生偏移如果没有 sourcemap 与之配套开发者最终在浏览器 DevTools 里看到的堆栈与断点位置将是完全错乱的。官方约定见 docs/apis/plugin-api/transformations.md是只要插件对源码做了转换就应当自动生成一份 sourcemap除非存在明确的sourceMap: false选项。这条约定的背后是 Rolldown 对 map 的处理策略——它只关心mappings属性其余字段sources、sourcesContent、names、file等都由打包器在最终阶段自动补齐。这一点在源码中有直接印证crates/rolldown/src/utils/process_code_and_sourcemap.rs的prepare_sourcemap会在产物落盘前统一重写 map 的file、sources、sourceContents等字段并按需重建x_google_ignoreList插件返回的 map 只是作为mappings语义的输入参与组合。二、返回 map 的三种正确姿势根据转换的性质插件返回值中的map字段有三种选择官方在文档中给出了完整范式1. 实在无法生成 map返回空 map如果转换本身不涉及逐行的可映射关系例如不基于原始源码行做改动生成一份无意义的 map 反而不如显式声明没有 mapreturn { code: transformedCode, map: { mappings: }, };空mappings表示这段转换结果不对应任何原始源码位置Rolldown 会据此继续处理而不会报错。2. 转换不移动代码保留原 map如果转换只是原地替换而没有增删行、移动代码片段例如纯字符串替换且不改变行结构那么原有的 sourcemap 依然有效直接返回null让 Rolldown 保留并沿用之前的 mapreturn { code: transformedCode, map: null, };3. 正常转换返回由 magic-string 生成的 map对于插入/删除代码片段这类基础转换官方推荐用 magic-string 生成 map配合hires: boundary在代码块边界处保持高精度映射import MagicString from magic-string; export default function myPlugin() { return { name: example, transform(code) { const s new MagicString(code); s.prepend(/* banner */\n); return { code: s.toString(), map: s.generateMap({ hires: boundary }), }; }, }; }需要留意的是Rolldown 对map字段的语义与 Rollup 完全对齐省略mapundefined和显式map: null是两个不同的状态。crates/rolldown_plugin/src/types/hook_transform_output.rs中的HookTransformOutputMap枚举明确区分了三种情形Omitted插件返回了转换结果但没设置mapJS 侧即undefined会被视为可能损坏的 sourcemapNull插件显式声明不提供 sourcemapSourcemap插件返回了真实 map。当renderChunk返回Omitted且当前构建启用了 sourcemap 时Rolldown 会抛出sourcemap_broken警告——这正是crates/rolldown_plugin/src/plugin_driver/output_hooks.rs中render_chunk实现L147-L207所处理的逻辑matches!(r.map, HookTransformOutputMap::Omitted) args.options.is_sourcemap_enabled()时收集一条 severity 为 warning 的诊断。三、转换 Chunk使用 renderChunk 钩子当插件需要改写的是已经渲染完成的 chunk而非单个模块时应使用renderChunk钩子。官方给出的最小示例是给每个 chunk 前置一段 bannerimport MagicString from magic-string; export default function myPlugin() { return { name: example, renderChunk(code) { const s new MagicString(code); s.prepend(/* banner */\n); return { code: s.toString(), map: s.generateMap({ hires: boundary }) }; }, }; }renderChunk 的源码级执行模型在 Rolldown 中renderChunk由 PluginDriver 统一调度见 crates/rolldown_plugin/src/plugin_driver/output_hooks.rs所有注册了renderChunk的插件按order_by_render_chunk_meta的顺序依次执行前一个插件输出的代码args.code会作为后一个插件的输入形成串行转换链每个插件返回的 map 会被压入sourcemap_chain向量最终随代码一起返回给生成阶段生成阶段crates/rolldown/src/stages/generate_stage/render_chunk_to_assets.rs 中的render_chunk_to_assets在instantiate_chunks之后调用render_chunks统一执行这条链。renderChunk钩子收到的参数结构定义在 crates/rolldown_plugin/src/types/hook_render_chunk_args.rs包含options规范化后的打包选项、code当前 chunk 代码、chunk该 chunk 的渲染元信息以及chunks本次生成的全部 chunk 映射表。返回类型则复用HookTransformOutputMap见 crates/rolldown_plugin/src/types/hook_render_chunk_output.rs——这也解释了为什么renderChunk与transform在map三态语义上保持一致。多段 map 的组合与 x_google_ignoreList 重建文档强调如果你在renderChunk中返回了转换对应的 sourcemapRolldown 会把它与之前所有转换产生的 map 组合并根据 options 重建x_google_ignoreList字段。x_google_ignoreList是 Chrome DevTools 用来忽略第三方/框架代码以便调试只命中业务代码的扩展字段。Rolldown 的重建逻辑位于 crates/rolldown/src/utils/process_code_and_sourcemap.rs 的prepare_sourcemapL31-L109遍历当前 map 的全部sources逐个按相对路径计算依据sourcemap_ignore_list选项布尔、字符串/正则或用户提供的函数判断该 source 是否应进入忽略列表对函数形式走异步慢路径对静态值走无异步开销的快路径命中者以 source 索引形式写入x_google_ignoreList。因此插件只需保证自己返回的 map 中sources尽量贴近真实相对路径Rolldown 会在最终阶段统一重建该字段插件侧无需自行维护。四、为什么不推荐在 generateBundle 中转换文档明确表达了态度官方不鼓励在generateBundle中做转换并给出两个硬性原因generateBundle在哈希计算之后运行此时文件名已经定稿。如果在这里改写 chunk 代码产物文件名保留的是未转换代码的哈希转换内容不会反映进文件名缓存失效策略将被破坏.map资产sourcemap 文件此时已经生成完毕直接修改chunk.map并不会同步改变磁盘上/内存中那份.map资产的内容两者会产生不一致。从实现顺序看crates/rolldown/src/stages/generate_stage/render_chunk_to_assets.rs 中chunk 渲染、哈希增补augment_chunk_hash、最终资产定稿finalize_assets都在generate_bundle钩子之前完成插件驱动器的generate_bundle位于 crates/rolldown_plugin/src/plugin_driver/output_hooks.rsL249-L265它拿到的已经是包含.map资产的完整bundle列表——这也与上述时序结论互相印证。五、如果必须在 generateBundle 转换官方完整范式有些场景例如必须基于最终产物整体结构做后处理绕不开generateBundle。官方给出了完整的自洽方案手动组合 map并自己改写.map资产。要点是使用jridgewell/remapping把本次转换的 map 与 chunk 已有的 map 组合组合时以chunk.map为目标逐层 remap回调() null表示不修改 intermediate sources由于.map资产是独立对象必须通过bundle[${chunk.fileName}.map]找到它并同步其sourcemap 精度用hires: boundary低分辨率 map 在组合时可能缩水到近乎为空边界级 hires 能保住映射关键节点。import remapping from jridgewell/remapping; import MagicString from magic-string; export default function myPlugin() { return { name: example, generateBundle(options, bundle) { for (const chunk of Object.values(bundle)) { if (chunk.type ! chunk) continue; const s new MagicString(chunk.code); // ...your transform... if (!s.hasChanged()) continue; // A low-resolution map can compose down to nothing, so keep the mappings at the boundaries. const step s.generateMap({ source: chunk.fileName, hires: boundary }); chunk.code s.toString(); if (chunk.map) { // compose the sourcemap chunk.map remapping([step, chunk.map], () null); // The emitted file comes from this asset, not from chunk.map. const asset bundle[${chunk.fileName}.map]; if (asset) asset.source chunk.map.toString(); } } }, }; }该范式与文档完全一致先以s.hasChanged()做廉价短路没有实际改动就不做任何 map 组合再按代码 → map → .map 资产的顺序逐层同步保证最终产物与 sourcemap 严格配套。六、实战要点速查默认带 map凡转换必返回 map除非构建明确sourceMap: false只关心mappingssources、sourcesContent、file等字段交给 Rolldown 的prepare_sourcemap统一补齐process_code_and_sourcemap.rs插件无需手工维护三态语义严格区分省略 map 会被当作Omitted并在开启 sourcemap 时触发sourcemap_broken警告确定不做映射就显式map: null确定不要映射但需要合法状态就map: { mappings: }hook_transform_output.rs改 chunk 用renderChunk它运行在哈希计算之前map 会自动进链组合x_google_ignoreList由打包器按sourcemap_ignore_list选项重建generateBundle是最后手段运行在哈希与.map资产生成之后文件名哈希不会反映你的改动必须手动 remap 并同步.map资产内容精度用hires: boundarymagic-string的 hires 级别直接影响组合后 map 的保真度Rolldown 内置的 MagicString 绑定同样支持该参数见 crates/string_wizard/src/magic_string/source_map.rs。按上述约定编写转换插件即可在 Rolldown 中获得与 Rollup 一致的语义体验同时让产物 sourcemap 始终保持正确、可调试且哈希可信。【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表