需求
我们在使用 vite 开发项目中,遇到一个需要在打包前,将 ts 文件中的变量导出后,生成对应的描述文件,比如以下场景
// src/vars.ts
import redColor from './red
const colors = {
red: redColor,
white: "#fff",
};
export { colors };
我们希望在 vite 的 configServer 和 beforeBuild 这两个钩子之前,将 src/vars.ts
中的 colors 获取到,然后将其生成一个对应的 src/colors.css
文件,里面内容为
.color-red {
color: #f20;
}
.color-white {
color: #fff;
}
如果仅仅通过添加一个 watch 脚本来通过正则处理,这不是太理想,因为 vars.ts
中还可能包含引入的 module,直接通过 import('src/vars.ts'')
的写法,会在 node 中报错,无法处理 .ts
文件
因此我们的思路是,首先以 vars.ts
作为入口文件,然后通过 vite 将其打包后,获取到 output.code,然后让 node 运行 output.code 得到最终的 colors
因为本身我们就是使用 vite 所以直接用 vite 提供的 build 来打包
import { build } from "vite";
import path from "path";
import Ajv from "ajv";
import vm from "vm";
import fs from "fs-extra";
const entryFilePath = path.join("src", "vars.ts");
const outputFolderPath = path.join("src", "color.css");
function runBuild() {
const filePath = `${process.cwd()}/${entryFilePath}`;
const outputPath = `${process.cwd()}/${outputFolderPath}`;
buildCsss(filePath, outputPath);
}
function buildCsss(filePath, outputPath) {
console.time("Pre Complied CSS");
build({
mode: "development",
root: `${process.cwd()}/src`,
build: {
write: false,
commonjsOptions: {
esmExternals: true,
},
emptyOutDir: false,
sourcemap: false,
cssCodeSplit: false,
minify: false,
lib: {
entry: filePath,
// 注意,由于我们需要将文件打包到一起,所以用的 iife 格式
// 这里给一个全局名称,方便等等在上下文中获取
name: "_",
formats: ["iife"],
},
},
}).then((data) => {
const script = new vm.Script(data[0].output[0].code);
const ctx: any = {};
script.runInNewContext(ctx);
// 获取到 vm 执行后上下文中的 colors
const colors = ctx._.colors;
// todo 根据 colors 变量生成 css 代码
const cssCode = "";
fs.ensureDirSync(path.dirname(outputPath));
fs.writeFileSync(outputPath, cssCode);
console.timeEnd("Pre Complied CSS");
});
}
runBuild();
总结
通过 vite 首先将 ts 文件打包成单个的 iffe 格式文件, 然后通过 vm Script 来执行它,这样就能从上下文中获取到 colors 后续如何生成文件,这个就比较简单了