tailwindcss-patch 9.2.1 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +2 -2
- package/dist/cli.mjs +2 -2
- package/dist/commands/cli-runtime.d.mts +1 -1
- package/dist/commands/cli-runtime.d.ts +1 -1
- package/dist/commands/cli-runtime.js +1 -1
- package/dist/commands/cli-runtime.mjs +1 -1
- package/dist/{index.bundle-Dnh0a6WS.mjs → index.bundle-CsMNxjyL.mjs} +4 -3
- package/dist/{index.bundle-BkqJb7rw.js → index.bundle-DSIUuQAH.js} +4 -3
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{validate-CceFRP7h.mjs → validate-B_jsptz9.mjs} +254 -11
- package/dist/{validate-Bu_rkfQF.d.ts → validate-Bqych-z9.d.ts} +17 -1
- package/dist/{validate-CIMnzW8O.d.mts → validate-C8-qsOWo.d.mts} +17 -1
- package/dist/{validate-Bo-Ua7Kg.js → validate-DzKStQUM.js} +265 -10
- package/package.json +1 -1
- package/src/extraction/candidate-extractor.ts +30 -4
- package/src/options/normalize.ts +5 -0
- package/src/options/types.ts +8 -0
- package/src/runtime/class-collector.ts +2 -0
- package/src/v4/bare-arbitrary-values.ts +326 -0
- package/src/v4/candidates.ts +68 -4
- package/src/v4/engine.ts +15 -3
- package/src/v4/types.ts +6 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
export interface BareArbitraryValueOptions {
|
|
2
|
+
/**
|
|
3
|
+
* 允许作为无方括号任意值的单位列表。
|
|
4
|
+
*/
|
|
5
|
+
units?: string[]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface BareArbitraryValueResolveResult {
|
|
9
|
+
candidate: string
|
|
10
|
+
canonicalCandidate: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
|
|
14
|
+
'%',
|
|
15
|
+
'px',
|
|
16
|
+
'rpx',
|
|
17
|
+
'rem',
|
|
18
|
+
'em',
|
|
19
|
+
'vw',
|
|
20
|
+
'vh',
|
|
21
|
+
'vmin',
|
|
22
|
+
'vmax',
|
|
23
|
+
'dvw',
|
|
24
|
+
'dvh',
|
|
25
|
+
'svw',
|
|
26
|
+
'svh',
|
|
27
|
+
'lvw',
|
|
28
|
+
'lvh',
|
|
29
|
+
'ch',
|
|
30
|
+
'ex',
|
|
31
|
+
'lh',
|
|
32
|
+
'rlh',
|
|
33
|
+
'fr',
|
|
34
|
+
'deg',
|
|
35
|
+
'rad',
|
|
36
|
+
'turn',
|
|
37
|
+
's',
|
|
38
|
+
'ms',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
const NUMBER_RE = /^-?(?:\d+|\d*\.\d+)$/
|
|
42
|
+
const FUNCTION_VALUE_RE = /^[a-zA-Z_-][a-zA-Z0-9_-]*\(/
|
|
43
|
+
|
|
44
|
+
function splitVariantPrefix(candidate: string) {
|
|
45
|
+
let depth = 0
|
|
46
|
+
let quote: string | undefined
|
|
47
|
+
let lastSeparator = -1
|
|
48
|
+
|
|
49
|
+
for (let index = 0; index < candidate.length; index++) {
|
|
50
|
+
const character = candidate[index]
|
|
51
|
+
if (character === '\\') {
|
|
52
|
+
index++
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (quote) {
|
|
57
|
+
if (character === quote) {
|
|
58
|
+
quote = undefined
|
|
59
|
+
}
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (character === '"' || character === '\'') {
|
|
64
|
+
quote = character
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (character === '[' || character === '(' || character === '{') {
|
|
69
|
+
depth++
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (character === ']' || character === ')' || character === '}') {
|
|
74
|
+
depth = Math.max(0, depth - 1)
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (depth === 0 && character === ':') {
|
|
79
|
+
lastSeparator = index
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (lastSeparator === -1) {
|
|
84
|
+
return {
|
|
85
|
+
prefix: '',
|
|
86
|
+
body: candidate,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
prefix: candidate.slice(0, lastSeparator + 1),
|
|
92
|
+
body: candidate.slice(lastSeparator + 1),
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isBalancedFunctionValue(value: string) {
|
|
97
|
+
let depth = 0
|
|
98
|
+
let quote: string | undefined
|
|
99
|
+
|
|
100
|
+
for (let index = 0; index < value.length; index++) {
|
|
101
|
+
const character = value[index]
|
|
102
|
+
|
|
103
|
+
if (character === '\\') {
|
|
104
|
+
index++
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (quote) {
|
|
109
|
+
if (character === quote) {
|
|
110
|
+
quote = undefined
|
|
111
|
+
}
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (character === '"' || character === '\'') {
|
|
116
|
+
quote = character
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (character === '(') {
|
|
121
|
+
depth++
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (character === ')') {
|
|
126
|
+
depth--
|
|
127
|
+
if (depth < 0) {
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return depth === 0 && quote === undefined
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isHexColorValue(value: string) {
|
|
137
|
+
return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6,8})$/.test(value)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isQuotedValue(value: string) {
|
|
141
|
+
const quote = value[0]
|
|
142
|
+
if ((quote !== '"' && quote !== '\'') || value[value.length - 1] !== quote) {
|
|
143
|
+
return false
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let escaped = false
|
|
147
|
+
for (let index = 1; index < value.length - 1; index++) {
|
|
148
|
+
const character = value[index]
|
|
149
|
+
if (escaped) {
|
|
150
|
+
escaped = false
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
if (character === '\\') {
|
|
154
|
+
escaped = true
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return !escaped
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function normalizeBareArbitraryValueOptions(options: boolean | BareArbitraryValueOptions | undefined) {
|
|
162
|
+
if (options === false || options === undefined || options === null) {
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const units = options === true ? DEFAULT_BARE_ARBITRARY_VALUE_UNITS : options.units ?? DEFAULT_BARE_ARBITRARY_VALUE_UNITS
|
|
167
|
+
const normalizedUnits = [...new Set(units.filter(unit => typeof unit === 'string' && unit.length > 0))]
|
|
168
|
+
if (normalizedUnits.length === 0) {
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
units: normalizedUnits.sort((a, b) => b.length - a.length),
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function resolveValueWithUnit(body: string, units: string[]) {
|
|
177
|
+
for (const unit of units) {
|
|
178
|
+
if (!body.endsWith(unit)) {
|
|
179
|
+
continue
|
|
180
|
+
}
|
|
181
|
+
const numberPart = body.slice(0, -unit.length)
|
|
182
|
+
if (NUMBER_RE.test(numberPart)) {
|
|
183
|
+
return `${numberPart}${unit}`
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function resolveArbitraryValue(body: string, units: string[]) {
|
|
189
|
+
const withUnit = resolveValueWithUnit(body, units)
|
|
190
|
+
if (withUnit) {
|
|
191
|
+
return withUnit
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (isHexColorValue(body)) {
|
|
195
|
+
return body
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (isQuotedValue(body)) {
|
|
199
|
+
return body
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (FUNCTION_VALUE_RE.test(body) && body.endsWith(')') && isBalancedFunctionValue(body)) {
|
|
203
|
+
return body
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function resolveUtilityAndValue(body: string, units: string[]) {
|
|
208
|
+
let depth = 0
|
|
209
|
+
let quote: string | undefined
|
|
210
|
+
|
|
211
|
+
for (let index = 1; index < body.length; index++) {
|
|
212
|
+
const character = body[index]
|
|
213
|
+
|
|
214
|
+
if (character === '\\') {
|
|
215
|
+
index++
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (quote) {
|
|
220
|
+
if (character === quote) {
|
|
221
|
+
quote = undefined
|
|
222
|
+
}
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (character === '"' || character === '\'') {
|
|
227
|
+
quote = character
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (character === '(' || character === '{') {
|
|
232
|
+
depth++
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (character === ')' || character === '}') {
|
|
237
|
+
depth = Math.max(0, depth - 1)
|
|
238
|
+
continue
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (depth > 0 || character !== '-') {
|
|
242
|
+
continue
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const utility = body.slice(0, index)
|
|
246
|
+
const rawValue = body.slice(index + 1)
|
|
247
|
+
if (!utility || !rawValue) {
|
|
248
|
+
continue
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const value = resolveArbitraryValue(rawValue, units)
|
|
252
|
+
if (value) {
|
|
253
|
+
return {
|
|
254
|
+
utility,
|
|
255
|
+
value,
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function resolveBareArbitraryValueCandidate(
|
|
262
|
+
candidate: string,
|
|
263
|
+
options?: boolean | BareArbitraryValueOptions,
|
|
264
|
+
): BareArbitraryValueResolveResult | undefined {
|
|
265
|
+
const normalizedOptions = normalizeBareArbitraryValueOptions(options)
|
|
266
|
+
if (!normalizedOptions || !candidate || candidate.includes('[') || candidate.includes(']')) {
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const { prefix, body } = splitVariantPrefix(candidate)
|
|
271
|
+
const important = body.startsWith('!') ? '!' : ''
|
|
272
|
+
let normalizedBody = important ? body.slice(1) : body
|
|
273
|
+
const negative = normalizedBody.startsWith('-') ? '-' : ''
|
|
274
|
+
if (negative) {
|
|
275
|
+
normalizedBody = normalizedBody.slice(1)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const resolved = resolveUtilityAndValue(normalizedBody, normalizedOptions.units)
|
|
279
|
+
if (!resolved) {
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
candidate,
|
|
285
|
+
canonicalCandidate: `${prefix}${important}${negative}${resolved.utility}-[${resolved.value}]`,
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Based on the CSS.escape algorithm, scoped to class selector escaping.
|
|
290
|
+
export function escapeCssClassName(value: string) {
|
|
291
|
+
let result = ''
|
|
292
|
+
for (let index = 0; index < value.length; index++) {
|
|
293
|
+
const codeUnit = value.charCodeAt(index)
|
|
294
|
+
const character = value.charAt(index)
|
|
295
|
+
|
|
296
|
+
if (codeUnit === 0x0000) {
|
|
297
|
+
result += '\uFFFD'
|
|
298
|
+
continue
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (
|
|
302
|
+
(codeUnit >= 0x0001 && codeUnit <= 0x001F)
|
|
303
|
+
|| codeUnit === 0x007F
|
|
304
|
+
|| (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039)
|
|
305
|
+
|| (index === 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && value.charCodeAt(0) === 0x002D)
|
|
306
|
+
) {
|
|
307
|
+
result += `\\${codeUnit.toString(16)} `
|
|
308
|
+
continue
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (
|
|
312
|
+
codeUnit >= 0x0080
|
|
313
|
+
|| codeUnit === 0x002D
|
|
314
|
+
|| codeUnit === 0x005F
|
|
315
|
+
|| (codeUnit >= 0x0030 && codeUnit <= 0x0039)
|
|
316
|
+
|| (codeUnit >= 0x0041 && codeUnit <= 0x005A)
|
|
317
|
+
|| (codeUnit >= 0x0061 && codeUnit <= 0x007A)
|
|
318
|
+
) {
|
|
319
|
+
result += character
|
|
320
|
+
continue
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
result += `\\${character}`
|
|
324
|
+
}
|
|
325
|
+
return result
|
|
326
|
+
}
|
package/src/v4/candidates.ts
CHANGED
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
import type { TailwindV4DesignSystem } from './types'
|
|
2
2
|
import postcss from 'postcss'
|
|
3
|
+
import {
|
|
4
|
+
escapeCssClassName,
|
|
5
|
+
type BareArbitraryValueOptions,
|
|
6
|
+
resolveBareArbitraryValueCandidate,
|
|
7
|
+
} from './bare-arbitrary-values'
|
|
3
8
|
|
|
4
9
|
export function resolveValidTailwindV4Candidates(
|
|
5
10
|
designSystem: TailwindV4DesignSystem,
|
|
6
11
|
candidates: Iterable<string>,
|
|
12
|
+
options?: {
|
|
13
|
+
bareArbitraryValues?: boolean | BareArbitraryValueOptions
|
|
14
|
+
},
|
|
7
15
|
): Set<string> {
|
|
8
16
|
const validCandidates = new Set<string>()
|
|
9
17
|
const parsedCandidates: string[] = []
|
|
18
|
+
const originalCandidateByCanonical = new Map<string, string>()
|
|
10
19
|
|
|
11
20
|
for (const candidate of candidates) {
|
|
12
|
-
if (!candidate
|
|
21
|
+
if (!candidate) {
|
|
13
22
|
continue
|
|
14
23
|
}
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options?.bareArbitraryValues)
|
|
26
|
+
const candidateToCheck = bareArbitrary?.canonicalCandidate ?? candidate
|
|
27
|
+
|
|
28
|
+
if (parsedCandidates.includes(candidateToCheck)) {
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (designSystem.parseCandidate(candidateToCheck).length > 0) {
|
|
33
|
+
parsedCandidates.push(candidateToCheck)
|
|
34
|
+
if (bareArbitrary) {
|
|
35
|
+
originalCandidateByCanonical.set(candidateToCheck, candidate)
|
|
36
|
+
}
|
|
18
37
|
}
|
|
19
38
|
}
|
|
20
39
|
|
|
@@ -27,13 +46,58 @@ export function resolveValidTailwindV4Candidates(
|
|
|
27
46
|
const candidate = parsedCandidates[index]
|
|
28
47
|
const candidateCss = cssByCandidate[index]
|
|
29
48
|
if (candidate && typeof candidateCss === 'string' && candidateCss.trim().length > 0) {
|
|
30
|
-
validCandidates.add(candidate)
|
|
49
|
+
validCandidates.add(originalCandidateByCanonical.get(candidate) ?? candidate)
|
|
31
50
|
}
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
return validCandidates
|
|
35
54
|
}
|
|
36
55
|
|
|
56
|
+
function createSelectorAliasMap(
|
|
57
|
+
candidates: Iterable<string>,
|
|
58
|
+
options?: boolean | BareArbitraryValueOptions,
|
|
59
|
+
) {
|
|
60
|
+
const aliases = new Map<string, string>()
|
|
61
|
+
for (const candidate of candidates) {
|
|
62
|
+
const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options)
|
|
63
|
+
if (!bareArbitrary) {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
aliases.set(
|
|
67
|
+
escapeCssClassName(bareArbitrary.canonicalCandidate),
|
|
68
|
+
escapeCssClassName(bareArbitrary.candidate),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
return aliases
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function replaceBareArbitraryValueSelectors(
|
|
75
|
+
css: string,
|
|
76
|
+
candidates: Iterable<string>,
|
|
77
|
+
options?: boolean | BareArbitraryValueOptions,
|
|
78
|
+
) {
|
|
79
|
+
const aliases = createSelectorAliasMap(candidates, options)
|
|
80
|
+
if (aliases.size === 0) {
|
|
81
|
+
return css
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let result = css
|
|
85
|
+
for (const [canonicalSelector, bareSelector] of aliases) {
|
|
86
|
+
result = result.replaceAll(canonicalSelector, bareSelector)
|
|
87
|
+
}
|
|
88
|
+
return result
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function canonicalizeBareArbitraryValueCandidates(
|
|
92
|
+
candidates: Iterable<string>,
|
|
93
|
+
options?: boolean | BareArbitraryValueOptions,
|
|
94
|
+
) {
|
|
95
|
+
return Array.from(candidates, (candidate) => {
|
|
96
|
+
const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options)
|
|
97
|
+
return bareArbitrary?.canonicalCandidate ?? candidate
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
37
101
|
function splitTopLevel(value: string, separator: string) {
|
|
38
102
|
const result: string[] = []
|
|
39
103
|
let start = 0
|
package/src/v4/engine.ts
CHANGED
|
@@ -6,7 +6,12 @@ import type {
|
|
|
6
6
|
TailwindV4SourcePattern,
|
|
7
7
|
} from './types'
|
|
8
8
|
import { extractRawCandidates, extractRawCandidatesWithPositions } from '../extraction/candidate-extractor'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
canonicalizeBareArbitraryValueCandidates,
|
|
11
|
+
extractTailwindV4InlineSourceCandidates,
|
|
12
|
+
replaceBareArbitraryValueSelectors,
|
|
13
|
+
resolveValidTailwindV4Candidates,
|
|
14
|
+
} from './candidates'
|
|
10
15
|
import { compileTailwindV4Source, loadTailwindV4DesignSystem } from './node-adapter'
|
|
11
16
|
|
|
12
17
|
function resolveScanSources(
|
|
@@ -72,13 +77,20 @@ export function createTailwindV4Engine(source: TailwindV4ResolvedSource): Tailwi
|
|
|
72
77
|
const { compiled, dependencies } = await compileTailwindV4Source(source)
|
|
73
78
|
const rawCandidates = await collectRawCandidates(source, options, compiled.sources)
|
|
74
79
|
const designSystem = await loadTailwindV4DesignSystem(source)
|
|
75
|
-
const classSet = resolveValidTailwindV4Candidates(designSystem, rawCandidates
|
|
80
|
+
const classSet = resolveValidTailwindV4Candidates(designSystem, rawCandidates, {
|
|
81
|
+
...(options?.bareArbitraryValues === undefined ? {} : { bareArbitraryValues: options.bareArbitraryValues }),
|
|
82
|
+
})
|
|
76
83
|
const inlineSources = extractTailwindV4InlineSourceCandidates(source.css)
|
|
77
84
|
for (const candidate of inlineSources.excluded) {
|
|
78
85
|
classSet.delete(candidate)
|
|
79
86
|
}
|
|
80
87
|
|
|
81
|
-
const
|
|
88
|
+
const buildCandidates = canonicalizeBareArbitraryValueCandidates(classSet, options?.bareArbitraryValues)
|
|
89
|
+
const css = replaceBareArbitraryValueSelectors(
|
|
90
|
+
compiled.build(buildCandidates),
|
|
91
|
+
classSet,
|
|
92
|
+
options?.bareArbitraryValues,
|
|
93
|
+
)
|
|
82
94
|
|
|
83
95
|
return {
|
|
84
96
|
css,
|
package/src/v4/types.ts
CHANGED
|
@@ -24,6 +24,12 @@ export interface TailwindV4CandidateSource {
|
|
|
24
24
|
export interface TailwindV4GenerateOptions {
|
|
25
25
|
candidates?: Iterable<string>
|
|
26
26
|
sources?: TailwindV4CandidateSource[]
|
|
27
|
+
/**
|
|
28
|
+
* Enables UnoCSS-style bare arbitrary values such as `p-10%` and `p-2.5px`.
|
|
29
|
+
*/
|
|
30
|
+
bareArbitraryValues?: boolean | {
|
|
31
|
+
units?: string[]
|
|
32
|
+
}
|
|
27
33
|
/**
|
|
28
34
|
* 扫描文件系统 source entries 中的候选类名。
|
|
29
35
|
*
|