tailwindcss-patch 8.0.0 → 8.2.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/README.md CHANGED
@@ -34,6 +34,9 @@ pnpm dlx tw-patch install
34
34
 
35
35
  # Extract all classes into the configured output file
36
36
  pnpm dlx tw-patch extract
37
+
38
+ # Capture every token (candidate) with file/position metadata
39
+ pnpm dlx tw-patch tokens --format lines
37
40
  ```
38
41
 
39
42
  ### Extract options
@@ -48,6 +51,16 @@ pnpm dlx tw-patch extract
48
51
 
49
52
  The CLI loads `tailwindcss-patch.config.ts` via `@tailwindcss-mangle/config`. Legacy configs continue to work; see the [migration guide](./MIGRATION.md) for hints on the new fields.
50
53
 
54
+ ### Token report options
55
+
56
+ | Flag | Description |
57
+ | ------------------------ | --------------------------------------------------------------------------- |
58
+ | `--cwd <dir>` | Use a different working directory when loading configuration. |
59
+ | `--output <file>` | Override the token report target file (defaults to `.tw-patch/tw-token-report.json`). |
60
+ | `--format <json\|lines\|grouped-json>` | Choose between a JSON payload (default), newline summaries, or JSON grouped by file path. |
61
+ | `--group-key <relative\|absolute>` | Control grouped-json keys (defaults to relative paths). |
62
+ | `--no-write` | Skip writing to disk and only print a preview. |
63
+
51
64
  ## Programmatic API
52
65
 
53
66
  ```ts
@@ -81,6 +94,12 @@ const patcher = new TailwindcssPatcher({
81
94
 
82
95
  await patcher.patch()
83
96
  const { classList, filename } = await patcher.extract()
97
+ const tokenReport = await patcher.collectContentTokens()
98
+ console.log(tokenReport.entries[0]) // { rawCandidate, file, line, column, ... }
99
+ const groupedTokens = await patcher.collectContentTokensByFile()
100
+ console.log(groupedTokens['src/button.tsx'][0].rawCandidate)
101
+ // Preserve absolute file paths:
102
+ // await patcher.collectContentTokensByFile({ key: 'absolute', stripAbsolutePaths: false })
84
103
  ```
85
104
 
86
105
  The constructor accepts either the new object shown above or the historical `patch`/`cache` shape. Conversions happen internally so existing configs remain backwards compatible.
@@ -89,6 +108,8 @@ The constructor accepts either the new object shown above or the historical `pat
89
108
 
90
109
  - `normalizeOptions` – normalise raw user input to the runtime shape.
91
110
  - `CacheStore` – read/write class caches respecting merge or overwrite semantics.
111
+ - `extractProjectCandidatesWithPositions` – gather Tailwind tokens for every configured source file with location metadata.
112
+ - `groupTokensByFile` – convert a token report into a `{ [filePath]: TailwindTokenLocation[] }` map.
92
113
  - `extractValidCandidates` – scan Tailwind v4 CSS/content sources with the Tailwind Oxide scanner.
93
114
  - `runTailwindBuild` – run the Tailwind PostCSS plugin for v2/v3 projects to prime runtime contexts.
94
115
 
@@ -16,6 +16,9 @@ var CacheStore = class {
16
16
  async ensureDir() {
17
17
  await _fsextra2.default.ensureDir(this.options.dir);
18
18
  }
19
+ ensureDirSync() {
20
+ _fsextra2.default.ensureDirSync(this.options.dir);
21
+ }
19
22
  async write(data) {
20
23
  if (!this.options.enabled) {
21
24
  return void 0;
@@ -29,6 +32,19 @@ var CacheStore = class {
29
32
  return void 0;
30
33
  }
31
34
  }
35
+ writeSync(data) {
36
+ if (!this.options.enabled) {
37
+ return void 0;
38
+ }
39
+ try {
40
+ this.ensureDirSync();
41
+ _fsextra2.default.writeJSONSync(this.options.path, Array.from(data));
42
+ return this.options.path;
43
+ } catch (error) {
44
+ logger_default.error("Unable to persist Tailwind class cache", error);
45
+ return void 0;
46
+ }
47
+ }
32
48
  async read() {
33
49
  if (!this.options.enabled) {
34
50
  return /* @__PURE__ */ new Set();
@@ -52,10 +68,35 @@ var CacheStore = class {
52
68
  }
53
69
  return /* @__PURE__ */ new Set();
54
70
  }
71
+ readSync() {
72
+ if (!this.options.enabled) {
73
+ return /* @__PURE__ */ new Set();
74
+ }
75
+ try {
76
+ const exists = _fsextra2.default.pathExistsSync(this.options.path);
77
+ if (!exists) {
78
+ return /* @__PURE__ */ new Set();
79
+ }
80
+ const data = _fsextra2.default.readJSONSync(this.options.path);
81
+ if (Array.isArray(data)) {
82
+ return new Set(data.filter((item) => typeof item === "string"));
83
+ }
84
+ } catch (error) {
85
+ logger_default.warn("Unable to read Tailwind class cache, removing invalid file.", error);
86
+ try {
87
+ _fsextra2.default.removeSync(this.options.path);
88
+ } catch (cleanupError) {
89
+ logger_default.error("Failed to clean up invalid cache file", cleanupError);
90
+ }
91
+ }
92
+ return /* @__PURE__ */ new Set();
93
+ }
55
94
  };
56
95
 
57
96
  // src/extraction/candidate-extractor.ts
97
+ var _fs = require('fs');
58
98
  var _process = require('process'); var _process2 = _interopRequireDefault(_process);
99
+ var _pathe = require('pathe'); var _pathe2 = _interopRequireDefault(_pathe);
59
100
  async function importNode() {
60
101
  return Promise.resolve().then(() => _interopRequireWildcard(require("@tailwindcss/node")));
61
102
  }
@@ -103,11 +144,144 @@ async function extractValidCandidates(options) {
103
144
  );
104
145
  return validCandidates;
105
146
  }
147
+ function normalizeSources(sources, cwd) {
148
+ const baseSources = _optionalChain([sources, 'optionalAccess', _ => _.length]) ? sources : [
149
+ {
150
+ base: cwd,
151
+ pattern: "**/*",
152
+ negated: false
153
+ }
154
+ ];
155
+ return baseSources.map((source) => ({
156
+ base: _nullishCoalesce(source.base, () => ( cwd)),
157
+ pattern: source.pattern,
158
+ negated: source.negated
159
+ }));
160
+ }
161
+ function buildLineOffsets(content) {
162
+ const offsets = [0];
163
+ for (let i = 0; i < content.length; i++) {
164
+ if (content[i] === "\n") {
165
+ offsets.push(i + 1);
166
+ }
167
+ }
168
+ if (offsets[offsets.length - 1] !== content.length) {
169
+ offsets.push(content.length);
170
+ }
171
+ return offsets;
172
+ }
173
+ function resolveLineMeta(content, offsets, index) {
174
+ let low = 0;
175
+ let high = offsets.length - 1;
176
+ while (low <= high) {
177
+ const mid = Math.floor((low + high) / 2);
178
+ const start = offsets[mid];
179
+ const nextStart = _nullishCoalesce(offsets[mid + 1], () => ( content.length));
180
+ if (index < start) {
181
+ high = mid - 1;
182
+ continue;
183
+ }
184
+ if (index >= nextStart) {
185
+ low = mid + 1;
186
+ continue;
187
+ }
188
+ const line = mid + 1;
189
+ const column = index - start + 1;
190
+ const lineEnd = content.indexOf("\n", start);
191
+ const lineText = content.slice(start, lineEnd === -1 ? content.length : lineEnd);
192
+ return { line, column, lineText };
193
+ }
194
+ const lastStart = _nullishCoalesce(offsets[offsets.length - 2], () => ( 0));
195
+ return {
196
+ line: offsets.length - 1,
197
+ column: index - lastStart + 1,
198
+ lineText: content.slice(lastStart)
199
+ };
200
+ }
201
+ function toExtension(filename) {
202
+ const ext = _pathe2.default.extname(filename).replace(/^\./, "");
203
+ return ext || "txt";
204
+ }
205
+ function toRelativeFile(cwd, filename) {
206
+ const relative = _pathe2.default.relative(cwd, filename);
207
+ return relative === "" ? _pathe2.default.basename(filename) : relative;
208
+ }
209
+ async function extractProjectCandidatesWithPositions(options) {
210
+ const cwd = _optionalChain([options, 'optionalAccess', _2 => _2.cwd]) ? _pathe2.default.resolve(options.cwd) : _process2.default.cwd();
211
+ const normalizedSources = normalizeSources(_optionalChain([options, 'optionalAccess', _3 => _3.sources]), cwd);
212
+ const { Scanner } = await importOxide();
213
+ const scanner = new Scanner({
214
+ sources: normalizedSources
215
+ });
216
+ const files = _nullishCoalesce(scanner.files, () => ( []));
217
+ const entries = [];
218
+ const skipped = [];
219
+ for (const file of files) {
220
+ let content;
221
+ try {
222
+ content = await _fs.promises.readFile(file, "utf8");
223
+ } catch (error) {
224
+ skipped.push({
225
+ file,
226
+ reason: error instanceof Error ? error.message : "Unknown error"
227
+ });
228
+ continue;
229
+ }
230
+ const extension = toExtension(file);
231
+ const matches = scanner.getCandidatesWithPositions({
232
+ file,
233
+ content,
234
+ extension
235
+ });
236
+ if (!matches.length) {
237
+ continue;
238
+ }
239
+ const offsets = buildLineOffsets(content);
240
+ const relativeFile = toRelativeFile(cwd, file);
241
+ for (const match of matches) {
242
+ const info = resolveLineMeta(content, offsets, match.position);
243
+ entries.push({
244
+ rawCandidate: match.candidate,
245
+ file,
246
+ relativeFile,
247
+ extension,
248
+ start: match.position,
249
+ end: match.position + match.candidate.length,
250
+ length: match.candidate.length,
251
+ line: info.line,
252
+ column: info.column,
253
+ lineText: info.lineText
254
+ });
255
+ }
256
+ }
257
+ return {
258
+ entries,
259
+ filesScanned: files.length,
260
+ skippedFiles: skipped,
261
+ sources: normalizedSources
262
+ };
263
+ }
264
+ function groupTokensByFile(report, options) {
265
+ const key = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _4 => _4.key]), () => ( "relative"));
266
+ const stripAbsolute = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _5 => _5.stripAbsolutePaths]), () => ( key !== "absolute"));
267
+ return report.entries.reduce((acc, entry) => {
268
+ const bucketKey = key === "absolute" ? entry.file : entry.relativeFile;
269
+ if (!acc[bucketKey]) {
270
+ acc[bucketKey] = [];
271
+ }
272
+ const value = stripAbsolute ? {
273
+ ...entry,
274
+ file: entry.relativeFile
275
+ } : entry;
276
+ acc[bucketKey].push(value);
277
+ return acc;
278
+ }, {});
279
+ }
106
280
 
107
281
  // src/options/legacy.ts
108
282
  function normalizeLegacyFeatures(patch) {
109
- const apply = _optionalChain([patch, 'optionalAccess', _ => _.applyPatches]);
110
- const extend = _optionalChain([apply, 'optionalAccess', _2 => _2.extendLengthUnits]);
283
+ const apply = _optionalChain([patch, 'optionalAccess', _6 => _6.applyPatches]);
284
+ const extend = _optionalChain([apply, 'optionalAccess', _7 => _7.extendLengthUnits]);
111
285
  let extendOption = false;
112
286
  if (extend && typeof extend === "object") {
113
287
  extendOption = {
@@ -118,11 +292,11 @@ function normalizeLegacyFeatures(patch) {
118
292
  extendOption = {
119
293
  enabled: true,
120
294
  units: ["rpx"],
121
- overwrite: _optionalChain([patch, 'optionalAccess', _3 => _3.overwrite])
295
+ overwrite: _optionalChain([patch, 'optionalAccess', _8 => _8.overwrite])
122
296
  };
123
297
  }
124
298
  return {
125
- exposeContext: _nullishCoalesce(_optionalChain([apply, 'optionalAccess', _4 => _4.exportContext]), () => ( true)),
299
+ exposeContext: _nullishCoalesce(_optionalChain([apply, 'optionalAccess', _9 => _9.exportContext]), () => ( true)),
126
300
  extendLengthUnits: extendOption
127
301
  };
128
302
  }
@@ -132,18 +306,18 @@ function fromLegacyOptions(options) {
132
306
  }
133
307
  const patch = options.patch;
134
308
  const features = normalizeLegacyFeatures(patch);
135
- const output = _optionalChain([patch, 'optionalAccess', _5 => _5.output]);
136
- const tailwindConfig = _optionalChain([patch, 'optionalAccess', _6 => _6.tailwindcss]);
137
- const tailwindVersion = _optionalChain([tailwindConfig, 'optionalAccess', _7 => _7.version]);
138
- const tailwindV2 = _optionalChain([tailwindConfig, 'optionalAccess', _8 => _8.v2]);
139
- const tailwindV3 = _optionalChain([tailwindConfig, 'optionalAccess', _9 => _9.v3]);
140
- const tailwindV4 = _optionalChain([tailwindConfig, 'optionalAccess', _10 => _10.v4]);
141
- const tailwindConfigPath = _nullishCoalesce(_optionalChain([tailwindV3, 'optionalAccess', _11 => _11.config]), () => ( _optionalChain([tailwindV2, 'optionalAccess', _12 => _12.config])));
142
- const tailwindCwd = _nullishCoalesce(_nullishCoalesce(_optionalChain([tailwindV3, 'optionalAccess', _13 => _13.cwd]), () => ( _optionalChain([tailwindV2, 'optionalAccess', _14 => _14.cwd]))), () => ( _optionalChain([patch, 'optionalAccess', _15 => _15.cwd])));
309
+ const output = _optionalChain([patch, 'optionalAccess', _10 => _10.output]);
310
+ const tailwindConfig = _optionalChain([patch, 'optionalAccess', _11 => _11.tailwindcss]);
311
+ const tailwindVersion = _optionalChain([tailwindConfig, 'optionalAccess', _12 => _12.version]);
312
+ const tailwindV2 = _optionalChain([tailwindConfig, 'optionalAccess', _13 => _13.v2]);
313
+ const tailwindV3 = _optionalChain([tailwindConfig, 'optionalAccess', _14 => _14.v3]);
314
+ const tailwindV4 = _optionalChain([tailwindConfig, 'optionalAccess', _15 => _15.v4]);
315
+ const tailwindConfigPath = _nullishCoalesce(_optionalChain([tailwindV3, 'optionalAccess', _16 => _16.config]), () => ( _optionalChain([tailwindV2, 'optionalAccess', _17 => _17.config])));
316
+ const tailwindCwd = _nullishCoalesce(_nullishCoalesce(_optionalChain([tailwindV3, 'optionalAccess', _18 => _18.cwd]), () => ( _optionalChain([tailwindV2, 'optionalAccess', _19 => _19.cwd]))), () => ( _optionalChain([patch, 'optionalAccess', _20 => _20.cwd])));
143
317
  return {
144
- cwd: _optionalChain([patch, 'optionalAccess', _16 => _16.cwd]),
145
- overwrite: _optionalChain([patch, 'optionalAccess', _17 => _17.overwrite]),
146
- filter: _optionalChain([patch, 'optionalAccess', _18 => _18.filter]),
318
+ cwd: _optionalChain([patch, 'optionalAccess', _21 => _21.cwd]),
319
+ overwrite: _optionalChain([patch, 'optionalAccess', _22 => _22.overwrite]),
320
+ filter: _optionalChain([patch, 'optionalAccess', _23 => _23.filter]),
147
321
  cache: typeof options.cache === "boolean" ? options.cache : options.cache ? {
148
322
  ...options.cache,
149
323
  enabled: _nullishCoalesce(options.cache.enabled, () => ( true))
@@ -154,9 +328,9 @@ function fromLegacyOptions(options) {
154
328
  removeUniversalSelector: output.removeUniversalSelector
155
329
  } : void 0,
156
330
  tailwind: {
157
- packageName: _optionalChain([patch, 'optionalAccess', _19 => _19.packageName]),
331
+ packageName: _optionalChain([patch, 'optionalAccess', _24 => _24.packageName]),
158
332
  version: tailwindVersion,
159
- resolve: _optionalChain([patch, 'optionalAccess', _20 => _20.resolve]),
333
+ resolve: _optionalChain([patch, 'optionalAccess', _25 => _25.resolve]),
160
334
  config: tailwindConfigPath,
161
335
  cwd: tailwindCwd,
162
336
  v2: tailwindV2,
@@ -176,7 +350,7 @@ function fromUnifiedConfig(registry) {
176
350
  const tailwind = registry.tailwind;
177
351
  const output = registry.output;
178
352
  const pretty = (() => {
179
- if (_optionalChain([output, 'optionalAccess', _21 => _21.pretty]) === void 0) {
353
+ if (_optionalChain([output, 'optionalAccess', _26 => _26.pretty]) === void 0) {
180
354
  return void 0;
181
355
  }
182
356
  if (typeof output.pretty === "boolean") {
@@ -205,7 +379,7 @@ function fromUnifiedConfig(registry) {
205
379
 
206
380
  // src/options/normalize.ts
207
381
 
208
- var _pathe = require('pathe'); var _pathe2 = _interopRequireDefault(_pathe);
382
+
209
383
 
210
384
  // src/constants.ts
211
385
  var pkgName = "tailwindcss-patch";
@@ -246,11 +420,11 @@ function normalizeCacheOptions(cache, projectRoot) {
246
420
  };
247
421
  }
248
422
  function normalizeOutputOptions(output) {
249
- const enabled = _nullishCoalesce(_optionalChain([output, 'optionalAccess', _22 => _22.enabled]), () => ( true));
250
- const file = _nullishCoalesce(_optionalChain([output, 'optionalAccess', _23 => _23.file]), () => ( ".tw-patch/tw-class-list.json"));
251
- const format = _nullishCoalesce(_optionalChain([output, 'optionalAccess', _24 => _24.format]), () => ( "json"));
252
- const pretty = toPrettyValue(_nullishCoalesce(_optionalChain([output, 'optionalAccess', _25 => _25.pretty]), () => ( true)));
253
- const removeUniversalSelector = _nullishCoalesce(_optionalChain([output, 'optionalAccess', _26 => _26.removeUniversalSelector]), () => ( true));
423
+ const enabled = _nullishCoalesce(_optionalChain([output, 'optionalAccess', _27 => _27.enabled]), () => ( true));
424
+ const file = _nullishCoalesce(_optionalChain([output, 'optionalAccess', _28 => _28.file]), () => ( ".tw-patch/tw-class-list.json"));
425
+ const format = _nullishCoalesce(_optionalChain([output, 'optionalAccess', _29 => _29.format]), () => ( "json"));
426
+ const pretty = toPrettyValue(_nullishCoalesce(_optionalChain([output, 'optionalAccess', _30 => _30.pretty]), () => ( true)));
427
+ const removeUniversalSelector = _nullishCoalesce(_optionalChain([output, 'optionalAccess', _31 => _31.removeUniversalSelector]), () => ( true));
254
428
  return {
255
429
  enabled,
256
430
  file,
@@ -260,13 +434,13 @@ function normalizeOutputOptions(output) {
260
434
  };
261
435
  }
262
436
  function normalizeExposeContextOptions(features) {
263
- if (_optionalChain([features, 'optionalAccess', _27 => _27.exposeContext]) === false) {
437
+ if (_optionalChain([features, 'optionalAccess', _32 => _32.exposeContext]) === false) {
264
438
  return {
265
439
  enabled: false,
266
440
  refProperty: "contextRef"
267
441
  };
268
442
  }
269
- if (typeof _optionalChain([features, 'optionalAccess', _28 => _28.exposeContext]) === "object" && features.exposeContext) {
443
+ if (typeof _optionalChain([features, 'optionalAccess', _33 => _33.exposeContext]) === "object" && features.exposeContext) {
270
444
  return {
271
445
  enabled: true,
272
446
  refProperty: _nullishCoalesce(features.exposeContext.refProperty, () => ( "contextRef"))
@@ -278,7 +452,7 @@ function normalizeExposeContextOptions(features) {
278
452
  };
279
453
  }
280
454
  function normalizeExtendLengthUnitsOptions(features) {
281
- const extend = _optionalChain([features, 'optionalAccess', _29 => _29.extendLengthUnits]);
455
+ const extend = _optionalChain([features, 'optionalAccess', _34 => _34.extendLengthUnits]);
282
456
  if (extend === false || extend === void 0) {
283
457
  return null;
284
458
  }
@@ -298,9 +472,9 @@ function normalizeExtendLengthUnitsOptions(features) {
298
472
  };
299
473
  }
300
474
  function normalizeTailwindV4Options(v4, fallbackBase) {
301
- const base = _optionalChain([v4, 'optionalAccess', _30 => _30.base]) ? _pathe2.default.resolve(v4.base) : fallbackBase;
302
- const cssEntries = Array.isArray(_optionalChain([v4, 'optionalAccess', _31 => _31.cssEntries])) ? v4.cssEntries.filter((entry) => Boolean(entry)).map((entry) => _pathe2.default.resolve(entry)) : [];
303
- const sources = _optionalChain([v4, 'optionalAccess', _32 => _32.sources, 'optionalAccess', _33 => _33.length]) ? v4.sources : [
475
+ const base = _optionalChain([v4, 'optionalAccess', _35 => _35.base]) ? _pathe2.default.resolve(v4.base) : fallbackBase;
476
+ const cssEntries = Array.isArray(_optionalChain([v4, 'optionalAccess', _36 => _36.cssEntries])) ? v4.cssEntries.filter((entry) => Boolean(entry)).map((entry) => _pathe2.default.resolve(entry)) : [];
477
+ const sources = _optionalChain([v4, 'optionalAccess', _37 => _37.sources, 'optionalAccess', _38 => _38.length]) ? v4.sources : [
304
478
  {
305
479
  base,
306
480
  pattern: "**/*",
@@ -309,19 +483,19 @@ function normalizeTailwindV4Options(v4, fallbackBase) {
309
483
  ];
310
484
  return {
311
485
  base,
312
- css: _optionalChain([v4, 'optionalAccess', _34 => _34.css]),
486
+ css: _optionalChain([v4, 'optionalAccess', _39 => _39.css]),
313
487
  cssEntries,
314
488
  sources
315
489
  };
316
490
  }
317
491
  function normalizeTailwindOptions(tailwind, projectRoot) {
318
- const packageName = _nullishCoalesce(_optionalChain([tailwind, 'optionalAccess', _35 => _35.packageName]), () => ( "tailwindcss"));
319
- const versionHint = _optionalChain([tailwind, 'optionalAccess', _36 => _36.version]);
320
- const resolve = _optionalChain([tailwind, 'optionalAccess', _37 => _37.resolve]);
321
- const cwd = _nullishCoalesce(_optionalChain([tailwind, 'optionalAccess', _38 => _38.cwd]), () => ( projectRoot));
322
- const config = _optionalChain([tailwind, 'optionalAccess', _39 => _39.config]);
323
- const postcssPlugin = _optionalChain([tailwind, 'optionalAccess', _40 => _40.postcssPlugin]);
324
- const v4 = normalizeTailwindV4Options(_optionalChain([tailwind, 'optionalAccess', _41 => _41.v4]), cwd);
492
+ const packageName = _nullishCoalesce(_optionalChain([tailwind, 'optionalAccess', _40 => _40.packageName]), () => ( "tailwindcss"));
493
+ const versionHint = _optionalChain([tailwind, 'optionalAccess', _41 => _41.version]);
494
+ const resolve = _optionalChain([tailwind, 'optionalAccess', _42 => _42.resolve]);
495
+ const cwd = _nullishCoalesce(_optionalChain([tailwind, 'optionalAccess', _43 => _43.cwd]), () => ( projectRoot));
496
+ const config = _optionalChain([tailwind, 'optionalAccess', _44 => _44.config]);
497
+ const postcssPlugin = _optionalChain([tailwind, 'optionalAccess', _45 => _45.postcssPlugin]);
498
+ const v4 = normalizeTailwindV4Options(_optionalChain([tailwind, 'optionalAccess', _46 => _46.v4]), cwd);
325
499
  return {
326
500
  packageName,
327
501
  versionHint,
@@ -329,8 +503,8 @@ function normalizeTailwindOptions(tailwind, projectRoot) {
329
503
  cwd,
330
504
  config,
331
505
  postcssPlugin,
332
- v2: _optionalChain([tailwind, 'optionalAccess', _42 => _42.v2]),
333
- v3: _optionalChain([tailwind, 'optionalAccess', _43 => _43.v3]),
506
+ v2: _optionalChain([tailwind, 'optionalAccess', _47 => _47.v2]),
507
+ v3: _optionalChain([tailwind, 'optionalAccess', _48 => _48.v3]),
334
508
  v4
335
509
  };
336
510
  }
@@ -417,7 +591,7 @@ async function collectClassesFromTailwindV4(options) {
417
591
  if (!v4Options) {
418
592
  return set;
419
593
  }
420
- const sources = _optionalChain([v4Options, 'access', _44 => _44.sources, 'optionalAccess', _45 => _45.map, 'call', _46 => _46((source) => {
594
+ const sources = _optionalChain([v4Options, 'access', _49 => _49.sources, 'optionalAccess', _50 => _50.map, 'call', _51 => _51((source) => {
421
595
  return {
422
596
  base: _nullishCoalesce(_nullishCoalesce(source.base, () => ( v4Options.base)), () => ( _process2.default.cwd())),
423
597
  pattern: source.pattern,
@@ -593,9 +767,9 @@ function transformProcessTailwindFeaturesReturnContextV2(content) {
593
767
  });
594
768
  let hasPatched = false;
595
769
  traverse(ast, {
596
- FunctionDeclaration(path8) {
597
- const node = path8.node;
598
- if (_optionalChain([node, 'access', _47 => _47.id, 'optionalAccess', _48 => _48.name]) !== "processTailwindFeatures" || node.body.body.length !== 1 || !t.isReturnStatement(node.body.body[0])) {
770
+ FunctionDeclaration(path9) {
771
+ const node = path9.node;
772
+ if (_optionalChain([node, 'access', _52 => _52.id, 'optionalAccess', _53 => _53.name]) !== "processTailwindFeatures" || node.body.body.length !== 1 || !t.isReturnStatement(node.body.body[0])) {
599
773
  return;
600
774
  }
601
775
  const returnStatement3 = node.body.body[0];
@@ -625,10 +799,10 @@ function transformPostcssPluginV2(content, options) {
625
799
  const ast = _parser.parse.call(void 0, content);
626
800
  let hasPatched = false;
627
801
  traverse(ast, {
628
- Program(path8) {
629
- const program = path8.node;
802
+ Program(path9) {
803
+ const program = path9.node;
630
804
  const index = program.body.findIndex((statement) => {
631
- return t.isFunctionDeclaration(statement) && _optionalChain([statement, 'access', _49 => _49.id, 'optionalAccess', _50 => _50.name]) === "_default";
805
+ return t.isFunctionDeclaration(statement) && _optionalChain([statement, 'access', _54 => _54.id, 'optionalAccess', _55 => _55.name]) === "_default";
632
806
  });
633
807
  if (index === -1) {
634
808
  return;
@@ -660,12 +834,12 @@ function transformPostcssPluginV2(content, options) {
660
834
  );
661
835
  }
662
836
  },
663
- FunctionDeclaration(path8) {
837
+ FunctionDeclaration(path9) {
664
838
  if (hasPatched) {
665
839
  return;
666
840
  }
667
- const fn = path8.node;
668
- if (_optionalChain([fn, 'access', _51 => _51.id, 'optionalAccess', _52 => _52.name]) !== "_default") {
841
+ const fn = path9.node;
842
+ if (_optionalChain([fn, 'access', _56 => _56.id, 'optionalAccess', _57 => _57.name]) !== "_default") {
669
843
  return;
670
844
  }
671
845
  if (fn.body.body.length !== 1 || !t.isReturnStatement(fn.body.body[0])) {
@@ -753,9 +927,9 @@ function transformProcessTailwindFeaturesReturnContext(content) {
753
927
  const ast = _parser.parse.call(void 0, content);
754
928
  let hasPatched = false;
755
929
  traverse(ast, {
756
- FunctionDeclaration(path8) {
757
- const node = path8.node;
758
- if (_optionalChain([node, 'access', _53 => _53.id, 'optionalAccess', _54 => _54.name]) !== "processTailwindFeatures" || node.body.body.length !== 1) {
930
+ FunctionDeclaration(path9) {
931
+ const node = path9.node;
932
+ if (_optionalChain([node, 'access', _58 => _58.id, 'optionalAccess', _59 => _59.name]) !== "processTailwindFeatures" || node.body.body.length !== 1) {
759
933
  return;
760
934
  }
761
935
  const [returnStatement3] = node.body.body;
@@ -786,10 +960,10 @@ function transformPostcssPlugin(content, { refProperty }) {
786
960
  const valueMember = t2.memberExpression(refIdentifier, t2.identifier("value"));
787
961
  let hasPatched = false;
788
962
  traverse(ast, {
789
- Program(path8) {
790
- const program = path8.node;
963
+ Program(path9) {
964
+ const program = path9.node;
791
965
  const index = program.body.findIndex((statement) => {
792
- return t2.isExpressionStatement(statement) && t2.isAssignmentExpression(statement.expression) && t2.isMemberExpression(statement.expression.left) && t2.isFunctionExpression(statement.expression.right) && _optionalChain([statement, 'access', _55 => _55.expression, 'access', _56 => _56.right, 'access', _57 => _57.id, 'optionalAccess', _58 => _58.name]) === "tailwindcss";
966
+ return t2.isExpressionStatement(statement) && t2.isAssignmentExpression(statement.expression) && t2.isMemberExpression(statement.expression.left) && t2.isFunctionExpression(statement.expression.right) && _optionalChain([statement, 'access', _60 => _60.expression, 'access', _61 => _61.right, 'access', _62 => _62.id, 'optionalAccess', _63 => _63.name]) === "tailwindcss";
793
967
  });
794
968
  if (index === -1) {
795
969
  return;
@@ -825,12 +999,12 @@ function transformPostcssPlugin(content, { refProperty }) {
825
999
  );
826
1000
  }
827
1001
  },
828
- FunctionExpression(path8) {
1002
+ FunctionExpression(path9) {
829
1003
  if (hasPatched) {
830
1004
  return;
831
1005
  }
832
- const fn = path8.node;
833
- if (_optionalChain([fn, 'access', _59 => _59.id, 'optionalAccess', _60 => _60.name]) !== "tailwindcss" || fn.body.body.length !== 1) {
1006
+ const fn = path9.node;
1007
+ if (_optionalChain([fn, 'access', _64 => _64.id, 'optionalAccess', _65 => _65.name]) !== "tailwindcss" || fn.body.body.length !== 1) {
834
1008
  return;
835
1009
  }
836
1010
  const [returnStatement3] = fn.body.body;
@@ -995,21 +1169,21 @@ function updateLengthUnitsArray(content, options) {
995
1169
  let arrayRef;
996
1170
  let changed = false;
997
1171
  traverse(ast, {
998
- Identifier(path8) {
999
- if (path8.node.name === variableName && t3.isVariableDeclarator(path8.parent) && t3.isArrayExpression(path8.parent.init)) {
1000
- arrayRef = path8.parent.init;
1172
+ Identifier(path9) {
1173
+ if (path9.node.name === variableName && t3.isVariableDeclarator(path9.parent) && t3.isArrayExpression(path9.parent.init)) {
1174
+ arrayRef = path9.parent.init;
1001
1175
  const existing = new Set(
1002
- path8.parent.init.elements.map((element) => t3.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
1176
+ path9.parent.init.elements.map((element) => t3.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
1003
1177
  );
1004
1178
  for (const unit of units) {
1005
1179
  if (!existing.has(unit)) {
1006
- path8.parent.init.elements = path8.parent.init.elements.map((element) => {
1180
+ path9.parent.init.elements = path9.parent.init.elements.map((element) => {
1007
1181
  if (t3.isStringLiteral(element)) {
1008
1182
  return t3.stringLiteral(element.value);
1009
1183
  }
1010
1184
  return element;
1011
1185
  });
1012
- path8.parent.init.elements.push(t3.stringLiteral(unit));
1186
+ path9.parent.init.elements.push(t3.stringLiteral(unit));
1013
1187
  changed = true;
1014
1188
  }
1015
1189
  }
@@ -1090,13 +1264,13 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
1090
1264
  const { code, file, match } = item;
1091
1265
  const ast = _parser.parse.call(void 0, match[0], { sourceType: "unambiguous" });
1092
1266
  traverse(ast, {
1093
- ArrayExpression(path8) {
1267
+ ArrayExpression(path9) {
1094
1268
  for (const unit of opts.units) {
1095
- if (path8.node.elements.some((element) => t3.isStringLiteral(element) && element.value === unit)) {
1269
+ if (path9.node.elements.some((element) => t3.isStringLiteral(element) && element.value === unit)) {
1096
1270
  item.hasPatched = true;
1097
1271
  return;
1098
1272
  }
1099
- path8.node.elements.push(t3.stringLiteral(unit));
1273
+ path9.node.elements.push(t3.stringLiteral(unit));
1100
1274
  }
1101
1275
  }
1102
1276
  });
@@ -1138,7 +1312,7 @@ function applyTailwindPatches(context) {
1138
1312
  majorVersion
1139
1313
  });
1140
1314
  }
1141
- if (_optionalChain([options, 'access', _61 => _61.features, 'access', _62 => _62.extendLengthUnits, 'optionalAccess', _63 => _63.enabled])) {
1315
+ if (_optionalChain([options, 'access', _66 => _66.features, 'access', _67 => _67.extendLengthUnits, 'optionalAccess', _68 => _68.enabled])) {
1142
1316
  if (majorVersion === 3) {
1143
1317
  results.extendLengthUnits = applyExtendLengthUnitsPatchV3(
1144
1318
  packageInfo.rootPath,
@@ -1249,6 +1423,13 @@ var TailwindcssPatcher = (_class = class {
1249
1423
  const contexts = this.getContexts();
1250
1424
  return collectClassesFromContexts(contexts, this.options.filter);
1251
1425
  }
1426
+ collectClassSetSync() {
1427
+ if (this.majorVersion === 4) {
1428
+ throw new Error("getClassSetSync is not supported for Tailwind CSS v4 projects. Use getClassSet instead.");
1429
+ }
1430
+ const contexts = this.getContexts();
1431
+ return collectClassesFromContexts(contexts, this.options.filter);
1432
+ }
1252
1433
  async mergeWithCache(set) {
1253
1434
  if (!this.options.cache.enabled) {
1254
1435
  return set;
@@ -1268,13 +1449,36 @@ var TailwindcssPatcher = (_class = class {
1268
1449
  }
1269
1450
  return set;
1270
1451
  }
1452
+ mergeWithCacheSync(set) {
1453
+ if (!this.options.cache.enabled) {
1454
+ return set;
1455
+ }
1456
+ const existing = this.cacheStore.readSync();
1457
+ if (this.options.cache.strategy === "merge") {
1458
+ for (const value of existing) {
1459
+ set.add(value);
1460
+ }
1461
+ this.cacheStore.writeSync(set);
1462
+ } else {
1463
+ if (set.size > 0) {
1464
+ this.cacheStore.writeSync(set);
1465
+ } else {
1466
+ return existing;
1467
+ }
1468
+ }
1469
+ return set;
1470
+ }
1271
1471
  async getClassSet() {
1272
1472
  await this.runTailwindBuildIfNeeded();
1273
1473
  const set = await this.collectClassSet();
1274
1474
  return this.mergeWithCache(set);
1275
1475
  }
1476
+ getClassSetSync() {
1477
+ const set = this.collectClassSetSync();
1478
+ return this.mergeWithCacheSync(set);
1479
+ }
1276
1480
  async extract(options) {
1277
- const shouldWrite = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _64 => _64.write]), () => ( this.options.output.enabled));
1481
+ const shouldWrite = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _69 => _69.write]), () => ( this.options.output.enabled));
1278
1482
  const classSet = await this.getClassSet();
1279
1483
  const classList = Array.from(classSet);
1280
1484
  const result = {
@@ -1301,6 +1505,22 @@ var TailwindcssPatcher = (_class = class {
1301
1505
  }
1302
1506
  // Backwards compatibility helper used by tests and API consumers.
1303
1507
  __init() {this.extractValidCandidates = exports.extractValidCandidates = extractValidCandidates}
1508
+ async collectContentTokens(options) {
1509
+ return extractProjectCandidatesWithPositions({
1510
+ cwd: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _70 => _70.cwd]), () => ( this.options.projectRoot)),
1511
+ sources: _nullishCoalesce(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _71 => _71.sources]), () => ( _optionalChain([this, 'access', _72 => _72.options, 'access', _73 => _73.tailwind, 'access', _74 => _74.v4, 'optionalAccess', _75 => _75.sources]))), () => ( []))
1512
+ });
1513
+ }
1514
+ async collectContentTokensByFile(options) {
1515
+ const report = await this.collectContentTokens({
1516
+ cwd: _optionalChain([options, 'optionalAccess', _76 => _76.cwd]),
1517
+ sources: _optionalChain([options, 'optionalAccess', _77 => _77.sources])
1518
+ });
1519
+ return groupTokensByFile(report, {
1520
+ key: _optionalChain([options, 'optionalAccess', _78 => _78.key]),
1521
+ stripAbsolutePaths: _optionalChain([options, 'optionalAccess', _79 => _79.stripAbsolutePaths])
1522
+ });
1523
+ }
1304
1524
  }, _class);
1305
1525
 
1306
1526
 
@@ -1317,4 +1537,6 @@ var TailwindcssPatcher = (_class = class {
1317
1537
 
1318
1538
 
1319
1539
 
1320
- exports.logger_default = logger_default; exports.CacheStore = CacheStore; exports.extractRawCandidatesWithPositions = extractRawCandidatesWithPositions; exports.extractRawCandidates = extractRawCandidates; exports.extractValidCandidates = extractValidCandidates; exports.fromLegacyOptions = fromLegacyOptions; exports.fromUnifiedConfig = fromUnifiedConfig; exports.normalizeOptions = normalizeOptions; exports.collectClassesFromContexts = collectClassesFromContexts; exports.collectClassesFromTailwindV4 = collectClassesFromTailwindV4; exports.loadRuntimeContexts = loadRuntimeContexts; exports.runTailwindBuild = runTailwindBuild; exports.TailwindcssPatcher = TailwindcssPatcher;
1540
+
1541
+
1542
+ exports.logger_default = logger_default; exports.CacheStore = CacheStore; exports.extractRawCandidatesWithPositions = extractRawCandidatesWithPositions; exports.extractRawCandidates = extractRawCandidates; exports.extractValidCandidates = extractValidCandidates; exports.extractProjectCandidatesWithPositions = extractProjectCandidatesWithPositions; exports.groupTokensByFile = groupTokensByFile; exports.fromLegacyOptions = fromLegacyOptions; exports.fromUnifiedConfig = fromUnifiedConfig; exports.normalizeOptions = normalizeOptions; exports.collectClassesFromContexts = collectClassesFromContexts; exports.collectClassesFromTailwindV4 = collectClassesFromTailwindV4; exports.loadRuntimeContexts = loadRuntimeContexts; exports.runTailwindBuild = runTailwindBuild; exports.TailwindcssPatcher = TailwindcssPatcher;