/
index.ts
92 lines (76 loc) · 2.48 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
import { cssIdRE } from '@unocss/core'
import type { SourceCodeTransformer, UnoGenerator } from '@unocss/core'
import type { CssNode, List, ListItem } from 'css-tree'
import { parse, walk } from 'css-tree'
import type MagicString from 'magic-string'
import { handleThemeFn, themeFnRE } from './theme'
import { handleScreen } from './screen'
import { handleApply } from './apply'
export interface TransformerDirectivesOptions {
enforce?: SourceCodeTransformer['enforce']
/**
* Treat CSS variables as directives for CSS syntax compatible.
*
* Pass `false` to disable, or a string to use as a prefix.
*
* @default '--at-'
*/
varStyle?: false | string
/**
* Throw an error if utils or themes are not found.
*
* @default true
*/
throwOnMissing?: boolean
}
export interface TransformerDirectivesContext {
code: MagicString
uno: UnoGenerator
options: TransformerDirectivesOptions
offset?: number
filename?: string
}
export default function transformerDirectives(options: TransformerDirectivesOptions = {}): SourceCodeTransformer {
return {
name: 'css-directive',
enforce: options?.enforce || 'pre',
idFilter: id => !!id.match(cssIdRE),
transform: (code, id, ctx) => {
return transformDirectives(code, ctx.uno, options, id)
},
}
}
export async function transformDirectives(
code: MagicString,
uno: UnoGenerator,
options: TransformerDirectivesOptions,
filename?: string,
originalCode?: string,
offset?: number,
) {
const { varStyle = '--at-' } = options
const isApply = code.original.includes('@apply') || (varStyle !== false && code.original.includes(varStyle))
const isScreen = code.original.includes('@screen')
const hasThemeFn = code.original.match(themeFnRE)
if (!isApply && !hasThemeFn && !isScreen)
return
const ast = parse(originalCode || code.original, {
parseAtrulePrelude: false,
positions: true,
filename,
})
if (ast.type !== 'StyleSheet')
return
const stack: Promise<void>[] = []
const processNode = async (node: CssNode, _item: ListItem<CssNode>, _list: List<CssNode>) => {
const ctx: TransformerDirectivesContext = { options, uno, code, filename, offset }
if (isScreen && node.type === 'Atrule')
handleScreen(ctx, node)
if (hasThemeFn && node.type === 'Declaration')
handleThemeFn(ctx, node)
if (isApply && node.type === 'Rule')
await handleApply(ctx, node)
}
walk(ast, (...args) => stack.push(processNode(...args)))
await Promise.all(stack)
}