import type { FilterPattern, Plugin, ResolvedConfig } from 'vite' import MagicString from 'magic-string' import { createFilter } from 'vite' interface Options { /** * @default ["**\/*"] */ include: FilterPattern /** * @default [] */ exclude: FilterPattern } type UserOptions = Partial type ResolvedOptions = Options let config: ResolvedConfig = undefined! const replaceMatched = (code: string, id: string) => { const env = config.env const source = new MagicString(code, { filename: id, }) source.replace( /^.*?#if(n?)def\s*(\S+).*[\r\n]{1,2}([\s\S]+?)\s*.*?#endif.*?$/gm, /** * 条件替换 * @param _ 匹配的字符串 * @param $1 是否为 not 模式 * @param $2 条件 * @param $3 code */ (_, $1, $2, $3) => { const isNot = !!$1 const isKeep = $2.split('||').some((v: string) => { let flag = false const [key, value] = v.split('=') if (value === undefined) flag = !!env[key] else flag = String(env[key]) === value flag = isNot ? !flag : flag return flag }) return isKeep ? $3 : '' }, ) if (source.hasChanged()) { return { code: source.toString(), map: source.generateMap({ source: id, file: `${id}.map`, includeContent: true, }), } } } const resolveOptions = (userOptions: UserOptions): ResolvedOptions => { return { include: ['**/*'], exclude: [], ...userOptions, } } const VitePluginConditionalCompile = ( userOptions: UserOptions = {}, ): Plugin => { const options = resolveOptions(userOptions) return { name: 'vite-plugin-conditional-compile', enforce: 'pre', configResolved(_config) { config = _config }, transform(code, id) { const filter = createFilter(options.include, options.exclude) if (filter(id)) return replaceMatched(code, id) }, } } export default VitePluginConditionalCompile