wot-design-uni/vite-plugins/vite-plugin-uni-conditional-compile.ts
不如摸鱼去 7e84c5c91f
feat: 引入vitest做组件测试
* chore: 🚀 引入vitest做组件测试

* chore: 🚀 引入vitest做组件测试

* chore: 🚀 update workflow

* chore: 🚀 update workflow

* chore: 🚀 update workflow

* chore: 🚀 update workflow

* chore: 🚀 update nodejs version

* chore: 🚀 update nodejs version
2025-05-06 13:38:08 +08:00

193 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createFilter, FilterPattern } from '@rollup/pluginutils'
import type { Plugin, TransformResult } from 'vite'
interface ConditionalCompileOptions {
include?: FilterPattern
exclude?: FilterPattern
platform?: string
/**
* 是否在测试环境中
* 在测试环境中,我们可能需要特殊处理一些条件编译代码
*/
isTest?: boolean
}
/**
* uni-app条件编译插件
* 用于处理uni-app的条件编译代码
*/
export default function vitePluginUniConditionalCompile(options: ConditionalCompileOptions = {}): Plugin {
const {
include = [/\.vue$/, /\.js$/, /\.ts$/, /\.css$/, /\.scss$/],
exclude = [],
platform = 'h5',
isTest = process.env.NODE_ENV === 'test' || process.env.VITEST
} = options
// 在测试环境中,我们需要处理所有文件,包括测试文件
const filter = (id: string) => {
// 如果是测试文件直接返回true
if (isTest && (id.includes('test.') || id.endsWith('.test.ts') || id.endsWith('.test.js'))) {
return true
}
// 否则使用createFilter
return createFilter(include, exclude)(id)
}
// 匹配条件编译注释的正则
// 处理HTML注释形式的条件编译
const htmlConditionalPattern = /<!--\s*#(ifdef|ifndef)\s+([\w|&!\-\s]+)\s*-->([\s\S]*?)<!--\s*#endif\s*-->/g
// 处理Vue编译后的条件编译注释
const vueCompiledConditionalPattern =
/_createCommentVNode\(\s*["']#(ifdef|ifndef)\s+([\w|&!\-\s]+)["']\s*\)([\s\S]*?)_createCommentVNode\(\s*["']#endif["']\s*\)/g
// 处理JS注释形式的条件编译 - 单行注释
const jsConditionalPattern = /\/\/\s*#(ifdef|ifndef)\s+([\w|&!\-\s]+)[\r\n]([\s\S]*?)[\r\n]\s*\/\/\s*#endif/g
// 处理多行JS注释形式的条件编译
const jsMultilineConditionalPattern = /\/\*\s*#(ifdef|ifndef)\s+([\w|&!\-\s]+)\s*\*\/([\s\S]*?)\/\*\s*#endif\s*\*\//g
// 处理CSS注释形式的条件编译
const cssConditionalPattern = /\/\*\s*#(ifdef|ifndef)\s+([\w|&!\-\s]+)\s*\*\/([\s\S]*?)\/\*\s*#endif\s*\*\//g
return {
name: 'vite-plugin-uni-conditional-compile',
transform(code: string, id: string): TransformResult | undefined {
if (!filter(id)) return undefined
let transformed = code
try {
// 处理HTML条件块
transformed = transformed.replace(htmlConditionalPattern, (_match, type, envExp, content) => {
return processCondition(type, envExp, content, platform, isTest as boolean)
})
// 处理Vue编译后的条件编译注释
transformed = transformed.replace(vueCompiledConditionalPattern, (_match, type, envExp, content) => {
return processCondition(type, envExp, content, platform, isTest as boolean)
})
// 处理JS单行注释条件块
transformed = transformed.replace(jsConditionalPattern, (_match, type, envExp, content) => {
return processCondition(type, envExp, content, platform, isTest as boolean)
})
// 处理JS多行注释条件块
transformed = transformed.replace(jsMultilineConditionalPattern, (_match, type, envExp, content) => {
return processCondition(type, envExp, content, platform, isTest as boolean)
})
// 处理CSS注释形式的条件编译
if (id.endsWith('.css') || id.endsWith('.scss') || id.endsWith('.less') || id.includes('style')) {
transformed = transformed.replace(cssConditionalPattern, (_match, type, envExp, content) => {
return processCondition(type, envExp, content, platform, isTest as boolean)
})
}
// 返回处理结果
if (transformed !== code) {
return {
code: transformed,
map: null // 可选生成 sourcemap
}
}
// 如果代码没有变化,但是在测试环境中,我们仍然返回结果
if (isTest && (id.includes('test.') || id.endsWith('.test.ts') || id.endsWith('.test.js'))) {
return {
code: transformed,
map: null
}
}
} catch (error) {
console.error(`Error processing conditional compilation in file ${id}:`, error)
// 在出错的情况下,返回原始代码,避免阻塞构建过程
return {
code,
map: null
}
}
return undefined
}
}
}
/**
* 处理条件表达式
* @param type 条件类型 ifdef 或 ifndef
* @param envExp 环境表达式
* @param content 条件块内容
* @param platform 当前平台
* @param isTest 是否在测试环境中
* @returns 处理后的内容
*/
function processCondition(type: string, envExp: string, content: string, platform: string, isTest: boolean = false): string {
// 在测试环境中,我们可能需要特殊处理一些条件
if (isTest) {
// 如果是测试环境,并且条件中包含测试相关的平台,则保留内容
if (envExp.toUpperCase().includes('TEST') || envExp.toUpperCase().includes('VITEST')) {
return content
}
}
// 处理逻辑或表达式 (||)
if (envExp.includes('||')) {
const orParts = envExp.split(/\s*\|\|\s*/)
const orResult = orParts.some((part) => evaluateCondition(part.trim(), platform))
return (type === 'ifdef' && orResult) || (type === 'ifndef' && !orResult) ? content : ''
}
// 处理逻辑与表达式 (&&)
if (envExp.includes('&&')) {
const andParts = envExp.split(/\s*&&\s*/)
const andResult = andParts.every((part) => evaluateCondition(part.trim(), platform))
return (type === 'ifdef' && andResult) || (type === 'ifndef' && !andResult) ? content : ''
}
// 处理简单表达式
const isMatch = evaluateCondition(envExp, platform)
const shouldKeep = (type === 'ifdef' && isMatch) || (type === 'ifndef' && !isMatch)
return shouldKeep ? content : ''
}
/**
* 评估单个条件
* @param condition 条件表达式
* @param platform 当前平台
* @returns 条件是否匹配
*/
function evaluateCondition(condition: string, platform: string): boolean {
// 处理空条件
if (!condition.trim()) {
return false
}
const isNegate = condition.startsWith('!')
const targetEnv = isNegate ? condition.slice(1).trim() : condition.trim()
// 平台名称可能是大写或小写,需要进行不区分大小写的比较
const currentPlatform = platform.toUpperCase()
const targetPlatform = targetEnv.toUpperCase()
// 检查平台是否匹配
// 支持完全匹配和部分匹配
let matched = false
// 完全匹配
if (targetPlatform === currentPlatform) {
matched = true
}
// 特殊处理:如果条件是 MP-WEIXIN而平台是 WEIXIN也应该匹配
// 或者条件是 WEIXIN而平台是 MP-WEIXIN也应该匹配
if (targetPlatform.includes(currentPlatform) || currentPlatform.includes(targetPlatform)) {
matched = true
}
// 如果是否定条件,则取反
return isNegate ? !matched : matched
}