tailwindcss-patch 8.1.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.
@@ -90,7 +90,9 @@ var CacheStore = class {
90
90
  };
91
91
 
92
92
  // src/extraction/candidate-extractor.ts
93
+ import { promises as fs2 } from "fs";
93
94
  import process from "process";
95
+ import path from "pathe";
94
96
  async function importNode() {
95
97
  return import("@tailwindcss/node");
96
98
  }
@@ -138,6 +140,139 @@ async function extractValidCandidates(options) {
138
140
  );
139
141
  return validCandidates;
140
142
  }
143
+ function normalizeSources(sources, cwd) {
144
+ const baseSources = sources?.length ? sources : [
145
+ {
146
+ base: cwd,
147
+ pattern: "**/*",
148
+ negated: false
149
+ }
150
+ ];
151
+ return baseSources.map((source) => ({
152
+ base: source.base ?? cwd,
153
+ pattern: source.pattern,
154
+ negated: source.negated
155
+ }));
156
+ }
157
+ function buildLineOffsets(content) {
158
+ const offsets = [0];
159
+ for (let i = 0; i < content.length; i++) {
160
+ if (content[i] === "\n") {
161
+ offsets.push(i + 1);
162
+ }
163
+ }
164
+ if (offsets[offsets.length - 1] !== content.length) {
165
+ offsets.push(content.length);
166
+ }
167
+ return offsets;
168
+ }
169
+ function resolveLineMeta(content, offsets, index) {
170
+ let low = 0;
171
+ let high = offsets.length - 1;
172
+ while (low <= high) {
173
+ const mid = Math.floor((low + high) / 2);
174
+ const start = offsets[mid];
175
+ const nextStart = offsets[mid + 1] ?? content.length;
176
+ if (index < start) {
177
+ high = mid - 1;
178
+ continue;
179
+ }
180
+ if (index >= nextStart) {
181
+ low = mid + 1;
182
+ continue;
183
+ }
184
+ const line = mid + 1;
185
+ const column = index - start + 1;
186
+ const lineEnd = content.indexOf("\n", start);
187
+ const lineText = content.slice(start, lineEnd === -1 ? content.length : lineEnd);
188
+ return { line, column, lineText };
189
+ }
190
+ const lastStart = offsets[offsets.length - 2] ?? 0;
191
+ return {
192
+ line: offsets.length - 1,
193
+ column: index - lastStart + 1,
194
+ lineText: content.slice(lastStart)
195
+ };
196
+ }
197
+ function toExtension(filename) {
198
+ const ext = path.extname(filename).replace(/^\./, "");
199
+ return ext || "txt";
200
+ }
201
+ function toRelativeFile(cwd, filename) {
202
+ const relative = path.relative(cwd, filename);
203
+ return relative === "" ? path.basename(filename) : relative;
204
+ }
205
+ async function extractProjectCandidatesWithPositions(options) {
206
+ const cwd = options?.cwd ? path.resolve(options.cwd) : process.cwd();
207
+ const normalizedSources = normalizeSources(options?.sources, cwd);
208
+ const { Scanner } = await importOxide();
209
+ const scanner = new Scanner({
210
+ sources: normalizedSources
211
+ });
212
+ const files = scanner.files ?? [];
213
+ const entries = [];
214
+ const skipped = [];
215
+ for (const file of files) {
216
+ let content;
217
+ try {
218
+ content = await fs2.readFile(file, "utf8");
219
+ } catch (error) {
220
+ skipped.push({
221
+ file,
222
+ reason: error instanceof Error ? error.message : "Unknown error"
223
+ });
224
+ continue;
225
+ }
226
+ const extension = toExtension(file);
227
+ const matches = scanner.getCandidatesWithPositions({
228
+ file,
229
+ content,
230
+ extension
231
+ });
232
+ if (!matches.length) {
233
+ continue;
234
+ }
235
+ const offsets = buildLineOffsets(content);
236
+ const relativeFile = toRelativeFile(cwd, file);
237
+ for (const match of matches) {
238
+ const info = resolveLineMeta(content, offsets, match.position);
239
+ entries.push({
240
+ rawCandidate: match.candidate,
241
+ file,
242
+ relativeFile,
243
+ extension,
244
+ start: match.position,
245
+ end: match.position + match.candidate.length,
246
+ length: match.candidate.length,
247
+ line: info.line,
248
+ column: info.column,
249
+ lineText: info.lineText
250
+ });
251
+ }
252
+ }
253
+ return {
254
+ entries,
255
+ filesScanned: files.length,
256
+ skippedFiles: skipped,
257
+ sources: normalizedSources
258
+ };
259
+ }
260
+ function groupTokensByFile(report, options) {
261
+ const key = options?.key ?? "relative";
262
+ const stripAbsolute = options?.stripAbsolutePaths ?? key !== "absolute";
263
+ return report.entries.reduce((acc, entry) => {
264
+ const bucketKey = key === "absolute" ? entry.file : entry.relativeFile;
265
+ if (!acc[bucketKey]) {
266
+ acc[bucketKey] = [];
267
+ }
268
+ const value = stripAbsolute ? {
269
+ ...entry,
270
+ file: entry.relativeFile
271
+ } : entry;
272
+ acc[bucketKey].push(value);
273
+ return acc;
274
+ }, {});
275
+ }
141
276
 
142
277
  // src/options/legacy.ts
143
278
  function normalizeLegacyFeatures(patch) {
@@ -240,7 +375,7 @@ function fromUnifiedConfig(registry) {
240
375
 
241
376
  // src/options/normalize.ts
242
377
  import process2 from "process";
243
- import path from "pathe";
378
+ import path2 from "pathe";
244
379
 
245
380
  // src/constants.ts
246
381
  var pkgName = "tailwindcss-patch";
@@ -258,7 +393,7 @@ function toPrettyValue(value) {
258
393
  function normalizeCacheOptions(cache, projectRoot) {
259
394
  let enabled = false;
260
395
  let cwd = projectRoot;
261
- let dir = path.resolve(cwd, "node_modules/.cache", pkgName);
396
+ let dir = path2.resolve(cwd, "node_modules/.cache", pkgName);
262
397
  let file = "class-cache.json";
263
398
  let strategy = "merge";
264
399
  if (typeof cache === "boolean") {
@@ -266,11 +401,11 @@ function normalizeCacheOptions(cache, projectRoot) {
266
401
  } else if (typeof cache === "object" && cache) {
267
402
  enabled = cache.enabled ?? true;
268
403
  cwd = cache.cwd ?? cwd;
269
- dir = cache.dir ? path.resolve(cache.dir) : path.resolve(cwd, "node_modules/.cache", pkgName);
404
+ dir = cache.dir ? path2.resolve(cache.dir) : path2.resolve(cwd, "node_modules/.cache", pkgName);
270
405
  file = cache.file ?? file;
271
406
  strategy = cache.strategy ?? strategy;
272
407
  }
273
- const filename = path.resolve(dir, file);
408
+ const filename = path2.resolve(dir, file);
274
409
  return {
275
410
  enabled,
276
411
  cwd,
@@ -333,8 +468,8 @@ function normalizeExtendLengthUnitsOptions(features) {
333
468
  };
334
469
  }
335
470
  function normalizeTailwindV4Options(v4, fallbackBase) {
336
- const base = v4?.base ? path.resolve(v4.base) : fallbackBase;
337
- const cssEntries = Array.isArray(v4?.cssEntries) ? v4.cssEntries.filter((entry) => Boolean(entry)).map((entry) => path.resolve(entry)) : [];
471
+ const base = v4?.base ? path2.resolve(v4.base) : fallbackBase;
472
+ const cssEntries = Array.isArray(v4?.cssEntries) ? v4.cssEntries.filter((entry) => Boolean(entry)).map((entry) => path2.resolve(entry)) : [];
338
473
  const sources = v4?.sources?.length ? v4.sources : [
339
474
  {
340
475
  base,
@@ -370,7 +505,7 @@ function normalizeTailwindOptions(tailwind, projectRoot) {
370
505
  };
371
506
  }
372
507
  function normalizeOptions(options = {}) {
373
- const projectRoot = options.cwd ? path.resolve(options.cwd) : process2.cwd();
508
+ const projectRoot = options.cwd ? path2.resolve(options.cwd) : process2.cwd();
374
509
  const overwrite = options.overwrite ?? true;
375
510
  const output = normalizeOutputOptions(options.output);
376
511
  const cache = normalizeCacheOptions(options.cache, projectRoot);
@@ -402,8 +537,8 @@ function normalizeOptions(options = {}) {
402
537
 
403
538
  // src/runtime/class-collector.ts
404
539
  import process3 from "process";
405
- import fs2 from "fs-extra";
406
- import path2 from "pathe";
540
+ import fs3 from "fs-extra";
541
+ import path3 from "pathe";
407
542
 
408
543
  // src/utils.ts
409
544
  function isObject(val) {
@@ -461,11 +596,11 @@ async function collectClassesFromTailwindV4(options) {
461
596
  });
462
597
  if (v4Options.cssEntries.length > 0) {
463
598
  for (const entry of v4Options.cssEntries) {
464
- const filePath = path2.isAbsolute(entry) ? entry : path2.resolve(options.projectRoot, entry);
465
- if (!await fs2.pathExists(filePath)) {
599
+ const filePath = path3.isAbsolute(entry) ? entry : path3.resolve(options.projectRoot, entry);
600
+ if (!await fs3.pathExists(filePath)) {
466
601
  continue;
467
602
  }
468
- const css = await fs2.readFile(filePath, "utf8");
603
+ const css = await fs3.readFile(filePath, "utf8");
469
604
  const candidates = await extractValidCandidates({
470
605
  cwd: options.projectRoot,
471
606
  base: v4Options.base,
@@ -496,23 +631,23 @@ async function collectClassesFromTailwindV4(options) {
496
631
 
497
632
  // src/runtime/context-registry.ts
498
633
  import { createRequire } from "module";
499
- import fs3 from "fs-extra";
500
- import path3 from "pathe";
634
+ import fs4 from "fs-extra";
635
+ import path4 from "pathe";
501
636
  var require2 = createRequire(import.meta.url);
502
637
  function resolveRuntimeEntry(packageInfo, majorVersion) {
503
638
  const root = packageInfo.rootPath;
504
639
  if (majorVersion === 2) {
505
- const jitIndex = path3.join(root, "lib/jit/index.js");
506
- if (fs3.existsSync(jitIndex)) {
640
+ const jitIndex = path4.join(root, "lib/jit/index.js");
641
+ if (fs4.existsSync(jitIndex)) {
507
642
  return jitIndex;
508
643
  }
509
644
  } else if (majorVersion === 3) {
510
- const plugin = path3.join(root, "lib/plugin.js");
511
- const index = path3.join(root, "lib/index.js");
512
- if (fs3.existsSync(plugin)) {
645
+ const plugin = path4.join(root, "lib/plugin.js");
646
+ const index = path4.join(root, "lib/index.js");
647
+ if (fs4.existsSync(plugin)) {
513
648
  return plugin;
514
649
  }
515
- if (fs3.existsSync(index)) {
650
+ if (fs4.existsSync(index)) {
516
651
  return index;
517
652
  }
518
653
  }
@@ -545,12 +680,12 @@ function loadRuntimeContexts(packageInfo, majorVersion, refProperty) {
545
680
 
546
681
  // src/runtime/process-tailwindcss.ts
547
682
  import { createRequire as createRequire2 } from "module";
548
- import path4 from "pathe";
683
+ import path5 from "pathe";
549
684
  import postcss from "postcss";
550
685
  import { loadConfig } from "tailwindcss-config";
551
686
  var require3 = createRequire2(import.meta.url);
552
687
  async function resolveConfigPath(options) {
553
- if (options.config && path4.isAbsolute(options.config)) {
688
+ if (options.config && path5.isAbsolute(options.config)) {
554
689
  return options.config;
555
690
  }
556
691
  const result = await loadConfig({ cwd: options.cwd });
@@ -582,14 +717,14 @@ async function runTailwindBuild(options) {
582
717
 
583
718
  // src/api/tailwindcss-patcher.ts
584
719
  import process4 from "process";
585
- import fs6 from "fs-extra";
720
+ import fs7 from "fs-extra";
586
721
  import { getPackageInfoSync } from "local-pkg";
587
- import path7 from "pathe";
722
+ import path8 from "pathe";
588
723
  import { coerce } from "semver";
589
724
 
590
725
  // src/patching/operations/export-context/index.ts
591
- import fs4 from "fs-extra";
592
- import path5 from "pathe";
726
+ import fs5 from "fs-extra";
727
+ import path6 from "pathe";
593
728
 
594
729
  // src/patching/operations/export-context/postcss-v2.ts
595
730
  import * as t from "@babel/types";
@@ -628,8 +763,8 @@ function transformProcessTailwindFeaturesReturnContextV2(content) {
628
763
  });
629
764
  let hasPatched = false;
630
765
  traverse(ast, {
631
- FunctionDeclaration(path8) {
632
- const node = path8.node;
766
+ FunctionDeclaration(path9) {
767
+ const node = path9.node;
633
768
  if (node.id?.name !== "processTailwindFeatures" || node.body.body.length !== 1 || !t.isReturnStatement(node.body.body[0])) {
634
769
  return;
635
770
  }
@@ -660,8 +795,8 @@ function transformPostcssPluginV2(content, options) {
660
795
  const ast = parse(content);
661
796
  let hasPatched = false;
662
797
  traverse(ast, {
663
- Program(path8) {
664
- const program = path8.node;
798
+ Program(path9) {
799
+ const program = path9.node;
665
800
  const index = program.body.findIndex((statement) => {
666
801
  return t.isFunctionDeclaration(statement) && statement.id?.name === "_default";
667
802
  });
@@ -695,11 +830,11 @@ function transformPostcssPluginV2(content, options) {
695
830
  );
696
831
  }
697
832
  },
698
- FunctionDeclaration(path8) {
833
+ FunctionDeclaration(path9) {
699
834
  if (hasPatched) {
700
835
  return;
701
836
  }
702
- const fn = path8.node;
837
+ const fn = path9.node;
703
838
  if (fn.id?.name !== "_default") {
704
839
  return;
705
840
  }
@@ -788,8 +923,8 @@ function transformProcessTailwindFeaturesReturnContext(content) {
788
923
  const ast = parse(content);
789
924
  let hasPatched = false;
790
925
  traverse(ast, {
791
- FunctionDeclaration(path8) {
792
- const node = path8.node;
926
+ FunctionDeclaration(path9) {
927
+ const node = path9.node;
793
928
  if (node.id?.name !== "processTailwindFeatures" || node.body.body.length !== 1) {
794
929
  return;
795
930
  }
@@ -821,8 +956,8 @@ function transformPostcssPlugin(content, { refProperty }) {
821
956
  const valueMember = t2.memberExpression(refIdentifier, t2.identifier("value"));
822
957
  let hasPatched = false;
823
958
  traverse(ast, {
824
- Program(path8) {
825
- const program = path8.node;
959
+ Program(path9) {
960
+ const program = path9.node;
826
961
  const index = program.body.findIndex((statement) => {
827
962
  return t2.isExpressionStatement(statement) && t2.isAssignmentExpression(statement.expression) && t2.isMemberExpression(statement.expression.left) && t2.isFunctionExpression(statement.expression.right) && statement.expression.right.id?.name === "tailwindcss";
828
963
  });
@@ -860,11 +995,11 @@ function transformPostcssPlugin(content, { refProperty }) {
860
995
  );
861
996
  }
862
997
  },
863
- FunctionExpression(path8) {
998
+ FunctionExpression(path9) {
864
999
  if (hasPatched) {
865
1000
  return;
866
1001
  }
867
- const fn = path8.node;
1002
+ const fn = path9.node;
868
1003
  if (fn.id?.name !== "tailwindcss" || fn.body.body.length !== 1) {
869
1004
  return;
870
1005
  }
@@ -938,7 +1073,7 @@ function writeFileIfRequired(filePath, code, overwrite, successMessage) {
938
1073
  if (!overwrite) {
939
1074
  return;
940
1075
  }
941
- fs4.writeFileSync(filePath, code, {
1076
+ fs5.writeFileSync(filePath, code, {
942
1077
  encoding: "utf8"
943
1078
  });
944
1079
  logger_default.success(successMessage);
@@ -951,9 +1086,9 @@ function applyExposeContextPatch(params) {
951
1086
  };
952
1087
  if (majorVersion === 3) {
953
1088
  const processFileRelative = "lib/processTailwindFeatures.js";
954
- const processFilePath = path5.resolve(rootDir, processFileRelative);
955
- if (fs4.existsSync(processFilePath)) {
956
- const content = fs4.readFileSync(processFilePath, "utf8");
1089
+ const processFilePath = path6.resolve(rootDir, processFileRelative);
1090
+ if (fs5.existsSync(processFilePath)) {
1091
+ const content = fs5.readFileSync(processFilePath, "utf8");
957
1092
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContext(content);
958
1093
  result.files[processFileRelative] = code;
959
1094
  if (!hasPatched) {
@@ -967,10 +1102,10 @@ function applyExposeContextPatch(params) {
967
1102
  }
968
1103
  }
969
1104
  const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
970
- const pluginRelative = pluginCandidates.find((candidate) => fs4.existsSync(path5.resolve(rootDir, candidate)));
1105
+ const pluginRelative = pluginCandidates.find((candidate) => fs5.existsSync(path6.resolve(rootDir, candidate)));
971
1106
  if (pluginRelative) {
972
- const pluginPath = path5.resolve(rootDir, pluginRelative);
973
- const content = fs4.readFileSync(pluginPath, "utf8");
1107
+ const pluginPath = path6.resolve(rootDir, pluginRelative);
1108
+ const content = fs5.readFileSync(pluginPath, "utf8");
974
1109
  const { code, hasPatched } = transformPostcssPlugin(content, { refProperty });
975
1110
  result.files[pluginRelative] = code;
976
1111
  if (!hasPatched) {
@@ -985,9 +1120,9 @@ function applyExposeContextPatch(params) {
985
1120
  }
986
1121
  } else if (majorVersion === 2) {
987
1122
  const processFileRelative = "lib/jit/processTailwindFeatures.js";
988
- const processFilePath = path5.resolve(rootDir, processFileRelative);
989
- if (fs4.existsSync(processFilePath)) {
990
- const content = fs4.readFileSync(processFilePath, "utf8");
1123
+ const processFilePath = path6.resolve(rootDir, processFileRelative);
1124
+ if (fs5.existsSync(processFilePath)) {
1125
+ const content = fs5.readFileSync(processFilePath, "utf8");
991
1126
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContextV2(content);
992
1127
  result.files[processFileRelative] = code;
993
1128
  if (!hasPatched) {
@@ -1001,9 +1136,9 @@ function applyExposeContextPatch(params) {
1001
1136
  }
1002
1137
  }
1003
1138
  const pluginRelative = "lib/jit/index.js";
1004
- const pluginPath = path5.resolve(rootDir, pluginRelative);
1005
- if (fs4.existsSync(pluginPath)) {
1006
- const content = fs4.readFileSync(pluginPath, "utf8");
1139
+ const pluginPath = path6.resolve(rootDir, pluginRelative);
1140
+ if (fs5.existsSync(pluginPath)) {
1141
+ const content = fs5.readFileSync(pluginPath, "utf8");
1007
1142
  const { code, hasPatched } = transformPostcssPluginV2(content, { refProperty });
1008
1143
  result.files[pluginRelative] = code;
1009
1144
  if (!hasPatched) {
@@ -1022,29 +1157,29 @@ function applyExposeContextPatch(params) {
1022
1157
 
1023
1158
  // src/patching/operations/extend-length-units.ts
1024
1159
  import * as t3 from "@babel/types";
1025
- import fs5 from "fs-extra";
1026
- import path6 from "pathe";
1160
+ import fs6 from "fs-extra";
1161
+ import path7 from "pathe";
1027
1162
  function updateLengthUnitsArray(content, options) {
1028
1163
  const { variableName = "lengthUnits", units } = options;
1029
1164
  const ast = parse(content);
1030
1165
  let arrayRef;
1031
1166
  let changed = false;
1032
1167
  traverse(ast, {
1033
- Identifier(path8) {
1034
- if (path8.node.name === variableName && t3.isVariableDeclarator(path8.parent) && t3.isArrayExpression(path8.parent.init)) {
1035
- arrayRef = path8.parent.init;
1168
+ Identifier(path9) {
1169
+ if (path9.node.name === variableName && t3.isVariableDeclarator(path9.parent) && t3.isArrayExpression(path9.parent.init)) {
1170
+ arrayRef = path9.parent.init;
1036
1171
  const existing = new Set(
1037
- path8.parent.init.elements.map((element) => t3.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
1172
+ path9.parent.init.elements.map((element) => t3.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
1038
1173
  );
1039
1174
  for (const unit of units) {
1040
1175
  if (!existing.has(unit)) {
1041
- path8.parent.init.elements = path8.parent.init.elements.map((element) => {
1176
+ path9.parent.init.elements = path9.parent.init.elements.map((element) => {
1042
1177
  if (t3.isStringLiteral(element)) {
1043
1178
  return t3.stringLiteral(element.value);
1044
1179
  }
1045
1180
  return element;
1046
1181
  });
1047
- path8.parent.init.elements.push(t3.stringLiteral(unit));
1182
+ path9.parent.init.elements.push(t3.stringLiteral(unit));
1048
1183
  changed = true;
1049
1184
  }
1050
1185
  }
@@ -1065,12 +1200,12 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
1065
1200
  lengthUnitsFilePath: options.lengthUnitsFilePath ?? "lib/util/dataTypes.js",
1066
1201
  variableName: options.variableName ?? "lengthUnits"
1067
1202
  };
1068
- const dataTypesFilePath = path6.resolve(rootDir, opts.lengthUnitsFilePath);
1069
- const exists = fs5.existsSync(dataTypesFilePath);
1203
+ const dataTypesFilePath = path7.resolve(rootDir, opts.lengthUnitsFilePath);
1204
+ const exists = fs6.existsSync(dataTypesFilePath);
1070
1205
  if (!exists) {
1071
1206
  return { changed: false, code: void 0 };
1072
1207
  }
1073
- const content = fs5.readFileSync(dataTypesFilePath, "utf8");
1208
+ const content = fs6.readFileSync(dataTypesFilePath, "utf8");
1074
1209
  const { arrayRef, changed } = updateLengthUnitsArray(content, opts);
1075
1210
  if (!arrayRef || !changed) {
1076
1211
  return { changed: false, code: void 0 };
@@ -1081,8 +1216,8 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
1081
1216
  if (arrayRef.start != null && arrayRef.end != null) {
1082
1217
  const nextCode = `${content.slice(0, arrayRef.start)}${code}${content.slice(arrayRef.end)}`;
1083
1218
  if (opts.overwrite) {
1084
- const target = opts.destPath ? path6.resolve(opts.destPath) : dataTypesFilePath;
1085
- fs5.writeFileSync(target, nextCode, "utf8");
1219
+ const target = opts.destPath ? path7.resolve(opts.destPath) : dataTypesFilePath;
1220
+ fs6.writeFileSync(target, nextCode, "utf8");
1086
1221
  logger_default.success("Patched Tailwind CSS length unit list (v3).");
1087
1222
  }
1088
1223
  return {
@@ -1100,16 +1235,16 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
1100
1235
  return { files: [], changed: false };
1101
1236
  }
1102
1237
  const opts = { ...options };
1103
- const distDir = path6.resolve(rootDir, "dist");
1104
- if (!fs5.existsSync(distDir)) {
1238
+ const distDir = path7.resolve(rootDir, "dist");
1239
+ if (!fs6.existsSync(distDir)) {
1105
1240
  return { files: [], changed: false };
1106
1241
  }
1107
- const entries = fs5.readdirSync(distDir);
1242
+ const entries = fs6.readdirSync(distDir);
1108
1243
  const chunkNames = entries.filter((entry) => entry.endsWith(".js") || entry.endsWith(".mjs"));
1109
1244
  const pattern = /\[\s*["']cm["'],\s*["']mm["'],[\w,"']+\]/;
1110
1245
  const candidates = chunkNames.map((chunkName) => {
1111
- const file = path6.join(distDir, chunkName);
1112
- const code = fs5.readFileSync(file, "utf8");
1246
+ const file = path7.join(distDir, chunkName);
1247
+ const code = fs6.readFileSync(file, "utf8");
1113
1248
  const match = pattern.exec(code);
1114
1249
  if (!match) {
1115
1250
  return null;
@@ -1125,13 +1260,13 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
1125
1260
  const { code, file, match } = item;
1126
1261
  const ast = parse(match[0], { sourceType: "unambiguous" });
1127
1262
  traverse(ast, {
1128
- ArrayExpression(path8) {
1263
+ ArrayExpression(path9) {
1129
1264
  for (const unit of opts.units) {
1130
- if (path8.node.elements.some((element) => t3.isStringLiteral(element) && element.value === unit)) {
1265
+ if (path9.node.elements.some((element) => t3.isStringLiteral(element) && element.value === unit)) {
1131
1266
  item.hasPatched = true;
1132
1267
  return;
1133
1268
  }
1134
- path8.node.elements.push(t3.stringLiteral(unit));
1269
+ path9.node.elements.push(t3.stringLiteral(unit));
1135
1270
  }
1136
1271
  }
1137
1272
  });
@@ -1149,7 +1284,7 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
1149
1284
  }
1150
1285
  ]);
1151
1286
  if (opts.overwrite) {
1152
- fs5.writeFileSync(file, item.code, "utf8");
1287
+ fs6.writeFileSync(file, item.code, "utf8");
1153
1288
  }
1154
1289
  }
1155
1290
  if (candidates.some((file) => !file.hasPatched)) {
@@ -1349,13 +1484,13 @@ var TailwindcssPatcher = class {
1349
1484
  if (!shouldWrite || !this.options.output.file) {
1350
1485
  return result;
1351
1486
  }
1352
- const target = path7.resolve(this.options.output.file);
1353
- await fs6.ensureDir(path7.dirname(target));
1487
+ const target = path8.resolve(this.options.output.file);
1488
+ await fs7.ensureDir(path8.dirname(target));
1354
1489
  if (this.options.output.format === "json") {
1355
1490
  const spaces = typeof this.options.output.pretty === "number" ? this.options.output.pretty : void 0;
1356
- await fs6.writeJSON(target, classList, { spaces });
1491
+ await fs7.writeJSON(target, classList, { spaces });
1357
1492
  } else {
1358
- await fs6.writeFile(target, `${classList.join("\n")}
1493
+ await fs7.writeFile(target, `${classList.join("\n")}
1359
1494
  `, "utf8");
1360
1495
  }
1361
1496
  logger_default.success(`Tailwind CSS class list saved to ${target.replace(process4.cwd(), ".")}`);
@@ -1366,6 +1501,22 @@ var TailwindcssPatcher = class {
1366
1501
  }
1367
1502
  // Backwards compatibility helper used by tests and API consumers.
1368
1503
  extractValidCandidates = extractValidCandidates;
1504
+ async collectContentTokens(options) {
1505
+ return extractProjectCandidatesWithPositions({
1506
+ cwd: options?.cwd ?? this.options.projectRoot,
1507
+ sources: options?.sources ?? this.options.tailwind.v4?.sources ?? []
1508
+ });
1509
+ }
1510
+ async collectContentTokensByFile(options) {
1511
+ const report = await this.collectContentTokens({
1512
+ cwd: options?.cwd,
1513
+ sources: options?.sources
1514
+ });
1515
+ return groupTokensByFile(report, {
1516
+ key: options?.key,
1517
+ stripAbsolutePaths: options?.stripAbsolutePaths
1518
+ });
1519
+ }
1369
1520
  };
1370
1521
 
1371
1522
  export {
@@ -1374,6 +1525,8 @@ export {
1374
1525
  extractRawCandidatesWithPositions,
1375
1526
  extractRawCandidates,
1376
1527
  extractValidCandidates,
1528
+ extractProjectCandidatesWithPositions,
1529
+ groupTokensByFile,
1377
1530
  fromLegacyOptions,
1378
1531
  fromUnifiedConfig,
1379
1532
  normalizeOptions,