tailwindcss-patch 8.7.2 → 8.7.4-alpha.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,3 +1,7 @@
1
+ import {
2
+ __dirname
3
+ } from "./chunk-A67ABH3M.mjs";
4
+
1
5
  // src/logger.ts
2
6
  import { createConsola } from "consola";
3
7
  var logger = createConsola();
@@ -16,7 +20,7 @@ import path from "pathe";
16
20
  // package.json
17
21
  var package_default = {
18
22
  name: "tailwindcss-patch",
19
- version: "8.7.2",
23
+ version: "8.7.4-alpha.0",
20
24
  description: "patch tailwindcss for exposing context and extract classes",
21
25
  author: "ice breaker <1324318532@qq.com>",
22
26
  license: "MIT",
@@ -37,17 +41,17 @@ var package_default = {
37
41
  ],
38
42
  exports: {
39
43
  ".": {
40
- types: "./src/index.ts",
41
- import: "./src/index.ts",
42
- require: "./src/index.ts"
44
+ types: "./dist/index.d.ts",
45
+ import: "./dist/index.mjs",
46
+ require: "./dist/index.js"
43
47
  },
44
48
  "./migration-report.schema.json": "./schema/migration-report.schema.json",
45
49
  "./restore-result.schema.json": "./schema/restore-result.schema.json",
46
50
  "./validate-result.schema.json": "./schema/validate-result.schema.json"
47
51
  },
48
- main: "./src/index.ts",
49
- module: "./src/index.ts",
50
- types: "./src/index.ts",
52
+ main: "./dist/index.js",
53
+ module: "./dist/index.mjs",
54
+ types: "./dist/index.d.ts",
51
55
  bin: {
52
56
  "tw-patch": "dev/bin.ts",
53
57
  "tailwindcss-patch": "dev/bin.ts"
@@ -62,6 +66,7 @@ var package_default = {
62
66
  build: "tsup",
63
67
  test: "vitest run",
64
68
  "test:dev": "vitest",
69
+ "bench:cold-start": "node --import tsx bench/cold-start.ts",
65
70
  patch: "tsx dev/bin.ts install",
66
71
  r0: "tsx dev/bin.ts extract",
67
72
  r1: "tsx dev/bin.ts extract --css index.css"
@@ -102,12 +107,12 @@ var package_default = {
102
107
  "@babel/types": "^7.29.0",
103
108
  "@tailwindcss-mangle/config": "workspace:*",
104
109
  "@tailwindcss/node": "^4.2.1",
105
- cac: "^6.7.14",
110
+ cac: "^7.0.0",
106
111
  consola: "^3.4.2",
107
- "fs-extra": "^11.3.3",
112
+ "fs-extra": "^11.3.4",
108
113
  "local-pkg": "^1.1.2",
109
114
  pathe: "^2.0.3",
110
- postcss: "^8.5.6",
115
+ postcss: "^8.5.8",
111
116
  semver: "^7.7.4",
112
117
  "tailwindcss-config": "^1.1.4"
113
118
  },
@@ -1507,230 +1512,10 @@ ${Date.now()}`, { flag: "wx" });
1507
1512
  }
1508
1513
  };
1509
1514
 
1510
- // src/extraction/candidate-extractor.ts
1511
- import { promises as fs3 } from "fs";
1515
+ // src/options/normalize.ts
1512
1516
  import process3 from "process";
1517
+ import fs3 from "fs-extra";
1513
1518
  import path2 from "pathe";
1514
- async function importNode() {
1515
- return import("@tailwindcss/node");
1516
- }
1517
- async function importOxide() {
1518
- return import("@tailwindcss/oxide");
1519
- }
1520
- async function loadDesignSystem(css, bases) {
1521
- const uniqueBases = Array.from(new Set(bases.filter(Boolean)));
1522
- if (uniqueBases.length === 0) {
1523
- throw new Error("No base directories provided for Tailwind CSS design system.");
1524
- }
1525
- const { __unstable__loadDesignSystem } = await importNode();
1526
- let lastError;
1527
- for (const base of uniqueBases) {
1528
- try {
1529
- return await __unstable__loadDesignSystem(css, { base });
1530
- } catch (error) {
1531
- lastError = error;
1532
- }
1533
- }
1534
- if (lastError instanceof Error) {
1535
- throw lastError;
1536
- }
1537
- throw new Error("Failed to load Tailwind CSS design system.");
1538
- }
1539
- async function extractRawCandidatesWithPositions(content, extension = "html") {
1540
- const { Scanner } = await importOxide();
1541
- const scanner = new Scanner({});
1542
- const result = scanner.getCandidatesWithPositions({ content, extension });
1543
- return result.map(({ candidate, position }) => ({
1544
- rawCandidate: candidate,
1545
- start: position,
1546
- end: position + candidate.length
1547
- }));
1548
- }
1549
- async function extractRawCandidates(sources) {
1550
- const { Scanner } = await importOxide();
1551
- const scanner = new Scanner(sources === void 0 ? {} : { sources });
1552
- return scanner.scan();
1553
- }
1554
- async function extractValidCandidates(options) {
1555
- const providedOptions = options ?? {};
1556
- const defaultCwd = providedOptions.cwd ?? process3.cwd();
1557
- const base = providedOptions.base ?? defaultCwd;
1558
- const baseFallbacks = providedOptions.baseFallbacks ?? [];
1559
- const css = providedOptions.css ?? '@import "tailwindcss";';
1560
- const sources = (providedOptions.sources ?? [
1561
- {
1562
- base: defaultCwd,
1563
- pattern: "**/*",
1564
- negated: false
1565
- }
1566
- ]).map((source) => ({
1567
- base: source.base ?? defaultCwd,
1568
- pattern: source.pattern,
1569
- negated: source.negated
1570
- }));
1571
- const designSystem = await loadDesignSystem(css, [base, ...baseFallbacks]);
1572
- const candidates = await extractRawCandidates(sources);
1573
- const parsedCandidates = candidates.filter(
1574
- (rawCandidate) => designSystem.parseCandidate(rawCandidate).length > 0
1575
- );
1576
- if (parsedCandidates.length === 0) {
1577
- return parsedCandidates;
1578
- }
1579
- const cssByCandidate = designSystem.candidatesToCss(parsedCandidates);
1580
- const validCandidates = [];
1581
- for (let index = 0; index < parsedCandidates.length; index++) {
1582
- const candidate = parsedCandidates[index];
1583
- if (candidate === void 0) {
1584
- continue;
1585
- }
1586
- const css2 = cssByCandidate[index];
1587
- if (typeof css2 === "string" && css2.trim().length > 0) {
1588
- validCandidates.push(candidate);
1589
- }
1590
- }
1591
- return validCandidates;
1592
- }
1593
- function normalizeSources(sources, cwd) {
1594
- const baseSources = sources?.length ? sources : [
1595
- {
1596
- base: cwd,
1597
- pattern: "**/*",
1598
- negated: false
1599
- }
1600
- ];
1601
- return baseSources.map((source) => ({
1602
- base: source.base ?? cwd,
1603
- pattern: source.pattern,
1604
- negated: source.negated
1605
- }));
1606
- }
1607
- function buildLineOffsets(content) {
1608
- const offsets = [0];
1609
- for (let i = 0; i < content.length; i++) {
1610
- if (content[i] === "\n") {
1611
- offsets.push(i + 1);
1612
- }
1613
- }
1614
- if (offsets[offsets.length - 1] !== content.length) {
1615
- offsets.push(content.length);
1616
- }
1617
- return offsets;
1618
- }
1619
- function resolveLineMeta(content, offsets, index) {
1620
- let low = 0;
1621
- let high = offsets.length - 1;
1622
- while (low <= high) {
1623
- const mid = Math.floor((low + high) / 2);
1624
- const start = offsets[mid];
1625
- if (start === void 0) {
1626
- break;
1627
- }
1628
- const nextStart = offsets[mid + 1] ?? content.length;
1629
- if (index < start) {
1630
- high = mid - 1;
1631
- continue;
1632
- }
1633
- if (index >= nextStart) {
1634
- low = mid + 1;
1635
- continue;
1636
- }
1637
- const line = mid + 1;
1638
- const column = index - start + 1;
1639
- const lineEnd = content.indexOf("\n", start);
1640
- const lineText = content.slice(start, lineEnd === -1 ? content.length : lineEnd);
1641
- return { line, column, lineText };
1642
- }
1643
- const lastStart = offsets[offsets.length - 2] ?? 0;
1644
- return {
1645
- line: offsets.length - 1,
1646
- column: index - lastStart + 1,
1647
- lineText: content.slice(lastStart)
1648
- };
1649
- }
1650
- function toExtension(filename) {
1651
- const ext = path2.extname(filename).replace(/^\./, "");
1652
- return ext || "txt";
1653
- }
1654
- function toRelativeFile(cwd, filename) {
1655
- const relative = path2.relative(cwd, filename);
1656
- return relative === "" ? path2.basename(filename) : relative;
1657
- }
1658
- async function extractProjectCandidatesWithPositions(options) {
1659
- const cwd = options?.cwd ? path2.resolve(options.cwd) : process3.cwd();
1660
- const normalizedSources = normalizeSources(options?.sources, cwd);
1661
- const { Scanner } = await importOxide();
1662
- const scanner = new Scanner({
1663
- sources: normalizedSources
1664
- });
1665
- const files = scanner.files ?? [];
1666
- const entries = [];
1667
- const skipped = [];
1668
- for (const file of files) {
1669
- let content;
1670
- try {
1671
- content = await fs3.readFile(file, "utf8");
1672
- } catch (error) {
1673
- skipped.push({
1674
- file,
1675
- reason: error instanceof Error ? error.message : "Unknown error"
1676
- });
1677
- continue;
1678
- }
1679
- const extension = toExtension(file);
1680
- const matches = scanner.getCandidatesWithPositions({
1681
- file,
1682
- content,
1683
- extension
1684
- });
1685
- if (!matches.length) {
1686
- continue;
1687
- }
1688
- const offsets = buildLineOffsets(content);
1689
- const relativeFile = toRelativeFile(cwd, file);
1690
- for (const match of matches) {
1691
- const info = resolveLineMeta(content, offsets, match.position);
1692
- entries.push({
1693
- rawCandidate: match.candidate,
1694
- file,
1695
- relativeFile,
1696
- extension,
1697
- start: match.position,
1698
- end: match.position + match.candidate.length,
1699
- length: match.candidate.length,
1700
- line: info.line,
1701
- column: info.column,
1702
- lineText: info.lineText
1703
- });
1704
- }
1705
- }
1706
- return {
1707
- entries,
1708
- filesScanned: files.length,
1709
- skippedFiles: skipped,
1710
- sources: normalizedSources
1711
- };
1712
- }
1713
- function groupTokensByFile(report, options) {
1714
- const key = options?.key ?? "relative";
1715
- const stripAbsolute = options?.stripAbsolutePaths ?? key !== "absolute";
1716
- return report.entries.reduce((acc, entry) => {
1717
- const bucketKey = key === "absolute" ? entry.file : entry.relativeFile;
1718
- if (!acc[bucketKey]) {
1719
- acc[bucketKey] = [];
1720
- }
1721
- const value = stripAbsolute ? {
1722
- ...entry,
1723
- file: entry.relativeFile
1724
- } : entry;
1725
- acc[bucketKey].push(value);
1726
- return acc;
1727
- }, {});
1728
- }
1729
-
1730
- // src/options/normalize.ts
1731
- import process4 from "process";
1732
- import fs4 from "fs-extra";
1733
- import path3 from "pathe";
1734
1519
  var hasWarnedDeprecatedOptions = false;
1735
1520
  var deprecatedOptionMapping = {
1736
1521
  cwd: "projectRoot",
@@ -1740,11 +1525,11 @@ var deprecatedOptionMapping = {
1740
1525
  output: "extract"
1741
1526
  };
1742
1527
  function resolveRealpathSafe(value) {
1743
- const resolved = path3.resolve(value);
1528
+ const resolved = path2.resolve(value);
1744
1529
  try {
1745
- return path3.normalize(fs4.realpathSync(resolved));
1530
+ return path2.normalize(fs3.realpathSync(resolved));
1746
1531
  } catch {
1747
- return path3.normalize(resolved);
1532
+ return path2.normalize(resolved);
1748
1533
  }
1749
1534
  }
1750
1535
  function toPrettyValue(value) {
@@ -1765,7 +1550,7 @@ function normalizeCacheDriver(driver) {
1765
1550
  function normalizeCacheOptions(cache, projectRoot) {
1766
1551
  let enabled = false;
1767
1552
  let cwd = resolveRealpathSafe(projectRoot);
1768
- let dir = path3.resolve(cwd, "node_modules/.cache", pkgName);
1553
+ let dir = path2.resolve(cwd, "node_modules/.cache", pkgName);
1769
1554
  let file = "class-cache.json";
1770
1555
  let strategy = "merge";
1771
1556
  let driver = "file";
@@ -1774,12 +1559,12 @@ function normalizeCacheOptions(cache, projectRoot) {
1774
1559
  } else if (typeof cache === "object" && cache) {
1775
1560
  enabled = cache.enabled ?? true;
1776
1561
  cwd = cache.cwd ? resolveRealpathSafe(cache.cwd) : cwd;
1777
- dir = cache.dir ? path3.resolve(cache.dir) : path3.resolve(cwd, "node_modules/.cache", pkgName);
1562
+ dir = cache.dir ? path2.resolve(cache.dir) : path2.resolve(cwd, "node_modules/.cache", pkgName);
1778
1563
  file = cache.file ?? file;
1779
1564
  strategy = cache.strategy ?? strategy;
1780
1565
  driver = normalizeCacheDriver(cache.driver);
1781
1566
  }
1782
- const filename = path3.resolve(dir, file);
1567
+ const filename = path2.resolve(dir, file);
1783
1568
  return {
1784
1569
  enabled,
1785
1570
  cwd,
@@ -1842,9 +1627,9 @@ function normalizeExtendLengthUnitsOptions(extend) {
1842
1627
  };
1843
1628
  }
1844
1629
  function normalizeTailwindV4Options(v4, fallbackBase) {
1845
- const configuredBase = v4?.base ? path3.resolve(v4.base) : void 0;
1630
+ const configuredBase = v4?.base ? path2.resolve(v4.base) : void 0;
1846
1631
  const base = configuredBase ?? fallbackBase;
1847
- const cssEntries = Array.isArray(v4?.cssEntries) ? v4.cssEntries.filter((entry) => Boolean(entry)).map((entry) => path3.resolve(entry)) : [];
1632
+ const cssEntries = Array.isArray(v4?.cssEntries) ? v4.cssEntries.filter((entry) => Boolean(entry)).map((entry) => path2.resolve(entry)) : [];
1848
1633
  const userSources = v4?.sources;
1849
1634
  const hasUserDefinedSources = Boolean(userSources?.length);
1850
1635
  const sources = hasUserDefinedSources ? userSources : [
@@ -1936,7 +1721,7 @@ function warnDeprecatedOptionsIfNeeded(options) {
1936
1721
  function normalizeOptions(options = {}) {
1937
1722
  warnDeprecatedOptionsIfNeeded(options);
1938
1723
  const resolved = resolveOptionSlices(options);
1939
- const projectRoot = resolveRealpathSafe(resolved.projectRoot ? path3.resolve(resolved.projectRoot) : process4.cwd());
1724
+ const projectRoot = resolveRealpathSafe(resolved.projectRoot ? path2.resolve(resolved.projectRoot) : process3.cwd());
1940
1725
  const overwrite = resolved.overwrite ?? true;
1941
1726
  const output = normalizeOutputOptions(resolved.extract);
1942
1727
  const cache = normalizeCacheOptions(options.cache, projectRoot);
@@ -1966,49 +1751,745 @@ function normalizeOptions(options = {}) {
1966
1751
  };
1967
1752
  }
1968
1753
 
1969
- // src/patching/status.ts
1970
- import * as t4 from "@babel/types";
1971
- import fs6 from "fs-extra";
1972
- import path5 from "pathe";
1973
-
1974
- // src/babel/index.ts
1975
- import _babelGenerate from "@babel/generator";
1976
- import _babelTraverse from "@babel/traverse";
1977
- import { parse, parseExpression } from "@babel/parser";
1978
- function _interopDefaultCompat(e) {
1979
- return e && typeof e === "object" && "default" in e ? e.default : e;
1980
- }
1981
- var generate = _interopDefaultCompat(_babelGenerate);
1982
- var traverse = _interopDefaultCompat(_babelTraverse);
1983
-
1984
- // src/patching/operations/export-context/postcss-v2.ts
1985
- import * as t from "@babel/types";
1986
- var IDENTIFIER_RE = /^[A-Z_$][\w$]*$/i;
1987
- function toIdentifierName(property) {
1988
- if (!property) {
1989
- return "contextRef";
1990
- }
1991
- const sanitized = property.replace(/[^\w$]/gu, "_");
1992
- if (/^\d/.test(sanitized)) {
1993
- return `_${sanitized}`;
1754
+ // src/options/legacy.ts
1755
+ function normalizeLegacyFeatures(patch) {
1756
+ const apply = patch?.applyPatches;
1757
+ const extend = apply?.extendLengthUnits;
1758
+ let extendOption = false;
1759
+ if (extend && typeof extend === "object") {
1760
+ extendOption = {
1761
+ ...extend,
1762
+ enabled: true
1763
+ };
1764
+ } else if (extend === true) {
1765
+ extendOption = {
1766
+ enabled: true,
1767
+ units: ["rpx"],
1768
+ ...patch?.overwrite === void 0 ? {} : { overwrite: patch.overwrite }
1769
+ };
1994
1770
  }
1995
- return sanitized || "contextRef";
1771
+ return {
1772
+ exposeContext: apply?.exportContext ?? true,
1773
+ extendLengthUnits: extendOption
1774
+ };
1996
1775
  }
1997
- function createExportsMember(property) {
1998
- if (IDENTIFIER_RE.test(property)) {
1999
- return t.memberExpression(t.identifier("exports"), t.identifier(property));
1776
+ function fromLegacyOptions(options) {
1777
+ if (!options) {
1778
+ return {};
2000
1779
  }
2001
- return t.memberExpression(t.identifier("exports"), t.stringLiteral(property), true);
2002
- }
2003
- function transformProcessTailwindFeaturesReturnContextV2(content) {
2004
- const ast = parse(content, {
2005
- sourceType: "unambiguous"
2006
- });
2007
- let hasPatched = false;
2008
- traverse(ast, {
2009
- FunctionDeclaration(path13) {
2010
- const node = path13.node;
2011
- if (node.id?.name !== "processTailwindFeatures" || node.body.body.length !== 1 || !t.isReturnStatement(node.body.body[0])) {
1780
+ const patch = options.patch;
1781
+ const features = normalizeLegacyFeatures(patch);
1782
+ const output = patch?.output;
1783
+ const tailwindConfig = patch?.tailwindcss;
1784
+ const tailwindVersion = tailwindConfig?.version;
1785
+ const tailwindV2 = tailwindConfig?.v2;
1786
+ const tailwindV3 = tailwindConfig?.v3;
1787
+ const tailwindV4 = tailwindConfig?.v4;
1788
+ const tailwindConfigPath = tailwindV3?.config ?? tailwindV2?.config;
1789
+ const tailwindCwd = tailwindV3?.cwd ?? tailwindV2?.cwd ?? patch?.cwd;
1790
+ const normalizedExtract = output ? {
1791
+ ...output.filename === void 0 ? {} : { file: output.filename },
1792
+ pretty: output.loose ? 2 : false,
1793
+ ...output.removeUniversalSelector === void 0 ? {} : { removeUniversalSelector: output.removeUniversalSelector }
1794
+ } : void 0;
1795
+ const normalizedTailwindcss = {
1796
+ ...patch?.packageName === void 0 ? {} : { packageName: patch.packageName },
1797
+ ...tailwindVersion === void 0 ? {} : { version: tailwindVersion },
1798
+ ...patch?.resolve === void 0 ? {} : { resolve: patch.resolve },
1799
+ ...tailwindConfigPath === void 0 ? {} : { config: tailwindConfigPath },
1800
+ ...tailwindCwd === void 0 ? {} : { cwd: tailwindCwd },
1801
+ ...tailwindV2 === void 0 ? {} : { v2: tailwindV2 },
1802
+ ...tailwindV3 === void 0 ? {} : { v3: tailwindV3 },
1803
+ ...tailwindV4 === void 0 ? {} : { v4: tailwindV4 }
1804
+ };
1805
+ const normalizedCache = typeof options.cache === "boolean" ? options.cache : options.cache ? {
1806
+ ...options.cache,
1807
+ enabled: options.cache.enabled ?? true
1808
+ } : void 0;
1809
+ const normalizedApply = {
1810
+ ...patch?.overwrite === void 0 ? {} : { overwrite: patch.overwrite },
1811
+ exposeContext: features.exposeContext,
1812
+ extendLengthUnits: features.extendLengthUnits
1813
+ };
1814
+ return {
1815
+ ...patch?.cwd === void 0 ? {} : { projectRoot: patch.cwd },
1816
+ ...patch?.filter === void 0 ? {} : { filter: patch.filter },
1817
+ ...normalizedCache === void 0 ? {} : { cache: normalizedCache },
1818
+ ...normalizedExtract === void 0 ? {} : { extract: normalizedExtract },
1819
+ ...Object.keys(normalizedTailwindcss).length === 0 ? {} : { tailwindcss: normalizedTailwindcss },
1820
+ apply: normalizedApply
1821
+ };
1822
+ }
1823
+ function fromUnifiedConfig(registry) {
1824
+ if (!registry) {
1825
+ return {};
1826
+ }
1827
+ const tailwind = registry.tailwindcss ?? registry.tailwind;
1828
+ const modernExtract = registry.extract;
1829
+ const legacyOutput = registry.output;
1830
+ const pretty = (() => {
1831
+ const value = modernExtract?.pretty ?? legacyOutput?.pretty;
1832
+ if (value === void 0) {
1833
+ return void 0;
1834
+ }
1835
+ if (typeof value === "boolean") {
1836
+ return value ? 2 : false;
1837
+ }
1838
+ return value;
1839
+ })();
1840
+ const removeUniversalSelector = modernExtract?.removeUniversalSelector ?? legacyOutput?.stripUniversalSelector;
1841
+ const outputFile = modernExtract?.file ?? legacyOutput?.file;
1842
+ const normalizedExtract = modernExtract || legacyOutput ? {
1843
+ ...modernExtract?.write === void 0 ? {} : { write: modernExtract.write },
1844
+ ...outputFile === void 0 ? {} : { file: outputFile },
1845
+ ...pretty === void 0 ? {} : { pretty },
1846
+ ...removeUniversalSelector === void 0 ? {} : { removeUniversalSelector },
1847
+ ...modernExtract?.format === void 0 ? {} : { format: modernExtract.format }
1848
+ } : void 0;
1849
+ const normalizedTailwindcss = tailwind ? {
1850
+ ...tailwind.version === void 0 ? {} : { version: tailwind.version },
1851
+ ...tailwind.packageName === void 0 ? tailwind.package === void 0 ? {} : { packageName: tailwind.package } : { packageName: tailwind.packageName },
1852
+ ...tailwind.resolve === void 0 ? {} : { resolve: tailwind.resolve },
1853
+ ...tailwind.config === void 0 ? {} : { config: tailwind.config },
1854
+ ...tailwind.cwd === void 0 ? {} : { cwd: tailwind.cwd },
1855
+ ...tailwind.v2 === void 0 ? tailwind.legacy === void 0 ? {} : { v2: tailwind.legacy } : { v2: tailwind.v2 },
1856
+ ...tailwind.v3 === void 0 ? tailwind.classic === void 0 ? {} : { v3: tailwind.classic } : { v3: tailwind.v3 },
1857
+ ...tailwind.v4 === void 0 ? tailwind.next === void 0 ? {} : { v4: tailwind.next } : { v4: tailwind.v4 }
1858
+ } : void 0;
1859
+ const normalizedApply = registry.apply ? {
1860
+ ...registry.apply.overwrite === void 0 ? {} : { overwrite: registry.apply.overwrite },
1861
+ ...registry.apply.exposeContext === void 0 ? {} : { exposeContext: registry.apply.exposeContext },
1862
+ ...registry.apply.extendLengthUnits === void 0 ? {} : { extendLengthUnits: registry.apply.extendLengthUnits }
1863
+ } : void 0;
1864
+ return {
1865
+ ...registry.projectRoot === void 0 ? {} : { projectRoot: registry.projectRoot },
1866
+ ...normalizedApply === void 0 ? {} : { apply: normalizedApply },
1867
+ ...registry.cache === void 0 ? {} : { cache: registry.cache },
1868
+ ...registry.filter === void 0 ? {} : { filter: registry.filter },
1869
+ ...normalizedExtract === void 0 ? {} : { extract: normalizedExtract },
1870
+ ...normalizedTailwindcss === void 0 ? {} : { tailwindcss: normalizedTailwindcss }
1871
+ };
1872
+ }
1873
+
1874
+ // src/config/workspace.ts
1875
+ import { pathToFileURL } from "url";
1876
+ import path3 from "pathe";
1877
+ var configModulePromise;
1878
+ var defuPromise;
1879
+ function isNodeError(error) {
1880
+ return !!error && typeof error === "object" && ("code" in error || "message" in error);
1881
+ }
1882
+ function isMissingConfigModuleError(error) {
1883
+ if (!isNodeError(error) || error.code !== "MODULE_NOT_FOUND") {
1884
+ return false;
1885
+ }
1886
+ const message = error.message ?? "";
1887
+ return message.includes("@tailwindcss-mangle/config");
1888
+ }
1889
+ function isMissingSharedModuleError(error) {
1890
+ if (!isNodeError(error) || error.code !== "MODULE_NOT_FOUND") {
1891
+ return false;
1892
+ }
1893
+ const message = error.message ?? "";
1894
+ return message.includes("@tailwindcss-mangle/shared");
1895
+ }
1896
+ async function loadWorkspaceConfigModule() {
1897
+ if (!configModulePromise) {
1898
+ configModulePromise = import("@tailwindcss-mangle/config").catch(async (error) => {
1899
+ if (!isMissingConfigModuleError(error)) {
1900
+ throw error;
1901
+ }
1902
+ const fallback = path3.resolve(__dirname, "../../../config/src/index.ts");
1903
+ return import(pathToFileURL(fallback).href);
1904
+ });
1905
+ }
1906
+ return configModulePromise;
1907
+ }
1908
+ async function loadWorkspaceDefu() {
1909
+ if (!defuPromise) {
1910
+ defuPromise = import("./dist-7UDSGIWH.mjs").then((mod) => mod.defu).catch(async (error) => {
1911
+ if (!isMissingSharedModuleError(error)) {
1912
+ throw error;
1913
+ }
1914
+ const fallback = path3.resolve(__dirname, "../../../shared/src/utils.ts");
1915
+ const mod = await import(pathToFileURL(fallback).href);
1916
+ return mod.defu;
1917
+ });
1918
+ }
1919
+ return defuPromise;
1920
+ }
1921
+ async function loadPatchOptionsForWorkspace(cwd, overrides) {
1922
+ const merge = await loadWorkspaceDefu();
1923
+ const configModule = await loadWorkspaceConfigModule();
1924
+ const { config } = await configModule.getConfig(cwd);
1925
+ const legacyConfig = config;
1926
+ const base = config?.registry ? fromUnifiedConfig(config.registry) : legacyConfig?.patch ? fromLegacyOptions({ patch: legacyConfig.patch }) : {};
1927
+ const merged = merge(overrides ?? {}, base);
1928
+ return merged;
1929
+ }
1930
+
1931
+ // src/extraction/candidate-extractor.ts
1932
+ import { promises as fs4 } from "fs";
1933
+ import process4 from "process";
1934
+ import path4 from "pathe";
1935
+ var nodeImportPromise;
1936
+ var oxideImportPromise;
1937
+ var designSystemPromiseCache = /* @__PURE__ */ new Map();
1938
+ var designSystemCandidateCache = /* @__PURE__ */ new Map();
1939
+ async function importNode() {
1940
+ return import("@tailwindcss/node");
1941
+ }
1942
+ async function importOxide() {
1943
+ return import("@tailwindcss/oxide");
1944
+ }
1945
+ function getNodeModule() {
1946
+ nodeImportPromise ??= importNode();
1947
+ return nodeImportPromise;
1948
+ }
1949
+ function getOxideModule() {
1950
+ oxideImportPromise ??= importOxide();
1951
+ return oxideImportPromise;
1952
+ }
1953
+ function createDesignSystemCacheKey(css, bases) {
1954
+ return JSON.stringify({
1955
+ css,
1956
+ bases: Array.from(new Set(bases.filter(Boolean)))
1957
+ });
1958
+ }
1959
+ async function loadDesignSystem(css, bases) {
1960
+ const uniqueBases = Array.from(new Set(bases.filter(Boolean)));
1961
+ if (uniqueBases.length === 0) {
1962
+ throw new Error("No base directories provided for Tailwind CSS design system.");
1963
+ }
1964
+ const cacheKey = createDesignSystemCacheKey(css, uniqueBases);
1965
+ const cached = designSystemPromiseCache.get(cacheKey);
1966
+ if (cached) {
1967
+ return cached;
1968
+ }
1969
+ const promise = (async () => {
1970
+ const { __unstable__loadDesignSystem } = await getNodeModule();
1971
+ let lastError;
1972
+ for (const base of uniqueBases) {
1973
+ try {
1974
+ return await __unstable__loadDesignSystem(css, { base });
1975
+ } catch (error) {
1976
+ lastError = error;
1977
+ }
1978
+ }
1979
+ if (lastError instanceof Error) {
1980
+ throw lastError;
1981
+ }
1982
+ throw new Error("Failed to load Tailwind CSS design system.");
1983
+ })();
1984
+ designSystemPromiseCache.set(cacheKey, promise);
1985
+ promise.catch(() => {
1986
+ if (designSystemPromiseCache.get(cacheKey) === promise) {
1987
+ designSystemPromiseCache.delete(cacheKey);
1988
+ designSystemCandidateCache.delete(cacheKey);
1989
+ }
1990
+ });
1991
+ return promise;
1992
+ }
1993
+ async function extractRawCandidatesWithPositions(content, extension = "html") {
1994
+ const { Scanner } = await getOxideModule();
1995
+ const scanner = new Scanner({});
1996
+ const result = scanner.getCandidatesWithPositions({ content, extension });
1997
+ return result.map(({ candidate, position }) => ({
1998
+ rawCandidate: candidate,
1999
+ start: position,
2000
+ end: position + candidate.length
2001
+ }));
2002
+ }
2003
+ async function extractRawCandidates(sources) {
2004
+ const { Scanner } = await getOxideModule();
2005
+ const scanner = new Scanner(sources === void 0 ? {} : { sources });
2006
+ return scanner.scan();
2007
+ }
2008
+ async function extractValidCandidates(options) {
2009
+ const providedOptions = options ?? {};
2010
+ const defaultCwd = providedOptions.cwd ?? process4.cwd();
2011
+ const base = providedOptions.base ?? defaultCwd;
2012
+ const baseFallbacks = providedOptions.baseFallbacks ?? [];
2013
+ const css = providedOptions.css ?? '@import "tailwindcss";';
2014
+ const sources = (providedOptions.sources ?? [
2015
+ {
2016
+ base: defaultCwd,
2017
+ pattern: "**/*",
2018
+ negated: false
2019
+ }
2020
+ ]).map((source) => ({
2021
+ base: source.base ?? defaultCwd,
2022
+ pattern: source.pattern,
2023
+ negated: source.negated
2024
+ }));
2025
+ const designSystemKey = createDesignSystemCacheKey(css, [base, ...baseFallbacks]);
2026
+ const designSystem = await loadDesignSystem(css, [base, ...baseFallbacks]);
2027
+ const candidateCache = designSystemCandidateCache.get(designSystemKey) ?? /* @__PURE__ */ new Map();
2028
+ designSystemCandidateCache.set(designSystemKey, candidateCache);
2029
+ const candidates = await extractRawCandidates(sources);
2030
+ const validCandidates = [];
2031
+ const uncachedCandidates = [];
2032
+ for (const rawCandidate of candidates) {
2033
+ const cached = candidateCache.get(rawCandidate);
2034
+ if (cached === true) {
2035
+ validCandidates.push(rawCandidate);
2036
+ continue;
2037
+ }
2038
+ if (cached === false) {
2039
+ continue;
2040
+ }
2041
+ if (designSystem.parseCandidate(rawCandidate).length > 0) {
2042
+ uncachedCandidates.push(rawCandidate);
2043
+ continue;
2044
+ }
2045
+ candidateCache.set(rawCandidate, false);
2046
+ }
2047
+ if (uncachedCandidates.length === 0) {
2048
+ return validCandidates;
2049
+ }
2050
+ const cssByCandidate = designSystem.candidatesToCss(uncachedCandidates);
2051
+ for (let index = 0; index < uncachedCandidates.length; index++) {
2052
+ const candidate = uncachedCandidates[index];
2053
+ if (candidate === void 0) {
2054
+ continue;
2055
+ }
2056
+ const candidateCss = cssByCandidate[index];
2057
+ const isValid = typeof candidateCss === "string" && candidateCss.trim().length > 0;
2058
+ candidateCache.set(candidate, isValid);
2059
+ if (!isValid) {
2060
+ continue;
2061
+ }
2062
+ validCandidates.push(candidate);
2063
+ }
2064
+ return validCandidates;
2065
+ }
2066
+ function normalizeSources(sources, cwd) {
2067
+ const baseSources = sources?.length ? sources : [
2068
+ {
2069
+ base: cwd,
2070
+ pattern: "**/*",
2071
+ negated: false
2072
+ }
2073
+ ];
2074
+ return baseSources.map((source) => ({
2075
+ base: source.base ?? cwd,
2076
+ pattern: source.pattern,
2077
+ negated: source.negated
2078
+ }));
2079
+ }
2080
+ function buildLineOffsets(content) {
2081
+ const offsets = [0];
2082
+ for (let i = 0; i < content.length; i++) {
2083
+ if (content[i] === "\n") {
2084
+ offsets.push(i + 1);
2085
+ }
2086
+ }
2087
+ if (offsets[offsets.length - 1] !== content.length) {
2088
+ offsets.push(content.length);
2089
+ }
2090
+ return offsets;
2091
+ }
2092
+ function resolveLineMeta(content, offsets, index) {
2093
+ let low = 0;
2094
+ let high = offsets.length - 1;
2095
+ while (low <= high) {
2096
+ const mid = Math.floor((low + high) / 2);
2097
+ const start = offsets[mid];
2098
+ if (start === void 0) {
2099
+ break;
2100
+ }
2101
+ const nextStart = offsets[mid + 1] ?? content.length;
2102
+ if (index < start) {
2103
+ high = mid - 1;
2104
+ continue;
2105
+ }
2106
+ if (index >= nextStart) {
2107
+ low = mid + 1;
2108
+ continue;
2109
+ }
2110
+ const line = mid + 1;
2111
+ const column = index - start + 1;
2112
+ const lineEnd = content.indexOf("\n", start);
2113
+ const lineText = content.slice(start, lineEnd === -1 ? content.length : lineEnd);
2114
+ return { line, column, lineText };
2115
+ }
2116
+ const lastStart = offsets[offsets.length - 2] ?? 0;
2117
+ return {
2118
+ line: offsets.length - 1,
2119
+ column: index - lastStart + 1,
2120
+ lineText: content.slice(lastStart)
2121
+ };
2122
+ }
2123
+ function toExtension(filename) {
2124
+ const ext = path4.extname(filename).replace(/^\./, "");
2125
+ return ext || "txt";
2126
+ }
2127
+ function toRelativeFile(cwd, filename) {
2128
+ const relative = path4.relative(cwd, filename);
2129
+ return relative === "" ? path4.basename(filename) : relative;
2130
+ }
2131
+ async function extractProjectCandidatesWithPositions(options) {
2132
+ const cwd = options?.cwd ? path4.resolve(options.cwd) : process4.cwd();
2133
+ const normalizedSources = normalizeSources(options?.sources, cwd);
2134
+ const { Scanner } = await getOxideModule();
2135
+ const scanner = new Scanner({
2136
+ sources: normalizedSources
2137
+ });
2138
+ const files = scanner.files ?? [];
2139
+ const entries = [];
2140
+ const skipped = [];
2141
+ for (const file of files) {
2142
+ let content;
2143
+ try {
2144
+ content = await fs4.readFile(file, "utf8");
2145
+ } catch (error) {
2146
+ skipped.push({
2147
+ file,
2148
+ reason: error instanceof Error ? error.message : "Unknown error"
2149
+ });
2150
+ continue;
2151
+ }
2152
+ const extension = toExtension(file);
2153
+ const matches = scanner.getCandidatesWithPositions({
2154
+ file,
2155
+ content,
2156
+ extension
2157
+ });
2158
+ if (!matches.length) {
2159
+ continue;
2160
+ }
2161
+ const offsets = buildLineOffsets(content);
2162
+ const relativeFile = toRelativeFile(cwd, file);
2163
+ for (const match of matches) {
2164
+ const info = resolveLineMeta(content, offsets, match.position);
2165
+ entries.push({
2166
+ rawCandidate: match.candidate,
2167
+ file,
2168
+ relativeFile,
2169
+ extension,
2170
+ start: match.position,
2171
+ end: match.position + match.candidate.length,
2172
+ length: match.candidate.length,
2173
+ line: info.line,
2174
+ column: info.column,
2175
+ lineText: info.lineText
2176
+ });
2177
+ }
2178
+ }
2179
+ return {
2180
+ entries,
2181
+ filesScanned: files.length,
2182
+ skippedFiles: skipped,
2183
+ sources: normalizedSources
2184
+ };
2185
+ }
2186
+ function groupTokensByFile(report, options) {
2187
+ const key = options?.key ?? "relative";
2188
+ const stripAbsolute = options?.stripAbsolutePaths ?? key !== "absolute";
2189
+ return report.entries.reduce((acc, entry) => {
2190
+ const bucketKey = key === "absolute" ? entry.file : entry.relativeFile;
2191
+ if (!acc[bucketKey]) {
2192
+ acc[bucketKey] = [];
2193
+ }
2194
+ const value = stripAbsolute ? {
2195
+ ...entry,
2196
+ file: entry.relativeFile
2197
+ } : entry;
2198
+ acc[bucketKey].push(value);
2199
+ return acc;
2200
+ }, {});
2201
+ }
2202
+
2203
+ // src/runtime/class-collector.ts
2204
+ import process5 from "process";
2205
+ import fs5 from "fs-extra";
2206
+ import path5 from "pathe";
2207
+
2208
+ // src/utils.ts
2209
+ function isObject(val) {
2210
+ return val !== null && typeof val === "object" && Array.isArray(val) === false;
2211
+ }
2212
+ function spliceChangesIntoString(str, changes) {
2213
+ if (!changes[0]) {
2214
+ return str;
2215
+ }
2216
+ changes.sort((a, b) => {
2217
+ return a.end - b.end || a.start - b.start;
2218
+ });
2219
+ let result = "";
2220
+ let previous = changes[0];
2221
+ result += str.slice(0, previous.start);
2222
+ result += previous.replacement;
2223
+ for (let i = 1; i < changes.length; ++i) {
2224
+ const change = changes[i];
2225
+ result += str.slice(previous.end, change.start);
2226
+ result += change.replacement;
2227
+ previous = change;
2228
+ }
2229
+ result += str.slice(previous.end);
2230
+ return result;
2231
+ }
2232
+
2233
+ // src/runtime/class-collector.ts
2234
+ function collectClassesFromContexts(contexts, filter) {
2235
+ const set = /* @__PURE__ */ new Set();
2236
+ for (const context of contexts) {
2237
+ if (!isObject(context) || !context.classCache) {
2238
+ continue;
2239
+ }
2240
+ for (const key of context.classCache.keys()) {
2241
+ const className = key.toString();
2242
+ if (filter(className)) {
2243
+ set.add(className);
2244
+ }
2245
+ }
2246
+ }
2247
+ return set;
2248
+ }
2249
+ async function collectClassesFromTailwindV4(options) {
2250
+ const set = /* @__PURE__ */ new Set();
2251
+ const v4Options = options.tailwind.v4;
2252
+ if (!v4Options) {
2253
+ return set;
2254
+ }
2255
+ const toAbsolute = (value) => {
2256
+ if (!value) {
2257
+ return void 0;
2258
+ }
2259
+ return path5.isAbsolute(value) ? value : path5.resolve(options.projectRoot, value);
2260
+ };
2261
+ const resolvedConfiguredBase = toAbsolute(v4Options.configuredBase);
2262
+ const resolvedDefaultBase = toAbsolute(v4Options.base) ?? process5.cwd();
2263
+ const resolveSources = (base) => {
2264
+ if (!v4Options.sources?.length) {
2265
+ return void 0;
2266
+ }
2267
+ return v4Options.sources.map((source) => ({
2268
+ base: source.base ?? base,
2269
+ pattern: source.pattern,
2270
+ negated: source.negated
2271
+ }));
2272
+ };
2273
+ if (v4Options.cssEntries.length > 0) {
2274
+ for (const entry of v4Options.cssEntries) {
2275
+ const filePath = path5.isAbsolute(entry) ? entry : path5.resolve(options.projectRoot, entry);
2276
+ if (!await fs5.pathExists(filePath)) {
2277
+ continue;
2278
+ }
2279
+ const css = await fs5.readFile(filePath, "utf8");
2280
+ const entryDir = path5.dirname(filePath);
2281
+ const designSystemBases = resolvedConfiguredBase && resolvedConfiguredBase !== entryDir ? [entryDir, resolvedConfiguredBase] : [entryDir];
2282
+ const sourcesBase = resolvedConfiguredBase ?? entryDir;
2283
+ const sources = resolveSources(sourcesBase);
2284
+ const firstBase = designSystemBases[0] ?? entryDir;
2285
+ const extractOptions = {
2286
+ cwd: options.projectRoot,
2287
+ base: firstBase,
2288
+ baseFallbacks: designSystemBases.slice(1),
2289
+ css,
2290
+ ...sources === void 0 ? {} : { sources }
2291
+ };
2292
+ const candidates = await extractValidCandidates(extractOptions);
2293
+ for (const candidate of candidates) {
2294
+ if (options.filter(candidate)) {
2295
+ set.add(candidate);
2296
+ }
2297
+ }
2298
+ }
2299
+ } else {
2300
+ const baseForCss = resolvedConfiguredBase ?? resolvedDefaultBase;
2301
+ const sources = resolveSources(baseForCss);
2302
+ const extractOptions = {
2303
+ cwd: options.projectRoot,
2304
+ base: baseForCss,
2305
+ ...v4Options.css === void 0 ? {} : { css: v4Options.css },
2306
+ ...sources === void 0 ? {} : { sources }
2307
+ };
2308
+ const candidates = await extractValidCandidates(extractOptions);
2309
+ for (const candidate of candidates) {
2310
+ if (options.filter(candidate)) {
2311
+ set.add(candidate);
2312
+ }
2313
+ }
2314
+ }
2315
+ return set;
2316
+ }
2317
+
2318
+ // src/runtime/context-registry.ts
2319
+ import { createRequire } from "module";
2320
+ import fs6 from "fs-extra";
2321
+ import path6 from "pathe";
2322
+ var require2 = createRequire(import.meta.url);
2323
+ function resolveRuntimeEntry(packageInfo, majorVersion) {
2324
+ const root = packageInfo.rootPath;
2325
+ if (majorVersion === 2) {
2326
+ const jitIndex = path6.join(root, "lib/jit/index.js");
2327
+ if (fs6.existsSync(jitIndex)) {
2328
+ return jitIndex;
2329
+ }
2330
+ } else if (majorVersion === 3) {
2331
+ const plugin = path6.join(root, "lib/plugin.js");
2332
+ const index = path6.join(root, "lib/index.js");
2333
+ if (fs6.existsSync(plugin)) {
2334
+ return plugin;
2335
+ }
2336
+ if (fs6.existsSync(index)) {
2337
+ return index;
2338
+ }
2339
+ }
2340
+ return void 0;
2341
+ }
2342
+ function loadRuntimeContexts(packageInfo, majorVersion, refProperty) {
2343
+ if (majorVersion === 4) {
2344
+ return [];
2345
+ }
2346
+ const entry = resolveRuntimeEntry(packageInfo, majorVersion);
2347
+ if (!entry) {
2348
+ return [];
2349
+ }
2350
+ const moduleExports = require2(entry);
2351
+ if (!moduleExports) {
2352
+ return [];
2353
+ }
2354
+ const ref = moduleExports[refProperty];
2355
+ if (!ref) {
2356
+ return [];
2357
+ }
2358
+ if (Array.isArray(ref)) {
2359
+ return ref;
2360
+ }
2361
+ if (typeof ref === "object" && Array.isArray(ref.value)) {
2362
+ return ref.value;
2363
+ }
2364
+ return [];
2365
+ }
2366
+
2367
+ // src/runtime/process-tailwindcss.ts
2368
+ import { createRequire as createRequire2 } from "module";
2369
+ import fs7 from "fs-extra";
2370
+ import path7 from "pathe";
2371
+ import postcss from "postcss";
2372
+ import { loadConfig } from "tailwindcss-config";
2373
+ var require3 = createRequire2(import.meta.url);
2374
+ function resolveModuleEntry(id) {
2375
+ return path7.isAbsolute(id) ? id : require3.resolve(id);
2376
+ }
2377
+ function resolvePackageRootFromEntry(entry) {
2378
+ let current = path7.dirname(entry);
2379
+ while (current && current !== path7.dirname(current)) {
2380
+ const packageJsonPath = path7.join(current, "package.json");
2381
+ if (fs7.pathExistsSync(packageJsonPath)) {
2382
+ return current;
2383
+ }
2384
+ current = path7.dirname(current);
2385
+ }
2386
+ return void 0;
2387
+ }
2388
+ function clearTailwindV3RuntimeState(pluginName) {
2389
+ try {
2390
+ const entry = resolveModuleEntry(pluginName);
2391
+ const root = resolvePackageRootFromEntry(entry);
2392
+ if (!root) {
2393
+ return;
2394
+ }
2395
+ const sharedStatePath = path7.join(root, "lib/lib/sharedState.js");
2396
+ if (!fs7.pathExistsSync(sharedStatePath)) {
2397
+ return;
2398
+ }
2399
+ const sharedState = require3.cache[sharedStatePath]?.exports;
2400
+ sharedState?.contextMap?.clear();
2401
+ sharedState?.configContextMap?.clear();
2402
+ sharedState?.contextSourcesMap?.clear();
2403
+ sharedState?.sourceHashMap?.clear();
2404
+ for (const candidate of ["lib/plugin.js", "lib/index.js"]) {
2405
+ const runtimeEntry = path7.join(root, candidate);
2406
+ if (!fs7.pathExistsSync(runtimeEntry)) {
2407
+ continue;
2408
+ }
2409
+ const runtimeModule = require3.cache[runtimeEntry]?.exports;
2410
+ runtimeModule?.contextRef?.value?.splice(0, runtimeModule.contextRef.value.length);
2411
+ break;
2412
+ }
2413
+ } catch {
2414
+ }
2415
+ }
2416
+ async function resolveConfigPath(options) {
2417
+ if (options.config && path7.isAbsolute(options.config)) {
2418
+ return options.config;
2419
+ }
2420
+ const result = await loadConfig({ cwd: options.cwd });
2421
+ if (!result) {
2422
+ throw new Error(`Unable to locate Tailwind CSS config from ${options.cwd}`);
2423
+ }
2424
+ return result.filepath;
2425
+ }
2426
+ async function runTailwindBuild(options) {
2427
+ const configPath = await resolveConfigPath(options);
2428
+ const pluginName = options.postcssPlugin ?? (options.majorVersion === 4 ? "@tailwindcss/postcss" : "tailwindcss");
2429
+ if (options.majorVersion === 3) {
2430
+ clearTailwindV3RuntimeState(pluginName);
2431
+ }
2432
+ if (options.majorVersion === 4) {
2433
+ return postcss([
2434
+ require3(pluginName)({
2435
+ config: configPath
2436
+ })
2437
+ ]).process("@import 'tailwindcss';", {
2438
+ from: void 0
2439
+ });
2440
+ }
2441
+ return postcss([
2442
+ require3(pluginName)({
2443
+ config: configPath
2444
+ })
2445
+ ]).process("@tailwind base;@tailwind components;@tailwind utilities;", {
2446
+ from: void 0
2447
+ });
2448
+ }
2449
+
2450
+ // src/patching/status.ts
2451
+ import * as t4 from "@babel/types";
2452
+ import fs9 from "fs-extra";
2453
+ import path9 from "pathe";
2454
+
2455
+ // src/babel/index.ts
2456
+ import _babelGenerate from "@babel/generator";
2457
+ import _babelTraverse from "@babel/traverse";
2458
+ import { parse, parseExpression } from "@babel/parser";
2459
+ function _interopDefaultCompat(e) {
2460
+ return e && typeof e === "object" && "default" in e ? e.default : e;
2461
+ }
2462
+ var generate = _interopDefaultCompat(_babelGenerate);
2463
+ var traverse = _interopDefaultCompat(_babelTraverse);
2464
+
2465
+ // src/patching/operations/export-context/postcss-v2.ts
2466
+ import * as t from "@babel/types";
2467
+ var IDENTIFIER_RE = /^[A-Z_$][\w$]*$/i;
2468
+ function toIdentifierName(property) {
2469
+ if (!property) {
2470
+ return "contextRef";
2471
+ }
2472
+ const sanitized = property.replace(/[^\w$]/gu, "_");
2473
+ if (/^\d/.test(sanitized)) {
2474
+ return `_${sanitized}`;
2475
+ }
2476
+ return sanitized || "contextRef";
2477
+ }
2478
+ function createExportsMember(property) {
2479
+ if (IDENTIFIER_RE.test(property)) {
2480
+ return t.memberExpression(t.identifier("exports"), t.identifier(property));
2481
+ }
2482
+ return t.memberExpression(t.identifier("exports"), t.stringLiteral(property), true);
2483
+ }
2484
+ function transformProcessTailwindFeaturesReturnContextV2(content) {
2485
+ const ast = parse(content, {
2486
+ sourceType: "unambiguous"
2487
+ });
2488
+ let hasPatched = false;
2489
+ traverse(ast, {
2490
+ FunctionDeclaration(path18) {
2491
+ const node = path18.node;
2492
+ if (node.id?.name !== "processTailwindFeatures" || node.body.body.length !== 1 || !t.isReturnStatement(node.body.body[0])) {
2012
2493
  return;
2013
2494
  }
2014
2495
  const returnStatement3 = node.body.body[0];
@@ -2038,8 +2519,8 @@ function transformPostcssPluginV2(content, options) {
2038
2519
  const ast = parse(content);
2039
2520
  let hasPatched = false;
2040
2521
  traverse(ast, {
2041
- Program(path13) {
2042
- const program = path13.node;
2522
+ Program(path18) {
2523
+ const program = path18.node;
2043
2524
  const index = program.body.findIndex((statement) => {
2044
2525
  return t.isFunctionDeclaration(statement) && statement.id?.name === "_default";
2045
2526
  });
@@ -2073,11 +2554,11 @@ function transformPostcssPluginV2(content, options) {
2073
2554
  );
2074
2555
  }
2075
2556
  },
2076
- FunctionDeclaration(path13) {
2557
+ FunctionDeclaration(path18) {
2077
2558
  if (hasPatched) {
2078
2559
  return;
2079
2560
  }
2080
- const fn = path13.node;
2561
+ const fn = path18.node;
2081
2562
  if (fn.id?.name !== "_default") {
2082
2563
  return;
2083
2564
  }
@@ -2166,8 +2647,8 @@ function transformProcessTailwindFeaturesReturnContext(content) {
2166
2647
  const ast = parse(content);
2167
2648
  let hasPatched = false;
2168
2649
  traverse(ast, {
2169
- FunctionDeclaration(path13) {
2170
- const node = path13.node;
2650
+ FunctionDeclaration(path18) {
2651
+ const node = path18.node;
2171
2652
  if (node.id?.name !== "processTailwindFeatures" || node.body.body.length !== 1) {
2172
2653
  return;
2173
2654
  }
@@ -2199,8 +2680,8 @@ function transformPostcssPlugin(content, { refProperty }) {
2199
2680
  const valueMember = t2.memberExpression(refIdentifier, t2.identifier("value"));
2200
2681
  let hasPatched = false;
2201
2682
  traverse(ast, {
2202
- Program(path13) {
2203
- const program = path13.node;
2683
+ Program(path18) {
2684
+ const program = path18.node;
2204
2685
  const index = program.body.findIndex((statement) => {
2205
2686
  return t2.isExpressionStatement(statement) && t2.isAssignmentExpression(statement.expression) && t2.isMemberExpression(statement.expression.left) && t2.isFunctionExpression(statement.expression.right) && statement.expression.right.id?.name === "tailwindcss";
2206
2687
  });
@@ -2238,11 +2719,11 @@ function transformPostcssPlugin(content, { refProperty }) {
2238
2719
  );
2239
2720
  }
2240
2721
  },
2241
- FunctionExpression(path13) {
2722
+ FunctionExpression(path18) {
2242
2723
  if (hasPatched) {
2243
2724
  return;
2244
2725
  }
2245
- const fn = path13.node;
2726
+ const fn = path18.node;
2246
2727
  if (fn.id?.name !== "tailwindcss" || fn.body.body.length !== 1) {
2247
2728
  return;
2248
2729
  }
@@ -2313,56 +2794,29 @@ function transformPostcssPlugin(content, { refProperty }) {
2313
2794
 
2314
2795
  // src/patching/operations/extend-length-units.ts
2315
2796
  import * as t3 from "@babel/types";
2316
- import fs5 from "fs-extra";
2317
- import path4 from "pathe";
2318
-
2319
- // src/utils.ts
2320
- function isObject(val) {
2321
- return val !== null && typeof val === "object" && Array.isArray(val) === false;
2322
- }
2323
- function spliceChangesIntoString(str, changes) {
2324
- if (!changes[0]) {
2325
- return str;
2326
- }
2327
- changes.sort((a, b) => {
2328
- return a.end - b.end || a.start - b.start;
2329
- });
2330
- let result = "";
2331
- let previous = changes[0];
2332
- result += str.slice(0, previous.start);
2333
- result += previous.replacement;
2334
- for (let i = 1; i < changes.length; ++i) {
2335
- const change = changes[i];
2336
- result += str.slice(previous.end, change.start);
2337
- result += change.replacement;
2338
- previous = change;
2339
- }
2340
- result += str.slice(previous.end);
2341
- return result;
2342
- }
2343
-
2344
- // src/patching/operations/extend-length-units.ts
2797
+ import fs8 from "fs-extra";
2798
+ import path8 from "pathe";
2345
2799
  function updateLengthUnitsArray(content, options) {
2346
2800
  const { variableName = "lengthUnits", units } = options;
2347
2801
  const ast = parse(content);
2348
2802
  let arrayRef;
2349
2803
  let changed = false;
2350
2804
  traverse(ast, {
2351
- Identifier(path13) {
2352
- if (path13.node.name === variableName && t3.isVariableDeclarator(path13.parent) && t3.isArrayExpression(path13.parent.init)) {
2353
- arrayRef = path13.parent.init;
2805
+ Identifier(path18) {
2806
+ if (path18.node.name === variableName && t3.isVariableDeclarator(path18.parent) && t3.isArrayExpression(path18.parent.init)) {
2807
+ arrayRef = path18.parent.init;
2354
2808
  const existing = new Set(
2355
- path13.parent.init.elements.map((element) => t3.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
2809
+ path18.parent.init.elements.map((element) => t3.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
2356
2810
  );
2357
2811
  for (const unit of units) {
2358
2812
  if (!existing.has(unit)) {
2359
- path13.parent.init.elements = path13.parent.init.elements.map((element) => {
2813
+ path18.parent.init.elements = path18.parent.init.elements.map((element) => {
2360
2814
  if (t3.isStringLiteral(element)) {
2361
2815
  return t3.stringLiteral(element.value);
2362
2816
  }
2363
2817
  return element;
2364
2818
  });
2365
- path13.parent.init.elements.push(t3.stringLiteral(unit));
2819
+ path18.parent.init.elements.push(t3.stringLiteral(unit));
2366
2820
  changed = true;
2367
2821
  }
2368
2822
  }
@@ -2383,12 +2837,12 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
2383
2837
  lengthUnitsFilePath: options.lengthUnitsFilePath ?? "lib/util/dataTypes.js",
2384
2838
  variableName: options.variableName ?? "lengthUnits"
2385
2839
  };
2386
- const dataTypesFilePath = path4.resolve(rootDir, opts.lengthUnitsFilePath);
2387
- const exists = fs5.existsSync(dataTypesFilePath);
2840
+ const dataTypesFilePath = path8.resolve(rootDir, opts.lengthUnitsFilePath);
2841
+ const exists = fs8.existsSync(dataTypesFilePath);
2388
2842
  if (!exists) {
2389
2843
  return { changed: false, code: void 0 };
2390
2844
  }
2391
- const content = fs5.readFileSync(dataTypesFilePath, "utf8");
2845
+ const content = fs8.readFileSync(dataTypesFilePath, "utf8");
2392
2846
  const { arrayRef, changed } = updateLengthUnitsArray(content, opts);
2393
2847
  if (!arrayRef || !changed) {
2394
2848
  return { changed: false, code: void 0 };
@@ -2399,8 +2853,8 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
2399
2853
  if (arrayRef.start != null && arrayRef.end != null) {
2400
2854
  const nextCode = `${content.slice(0, arrayRef.start)}${code}${content.slice(arrayRef.end)}`;
2401
2855
  if (opts.overwrite) {
2402
- const target = opts.destPath ? path4.resolve(opts.destPath) : dataTypesFilePath;
2403
- fs5.writeFileSync(target, nextCode, "utf8");
2856
+ const target = opts.destPath ? path8.resolve(opts.destPath) : dataTypesFilePath;
2857
+ fs8.writeFileSync(target, nextCode, "utf8");
2404
2858
  logger_default.success("Patched Tailwind CSS length unit list (v3).");
2405
2859
  }
2406
2860
  return {
@@ -2418,16 +2872,16 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
2418
2872
  return { files: [], changed: false };
2419
2873
  }
2420
2874
  const opts = { ...options };
2421
- const distDir = path4.resolve(rootDir, "dist");
2422
- if (!fs5.existsSync(distDir)) {
2875
+ const distDir = path8.resolve(rootDir, "dist");
2876
+ if (!fs8.existsSync(distDir)) {
2423
2877
  return { files: [], changed: false };
2424
2878
  }
2425
- const entries = fs5.readdirSync(distDir);
2879
+ const entries = fs8.readdirSync(distDir);
2426
2880
  const chunkNames = entries.filter((entry) => entry.endsWith(".js") || entry.endsWith(".mjs"));
2427
2881
  const pattern = /\[\s*["']cm["'],\s*["']mm["'],[\w,"']+\]/;
2428
2882
  const candidates = chunkNames.map((chunkName) => {
2429
- const file = path4.join(distDir, chunkName);
2430
- const code = fs5.readFileSync(file, "utf8");
2883
+ const file = path8.join(distDir, chunkName);
2884
+ const code = fs8.readFileSync(file, "utf8");
2431
2885
  const match = pattern.exec(code);
2432
2886
  if (!match) {
2433
2887
  return null;
@@ -2443,13 +2897,13 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
2443
2897
  const { code, file, match } = item;
2444
2898
  const ast = parse(match[0], { sourceType: "unambiguous" });
2445
2899
  traverse(ast, {
2446
- ArrayExpression(path13) {
2900
+ ArrayExpression(path18) {
2447
2901
  for (const unit of opts.units) {
2448
- if (path13.node.elements.some((element) => t3.isStringLiteral(element) && element.value === unit)) {
2902
+ if (path18.node.elements.some((element) => t3.isStringLiteral(element) && element.value === unit)) {
2449
2903
  item.hasPatched = true;
2450
2904
  return;
2451
2905
  }
2452
- path13.node.elements.push(t3.stringLiteral(unit));
2906
+ path18.node.elements.push(t3.stringLiteral(unit));
2453
2907
  }
2454
2908
  }
2455
2909
  });
@@ -2467,7 +2921,7 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
2467
2921
  }
2468
2922
  ]);
2469
2923
  if (opts.overwrite) {
2470
- fs5.writeFileSync(file, item.code, "utf8");
2924
+ fs8.writeFileSync(file, item.code, "utf8");
2471
2925
  }
2472
2926
  }
2473
2927
  if (candidates.some((file) => !file.hasPatched)) {
@@ -2485,509 +2939,215 @@ function inspectLengthUnitsArray(content, variableName, units) {
2485
2939
  let found = false;
2486
2940
  let missingUnits = [];
2487
2941
  traverse(ast, {
2488
- Identifier(path13) {
2489
- if (path13.node.name === variableName && t4.isVariableDeclarator(path13.parent) && t4.isArrayExpression(path13.parent.init)) {
2942
+ Identifier(path18) {
2943
+ if (path18.node.name === variableName && t4.isVariableDeclarator(path18.parent) && t4.isArrayExpression(path18.parent.init)) {
2490
2944
  found = true;
2491
2945
  const existing = new Set(
2492
- path13.parent.init.elements.map((element) => t4.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
2493
- );
2494
- missingUnits = units.filter((unit) => !existing.has(unit));
2495
- path13.stop();
2496
- }
2497
- }
2498
- });
2499
- return {
2500
- found,
2501
- missingUnits
2502
- };
2503
- }
2504
- function checkExposeContextPatch(context) {
2505
- const { packageInfo, options, majorVersion } = context;
2506
- const refProperty = options.features.exposeContext.refProperty;
2507
- if (!options.features.exposeContext.enabled) {
2508
- return {
2509
- name: "exposeContext",
2510
- status: "skipped",
2511
- reason: "exposeContext feature disabled",
2512
- files: []
2513
- };
2514
- }
2515
- if (majorVersion === 4) {
2516
- return {
2517
- name: "exposeContext",
2518
- status: "unsupported",
2519
- reason: "Context export patch is only required for Tailwind v2/v3",
2520
- files: []
2521
- };
2522
- }
2523
- const checks = [];
2524
- function inspectFile(relative, transform) {
2525
- const filePath = path5.resolve(packageInfo.rootPath, relative);
2526
- if (!fs6.existsSync(filePath)) {
2527
- checks.push({ relative, exists: false, patched: false });
2528
- return;
2529
- }
2530
- const content = fs6.readFileSync(filePath, "utf8");
2531
- const { hasPatched } = transform(content);
2532
- checks.push({
2533
- relative,
2534
- exists: true,
2535
- patched: hasPatched
2536
- });
2537
- }
2538
- if (majorVersion === 3) {
2539
- inspectFile("lib/processTailwindFeatures.js", transformProcessTailwindFeaturesReturnContext);
2540
- const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
2541
- const pluginRelative = pluginCandidates.find((candidate) => fs6.existsSync(path5.resolve(packageInfo.rootPath, candidate)));
2542
- if (pluginRelative) {
2543
- inspectFile(pluginRelative, (content) => transformPostcssPlugin(content, { refProperty }));
2544
- } else {
2545
- checks.push({ relative: "lib/plugin.js", exists: false, patched: false });
2546
- }
2547
- } else {
2548
- inspectFile("lib/jit/processTailwindFeatures.js", transformProcessTailwindFeaturesReturnContextV2);
2549
- inspectFile("lib/jit/index.js", (content) => transformPostcssPluginV2(content, { refProperty }));
2550
- }
2551
- const files = checks.filter((check) => check.exists).map((check) => check.relative);
2552
- const missingFiles = checks.filter((check) => !check.exists);
2553
- const unpatchedFiles = checks.filter((check) => check.exists && !check.patched);
2554
- const reasons = [];
2555
- if (missingFiles.length) {
2556
- reasons.push(`missing files: ${missingFiles.map((item) => item.relative).join(", ")}`);
2557
- }
2558
- if (unpatchedFiles.length) {
2559
- reasons.push(`unpatched files: ${unpatchedFiles.map((item) => item.relative).join(", ")}`);
2560
- }
2561
- return {
2562
- name: "exposeContext",
2563
- status: reasons.length ? "not-applied" : "applied",
2564
- ...reasons.length ? { reason: reasons.join("; ") } : {},
2565
- files
2566
- };
2567
- }
2568
- function checkExtendLengthUnitsV3(rootDir, options) {
2569
- const lengthUnitsFilePath = options.lengthUnitsFilePath ?? "lib/util/dataTypes.js";
2570
- const variableName = options.variableName ?? "lengthUnits";
2571
- const target = path5.resolve(rootDir, lengthUnitsFilePath);
2572
- const files = fs6.existsSync(target) ? [path5.relative(rootDir, target)] : [];
2573
- if (!fs6.existsSync(target)) {
2574
- return {
2575
- name: "extendLengthUnits",
2576
- status: "not-applied",
2577
- reason: `missing ${lengthUnitsFilePath}`,
2578
- files
2579
- };
2580
- }
2581
- const content = fs6.readFileSync(target, "utf8");
2582
- const { found, missingUnits } = inspectLengthUnitsArray(content, variableName, options.units);
2583
- if (!found) {
2584
- return {
2585
- name: "extendLengthUnits",
2586
- status: "not-applied",
2587
- reason: `could not locate ${variableName} array in ${lengthUnitsFilePath}`,
2588
- files
2589
- };
2590
- }
2591
- if (missingUnits.length) {
2592
- return {
2593
- name: "extendLengthUnits",
2594
- status: "not-applied",
2595
- reason: `missing units: ${missingUnits.join(", ")}`,
2596
- files
2597
- };
2598
- }
2599
- return {
2600
- name: "extendLengthUnits",
2601
- status: "applied",
2602
- files
2603
- };
2604
- }
2605
- function checkExtendLengthUnitsV4(rootDir, options) {
2606
- const distDir = path5.resolve(rootDir, "dist");
2607
- if (!fs6.existsSync(distDir)) {
2608
- return {
2609
- name: "extendLengthUnits",
2610
- status: "not-applied",
2611
- reason: "dist directory not found for Tailwind v4 package",
2612
- files: []
2613
- };
2614
- }
2615
- const result = applyExtendLengthUnitsPatchV4(rootDir, {
2616
- ...options,
2617
- enabled: true,
2618
- overwrite: false
2619
- });
2620
- if (result.files.length === 0) {
2621
- return {
2622
- name: "extendLengthUnits",
2623
- status: "not-applied",
2624
- reason: "no bundle chunks matched the length unit pattern",
2625
- files: []
2626
- };
2627
- }
2628
- const files = result.files.map((file) => path5.relative(rootDir, file.file));
2629
- const pending = result.files.filter((file) => !file.hasPatched);
2630
- if (pending.length) {
2631
- return {
2632
- name: "extendLengthUnits",
2633
- status: "not-applied",
2634
- reason: `missing units in ${pending.length} bundle${pending.length > 1 ? "s" : ""}`,
2635
- files: pending.map((file) => path5.relative(rootDir, file.file))
2636
- };
2637
- }
2946
+ path18.parent.init.elements.map((element) => t4.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
2947
+ );
2948
+ missingUnits = units.filter((unit) => !existing.has(unit));
2949
+ path18.stop();
2950
+ }
2951
+ }
2952
+ });
2638
2953
  return {
2639
- name: "extendLengthUnits",
2640
- status: "applied",
2641
- files
2954
+ found,
2955
+ missingUnits
2642
2956
  };
2643
2957
  }
2644
- function checkExtendLengthUnitsPatch(context) {
2958
+ function checkExposeContextPatch(context) {
2645
2959
  const { packageInfo, options, majorVersion } = context;
2646
- if (!options.features.extendLengthUnits) {
2960
+ const refProperty = options.features.exposeContext.refProperty;
2961
+ if (!options.features.exposeContext.enabled) {
2647
2962
  return {
2648
- name: "extendLengthUnits",
2963
+ name: "exposeContext",
2649
2964
  status: "skipped",
2650
- reason: "extendLengthUnits feature disabled",
2965
+ reason: "exposeContext feature disabled",
2651
2966
  files: []
2652
2967
  };
2653
2968
  }
2654
- if (majorVersion === 2) {
2969
+ if (majorVersion === 4) {
2655
2970
  return {
2656
- name: "extendLengthUnits",
2971
+ name: "exposeContext",
2657
2972
  status: "unsupported",
2658
- reason: "length unit extension is only applied for Tailwind v3/v4",
2973
+ reason: "Context export patch is only required for Tailwind v2/v3",
2659
2974
  files: []
2660
2975
  };
2661
2976
  }
2662
- if (majorVersion === 3) {
2663
- return checkExtendLengthUnitsV3(packageInfo.rootPath, options.features.extendLengthUnits);
2664
- }
2665
- return checkExtendLengthUnitsV4(packageInfo.rootPath, options.features.extendLengthUnits);
2666
- }
2667
- function getPatchStatusReport(context) {
2668
- return {
2669
- package: {
2670
- name: context.packageInfo.name ?? context.packageInfo.packageJson?.name,
2671
- version: context.packageInfo.version,
2672
- root: context.packageInfo.rootPath
2673
- },
2674
- majorVersion: context.majorVersion,
2675
- entries: [
2676
- checkExposeContextPatch(context),
2677
- checkExtendLengthUnitsPatch(context)
2678
- ]
2679
- };
2680
- }
2681
-
2682
- // src/runtime/class-collector.ts
2683
- import process5 from "process";
2684
- import fs7 from "fs-extra";
2685
- import path6 from "pathe";
2686
- function collectClassesFromContexts(contexts, filter) {
2687
- const set = /* @__PURE__ */ new Set();
2688
- for (const context of contexts) {
2689
- if (!isObject(context) || !context.classCache) {
2690
- continue;
2691
- }
2692
- for (const key of context.classCache.keys()) {
2693
- const className = key.toString();
2694
- if (filter(className)) {
2695
- set.add(className);
2696
- }
2977
+ const checks = [];
2978
+ function inspectFile(relative, transform) {
2979
+ const filePath = path9.resolve(packageInfo.rootPath, relative);
2980
+ if (!fs9.existsSync(filePath)) {
2981
+ checks.push({ relative, exists: false, patched: false });
2982
+ return;
2697
2983
  }
2984
+ const content = fs9.readFileSync(filePath, "utf8");
2985
+ const { hasPatched } = transform(content);
2986
+ checks.push({
2987
+ relative,
2988
+ exists: true,
2989
+ patched: hasPatched
2990
+ });
2698
2991
  }
2699
- return set;
2700
- }
2701
- async function collectClassesFromTailwindV4(options) {
2702
- const set = /* @__PURE__ */ new Set();
2703
- const v4Options = options.tailwind.v4;
2704
- if (!v4Options) {
2705
- return set;
2706
- }
2707
- const toAbsolute = (value) => {
2708
- if (!value) {
2709
- return void 0;
2710
- }
2711
- return path6.isAbsolute(value) ? value : path6.resolve(options.projectRoot, value);
2712
- };
2713
- const resolvedConfiguredBase = toAbsolute(v4Options.configuredBase);
2714
- const resolvedDefaultBase = toAbsolute(v4Options.base) ?? process5.cwd();
2715
- const resolveSources = (base) => {
2716
- if (!v4Options.sources?.length) {
2717
- return void 0;
2718
- }
2719
- return v4Options.sources.map((source) => ({
2720
- base: source.base ?? base,
2721
- pattern: source.pattern,
2722
- negated: source.negated
2723
- }));
2724
- };
2725
- if (v4Options.cssEntries.length > 0) {
2726
- for (const entry of v4Options.cssEntries) {
2727
- const filePath = path6.isAbsolute(entry) ? entry : path6.resolve(options.projectRoot, entry);
2728
- if (!await fs7.pathExists(filePath)) {
2729
- continue;
2730
- }
2731
- const css = await fs7.readFile(filePath, "utf8");
2732
- const entryDir = path6.dirname(filePath);
2733
- const designSystemBases = resolvedConfiguredBase && resolvedConfiguredBase !== entryDir ? [entryDir, resolvedConfiguredBase] : [entryDir];
2734
- const sourcesBase = resolvedConfiguredBase ?? entryDir;
2735
- const sources = resolveSources(sourcesBase);
2736
- const firstBase = designSystemBases[0] ?? entryDir;
2737
- const extractOptions = {
2738
- cwd: options.projectRoot,
2739
- base: firstBase,
2740
- baseFallbacks: designSystemBases.slice(1),
2741
- css,
2742
- ...sources === void 0 ? {} : { sources }
2743
- };
2744
- const candidates = await extractValidCandidates(extractOptions);
2745
- for (const candidate of candidates) {
2746
- if (options.filter(candidate)) {
2747
- set.add(candidate);
2748
- }
2749
- }
2992
+ if (majorVersion === 3) {
2993
+ inspectFile("lib/processTailwindFeatures.js", transformProcessTailwindFeaturesReturnContext);
2994
+ const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
2995
+ const pluginRelative = pluginCandidates.find((candidate) => fs9.existsSync(path9.resolve(packageInfo.rootPath, candidate)));
2996
+ if (pluginRelative) {
2997
+ inspectFile(pluginRelative, (content) => transformPostcssPlugin(content, { refProperty }));
2998
+ } else {
2999
+ checks.push({ relative: "lib/plugin.js", exists: false, patched: false });
2750
3000
  }
2751
3001
  } else {
2752
- const baseForCss = resolvedConfiguredBase ?? resolvedDefaultBase;
2753
- const sources = resolveSources(baseForCss);
2754
- const extractOptions = {
2755
- cwd: options.projectRoot,
2756
- base: baseForCss,
2757
- ...v4Options.css === void 0 ? {} : { css: v4Options.css },
2758
- ...sources === void 0 ? {} : { sources }
2759
- };
2760
- const candidates = await extractValidCandidates(extractOptions);
2761
- for (const candidate of candidates) {
2762
- if (options.filter(candidate)) {
2763
- set.add(candidate);
2764
- }
2765
- }
2766
- }
2767
- return set;
2768
- }
2769
-
2770
- // src/runtime/context-registry.ts
2771
- import { createRequire } from "module";
2772
- import fs8 from "fs-extra";
2773
- import path7 from "pathe";
2774
- var require2 = createRequire(import.meta.url);
2775
- function resolveRuntimeEntry(packageInfo, majorVersion) {
2776
- const root = packageInfo.rootPath;
2777
- if (majorVersion === 2) {
2778
- const jitIndex = path7.join(root, "lib/jit/index.js");
2779
- if (fs8.existsSync(jitIndex)) {
2780
- return jitIndex;
2781
- }
2782
- } else if (majorVersion === 3) {
2783
- const plugin = path7.join(root, "lib/plugin.js");
2784
- const index = path7.join(root, "lib/index.js");
2785
- if (fs8.existsSync(plugin)) {
2786
- return plugin;
2787
- }
2788
- if (fs8.existsSync(index)) {
2789
- return index;
2790
- }
2791
- }
2792
- return void 0;
2793
- }
2794
- function loadRuntimeContexts(packageInfo, majorVersion, refProperty) {
2795
- if (majorVersion === 4) {
2796
- return [];
2797
- }
2798
- const entry = resolveRuntimeEntry(packageInfo, majorVersion);
2799
- if (!entry) {
2800
- return [];
2801
- }
2802
- const moduleExports = require2(entry);
2803
- if (!moduleExports) {
2804
- return [];
2805
- }
2806
- const ref = moduleExports[refProperty];
2807
- if (!ref) {
2808
- return [];
2809
- }
2810
- if (Array.isArray(ref)) {
2811
- return ref;
2812
- }
2813
- if (typeof ref === "object" && Array.isArray(ref.value)) {
2814
- return ref.value;
3002
+ inspectFile("lib/jit/processTailwindFeatures.js", transformProcessTailwindFeaturesReturnContextV2);
3003
+ inspectFile("lib/jit/index.js", (content) => transformPostcssPluginV2(content, { refProperty }));
2815
3004
  }
2816
- return [];
2817
- }
2818
-
2819
- // src/runtime/process-tailwindcss.ts
2820
- import { createRequire as createRequire2 } from "module";
2821
- import path8 from "pathe";
2822
- import postcss from "postcss";
2823
- import { loadConfig } from "tailwindcss-config";
2824
- var require3 = createRequire2(import.meta.url);
2825
- async function resolveConfigPath(options) {
2826
- if (options.config && path8.isAbsolute(options.config)) {
2827
- return options.config;
3005
+ const files = checks.filter((check) => check.exists).map((check) => check.relative);
3006
+ const missingFiles = checks.filter((check) => !check.exists);
3007
+ const unpatchedFiles = checks.filter((check) => check.exists && !check.patched);
3008
+ const reasons = [];
3009
+ if (missingFiles.length) {
3010
+ reasons.push(`missing files: ${missingFiles.map((item) => item.relative).join(", ")}`);
2828
3011
  }
2829
- const result = await loadConfig({ cwd: options.cwd });
2830
- if (!result) {
2831
- throw new Error(`Unable to locate Tailwind CSS config from ${options.cwd}`);
3012
+ if (unpatchedFiles.length) {
3013
+ reasons.push(`unpatched files: ${unpatchedFiles.map((item) => item.relative).join(", ")}`);
2832
3014
  }
2833
- return result.filepath;
3015
+ return {
3016
+ name: "exposeContext",
3017
+ status: reasons.length ? "not-applied" : "applied",
3018
+ ...reasons.length ? { reason: reasons.join("; ") } : {},
3019
+ files
3020
+ };
2834
3021
  }
2835
- async function runTailwindBuild(options) {
2836
- const configPath = await resolveConfigPath(options);
2837
- const pluginName = options.postcssPlugin ?? (options.majorVersion === 4 ? "@tailwindcss/postcss" : "tailwindcss");
2838
- if (options.majorVersion === 4) {
2839
- return postcss([
2840
- require3(pluginName)({
2841
- config: configPath
2842
- })
2843
- ]).process("@import 'tailwindcss';", {
2844
- from: void 0
2845
- });
3022
+ function checkExtendLengthUnitsV3(rootDir, options) {
3023
+ const lengthUnitsFilePath = options.lengthUnitsFilePath ?? "lib/util/dataTypes.js";
3024
+ const variableName = options.variableName ?? "lengthUnits";
3025
+ const target = path9.resolve(rootDir, lengthUnitsFilePath);
3026
+ const files = fs9.existsSync(target) ? [path9.relative(rootDir, target)] : [];
3027
+ if (!fs9.existsSync(target)) {
3028
+ return {
3029
+ name: "extendLengthUnits",
3030
+ status: "not-applied",
3031
+ reason: `missing ${lengthUnitsFilePath}`,
3032
+ files
3033
+ };
2846
3034
  }
2847
- return postcss([
2848
- require3(pluginName)({
2849
- config: configPath
2850
- })
2851
- ]).process("@tailwind base;@tailwind components;@tailwind utilities;", {
2852
- from: void 0
2853
- });
2854
- }
2855
-
2856
- // src/api/tailwindcss-patcher.ts
2857
- import process6 from "process";
2858
- import fs10 from "fs-extra";
2859
- import { getPackageInfoSync } from "local-pkg";
2860
- import path10 from "pathe";
2861
- import { coerce } from "semver";
2862
-
2863
- // src/options/legacy.ts
2864
- function normalizeLegacyFeatures(patch) {
2865
- const apply = patch?.applyPatches;
2866
- const extend = apply?.extendLengthUnits;
2867
- let extendOption = false;
2868
- if (extend && typeof extend === "object") {
2869
- extendOption = {
2870
- ...extend,
2871
- enabled: true
3035
+ const content = fs9.readFileSync(target, "utf8");
3036
+ const { found, missingUnits } = inspectLengthUnitsArray(content, variableName, options.units);
3037
+ if (!found) {
3038
+ return {
3039
+ name: "extendLengthUnits",
3040
+ status: "not-applied",
3041
+ reason: `could not locate ${variableName} array in ${lengthUnitsFilePath}`,
3042
+ files
2872
3043
  };
2873
- } else if (extend === true) {
2874
- extendOption = {
2875
- enabled: true,
2876
- units: ["rpx"],
2877
- ...patch?.overwrite === void 0 ? {} : { overwrite: patch.overwrite }
3044
+ }
3045
+ if (missingUnits.length) {
3046
+ return {
3047
+ name: "extendLengthUnits",
3048
+ status: "not-applied",
3049
+ reason: `missing units: ${missingUnits.join(", ")}`,
3050
+ files
2878
3051
  };
2879
3052
  }
2880
3053
  return {
2881
- exposeContext: apply?.exportContext ?? true,
2882
- extendLengthUnits: extendOption
3054
+ name: "extendLengthUnits",
3055
+ status: "applied",
3056
+ files
2883
3057
  };
2884
3058
  }
2885
- function fromLegacyOptions(options) {
2886
- if (!options) {
2887
- return {};
3059
+ function checkExtendLengthUnitsV4(rootDir, options) {
3060
+ const distDir = path9.resolve(rootDir, "dist");
3061
+ if (!fs9.existsSync(distDir)) {
3062
+ return {
3063
+ name: "extendLengthUnits",
3064
+ status: "not-applied",
3065
+ reason: "dist directory not found for Tailwind v4 package",
3066
+ files: []
3067
+ };
3068
+ }
3069
+ const result = applyExtendLengthUnitsPatchV4(rootDir, {
3070
+ ...options,
3071
+ enabled: true,
3072
+ overwrite: false
3073
+ });
3074
+ if (result.files.length === 0) {
3075
+ return {
3076
+ name: "extendLengthUnits",
3077
+ status: "not-applied",
3078
+ reason: "no bundle chunks matched the length unit pattern",
3079
+ files: []
3080
+ };
3081
+ }
3082
+ const files = result.files.map((file) => path9.relative(rootDir, file.file));
3083
+ const pending = result.files.filter((file) => !file.hasPatched);
3084
+ if (pending.length) {
3085
+ return {
3086
+ name: "extendLengthUnits",
3087
+ status: "not-applied",
3088
+ reason: `missing units in ${pending.length} bundle${pending.length > 1 ? "s" : ""}`,
3089
+ files: pending.map((file) => path9.relative(rootDir, file.file))
3090
+ };
2888
3091
  }
2889
- const patch = options.patch;
2890
- const features = normalizeLegacyFeatures(patch);
2891
- const output = patch?.output;
2892
- const tailwindConfig = patch?.tailwindcss;
2893
- const tailwindVersion = tailwindConfig?.version;
2894
- const tailwindV2 = tailwindConfig?.v2;
2895
- const tailwindV3 = tailwindConfig?.v3;
2896
- const tailwindV4 = tailwindConfig?.v4;
2897
- const tailwindConfigPath = tailwindV3?.config ?? tailwindV2?.config;
2898
- const tailwindCwd = tailwindV3?.cwd ?? tailwindV2?.cwd ?? patch?.cwd;
2899
- const normalizedExtract = output ? {
2900
- ...output.filename === void 0 ? {} : { file: output.filename },
2901
- pretty: output.loose ? 2 : false,
2902
- ...output.removeUniversalSelector === void 0 ? {} : { removeUniversalSelector: output.removeUniversalSelector }
2903
- } : void 0;
2904
- const normalizedTailwindcss = {
2905
- ...patch?.packageName === void 0 ? {} : { packageName: patch.packageName },
2906
- ...tailwindVersion === void 0 ? {} : { version: tailwindVersion },
2907
- ...patch?.resolve === void 0 ? {} : { resolve: patch.resolve },
2908
- ...tailwindConfigPath === void 0 ? {} : { config: tailwindConfigPath },
2909
- ...tailwindCwd === void 0 ? {} : { cwd: tailwindCwd },
2910
- ...tailwindV2 === void 0 ? {} : { v2: tailwindV2 },
2911
- ...tailwindV3 === void 0 ? {} : { v3: tailwindV3 },
2912
- ...tailwindV4 === void 0 ? {} : { v4: tailwindV4 }
2913
- };
2914
- const normalizedCache = typeof options.cache === "boolean" ? options.cache : options.cache ? {
2915
- ...options.cache,
2916
- enabled: options.cache.enabled ?? true
2917
- } : void 0;
2918
- const normalizedApply = {
2919
- ...patch?.overwrite === void 0 ? {} : { overwrite: patch.overwrite },
2920
- exposeContext: features.exposeContext,
2921
- extendLengthUnits: features.extendLengthUnits
2922
- };
2923
3092
  return {
2924
- ...patch?.cwd === void 0 ? {} : { projectRoot: patch.cwd },
2925
- ...patch?.filter === void 0 ? {} : { filter: patch.filter },
2926
- ...normalizedCache === void 0 ? {} : { cache: normalizedCache },
2927
- ...normalizedExtract === void 0 ? {} : { extract: normalizedExtract },
2928
- ...Object.keys(normalizedTailwindcss).length === 0 ? {} : { tailwindcss: normalizedTailwindcss },
2929
- apply: normalizedApply
3093
+ name: "extendLengthUnits",
3094
+ status: "applied",
3095
+ files
2930
3096
  };
2931
3097
  }
2932
- function fromUnifiedConfig(registry) {
2933
- if (!registry) {
2934
- return {};
3098
+ function checkExtendLengthUnitsPatch(context) {
3099
+ const { packageInfo, options, majorVersion } = context;
3100
+ if (!options.features.extendLengthUnits) {
3101
+ return {
3102
+ name: "extendLengthUnits",
3103
+ status: "skipped",
3104
+ reason: "extendLengthUnits feature disabled",
3105
+ files: []
3106
+ };
2935
3107
  }
2936
- const tailwind = registry.tailwindcss ?? registry.tailwind;
2937
- const modernExtract = registry.extract;
2938
- const legacyOutput = registry.output;
2939
- const pretty = (() => {
2940
- const value = modernExtract?.pretty ?? legacyOutput?.pretty;
2941
- if (value === void 0) {
2942
- return void 0;
2943
- }
2944
- if (typeof value === "boolean") {
2945
- return value ? 2 : false;
2946
- }
2947
- return value;
2948
- })();
2949
- const removeUniversalSelector = modernExtract?.removeUniversalSelector ?? legacyOutput?.stripUniversalSelector;
2950
- const outputFile = modernExtract?.file ?? legacyOutput?.file;
2951
- const normalizedExtract = modernExtract || legacyOutput ? {
2952
- ...modernExtract?.write === void 0 ? {} : { write: modernExtract.write },
2953
- ...outputFile === void 0 ? {} : { file: outputFile },
2954
- ...pretty === void 0 ? {} : { pretty },
2955
- ...removeUniversalSelector === void 0 ? {} : { removeUniversalSelector },
2956
- ...modernExtract?.format === void 0 ? {} : { format: modernExtract.format }
2957
- } : void 0;
2958
- const normalizedTailwindcss = tailwind ? {
2959
- ...tailwind.version === void 0 ? {} : { version: tailwind.version },
2960
- ...tailwind.packageName === void 0 ? tailwind.package === void 0 ? {} : { packageName: tailwind.package } : { packageName: tailwind.packageName },
2961
- ...tailwind.resolve === void 0 ? {} : { resolve: tailwind.resolve },
2962
- ...tailwind.config === void 0 ? {} : { config: tailwind.config },
2963
- ...tailwind.cwd === void 0 ? {} : { cwd: tailwind.cwd },
2964
- ...tailwind.v2 === void 0 ? tailwind.legacy === void 0 ? {} : { v2: tailwind.legacy } : { v2: tailwind.v2 },
2965
- ...tailwind.v3 === void 0 ? tailwind.classic === void 0 ? {} : { v3: tailwind.classic } : { v3: tailwind.v3 },
2966
- ...tailwind.v4 === void 0 ? tailwind.next === void 0 ? {} : { v4: tailwind.next } : { v4: tailwind.v4 }
2967
- } : void 0;
2968
- const normalizedApply = registry.apply ? {
2969
- ...registry.apply.overwrite === void 0 ? {} : { overwrite: registry.apply.overwrite },
2970
- ...registry.apply.exposeContext === void 0 ? {} : { exposeContext: registry.apply.exposeContext },
2971
- ...registry.apply.extendLengthUnits === void 0 ? {} : { extendLengthUnits: registry.apply.extendLengthUnits }
2972
- } : void 0;
3108
+ if (majorVersion === 2) {
3109
+ return {
3110
+ name: "extendLengthUnits",
3111
+ status: "unsupported",
3112
+ reason: "length unit extension is only applied for Tailwind v3/v4",
3113
+ files: []
3114
+ };
3115
+ }
3116
+ if (majorVersion === 3) {
3117
+ return checkExtendLengthUnitsV3(packageInfo.rootPath, options.features.extendLengthUnits);
3118
+ }
3119
+ return checkExtendLengthUnitsV4(packageInfo.rootPath, options.features.extendLengthUnits);
3120
+ }
3121
+ function getPatchStatusReport(context) {
2973
3122
  return {
2974
- ...registry.projectRoot === void 0 ? {} : { projectRoot: registry.projectRoot },
2975
- ...normalizedApply === void 0 ? {} : { apply: normalizedApply },
2976
- ...registry.cache === void 0 ? {} : { cache: registry.cache },
2977
- ...registry.filter === void 0 ? {} : { filter: registry.filter },
2978
- ...normalizedExtract === void 0 ? {} : { extract: normalizedExtract },
2979
- ...normalizedTailwindcss === void 0 ? {} : { tailwindcss: normalizedTailwindcss }
3123
+ package: {
3124
+ name: context.packageInfo.name ?? context.packageInfo.packageJson?.name,
3125
+ version: context.packageInfo.version,
3126
+ root: context.packageInfo.rootPath
3127
+ },
3128
+ majorVersion: context.majorVersion,
3129
+ entries: [
3130
+ checkExposeContextPatch(context),
3131
+ checkExtendLengthUnitsPatch(context)
3132
+ ]
2980
3133
  };
2981
3134
  }
2982
3135
 
3136
+ // src/api/tailwindcss-patcher.ts
3137
+ import process6 from "process";
3138
+ import fs11 from "fs-extra";
3139
+ import { getPackageInfoSync } from "local-pkg";
3140
+ import path11 from "pathe";
3141
+ import { coerce } from "semver";
3142
+
2983
3143
  // src/patching/operations/export-context/index.ts
2984
- import fs9 from "fs-extra";
2985
- import path9 from "pathe";
3144
+ import fs10 from "fs-extra";
3145
+ import path10 from "pathe";
2986
3146
  function writeFileIfRequired(filePath, code, overwrite, successMessage) {
2987
3147
  if (!overwrite) {
2988
3148
  return;
2989
3149
  }
2990
- fs9.writeFileSync(filePath, code, {
3150
+ fs10.writeFileSync(filePath, code, {
2991
3151
  encoding: "utf8"
2992
3152
  });
2993
3153
  logger_default.success(successMessage);
@@ -3000,9 +3160,9 @@ function applyExposeContextPatch(params) {
3000
3160
  };
3001
3161
  if (majorVersion === 3) {
3002
3162
  const processFileRelative = "lib/processTailwindFeatures.js";
3003
- const processFilePath = path9.resolve(rootDir, processFileRelative);
3004
- if (fs9.existsSync(processFilePath)) {
3005
- const content = fs9.readFileSync(processFilePath, "utf8");
3163
+ const processFilePath = path10.resolve(rootDir, processFileRelative);
3164
+ if (fs10.existsSync(processFilePath)) {
3165
+ const content = fs10.readFileSync(processFilePath, "utf8");
3006
3166
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContext(content);
3007
3167
  result.files[processFileRelative] = code;
3008
3168
  if (!hasPatched) {
@@ -3016,10 +3176,10 @@ function applyExposeContextPatch(params) {
3016
3176
  }
3017
3177
  }
3018
3178
  const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
3019
- const pluginRelative = pluginCandidates.find((candidate) => fs9.existsSync(path9.resolve(rootDir, candidate)));
3179
+ const pluginRelative = pluginCandidates.find((candidate) => fs10.existsSync(path10.resolve(rootDir, candidate)));
3020
3180
  if (pluginRelative) {
3021
- const pluginPath = path9.resolve(rootDir, pluginRelative);
3022
- const content = fs9.readFileSync(pluginPath, "utf8");
3181
+ const pluginPath = path10.resolve(rootDir, pluginRelative);
3182
+ const content = fs10.readFileSync(pluginPath, "utf8");
3023
3183
  const { code, hasPatched } = transformPostcssPlugin(content, { refProperty });
3024
3184
  result.files[pluginRelative] = code;
3025
3185
  if (!hasPatched) {
@@ -3034,9 +3194,9 @@ function applyExposeContextPatch(params) {
3034
3194
  }
3035
3195
  } else if (majorVersion === 2) {
3036
3196
  const processFileRelative = "lib/jit/processTailwindFeatures.js";
3037
- const processFilePath = path9.resolve(rootDir, processFileRelative);
3038
- if (fs9.existsSync(processFilePath)) {
3039
- const content = fs9.readFileSync(processFilePath, "utf8");
3197
+ const processFilePath = path10.resolve(rootDir, processFileRelative);
3198
+ if (fs10.existsSync(processFilePath)) {
3199
+ const content = fs10.readFileSync(processFilePath, "utf8");
3040
3200
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContextV2(content);
3041
3201
  result.files[processFileRelative] = code;
3042
3202
  if (!hasPatched) {
@@ -3050,9 +3210,9 @@ function applyExposeContextPatch(params) {
3050
3210
  }
3051
3211
  }
3052
3212
  const pluginRelative = "lib/jit/index.js";
3053
- const pluginPath = path9.resolve(rootDir, pluginRelative);
3054
- if (fs9.existsSync(pluginPath)) {
3055
- const content = fs9.readFileSync(pluginPath, "utf8");
3213
+ const pluginPath = path10.resolve(rootDir, pluginRelative);
3214
+ if (fs10.existsSync(pluginPath)) {
3215
+ const content = fs10.readFileSync(pluginPath, "utf8");
3056
3216
  const { code, hasPatched } = transformPostcssPluginV2(content, { refProperty });
3057
3217
  result.files[pluginRelative] = code;
3058
3218
  if (!hasPatched) {
@@ -3144,6 +3304,8 @@ var TailwindcssPatcher = class {
3144
3304
  majorVersion;
3145
3305
  cacheContext;
3146
3306
  cacheStore;
3307
+ patchMemo;
3308
+ inFlightBuild;
3147
3309
  constructor(options = {}) {
3148
3310
  const resolvedOptions = options && typeof options === "object" && "patch" in options ? fromLegacyOptions(options) : options;
3149
3311
  this.options = normalizeOptions(resolvedOptions);
@@ -3167,11 +3329,20 @@ var TailwindcssPatcher = class {
3167
3329
  this.cacheStore = new CacheStore(this.options.cache, this.cacheContext);
3168
3330
  }
3169
3331
  async patch() {
3170
- return applyTailwindPatches({
3332
+ const snapshot = this.createPatchSnapshot();
3333
+ if (this.patchMemo && this.patchMemo.snapshot === snapshot) {
3334
+ return this.patchMemo.result;
3335
+ }
3336
+ const result = applyTailwindPatches({
3171
3337
  packageInfo: this.packageInfo,
3172
3338
  options: this.options,
3173
3339
  majorVersion: this.majorVersion
3174
3340
  });
3341
+ this.patchMemo = {
3342
+ result,
3343
+ snapshot: this.createPatchSnapshot()
3344
+ };
3345
+ return result;
3175
3346
  }
3176
3347
  async getPatchStatus() {
3177
3348
  return getPatchStatusReport({
@@ -3189,6 +3360,9 @@ var TailwindcssPatcher = class {
3189
3360
  }
3190
3361
  async runTailwindBuildIfNeeded() {
3191
3362
  if (this.majorVersion === 2 || this.majorVersion === 3) {
3363
+ if (this.inFlightBuild) {
3364
+ return this.inFlightBuild;
3365
+ }
3192
3366
  const executionOptions = resolveTailwindExecutionOptions(this.options, this.majorVersion);
3193
3367
  const buildOptions = {
3194
3368
  cwd: executionOptions.cwd,
@@ -3196,8 +3370,53 @@ var TailwindcssPatcher = class {
3196
3370
  ...executionOptions.config === void 0 ? {} : { config: executionOptions.config },
3197
3371
  ...executionOptions.postcssPlugin === void 0 ? {} : { postcssPlugin: executionOptions.postcssPlugin }
3198
3372
  };
3199
- await runTailwindBuild(buildOptions);
3373
+ this.inFlightBuild = runTailwindBuild(buildOptions).then(() => void 0);
3374
+ try {
3375
+ await this.inFlightBuild;
3376
+ } finally {
3377
+ this.inFlightBuild = void 0;
3378
+ }
3379
+ }
3380
+ }
3381
+ createPatchSnapshot() {
3382
+ const entries = [];
3383
+ const pushSnapshot = (filePath) => {
3384
+ if (!fs11.pathExistsSync(filePath)) {
3385
+ entries.push(`${filePath}:missing`);
3386
+ return;
3387
+ }
3388
+ const stat = fs11.statSync(filePath);
3389
+ entries.push(`${filePath}:${stat.size}:${Math.trunc(stat.mtimeMs)}`);
3390
+ };
3391
+ if (this.options.features.exposeContext.enabled && (this.majorVersion === 2 || this.majorVersion === 3)) {
3392
+ if (this.majorVersion === 2) {
3393
+ pushSnapshot(path11.resolve(this.packageInfo.rootPath, "lib/jit/processTailwindFeatures.js"));
3394
+ pushSnapshot(path11.resolve(this.packageInfo.rootPath, "lib/jit/index.js"));
3395
+ } else {
3396
+ pushSnapshot(path11.resolve(this.packageInfo.rootPath, "lib/processTailwindFeatures.js"));
3397
+ const pluginPath = ["lib/plugin.js", "lib/index.js"].map((file) => path11.resolve(this.packageInfo.rootPath, file)).find((file) => fs11.pathExistsSync(file));
3398
+ if (pluginPath) {
3399
+ pushSnapshot(pluginPath);
3400
+ }
3401
+ }
3402
+ }
3403
+ if (this.options.features.extendLengthUnits?.enabled) {
3404
+ if (this.majorVersion === 3) {
3405
+ const target = this.options.features.extendLengthUnits.lengthUnitsFilePath ?? "lib/util/dataTypes.js";
3406
+ pushSnapshot(path11.resolve(this.packageInfo.rootPath, target));
3407
+ } else if (this.majorVersion === 4) {
3408
+ const distDir = path11.resolve(this.packageInfo.rootPath, "dist");
3409
+ if (fs11.pathExistsSync(distDir)) {
3410
+ const chunkNames = fs11.readdirSync(distDir).filter((entry) => entry.endsWith(".js") || entry.endsWith(".mjs")).sort();
3411
+ for (const chunkName of chunkNames) {
3412
+ pushSnapshot(path11.join(distDir, chunkName));
3413
+ }
3414
+ } else {
3415
+ entries.push(`${distDir}:missing`);
3416
+ }
3417
+ }
3200
3418
  }
3419
+ return entries.join("|");
3201
3420
  }
3202
3421
  async collectClassSet() {
3203
3422
  if (this.majorVersion === 4) {
@@ -3227,13 +3446,13 @@ var TailwindcssPatcher = class {
3227
3446
  for (const value of existing) {
3228
3447
  set.add(value);
3229
3448
  }
3230
- const writeTarget = await this.cacheStore.write(set);
3449
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : await this.cacheStore.write(set);
3231
3450
  if (writeTarget) {
3232
3451
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3233
3452
  }
3234
3453
  } else {
3235
3454
  if (set.size > 0) {
3236
- const writeTarget = await this.cacheStore.write(set);
3455
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : await this.cacheStore.write(set);
3237
3456
  if (writeTarget) {
3238
3457
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3239
3458
  }
@@ -3253,13 +3472,13 @@ var TailwindcssPatcher = class {
3253
3472
  for (const value of existing) {
3254
3473
  set.add(value);
3255
3474
  }
3256
- const writeTarget = this.cacheStore.writeSync(set);
3475
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : this.cacheStore.writeSync(set);
3257
3476
  if (writeTarget) {
3258
3477
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3259
3478
  }
3260
3479
  } else {
3261
3480
  if (set.size > 0) {
3262
- const writeTarget = this.cacheStore.writeSync(set);
3481
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : this.cacheStore.writeSync(set);
3263
3482
  if (writeTarget) {
3264
3483
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3265
3484
  }
@@ -3269,6 +3488,17 @@ var TailwindcssPatcher = class {
3269
3488
  }
3270
3489
  return set;
3271
3490
  }
3491
+ areSetsEqual(a, b) {
3492
+ if (a.size !== b.size) {
3493
+ return false;
3494
+ }
3495
+ for (const value of a) {
3496
+ if (!b.has(value)) {
3497
+ return false;
3498
+ }
3499
+ }
3500
+ return true;
3501
+ }
3272
3502
  async getClassSet() {
3273
3503
  await this.runTailwindBuildIfNeeded();
3274
3504
  const set = await this.collectClassSet();
@@ -3297,13 +3527,13 @@ var TailwindcssPatcher = class {
3297
3527
  if (!shouldWrite || !this.options.output.file) {
3298
3528
  return result;
3299
3529
  }
3300
- const target = path10.resolve(this.options.output.file);
3301
- await fs10.ensureDir(path10.dirname(target));
3530
+ const target = path11.resolve(this.options.output.file);
3531
+ await fs11.ensureDir(path11.dirname(target));
3302
3532
  if (this.options.output.format === "json") {
3303
3533
  const spaces = typeof this.options.output.pretty === "number" ? this.options.output.pretty : void 0;
3304
- await fs10.writeJSON(target, classList, { spaces });
3534
+ await fs11.writeJSON(target, classList, { spaces });
3305
3535
  } else {
3306
- await fs10.writeFile(target, `${classList.join("\n")}
3536
+ await fs11.writeFile(target, `${classList.join("\n")}
3307
3537
  `, "utf8");
3308
3538
  }
3309
3539
  logger_default.success(`Tailwind CSS class list saved to ${target.replace(process6.cwd(), ".")}`);
@@ -3341,37 +3571,94 @@ var TailwindcssPatcher = class {
3341
3571
  }
3342
3572
  };
3343
3573
 
3344
- // src/cli/migrate-config.ts
3345
- import generate2 from "@babel/generator";
3346
- import { parse as parse2 } from "@babel/parser";
3347
- import * as t5 from "@babel/types";
3348
- import fs11 from "fs-extra";
3349
- import path11 from "pathe";
3350
- var DEFAULT_CONFIG_FILENAMES = [
3351
- "tailwindcss-patch.config.ts",
3352
- "tailwindcss-patch.config.js",
3353
- "tailwindcss-patch.config.mjs",
3354
- "tailwindcss-patch.config.cjs",
3355
- "tailwindcss-mangle.config.ts",
3356
- "tailwindcss-mangle.config.js",
3357
- "tailwindcss-mangle.config.mjs",
3358
- "tailwindcss-mangle.config.cjs"
3359
- ];
3360
- var DEFAULT_CONFIG_FILENAME_SET = new Set(DEFAULT_CONFIG_FILENAMES);
3361
- var DEFAULT_WORKSPACE_IGNORED_DIRS = /* @__PURE__ */ new Set([
3362
- ".git",
3363
- ".idea",
3364
- ".turbo",
3365
- ".vscode",
3366
- ".yarn",
3367
- "coverage",
3368
- "dist",
3369
- "node_modules",
3370
- "tmp"
3371
- ]);
3372
- var DEFAULT_WORKSPACE_MAX_DEPTH = 6;
3574
+ // src/commands/migration-report.ts
3373
3575
  var MIGRATION_REPORT_KIND = "tw-patch-migrate-report";
3374
3576
  var MIGRATION_REPORT_SCHEMA_VERSION = 1;
3577
+ function assertMigrationReportCompatibility(report, reportFile) {
3578
+ if (report.reportKind !== void 0 && report.reportKind !== MIGRATION_REPORT_KIND) {
3579
+ throw new Error(`Unsupported report kind "${report.reportKind}" in ${reportFile}.`);
3580
+ }
3581
+ if (report.schemaVersion !== void 0 && (!Number.isInteger(report.schemaVersion) || report.schemaVersion > MIGRATION_REPORT_SCHEMA_VERSION)) {
3582
+ throw new Error(
3583
+ `Unsupported report schema version "${String(report.schemaVersion)}" in ${reportFile}. Current supported version is ${MIGRATION_REPORT_SCHEMA_VERSION}.`
3584
+ );
3585
+ }
3586
+ }
3587
+
3588
+ // src/commands/migrate-config.ts
3589
+ import path14 from "pathe";
3590
+
3591
+ // src/commands/migration-aggregation.ts
3592
+ function createMigrationAggregationState() {
3593
+ return {
3594
+ scannedFiles: 0,
3595
+ changedFiles: 0,
3596
+ writtenFiles: 0,
3597
+ backupsWritten: 0,
3598
+ unchangedFiles: 0,
3599
+ missingFiles: 0,
3600
+ entries: []
3601
+ };
3602
+ }
3603
+ function collectMigrationExecutionResult(state, result) {
3604
+ if (result.missing) {
3605
+ state.missingFiles += 1;
3606
+ return;
3607
+ }
3608
+ state.scannedFiles += 1;
3609
+ state.entries.push(result.entry);
3610
+ if (result.changed) {
3611
+ state.changedFiles += 1;
3612
+ if (result.wrote) {
3613
+ state.writtenFiles += 1;
3614
+ }
3615
+ if (result.backupWritten) {
3616
+ state.backupsWritten += 1;
3617
+ }
3618
+ } else {
3619
+ state.unchangedFiles += 1;
3620
+ }
3621
+ }
3622
+ function buildMigrationReport(state, context) {
3623
+ const {
3624
+ cwd,
3625
+ dryRun,
3626
+ rollbackOnError,
3627
+ backupDirectory,
3628
+ toolName,
3629
+ toolVersion,
3630
+ generatedAt = (/* @__PURE__ */ new Date()).toISOString()
3631
+ } = context;
3632
+ return {
3633
+ reportKind: MIGRATION_REPORT_KIND,
3634
+ schemaVersion: MIGRATION_REPORT_SCHEMA_VERSION,
3635
+ generatedAt,
3636
+ tool: {
3637
+ name: toolName,
3638
+ version: toolVersion
3639
+ },
3640
+ cwd,
3641
+ dryRun,
3642
+ rollbackOnError,
3643
+ ...backupDirectory ? { backupDirectory } : {},
3644
+ scannedFiles: state.scannedFiles,
3645
+ changedFiles: state.changedFiles,
3646
+ writtenFiles: state.writtenFiles,
3647
+ backupsWritten: state.backupsWritten,
3648
+ unchangedFiles: state.unchangedFiles,
3649
+ missingFiles: state.missingFiles,
3650
+ entries: state.entries
3651
+ };
3652
+ }
3653
+
3654
+ // src/commands/migration-file-executor.ts
3655
+ import fs13 from "fs-extra";
3656
+ import path13 from "pathe";
3657
+
3658
+ // src/commands/migration-source.ts
3659
+ import generate2 from "@babel/generator";
3660
+ import { parse as parse2 } from "@babel/parser";
3661
+ import * as t5 from "@babel/types";
3375
3662
  var ROOT_LEGACY_KEYS = ["cwd", "overwrite", "tailwind", "features", "output", "applyPatches"];
3376
3663
  function getPropertyKeyName(property) {
3377
3664
  if (!property.computed && t5.isIdentifier(property.key)) {
@@ -3632,11 +3919,38 @@ function migrateConfigSource(source) {
3632
3919
  changes: [...changes]
3633
3920
  };
3634
3921
  }
3922
+
3923
+ // src/commands/migration-target-files.ts
3924
+ import fs12 from "fs-extra";
3925
+ import path12 from "pathe";
3926
+ var DEFAULT_CONFIG_FILENAMES = [
3927
+ "tailwindcss-patch.config.ts",
3928
+ "tailwindcss-patch.config.js",
3929
+ "tailwindcss-patch.config.mjs",
3930
+ "tailwindcss-patch.config.cjs",
3931
+ "tailwindcss-mangle.config.ts",
3932
+ "tailwindcss-mangle.config.js",
3933
+ "tailwindcss-mangle.config.mjs",
3934
+ "tailwindcss-mangle.config.cjs"
3935
+ ];
3936
+ var DEFAULT_CONFIG_FILENAME_SET = new Set(DEFAULT_CONFIG_FILENAMES);
3937
+ var DEFAULT_WORKSPACE_IGNORED_DIRS = /* @__PURE__ */ new Set([
3938
+ ".git",
3939
+ ".idea",
3940
+ ".turbo",
3941
+ ".vscode",
3942
+ ".yarn",
3943
+ "coverage",
3944
+ "dist",
3945
+ "node_modules",
3946
+ "tmp"
3947
+ ]);
3948
+ var DEFAULT_WORKSPACE_MAX_DEPTH = 6;
3635
3949
  function resolveTargetFiles(cwd, files) {
3636
3950
  const candidates = files && files.length > 0 ? files : [...DEFAULT_CONFIG_FILENAMES];
3637
3951
  const resolved = /* @__PURE__ */ new Set();
3638
3952
  for (const file of candidates) {
3639
- resolved.add(path11.resolve(cwd, file));
3953
+ resolved.add(path12.resolve(cwd, file));
3640
3954
  }
3641
3955
  return [...resolved];
3642
3956
  }
@@ -3651,12 +3965,12 @@ async function collectWorkspaceConfigFiles(cwd, maxDepth) {
3651
3965
  const { dir, depth } = current;
3652
3966
  let entries;
3653
3967
  try {
3654
- entries = await fs11.readdir(dir, { withFileTypes: true });
3968
+ entries = await fs12.readdir(dir, { withFileTypes: true });
3655
3969
  } catch {
3656
3970
  continue;
3657
3971
  }
3658
3972
  for (const entry of entries) {
3659
- const absolutePath = path11.resolve(dir, entry.name);
3973
+ const absolutePath = path12.resolve(dir, entry.name);
3660
3974
  if (entry.isFile() && DEFAULT_CONFIG_FILENAME_SET.has(entry.name)) {
3661
3975
  files.add(absolutePath);
3662
3976
  continue;
@@ -3676,11 +3990,11 @@ async function collectWorkspaceConfigFiles(cwd, maxDepth) {
3676
3990
  return [...files].sort((a, b) => a.localeCompare(b));
3677
3991
  }
3678
3992
  function resolveBackupRelativePath(cwd, file) {
3679
- const relative = path11.relative(cwd, file);
3680
- const isExternal = relative.startsWith("..") || path11.isAbsolute(relative);
3993
+ const relative = path12.relative(cwd, file);
3994
+ const isExternal = relative.startsWith("..") || path12.isAbsolute(relative);
3681
3995
  if (isExternal) {
3682
3996
  const sanitized = file.replace(/[:/\\]+/g, "_");
3683
- return path11.join("__external__", `${sanitized}.bak`);
3997
+ return path12.join("__external__", `${sanitized}.bak`);
3684
3998
  }
3685
3999
  return `${relative}.bak`;
3686
4000
  }
@@ -3720,8 +4034,8 @@ function toPatternList(patterns) {
3720
4034
  return patterns.map((pattern) => pattern.trim()).filter(Boolean).map(globToRegExp);
3721
4035
  }
3722
4036
  function normalizeFileForPattern(file, cwd) {
3723
- const relative = path11.relative(cwd, file);
3724
- if (!relative.startsWith("..") && !path11.isAbsolute(relative)) {
4037
+ const relative = path12.relative(cwd, file);
4038
+ if (!relative.startsWith("..") && !path12.isAbsolute(relative)) {
3725
4039
  return relative.replace(/\\/g, "/");
3726
4040
  }
3727
4041
  return file.replace(/\\/g, "/");
@@ -3742,114 +4056,85 @@ function filterTargetFiles(targetFiles, cwd, include, exclude) {
3742
4056
  return !inExclude;
3743
4057
  });
3744
4058
  }
3745
- async function migrateConfigFiles(options) {
3746
- const cwd = path11.resolve(options.cwd);
3747
- const dryRun = options.dryRun ?? false;
3748
- const rollbackOnError = options.rollbackOnError ?? true;
3749
- const backupDirectory = options.backupDir ? path11.resolve(cwd, options.backupDir) : void 0;
3750
- const maxDepth = options.maxDepth ?? DEFAULT_WORKSPACE_MAX_DEPTH;
3751
- const discoveredTargetFiles = options.files && options.files.length > 0 ? resolveTargetFiles(cwd, options.files) : options.workspace ? await collectWorkspaceConfigFiles(cwd, maxDepth) : resolveTargetFiles(cwd);
3752
- const targetFiles = filterTargetFiles(discoveredTargetFiles, cwd, options.include, options.exclude);
3753
- const entries = [];
3754
- let scannedFiles = 0;
3755
- let changedFiles = 0;
3756
- let writtenFiles = 0;
3757
- let backupsWritten = 0;
3758
- let unchangedFiles = 0;
3759
- let missingFiles = 0;
3760
- const wroteEntries = [];
3761
- for (const file of targetFiles) {
3762
- const exists = await fs11.pathExists(file);
3763
- if (!exists) {
3764
- missingFiles += 1;
3765
- continue;
3766
- }
3767
- scannedFiles += 1;
3768
- const source = await fs11.readFile(file, "utf8");
3769
- const migrated = migrateConfigSource(source);
3770
- const entry = {
3771
- file,
3772
- changed: migrated.changed,
3773
- written: false,
3774
- rolledBack: false,
3775
- changes: migrated.changes
3776
- };
3777
- entries.push(entry);
3778
- if (migrated.changed) {
3779
- changedFiles += 1;
3780
- if (!dryRun) {
3781
- try {
3782
- if (backupDirectory) {
3783
- const backupRelativePath = resolveBackupRelativePath(cwd, file);
3784
- const backupFile = path11.resolve(backupDirectory, backupRelativePath);
3785
- await fs11.ensureDir(path11.dirname(backupFile));
3786
- await fs11.writeFile(backupFile, source, "utf8");
3787
- entry.backupFile = backupFile;
3788
- backupsWritten += 1;
3789
- }
3790
- await fs11.writeFile(file, migrated.code, "utf8");
3791
- entry.written = true;
3792
- wroteEntries.push({ file, source, entry });
3793
- writtenFiles += 1;
3794
- } catch (error) {
3795
- let rollbackCount = 0;
3796
- if (rollbackOnError && wroteEntries.length > 0) {
3797
- for (const written of [...wroteEntries].reverse()) {
3798
- try {
3799
- await fs11.writeFile(written.file, written.source, "utf8");
3800
- written.entry.written = false;
3801
- written.entry.rolledBack = true;
3802
- rollbackCount += 1;
3803
- } catch {
3804
- }
3805
- }
3806
- writtenFiles = Math.max(0, writtenFiles - rollbackCount);
3807
- }
3808
- const reason = error instanceof Error ? error.message : String(error);
3809
- const rollbackHint = rollbackOnError && rollbackCount > 0 ? ` Rolled back ${rollbackCount} previously written file(s).` : "";
3810
- throw new Error(`Failed to write migrated config "${file}": ${reason}.${rollbackHint}`);
3811
- }
3812
- }
3813
- } else {
3814
- unchangedFiles += 1;
4059
+
4060
+ // src/commands/migration-file-executor.ts
4061
+ async function rollbackWrittenEntries(wroteEntries) {
4062
+ let rollbackCount = 0;
4063
+ for (const written of [...wroteEntries].reverse()) {
4064
+ try {
4065
+ await fs13.writeFile(written.file, written.source, "utf8");
4066
+ written.entry.written = false;
4067
+ written.entry.rolledBack = true;
4068
+ rollbackCount += 1;
4069
+ } catch {
3815
4070
  }
3816
4071
  }
3817
- return {
3818
- reportKind: MIGRATION_REPORT_KIND,
3819
- schemaVersion: MIGRATION_REPORT_SCHEMA_VERSION,
3820
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3821
- tool: {
3822
- name: pkgName,
3823
- version: pkgVersion
3824
- },
4072
+ return rollbackCount;
4073
+ }
4074
+ async function executeMigrationFile(options) {
4075
+ const {
3825
4076
  cwd,
4077
+ file,
3826
4078
  dryRun,
3827
4079
  rollbackOnError,
3828
- ...backupDirectory ? { backupDirectory } : {},
3829
- scannedFiles,
3830
- changedFiles,
3831
- writtenFiles,
3832
- backupsWritten,
3833
- unchangedFiles,
3834
- missingFiles,
3835
- entries
4080
+ backupDirectory,
4081
+ wroteEntries
4082
+ } = options;
4083
+ const exists = await fs13.pathExists(file);
4084
+ if (!exists) {
4085
+ return {
4086
+ missing: true,
4087
+ changed: false,
4088
+ wrote: false,
4089
+ backupWritten: false
4090
+ };
4091
+ }
4092
+ const source = await fs13.readFile(file, "utf8");
4093
+ const migrated = migrateConfigSource(source);
4094
+ const entry = {
4095
+ file,
4096
+ changed: migrated.changed,
4097
+ written: false,
4098
+ rolledBack: false,
4099
+ changes: migrated.changes
3836
4100
  };
3837
- }
3838
- async function restoreConfigFiles(options) {
3839
- const cwd = path11.resolve(options.cwd);
3840
- const dryRun = options.dryRun ?? false;
3841
- const strict = options.strict ?? false;
3842
- const reportFile = path11.resolve(cwd, options.reportFile);
3843
- const report = await fs11.readJSON(reportFile);
3844
- if (report.reportKind !== void 0 && report.reportKind !== MIGRATION_REPORT_KIND) {
3845
- throw new Error(`Unsupported report kind "${report.reportKind}" in ${reportFile}.`);
4101
+ if (!migrated.changed || dryRun) {
4102
+ return {
4103
+ missing: false,
4104
+ changed: migrated.changed,
4105
+ wrote: false,
4106
+ backupWritten: false,
4107
+ entry
4108
+ };
3846
4109
  }
3847
- if (report.schemaVersion !== void 0 && (!Number.isInteger(report.schemaVersion) || report.schemaVersion > MIGRATION_REPORT_SCHEMA_VERSION)) {
3848
- throw new Error(
3849
- `Unsupported report schema version "${String(report.schemaVersion)}" in ${reportFile}. Current supported version is ${MIGRATION_REPORT_SCHEMA_VERSION}.`
3850
- );
4110
+ let backupWritten = false;
4111
+ try {
4112
+ if (backupDirectory) {
4113
+ const backupRelativePath = resolveBackupRelativePath(cwd, file);
4114
+ const backupFile = path13.resolve(backupDirectory, backupRelativePath);
4115
+ await fs13.ensureDir(path13.dirname(backupFile));
4116
+ await fs13.writeFile(backupFile, source, "utf8");
4117
+ entry.backupFile = backupFile;
4118
+ backupWritten = true;
4119
+ }
4120
+ await fs13.writeFile(file, migrated.code, "utf8");
4121
+ entry.written = true;
4122
+ wroteEntries.push({ file, source, entry });
4123
+ return {
4124
+ missing: false,
4125
+ changed: true,
4126
+ wrote: true,
4127
+ backupWritten,
4128
+ entry
4129
+ };
4130
+ } catch (error) {
4131
+ const rollbackCount = rollbackOnError && wroteEntries.length > 0 ? await rollbackWrittenEntries(wroteEntries) : 0;
4132
+ const reason = error instanceof Error ? error.message : String(error);
4133
+ const rollbackHint = rollbackOnError && rollbackCount > 0 ? ` Rolled back ${rollbackCount} previously written file(s).` : "";
4134
+ throw new Error(`Failed to write migrated config "${file}": ${reason}.${rollbackHint}`);
3851
4135
  }
3852
- const entries = Array.isArray(report.entries) ? report.entries : [];
4136
+ }
4137
+ async function restoreConfigEntries(entries, dryRun) {
3853
4138
  let scannedEntries = 0;
3854
4139
  let restorableEntries = 0;
3855
4140
  let restoredFiles = 0;
@@ -3858,26 +4143,113 @@ async function restoreConfigFiles(options) {
3858
4143
  const restored = [];
3859
4144
  for (const entry of entries) {
3860
4145
  scannedEntries += 1;
3861
- const targetFile = entry.file ? path11.resolve(entry.file) : void 0;
3862
- const backupFile = entry.backupFile ? path11.resolve(entry.backupFile) : void 0;
4146
+ const targetFile = entry.file ? path13.resolve(entry.file) : void 0;
4147
+ const backupFile = entry.backupFile ? path13.resolve(entry.backupFile) : void 0;
3863
4148
  if (!targetFile || !backupFile) {
3864
4149
  skippedEntries += 1;
3865
4150
  continue;
3866
4151
  }
3867
4152
  restorableEntries += 1;
3868
- const backupExists = await fs11.pathExists(backupFile);
4153
+ const backupExists = await fs13.pathExists(backupFile);
3869
4154
  if (!backupExists) {
3870
4155
  missingBackups += 1;
3871
4156
  continue;
3872
4157
  }
3873
4158
  if (!dryRun) {
3874
- const backupContent = await fs11.readFile(backupFile, "utf8");
3875
- await fs11.ensureDir(path11.dirname(targetFile));
3876
- await fs11.writeFile(targetFile, backupContent, "utf8");
4159
+ const backupContent = await fs13.readFile(backupFile, "utf8");
4160
+ await fs13.ensureDir(path13.dirname(targetFile));
4161
+ await fs13.writeFile(targetFile, backupContent, "utf8");
3877
4162
  }
3878
4163
  restoredFiles += 1;
3879
4164
  restored.push(targetFile);
3880
4165
  }
4166
+ return {
4167
+ scannedEntries,
4168
+ restorableEntries,
4169
+ restoredFiles,
4170
+ missingBackups,
4171
+ skippedEntries,
4172
+ restored
4173
+ };
4174
+ }
4175
+
4176
+ // src/commands/migration-report-loader.ts
4177
+ import fs14 from "fs-extra";
4178
+ async function loadMigrationReportForRestore(reportFile) {
4179
+ const report = await fs14.readJSON(reportFile);
4180
+ assertMigrationReportCompatibility(report, reportFile);
4181
+ return {
4182
+ ...report.reportKind === void 0 ? {} : { reportKind: report.reportKind },
4183
+ ...report.schemaVersion === void 0 ? {} : { schemaVersion: report.schemaVersion },
4184
+ entries: Array.isArray(report.entries) ? report.entries : []
4185
+ };
4186
+ }
4187
+
4188
+ // src/commands/migration-target-resolver.ts
4189
+ async function resolveMigrationTargetFiles(options) {
4190
+ const {
4191
+ cwd,
4192
+ files,
4193
+ workspace,
4194
+ maxDepth,
4195
+ include,
4196
+ exclude
4197
+ } = options;
4198
+ const resolvedMaxDepth = maxDepth ?? DEFAULT_WORKSPACE_MAX_DEPTH;
4199
+ const discoveredTargetFiles = files && files.length > 0 ? resolveTargetFiles(cwd, files) : workspace ? await collectWorkspaceConfigFiles(cwd, resolvedMaxDepth) : resolveTargetFiles(cwd);
4200
+ return filterTargetFiles(discoveredTargetFiles, cwd, include, exclude);
4201
+ }
4202
+
4203
+ // src/commands/migrate-config.ts
4204
+ async function migrateConfigFiles(options) {
4205
+ const cwd = path14.resolve(options.cwd);
4206
+ const dryRun = options.dryRun ?? false;
4207
+ const rollbackOnError = options.rollbackOnError ?? true;
4208
+ const backupDirectory = options.backupDir ? path14.resolve(cwd, options.backupDir) : void 0;
4209
+ const targetFiles = await resolveMigrationTargetFiles({
4210
+ cwd,
4211
+ files: options.files,
4212
+ workspace: options.workspace,
4213
+ maxDepth: options.maxDepth,
4214
+ include: options.include,
4215
+ exclude: options.exclude
4216
+ });
4217
+ const aggregation = createMigrationAggregationState();
4218
+ const wroteEntries = [];
4219
+ for (const file of targetFiles) {
4220
+ const result = await executeMigrationFile({
4221
+ cwd,
4222
+ file,
4223
+ dryRun,
4224
+ rollbackOnError,
4225
+ wroteEntries,
4226
+ ...backupDirectory ? { backupDirectory } : {}
4227
+ });
4228
+ collectMigrationExecutionResult(aggregation, result);
4229
+ }
4230
+ return buildMigrationReport(aggregation, {
4231
+ cwd,
4232
+ dryRun,
4233
+ rollbackOnError,
4234
+ ...backupDirectory ? { backupDirectory } : {},
4235
+ toolName: pkgName,
4236
+ toolVersion: pkgVersion
4237
+ });
4238
+ }
4239
+ async function restoreConfigFiles(options) {
4240
+ const cwd = path14.resolve(options.cwd);
4241
+ const dryRun = options.dryRun ?? false;
4242
+ const strict = options.strict ?? false;
4243
+ const reportFile = path14.resolve(cwd, options.reportFile);
4244
+ const report = await loadMigrationReportForRestore(reportFile);
4245
+ const {
4246
+ scannedEntries,
4247
+ restorableEntries,
4248
+ restoredFiles,
4249
+ missingBackups,
4250
+ skippedEntries,
4251
+ restored
4252
+ } = await restoreConfigEntries(report.entries, dryRun);
3881
4253
  if (strict && missingBackups > 0) {
3882
4254
  throw new Error(`Restore failed: ${missingBackups} backup file(s) missing in report ${reportFile}.`);
3883
4255
  }
@@ -3897,105 +4269,7 @@ async function restoreConfigFiles(options) {
3897
4269
  };
3898
4270
  }
3899
4271
 
3900
- // src/cli/commands.ts
3901
- import process7 from "process";
3902
- import { CONFIG_NAME, getConfig, initConfig } from "@tailwindcss-mangle/config";
3903
-
3904
- // ../../node_modules/.pnpm/defu@6.1.4/node_modules/defu/dist/defu.mjs
3905
- function isPlainObject(value) {
3906
- if (value === null || typeof value !== "object") {
3907
- return false;
3908
- }
3909
- const prototype = Object.getPrototypeOf(value);
3910
- if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
3911
- return false;
3912
- }
3913
- if (Symbol.iterator in value) {
3914
- return false;
3915
- }
3916
- if (Symbol.toStringTag in value) {
3917
- return Object.prototype.toString.call(value) === "[object Module]";
3918
- }
3919
- return true;
3920
- }
3921
- function _defu(baseObject, defaults, namespace = ".", merger) {
3922
- if (!isPlainObject(defaults)) {
3923
- return _defu(baseObject, {}, namespace, merger);
3924
- }
3925
- const object = Object.assign({}, defaults);
3926
- for (const key in baseObject) {
3927
- if (key === "__proto__" || key === "constructor") {
3928
- continue;
3929
- }
3930
- const value = baseObject[key];
3931
- if (value === null || value === void 0) {
3932
- continue;
3933
- }
3934
- if (merger && merger(object, key, value, namespace)) {
3935
- continue;
3936
- }
3937
- if (Array.isArray(value) && Array.isArray(object[key])) {
3938
- object[key] = [...value, ...object[key]];
3939
- } else if (isPlainObject(value) && isPlainObject(object[key])) {
3940
- object[key] = _defu(
3941
- value,
3942
- object[key],
3943
- (namespace ? `${namespace}.` : "") + key.toString(),
3944
- merger
3945
- );
3946
- } else {
3947
- object[key] = value;
3948
- }
3949
- }
3950
- return object;
3951
- }
3952
- function createDefu(merger) {
3953
- return (...arguments_) => (
3954
- // eslint-disable-next-line unicorn/no-array-reduce
3955
- arguments_.reduce((p, c) => _defu(p, c, "", merger), {})
3956
- );
3957
- }
3958
- var defu = createDefu();
3959
- var defuFn = createDefu((object, key, currentValue) => {
3960
- if (object[key] !== void 0 && typeof currentValue === "function") {
3961
- object[key] = currentValue(object[key]);
3962
- return true;
3963
- }
3964
- });
3965
- var defuArrayFn = createDefu((object, key, currentValue) => {
3966
- if (Array.isArray(object[key]) && typeof currentValue === "function") {
3967
- object[key] = currentValue(object[key]);
3968
- return true;
3969
- }
3970
- });
3971
-
3972
- // ../shared/src/utils.ts
3973
- var defuOverrideArray = createDefu((obj, key, value) => {
3974
- if (Array.isArray(obj[key]) && Array.isArray(value)) {
3975
- obj[key] = value;
3976
- return true;
3977
- }
3978
- });
3979
- var preserveClassNames = [
3980
- // https://tailwindcss.com/docs/transition-timing-function start
3981
- // https://github.com/sonofmagic/tailwindcss-mangle/issues/21
3982
- "ease-out",
3983
- "ease-linear",
3984
- "ease-in",
3985
- "ease-in-out"
3986
- // https://tailwindcss.com/docs/transition-timing-function end
3987
- ];
3988
- var preserveClassNamesMap = preserveClassNames.reduce((acc, cur) => {
3989
- acc[cur] = true;
3990
- return acc;
3991
- }, {});
3992
- var acceptChars = [..."abcdefghijklmnopqrstuvwxyz"];
3993
-
3994
- // src/cli/commands.ts
3995
- import cac from "cac";
3996
- import fs12 from "fs-extra";
3997
- import path12 from "pathe";
3998
- var tailwindcssPatchCommands = ["install", "extract", "tokens", "init", "migrate", "restore", "validate", "status"];
4272
+ // src/commands/validate.ts
3999
4273
  var VALIDATE_EXIT_CODES = {
4000
4274
  OK: 0,
4001
4275
  REPORT_INCOMPATIBLE: 21,
@@ -4010,7 +4284,7 @@ var VALIDATE_FAILURE_REASONS = [
4010
4284
  "unknown-error"
4011
4285
  ];
4012
4286
  var IO_ERROR_CODES = /* @__PURE__ */ new Set(["ENOENT", "EACCES", "EPERM", "EISDIR", "ENOTDIR", "EMFILE", "ENFILE"]);
4013
- function isNodeError(error) {
4287
+ function isNodeError2(error) {
4014
4288
  return !!error && typeof error === "object" && ("code" in error || "message" in error);
4015
4289
  }
4016
4290
  function classifyValidateError(error) {
@@ -4029,7 +4303,7 @@ function classifyValidateError(error) {
4029
4303
  message
4030
4304
  };
4031
4305
  }
4032
- if (isNodeError(error) && typeof error.code === "string" && IO_ERROR_CODES.has(error.code)) {
4306
+ if (isNodeError2(error) && typeof error.code === "string" && IO_ERROR_CODES.has(error.code)) {
4033
4307
  return {
4034
4308
  reason: "io-error",
4035
4309
  exitCode: VALIDATE_EXIT_CODES.IO_ERROR,
@@ -4052,6 +4326,17 @@ var ValidateCommandError = class extends Error {
4052
4326
  this.exitCode = summary.exitCode;
4053
4327
  }
4054
4328
  };
4329
+
4330
+ // src/commands/types.ts
4331
+ var tailwindcssPatchCommands = ["install", "extract", "tokens", "init", "migrate", "restore", "validate", "status"];
4332
+
4333
+ // src/commands/cli.ts
4334
+ import cac from "cac";
4335
+
4336
+ // src/commands/command-definitions.ts
4337
+ import process7 from "process";
4338
+
4339
+ // src/commands/token-output.ts
4055
4340
  var TOKEN_FORMATS = ["json", "lines", "grouped-json"];
4056
4341
  var DEFAULT_TOKEN_REPORT = ".tw-patch/tw-token-report.json";
4057
4342
  function formatTokenLine(entry) {
@@ -4073,68 +4358,8 @@ function formatGroupedPreview(map, limit = 3) {
4073
4358
  moreFiles: Math.max(0, files.length - limit)
4074
4359
  };
4075
4360
  }
4076
- function resolveCwd(rawCwd) {
4077
- if (!rawCwd) {
4078
- return process7.cwd();
4079
- }
4080
- return path12.resolve(rawCwd);
4081
- }
4082
- function createDefaultRunner(factory) {
4083
- let promise;
4084
- return () => {
4085
- if (!promise) {
4086
- promise = factory();
4087
- }
4088
- return promise;
4089
- };
4090
- }
4091
- async function loadPatchOptionsForCwd(cwd, overrides) {
4092
- const { config } = await getConfig(cwd);
4093
- const legacyConfig = config;
4094
- const base = config?.registry ? fromUnifiedConfig(config.registry) : legacyConfig?.patch ? fromLegacyOptions({ patch: legacyConfig.patch }) : {};
4095
- const merged = defu(overrides ?? {}, base);
4096
- return merged;
4097
- }
4098
- function createCommandContext(cli, command, commandName, args, cwd) {
4099
- let cachedOptions;
4100
- let cachedPatcher;
4101
- let cachedConfig;
4102
- const loadPatchOptionsForContext = (overrides) => {
4103
- if (overrides) {
4104
- return loadPatchOptionsForCwd(cwd, overrides);
4105
- }
4106
- if (!cachedOptions) {
4107
- cachedOptions = loadPatchOptionsForCwd(cwd);
4108
- }
4109
- return cachedOptions;
4110
- };
4111
- const createPatcherForContext = async (overrides) => {
4112
- if (overrides) {
4113
- const patchOptions = await loadPatchOptionsForCwd(cwd, overrides);
4114
- return new TailwindcssPatcher(patchOptions);
4115
- }
4116
- if (!cachedPatcher) {
4117
- cachedPatcher = loadPatchOptionsForContext().then((options) => new TailwindcssPatcher(options));
4118
- }
4119
- return cachedPatcher;
4120
- };
4121
- return {
4122
- cli,
4123
- command,
4124
- commandName,
4125
- args,
4126
- cwd,
4127
- logger: logger_default,
4128
- loadConfig: () => {
4129
- if (!cachedConfig) {
4130
- cachedConfig = getConfig(cwd);
4131
- }
4132
- return cachedConfig;
4133
- },
4134
- loadPatchOptions: loadPatchOptionsForContext,
4135
- createPatcher: createPatcherForContext
4136
- };
4137
- }
4361
+
4362
+ // src/commands/command-definitions.ts
4138
4363
  function createCwdOptionDefinition(description = "Working directory") {
4139
4364
  return {
4140
4365
  flags: "--cwd <dir>",
@@ -4224,6 +4449,8 @@ function buildDefaultCommandDefinitions() {
4224
4449
  }
4225
4450
  };
4226
4451
  }
4452
+
4453
+ // src/commands/command-metadata.ts
4227
4454
  function addPrefixIfMissing(value, prefix) {
4228
4455
  if (!prefix || value.startsWith(prefix)) {
4229
4456
  return value;
@@ -4251,30 +4478,93 @@ function resolveOptionDefinitions(defaults, override) {
4251
4478
  }
4252
4479
  return [...defaults, ...customDefs];
4253
4480
  }
4254
- function applyCommandOptions(command, optionDefs) {
4255
- for (const option of optionDefs) {
4256
- command.option(option.flags, option.description ?? "", option.config);
4257
- }
4481
+ function resolveCommandMetadata(command, mountOptions, prefix, defaults) {
4482
+ const names = resolveCommandNames(command, mountOptions, prefix);
4483
+ const definition = defaults[command];
4484
+ const override = mountOptions.commandOptions?.[command];
4485
+ const description = override?.description ?? definition.description;
4486
+ const optionDefs = resolveOptionDefinitions(definition.optionDefs, override);
4487
+ return { ...names, description, optionDefs };
4488
+ }
4489
+ function applyCommandOptions(command, optionDefs) {
4490
+ for (const option of optionDefs) {
4491
+ command.option(option.flags, option.description ?? "", option.config);
4492
+ }
4493
+ }
4494
+
4495
+ // src/commands/command-context.ts
4496
+ import process8 from "process";
4497
+ import path15 from "pathe";
4498
+ function resolveCommandCwd(rawCwd) {
4499
+ if (!rawCwd) {
4500
+ return process8.cwd();
4501
+ }
4502
+ return path15.resolve(rawCwd);
4503
+ }
4504
+ function createMemoizedPromiseRunner(factory) {
4505
+ let promise;
4506
+ return () => {
4507
+ if (!promise) {
4508
+ promise = factory();
4509
+ }
4510
+ return promise;
4511
+ };
4512
+ }
4513
+ function createTailwindcssPatchCommandContext(cli, command, commandName, args, cwd) {
4514
+ const loadCachedConfig = createMemoizedPromiseRunner(
4515
+ () => loadWorkspaceConfigModule().then((mod) => mod.getConfig(cwd))
4516
+ );
4517
+ const loadCachedPatchOptions = createMemoizedPromiseRunner(
4518
+ () => loadPatchOptionsForWorkspace(cwd)
4519
+ );
4520
+ const createCachedPatcher = createMemoizedPromiseRunner(async () => {
4521
+ const patchOptions = await loadCachedPatchOptions();
4522
+ return new TailwindcssPatcher(patchOptions);
4523
+ });
4524
+ const loadPatchOptionsForContext = (overrides) => {
4525
+ if (overrides) {
4526
+ return loadPatchOptionsForWorkspace(cwd, overrides);
4527
+ }
4528
+ return loadCachedPatchOptions();
4529
+ };
4530
+ const createPatcherForContext = async (overrides) => {
4531
+ if (overrides) {
4532
+ const patchOptions = await loadPatchOptionsForWorkspace(cwd, overrides);
4533
+ return new TailwindcssPatcher(patchOptions);
4534
+ }
4535
+ return createCachedPatcher();
4536
+ };
4537
+ return {
4538
+ cli,
4539
+ command,
4540
+ commandName,
4541
+ args,
4542
+ cwd,
4543
+ logger: logger_default,
4544
+ loadConfig: loadCachedConfig,
4545
+ loadPatchOptions: loadPatchOptionsForContext,
4546
+ createPatcher: createPatcherForContext
4547
+ };
4258
4548
  }
4549
+
4550
+ // src/commands/command-runtime.ts
4259
4551
  function runWithCommandHandler(cli, command, commandName, args, handler, defaultHandler) {
4260
- const cwd = resolveCwd(args.cwd);
4261
- const context = createCommandContext(cli, command, commandName, args, cwd);
4262
- const runDefault = createDefaultRunner(() => defaultHandler(context));
4552
+ const cwd = resolveCommandCwd(args.cwd);
4553
+ const context = createTailwindcssPatchCommandContext(cli, command, commandName, args, cwd);
4554
+ const runDefault = createMemoizedPromiseRunner(() => defaultHandler(context));
4263
4555
  if (!handler) {
4264
4556
  return runDefault();
4265
4557
  }
4266
4558
  return handler(context, runDefault);
4267
4559
  }
4268
- function resolveCommandMetadata(command, mountOptions, prefix, defaults) {
4269
- const names = resolveCommandNames(command, mountOptions, prefix);
4270
- const definition = defaults[command];
4271
- const override = mountOptions.commandOptions?.[command];
4272
- const description = override?.description ?? definition.description;
4273
- const optionDefs = resolveOptionDefinitions(definition.optionDefs, override);
4274
- return { ...names, description, optionDefs };
4275
- }
4276
- async function installCommandDefaultHandler(ctx) {
4277
- const patcher = await ctx.createPatcher();
4560
+
4561
+ // src/commands/basic-handlers.ts
4562
+ import process9 from "process";
4563
+ import fs15 from "fs-extra";
4564
+ import path16 from "pathe";
4565
+ var DEFAULT_CONFIG_NAME = "tailwindcss-mangle";
4566
+ async function installCommandDefaultHandler(_ctx) {
4567
+ const patcher = new TailwindcssPatcher();
4278
4568
  await patcher.patch();
4279
4569
  logger_default.success("Tailwind CSS runtime patched successfully.");
4280
4570
  }
@@ -4325,18 +4615,18 @@ async function tokensCommandDefaultHandler(ctx) {
4325
4615
  const grouped = format === "grouped-json" ? buildGrouped() : null;
4326
4616
  const resolveGrouped = () => grouped ?? buildGrouped();
4327
4617
  if (shouldWrite) {
4328
- const target = path12.resolve(targetFile);
4329
- await fs12.ensureDir(path12.dirname(target));
4618
+ const target = path16.resolve(targetFile);
4619
+ await fs15.ensureDir(path16.dirname(target));
4330
4620
  if (format === "json") {
4331
- await fs12.writeJSON(target, report, { spaces: 2 });
4621
+ await fs15.writeJSON(target, report, { spaces: 2 });
4332
4622
  } else if (format === "grouped-json") {
4333
- await fs12.writeJSON(target, resolveGrouped(), { spaces: 2 });
4623
+ await fs15.writeJSON(target, resolveGrouped(), { spaces: 2 });
4334
4624
  } else {
4335
4625
  const lines = report.entries.map(formatTokenLine);
4336
- await fs12.writeFile(target, `${lines.join("\n")}
4626
+ await fs15.writeFile(target, `${lines.join("\n")}
4337
4627
  `, "utf8");
4338
4628
  }
4339
- logger_default.success(`Collected ${report.entries.length} tokens (${format}) \u2192 ${target.replace(process7.cwd(), ".")}`);
4629
+ logger_default.success(`Collected ${report.entries.length} tokens (${format}) \u2192 ${target.replace(process9.cwd(), ".")}`);
4340
4630
  } else {
4341
4631
  logger_default.success(`Collected ${report.entries.length} tokens from ${report.filesScanned} files.`);
4342
4632
  if (format === "lines") {
@@ -4375,60 +4665,94 @@ async function tokensCommandDefaultHandler(ctx) {
4375
4665
  return report;
4376
4666
  }
4377
4667
  async function initCommandDefaultHandler(ctx) {
4378
- await initConfig(ctx.cwd);
4379
- logger_default.success(`\u2728 ${CONFIG_NAME}.config.ts initialized!`);
4668
+ const configModule = await loadWorkspaceConfigModule();
4669
+ await configModule.initConfig(ctx.cwd);
4670
+ const configName = configModule.CONFIG_NAME || DEFAULT_CONFIG_NAME;
4671
+ logger_default.success(`\u2728 ${configName}.config.ts initialized!`);
4380
4672
  }
4381
- async function migrateCommandDefaultHandler(ctx) {
4382
- const { args } = ctx;
4383
- const normalizePatternArgs = (value) => {
4384
- if (!value) {
4385
- return void 0;
4386
- }
4387
- const raw = Array.isArray(value) ? value : [value];
4388
- const values = raw.flatMap((item) => item.split(",")).map((item) => item.trim()).filter(Boolean);
4389
- return values.length > 0 ? values : void 0;
4673
+
4674
+ // src/commands/migration-args.ts
4675
+ function normalizePatternArgs(value) {
4676
+ if (!value) {
4677
+ return void 0;
4678
+ }
4679
+ const raw = Array.isArray(value) ? value : [value];
4680
+ const values = raw.flatMap((item) => item.split(",")).map((item) => item.trim()).filter(Boolean);
4681
+ return values.length > 0 ? values : void 0;
4682
+ }
4683
+ function parseMaxDepth(value) {
4684
+ if (value === void 0) {
4685
+ return {
4686
+ maxDepth: void 0,
4687
+ hasInvalidMaxDepth: false
4688
+ };
4689
+ }
4690
+ const parsed = Number(value);
4691
+ if (!Number.isFinite(parsed) || parsed < 0) {
4692
+ return {
4693
+ maxDepth: void 0,
4694
+ hasInvalidMaxDepth: true
4695
+ };
4696
+ }
4697
+ return {
4698
+ maxDepth: Math.floor(parsed),
4699
+ hasInvalidMaxDepth: false
4390
4700
  };
4701
+ }
4702
+ function resolveMigrateCommandArgs(args) {
4391
4703
  const include = normalizePatternArgs(args.include);
4392
4704
  const exclude = normalizePatternArgs(args.exclude);
4393
- const parsedMaxDepth = args.maxDepth === void 0 ? void 0 : Number(args.maxDepth);
4394
- const maxDepth = parsedMaxDepth !== void 0 && Number.isFinite(parsedMaxDepth) && parsedMaxDepth >= 0 ? Math.floor(parsedMaxDepth) : void 0;
4705
+ const { maxDepth, hasInvalidMaxDepth } = parseMaxDepth(args.maxDepth);
4395
4706
  const checkMode = args.check ?? false;
4396
4707
  const dryRun = args.dryRun ?? checkMode;
4397
- if (args.workspace && args.maxDepth !== void 0 && maxDepth === void 0) {
4398
- logger_default.warn(`Invalid --max-depth value "${String(args.maxDepth)}", fallback to default depth.`);
4399
- }
4400
- const report = await migrateConfigFiles({
4401
- cwd: ctx.cwd,
4708
+ return {
4709
+ include,
4710
+ exclude,
4711
+ maxDepth,
4712
+ checkMode,
4402
4713
  dryRun,
4403
- ...args.config ? { files: [args.config] } : {},
4404
- ...args.workspace ? { workspace: true } : {},
4405
- ...args.workspace && maxDepth !== void 0 ? { maxDepth } : {},
4406
- ...args.backupDir ? { backupDir: args.backupDir } : {},
4407
- ...include ? { include } : {},
4408
- ...exclude ? { exclude } : {}
4409
- });
4410
- if (args.reportFile) {
4411
- const reportPath = path12.resolve(ctx.cwd, args.reportFile);
4412
- await fs12.ensureDir(path12.dirname(reportPath));
4413
- await fs12.writeJSON(reportPath, report, { spaces: 2 });
4414
- logger_default.info(`Migration report written: ${reportPath.replace(process7.cwd(), ".")}`);
4415
- }
4416
- if (args.json) {
4417
- logger_default.log(JSON.stringify(report, null, 2));
4418
- if (checkMode && report.changedFiles > 0) {
4419
- throw new Error(`Migration check failed: ${report.changedFiles} file(s) still need migration.`);
4420
- }
4421
- if (report.scannedFiles === 0) {
4422
- logger_default.warn("No config files found for migration.");
4423
- }
4424
- return report;
4425
- }
4426
- if (report.scannedFiles === 0) {
4427
- logger_default.warn("No config files found for migration.");
4428
- return report;
4429
- }
4714
+ hasInvalidMaxDepth
4715
+ };
4716
+ }
4717
+ function resolveRestoreCommandArgs(args) {
4718
+ return {
4719
+ reportFile: args.reportFile ?? ".tw-patch/migrate-report.json",
4720
+ dryRun: args.dryRun ?? false,
4721
+ strict: args.strict ?? false
4722
+ };
4723
+ }
4724
+ function resolveValidateCommandArgs(args) {
4725
+ return {
4726
+ reportFile: args.reportFile ?? ".tw-patch/migrate-report.json",
4727
+ strict: args.strict ?? false
4728
+ };
4729
+ }
4730
+
4731
+ // src/commands/migration-output.ts
4732
+ import process10 from "process";
4733
+ import fs16 from "fs-extra";
4734
+ import path17 from "pathe";
4735
+ function formatPathForLog(file) {
4736
+ return file.replace(process10.cwd(), ".");
4737
+ }
4738
+ function createMigrationCheckFailureError(changedFiles) {
4739
+ return new Error(`Migration check failed: ${changedFiles} file(s) still need migration.`);
4740
+ }
4741
+ async function writeMigrationReportFile(cwd, reportFile, report) {
4742
+ const reportPath = path17.resolve(cwd, reportFile);
4743
+ await fs16.ensureDir(path17.dirname(reportPath));
4744
+ await fs16.writeJSON(reportPath, report, { spaces: 2 });
4745
+ logger_default.info(`Migration report written: ${formatPathForLog(reportPath)}`);
4746
+ }
4747
+ function logMigrationReportAsJson(report) {
4748
+ logger_default.log(JSON.stringify(report, null, 2));
4749
+ }
4750
+ function logNoMigrationConfigFilesWarning() {
4751
+ logger_default.warn("No config files found for migration.");
4752
+ }
4753
+ function logMigrationEntries(report, dryRun) {
4430
4754
  for (const entry of report.entries) {
4431
- const fileLabel = entry.file.replace(process7.cwd(), ".");
4755
+ const fileLabel = formatPathForLog(entry.file);
4432
4756
  if (!entry.changed) {
4433
4757
  logger_default.info(`No changes: ${fileLabel}`);
4434
4758
  continue;
@@ -4442,105 +4766,156 @@ async function migrateCommandDefaultHandler(ctx) {
4442
4766
  logger_default.info(` - ${change}`);
4443
4767
  }
4444
4768
  if (entry.backupFile) {
4445
- logger_default.info(` - backup: ${entry.backupFile.replace(process7.cwd(), ".")}`);
4769
+ logger_default.info(` - backup: ${formatPathForLog(entry.backupFile)}`);
4446
4770
  }
4447
4771
  }
4772
+ }
4773
+ function logMigrationSummary(report) {
4448
4774
  logger_default.info(
4449
4775
  `Migration summary: scanned=${report.scannedFiles}, changed=${report.changedFiles}, written=${report.writtenFiles}, backups=${report.backupsWritten}, missing=${report.missingFiles}, unchanged=${report.unchangedFiles}`
4450
4776
  );
4451
- if (checkMode && report.changedFiles > 0) {
4452
- throw new Error(`Migration check failed: ${report.changedFiles} file(s) still need migration.`);
4453
- }
4454
- return report;
4455
4777
  }
4456
- async function restoreCommandDefaultHandler(ctx) {
4457
- const { args } = ctx;
4458
- const reportFile = args.reportFile ?? ".tw-patch/migrate-report.json";
4459
- const result = await restoreConfigFiles({
4460
- cwd: ctx.cwd,
4461
- reportFile,
4462
- dryRun: args.dryRun ?? false,
4463
- strict: args.strict ?? false
4464
- });
4465
- if (args.json) {
4466
- logger_default.log(JSON.stringify(result, null, 2));
4467
- return result;
4468
- }
4778
+ function logRestoreResultAsJson(result) {
4779
+ logger_default.log(JSON.stringify(result, null, 2));
4780
+ }
4781
+ function logRestoreSummary(result) {
4469
4782
  logger_default.info(
4470
4783
  `Restore summary: scanned=${result.scannedEntries}, restorable=${result.restorableEntries}, restored=${result.restoredFiles}, missingBackups=${result.missingBackups}, skipped=${result.skippedEntries}`
4471
4784
  );
4472
4785
  if (result.restored.length > 0) {
4473
4786
  const preview = result.restored.slice(0, 5);
4474
4787
  for (const file of preview) {
4475
- logger_default.info(` - ${file.replace(process7.cwd(), ".")}`);
4788
+ logger_default.info(` - ${formatPathForLog(file)}`);
4476
4789
  }
4477
4790
  if (result.restored.length > preview.length) {
4478
4791
  logger_default.info(` ...and ${result.restored.length - preview.length} more`);
4479
4792
  }
4480
4793
  }
4481
- return result;
4482
4794
  }
4483
- async function validateCommandDefaultHandler(ctx) {
4795
+ function logValidateSuccessAsJson(result) {
4796
+ const payload = {
4797
+ ok: true,
4798
+ ...result
4799
+ };
4800
+ logger_default.log(JSON.stringify(payload, null, 2));
4801
+ }
4802
+ function logValidateSuccessSummary(result) {
4803
+ logger_default.success(
4804
+ `Migration report validated: scanned=${result.scannedEntries}, restorable=${result.restorableEntries}, missingBackups=${result.missingBackups}, skipped=${result.skippedEntries}`
4805
+ );
4806
+ if (result.reportKind || result.reportSchemaVersion !== void 0) {
4807
+ const kind = result.reportKind ?? "unknown";
4808
+ const schema = result.reportSchemaVersion === void 0 ? "unknown" : String(result.reportSchemaVersion);
4809
+ logger_default.info(` metadata: kind=${kind}, schema=${schema}`);
4810
+ }
4811
+ }
4812
+ function logValidateFailureAsJson(summary) {
4813
+ const payload = {
4814
+ ok: false,
4815
+ reason: summary.reason,
4816
+ exitCode: summary.exitCode,
4817
+ message: summary.message
4818
+ };
4819
+ logger_default.log(JSON.stringify(payload, null, 2));
4820
+ }
4821
+ function logValidateFailureSummary(summary) {
4822
+ logger_default.error(`Validation failed [${summary.reason}] (exit ${summary.exitCode}): ${summary.message}`);
4823
+ }
4824
+
4825
+ // src/commands/migrate-handler.ts
4826
+ async function migrateCommandDefaultHandler(ctx) {
4484
4827
  const { args } = ctx;
4485
- const reportFile = args.reportFile ?? ".tw-patch/migrate-report.json";
4486
- try {
4487
- const result = await restoreConfigFiles({
4488
- cwd: ctx.cwd,
4489
- reportFile,
4490
- dryRun: true,
4491
- strict: args.strict ?? false
4492
- });
4493
- if (args.json) {
4494
- const payload = {
4495
- ok: true,
4496
- ...result
4497
- };
4498
- logger_default.log(JSON.stringify(payload, null, 2));
4499
- return result;
4828
+ const {
4829
+ include,
4830
+ exclude,
4831
+ maxDepth,
4832
+ checkMode,
4833
+ dryRun,
4834
+ hasInvalidMaxDepth
4835
+ } = resolveMigrateCommandArgs(args);
4836
+ if (args.workspace && hasInvalidMaxDepth) {
4837
+ logger_default.warn(`Invalid --max-depth value "${String(args.maxDepth)}", fallback to default depth.`);
4838
+ }
4839
+ const report = await migrateConfigFiles({
4840
+ cwd: ctx.cwd,
4841
+ dryRun,
4842
+ ...args.config ? { files: [args.config] } : {},
4843
+ ...args.workspace ? { workspace: true } : {},
4844
+ ...args.workspace && maxDepth !== void 0 ? { maxDepth } : {},
4845
+ ...args.backupDir ? { backupDir: args.backupDir } : {},
4846
+ ...include ? { include } : {},
4847
+ ...exclude ? { exclude } : {}
4848
+ });
4849
+ if (args.reportFile) {
4850
+ await writeMigrationReportFile(ctx.cwd, args.reportFile, report);
4851
+ }
4852
+ if (args.json) {
4853
+ logMigrationReportAsJson(report);
4854
+ if (checkMode && report.changedFiles > 0) {
4855
+ throw createMigrationCheckFailureError(report.changedFiles);
4500
4856
  }
4501
- logger_default.success(
4502
- `Migration report validated: scanned=${result.scannedEntries}, restorable=${result.restorableEntries}, missingBackups=${result.missingBackups}, skipped=${result.skippedEntries}`
4503
- );
4504
- if (result.reportKind || result.reportSchemaVersion !== void 0) {
4505
- const kind = result.reportKind ?? "unknown";
4506
- const schema = result.reportSchemaVersion === void 0 ? "unknown" : String(result.reportSchemaVersion);
4507
- logger_default.info(` metadata: kind=${kind}, schema=${schema}`);
4857
+ if (report.scannedFiles === 0) {
4858
+ logNoMigrationConfigFilesWarning();
4508
4859
  }
4860
+ return report;
4861
+ }
4862
+ if (report.scannedFiles === 0) {
4863
+ logNoMigrationConfigFilesWarning();
4864
+ return report;
4865
+ }
4866
+ logMigrationEntries(report, dryRun);
4867
+ logMigrationSummary(report);
4868
+ if (checkMode && report.changedFiles > 0) {
4869
+ throw createMigrationCheckFailureError(report.changedFiles);
4870
+ }
4871
+ return report;
4872
+ }
4873
+
4874
+ // src/commands/restore-handler.ts
4875
+ async function restoreCommandDefaultHandler(ctx) {
4876
+ const { args } = ctx;
4877
+ const restoreArgs = resolveRestoreCommandArgs(args);
4878
+ const result = await restoreConfigFiles({
4879
+ cwd: ctx.cwd,
4880
+ reportFile: restoreArgs.reportFile,
4881
+ dryRun: restoreArgs.dryRun,
4882
+ strict: restoreArgs.strict
4883
+ });
4884
+ if (args.json) {
4885
+ logRestoreResultAsJson(result);
4509
4886
  return result;
4510
- } catch (error) {
4511
- const summary = classifyValidateError(error);
4512
- if (args.json) {
4513
- const payload = {
4514
- ok: false,
4515
- reason: summary.reason,
4516
- exitCode: summary.exitCode,
4517
- message: summary.message
4518
- };
4519
- logger_default.log(JSON.stringify(payload, null, 2));
4520
- } else {
4521
- logger_default.error(`Validation failed [${summary.reason}] (exit ${summary.exitCode}): ${summary.message}`);
4522
- }
4523
- throw new ValidateCommandError(summary, { cause: error });
4524
4887
  }
4888
+ logRestoreSummary(result);
4889
+ return result;
4525
4890
  }
4891
+
4892
+ // src/commands/status-output.ts
4526
4893
  function formatFilesHint(entry) {
4527
4894
  if (!entry.files.length) {
4528
4895
  return "";
4529
4896
  }
4530
4897
  return ` (${entry.files.join(", ")})`;
4531
4898
  }
4532
- async function statusCommandDefaultHandler(ctx) {
4533
- const patcher = await ctx.createPatcher();
4534
- const report = await patcher.getPatchStatus();
4535
- if (ctx.args.json) {
4536
- logger_default.log(JSON.stringify(report, null, 2));
4537
- return report;
4538
- }
4539
- const applied = report.entries.filter((entry) => entry.status === "applied");
4540
- const pending = report.entries.filter((entry) => entry.status === "not-applied");
4541
- const skipped = report.entries.filter((entry) => entry.status === "skipped" || entry.status === "unsupported");
4542
- const packageLabel = `${report.package.name ?? "tailwindcss"}@${report.package.version ?? "unknown"}`;
4543
- logger_default.info(`Patch status for ${packageLabel} (v${report.majorVersion})`);
4899
+ function formatPackageLabel(report) {
4900
+ return `${report.package.name ?? "tailwindcss"}@${report.package.version ?? "unknown"}`;
4901
+ }
4902
+ function partitionStatusEntries(report) {
4903
+ return {
4904
+ applied: report.entries.filter((entry) => entry.status === "applied"),
4905
+ pending: report.entries.filter((entry) => entry.status === "not-applied"),
4906
+ skipped: report.entries.filter((entry) => entry.status === "skipped" || entry.status === "unsupported")
4907
+ };
4908
+ }
4909
+ function logStatusReportAsJson(report) {
4910
+ logger_default.log(JSON.stringify(report, null, 2));
4911
+ }
4912
+ function logStatusReportSummary(report) {
4913
+ const {
4914
+ applied,
4915
+ pending,
4916
+ skipped
4917
+ } = partitionStatusEntries(report);
4918
+ logger_default.info(`Patch status for ${formatPackageLabel(report)} (v${report.majorVersion})`);
4544
4919
  if (applied.length) {
4545
4920
  logger_default.success("Applied:");
4546
4921
  applied.forEach((entry) => logger_default.success(` \u2022 ${entry.name}${formatFilesHint(entry)}`));
@@ -4548,7 +4923,7 @@ async function statusCommandDefaultHandler(ctx) {
4548
4923
  if (pending.length) {
4549
4924
  logger_default.warn("Needs attention:");
4550
4925
  pending.forEach((entry) => {
4551
- const details = entry.reason ? ` \u2013 ${entry.reason}` : "";
4926
+ const details = entry.reason ? ` - ${entry.reason}` : "";
4552
4927
  logger_default.warn(` \u2022 ${entry.name}${formatFilesHint(entry)}${details}`);
4553
4928
  });
4554
4929
  } else {
@@ -4557,151 +4932,89 @@ async function statusCommandDefaultHandler(ctx) {
4557
4932
  if (skipped.length) {
4558
4933
  logger_default.info("Skipped:");
4559
4934
  skipped.forEach((entry) => {
4560
- const details = entry.reason ? ` \u2013 ${entry.reason}` : "";
4935
+ const details = entry.reason ? ` - ${entry.reason}` : "";
4561
4936
  logger_default.info(` \u2022 ${entry.name}${details}`);
4562
4937
  });
4563
4938
  }
4939
+ }
4940
+
4941
+ // src/commands/status-handler.ts
4942
+ async function statusCommandDefaultHandler(ctx) {
4943
+ const patcher = await ctx.createPatcher();
4944
+ const report = await patcher.getPatchStatus();
4945
+ if (ctx.args.json) {
4946
+ logStatusReportAsJson(report);
4947
+ return report;
4948
+ }
4949
+ logStatusReportSummary(report);
4564
4950
  return report;
4565
4951
  }
4952
+
4953
+ // src/commands/validate-handler.ts
4954
+ async function validateCommandDefaultHandler(ctx) {
4955
+ const { args } = ctx;
4956
+ const validateArgs = resolveValidateCommandArgs(args);
4957
+ try {
4958
+ const result = await restoreConfigFiles({
4959
+ cwd: ctx.cwd,
4960
+ reportFile: validateArgs.reportFile,
4961
+ dryRun: true,
4962
+ strict: validateArgs.strict
4963
+ });
4964
+ if (args.json) {
4965
+ logValidateSuccessAsJson(result);
4966
+ return result;
4967
+ }
4968
+ logValidateSuccessSummary(result);
4969
+ return result;
4970
+ } catch (error) {
4971
+ const summary = classifyValidateError(error);
4972
+ if (args.json) {
4973
+ logValidateFailureAsJson(summary);
4974
+ } else {
4975
+ logValidateFailureSummary(summary);
4976
+ }
4977
+ throw new ValidateCommandError(summary, { cause: error });
4978
+ }
4979
+ }
4980
+
4981
+ // src/commands/default-handler-map.ts
4982
+ var defaultCommandHandlers = {
4983
+ install: installCommandDefaultHandler,
4984
+ extract: extractCommandDefaultHandler,
4985
+ tokens: tokensCommandDefaultHandler,
4986
+ init: initCommandDefaultHandler,
4987
+ migrate: migrateCommandDefaultHandler,
4988
+ restore: restoreCommandDefaultHandler,
4989
+ validate: validateCommandDefaultHandler,
4990
+ status: statusCommandDefaultHandler
4991
+ };
4992
+
4993
+ // src/commands/command-registrar.ts
4994
+ function registerTailwindcssPatchCommand(cli, commandName, options, prefix, defaultDefinitions) {
4995
+ const metadata = resolveCommandMetadata(commandName, options, prefix, defaultDefinitions);
4996
+ const command = cli.command(metadata.name, metadata.description);
4997
+ applyCommandOptions(command, metadata.optionDefs);
4998
+ command.action(async (args) => {
4999
+ return runWithCommandHandler(
5000
+ cli,
5001
+ command,
5002
+ commandName,
5003
+ args,
5004
+ options.commandHandlers?.[commandName],
5005
+ defaultCommandHandlers[commandName]
5006
+ );
5007
+ });
5008
+ metadata.aliases.forEach((alias) => command.alias(alias));
5009
+ }
5010
+
5011
+ // src/commands/cli.ts
4566
5012
  function mountTailwindcssPatchCommands(cli, options = {}) {
4567
5013
  const prefix = options.commandPrefix ?? "";
4568
5014
  const selectedCommands = options.commands ?? tailwindcssPatchCommands;
4569
5015
  const defaultDefinitions = buildDefaultCommandDefinitions();
4570
- const registrars = {
4571
- install: () => {
4572
- const metadata = resolveCommandMetadata("install", options, prefix, defaultDefinitions);
4573
- const command = cli.command(metadata.name, metadata.description);
4574
- applyCommandOptions(command, metadata.optionDefs);
4575
- command.action(async (args) => {
4576
- return runWithCommandHandler(
4577
- cli,
4578
- command,
4579
- "install",
4580
- args,
4581
- options.commandHandlers?.install,
4582
- installCommandDefaultHandler
4583
- );
4584
- });
4585
- metadata.aliases.forEach((alias) => command.alias(alias));
4586
- },
4587
- extract: () => {
4588
- const metadata = resolveCommandMetadata("extract", options, prefix, defaultDefinitions);
4589
- const command = cli.command(metadata.name, metadata.description);
4590
- applyCommandOptions(command, metadata.optionDefs);
4591
- command.action(async (args) => {
4592
- return runWithCommandHandler(
4593
- cli,
4594
- command,
4595
- "extract",
4596
- args,
4597
- options.commandHandlers?.extract,
4598
- extractCommandDefaultHandler
4599
- );
4600
- });
4601
- metadata.aliases.forEach((alias) => command.alias(alias));
4602
- },
4603
- tokens: () => {
4604
- const metadata = resolveCommandMetadata("tokens", options, prefix, defaultDefinitions);
4605
- const command = cli.command(metadata.name, metadata.description);
4606
- applyCommandOptions(command, metadata.optionDefs);
4607
- command.action(async (args) => {
4608
- return runWithCommandHandler(
4609
- cli,
4610
- command,
4611
- "tokens",
4612
- args,
4613
- options.commandHandlers?.tokens,
4614
- tokensCommandDefaultHandler
4615
- );
4616
- });
4617
- metadata.aliases.forEach((alias) => command.alias(alias));
4618
- },
4619
- init: () => {
4620
- const metadata = resolveCommandMetadata("init", options, prefix, defaultDefinitions);
4621
- const command = cli.command(metadata.name, metadata.description);
4622
- applyCommandOptions(command, metadata.optionDefs);
4623
- command.action(async (args) => {
4624
- return runWithCommandHandler(
4625
- cli,
4626
- command,
4627
- "init",
4628
- args,
4629
- options.commandHandlers?.init,
4630
- initCommandDefaultHandler
4631
- );
4632
- });
4633
- metadata.aliases.forEach((alias) => command.alias(alias));
4634
- },
4635
- migrate: () => {
4636
- const metadata = resolveCommandMetadata("migrate", options, prefix, defaultDefinitions);
4637
- const command = cli.command(metadata.name, metadata.description);
4638
- applyCommandOptions(command, metadata.optionDefs);
4639
- command.action(async (args) => {
4640
- return runWithCommandHandler(
4641
- cli,
4642
- command,
4643
- "migrate",
4644
- args,
4645
- options.commandHandlers?.migrate,
4646
- migrateCommandDefaultHandler
4647
- );
4648
- });
4649
- metadata.aliases.forEach((alias) => command.alias(alias));
4650
- },
4651
- restore: () => {
4652
- const metadata = resolveCommandMetadata("restore", options, prefix, defaultDefinitions);
4653
- const command = cli.command(metadata.name, metadata.description);
4654
- applyCommandOptions(command, metadata.optionDefs);
4655
- command.action(async (args) => {
4656
- return runWithCommandHandler(
4657
- cli,
4658
- command,
4659
- "restore",
4660
- args,
4661
- options.commandHandlers?.restore,
4662
- restoreCommandDefaultHandler
4663
- );
4664
- });
4665
- metadata.aliases.forEach((alias) => command.alias(alias));
4666
- },
4667
- validate: () => {
4668
- const metadata = resolveCommandMetadata("validate", options, prefix, defaultDefinitions);
4669
- const command = cli.command(metadata.name, metadata.description);
4670
- applyCommandOptions(command, metadata.optionDefs);
4671
- command.action(async (args) => {
4672
- return runWithCommandHandler(
4673
- cli,
4674
- command,
4675
- "validate",
4676
- args,
4677
- options.commandHandlers?.validate,
4678
- validateCommandDefaultHandler
4679
- );
4680
- });
4681
- metadata.aliases.forEach((alias) => command.alias(alias));
4682
- },
4683
- status: () => {
4684
- const metadata = resolveCommandMetadata("status", options, prefix, defaultDefinitions);
4685
- const command = cli.command(metadata.name, metadata.description);
4686
- applyCommandOptions(command, metadata.optionDefs);
4687
- command.action(async (args) => {
4688
- return runWithCommandHandler(
4689
- cli,
4690
- command,
4691
- "status",
4692
- args,
4693
- options.commandHandlers?.status,
4694
- statusCommandDefaultHandler
4695
- );
4696
- });
4697
- metadata.aliases.forEach((alias) => command.alias(alias));
4698
- }
4699
- };
4700
5016
  for (const name of selectedCommands) {
4701
- const register = registrars[name];
4702
- if (register) {
4703
- register();
4704
- }
5017
+ registerTailwindcssPatchCommand(cli, name, options, prefix, defaultDefinitions);
4705
5018
  }
4706
5019
  return cli;
4707
5020
  }
@@ -4714,26 +5027,26 @@ function createTailwindcssPatchCli(options = {}) {
4714
5027
  export {
4715
5028
  logger_default,
4716
5029
  CacheStore,
5030
+ normalizeOptions,
4717
5031
  extractRawCandidatesWithPositions,
4718
5032
  extractRawCandidates,
4719
5033
  extractValidCandidates,
4720
5034
  extractProjectCandidatesWithPositions,
4721
5035
  groupTokensByFile,
4722
- normalizeOptions,
4723
- getPatchStatusReport,
4724
5036
  collectClassesFromContexts,
4725
5037
  collectClassesFromTailwindV4,
4726
5038
  loadRuntimeContexts,
4727
5039
  runTailwindBuild,
5040
+ getPatchStatusReport,
4728
5041
  TailwindcssPatcher,
4729
5042
  MIGRATION_REPORT_KIND,
4730
5043
  MIGRATION_REPORT_SCHEMA_VERSION,
4731
5044
  migrateConfigFiles,
4732
5045
  restoreConfigFiles,
4733
- tailwindcssPatchCommands,
4734
5046
  VALIDATE_EXIT_CODES,
4735
5047
  VALIDATE_FAILURE_REASONS,
4736
5048
  ValidateCommandError,
5049
+ tailwindcssPatchCommands,
4737
5050
  mountTailwindcssPatchCommands,
4738
5051
  createTailwindcssPatchCli
4739
5052
  };