forked from unocss/unocss
/
index.ts
103 lines (89 loc) · 3.02 KB
/
index.ts
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
import type { SourceCodeTransformer } from '@unocss/core'
import { toArray } from '@unocss/core'
export type FilterPattern = Array<string | RegExp> | string | RegExp | null
function createFilter(
include: FilterPattern,
exclude: FilterPattern,
): (id: string) => boolean {
const includePattern = toArray(include || [])
const excludePattern = toArray(exclude || [])
return (id: string) => {
if (excludePattern.some(p => id.match(p)))
return false
return includePattern.some(p => id.match(p))
}
}
export interface TransformerAttributifyJsxOptions {
/**
* the list of attributes to ignore
* @default []
*/
blocklist?: (string | RegExp)[]
/**
* Regex of modules to be included from processing
* @default [/\.[jt]sx$/, /\.mdx$/]
*/
include?: FilterPattern
/**
* Regex of modules to exclude from processing
*
* @default []
*/
exclude?: FilterPattern
}
const elementRE = /<!--[\s\S]*?-->|<(\/?)([a-zA-Z][-.:0-9_a-zA-Z]*)((?:\s+[^>]*?(?:(?:'[^']*')|(?:"[^"]*"))?)*)\s*(\/?)>/gs
const attributeRE = /([a-zA-Z()#][\[?a-zA-Z0-9-_:()#%\]?]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g
const classFilterRE = /(className|class)\s*=\s*\{[^\}]*\}/i
const curlybraceRE = /\{.+\}/g
export default function transformerAttributifyJsx(options: TransformerAttributifyJsxOptions = {}): SourceCodeTransformer {
const {
blocklist = [],
} = options
const isBlocked = (matchedRule: string) => {
for (const blockedRule of blocklist) {
if (blockedRule instanceof RegExp) {
if (blockedRule.test(matchedRule))
return true
}
else if (matchedRule === blockedRule) {
return true
}
}
return false
}
const idFilter = createFilter(
options.include || [/\.[jt]sx$/, /\.mdx$/],
options.exclude || [],
)
return {
name: 'transformer-jsx',
enforce: 'pre',
idFilter,
async transform(code, _, { uno }) {
const tasks: Promise<void>[] = []
for (const item of Array.from(code.original.matchAll(elementRE))) {
// Get the length of the className part, and replace it with the equal length of empty string
const classNamePart = item[3].match(classFilterRE)
let attributifyPart = item[3]
if (classNamePart)
attributifyPart = item[3].replace(classFilterRE, ' '.repeat(classNamePart[0].length))
if (curlybraceRE.test(attributifyPart))
continue
for (const attr of attributifyPart.matchAll(attributeRE)) {
const matchedRule = attr[0].replace(/\:/i, '-')
if (matchedRule.includes('=') || isBlocked(matchedRule))
continue
tasks.push(uno.parseToken(matchedRule).then((matched) => {
if (matched) {
const tag = item[2]
const startIdx = (item.index || 0) + (attr.index || 0) + tag.length + 1
const endIdx = startIdx + matchedRule.length
code.overwrite(startIdx, endIdx, `${matchedRule}=""`)
}
}))
}
}
await Promise.all(tasks)
},
}
}