tailwindcss-patch 9.3.5 → 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.
@@ -14,6 +14,7 @@ let node_url = require("node:url");
14
14
  let node_fs = require("node:fs");
15
15
  let postcss = require("postcss");
16
16
  postcss = require_chunk.__toESM(postcss);
17
+ require("micromatch");
17
18
  let _babel_types = require("@babel/types");
18
19
  _babel_types = require_chunk.__toESM(_babel_types);
19
20
  let _babel_generator = require("@babel/generator");
@@ -23,7 +24,7 @@ _babel_traverse = require_chunk.__toESM(_babel_traverse);
23
24
  let _babel_parser = require("@babel/parser");
24
25
  let tailwindcss_config = require("tailwindcss-config");
25
26
  //#region package.json
26
- var version = "9.3.5";
27
+ var version = "9.3.7";
27
28
  //#endregion
28
29
  //#region src/constants.ts
29
30
  const pkgName = "tailwindcss-patch";
@@ -1800,7 +1801,7 @@ function canonicalizeBareArbitraryValueCandidates(candidates, options) {
1800
1801
  return resolveBareArbitraryValueCandidate(candidate, options)?.canonicalCandidate ?? candidate;
1801
1802
  });
1802
1803
  }
1803
- function splitTopLevel(value, separator) {
1804
+ function splitTopLevel(value, separator, options) {
1804
1805
  const result = [];
1805
1806
  let start = 0;
1806
1807
  let depth = 0;
@@ -1829,12 +1830,12 @@ function splitTopLevel(value, separator) {
1829
1830
  }
1830
1831
  if (depth === 0 && character === separator) {
1831
1832
  const item = value.slice(start, index).trim();
1832
- if (item) result.push(item);
1833
+ if (item || options?.keepEmpty) result.push(item);
1833
1834
  start = index + 1;
1834
1835
  }
1835
1836
  }
1836
1837
  const item = value.slice(start).trim();
1837
- if (item) result.push(item);
1838
+ if (item || options?.keepEmpty) result.push(item);
1838
1839
  return result;
1839
1840
  }
1840
1841
  const sequencePattern = /^(-?\d+)\.\.(-?\d+)(?:\.\.(-?\d+))?$/;
@@ -1875,7 +1876,7 @@ function expandInlinePattern(pattern) {
1875
1876
  if (closeIndex === -1) throw new Error(`The Tailwind CSS v4 inline source pattern "${pattern}" is not balanced.`);
1876
1877
  const body = rest.slice(1, closeIndex);
1877
1878
  const suffix = rest.slice(closeIndex + 1);
1878
- const parts = sequencePattern.test(body) ? expandSequence(body) : splitTopLevel(body, ",").flatMap((part) => expandInlinePattern(part));
1879
+ const parts = sequencePattern.test(body) ? expandSequence(body) : splitTopLevel(body, ",", { keepEmpty: true }).flatMap((part) => expandInlinePattern(part));
1879
1880
  const suffixes = expandInlinePattern(suffix);
1880
1881
  const result = [];
1881
1882
  for (const part of parts) for (const expandedSuffix of suffixes) result.push(`${prefix}${part}${expandedSuffix}`);
@@ -2042,6 +2043,94 @@ async function compileTailwindV4Source(source) {
2042
2043
  throw new Error("Failed to compile Tailwind CSS v4 source.");
2043
2044
  }
2044
2045
  //#endregion
2046
+ //#region src/v4/source-scan.ts
2047
+ const TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN = "**/*";
2048
+ function createTailwindV4RootSources(root, fallbackBase) {
2049
+ if (root === "none") return [];
2050
+ if (root === null) return [{
2051
+ base: fallbackBase,
2052
+ pattern: TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
2053
+ negated: false
2054
+ }];
2055
+ return [{
2056
+ ...root,
2057
+ negated: false
2058
+ }];
2059
+ }
2060
+ function createTailwindV4CompiledSourceEntries(root, sources, fallbackBase) {
2061
+ return [...createTailwindV4RootSources(root, fallbackBase), ...sources];
2062
+ }
2063
+ function expandBracePattern(pattern) {
2064
+ const index = pattern.indexOf("{");
2065
+ if (index === -1) return [pattern];
2066
+ const rest = pattern.slice(index);
2067
+ let depth = 0;
2068
+ let endIndex = -1;
2069
+ for (let i = 0; i < rest.length; i++) {
2070
+ const char = rest[i];
2071
+ if (char === "\\") {
2072
+ i += 1;
2073
+ continue;
2074
+ }
2075
+ if (char === "{") {
2076
+ depth += 1;
2077
+ continue;
2078
+ }
2079
+ if (char === "}") {
2080
+ depth -= 1;
2081
+ if (depth === 0) {
2082
+ endIndex = i;
2083
+ break;
2084
+ }
2085
+ }
2086
+ }
2087
+ if (endIndex === -1) return [pattern];
2088
+ const prefix = pattern.slice(0, index);
2089
+ const inner = rest.slice(1, endIndex);
2090
+ const suffix = rest.slice(endIndex + 1);
2091
+ const parts = [];
2092
+ const stack = [];
2093
+ let lastPos = 0;
2094
+ for (let i = 0; i < inner.length; i++) {
2095
+ const char = inner[i];
2096
+ if (char === "\\") {
2097
+ i += 1;
2098
+ continue;
2099
+ }
2100
+ if (char === "{") {
2101
+ stack.push("}");
2102
+ continue;
2103
+ }
2104
+ if (char === "}" && stack[stack.length - 1] === "}") {
2105
+ stack.pop();
2106
+ continue;
2107
+ }
2108
+ if (char === "," && stack.length === 0) {
2109
+ parts.push(inner.slice(lastPos, i));
2110
+ lastPos = i + 1;
2111
+ }
2112
+ }
2113
+ parts.push(inner.slice(lastPos));
2114
+ return parts.flatMap((part) => expandBracePattern(`${prefix}${part}${suffix}`));
2115
+ }
2116
+ function expandTailwindV4SourceEntryBraces(sources) {
2117
+ return sources.flatMap((source) => {
2118
+ const base = pathe.default.resolve(source.base);
2119
+ return expandBracePattern(source.pattern).map((pattern) => ({
2120
+ base,
2121
+ pattern,
2122
+ negated: source.negated
2123
+ }));
2124
+ });
2125
+ }
2126
+ function normalizeTailwindV4ScannerSources(sources, cwd, ignoredSources = []) {
2127
+ return expandTailwindV4SourceEntryBraces([...sources?.length ? sources : [{
2128
+ base: cwd,
2129
+ pattern: TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
2130
+ negated: false
2131
+ }], ...ignoredSources]);
2132
+ }
2133
+ //#endregion
2045
2134
  //#region src/extraction/candidate-extractor.ts
2046
2135
  let oxideImportPromise;
2047
2136
  const designSystemCandidateCache = /* @__PURE__ */ new Map();
@@ -2286,6 +2375,15 @@ async function extractValidCandidates(options) {
2286
2375
  const candidateCache = designSystemCandidateCache.get(candidateCacheKey) ?? /* @__PURE__ */ new Map();
2287
2376
  designSystemCandidateCache.set(candidateCacheKey, candidateCache);
2288
2377
  const candidates = await extractRawCandidates(sources);
2378
+ const inlineSources = extractTailwindV4InlineSourceCandidates(css);
2379
+ for (const candidate of inlineSources.included) candidates.push(candidate);
2380
+ for (const candidate of inlineSources.excluded) {
2381
+ let index = candidates.indexOf(candidate);
2382
+ while (index !== -1) {
2383
+ candidates.splice(index, 1);
2384
+ index = candidates.indexOf(candidate);
2385
+ }
2386
+ }
2289
2387
  const validCandidates = [];
2290
2388
  const uncachedCandidates = [];
2291
2389
  for (const rawCandidate of candidates) {
@@ -2312,17 +2410,6 @@ async function extractValidCandidates(options) {
2312
2410
  }
2313
2411
  return validCandidates;
2314
2412
  }
2315
- function normalizeSources(sources, cwd) {
2316
- return (sources?.length ? sources : [{
2317
- base: cwd,
2318
- pattern: "**/*",
2319
- negated: false
2320
- }]).map((source) => ({
2321
- base: source.base ?? cwd,
2322
- pattern: source.pattern,
2323
- negated: source.negated
2324
- }));
2325
- }
2326
2413
  function buildLineOffsets(content) {
2327
2414
  const offsets = [0];
2328
2415
  for (let i = 0; i < content.length; i++) if (content[i] === "\n") offsets.push(i + 1);
@@ -2368,11 +2455,35 @@ function toRelativeFile(cwd, filename) {
2368
2455
  const relative = pathe.default.relative(cwd, filename);
2369
2456
  return relative === "" ? pathe.default.basename(filename) : relative;
2370
2457
  }
2371
- async function extractProjectCandidatesWithPositions(options) {
2458
+ async function resolveScannerSources(options) {
2372
2459
  const cwd = options?.cwd ? pathe.default.resolve(options.cwd) : node_process.default.cwd();
2373
- const normalizedSources = normalizeSources(options?.sources, cwd);
2460
+ if (options?.sources?.length || options?.css === void 0) return {
2461
+ cwd,
2462
+ sources: normalizeTailwindV4ScannerSources(options?.sources, cwd, options?.ignoredSources)
2463
+ };
2464
+ const base = options.base ? pathe.default.resolve(options.base) : cwd;
2465
+ const { compiled } = await compileTailwindV4Source({
2466
+ projectRoot: cwd,
2467
+ base,
2468
+ baseFallbacks: options.baseFallbacks?.map((baseFallback) => pathe.default.resolve(baseFallback)) ?? [],
2469
+ css: options.css,
2470
+ dependencies: []
2471
+ });
2472
+ return {
2473
+ cwd,
2474
+ sources: normalizeTailwindV4ScannerSources(createTailwindV4CompiledSourceEntries(compiled.root, compiled.sources, base), cwd, options.ignoredSources)
2475
+ };
2476
+ }
2477
+ async function resolveProjectSourceFiles(options) {
2478
+ const { sources } = await resolveScannerSources(options);
2479
+ const { Scanner } = await getOxideModule();
2480
+ const files = new Scanner({ sources }).files ?? [];
2481
+ return options?.filter ? files.filter(options.filter) : files;
2482
+ }
2483
+ async function extractProjectCandidatesWithPositions(options) {
2484
+ const { cwd, sources } = await resolveScannerSources(options);
2374
2485
  const { Scanner } = await getOxideModule();
2375
- const scanner = new Scanner({ sources: normalizedSources });
2486
+ const scanner = new Scanner({ sources });
2376
2487
  const files = scanner.files ?? [];
2377
2488
  const entries = [];
2378
2489
  const skipped = [];
@@ -2416,7 +2527,7 @@ async function extractProjectCandidatesWithPositions(options) {
2416
2527
  entries,
2417
2528
  filesScanned: files.length,
2418
2529
  skippedFiles: skipped,
2419
- sources: normalizedSources
2530
+ sources
2420
2531
  };
2421
2532
  }
2422
2533
  function groupTokensByFile(report, options) {
@@ -4181,6 +4292,12 @@ Object.defineProperty(exports, "compileTailwindV4Source", {
4181
4292
  return compileTailwindV4Source;
4182
4293
  }
4183
4294
  });
4295
+ Object.defineProperty(exports, "createTailwindV4CompiledSourceEntries", {
4296
+ enumerable: true,
4297
+ get: function() {
4298
+ return createTailwindV4CompiledSourceEntries;
4299
+ }
4300
+ });
4184
4301
  Object.defineProperty(exports, "extractProjectCandidatesWithPositions", {
4185
4302
  enumerable: true,
4186
4303
  get: function() {
@@ -4283,6 +4400,12 @@ Object.defineProperty(exports, "replaceBareArbitraryValueSelectors", {
4283
4400
  return replaceBareArbitraryValueSelectors;
4284
4401
  }
4285
4402
  });
4403
+ Object.defineProperty(exports, "resolveProjectSourceFiles", {
4404
+ enumerable: true,
4405
+ get: function() {
4406
+ return resolveProjectSourceFiles;
4407
+ }
4408
+ });
4286
4409
  Object.defineProperty(exports, "resolveValidTailwindV4Candidates", {
4287
4410
  enumerable: true,
4288
4411
  get: function() {
@@ -1,9 +1,10 @@
1
- import { PackageInfo, PackageResolvingOptions } from "local-pkg";
2
- import * as _$consola from "consola";
3
- import { Node, Rule } from "postcss";
4
1
  import { CAC, Command } from "cac";
2
+ import { PackageInfo, PackageResolvingOptions } from "local-pkg";
5
3
  import { SourceEntry } from "@tailwindcss/oxide";
4
+ import { Node, Rule } from "postcss";
6
5
  import { Config } from "tailwindcss";
6
+ import * as _$consola from "consola";
7
+
7
8
  //#region src/cache/types.d.ts
8
9
  declare const CACHE_SCHEMA_VERSION = 2;
9
10
  declare const CACHE_FINGERPRINT_VERSION = 1;
@@ -203,6 +204,10 @@ interface TailwindV4GenerateOptions {
203
204
  */
204
205
  scanSources?: boolean | TailwindV4SourcePattern[];
205
206
  }
207
+ type TailwindV4CompiledSourceRoot = null | 'none' | {
208
+ base: string;
209
+ pattern: string;
210
+ };
206
211
  interface TailwindV4SourcePattern {
207
212
  base: string;
208
213
  pattern: string;
@@ -214,10 +219,7 @@ interface TailwindV4GenerateResult {
214
219
  rawCandidates: Set<string>;
215
220
  dependencies: string[];
216
221
  sources: TailwindV4SourcePattern[];
217
- root: null | 'none' | {
218
- base: string;
219
- pattern: string;
220
- };
222
+ root: TailwindV4CompiledSourceRoot;
221
223
  }
222
224
  interface TailwindV4DesignSystem {
223
225
  parseCandidate: (candidate: string) => unknown[];
@@ -525,7 +527,20 @@ declare function extractValidCandidates(options?: ExtractValidCandidatesOption):
525
527
  interface ExtractProjectCandidatesOptions {
526
528
  cwd?: string;
527
529
  sources?: SourceEntry[];
530
+ base?: string;
531
+ baseFallbacks?: string[];
532
+ css?: string;
533
+ }
534
+ interface ResolveProjectSourceFilesOptions {
535
+ cwd?: string;
536
+ sources?: SourceEntry[];
537
+ ignoredSources?: SourceEntry[];
538
+ base?: string;
539
+ baseFallbacks?: string[];
540
+ css?: string;
541
+ filter?: (file: string) => boolean;
528
542
  }
543
+ declare function resolveProjectSourceFiles(options?: ResolveProjectSourceFilesOptions): Promise<string[]>;
529
544
  declare function extractProjectCandidatesWithPositions(options?: ExtractProjectCandidatesOptions): Promise<TailwindTokenReport>;
530
545
  declare function groupTokensByFile(report: TailwindTokenReport, options?: {
531
546
  key?: TailwindTokenFileKey;
@@ -776,4 +791,4 @@ declare class ValidateCommandError extends Error {
776
791
  constructor(summary: ValidateFailureSummary, options?: ErrorOptions);
777
792
  }
778
793
  //#endregion
779
- export { TailwindV4GenerateResult as $, extractSourceCandidatesWithPositions as A, ExtractOptions as B, MIGRATION_REPORT_SCHEMA_VERSION as C, extractRawCandidates as D, extractProjectCandidatesWithPositions as E, ApplyOptions as F, TailwindV2Options as G, NormalizedTailwindCssPatchOptions as H, CacheOptions as I, TailwindV4CandidateSource as J, TailwindV3Options as K, CacheStrategy as L, groupTokensByFile as M, BareArbitraryValueOptions as N, extractRawCandidatesWithPositions as O, normalizeOptions as P, TailwindV4GenerateOptions as Q, ExposeContextOptions as R, MIGRATION_REPORT_KIND as S, CacheReadResult as St, TailwindcssPatcher as T, TailwindCssOptions as U, NormalizedCacheOptions as V, TailwindCssPatchOptions as W, TailwindV4DesignSystem as X, TailwindV4CssSource as Y, TailwindV4Engine as Z, ConfigFileMigrationEntry as _, CacheClearScope as _t, ValidateFailureSummary as a, PatchName as at, RestoreConfigFilesOptions as b, CacheIndexFileV2 as bt, TailwindcssPatchCliMountOptions as c, TailwindPatchRuntime as ct, TailwindcssPatchCommandContext as d, TailwindTokenLocation as dt, TailwindV4ResolvedSource as et, TailwindcssPatchCommandHandler as f, TailwindTokenReport as ft, tailwindcssPatchCommands as g, CacheClearResult as gt, TailwindcssPatchCommandOptions as h, CacheClearOptions as ht, ValidateFailureReason as i, PatchCheckStatus as it, extractValidCandidates as j, extractSourceCandidates as k, TailwindcssPatchCliOptions as l, TailwindTokenByFileMap as lt, TailwindcssPatchCommandOptionDefinition as m, TailwindcssRuntimeContext as mt, VALIDATE_FAILURE_REASONS as n, ExtractResult as nt, ValidateJsonFailurePayload as o, PatchStatusEntry as ot, TailwindcssPatchCommandHandlerMap as p, TailwindcssClassCache as pt, TailwindV4Options as q, ValidateCommandError as r, ILengthUnitsPatchOptions as rt, ValidateJsonSuccessPayload as s, PatchStatusReport as st, VALIDATE_EXIT_CODES as t, TailwindV4SourceOptions as tt, TailwindcssPatchCommand as u, TailwindTokenFileKey as ut, ConfigFileMigrationReport as v, CacheContextDescriptor as vt, logger as w, RestoreConfigFilesResult as x, CacheReadMeta as xt, MigrateConfigFilesOptions as y, CacheContextMetadata as yt, ExtendLengthUnitsOptions as z };
794
+ export { TailwindV4GenerateOptions as $, extractSourceCandidatesWithPositions as A, ExtendLengthUnitsOptions as B, MIGRATION_REPORT_SCHEMA_VERSION as C, CacheReadResult as Ct, extractRawCandidates as D, extractProjectCandidatesWithPositions as E, normalizeOptions as F, TailwindCssPatchOptions as G, NormalizedCacheOptions as H, ApplyOptions as I, TailwindV4Options as J, TailwindV2Options as K, CacheOptions as L, groupTokensByFile as M, resolveProjectSourceFiles as N, extractRawCandidatesWithPositions as O, BareArbitraryValueOptions as P, TailwindV4Engine as Q, CacheStrategy as R, MIGRATION_REPORT_KIND as S, CacheReadMeta as St, TailwindcssPatcher as T, NormalizedTailwindCssPatchOptions as U, ExtractOptions as V, TailwindCssOptions as W, TailwindV4CssSource as X, TailwindV4CandidateSource as Y, TailwindV4DesignSystem as Z, ConfigFileMigrationEntry as _, CacheClearResult as _t, ValidateFailureSummary as a, PatchCheckStatus as at, RestoreConfigFilesOptions as b, CacheContextMetadata as bt, TailwindcssPatchCliMountOptions as c, PatchStatusReport as ct, TailwindcssPatchCommandContext as d, TailwindTokenFileKey as dt, TailwindV4GenerateResult as et, TailwindcssPatchCommandHandler as f, TailwindTokenLocation as ft, tailwindcssPatchCommands as g, CacheClearOptions as gt, TailwindcssPatchCommandOptions as h, TailwindcssRuntimeContext as ht, ValidateFailureReason as i, ILengthUnitsPatchOptions as it, extractValidCandidates as j, extractSourceCandidates as k, TailwindcssPatchCliOptions as l, TailwindPatchRuntime as lt, TailwindcssPatchCommandOptionDefinition as m, TailwindcssClassCache as mt, VALIDATE_FAILURE_REASONS as n, TailwindV4SourceOptions as nt, ValidateJsonFailurePayload as o, PatchName as ot, TailwindcssPatchCommandHandlerMap as p, TailwindTokenReport as pt, TailwindV3Options as q, ValidateCommandError as r, ExtractResult as rt, ValidateJsonSuccessPayload as s, PatchStatusEntry as st, VALIDATE_EXIT_CODES as t, TailwindV4ResolvedSource as tt, TailwindcssPatchCommand as u, TailwindTokenByFileMap as ut, ConfigFileMigrationReport as v, CacheClearScope as vt, logger as w, RestoreConfigFilesResult as x, CacheIndexFileV2 as xt, MigrateConfigFilesOptions as y, CacheContextDescriptor as yt, ExposeContextOptions as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwindcss-patch",
3
- "version": "9.3.5",
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",
@@ -70,10 +70,11 @@
70
70
  "cac": "6.7.14",
71
71
  "consola": "^3.4.2",
72
72
  "fs-extra": "^11.3.5",
73
- "local-pkg": "^1.1.2",
73
+ "local-pkg": "^1.2.1",
74
+ "micromatch": "^4.0.8",
74
75
  "pathe": "^2.0.3",
75
- "postcss": "^8.5.14",
76
- "semver": "^7.8.0",
76
+ "postcss": "^8.5.15",
77
+ "semver": "^7.8.1",
77
78
  "tailwindcss-config": "^1.1.5",
78
79
  "@tailwindcss-mangle/config": "7.0.2"
79
80
  },
@@ -12,8 +12,12 @@ import {
12
12
  type BareArbitraryValueOptions,
13
13
  resolveBareArbitraryValueCandidate,
14
14
  } from '../v4/bare-arbitrary-values'
15
- import { resolveValidTailwindV4Candidates } from '../v4/candidates'
16
- import { getTailwindV4DesignSystemCacheKey, loadTailwindV4DesignSystem } from '../v4/node-adapter'
15
+ import { extractTailwindV4InlineSourceCandidates, resolveValidTailwindV4Candidates } from '../v4/candidates'
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>>()
@@ -367,6 +371,17 @@ export async function extractValidCandidates(options?: ExtractValidCandidatesOpt
367
371
  designSystemCandidateCache.set(candidateCacheKey, candidateCache)
368
372
 
369
373
  const candidates = await extractRawCandidates(sources)
374
+ const inlineSources = extractTailwindV4InlineSourceCandidates(css)
375
+ for (const candidate of inlineSources.included) {
376
+ candidates.push(candidate)
377
+ }
378
+ for (const candidate of inlineSources.excluded) {
379
+ let index = candidates.indexOf(candidate)
380
+ while (index !== -1) {
381
+ candidates.splice(index, 1)
382
+ index = candidates.indexOf(candidate)
383
+ }
384
+ }
370
385
  const validCandidates: string[] = []
371
386
  const uncachedCandidates: string[] = []
372
387
 
@@ -417,24 +432,6 @@ export async function extractValidCandidates(options?: ExtractValidCandidatesOpt
417
432
  return validCandidates
418
433
  }
419
434
 
420
- function normalizeSources(sources: SourceEntry[] | undefined, cwd: string) {
421
- const baseSources = sources?.length
422
- ? sources
423
- : [
424
- {
425
- base: cwd,
426
- pattern: '**/*',
427
- negated: false,
428
- },
429
- ]
430
-
431
- return baseSources.map(source => ({
432
- base: source.base ?? cwd,
433
- pattern: source.pattern,
434
- negated: source.negated,
435
- }))
436
- }
437
-
438
435
  function buildLineOffsets(content: string) {
439
436
  const offsets: number[] = [0]
440
437
  for (let i = 0; i < content.length; i++) {
@@ -499,16 +496,67 @@ function toRelativeFile(cwd: string, filename: string) {
499
496
  export interface ExtractProjectCandidatesOptions {
500
497
  cwd?: string
501
498
  sources?: SourceEntry[]
499
+ base?: string
500
+ baseFallbacks?: string[]
501
+ css?: string
502
+ }
503
+
504
+ export interface ResolveProjectSourceFilesOptions {
505
+ cwd?: string
506
+ sources?: SourceEntry[]
507
+ ignoredSources?: SourceEntry[]
508
+ base?: string
509
+ baseFallbacks?: string[]
510
+ css?: string
511
+ filter?: (file: string) => boolean
512
+ }
513
+
514
+ async function resolveScannerSources(options?: ResolveProjectSourceFilesOptions) {
515
+ const cwd = options?.cwd ? path.resolve(options.cwd) : process.cwd()
516
+ if (options?.sources?.length || options?.css === undefined) {
517
+ return {
518
+ cwd,
519
+ sources: normalizeTailwindV4ScannerSources(options?.sources, cwd, options?.ignoredSources),
520
+ }
521
+ }
522
+
523
+ const base = options.base ? path.resolve(options.base) : cwd
524
+ const { compiled } = await compileTailwindV4Source({
525
+ projectRoot: cwd,
526
+ base,
527
+ baseFallbacks: options.baseFallbacks?.map(baseFallback => path.resolve(baseFallback)) ?? [],
528
+ css: options.css,
529
+ dependencies: [],
530
+ })
531
+ return {
532
+ cwd,
533
+ sources: normalizeTailwindV4ScannerSources(
534
+ createTailwindV4CompiledSourceEntries(compiled.root, compiled.sources, base),
535
+ cwd,
536
+ options.ignoredSources,
537
+ ),
538
+ }
539
+ }
540
+
541
+ export async function resolveProjectSourceFiles(options?: ResolveProjectSourceFilesOptions): Promise<string[]> {
542
+ const { sources } = await resolveScannerSources(options)
543
+ const { Scanner } = await getOxideModule()
544
+ const scanner = new Scanner({
545
+ sources,
546
+ })
547
+ const files = scanner.files ?? []
548
+ return options?.filter
549
+ ? files.filter(options.filter)
550
+ : files
502
551
  }
503
552
 
504
553
  export async function extractProjectCandidatesWithPositions(
505
554
  options?: ExtractProjectCandidatesOptions,
506
555
  ): Promise<TailwindTokenReport> {
507
- const cwd = options?.cwd ? path.resolve(options.cwd) : process.cwd()
508
- const normalizedSources = normalizeSources(options?.sources, cwd)
556
+ const { cwd, sources } = await resolveScannerSources(options)
509
557
  const { Scanner } = await getOxideModule()
510
558
  const scanner = new Scanner({
511
- sources: normalizedSources,
559
+ sources,
512
560
  })
513
561
 
514
562
  const files = scanner.files ?? []
@@ -563,7 +611,7 @@ export async function extractProjectCandidatesWithPositions(
563
611
  entries,
564
612
  filesScanned: files.length,
565
613
  skippedFiles: skipped,
566
- sources: normalizedSources,
614
+ sources,
567
615
  }
568
616
  }
569
617
 
@@ -26,6 +26,7 @@ import {
26
26
  extractSourceCandidatesWithPositions,
27
27
  extractValidCandidates,
28
28
  groupTokensByFile,
29
+ resolveProjectSourceFiles,
29
30
  } from './extraction/candidate-extractor'
30
31
  import {
31
32
  collectClassesFromContexts,
@@ -73,6 +74,7 @@ export {
73
74
  normalizeOptions,
74
75
  resolveTailwindV4Source,
75
76
  resolveTailwindV4SourceFromPatchOptions,
77
+ resolveProjectSourceFiles,
76
78
  resolveValidTailwindV4Candidates,
77
79
  restoreConfigFiles,
78
80
  runTailwindBuild,
package/src/index.ts CHANGED
@@ -43,6 +43,7 @@ export {
43
43
  extractSourceCandidatesWithPositions,
44
44
  extractValidCandidates,
45
45
  groupTokensByFile,
46
+ resolveProjectSourceFiles,
46
47
  type ExtractSourceCandidate,
47
48
  } from './extraction/candidate-extractor'
48
49
  export {
@@ -55,14 +56,34 @@ export {
55
56
  export { default as logger } from './logger'
56
57
  export * from './types'
57
58
  export {
59
+ createTailwindV4CompiledSourceEntries,
58
60
  createTailwindV4Engine,
61
+ createTailwindV4DefaultIgnoreSources,
62
+ createTailwindV4RootSources,
63
+ createTailwindV4SourceEntryMatcher,
64
+ createTailwindV4SourceExclusionMatcher,
65
+ expandTailwindV4SourceEntries,
66
+ expandTailwindV4SourceEntryBraces,
67
+ isFileExcludedByTailwindV4SourceEntries,
68
+ isFileMatchedByTailwindV4SourceEntries,
59
69
  loadTailwindV4DesignSystem,
70
+ mergeTailwindV4SourceEntries,
71
+ normalizeTailwindV4ScannerSources,
72
+ normalizeTailwindV4SourceEntries,
73
+ resolveSourceScanPath,
60
74
  resolveTailwindV4Source,
75
+ resolveTailwindV4SourceBaseCandidates,
76
+ resolveTailwindV4SourceEntry,
61
77
  resolveTailwindV4SourceFromPatchOptions,
62
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,
63
83
  } from './v4'
64
84
  export type {
65
85
  TailwindV4CandidateSource,
86
+ TailwindV4CompiledSourceRoot,
66
87
  TailwindV4CssSource,
67
88
  TailwindV4DesignSystem,
68
89
  TailwindV4Engine,
@@ -70,6 +91,7 @@ export type {
70
91
  TailwindV4GenerateResult,
71
92
  TailwindV4ResolvedSource,
72
93
  TailwindV4SourceOptions,
94
+ TailwindV4SourcePattern,
73
95
  } from './v4'
74
96
 
75
97
  export function defineConfig<T extends TailwindcssMangleConfig>(config: T): T {
@@ -126,7 +126,7 @@ export function canonicalizeBareArbitraryValueCandidates(
126
126
  })
127
127
  }
128
128
 
129
- function splitTopLevel(value: string, separator: string) {
129
+ function splitTopLevel(value: string, separator: string, options?: { keepEmpty?: boolean }) {
130
130
  const result: string[] = []
131
131
  let start = 0
132
132
  let depth = 0
@@ -163,7 +163,7 @@ function splitTopLevel(value: string, separator: string) {
163
163
 
164
164
  if (depth === 0 && character === separator) {
165
165
  const item = value.slice(start, index).trim()
166
- if (item) {
166
+ if (item || options?.keepEmpty) {
167
167
  result.push(item)
168
168
  }
169
169
  start = index + 1
@@ -171,7 +171,7 @@ function splitTopLevel(value: string, separator: string) {
171
171
  }
172
172
 
173
173
  const item = value.slice(start).trim()
174
- if (item) {
174
+ if (item || options?.keepEmpty) {
175
175
  result.push(item)
176
176
  }
177
177
  return result
@@ -244,7 +244,7 @@ function expandInlinePattern(pattern: string): string[] {
244
244
  const suffix = rest.slice(closeIndex + 1)
245
245
  const parts = sequencePattern.test(body)
246
246
  ? expandSequence(body)
247
- : splitTopLevel(body, ',').flatMap(part => expandInlinePattern(part))
247
+ : splitTopLevel(body, ',', { keepEmpty: true }).flatMap(part => expandInlinePattern(part))
248
248
  const suffixes = expandInlinePattern(suffix)
249
249
 
250
250
  const result: string[] = []
package/src/v4/engine.ts CHANGED
@@ -13,16 +13,19 @@ 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,
20
+ source: TailwindV4ResolvedSource,
21
+ compiledRoot: TailwindV4GenerateResult['root'],
19
22
  compiledSources: TailwindV4SourcePattern[],
20
23
  ) {
21
24
  if (Array.isArray(options?.scanSources)) {
22
25
  return options.scanSources
23
26
  }
24
27
  if (options?.scanSources === true) {
25
- return compiledSources
28
+ return createTailwindV4CompiledSourceEntries(compiledRoot, compiledSources, source.base)
26
29
  }
27
30
  return []
28
31
  }
@@ -30,6 +33,7 @@ function resolveScanSources(
30
33
  async function collectRawCandidates(
31
34
  source: TailwindV4ResolvedSource,
32
35
  options: TailwindV4GenerateOptions | undefined,
36
+ compiledRoot: TailwindV4GenerateResult['root'],
33
37
  compiledSources: TailwindV4SourcePattern[] = [],
34
38
  ) {
35
39
  const rawCandidates = new Set<string>()
@@ -45,7 +49,7 @@ async function collectRawCandidates(
45
49
  }
46
50
  }
47
51
 
48
- const filesystemSources = resolveScanSources(options, compiledSources)
52
+ const filesystemSources = resolveScanSources(options, source, compiledRoot, compiledSources)
49
53
  if (filesystemSources.length > 0) {
50
54
  for (const candidate of await extractRawCandidates(filesystemSources)) {
51
55
  rawCandidates.add(candidate)
@@ -75,7 +79,7 @@ export function createTailwindV4Engine(source: TailwindV4ResolvedSource): Tailwi
75
79
  },
76
80
  async generate(options): Promise<TailwindV4GenerateResult> {
77
81
  const { compiled, dependencies } = await compileTailwindV4Source(source)
78
- const rawCandidates = await collectRawCandidates(source, options, compiled.sources)
82
+ const rawCandidates = await collectRawCandidates(source, options, compiled.root, compiled.sources)
79
83
  const designSystem = await loadTailwindV4DesignSystem(source)
80
84
  const classSet = resolveValidTailwindV4Candidates(designSystem, rawCandidates, {
81
85
  ...(options?.bareArbitraryValues === undefined ? {} : { bareArbitraryValues: options.bareArbitraryValues }),
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