tailwindcss-patch 9.3.6 → 9.3.7
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-BR8XOc2F.mjs → index.bundle-ByoXMvTR.mjs} +2 -13
- package/dist/{index.bundle-BF-qTWsU.js → index.bundle-CuqnqGSX.js} +2 -13
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{validate-BF0OladD.mjs → validate-B7mTl9eT.mjs} +93 -82
- package/dist/{validate-DoeOD2Su.d.mts → validate-CDegYLlg.d.mts} +5 -4
- package/dist/{validate-CLTQnfBI.js → validate-D7h8SP0T.js} +98 -81
- package/dist/{validate-eCzgDdk_.d.ts → validate-DFiRmBtJ.d.ts} +5 -4
- package/package.json +2 -1
- package/src/extraction/candidate-extractor.ts +10 -99
- package/src/index.ts +21 -0
- package/src/v4/engine.ts +2 -10
- package/src/v4/index.ts +22 -0
- package/src/v4/node-adapter.ts +2 -4
- package/src/v4/source-scan.ts +432 -0
- package/src/v4/types.ts +6 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailwindcss-patch",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.7",
|
|
4
4
|
"description": "patch tailwindcss for exposing context and extract classes",
|
|
5
5
|
"author": "ice breaker <1324318532@qq.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"consola": "^3.4.2",
|
|
72
72
|
"fs-extra": "^11.3.5",
|
|
73
73
|
"local-pkg": "^1.2.1",
|
|
74
|
+
"micromatch": "^4.0.8",
|
|
74
75
|
"pathe": "^2.0.3",
|
|
75
76
|
"postcss": "^8.5.15",
|
|
76
77
|
"semver": "^7.8.1",
|
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
} from '../v4/bare-arbitrary-values'
|
|
15
15
|
import { extractTailwindV4InlineSourceCandidates, resolveValidTailwindV4Candidates } from '../v4/candidates'
|
|
16
16
|
import { compileTailwindV4Source, getTailwindV4DesignSystemCacheKey, loadTailwindV4DesignSystem } from '../v4/node-adapter'
|
|
17
|
+
import {
|
|
18
|
+
createTailwindV4CompiledSourceEntries,
|
|
19
|
+
normalizeTailwindV4ScannerSources,
|
|
20
|
+
} from '../v4/source-scan'
|
|
17
21
|
|
|
18
22
|
let oxideImportPromise: ReturnType<typeof importOxide> | undefined
|
|
19
23
|
const designSystemCandidateCache = new Map<string, Map<string, boolean>>()
|
|
@@ -507,99 +511,12 @@ export interface ResolveProjectSourceFilesOptions {
|
|
|
507
511
|
filter?: (file: string) => boolean
|
|
508
512
|
}
|
|
509
513
|
|
|
510
|
-
function expandBracePattern(pattern: string) {
|
|
511
|
-
const index = pattern.indexOf('{')
|
|
512
|
-
if (index === -1) {
|
|
513
|
-
return [pattern]
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
const rest = pattern.slice(index)
|
|
517
|
-
let depth = 0
|
|
518
|
-
let endIndex = -1
|
|
519
|
-
for (let i = 0; i < rest.length; i++) {
|
|
520
|
-
const char = rest[i]
|
|
521
|
-
if (char === '\\') {
|
|
522
|
-
i += 1
|
|
523
|
-
continue
|
|
524
|
-
}
|
|
525
|
-
if (char === '{') {
|
|
526
|
-
depth += 1
|
|
527
|
-
continue
|
|
528
|
-
}
|
|
529
|
-
if (char === '}') {
|
|
530
|
-
depth -= 1
|
|
531
|
-
if (depth === 0) {
|
|
532
|
-
endIndex = i
|
|
533
|
-
break
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
if (endIndex === -1) {
|
|
538
|
-
return [pattern]
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
const prefix = pattern.slice(0, index)
|
|
542
|
-
const inner = rest.slice(1, endIndex)
|
|
543
|
-
const suffix = rest.slice(endIndex + 1)
|
|
544
|
-
const parts: string[] = []
|
|
545
|
-
const stack: string[] = []
|
|
546
|
-
let lastPos = 0
|
|
547
|
-
for (let i = 0; i < inner.length; i++) {
|
|
548
|
-
const char = inner[i]
|
|
549
|
-
if (char === '\\') {
|
|
550
|
-
i += 1
|
|
551
|
-
continue
|
|
552
|
-
}
|
|
553
|
-
if (char === '{') {
|
|
554
|
-
stack.push('}')
|
|
555
|
-
continue
|
|
556
|
-
}
|
|
557
|
-
if (char === '}' && stack[stack.length - 1] === '}') {
|
|
558
|
-
stack.pop()
|
|
559
|
-
continue
|
|
560
|
-
}
|
|
561
|
-
if (char === ',' && stack.length === 0) {
|
|
562
|
-
parts.push(inner.slice(lastPos, i))
|
|
563
|
-
lastPos = i + 1
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
parts.push(inner.slice(lastPos))
|
|
567
|
-
|
|
568
|
-
return parts.flatMap(part =>
|
|
569
|
-
expandBracePattern(`${prefix}${part}${suffix}`))
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function normalizeScannerSources(
|
|
573
|
-
sources: SourceEntry[] | undefined,
|
|
574
|
-
cwd: string,
|
|
575
|
-
ignoredSources: SourceEntry[] = [],
|
|
576
|
-
) {
|
|
577
|
-
const baseSources = sources?.length
|
|
578
|
-
? sources
|
|
579
|
-
: [
|
|
580
|
-
{
|
|
581
|
-
base: cwd,
|
|
582
|
-
pattern: '**/*',
|
|
583
|
-
negated: false,
|
|
584
|
-
},
|
|
585
|
-
]
|
|
586
|
-
|
|
587
|
-
return [...baseSources, ...ignoredSources].flatMap((source) => {
|
|
588
|
-
const base = source.base ?? cwd
|
|
589
|
-
return expandBracePattern(source.pattern).map(pattern => ({
|
|
590
|
-
base,
|
|
591
|
-
pattern,
|
|
592
|
-
negated: source.negated,
|
|
593
|
-
}))
|
|
594
|
-
})
|
|
595
|
-
}
|
|
596
|
-
|
|
597
514
|
async function resolveScannerSources(options?: ResolveProjectSourceFilesOptions) {
|
|
598
515
|
const cwd = options?.cwd ? path.resolve(options.cwd) : process.cwd()
|
|
599
516
|
if (options?.sources?.length || options?.css === undefined) {
|
|
600
517
|
return {
|
|
601
518
|
cwd,
|
|
602
|
-
sources:
|
|
519
|
+
sources: normalizeTailwindV4ScannerSources(options?.sources, cwd, options?.ignoredSources),
|
|
603
520
|
}
|
|
604
521
|
}
|
|
605
522
|
|
|
@@ -611,19 +528,13 @@ async function resolveScannerSources(options?: ResolveProjectSourceFilesOptions)
|
|
|
611
528
|
css: options.css,
|
|
612
529
|
dependencies: [],
|
|
613
530
|
})
|
|
614
|
-
const rootSources = (() => {
|
|
615
|
-
if (compiled.root === 'none') {
|
|
616
|
-
return []
|
|
617
|
-
}
|
|
618
|
-
if (compiled.root === null) {
|
|
619
|
-
return [{ base, pattern: '**/*', negated: false }]
|
|
620
|
-
}
|
|
621
|
-
return [{ ...compiled.root, negated: false }]
|
|
622
|
-
})()
|
|
623
|
-
|
|
624
531
|
return {
|
|
625
532
|
cwd,
|
|
626
|
-
sources:
|
|
533
|
+
sources: normalizeTailwindV4ScannerSources(
|
|
534
|
+
createTailwindV4CompiledSourceEntries(compiled.root, compiled.sources, base),
|
|
535
|
+
cwd,
|
|
536
|
+
options.ignoredSources,
|
|
537
|
+
),
|
|
627
538
|
}
|
|
628
539
|
}
|
|
629
540
|
|
package/src/index.ts
CHANGED
|
@@ -56,14 +56,34 @@ export {
|
|
|
56
56
|
export { default as logger } from './logger'
|
|
57
57
|
export * from './types'
|
|
58
58
|
export {
|
|
59
|
+
createTailwindV4CompiledSourceEntries,
|
|
59
60
|
createTailwindV4Engine,
|
|
61
|
+
createTailwindV4DefaultIgnoreSources,
|
|
62
|
+
createTailwindV4RootSources,
|
|
63
|
+
createTailwindV4SourceEntryMatcher,
|
|
64
|
+
createTailwindV4SourceExclusionMatcher,
|
|
65
|
+
expandTailwindV4SourceEntries,
|
|
66
|
+
expandTailwindV4SourceEntryBraces,
|
|
67
|
+
isFileExcludedByTailwindV4SourceEntries,
|
|
68
|
+
isFileMatchedByTailwindV4SourceEntries,
|
|
60
69
|
loadTailwindV4DesignSystem,
|
|
70
|
+
mergeTailwindV4SourceEntries,
|
|
71
|
+
normalizeTailwindV4ScannerSources,
|
|
72
|
+
normalizeTailwindV4SourceEntries,
|
|
73
|
+
resolveSourceScanPath,
|
|
61
74
|
resolveTailwindV4Source,
|
|
75
|
+
resolveTailwindV4SourceBaseCandidates,
|
|
76
|
+
resolveTailwindV4SourceEntry,
|
|
62
77
|
resolveTailwindV4SourceFromPatchOptions,
|
|
63
78
|
resolveValidTailwindV4Candidates,
|
|
79
|
+
TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
|
|
80
|
+
TAILWIND_V4_IGNORED_CONTENT_DIRS,
|
|
81
|
+
TAILWIND_V4_IGNORED_EXTENSIONS,
|
|
82
|
+
TAILWIND_V4_IGNORED_FILES,
|
|
64
83
|
} from './v4'
|
|
65
84
|
export type {
|
|
66
85
|
TailwindV4CandidateSource,
|
|
86
|
+
TailwindV4CompiledSourceRoot,
|
|
67
87
|
TailwindV4CssSource,
|
|
68
88
|
TailwindV4DesignSystem,
|
|
69
89
|
TailwindV4Engine,
|
|
@@ -71,6 +91,7 @@ export type {
|
|
|
71
91
|
TailwindV4GenerateResult,
|
|
72
92
|
TailwindV4ResolvedSource,
|
|
73
93
|
TailwindV4SourceOptions,
|
|
94
|
+
TailwindV4SourcePattern,
|
|
74
95
|
} from './v4'
|
|
75
96
|
|
|
76
97
|
export function defineConfig<T extends TailwindcssMangleConfig>(config: T): T {
|
package/src/v4/engine.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
resolveValidTailwindV4Candidates,
|
|
14
14
|
} from './candidates'
|
|
15
15
|
import { compileTailwindV4Source, loadTailwindV4DesignSystem } from './node-adapter'
|
|
16
|
+
import { createTailwindV4CompiledSourceEntries } from './source-scan'
|
|
16
17
|
|
|
17
18
|
function resolveScanSources(
|
|
18
19
|
options: TailwindV4GenerateOptions | undefined,
|
|
@@ -24,16 +25,7 @@ function resolveScanSources(
|
|
|
24
25
|
return options.scanSources
|
|
25
26
|
}
|
|
26
27
|
if (options?.scanSources === true) {
|
|
27
|
-
|
|
28
|
-
if (compiledRoot === 'none') {
|
|
29
|
-
return []
|
|
30
|
-
}
|
|
31
|
-
if (compiledRoot === null) {
|
|
32
|
-
return [{ base: source.base, pattern: '**/*', negated: false }]
|
|
33
|
-
}
|
|
34
|
-
return [{ ...compiledRoot, negated: false }]
|
|
35
|
-
})()
|
|
36
|
-
return [...rootSources, ...compiledSources]
|
|
28
|
+
return createTailwindV4CompiledSourceEntries(compiledRoot, compiledSources, source.base)
|
|
37
29
|
}
|
|
38
30
|
return []
|
|
39
31
|
}
|
package/src/v4/index.ts
CHANGED
|
@@ -8,6 +8,27 @@ export {
|
|
|
8
8
|
loadTailwindV4DesignSystem,
|
|
9
9
|
loadTailwindV4NodeModule,
|
|
10
10
|
} from './node-adapter'
|
|
11
|
+
export {
|
|
12
|
+
createTailwindV4CompiledSourceEntries,
|
|
13
|
+
createTailwindV4DefaultIgnoreSources,
|
|
14
|
+
createTailwindV4RootSources,
|
|
15
|
+
createTailwindV4SourceEntryMatcher,
|
|
16
|
+
createTailwindV4SourceExclusionMatcher,
|
|
17
|
+
expandTailwindV4SourceEntries,
|
|
18
|
+
expandTailwindV4SourceEntryBraces,
|
|
19
|
+
isFileExcludedByTailwindV4SourceEntries,
|
|
20
|
+
isFileMatchedByTailwindV4SourceEntries,
|
|
21
|
+
mergeTailwindV4SourceEntries,
|
|
22
|
+
normalizeTailwindV4ScannerSources,
|
|
23
|
+
normalizeTailwindV4SourceEntries,
|
|
24
|
+
resolveSourceScanPath,
|
|
25
|
+
resolveTailwindV4SourceBaseCandidates,
|
|
26
|
+
resolveTailwindV4SourceEntry,
|
|
27
|
+
TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
|
|
28
|
+
TAILWIND_V4_IGNORED_CONTENT_DIRS,
|
|
29
|
+
TAILWIND_V4_IGNORED_EXTENSIONS,
|
|
30
|
+
TAILWIND_V4_IGNORED_FILES,
|
|
31
|
+
} from './source-scan'
|
|
11
32
|
export {
|
|
12
33
|
resolveTailwindV4Source,
|
|
13
34
|
resolveTailwindV4SourceFromPatchOptions,
|
|
@@ -15,6 +36,7 @@ export {
|
|
|
15
36
|
} from './source'
|
|
16
37
|
export type {
|
|
17
38
|
TailwindV4CandidateSource,
|
|
39
|
+
TailwindV4CompiledSourceRoot,
|
|
18
40
|
TailwindV4CssSource,
|
|
19
41
|
TailwindV4DesignSystem,
|
|
20
42
|
TailwindV4Engine,
|
package/src/v4/node-adapter.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
TailwindV4DesignSystem,
|
|
3
3
|
TailwindV4ResolvedSource,
|
|
4
|
+
TailwindV4CompiledSourceRoot,
|
|
4
5
|
TailwindV4SourcePattern,
|
|
5
6
|
} from './types'
|
|
6
7
|
import { createRequire } from 'node:module'
|
|
@@ -9,10 +10,7 @@ import path from 'pathe'
|
|
|
9
10
|
|
|
10
11
|
interface TailwindV4CompiledSource {
|
|
11
12
|
sources: TailwindV4SourcePattern[]
|
|
12
|
-
root:
|
|
13
|
-
base: string
|
|
14
|
-
pattern: string
|
|
15
|
-
}
|
|
13
|
+
root: TailwindV4CompiledSourceRoot
|
|
16
14
|
build: (candidates: string[]) => string
|
|
17
15
|
}
|
|
18
16
|
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import type { SourceEntry } from '@tailwindcss/oxide'
|
|
2
|
+
import type { TailwindV4CompiledSourceRoot, TailwindV4SourcePattern } from './types'
|
|
3
|
+
import { realpathSync } from 'node:fs'
|
|
4
|
+
import { stat } from 'node:fs/promises'
|
|
5
|
+
import process from 'node:process'
|
|
6
|
+
import path from 'pathe'
|
|
7
|
+
import micromatch from 'micromatch'
|
|
8
|
+
|
|
9
|
+
export const TAILWIND_V4_IGNORED_CONTENT_DIRS = [
|
|
10
|
+
'.git',
|
|
11
|
+
'.hg',
|
|
12
|
+
'.jj',
|
|
13
|
+
'.next',
|
|
14
|
+
'.parcel-cache',
|
|
15
|
+
'.pnpm-store',
|
|
16
|
+
'.svelte-kit',
|
|
17
|
+
'.svn',
|
|
18
|
+
'.turbo',
|
|
19
|
+
'.venv',
|
|
20
|
+
'.vercel',
|
|
21
|
+
'.yarn',
|
|
22
|
+
'__pycache__',
|
|
23
|
+
'node_modules',
|
|
24
|
+
'venv',
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
export const TAILWIND_V4_IGNORED_EXTENSIONS = [
|
|
28
|
+
'less',
|
|
29
|
+
'lock',
|
|
30
|
+
'sass',
|
|
31
|
+
'scss',
|
|
32
|
+
'styl',
|
|
33
|
+
'log',
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
export const TAILWIND_V4_IGNORED_FILES = [
|
|
37
|
+
'package-lock.json',
|
|
38
|
+
'pnpm-lock.yaml',
|
|
39
|
+
'bun.lockb',
|
|
40
|
+
'.gitignore',
|
|
41
|
+
'.env',
|
|
42
|
+
'.env.*',
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
export const TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN = '**/*'
|
|
46
|
+
|
|
47
|
+
function uniqueResolvedPaths(values: Iterable<string | undefined>) {
|
|
48
|
+
const result: string[] = []
|
|
49
|
+
for (const value of values) {
|
|
50
|
+
if (!value) {
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
const resolved = path.resolve(value)
|
|
54
|
+
if (!result.includes(resolved)) {
|
|
55
|
+
result.push(resolved)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return result
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function toPosixPath(value: string) {
|
|
62
|
+
return value.replaceAll(path.sep, '/')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function resolveSourceScanPath(value: string) {
|
|
66
|
+
const resolved = path.resolve(value)
|
|
67
|
+
try {
|
|
68
|
+
return realpathSync.native(resolved)
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return resolved
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function normalizeGlobPattern(pattern: string) {
|
|
76
|
+
return pattern.startsWith('./') ? pattern.slice(2) : pattern
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function hasGlobMagic(value: string) {
|
|
80
|
+
return /[*?[\]{}()!+@]/.test(value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function splitStaticGlobPrefix(pattern: string) {
|
|
84
|
+
const normalized = normalizeGlobPattern(pattern)
|
|
85
|
+
const segments = normalized.split(/[\\/]+/)
|
|
86
|
+
const prefix: string[] = []
|
|
87
|
+
const rest: string[] = []
|
|
88
|
+
let reachedGlob = false
|
|
89
|
+
|
|
90
|
+
for (const segment of segments) {
|
|
91
|
+
if (!reachedGlob && segment && !hasGlobMagic(segment)) {
|
|
92
|
+
prefix.push(segment)
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
reachedGlob = true
|
|
96
|
+
rest.push(segment)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
prefix,
|
|
101
|
+
rest,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function pathExistsAsDirectory(file: string) {
|
|
106
|
+
try {
|
|
107
|
+
return (await stat(file)).isDirectory()
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function createTailwindV4DefaultIgnoreSources(base: string): TailwindV4SourcePattern[] {
|
|
115
|
+
return [
|
|
116
|
+
...TAILWIND_V4_IGNORED_CONTENT_DIRS.map(pattern => ({
|
|
117
|
+
base,
|
|
118
|
+
pattern: `**/${pattern}/**`,
|
|
119
|
+
negated: true,
|
|
120
|
+
})),
|
|
121
|
+
...TAILWIND_V4_IGNORED_EXTENSIONS.map(extension => ({
|
|
122
|
+
base,
|
|
123
|
+
pattern: `**/*.${extension}`,
|
|
124
|
+
negated: true,
|
|
125
|
+
})),
|
|
126
|
+
...TAILWIND_V4_IGNORED_FILES.map(pattern => ({
|
|
127
|
+
base,
|
|
128
|
+
pattern: `**/${pattern}`,
|
|
129
|
+
negated: true,
|
|
130
|
+
})),
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function createTailwindV4RootSources(
|
|
135
|
+
root: TailwindV4CompiledSourceRoot,
|
|
136
|
+
fallbackBase: string,
|
|
137
|
+
): TailwindV4SourcePattern[] {
|
|
138
|
+
if (root === 'none') {
|
|
139
|
+
return []
|
|
140
|
+
}
|
|
141
|
+
if (root === null) {
|
|
142
|
+
return [{ base: fallbackBase, pattern: TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN, negated: false }]
|
|
143
|
+
}
|
|
144
|
+
return [{ ...root, negated: false }]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function createTailwindV4CompiledSourceEntries(
|
|
148
|
+
root: TailwindV4CompiledSourceRoot,
|
|
149
|
+
sources: TailwindV4SourcePattern[],
|
|
150
|
+
fallbackBase: string,
|
|
151
|
+
) {
|
|
152
|
+
return [
|
|
153
|
+
...createTailwindV4RootSources(root, fallbackBase),
|
|
154
|
+
...sources,
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function resolveTailwindV4SourceEntry(
|
|
159
|
+
sourcePath: string,
|
|
160
|
+
base: string,
|
|
161
|
+
negated: boolean,
|
|
162
|
+
defaultPattern = TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
|
|
163
|
+
): Promise<TailwindV4SourcePattern> {
|
|
164
|
+
const absoluteSource = path.isAbsolute(sourcePath) ? path.resolve(sourcePath) : path.resolve(base, sourcePath)
|
|
165
|
+
|
|
166
|
+
if (await pathExistsAsDirectory(absoluteSource)) {
|
|
167
|
+
return {
|
|
168
|
+
base: absoluteSource,
|
|
169
|
+
negated,
|
|
170
|
+
pattern: normalizeGlobPattern(defaultPattern),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (path.isAbsolute(sourcePath)) {
|
|
175
|
+
return {
|
|
176
|
+
base: path.dirname(absoluteSource),
|
|
177
|
+
negated,
|
|
178
|
+
pattern: normalizeGlobPattern(path.basename(absoluteSource)),
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { prefix, rest } = splitStaticGlobPrefix(sourcePath)
|
|
183
|
+
if (prefix.length > 0 && rest.length > 0) {
|
|
184
|
+
return {
|
|
185
|
+
base: path.resolve(base, ...prefix),
|
|
186
|
+
negated,
|
|
187
|
+
pattern: normalizeGlobPattern(rest.join('/')),
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
base,
|
|
193
|
+
negated,
|
|
194
|
+
pattern: normalizeGlobPattern(sourcePath),
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export async function normalizeTailwindV4SourceEntries(
|
|
199
|
+
sources: TailwindV4SourcePattern[],
|
|
200
|
+
options: {
|
|
201
|
+
cwd?: string
|
|
202
|
+
defaultPattern?: string
|
|
203
|
+
} = {},
|
|
204
|
+
) {
|
|
205
|
+
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd()
|
|
206
|
+
return Promise.all(sources.map(source =>
|
|
207
|
+
resolveTailwindV4SourceEntry(
|
|
208
|
+
source.pattern,
|
|
209
|
+
source.base ? path.resolve(source.base) : cwd,
|
|
210
|
+
source.negated,
|
|
211
|
+
options.defaultPattern,
|
|
212
|
+
)))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function expandBracePattern(pattern: string): string[] {
|
|
216
|
+
const index = pattern.indexOf('{')
|
|
217
|
+
if (index === -1) {
|
|
218
|
+
return [pattern]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const rest = pattern.slice(index)
|
|
222
|
+
let depth = 0
|
|
223
|
+
let endIndex = -1
|
|
224
|
+
for (let i = 0; i < rest.length; i++) {
|
|
225
|
+
const char = rest[i]
|
|
226
|
+
if (char === '\\') {
|
|
227
|
+
i += 1
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
if (char === '{') {
|
|
231
|
+
depth += 1
|
|
232
|
+
continue
|
|
233
|
+
}
|
|
234
|
+
if (char === '}') {
|
|
235
|
+
depth -= 1
|
|
236
|
+
if (depth === 0) {
|
|
237
|
+
endIndex = i
|
|
238
|
+
break
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (endIndex === -1) {
|
|
243
|
+
return [pattern]
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const prefix = pattern.slice(0, index)
|
|
247
|
+
const inner = rest.slice(1, endIndex)
|
|
248
|
+
const suffix = rest.slice(endIndex + 1)
|
|
249
|
+
const parts: string[] = []
|
|
250
|
+
const stack: string[] = []
|
|
251
|
+
let lastPos = 0
|
|
252
|
+
for (let i = 0; i < inner.length; i++) {
|
|
253
|
+
const char = inner[i]
|
|
254
|
+
if (char === '\\') {
|
|
255
|
+
i += 1
|
|
256
|
+
continue
|
|
257
|
+
}
|
|
258
|
+
if (char === '{') {
|
|
259
|
+
stack.push('}')
|
|
260
|
+
continue
|
|
261
|
+
}
|
|
262
|
+
if (char === '}' && stack[stack.length - 1] === '}') {
|
|
263
|
+
stack.pop()
|
|
264
|
+
continue
|
|
265
|
+
}
|
|
266
|
+
if (char === ',' && stack.length === 0) {
|
|
267
|
+
parts.push(inner.slice(lastPos, i))
|
|
268
|
+
lastPos = i + 1
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
parts.push(inner.slice(lastPos))
|
|
272
|
+
|
|
273
|
+
return parts.flatMap(part =>
|
|
274
|
+
expandBracePattern(`${prefix}${part}${suffix}`))
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function expandTailwindV4SourceEntryBraces(sources: TailwindV4SourcePattern[]): TailwindV4SourcePattern[] {
|
|
278
|
+
return sources.flatMap((source) => {
|
|
279
|
+
const base = path.resolve(source.base)
|
|
280
|
+
return expandBracePattern(source.pattern).map(pattern => ({
|
|
281
|
+
base,
|
|
282
|
+
pattern,
|
|
283
|
+
negated: source.negated,
|
|
284
|
+
}))
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function normalizeTailwindV4ScannerSources(
|
|
289
|
+
sources: TailwindV4SourcePattern[] | undefined,
|
|
290
|
+
cwd: string,
|
|
291
|
+
ignoredSources: TailwindV4SourcePattern[] = [],
|
|
292
|
+
): SourceEntry[] {
|
|
293
|
+
const baseSources = sources?.length
|
|
294
|
+
? sources
|
|
295
|
+
: [
|
|
296
|
+
{
|
|
297
|
+
base: cwd,
|
|
298
|
+
pattern: TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
|
|
299
|
+
negated: false,
|
|
300
|
+
},
|
|
301
|
+
]
|
|
302
|
+
|
|
303
|
+
return expandTailwindV4SourceEntryBraces([...baseSources, ...ignoredSources])
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function normalizeEntryPattern(entry: TailwindV4SourcePattern) {
|
|
307
|
+
return path.isAbsolute(entry.pattern)
|
|
308
|
+
? toPosixPath(path.relative(resolveSourceScanPath(entry.base), entry.pattern))
|
|
309
|
+
: normalizeGlobPattern(entry.pattern)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function isFileMatchedByTailwindV4SourceEntry(file: string, entry: TailwindV4SourcePattern) {
|
|
313
|
+
const relative = toPosixPath(path.relative(resolveSourceScanPath(entry.base), file))
|
|
314
|
+
return Boolean(relative)
|
|
315
|
+
&& !relative.startsWith('../')
|
|
316
|
+
&& !path.isAbsolute(relative)
|
|
317
|
+
&& micromatch.isMatch(relative, normalizeEntryPattern(entry))
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function isFileExcludedByTailwindV4SourceEntries(
|
|
321
|
+
file: string,
|
|
322
|
+
entries: TailwindV4SourcePattern[] | undefined,
|
|
323
|
+
) {
|
|
324
|
+
if (!entries?.length) {
|
|
325
|
+
return false
|
|
326
|
+
}
|
|
327
|
+
const resolvedFile = resolveSourceScanPath(file)
|
|
328
|
+
return entries.some(entry => entry.negated && isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry))
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function isFileMatchedByTailwindV4SourceEntries(
|
|
332
|
+
file: string,
|
|
333
|
+
entries: TailwindV4SourcePattern[] | undefined,
|
|
334
|
+
) {
|
|
335
|
+
if (!entries?.length) {
|
|
336
|
+
return true
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const positiveEntries = entries.filter(entry => !entry.negated)
|
|
340
|
+
const negativeEntries = entries.filter(entry => entry.negated)
|
|
341
|
+
if (positiveEntries.length === 0) {
|
|
342
|
+
return false
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const resolvedFile = resolveSourceScanPath(file)
|
|
346
|
+
const matchesPositive = positiveEntries.some(entry => isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry))
|
|
347
|
+
if (!matchesPositive) {
|
|
348
|
+
return false
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return !negativeEntries.some(entry => isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry))
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function createTailwindV4SourceEntryMatcher(entries: TailwindV4SourcePattern[] | undefined) {
|
|
355
|
+
if (!entries?.length) {
|
|
356
|
+
return undefined
|
|
357
|
+
}
|
|
358
|
+
return (file: string) => isFileMatchedByTailwindV4SourceEntries(file, entries)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function createTailwindV4SourceExclusionMatcher(entries: TailwindV4SourcePattern[] | undefined) {
|
|
362
|
+
if (!entries?.length) {
|
|
363
|
+
return undefined
|
|
364
|
+
}
|
|
365
|
+
return (file: string) => isFileExcludedByTailwindV4SourceEntries(file, entries)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export function groupTailwindV4SourceEntriesByBase(entries: TailwindV4SourcePattern[]) {
|
|
369
|
+
const entriesByBase = new Map<string, TailwindV4SourcePattern[]>()
|
|
370
|
+
for (const entry of entries) {
|
|
371
|
+
const base = path.resolve(entry.base)
|
|
372
|
+
const group = entriesByBase.get(base) ?? []
|
|
373
|
+
group.push({
|
|
374
|
+
...entry,
|
|
375
|
+
base,
|
|
376
|
+
pattern: normalizeGlobPattern(entry.pattern),
|
|
377
|
+
})
|
|
378
|
+
entriesByBase.set(base, group)
|
|
379
|
+
}
|
|
380
|
+
return entriesByBase
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export async function expandTailwindV4SourceEntries(
|
|
384
|
+
entries: TailwindV4SourcePattern[],
|
|
385
|
+
resolveFiles: (options: { cwd: string, sources: TailwindV4SourcePattern[] }) => Promise<string[]>,
|
|
386
|
+
) {
|
|
387
|
+
if (entries.length === 0) {
|
|
388
|
+
return []
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const files = new Set<string>()
|
|
392
|
+
await Promise.all([...groupTailwindV4SourceEntriesByBase(entries).entries()].map(async ([base, group]) => {
|
|
393
|
+
const matched = await resolveFiles({
|
|
394
|
+
cwd: base,
|
|
395
|
+
sources: group,
|
|
396
|
+
})
|
|
397
|
+
for (const file of matched) {
|
|
398
|
+
files.add(path.resolve(file))
|
|
399
|
+
}
|
|
400
|
+
}))
|
|
401
|
+
|
|
402
|
+
return [...files].filter(file => !isFileExcludedByTailwindV4SourceEntries(file, entries))
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function mergeTailwindV4SourceEntries(...entries: Array<TailwindV4SourcePattern[] | undefined>) {
|
|
406
|
+
const result: TailwindV4SourcePattern[] = []
|
|
407
|
+
const seen = new Set<string>()
|
|
408
|
+
for (const group of entries) {
|
|
409
|
+
for (const entry of group ?? []) {
|
|
410
|
+
const normalized = {
|
|
411
|
+
base: path.resolve(entry.base),
|
|
412
|
+
pattern: normalizeGlobPattern(entry.pattern),
|
|
413
|
+
negated: entry.negated,
|
|
414
|
+
}
|
|
415
|
+
const key = JSON.stringify(normalized)
|
|
416
|
+
if (seen.has(key)) {
|
|
417
|
+
continue
|
|
418
|
+
}
|
|
419
|
+
seen.add(key)
|
|
420
|
+
result.push(normalized)
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return result
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export function resolveTailwindV4SourceBaseCandidates(
|
|
427
|
+
projectRoot: string,
|
|
428
|
+
base: string,
|
|
429
|
+
baseFallbacks: string[],
|
|
430
|
+
) {
|
|
431
|
+
return uniqueResolvedPaths([base, projectRoot, ...baseFallbacks])
|
|
432
|
+
}
|