如何在Vite/Rollup的transform或transformIndexHtml中获取输入键名?
问题场景
我要开发一款文件路由Vite插件,翻了Awesome Vite和Rollup Awesome没找到合适的,也懒得挨个筛选。核心问题是Rollup的input选项可以给文件指定名称,用来关联输出和入口文件,但不知道怎么在Rollup的transform或者Vite的transformIndexHtml钩子中拿到当前的转换名称。
我的需求很明确:在transform的处理函数里拿到类似about/know-me的输入名称,把HTML里的%SCRIPT_SRC%替换成对应路径。整体构想是插件扫描所有以.page.tsx结尾的脚本文件,生成对应的.html页面,同时根据文件名存储临时配置,这样就能调用config[name].script。说白了就是想知道怎么在transform函数里同步或者异步获取当前编译的输入文件名。
解决方案
核心思路
在Vite插件里,最直接的方式是先在configResolved钩子中收集「文件路径→input名称」的映射表,之后在transform或transformIndexHtml钩子中,通过当前文件的路径反向匹配对应的input名称。如果是动态生成的HTML,也可以在生成时嵌入标识,直接在transform阶段提取。
具体实现步骤
提前收集入口映射
在插件的configResolved钩子中,遍历Rollup的input配置,把每个入口的绝对路径和对应的input名称存进映射表:// 插件实例中初始化映射表 this.inputMap = new Map(); // 在configResolved钩子中填充映射 configResolved(config) { const { root, build: { rollupOptions: { input } } } = config; for (const [inputName, filePath] of Object.entries(input)) { const absFilePath = path.resolve(root, filePath); this.inputMap.set(absFilePath, inputName); } // 同时存储自定义配置,比如从.page.tsx文件解析的配置 this.pageConfigs = await this.scanPageConfigs(root); }在transform钩子中匹配input名称
拿到当前转换文件的绝对路径,去映射表里找对应的input名称,再替换模板变量:async transform(code, id) { // 只处理HTML文件 if (!id.endsWith('.html')) return code; const inputName = this.inputMap.get(id); if (!inputName) return code; // 从自定义配置中拿到脚本路径 const scriptSrc = this.pageConfigs[inputName]?.script || `/assets/${inputName}.js`; return code.replace('%SCRIPT_SRC%', scriptSrc); }适配transformIndexHtml钩子
这个钩子的上下文参数context包含bundle信息,可以通过chunk的name关联到input名称:transformIndexHtml(html, context) { if (!context.bundle) return html; // 找到当前HTML对应的chunk const targetChunk = Object.values(context.bundle).find( chunk => chunk.type === 'asset' && chunk.fileName === context.filename ); if (!targetChunk?.name) return html; const scriptSrc = this.pageConfigs[targetChunk.name]?.script || `/assets/${targetChunk.name}.js`; return html.replace('%SCRIPT_SRC%', scriptSrc); }
备用方案:嵌入标识直接提取
如果是动态生成的HTML,建议生成时就把input名称以注释或自定义属性嵌入,避免路径匹配的问题:
<!-- input-name: about/know-me --> <script src="%SCRIPT_SRC%"></script>
然后在transform中直接解析这个注释:
transform(code) { const matchResult = code.match(/<!-- input-name: (.*?) -->/); if (!matchResult) return code; const inputName = matchResult[1]; const scriptSrc = this.pageConfigs[inputName]?.script || `/assets/${inputName}.js`; return code.replace('%SCRIPT_SRC%', scriptSrc); }
内容的提问来源于stack exchange,提问作者Farhan M Sabran

