forked from mxx1111/mdlook
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcssProcessor.ts
More file actions
106 lines (94 loc) · 3.29 KB
/
Copy pathcssProcessor.ts
File metadata and controls
106 lines (94 loc) · 3.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* CSS 运行时处理工具(浏览器原生实现)
* 不依赖 PostCSS,直接在浏览器中解析 CSS 自定义属性(var())
*/
/**
* 从 CSS 字符串中提取所有 CSS 自定义属性定义
*/
function extractCSSVariables(css: string): Map<string, string> {
const vars = new Map<string, string>()
const regex = /--([\w-]+)\s*:\s*([^;}\n]+)/g
for (const match of css.matchAll(regex)) {
vars.set(`--${match[1]}`, match[2].trim())
}
return vars
}
/**
* 使用浏览器原生方式处理 CSS 字符串
* 将所有 var(--xxx) 替换为实际值(支持 fallback 和嵌套变量)
*
* @param css - 原始 CSS 字符串
* @returns 处理后的 CSS 字符串
*/
export function processCSS(css: string): string {
// 1. 从 CSS 字符串本身提取变量定义
const vars = extractCSSVariables(css)
// 注意:不从 DOM 计算值覆盖 CSS 字符串中已定义的变量。
// 若用 getComputedStyle 覆盖,会读取到上一次注入的旧值,导致主题色更新滞后一次点击。
// 2. 迭代替换 var() 引用,直到没有可解析的为止(处理嵌套变量)
const varRegex = /var\(\s*(--[\w-]+)\s*(?:,([^()]*(?:\([^()]*\)[^()]*)*))?\)/g
let result = css
let prev = ``
let iterations = 0
while (result !== prev && iterations < 10) {
prev = result
result = result.replace(varRegex, (_, varName: string, fallback?: string) => {
const val = vars.get(varName)
if (val !== undefined)
return val
return fallback ? fallback.trim() : `var(${varName})`
})
iterations++
}
// 3. 化简 calc() 表达式(由内而外,迭代处理嵌套)
const calcRegex = /calc\(([^()]+)\)/g
prev = ``
iterations = 0
while (result !== prev && iterations < 10) {
prev = result
result = result.replace(calcRegex, (_, inner: string) => evaluateCalcInner(inner.trim()))
iterations++
}
return result
}
const UNITS = `px|em|rem|vw|vh|vmin|vmax|%|pt|pc|cm|mm|in|ex|ch`
const NUM = `(-?[\\d.]+)`
const UNIT_VAL = `(-?[\\d.]+)(${UNITS})?`
/**
* 对 calc() 内部表达式求值(不含外层 calc())
* 支持:加减(同单位)、乘除(一个操作数无单位)
*/
function evaluateCalcInner(expr: string): string {
// 乘法:Xunit * N 或 N * Xunit
const mul = expr.match(new RegExp(`^${UNIT_VAL}\\s*\\*\\s*${UNIT_VAL}$`))
if (mul) {
const [, a, ua, b, ub] = mul
// 有且仅有一侧有单位
if (!ua !== !ub) {
const unit = ua || ub
const result = round(Number.parseFloat(a) * Number.parseFloat(b))
return `${result}${unit}`
}
}
// 除法:Xunit / N
const div = expr.match(new RegExp(`^${UNIT_VAL}\\s*/\\s*${NUM}$`))
if (div) {
const [, a, unit,, b] = div
const result = round(Number.parseFloat(a) / Number.parseFloat(b))
return `${result}${unit ?? ``}`
}
// 加减:同单位
const addSub = expr.match(new RegExp(`^${UNIT_VAL}\\s*([+-])\\s*${UNIT_VAL}$`))
if (addSub) {
const [, a, ua, op, b, ub] = addSub
if (ua === ub) {
const result = round(op === `+` ? Number.parseFloat(a) + Number.parseFloat(b) : Number.parseFloat(a) - Number.parseFloat(b))
return `${result}${ua ?? ``}`
}
}
// 无法化简,保留 calc()
return `calc(${expr})`
}
function round(n: number): number {
return Math.round(n * 10000) / 10000
}