tailwindcss-patch 9.4.4 → 9.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.
Files changed (36) hide show
  1. package/dist/{cli-srv0kRlt.js → cli-CGyUnvFc.js} +3 -2
  2. package/dist/{cli-CLvyx3xl.mjs → cli-DRfALTSo.mjs} +1 -1
  3. package/dist/cli.js +2 -2
  4. package/dist/cli.mjs +2 -2
  5. package/dist/commands/cli-runtime.d.mts +1 -1
  6. package/dist/commands/cli-runtime.d.ts +1 -1
  7. package/dist/commands/cli-runtime.js +2 -2
  8. package/dist/commands/cli-runtime.mjs +2 -2
  9. package/dist/{dist-Dn7cMVhi.js → dist-DlC5vuI2.js} +1 -1
  10. package/dist/index.d.mts +7 -149
  11. package/dist/index.d.ts +8 -150
  12. package/dist/index.js +294 -521
  13. package/dist/index.mjs +6 -471
  14. package/dist/{validate-oAkURzUC.d.mts → validate-B5-08lrU.d.ts} +8 -252
  15. package/dist/{validate-BuqRodYI.d.ts → validate-CgrG4aAY.d.mts} +8 -252
  16. package/dist/{validate-CUNJFfHh.mjs → validate-Q00Ccqht.mjs} +5 -1402
  17. package/dist/{validate-RpdgpjgT.js → validate-XiYmTZcd.js} +79 -1705
  18. package/package.json +4 -6
  19. package/src/api/tailwindcss-patcher.ts +3 -2
  20. package/src/extraction/candidate-extractor.ts +17 -701
  21. package/src/extraction/split-candidate-tokens.ts +5 -101
  22. package/src/options/types.ts +1 -2
  23. package/src/style-candidates.ts +5 -35
  24. package/src/style-generator.ts +11 -80
  25. package/src/types.ts +21 -95
  26. package/src/v3/index.ts +2 -2
  27. package/src/v4/index.ts +104 -28
  28. package/src/v3/style-generator.ts +0 -384
  29. package/src/v4/bare-arbitrary-values.ts +0 -545
  30. package/src/v4/candidates.ts +0 -316
  31. package/src/v4/engine.ts +0 -112
  32. package/src/v4/node-adapter.ts +0 -207
  33. package/src/v4/source-scan.ts +0 -432
  34. package/src/v4/source.ts +0 -235
  35. package/src/v4/style-generator.ts +0 -44
  36. package/src/v4/types.ts +0 -103
@@ -8,17 +8,15 @@ import { createHash } from "node:crypto";
8
8
  import { createConsola } from "consola";
9
9
  import path$1 from "node:path";
10
10
  import { fileURLToPath, pathToFileURL } from "node:url";
11
- import { promises, realpathSync } from "node:fs";
12
- import postcss from "postcss";
13
- import { stat } from "node:fs/promises";
14
- import micromatch from "micromatch";
11
+ import { extractProjectCandidatesWithPositions, extractRawCandidates, extractRawCandidatesWithPositions, extractSourceCandidates, extractSourceCandidatesWithPositions, extractValidCandidates, groupTokensByFile, resolveProjectSourceFiles } from "@tailwindcss-mangle/engine";
15
12
  import * as t from "@babel/types";
16
13
  import generate from "@babel/generator";
17
14
  import _babelTraverse from "@babel/traverse";
18
15
  import { parse, parse as parse$1 } from "@babel/parser";
16
+ import postcss from "postcss";
19
17
  import { loadConfig } from "tailwindcss-config";
20
18
  //#region package.json
21
- var version = "9.4.4";
19
+ var version = "9.5.0";
22
20
  //#endregion
23
21
  //#region src/constants.ts
24
22
  const pkgName = "tailwindcss-patch";
@@ -1412,7 +1410,7 @@ function normalizeOptions(options = {}) {
1412
1410
  };
1413
1411
  }
1414
1412
  //#endregion
1415
- //#region ../../node_modules/.pnpm/tsdown@0.22.2_tsx@4.22.4_typescript@6.0.3_unrun@0.2.37_synckit@0.11.13_/node_modules/tsdown/esm-shims.js
1413
+ //#region ../../node_modules/.pnpm/tsdown@0.22.3_tsx@4.22.4_typescript@6.0.3_unrun@0.2.37_synckit@0.11.13_/node_modules/tsdown/esm-shims.js
1416
1414
  const getFilename = () => fileURLToPath(import.meta.url);
1417
1415
  const getDirname = () => path$1.dirname(getFilename());
1418
1416
  const __dirname = /* @__PURE__ */ getDirname();
@@ -1458,1401 +1456,6 @@ async function loadPatchOptionsForWorkspace(cwd, overrides) {
1458
1456
  return merge(overrides ?? {}, base, { projectRoot: cwd });
1459
1457
  }
1460
1458
  //#endregion
1461
- //#region src/v4/bare-arbitrary-values.ts
1462
- const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
1463
- "%",
1464
- "px",
1465
- "rpx",
1466
- "rem",
1467
- "em",
1468
- "vw",
1469
- "vh",
1470
- "vmin",
1471
- "vmax",
1472
- "dvw",
1473
- "dvh",
1474
- "svw",
1475
- "svh",
1476
- "lvw",
1477
- "lvh",
1478
- "ch",
1479
- "ex",
1480
- "lh",
1481
- "rlh",
1482
- "fr",
1483
- "deg",
1484
- "rad",
1485
- "turn",
1486
- "s",
1487
- "ms"
1488
- ];
1489
- const NUMBER_RE = /^-?(?:\d+|\d*\.\d+)$/;
1490
- const FUNCTION_VALUE_RE = /^[a-z_-][\w-]*\(/i;
1491
- const HEX_ESCAPE_RE = /^[\da-f]$/i;
1492
- const ASPECT_RATIO_RE = /^\d+\/\d+$/;
1493
- const ESCAPED_WHITESPACE_RE = /\\[nrt]/g;
1494
- function splitVariantPrefix(candidate) {
1495
- let depth = 0;
1496
- let quote;
1497
- let lastSeparator = -1;
1498
- for (let index = 0; index < candidate.length; index++) {
1499
- const character = candidate[index];
1500
- if (character === "\\") {
1501
- index++;
1502
- continue;
1503
- }
1504
- if (quote) {
1505
- if (character === quote) quote = void 0;
1506
- continue;
1507
- }
1508
- if (character === "\"" || character === "'") {
1509
- quote = character;
1510
- continue;
1511
- }
1512
- if (character === "[" || character === "(" || character === "{") {
1513
- depth++;
1514
- continue;
1515
- }
1516
- if (character === "]" || character === ")" || character === "}") {
1517
- depth = Math.max(0, depth - 1);
1518
- continue;
1519
- }
1520
- if (depth === 0 && character === ":") lastSeparator = index;
1521
- }
1522
- if (lastSeparator === -1) return {
1523
- prefix: "",
1524
- body: candidate
1525
- };
1526
- return {
1527
- prefix: candidate.slice(0, lastSeparator + 1),
1528
- body: candidate.slice(lastSeparator + 1)
1529
- };
1530
- }
1531
- function isBalancedFunctionValue(value) {
1532
- let depth = 0;
1533
- let quote;
1534
- for (let index = 0; index < value.length; index++) {
1535
- const character = value[index];
1536
- if (character === "\\") {
1537
- index++;
1538
- continue;
1539
- }
1540
- if (quote) {
1541
- if (character === quote) quote = void 0;
1542
- continue;
1543
- }
1544
- if (character === "\"" || character === "'") {
1545
- quote = character;
1546
- continue;
1547
- }
1548
- if (character === "(") {
1549
- depth++;
1550
- continue;
1551
- }
1552
- if (character === ")") {
1553
- depth--;
1554
- if (depth < 0) return false;
1555
- }
1556
- }
1557
- return depth === 0 && quote === void 0;
1558
- }
1559
- function isEscapedAt(value, index) {
1560
- let slashCount = 0;
1561
- for (let slashIndex = index - 1; slashIndex >= 0 && value[slashIndex] === "\\"; slashIndex--) slashCount++;
1562
- return slashCount % 2 === 1;
1563
- }
1564
- function isBalancedBareArbitraryBody(value) {
1565
- let depth = 0;
1566
- let quote;
1567
- for (let index = 0; index < value.length; index++) {
1568
- const character = value[index];
1569
- if (isEscapedAt(value, index)) continue;
1570
- if (quote) {
1571
- if (character === quote) quote = void 0;
1572
- continue;
1573
- }
1574
- if (character === "\"" || character === "'") {
1575
- quote = character;
1576
- continue;
1577
- }
1578
- if (character === "(" || character === "{") {
1579
- depth++;
1580
- continue;
1581
- }
1582
- if (character === ")" || character === "}") {
1583
- depth--;
1584
- if (depth < 0) return false;
1585
- }
1586
- }
1587
- return depth === 0 && quote === void 0;
1588
- }
1589
- function isHexColorValue(value) {
1590
- return /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6,8})$/i.test(value);
1591
- }
1592
- function isQuotedValue(value) {
1593
- const quote = value[0];
1594
- if (quote !== "\"" && quote !== "'" || value[value.length - 1] !== quote) return false;
1595
- let escaped = false;
1596
- for (let index = 1; index < value.length - 1; index++) {
1597
- const character = value[index];
1598
- if (escaped) {
1599
- escaped = false;
1600
- continue;
1601
- }
1602
- if (character === "\\") escaped = true;
1603
- }
1604
- return !escaped;
1605
- }
1606
- function normalizeBareArbitraryValueOptions(options) {
1607
- if (options === false || options === void 0 || options === null) return;
1608
- const units = options === true ? DEFAULT_BARE_ARBITRARY_VALUE_UNITS : options.units ?? DEFAULT_BARE_ARBITRARY_VALUE_UNITS;
1609
- const normalizedUnits = [...new Set(units.filter((unit) => typeof unit === "string" && unit.length > 0))];
1610
- if (normalizedUnits.length === 0) return;
1611
- return { units: normalizedUnits.sort((a, b) => b.length - a.length) };
1612
- }
1613
- function isBareArbitraryValuesEnabled(options) {
1614
- return normalizeBareArbitraryValueOptions(options) !== void 0;
1615
- }
1616
- function normalizeEscapedValue(value) {
1617
- let result = "";
1618
- for (let index = 0; index < value.length; index++) {
1619
- const character = value[index];
1620
- if (character !== "\\") {
1621
- result += character;
1622
- continue;
1623
- }
1624
- const nextCharacter = value[index + 1];
1625
- if (nextCharacter === void 0) {
1626
- result += character;
1627
- continue;
1628
- }
1629
- if (HEX_ESCAPE_RE.test(nextCharacter)) {
1630
- let hex = "";
1631
- let nextIndex = index + 1;
1632
- while (nextIndex < value.length && hex.length < 6) {
1633
- const hexCharacter = value[nextIndex];
1634
- if (hexCharacter === void 0 || !HEX_ESCAPE_RE.test(hexCharacter)) break;
1635
- hex += hexCharacter;
1636
- nextIndex++;
1637
- }
1638
- if (/[\t\n\f\r ]/.test(value[nextIndex] ?? "")) nextIndex++;
1639
- const decoded = String.fromCodePoint(Number.parseInt(hex, 16));
1640
- result += decoded === "_" ? "\\_" : decoded;
1641
- index = nextIndex - 1;
1642
- continue;
1643
- }
1644
- result += nextCharacter === "_" ? "\\_" : nextCharacter;
1645
- index++;
1646
- }
1647
- return result;
1648
- }
1649
- function resolveValueWithUnit(body, units) {
1650
- const value = normalizeEscapedValue(body);
1651
- for (const unit of units) {
1652
- if (!value.endsWith(unit)) continue;
1653
- const numberPart = value.slice(0, -unit.length);
1654
- if (NUMBER_RE.test(numberPart)) return `${numberPart}${unit}`;
1655
- }
1656
- }
1657
- function resolveArbitraryValue(utility, body, units) {
1658
- const value = normalizeEscapedValue(body);
1659
- const withUnit = resolveValueWithUnit(value, units);
1660
- if (withUnit) return withUnit;
1661
- if (utility === "aspect" && ASPECT_RATIO_RE.test(value)) return value;
1662
- if (isHexColorValue(value)) return value;
1663
- if (isQuotedValue(value)) return value;
1664
- if (FUNCTION_VALUE_RE.test(value) && value.endsWith(")") && isBalancedFunctionValue(value)) {
1665
- if (utility === "text" && /^var\(/i.test(value)) return `color:${value}`;
1666
- return value;
1667
- }
1668
- }
1669
- function resolveUtilityAndValue(body, units) {
1670
- let depth = 0;
1671
- let quote;
1672
- for (let index = body.length - 1; index > 0; index--) {
1673
- const character = body[index];
1674
- if (isEscapedAt(body, index)) continue;
1675
- if (quote) {
1676
- if (character === quote) quote = void 0;
1677
- continue;
1678
- }
1679
- if (character === "\"" || character === "'") {
1680
- quote = character;
1681
- continue;
1682
- }
1683
- if (character === ")" || character === "}") {
1684
- depth++;
1685
- continue;
1686
- }
1687
- if (character === "(" || character === "{") {
1688
- depth = Math.max(0, depth - 1);
1689
- continue;
1690
- }
1691
- if (depth > 0 || character !== "-") continue;
1692
- const utility = body.slice(0, index);
1693
- const rawValue = body.slice(index + 1);
1694
- if (!utility || !rawValue) continue;
1695
- const value = resolveArbitraryValue(utility, rawValue, units);
1696
- if (value) return {
1697
- utility,
1698
- value
1699
- };
1700
- }
1701
- }
1702
- function resolveBareArbitraryValueCandidate(candidate, options) {
1703
- const normalizedOptions = normalizeBareArbitraryValueOptions(options);
1704
- if (!normalizedOptions || !candidate || candidate.includes("[") || candidate.includes("]")) return;
1705
- const { prefix, body } = splitVariantPrefix(candidate);
1706
- const important = body.startsWith("!") ? "!" : "";
1707
- let normalizedBody = important ? body.slice(1) : body;
1708
- const negative = normalizedBody.startsWith("-") ? "-" : "";
1709
- if (negative) normalizedBody = normalizedBody.slice(1);
1710
- if (!isBalancedBareArbitraryBody(normalizedBody)) return;
1711
- const resolved = resolveUtilityAndValue(normalizedBody, normalizedOptions.units);
1712
- if (!resolved) return;
1713
- return {
1714
- candidate,
1715
- canonicalCandidate: `${prefix}${important}${negative}${resolved.utility}-[${resolved.value}]`
1716
- };
1717
- }
1718
- function isBareArbitrarySourceSplitter(char) {
1719
- return /\s/.test(char);
1720
- }
1721
- function isQuoteBoundary(content, start, index) {
1722
- const tokenPrefix = content.slice(start, index);
1723
- return tokenPrefix.length === 0 || !tokenPrefix.endsWith("-");
1724
- }
1725
- function trimBareArbitrarySourceToken(token, start) {
1726
- let nextToken = token;
1727
- let nextStart = start;
1728
- while (nextToken.length > 0 && /^[<{([]$/.test(nextToken[0])) {
1729
- nextToken = nextToken.slice(1);
1730
- nextStart++;
1731
- }
1732
- while (nextToken.length > 0 && /^[>\],;]$/.test(nextToken[nextToken.length - 1])) nextToken = nextToken.slice(0, -1);
1733
- return {
1734
- token: nextToken,
1735
- start: nextStart
1736
- };
1737
- }
1738
- function pushBareArbitrarySourceCandidate(result, token, start, options) {
1739
- const trimmed = trimBareArbitrarySourceToken(token, start);
1740
- if (!trimmed.token || trimmed.token.includes("=") || trimmed.token.includes("[") || trimmed.token.includes("]")) return;
1741
- if (!resolveBareArbitraryValueCandidate(trimmed.token, options)) return;
1742
- result.push({
1743
- rawCandidate: trimmed.token,
1744
- start: trimmed.start,
1745
- end: trimmed.start + trimmed.token.length
1746
- });
1747
- }
1748
- function extractBareArbitraryValueSourceCandidatesWithPositions(content, options) {
1749
- if (!isBareArbitraryValuesEnabled(options)) return [];
1750
- const normalized = content.includes("\\") ? content.replace(ESCAPED_WHITESPACE_RE, " ") : content;
1751
- const result = [];
1752
- let depth = 0;
1753
- let quote;
1754
- let start = 0;
1755
- for (let index = 0; index < normalized.length; index++) {
1756
- const char = normalized[index];
1757
- if (char === void 0) continue;
1758
- if (char === "\\") {
1759
- index++;
1760
- continue;
1761
- }
1762
- if (quote) {
1763
- if (char === quote) quote = void 0;
1764
- } else if ((char === "\"" || char === "'" || char === "`") && !isQuoteBoundary(normalized, start, index)) quote = char;
1765
- else if (char === "(" || char === "{" || char === "[") depth++;
1766
- else if (char === ")" || char === "}" || char === "]") depth = Math.max(0, depth - 1);
1767
- if (!isBareArbitrarySourceSplitter(char) && !((char === "\"" || char === "'" || char === "`") && depth === 0 && isQuoteBoundary(normalized, start, index))) continue;
1768
- pushBareArbitrarySourceCandidate(result, normalized.slice(start, index), start, options);
1769
- start = index + 1;
1770
- }
1771
- pushBareArbitrarySourceCandidate(result, normalized.slice(start), start, options);
1772
- return result;
1773
- }
1774
- function extractBareArbitraryValueSourceCandidates(content, options) {
1775
- return [...new Set(extractBareArbitraryValueSourceCandidatesWithPositions(content, options).map((candidate) => candidate.rawCandidate))];
1776
- }
1777
- function escapeCssClassName(value) {
1778
- let result = "";
1779
- for (let index = 0; index < value.length; index++) {
1780
- const codeUnit = value.charCodeAt(index);
1781
- const character = value.charAt(index);
1782
- if (codeUnit === 0) {
1783
- result += "�";
1784
- continue;
1785
- }
1786
- if (codeUnit >= 1 && codeUnit <= 31 || codeUnit === 127 || index === 0 && codeUnit >= 48 && codeUnit <= 57 || index === 1 && codeUnit >= 48 && codeUnit <= 57 && value.charCodeAt(0) === 45) {
1787
- result += `\\${codeUnit.toString(16)} `;
1788
- continue;
1789
- }
1790
- if (codeUnit >= 128 || codeUnit === 45 || codeUnit === 95 || codeUnit >= 48 && codeUnit <= 57 || codeUnit >= 65 && codeUnit <= 90 || codeUnit >= 97 && codeUnit <= 122) {
1791
- result += character;
1792
- continue;
1793
- }
1794
- result += `\\${character}`;
1795
- }
1796
- return result;
1797
- }
1798
- //#endregion
1799
- //#region src/v4/candidates.ts
1800
- function resolveValidTailwindV4Candidates(designSystem, candidates, options) {
1801
- const validCandidates = /* @__PURE__ */ new Set();
1802
- const parsedCandidates = [];
1803
- const originalCandidatesByCanonical = /* @__PURE__ */ new Map();
1804
- for (const candidate of candidates) {
1805
- if (!candidate) continue;
1806
- const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options?.bareArbitraryValues);
1807
- const candidateToCheck = bareArbitrary?.canonicalCandidate ?? candidate;
1808
- if (bareArbitrary) {
1809
- const originalCandidates = originalCandidatesByCanonical.get(candidateToCheck) ?? /* @__PURE__ */ new Set();
1810
- originalCandidates.add(candidate);
1811
- originalCandidatesByCanonical.set(candidateToCheck, originalCandidates);
1812
- }
1813
- if (parsedCandidates.includes(candidateToCheck)) continue;
1814
- if (designSystem.parseCandidate(candidateToCheck).length > 0) parsedCandidates.push(candidateToCheck);
1815
- }
1816
- if (parsedCandidates.length === 0) return validCandidates;
1817
- const cssByCandidate = designSystem.candidatesToCss(parsedCandidates);
1818
- for (let index = 0; index < parsedCandidates.length; index++) {
1819
- const candidate = parsedCandidates[index];
1820
- const candidateCss = cssByCandidate[index];
1821
- if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) {
1822
- const originalCandidates = originalCandidatesByCanonical.get(candidate);
1823
- if (originalCandidates) {
1824
- for (const originalCandidate of originalCandidates) validCandidates.add(originalCandidate);
1825
- continue;
1826
- }
1827
- validCandidates.add(candidate);
1828
- }
1829
- }
1830
- return validCandidates;
1831
- }
1832
- function createSelectorAliasMap(candidates, options) {
1833
- const aliases = /* @__PURE__ */ new Map();
1834
- for (const candidate of candidates) {
1835
- const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options);
1836
- if (!bareArbitrary) continue;
1837
- const canonicalSelector = escapeCssClassName(bareArbitrary.canonicalCandidate);
1838
- const bareSelectors = aliases.get(canonicalSelector) ?? /* @__PURE__ */ new Set();
1839
- bareSelectors.add(escapeCssClassName(bareArbitrary.candidate));
1840
- aliases.set(canonicalSelector, bareSelectors);
1841
- }
1842
- return aliases;
1843
- }
1844
- function replaceBareArbitraryValueSelectors(css, candidates, options) {
1845
- const aliases = createSelectorAliasMap(candidates, options);
1846
- if (aliases.size === 0) return css;
1847
- if (Array.from(aliases.values()).every((bareSelectors) => bareSelectors.size === 1)) {
1848
- let result = css;
1849
- for (const [canonicalSelector, bareSelectors] of aliases) {
1850
- const bareSelector = Array.from(bareSelectors)[0];
1851
- if (bareSelector !== void 0) result = result.replaceAll(canonicalSelector, bareSelector);
1852
- }
1853
- return result;
1854
- }
1855
- const root = postcss.parse(css);
1856
- root.walkRules((rule) => {
1857
- let selectors = rule.selectors;
1858
- for (const [canonicalSelector, bareSelectors] of aliases) selectors = selectors.flatMap((selector) => {
1859
- if (!selector.includes(canonicalSelector)) return selector;
1860
- return Array.from(bareSelectors, (bareSelector) => selector.replaceAll(canonicalSelector, bareSelector));
1861
- });
1862
- rule.selectors = selectors;
1863
- });
1864
- return root.toString();
1865
- }
1866
- function canonicalizeBareArbitraryValueCandidates(candidates, options) {
1867
- return Array.from(candidates, (candidate) => {
1868
- return resolveBareArbitraryValueCandidate(candidate, options)?.canonicalCandidate ?? candidate;
1869
- });
1870
- }
1871
- function splitTopLevel(value, separator, options) {
1872
- const result = [];
1873
- let start = 0;
1874
- let depth = 0;
1875
- let quote;
1876
- for (let index = 0; index < value.length; index++) {
1877
- const character = value[index];
1878
- if (character === "\\") {
1879
- index++;
1880
- continue;
1881
- }
1882
- if (quote) {
1883
- if (character === quote) quote = void 0;
1884
- continue;
1885
- }
1886
- if (character === "\"" || character === "'") {
1887
- quote = character;
1888
- continue;
1889
- }
1890
- if (character === "(" || character === "[" || character === "{") {
1891
- depth++;
1892
- continue;
1893
- }
1894
- if (character === ")" || character === "]" || character === "}") {
1895
- depth = Math.max(0, depth - 1);
1896
- continue;
1897
- }
1898
- if (depth === 0 && character === separator) {
1899
- const item = value.slice(start, index).trim();
1900
- if (item || options?.keepEmpty) result.push(item);
1901
- start = index + 1;
1902
- }
1903
- }
1904
- const item = value.slice(start).trim();
1905
- if (item || options?.keepEmpty) result.push(item);
1906
- return result;
1907
- }
1908
- const sequencePattern = /^(-?\d+)\.\.(-?\d+)(?:\.\.(-?\d+))?$/;
1909
- function expandSequence(value) {
1910
- const match = value.match(sequencePattern);
1911
- if (!match) return [value];
1912
- const [, startValue, endValue, stepValue] = match;
1913
- if (startValue === void 0 || endValue === void 0) return [value];
1914
- const start = Number.parseInt(startValue, 10);
1915
- const end = Number.parseInt(endValue, 10);
1916
- let step = stepValue === void 0 ? start <= end ? 1 : -1 : Number.parseInt(stepValue, 10);
1917
- if (step === 0) throw new Error("Step cannot be zero in Tailwind CSS v4 inline source sequence.");
1918
- const ascending = start < end;
1919
- if (ascending && step < 0) step = -step;
1920
- if (!ascending && step > 0) step = -step;
1921
- const result = [];
1922
- for (let current = start; ascending ? current <= end : current >= end; current += step) result.push(current.toString());
1923
- return result;
1924
- }
1925
- function expandInlinePattern(pattern) {
1926
- const openIndex = pattern.indexOf("{");
1927
- if (openIndex === -1) return [pattern];
1928
- const prefix = pattern.slice(0, openIndex);
1929
- const rest = pattern.slice(openIndex);
1930
- let depth = 0;
1931
- let closeIndex = -1;
1932
- for (let index = 0; index < rest.length; index++) {
1933
- const character = rest[index];
1934
- if (character === "{") depth++;
1935
- else if (character === "}") {
1936
- depth--;
1937
- if (depth === 0) {
1938
- closeIndex = index;
1939
- break;
1940
- }
1941
- }
1942
- }
1943
- if (closeIndex === -1) throw new Error(`The Tailwind CSS v4 inline source pattern "${pattern}" is not balanced.`);
1944
- const body = rest.slice(1, closeIndex);
1945
- const suffix = rest.slice(closeIndex + 1);
1946
- const parts = sequencePattern.test(body) ? expandSequence(body) : splitTopLevel(body, ",", { keepEmpty: true }).flatMap((part) => expandInlinePattern(part));
1947
- const suffixes = expandInlinePattern(suffix);
1948
- const result = [];
1949
- for (const part of parts) for (const expandedSuffix of suffixes) result.push(`${prefix}${part}${expandedSuffix}`);
1950
- return result;
1951
- }
1952
- function unquoteCssString(value) {
1953
- const quote = value[0];
1954
- if (quote !== "\"" && quote !== "'" || value[value.length - 1] !== quote) return;
1955
- let result = "";
1956
- for (let index = 1; index < value.length - 1; index++) {
1957
- const character = value[index];
1958
- if (character === "\\") {
1959
- index++;
1960
- result += value[index] ?? "";
1961
- continue;
1962
- }
1963
- result += character;
1964
- }
1965
- return result;
1966
- }
1967
- function extractTailwindV4InlineSourceCandidates(css) {
1968
- const included = /* @__PURE__ */ new Set();
1969
- const excluded = /* @__PURE__ */ new Set();
1970
- postcss.parse(css).walkAtRules("source", (rule) => {
1971
- let params = rule.params.trim();
1972
- if (!params) return;
1973
- let negated = false;
1974
- if (params.startsWith("not ")) {
1975
- negated = true;
1976
- params = params.slice(4).trim();
1977
- }
1978
- if (!params.startsWith("inline(") || !params.endsWith(")")) return;
1979
- const inlineValue = unquoteCssString(params.slice(7, -1).trim());
1980
- if (inlineValue === void 0) return;
1981
- const target = negated ? excluded : included;
1982
- for (const part of splitTopLevel(inlineValue, " ")) for (const candidate of expandInlinePattern(part)) target.add(candidate);
1983
- });
1984
- return {
1985
- included,
1986
- excluded
1987
- };
1988
- }
1989
- //#endregion
1990
- //#region src/v4/node-adapter.ts
1991
- const nodeModulePromiseCache = /* @__PURE__ */ new Map();
1992
- const designSystemPromiseCache = /* @__PURE__ */ new Map();
1993
- function unique(values) {
1994
- return Array.from(new Set(Array.from(values).filter(Boolean).map((value) => path.resolve(value))));
1995
- }
1996
- function createRequireBase(base) {
1997
- return path.join(base, "package.json");
1998
- }
1999
- function isRelativeSpecifier(id) {
2000
- return id.startsWith("./") || id.startsWith("../") || id === "." || id === "..";
2001
- }
2002
- function isAbsoluteSpecifier(id) {
2003
- return path.isAbsolute(id);
2004
- }
2005
- function isCssSpecifier(id) {
2006
- return path.extname(id) === ".css";
2007
- }
2008
- function createCssResolutionCandidates(id) {
2009
- if (isCssSpecifier(id)) return [id];
2010
- return [`${id}/index.css`, id];
2011
- }
2012
- function createFallbackCssResolver(baseCandidates) {
2013
- const bases = unique(baseCandidates);
2014
- return async (id) => {
2015
- if (isRelativeSpecifier(id) || isAbsoluteSpecifier(id)) return;
2016
- for (const base of bases) {
2017
- const requireFromBase = createRequire(createRequireBase(base));
2018
- for (const candidate of createCssResolutionCandidates(id)) try {
2019
- return requireFromBase.resolve(candidate);
2020
- } catch {}
2021
- }
2022
- };
2023
- }
2024
- async function importResolvedModule(resolved) {
2025
- return import(pathToFileURL(resolved).href);
2026
- }
2027
- async function importTailwindNodeFromBase(base) {
2028
- try {
2029
- return await importResolvedModule(createRequire(createRequireBase(base)).resolve("@tailwindcss/node"));
2030
- } catch {
2031
- return;
2032
- }
2033
- }
2034
- async function importFallbackTailwindNode() {
2035
- return import("@tailwindcss/node");
2036
- }
2037
- async function loadTailwindV4NodeModule(baseCandidates) {
2038
- const bases = unique(baseCandidates);
2039
- const cacheKey = JSON.stringify(bases);
2040
- const cached = nodeModulePromiseCache.get(cacheKey);
2041
- if (cached) return cached;
2042
- const promise = (async () => {
2043
- for (const base of bases) {
2044
- const loaded = await importTailwindNodeFromBase(base);
2045
- if (loaded) return loaded;
2046
- }
2047
- return importFallbackTailwindNode();
2048
- })();
2049
- nodeModulePromiseCache.set(cacheKey, promise);
2050
- promise.catch(() => {
2051
- if (nodeModulePromiseCache.get(cacheKey) === promise) nodeModulePromiseCache.delete(cacheKey);
2052
- });
2053
- return promise;
2054
- }
2055
- function createDesignSystemCacheKey(css, bases) {
2056
- return JSON.stringify({
2057
- css,
2058
- bases: unique(bases)
2059
- });
2060
- }
2061
- function getTailwindV4DesignSystemCacheKey(source) {
2062
- return createDesignSystemCacheKey(source.css, [source.base, ...source.baseFallbacks]);
2063
- }
2064
- async function loadTailwindV4DesignSystem(source) {
2065
- const bases = unique([source.base, ...source.baseFallbacks]);
2066
- if (bases.length === 0) throw new Error("No base directories provided for Tailwind CSS v4 design system.");
2067
- const cacheKey = createDesignSystemCacheKey(source.css, bases);
2068
- const cached = designSystemPromiseCache.get(cacheKey);
2069
- if (cached) return cached;
2070
- const promise = (async () => {
2071
- const node = await loadTailwindV4NodeModule([source.projectRoot, ...bases]);
2072
- let lastError;
2073
- for (const base of bases) try {
2074
- return await node.__unstable__loadDesignSystem(source.css, { base });
2075
- } catch (error) {
2076
- lastError = error;
2077
- }
2078
- if (lastError instanceof Error) throw lastError;
2079
- throw new Error("Failed to load Tailwind CSS v4 design system.");
2080
- })();
2081
- designSystemPromiseCache.set(cacheKey, promise);
2082
- promise.catch(() => {
2083
- if (designSystemPromiseCache.get(cacheKey) === promise) designSystemPromiseCache.delete(cacheKey);
2084
- });
2085
- return promise;
2086
- }
2087
- async function compileTailwindV4Source(source) {
2088
- const bases = unique([source.base, ...source.baseFallbacks]);
2089
- if (bases.length === 0) throw new Error("No base directories provided for Tailwind CSS v4 compiler.");
2090
- const node = await loadTailwindV4NodeModule([source.projectRoot, ...bases]);
2091
- let lastError;
2092
- for (const base of bases) {
2093
- const dependencies = new Set(source.dependencies);
2094
- try {
2095
- return {
2096
- compiled: await node.compile(source.css, {
2097
- base,
2098
- customCssResolver: createFallbackCssResolver([source.projectRoot, ...bases]),
2099
- onDependency(dependency) {
2100
- dependencies.add(path.resolve(dependency));
2101
- }
2102
- }),
2103
- dependencies
2104
- };
2105
- } catch (error) {
2106
- lastError = error;
2107
- }
2108
- }
2109
- if (lastError instanceof Error) throw lastError;
2110
- throw new Error("Failed to compile Tailwind CSS v4 source.");
2111
- }
2112
- //#endregion
2113
- //#region src/v4/source-scan.ts
2114
- const TAILWIND_V4_IGNORED_CONTENT_DIRS = [
2115
- ".git",
2116
- ".hg",
2117
- ".jj",
2118
- ".next",
2119
- ".parcel-cache",
2120
- ".pnpm-store",
2121
- ".svelte-kit",
2122
- ".svn",
2123
- ".turbo",
2124
- ".venv",
2125
- ".vercel",
2126
- ".yarn",
2127
- "__pycache__",
2128
- "node_modules",
2129
- "venv"
2130
- ];
2131
- const TAILWIND_V4_IGNORED_EXTENSIONS = [
2132
- "less",
2133
- "lock",
2134
- "sass",
2135
- "scss",
2136
- "styl",
2137
- "log"
2138
- ];
2139
- const TAILWIND_V4_IGNORED_FILES = [
2140
- "package-lock.json",
2141
- "pnpm-lock.yaml",
2142
- "bun.lockb",
2143
- ".gitignore",
2144
- ".env",
2145
- ".env.*"
2146
- ];
2147
- const TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN = "**/*";
2148
- function uniqueResolvedPaths(values) {
2149
- const result = [];
2150
- for (const value of values) {
2151
- if (!value) continue;
2152
- const resolved = path.resolve(value);
2153
- if (!result.includes(resolved)) result.push(resolved);
2154
- }
2155
- return result;
2156
- }
2157
- function toPosixPath(value) {
2158
- return value.replaceAll(path.sep, "/");
2159
- }
2160
- function resolveSourceScanPath(value) {
2161
- const resolved = path.resolve(value);
2162
- try {
2163
- return realpathSync.native(resolved);
2164
- } catch {
2165
- return resolved;
2166
- }
2167
- }
2168
- function normalizeGlobPattern(pattern) {
2169
- return pattern.startsWith("./") ? pattern.slice(2) : pattern;
2170
- }
2171
- function hasGlobMagic(value) {
2172
- return /[*?[\]{}()!+@]/.test(value);
2173
- }
2174
- function splitStaticGlobPrefix(pattern) {
2175
- const segments = normalizeGlobPattern(pattern).split(/[\\/]+/);
2176
- const prefix = [];
2177
- const rest = [];
2178
- let reachedGlob = false;
2179
- for (const segment of segments) {
2180
- if (!reachedGlob && segment && !hasGlobMagic(segment)) {
2181
- prefix.push(segment);
2182
- continue;
2183
- }
2184
- reachedGlob = true;
2185
- rest.push(segment);
2186
- }
2187
- return {
2188
- prefix,
2189
- rest
2190
- };
2191
- }
2192
- async function pathExistsAsDirectory(file) {
2193
- try {
2194
- return (await stat(file)).isDirectory();
2195
- } catch {
2196
- return false;
2197
- }
2198
- }
2199
- function createTailwindV4DefaultIgnoreSources(base) {
2200
- return [
2201
- ...TAILWIND_V4_IGNORED_CONTENT_DIRS.map((pattern) => ({
2202
- base,
2203
- pattern: `**/${pattern}/**`,
2204
- negated: true
2205
- })),
2206
- ...TAILWIND_V4_IGNORED_EXTENSIONS.map((extension) => ({
2207
- base,
2208
- pattern: `**/*.${extension}`,
2209
- negated: true
2210
- })),
2211
- ...TAILWIND_V4_IGNORED_FILES.map((pattern) => ({
2212
- base,
2213
- pattern: `**/${pattern}`,
2214
- negated: true
2215
- }))
2216
- ];
2217
- }
2218
- function createTailwindV4RootSources(root, fallbackBase) {
2219
- if (root === "none") return [];
2220
- if (root === null) return [{
2221
- base: fallbackBase,
2222
- pattern: TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
2223
- negated: false
2224
- }];
2225
- return [{
2226
- ...root,
2227
- negated: false
2228
- }];
2229
- }
2230
- function createTailwindV4CompiledSourceEntries(root, sources, fallbackBase) {
2231
- return [...createTailwindV4RootSources(root, fallbackBase), ...sources];
2232
- }
2233
- async function resolveTailwindV4SourceEntry(sourcePath, base, negated, defaultPattern = TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN) {
2234
- const absoluteSource = path.isAbsolute(sourcePath) ? path.resolve(sourcePath) : path.resolve(base, sourcePath);
2235
- if (await pathExistsAsDirectory(absoluteSource)) return {
2236
- base: absoluteSource,
2237
- negated,
2238
- pattern: normalizeGlobPattern(defaultPattern)
2239
- };
2240
- if (path.isAbsolute(sourcePath)) return {
2241
- base: path.dirname(absoluteSource),
2242
- negated,
2243
- pattern: normalizeGlobPattern(path.basename(absoluteSource))
2244
- };
2245
- const { prefix, rest } = splitStaticGlobPrefix(sourcePath);
2246
- if (prefix.length > 0 && rest.length > 0) return {
2247
- base: path.resolve(base, ...prefix),
2248
- negated,
2249
- pattern: normalizeGlobPattern(rest.join("/"))
2250
- };
2251
- return {
2252
- base,
2253
- negated,
2254
- pattern: normalizeGlobPattern(sourcePath)
2255
- };
2256
- }
2257
- async function normalizeTailwindV4SourceEntries(sources, options = {}) {
2258
- const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
2259
- return Promise.all(sources.map((source) => resolveTailwindV4SourceEntry(source.pattern, source.base ? path.resolve(source.base) : cwd, source.negated, options.defaultPattern)));
2260
- }
2261
- function expandBracePattern(pattern) {
2262
- const index = pattern.indexOf("{");
2263
- if (index === -1) return [pattern];
2264
- const rest = pattern.slice(index);
2265
- let depth = 0;
2266
- let endIndex = -1;
2267
- for (let i = 0; i < rest.length; i++) {
2268
- const char = rest[i];
2269
- if (char === "\\") {
2270
- i += 1;
2271
- continue;
2272
- }
2273
- if (char === "{") {
2274
- depth += 1;
2275
- continue;
2276
- }
2277
- if (char === "}") {
2278
- depth -= 1;
2279
- if (depth === 0) {
2280
- endIndex = i;
2281
- break;
2282
- }
2283
- }
2284
- }
2285
- if (endIndex === -1) return [pattern];
2286
- const prefix = pattern.slice(0, index);
2287
- const inner = rest.slice(1, endIndex);
2288
- const suffix = rest.slice(endIndex + 1);
2289
- const parts = [];
2290
- const stack = [];
2291
- let lastPos = 0;
2292
- for (let i = 0; i < inner.length; i++) {
2293
- const char = inner[i];
2294
- if (char === "\\") {
2295
- i += 1;
2296
- continue;
2297
- }
2298
- if (char === "{") {
2299
- stack.push("}");
2300
- continue;
2301
- }
2302
- if (char === "}" && stack[stack.length - 1] === "}") {
2303
- stack.pop();
2304
- continue;
2305
- }
2306
- if (char === "," && stack.length === 0) {
2307
- parts.push(inner.slice(lastPos, i));
2308
- lastPos = i + 1;
2309
- }
2310
- }
2311
- parts.push(inner.slice(lastPos));
2312
- return parts.flatMap((part) => expandBracePattern(`${prefix}${part}${suffix}`));
2313
- }
2314
- function expandTailwindV4SourceEntryBraces(sources) {
2315
- return sources.flatMap((source) => {
2316
- const base = path.resolve(source.base);
2317
- return expandBracePattern(source.pattern).map((pattern) => ({
2318
- base,
2319
- pattern,
2320
- negated: source.negated
2321
- }));
2322
- });
2323
- }
2324
- function normalizeTailwindV4ScannerSources(sources, cwd, ignoredSources = []) {
2325
- return expandTailwindV4SourceEntryBraces([...sources?.length ? sources : [{
2326
- base: cwd,
2327
- pattern: TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
2328
- negated: false
2329
- }], ...ignoredSources]);
2330
- }
2331
- function normalizeEntryPattern(entry) {
2332
- return path.isAbsolute(entry.pattern) ? toPosixPath(path.relative(resolveSourceScanPath(entry.base), entry.pattern)) : normalizeGlobPattern(entry.pattern);
2333
- }
2334
- function isFileMatchedByTailwindV4SourceEntry(file, entry) {
2335
- const relative = toPosixPath(path.relative(resolveSourceScanPath(entry.base), file));
2336
- return Boolean(relative) && !relative.startsWith("../") && !path.isAbsolute(relative) && micromatch.isMatch(relative, normalizeEntryPattern(entry));
2337
- }
2338
- function isFileExcludedByTailwindV4SourceEntries(file, entries) {
2339
- if (!entries?.length) return false;
2340
- const resolvedFile = resolveSourceScanPath(file);
2341
- return entries.some((entry) => entry.negated && isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry));
2342
- }
2343
- function isFileMatchedByTailwindV4SourceEntries(file, entries) {
2344
- if (!entries?.length) return true;
2345
- const positiveEntries = entries.filter((entry) => !entry.negated);
2346
- const negativeEntries = entries.filter((entry) => entry.negated);
2347
- if (positiveEntries.length === 0) return false;
2348
- const resolvedFile = resolveSourceScanPath(file);
2349
- if (!positiveEntries.some((entry) => isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry))) return false;
2350
- return !negativeEntries.some((entry) => isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry));
2351
- }
2352
- function createTailwindV4SourceEntryMatcher(entries) {
2353
- if (!entries?.length) return;
2354
- return (file) => isFileMatchedByTailwindV4SourceEntries(file, entries);
2355
- }
2356
- function createTailwindV4SourceExclusionMatcher(entries) {
2357
- if (!entries?.length) return;
2358
- return (file) => isFileExcludedByTailwindV4SourceEntries(file, entries);
2359
- }
2360
- function groupTailwindV4SourceEntriesByBase(entries) {
2361
- const entriesByBase = /* @__PURE__ */ new Map();
2362
- for (const entry of entries) {
2363
- const base = path.resolve(entry.base);
2364
- const group = entriesByBase.get(base) ?? [];
2365
- group.push({
2366
- ...entry,
2367
- base,
2368
- pattern: normalizeGlobPattern(entry.pattern)
2369
- });
2370
- entriesByBase.set(base, group);
2371
- }
2372
- return entriesByBase;
2373
- }
2374
- async function expandTailwindV4SourceEntries(entries, resolveFiles) {
2375
- if (entries.length === 0) return [];
2376
- const files = /* @__PURE__ */ new Set();
2377
- await Promise.all([...groupTailwindV4SourceEntriesByBase(entries).entries()].map(async ([base, group]) => {
2378
- const matched = await resolveFiles({
2379
- cwd: base,
2380
- sources: group
2381
- });
2382
- for (const file of matched) files.add(path.resolve(file));
2383
- }));
2384
- return [...files].filter((file) => !isFileExcludedByTailwindV4SourceEntries(file, entries));
2385
- }
2386
- function mergeTailwindV4SourceEntries(...entries) {
2387
- const result = [];
2388
- const seen = /* @__PURE__ */ new Set();
2389
- for (const group of entries) for (const entry of group ?? []) {
2390
- const normalized = {
2391
- base: path.resolve(entry.base),
2392
- pattern: normalizeGlobPattern(entry.pattern),
2393
- negated: entry.negated
2394
- };
2395
- const key = JSON.stringify(normalized);
2396
- if (seen.has(key)) continue;
2397
- seen.add(key);
2398
- result.push(normalized);
2399
- }
2400
- return result;
2401
- }
2402
- function resolveTailwindV4SourceBaseCandidates(projectRoot, base, baseFallbacks) {
2403
- return uniqueResolvedPaths([
2404
- base,
2405
- projectRoot,
2406
- ...baseFallbacks
2407
- ]);
2408
- }
2409
- //#endregion
2410
- //#region src/extraction/candidate-extractor.ts
2411
- let oxideImportPromise;
2412
- const designSystemCandidateCache = /* @__PURE__ */ new Map();
2413
- function createOxideRuntimeDependencyError(cause) {
2414
- return new Error([
2415
- "tailwindcss-patch could not load @tailwindcss/oxide, which is required for source candidate scanning.",
2416
- "This dependency should be installed automatically by tailwindcss-patch.",
2417
- "Reinstall dependencies without disabling optional dependencies, or install @tailwindcss/oxide@^4.2.4 manually if your package manager omitted it."
2418
- ].join(" "), { cause });
2419
- }
2420
- async function importOxide() {
2421
- try {
2422
- return await import("@tailwindcss/oxide");
2423
- } catch (error) {
2424
- throw createOxideRuntimeDependencyError(error);
2425
- }
2426
- }
2427
- function getOxideModule() {
2428
- oxideImportPromise ??= importOxide();
2429
- oxideImportPromise.catch(() => {
2430
- oxideImportPromise = void 0;
2431
- });
2432
- return oxideImportPromise;
2433
- }
2434
- const HTML_ATTRIBUTE_NAME_CANDIDATE_RE = /^(?:class|className|hover-class|hoverClass)$/;
2435
- const CSS_DIRECTIVE_CANDIDATE_RE = /^@(?:apply|tailwind|source|config|plugin|theme|utility|custom-variant|variant)$/;
2436
- const CSS_APPLY_IMPORTANT = "!important";
2437
- const CSS_APPLY_RE = /@apply\s+([^;{}]+)/g;
2438
- const JS_LIKE_SOURCE_EXTENSION_RE = /^[cm]?[jt]sx?$/;
2439
- const MIXED_TEMPLATE_SOURCE_EXTENSION_RE = /^(?:vue|uvue|nvue|svelte|mpx)$/;
2440
- const CSS_LIKE_SOURCE_EXTENSION_RE = /^(?:css|wxss|acss|jxss|ttss|qss|tyss|scss|sass|less|styl|stylus)$/;
2441
- const SFC_SCRIPT_BLOCK_RE = /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
2442
- function isWhitespace(value) {
2443
- return value === " " || value === "\n" || value === "\r" || value === " " || value === "\f";
2444
- }
2445
- function isHtmlAttributeNameCandidate(content, candidate) {
2446
- if (!HTML_ATTRIBUTE_NAME_CANDIDATE_RE.test(candidate.rawCandidate)) return false;
2447
- let index = candidate.end;
2448
- while (isWhitespace(content[index])) index++;
2449
- return content[index] === "=";
2450
- }
2451
- function isInsideHtmlTagText(content, candidate) {
2452
- if (content.lastIndexOf("<", candidate.start) > content.lastIndexOf(">", candidate.start)) return false;
2453
- const nextOpen = content.indexOf("<", candidate.end);
2454
- return nextOpen !== -1 && (nextOpen < content.indexOf(">", candidate.end) || !content.includes(">", candidate.end));
2455
- }
2456
- function isCssDirectiveCandidate(candidate) {
2457
- return candidate === CSS_APPLY_IMPORTANT || CSS_DIRECTIVE_CANDIDATE_RE.test(candidate);
2458
- }
2459
- function isCandidateInCssApplyParams(content, candidate) {
2460
- const apply = content.lastIndexOf("@apply", candidate.start);
2461
- if (apply === -1) return false;
2462
- const boundary = content.slice(apply + 6, candidate.start);
2463
- return !/[;{}]/.test(boundary);
2464
- }
2465
- function isCandidateInsideJsStringStaticContent(content, start) {
2466
- let quote;
2467
- let templateExpressionDepth = 0;
2468
- for (let index = 0; index < start; index++) {
2469
- const char = content[index];
2470
- const next = content[index + 1];
2471
- if (quote && char === "\\") {
2472
- index++;
2473
- continue;
2474
- }
2475
- if (quote === "`" && templateExpressionDepth > 0) {
2476
- if (char === "\"" || char === "'") {
2477
- const nestedQuote = char;
2478
- index++;
2479
- while (index < start) {
2480
- const nestedChar = content[index];
2481
- if (nestedChar === "\\") {
2482
- index += 2;
2483
- continue;
2484
- }
2485
- if (nestedChar === nestedQuote) break;
2486
- index++;
2487
- }
2488
- continue;
2489
- }
2490
- if (char === "`") {
2491
- index++;
2492
- while (index < start) {
2493
- const nestedChar = content[index];
2494
- if (nestedChar === "\\") {
2495
- index += 2;
2496
- continue;
2497
- }
2498
- if (nestedChar === "`") break;
2499
- index++;
2500
- }
2501
- continue;
2502
- }
2503
- if (char === "{") {
2504
- templateExpressionDepth++;
2505
- continue;
2506
- }
2507
- if (char === "}") {
2508
- templateExpressionDepth--;
2509
- continue;
2510
- }
2511
- continue;
2512
- }
2513
- if (quote) {
2514
- if (quote === "`" && char === "$" && next === "{") {
2515
- templateExpressionDepth = 1;
2516
- index++;
2517
- continue;
2518
- }
2519
- if (char === quote) quote = void 0;
2520
- continue;
2521
- }
2522
- if (char === "\"" || char === "'" || char === "`") quote = char;
2523
- }
2524
- return quote !== void 0 && templateExpressionDepth === 0;
2525
- }
2526
- function shouldKeepSourceCandidate(content, extension, candidate) {
2527
- if (!candidate.rawCandidate || isCssDirectiveCandidate(candidate.rawCandidate)) return false;
2528
- if (CSS_LIKE_SOURCE_EXTENSION_RE.test(extension) && !isCandidateInCssApplyParams(content, candidate)) return false;
2529
- if (isHtmlAttributeNameCandidate(content, candidate)) return false;
2530
- if (isInsideHtmlTagText(content, candidate)) return false;
2531
- if (JS_LIKE_SOURCE_EXTENSION_RE.test(extension) && !isCandidateInsideJsStringStaticContent(content, candidate.start)) return false;
2532
- return true;
2533
- }
2534
- function createLocalCandidate(candidate) {
2535
- return {
2536
- rawCandidate: candidate.rawCandidate,
2537
- start: candidate.localStart,
2538
- end: candidate.localStart + candidate.rawCandidate.length
2539
- };
2540
- }
2541
- function dedupeCandidatesWithPositions(candidates) {
2542
- const seen = /* @__PURE__ */ new Set();
2543
- return candidates.filter((candidate) => {
2544
- const key = `${candidate.start}:${candidate.end}:${candidate.rawCandidate}`;
2545
- if (seen.has(key)) return false;
2546
- seen.add(key);
2547
- return true;
2548
- });
2549
- }
2550
- function createBareArbitraryValueCandidateContexts(content, extension, offset, options) {
2551
- return extractBareArbitraryValueSourceCandidatesWithPositions(content, options?.bareArbitraryValues).map((candidate) => ({
2552
- content,
2553
- extension,
2554
- localStart: candidate.start,
2555
- rawCandidate: candidate.rawCandidate,
2556
- start: candidate.start + offset,
2557
- end: candidate.end + offset
2558
- }));
2559
- }
2560
- async function extractCssApplyCandidates(content, extension, options) {
2561
- const candidates = [];
2562
- CSS_APPLY_RE.lastIndex = 0;
2563
- let match = CSS_APPLY_RE.exec(content);
2564
- while (match !== null) {
2565
- const applyParams = match[1] ?? "";
2566
- const applyParamsStart = match.index + match[0].indexOf(applyParams);
2567
- const applyCandidates = await extractRawCandidatesWithPositions(applyParams, extension);
2568
- candidates.push(...applyCandidates.map((candidate) => ({
2569
- content: applyParams,
2570
- extension: "html",
2571
- localStart: candidate.start,
2572
- rawCandidate: candidate.rawCandidate,
2573
- start: candidate.start + applyParamsStart,
2574
- end: candidate.end + applyParamsStart
2575
- })));
2576
- candidates.push(...createBareArbitraryValueCandidateContexts(applyParams, "html", applyParamsStart, options));
2577
- match = CSS_APPLY_RE.exec(content);
2578
- }
2579
- return candidates;
2580
- }
2581
- async function extractMixedSourceScriptCandidates(content, options) {
2582
- const candidates = [];
2583
- SFC_SCRIPT_BLOCK_RE.lastIndex = 0;
2584
- let match = SFC_SCRIPT_BLOCK_RE.exec(content);
2585
- while (match !== null) {
2586
- const scriptContent = match[1] ?? "";
2587
- const scriptStart = match.index + match[0].indexOf(scriptContent);
2588
- const scriptCandidates = await extractRawCandidatesWithPositions(scriptContent, "js");
2589
- candidates.push(...scriptCandidates.map((candidate) => ({
2590
- content: scriptContent,
2591
- extension: "js",
2592
- localStart: candidate.start,
2593
- rawCandidate: candidate.rawCandidate,
2594
- start: candidate.start + scriptStart,
2595
- end: candidate.end + scriptStart
2596
- })));
2597
- candidates.push(...createBareArbitraryValueCandidateContexts(scriptContent, "js", scriptStart, options));
2598
- match = SFC_SCRIPT_BLOCK_RE.exec(content);
2599
- }
2600
- return candidates;
2601
- }
2602
- function createCandidateCacheKey(designSystemKey, options) {
2603
- if (options.bareArbitraryValues == null || options.bareArbitraryValues === false) return designSystemKey;
2604
- return `${designSystemKey}:bare-arbitrary:${JSON.stringify(options.bareArbitraryValues)}`;
2605
- }
2606
- async function extractRawCandidatesWithPositions(content, extension = "html", options) {
2607
- const { Scanner } = await getOxideModule();
2608
- const candidates = new Scanner({}).getCandidatesWithPositions({
2609
- content,
2610
- extension
2611
- }).map(({ candidate, position }) => ({
2612
- rawCandidate: candidate,
2613
- start: position,
2614
- end: position + candidate.length
2615
- }));
2616
- candidates.push(...extractBareArbitraryValueSourceCandidatesWithPositions(content, options?.bareArbitraryValues));
2617
- return dedupeCandidatesWithPositions(candidates);
2618
- }
2619
- async function extractSourceCandidatesWithPositions(content, extension = "html", options) {
2620
- const normalizedExtension = extension.replace(/^\./, "");
2621
- const candidates = CSS_LIKE_SOURCE_EXTENSION_RE.test(normalizedExtension) ? await extractCssApplyCandidates(content, normalizedExtension, options) : (await extractRawCandidatesWithPositions(content, normalizedExtension, options)).map((candidate) => ({
2622
- ...candidate,
2623
- content,
2624
- extension: normalizedExtension,
2625
- localStart: candidate.start
2626
- }));
2627
- if (MIXED_TEMPLATE_SOURCE_EXTENSION_RE.test(normalizedExtension)) candidates.push(...await extractMixedSourceScriptCandidates(content, options));
2628
- const seen = /* @__PURE__ */ new Set();
2629
- return candidates.filter((candidate) => {
2630
- if (!shouldKeepSourceCandidate(candidate.content, candidate.extension, createLocalCandidate(candidate))) return false;
2631
- const key = `${candidate.start}:${candidate.end}:${candidate.rawCandidate}`;
2632
- if (seen.has(key)) return false;
2633
- seen.add(key);
2634
- return true;
2635
- }).map(({ rawCandidate, start, end }) => ({
2636
- rawCandidate,
2637
- start,
2638
- end
2639
- }));
2640
- }
2641
- async function extractSourceCandidates(content, extension = "html", options) {
2642
- const candidates = await extractSourceCandidatesWithPositions(content, extension, options);
2643
- return [...new Set(candidates.map((candidate) => candidate.rawCandidate))];
2644
- }
2645
- async function extractRawCandidates(sources, options) {
2646
- const { Scanner } = await getOxideModule();
2647
- const scanner = new Scanner(sources === void 0 ? {} : { sources });
2648
- const candidates = new Set(scanner.scan());
2649
- if (options?.bareArbitraryValues !== void 0 && options.bareArbitraryValues !== false) await Promise.all((scanner.files ?? []).map(async (file) => {
2650
- try {
2651
- const content = await promises.readFile(file, "utf8");
2652
- const extension = toExtension(file);
2653
- for (const candidate of extractBareArbitraryValueSourceCandidatesWithPositions(content, options.bareArbitraryValues)) if (shouldKeepSourceCandidate(content, extension, candidate)) candidates.add(candidate.rawCandidate);
2654
- } catch {}
2655
- }));
2656
- return [...candidates];
2657
- }
2658
- async function extractValidCandidates(options) {
2659
- const providedOptions = options ?? {};
2660
- const defaultCwd = providedOptions.cwd ?? process.cwd();
2661
- const base = providedOptions.base ?? defaultCwd;
2662
- const baseFallbacks = providedOptions.baseFallbacks ?? [];
2663
- const css = providedOptions.css ?? "@import \"tailwindcss\";";
2664
- const sources = (providedOptions.sources ?? [{
2665
- base: defaultCwd,
2666
- pattern: "**/*",
2667
- negated: false
2668
- }]).map((source) => ({
2669
- base: source.base ?? defaultCwd,
2670
- pattern: source.pattern,
2671
- negated: source.negated
2672
- }));
2673
- const source = {
2674
- projectRoot: defaultCwd,
2675
- base,
2676
- baseFallbacks,
2677
- css,
2678
- dependencies: []
2679
- };
2680
- const designSystemKey = getTailwindV4DesignSystemCacheKey(source);
2681
- const designSystem = await loadTailwindV4DesignSystem(source);
2682
- const candidateCacheKey = createCandidateCacheKey(designSystemKey, providedOptions);
2683
- const candidateCache = designSystemCandidateCache.get(candidateCacheKey) ?? /* @__PURE__ */ new Map();
2684
- designSystemCandidateCache.set(candidateCacheKey, candidateCache);
2685
- const candidates = await extractRawCandidates(sources, providedOptions.bareArbitraryValues === void 0 ? void 0 : { bareArbitraryValues: providedOptions.bareArbitraryValues });
2686
- const inlineSources = extractTailwindV4InlineSourceCandidates(css);
2687
- for (const candidate of inlineSources.included) candidates.push(candidate);
2688
- for (const candidate of inlineSources.excluded) {
2689
- let index = candidates.indexOf(candidate);
2690
- while (index !== -1) {
2691
- candidates.splice(index, 1);
2692
- index = candidates.indexOf(candidate);
2693
- }
2694
- }
2695
- const validCandidates = [];
2696
- const uncachedCandidates = [];
2697
- for (const rawCandidate of candidates) {
2698
- const cached = candidateCache.get(rawCandidate);
2699
- if (cached === true) {
2700
- validCandidates.push(rawCandidate);
2701
- continue;
2702
- }
2703
- if (cached === false) continue;
2704
- const bareArbitrary = resolveBareArbitraryValueCandidate(rawCandidate, providedOptions.bareArbitraryValues);
2705
- if (designSystem.parseCandidate(rawCandidate).length > 0 || bareArbitrary && designSystem.parseCandidate(bareArbitrary.canonicalCandidate).length > 0) {
2706
- uncachedCandidates.push(rawCandidate);
2707
- continue;
2708
- }
2709
- candidateCache.set(rawCandidate, false);
2710
- }
2711
- if (uncachedCandidates.length === 0) return validCandidates;
2712
- const validUncachedCandidates = resolveValidTailwindV4Candidates(designSystem, uncachedCandidates, providedOptions.bareArbitraryValues === void 0 ? void 0 : { bareArbitraryValues: providedOptions.bareArbitraryValues });
2713
- for (const candidate of uncachedCandidates) {
2714
- const isValid = validUncachedCandidates.has(candidate);
2715
- candidateCache.set(candidate, isValid);
2716
- if (!isValid) continue;
2717
- validCandidates.push(candidate);
2718
- }
2719
- return validCandidates;
2720
- }
2721
- function buildLineOffsets(content) {
2722
- const offsets = [0];
2723
- for (let i = 0; i < content.length; i++) if (content[i] === "\n") offsets.push(i + 1);
2724
- if (offsets[offsets.length - 1] !== content.length) offsets.push(content.length);
2725
- return offsets;
2726
- }
2727
- function resolveLineMeta(content, offsets, index) {
2728
- let low = 0;
2729
- let high = offsets.length - 1;
2730
- while (low <= high) {
2731
- const mid = Math.floor((low + high) / 2);
2732
- const start = offsets[mid];
2733
- if (start === void 0) break;
2734
- const nextStart = offsets[mid + 1] ?? content.length;
2735
- if (index < start) {
2736
- high = mid - 1;
2737
- continue;
2738
- }
2739
- if (index >= nextStart) {
2740
- low = mid + 1;
2741
- continue;
2742
- }
2743
- const line = mid + 1;
2744
- const column = index - start + 1;
2745
- const lineEnd = content.indexOf("\n", start);
2746
- return {
2747
- line,
2748
- column,
2749
- lineText: content.slice(start, lineEnd === -1 ? content.length : lineEnd)
2750
- };
2751
- }
2752
- const lastStart = offsets[offsets.length - 2] ?? 0;
2753
- return {
2754
- line: offsets.length - 1,
2755
- column: index - lastStart + 1,
2756
- lineText: content.slice(lastStart)
2757
- };
2758
- }
2759
- function toExtension(filename) {
2760
- return path.extname(filename).replace(/^\./, "") || "txt";
2761
- }
2762
- function toRelativeFile(cwd, filename) {
2763
- const relative = path.relative(cwd, filename);
2764
- return relative === "" ? path.basename(filename) : relative;
2765
- }
2766
- async function resolveScannerSources(options) {
2767
- const cwd = options?.cwd ? path.resolve(options.cwd) : process.cwd();
2768
- if (options?.sources?.length || options?.css === void 0) return {
2769
- cwd,
2770
- sources: normalizeTailwindV4ScannerSources(options?.sources, cwd, options?.ignoredSources)
2771
- };
2772
- const base = options.base ? path.resolve(options.base) : cwd;
2773
- const { compiled } = await compileTailwindV4Source({
2774
- projectRoot: cwd,
2775
- base,
2776
- baseFallbacks: options.baseFallbacks?.map((baseFallback) => path.resolve(baseFallback)) ?? [],
2777
- css: options.css,
2778
- dependencies: []
2779
- });
2780
- return {
2781
- cwd,
2782
- sources: normalizeTailwindV4ScannerSources(createTailwindV4CompiledSourceEntries(compiled.root, compiled.sources, base), cwd, options.ignoredSources)
2783
- };
2784
- }
2785
- async function resolveProjectSourceFiles(options) {
2786
- const { sources } = await resolveScannerSources(options);
2787
- const { Scanner } = await getOxideModule();
2788
- const files = new Scanner({ sources }).files ?? [];
2789
- return options?.filter ? files.filter(options.filter) : files;
2790
- }
2791
- async function extractProjectCandidatesWithPositions(options) {
2792
- const { cwd, sources } = await resolveScannerSources(options);
2793
- const { Scanner } = await getOxideModule();
2794
- const scanner = new Scanner({ sources });
2795
- const files = scanner.files ?? [];
2796
- const entries = [];
2797
- const skipped = [];
2798
- for (const file of files) {
2799
- let content;
2800
- try {
2801
- content = await promises.readFile(file, "utf8");
2802
- } catch (error) {
2803
- skipped.push({
2804
- file,
2805
- reason: error instanceof Error ? error.message : "Unknown error"
2806
- });
2807
- continue;
2808
- }
2809
- const extension = toExtension(file);
2810
- const matches = scanner.getCandidatesWithPositions({
2811
- file,
2812
- content,
2813
- extension
2814
- });
2815
- if (!matches.length) continue;
2816
- const offsets = buildLineOffsets(content);
2817
- const relativeFile = toRelativeFile(cwd, file);
2818
- for (const match of matches) {
2819
- const info = resolveLineMeta(content, offsets, match.position);
2820
- entries.push({
2821
- rawCandidate: match.candidate,
2822
- file,
2823
- relativeFile,
2824
- extension,
2825
- start: match.position,
2826
- end: match.position + match.candidate.length,
2827
- length: match.candidate.length,
2828
- line: info.line,
2829
- column: info.column,
2830
- lineText: info.lineText
2831
- });
2832
- }
2833
- }
2834
- return {
2835
- entries,
2836
- filesScanned: files.length,
2837
- skippedFiles: skipped,
2838
- sources
2839
- };
2840
- }
2841
- function groupTokensByFile(report, options) {
2842
- const key = options?.key ?? "relative";
2843
- const stripAbsolute = options?.stripAbsolutePaths ?? key !== "absolute";
2844
- return report.entries.reduce((acc, entry) => {
2845
- const bucketKey = key === "absolute" ? entry.file : entry.relativeFile;
2846
- const bucket = acc[bucketKey] ?? (acc[bucketKey] = []);
2847
- const value = stripAbsolute ? {
2848
- ...entry,
2849
- file: entry.relativeFile
2850
- } : entry;
2851
- bucket.push(value);
2852
- return acc;
2853
- }, {});
2854
- }
2855
- //#endregion
2856
1459
  //#region src/utils.ts
2857
1460
  function isObject(val) {
2858
1461
  return val !== null && typeof val === "object" && Array.isArray(val) === false;
@@ -4528,4 +3131,4 @@ var ValidateCommandError = class extends Error {
4528
3131
  }
4529
3132
  };
4530
3133
  //#endregion
4531
- export { resolveBareArbitraryValueCandidate as $, createTailwindV4RootSources as A, resolveSourceScanPath as B, resolveProjectSourceFiles as C, TAILWIND_V4_IGNORED_FILES as D, TAILWIND_V4_IGNORED_EXTENSIONS as E, isFileExcludedByTailwindV4SourceEntries as F, canonicalizeBareArbitraryValueCandidates as G, resolveTailwindV4SourceEntry as H, isFileMatchedByTailwindV4SourceEntries as I, resolveValidTailwindV4Candidates as J, extractTailwindV4InlineSourceCandidates as K, mergeTailwindV4SourceEntries as L, createTailwindV4SourceExclusionMatcher as M, expandTailwindV4SourceEntries as N, createTailwindV4CompiledSourceEntries as O, expandTailwindV4SourceEntryBraces as P, isBareArbitraryValuesEnabled as Q, normalizeTailwindV4ScannerSources as R, groupTokensByFile as S, TAILWIND_V4_IGNORED_CONTENT_DIRS as T, compileTailwindV4Source as U, resolveTailwindV4SourceBaseCandidates as V, loadTailwindV4DesignSystem as W, extractBareArbitraryValueSourceCandidates as X, escapeCssClassName as Y, extractBareArbitraryValueSourceCandidatesWithPositions as Z, extractRawCandidates as _, tailwindcssPatchCommands as a, extractSourceCandidatesWithPositions as b, MIGRATION_REPORT_KIND as c, getPatchStatusReport as d, loadPatchOptionsForWorkspace as et, runTailwindBuild as f, extractProjectCandidatesWithPositions as g, collectClassesFromTailwindV4 as h, classifyValidateError as i, logger as it, createTailwindV4SourceEntryMatcher as j, createTailwindV4DefaultIgnoreSources as k, MIGRATION_REPORT_SCHEMA_VERSION as l, collectClassesFromContexts as m, VALIDATE_FAILURE_REASONS as n, normalizeOptions as nt, migrateConfigFiles as o, loadRuntimeContexts as p, replaceBareArbitraryValueSelectors as q, ValidateCommandError as r, CacheStore as rt, restoreConfigFiles as s, VALIDATE_EXIT_CODES as t, loadWorkspaceConfigModule as tt, TailwindcssPatcher as u, extractRawCandidatesWithPositions as v, TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN as w, extractValidCandidates as x, extractSourceCandidates as y, normalizeTailwindV4SourceEntries as z };
3134
+ export { resolveProjectSourceFiles as C, CacheStore as D, normalizeOptions as E, logger as O, groupTokensByFile as S, loadWorkspaceConfigModule as T, extractRawCandidates as _, tailwindcssPatchCommands as a, extractSourceCandidatesWithPositions as b, MIGRATION_REPORT_KIND as c, getPatchStatusReport as d, runTailwindBuild as f, extractProjectCandidatesWithPositions as g, collectClassesFromTailwindV4 as h, classifyValidateError as i, MIGRATION_REPORT_SCHEMA_VERSION as l, collectClassesFromContexts as m, VALIDATE_FAILURE_REASONS as n, migrateConfigFiles as o, loadRuntimeContexts as p, ValidateCommandError as r, restoreConfigFiles as s, VALIDATE_EXIT_CODES as t, TailwindcssPatcher as u, extractRawCandidatesWithPositions as v, loadPatchOptionsForWorkspace as w, extractValidCandidates as x, extractSourceCandidates as y };