tailwindcss-patch 9.4.1 → 9.4.2
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-CgBdW1U5.mjs → cli-D5D3Yj69.mjs} +1 -1
- package/dist/{cli-CBVPia5Z.js → cli-DsXcyl2O.js} +59 -59
- package/dist/cli.js +4 -4
- 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 +6 -6
- package/dist/commands/cli-runtime.mjs +2 -2
- package/dist/index.d.mts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.js +67 -59
- package/dist/index.mjs +5 -4
- package/dist/{migrate-config-Dn9OTXpO.js → validate-5eUbRAzl.js} +220 -90
- package/dist/{migrate-config-DqknZpUe.mjs → validate-CInDiiYy.mjs} +191 -91
- package/dist/{validate-CaJv2g5K.d.ts → validate-CYB-90BG.d.ts} +22 -5
- package/dist/{validate-Dr7IkGU8.d.mts → validate-D6MO6lZo.d.mts} +22 -5
- package/package.json +1 -1
- package/src/extraction/candidate-extractor.ts +76 -12
- package/src/public-api.ts +20 -13
- package/src/v4/bare-arbitrary-values.ts +127 -2
- package/src/v4/engine.ts +5 -2
- package/src/v4/index.ts +14 -5
- package/src/v4/node-adapter.ts +1 -1
- package/src/v4/source-scan.ts +1 -1
|
@@ -5,11 +5,12 @@ import type {
|
|
|
5
5
|
TailwindTokenLocation,
|
|
6
6
|
TailwindTokenReport,
|
|
7
7
|
} from '../types'
|
|
8
|
+
import type { BareArbitraryValueOptions } from '../v4/bare-arbitrary-values'
|
|
8
9
|
import { promises as fs } from 'node:fs'
|
|
9
10
|
import process from 'node:process'
|
|
10
11
|
import path from 'pathe'
|
|
11
12
|
import {
|
|
12
|
-
|
|
13
|
+
extractBareArbitraryValueSourceCandidatesWithPositions,
|
|
13
14
|
resolveBareArbitraryValueCandidate,
|
|
14
15
|
} from '../v4/bare-arbitrary-values'
|
|
15
16
|
import { extractTailwindV4InlineSourceCandidates, resolveValidTailwindV4Candidates } from '../v4/candidates'
|
|
@@ -59,6 +60,10 @@ export interface ExtractValidCandidatesOption {
|
|
|
59
60
|
bareArbitraryValues?: boolean | BareArbitraryValueOptions
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
export interface ExtractCandidateOptions {
|
|
64
|
+
bareArbitraryValues?: boolean | BareArbitraryValueOptions
|
|
65
|
+
}
|
|
66
|
+
|
|
62
67
|
export interface ExtractSourceCandidate {
|
|
63
68
|
rawCandidate: string
|
|
64
69
|
start: number
|
|
@@ -75,7 +80,7 @@ const HTML_ATTRIBUTE_NAME_CANDIDATE_RE = /^(?:class|className|hover-class|hoverC
|
|
|
75
80
|
const CSS_DIRECTIVE_CANDIDATE_RE = /^@(?:apply|tailwind|source|config|plugin|theme|utility|custom-variant|variant)$/
|
|
76
81
|
const CSS_APPLY_IMPORTANT = '!important'
|
|
77
82
|
const CSS_APPLY_RE = /@apply\s+([^;{}]+)/g
|
|
78
|
-
const JS_LIKE_SOURCE_EXTENSION_RE = /^
|
|
83
|
+
const JS_LIKE_SOURCE_EXTENSION_RE = /^[cm]?[jt]sx?$/
|
|
79
84
|
const MIXED_TEMPLATE_SOURCE_EXTENSION_RE = /^(?:vue|uvue|nvue|svelte|mpx)$/
|
|
80
85
|
const CSS_LIKE_SOURCE_EXTENSION_RE = /^(?:css|wxss|acss|jxss|ttss|qss|tyss|scss|sass|less|styl|stylus)$/
|
|
81
86
|
const SFC_SCRIPT_BLOCK_RE = /<script\b[^>]*>([\s\S]*?)<\/script>/gi
|
|
@@ -102,7 +107,7 @@ function isInsideHtmlTagText(content: string, candidate: ExtractSourceCandidate)
|
|
|
102
107
|
return false
|
|
103
108
|
}
|
|
104
109
|
const nextOpen = content.indexOf('<', candidate.end)
|
|
105
|
-
return nextOpen !== -1 && (nextOpen < content.indexOf('>', candidate.end) || content.
|
|
110
|
+
return nextOpen !== -1 && (nextOpen < content.indexOf('>', candidate.end) || !content.includes('>', candidate.end))
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
function isCssDirectiveCandidate(candidate: string) {
|
|
@@ -223,7 +228,36 @@ function createLocalCandidate(candidate: ExtractSourceCandidateWithContext): Ext
|
|
|
223
228
|
}
|
|
224
229
|
}
|
|
225
230
|
|
|
226
|
-
|
|
231
|
+
function dedupeCandidatesWithPositions(candidates: ExtractSourceCandidate[]) {
|
|
232
|
+
const seen = new Set<string>()
|
|
233
|
+
return candidates.filter((candidate) => {
|
|
234
|
+
const key = `${candidate.start}:${candidate.end}:${candidate.rawCandidate}`
|
|
235
|
+
if (seen.has(key)) {
|
|
236
|
+
return false
|
|
237
|
+
}
|
|
238
|
+
seen.add(key)
|
|
239
|
+
return true
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function createBareArbitraryValueCandidateContexts(
|
|
244
|
+
content: string,
|
|
245
|
+
extension: string,
|
|
246
|
+
offset: number,
|
|
247
|
+
options?: ExtractCandidateOptions,
|
|
248
|
+
): ExtractSourceCandidateWithContext[] {
|
|
249
|
+
return extractBareArbitraryValueSourceCandidatesWithPositions(content, options?.bareArbitraryValues)
|
|
250
|
+
.map(candidate => ({
|
|
251
|
+
content,
|
|
252
|
+
extension,
|
|
253
|
+
localStart: candidate.start,
|
|
254
|
+
rawCandidate: candidate.rawCandidate,
|
|
255
|
+
start: candidate.start + offset,
|
|
256
|
+
end: candidate.end + offset,
|
|
257
|
+
}))
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function extractCssApplyCandidates(content: string, extension: string, options?: ExtractCandidateOptions) {
|
|
227
261
|
const candidates: ExtractSourceCandidateWithContext[] = []
|
|
228
262
|
CSS_APPLY_RE.lastIndex = 0
|
|
229
263
|
let match = CSS_APPLY_RE.exec(content)
|
|
@@ -239,12 +273,13 @@ async function extractCssApplyCandidates(content: string, extension: string) {
|
|
|
239
273
|
start: candidate.start + applyParamsStart,
|
|
240
274
|
end: candidate.end + applyParamsStart,
|
|
241
275
|
})))
|
|
276
|
+
candidates.push(...createBareArbitraryValueCandidateContexts(applyParams, 'html', applyParamsStart, options))
|
|
242
277
|
match = CSS_APPLY_RE.exec(content)
|
|
243
278
|
}
|
|
244
279
|
return candidates
|
|
245
280
|
}
|
|
246
281
|
|
|
247
|
-
async function extractMixedSourceScriptCandidates(content: string) {
|
|
282
|
+
async function extractMixedSourceScriptCandidates(content: string, options?: ExtractCandidateOptions) {
|
|
248
283
|
const candidates: ExtractSourceCandidateWithContext[] = []
|
|
249
284
|
SFC_SCRIPT_BLOCK_RE.lastIndex = 0
|
|
250
285
|
let match = SFC_SCRIPT_BLOCK_RE.exec(content)
|
|
@@ -260,6 +295,7 @@ async function extractMixedSourceScriptCandidates(content: string) {
|
|
|
260
295
|
start: candidate.start + scriptStart,
|
|
261
296
|
end: candidate.end + scriptStart,
|
|
262
297
|
})))
|
|
298
|
+
candidates.push(...createBareArbitraryValueCandidateContexts(scriptContent, 'js', scriptStart, options))
|
|
263
299
|
match = SFC_SCRIPT_BLOCK_RE.exec(content)
|
|
264
300
|
}
|
|
265
301
|
return candidates
|
|
@@ -278,26 +314,30 @@ function createCandidateCacheKey(
|
|
|
278
314
|
export async function extractRawCandidatesWithPositions(
|
|
279
315
|
content: string,
|
|
280
316
|
extension: string = 'html',
|
|
317
|
+
options?: ExtractCandidateOptions,
|
|
281
318
|
): Promise<ExtractSourceCandidate[]> {
|
|
282
319
|
const { Scanner } = await getOxideModule()
|
|
283
320
|
const scanner = new Scanner({})
|
|
284
321
|
const result = scanner.getCandidatesWithPositions({ content, extension })
|
|
285
322
|
|
|
286
|
-
|
|
323
|
+
const candidates = result.map(({ candidate, position }) => ({
|
|
287
324
|
rawCandidate: candidate,
|
|
288
325
|
start: position,
|
|
289
326
|
end: position + candidate.length,
|
|
290
327
|
}))
|
|
328
|
+
candidates.push(...extractBareArbitraryValueSourceCandidatesWithPositions(content, options?.bareArbitraryValues))
|
|
329
|
+
return dedupeCandidatesWithPositions(candidates)
|
|
291
330
|
}
|
|
292
331
|
|
|
293
332
|
export async function extractSourceCandidatesWithPositions(
|
|
294
333
|
content: string,
|
|
295
334
|
extension: string = 'html',
|
|
335
|
+
options?: ExtractCandidateOptions,
|
|
296
336
|
): Promise<ExtractSourceCandidate[]> {
|
|
297
337
|
const normalizedExtension = extension.replace(/^\./, '')
|
|
298
338
|
const candidates: ExtractSourceCandidateWithContext[] = CSS_LIKE_SOURCE_EXTENSION_RE.test(normalizedExtension)
|
|
299
|
-
? await extractCssApplyCandidates(content, normalizedExtension)
|
|
300
|
-
: (await extractRawCandidatesWithPositions(content, normalizedExtension))
|
|
339
|
+
? await extractCssApplyCandidates(content, normalizedExtension, options)
|
|
340
|
+
: (await extractRawCandidatesWithPositions(content, normalizedExtension, options))
|
|
301
341
|
.map(candidate => ({
|
|
302
342
|
...candidate,
|
|
303
343
|
content,
|
|
@@ -305,7 +345,7 @@ export async function extractSourceCandidatesWithPositions(
|
|
|
305
345
|
localStart: candidate.start,
|
|
306
346
|
}))
|
|
307
347
|
if (MIXED_TEMPLATE_SOURCE_EXTENSION_RE.test(normalizedExtension)) {
|
|
308
|
-
candidates.push(...await extractMixedSourceScriptCandidates(content))
|
|
348
|
+
candidates.push(...await extractMixedSourceScriptCandidates(content, options))
|
|
309
349
|
}
|
|
310
350
|
const seen = new Set<string>()
|
|
311
351
|
return candidates.filter((candidate) => {
|
|
@@ -324,18 +364,37 @@ export async function extractSourceCandidatesWithPositions(
|
|
|
324
364
|
export async function extractSourceCandidates(
|
|
325
365
|
content: string,
|
|
326
366
|
extension: string = 'html',
|
|
367
|
+
options?: ExtractCandidateOptions,
|
|
327
368
|
): Promise<string[]> {
|
|
328
|
-
const candidates = await extractSourceCandidatesWithPositions(content, extension)
|
|
369
|
+
const candidates = await extractSourceCandidatesWithPositions(content, extension, options)
|
|
329
370
|
return [...new Set(candidates.map(candidate => candidate.rawCandidate))]
|
|
330
371
|
}
|
|
331
372
|
|
|
332
373
|
export async function extractRawCandidates(
|
|
333
374
|
sources?: SourceEntry[],
|
|
375
|
+
options?: ExtractCandidateOptions,
|
|
334
376
|
): Promise<string[]> {
|
|
335
377
|
const { Scanner } = await getOxideModule()
|
|
336
378
|
const scanner = new Scanner(sources === undefined ? {} : { sources })
|
|
337
379
|
|
|
338
|
-
|
|
380
|
+
const candidates = new Set(scanner.scan())
|
|
381
|
+
if (options?.bareArbitraryValues !== undefined && options.bareArbitraryValues !== false) {
|
|
382
|
+
await Promise.all((scanner.files ?? []).map(async (file) => {
|
|
383
|
+
try {
|
|
384
|
+
const content = await fs.readFile(file, 'utf8')
|
|
385
|
+
const extension = toExtension(file)
|
|
386
|
+
for (const candidate of extractBareArbitraryValueSourceCandidatesWithPositions(content, options.bareArbitraryValues)) {
|
|
387
|
+
if (shouldKeepSourceCandidate(content, extension, candidate)) {
|
|
388
|
+
candidates.add(candidate.rawCandidate)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
// 文件可能在扫描和读取之间被移除,保持与 Tailwind 原扫描结果一致。
|
|
394
|
+
}
|
|
395
|
+
}))
|
|
396
|
+
}
|
|
397
|
+
return [...candidates]
|
|
339
398
|
}
|
|
340
399
|
|
|
341
400
|
export async function extractValidCandidates(options?: ExtractValidCandidatesOption) {
|
|
@@ -370,7 +429,12 @@ export async function extractValidCandidates(options?: ExtractValidCandidatesOpt
|
|
|
370
429
|
const candidateCache = designSystemCandidateCache.get(candidateCacheKey) ?? new Map<string, boolean>()
|
|
371
430
|
designSystemCandidateCache.set(candidateCacheKey, candidateCache)
|
|
372
431
|
|
|
373
|
-
const candidates = await extractRawCandidates(
|
|
432
|
+
const candidates = await extractRawCandidates(
|
|
433
|
+
sources,
|
|
434
|
+
providedOptions.bareArbitraryValues === undefined
|
|
435
|
+
? undefined
|
|
436
|
+
: { bareArbitraryValues: providedOptions.bareArbitraryValues },
|
|
437
|
+
)
|
|
374
438
|
const inlineSources = extractTailwindV4InlineSourceCandidates(css)
|
|
375
439
|
for (const candidate of inlineSources.included) {
|
|
376
440
|
candidates.push(candidate)
|
package/src/public-api.ts
CHANGED
|
@@ -2,6 +2,17 @@ import type { TailwindcssMangleConfig } from '@tailwindcss-mangle/config'
|
|
|
2
2
|
|
|
3
3
|
export { TailwindcssPatcher } from './api/tailwindcss-patcher'
|
|
4
4
|
export { CacheStore } from './cache/store'
|
|
5
|
+
export {
|
|
6
|
+
type ConfigFileMigrationEntry,
|
|
7
|
+
type ConfigFileMigrationReport,
|
|
8
|
+
migrateConfigFiles,
|
|
9
|
+
type MigrateConfigFilesOptions,
|
|
10
|
+
MIGRATION_REPORT_KIND,
|
|
11
|
+
MIGRATION_REPORT_SCHEMA_VERSION,
|
|
12
|
+
restoreConfigFiles,
|
|
13
|
+
type RestoreConfigFilesOptions,
|
|
14
|
+
type RestoreConfigFilesResult,
|
|
15
|
+
} from './commands/migrate-config'
|
|
5
16
|
export {
|
|
6
17
|
type TailwindcssPatchCliMountOptions,
|
|
7
18
|
type TailwindcssPatchCliOptions,
|
|
@@ -22,29 +33,18 @@ export {
|
|
|
22
33
|
type ValidateJsonFailurePayload,
|
|
23
34
|
type ValidateJsonSuccessPayload,
|
|
24
35
|
} from './commands/validate'
|
|
25
|
-
export {
|
|
26
|
-
type ConfigFileMigrationEntry,
|
|
27
|
-
type ConfigFileMigrationReport,
|
|
28
|
-
migrateConfigFiles,
|
|
29
|
-
type MigrateConfigFilesOptions,
|
|
30
|
-
MIGRATION_REPORT_KIND,
|
|
31
|
-
MIGRATION_REPORT_SCHEMA_VERSION,
|
|
32
|
-
restoreConfigFiles,
|
|
33
|
-
type RestoreConfigFilesOptions,
|
|
34
|
-
type RestoreConfigFilesResult,
|
|
35
|
-
} from './commands/migrate-config'
|
|
36
36
|
export { normalizeOptions } from './config'
|
|
37
37
|
export type { TailwindCssPatchOptions } from './config'
|
|
38
38
|
export {
|
|
39
39
|
extractProjectCandidatesWithPositions,
|
|
40
40
|
extractRawCandidates,
|
|
41
41
|
extractRawCandidatesWithPositions,
|
|
42
|
+
type ExtractSourceCandidate,
|
|
42
43
|
extractSourceCandidates,
|
|
43
44
|
extractSourceCandidatesWithPositions,
|
|
44
45
|
extractValidCandidates,
|
|
45
46
|
groupTokensByFile,
|
|
46
47
|
resolveProjectSourceFiles,
|
|
47
|
-
type ExtractSourceCandidate,
|
|
48
48
|
} from './extraction/candidate-extractor'
|
|
49
49
|
export {
|
|
50
50
|
isValidCandidateToken,
|
|
@@ -61,21 +61,28 @@ export {
|
|
|
61
61
|
export { default as logger } from './logger'
|
|
62
62
|
export * from './types'
|
|
63
63
|
export {
|
|
64
|
+
canonicalizeBareArbitraryValueCandidates,
|
|
64
65
|
createTailwindV4CompiledSourceEntries,
|
|
65
|
-
createTailwindV4Engine,
|
|
66
66
|
createTailwindV4DefaultIgnoreSources,
|
|
67
|
+
createTailwindV4Engine,
|
|
67
68
|
createTailwindV4RootSources,
|
|
68
69
|
createTailwindV4SourceEntryMatcher,
|
|
69
70
|
createTailwindV4SourceExclusionMatcher,
|
|
71
|
+
escapeCssClassName,
|
|
70
72
|
expandTailwindV4SourceEntries,
|
|
71
73
|
expandTailwindV4SourceEntryBraces,
|
|
74
|
+
extractBareArbitraryValueSourceCandidates,
|
|
75
|
+
extractBareArbitraryValueSourceCandidatesWithPositions,
|
|
72
76
|
isFileExcludedByTailwindV4SourceEntries,
|
|
73
77
|
isFileMatchedByTailwindV4SourceEntries,
|
|
78
|
+
isBareArbitraryValuesEnabled,
|
|
74
79
|
loadTailwindV4DesignSystem,
|
|
75
80
|
mergeTailwindV4SourceEntries,
|
|
76
81
|
normalizeTailwindV4ScannerSources,
|
|
77
82
|
normalizeTailwindV4SourceEntries,
|
|
78
83
|
resolveSourceScanPath,
|
|
84
|
+
replaceBareArbitraryValueSelectors,
|
|
85
|
+
resolveBareArbitraryValueCandidate,
|
|
79
86
|
resolveTailwindV4Source,
|
|
80
87
|
resolveTailwindV4SourceBaseCandidates,
|
|
81
88
|
resolveTailwindV4SourceEntry,
|
|
@@ -10,6 +10,12 @@ export interface BareArbitraryValueResolveResult {
|
|
|
10
10
|
canonicalCandidate: string
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export interface BareArbitraryValueSourceCandidate {
|
|
14
|
+
rawCandidate: string
|
|
15
|
+
start: number
|
|
16
|
+
end: number
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
|
|
14
20
|
'%',
|
|
15
21
|
'px',
|
|
@@ -41,6 +47,8 @@ const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
|
|
|
41
47
|
const NUMBER_RE = /^-?(?:\d+|\d*\.\d+)$/
|
|
42
48
|
const FUNCTION_VALUE_RE = /^[a-z_-][\w-]*\(/i
|
|
43
49
|
const HEX_ESCAPE_RE = /^[\da-f]$/i
|
|
50
|
+
const ASPECT_RATIO_RE = /^\d+\/\d+$/
|
|
51
|
+
const ESCAPED_WHITESPACE_RE = /\\[nrt]/g
|
|
44
52
|
|
|
45
53
|
function splitVariantPrefix(candidate: string) {
|
|
46
54
|
let depth = 0
|
|
@@ -221,6 +229,10 @@ function normalizeBareArbitraryValueOptions(options: boolean | BareArbitraryValu
|
|
|
221
229
|
}
|
|
222
230
|
}
|
|
223
231
|
|
|
232
|
+
export function isBareArbitraryValuesEnabled(options: boolean | BareArbitraryValueOptions | undefined) {
|
|
233
|
+
return normalizeBareArbitraryValueOptions(options) !== undefined
|
|
234
|
+
}
|
|
235
|
+
|
|
224
236
|
function normalizeEscapedValue(value: string) {
|
|
225
237
|
let result = ''
|
|
226
238
|
for (let index = 0; index < value.length; index++) {
|
|
@@ -276,13 +288,17 @@ function resolveValueWithUnit(body: string, units: string[]) {
|
|
|
276
288
|
}
|
|
277
289
|
}
|
|
278
290
|
|
|
279
|
-
function resolveArbitraryValue(body: string, units: string[]) {
|
|
291
|
+
function resolveArbitraryValue(utility: string, body: string, units: string[]) {
|
|
280
292
|
const value = normalizeEscapedValue(body)
|
|
281
293
|
const withUnit = resolveValueWithUnit(value, units)
|
|
282
294
|
if (withUnit) {
|
|
283
295
|
return withUnit
|
|
284
296
|
}
|
|
285
297
|
|
|
298
|
+
if (utility === 'aspect' && ASPECT_RATIO_RE.test(value)) {
|
|
299
|
+
return value
|
|
300
|
+
}
|
|
301
|
+
|
|
286
302
|
if (isHexColorValue(value)) {
|
|
287
303
|
return value
|
|
288
304
|
}
|
|
@@ -292,6 +308,9 @@ function resolveArbitraryValue(body: string, units: string[]) {
|
|
|
292
308
|
}
|
|
293
309
|
|
|
294
310
|
if (FUNCTION_VALUE_RE.test(value) && value.endsWith(')') && isBalancedFunctionValue(value)) {
|
|
311
|
+
if (utility === 'text' && /^var\(/i.test(value)) {
|
|
312
|
+
return `color:${value}`
|
|
313
|
+
}
|
|
295
314
|
return value
|
|
296
315
|
}
|
|
297
316
|
}
|
|
@@ -339,7 +358,7 @@ function resolveUtilityAndValue(body: string, units: string[]) {
|
|
|
339
358
|
continue
|
|
340
359
|
}
|
|
341
360
|
|
|
342
|
-
const value = resolveArbitraryValue(rawValue, units)
|
|
361
|
+
const value = resolveArbitraryValue(utility, rawValue, units)
|
|
343
362
|
if (value) {
|
|
344
363
|
return {
|
|
345
364
|
utility,
|
|
@@ -380,6 +399,112 @@ export function resolveBareArbitraryValueCandidate(
|
|
|
380
399
|
}
|
|
381
400
|
}
|
|
382
401
|
|
|
402
|
+
function isBareArbitrarySourceSplitter(char: string) {
|
|
403
|
+
return /\s/.test(char)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function isQuoteBoundary(content: string, start: number, index: number) {
|
|
407
|
+
const tokenPrefix = content.slice(start, index)
|
|
408
|
+
return tokenPrefix.length === 0 || !tokenPrefix.endsWith('-')
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function trimBareArbitrarySourceToken(token: string, start: number) {
|
|
412
|
+
let nextToken = token
|
|
413
|
+
let nextStart = start
|
|
414
|
+
while (nextToken.length > 0 && /^[<{([]$/.test(nextToken[0]!)) {
|
|
415
|
+
nextToken = nextToken.slice(1)
|
|
416
|
+
nextStart++
|
|
417
|
+
}
|
|
418
|
+
while (nextToken.length > 0 && /^[>\],;]$/.test(nextToken[nextToken.length - 1]!)) {
|
|
419
|
+
nextToken = nextToken.slice(0, -1)
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
token: nextToken,
|
|
423
|
+
start: nextStart,
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function pushBareArbitrarySourceCandidate(
|
|
428
|
+
result: BareArbitraryValueSourceCandidate[],
|
|
429
|
+
token: string,
|
|
430
|
+
start: number,
|
|
431
|
+
options: boolean | BareArbitraryValueOptions | undefined,
|
|
432
|
+
) {
|
|
433
|
+
const trimmed = trimBareArbitrarySourceToken(token, start)
|
|
434
|
+
if (!trimmed.token || trimmed.token.includes('=') || trimmed.token.includes('[') || trimmed.token.includes(']')) {
|
|
435
|
+
return
|
|
436
|
+
}
|
|
437
|
+
if (!resolveBareArbitraryValueCandidate(trimmed.token, options)) {
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
result.push({
|
|
441
|
+
rawCandidate: trimmed.token,
|
|
442
|
+
start: trimmed.start,
|
|
443
|
+
end: trimmed.start + trimmed.token.length,
|
|
444
|
+
})
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export function extractBareArbitraryValueSourceCandidatesWithPositions(
|
|
448
|
+
content: string,
|
|
449
|
+
options?: boolean | BareArbitraryValueOptions,
|
|
450
|
+
): BareArbitraryValueSourceCandidate[] {
|
|
451
|
+
if (!isBareArbitraryValuesEnabled(options)) {
|
|
452
|
+
return []
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const normalized = content.includes('\\') ? content.replace(ESCAPED_WHITESPACE_RE, ' ') : content
|
|
456
|
+
const result: BareArbitraryValueSourceCandidate[] = []
|
|
457
|
+
let depth = 0
|
|
458
|
+
let quote: string | undefined
|
|
459
|
+
let start = 0
|
|
460
|
+
|
|
461
|
+
for (let index = 0; index < normalized.length; index++) {
|
|
462
|
+
const char = normalized[index]
|
|
463
|
+
if (char === undefined) {
|
|
464
|
+
continue
|
|
465
|
+
}
|
|
466
|
+
if (char === '\\') {
|
|
467
|
+
index++
|
|
468
|
+
continue
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (quote) {
|
|
472
|
+
if (char === quote) {
|
|
473
|
+
quote = undefined
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
else if ((char === '"' || char === '\'' || char === '`') && !isQuoteBoundary(normalized, start, index)) {
|
|
477
|
+
quote = char
|
|
478
|
+
}
|
|
479
|
+
else if (char === '(' || char === '{' || char === '[') {
|
|
480
|
+
depth++
|
|
481
|
+
}
|
|
482
|
+
else if (char === ')' || char === '}' || char === ']') {
|
|
483
|
+
depth = Math.max(0, depth - 1)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (!isBareArbitrarySourceSplitter(char) && !((char === '"' || char === '\'' || char === '`') && depth === 0 && isQuoteBoundary(normalized, start, index))) {
|
|
487
|
+
continue
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
pushBareArbitrarySourceCandidate(result, normalized.slice(start, index), start, options)
|
|
491
|
+
start = index + 1
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
pushBareArbitrarySourceCandidate(result, normalized.slice(start), start, options)
|
|
495
|
+
return result
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export function extractBareArbitraryValueSourceCandidates(
|
|
499
|
+
content: string,
|
|
500
|
+
options?: boolean | BareArbitraryValueOptions,
|
|
501
|
+
) {
|
|
502
|
+
return [...new Set(
|
|
503
|
+
extractBareArbitraryValueSourceCandidatesWithPositions(content, options)
|
|
504
|
+
.map(candidate => candidate.rawCandidate),
|
|
505
|
+
)]
|
|
506
|
+
}
|
|
507
|
+
|
|
383
508
|
// Based on the CSS.escape algorithm, scoped to class selector escaping.
|
|
384
509
|
export function escapeCssClassName(value: string) {
|
|
385
510
|
let result = ''
|
package/src/v4/engine.ts
CHANGED
|
@@ -37,13 +37,16 @@ async function collectRawCandidates(
|
|
|
37
37
|
compiledSources: TailwindV4SourcePattern[] = [],
|
|
38
38
|
) {
|
|
39
39
|
const rawCandidates = new Set<string>()
|
|
40
|
+
const extractOptions = options?.bareArbitraryValues === undefined
|
|
41
|
+
? undefined
|
|
42
|
+
: { bareArbitraryValues: options.bareArbitraryValues }
|
|
40
43
|
|
|
41
44
|
for (const candidate of options?.candidates ?? []) {
|
|
42
45
|
rawCandidates.add(candidate)
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
for (const candidateSource of options?.sources ?? []) {
|
|
46
|
-
const candidates = await extractRawCandidatesWithPositions(candidateSource.content, candidateSource.extension)
|
|
49
|
+
const candidates = await extractRawCandidatesWithPositions(candidateSource.content, candidateSource.extension, extractOptions)
|
|
47
50
|
for (const candidate of candidates) {
|
|
48
51
|
rawCandidates.add(candidate.rawCandidate)
|
|
49
52
|
}
|
|
@@ -51,7 +54,7 @@ async function collectRawCandidates(
|
|
|
51
54
|
|
|
52
55
|
const filesystemSources = resolveScanSources(options, source, compiledRoot, compiledSources)
|
|
53
56
|
if (filesystemSources.length > 0) {
|
|
54
|
-
for (const candidate of await extractRawCandidates(filesystemSources)) {
|
|
57
|
+
for (const candidate of await extractRawCandidates(filesystemSources, extractOptions)) {
|
|
55
58
|
rawCandidates.add(candidate)
|
|
56
59
|
}
|
|
57
60
|
}
|
package/src/v4/index.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
export {
|
|
2
|
+
canonicalizeBareArbitraryValueCandidates,
|
|
2
3
|
extractTailwindV4InlineSourceCandidates,
|
|
4
|
+
replaceBareArbitraryValueSelectors,
|
|
3
5
|
resolveValidTailwindV4Candidates,
|
|
4
6
|
} from './candidates'
|
|
7
|
+
export {
|
|
8
|
+
escapeCssClassName,
|
|
9
|
+
extractBareArbitraryValueSourceCandidates,
|
|
10
|
+
extractBareArbitraryValueSourceCandidatesWithPositions,
|
|
11
|
+
isBareArbitraryValuesEnabled,
|
|
12
|
+
resolveBareArbitraryValueCandidate,
|
|
13
|
+
} from './bare-arbitrary-values'
|
|
5
14
|
export { createTailwindV4Engine } from './engine'
|
|
6
15
|
export {
|
|
7
16
|
compileTailwindV4Source,
|
|
8
17
|
loadTailwindV4DesignSystem,
|
|
9
18
|
loadTailwindV4NodeModule,
|
|
10
19
|
} from './node-adapter'
|
|
20
|
+
export {
|
|
21
|
+
resolveTailwindV4Source,
|
|
22
|
+
resolveTailwindV4SourceFromPatchOptions,
|
|
23
|
+
tailwindV4SourceOptionsFromPatchOptions,
|
|
24
|
+
} from './source'
|
|
11
25
|
export {
|
|
12
26
|
createTailwindV4CompiledSourceEntries,
|
|
13
27
|
createTailwindV4DefaultIgnoreSources,
|
|
@@ -29,11 +43,6 @@ export {
|
|
|
29
43
|
TAILWIND_V4_IGNORED_EXTENSIONS,
|
|
30
44
|
TAILWIND_V4_IGNORED_FILES,
|
|
31
45
|
} from './source-scan'
|
|
32
|
-
export {
|
|
33
|
-
resolveTailwindV4Source,
|
|
34
|
-
resolveTailwindV4SourceFromPatchOptions,
|
|
35
|
-
tailwindV4SourceOptionsFromPatchOptions,
|
|
36
|
-
} from './source'
|
|
37
46
|
export type {
|
|
38
47
|
TailwindV4CandidateSource,
|
|
39
48
|
TailwindV4CompiledSourceRoot,
|
package/src/v4/node-adapter.ts
CHANGED
package/src/v4/source-scan.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { TailwindV4CompiledSourceRoot, TailwindV4SourcePattern } from './ty
|
|
|
3
3
|
import { realpathSync } from 'node:fs'
|
|
4
4
|
import { stat } from 'node:fs/promises'
|
|
5
5
|
import process from 'node:process'
|
|
6
|
-
import path from 'pathe'
|
|
7
6
|
import micromatch from 'micromatch'
|
|
7
|
+
import path from 'pathe'
|
|
8
8
|
|
|
9
9
|
export const TAILWIND_V4_IGNORED_CONTENT_DIRS = [
|
|
10
10
|
'.git',
|