tailwindcss-patch 9.3.6 → 9.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwindcss-patch",
3
- "version": "9.3.6",
3
+ "version": "9.4.0",
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: normalizeScannerSources(options?.sources, cwd, options?.ignoredSources),
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: normalizeScannerSources([...rootSources, ...compiled.sources], cwd, options.ignoredSources),
533
+ sources: normalizeTailwindV4ScannerSources(
534
+ createTailwindV4CompiledSourceEntries(compiled.root, compiled.sources, base),
535
+ cwd,
536
+ options.ignoredSources,
537
+ ),
627
538
  }
628
539
  }
629
540
 
@@ -0,0 +1,98 @@
1
+ // 参考链接:https://github.com/tailwindlabs/tailwindcss/blob/master/src/lib/regex.js
2
+ // eslint-disable-next-line regexp/no-obscure-range
3
+ export const validateCandidateTokenRE = /[\w\u00A0-\uFFFF%-?]/
4
+
5
+ export function isValidCandidateToken(token = ''): token is string {
6
+ return validateCandidateTokenRE.test(token)
7
+ }
8
+
9
+ const SPLIT_CACHE_LIMIT = 8192
10
+ const ESCAPED_WHITESPACE_RE = /\\[nrt]/g
11
+ const splitCache = new Map<string, string[]>()
12
+
13
+ function isSplitter(char: string, bracketDepth: number) {
14
+ return bracketDepth === 0 && (char === '"' || /\s/.test(char))
15
+ }
16
+
17
+ function hasClosingQuotedArbitraryValue(code: string, start: number, quote: string) {
18
+ for (let index = start; index < code.length; index++) {
19
+ if (code[index] === '\\') {
20
+ index++
21
+ continue
22
+ }
23
+ if (code[index] === quote) {
24
+ return code.includes(']', index + 1)
25
+ }
26
+ }
27
+
28
+ return false
29
+ }
30
+
31
+ function splitBracketAware(code: string) {
32
+ const result: string[] = []
33
+ let bracketDepth = 0
34
+ let bracketQuote: string | undefined
35
+ let start = 0
36
+
37
+ for (let index = 0; index < code.length; index++) {
38
+ const char = code[index]
39
+ if (bracketDepth > 0 && char === '\\') {
40
+ index++
41
+ continue
42
+ }
43
+
44
+ if (bracketDepth > 0 && (char === '"' || char === '\'')) {
45
+ if (bracketQuote === char) {
46
+ bracketQuote = undefined
47
+ }
48
+ else if (bracketQuote === undefined && hasClosingQuotedArbitraryValue(code, index + 1, char)) {
49
+ bracketQuote = char
50
+ }
51
+ }
52
+
53
+ if (bracketQuote === undefined) {
54
+ if (char === '[' && code.includes(']', index + 1)) {
55
+ bracketDepth++
56
+ }
57
+ else if (char === ']' && bracketDepth > 0) {
58
+ bracketDepth--
59
+ }
60
+ }
61
+
62
+ if (!isSplitter(char, bracketDepth)) {
63
+ continue
64
+ }
65
+
66
+ const candidate = code.slice(start, index)
67
+ if (isValidCandidateToken(candidate)) {
68
+ result.push(candidate)
69
+ }
70
+ start = index + 1
71
+ }
72
+
73
+ const candidate = code.slice(start)
74
+ if (isValidCandidateToken(candidate)) {
75
+ result.push(candidate)
76
+ }
77
+
78
+ return result
79
+ }
80
+
81
+ export function splitCandidateTokens(code: string) {
82
+ const cached = splitCache.get(code)
83
+ if (cached) {
84
+ return cached
85
+ }
86
+
87
+ // 把压缩产物中的转义空白字符(\n \r \t)先还原成空格,避免被粘连到类名上。
88
+ const normalized = code.includes('\\') ? code.replace(ESCAPED_WHITESPACE_RE, ' ') : code
89
+ const result = splitBracketAware(normalized)
90
+
91
+ // 防止缓存无限增长。
92
+ if (splitCache.size >= SPLIT_CACHE_LIMIT) {
93
+ splitCache.clear()
94
+ }
95
+ splitCache.set(code, result)
96
+
97
+ return result
98
+ }
package/src/index.ts CHANGED
@@ -46,6 +46,11 @@ export {
46
46
  resolveProjectSourceFiles,
47
47
  type ExtractSourceCandidate,
48
48
  } from './extraction/candidate-extractor'
49
+ export {
50
+ isValidCandidateToken,
51
+ splitCandidateTokens,
52
+ validateCandidateTokenRE,
53
+ } from './extraction/split-candidate-tokens'
49
54
  export {
50
55
  collectClassesFromContexts,
51
56
  collectClassesFromTailwindV4,
@@ -56,14 +61,34 @@ export {
56
61
  export { default as logger } from './logger'
57
62
  export * from './types'
58
63
  export {
64
+ createTailwindV4CompiledSourceEntries,
59
65
  createTailwindV4Engine,
66
+ createTailwindV4DefaultIgnoreSources,
67
+ createTailwindV4RootSources,
68
+ createTailwindV4SourceEntryMatcher,
69
+ createTailwindV4SourceExclusionMatcher,
70
+ expandTailwindV4SourceEntries,
71
+ expandTailwindV4SourceEntryBraces,
72
+ isFileExcludedByTailwindV4SourceEntries,
73
+ isFileMatchedByTailwindV4SourceEntries,
60
74
  loadTailwindV4DesignSystem,
75
+ mergeTailwindV4SourceEntries,
76
+ normalizeTailwindV4ScannerSources,
77
+ normalizeTailwindV4SourceEntries,
78
+ resolveSourceScanPath,
61
79
  resolveTailwindV4Source,
80
+ resolveTailwindV4SourceBaseCandidates,
81
+ resolveTailwindV4SourceEntry,
62
82
  resolveTailwindV4SourceFromPatchOptions,
63
83
  resolveValidTailwindV4Candidates,
84
+ TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
85
+ TAILWIND_V4_IGNORED_CONTENT_DIRS,
86
+ TAILWIND_V4_IGNORED_EXTENSIONS,
87
+ TAILWIND_V4_IGNORED_FILES,
64
88
  } from './v4'
65
89
  export type {
66
90
  TailwindV4CandidateSource,
91
+ TailwindV4CompiledSourceRoot,
67
92
  TailwindV4CssSource,
68
93
  TailwindV4DesignSystem,
69
94
  TailwindV4Engine,
@@ -71,6 +96,7 @@ export type {
71
96
  TailwindV4GenerateResult,
72
97
  TailwindV4ResolvedSource,
73
98
  TailwindV4SourceOptions,
99
+ TailwindV4SourcePattern,
74
100
  } from './v4'
75
101
 
76
102
  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
- const rootSources = (() => {
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,
@@ -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: null | 'none' | {
13
- base: string
14
- pattern: string
15
- }
13
+ root: TailwindV4CompiledSourceRoot
16
14
  build: (candidates: string[]) => string
17
15
  }
18
16