tailwindcss-patch 8.7.3 → 9.0.0-alpha.1

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,6 +1,6 @@
1
1
  import {
2
2
  __dirname
3
- } from "./chunk-JHEI2MLC.mjs";
3
+ } from "./chunk-A67ABH3M.mjs";
4
4
 
5
5
  // src/logger.ts
6
6
  import { createConsola } from "consola";
@@ -20,7 +20,7 @@ import path from "pathe";
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "tailwindcss-patch",
23
- version: "8.7.3",
23
+ version: "9.0.0-alpha.1",
24
24
  description: "patch tailwindcss for exposing context and extract classes",
25
25
  author: "ice breaker <1324318532@qq.com>",
26
26
  license: "MIT",
@@ -41,17 +41,17 @@ var package_default = {
41
41
  ],
42
42
  exports: {
43
43
  ".": {
44
- types: "./dist/index.d.ts",
45
- import: "./dist/index.mjs",
46
- require: "./dist/index.js"
44
+ types: "./src/index.ts",
45
+ import: "./src/index.ts",
46
+ require: "./src/index.ts"
47
47
  },
48
48
  "./migration-report.schema.json": "./schema/migration-report.schema.json",
49
49
  "./restore-result.schema.json": "./schema/restore-result.schema.json",
50
50
  "./validate-result.schema.json": "./schema/validate-result.schema.json"
51
51
  },
52
- main: "./dist/index.js",
53
- module: "./dist/index.mjs",
54
- types: "./dist/index.d.ts",
52
+ main: "./src/index.ts",
53
+ module: "./src/index.ts",
54
+ types: "./src/index.ts",
55
55
  bin: {
56
56
  "tw-patch": "dev/bin.ts",
57
57
  "tailwindcss-patch": "dev/bin.ts"
@@ -66,6 +66,7 @@ var package_default = {
66
66
  build: "tsup",
67
67
  test: "vitest run",
68
68
  "test:dev": "vitest",
69
+ "bench:cold-start": "node --import tsx bench/cold-start.ts",
69
70
  patch: "tsx dev/bin.ts install",
70
71
  r0: "tsx dev/bin.ts extract",
71
72
  r1: "tsx dev/bin.ts extract --css index.css"
@@ -106,12 +107,12 @@ var package_default = {
106
107
  "@babel/types": "^7.29.0",
107
108
  "@tailwindcss-mangle/config": "workspace:*",
108
109
  "@tailwindcss/node": "^4.2.1",
109
- cac: "^6.7.14",
110
+ cac: "^7.0.0",
110
111
  consola: "^3.4.2",
111
- "fs-extra": "^11.3.3",
112
+ "fs-extra": "^11.3.4",
112
113
  "local-pkg": "^1.1.2",
113
114
  pathe: "^2.0.3",
114
- postcss: "^8.5.6",
115
+ postcss: "^8.5.8",
115
116
  semver: "^7.7.4",
116
117
  "tailwindcss-config": "^1.1.4"
117
118
  },
@@ -1515,14 +1516,6 @@ ${Date.now()}`, { flag: "wx" });
1515
1516
  import process3 from "process";
1516
1517
  import fs3 from "fs-extra";
1517
1518
  import path2 from "pathe";
1518
- var hasWarnedDeprecatedOptions = false;
1519
- var deprecatedOptionMapping = {
1520
- cwd: "projectRoot",
1521
- overwrite: "apply.overwrite",
1522
- tailwind: "tailwindcss",
1523
- features: "apply",
1524
- output: "extract"
1525
- };
1526
1519
  function resolveRealpathSafe(value) {
1527
1520
  const resolved = path2.resolve(value);
1528
1521
  try {
@@ -1667,66 +1660,32 @@ function normalizeTailwindOptions(tailwind, projectRoot) {
1667
1660
  v4
1668
1661
  };
1669
1662
  }
1670
- function resolveOptionSlices(options) {
1671
- const projectRoot = options.projectRoot ?? options.cwd;
1672
- const overwrite = options.apply?.overwrite ?? options.overwrite;
1673
- const tailwind = options.tailwindcss ?? options.tailwind;
1674
- const exposeContext = options.apply?.exposeContext !== void 0 ? options.apply.exposeContext : options.features?.exposeContext;
1675
- const extendLengthUnits = options.apply?.extendLengthUnits !== void 0 ? options.apply.extendLengthUnits : options.features?.extendLengthUnits;
1676
- const write = options.extract?.write ?? options.output?.enabled;
1677
- const file = options.extract?.file ?? options.output?.file;
1678
- const format = options.extract?.format ?? options.output?.format;
1679
- const pretty = options.extract?.pretty ?? options.output?.pretty;
1680
- const removeUniversalSelector = options.extract?.removeUniversalSelector ?? options.output?.removeUniversalSelector;
1681
- const extract = {
1682
- ...write === void 0 ? {} : { write },
1683
- ...file === void 0 ? {} : { file },
1684
- ...format === void 0 ? {} : { format },
1685
- ...pretty === void 0 ? {} : { pretty },
1686
- ...removeUniversalSelector === void 0 ? {} : { removeUniversalSelector }
1687
- };
1688
- return {
1689
- ...projectRoot === void 0 ? {} : { projectRoot },
1690
- ...overwrite === void 0 ? {} : { overwrite },
1691
- ...tailwind === void 0 ? {} : { tailwind },
1692
- ...Object.keys(extract).length === 0 ? {} : { extract },
1693
- ...exposeContext === void 0 ? {} : { exposeContext },
1694
- ...extendLengthUnits === void 0 ? {} : { extendLengthUnits }
1695
- };
1696
- }
1697
- function findUsedDeprecatedOptions(options) {
1698
- const result = [];
1699
- for (const key of Object.keys(deprecatedOptionMapping)) {
1700
- if (options[key] !== void 0) {
1701
- result.push(key);
1702
- }
1703
- }
1704
- return result;
1705
- }
1706
- function warnDeprecatedOptionsIfNeeded(options) {
1707
- if (hasWarnedDeprecatedOptions) {
1708
- return;
1709
- }
1710
- const used = findUsedDeprecatedOptions(options);
1663
+ var deprecatedOptionMapping = {
1664
+ cwd: "projectRoot",
1665
+ overwrite: "apply.overwrite",
1666
+ tailwind: "tailwindcss",
1667
+ features: "apply",
1668
+ output: "extract"
1669
+ };
1670
+ function assertNoDeprecatedOptions(options) {
1671
+ const used = Object.keys(deprecatedOptionMapping).filter((key) => Object.prototype.hasOwnProperty.call(options, key));
1711
1672
  if (used.length === 0) {
1712
1673
  return;
1713
1674
  }
1714
- hasWarnedDeprecatedOptions = true;
1715
1675
  const mapping = used.map((key) => `${key} -> ${deprecatedOptionMapping[key]}`).join(", ");
1716
- logger_default.warn(
1717
- `[deprecated] TailwindcssPatcher options (${used.join(", ")}) are deprecated and will be removed in the next major version. Please migrate to: ${mapping}.`
1676
+ throw new Error(
1677
+ `Legacy TailwindcssPatcher options are no longer supported: ${used.join(", ")}. Use the modern fields instead: ${mapping}.`
1718
1678
  );
1719
1679
  }
1720
1680
  function normalizeOptions(options = {}) {
1721
- warnDeprecatedOptionsIfNeeded(options);
1722
- const resolved = resolveOptionSlices(options);
1723
- const projectRoot = resolveRealpathSafe(resolved.projectRoot ? path2.resolve(resolved.projectRoot) : process3.cwd());
1724
- const overwrite = resolved.overwrite ?? true;
1725
- const output = normalizeOutputOptions(resolved.extract);
1681
+ assertNoDeprecatedOptions(options);
1682
+ const projectRoot = resolveRealpathSafe(options.projectRoot ? path2.resolve(options.projectRoot) : process3.cwd());
1683
+ const overwrite = options.apply?.overwrite ?? true;
1684
+ const output = normalizeOutputOptions(options.extract);
1726
1685
  const cache = normalizeCacheOptions(options.cache, projectRoot);
1727
- const tailwind = normalizeTailwindOptions(resolved.tailwind, projectRoot);
1728
- const exposeContext = normalizeExposeContextOptions(resolved.exposeContext);
1729
- const extendLengthUnits = normalizeExtendLengthUnitsOptions(resolved.extendLengthUnits);
1686
+ const tailwind = normalizeTailwindOptions(options.tailwindcss, projectRoot);
1687
+ const exposeContext = normalizeExposeContextOptions(options.apply?.exposeContext);
1688
+ const extendLengthUnits = normalizeExtendLengthUnitsOptions(options.apply?.extendLengthUnits);
1730
1689
  const filter = (className) => {
1731
1690
  if (output.removeUniversalSelector && className === "*") {
1732
1691
  return false;
@@ -1751,122 +1710,70 @@ function normalizeOptions(options = {}) {
1751
1710
  }
1752
1711
 
1753
1712
  // src/options/legacy.ts
1754
- function normalizeLegacyFeatures(patch) {
1755
- const apply = patch?.applyPatches;
1756
- const extend = apply?.extendLengthUnits;
1757
- let extendOption = false;
1758
- if (extend && typeof extend === "object") {
1759
- extendOption = {
1760
- ...extend,
1761
- enabled: true
1762
- };
1763
- } else if (extend === true) {
1764
- extendOption = {
1765
- enabled: true,
1766
- units: ["rpx"],
1767
- ...patch?.overwrite === void 0 ? {} : { overwrite: patch.overwrite }
1768
- };
1713
+ var deprecatedRegistryMapping = {
1714
+ output: "extract",
1715
+ tailwind: "tailwindcss"
1716
+ };
1717
+ var deprecatedTailwindMapping = {
1718
+ package: "packageName",
1719
+ legacy: "v2",
1720
+ classic: "v3",
1721
+ next: "v4"
1722
+ };
1723
+ function assertNoDeprecatedRegistryOptions(registry) {
1724
+ const usedRegistryKeys = Object.keys(deprecatedRegistryMapping).filter((key) => Object.prototype.hasOwnProperty.call(registry, key));
1725
+ if (usedRegistryKeys.length > 0) {
1726
+ const mapping = usedRegistryKeys.map((key) => `${key} -> ${deprecatedRegistryMapping[key]}`).join(", ");
1727
+ throw new Error(
1728
+ `Legacy registry fields are no longer supported: ${usedRegistryKeys.join(", ")}. Use the modern fields instead: ${mapping}.`
1729
+ );
1769
1730
  }
1770
- return {
1771
- exposeContext: apply?.exportContext ?? true,
1772
- extendLengthUnits: extendOption
1773
- };
1774
- }
1775
- function fromLegacyOptions(options) {
1776
- if (!options) {
1777
- return {};
1731
+ const tailwind = registry.tailwindcss;
1732
+ if (!tailwind) {
1733
+ return;
1734
+ }
1735
+ const usedTailwindKeys = Object.keys(deprecatedTailwindMapping).filter((key) => Object.prototype.hasOwnProperty.call(tailwind, key));
1736
+ if (usedTailwindKeys.length > 0) {
1737
+ const mapping = usedTailwindKeys.map((key) => `${key} -> tailwindcss.${deprecatedTailwindMapping[key]}`).join(", ");
1738
+ throw new Error(
1739
+ `Legacy "registry.tailwindcss" fields are no longer supported: ${usedTailwindKeys.join(", ")}. Use the modern fields instead: ${mapping}.`
1740
+ );
1778
1741
  }
1779
- const patch = options.patch;
1780
- const features = normalizeLegacyFeatures(patch);
1781
- const output = patch?.output;
1782
- const tailwindConfig = patch?.tailwindcss;
1783
- const tailwindVersion = tailwindConfig?.version;
1784
- const tailwindV2 = tailwindConfig?.v2;
1785
- const tailwindV3 = tailwindConfig?.v3;
1786
- const tailwindV4 = tailwindConfig?.v4;
1787
- const tailwindConfigPath = tailwindV3?.config ?? tailwindV2?.config;
1788
- const tailwindCwd = tailwindV3?.cwd ?? tailwindV2?.cwd ?? patch?.cwd;
1789
- const normalizedExtract = output ? {
1790
- ...output.filename === void 0 ? {} : { file: output.filename },
1791
- pretty: output.loose ? 2 : false,
1792
- ...output.removeUniversalSelector === void 0 ? {} : { removeUniversalSelector: output.removeUniversalSelector }
1793
- } : void 0;
1794
- const normalizedTailwindcss = {
1795
- ...patch?.packageName === void 0 ? {} : { packageName: patch.packageName },
1796
- ...tailwindVersion === void 0 ? {} : { version: tailwindVersion },
1797
- ...patch?.resolve === void 0 ? {} : { resolve: patch.resolve },
1798
- ...tailwindConfigPath === void 0 ? {} : { config: tailwindConfigPath },
1799
- ...tailwindCwd === void 0 ? {} : { cwd: tailwindCwd },
1800
- ...tailwindV2 === void 0 ? {} : { v2: tailwindV2 },
1801
- ...tailwindV3 === void 0 ? {} : { v3: tailwindV3 },
1802
- ...tailwindV4 === void 0 ? {} : { v4: tailwindV4 }
1803
- };
1804
- const normalizedCache = typeof options.cache === "boolean" ? options.cache : options.cache ? {
1805
- ...options.cache,
1806
- enabled: options.cache.enabled ?? true
1807
- } : void 0;
1808
- const normalizedApply = {
1809
- ...patch?.overwrite === void 0 ? {} : { overwrite: patch.overwrite },
1810
- exposeContext: features.exposeContext,
1811
- extendLengthUnits: features.extendLengthUnits
1812
- };
1813
- return {
1814
- ...patch?.cwd === void 0 ? {} : { projectRoot: patch.cwd },
1815
- ...patch?.filter === void 0 ? {} : { filter: patch.filter },
1816
- ...normalizedCache === void 0 ? {} : { cache: normalizedCache },
1817
- ...normalizedExtract === void 0 ? {} : { extract: normalizedExtract },
1818
- ...Object.keys(normalizedTailwindcss).length === 0 ? {} : { tailwindcss: normalizedTailwindcss },
1819
- apply: normalizedApply
1820
- };
1821
1742
  }
1822
1743
  function fromUnifiedConfig(registry) {
1823
1744
  if (!registry) {
1824
1745
  return {};
1825
1746
  }
1826
- const tailwind = registry.tailwindcss ?? registry.tailwind;
1827
- const modernExtract = registry.extract;
1828
- const legacyOutput = registry.output;
1829
- const pretty = (() => {
1830
- const value = modernExtract?.pretty ?? legacyOutput?.pretty;
1831
- if (value === void 0) {
1832
- return void 0;
1833
- }
1834
- if (typeof value === "boolean") {
1835
- return value ? 2 : false;
1836
- }
1837
- return value;
1838
- })();
1839
- const removeUniversalSelector = modernExtract?.removeUniversalSelector ?? legacyOutput?.stripUniversalSelector;
1840
- const outputFile = modernExtract?.file ?? legacyOutput?.file;
1841
- const normalizedExtract = modernExtract || legacyOutput ? {
1842
- ...modernExtract?.write === void 0 ? {} : { write: modernExtract.write },
1843
- ...outputFile === void 0 ? {} : { file: outputFile },
1844
- ...pretty === void 0 ? {} : { pretty },
1845
- ...removeUniversalSelector === void 0 ? {} : { removeUniversalSelector },
1846
- ...modernExtract?.format === void 0 ? {} : { format: modernExtract.format }
1747
+ assertNoDeprecatedRegistryOptions(registry);
1748
+ const extract = registry.extract ? {
1749
+ ...registry.extract.write === void 0 ? {} : { write: registry.extract.write },
1750
+ ...registry.extract.file === void 0 ? {} : { file: registry.extract.file },
1751
+ ...registry.extract.format === void 0 ? {} : { format: registry.extract.format },
1752
+ ...registry.extract.pretty === void 0 ? {} : { pretty: registry.extract.pretty },
1753
+ ...registry.extract.removeUniversalSelector === void 0 ? {} : { removeUniversalSelector: registry.extract.removeUniversalSelector }
1847
1754
  } : void 0;
1848
- const normalizedTailwindcss = tailwind ? {
1849
- ...tailwind.version === void 0 ? {} : { version: tailwind.version },
1850
- ...tailwind.packageName === void 0 ? tailwind.package === void 0 ? {} : { packageName: tailwind.package } : { packageName: tailwind.packageName },
1851
- ...tailwind.resolve === void 0 ? {} : { resolve: tailwind.resolve },
1852
- ...tailwind.config === void 0 ? {} : { config: tailwind.config },
1853
- ...tailwind.cwd === void 0 ? {} : { cwd: tailwind.cwd },
1854
- ...tailwind.v2 === void 0 ? tailwind.legacy === void 0 ? {} : { v2: tailwind.legacy } : { v2: tailwind.v2 },
1855
- ...tailwind.v3 === void 0 ? tailwind.classic === void 0 ? {} : { v3: tailwind.classic } : { v3: tailwind.v3 },
1856
- ...tailwind.v4 === void 0 ? tailwind.next === void 0 ? {} : { v4: tailwind.next } : { v4: tailwind.v4 }
1755
+ const tailwindcss = registry.tailwindcss ? {
1756
+ ...registry.tailwindcss.version === void 0 ? {} : { version: registry.tailwindcss.version },
1757
+ ...registry.tailwindcss.packageName === void 0 ? {} : { packageName: registry.tailwindcss.packageName },
1758
+ ...registry.tailwindcss.resolve === void 0 ? {} : { resolve: registry.tailwindcss.resolve },
1759
+ ...registry.tailwindcss.config === void 0 ? {} : { config: registry.tailwindcss.config },
1760
+ ...registry.tailwindcss.cwd === void 0 ? {} : { cwd: registry.tailwindcss.cwd },
1761
+ ...registry.tailwindcss.v2 === void 0 ? {} : { v2: registry.tailwindcss.v2 },
1762
+ ...registry.tailwindcss.v3 === void 0 ? {} : { v3: registry.tailwindcss.v3 },
1763
+ ...registry.tailwindcss.v4 === void 0 ? {} : { v4: registry.tailwindcss.v4 }
1857
1764
  } : void 0;
1858
- const normalizedApply = registry.apply ? {
1765
+ const apply = registry.apply ? {
1859
1766
  ...registry.apply.overwrite === void 0 ? {} : { overwrite: registry.apply.overwrite },
1860
1767
  ...registry.apply.exposeContext === void 0 ? {} : { exposeContext: registry.apply.exposeContext },
1861
1768
  ...registry.apply.extendLengthUnits === void 0 ? {} : { extendLengthUnits: registry.apply.extendLengthUnits }
1862
1769
  } : void 0;
1863
1770
  return {
1864
1771
  ...registry.projectRoot === void 0 ? {} : { projectRoot: registry.projectRoot },
1865
- ...normalizedApply === void 0 ? {} : { apply: normalizedApply },
1772
+ ...apply === void 0 ? {} : { apply },
1866
1773
  ...registry.cache === void 0 ? {} : { cache: registry.cache },
1867
1774
  ...registry.filter === void 0 ? {} : { filter: registry.filter },
1868
- ...normalizedExtract === void 0 ? {} : { extract: normalizedExtract },
1869
- ...normalizedTailwindcss === void 0 ? {} : { tailwindcss: normalizedTailwindcss }
1775
+ ...extract === void 0 ? {} : { extract },
1776
+ ...tailwindcss === void 0 ? {} : { tailwindcss }
1870
1777
  };
1871
1778
  }
1872
1779
 
@@ -1878,19 +1785,22 @@ var defuPromise;
1878
1785
  function isNodeError(error) {
1879
1786
  return !!error && typeof error === "object" && ("code" in error || "message" in error);
1880
1787
  }
1881
- function isMissingConfigModuleError(error) {
1882
- if (!isNodeError(error) || error.code !== "MODULE_NOT_FOUND") {
1788
+ function isMissingModuleError(error, pkgName2) {
1789
+ if (!isNodeError(error)) {
1883
1790
  return false;
1884
1791
  }
1885
- const message = error.message ?? "";
1886
- return message.includes("@tailwindcss-mangle/config");
1887
- }
1888
- function isMissingSharedModuleError(error) {
1889
- if (!isNodeError(error) || error.code !== "MODULE_NOT_FOUND") {
1792
+ const code = error.code;
1793
+ if (code !== "MODULE_NOT_FOUND" && code !== "ERR_MODULE_NOT_FOUND") {
1890
1794
  return false;
1891
1795
  }
1892
1796
  const message = error.message ?? "";
1893
- return message.includes("@tailwindcss-mangle/shared");
1797
+ return message.includes(pkgName2) || message.includes(`${pkgName2}/dist/`);
1798
+ }
1799
+ function isMissingConfigModuleError(error) {
1800
+ return isMissingModuleError(error, "@tailwindcss-mangle/config");
1801
+ }
1802
+ function isMissingSharedModuleError(error) {
1803
+ return isMissingModuleError(error, "@tailwindcss-mangle/shared");
1894
1804
  }
1895
1805
  async function loadWorkspaceConfigModule() {
1896
1806
  if (!configModulePromise) {
@@ -1906,7 +1816,7 @@ async function loadWorkspaceConfigModule() {
1906
1816
  }
1907
1817
  async function loadWorkspaceDefu() {
1908
1818
  if (!defuPromise) {
1909
- defuPromise = import("./dist-EMUBVNNO.mjs").then((mod) => mod.defu).catch(async (error) => {
1819
+ defuPromise = import("./dist-7UDSGIWH.mjs").then((mod) => mod.defu).catch(async (error) => {
1910
1820
  if (!isMissingSharedModuleError(error)) {
1911
1821
  throw error;
1912
1822
  }
@@ -1921,8 +1831,10 @@ async function loadPatchOptionsForWorkspace(cwd, overrides) {
1921
1831
  const merge = await loadWorkspaceDefu();
1922
1832
  const configModule = await loadWorkspaceConfigModule();
1923
1833
  const { config } = await configModule.getConfig(cwd);
1924
- const legacyConfig = config;
1925
- const base = config?.registry ? fromUnifiedConfig(config.registry) : legacyConfig?.patch ? fromLegacyOptions({ patch: legacyConfig.patch }) : {};
1834
+ if (config && typeof config === "object" && "patch" in config && config.patch !== void 0) {
1835
+ throw new Error('Legacy workspace config field "patch" is no longer supported. Move patcher options under "registry".');
1836
+ }
1837
+ const base = config?.registry ? fromUnifiedConfig(config.registry) : {};
1926
1838
  const merged = merge(overrides ?? {}, base);
1927
1839
  return merged;
1928
1840
  }
@@ -1931,33 +1843,66 @@ async function loadPatchOptionsForWorkspace(cwd, overrides) {
1931
1843
  import { promises as fs4 } from "fs";
1932
1844
  import process4 from "process";
1933
1845
  import path4 from "pathe";
1846
+ var nodeImportPromise;
1847
+ var oxideImportPromise;
1848
+ var designSystemPromiseCache = /* @__PURE__ */ new Map();
1849
+ var designSystemCandidateCache = /* @__PURE__ */ new Map();
1934
1850
  async function importNode() {
1935
1851
  return import("@tailwindcss/node");
1936
1852
  }
1937
1853
  async function importOxide() {
1938
1854
  return import("@tailwindcss/oxide");
1939
1855
  }
1856
+ function getNodeModule() {
1857
+ nodeImportPromise ??= importNode();
1858
+ return nodeImportPromise;
1859
+ }
1860
+ function getOxideModule() {
1861
+ oxideImportPromise ??= importOxide();
1862
+ return oxideImportPromise;
1863
+ }
1864
+ function createDesignSystemCacheKey(css, bases) {
1865
+ return JSON.stringify({
1866
+ css,
1867
+ bases: Array.from(new Set(bases.filter(Boolean)))
1868
+ });
1869
+ }
1940
1870
  async function loadDesignSystem(css, bases) {
1941
1871
  const uniqueBases = Array.from(new Set(bases.filter(Boolean)));
1942
1872
  if (uniqueBases.length === 0) {
1943
1873
  throw new Error("No base directories provided for Tailwind CSS design system.");
1944
1874
  }
1945
- const { __unstable__loadDesignSystem } = await importNode();
1946
- let lastError;
1947
- for (const base of uniqueBases) {
1948
- try {
1949
- return await __unstable__loadDesignSystem(css, { base });
1950
- } catch (error) {
1951
- lastError = error;
1952
- }
1953
- }
1954
- if (lastError instanceof Error) {
1955
- throw lastError;
1875
+ const cacheKey = createDesignSystemCacheKey(css, uniqueBases);
1876
+ const cached = designSystemPromiseCache.get(cacheKey);
1877
+ if (cached) {
1878
+ return cached;
1956
1879
  }
1957
- throw new Error("Failed to load Tailwind CSS design system.");
1880
+ const promise = (async () => {
1881
+ const { __unstable__loadDesignSystem } = await getNodeModule();
1882
+ let lastError;
1883
+ for (const base of uniqueBases) {
1884
+ try {
1885
+ return await __unstable__loadDesignSystem(css, { base });
1886
+ } catch (error) {
1887
+ lastError = error;
1888
+ }
1889
+ }
1890
+ if (lastError instanceof Error) {
1891
+ throw lastError;
1892
+ }
1893
+ throw new Error("Failed to load Tailwind CSS design system.");
1894
+ })();
1895
+ designSystemPromiseCache.set(cacheKey, promise);
1896
+ promise.catch(() => {
1897
+ if (designSystemPromiseCache.get(cacheKey) === promise) {
1898
+ designSystemPromiseCache.delete(cacheKey);
1899
+ designSystemCandidateCache.delete(cacheKey);
1900
+ }
1901
+ });
1902
+ return promise;
1958
1903
  }
1959
1904
  async function extractRawCandidatesWithPositions(content, extension = "html") {
1960
- const { Scanner } = await importOxide();
1905
+ const { Scanner } = await getOxideModule();
1961
1906
  const scanner = new Scanner({});
1962
1907
  const result = scanner.getCandidatesWithPositions({ content, extension });
1963
1908
  return result.map(({ candidate, position }) => ({
@@ -1967,7 +1912,7 @@ async function extractRawCandidatesWithPositions(content, extension = "html") {
1967
1912
  }));
1968
1913
  }
1969
1914
  async function extractRawCandidates(sources) {
1970
- const { Scanner } = await importOxide();
1915
+ const { Scanner } = await getOxideModule();
1971
1916
  const scanner = new Scanner(sources === void 0 ? {} : { sources });
1972
1917
  return scanner.scan();
1973
1918
  }
@@ -1988,25 +1933,44 @@ async function extractValidCandidates(options) {
1988
1933
  pattern: source.pattern,
1989
1934
  negated: source.negated
1990
1935
  }));
1936
+ const designSystemKey = createDesignSystemCacheKey(css, [base, ...baseFallbacks]);
1991
1937
  const designSystem = await loadDesignSystem(css, [base, ...baseFallbacks]);
1938
+ const candidateCache = designSystemCandidateCache.get(designSystemKey) ?? /* @__PURE__ */ new Map();
1939
+ designSystemCandidateCache.set(designSystemKey, candidateCache);
1992
1940
  const candidates = await extractRawCandidates(sources);
1993
- const parsedCandidates = candidates.filter(
1994
- (rawCandidate) => designSystem.parseCandidate(rawCandidate).length > 0
1995
- );
1996
- if (parsedCandidates.length === 0) {
1997
- return parsedCandidates;
1998
- }
1999
- const cssByCandidate = designSystem.candidatesToCss(parsedCandidates);
2000
1941
  const validCandidates = [];
2001
- for (let index = 0; index < parsedCandidates.length; index++) {
2002
- const candidate = parsedCandidates[index];
1942
+ const uncachedCandidates = [];
1943
+ for (const rawCandidate of candidates) {
1944
+ const cached = candidateCache.get(rawCandidate);
1945
+ if (cached === true) {
1946
+ validCandidates.push(rawCandidate);
1947
+ continue;
1948
+ }
1949
+ if (cached === false) {
1950
+ continue;
1951
+ }
1952
+ if (designSystem.parseCandidate(rawCandidate).length > 0) {
1953
+ uncachedCandidates.push(rawCandidate);
1954
+ continue;
1955
+ }
1956
+ candidateCache.set(rawCandidate, false);
1957
+ }
1958
+ if (uncachedCandidates.length === 0) {
1959
+ return validCandidates;
1960
+ }
1961
+ const cssByCandidate = designSystem.candidatesToCss(uncachedCandidates);
1962
+ for (let index = 0; index < uncachedCandidates.length; index++) {
1963
+ const candidate = uncachedCandidates[index];
2003
1964
  if (candidate === void 0) {
2004
1965
  continue;
2005
1966
  }
2006
- const css2 = cssByCandidate[index];
2007
- if (typeof css2 === "string" && css2.trim().length > 0) {
2008
- validCandidates.push(candidate);
1967
+ const candidateCss = cssByCandidate[index];
1968
+ const isValid = typeof candidateCss === "string" && candidateCss.trim().length > 0;
1969
+ candidateCache.set(candidate, isValid);
1970
+ if (!isValid) {
1971
+ continue;
2009
1972
  }
1973
+ validCandidates.push(candidate);
2010
1974
  }
2011
1975
  return validCandidates;
2012
1976
  }
@@ -2078,7 +2042,7 @@ function toRelativeFile(cwd, filename) {
2078
2042
  async function extractProjectCandidatesWithPositions(options) {
2079
2043
  const cwd = options?.cwd ? path4.resolve(options.cwd) : process4.cwd();
2080
2044
  const normalizedSources = normalizeSources(options?.sources, cwd);
2081
- const { Scanner } = await importOxide();
2045
+ const { Scanner } = await getOxideModule();
2082
2046
  const scanner = new Scanner({
2083
2047
  sources: normalizedSources
2084
2048
  });
@@ -2313,10 +2277,53 @@ function loadRuntimeContexts(packageInfo, majorVersion, refProperty) {
2313
2277
 
2314
2278
  // src/runtime/process-tailwindcss.ts
2315
2279
  import { createRequire as createRequire2 } from "module";
2280
+ import fs7 from "fs-extra";
2316
2281
  import path7 from "pathe";
2317
2282
  import postcss from "postcss";
2318
2283
  import { loadConfig } from "tailwindcss-config";
2319
2284
  var require3 = createRequire2(import.meta.url);
2285
+ function resolveModuleEntry(id) {
2286
+ return path7.isAbsolute(id) ? id : require3.resolve(id);
2287
+ }
2288
+ function resolvePackageRootFromEntry(entry) {
2289
+ let current = path7.dirname(entry);
2290
+ while (current && current !== path7.dirname(current)) {
2291
+ const packageJsonPath = path7.join(current, "package.json");
2292
+ if (fs7.pathExistsSync(packageJsonPath)) {
2293
+ return current;
2294
+ }
2295
+ current = path7.dirname(current);
2296
+ }
2297
+ return void 0;
2298
+ }
2299
+ function clearTailwindV3RuntimeState(pluginName) {
2300
+ try {
2301
+ const entry = resolveModuleEntry(pluginName);
2302
+ const root = resolvePackageRootFromEntry(entry);
2303
+ if (!root) {
2304
+ return;
2305
+ }
2306
+ const sharedStatePath = path7.join(root, "lib/lib/sharedState.js");
2307
+ if (!fs7.pathExistsSync(sharedStatePath)) {
2308
+ return;
2309
+ }
2310
+ const sharedState = require3.cache[sharedStatePath]?.exports;
2311
+ sharedState?.contextMap?.clear();
2312
+ sharedState?.configContextMap?.clear();
2313
+ sharedState?.contextSourcesMap?.clear();
2314
+ sharedState?.sourceHashMap?.clear();
2315
+ for (const candidate of ["lib/plugin.js", "lib/index.js"]) {
2316
+ const runtimeEntry = path7.join(root, candidate);
2317
+ if (!fs7.pathExistsSync(runtimeEntry)) {
2318
+ continue;
2319
+ }
2320
+ const runtimeModule = require3.cache[runtimeEntry]?.exports;
2321
+ runtimeModule?.contextRef?.value?.splice(0, runtimeModule.contextRef.value.length);
2322
+ break;
2323
+ }
2324
+ } catch {
2325
+ }
2326
+ }
2320
2327
  async function resolveConfigPath(options) {
2321
2328
  if (options.config && path7.isAbsolute(options.config)) {
2322
2329
  return options.config;
@@ -2330,6 +2337,9 @@ async function resolveConfigPath(options) {
2330
2337
  async function runTailwindBuild(options) {
2331
2338
  const configPath = await resolveConfigPath(options);
2332
2339
  const pluginName = options.postcssPlugin ?? (options.majorVersion === 4 ? "@tailwindcss/postcss" : "tailwindcss");
2340
+ if (options.majorVersion === 3) {
2341
+ clearTailwindV3RuntimeState(pluginName);
2342
+ }
2333
2343
  if (options.majorVersion === 4) {
2334
2344
  return postcss([
2335
2345
  require3(pluginName)({
@@ -2350,7 +2360,7 @@ async function runTailwindBuild(options) {
2350
2360
 
2351
2361
  // src/patching/status.ts
2352
2362
  import * as t4 from "@babel/types";
2353
- import fs8 from "fs-extra";
2363
+ import fs9 from "fs-extra";
2354
2364
  import path9 from "pathe";
2355
2365
 
2356
2366
  // src/babel/index.ts
@@ -2695,7 +2705,7 @@ function transformPostcssPlugin(content, { refProperty }) {
2695
2705
 
2696
2706
  // src/patching/operations/extend-length-units.ts
2697
2707
  import * as t3 from "@babel/types";
2698
- import fs7 from "fs-extra";
2708
+ import fs8 from "fs-extra";
2699
2709
  import path8 from "pathe";
2700
2710
  function updateLengthUnitsArray(content, options) {
2701
2711
  const { variableName = "lengthUnits", units } = options;
@@ -2739,11 +2749,11 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
2739
2749
  variableName: options.variableName ?? "lengthUnits"
2740
2750
  };
2741
2751
  const dataTypesFilePath = path8.resolve(rootDir, opts.lengthUnitsFilePath);
2742
- const exists = fs7.existsSync(dataTypesFilePath);
2752
+ const exists = fs8.existsSync(dataTypesFilePath);
2743
2753
  if (!exists) {
2744
2754
  return { changed: false, code: void 0 };
2745
2755
  }
2746
- const content = fs7.readFileSync(dataTypesFilePath, "utf8");
2756
+ const content = fs8.readFileSync(dataTypesFilePath, "utf8");
2747
2757
  const { arrayRef, changed } = updateLengthUnitsArray(content, opts);
2748
2758
  if (!arrayRef || !changed) {
2749
2759
  return { changed: false, code: void 0 };
@@ -2755,7 +2765,7 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
2755
2765
  const nextCode = `${content.slice(0, arrayRef.start)}${code}${content.slice(arrayRef.end)}`;
2756
2766
  if (opts.overwrite) {
2757
2767
  const target = opts.destPath ? path8.resolve(opts.destPath) : dataTypesFilePath;
2758
- fs7.writeFileSync(target, nextCode, "utf8");
2768
+ fs8.writeFileSync(target, nextCode, "utf8");
2759
2769
  logger_default.success("Patched Tailwind CSS length unit list (v3).");
2760
2770
  }
2761
2771
  return {
@@ -2774,15 +2784,15 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
2774
2784
  }
2775
2785
  const opts = { ...options };
2776
2786
  const distDir = path8.resolve(rootDir, "dist");
2777
- if (!fs7.existsSync(distDir)) {
2787
+ if (!fs8.existsSync(distDir)) {
2778
2788
  return { files: [], changed: false };
2779
2789
  }
2780
- const entries = fs7.readdirSync(distDir);
2790
+ const entries = fs8.readdirSync(distDir);
2781
2791
  const chunkNames = entries.filter((entry) => entry.endsWith(".js") || entry.endsWith(".mjs"));
2782
2792
  const pattern = /\[\s*["']cm["'],\s*["']mm["'],[\w,"']+\]/;
2783
2793
  const candidates = chunkNames.map((chunkName) => {
2784
2794
  const file = path8.join(distDir, chunkName);
2785
- const code = fs7.readFileSync(file, "utf8");
2795
+ const code = fs8.readFileSync(file, "utf8");
2786
2796
  const match = pattern.exec(code);
2787
2797
  if (!match) {
2788
2798
  return null;
@@ -2822,7 +2832,7 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
2822
2832
  }
2823
2833
  ]);
2824
2834
  if (opts.overwrite) {
2825
- fs7.writeFileSync(file, item.code, "utf8");
2835
+ fs8.writeFileSync(file, item.code, "utf8");
2826
2836
  }
2827
2837
  }
2828
2838
  if (candidates.some((file) => !file.hasPatched)) {
@@ -2878,11 +2888,11 @@ function checkExposeContextPatch(context) {
2878
2888
  const checks = [];
2879
2889
  function inspectFile(relative, transform) {
2880
2890
  const filePath = path9.resolve(packageInfo.rootPath, relative);
2881
- if (!fs8.existsSync(filePath)) {
2891
+ if (!fs9.existsSync(filePath)) {
2882
2892
  checks.push({ relative, exists: false, patched: false });
2883
2893
  return;
2884
2894
  }
2885
- const content = fs8.readFileSync(filePath, "utf8");
2895
+ const content = fs9.readFileSync(filePath, "utf8");
2886
2896
  const { hasPatched } = transform(content);
2887
2897
  checks.push({
2888
2898
  relative,
@@ -2893,7 +2903,7 @@ function checkExposeContextPatch(context) {
2893
2903
  if (majorVersion === 3) {
2894
2904
  inspectFile("lib/processTailwindFeatures.js", transformProcessTailwindFeaturesReturnContext);
2895
2905
  const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
2896
- const pluginRelative = pluginCandidates.find((candidate) => fs8.existsSync(path9.resolve(packageInfo.rootPath, candidate)));
2906
+ const pluginRelative = pluginCandidates.find((candidate) => fs9.existsSync(path9.resolve(packageInfo.rootPath, candidate)));
2897
2907
  if (pluginRelative) {
2898
2908
  inspectFile(pluginRelative, (content) => transformPostcssPlugin(content, { refProperty }));
2899
2909
  } else {
@@ -2924,8 +2934,8 @@ function checkExtendLengthUnitsV3(rootDir, options) {
2924
2934
  const lengthUnitsFilePath = options.lengthUnitsFilePath ?? "lib/util/dataTypes.js";
2925
2935
  const variableName = options.variableName ?? "lengthUnits";
2926
2936
  const target = path9.resolve(rootDir, lengthUnitsFilePath);
2927
- const files = fs8.existsSync(target) ? [path9.relative(rootDir, target)] : [];
2928
- if (!fs8.existsSync(target)) {
2937
+ const files = fs9.existsSync(target) ? [path9.relative(rootDir, target)] : [];
2938
+ if (!fs9.existsSync(target)) {
2929
2939
  return {
2930
2940
  name: "extendLengthUnits",
2931
2941
  status: "not-applied",
@@ -2933,7 +2943,7 @@ function checkExtendLengthUnitsV3(rootDir, options) {
2933
2943
  files
2934
2944
  };
2935
2945
  }
2936
- const content = fs8.readFileSync(target, "utf8");
2946
+ const content = fs9.readFileSync(target, "utf8");
2937
2947
  const { found, missingUnits } = inspectLengthUnitsArray(content, variableName, options.units);
2938
2948
  if (!found) {
2939
2949
  return {
@@ -2959,7 +2969,7 @@ function checkExtendLengthUnitsV3(rootDir, options) {
2959
2969
  }
2960
2970
  function checkExtendLengthUnitsV4(rootDir, options) {
2961
2971
  const distDir = path9.resolve(rootDir, "dist");
2962
- if (!fs8.existsSync(distDir)) {
2972
+ if (!fs9.existsSync(distDir)) {
2963
2973
  return {
2964
2974
  name: "extendLengthUnits",
2965
2975
  status: "not-applied",
@@ -3036,19 +3046,19 @@ function getPatchStatusReport(context) {
3036
3046
 
3037
3047
  // src/api/tailwindcss-patcher.ts
3038
3048
  import process6 from "process";
3039
- import fs10 from "fs-extra";
3049
+ import fs11 from "fs-extra";
3040
3050
  import { getPackageInfoSync } from "local-pkg";
3041
3051
  import path11 from "pathe";
3042
3052
  import { coerce } from "semver";
3043
3053
 
3044
3054
  // src/patching/operations/export-context/index.ts
3045
- import fs9 from "fs-extra";
3055
+ import fs10 from "fs-extra";
3046
3056
  import path10 from "pathe";
3047
3057
  function writeFileIfRequired(filePath, code, overwrite, successMessage) {
3048
3058
  if (!overwrite) {
3049
3059
  return;
3050
3060
  }
3051
- fs9.writeFileSync(filePath, code, {
3061
+ fs10.writeFileSync(filePath, code, {
3052
3062
  encoding: "utf8"
3053
3063
  });
3054
3064
  logger_default.success(successMessage);
@@ -3062,8 +3072,8 @@ function applyExposeContextPatch(params) {
3062
3072
  if (majorVersion === 3) {
3063
3073
  const processFileRelative = "lib/processTailwindFeatures.js";
3064
3074
  const processFilePath = path10.resolve(rootDir, processFileRelative);
3065
- if (fs9.existsSync(processFilePath)) {
3066
- const content = fs9.readFileSync(processFilePath, "utf8");
3075
+ if (fs10.existsSync(processFilePath)) {
3076
+ const content = fs10.readFileSync(processFilePath, "utf8");
3067
3077
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContext(content);
3068
3078
  result.files[processFileRelative] = code;
3069
3079
  if (!hasPatched) {
@@ -3077,10 +3087,10 @@ function applyExposeContextPatch(params) {
3077
3087
  }
3078
3088
  }
3079
3089
  const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
3080
- const pluginRelative = pluginCandidates.find((candidate) => fs9.existsSync(path10.resolve(rootDir, candidate)));
3090
+ const pluginRelative = pluginCandidates.find((candidate) => fs10.existsSync(path10.resolve(rootDir, candidate)));
3081
3091
  if (pluginRelative) {
3082
3092
  const pluginPath = path10.resolve(rootDir, pluginRelative);
3083
- const content = fs9.readFileSync(pluginPath, "utf8");
3093
+ const content = fs10.readFileSync(pluginPath, "utf8");
3084
3094
  const { code, hasPatched } = transformPostcssPlugin(content, { refProperty });
3085
3095
  result.files[pluginRelative] = code;
3086
3096
  if (!hasPatched) {
@@ -3096,8 +3106,8 @@ function applyExposeContextPatch(params) {
3096
3106
  } else if (majorVersion === 2) {
3097
3107
  const processFileRelative = "lib/jit/processTailwindFeatures.js";
3098
3108
  const processFilePath = path10.resolve(rootDir, processFileRelative);
3099
- if (fs9.existsSync(processFilePath)) {
3100
- const content = fs9.readFileSync(processFilePath, "utf8");
3109
+ if (fs10.existsSync(processFilePath)) {
3110
+ const content = fs10.readFileSync(processFilePath, "utf8");
3101
3111
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContextV2(content);
3102
3112
  result.files[processFileRelative] = code;
3103
3113
  if (!hasPatched) {
@@ -3112,8 +3122,8 @@ function applyExposeContextPatch(params) {
3112
3122
  }
3113
3123
  const pluginRelative = "lib/jit/index.js";
3114
3124
  const pluginPath = path10.resolve(rootDir, pluginRelative);
3115
- if (fs9.existsSync(pluginPath)) {
3116
- const content = fs9.readFileSync(pluginPath, "utf8");
3125
+ if (fs10.existsSync(pluginPath)) {
3126
+ const content = fs10.readFileSync(pluginPath, "utf8");
3117
3127
  const { code, hasPatched } = transformPostcssPluginV2(content, { refProperty });
3118
3128
  result.files[pluginRelative] = code;
3119
3129
  if (!hasPatched) {
@@ -3158,25 +3168,7 @@ function applyTailwindPatches(context) {
3158
3168
  return results;
3159
3169
  }
3160
3170
 
3161
- // src/api/tailwindcss-patcher.ts
3162
- function resolveMajorVersion(version, hint) {
3163
- if (hint && [2, 3, 4].includes(hint)) {
3164
- return hint;
3165
- }
3166
- if (version) {
3167
- const coerced = coerce(version);
3168
- if (coerced) {
3169
- const major = coerced.major;
3170
- if (major === 2 || major === 3 || major === 4) {
3171
- return major;
3172
- }
3173
- if (major >= 4) {
3174
- return 4;
3175
- }
3176
- }
3177
- }
3178
- return 3;
3179
- }
3171
+ // src/runtime/collector.ts
3180
3172
  function resolveTailwindExecutionOptions(normalized, majorVersion) {
3181
3173
  const base = normalized.tailwind;
3182
3174
  if (majorVersion === 2 && base.v2) {
@@ -3199,15 +3191,138 @@ function resolveTailwindExecutionOptions(normalized, majorVersion) {
3199
3191
  postcssPlugin: base.postcssPlugin
3200
3192
  };
3201
3193
  }
3194
+ var BaseCollector = class {
3195
+ constructor(packageInfo, options, majorVersion) {
3196
+ this.packageInfo = packageInfo;
3197
+ this.options = options;
3198
+ this.majorVersion = majorVersion;
3199
+ }
3200
+ async patch() {
3201
+ return applyTailwindPatches({
3202
+ packageInfo: this.packageInfo,
3203
+ options: this.options,
3204
+ majorVersion: this.majorVersion
3205
+ });
3206
+ }
3207
+ async getPatchStatus() {
3208
+ return getPatchStatusReport({
3209
+ packageInfo: this.packageInfo,
3210
+ options: this.options,
3211
+ majorVersion: this.majorVersion
3212
+ });
3213
+ }
3214
+ getContexts() {
3215
+ return loadRuntimeContexts(
3216
+ this.packageInfo,
3217
+ this.majorVersion,
3218
+ this.options.features.exposeContext.refProperty
3219
+ );
3220
+ }
3221
+ };
3222
+ var RuntimeCollector = class extends BaseCollector {
3223
+ constructor(packageInfo, options, majorVersion, snapshotFactory) {
3224
+ super(packageInfo, options, majorVersion);
3225
+ this.snapshotFactory = snapshotFactory;
3226
+ }
3227
+ inFlightBuild;
3228
+ async collectClassSet() {
3229
+ const contexts = this.getContexts();
3230
+ return collectClassesFromContexts(contexts, this.options.filter);
3231
+ }
3232
+ getPatchSnapshot() {
3233
+ return this.snapshotFactory();
3234
+ }
3235
+ async runTailwindBuildIfNeeded() {
3236
+ if (this.inFlightBuild) {
3237
+ return this.inFlightBuild;
3238
+ }
3239
+ const executionOptions = resolveTailwindExecutionOptions(this.options, this.majorVersion);
3240
+ const buildOptions = {
3241
+ cwd: executionOptions.cwd,
3242
+ majorVersion: this.majorVersion,
3243
+ ...executionOptions.config === void 0 ? {} : { config: executionOptions.config },
3244
+ ...executionOptions.postcssPlugin === void 0 ? {} : { postcssPlugin: executionOptions.postcssPlugin }
3245
+ };
3246
+ this.inFlightBuild = runTailwindBuild(buildOptions).then(() => void 0);
3247
+ try {
3248
+ await this.inFlightBuild;
3249
+ } finally {
3250
+ this.inFlightBuild = void 0;
3251
+ }
3252
+ }
3253
+ };
3254
+ var TailwindV4Collector = class extends BaseCollector {
3255
+ constructor(packageInfo, options, snapshotFactory) {
3256
+ super(packageInfo, options, 4);
3257
+ this.snapshotFactory = snapshotFactory;
3258
+ }
3259
+ snapshotFactory;
3260
+ async collectClassSet() {
3261
+ return collectClassesFromTailwindV4(this.options);
3262
+ }
3263
+ getPatchSnapshot() {
3264
+ return this.snapshotFactory();
3265
+ }
3266
+ };
3267
+
3268
+ // src/api/tailwindcss-patcher.ts
3269
+ function resolveInstalledMajorVersion(version) {
3270
+ if (!version) {
3271
+ return void 0;
3272
+ }
3273
+ const coerced = coerce(version);
3274
+ if (!coerced) {
3275
+ return void 0;
3276
+ }
3277
+ const major = coerced.major;
3278
+ if (major === 2 || major === 3 || major === 4) {
3279
+ return major;
3280
+ }
3281
+ if (major >= 4) {
3282
+ return 4;
3283
+ }
3284
+ return void 0;
3285
+ }
3286
+ function validateInstalledVersion(packageVersion, expectedMajor, packageName) {
3287
+ const installedMajor = resolveInstalledMajorVersion(packageVersion);
3288
+ if (installedMajor === void 0) {
3289
+ return;
3290
+ }
3291
+ if (installedMajor !== expectedMajor) {
3292
+ throw new Error(
3293
+ `Configured tailwindcss.version=${expectedMajor}, but resolved package "${packageName}" is version ${packageVersion}. Update the configuration or resolve the correct package.`
3294
+ );
3295
+ }
3296
+ }
3297
+ function resolveMajorVersionOrThrow(configuredMajor, packageVersion, packageName) {
3298
+ if (configuredMajor !== void 0) {
3299
+ validateInstalledVersion(packageVersion, configuredMajor, packageName);
3300
+ return configuredMajor;
3301
+ }
3302
+ const installedMajor = resolveInstalledMajorVersion(packageVersion);
3303
+ if (installedMajor !== void 0) {
3304
+ return installedMajor;
3305
+ }
3306
+ throw new Error(
3307
+ `Unable to infer Tailwind CSS major version from resolved package "${packageName}" (${packageVersion ?? "unknown"}). Set "tailwindcss.version" to 2, 3, or 4 explicitly.`
3308
+ );
3309
+ }
3310
+ function createCollector(packageInfo, options, majorVersion, snapshotFactory) {
3311
+ if (majorVersion === 4) {
3312
+ return new TailwindV4Collector(packageInfo, options, snapshotFactory);
3313
+ }
3314
+ return new RuntimeCollector(packageInfo, options, majorVersion, snapshotFactory);
3315
+ }
3202
3316
  var TailwindcssPatcher = class {
3203
3317
  options;
3204
3318
  packageInfo;
3205
3319
  majorVersion;
3206
3320
  cacheContext;
3207
3321
  cacheStore;
3322
+ collector;
3323
+ patchMemo;
3208
3324
  constructor(options = {}) {
3209
- const resolvedOptions = options && typeof options === "object" && "patch" in options ? fromLegacyOptions(options) : options;
3210
- this.options = normalizeOptions(resolvedOptions);
3325
+ this.options = normalizeOptions(options);
3211
3326
  const packageInfo = getPackageInfoSync(
3212
3327
  this.options.tailwind.packageName,
3213
3328
  this.options.tailwind.resolve
@@ -3216,9 +3331,10 @@ var TailwindcssPatcher = class {
3216
3331
  throw new Error(`Unable to locate Tailwind CSS package "${this.options.tailwind.packageName}".`);
3217
3332
  }
3218
3333
  this.packageInfo = packageInfo;
3219
- this.majorVersion = resolveMajorVersion(
3334
+ this.majorVersion = resolveMajorVersionOrThrow(
3335
+ this.options.tailwind.versionHint,
3220
3336
  this.packageInfo.version,
3221
- this.options.tailwind.versionHint
3337
+ this.options.tailwind.packageName
3222
3338
  );
3223
3339
  this.cacheContext = createCacheContextDescriptor(
3224
3340
  this.options,
@@ -3226,47 +3342,81 @@ var TailwindcssPatcher = class {
3226
3342
  this.majorVersion
3227
3343
  );
3228
3344
  this.cacheStore = new CacheStore(this.options.cache, this.cacheContext);
3345
+ this.collector = createCollector(
3346
+ this.packageInfo,
3347
+ this.options,
3348
+ this.majorVersion,
3349
+ () => this.createPatchSnapshot()
3350
+ );
3229
3351
  }
3230
3352
  async patch() {
3231
- return applyTailwindPatches({
3232
- packageInfo: this.packageInfo,
3233
- options: this.options,
3234
- majorVersion: this.majorVersion
3235
- });
3353
+ const snapshot = this.collector.getPatchSnapshot();
3354
+ if (this.patchMemo && this.patchMemo.snapshot === snapshot) {
3355
+ return this.patchMemo.result;
3356
+ }
3357
+ const result = await this.collector.patch();
3358
+ this.patchMemo = {
3359
+ result,
3360
+ snapshot: this.collector.getPatchSnapshot()
3361
+ };
3362
+ return result;
3236
3363
  }
3237
3364
  async getPatchStatus() {
3238
- return getPatchStatusReport({
3239
- packageInfo: this.packageInfo,
3240
- options: this.options,
3241
- majorVersion: this.majorVersion
3242
- });
3365
+ return this.collector.getPatchStatus();
3243
3366
  }
3244
3367
  getContexts() {
3245
- return loadRuntimeContexts(
3246
- this.packageInfo,
3247
- this.majorVersion,
3248
- this.options.features.exposeContext.refProperty
3249
- );
3368
+ return this.collector.getContexts();
3250
3369
  }
3251
- async runTailwindBuildIfNeeded() {
3252
- if (this.majorVersion === 2 || this.majorVersion === 3) {
3253
- const executionOptions = resolveTailwindExecutionOptions(this.options, this.majorVersion);
3254
- const buildOptions = {
3255
- cwd: executionOptions.cwd,
3256
- majorVersion: this.majorVersion,
3257
- ...executionOptions.config === void 0 ? {} : { config: executionOptions.config },
3258
- ...executionOptions.postcssPlugin === void 0 ? {} : { postcssPlugin: executionOptions.postcssPlugin }
3259
- };
3260
- await runTailwindBuild(buildOptions);
3370
+ createPatchSnapshot() {
3371
+ const entries = [];
3372
+ const pushSnapshot = (filePath) => {
3373
+ if (!fs11.pathExistsSync(filePath)) {
3374
+ entries.push(`${filePath}:missing`);
3375
+ return;
3376
+ }
3377
+ const stat = fs11.statSync(filePath);
3378
+ entries.push(`${filePath}:${stat.size}:${Math.trunc(stat.mtimeMs)}`);
3379
+ };
3380
+ if (this.options.features.exposeContext.enabled && (this.majorVersion === 2 || this.majorVersion === 3)) {
3381
+ if (this.majorVersion === 2) {
3382
+ pushSnapshot(path11.resolve(this.packageInfo.rootPath, "lib/jit/processTailwindFeatures.js"));
3383
+ pushSnapshot(path11.resolve(this.packageInfo.rootPath, "lib/jit/index.js"));
3384
+ } else {
3385
+ pushSnapshot(path11.resolve(this.packageInfo.rootPath, "lib/processTailwindFeatures.js"));
3386
+ const pluginPath = ["lib/plugin.js", "lib/index.js"].map((file) => path11.resolve(this.packageInfo.rootPath, file)).find((file) => fs11.pathExistsSync(file));
3387
+ if (pluginPath) {
3388
+ pushSnapshot(pluginPath);
3389
+ }
3390
+ }
3261
3391
  }
3392
+ if (this.options.features.extendLengthUnits?.enabled) {
3393
+ if (this.majorVersion === 3) {
3394
+ const target = this.options.features.extendLengthUnits.lengthUnitsFilePath ?? "lib/util/dataTypes.js";
3395
+ pushSnapshot(path11.resolve(this.packageInfo.rootPath, target));
3396
+ } else if (this.majorVersion === 4) {
3397
+ const distDir = path11.resolve(this.packageInfo.rootPath, "dist");
3398
+ if (fs11.pathExistsSync(distDir)) {
3399
+ const chunkNames = fs11.readdirSync(distDir).filter((entry) => entry.endsWith(".js") || entry.endsWith(".mjs")).sort();
3400
+ for (const chunkName of chunkNames) {
3401
+ pushSnapshot(path11.join(distDir, chunkName));
3402
+ }
3403
+ } else {
3404
+ entries.push(`${distDir}:missing`);
3405
+ }
3406
+ }
3407
+ }
3408
+ return entries.join("|");
3262
3409
  }
3263
3410
  async collectClassSet() {
3264
3411
  if (this.majorVersion === 4) {
3265
- return collectClassesFromTailwindV4(this.options);
3412
+ return this.collector.collectClassSet();
3266
3413
  }
3267
3414
  const contexts = this.getContexts();
3268
3415
  return collectClassesFromContexts(contexts, this.options.filter);
3269
3416
  }
3417
+ async runTailwindBuildIfNeeded() {
3418
+ await this.collector.runTailwindBuildIfNeeded?.();
3419
+ }
3270
3420
  debugCacheRead(meta) {
3271
3421
  if (meta.hit) {
3272
3422
  logger_default.debug(
@@ -3288,13 +3438,13 @@ var TailwindcssPatcher = class {
3288
3438
  for (const value of existing) {
3289
3439
  set.add(value);
3290
3440
  }
3291
- const writeTarget = await this.cacheStore.write(set);
3441
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : await this.cacheStore.write(set);
3292
3442
  if (writeTarget) {
3293
3443
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3294
3444
  }
3295
3445
  } else {
3296
3446
  if (set.size > 0) {
3297
- const writeTarget = await this.cacheStore.write(set);
3447
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : await this.cacheStore.write(set);
3298
3448
  if (writeTarget) {
3299
3449
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3300
3450
  }
@@ -3314,13 +3464,13 @@ var TailwindcssPatcher = class {
3314
3464
  for (const value of existing) {
3315
3465
  set.add(value);
3316
3466
  }
3317
- const writeTarget = this.cacheStore.writeSync(set);
3467
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : this.cacheStore.writeSync(set);
3318
3468
  if (writeTarget) {
3319
3469
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3320
3470
  }
3321
3471
  } else {
3322
3472
  if (set.size > 0) {
3323
- const writeTarget = this.cacheStore.writeSync(set);
3473
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : this.cacheStore.writeSync(set);
3324
3474
  if (writeTarget) {
3325
3475
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3326
3476
  }
@@ -3330,6 +3480,17 @@ var TailwindcssPatcher = class {
3330
3480
  }
3331
3481
  return set;
3332
3482
  }
3483
+ areSetsEqual(a, b) {
3484
+ if (a.size !== b.size) {
3485
+ return false;
3486
+ }
3487
+ for (const value of a) {
3488
+ if (!b.has(value)) {
3489
+ return false;
3490
+ }
3491
+ }
3492
+ return true;
3493
+ }
3333
3494
  async getClassSet() {
3334
3495
  await this.runTailwindBuildIfNeeded();
3335
3496
  const set = await this.collectClassSet();
@@ -3359,12 +3520,12 @@ var TailwindcssPatcher = class {
3359
3520
  return result;
3360
3521
  }
3361
3522
  const target = path11.resolve(this.options.output.file);
3362
- await fs10.ensureDir(path11.dirname(target));
3523
+ await fs11.ensureDir(path11.dirname(target));
3363
3524
  if (this.options.output.format === "json") {
3364
3525
  const spaces = typeof this.options.output.pretty === "number" ? this.options.output.pretty : void 0;
3365
- await fs10.writeJSON(target, classList, { spaces });
3526
+ await fs11.writeJSON(target, classList, { spaces });
3366
3527
  } else {
3367
- await fs10.writeFile(target, `${classList.join("\n")}
3528
+ await fs11.writeFile(target, `${classList.join("\n")}
3368
3529
  `, "utf8");
3369
3530
  }
3370
3531
  logger_default.success(`Tailwind CSS class list saved to ${target.replace(process6.cwd(), ".")}`);
@@ -3483,7 +3644,7 @@ function buildMigrationReport(state, context) {
3483
3644
  }
3484
3645
 
3485
3646
  // src/commands/migration-file-executor.ts
3486
- import fs12 from "fs-extra";
3647
+ import fs13 from "fs-extra";
3487
3648
  import path13 from "pathe";
3488
3649
 
3489
3650
  // src/commands/migration-source.ts
@@ -3752,7 +3913,7 @@ function migrateConfigSource(source) {
3752
3913
  }
3753
3914
 
3754
3915
  // src/commands/migration-target-files.ts
3755
- import fs11 from "fs-extra";
3916
+ import fs12 from "fs-extra";
3756
3917
  import path12 from "pathe";
3757
3918
  var DEFAULT_CONFIG_FILENAMES = [
3758
3919
  "tailwindcss-patch.config.ts",
@@ -3796,7 +3957,7 @@ async function collectWorkspaceConfigFiles(cwd, maxDepth) {
3796
3957
  const { dir, depth } = current;
3797
3958
  let entries;
3798
3959
  try {
3799
- entries = await fs11.readdir(dir, { withFileTypes: true });
3960
+ entries = await fs12.readdir(dir, { withFileTypes: true });
3800
3961
  } catch {
3801
3962
  continue;
3802
3963
  }
@@ -3893,7 +4054,7 @@ async function rollbackWrittenEntries(wroteEntries) {
3893
4054
  let rollbackCount = 0;
3894
4055
  for (const written of [...wroteEntries].reverse()) {
3895
4056
  try {
3896
- await fs12.writeFile(written.file, written.source, "utf8");
4057
+ await fs13.writeFile(written.file, written.source, "utf8");
3897
4058
  written.entry.written = false;
3898
4059
  written.entry.rolledBack = true;
3899
4060
  rollbackCount += 1;
@@ -3911,7 +4072,7 @@ async function executeMigrationFile(options) {
3911
4072
  backupDirectory,
3912
4073
  wroteEntries
3913
4074
  } = options;
3914
- const exists = await fs12.pathExists(file);
4075
+ const exists = await fs13.pathExists(file);
3915
4076
  if (!exists) {
3916
4077
  return {
3917
4078
  missing: true,
@@ -3920,7 +4081,7 @@ async function executeMigrationFile(options) {
3920
4081
  backupWritten: false
3921
4082
  };
3922
4083
  }
3923
- const source = await fs12.readFile(file, "utf8");
4084
+ const source = await fs13.readFile(file, "utf8");
3924
4085
  const migrated = migrateConfigSource(source);
3925
4086
  const entry = {
3926
4087
  file,
@@ -3943,12 +4104,12 @@ async function executeMigrationFile(options) {
3943
4104
  if (backupDirectory) {
3944
4105
  const backupRelativePath = resolveBackupRelativePath(cwd, file);
3945
4106
  const backupFile = path13.resolve(backupDirectory, backupRelativePath);
3946
- await fs12.ensureDir(path13.dirname(backupFile));
3947
- await fs12.writeFile(backupFile, source, "utf8");
4107
+ await fs13.ensureDir(path13.dirname(backupFile));
4108
+ await fs13.writeFile(backupFile, source, "utf8");
3948
4109
  entry.backupFile = backupFile;
3949
4110
  backupWritten = true;
3950
4111
  }
3951
- await fs12.writeFile(file, migrated.code, "utf8");
4112
+ await fs13.writeFile(file, migrated.code, "utf8");
3952
4113
  entry.written = true;
3953
4114
  wroteEntries.push({ file, source, entry });
3954
4115
  return {
@@ -3981,15 +4142,15 @@ async function restoreConfigEntries(entries, dryRun) {
3981
4142
  continue;
3982
4143
  }
3983
4144
  restorableEntries += 1;
3984
- const backupExists = await fs12.pathExists(backupFile);
4145
+ const backupExists = await fs13.pathExists(backupFile);
3985
4146
  if (!backupExists) {
3986
4147
  missingBackups += 1;
3987
4148
  continue;
3988
4149
  }
3989
4150
  if (!dryRun) {
3990
- const backupContent = await fs12.readFile(backupFile, "utf8");
3991
- await fs12.ensureDir(path13.dirname(targetFile));
3992
- await fs12.writeFile(targetFile, backupContent, "utf8");
4151
+ const backupContent = await fs13.readFile(backupFile, "utf8");
4152
+ await fs13.ensureDir(path13.dirname(targetFile));
4153
+ await fs13.writeFile(targetFile, backupContent, "utf8");
3993
4154
  }
3994
4155
  restoredFiles += 1;
3995
4156
  restored.push(targetFile);
@@ -4005,9 +4166,9 @@ async function restoreConfigEntries(entries, dryRun) {
4005
4166
  }
4006
4167
 
4007
4168
  // src/commands/migration-report-loader.ts
4008
- import fs13 from "fs-extra";
4169
+ import fs14 from "fs-extra";
4009
4170
  async function loadMigrationReportForRestore(reportFile) {
4010
- const report = await fs13.readJSON(reportFile);
4171
+ const report = await fs14.readJSON(reportFile);
4011
4172
  assertMigrationReportCompatibility(report, reportFile);
4012
4173
  return {
4013
4174
  ...report.reportKind === void 0 ? {} : { reportKind: report.reportKind },
@@ -4391,11 +4552,11 @@ function runWithCommandHandler(cli, command, commandName, args, handler, default
4391
4552
 
4392
4553
  // src/commands/basic-handlers.ts
4393
4554
  import process9 from "process";
4394
- import fs14 from "fs-extra";
4555
+ import fs15 from "fs-extra";
4395
4556
  import path16 from "pathe";
4396
4557
  var DEFAULT_CONFIG_NAME = "tailwindcss-mangle";
4397
4558
  async function installCommandDefaultHandler(_ctx) {
4398
- const patcher = new TailwindcssPatcher();
4559
+ const patcher = await _ctx.createPatcher();
4399
4560
  await patcher.patch();
4400
4561
  logger_default.success("Tailwind CSS runtime patched successfully.");
4401
4562
  }
@@ -4447,14 +4608,14 @@ async function tokensCommandDefaultHandler(ctx) {
4447
4608
  const resolveGrouped = () => grouped ?? buildGrouped();
4448
4609
  if (shouldWrite) {
4449
4610
  const target = path16.resolve(targetFile);
4450
- await fs14.ensureDir(path16.dirname(target));
4611
+ await fs15.ensureDir(path16.dirname(target));
4451
4612
  if (format === "json") {
4452
- await fs14.writeJSON(target, report, { spaces: 2 });
4613
+ await fs15.writeJSON(target, report, { spaces: 2 });
4453
4614
  } else if (format === "grouped-json") {
4454
- await fs14.writeJSON(target, resolveGrouped(), { spaces: 2 });
4615
+ await fs15.writeJSON(target, resolveGrouped(), { spaces: 2 });
4455
4616
  } else {
4456
4617
  const lines = report.entries.map(formatTokenLine);
4457
- await fs14.writeFile(target, `${lines.join("\n")}
4618
+ await fs15.writeFile(target, `${lines.join("\n")}
4458
4619
  `, "utf8");
4459
4620
  }
4460
4621
  logger_default.success(`Collected ${report.entries.length} tokens (${format}) \u2192 ${target.replace(process9.cwd(), ".")}`);
@@ -4561,7 +4722,7 @@ function resolveValidateCommandArgs(args) {
4561
4722
 
4562
4723
  // src/commands/migration-output.ts
4563
4724
  import process10 from "process";
4564
- import fs15 from "fs-extra";
4725
+ import fs16 from "fs-extra";
4565
4726
  import path17 from "pathe";
4566
4727
  function formatPathForLog(file) {
4567
4728
  return file.replace(process10.cwd(), ".");
@@ -4571,8 +4732,8 @@ function createMigrationCheckFailureError(changedFiles) {
4571
4732
  }
4572
4733
  async function writeMigrationReportFile(cwd, reportFile, report) {
4573
4734
  const reportPath = path17.resolve(cwd, reportFile);
4574
- await fs15.ensureDir(path17.dirname(reportPath));
4575
- await fs15.writeJSON(reportPath, report, { spaces: 2 });
4735
+ await fs16.ensureDir(path17.dirname(reportPath));
4736
+ await fs16.writeJSON(reportPath, report, { spaces: 2 });
4576
4737
  logger_default.info(`Migration report written: ${formatPathForLog(reportPath)}`);
4577
4738
  }
4578
4739
  function logMigrationReportAsJson(report) {