简介
要给大家介绍的是这款 vite-plugin-env-parse
该插件核心功能
- 环境变量值具有确切类型,非 DotEnv 解析出的字符串类型
- 自动生成声明文件,开发中有代码提示
虽然功能不多,但确实解决了开发中环境变量使用的痛点
前置知识
核心逻辑
以下代码为简化后的源码
开发阶段
configResolved 钩子中可以通过 config.env 获取到解析后的环境变量,对变量值进行转换
export default function ViteEnvParse(): PluginOption {
return {
name: 'vite-env-parse',
enforce: 'pre',
configResolved(config) {
const prefixes = arraify(config.envPrefix || ['VITE_']);
// 环境变量类型转换
typeConversion(config.env, prefixes)
// 生成声明文件
if(config.command !== 'build') {
const declareStr = generateDeclarationStr(config.env)
if(declareStr) {
writeDeclarationStr(path.resolve(config.root, 'src/vite-env.d.ts'), declareStr)
}
}
}
}
}
将解析后的环境变量进行类型转换,开发中就可以获得具有类型的环境变量值
function typeConversion(obj, prefixes) {
for (const key in obj) {
if (!obj.hasOwnProperty(key)) continue
if (!prefixes.some((prefix) => key.startsWith(prefix))) continue
const value = obj[key]
// 解析数字和布尔值
try {
obj[key] = JSON.parse(value);
continue
} catch (e) {
obj[key] = value;
}
// 解析数组和对象
try {
obj[key] = eval(`(${value})`)
continue
} catch (e) {
obj[key] = value;
}
}
return obj;
}
根据类型转换后的结果生成或修改声明文件
function writeDeclarationStr(path: string, str: string) {
const importMetaEnvRegexp = /interface ImportMetaEnv\s*\{[\s\S]*?\}/g
if (fs.existsSync(path)) {
// 声明文件存在
const fileContent = fs.readFileSync(path, { encoding: 'utf-8' })
if (importMetaEnvRegexp.test(fileContent)) {
// 替换
str = fileContent.replace(importMetaEnvRegexp, str)
} else {
// 追加
str = `${fileContent}
${str}`
}
} else {
// 声明文件不存在
str = `/// <reference types="vite/client" />
${str}`
}
fs.writeFileSync(path, str)
}
生产阶段
该阶段原本是通过 vite 内置的 vite:define 插件对代码中的环境变量进行替换,但该插件会对环境变量值进行 JSON.stringify,无奈只能自己实现该插件的逻辑来实现环境变量具有类型的效果
const importMetaEnvReg = /(?<![\'\"])import\.meta\.env\.([\w-]+)/gi
const importObjReg = /(import\.meta\.env)(?:[^.])/gi
{
name: 'vite-env-parse',
enforce: 'pre',
transform(code, id) {
if (
!isBuild ||
isNonJsRequest(id) ||
) {
return
}
if (code.includes('import.meta.env')) {
code = code
.replace(importMetaEnvReg, (matched, envKey) => {
let val = parsedEnv[envKey]
return JSON.stringify(val)
})
return {
code,
map: null
}
}
}
}