tailwindcss-patch 9.5.0 → 10.0.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.
@@ -1,8 +1,9 @@
1
1
  import { createRequire } from "node:module";
2
+ import cac from "cac";
2
3
  import process from "node:process";
4
+ import path from "pathe";
3
5
  import fs from "fs-extra";
4
6
  import { getPackageInfoSync } from "local-pkg";
5
- import path from "pathe";
6
7
  import { coerce } from "semver";
7
8
  import { createHash } from "node:crypto";
8
9
  import { createConsola } from "consola";
@@ -11,12 +12,237 @@ import { fileURLToPath, pathToFileURL } from "node:url";
11
12
  import { extractProjectCandidatesWithPositions, extractRawCandidates, extractRawCandidatesWithPositions, extractSourceCandidates, extractSourceCandidatesWithPositions, extractValidCandidates, groupTokensByFile, resolveProjectSourceFiles } from "@tailwindcss-mangle/engine";
12
13
  import * as t from "@babel/types";
13
14
  import generate from "@babel/generator";
14
- import _babelTraverse from "@babel/traverse";
15
+ import traverse from "@babel/traverse";
15
16
  import { parse, parse as parse$1 } from "@babel/parser";
16
17
  import postcss from "postcss";
17
18
  import { loadConfig } from "tailwindcss-config";
19
+ //#region src/commands/token-output.ts
20
+ const TOKEN_FORMATS = [
21
+ "json",
22
+ "lines",
23
+ "grouped-json"
24
+ ];
25
+ const DEFAULT_TOKEN_REPORT = ".tw-patch/tw-token-report.json";
26
+ function formatTokenLine(entry) {
27
+ return `${entry.relativeFile}:${entry.line}:${entry.column} ${entry.rawCandidate} (${entry.start}-${entry.end})`;
28
+ }
29
+ function formatGroupedPreview(map, limit = 3) {
30
+ const files = Object.keys(map);
31
+ if (!files.length) return {
32
+ preview: "",
33
+ moreFiles: 0
34
+ };
35
+ return {
36
+ preview: files.slice(0, limit).map((file) => {
37
+ const tokens = map[file] ?? [];
38
+ const sample = tokens.slice(0, 3).map((token) => token.rawCandidate).join(", ");
39
+ const suffix = tokens.length > 3 ? ", …" : "";
40
+ return `${file}: ${tokens.length} tokens (${sample}${suffix})`;
41
+ }).join("\n"),
42
+ moreFiles: Math.max(0, files.length - limit)
43
+ };
44
+ }
45
+ //#endregion
46
+ //#region src/commands/command-definitions.ts
47
+ function createCwdOptionDefinition(description = "Working directory") {
48
+ return {
49
+ flags: "--cwd <dir>",
50
+ description,
51
+ config: { default: process.cwd() }
52
+ };
53
+ }
54
+ function buildDefaultCommandDefinitions() {
55
+ return {
56
+ install: {
57
+ description: "Apply Tailwind CSS runtime patches",
58
+ optionDefs: [createCwdOptionDefinition()]
59
+ },
60
+ extract: {
61
+ description: "Collect generated class names into a cache file",
62
+ optionDefs: [
63
+ createCwdOptionDefinition(),
64
+ {
65
+ flags: "--output <file>",
66
+ description: "Override output file path"
67
+ },
68
+ {
69
+ flags: "--format <format>",
70
+ description: "Output format (json|lines)"
71
+ },
72
+ {
73
+ flags: "--css <file>",
74
+ description: "Tailwind CSS entry CSS when using v4"
75
+ },
76
+ {
77
+ flags: "--no-write",
78
+ description: "Skip writing to disk"
79
+ }
80
+ ]
81
+ },
82
+ tokens: {
83
+ description: "Extract Tailwind tokens with file/position metadata",
84
+ optionDefs: [
85
+ createCwdOptionDefinition(),
86
+ {
87
+ flags: "--output <file>",
88
+ description: "Override output file path",
89
+ config: { default: DEFAULT_TOKEN_REPORT }
90
+ },
91
+ {
92
+ flags: "--format <format>",
93
+ description: "Output format (json|lines|grouped-json)",
94
+ config: { default: "json" }
95
+ },
96
+ {
97
+ flags: "--group-key <key>",
98
+ description: "Grouping key for grouped-json output (relative|absolute)",
99
+ config: { default: "relative" }
100
+ },
101
+ {
102
+ flags: "--no-write",
103
+ description: "Skip writing to disk"
104
+ }
105
+ ]
106
+ },
107
+ init: {
108
+ description: "Generate a tailwindcss-patch config file",
109
+ optionDefs: [createCwdOptionDefinition()]
110
+ },
111
+ migrate: {
112
+ description: "Migrate deprecated config fields to modern options",
113
+ optionDefs: [
114
+ createCwdOptionDefinition(),
115
+ {
116
+ flags: "--config <file>",
117
+ description: "Migrate a specific config file path"
118
+ },
119
+ {
120
+ flags: "--workspace",
121
+ description: "Scan workspace recursively for config files"
122
+ },
123
+ {
124
+ flags: "--max-depth <n>",
125
+ description: "Maximum recursion depth for --workspace",
126
+ config: { default: 6 }
127
+ },
128
+ {
129
+ flags: "--include <glob>",
130
+ description: "Only migrate files that match this glob (repeatable)"
131
+ },
132
+ {
133
+ flags: "--exclude <glob>",
134
+ description: "Skip files that match this glob (repeatable)"
135
+ },
136
+ {
137
+ flags: "--report-file <file>",
138
+ description: "Write migration report JSON to a file"
139
+ },
140
+ {
141
+ flags: "--backup-dir <dir>",
142
+ description: "Write pre-migration backups into this directory"
143
+ },
144
+ {
145
+ flags: "--check",
146
+ description: "Exit with an error when migration changes are required"
147
+ },
148
+ {
149
+ flags: "--json",
150
+ description: "Print the migration report as JSON"
151
+ },
152
+ {
153
+ flags: "--dry-run",
154
+ description: "Preview changes without writing files"
155
+ }
156
+ ]
157
+ },
158
+ restore: {
159
+ description: "Restore config files from a previous migration report backup snapshot",
160
+ optionDefs: [
161
+ createCwdOptionDefinition(),
162
+ {
163
+ flags: "--report-file <file>",
164
+ description: "Migration report file generated by migrate"
165
+ },
166
+ {
167
+ flags: "--dry-run",
168
+ description: "Preview restore targets without writing files"
169
+ },
170
+ {
171
+ flags: "--strict",
172
+ description: "Fail when any backup file is missing"
173
+ },
174
+ {
175
+ flags: "--json",
176
+ description: "Print the restore result as JSON"
177
+ }
178
+ ]
179
+ },
180
+ validate: {
181
+ description: "Validate migration report compatibility without modifying files",
182
+ optionDefs: [
183
+ createCwdOptionDefinition(),
184
+ {
185
+ flags: "--report-file <file>",
186
+ description: "Migration report file to validate"
187
+ },
188
+ {
189
+ flags: "--strict",
190
+ description: "Fail when any backup file is missing"
191
+ },
192
+ {
193
+ flags: "--json",
194
+ description: "Print validation result as JSON"
195
+ }
196
+ ]
197
+ },
198
+ status: {
199
+ description: "Check which Tailwind patches are applied",
200
+ optionDefs: [createCwdOptionDefinition(), {
201
+ flags: "--json",
202
+ description: "Print a JSON report of patch status"
203
+ }]
204
+ }
205
+ };
206
+ }
207
+ //#endregion
208
+ //#region src/commands/command-metadata.ts
209
+ function addPrefixIfMissing(value, prefix) {
210
+ if (!prefix || value.startsWith(prefix)) return value;
211
+ return `${prefix}${value}`;
212
+ }
213
+ function resolveCommandNames(command, mountOptions, prefix) {
214
+ const override = mountOptions.commandOptions?.[command];
215
+ return {
216
+ name: addPrefixIfMissing(override?.name ?? command, prefix),
217
+ aliases: (override?.aliases ?? []).map((alias) => addPrefixIfMissing(alias, prefix))
218
+ };
219
+ }
220
+ function resolveOptionDefinitions(defaults, override) {
221
+ if (!override) return defaults;
222
+ const appendDefaults = override.appendDefaultOptions ?? true;
223
+ const customDefs = override.optionDefs ?? [];
224
+ if (!appendDefaults) return customDefs;
225
+ if (customDefs.length === 0) return defaults;
226
+ return [...defaults, ...customDefs];
227
+ }
228
+ function resolveCommandMetadata(command, mountOptions, prefix, defaults) {
229
+ const names = resolveCommandNames(command, mountOptions, prefix);
230
+ const definition = defaults[command];
231
+ const override = mountOptions.commandOptions?.[command];
232
+ const description = override?.description ?? definition.description;
233
+ const optionDefs = resolveOptionDefinitions(definition.optionDefs, override);
234
+ return {
235
+ ...names,
236
+ description,
237
+ optionDefs
238
+ };
239
+ }
240
+ function applyCommandOptions(command, optionDefs) {
241
+ for (const option of optionDefs) command.option(option.flags, option.description ?? "", option.config);
242
+ }
243
+ //#endregion
18
244
  //#region package.json
19
- var version = "9.5.0";
245
+ var version = "10.0.0";
20
246
  //#endregion
21
247
  //#region src/constants.ts
22
248
  const pkgName = "tailwindcss-patch";
@@ -1442,7 +1668,7 @@ async function loadWorkspaceConfigModule() {
1442
1668
  return configModulePromise;
1443
1669
  }
1444
1670
  async function loadWorkspaceDefu() {
1445
- if (!defuPromise) defuPromise = import("./dist-CxmNpfyy.mjs").then((mod) => mod.defu).catch(async (error) => {
1671
+ if (!defuPromise) defuPromise = import("./dist-CxmNpfyy.js").then((mod) => mod.defu).catch(async (error) => {
1446
1672
  if (!isMissingSharedModuleError(error)) throw error;
1447
1673
  return (await import(pathToFileURL(path.resolve(__dirname, "../../../shared/src/utils.ts")).href)).defu;
1448
1674
  });
@@ -1597,13 +1823,6 @@ function loadRuntimeContexts(packageInfo, majorVersion, refProperty) {
1597
1823
  return [];
1598
1824
  }
1599
1825
  //#endregion
1600
- //#region src/babel/index.ts
1601
- function _interopDefaultCompat(e) {
1602
- return e && typeof e === "object" && "default" in e ? e.default : e;
1603
- }
1604
- const generate$1 = _interopDefaultCompat(generate);
1605
- const traverse = _interopDefaultCompat(_babelTraverse);
1606
- //#endregion
1607
1826
  //#region src/patching/operations/export-context/postcss-v2.ts
1608
1827
  const IDENTIFIER_RE$1 = /^[A-Z_$][\w$]*$/i;
1609
1828
  function toIdentifierName$1(property) {
@@ -1631,7 +1850,7 @@ function transformProcessTailwindFeaturesReturnContextV2(content) {
1631
1850
  if (!alreadyReturnsContext) body.push(t.returnStatement(t.identifier("context")));
1632
1851
  } });
1633
1852
  return {
1634
- code: hasPatched ? content : generate$1(ast).code,
1853
+ code: hasPatched ? content : generate(ast).code,
1635
1854
  hasPatched
1636
1855
  };
1637
1856
  }
@@ -1654,9 +1873,9 @@ function transformPostcssPluginV2(content, options) {
1654
1873
  const declaration = previous.declarations[0];
1655
1874
  return Boolean(declaration && t.isIdentifier(declaration.id) && declaration.id.name === refIdentifier.name);
1656
1875
  })());
1657
- const alreadyAssignsExports = Boolean(beforePrevious && t.isExpressionStatement(beforePrevious) && t.isAssignmentExpression(beforePrevious.expression) && t.isMemberExpression(beforePrevious.expression.left) && t.isIdentifier(beforePrevious.expression.right) && beforePrevious.expression.right.name === refIdentifier.name && generate$1(beforePrevious.expression.left).code === generate$1(exportMember).code);
1876
+ const alreadyAssignsExports = Boolean(beforePrevious && t.isExpressionStatement(beforePrevious) && t.isAssignmentExpression(beforePrevious.expression) && t.isMemberExpression(beforePrevious.expression.left) && t.isIdentifier(beforePrevious.expression.right) && beforePrevious.expression.right.name === refIdentifier.name && generate(beforePrevious.expression.left).code === generate(exportMember).code);
1658
1877
  hasPatched = alreadyHasVariable && alreadyAssignsExports;
1659
- if (!alreadyHasVariable) program.body.splice(index, 0, t.variableDeclaration("var", [t.variableDeclarator(refIdentifier, t.objectExpression([t.objectProperty(t.identifier("value"), t.arrayExpression())]))]), t.expressionStatement(t.assignmentExpression("=", exportMember, refIdentifier)));
1878
+ if (!alreadyHasVariable) program.body.splice(index, 0, t.variableDeclaration("var", [t.variableDeclarator(refIdentifier, t.objectExpression([t.objectProperty(t.identifier("value"), t.arrayExpression([]))]))]), t.expressionStatement(t.assignmentExpression("=", exportMember, refIdentifier)));
1660
1879
  },
1661
1880
  FunctionDeclaration(path) {
1662
1881
  if (hasPatched) return;
@@ -1689,7 +1908,7 @@ function transformPostcssPluginV2(content, options) {
1689
1908
  }
1690
1909
  });
1691
1910
  return {
1692
- code: hasPatched ? content : generate$1(ast).code,
1911
+ code: hasPatched ? content : generate(ast).code,
1693
1912
  hasPatched
1694
1913
  };
1695
1914
  }
@@ -1722,7 +1941,7 @@ function transformProcessTailwindFeaturesReturnContext(content) {
1722
1941
  if (!alreadyReturnsContext) body.push(t.returnStatement(t.identifier("context")));
1723
1942
  } });
1724
1943
  return {
1725
- code: hasPatched ? content : generate$1(ast).code,
1944
+ code: hasPatched ? content : generate(ast).code,
1726
1945
  hasPatched
1727
1946
  };
1728
1947
  }
@@ -1745,9 +1964,9 @@ function transformPostcssPlugin(content, { refProperty }) {
1745
1964
  const declaration = previousStatement.declarations[0];
1746
1965
  return Boolean(declaration && t.isIdentifier(declaration.id) && declaration.id.name === refIdentifier.name);
1747
1966
  })());
1748
- const alreadyAssignsModuleExports = Boolean(t.isExpressionStatement(lastStatement) && t.isAssignmentExpression(lastStatement.expression) && t.isMemberExpression(lastStatement.expression.left) && t.isIdentifier(lastStatement.expression.right) && lastStatement.expression.right.name === refIdentifier.name && generate$1(lastStatement.expression.left).code === generate$1(moduleExportsMember).code);
1967
+ const alreadyAssignsModuleExports = Boolean(t.isExpressionStatement(lastStatement) && t.isAssignmentExpression(lastStatement.expression) && t.isMemberExpression(lastStatement.expression.left) && t.isIdentifier(lastStatement.expression.right) && lastStatement.expression.right.name === refIdentifier.name && generate(lastStatement.expression.left).code === generate(moduleExportsMember).code);
1749
1968
  hasPatched = alreadyHasVariable && alreadyAssignsModuleExports;
1750
- if (!alreadyHasVariable) program.body.splice(index, 0, t.variableDeclaration("const", [t.variableDeclarator(refIdentifier, t.objectExpression([t.objectProperty(t.identifier("value"), t.arrayExpression())]))]));
1969
+ if (!alreadyHasVariable) program.body.splice(index, 0, t.variableDeclaration("const", [t.variableDeclarator(refIdentifier, t.objectExpression([t.objectProperty(t.identifier("value"), t.arrayExpression([]))]))]));
1751
1970
  if (!alreadyAssignsModuleExports) program.body.push(t.expressionStatement(t.assignmentExpression("=", moduleExportsMember, refIdentifier)));
1752
1971
  },
1753
1972
  FunctionExpression(path) {
@@ -1780,7 +1999,7 @@ function transformPostcssPlugin(content, { refProperty }) {
1780
1999
  }
1781
2000
  });
1782
2001
  return {
1783
- code: hasPatched ? content : generate$1(ast).code,
2002
+ code: hasPatched ? content : generate(ast).code,
1784
2003
  hasPatched
1785
2004
  };
1786
2005
  }
@@ -1889,7 +2108,7 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
1889
2108
  changed: false,
1890
2109
  code: void 0
1891
2110
  };
1892
- const { code } = generate$1(arrayRef, { jsescOption: { quotes: "single" } });
2111
+ const { code } = generate(arrayRef, { jsescOption: { quotes: "single" } });
1893
2112
  if (arrayRef.start != null && arrayRef.end != null) {
1894
2113
  const nextCode = `${content.slice(0, arrayRef.start)}${code}${content.slice(arrayRef.end)}`;
1895
2114
  if (opts.overwrite) {
@@ -1945,7 +2164,7 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
1945
2164
  }
1946
2165
  } });
1947
2166
  if (item.hasPatched) continue;
1948
- const { code: replacement } = generate$1(ast, { minified: true });
2167
+ const { code: replacement } = generate(ast, { minified: true });
1949
2168
  const start = match.index ?? 0;
1950
2169
  item.code = spliceChangesIntoString(code, [{
1951
2170
  start,
@@ -2513,6 +2732,142 @@ var TailwindcssPatcher = class {
2513
2732
  }
2514
2733
  };
2515
2734
  //#endregion
2735
+ //#region src/commands/command-context.ts
2736
+ function resolveCommandCwd(rawCwd) {
2737
+ if (!rawCwd) return process.cwd();
2738
+ return path.resolve(rawCwd);
2739
+ }
2740
+ function createMemoizedPromiseRunner(factory) {
2741
+ let promise;
2742
+ return () => {
2743
+ if (!promise) promise = factory();
2744
+ return promise;
2745
+ };
2746
+ }
2747
+ function createTailwindcssPatchCommandContext(cli, command, commandName, args, cwd) {
2748
+ const loadCachedConfig = createMemoizedPromiseRunner(() => loadWorkspaceConfigModule().then((mod) => mod.getConfig(cwd)));
2749
+ const loadCachedPatchOptions = createMemoizedPromiseRunner(() => loadPatchOptionsForWorkspace(cwd));
2750
+ const createCachedPatcher = createMemoizedPromiseRunner(async () => {
2751
+ return new TailwindcssPatcher(await loadCachedPatchOptions());
2752
+ });
2753
+ const loadPatchOptionsForContext = (overrides) => {
2754
+ if (overrides) return loadPatchOptionsForWorkspace(cwd, overrides);
2755
+ return loadCachedPatchOptions();
2756
+ };
2757
+ const createPatcherForContext = async (overrides) => {
2758
+ if (overrides) return new TailwindcssPatcher(await loadPatchOptionsForWorkspace(cwd, overrides));
2759
+ return createCachedPatcher();
2760
+ };
2761
+ return {
2762
+ cli,
2763
+ command,
2764
+ commandName,
2765
+ args,
2766
+ cwd,
2767
+ logger,
2768
+ loadConfig: loadCachedConfig,
2769
+ loadPatchOptions: loadPatchOptionsForContext,
2770
+ createPatcher: createPatcherForContext
2771
+ };
2772
+ }
2773
+ //#endregion
2774
+ //#region src/commands/command-runtime.ts
2775
+ function runWithCommandHandler(cli, command, commandName, args, handler, defaultHandler) {
2776
+ const context = createTailwindcssPatchCommandContext(cli, command, commandName, args, resolveCommandCwd(args.cwd));
2777
+ const runDefault = createMemoizedPromiseRunner(() => defaultHandler(context));
2778
+ if (!handler) return runDefault();
2779
+ return handler(context, runDefault);
2780
+ }
2781
+ //#endregion
2782
+ //#region src/commands/basic-handlers.ts
2783
+ const DEFAULT_CONFIG_NAME = "tailwindcss-mangle";
2784
+ async function installCommandDefaultHandler(_ctx) {
2785
+ await (await _ctx.createPatcher()).patch();
2786
+ logger.success("Tailwind CSS runtime patched successfully.");
2787
+ }
2788
+ async function extractCommandDefaultHandler(ctx) {
2789
+ const { args } = ctx;
2790
+ const overrides = {};
2791
+ let hasOverrides = false;
2792
+ if (args.output || args.format) {
2793
+ overrides.extract = {
2794
+ ...args.output === void 0 ? {} : { file: args.output },
2795
+ ...args.format === void 0 ? {} : { format: args.format }
2796
+ };
2797
+ hasOverrides = true;
2798
+ }
2799
+ if (args.css) {
2800
+ overrides.tailwindcss = { v4: { cssEntries: [args.css] } };
2801
+ hasOverrides = true;
2802
+ }
2803
+ const patcher = await ctx.createPatcher(hasOverrides ? overrides : void 0);
2804
+ const extractOptions = args.write === void 0 ? {} : { write: args.write };
2805
+ const result = await patcher.extract(extractOptions);
2806
+ if (result.filename) logger.success(`Collected ${result.classList.length} classes → ${result.filename}`);
2807
+ else logger.success(`Collected ${result.classList.length} classes.`);
2808
+ return result;
2809
+ }
2810
+ async function tokensCommandDefaultHandler(ctx) {
2811
+ const { args } = ctx;
2812
+ const report = await (await ctx.createPatcher()).collectContentTokens();
2813
+ const shouldWrite = args.write ?? true;
2814
+ let format = args.format ?? "json";
2815
+ if (!TOKEN_FORMATS.includes(format)) format = "json";
2816
+ const targetFile = args.output ?? ".tw-patch/tw-token-report.json";
2817
+ const groupKey = args.groupKey === "absolute" ? "absolute" : "relative";
2818
+ const buildGrouped = () => groupTokensByFile(report, {
2819
+ key: groupKey,
2820
+ stripAbsolutePaths: groupKey !== "absolute"
2821
+ });
2822
+ const grouped = format === "grouped-json" ? buildGrouped() : null;
2823
+ const resolveGrouped = () => grouped ?? buildGrouped();
2824
+ if (shouldWrite) {
2825
+ const target = path.resolve(targetFile);
2826
+ await fs.ensureDir(path.dirname(target));
2827
+ if (format === "json") await fs.writeJSON(target, report, { spaces: 2 });
2828
+ else if (format === "grouped-json") await fs.writeJSON(target, resolveGrouped(), { spaces: 2 });
2829
+ else {
2830
+ const lines = report.entries.map(formatTokenLine);
2831
+ await fs.writeFile(target, `${lines.join("\n")}\n`, "utf8");
2832
+ }
2833
+ logger.success(`Collected ${report.entries.length} tokens (${format}) → ${target.replace(process.cwd(), ".")}`);
2834
+ } else {
2835
+ logger.success(`Collected ${report.entries.length} tokens from ${report.filesScanned} files.`);
2836
+ if (format === "lines") {
2837
+ const preview = report.entries.slice(0, 5).map(formatTokenLine).join("\n");
2838
+ if (preview) {
2839
+ logger.log("");
2840
+ logger.info(preview);
2841
+ if (report.entries.length > 5) logger.info(`…and ${report.entries.length - 5} more.`);
2842
+ }
2843
+ } else if (format === "grouped-json") {
2844
+ const { preview, moreFiles } = formatGroupedPreview(resolveGrouped());
2845
+ if (preview) {
2846
+ logger.log("");
2847
+ logger.info(preview);
2848
+ if (moreFiles > 0) logger.info(`…and ${moreFiles} more files.`);
2849
+ }
2850
+ } else {
2851
+ const previewEntries = report.entries.slice(0, 3);
2852
+ if (previewEntries.length) {
2853
+ logger.log("");
2854
+ logger.info(JSON.stringify(previewEntries, null, 2));
2855
+ }
2856
+ }
2857
+ }
2858
+ if (report.skippedFiles.length) {
2859
+ logger.warn("Skipped files:");
2860
+ for (const skipped of report.skippedFiles) logger.warn(` • ${skipped.file} (${skipped.reason})`);
2861
+ }
2862
+ return report;
2863
+ }
2864
+ async function initCommandDefaultHandler(ctx) {
2865
+ const configModule = await loadWorkspaceConfigModule();
2866
+ await configModule.initConfig(ctx.cwd);
2867
+ const configName = configModule.CONFIG_NAME || DEFAULT_CONFIG_NAME;
2868
+ logger.success(`✨ ${configName}.config.ts initialized!`);
2869
+ }
2870
+ //#endregion
2516
2871
  //#region src/commands/migration-report.ts
2517
2872
  const MIGRATION_REPORT_KIND = "tw-patch-migrate-report";
2518
2873
  const MIGRATION_REPORT_SCHEMA_VERSION = 1;
@@ -3059,17 +3414,230 @@ async function restoreConfigFiles(options) {
3059
3414
  };
3060
3415
  }
3061
3416
  //#endregion
3062
- //#region src/commands/types.ts
3063
- const tailwindcssPatchCommands = [
3064
- "install",
3065
- "extract",
3066
- "tokens",
3067
- "init",
3068
- "migrate",
3069
- "restore",
3070
- "validate",
3071
- "status"
3072
- ];
3417
+ //#region src/commands/migration-args.ts
3418
+ function normalizePatternArgs(value) {
3419
+ if (!value) return;
3420
+ const values = (Array.isArray(value) ? value : [value]).flatMap((item) => item.split(",")).map((item) => item.trim()).filter(Boolean);
3421
+ return values.length > 0 ? values : void 0;
3422
+ }
3423
+ function parseMaxDepth(value) {
3424
+ if (value === void 0) return {
3425
+ maxDepth: void 0,
3426
+ hasInvalidMaxDepth: false
3427
+ };
3428
+ const parsed = Number(value);
3429
+ if (!Number.isFinite(parsed) || parsed < 0) return {
3430
+ maxDepth: void 0,
3431
+ hasInvalidMaxDepth: true
3432
+ };
3433
+ return {
3434
+ maxDepth: Math.floor(parsed),
3435
+ hasInvalidMaxDepth: false
3436
+ };
3437
+ }
3438
+ function resolveMigrateCommandArgs(args) {
3439
+ const include = normalizePatternArgs(args.include);
3440
+ const exclude = normalizePatternArgs(args.exclude);
3441
+ const { maxDepth, hasInvalidMaxDepth } = parseMaxDepth(args.maxDepth);
3442
+ const checkMode = args.check ?? false;
3443
+ return {
3444
+ include,
3445
+ exclude,
3446
+ maxDepth,
3447
+ checkMode,
3448
+ dryRun: args.dryRun ?? checkMode,
3449
+ hasInvalidMaxDepth
3450
+ };
3451
+ }
3452
+ function resolveRestoreCommandArgs(args) {
3453
+ return {
3454
+ reportFile: args.reportFile ?? ".tw-patch/migrate-report.json",
3455
+ dryRun: args.dryRun ?? false,
3456
+ strict: args.strict ?? false
3457
+ };
3458
+ }
3459
+ function resolveValidateCommandArgs(args) {
3460
+ return {
3461
+ reportFile: args.reportFile ?? ".tw-patch/migrate-report.json",
3462
+ strict: args.strict ?? false
3463
+ };
3464
+ }
3465
+ //#endregion
3466
+ //#region src/commands/migration-output.ts
3467
+ function formatPathForLog(file) {
3468
+ return file.replace(process.cwd(), ".");
3469
+ }
3470
+ function createMigrationCheckFailureError(changedFiles) {
3471
+ return /* @__PURE__ */ new Error(`Migration check failed: ${changedFiles} file(s) still need migration.`);
3472
+ }
3473
+ async function writeMigrationReportFile(cwd, reportFile, report) {
3474
+ const reportPath = path.resolve(cwd, reportFile);
3475
+ await fs.ensureDir(path.dirname(reportPath));
3476
+ await fs.writeJSON(reportPath, report, { spaces: 2 });
3477
+ logger.info(`Migration report written: ${formatPathForLog(reportPath)}`);
3478
+ }
3479
+ function logMigrationReportAsJson(report) {
3480
+ logger.log(JSON.stringify(report, null, 2));
3481
+ }
3482
+ function logNoMigrationConfigFilesWarning() {
3483
+ logger.warn("No config files found for migration.");
3484
+ }
3485
+ function logMigrationEntries(report, dryRun) {
3486
+ for (const entry of report.entries) {
3487
+ const fileLabel = formatPathForLog(entry.file);
3488
+ if (!entry.changed) {
3489
+ logger.info(`No changes: ${fileLabel}`);
3490
+ continue;
3491
+ }
3492
+ if (dryRun) logger.info(`[dry-run] ${fileLabel}`);
3493
+ else logger.success(`Migrated: ${fileLabel}`);
3494
+ for (const change of entry.changes) logger.info(` - ${change}`);
3495
+ if (entry.backupFile) logger.info(` - backup: ${formatPathForLog(entry.backupFile)}`);
3496
+ }
3497
+ }
3498
+ function logMigrationSummary(report) {
3499
+ logger.info(`Migration summary: scanned=${report.scannedFiles}, changed=${report.changedFiles}, written=${report.writtenFiles}, backups=${report.backupsWritten}, missing=${report.missingFiles}, unchanged=${report.unchangedFiles}`);
3500
+ }
3501
+ function logRestoreResultAsJson(result) {
3502
+ logger.log(JSON.stringify(result, null, 2));
3503
+ }
3504
+ function logRestoreSummary(result) {
3505
+ logger.info(`Restore summary: scanned=${result.scannedEntries}, restorable=${result.restorableEntries}, restored=${result.restoredFiles}, missingBackups=${result.missingBackups}, skipped=${result.skippedEntries}`);
3506
+ if (result.restored.length > 0) {
3507
+ const preview = result.restored.slice(0, 5);
3508
+ for (const file of preview) logger.info(` - ${formatPathForLog(file)}`);
3509
+ if (result.restored.length > preview.length) logger.info(` ...and ${result.restored.length - preview.length} more`);
3510
+ }
3511
+ }
3512
+ function logValidateSuccessAsJson(result) {
3513
+ const payload = {
3514
+ ok: true,
3515
+ ...result
3516
+ };
3517
+ logger.log(JSON.stringify(payload, null, 2));
3518
+ }
3519
+ function logValidateSuccessSummary(result) {
3520
+ logger.success(`Migration report validated: scanned=${result.scannedEntries}, restorable=${result.restorableEntries}, missingBackups=${result.missingBackups}, skipped=${result.skippedEntries}`);
3521
+ if (result.reportKind || result.reportSchemaVersion !== void 0) {
3522
+ const kind = result.reportKind ?? "unknown";
3523
+ const schema = result.reportSchemaVersion === void 0 ? "unknown" : String(result.reportSchemaVersion);
3524
+ logger.info(` metadata: kind=${kind}, schema=${schema}`);
3525
+ }
3526
+ }
3527
+ function logValidateFailureAsJson(summary) {
3528
+ const payload = {
3529
+ ok: false,
3530
+ reason: summary.reason,
3531
+ exitCode: summary.exitCode,
3532
+ message: summary.message
3533
+ };
3534
+ logger.log(JSON.stringify(payload, null, 2));
3535
+ }
3536
+ function logValidateFailureSummary(summary) {
3537
+ logger.error(`Validation failed [${summary.reason}] (exit ${summary.exitCode}): ${summary.message}`);
3538
+ }
3539
+ //#endregion
3540
+ //#region src/commands/migrate-handler.ts
3541
+ async function migrateCommandDefaultHandler(ctx) {
3542
+ const { args } = ctx;
3543
+ const { include, exclude, maxDepth, checkMode, dryRun, hasInvalidMaxDepth } = resolveMigrateCommandArgs(args);
3544
+ if (args.workspace && hasInvalidMaxDepth) logger.warn(`Invalid --max-depth value "${String(args.maxDepth)}", fallback to default depth.`);
3545
+ const report = await migrateConfigFiles({
3546
+ cwd: ctx.cwd,
3547
+ dryRun,
3548
+ ...args.config ? { files: [args.config] } : {},
3549
+ ...args.workspace ? { workspace: true } : {},
3550
+ ...args.workspace && maxDepth !== void 0 ? { maxDepth } : {},
3551
+ ...args.backupDir ? { backupDir: args.backupDir } : {},
3552
+ ...include ? { include } : {},
3553
+ ...exclude ? { exclude } : {}
3554
+ });
3555
+ if (args.reportFile) await writeMigrationReportFile(ctx.cwd, args.reportFile, report);
3556
+ if (args.json) {
3557
+ logMigrationReportAsJson(report);
3558
+ if (checkMode && report.changedFiles > 0) throw createMigrationCheckFailureError(report.changedFiles);
3559
+ if (report.scannedFiles === 0) logNoMigrationConfigFilesWarning();
3560
+ return report;
3561
+ }
3562
+ if (report.scannedFiles === 0) {
3563
+ logNoMigrationConfigFilesWarning();
3564
+ return report;
3565
+ }
3566
+ logMigrationEntries(report, dryRun);
3567
+ logMigrationSummary(report);
3568
+ if (checkMode && report.changedFiles > 0) throw createMigrationCheckFailureError(report.changedFiles);
3569
+ return report;
3570
+ }
3571
+ //#endregion
3572
+ //#region src/commands/restore-handler.ts
3573
+ async function restoreCommandDefaultHandler(ctx) {
3574
+ const { args } = ctx;
3575
+ const restoreArgs = resolveRestoreCommandArgs(args);
3576
+ const result = await restoreConfigFiles({
3577
+ cwd: ctx.cwd,
3578
+ reportFile: restoreArgs.reportFile,
3579
+ dryRun: restoreArgs.dryRun,
3580
+ strict: restoreArgs.strict
3581
+ });
3582
+ if (args.json) {
3583
+ logRestoreResultAsJson(result);
3584
+ return result;
3585
+ }
3586
+ logRestoreSummary(result);
3587
+ return result;
3588
+ }
3589
+ //#endregion
3590
+ //#region src/commands/status-output.ts
3591
+ function formatFilesHint(entry) {
3592
+ if (!entry.files.length) return "";
3593
+ return ` (${entry.files.join(", ")})`;
3594
+ }
3595
+ function formatPackageLabel(report) {
3596
+ return `${report.package.name ?? "tailwindcss"}@${report.package.version ?? "unknown"}`;
3597
+ }
3598
+ function partitionStatusEntries(report) {
3599
+ return {
3600
+ applied: report.entries.filter((entry) => entry.status === "applied"),
3601
+ pending: report.entries.filter((entry) => entry.status === "not-applied"),
3602
+ skipped: report.entries.filter((entry) => entry.status === "skipped" || entry.status === "unsupported")
3603
+ };
3604
+ }
3605
+ function logStatusReportAsJson(report) {
3606
+ logger.log(JSON.stringify(report, null, 2));
3607
+ }
3608
+ function logStatusReportSummary(report) {
3609
+ const { applied, pending, skipped } = partitionStatusEntries(report);
3610
+ logger.info(`Patch status for ${formatPackageLabel(report)} (v${report.majorVersion})`);
3611
+ if (applied.length) {
3612
+ logger.success("Applied:");
3613
+ applied.forEach((entry) => logger.success(` • ${entry.name}${formatFilesHint(entry)}`));
3614
+ }
3615
+ if (pending.length) {
3616
+ logger.warn("Needs attention:");
3617
+ pending.forEach((entry) => {
3618
+ const details = entry.reason ? ` - ${entry.reason}` : "";
3619
+ logger.warn(` • ${entry.name}${formatFilesHint(entry)}${details}`);
3620
+ });
3621
+ } else logger.success("All applicable patches are applied.");
3622
+ if (skipped.length) {
3623
+ logger.info("Skipped:");
3624
+ skipped.forEach((entry) => {
3625
+ const details = entry.reason ? ` - ${entry.reason}` : "";
3626
+ logger.info(` • ${entry.name}${details}`);
3627
+ });
3628
+ }
3629
+ }
3630
+ //#endregion
3631
+ //#region src/commands/status-handler.ts
3632
+ async function statusCommandDefaultHandler(ctx) {
3633
+ const report = await (await ctx.createPatcher()).getPatchStatus();
3634
+ if (ctx.args.json) {
3635
+ logStatusReportAsJson(report);
3636
+ return report;
3637
+ }
3638
+ logStatusReportSummary(report);
3639
+ return report;
3640
+ }
3073
3641
  //#endregion
3074
3642
  //#region src/commands/validate.ts
3075
3643
  const VALIDATE_EXIT_CODES = {
@@ -3131,4 +3699,79 @@ var ValidateCommandError = class extends Error {
3131
3699
  }
3132
3700
  };
3133
3701
  //#endregion
3134
- export { resolveProjectSourceFiles as C, CacheStore as D, normalizeOptions as E, logger as O, groupTokensByFile as S, loadWorkspaceConfigModule as T, extractRawCandidates as _, tailwindcssPatchCommands as a, extractSourceCandidatesWithPositions as b, MIGRATION_REPORT_KIND as c, getPatchStatusReport as d, runTailwindBuild as f, extractProjectCandidatesWithPositions as g, collectClassesFromTailwindV4 as h, classifyValidateError as i, MIGRATION_REPORT_SCHEMA_VERSION as l, collectClassesFromContexts as m, VALIDATE_FAILURE_REASONS as n, migrateConfigFiles as o, loadRuntimeContexts as p, ValidateCommandError as r, restoreConfigFiles as s, VALIDATE_EXIT_CODES as t, TailwindcssPatcher as u, extractRawCandidatesWithPositions as v, loadPatchOptionsForWorkspace as w, extractValidCandidates as x, extractSourceCandidates as y };
3702
+ //#region src/commands/validate-handler.ts
3703
+ async function validateCommandDefaultHandler(ctx) {
3704
+ const { args } = ctx;
3705
+ const validateArgs = resolveValidateCommandArgs(args);
3706
+ try {
3707
+ const result = await restoreConfigFiles({
3708
+ cwd: ctx.cwd,
3709
+ reportFile: validateArgs.reportFile,
3710
+ dryRun: true,
3711
+ strict: validateArgs.strict
3712
+ });
3713
+ if (args.json) {
3714
+ logValidateSuccessAsJson(result);
3715
+ return result;
3716
+ }
3717
+ logValidateSuccessSummary(result);
3718
+ return result;
3719
+ } catch (error) {
3720
+ const summary = classifyValidateError(error);
3721
+ if (args.json) logValidateFailureAsJson(summary);
3722
+ else logValidateFailureSummary(summary);
3723
+ throw new ValidateCommandError(summary, { cause: error });
3724
+ }
3725
+ }
3726
+ //#endregion
3727
+ //#region src/commands/default-handler-map.ts
3728
+ const defaultCommandHandlers = {
3729
+ install: installCommandDefaultHandler,
3730
+ extract: extractCommandDefaultHandler,
3731
+ tokens: tokensCommandDefaultHandler,
3732
+ init: initCommandDefaultHandler,
3733
+ migrate: migrateCommandDefaultHandler,
3734
+ restore: restoreCommandDefaultHandler,
3735
+ validate: validateCommandDefaultHandler,
3736
+ status: statusCommandDefaultHandler
3737
+ };
3738
+ //#endregion
3739
+ //#region src/commands/command-registrar.ts
3740
+ function registerTailwindcssPatchCommand(cli, commandName, options, prefix, defaultDefinitions) {
3741
+ const metadata = resolveCommandMetadata(commandName, options, prefix, defaultDefinitions);
3742
+ const command = cli.command(metadata.name, metadata.description);
3743
+ applyCommandOptions(command, metadata.optionDefs);
3744
+ command.action(async (args) => {
3745
+ const defaultHandler = defaultCommandHandlers[commandName];
3746
+ return runWithCommandHandler(cli, command, commandName, args, options.commandHandlers?.[commandName], defaultHandler);
3747
+ });
3748
+ metadata.aliases.forEach((alias) => command.alias(alias));
3749
+ }
3750
+ //#endregion
3751
+ //#region src/commands/types.ts
3752
+ const tailwindcssPatchCommands = [
3753
+ "install",
3754
+ "extract",
3755
+ "tokens",
3756
+ "init",
3757
+ "migrate",
3758
+ "restore",
3759
+ "validate",
3760
+ "status"
3761
+ ];
3762
+ //#endregion
3763
+ //#region src/commands/cli.ts
3764
+ function mountTailwindcssPatchCommands(cli, options = {}) {
3765
+ const prefix = options.commandPrefix ?? "";
3766
+ const selectedCommands = options.commands ?? tailwindcssPatchCommands;
3767
+ const defaultDefinitions = buildDefaultCommandDefinitions();
3768
+ for (const name of selectedCommands) registerTailwindcssPatchCommand(cli, name, options, prefix, defaultDefinitions);
3769
+ return cli;
3770
+ }
3771
+ function createTailwindcssPatchCli(options = {}) {
3772
+ const cli = cac(options.name ?? "tw-patch");
3773
+ mountTailwindcssPatchCommands(cli, options.mountOptions);
3774
+ return cli;
3775
+ }
3776
+ //#endregion
3777
+ export { groupTokensByFile as C, logger as D, CacheStore as E, extractValidCandidates as S, normalizeOptions as T, extractProjectCandidatesWithPositions as _, VALIDATE_FAILURE_REASONS as a, extractSourceCandidates as b, restoreConfigFiles as c, TailwindcssPatcher as d, getPatchStatusReport as f, collectClassesFromTailwindV4 as g, collectClassesFromContexts as h, VALIDATE_EXIT_CODES as i, MIGRATION_REPORT_KIND as l, loadRuntimeContexts as m, mountTailwindcssPatchCommands as n, ValidateCommandError as o, runTailwindBuild as p, tailwindcssPatchCommands as r, migrateConfigFiles as s, createTailwindcssPatchCli as t, MIGRATION_REPORT_SCHEMA_VERSION as u, extractRawCandidates as v, resolveProjectSourceFiles as w, extractSourceCandidatesWithPositions as x, extractRawCandidatesWithPositions as y };