tailwindcss-patch 8.4.2 → 8.5.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.
@@ -520,372 +520,69 @@ function normalizeOptions(options = {}) {
520
520
  };
521
521
  }
522
522
 
523
- // src/runtime/class-collector.ts
524
- import process4 from "process";
525
- import fs3 from "fs-extra";
526
- import path3 from "pathe";
523
+ // src/patching/status.ts
524
+ import * as t4 from "@babel/types";
525
+ import fs4 from "fs-extra";
526
+ import path4 from "pathe";
527
527
 
528
- // src/utils.ts
529
- function isObject(val) {
530
- return val !== null && typeof val === "object" && Array.isArray(val) === false;
528
+ // src/babel/index.ts
529
+ import _babelGenerate from "@babel/generator";
530
+ import _babelTraverse from "@babel/traverse";
531
+ import { parse, parseExpression } from "@babel/parser";
532
+ function _interopDefaultCompat(e) {
533
+ return e && typeof e === "object" && "default" in e ? e.default : e;
531
534
  }
532
- function spliceChangesIntoString(str, changes) {
533
- if (!changes[0]) {
534
- return str;
535
+ var generate = _interopDefaultCompat(_babelGenerate);
536
+ var traverse = _interopDefaultCompat(_babelTraverse);
537
+
538
+ // src/patching/operations/export-context/postcss-v2.ts
539
+ import * as t from "@babel/types";
540
+ var IDENTIFIER_RE = /^[A-Z_$][\w$]*$/i;
541
+ function toIdentifierName(property) {
542
+ if (!property) {
543
+ return "contextRef";
535
544
  }
536
- changes.sort((a, b) => {
537
- return a.end - b.end || a.start - b.start;
538
- });
539
- let result = "";
540
- let previous = changes[0];
541
- result += str.slice(0, previous.start);
542
- result += previous.replacement;
543
- for (let i = 1; i < changes.length; ++i) {
544
- const change = changes[i];
545
- result += str.slice(previous.end, change.start);
546
- result += change.replacement;
547
- previous = change;
545
+ const sanitized = property.replace(/[^\w$]/gu, "_");
546
+ if (/^\d/.test(sanitized)) {
547
+ return `_${sanitized}`;
548
548
  }
549
- result += str.slice(previous.end);
550
- return result;
549
+ return sanitized || "contextRef";
551
550
  }
552
-
553
- // src/runtime/class-collector.ts
554
- function collectClassesFromContexts(contexts, filter) {
555
- const set = /* @__PURE__ */ new Set();
556
- for (const context of contexts) {
557
- if (!isObject(context) || !context.classCache) {
558
- continue;
559
- }
560
- for (const key of context.classCache.keys()) {
561
- const className = key.toString();
562
- if (filter(className)) {
563
- set.add(className);
564
- }
565
- }
551
+ function createExportsMember(property) {
552
+ if (IDENTIFIER_RE.test(property)) {
553
+ return t.memberExpression(t.identifier("exports"), t.identifier(property));
566
554
  }
567
- return set;
555
+ return t.memberExpression(t.identifier("exports"), t.stringLiteral(property), true);
568
556
  }
569
- async function collectClassesFromTailwindV4(options) {
570
- const set = /* @__PURE__ */ new Set();
571
- const v4Options = options.tailwind.v4;
572
- if (!v4Options) {
573
- return set;
574
- }
575
- const toAbsolute = (value) => {
576
- if (!value) {
577
- return void 0;
578
- }
579
- return path3.isAbsolute(value) ? value : path3.resolve(options.projectRoot, value);
580
- };
581
- const resolvedConfiguredBase = toAbsolute(v4Options.configuredBase);
582
- const resolvedDefaultBase = toAbsolute(v4Options.base) ?? process4.cwd();
583
- const resolveSources = (base) => {
584
- if (!v4Options.sources?.length) {
585
- return void 0;
586
- }
587
- return v4Options.sources.map((source) => ({
588
- base: source.base ?? base,
589
- pattern: source.pattern,
590
- negated: source.negated
591
- }));
592
- };
593
- if (v4Options.cssEntries.length > 0) {
594
- for (const entry of v4Options.cssEntries) {
595
- const filePath = path3.isAbsolute(entry) ? entry : path3.resolve(options.projectRoot, entry);
596
- if (!await fs3.pathExists(filePath)) {
597
- continue;
557
+ function transformProcessTailwindFeaturesReturnContextV2(content) {
558
+ const ast = parse(content, {
559
+ sourceType: "unambiguous"
560
+ });
561
+ let hasPatched = false;
562
+ traverse(ast, {
563
+ FunctionDeclaration(path11) {
564
+ const node = path11.node;
565
+ if (node.id?.name !== "processTailwindFeatures" || node.body.body.length !== 1 || !t.isReturnStatement(node.body.body[0])) {
566
+ return;
598
567
  }
599
- const css = await fs3.readFile(filePath, "utf8");
600
- const entryDir = path3.dirname(filePath);
601
- const baseForEntry = resolvedConfiguredBase ?? entryDir;
602
- const sources = resolveSources(baseForEntry);
603
- const candidates = await extractValidCandidates({
604
- cwd: options.projectRoot,
605
- base: baseForEntry,
606
- css,
607
- sources
608
- });
609
- for (const candidate of candidates) {
610
- if (options.filter(candidate)) {
611
- set.add(candidate);
612
- }
568
+ const returnStatement3 = node.body.body[0];
569
+ if (!t.isFunctionExpression(returnStatement3.argument)) {
570
+ return;
613
571
  }
614
- }
615
- } else {
616
- const baseForCss = resolvedConfiguredBase ?? resolvedDefaultBase;
617
- const sources = resolveSources(baseForCss);
618
- const candidates = await extractValidCandidates({
619
- cwd: options.projectRoot,
620
- base: baseForCss,
621
- css: v4Options.css,
622
- sources
623
- });
624
- for (const candidate of candidates) {
625
- if (options.filter(candidate)) {
626
- set.add(candidate);
572
+ const body = returnStatement3.argument.body.body;
573
+ const lastStatement = body[body.length - 1];
574
+ const alreadyReturnsContext = Boolean(
575
+ t.isReturnStatement(lastStatement) && t.isIdentifier(lastStatement.argument) && lastStatement.argument.name === "context"
576
+ );
577
+ hasPatched = alreadyReturnsContext;
578
+ if (!alreadyReturnsContext) {
579
+ body.push(t.returnStatement(t.identifier("context")));
627
580
  }
628
581
  }
629
- }
630
- return set;
631
- }
632
-
633
- // src/runtime/context-registry.ts
634
- import { createRequire } from "module";
635
- import fs4 from "fs-extra";
636
- import path4 from "pathe";
637
- var require2 = createRequire(import.meta.url);
638
- function resolveRuntimeEntry(packageInfo, majorVersion) {
639
- const root = packageInfo.rootPath;
640
- if (majorVersion === 2) {
641
- const jitIndex = path4.join(root, "lib/jit/index.js");
642
- if (fs4.existsSync(jitIndex)) {
643
- return jitIndex;
644
- }
645
- } else if (majorVersion === 3) {
646
- const plugin = path4.join(root, "lib/plugin.js");
647
- const index = path4.join(root, "lib/index.js");
648
- if (fs4.existsSync(plugin)) {
649
- return plugin;
650
- }
651
- if (fs4.existsSync(index)) {
652
- return index;
653
- }
654
- }
655
- return void 0;
656
- }
657
- function loadRuntimeContexts(packageInfo, majorVersion, refProperty) {
658
- if (majorVersion === 4) {
659
- return [];
660
- }
661
- const entry = resolveRuntimeEntry(packageInfo, majorVersion);
662
- if (!entry) {
663
- return [];
664
- }
665
- const moduleExports = require2(entry);
666
- if (!moduleExports) {
667
- return [];
668
- }
669
- const ref = moduleExports[refProperty];
670
- if (!ref) {
671
- return [];
672
- }
673
- if (Array.isArray(ref)) {
674
- return ref;
675
- }
676
- if (typeof ref === "object" && Array.isArray(ref.value)) {
677
- return ref.value;
678
- }
679
- return [];
680
- }
681
-
682
- // src/runtime/process-tailwindcss.ts
683
- import { createRequire as createRequire2 } from "module";
684
- import path5 from "pathe";
685
- import postcss from "postcss";
686
- import { loadConfig } from "tailwindcss-config";
687
- var require3 = createRequire2(import.meta.url);
688
- async function resolveConfigPath(options) {
689
- if (options.config && path5.isAbsolute(options.config)) {
690
- return options.config;
691
- }
692
- const result = await loadConfig({ cwd: options.cwd });
693
- if (!result) {
694
- throw new Error(`Unable to locate Tailwind CSS config from ${options.cwd}`);
695
- }
696
- return result.filepath;
697
- }
698
- async function runTailwindBuild(options) {
699
- const configPath = await resolveConfigPath(options);
700
- const pluginName = options.postcssPlugin ?? (options.majorVersion === 4 ? "@tailwindcss/postcss" : "tailwindcss");
701
- if (options.majorVersion === 4) {
702
- return postcss([
703
- require3(pluginName)({
704
- config: configPath
705
- })
706
- ]).process("@import 'tailwindcss';", {
707
- from: void 0
708
- });
709
- }
710
- return postcss([
711
- require3(pluginName)({
712
- config: configPath
713
- })
714
- ]).process("@tailwind base;@tailwind components;@tailwind utilities;", {
715
- from: void 0
716
582
  });
717
- }
718
-
719
- // src/api/tailwindcss-patcher.ts
720
- import process5 from "process";
721
- import fs7 from "fs-extra";
722
- import { getPackageInfoSync } from "local-pkg";
723
- import path8 from "pathe";
724
- import { coerce } from "semver";
725
-
726
- // src/options/legacy.ts
727
- function normalizeLegacyFeatures(patch) {
728
- const apply = patch?.applyPatches;
729
- const extend = apply?.extendLengthUnits;
730
- let extendOption = false;
731
- if (extend && typeof extend === "object") {
732
- extendOption = {
733
- ...extend,
734
- enabled: true
735
- };
736
- } else if (extend === true) {
737
- extendOption = {
738
- enabled: true,
739
- units: ["rpx"],
740
- overwrite: patch?.overwrite
741
- };
742
- }
743
583
  return {
744
- exposeContext: apply?.exportContext ?? true,
745
- extendLengthUnits: extendOption
746
- };
747
- }
748
- function fromLegacyOptions(options) {
749
- if (!options) {
750
- return {};
751
- }
752
- const patch = options.patch;
753
- const features = normalizeLegacyFeatures(patch);
754
- const output = patch?.output;
755
- const tailwindConfig = patch?.tailwindcss;
756
- const tailwindVersion = tailwindConfig?.version;
757
- const tailwindV2 = tailwindConfig?.v2;
758
- const tailwindV3 = tailwindConfig?.v3;
759
- const tailwindV4 = tailwindConfig?.v4;
760
- const tailwindConfigPath = tailwindV3?.config ?? tailwindV2?.config;
761
- const tailwindCwd = tailwindV3?.cwd ?? tailwindV2?.cwd ?? patch?.cwd;
762
- return {
763
- cwd: patch?.cwd,
764
- overwrite: patch?.overwrite,
765
- filter: patch?.filter,
766
- cache: typeof options.cache === "boolean" ? options.cache : options.cache ? {
767
- ...options.cache,
768
- enabled: options.cache.enabled ?? true
769
- } : void 0,
770
- output: output ? {
771
- file: output.filename,
772
- pretty: output.loose ? 2 : false,
773
- removeUniversalSelector: output.removeUniversalSelector
774
- } : void 0,
775
- tailwind: {
776
- packageName: patch?.packageName,
777
- version: tailwindVersion,
778
- resolve: patch?.resolve,
779
- config: tailwindConfigPath,
780
- cwd: tailwindCwd,
781
- v2: tailwindV2,
782
- v3: tailwindV3,
783
- v4: tailwindV4
784
- },
785
- features: {
786
- exposeContext: features.exposeContext,
787
- extendLengthUnits: features.extendLengthUnits
788
- }
789
- };
790
- }
791
- function fromUnifiedConfig(registry) {
792
- if (!registry) {
793
- return {};
794
- }
795
- const tailwind = registry.tailwind;
796
- const output = registry.output;
797
- const pretty = (() => {
798
- if (output?.pretty === void 0) {
799
- return void 0;
800
- }
801
- if (typeof output.pretty === "boolean") {
802
- return output.pretty ? 2 : false;
803
- }
804
- return output.pretty;
805
- })();
806
- return {
807
- output: output ? {
808
- file: output.file,
809
- pretty,
810
- removeUniversalSelector: output.stripUniversalSelector
811
- } : void 0,
812
- tailwind: tailwind ? {
813
- version: tailwind.version,
814
- packageName: tailwind.package,
815
- resolve: tailwind.resolve,
816
- config: tailwind.config,
817
- cwd: tailwind.cwd,
818
- v2: tailwind.legacy,
819
- v3: tailwind.classic,
820
- v4: tailwind.next
821
- } : void 0
822
- };
823
- }
824
-
825
- // src/patching/operations/export-context/index.ts
826
- import fs5 from "fs-extra";
827
- import path6 from "pathe";
828
-
829
- // src/patching/operations/export-context/postcss-v2.ts
830
- import * as t from "@babel/types";
831
-
832
- // src/babel/index.ts
833
- import _babelGenerate from "@babel/generator";
834
- import _babelTraverse from "@babel/traverse";
835
- import { parse, parseExpression } from "@babel/parser";
836
- function _interopDefaultCompat(e) {
837
- return e && typeof e === "object" && "default" in e ? e.default : e;
838
- }
839
- var generate = _interopDefaultCompat(_babelGenerate);
840
- var traverse = _interopDefaultCompat(_babelTraverse);
841
-
842
- // src/patching/operations/export-context/postcss-v2.ts
843
- var IDENTIFIER_RE = /^[A-Z_$][\w$]*$/i;
844
- function toIdentifierName(property) {
845
- if (!property) {
846
- return "contextRef";
847
- }
848
- const sanitized = property.replace(/[^\w$]/gu, "_");
849
- if (/^\d/.test(sanitized)) {
850
- return `_${sanitized}`;
851
- }
852
- return sanitized || "contextRef";
853
- }
854
- function createExportsMember(property) {
855
- if (IDENTIFIER_RE.test(property)) {
856
- return t.memberExpression(t.identifier("exports"), t.identifier(property));
857
- }
858
- return t.memberExpression(t.identifier("exports"), t.stringLiteral(property), true);
859
- }
860
- function transformProcessTailwindFeaturesReturnContextV2(content) {
861
- const ast = parse(content, {
862
- sourceType: "unambiguous"
863
- });
864
- let hasPatched = false;
865
- traverse(ast, {
866
- FunctionDeclaration(path10) {
867
- const node = path10.node;
868
- if (node.id?.name !== "processTailwindFeatures" || node.body.body.length !== 1 || !t.isReturnStatement(node.body.body[0])) {
869
- return;
870
- }
871
- const returnStatement3 = node.body.body[0];
872
- if (!t.isFunctionExpression(returnStatement3.argument)) {
873
- return;
874
- }
875
- const body = returnStatement3.argument.body.body;
876
- const lastStatement = body[body.length - 1];
877
- const alreadyReturnsContext = Boolean(
878
- t.isReturnStatement(lastStatement) && t.isIdentifier(lastStatement.argument) && lastStatement.argument.name === "context"
879
- );
880
- hasPatched = alreadyReturnsContext;
881
- if (!alreadyReturnsContext) {
882
- body.push(t.returnStatement(t.identifier("context")));
883
- }
884
- }
885
- });
886
- return {
887
- code: hasPatched ? content : generate(ast).code,
888
- hasPatched
584
+ code: hasPatched ? content : generate(ast).code,
585
+ hasPatched
889
586
  };
890
587
  }
891
588
  function transformPostcssPluginV2(content, options) {
@@ -895,8 +592,8 @@ function transformPostcssPluginV2(content, options) {
895
592
  const ast = parse(content);
896
593
  let hasPatched = false;
897
594
  traverse(ast, {
898
- Program(path10) {
899
- const program = path10.node;
595
+ Program(path11) {
596
+ const program = path11.node;
900
597
  const index = program.body.findIndex((statement) => {
901
598
  return t.isFunctionDeclaration(statement) && statement.id?.name === "_default";
902
599
  });
@@ -930,11 +627,11 @@ function transformPostcssPluginV2(content, options) {
930
627
  );
931
628
  }
932
629
  },
933
- FunctionDeclaration(path10) {
630
+ FunctionDeclaration(path11) {
934
631
  if (hasPatched) {
935
632
  return;
936
633
  }
937
- const fn = path10.node;
634
+ const fn = path11.node;
938
635
  if (fn.id?.name !== "_default") {
939
636
  return;
940
637
  }
@@ -1023,8 +720,8 @@ function transformProcessTailwindFeaturesReturnContext(content) {
1023
720
  const ast = parse(content);
1024
721
  let hasPatched = false;
1025
722
  traverse(ast, {
1026
- FunctionDeclaration(path10) {
1027
- const node = path10.node;
723
+ FunctionDeclaration(path11) {
724
+ const node = path11.node;
1028
725
  if (node.id?.name !== "processTailwindFeatures" || node.body.body.length !== 1) {
1029
726
  return;
1030
727
  }
@@ -1056,8 +753,8 @@ function transformPostcssPlugin(content, { refProperty }) {
1056
753
  const valueMember = t2.memberExpression(refIdentifier, t2.identifier("value"));
1057
754
  let hasPatched = false;
1058
755
  traverse(ast, {
1059
- Program(path10) {
1060
- const program = path10.node;
756
+ Program(path11) {
757
+ const program = path11.node;
1061
758
  const index = program.body.findIndex((statement) => {
1062
759
  return t2.isExpressionStatement(statement) && t2.isAssignmentExpression(statement.expression) && t2.isMemberExpression(statement.expression.left) && t2.isFunctionExpression(statement.expression.right) && statement.expression.right.id?.name === "tailwindcss";
1063
760
  });
@@ -1095,11 +792,11 @@ function transformPostcssPlugin(content, { refProperty }) {
1095
792
  );
1096
793
  }
1097
794
  },
1098
- FunctionExpression(path10) {
795
+ FunctionExpression(path11) {
1099
796
  if (hasPatched) {
1100
797
  return;
1101
798
  }
1102
- const fn = path10.node;
799
+ const fn = path11.node;
1103
800
  if (fn.id?.name !== "tailwindcss" || fn.body.body.length !== 1) {
1104
801
  return;
1105
802
  }
@@ -1161,19 +858,664 @@ function transformPostcssPlugin(content, { refProperty }) {
1161
858
  )
1162
859
  );
1163
860
  }
1164
- });
861
+ });
862
+ return {
863
+ code: hasPatched ? content : generate(ast).code,
864
+ hasPatched
865
+ };
866
+ }
867
+
868
+ // src/patching/operations/extend-length-units.ts
869
+ import * as t3 from "@babel/types";
870
+ import fs3 from "fs-extra";
871
+ import path3 from "pathe";
872
+
873
+ // src/utils.ts
874
+ function isObject(val) {
875
+ return val !== null && typeof val === "object" && Array.isArray(val) === false;
876
+ }
877
+ function spliceChangesIntoString(str, changes) {
878
+ if (!changes[0]) {
879
+ return str;
880
+ }
881
+ changes.sort((a, b) => {
882
+ return a.end - b.end || a.start - b.start;
883
+ });
884
+ let result = "";
885
+ let previous = changes[0];
886
+ result += str.slice(0, previous.start);
887
+ result += previous.replacement;
888
+ for (let i = 1; i < changes.length; ++i) {
889
+ const change = changes[i];
890
+ result += str.slice(previous.end, change.start);
891
+ result += change.replacement;
892
+ previous = change;
893
+ }
894
+ result += str.slice(previous.end);
895
+ return result;
896
+ }
897
+
898
+ // src/patching/operations/extend-length-units.ts
899
+ function updateLengthUnitsArray(content, options) {
900
+ const { variableName = "lengthUnits", units } = options;
901
+ const ast = parse(content);
902
+ let arrayRef;
903
+ let changed = false;
904
+ traverse(ast, {
905
+ Identifier(path11) {
906
+ if (path11.node.name === variableName && t3.isVariableDeclarator(path11.parent) && t3.isArrayExpression(path11.parent.init)) {
907
+ arrayRef = path11.parent.init;
908
+ const existing = new Set(
909
+ path11.parent.init.elements.map((element) => t3.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
910
+ );
911
+ for (const unit of units) {
912
+ if (!existing.has(unit)) {
913
+ path11.parent.init.elements = path11.parent.init.elements.map((element) => {
914
+ if (t3.isStringLiteral(element)) {
915
+ return t3.stringLiteral(element.value);
916
+ }
917
+ return element;
918
+ });
919
+ path11.parent.init.elements.push(t3.stringLiteral(unit));
920
+ changed = true;
921
+ }
922
+ }
923
+ }
924
+ }
925
+ });
926
+ return {
927
+ arrayRef,
928
+ changed
929
+ };
930
+ }
931
+ function applyExtendLengthUnitsPatchV3(rootDir, options) {
932
+ if (!options.enabled) {
933
+ return { changed: false, code: void 0 };
934
+ }
935
+ const opts = {
936
+ ...options,
937
+ lengthUnitsFilePath: options.lengthUnitsFilePath ?? "lib/util/dataTypes.js",
938
+ variableName: options.variableName ?? "lengthUnits"
939
+ };
940
+ const dataTypesFilePath = path3.resolve(rootDir, opts.lengthUnitsFilePath);
941
+ const exists = fs3.existsSync(dataTypesFilePath);
942
+ if (!exists) {
943
+ return { changed: false, code: void 0 };
944
+ }
945
+ const content = fs3.readFileSync(dataTypesFilePath, "utf8");
946
+ const { arrayRef, changed } = updateLengthUnitsArray(content, opts);
947
+ if (!arrayRef || !changed) {
948
+ return { changed: false, code: void 0 };
949
+ }
950
+ const { code } = generate(arrayRef, {
951
+ jsescOption: { quotes: "single" }
952
+ });
953
+ if (arrayRef.start != null && arrayRef.end != null) {
954
+ const nextCode = `${content.slice(0, arrayRef.start)}${code}${content.slice(arrayRef.end)}`;
955
+ if (opts.overwrite) {
956
+ const target = opts.destPath ? path3.resolve(opts.destPath) : dataTypesFilePath;
957
+ fs3.writeFileSync(target, nextCode, "utf8");
958
+ logger_default.success("Patched Tailwind CSS length unit list (v3).");
959
+ }
960
+ return {
961
+ changed: true,
962
+ code: nextCode
963
+ };
964
+ }
965
+ return {
966
+ changed: false,
967
+ code: void 0
968
+ };
969
+ }
970
+ function applyExtendLengthUnitsPatchV4(rootDir, options) {
971
+ if (!options.enabled) {
972
+ return { files: [], changed: false };
973
+ }
974
+ const opts = { ...options };
975
+ const distDir = path3.resolve(rootDir, "dist");
976
+ if (!fs3.existsSync(distDir)) {
977
+ return { files: [], changed: false };
978
+ }
979
+ const entries = fs3.readdirSync(distDir);
980
+ const chunkNames = entries.filter((entry) => entry.endsWith(".js") || entry.endsWith(".mjs"));
981
+ const pattern = /\[\s*["']cm["'],\s*["']mm["'],[\w,"']+\]/;
982
+ const candidates = chunkNames.map((chunkName) => {
983
+ const file = path3.join(distDir, chunkName);
984
+ const code = fs3.readFileSync(file, "utf8");
985
+ const match = pattern.exec(code);
986
+ if (!match) {
987
+ return null;
988
+ }
989
+ return {
990
+ file,
991
+ code,
992
+ match,
993
+ hasPatched: false
994
+ };
995
+ }).filter((candidate) => candidate !== null);
996
+ for (const item of candidates) {
997
+ const { code, file, match } = item;
998
+ const ast = parse(match[0], { sourceType: "unambiguous" });
999
+ traverse(ast, {
1000
+ ArrayExpression(path11) {
1001
+ for (const unit of opts.units) {
1002
+ if (path11.node.elements.some((element) => t3.isStringLiteral(element) && element.value === unit)) {
1003
+ item.hasPatched = true;
1004
+ return;
1005
+ }
1006
+ path11.node.elements.push(t3.stringLiteral(unit));
1007
+ }
1008
+ }
1009
+ });
1010
+ if (item.hasPatched) {
1011
+ continue;
1012
+ }
1013
+ const { code: replacement } = generate(ast, { minified: true });
1014
+ const start = match.index ?? 0;
1015
+ const end = start + match[0].length;
1016
+ item.code = spliceChangesIntoString(code, [
1017
+ {
1018
+ start,
1019
+ end,
1020
+ replacement: replacement.endsWith(";") ? replacement.slice(0, -1) : replacement
1021
+ }
1022
+ ]);
1023
+ if (opts.overwrite) {
1024
+ fs3.writeFileSync(file, item.code, "utf8");
1025
+ }
1026
+ }
1027
+ if (candidates.some((file) => !file.hasPatched)) {
1028
+ logger_default.success("Patched Tailwind CSS length unit list (v4).");
1029
+ }
1030
+ return {
1031
+ changed: candidates.some((file) => !file.hasPatched),
1032
+ files: candidates
1033
+ };
1034
+ }
1035
+
1036
+ // src/patching/status.ts
1037
+ function inspectLengthUnitsArray(content, variableName, units) {
1038
+ const ast = parse(content);
1039
+ let found = false;
1040
+ let missingUnits = [];
1041
+ traverse(ast, {
1042
+ Identifier(path11) {
1043
+ if (path11.node.name === variableName && t4.isVariableDeclarator(path11.parent) && t4.isArrayExpression(path11.parent.init)) {
1044
+ found = true;
1045
+ const existing = new Set(
1046
+ path11.parent.init.elements.map((element) => t4.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
1047
+ );
1048
+ missingUnits = units.filter((unit) => !existing.has(unit));
1049
+ path11.stop();
1050
+ }
1051
+ }
1052
+ });
1053
+ return {
1054
+ found,
1055
+ missingUnits
1056
+ };
1057
+ }
1058
+ function checkExposeContextPatch(context) {
1059
+ const { packageInfo, options, majorVersion } = context;
1060
+ const refProperty = options.features.exposeContext.refProperty;
1061
+ if (!options.features.exposeContext.enabled) {
1062
+ return {
1063
+ name: "exposeContext",
1064
+ status: "skipped",
1065
+ reason: "exposeContext feature disabled",
1066
+ files: []
1067
+ };
1068
+ }
1069
+ if (majorVersion === 4) {
1070
+ return {
1071
+ name: "exposeContext",
1072
+ status: "unsupported",
1073
+ reason: "Context export patch is only required for Tailwind v2/v3",
1074
+ files: []
1075
+ };
1076
+ }
1077
+ const checks = [];
1078
+ function inspectFile(relative, transform) {
1079
+ const filePath = path4.resolve(packageInfo.rootPath, relative);
1080
+ if (!fs4.existsSync(filePath)) {
1081
+ checks.push({ relative, exists: false, patched: false });
1082
+ return;
1083
+ }
1084
+ const content = fs4.readFileSync(filePath, "utf8");
1085
+ const { hasPatched } = transform(content);
1086
+ checks.push({
1087
+ relative,
1088
+ exists: true,
1089
+ patched: hasPatched
1090
+ });
1091
+ }
1092
+ if (majorVersion === 3) {
1093
+ inspectFile("lib/processTailwindFeatures.js", transformProcessTailwindFeaturesReturnContext);
1094
+ const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
1095
+ const pluginRelative = pluginCandidates.find((candidate) => fs4.existsSync(path4.resolve(packageInfo.rootPath, candidate)));
1096
+ if (pluginRelative) {
1097
+ inspectFile(pluginRelative, (content) => transformPostcssPlugin(content, { refProperty }));
1098
+ } else {
1099
+ checks.push({ relative: "lib/plugin.js", exists: false, patched: false });
1100
+ }
1101
+ } else {
1102
+ inspectFile("lib/jit/processTailwindFeatures.js", transformProcessTailwindFeaturesReturnContextV2);
1103
+ inspectFile("lib/jit/index.js", (content) => transformPostcssPluginV2(content, { refProperty }));
1104
+ }
1105
+ const files = checks.filter((check) => check.exists).map((check) => check.relative);
1106
+ const missingFiles = checks.filter((check) => !check.exists);
1107
+ const unpatchedFiles = checks.filter((check) => check.exists && !check.patched);
1108
+ const reasons = [];
1109
+ if (missingFiles.length) {
1110
+ reasons.push(`missing files: ${missingFiles.map((item) => item.relative).join(", ")}`);
1111
+ }
1112
+ if (unpatchedFiles.length) {
1113
+ reasons.push(`unpatched files: ${unpatchedFiles.map((item) => item.relative).join(", ")}`);
1114
+ }
1115
+ return {
1116
+ name: "exposeContext",
1117
+ status: reasons.length ? "not-applied" : "applied",
1118
+ reason: reasons.length ? reasons.join("; ") : void 0,
1119
+ files
1120
+ };
1121
+ }
1122
+ function checkExtendLengthUnitsV3(rootDir, options) {
1123
+ const lengthUnitsFilePath = options.lengthUnitsFilePath ?? "lib/util/dataTypes.js";
1124
+ const variableName = options.variableName ?? "lengthUnits";
1125
+ const target = path4.resolve(rootDir, lengthUnitsFilePath);
1126
+ const files = fs4.existsSync(target) ? [path4.relative(rootDir, target)] : [];
1127
+ if (!fs4.existsSync(target)) {
1128
+ return {
1129
+ name: "extendLengthUnits",
1130
+ status: "not-applied",
1131
+ reason: `missing ${lengthUnitsFilePath}`,
1132
+ files
1133
+ };
1134
+ }
1135
+ const content = fs4.readFileSync(target, "utf8");
1136
+ const { found, missingUnits } = inspectLengthUnitsArray(content, variableName, options.units);
1137
+ if (!found) {
1138
+ return {
1139
+ name: "extendLengthUnits",
1140
+ status: "not-applied",
1141
+ reason: `could not locate ${variableName} array in ${lengthUnitsFilePath}`,
1142
+ files
1143
+ };
1144
+ }
1145
+ if (missingUnits.length) {
1146
+ return {
1147
+ name: "extendLengthUnits",
1148
+ status: "not-applied",
1149
+ reason: `missing units: ${missingUnits.join(", ")}`,
1150
+ files
1151
+ };
1152
+ }
1153
+ return {
1154
+ name: "extendLengthUnits",
1155
+ status: "applied",
1156
+ files
1157
+ };
1158
+ }
1159
+ function checkExtendLengthUnitsV4(rootDir, options) {
1160
+ const distDir = path4.resolve(rootDir, "dist");
1161
+ if (!fs4.existsSync(distDir)) {
1162
+ return {
1163
+ name: "extendLengthUnits",
1164
+ status: "not-applied",
1165
+ reason: "dist directory not found for Tailwind v4 package",
1166
+ files: []
1167
+ };
1168
+ }
1169
+ const result = applyExtendLengthUnitsPatchV4(rootDir, {
1170
+ ...options,
1171
+ enabled: true,
1172
+ overwrite: false
1173
+ });
1174
+ if (result.files.length === 0) {
1175
+ return {
1176
+ name: "extendLengthUnits",
1177
+ status: "not-applied",
1178
+ reason: "no bundle chunks matched the length unit pattern",
1179
+ files: []
1180
+ };
1181
+ }
1182
+ const files = result.files.map((file) => path4.relative(rootDir, file.file));
1183
+ const pending = result.files.filter((file) => !file.hasPatched);
1184
+ if (pending.length) {
1185
+ return {
1186
+ name: "extendLengthUnits",
1187
+ status: "not-applied",
1188
+ reason: `missing units in ${pending.length} bundle${pending.length > 1 ? "s" : ""}`,
1189
+ files: pending.map((file) => path4.relative(rootDir, file.file))
1190
+ };
1191
+ }
1192
+ return {
1193
+ name: "extendLengthUnits",
1194
+ status: "applied",
1195
+ files
1196
+ };
1197
+ }
1198
+ function checkExtendLengthUnitsPatch(context) {
1199
+ const { packageInfo, options, majorVersion } = context;
1200
+ if (!options.features.extendLengthUnits) {
1201
+ return {
1202
+ name: "extendLengthUnits",
1203
+ status: "skipped",
1204
+ reason: "extendLengthUnits feature disabled",
1205
+ files: []
1206
+ };
1207
+ }
1208
+ if (majorVersion === 2) {
1209
+ return {
1210
+ name: "extendLengthUnits",
1211
+ status: "unsupported",
1212
+ reason: "length unit extension is only applied for Tailwind v3/v4",
1213
+ files: []
1214
+ };
1215
+ }
1216
+ if (majorVersion === 3) {
1217
+ return checkExtendLengthUnitsV3(packageInfo.rootPath, options.features.extendLengthUnits);
1218
+ }
1219
+ return checkExtendLengthUnitsV4(packageInfo.rootPath, options.features.extendLengthUnits);
1220
+ }
1221
+ function getPatchStatusReport(context) {
1222
+ return {
1223
+ package: {
1224
+ name: context.packageInfo.name ?? context.packageInfo.packageJson?.name,
1225
+ version: context.packageInfo.version,
1226
+ root: context.packageInfo.rootPath
1227
+ },
1228
+ majorVersion: context.majorVersion,
1229
+ entries: [
1230
+ checkExposeContextPatch(context),
1231
+ checkExtendLengthUnitsPatch(context)
1232
+ ]
1233
+ };
1234
+ }
1235
+
1236
+ // src/runtime/class-collector.ts
1237
+ import process4 from "process";
1238
+ import fs5 from "fs-extra";
1239
+ import path5 from "pathe";
1240
+ function collectClassesFromContexts(contexts, filter) {
1241
+ const set = /* @__PURE__ */ new Set();
1242
+ for (const context of contexts) {
1243
+ if (!isObject(context) || !context.classCache) {
1244
+ continue;
1245
+ }
1246
+ for (const key of context.classCache.keys()) {
1247
+ const className = key.toString();
1248
+ if (filter(className)) {
1249
+ set.add(className);
1250
+ }
1251
+ }
1252
+ }
1253
+ return set;
1254
+ }
1255
+ async function collectClassesFromTailwindV4(options) {
1256
+ const set = /* @__PURE__ */ new Set();
1257
+ const v4Options = options.tailwind.v4;
1258
+ if (!v4Options) {
1259
+ return set;
1260
+ }
1261
+ const toAbsolute = (value) => {
1262
+ if (!value) {
1263
+ return void 0;
1264
+ }
1265
+ return path5.isAbsolute(value) ? value : path5.resolve(options.projectRoot, value);
1266
+ };
1267
+ const resolvedConfiguredBase = toAbsolute(v4Options.configuredBase);
1268
+ const resolvedDefaultBase = toAbsolute(v4Options.base) ?? process4.cwd();
1269
+ const resolveSources = (base) => {
1270
+ if (!v4Options.sources?.length) {
1271
+ return void 0;
1272
+ }
1273
+ return v4Options.sources.map((source) => ({
1274
+ base: source.base ?? base,
1275
+ pattern: source.pattern,
1276
+ negated: source.negated
1277
+ }));
1278
+ };
1279
+ if (v4Options.cssEntries.length > 0) {
1280
+ for (const entry of v4Options.cssEntries) {
1281
+ const filePath = path5.isAbsolute(entry) ? entry : path5.resolve(options.projectRoot, entry);
1282
+ if (!await fs5.pathExists(filePath)) {
1283
+ continue;
1284
+ }
1285
+ const css = await fs5.readFile(filePath, "utf8");
1286
+ const entryDir = path5.dirname(filePath);
1287
+ const baseForEntry = resolvedConfiguredBase ?? entryDir;
1288
+ const sources = resolveSources(baseForEntry);
1289
+ const candidates = await extractValidCandidates({
1290
+ cwd: options.projectRoot,
1291
+ base: baseForEntry,
1292
+ css,
1293
+ sources
1294
+ });
1295
+ for (const candidate of candidates) {
1296
+ if (options.filter(candidate)) {
1297
+ set.add(candidate);
1298
+ }
1299
+ }
1300
+ }
1301
+ } else {
1302
+ const baseForCss = resolvedConfiguredBase ?? resolvedDefaultBase;
1303
+ const sources = resolveSources(baseForCss);
1304
+ const candidates = await extractValidCandidates({
1305
+ cwd: options.projectRoot,
1306
+ base: baseForCss,
1307
+ css: v4Options.css,
1308
+ sources
1309
+ });
1310
+ for (const candidate of candidates) {
1311
+ if (options.filter(candidate)) {
1312
+ set.add(candidate);
1313
+ }
1314
+ }
1315
+ }
1316
+ return set;
1317
+ }
1318
+
1319
+ // src/runtime/context-registry.ts
1320
+ import { createRequire } from "module";
1321
+ import fs6 from "fs-extra";
1322
+ import path6 from "pathe";
1323
+ var require2 = createRequire(import.meta.url);
1324
+ function resolveRuntimeEntry(packageInfo, majorVersion) {
1325
+ const root = packageInfo.rootPath;
1326
+ if (majorVersion === 2) {
1327
+ const jitIndex = path6.join(root, "lib/jit/index.js");
1328
+ if (fs6.existsSync(jitIndex)) {
1329
+ return jitIndex;
1330
+ }
1331
+ } else if (majorVersion === 3) {
1332
+ const plugin = path6.join(root, "lib/plugin.js");
1333
+ const index = path6.join(root, "lib/index.js");
1334
+ if (fs6.existsSync(plugin)) {
1335
+ return plugin;
1336
+ }
1337
+ if (fs6.existsSync(index)) {
1338
+ return index;
1339
+ }
1340
+ }
1341
+ return void 0;
1342
+ }
1343
+ function loadRuntimeContexts(packageInfo, majorVersion, refProperty) {
1344
+ if (majorVersion === 4) {
1345
+ return [];
1346
+ }
1347
+ const entry = resolveRuntimeEntry(packageInfo, majorVersion);
1348
+ if (!entry) {
1349
+ return [];
1350
+ }
1351
+ const moduleExports = require2(entry);
1352
+ if (!moduleExports) {
1353
+ return [];
1354
+ }
1355
+ const ref = moduleExports[refProperty];
1356
+ if (!ref) {
1357
+ return [];
1358
+ }
1359
+ if (Array.isArray(ref)) {
1360
+ return ref;
1361
+ }
1362
+ if (typeof ref === "object" && Array.isArray(ref.value)) {
1363
+ return ref.value;
1364
+ }
1365
+ return [];
1366
+ }
1367
+
1368
+ // src/runtime/process-tailwindcss.ts
1369
+ import { createRequire as createRequire2 } from "module";
1370
+ import path7 from "pathe";
1371
+ import postcss from "postcss";
1372
+ import { loadConfig } from "tailwindcss-config";
1373
+ var require3 = createRequire2(import.meta.url);
1374
+ async function resolveConfigPath(options) {
1375
+ if (options.config && path7.isAbsolute(options.config)) {
1376
+ return options.config;
1377
+ }
1378
+ const result = await loadConfig({ cwd: options.cwd });
1379
+ if (!result) {
1380
+ throw new Error(`Unable to locate Tailwind CSS config from ${options.cwd}`);
1381
+ }
1382
+ return result.filepath;
1383
+ }
1384
+ async function runTailwindBuild(options) {
1385
+ const configPath = await resolveConfigPath(options);
1386
+ const pluginName = options.postcssPlugin ?? (options.majorVersion === 4 ? "@tailwindcss/postcss" : "tailwindcss");
1387
+ if (options.majorVersion === 4) {
1388
+ return postcss([
1389
+ require3(pluginName)({
1390
+ config: configPath
1391
+ })
1392
+ ]).process("@import 'tailwindcss';", {
1393
+ from: void 0
1394
+ });
1395
+ }
1396
+ return postcss([
1397
+ require3(pluginName)({
1398
+ config: configPath
1399
+ })
1400
+ ]).process("@tailwind base;@tailwind components;@tailwind utilities;", {
1401
+ from: void 0
1402
+ });
1403
+ }
1404
+
1405
+ // src/api/tailwindcss-patcher.ts
1406
+ import process5 from "process";
1407
+ import fs8 from "fs-extra";
1408
+ import { getPackageInfoSync } from "local-pkg";
1409
+ import path9 from "pathe";
1410
+ import { coerce } from "semver";
1411
+
1412
+ // src/options/legacy.ts
1413
+ function normalizeLegacyFeatures(patch) {
1414
+ const apply = patch?.applyPatches;
1415
+ const extend = apply?.extendLengthUnits;
1416
+ let extendOption = false;
1417
+ if (extend && typeof extend === "object") {
1418
+ extendOption = {
1419
+ ...extend,
1420
+ enabled: true
1421
+ };
1422
+ } else if (extend === true) {
1423
+ extendOption = {
1424
+ enabled: true,
1425
+ units: ["rpx"],
1426
+ overwrite: patch?.overwrite
1427
+ };
1428
+ }
1429
+ return {
1430
+ exposeContext: apply?.exportContext ?? true,
1431
+ extendLengthUnits: extendOption
1432
+ };
1433
+ }
1434
+ function fromLegacyOptions(options) {
1435
+ if (!options) {
1436
+ return {};
1437
+ }
1438
+ const patch = options.patch;
1439
+ const features = normalizeLegacyFeatures(patch);
1440
+ const output = patch?.output;
1441
+ const tailwindConfig = patch?.tailwindcss;
1442
+ const tailwindVersion = tailwindConfig?.version;
1443
+ const tailwindV2 = tailwindConfig?.v2;
1444
+ const tailwindV3 = tailwindConfig?.v3;
1445
+ const tailwindV4 = tailwindConfig?.v4;
1446
+ const tailwindConfigPath = tailwindV3?.config ?? tailwindV2?.config;
1447
+ const tailwindCwd = tailwindV3?.cwd ?? tailwindV2?.cwd ?? patch?.cwd;
1448
+ return {
1449
+ cwd: patch?.cwd,
1450
+ overwrite: patch?.overwrite,
1451
+ filter: patch?.filter,
1452
+ cache: typeof options.cache === "boolean" ? options.cache : options.cache ? {
1453
+ ...options.cache,
1454
+ enabled: options.cache.enabled ?? true
1455
+ } : void 0,
1456
+ output: output ? {
1457
+ file: output.filename,
1458
+ pretty: output.loose ? 2 : false,
1459
+ removeUniversalSelector: output.removeUniversalSelector
1460
+ } : void 0,
1461
+ tailwind: {
1462
+ packageName: patch?.packageName,
1463
+ version: tailwindVersion,
1464
+ resolve: patch?.resolve,
1465
+ config: tailwindConfigPath,
1466
+ cwd: tailwindCwd,
1467
+ v2: tailwindV2,
1468
+ v3: tailwindV3,
1469
+ v4: tailwindV4
1470
+ },
1471
+ features: {
1472
+ exposeContext: features.exposeContext,
1473
+ extendLengthUnits: features.extendLengthUnits
1474
+ }
1475
+ };
1476
+ }
1477
+ function fromUnifiedConfig(registry) {
1478
+ if (!registry) {
1479
+ return {};
1480
+ }
1481
+ const tailwind = registry.tailwind;
1482
+ const output = registry.output;
1483
+ const pretty = (() => {
1484
+ if (output?.pretty === void 0) {
1485
+ return void 0;
1486
+ }
1487
+ if (typeof output.pretty === "boolean") {
1488
+ return output.pretty ? 2 : false;
1489
+ }
1490
+ return output.pretty;
1491
+ })();
1165
1492
  return {
1166
- code: hasPatched ? content : generate(ast).code,
1167
- hasPatched
1493
+ output: output ? {
1494
+ file: output.file,
1495
+ pretty,
1496
+ removeUniversalSelector: output.stripUniversalSelector
1497
+ } : void 0,
1498
+ tailwind: tailwind ? {
1499
+ version: tailwind.version,
1500
+ packageName: tailwind.package,
1501
+ resolve: tailwind.resolve,
1502
+ config: tailwind.config,
1503
+ cwd: tailwind.cwd,
1504
+ v2: tailwind.legacy,
1505
+ v3: tailwind.classic,
1506
+ v4: tailwind.next
1507
+ } : void 0
1168
1508
  };
1169
1509
  }
1170
1510
 
1171
1511
  // src/patching/operations/export-context/index.ts
1512
+ import fs7 from "fs-extra";
1513
+ import path8 from "pathe";
1172
1514
  function writeFileIfRequired(filePath, code, overwrite, successMessage) {
1173
1515
  if (!overwrite) {
1174
1516
  return;
1175
1517
  }
1176
- fs5.writeFileSync(filePath, code, {
1518
+ fs7.writeFileSync(filePath, code, {
1177
1519
  encoding: "utf8"
1178
1520
  });
1179
1521
  logger_default.success(successMessage);
@@ -1186,9 +1528,9 @@ function applyExposeContextPatch(params) {
1186
1528
  };
1187
1529
  if (majorVersion === 3) {
1188
1530
  const processFileRelative = "lib/processTailwindFeatures.js";
1189
- const processFilePath = path6.resolve(rootDir, processFileRelative);
1190
- if (fs5.existsSync(processFilePath)) {
1191
- const content = fs5.readFileSync(processFilePath, "utf8");
1531
+ const processFilePath = path8.resolve(rootDir, processFileRelative);
1532
+ if (fs7.existsSync(processFilePath)) {
1533
+ const content = fs7.readFileSync(processFilePath, "utf8");
1192
1534
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContext(content);
1193
1535
  result.files[processFileRelative] = code;
1194
1536
  if (!hasPatched) {
@@ -1202,10 +1544,10 @@ function applyExposeContextPatch(params) {
1202
1544
  }
1203
1545
  }
1204
1546
  const pluginCandidates = ["lib/plugin.js", "lib/index.js"];
1205
- const pluginRelative = pluginCandidates.find((candidate) => fs5.existsSync(path6.resolve(rootDir, candidate)));
1547
+ const pluginRelative = pluginCandidates.find((candidate) => fs7.existsSync(path8.resolve(rootDir, candidate)));
1206
1548
  if (pluginRelative) {
1207
- const pluginPath = path6.resolve(rootDir, pluginRelative);
1208
- const content = fs5.readFileSync(pluginPath, "utf8");
1549
+ const pluginPath = path8.resolve(rootDir, pluginRelative);
1550
+ const content = fs7.readFileSync(pluginPath, "utf8");
1209
1551
  const { code, hasPatched } = transformPostcssPlugin(content, { refProperty });
1210
1552
  result.files[pluginRelative] = code;
1211
1553
  if (!hasPatched) {
@@ -1220,9 +1562,9 @@ function applyExposeContextPatch(params) {
1220
1562
  }
1221
1563
  } else if (majorVersion === 2) {
1222
1564
  const processFileRelative = "lib/jit/processTailwindFeatures.js";
1223
- const processFilePath = path6.resolve(rootDir, processFileRelative);
1224
- if (fs5.existsSync(processFilePath)) {
1225
- const content = fs5.readFileSync(processFilePath, "utf8");
1565
+ const processFilePath = path8.resolve(rootDir, processFileRelative);
1566
+ if (fs7.existsSync(processFilePath)) {
1567
+ const content = fs7.readFileSync(processFilePath, "utf8");
1226
1568
  const { code, hasPatched } = transformProcessTailwindFeaturesReturnContextV2(content);
1227
1569
  result.files[processFileRelative] = code;
1228
1570
  if (!hasPatched) {
@@ -1236,9 +1578,9 @@ function applyExposeContextPatch(params) {
1236
1578
  }
1237
1579
  }
1238
1580
  const pluginRelative = "lib/jit/index.js";
1239
- const pluginPath = path6.resolve(rootDir, pluginRelative);
1240
- if (fs5.existsSync(pluginPath)) {
1241
- const content = fs5.readFileSync(pluginPath, "utf8");
1581
+ const pluginPath = path8.resolve(rootDir, pluginRelative);
1582
+ if (fs7.existsSync(pluginPath)) {
1583
+ const content = fs7.readFileSync(pluginPath, "utf8");
1242
1584
  const { code, hasPatched } = transformPostcssPluginV2(content, { refProperty });
1243
1585
  result.files[pluginRelative] = code;
1244
1586
  if (!hasPatched) {
@@ -1255,147 +1597,6 @@ function applyExposeContextPatch(params) {
1255
1597
  return result;
1256
1598
  }
1257
1599
 
1258
- // src/patching/operations/extend-length-units.ts
1259
- import * as t3 from "@babel/types";
1260
- import fs6 from "fs-extra";
1261
- import path7 from "pathe";
1262
- function updateLengthUnitsArray(content, options) {
1263
- const { variableName = "lengthUnits", units } = options;
1264
- const ast = parse(content);
1265
- let arrayRef;
1266
- let changed = false;
1267
- traverse(ast, {
1268
- Identifier(path10) {
1269
- if (path10.node.name === variableName && t3.isVariableDeclarator(path10.parent) && t3.isArrayExpression(path10.parent.init)) {
1270
- arrayRef = path10.parent.init;
1271
- const existing = new Set(
1272
- path10.parent.init.elements.map((element) => t3.isStringLiteral(element) ? element.value : void 0).filter(Boolean)
1273
- );
1274
- for (const unit of units) {
1275
- if (!existing.has(unit)) {
1276
- path10.parent.init.elements = path10.parent.init.elements.map((element) => {
1277
- if (t3.isStringLiteral(element)) {
1278
- return t3.stringLiteral(element.value);
1279
- }
1280
- return element;
1281
- });
1282
- path10.parent.init.elements.push(t3.stringLiteral(unit));
1283
- changed = true;
1284
- }
1285
- }
1286
- }
1287
- }
1288
- });
1289
- return {
1290
- arrayRef,
1291
- changed
1292
- };
1293
- }
1294
- function applyExtendLengthUnitsPatchV3(rootDir, options) {
1295
- if (!options.enabled) {
1296
- return { changed: false, code: void 0 };
1297
- }
1298
- const opts = {
1299
- ...options,
1300
- lengthUnitsFilePath: options.lengthUnitsFilePath ?? "lib/util/dataTypes.js",
1301
- variableName: options.variableName ?? "lengthUnits"
1302
- };
1303
- const dataTypesFilePath = path7.resolve(rootDir, opts.lengthUnitsFilePath);
1304
- const exists = fs6.existsSync(dataTypesFilePath);
1305
- if (!exists) {
1306
- return { changed: false, code: void 0 };
1307
- }
1308
- const content = fs6.readFileSync(dataTypesFilePath, "utf8");
1309
- const { arrayRef, changed } = updateLengthUnitsArray(content, opts);
1310
- if (!arrayRef || !changed) {
1311
- return { changed: false, code: void 0 };
1312
- }
1313
- const { code } = generate(arrayRef, {
1314
- jsescOption: { quotes: "single" }
1315
- });
1316
- if (arrayRef.start != null && arrayRef.end != null) {
1317
- const nextCode = `${content.slice(0, arrayRef.start)}${code}${content.slice(arrayRef.end)}`;
1318
- if (opts.overwrite) {
1319
- const target = opts.destPath ? path7.resolve(opts.destPath) : dataTypesFilePath;
1320
- fs6.writeFileSync(target, nextCode, "utf8");
1321
- logger_default.success("Patched Tailwind CSS length unit list (v3).");
1322
- }
1323
- return {
1324
- changed: true,
1325
- code: nextCode
1326
- };
1327
- }
1328
- return {
1329
- changed: false,
1330
- code: void 0
1331
- };
1332
- }
1333
- function applyExtendLengthUnitsPatchV4(rootDir, options) {
1334
- if (!options.enabled) {
1335
- return { files: [], changed: false };
1336
- }
1337
- const opts = { ...options };
1338
- const distDir = path7.resolve(rootDir, "dist");
1339
- if (!fs6.existsSync(distDir)) {
1340
- return { files: [], changed: false };
1341
- }
1342
- const entries = fs6.readdirSync(distDir);
1343
- const chunkNames = entries.filter((entry) => entry.endsWith(".js") || entry.endsWith(".mjs"));
1344
- const pattern = /\[\s*["']cm["'],\s*["']mm["'],[\w,"']+\]/;
1345
- const candidates = chunkNames.map((chunkName) => {
1346
- const file = path7.join(distDir, chunkName);
1347
- const code = fs6.readFileSync(file, "utf8");
1348
- const match = pattern.exec(code);
1349
- if (!match) {
1350
- return null;
1351
- }
1352
- return {
1353
- file,
1354
- code,
1355
- match,
1356
- hasPatched: false
1357
- };
1358
- }).filter((candidate) => candidate !== null);
1359
- for (const item of candidates) {
1360
- const { code, file, match } = item;
1361
- const ast = parse(match[0], { sourceType: "unambiguous" });
1362
- traverse(ast, {
1363
- ArrayExpression(path10) {
1364
- for (const unit of opts.units) {
1365
- if (path10.node.elements.some((element) => t3.isStringLiteral(element) && element.value === unit)) {
1366
- item.hasPatched = true;
1367
- return;
1368
- }
1369
- path10.node.elements.push(t3.stringLiteral(unit));
1370
- }
1371
- }
1372
- });
1373
- if (item.hasPatched) {
1374
- continue;
1375
- }
1376
- const { code: replacement } = generate(ast, { minified: true });
1377
- const start = match.index ?? 0;
1378
- const end = start + match[0].length;
1379
- item.code = spliceChangesIntoString(code, [
1380
- {
1381
- start,
1382
- end,
1383
- replacement: replacement.endsWith(";") ? replacement.slice(0, -1) : replacement
1384
- }
1385
- ]);
1386
- if (opts.overwrite) {
1387
- fs6.writeFileSync(file, item.code, "utf8");
1388
- }
1389
- }
1390
- if (candidates.some((file) => !file.hasPatched)) {
1391
- logger_default.success("Patched Tailwind CSS length unit list (v4).");
1392
- }
1393
- return {
1394
- changed: candidates.some((file) => !file.hasPatched),
1395
- files: candidates
1396
- };
1397
- }
1398
-
1399
1600
  // src/patching/patch-runner.ts
1400
1601
  function applyTailwindPatches(context) {
1401
1602
  const { packageInfo, options, majorVersion } = context;
@@ -1494,6 +1695,13 @@ var TailwindcssPatcher = class {
1494
1695
  majorVersion: this.majorVersion
1495
1696
  });
1496
1697
  }
1698
+ async getPatchStatus() {
1699
+ return getPatchStatusReport({
1700
+ packageInfo: this.packageInfo,
1701
+ options: this.options,
1702
+ majorVersion: this.majorVersion
1703
+ });
1704
+ }
1497
1705
  getContexts() {
1498
1706
  return loadRuntimeContexts(
1499
1707
  this.packageInfo,
@@ -1519,13 +1727,6 @@ var TailwindcssPatcher = class {
1519
1727
  const contexts = this.getContexts();
1520
1728
  return collectClassesFromContexts(contexts, this.options.filter);
1521
1729
  }
1522
- collectClassSetSync() {
1523
- if (this.majorVersion === 4) {
1524
- throw new Error("getClassSetSync is not supported for Tailwind CSS v4 projects. Use getClassSet instead.");
1525
- }
1526
- const contexts = this.getContexts();
1527
- return collectClassesFromContexts(contexts, this.options.filter);
1528
- }
1529
1730
  async mergeWithCache(set) {
1530
1731
  if (!this.options.cache.enabled) {
1531
1732
  return set;
@@ -1570,8 +1771,16 @@ var TailwindcssPatcher = class {
1570
1771
  return this.mergeWithCache(set);
1571
1772
  }
1572
1773
  getClassSetSync() {
1573
- const set = this.collectClassSetSync();
1574
- return this.mergeWithCacheSync(set);
1774
+ if (this.majorVersion === 4) {
1775
+ throw new Error("getClassSetSync is not supported for Tailwind CSS v4 projects. Use getClassSet instead.");
1776
+ }
1777
+ const contexts = this.getContexts();
1778
+ const set = collectClassesFromContexts(contexts, this.options.filter);
1779
+ const merged = this.mergeWithCacheSync(set);
1780
+ if (contexts.length === 0 && merged.size === 0) {
1781
+ return void 0;
1782
+ }
1783
+ return merged;
1575
1784
  }
1576
1785
  async extract(options) {
1577
1786
  const shouldWrite = options?.write ?? this.options.output.enabled;
@@ -1584,13 +1793,13 @@ var TailwindcssPatcher = class {
1584
1793
  if (!shouldWrite || !this.options.output.file) {
1585
1794
  return result;
1586
1795
  }
1587
- const target = path8.resolve(this.options.output.file);
1588
- await fs7.ensureDir(path8.dirname(target));
1796
+ const target = path9.resolve(this.options.output.file);
1797
+ await fs8.ensureDir(path9.dirname(target));
1589
1798
  if (this.options.output.format === "json") {
1590
1799
  const spaces = typeof this.options.output.pretty === "number" ? this.options.output.pretty : void 0;
1591
- await fs7.writeJSON(target, classList, { spaces });
1800
+ await fs8.writeJSON(target, classList, { spaces });
1592
1801
  } else {
1593
- await fs7.writeFile(target, `${classList.join("\n")}
1802
+ await fs8.writeFile(target, `${classList.join("\n")}
1594
1803
  `, "utf8");
1595
1804
  }
1596
1805
  logger_default.success(`Tailwind CSS class list saved to ${target.replace(process5.cwd(), ".")}`);
@@ -1715,9 +1924,9 @@ var acceptChars = [..."abcdefghijklmnopqrstuvwxyz"];
1715
1924
 
1716
1925
  // src/cli/commands.ts
1717
1926
  import cac from "cac";
1718
- import fs8 from "fs-extra";
1719
- import path9 from "pathe";
1720
- var tailwindcssPatchCommands = ["install", "extract", "tokens", "init"];
1927
+ import fs9 from "fs-extra";
1928
+ import path10 from "pathe";
1929
+ var tailwindcssPatchCommands = ["install", "extract", "tokens", "init", "status"];
1721
1930
  var TOKEN_FORMATS = ["json", "lines", "grouped-json"];
1722
1931
  var DEFAULT_TOKEN_REPORT = ".tw-patch/tw-token-report.json";
1723
1932
  function formatTokenLine(entry) {
@@ -1743,7 +1952,7 @@ function resolveCwd(rawCwd) {
1743
1952
  if (!rawCwd) {
1744
1953
  return process6.cwd();
1745
1954
  }
1746
- return path9.resolve(rawCwd);
1955
+ return path10.resolve(rawCwd);
1747
1956
  }
1748
1957
  function createDefaultRunner(factory) {
1749
1958
  let promise;
@@ -1845,6 +2054,13 @@ function buildDefaultCommandDefinitions() {
1845
2054
  init: {
1846
2055
  description: "Generate a tailwindcss-patch config file",
1847
2056
  optionDefs: [createCwdOptionDefinition()]
2057
+ },
2058
+ status: {
2059
+ description: "Check which Tailwind patches are applied",
2060
+ optionDefs: [
2061
+ createCwdOptionDefinition(),
2062
+ { flags: "--json", description: "Print a JSON report of patch status" }
2063
+ ]
1848
2064
  }
1849
2065
  };
1850
2066
  }
@@ -1948,15 +2164,15 @@ async function tokensCommandDefaultHandler(ctx) {
1948
2164
  const grouped = format === "grouped-json" ? buildGrouped() : null;
1949
2165
  const resolveGrouped = () => grouped ?? buildGrouped();
1950
2166
  if (shouldWrite) {
1951
- const target = path9.resolve(targetFile);
1952
- await fs8.ensureDir(path9.dirname(target));
2167
+ const target = path10.resolve(targetFile);
2168
+ await fs9.ensureDir(path10.dirname(target));
1953
2169
  if (format === "json") {
1954
- await fs8.writeJSON(target, report, { spaces: 2 });
2170
+ await fs9.writeJSON(target, report, { spaces: 2 });
1955
2171
  } else if (format === "grouped-json") {
1956
- await fs8.writeJSON(target, resolveGrouped(), { spaces: 2 });
2172
+ await fs9.writeJSON(target, resolveGrouped(), { spaces: 2 });
1957
2173
  } else {
1958
2174
  const lines = report.entries.map(formatTokenLine);
1959
- await fs8.writeFile(target, `${lines.join("\n")}
2175
+ await fs9.writeFile(target, `${lines.join("\n")}
1960
2176
  `, "utf8");
1961
2177
  }
1962
2178
  logger_default.success(`Collected ${report.entries.length} tokens (${format}) \u2192 ${target.replace(process6.cwd(), ".")}`);
@@ -2001,6 +2217,46 @@ async function initCommandDefaultHandler(ctx) {
2001
2217
  await initConfig(ctx.cwd);
2002
2218
  logger_default.success(`\u2728 ${CONFIG_NAME}.config.ts initialized!`);
2003
2219
  }
2220
+ function formatFilesHint(entry) {
2221
+ if (!entry.files.length) {
2222
+ return "";
2223
+ }
2224
+ return ` (${entry.files.join(", ")})`;
2225
+ }
2226
+ async function statusCommandDefaultHandler(ctx) {
2227
+ const patcher = await ctx.createPatcher();
2228
+ const report = await patcher.getPatchStatus();
2229
+ if (ctx.args.json) {
2230
+ logger_default.log(JSON.stringify(report, null, 2));
2231
+ return report;
2232
+ }
2233
+ const applied = report.entries.filter((entry) => entry.status === "applied");
2234
+ const pending = report.entries.filter((entry) => entry.status === "not-applied");
2235
+ const skipped = report.entries.filter((entry) => entry.status === "skipped" || entry.status === "unsupported");
2236
+ const packageLabel = `${report.package.name ?? "tailwindcss"}@${report.package.version ?? "unknown"}`;
2237
+ logger_default.info(`Patch status for ${packageLabel} (v${report.majorVersion})`);
2238
+ if (applied.length) {
2239
+ logger_default.success("Applied:");
2240
+ applied.forEach((entry) => logger_default.success(` \u2022 ${entry.name}${formatFilesHint(entry)}`));
2241
+ }
2242
+ if (pending.length) {
2243
+ logger_default.warn("Needs attention:");
2244
+ pending.forEach((entry) => {
2245
+ const details = entry.reason ? ` \u2013 ${entry.reason}` : "";
2246
+ logger_default.warn(` \u2022 ${entry.name}${formatFilesHint(entry)}${details}`);
2247
+ });
2248
+ } else {
2249
+ logger_default.success("All applicable patches are applied.");
2250
+ }
2251
+ if (skipped.length) {
2252
+ logger_default.info("Skipped:");
2253
+ skipped.forEach((entry) => {
2254
+ const details = entry.reason ? ` \u2013 ${entry.reason}` : "";
2255
+ logger_default.info(` \u2022 ${entry.name}${details}`);
2256
+ });
2257
+ }
2258
+ return report;
2259
+ }
2004
2260
  function mountTailwindcssPatchCommands(cli, options = {}) {
2005
2261
  const prefix = options.commandPrefix ?? "";
2006
2262
  const selectedCommands = options.commands ?? tailwindcssPatchCommands;
@@ -2069,6 +2325,22 @@ function mountTailwindcssPatchCommands(cli, options = {}) {
2069
2325
  );
2070
2326
  });
2071
2327
  metadata.aliases.forEach((alias) => command.alias(alias));
2328
+ },
2329
+ status: () => {
2330
+ const metadata = resolveCommandMetadata("status", options, prefix, defaultDefinitions);
2331
+ const command = cli.command(metadata.name, metadata.description);
2332
+ applyCommandOptions(command, metadata.optionDefs);
2333
+ command.action(async (args) => {
2334
+ return runWithCommandHandler(
2335
+ cli,
2336
+ command,
2337
+ "status",
2338
+ args,
2339
+ options.commandHandlers?.status,
2340
+ statusCommandDefaultHandler
2341
+ );
2342
+ });
2343
+ metadata.aliases.forEach((alias) => command.alias(alias));
2072
2344
  }
2073
2345
  };
2074
2346
  for (const name of selectedCommands) {
@@ -2094,6 +2366,7 @@ export {
2094
2366
  extractProjectCandidatesWithPositions,
2095
2367
  groupTokensByFile,
2096
2368
  normalizeOptions,
2369
+ getPatchStatusReport,
2097
2370
  collectClassesFromContexts,
2098
2371
  collectClassesFromTailwindV4,
2099
2372
  loadRuntimeContexts,