/
apply.ts
102 lines (87 loc) · 3.52 KB
/
apply.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
import type { StringifiedUtil } from '@unocss/core'
import { expandVariantGroup, notNull, regexScopePlaceholder } from '@unocss/core'
import type { CssNode, Rule, Selector, SelectorList } from 'css-tree'
import { clone, generate, parse } from 'css-tree'
import type { TransformerDirectivesContext } from '.'
import { transformDirectives } from '.'
type Writeable<T> = { -readonly [P in keyof T]: T[P] }
export async function handleApply(ctx: TransformerDirectivesContext, node: Rule) {
const { code, uno, options, filename, offset } = ctx
const calcOffset = (pos: number) => offset ? pos + offset : pos
await Promise.all(
node.block.children.map(async (childNode) => {
if (childNode.type === 'Raw')
return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc!.start.offset))
await parseApply(ctx, node, childNode)
}).toArray(),
)
}
export async function parseApply({ code, uno, offset, applyVariable }: TransformerDirectivesContext, node: Rule, childNode: CssNode) {
const calcOffset = (pos: number) => offset ? pos + offset : pos
let body: string | undefined
if (childNode.type === 'Atrule' && childNode.name === 'apply' && childNode.prelude && childNode.prelude.type === 'Raw') {
body = childNode.prelude.value.trim()
}
else if (childNode!.type === 'Declaration' && applyVariable.includes(childNode.property) && childNode.value.type === 'Raw') {
body = childNode.value.value.trim()
// remove quotes
if (body.match(/^(['"]).*\1$/))
body = body.slice(1, -1)
}
if (!body)
return
const classNames = expandVariantGroup(body)
.split(/\s+/g)
.map(className => className.trim().replace(/\\/, ''))
const utils = (
await Promise.all(
classNames.map(i => uno.parseToken(i, '-')),
))
.filter(notNull).flat()
.sort((a, b) => a[0] - b[0])
.sort((a, b) => (a[3] ? uno.parentOrders.get(a[3]) ?? 0 : 0) - (b[3] ? uno.parentOrders.get(b[3]) ?? 0 : 0))
.reduce((acc, item) => {
const target = acc.find(i => i[1] === item[1] && i[3] === item[3])
if (target)
target[2] += item[2]
else
// use spread operator to prevent reassign to uno internal cache
acc.push([...item] as Writeable<StringifiedUtil>)
return acc
}, [] as Writeable<StringifiedUtil>[])
if (!utils.length)
return
for (const i of utils) {
const [, _selector, body, parent] = i
const selector = _selector?.replace(regexScopePlaceholder, ' ') || _selector
if (parent || (selector && selector !== '.\\-')) {
let newSelector = generate(node.prelude)
if (selector && selector !== '.\\-') {
const selectorAST = parse(selector, {
context: 'selector',
}) as Selector
const prelude = clone(node.prelude) as SelectorList
prelude.children.forEach((child) => {
const parentSelectorAst = clone(selectorAST) as Selector
parentSelectorAst.children.forEach((i) => {
if (i.type === 'ClassSelector' && i.name === '\\-')
Object.assign(i, clone(child))
})
Object.assign(child, parentSelectorAst)
})
newSelector = generate(prelude)
}
let css = `${newSelector}{${body}}`
if (parent)
css = `${parent}{${css}}`
code.appendLeft(calcOffset(node.loc!.end.offset), css)
}
else {
code.appendRight(calcOffset(childNode!.loc!.end.offset), body)
}
}
code.remove(
calcOffset(childNode!.loc!.start.offset),
calcOffset(childNode!.loc!.end.offset),
)
}