tailwindcss-patch 8.7.3 → 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,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: "8.7.4-alpha.0",
24
24
  description: "patch tailwindcss for exposing context and extract classes",
25
25
  author: "ice breaker <1324318532@qq.com>",
26
26
  license: "MIT",
@@ -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
  },
@@ -1906,7 +1907,7 @@ async function loadWorkspaceConfigModule() {
1906
1907
  }
1907
1908
  async function loadWorkspaceDefu() {
1908
1909
  if (!defuPromise) {
1909
- defuPromise = import("./dist-EMUBVNNO.mjs").then((mod) => mod.defu).catch(async (error) => {
1910
+ defuPromise = import("./dist-7UDSGIWH.mjs").then((mod) => mod.defu).catch(async (error) => {
1910
1911
  if (!isMissingSharedModuleError(error)) {
1911
1912
  throw error;
1912
1913
  }
@@ -1931,33 +1932,66 @@ async function loadPatchOptionsForWorkspace(cwd, overrides) {
1931
1932
  import { promises as fs4 } from "fs";
1932
1933
  import process4 from "process";
1933
1934
  import path4 from "pathe";
1935
+ var nodeImportPromise;
1936
+ var oxideImportPromise;
1937
+ var designSystemPromiseCache = /* @__PURE__ */ new Map();
1938
+ var designSystemCandidateCache = /* @__PURE__ */ new Map();
1934
1939
  async function importNode() {
1935
1940
  return import("@tailwindcss/node");
1936
1941
  }
1937
1942
  async function importOxide() {
1938
1943
  return import("@tailwindcss/oxide");
1939
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
+ }
1940
1959
  async function loadDesignSystem(css, bases) {
1941
1960
  const uniqueBases = Array.from(new Set(bases.filter(Boolean)));
1942
1961
  if (uniqueBases.length === 0) {
1943
1962
  throw new Error("No base directories provided for Tailwind CSS design system.");
1944
1963
  }
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;
1964
+ const cacheKey = createDesignSystemCacheKey(css, uniqueBases);
1965
+ const cached = designSystemPromiseCache.get(cacheKey);
1966
+ if (cached) {
1967
+ return cached;
1956
1968
  }
1957
- throw new Error("Failed to load Tailwind CSS design system.");
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;
1958
1992
  }
1959
1993
  async function extractRawCandidatesWithPositions(content, extension = "html") {
1960
- const { Scanner } = await importOxide();
1994
+ const { Scanner } = await getOxideModule();
1961
1995
  const scanner = new Scanner({});
1962
1996
  const result = scanner.getCandidatesWithPositions({ content, extension });
1963
1997
  return result.map(({ candidate, position }) => ({
@@ -1967,7 +2001,7 @@ async function extractRawCandidatesWithPositions(content, extension = "html") {
1967
2001
  }));
1968
2002
  }
1969
2003
  async function extractRawCandidates(sources) {
1970
- const { Scanner } = await importOxide();
2004
+ const { Scanner } = await getOxideModule();
1971
2005
  const scanner = new Scanner(sources === void 0 ? {} : { sources });
1972
2006
  return scanner.scan();
1973
2007
  }
@@ -1988,25 +2022,44 @@ async function extractValidCandidates(options) {
1988
2022
  pattern: source.pattern,
1989
2023
  negated: source.negated
1990
2024
  }));
2025
+ const designSystemKey = createDesignSystemCacheKey(css, [base, ...baseFallbacks]);
1991
2026
  const designSystem = await loadDesignSystem(css, [base, ...baseFallbacks]);
2027
+ const candidateCache = designSystemCandidateCache.get(designSystemKey) ?? /* @__PURE__ */ new Map();
2028
+ designSystemCandidateCache.set(designSystemKey, candidateCache);
1992
2029
  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
2030
  const validCandidates = [];
2001
- for (let index = 0; index < parsedCandidates.length; index++) {
2002
- const candidate = parsedCandidates[index];
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];
2003
2053
  if (candidate === void 0) {
2004
2054
  continue;
2005
2055
  }
2006
- const css2 = cssByCandidate[index];
2007
- if (typeof css2 === "string" && css2.trim().length > 0) {
2008
- validCandidates.push(candidate);
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;
2009
2061
  }
2062
+ validCandidates.push(candidate);
2010
2063
  }
2011
2064
  return validCandidates;
2012
2065
  }
@@ -2078,7 +2131,7 @@ function toRelativeFile(cwd, filename) {
2078
2131
  async function extractProjectCandidatesWithPositions(options) {
2079
2132
  const cwd = options?.cwd ? path4.resolve(options.cwd) : process4.cwd();
2080
2133
  const normalizedSources = normalizeSources(options?.sources, cwd);
2081
- const { Scanner } = await importOxide();
2134
+ const { Scanner } = await getOxideModule();
2082
2135
  const scanner = new Scanner({
2083
2136
  sources: normalizedSources
2084
2137
  });
@@ -2313,10 +2366,53 @@ function loadRuntimeContexts(packageInfo, majorVersion, refProperty) {
2313
2366
 
2314
2367
  // src/runtime/process-tailwindcss.ts
2315
2368
  import { createRequire as createRequire2 } from "module";
2369
+ import fs7 from "fs-extra";
2316
2370
  import path7 from "pathe";
2317
2371
  import postcss from "postcss";
2318
2372
  import { loadConfig } from "tailwindcss-config";
2319
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
+ }
2320
2416
  async function resolveConfigPath(options) {
2321
2417
  if (options.config && path7.isAbsolute(options.config)) {
2322
2418
  return options.config;
@@ -2330,6 +2426,9 @@ async function resolveConfigPath(options) {
2330
2426
  async function runTailwindBuild(options) {
2331
2427
  const configPath = await resolveConfigPath(options);
2332
2428
  const pluginName = options.postcssPlugin ?? (options.majorVersion === 4 ? "@tailwindcss/postcss" : "tailwindcss");
2429
+ if (options.majorVersion === 3) {
2430
+ clearTailwindV3RuntimeState(pluginName);
2431
+ }
2333
2432
  if (options.majorVersion === 4) {
2334
2433
  return postcss([
2335
2434
  require3(pluginName)({
@@ -2350,7 +2449,7 @@ async function runTailwindBuild(options) {
2350
2449
 
2351
2450
  // src/patching/status.ts
2352
2451
  import * as t4 from "@babel/types";
2353
- import fs8 from "fs-extra";
2452
+ import fs9 from "fs-extra";
2354
2453
  import path9 from "pathe";
2355
2454
 
2356
2455
  // src/babel/index.ts
@@ -2695,7 +2794,7 @@ function transformPostcssPlugin(content, { refProperty }) {
2695
2794
 
2696
2795
  // src/patching/operations/extend-length-units.ts
2697
2796
  import * as t3 from "@babel/types";
2698
- import fs7 from "fs-extra";
2797
+ import fs8 from "fs-extra";
2699
2798
  import path8 from "pathe";
2700
2799
  function updateLengthUnitsArray(content, options) {
2701
2800
  const { variableName = "lengthUnits", units } = options;
@@ -2739,11 +2838,11 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
2739
2838
  variableName: options.variableName ?? "lengthUnits"
2740
2839
  };
2741
2840
  const dataTypesFilePath = path8.resolve(rootDir, opts.lengthUnitsFilePath);
2742
- const exists = fs7.existsSync(dataTypesFilePath);
2841
+ const exists = fs8.existsSync(dataTypesFilePath);
2743
2842
  if (!exists) {
2744
2843
  return { changed: false, code: void 0 };
2745
2844
  }
2746
- const content = fs7.readFileSync(dataTypesFilePath, "utf8");
2845
+ const content = fs8.readFileSync(dataTypesFilePath, "utf8");
2747
2846
  const { arrayRef, changed } = updateLengthUnitsArray(content, opts);
2748
2847
  if (!arrayRef || !changed) {
2749
2848
  return { changed: false, code: void 0 };
@@ -2755,7 +2854,7 @@ function applyExtendLengthUnitsPatchV3(rootDir, options) {
2755
2854
  const nextCode = `${content.slice(0, arrayRef.start)}${code}${content.slice(arrayRef.end)}`;
2756
2855
  if (opts.overwrite) {
2757
2856
  const target = opts.destPath ? path8.resolve(opts.destPath) : dataTypesFilePath;
2758
- fs7.writeFileSync(target, nextCode, "utf8");
2857
+ fs8.writeFileSync(target, nextCode, "utf8");
2759
2858
  logger_default.success("Patched Tailwind CSS length unit list (v3).");
2760
2859
  }
2761
2860
  return {
@@ -2774,15 +2873,15 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
2774
2873
  }
2775
2874
  const opts = { ...options };
2776
2875
  const distDir = path8.resolve(rootDir, "dist");
2777
- if (!fs7.existsSync(distDir)) {
2876
+ if (!fs8.existsSync(distDir)) {
2778
2877
  return { files: [], changed: false };
2779
2878
  }
2780
- const entries = fs7.readdirSync(distDir);
2879
+ const entries = fs8.readdirSync(distDir);
2781
2880
  const chunkNames = entries.filter((entry) => entry.endsWith(".js") || entry.endsWith(".mjs"));
2782
2881
  const pattern = /\[\s*["']cm["'],\s*["']mm["'],[\w,"']+\]/;
2783
2882
  const candidates = chunkNames.map((chunkName) => {
2784
2883
  const file = path8.join(distDir, chunkName);
2785
- const code = fs7.readFileSync(file, "utf8");
2884
+ const code = fs8.readFileSync(file, "utf8");
2786
2885
  const match = pattern.exec(code);
2787
2886
  if (!match) {
2788
2887
  return null;
@@ -2822,7 +2921,7 @@ function applyExtendLengthUnitsPatchV4(rootDir, options) {
2822
2921
  }
2823
2922
  ]);
2824
2923
  if (opts.overwrite) {
2825
- fs7.writeFileSync(file, item.code, "utf8");
2924
+ fs8.writeFileSync(file, item.code, "utf8");
2826
2925
  }
2827
2926
  }
2828
2927
  if (candidates.some((file) => !file.hasPatched)) {
@@ -2878,11 +2977,11 @@ function checkExposeContextPatch(context) {
2878
2977
  const checks = [];
2879
2978
  function inspectFile(relative, transform) {
2880
2979
  const filePath = path9.resolve(packageInfo.rootPath, relative);
2881
- if (!fs8.existsSync(filePath)) {
2980
+ if (!fs9.existsSync(filePath)) {
2882
2981
  checks.push({ relative, exists: false, patched: false });
2883
2982
  return;
2884
2983
  }
2885
- const content = fs8.readFileSync(filePath, "utf8");
2984
+ const content = fs9.readFileSync(filePath, "utf8");
2886
2985
  const { hasPatched } = transform(content);
2887
2986
  checks.push({
2888
2987
  relative,
@@ -2893,7 +2992,7 @@ function checkExposeContextPatch(context) {
2893
2992
  if (majorVersion === 3) {
2894
2993
  inspectFile("lib/processTailwindFeatures.js", transformProcessTailwindFeaturesReturnContext);
2895
2994
  const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
2896
- const pluginRelative = pluginCandidates.find((candidate) => fs8.existsSync(path9.resolve(packageInfo.rootPath, candidate)));
2995
+ const pluginRelative = pluginCandidates.find((candidate) => fs9.existsSync(path9.resolve(packageInfo.rootPath, candidate)));
2897
2996
  if (pluginRelative) {
2898
2997
  inspectFile(pluginRelative, (content) => transformPostcssPlugin(content, { refProperty }));
2899
2998
  } else {
@@ -2924,8 +3023,8 @@ function checkExtendLengthUnitsV3(rootDir, options) {
2924
3023
  const lengthUnitsFilePath = options.lengthUnitsFilePath ?? "lib/util/dataTypes.js";
2925
3024
  const variableName = options.variableName ?? "lengthUnits";
2926
3025
  const target = path9.resolve(rootDir, lengthUnitsFilePath);
2927
- const files = fs8.existsSync(target) ? [path9.relative(rootDir, target)] : [];
2928
- if (!fs8.existsSync(target)) {
3026
+ const files = fs9.existsSync(target) ? [path9.relative(rootDir, target)] : [];
3027
+ if (!fs9.existsSync(target)) {
2929
3028
  return {
2930
3029
  name: "extendLengthUnits",
2931
3030
  status: "not-applied",
@@ -2933,7 +3032,7 @@ function checkExtendLengthUnitsV3(rootDir, options) {
2933
3032
  files
2934
3033
  };
2935
3034
  }
2936
- const content = fs8.readFileSync(target, "utf8");
3035
+ const content = fs9.readFileSync(target, "utf8");
2937
3036
  const { found, missingUnits } = inspectLengthUnitsArray(content, variableName, options.units);
2938
3037
  if (!found) {
2939
3038
  return {
@@ -2959,7 +3058,7 @@ function checkExtendLengthUnitsV3(rootDir, options) {
2959
3058
  }
2960
3059
  function checkExtendLengthUnitsV4(rootDir, options) {
2961
3060
  const distDir = path9.resolve(rootDir, "dist");
2962
- if (!fs8.existsSync(distDir)) {
3061
+ if (!fs9.existsSync(distDir)) {
2963
3062
  return {
2964
3063
  name: "extendLengthUnits",
2965
3064
  status: "not-applied",
@@ -3036,19 +3135,19 @@ function getPatchStatusReport(context) {
3036
3135
 
3037
3136
  // src/api/tailwindcss-patcher.ts
3038
3137
  import process6 from "process";
3039
- import fs10 from "fs-extra";
3138
+ import fs11 from "fs-extra";
3040
3139
  import { getPackageInfoSync } from "local-pkg";
3041
3140
  import path11 from "pathe";
3042
3141
  import { coerce } from "semver";
3043
3142
 
3044
3143
  // src/patching/operations/export-context/index.ts
3045
- import fs9 from "fs-extra";
3144
+ import fs10 from "fs-extra";
3046
3145
  import path10 from "pathe";
3047
3146
  function writeFileIfRequired(filePath, code, overwrite, successMessage) {
3048
3147
  if (!overwrite) {
3049
3148
  return;
3050
3149
  }
3051
- fs9.writeFileSync(filePath, code, {
3150
+ fs10.writeFileSync(filePath, code, {
3052
3151
  encoding: "utf8"
3053
3152
  });
3054
3153
  logger_default.success(successMessage);
@@ -3062,8 +3161,8 @@ function applyExposeContextPatch(params) {
3062
3161
  if (majorVersion === 3) {
3063
3162
  const processFileRelative = "lib/processTailwindFeatures.js";
3064
3163
  const processFilePath = path10.resolve(rootDir, processFileRelative);
3065
- if (fs9.existsSync(processFilePath)) {
3066
- const content = fs9.readFileSync(processFilePath, "utf8");
3164
+ if (fs10.existsSync(processFilePath)) {
3165
+ const content = fs10.readFileSync(processFilePath, "utf8");
3067
3166
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContext(content);
3068
3167
  result.files[processFileRelative] = code;
3069
3168
  if (!hasPatched) {
@@ -3077,10 +3176,10 @@ function applyExposeContextPatch(params) {
3077
3176
  }
3078
3177
  }
3079
3178
  const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
3080
- const pluginRelative = pluginCandidates.find((candidate) => fs9.existsSync(path10.resolve(rootDir, candidate)));
3179
+ const pluginRelative = pluginCandidates.find((candidate) => fs10.existsSync(path10.resolve(rootDir, candidate)));
3081
3180
  if (pluginRelative) {
3082
3181
  const pluginPath = path10.resolve(rootDir, pluginRelative);
3083
- const content = fs9.readFileSync(pluginPath, "utf8");
3182
+ const content = fs10.readFileSync(pluginPath, "utf8");
3084
3183
  const { code, hasPatched } = transformPostcssPlugin(content, { refProperty });
3085
3184
  result.files[pluginRelative] = code;
3086
3185
  if (!hasPatched) {
@@ -3096,8 +3195,8 @@ function applyExposeContextPatch(params) {
3096
3195
  } else if (majorVersion === 2) {
3097
3196
  const processFileRelative = "lib/jit/processTailwindFeatures.js";
3098
3197
  const processFilePath = path10.resolve(rootDir, processFileRelative);
3099
- if (fs9.existsSync(processFilePath)) {
3100
- const content = fs9.readFileSync(processFilePath, "utf8");
3198
+ if (fs10.existsSync(processFilePath)) {
3199
+ const content = fs10.readFileSync(processFilePath, "utf8");
3101
3200
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContextV2(content);
3102
3201
  result.files[processFileRelative] = code;
3103
3202
  if (!hasPatched) {
@@ -3112,8 +3211,8 @@ function applyExposeContextPatch(params) {
3112
3211
  }
3113
3212
  const pluginRelative = "lib/jit/index.js";
3114
3213
  const pluginPath = path10.resolve(rootDir, pluginRelative);
3115
- if (fs9.existsSync(pluginPath)) {
3116
- const content = fs9.readFileSync(pluginPath, "utf8");
3214
+ if (fs10.existsSync(pluginPath)) {
3215
+ const content = fs10.readFileSync(pluginPath, "utf8");
3117
3216
  const { code, hasPatched } = transformPostcssPluginV2(content, { refProperty });
3118
3217
  result.files[pluginRelative] = code;
3119
3218
  if (!hasPatched) {
@@ -3205,6 +3304,8 @@ var TailwindcssPatcher = class {
3205
3304
  majorVersion;
3206
3305
  cacheContext;
3207
3306
  cacheStore;
3307
+ patchMemo;
3308
+ inFlightBuild;
3208
3309
  constructor(options = {}) {
3209
3310
  const resolvedOptions = options && typeof options === "object" && "patch" in options ? fromLegacyOptions(options) : options;
3210
3311
  this.options = normalizeOptions(resolvedOptions);
@@ -3228,11 +3329,20 @@ var TailwindcssPatcher = class {
3228
3329
  this.cacheStore = new CacheStore(this.options.cache, this.cacheContext);
3229
3330
  }
3230
3331
  async patch() {
3231
- 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({
3232
3337
  packageInfo: this.packageInfo,
3233
3338
  options: this.options,
3234
3339
  majorVersion: this.majorVersion
3235
3340
  });
3341
+ this.patchMemo = {
3342
+ result,
3343
+ snapshot: this.createPatchSnapshot()
3344
+ };
3345
+ return result;
3236
3346
  }
3237
3347
  async getPatchStatus() {
3238
3348
  return getPatchStatusReport({
@@ -3250,6 +3360,9 @@ var TailwindcssPatcher = class {
3250
3360
  }
3251
3361
  async runTailwindBuildIfNeeded() {
3252
3362
  if (this.majorVersion === 2 || this.majorVersion === 3) {
3363
+ if (this.inFlightBuild) {
3364
+ return this.inFlightBuild;
3365
+ }
3253
3366
  const executionOptions = resolveTailwindExecutionOptions(this.options, this.majorVersion);
3254
3367
  const buildOptions = {
3255
3368
  cwd: executionOptions.cwd,
@@ -3257,8 +3370,53 @@ var TailwindcssPatcher = class {
3257
3370
  ...executionOptions.config === void 0 ? {} : { config: executionOptions.config },
3258
3371
  ...executionOptions.postcssPlugin === void 0 ? {} : { postcssPlugin: executionOptions.postcssPlugin }
3259
3372
  };
3260
- 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
+ }
3261
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
+ }
3418
+ }
3419
+ return entries.join("|");
3262
3420
  }
3263
3421
  async collectClassSet() {
3264
3422
  if (this.majorVersion === 4) {
@@ -3288,13 +3446,13 @@ var TailwindcssPatcher = class {
3288
3446
  for (const value of existing) {
3289
3447
  set.add(value);
3290
3448
  }
3291
- const writeTarget = await this.cacheStore.write(set);
3449
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : await this.cacheStore.write(set);
3292
3450
  if (writeTarget) {
3293
3451
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3294
3452
  }
3295
3453
  } else {
3296
3454
  if (set.size > 0) {
3297
- const writeTarget = await this.cacheStore.write(set);
3455
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : await this.cacheStore.write(set);
3298
3456
  if (writeTarget) {
3299
3457
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3300
3458
  }
@@ -3314,13 +3472,13 @@ var TailwindcssPatcher = class {
3314
3472
  for (const value of existing) {
3315
3473
  set.add(value);
3316
3474
  }
3317
- const writeTarget = this.cacheStore.writeSync(set);
3475
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : this.cacheStore.writeSync(set);
3318
3476
  if (writeTarget) {
3319
3477
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3320
3478
  }
3321
3479
  } else {
3322
3480
  if (set.size > 0) {
3323
- const writeTarget = this.cacheStore.writeSync(set);
3481
+ const writeTarget = this.areSetsEqual(existing, set) ? void 0 : this.cacheStore.writeSync(set);
3324
3482
  if (writeTarget) {
3325
3483
  logger_default.debug(`[cache] stored ${set.size} classes -> ${writeTarget}`);
3326
3484
  }
@@ -3330,6 +3488,17 @@ var TailwindcssPatcher = class {
3330
3488
  }
3331
3489
  return set;
3332
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
+ }
3333
3502
  async getClassSet() {
3334
3503
  await this.runTailwindBuildIfNeeded();
3335
3504
  const set = await this.collectClassSet();
@@ -3359,12 +3528,12 @@ var TailwindcssPatcher = class {
3359
3528
  return result;
3360
3529
  }
3361
3530
  const target = path11.resolve(this.options.output.file);
3362
- await fs10.ensureDir(path11.dirname(target));
3531
+ await fs11.ensureDir(path11.dirname(target));
3363
3532
  if (this.options.output.format === "json") {
3364
3533
  const spaces = typeof this.options.output.pretty === "number" ? this.options.output.pretty : void 0;
3365
- await fs10.writeJSON(target, classList, { spaces });
3534
+ await fs11.writeJSON(target, classList, { spaces });
3366
3535
  } else {
3367
- await fs10.writeFile(target, `${classList.join("\n")}
3536
+ await fs11.writeFile(target, `${classList.join("\n")}
3368
3537
  `, "utf8");
3369
3538
  }
3370
3539
  logger_default.success(`Tailwind CSS class list saved to ${target.replace(process6.cwd(), ".")}`);
@@ -3483,7 +3652,7 @@ function buildMigrationReport(state, context) {
3483
3652
  }
3484
3653
 
3485
3654
  // src/commands/migration-file-executor.ts
3486
- import fs12 from "fs-extra";
3655
+ import fs13 from "fs-extra";
3487
3656
  import path13 from "pathe";
3488
3657
 
3489
3658
  // src/commands/migration-source.ts
@@ -3752,7 +3921,7 @@ function migrateConfigSource(source) {
3752
3921
  }
3753
3922
 
3754
3923
  // src/commands/migration-target-files.ts
3755
- import fs11 from "fs-extra";
3924
+ import fs12 from "fs-extra";
3756
3925
  import path12 from "pathe";
3757
3926
  var DEFAULT_CONFIG_FILENAMES = [
3758
3927
  "tailwindcss-patch.config.ts",
@@ -3796,7 +3965,7 @@ async function collectWorkspaceConfigFiles(cwd, maxDepth) {
3796
3965
  const { dir, depth } = current;
3797
3966
  let entries;
3798
3967
  try {
3799
- entries = await fs11.readdir(dir, { withFileTypes: true });
3968
+ entries = await fs12.readdir(dir, { withFileTypes: true });
3800
3969
  } catch {
3801
3970
  continue;
3802
3971
  }
@@ -3893,7 +4062,7 @@ async function rollbackWrittenEntries(wroteEntries) {
3893
4062
  let rollbackCount = 0;
3894
4063
  for (const written of [...wroteEntries].reverse()) {
3895
4064
  try {
3896
- await fs12.writeFile(written.file, written.source, "utf8");
4065
+ await fs13.writeFile(written.file, written.source, "utf8");
3897
4066
  written.entry.written = false;
3898
4067
  written.entry.rolledBack = true;
3899
4068
  rollbackCount += 1;
@@ -3911,7 +4080,7 @@ async function executeMigrationFile(options) {
3911
4080
  backupDirectory,
3912
4081
  wroteEntries
3913
4082
  } = options;
3914
- const exists = await fs12.pathExists(file);
4083
+ const exists = await fs13.pathExists(file);
3915
4084
  if (!exists) {
3916
4085
  return {
3917
4086
  missing: true,
@@ -3920,7 +4089,7 @@ async function executeMigrationFile(options) {
3920
4089
  backupWritten: false
3921
4090
  };
3922
4091
  }
3923
- const source = await fs12.readFile(file, "utf8");
4092
+ const source = await fs13.readFile(file, "utf8");
3924
4093
  const migrated = migrateConfigSource(source);
3925
4094
  const entry = {
3926
4095
  file,
@@ -3943,12 +4112,12 @@ async function executeMigrationFile(options) {
3943
4112
  if (backupDirectory) {
3944
4113
  const backupRelativePath = resolveBackupRelativePath(cwd, file);
3945
4114
  const backupFile = path13.resolve(backupDirectory, backupRelativePath);
3946
- await fs12.ensureDir(path13.dirname(backupFile));
3947
- await fs12.writeFile(backupFile, source, "utf8");
4115
+ await fs13.ensureDir(path13.dirname(backupFile));
4116
+ await fs13.writeFile(backupFile, source, "utf8");
3948
4117
  entry.backupFile = backupFile;
3949
4118
  backupWritten = true;
3950
4119
  }
3951
- await fs12.writeFile(file, migrated.code, "utf8");
4120
+ await fs13.writeFile(file, migrated.code, "utf8");
3952
4121
  entry.written = true;
3953
4122
  wroteEntries.push({ file, source, entry });
3954
4123
  return {
@@ -3981,15 +4150,15 @@ async function restoreConfigEntries(entries, dryRun) {
3981
4150
  continue;
3982
4151
  }
3983
4152
  restorableEntries += 1;
3984
- const backupExists = await fs12.pathExists(backupFile);
4153
+ const backupExists = await fs13.pathExists(backupFile);
3985
4154
  if (!backupExists) {
3986
4155
  missingBackups += 1;
3987
4156
  continue;
3988
4157
  }
3989
4158
  if (!dryRun) {
3990
- const backupContent = await fs12.readFile(backupFile, "utf8");
3991
- await fs12.ensureDir(path13.dirname(targetFile));
3992
- await fs12.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");
3993
4162
  }
3994
4163
  restoredFiles += 1;
3995
4164
  restored.push(targetFile);
@@ -4005,9 +4174,9 @@ async function restoreConfigEntries(entries, dryRun) {
4005
4174
  }
4006
4175
 
4007
4176
  // src/commands/migration-report-loader.ts
4008
- import fs13 from "fs-extra";
4177
+ import fs14 from "fs-extra";
4009
4178
  async function loadMigrationReportForRestore(reportFile) {
4010
- const report = await fs13.readJSON(reportFile);
4179
+ const report = await fs14.readJSON(reportFile);
4011
4180
  assertMigrationReportCompatibility(report, reportFile);
4012
4181
  return {
4013
4182
  ...report.reportKind === void 0 ? {} : { reportKind: report.reportKind },
@@ -4391,7 +4560,7 @@ function runWithCommandHandler(cli, command, commandName, args, handler, default
4391
4560
 
4392
4561
  // src/commands/basic-handlers.ts
4393
4562
  import process9 from "process";
4394
- import fs14 from "fs-extra";
4563
+ import fs15 from "fs-extra";
4395
4564
  import path16 from "pathe";
4396
4565
  var DEFAULT_CONFIG_NAME = "tailwindcss-mangle";
4397
4566
  async function installCommandDefaultHandler(_ctx) {
@@ -4447,14 +4616,14 @@ async function tokensCommandDefaultHandler(ctx) {
4447
4616
  const resolveGrouped = () => grouped ?? buildGrouped();
4448
4617
  if (shouldWrite) {
4449
4618
  const target = path16.resolve(targetFile);
4450
- await fs14.ensureDir(path16.dirname(target));
4619
+ await fs15.ensureDir(path16.dirname(target));
4451
4620
  if (format === "json") {
4452
- await fs14.writeJSON(target, report, { spaces: 2 });
4621
+ await fs15.writeJSON(target, report, { spaces: 2 });
4453
4622
  } else if (format === "grouped-json") {
4454
- await fs14.writeJSON(target, resolveGrouped(), { spaces: 2 });
4623
+ await fs15.writeJSON(target, resolveGrouped(), { spaces: 2 });
4455
4624
  } else {
4456
4625
  const lines = report.entries.map(formatTokenLine);
4457
- await fs14.writeFile(target, `${lines.join("\n")}
4626
+ await fs15.writeFile(target, `${lines.join("\n")}
4458
4627
  `, "utf8");
4459
4628
  }
4460
4629
  logger_default.success(`Collected ${report.entries.length} tokens (${format}) \u2192 ${target.replace(process9.cwd(), ".")}`);
@@ -4561,7 +4730,7 @@ function resolveValidateCommandArgs(args) {
4561
4730
 
4562
4731
  // src/commands/migration-output.ts
4563
4732
  import process10 from "process";
4564
- import fs15 from "fs-extra";
4733
+ import fs16 from "fs-extra";
4565
4734
  import path17 from "pathe";
4566
4735
  function formatPathForLog(file) {
4567
4736
  return file.replace(process10.cwd(), ".");
@@ -4571,8 +4740,8 @@ function createMigrationCheckFailureError(changedFiles) {
4571
4740
  }
4572
4741
  async function writeMigrationReportFile(cwd, reportFile, report) {
4573
4742
  const reportPath = path17.resolve(cwd, reportFile);
4574
- await fs15.ensureDir(path17.dirname(reportPath));
4575
- await fs15.writeJSON(reportPath, report, { spaces: 2 });
4743
+ await fs16.ensureDir(path17.dirname(reportPath));
4744
+ await fs16.writeJSON(reportPath, report, { spaces: 2 });
4576
4745
  logger_default.info(`Migration report written: ${formatPathForLog(reportPath)}`);
4577
4746
  }
4578
4747
  function logMigrationReportAsJson(report) {