tailwindcss-patch 9.3.1 → 9.3.2

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.
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const require_chunk = require("./chunk-8l464Juk.js");
2
- const require_validate = require("./validate-Bd1fbQsd.js");
3
- const require_index_bundle = require("./index.bundle-Y1jzXpYx.js");
2
+ const require_validate = require("./validate-CF0M_7KH.js");
3
+ const require_index_bundle = require("./index.bundle-CcVIAfDb.js");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_chunk.__toESM(node_process);
6
6
  //#region src/cli.bundle.ts
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { j as logger, r as ValidateCommandError } from "./validate-CVInpab6.mjs";
2
- import { t as createTailwindcssPatchCli } from "./index.bundle-BKRsKEHP.mjs";
1
+ import { j as logger, r as ValidateCommandError } from "./validate-Sq_yuh3r.mjs";
2
+ import { t as createTailwindcssPatchCli } from "./index.bundle-DjS24i5r.mjs";
3
3
  import process from "node:process";
4
4
  //#region src/cli.bundle.ts
5
5
  async function main() {
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../chunk-8l464Juk.js");
3
- const require_validate = require("../validate-Bd1fbQsd.js");
3
+ const require_validate = require("../validate-CF0M_7KH.js");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_chunk.__toESM(node_process);
6
6
  let fs_extra = require("fs-extra");
@@ -1,4 +1,4 @@
1
- import { D as loadPatchOptionsForWorkspace, O as loadWorkspaceConfigModule, a as tailwindcssPatchCommands, b as groupTokensByFile, i as classifyValidateError, j as logger, n as VALIDATE_FAILURE_REASONS, o as migrateConfigFiles, r as ValidateCommandError, s as restoreConfigFiles, t as VALIDATE_EXIT_CODES, u as TailwindcssPatcher } from "../validate-CVInpab6.mjs";
1
+ import { D as loadPatchOptionsForWorkspace, O as loadWorkspaceConfigModule, a as tailwindcssPatchCommands, b as groupTokensByFile, i as classifyValidateError, j as logger, n as VALIDATE_FAILURE_REASONS, o as migrateConfigFiles, r as ValidateCommandError, s as restoreConfigFiles, t as VALIDATE_EXIT_CODES, u as TailwindcssPatcher } from "../validate-Sq_yuh3r.mjs";
2
2
  import process from "node:process";
3
3
  import fs from "fs-extra";
4
4
  import path from "pathe";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-8l464Juk.js");
2
- const require_validate = require("./validate-Bd1fbQsd.js");
2
+ const require_validate = require("./validate-CF0M_7KH.js");
3
3
  let node_module = require("node:module");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_chunk.__toESM(node_process);
@@ -1,4 +1,4 @@
1
- import { C as canonicalizeBareArbitraryValueCandidates, E as resolveValidTailwindV4Candidates, S as loadTailwindV4DesignSystem, T as replaceBareArbitraryValueSelectors, _ as extractRawCandidates, k as normalizeOptions, v as extractRawCandidatesWithPositions, w as extractTailwindV4InlineSourceCandidates, x as compileTailwindV4Source } from "./validate-CVInpab6.mjs";
1
+ import { C as canonicalizeBareArbitraryValueCandidates, E as resolveValidTailwindV4Candidates, S as loadTailwindV4DesignSystem, T as replaceBareArbitraryValueSelectors, _ as extractRawCandidates, k as normalizeOptions, v as extractRawCandidatesWithPositions, w as extractTailwindV4InlineSourceCandidates, x as compileTailwindV4Source } from "./validate-Sq_yuh3r.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import process from "node:process";
4
4
  import path from "pathe";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_validate = require("./validate-Bd1fbQsd.js");
3
- const require_index_bundle = require("./index.bundle-Y1jzXpYx.js");
2
+ const require_validate = require("./validate-CF0M_7KH.js");
3
+ const require_index_bundle = require("./index.bundle-CcVIAfDb.js");
4
4
  exports.CacheStore = require_validate.CacheStore;
5
5
  exports.MIGRATION_REPORT_KIND = require_validate.MIGRATION_REPORT_KIND;
6
6
  exports.MIGRATION_REPORT_SCHEMA_VERSION = require_validate.MIGRATION_REPORT_SCHEMA_VERSION;
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { A as CacheStore, E as resolveValidTailwindV4Candidates, S as loadTailwindV4DesignSystem, _ as extractRawCandidates, a as tailwindcssPatchCommands, b as groupTokensByFile, c as MIGRATION_REPORT_KIND, d as getPatchStatusReport, f as runTailwindBuild, g as extractProjectCandidatesWithPositions, h as collectClassesFromTailwindV4, j as logger, k as normalizeOptions, l as MIGRATION_REPORT_SCHEMA_VERSION, m as collectClassesFromContexts, n as VALIDATE_FAILURE_REASONS, o as migrateConfigFiles, p as loadRuntimeContexts, r as ValidateCommandError, s as restoreConfigFiles, t as VALIDATE_EXIT_CODES, u as TailwindcssPatcher, v as extractRawCandidatesWithPositions, y as extractValidCandidates } from "./validate-CVInpab6.mjs";
2
- import { a as resolveTailwindV4SourceFromPatchOptions, i as resolveTailwindV4Source, n as defineConfig, o as createTailwindV4Engine, r as mountTailwindcssPatchCommands, t as createTailwindcssPatchCli } from "./index.bundle-BKRsKEHP.mjs";
1
+ import { A as CacheStore, E as resolveValidTailwindV4Candidates, S as loadTailwindV4DesignSystem, _ as extractRawCandidates, a as tailwindcssPatchCommands, b as groupTokensByFile, c as MIGRATION_REPORT_KIND, d as getPatchStatusReport, f as runTailwindBuild, g as extractProjectCandidatesWithPositions, h as collectClassesFromTailwindV4, j as logger, k as normalizeOptions, l as MIGRATION_REPORT_SCHEMA_VERSION, m as collectClassesFromContexts, n as VALIDATE_FAILURE_REASONS, o as migrateConfigFiles, p as loadRuntimeContexts, r as ValidateCommandError, s as restoreConfigFiles, t as VALIDATE_EXIT_CODES, u as TailwindcssPatcher, v as extractRawCandidatesWithPositions, y as extractValidCandidates } from "./validate-Sq_yuh3r.mjs";
2
+ import { a as resolveTailwindV4SourceFromPatchOptions, i as resolveTailwindV4Source, n as defineConfig, o as createTailwindV4Engine, r as mountTailwindcssPatchCommands, t as createTailwindcssPatchCli } from "./index.bundle-DjS24i5r.mjs";
3
3
  export { CacheStore, MIGRATION_REPORT_KIND, MIGRATION_REPORT_SCHEMA_VERSION, TailwindcssPatcher, VALIDATE_EXIT_CODES, VALIDATE_FAILURE_REASONS, ValidateCommandError, collectClassesFromContexts, collectClassesFromTailwindV4, createTailwindV4Engine, createTailwindcssPatchCli, defineConfig, extractProjectCandidatesWithPositions, extractRawCandidates, extractRawCandidatesWithPositions, extractValidCandidates, getPatchStatusReport, groupTokensByFile, loadRuntimeContexts, loadTailwindV4DesignSystem, logger, migrateConfigFiles, mountTailwindcssPatchCommands, normalizeOptions, resolveTailwindV4Source, resolveTailwindV4SourceFromPatchOptions, resolveValidTailwindV4Candidates, restoreConfigFiles, runTailwindBuild, tailwindcssPatchCommands };
@@ -23,7 +23,7 @@ _babel_traverse = require_chunk.__toESM(_babel_traverse);
23
23
  let _babel_parser = require("@babel/parser");
24
24
  let tailwindcss_config = require("tailwindcss-config");
25
25
  //#region package.json
26
- var version = "9.3.1";
26
+ var version = "9.3.2";
27
27
  //#endregion
28
28
  //#region src/constants.ts
29
29
  const pkgName = "tailwindcss-patch";
@@ -1479,7 +1479,8 @@ const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
1479
1479
  "ms"
1480
1480
  ];
1481
1481
  const NUMBER_RE = /^-?(?:\d+|\d*\.\d+)$/;
1482
- const FUNCTION_VALUE_RE = /^[a-zA-Z_-][a-zA-Z0-9_-]*\(/;
1482
+ const FUNCTION_VALUE_RE = /^[a-z_-][\w-]*\(/i;
1483
+ const HEX_ESCAPE_RE = /^[\da-f]$/i;
1483
1484
  function splitVariantPrefix(candidate) {
1484
1485
  let depth = 0;
1485
1486
  let quote;
@@ -1545,8 +1546,38 @@ function isBalancedFunctionValue(value) {
1545
1546
  }
1546
1547
  return depth === 0 && quote === void 0;
1547
1548
  }
1549
+ function isEscapedAt(value, index) {
1550
+ let slashCount = 0;
1551
+ for (let slashIndex = index - 1; slashIndex >= 0 && value[slashIndex] === "\\"; slashIndex--) slashCount++;
1552
+ return slashCount % 2 === 1;
1553
+ }
1554
+ function isBalancedBareArbitraryBody(value) {
1555
+ let depth = 0;
1556
+ let quote;
1557
+ for (let index = 0; index < value.length; index++) {
1558
+ const character = value[index];
1559
+ if (isEscapedAt(value, index)) continue;
1560
+ if (quote) {
1561
+ if (character === quote) quote = void 0;
1562
+ continue;
1563
+ }
1564
+ if (character === "\"" || character === "'") {
1565
+ quote = character;
1566
+ continue;
1567
+ }
1568
+ if (character === "(" || character === "{") {
1569
+ depth++;
1570
+ continue;
1571
+ }
1572
+ if (character === ")" || character === "}") {
1573
+ depth--;
1574
+ if (depth < 0) return false;
1575
+ }
1576
+ }
1577
+ return depth === 0 && quote === void 0;
1578
+ }
1548
1579
  function isHexColorValue(value) {
1549
- return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6,8})$/.test(value);
1580
+ return /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6,8})$/i.test(value);
1550
1581
  }
1551
1582
  function isQuotedValue(value) {
1552
1583
  const quote = value[0];
@@ -1569,29 +1600,61 @@ function normalizeBareArbitraryValueOptions(options) {
1569
1600
  if (normalizedUnits.length === 0) return;
1570
1601
  return { units: normalizedUnits.sort((a, b) => b.length - a.length) };
1571
1602
  }
1603
+ function normalizeEscapedValue(value) {
1604
+ let result = "";
1605
+ for (let index = 0; index < value.length; index++) {
1606
+ const character = value[index];
1607
+ if (character !== "\\") {
1608
+ result += character;
1609
+ continue;
1610
+ }
1611
+ const nextCharacter = value[index + 1];
1612
+ if (nextCharacter === void 0) {
1613
+ result += character;
1614
+ continue;
1615
+ }
1616
+ if (HEX_ESCAPE_RE.test(nextCharacter)) {
1617
+ let hex = "";
1618
+ let nextIndex = index + 1;
1619
+ while (nextIndex < value.length && hex.length < 6) {
1620
+ const hexCharacter = value[nextIndex];
1621
+ if (hexCharacter === void 0 || !HEX_ESCAPE_RE.test(hexCharacter)) break;
1622
+ hex += hexCharacter;
1623
+ nextIndex++;
1624
+ }
1625
+ if (/[\t\n\f\r ]/.test(value[nextIndex] ?? "")) nextIndex++;
1626
+ const decoded = String.fromCodePoint(Number.parseInt(hex, 16));
1627
+ result += decoded === "_" ? "\\_" : decoded;
1628
+ index = nextIndex - 1;
1629
+ continue;
1630
+ }
1631
+ result += nextCharacter === "_" ? "\\_" : nextCharacter;
1632
+ index++;
1633
+ }
1634
+ return result;
1635
+ }
1572
1636
  function resolveValueWithUnit(body, units) {
1637
+ const value = normalizeEscapedValue(body);
1573
1638
  for (const unit of units) {
1574
- if (!body.endsWith(unit)) continue;
1575
- const numberPart = body.slice(0, -unit.length);
1639
+ if (!value.endsWith(unit)) continue;
1640
+ const numberPart = value.slice(0, -unit.length);
1576
1641
  if (NUMBER_RE.test(numberPart)) return `${numberPart}${unit}`;
1577
1642
  }
1578
1643
  }
1579
1644
  function resolveArbitraryValue(body, units) {
1580
- const withUnit = resolveValueWithUnit(body, units);
1645
+ const value = normalizeEscapedValue(body);
1646
+ const withUnit = resolveValueWithUnit(value, units);
1581
1647
  if (withUnit) return withUnit;
1582
- if (isHexColorValue(body)) return body;
1583
- if (isQuotedValue(body)) return body;
1584
- if (FUNCTION_VALUE_RE.test(body) && body.endsWith(")") && isBalancedFunctionValue(body)) return body;
1648
+ if (isHexColorValue(value)) return value;
1649
+ if (isQuotedValue(value)) return value;
1650
+ if (FUNCTION_VALUE_RE.test(value) && value.endsWith(")") && isBalancedFunctionValue(value)) return value;
1585
1651
  }
1586
1652
  function resolveUtilityAndValue(body, units) {
1587
1653
  let depth = 0;
1588
1654
  let quote;
1589
- for (let index = 1; index < body.length; index++) {
1655
+ for (let index = body.length - 1; index > 0; index--) {
1590
1656
  const character = body[index];
1591
- if (character === "\\") {
1592
- index++;
1593
- continue;
1594
- }
1657
+ if (isEscapedAt(body, index)) continue;
1595
1658
  if (quote) {
1596
1659
  if (character === quote) quote = void 0;
1597
1660
  continue;
@@ -1600,11 +1663,11 @@ function resolveUtilityAndValue(body, units) {
1600
1663
  quote = character;
1601
1664
  continue;
1602
1665
  }
1603
- if (character === "(" || character === "{") {
1666
+ if (character === ")" || character === "}") {
1604
1667
  depth++;
1605
1668
  continue;
1606
1669
  }
1607
- if (character === ")" || character === "}") {
1670
+ if (character === "(" || character === "{") {
1608
1671
  depth = Math.max(0, depth - 1);
1609
1672
  continue;
1610
1673
  }
@@ -1627,6 +1690,7 @@ function resolveBareArbitraryValueCandidate(candidate, options) {
1627
1690
  let normalizedBody = important ? body.slice(1) : body;
1628
1691
  const negative = normalizedBody.startsWith("-") ? "-" : "";
1629
1692
  if (negative) normalizedBody = normalizedBody.slice(1);
1693
+ if (!isBalancedBareArbitraryBody(normalizedBody)) return;
1630
1694
  const resolved = resolveUtilityAndValue(normalizedBody, normalizedOptions.units);
1631
1695
  if (!resolved) return;
1632
1696
  return {
@@ -1660,23 +1724,32 @@ function escapeCssClassName(value) {
1660
1724
  function resolveValidTailwindV4Candidates(designSystem, candidates, options) {
1661
1725
  const validCandidates = /* @__PURE__ */ new Set();
1662
1726
  const parsedCandidates = [];
1663
- const originalCandidateByCanonical = /* @__PURE__ */ new Map();
1727
+ const originalCandidatesByCanonical = /* @__PURE__ */ new Map();
1664
1728
  for (const candidate of candidates) {
1665
1729
  if (!candidate) continue;
1666
1730
  const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options?.bareArbitraryValues);
1667
1731
  const candidateToCheck = bareArbitrary?.canonicalCandidate ?? candidate;
1668
- if (parsedCandidates.includes(candidateToCheck)) continue;
1669
- if (designSystem.parseCandidate(candidateToCheck).length > 0) {
1670
- parsedCandidates.push(candidateToCheck);
1671
- if (bareArbitrary) originalCandidateByCanonical.set(candidateToCheck, candidate);
1732
+ if (bareArbitrary) {
1733
+ const originalCandidates = originalCandidatesByCanonical.get(candidateToCheck) ?? /* @__PURE__ */ new Set();
1734
+ originalCandidates.add(candidate);
1735
+ originalCandidatesByCanonical.set(candidateToCheck, originalCandidates);
1672
1736
  }
1737
+ if (parsedCandidates.includes(candidateToCheck)) continue;
1738
+ if (designSystem.parseCandidate(candidateToCheck).length > 0) parsedCandidates.push(candidateToCheck);
1673
1739
  }
1674
1740
  if (parsedCandidates.length === 0) return validCandidates;
1675
1741
  const cssByCandidate = designSystem.candidatesToCss(parsedCandidates);
1676
1742
  for (let index = 0; index < parsedCandidates.length; index++) {
1677
1743
  const candidate = parsedCandidates[index];
1678
1744
  const candidateCss = cssByCandidate[index];
1679
- if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) validCandidates.add(originalCandidateByCanonical.get(candidate) ?? candidate);
1745
+ if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) {
1746
+ const originalCandidates = originalCandidatesByCanonical.get(candidate);
1747
+ if (originalCandidates) {
1748
+ for (const originalCandidate of originalCandidates) validCandidates.add(originalCandidate);
1749
+ continue;
1750
+ }
1751
+ validCandidates.add(candidate);
1752
+ }
1680
1753
  }
1681
1754
  return validCandidates;
1682
1755
  }
@@ -1685,16 +1758,34 @@ function createSelectorAliasMap(candidates, options) {
1685
1758
  for (const candidate of candidates) {
1686
1759
  const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options);
1687
1760
  if (!bareArbitrary) continue;
1688
- aliases.set(escapeCssClassName(bareArbitrary.canonicalCandidate), escapeCssClassName(bareArbitrary.candidate));
1761
+ const canonicalSelector = escapeCssClassName(bareArbitrary.canonicalCandidate);
1762
+ const bareSelectors = aliases.get(canonicalSelector) ?? /* @__PURE__ */ new Set();
1763
+ bareSelectors.add(escapeCssClassName(bareArbitrary.candidate));
1764
+ aliases.set(canonicalSelector, bareSelectors);
1689
1765
  }
1690
1766
  return aliases;
1691
1767
  }
1692
1768
  function replaceBareArbitraryValueSelectors(css, candidates, options) {
1693
1769
  const aliases = createSelectorAliasMap(candidates, options);
1694
1770
  if (aliases.size === 0) return css;
1695
- let result = css;
1696
- for (const [canonicalSelector, bareSelector] of aliases) result = result.replaceAll(canonicalSelector, bareSelector);
1697
- return result;
1771
+ if (Array.from(aliases.values()).every((bareSelectors) => bareSelectors.size === 1)) {
1772
+ let result = css;
1773
+ for (const [canonicalSelector, bareSelectors] of aliases) {
1774
+ const bareSelector = Array.from(bareSelectors)[0];
1775
+ if (bareSelector !== void 0) result = result.replaceAll(canonicalSelector, bareSelector);
1776
+ }
1777
+ return result;
1778
+ }
1779
+ const root = postcss.default.parse(css);
1780
+ root.walkRules((rule) => {
1781
+ let selectors = rule.selectors;
1782
+ for (const [canonicalSelector, bareSelectors] of aliases) selectors = selectors.flatMap((selector) => {
1783
+ if (!selector.includes(canonicalSelector)) return selector;
1784
+ return Array.from(bareSelectors, (bareSelector) => selector.replaceAll(canonicalSelector, bareSelector));
1785
+ });
1786
+ rule.selectors = selectors;
1787
+ });
1788
+ return root.toString();
1698
1789
  }
1699
1790
  function canonicalizeBareArbitraryValueCandidates(candidates, options) {
1700
1791
  return Array.from(candidates, (candidate) => {
@@ -16,7 +16,7 @@ import _babelTraverse from "@babel/traverse";
16
16
  import { parse, parse as parse$1 } from "@babel/parser";
17
17
  import { loadConfig } from "tailwindcss-config";
18
18
  //#region package.json
19
- var version = "9.3.1";
19
+ var version = "9.3.2";
20
20
  //#endregion
21
21
  //#region src/constants.ts
22
22
  const pkgName = "tailwindcss-patch";
@@ -1477,7 +1477,8 @@ const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
1477
1477
  "ms"
1478
1478
  ];
1479
1479
  const NUMBER_RE = /^-?(?:\d+|\d*\.\d+)$/;
1480
- const FUNCTION_VALUE_RE = /^[a-zA-Z_-][a-zA-Z0-9_-]*\(/;
1480
+ const FUNCTION_VALUE_RE = /^[a-z_-][\w-]*\(/i;
1481
+ const HEX_ESCAPE_RE = /^[\da-f]$/i;
1481
1482
  function splitVariantPrefix(candidate) {
1482
1483
  let depth = 0;
1483
1484
  let quote;
@@ -1543,8 +1544,38 @@ function isBalancedFunctionValue(value) {
1543
1544
  }
1544
1545
  return depth === 0 && quote === void 0;
1545
1546
  }
1547
+ function isEscapedAt(value, index) {
1548
+ let slashCount = 0;
1549
+ for (let slashIndex = index - 1; slashIndex >= 0 && value[slashIndex] === "\\"; slashIndex--) slashCount++;
1550
+ return slashCount % 2 === 1;
1551
+ }
1552
+ function isBalancedBareArbitraryBody(value) {
1553
+ let depth = 0;
1554
+ let quote;
1555
+ for (let index = 0; index < value.length; index++) {
1556
+ const character = value[index];
1557
+ if (isEscapedAt(value, index)) continue;
1558
+ if (quote) {
1559
+ if (character === quote) quote = void 0;
1560
+ continue;
1561
+ }
1562
+ if (character === "\"" || character === "'") {
1563
+ quote = character;
1564
+ continue;
1565
+ }
1566
+ if (character === "(" || character === "{") {
1567
+ depth++;
1568
+ continue;
1569
+ }
1570
+ if (character === ")" || character === "}") {
1571
+ depth--;
1572
+ if (depth < 0) return false;
1573
+ }
1574
+ }
1575
+ return depth === 0 && quote === void 0;
1576
+ }
1546
1577
  function isHexColorValue(value) {
1547
- return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6,8})$/.test(value);
1578
+ return /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6,8})$/i.test(value);
1548
1579
  }
1549
1580
  function isQuotedValue(value) {
1550
1581
  const quote = value[0];
@@ -1567,29 +1598,61 @@ function normalizeBareArbitraryValueOptions(options) {
1567
1598
  if (normalizedUnits.length === 0) return;
1568
1599
  return { units: normalizedUnits.sort((a, b) => b.length - a.length) };
1569
1600
  }
1601
+ function normalizeEscapedValue(value) {
1602
+ let result = "";
1603
+ for (let index = 0; index < value.length; index++) {
1604
+ const character = value[index];
1605
+ if (character !== "\\") {
1606
+ result += character;
1607
+ continue;
1608
+ }
1609
+ const nextCharacter = value[index + 1];
1610
+ if (nextCharacter === void 0) {
1611
+ result += character;
1612
+ continue;
1613
+ }
1614
+ if (HEX_ESCAPE_RE.test(nextCharacter)) {
1615
+ let hex = "";
1616
+ let nextIndex = index + 1;
1617
+ while (nextIndex < value.length && hex.length < 6) {
1618
+ const hexCharacter = value[nextIndex];
1619
+ if (hexCharacter === void 0 || !HEX_ESCAPE_RE.test(hexCharacter)) break;
1620
+ hex += hexCharacter;
1621
+ nextIndex++;
1622
+ }
1623
+ if (/[\t\n\f\r ]/.test(value[nextIndex] ?? "")) nextIndex++;
1624
+ const decoded = String.fromCodePoint(Number.parseInt(hex, 16));
1625
+ result += decoded === "_" ? "\\_" : decoded;
1626
+ index = nextIndex - 1;
1627
+ continue;
1628
+ }
1629
+ result += nextCharacter === "_" ? "\\_" : nextCharacter;
1630
+ index++;
1631
+ }
1632
+ return result;
1633
+ }
1570
1634
  function resolveValueWithUnit(body, units) {
1635
+ const value = normalizeEscapedValue(body);
1571
1636
  for (const unit of units) {
1572
- if (!body.endsWith(unit)) continue;
1573
- const numberPart = body.slice(0, -unit.length);
1637
+ if (!value.endsWith(unit)) continue;
1638
+ const numberPart = value.slice(0, -unit.length);
1574
1639
  if (NUMBER_RE.test(numberPart)) return `${numberPart}${unit}`;
1575
1640
  }
1576
1641
  }
1577
1642
  function resolveArbitraryValue(body, units) {
1578
- const withUnit = resolveValueWithUnit(body, units);
1643
+ const value = normalizeEscapedValue(body);
1644
+ const withUnit = resolveValueWithUnit(value, units);
1579
1645
  if (withUnit) return withUnit;
1580
- if (isHexColorValue(body)) return body;
1581
- if (isQuotedValue(body)) return body;
1582
- if (FUNCTION_VALUE_RE.test(body) && body.endsWith(")") && isBalancedFunctionValue(body)) return body;
1646
+ if (isHexColorValue(value)) return value;
1647
+ if (isQuotedValue(value)) return value;
1648
+ if (FUNCTION_VALUE_RE.test(value) && value.endsWith(")") && isBalancedFunctionValue(value)) return value;
1583
1649
  }
1584
1650
  function resolveUtilityAndValue(body, units) {
1585
1651
  let depth = 0;
1586
1652
  let quote;
1587
- for (let index = 1; index < body.length; index++) {
1653
+ for (let index = body.length - 1; index > 0; index--) {
1588
1654
  const character = body[index];
1589
- if (character === "\\") {
1590
- index++;
1591
- continue;
1592
- }
1655
+ if (isEscapedAt(body, index)) continue;
1593
1656
  if (quote) {
1594
1657
  if (character === quote) quote = void 0;
1595
1658
  continue;
@@ -1598,11 +1661,11 @@ function resolveUtilityAndValue(body, units) {
1598
1661
  quote = character;
1599
1662
  continue;
1600
1663
  }
1601
- if (character === "(" || character === "{") {
1664
+ if (character === ")" || character === "}") {
1602
1665
  depth++;
1603
1666
  continue;
1604
1667
  }
1605
- if (character === ")" || character === "}") {
1668
+ if (character === "(" || character === "{") {
1606
1669
  depth = Math.max(0, depth - 1);
1607
1670
  continue;
1608
1671
  }
@@ -1625,6 +1688,7 @@ function resolveBareArbitraryValueCandidate(candidate, options) {
1625
1688
  let normalizedBody = important ? body.slice(1) : body;
1626
1689
  const negative = normalizedBody.startsWith("-") ? "-" : "";
1627
1690
  if (negative) normalizedBody = normalizedBody.slice(1);
1691
+ if (!isBalancedBareArbitraryBody(normalizedBody)) return;
1628
1692
  const resolved = resolveUtilityAndValue(normalizedBody, normalizedOptions.units);
1629
1693
  if (!resolved) return;
1630
1694
  return {
@@ -1658,23 +1722,32 @@ function escapeCssClassName(value) {
1658
1722
  function resolveValidTailwindV4Candidates(designSystem, candidates, options) {
1659
1723
  const validCandidates = /* @__PURE__ */ new Set();
1660
1724
  const parsedCandidates = [];
1661
- const originalCandidateByCanonical = /* @__PURE__ */ new Map();
1725
+ const originalCandidatesByCanonical = /* @__PURE__ */ new Map();
1662
1726
  for (const candidate of candidates) {
1663
1727
  if (!candidate) continue;
1664
1728
  const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options?.bareArbitraryValues);
1665
1729
  const candidateToCheck = bareArbitrary?.canonicalCandidate ?? candidate;
1666
- if (parsedCandidates.includes(candidateToCheck)) continue;
1667
- if (designSystem.parseCandidate(candidateToCheck).length > 0) {
1668
- parsedCandidates.push(candidateToCheck);
1669
- if (bareArbitrary) originalCandidateByCanonical.set(candidateToCheck, candidate);
1730
+ if (bareArbitrary) {
1731
+ const originalCandidates = originalCandidatesByCanonical.get(candidateToCheck) ?? /* @__PURE__ */ new Set();
1732
+ originalCandidates.add(candidate);
1733
+ originalCandidatesByCanonical.set(candidateToCheck, originalCandidates);
1670
1734
  }
1735
+ if (parsedCandidates.includes(candidateToCheck)) continue;
1736
+ if (designSystem.parseCandidate(candidateToCheck).length > 0) parsedCandidates.push(candidateToCheck);
1671
1737
  }
1672
1738
  if (parsedCandidates.length === 0) return validCandidates;
1673
1739
  const cssByCandidate = designSystem.candidatesToCss(parsedCandidates);
1674
1740
  for (let index = 0; index < parsedCandidates.length; index++) {
1675
1741
  const candidate = parsedCandidates[index];
1676
1742
  const candidateCss = cssByCandidate[index];
1677
- if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) validCandidates.add(originalCandidateByCanonical.get(candidate) ?? candidate);
1743
+ if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) {
1744
+ const originalCandidates = originalCandidatesByCanonical.get(candidate);
1745
+ if (originalCandidates) {
1746
+ for (const originalCandidate of originalCandidates) validCandidates.add(originalCandidate);
1747
+ continue;
1748
+ }
1749
+ validCandidates.add(candidate);
1750
+ }
1678
1751
  }
1679
1752
  return validCandidates;
1680
1753
  }
@@ -1683,16 +1756,34 @@ function createSelectorAliasMap(candidates, options) {
1683
1756
  for (const candidate of candidates) {
1684
1757
  const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options);
1685
1758
  if (!bareArbitrary) continue;
1686
- aliases.set(escapeCssClassName(bareArbitrary.canonicalCandidate), escapeCssClassName(bareArbitrary.candidate));
1759
+ const canonicalSelector = escapeCssClassName(bareArbitrary.canonicalCandidate);
1760
+ const bareSelectors = aliases.get(canonicalSelector) ?? /* @__PURE__ */ new Set();
1761
+ bareSelectors.add(escapeCssClassName(bareArbitrary.candidate));
1762
+ aliases.set(canonicalSelector, bareSelectors);
1687
1763
  }
1688
1764
  return aliases;
1689
1765
  }
1690
1766
  function replaceBareArbitraryValueSelectors(css, candidates, options) {
1691
1767
  const aliases = createSelectorAliasMap(candidates, options);
1692
1768
  if (aliases.size === 0) return css;
1693
- let result = css;
1694
- for (const [canonicalSelector, bareSelector] of aliases) result = result.replaceAll(canonicalSelector, bareSelector);
1695
- return result;
1769
+ if (Array.from(aliases.values()).every((bareSelectors) => bareSelectors.size === 1)) {
1770
+ let result = css;
1771
+ for (const [canonicalSelector, bareSelectors] of aliases) {
1772
+ const bareSelector = Array.from(bareSelectors)[0];
1773
+ if (bareSelector !== void 0) result = result.replaceAll(canonicalSelector, bareSelector);
1774
+ }
1775
+ return result;
1776
+ }
1777
+ const root = postcss.parse(css);
1778
+ root.walkRules((rule) => {
1779
+ let selectors = rule.selectors;
1780
+ for (const [canonicalSelector, bareSelectors] of aliases) selectors = selectors.flatMap((selector) => {
1781
+ if (!selector.includes(canonicalSelector)) return selector;
1782
+ return Array.from(bareSelectors, (bareSelector) => selector.replaceAll(canonicalSelector, bareSelector));
1783
+ });
1784
+ rule.selectors = selectors;
1785
+ });
1786
+ return root.toString();
1696
1787
  }
1697
1788
  function canonicalizeBareArbitraryValueCandidates(candidates, options) {
1698
1789
  return Array.from(candidates, (candidate) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwindcss-patch",
3
- "version": "9.3.1",
3
+ "version": "9.3.2",
4
4
  "description": "patch tailwindcss for exposing context and extract classes",
5
5
  "author": "ice breaker <1324318532@qq.com>",
6
6
  "license": "MIT",
@@ -39,7 +39,8 @@ const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
39
39
  ]
40
40
 
41
41
  const NUMBER_RE = /^-?(?:\d+|\d*\.\d+)$/
42
- const FUNCTION_VALUE_RE = /^[a-zA-Z_-][a-zA-Z0-9_-]*\(/
42
+ const FUNCTION_VALUE_RE = /^[a-z_-][\w-]*\(/i
43
+ const HEX_ESCAPE_RE = /^[\da-f]$/i
43
44
 
44
45
  function splitVariantPrefix(candidate: string) {
45
46
  let depth = 0
@@ -133,8 +134,55 @@ function isBalancedFunctionValue(value: string) {
133
134
  return depth === 0 && quote === undefined
134
135
  }
135
136
 
137
+ function isEscapedAt(value: string, index: number) {
138
+ let slashCount = 0
139
+ for (let slashIndex = index - 1; slashIndex >= 0 && value[slashIndex] === '\\'; slashIndex--) {
140
+ slashCount++
141
+ }
142
+ return slashCount % 2 === 1
143
+ }
144
+
145
+ function isBalancedBareArbitraryBody(value: string) {
146
+ let depth = 0
147
+ let quote: string | undefined
148
+
149
+ for (let index = 0; index < value.length; index++) {
150
+ const character = value[index]
151
+
152
+ if (isEscapedAt(value, index)) {
153
+ continue
154
+ }
155
+
156
+ if (quote) {
157
+ if (character === quote) {
158
+ quote = undefined
159
+ }
160
+ continue
161
+ }
162
+
163
+ if (character === '"' || character === '\'') {
164
+ quote = character
165
+ continue
166
+ }
167
+
168
+ if (character === '(' || character === '{') {
169
+ depth++
170
+ continue
171
+ }
172
+
173
+ if (character === ')' || character === '}') {
174
+ depth--
175
+ if (depth < 0) {
176
+ return false
177
+ }
178
+ }
179
+ }
180
+
181
+ return depth === 0 && quote === undefined
182
+ }
183
+
136
184
  function isHexColorValue(value: string) {
137
- return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6,8})$/.test(value)
185
+ return /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6,8})$/i.test(value)
138
186
  }
139
187
 
140
188
  function isQuotedValue(value: string) {
@@ -173,12 +221,55 @@ function normalizeBareArbitraryValueOptions(options: boolean | BareArbitraryValu
173
221
  }
174
222
  }
175
223
 
224
+ function normalizeEscapedValue(value: string) {
225
+ let result = ''
226
+ for (let index = 0; index < value.length; index++) {
227
+ const character = value[index]
228
+ if (character !== '\\') {
229
+ result += character
230
+ continue
231
+ }
232
+
233
+ const nextCharacter = value[index + 1]
234
+ if (nextCharacter === undefined) {
235
+ result += character
236
+ continue
237
+ }
238
+
239
+ if (HEX_ESCAPE_RE.test(nextCharacter)) {
240
+ let hex = ''
241
+ let nextIndex = index + 1
242
+ while (nextIndex < value.length && hex.length < 6) {
243
+ const hexCharacter = value[nextIndex]
244
+ if (hexCharacter === undefined || !HEX_ESCAPE_RE.test(hexCharacter)) {
245
+ break
246
+ }
247
+ hex += hexCharacter
248
+ nextIndex++
249
+ }
250
+ if (/[\t\n\f\r ]/.test(value[nextIndex] ?? '')) {
251
+ nextIndex++
252
+ }
253
+
254
+ const decoded = String.fromCodePoint(Number.parseInt(hex, 16))
255
+ result += decoded === '_' ? '\\_' : decoded
256
+ index = nextIndex - 1
257
+ continue
258
+ }
259
+
260
+ result += nextCharacter === '_' ? '\\_' : nextCharacter
261
+ index++
262
+ }
263
+ return result
264
+ }
265
+
176
266
  function resolveValueWithUnit(body: string, units: string[]) {
267
+ const value = normalizeEscapedValue(body)
177
268
  for (const unit of units) {
178
- if (!body.endsWith(unit)) {
269
+ if (!value.endsWith(unit)) {
179
270
  continue
180
271
  }
181
- const numberPart = body.slice(0, -unit.length)
272
+ const numberPart = value.slice(0, -unit.length)
182
273
  if (NUMBER_RE.test(numberPart)) {
183
274
  return `${numberPart}${unit}`
184
275
  }
@@ -186,21 +277,22 @@ function resolveValueWithUnit(body: string, units: string[]) {
186
277
  }
187
278
 
188
279
  function resolveArbitraryValue(body: string, units: string[]) {
189
- const withUnit = resolveValueWithUnit(body, units)
280
+ const value = normalizeEscapedValue(body)
281
+ const withUnit = resolveValueWithUnit(value, units)
190
282
  if (withUnit) {
191
283
  return withUnit
192
284
  }
193
285
 
194
- if (isHexColorValue(body)) {
195
- return body
286
+ if (isHexColorValue(value)) {
287
+ return value
196
288
  }
197
289
 
198
- if (isQuotedValue(body)) {
199
- return body
290
+ if (isQuotedValue(value)) {
291
+ return value
200
292
  }
201
293
 
202
- if (FUNCTION_VALUE_RE.test(body) && body.endsWith(')') && isBalancedFunctionValue(body)) {
203
- return body
294
+ if (FUNCTION_VALUE_RE.test(value) && value.endsWith(')') && isBalancedFunctionValue(value)) {
295
+ return value
204
296
  }
205
297
  }
206
298
 
@@ -208,11 +300,10 @@ function resolveUtilityAndValue(body: string, units: string[]) {
208
300
  let depth = 0
209
301
  let quote: string | undefined
210
302
 
211
- for (let index = 1; index < body.length; index++) {
303
+ for (let index = body.length - 1; index > 0; index--) {
212
304
  const character = body[index]
213
305
 
214
- if (character === '\\') {
215
- index++
306
+ if (isEscapedAt(body, index)) {
216
307
  continue
217
308
  }
218
309
 
@@ -228,12 +319,12 @@ function resolveUtilityAndValue(body: string, units: string[]) {
228
319
  continue
229
320
  }
230
321
 
231
- if (character === '(' || character === '{') {
322
+ if (character === ')' || character === '}') {
232
323
  depth++
233
324
  continue
234
325
  }
235
326
 
236
- if (character === ')' || character === '}') {
327
+ if (character === '(' || character === '{') {
237
328
  depth = Math.max(0, depth - 1)
238
329
  continue
239
330
  }
@@ -274,6 +365,9 @@ export function resolveBareArbitraryValueCandidate(
274
365
  if (negative) {
275
366
  normalizedBody = normalizedBody.slice(1)
276
367
  }
368
+ if (!isBalancedBareArbitraryBody(normalizedBody)) {
369
+ return
370
+ }
277
371
 
278
372
  const resolved = resolveUtilityAndValue(normalizedBody, normalizedOptions.units)
279
373
  if (!resolved) {
@@ -1,10 +1,7 @@
1
+ import type { BareArbitraryValueOptions } from './bare-arbitrary-values'
1
2
  import type { TailwindV4DesignSystem } from './types'
2
3
  import postcss from 'postcss'
3
- import {
4
- escapeCssClassName,
5
- type BareArbitraryValueOptions,
6
- resolveBareArbitraryValueCandidate,
7
- } from './bare-arbitrary-values'
4
+ import { escapeCssClassName, resolveBareArbitraryValueCandidate } from './bare-arbitrary-values'
8
5
 
9
6
  export function resolveValidTailwindV4Candidates(
10
7
  designSystem: TailwindV4DesignSystem,
@@ -15,7 +12,7 @@ export function resolveValidTailwindV4Candidates(
15
12
  ): Set<string> {
16
13
  const validCandidates = new Set<string>()
17
14
  const parsedCandidates: string[] = []
18
- const originalCandidateByCanonical = new Map<string, string>()
15
+ const originalCandidatesByCanonical = new Map<string, Set<string>>()
19
16
 
20
17
  for (const candidate of candidates) {
21
18
  if (!candidate) {
@@ -25,15 +22,19 @@ export function resolveValidTailwindV4Candidates(
25
22
  const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options?.bareArbitraryValues)
26
23
  const candidateToCheck = bareArbitrary?.canonicalCandidate ?? candidate
27
24
 
28
- if (parsedCandidates.includes(candidateToCheck)) {
25
+ if (bareArbitrary) {
26
+ const originalCandidates = originalCandidatesByCanonical.get(candidateToCheck) ?? new Set<string>()
27
+ originalCandidates.add(candidate)
28
+ originalCandidatesByCanonical.set(candidateToCheck, originalCandidates)
29
+ }
30
+
31
+ const alreadyParsed = parsedCandidates.includes(candidateToCheck)
32
+ if (alreadyParsed) {
29
33
  continue
30
34
  }
31
35
 
32
36
  if (designSystem.parseCandidate(candidateToCheck).length > 0) {
33
37
  parsedCandidates.push(candidateToCheck)
34
- if (bareArbitrary) {
35
- originalCandidateByCanonical.set(candidateToCheck, candidate)
36
- }
37
38
  }
38
39
  }
39
40
 
@@ -46,7 +47,14 @@ export function resolveValidTailwindV4Candidates(
46
47
  const candidate = parsedCandidates[index]
47
48
  const candidateCss = cssByCandidate[index]
48
49
  if (candidate && typeof candidateCss === 'string' && candidateCss.trim().length > 0) {
49
- validCandidates.add(originalCandidateByCanonical.get(candidate) ?? candidate)
50
+ const originalCandidates = originalCandidatesByCanonical.get(candidate)
51
+ if (originalCandidates) {
52
+ for (const originalCandidate of originalCandidates) {
53
+ validCandidates.add(originalCandidate)
54
+ }
55
+ continue
56
+ }
57
+ validCandidates.add(candidate)
50
58
  }
51
59
  }
52
60
 
@@ -57,16 +65,16 @@ function createSelectorAliasMap(
57
65
  candidates: Iterable<string>,
58
66
  options?: boolean | BareArbitraryValueOptions,
59
67
  ) {
60
- const aliases = new Map<string, string>()
68
+ const aliases = new Map<string, Set<string>>()
61
69
  for (const candidate of candidates) {
62
70
  const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options)
63
71
  if (!bareArbitrary) {
64
72
  continue
65
73
  }
66
- aliases.set(
67
- escapeCssClassName(bareArbitrary.canonicalCandidate),
68
- escapeCssClassName(bareArbitrary.candidate),
69
- )
74
+ const canonicalSelector = escapeCssClassName(bareArbitrary.canonicalCandidate)
75
+ const bareSelectors = aliases.get(canonicalSelector) ?? new Set<string>()
76
+ bareSelectors.add(escapeCssClassName(bareArbitrary.candidate))
77
+ aliases.set(canonicalSelector, bareSelectors)
70
78
  }
71
79
  return aliases
72
80
  }
@@ -81,11 +89,31 @@ export function replaceBareArbitraryValueSelectors(
81
89
  return css
82
90
  }
83
91
 
84
- let result = css
85
- for (const [canonicalSelector, bareSelector] of aliases) {
86
- result = result.replaceAll(canonicalSelector, bareSelector)
92
+ if (Array.from(aliases.values()).every(bareSelectors => bareSelectors.size === 1)) {
93
+ let result = css
94
+ for (const [canonicalSelector, bareSelectors] of aliases) {
95
+ const bareSelector = Array.from(bareSelectors)[0]
96
+ if (bareSelector !== undefined) {
97
+ result = result.replaceAll(canonicalSelector, bareSelector)
98
+ }
99
+ }
100
+ return result
87
101
  }
88
- return result
102
+
103
+ const root = postcss.parse(css)
104
+ root.walkRules((rule) => {
105
+ let selectors = rule.selectors
106
+ for (const [canonicalSelector, bareSelectors] of aliases) {
107
+ selectors = selectors.flatMap((selector) => {
108
+ if (!selector.includes(canonicalSelector)) {
109
+ return selector
110
+ }
111
+ return Array.from(bareSelectors, bareSelector => selector.replaceAll(canonicalSelector, bareSelector))
112
+ })
113
+ }
114
+ rule.selectors = selectors
115
+ })
116
+ return root.toString()
89
117
  }
90
118
 
91
119
  export function canonicalizeBareArbitraryValueCandidates(