-
Notifications
You must be signed in to change notification settings - Fork 1
/
transformerAttributifyJsx.ts
100 lines (86 loc) · 3.02 KB
/
transformerAttributifyJsx.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
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)((?:'.*?'|".*?"|`.*?`|\{.*?\}|[^>]*?)*)/g
const attributeRE = /([a-zA-Z()#][\[?a-zA-Z0-9-_:()#%\]?]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g
const valuedAttributeRE = /((?!\d|-{2}|-\d)[a-zA-Z0-9\u00A0-\uFFFF-_:!%-.~<]+)=(?:["]([^"]*)["]|[']([^']*)[']|[{]((?:[`(](?:[^`)]*)[`)]|[^}])+)[}])/gms
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: '@unocss/transformer-attributify-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
let attributifyPart = item[2]
if (attributifyPart === undefined) { continue }
if (valuedAttributeRE.test(attributifyPart))
attributifyPart = attributifyPart.replace(valuedAttributeRE, match => ' '.repeat(match.length))
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[1]
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)
},
}
}