ARTICLE DETAIL

资讯详情

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

Rolldown 插件上下文 `this.getModuleInfo()` 详解:模块信息生命周期与最佳实践

Rolldown 插件上下文 `this.getModuleInfo()` 详解:模块信息生命周期与最佳实践 Rolldown 插件上下文this.getModuleInfo()详解模块信息生命周期与最佳实践【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown构建期插件通过this.getModuleInfo(moduleId)获取指定模块的实时信息是 Rolldown 中实现依赖分析、元信息注入、入口判断等高级插件能力的核心 API。本篇指南以 Rolldown 仓库中 plugin-context-getmoduleinfo.md 为骨架结合 TypeScript 插件层与 Rust 内核实现系统讲解ModuleInfo各字段在构建各阶段的可用性、可变性规则以及正确的使用时机帮助你在编写插件时避开信息尚未就绪的常见陷阱。一、API 概览签名、返回值与数据来源1. 签名与基本用法在 Rolldown 的插件类型定义plugin-context.ts中getModuleInfo的类型为export type GetModuleInfo (moduleId: string) ModuleInfo | null;它接收一个模块 id即解析后的绝对文件路径返回该模块的ModuleInfo对象如果找不到对应模块则返回null因此调用方需要做好空值判断。ModuleInfo接口定义在 module-info.ts核心字段包括字段类型含义idstring模块 id解析后的绝对路径codestring \| null模块源码external 模块或尚未解析时为nullexportsstring[]模块导出的所有变量名isEntryboolean是否为用户或插件定义的入口点importersstring[]静态导入该模块的所有模块 iddynamicImportersstring[]动态导入import()该模块的所有模块 idimportedIdsstring[]该模块静态导入的模块 id 列表dynamicallyImportedIdsstring[]该模块动态导入的模块 id 列表metaobject可自由读写的模块元信息继承自ModuleOptionsmoduleSideEffectsboolean \| no-treeshake \| null模块副作用标记inputFormates \| cjs \| unknown模块格式探测结果实验性字段注意ModuleInfo#ast在 Rolldown 中标注为hidden Not supported访问会抛出 unsupported 错误见 transform-module-info.ts。2. JS 层与 Rust 内核的对接getModuleInfo在 JS 层经由 plugin-context-data.ts 中的getModuleInfo(id, context)实现它先调用 N-API 绑定层context.getModuleInfo(id)拿到内核数据再用transformModuleInfo与插件层的ModuleOptionsmeta、moduleSideEffects合并后返回。绑定层实现位于 binding_plugin_context.rspub fn get_module_info(self, module_id: String) - OptionBindingModuleInfo { self.inner.get_module_info(module_id).map(BindingModuleInfo::new) }Rust 侧真实的数据结构定义在 module_info.rspub struct ModuleInfo { pub code: OptionArcStr, pub id: ModuleId, pub is_entry: bool, pub importers: FxIndexSetModuleId, pub dynamic_importers: FxIndexSetModuleId, pub imported_ids: FxIndexSetModuleId, pub dynamically_imported_ids: FxIndexSetModuleId, pub exports: VecCompactStr, pub input_format: ExportsKind, }可以看到 JS 层ModuleInfo的importers、importedIds等列表在内核中使用FxIndexSet存储保证去重的同时保持插入顺序先发现的导入者先出现。理解这条链路有助于判断getModuleInfo返回的信息是构建过程中不断累积、动态变化的不同 hook 阶段看到的内容可能不同这正是下文要展开的核心。二、字段可用性时间线哪些信息可能不准构建是分阶段推进的模块先被扫描/解析再被发现依赖、被其他模块引用。因此getModuleInfo返回的对象在不同时机呈现的信息完整度不同。原文档明确提示在buildEnd之前该对象只代表当前可获得的模块信息可能是不准确的。逐项拆解如下。1.id自始至终不变模块 id 一旦确定就不会变化任何 hook 阶段读取都可靠可作为模块的稳定标识。2.code与exports解析完成后可用code源码和exports导出列表只有在模块被解析之后才可用也就是在moduleParsedhook 中或在等待this.load返回之后即await this.load({ id })之后。到达这一时点后两者不再变化。注意code对外部external模块或尚未解析的模块为null见 module-info.ts 中code: string | null的注释。3.isEntry一旦为true便不再变化但存在迟到的入口isEntry一旦为true就不会再变化但文档特别强调模块可能在解析完成后才成为入口途径有两种插件调用this.emitFile发射新的入口 chunk插件在resolveIdhook 解析入口时通过this.load检查了某个潜在入口点。因此在transformhook 中不建议依赖isEntry做决策。它只保证在buildEnd之后不再变化。仓库中与之呼应的文档 plugin-hooks-resolveid-isentry.md 专门讨论了resolveId中的isEntry语义可作为延伸阅读。4.importers与dynamicImporters随发现不断追加这两个数组从空数组开始随着新的导入者被发现而不断加入新条目。这意味着在扫描早期一个模块的importers可能为空即使它最终会被多个模块引用。它们同样在buildEnd之后不再变化。5.importedIds与dynamicallyImportedIds依赖解析完成后可用当模块被解析且其依赖已被解析后这两个字段才可用即在moduleParsedhook 中或等待this.load且传入resolveDependencies: true之后。此时它们不再变化。注意this.load的resolveDependencies参数在 Rolldown 的插件上下文实现中plugin-context.ts注释表明 resolveDependencies always true at rolldown即 Rolldown 侧load总是解析依赖返回的ModuleInfo通常已包含完整的importedIds。6.meta与moduleSideEffects唯一可写字段与只读的id、code、importers等不同meta和moduleSideEffects是可写的修改会在buildEnd触发前生效被构建流程拾取。约束如下meta对象本身不要整体覆盖info.meta {...}但可以随时修改其属性info.meta.foo bar来为模块存储元信息这种存到meta而不是插件闭包状态的做法有一个关键优势在使用了缓存的情况下例如 CLI watch 模式meta会随缓存持久化并在重建时恢复而插件内部的闭包状态做不到。JS 层的可写机制在 plugin-context-data.ts 的proxyModuleInfo中实现通过Object.defineProperty为moduleSideEffects定义了 setter写入时调用updateModuleOption把moduleSideEffects、meta、invalidate: true回传到模块选项表meta的修改则通过Object.assign(existing.meta, option.meta)合并进现有对象见updateModuleOption。三、字段状态汇总表把上面的分析整理成一张速查表便于在编写插件时快速对照字段何时可用/可靠之后是否变化可写性id始终不变只读codemoduleParsed/await this.load之后不再变化只读exportsmoduleParsed/await this.load之后不再变化只读isEntry一旦为true不再变但入口可能迟到buildEnd后稳定只读importers从空数组开始随发现追加buildEnd后稳定只读dynamicImporters从空数组开始随发现追加buildEnd后稳定只读importedIdsmoduleParsed/await this.load(resolveDependencies)之后不再变化只读dynamicallyImportedIds同上不再变化只读meta始终可用buildEnd前修改生效可随缓存持久化可写改属性勿整体覆盖moduleSideEffects始终可用buildEnd前修改生效可写核心口诀依赖/源码类字段要看解析时机引用类字段要看扫描进度meta与moduleSideEffects才是插件可以主动写入的通道。四、实战示例在各 hook 中正确使用1. 在moduleParsed中读取已解析模块的信息moduleParsed是读取code、exports、importedIds的安全时机export default { name: inspect-module, moduleParsed(moduleInfo) { console.log(模块 ${moduleInfo.id} 已解析); console.log(导出: ${moduleInfo.exports.join(, )}); console.log(静态依赖: ${moduleInfo.importedIds.join(, )}); console.log(动态依赖: ${moduleInfo.dynamicallyImportedIds.join(, )}); }, };2. 在buildEnd中读取最终稳定的模块图buildEnd之后所有字段含importers、dynamicImporters、isEntry都稳定下来是做全局分析的推荐时机export default { name: report-importers, buildEnd() { for (const id of this.getModuleIds()) { const info this.getModuleInfo(id); if (info) { console.log(模块 ${id}:); console.log( 被静态导入: ${info.importers.length} 次); console.log( 被动态导入: ${info.dynamicImporters.length} 次); console.log( 是否为入口: ${info.isEntry}); } } }, };getModuleIds()同样来自插件上下文plugin-context.ts返回可迭代的模块 id 集合与getModuleInfo配合即可遍历整个模块图。3. 在resolveId/transform中谨慎对待isEntry由于模块可能在解析之后才通过emitFile或load成为入口transform阶段读取到的isEntry可能为false但最终该模块仍是入口。如果需要确定入口身份请在buildEnd之后再下结论或在load/transform中仅把它当作目前已知是入口的弱信号。4. 利用meta存储跨 hook、跨重建的元信息export default { name: meta-writer, transform(code, id) { const info this.getModuleInfo(id); if (info) { // 只修改属性不整体覆盖 meta 对象 info.meta.version 1; info.meta.analyzed true; } return code; }, };由于meta会随缓存持久化在 watch 模式下模块重建后之前写入的meta数据依然可用这是相对插件闭包变量的显著优势。5. 在load/transform中调整moduleSideEffectsexport default { name: side-effects, load(id) { if (id.endsWith(.css)) { const info this.getModuleInfo(id); if (info) info.moduleSideEffects true; } }, };moduleSideEffects的写入会通过proxyModuleInfo的 setter 回传到内核影响摇树tree-shaking对模块副作用的判定。五、与其他插件 API 的关系与延伸阅读this.load加载并解析指定模块返回对应的ModuleInfo可传入resolveDependencies以同时解析依赖使importedIds/dynamicallyImportedIds立即可用详见 plugin-context-load.mdthis.emitFile发射新的 chunk/asset其中type: chunk的入口会走完整的构建管线并可能使模块在解析后成为入口影响isEntry详见 plugin-context-emitfile.mdthis.getModuleIds遍历当前模块图中所有模块 id与getModuleInfo组合实现全图分析resolveId中的isEntry语义plugin-hooks-resolveid-isentry.md插件上下文其他能力addWatchFile、resolve、warn、error等集中在 plugin-context.ts。六、常见误区小结在transform中把isEntry false当作最终结论——入口可能迟到应在buildEnd后判定在早期 hook 中读取importers并认为它完整——它从空数组开始逐步追加在load返回前读取code/exports——需要等待await this.load(...)或moduleParsed整体覆盖info.meta——应只修改其属性否则可能丢失其他插件写入的元信息忽略返回值为null的情况——传入不存在的模块 id 时getModuleInfo返回null直接解构会报错。掌握getModuleInfo的信息时间线就能在 Rolldown 构建管线的正确时机读取到准确、完整的模块信息写出既健壮又高效的构建插件。【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表