tailwindcss-patch 9.2.0 → 9.3.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.
@@ -249,6 +249,10 @@ interface TailwindV4Options {
249
249
  cssEntries?: string[];
250
250
  /** Overrides the content sources scanned by the oxide scanner. */
251
251
  sources?: SourceEntry[];
252
+ /** Enables UnoCSS-style bare arbitrary values such as `p-10%` and `p-2.5px`. */
253
+ bareArbitraryValues?: boolean | {
254
+ /** Unit allow-list used when detecting bare arbitrary values. */units?: string[];
255
+ } | undefined;
252
256
  }
253
257
  /**
254
258
  * High-level Tailwind patch configuration shared across versions.
@@ -326,6 +330,9 @@ interface NormalizedTailwindV4Options {
326
330
  cssEntries: string[];
327
331
  sources: SourceEntry[];
328
332
  hasUserDefinedSources: boolean;
333
+ bareArbitraryValues: false | {
334
+ units?: string[];
335
+ } | undefined;
329
336
  }
330
337
  /**
331
338
  * Tailwind configuration ready for consumption by the runtime after normalization.
@@ -413,6 +420,14 @@ type TailwindcssConfigResult = Awaited<ReturnType<TailwindcssConfigModule['getCo
413
420
  //#region src/runtime/collector.d.ts
414
421
  type TailwindMajorVersion = 2 | 3 | 4;
415
422
  //#endregion
423
+ //#region src/v4/bare-arbitrary-values.d.ts
424
+ interface BareArbitraryValueOptions {
425
+ /**
426
+ * 允许作为无方括号任意值的单位列表。
427
+ */
428
+ units?: string[];
429
+ }
430
+ //#endregion
416
431
  //#region src/extraction/candidate-extractor.d.ts
417
432
  interface ExtractValidCandidatesOption {
418
433
  sources?: SourceEntry[];
@@ -420,6 +435,7 @@ interface ExtractValidCandidatesOption {
420
435
  baseFallbacks?: string[];
421
436
  css?: string;
422
437
  cwd?: string;
438
+ bareArbitraryValues?: boolean | BareArbitraryValueOptions;
423
439
  }
424
440
  declare function extractRawCandidatesWithPositions(content: string, extension?: string): Promise<{
425
441
  rawCandidate: string;
@@ -682,4 +698,4 @@ declare class ValidateCommandError extends Error {
682
698
  constructor(summary: ValidateFailureSummary, options?: ErrorOptions);
683
699
  }
684
700
  //#endregion
685
- export { TailwindTokenFileKey as $, groupTokensByFile as A, TailwindCssOptions as B, MIGRATION_REPORT_SCHEMA_VERSION as C, extractRawCandidates as D, extractProjectCandidatesWithPositions as E, ExposeContextOptions as F, ExtractResult as G, TailwindV2Options as H, ExtendLengthUnitsOptions as I, PatchName as J, ILengthUnitsPatchOptions as K, ExtractOptions as L, ApplyOptions as M, CacheOptions as N, extractRawCandidatesWithPositions as O, CacheStrategy as P, TailwindTokenByFileMap as Q, NormalizedCacheOptions as R, MIGRATION_REPORT_KIND as S, TailwindcssPatcher as T, TailwindV3Options as U, TailwindCssPatchOptions as V, TailwindV4Options as W, PatchStatusReport as X, PatchStatusEntry as Y, TailwindPatchRuntime as Z, ConfigFileMigrationEntry as _, ValidateFailureSummary as a, CacheClearResult as at, RestoreConfigFilesOptions as b, TailwindcssPatchCliMountOptions as c, CacheContextMetadata as ct, TailwindcssPatchCommandContext as d, CacheReadResult as dt, TailwindTokenLocation as et, TailwindcssPatchCommandHandler as f, tailwindcssPatchCommands as g, TailwindcssPatchCommandOptions as h, ValidateFailureReason as i, CacheClearOptions as it, normalizeOptions as j, extractValidCandidates as k, TailwindcssPatchCliOptions as l, CacheIndexFileV2 as lt, TailwindcssPatchCommandOptionDefinition as m, VALIDATE_FAILURE_REASONS as n, TailwindcssClassCache as nt, ValidateJsonFailurePayload as o, CacheClearScope as ot, TailwindcssPatchCommandHandlerMap as p, PatchCheckStatus as q, ValidateCommandError as r, TailwindcssRuntimeContext as rt, ValidateJsonSuccessPayload as s, CacheContextDescriptor as st, VALIDATE_EXIT_CODES as t, TailwindTokenReport as tt, TailwindcssPatchCommand as u, CacheReadMeta as ut, ConfigFileMigrationReport as v, logger as w, RestoreConfigFilesResult as x, MigrateConfigFilesOptions as y, NormalizedTailwindCssPatchOptions as z };
701
+ export { TailwindTokenByFileMap as $, groupTokensByFile as A, NormalizedTailwindCssPatchOptions as B, MIGRATION_REPORT_SCHEMA_VERSION as C, extractRawCandidates as D, extractProjectCandidatesWithPositions as E, CacheStrategy as F, TailwindV4Options as G, TailwindCssPatchOptions as H, ExposeContextOptions as I, PatchCheckStatus as J, ExtractResult as K, ExtendLengthUnitsOptions as L, normalizeOptions as M, ApplyOptions as N, extractRawCandidatesWithPositions as O, CacheOptions as P, TailwindPatchRuntime as Q, ExtractOptions as R, MIGRATION_REPORT_KIND as S, TailwindcssPatcher as T, TailwindV2Options as U, TailwindCssOptions as V, TailwindV3Options as W, PatchStatusEntry as X, PatchName as Y, PatchStatusReport as Z, ConfigFileMigrationEntry as _, ValidateFailureSummary as a, CacheClearOptions as at, RestoreConfigFilesOptions as b, TailwindcssPatchCliMountOptions as c, CacheContextDescriptor as ct, TailwindcssPatchCommandContext as d, CacheReadMeta as dt, TailwindTokenFileKey as et, TailwindcssPatchCommandHandler as f, CacheReadResult as ft, tailwindcssPatchCommands as g, TailwindcssPatchCommandOptions as h, ValidateFailureReason as i, TailwindcssRuntimeContext as it, BareArbitraryValueOptions as j, extractValidCandidates as k, TailwindcssPatchCliOptions as l, CacheContextMetadata as lt, TailwindcssPatchCommandOptionDefinition as m, VALIDATE_FAILURE_REASONS as n, TailwindTokenReport as nt, ValidateJsonFailurePayload as o, CacheClearResult as ot, TailwindcssPatchCommandHandlerMap as p, ILengthUnitsPatchOptions as q, ValidateCommandError as r, TailwindcssClassCache as rt, ValidateJsonSuccessPayload as s, CacheClearScope as st, VALIDATE_EXIT_CODES as t, TailwindTokenLocation as tt, TailwindcssPatchCommand as u, CacheIndexFileV2 as ut, ConfigFileMigrationReport as v, logger as w, RestoreConfigFilesResult as x, MigrateConfigFilesOptions as y, NormalizedCacheOptions as z };
@@ -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.2.0";
26
+ var version = "9.3.0";
27
27
  //#endregion
28
28
  //#region src/constants.ts
29
29
  const pkgName = "tailwindcss-patch";
@@ -1342,7 +1342,8 @@ function normalizeTailwindV4Options(v4, fallbackBase) {
1342
1342
  ...v4?.css === void 0 ? {} : { css: v4.css },
1343
1343
  cssEntries,
1344
1344
  sources,
1345
- hasUserDefinedSources
1345
+ hasUserDefinedSources,
1346
+ bareArbitraryValues: v4?.bareArbitraryValues === true ? {} : typeof v4?.bareArbitraryValues === "object" && v4.bareArbitraryValues !== null ? { ...v4.bareArbitraryValues } : false
1346
1347
  };
1347
1348
  }
1348
1349
  function normalizeTailwindOptions(tailwind, projectRoot, shouldDefaultResolveFromCwd) {
@@ -1447,23 +1448,257 @@ async function loadPatchOptionsForWorkspace(cwd, overrides) {
1447
1448
  return merge(overrides ?? {}, base, { projectRoot: cwd });
1448
1449
  }
1449
1450
  //#endregion
1451
+ //#region src/v4/bare-arbitrary-values.ts
1452
+ const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
1453
+ "%",
1454
+ "px",
1455
+ "rpx",
1456
+ "rem",
1457
+ "em",
1458
+ "vw",
1459
+ "vh",
1460
+ "vmin",
1461
+ "vmax",
1462
+ "dvw",
1463
+ "dvh",
1464
+ "svw",
1465
+ "svh",
1466
+ "lvw",
1467
+ "lvh",
1468
+ "ch",
1469
+ "ex",
1470
+ "lh",
1471
+ "rlh",
1472
+ "fr",
1473
+ "deg",
1474
+ "rad",
1475
+ "turn",
1476
+ "s",
1477
+ "ms"
1478
+ ];
1479
+ const NUMBER_RE = /^-?(?:\d+|\d*\.\d+)$/;
1480
+ const FUNCTION_VALUE_RE = /^[a-zA-Z_-][a-zA-Z0-9_-]*\(/;
1481
+ function splitVariantPrefix(candidate) {
1482
+ let depth = 0;
1483
+ let quote;
1484
+ let lastSeparator = -1;
1485
+ for (let index = 0; index < candidate.length; index++) {
1486
+ const character = candidate[index];
1487
+ if (character === "\\") {
1488
+ index++;
1489
+ continue;
1490
+ }
1491
+ if (quote) {
1492
+ if (character === quote) quote = void 0;
1493
+ continue;
1494
+ }
1495
+ if (character === "\"" || character === "'") {
1496
+ quote = character;
1497
+ continue;
1498
+ }
1499
+ if (character === "[" || character === "(" || character === "{") {
1500
+ depth++;
1501
+ continue;
1502
+ }
1503
+ if (character === "]" || character === ")" || character === "}") {
1504
+ depth = Math.max(0, depth - 1);
1505
+ continue;
1506
+ }
1507
+ if (depth === 0 && character === ":") lastSeparator = index;
1508
+ }
1509
+ if (lastSeparator === -1) return {
1510
+ prefix: "",
1511
+ body: candidate
1512
+ };
1513
+ return {
1514
+ prefix: candidate.slice(0, lastSeparator + 1),
1515
+ body: candidate.slice(lastSeparator + 1)
1516
+ };
1517
+ }
1518
+ function isBalancedFunctionValue(value) {
1519
+ let depth = 0;
1520
+ let quote;
1521
+ for (let index = 0; index < value.length; index++) {
1522
+ const character = value[index];
1523
+ if (character === "\\") {
1524
+ index++;
1525
+ continue;
1526
+ }
1527
+ if (quote) {
1528
+ if (character === quote) quote = void 0;
1529
+ continue;
1530
+ }
1531
+ if (character === "\"" || character === "'") {
1532
+ quote = character;
1533
+ continue;
1534
+ }
1535
+ if (character === "(") {
1536
+ depth++;
1537
+ continue;
1538
+ }
1539
+ if (character === ")") {
1540
+ depth--;
1541
+ if (depth < 0) return false;
1542
+ }
1543
+ }
1544
+ return depth === 0 && quote === void 0;
1545
+ }
1546
+ function isHexColorValue(value) {
1547
+ return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6,8})$/.test(value);
1548
+ }
1549
+ function isQuotedValue(value) {
1550
+ const quote = value[0];
1551
+ if (quote !== "\"" && quote !== "'" || value[value.length - 1] !== quote) return false;
1552
+ let escaped = false;
1553
+ for (let index = 1; index < value.length - 1; index++) {
1554
+ const character = value[index];
1555
+ if (escaped) {
1556
+ escaped = false;
1557
+ continue;
1558
+ }
1559
+ if (character === "\\") escaped = true;
1560
+ }
1561
+ return !escaped;
1562
+ }
1563
+ function normalizeBareArbitraryValueOptions(options) {
1564
+ if (options === false || options === void 0 || options === null) return;
1565
+ const units = options === true ? DEFAULT_BARE_ARBITRARY_VALUE_UNITS : options.units ?? DEFAULT_BARE_ARBITRARY_VALUE_UNITS;
1566
+ const normalizedUnits = [...new Set(units.filter((unit) => typeof unit === "string" && unit.length > 0))];
1567
+ if (normalizedUnits.length === 0) return;
1568
+ return { units: normalizedUnits.sort((a, b) => b.length - a.length) };
1569
+ }
1570
+ function resolveValueWithUnit(body, units) {
1571
+ for (const unit of units) {
1572
+ if (!body.endsWith(unit)) continue;
1573
+ const numberPart = body.slice(0, -unit.length);
1574
+ if (NUMBER_RE.test(numberPart)) return `${numberPart}${unit}`;
1575
+ }
1576
+ }
1577
+ function resolveArbitraryValue(body, units) {
1578
+ const withUnit = resolveValueWithUnit(body, units);
1579
+ 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;
1583
+ }
1584
+ function resolveUtilityAndValue(body, units) {
1585
+ let depth = 0;
1586
+ let quote;
1587
+ for (let index = 1; index < body.length; index++) {
1588
+ const character = body[index];
1589
+ if (character === "\\") {
1590
+ index++;
1591
+ continue;
1592
+ }
1593
+ if (quote) {
1594
+ if (character === quote) quote = void 0;
1595
+ continue;
1596
+ }
1597
+ if (character === "\"" || character === "'") {
1598
+ quote = character;
1599
+ continue;
1600
+ }
1601
+ if (character === "(" || character === "{") {
1602
+ depth++;
1603
+ continue;
1604
+ }
1605
+ if (character === ")" || character === "}") {
1606
+ depth = Math.max(0, depth - 1);
1607
+ continue;
1608
+ }
1609
+ if (depth > 0 || character !== "-") continue;
1610
+ const utility = body.slice(0, index);
1611
+ const rawValue = body.slice(index + 1);
1612
+ if (!utility || !rawValue) continue;
1613
+ const value = resolveArbitraryValue(rawValue, units);
1614
+ if (value) return {
1615
+ utility,
1616
+ value
1617
+ };
1618
+ }
1619
+ }
1620
+ function resolveBareArbitraryValueCandidate(candidate, options) {
1621
+ const normalizedOptions = normalizeBareArbitraryValueOptions(options);
1622
+ if (!normalizedOptions || !candidate || candidate.includes("[") || candidate.includes("]")) return;
1623
+ const { prefix, body } = splitVariantPrefix(candidate);
1624
+ const important = body.startsWith("!") ? "!" : "";
1625
+ let normalizedBody = important ? body.slice(1) : body;
1626
+ const negative = normalizedBody.startsWith("-") ? "-" : "";
1627
+ if (negative) normalizedBody = normalizedBody.slice(1);
1628
+ const resolved = resolveUtilityAndValue(normalizedBody, normalizedOptions.units);
1629
+ if (!resolved) return;
1630
+ return {
1631
+ candidate,
1632
+ canonicalCandidate: `${prefix}${important}${negative}${resolved.utility}-[${resolved.value}]`
1633
+ };
1634
+ }
1635
+ function escapeCssClassName(value) {
1636
+ let result = "";
1637
+ for (let index = 0; index < value.length; index++) {
1638
+ const codeUnit = value.charCodeAt(index);
1639
+ const character = value.charAt(index);
1640
+ if (codeUnit === 0) {
1641
+ result += "�";
1642
+ continue;
1643
+ }
1644
+ if (codeUnit >= 1 && codeUnit <= 31 || codeUnit === 127 || index === 0 && codeUnit >= 48 && codeUnit <= 57 || index === 1 && codeUnit >= 48 && codeUnit <= 57 && value.charCodeAt(0) === 45) {
1645
+ result += `\\${codeUnit.toString(16)} `;
1646
+ continue;
1647
+ }
1648
+ if (codeUnit >= 128 || codeUnit === 45 || codeUnit === 95 || codeUnit >= 48 && codeUnit <= 57 || codeUnit >= 65 && codeUnit <= 90 || codeUnit >= 97 && codeUnit <= 122) {
1649
+ result += character;
1650
+ continue;
1651
+ }
1652
+ result += `\\${character}`;
1653
+ }
1654
+ return result;
1655
+ }
1656
+ //#endregion
1450
1657
  //#region src/v4/candidates.ts
1451
- function resolveValidTailwindV4Candidates(designSystem, candidates) {
1658
+ function resolveValidTailwindV4Candidates(designSystem, candidates, options) {
1452
1659
  const validCandidates = /* @__PURE__ */ new Set();
1453
1660
  const parsedCandidates = [];
1661
+ const originalCandidateByCanonical = /* @__PURE__ */ new Map();
1454
1662
  for (const candidate of candidates) {
1455
- if (!candidate || parsedCandidates.includes(candidate)) continue;
1456
- if (designSystem.parseCandidate(candidate).length > 0) parsedCandidates.push(candidate);
1663
+ if (!candidate) continue;
1664
+ const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options?.bareArbitraryValues);
1665
+ 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);
1670
+ }
1457
1671
  }
1458
1672
  if (parsedCandidates.length === 0) return validCandidates;
1459
1673
  const cssByCandidate = designSystem.candidatesToCss(parsedCandidates);
1460
1674
  for (let index = 0; index < parsedCandidates.length; index++) {
1461
1675
  const candidate = parsedCandidates[index];
1462
1676
  const candidateCss = cssByCandidate[index];
1463
- if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) validCandidates.add(candidate);
1677
+ if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) validCandidates.add(originalCandidateByCanonical.get(candidate) ?? candidate);
1464
1678
  }
1465
1679
  return validCandidates;
1466
1680
  }
1681
+ function createSelectorAliasMap(candidates, options) {
1682
+ const aliases = /* @__PURE__ */ new Map();
1683
+ for (const candidate of candidates) {
1684
+ const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options);
1685
+ if (!bareArbitrary) continue;
1686
+ aliases.set(escapeCssClassName(bareArbitrary.canonicalCandidate), escapeCssClassName(bareArbitrary.candidate));
1687
+ }
1688
+ return aliases;
1689
+ }
1690
+ function replaceBareArbitraryValueSelectors(css, candidates, options) {
1691
+ const aliases = createSelectorAliasMap(candidates, options);
1692
+ 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;
1696
+ }
1697
+ function canonicalizeBareArbitraryValueCandidates(candidates, options) {
1698
+ return Array.from(candidates, (candidate) => {
1699
+ return resolveBareArbitraryValueCandidate(candidate, options)?.canonicalCandidate ?? candidate;
1700
+ });
1701
+ }
1467
1702
  function splitTopLevel(value, separator) {
1468
1703
  const result = [];
1469
1704
  let start = 0;
@@ -1709,13 +1944,31 @@ async function compileTailwindV4Source(source) {
1709
1944
  //#region src/extraction/candidate-extractor.ts
1710
1945
  let oxideImportPromise;
1711
1946
  const designSystemCandidateCache = /* @__PURE__ */ new Map();
1947
+ function createOxideRuntimeDependencyError(cause) {
1948
+ return new Error([
1949
+ "tailwindcss-patch could not load @tailwindcss/oxide, which is required for source candidate scanning.",
1950
+ "This dependency should be installed automatically by tailwindcss-patch.",
1951
+ "Reinstall dependencies without disabling optional dependencies, or install @tailwindcss/oxide@^4.2.4 manually if your package manager omitted it."
1952
+ ].join(" "), { cause });
1953
+ }
1712
1954
  async function importOxide() {
1713
- return import("@tailwindcss/oxide");
1955
+ try {
1956
+ return await import("@tailwindcss/oxide");
1957
+ } catch (error) {
1958
+ throw createOxideRuntimeDependencyError(error);
1959
+ }
1714
1960
  }
1715
1961
  function getOxideModule() {
1716
1962
  oxideImportPromise ??= importOxide();
1963
+ oxideImportPromise.catch(() => {
1964
+ oxideImportPromise = void 0;
1965
+ });
1717
1966
  return oxideImportPromise;
1718
1967
  }
1968
+ function createCandidateCacheKey(designSystemKey, options) {
1969
+ if (options.bareArbitraryValues == null || options.bareArbitraryValues === false) return designSystemKey;
1970
+ return `${designSystemKey}:bare-arbitrary:${JSON.stringify(options.bareArbitraryValues)}`;
1971
+ }
1719
1972
  async function extractRawCandidatesWithPositions(content, extension = "html") {
1720
1973
  const { Scanner } = await getOxideModule();
1721
1974
  return new Scanner({}).getCandidatesWithPositions({
@@ -1755,8 +2008,9 @@ async function extractValidCandidates(options) {
1755
2008
  };
1756
2009
  const designSystemKey = getTailwindV4DesignSystemCacheKey(source);
1757
2010
  const designSystem = await loadTailwindV4DesignSystem(source);
1758
- const candidateCache = designSystemCandidateCache.get(designSystemKey) ?? /* @__PURE__ */ new Map();
1759
- designSystemCandidateCache.set(designSystemKey, candidateCache);
2011
+ const candidateCacheKey = createCandidateCacheKey(designSystemKey, providedOptions);
2012
+ const candidateCache = designSystemCandidateCache.get(candidateCacheKey) ?? /* @__PURE__ */ new Map();
2013
+ designSystemCandidateCache.set(candidateCacheKey, candidateCache);
1760
2014
  const candidates = await extractRawCandidates(sources);
1761
2015
  const validCandidates = [];
1762
2016
  const uncachedCandidates = [];
@@ -1767,14 +2021,15 @@ async function extractValidCandidates(options) {
1767
2021
  continue;
1768
2022
  }
1769
2023
  if (cached === false) continue;
1770
- if (designSystem.parseCandidate(rawCandidate).length > 0) {
2024
+ const bareArbitrary = resolveBareArbitraryValueCandidate(rawCandidate, providedOptions.bareArbitraryValues);
2025
+ if (designSystem.parseCandidate(rawCandidate).length > 0 || bareArbitrary && designSystem.parseCandidate(bareArbitrary.canonicalCandidate).length > 0) {
1771
2026
  uncachedCandidates.push(rawCandidate);
1772
2027
  continue;
1773
2028
  }
1774
2029
  candidateCache.set(rawCandidate, false);
1775
2030
  }
1776
2031
  if (uncachedCandidates.length === 0) return validCandidates;
1777
- const validUncachedCandidates = resolveValidTailwindV4Candidates(designSystem, uncachedCandidates);
2032
+ const validUncachedCandidates = resolveValidTailwindV4Candidates(designSystem, uncachedCandidates, providedOptions.bareArbitraryValues === void 0 ? void 0 : { bareArbitraryValues: providedOptions.bareArbitraryValues });
1778
2033
  for (const candidate of uncachedCandidates) {
1779
2034
  const isValid = validUncachedCandidates.has(candidate);
1780
2035
  candidateCache.set(candidate, isValid);
@@ -1982,6 +2237,7 @@ async function collectClassesFromTailwindV4(options) {
1982
2237
  base: firstBase,
1983
2238
  baseFallbacks: designSystemBases.slice(1),
1984
2239
  css,
2240
+ ...v4Options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues },
1985
2241
  ...sources === void 0 ? {} : { sources }
1986
2242
  });
1987
2243
  for (const candidate of candidates) if (options.filter(candidate)) set.add(candidate);
@@ -1992,6 +2248,7 @@ async function collectClassesFromTailwindV4(options) {
1992
2248
  const candidates = await extractValidCandidates({
1993
2249
  cwd: options.projectRoot,
1994
2250
  base: baseForCss,
2251
+ ...v4Options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues },
1995
2252
  ...v4Options.css === void 0 ? {} : { css: v4Options.css },
1996
2253
  ...sources === void 0 ? {} : { sources }
1997
2254
  });
@@ -3599,6 +3856,12 @@ Object.defineProperty(exports, "ValidateCommandError", {
3599
3856
  return ValidateCommandError;
3600
3857
  }
3601
3858
  });
3859
+ Object.defineProperty(exports, "canonicalizeBareArbitraryValueCandidates", {
3860
+ enumerable: true,
3861
+ get: function() {
3862
+ return canonicalizeBareArbitraryValueCandidates;
3863
+ }
3864
+ });
3602
3865
  Object.defineProperty(exports, "classifyValidateError", {
3603
3866
  enumerable: true,
3604
3867
  get: function() {
@@ -3707,6 +3970,12 @@ Object.defineProperty(exports, "normalizeOptions", {
3707
3970
  return normalizeOptions;
3708
3971
  }
3709
3972
  });
3973
+ Object.defineProperty(exports, "replaceBareArbitraryValueSelectors", {
3974
+ enumerable: true,
3975
+ get: function() {
3976
+ return replaceBareArbitraryValueSelectors;
3977
+ }
3978
+ });
3710
3979
  Object.defineProperty(exports, "resolveValidTailwindV4Candidates", {
3711
3980
  enumerable: true,
3712
3981
  get: function() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwindcss-patch",
3
- "version": "9.2.0",
3
+ "version": "9.3.0",
4
4
  "description": "patch tailwindcss for exposing context and extract classes",
5
5
  "author": "ice breaker <1324318532@qq.com>",
6
6
  "license": "MIT",
@@ -66,6 +66,7 @@
66
66
  "@babel/traverse": "^7.29.0",
67
67
  "@babel/types": "^7.29.0",
68
68
  "@tailwindcss/node": "^4.2.4",
69
+ "@tailwindcss/oxide": "^4.2.4",
69
70
  "cac": "6.7.14",
70
71
  "consola": "^3.4.2",
71
72
  "fs-extra": "^11.3.4",
@@ -77,7 +78,6 @@
77
78
  "@tailwindcss-mangle/config": "7.0.1"
78
79
  },
79
80
  "devDependencies": {
80
- "@tailwindcss/oxide": "^4.2.4",
81
81
  "@tailwindcss/postcss": "^4.2.4",
82
82
  "@tailwindcss/vite": "^4.2.4",
83
83
  "tailwindcss": "^4.1.18",
@@ -8,18 +8,41 @@ import type {
8
8
  import { promises as fs } from 'node:fs'
9
9
  import process from 'node:process'
10
10
  import path from 'pathe'
11
+ import {
12
+ type BareArbitraryValueOptions,
13
+ resolveBareArbitraryValueCandidate,
14
+ } from '../v4/bare-arbitrary-values'
11
15
  import { resolveValidTailwindV4Candidates } from '../v4/candidates'
12
16
  import { getTailwindV4DesignSystemCacheKey, loadTailwindV4DesignSystem } from '../v4/node-adapter'
13
17
 
14
18
  let oxideImportPromise: ReturnType<typeof importOxide> | undefined
15
19
  const designSystemCandidateCache = new Map<string, Map<string, boolean>>()
16
20
 
21
+ function createOxideRuntimeDependencyError(cause: unknown) {
22
+ return new Error(
23
+ [
24
+ 'tailwindcss-patch could not load @tailwindcss/oxide, which is required for source candidate scanning.',
25
+ 'This dependency should be installed automatically by tailwindcss-patch.',
26
+ 'Reinstall dependencies without disabling optional dependencies, or install @tailwindcss/oxide@^4.2.4 manually if your package manager omitted it.',
27
+ ].join(' '),
28
+ { cause },
29
+ )
30
+ }
31
+
17
32
  async function importOxide() {
18
- return import('@tailwindcss/oxide')
33
+ try {
34
+ return await import('@tailwindcss/oxide')
35
+ }
36
+ catch (error) {
37
+ throw createOxideRuntimeDependencyError(error)
38
+ }
19
39
  }
20
40
 
21
41
  function getOxideModule() {
22
42
  oxideImportPromise ??= importOxide()
43
+ oxideImportPromise.catch(() => {
44
+ oxideImportPromise = undefined
45
+ })
23
46
  return oxideImportPromise
24
47
  }
25
48
 
@@ -29,6 +52,17 @@ export interface ExtractValidCandidatesOption {
29
52
  baseFallbacks?: string[]
30
53
  css?: string
31
54
  cwd?: string
55
+ bareArbitraryValues?: boolean | BareArbitraryValueOptions
56
+ }
57
+
58
+ function createCandidateCacheKey(
59
+ designSystemKey: string,
60
+ options: Pick<ExtractValidCandidatesOption, 'bareArbitraryValues'>,
61
+ ) {
62
+ if (options.bareArbitraryValues == null || options.bareArbitraryValues === false) {
63
+ return designSystemKey
64
+ }
65
+ return `${designSystemKey}:bare-arbitrary:${JSON.stringify(options.bareArbitraryValues)}`
32
66
  }
33
67
 
34
68
  export async function extractRawCandidatesWithPositions(
@@ -83,8 +117,9 @@ export async function extractValidCandidates(options?: ExtractValidCandidatesOpt
83
117
  }
84
118
  const designSystemKey = getTailwindV4DesignSystemCacheKey(source)
85
119
  const designSystem = await loadTailwindV4DesignSystem(source)
86
- const candidateCache = designSystemCandidateCache.get(designSystemKey) ?? new Map<string, boolean>()
87
- designSystemCandidateCache.set(designSystemKey, candidateCache)
120
+ const candidateCacheKey = createCandidateCacheKey(designSystemKey, providedOptions)
121
+ const candidateCache = designSystemCandidateCache.get(candidateCacheKey) ?? new Map<string, boolean>()
122
+ designSystemCandidateCache.set(candidateCacheKey, candidateCache)
88
123
 
89
124
  const candidates = await extractRawCandidates(sources)
90
125
  const validCandidates: string[] = []
@@ -101,7 +136,11 @@ export async function extractValidCandidates(options?: ExtractValidCandidatesOpt
101
136
  continue
102
137
  }
103
138
 
104
- if (designSystem.parseCandidate(rawCandidate).length > 0) {
139
+ const bareArbitrary = resolveBareArbitraryValueCandidate(rawCandidate, providedOptions.bareArbitraryValues)
140
+ if (
141
+ designSystem.parseCandidate(rawCandidate).length > 0
142
+ || (bareArbitrary && designSystem.parseCandidate(bareArbitrary.canonicalCandidate).length > 0)
143
+ ) {
105
144
  uncachedCandidates.push(rawCandidate)
106
145
  continue
107
146
  }
@@ -113,7 +152,13 @@ export async function extractValidCandidates(options?: ExtractValidCandidatesOpt
113
152
  return validCandidates
114
153
  }
115
154
 
116
- const validUncachedCandidates = resolveValidTailwindV4Candidates(designSystem, uncachedCandidates)
155
+ const validUncachedCandidates = resolveValidTailwindV4Candidates(
156
+ designSystem,
157
+ uncachedCandidates,
158
+ providedOptions.bareArbitraryValues === undefined
159
+ ? undefined
160
+ : { bareArbitraryValues: providedOptions.bareArbitraryValues },
161
+ )
117
162
 
118
163
  for (const candidate of uncachedCandidates) {
119
164
  const isValid = validUncachedCandidates.has(candidate)
@@ -170,6 +170,11 @@ function normalizeTailwindV4Options(
170
170
  cssEntries,
171
171
  sources,
172
172
  hasUserDefinedSources,
173
+ bareArbitraryValues: v4?.bareArbitraryValues === true
174
+ ? {}
175
+ : typeof v4?.bareArbitraryValues === 'object' && v4.bareArbitraryValues !== null
176
+ ? { ...v4.bareArbitraryValues }
177
+ : false,
173
178
  }
174
179
  }
175
180
 
@@ -101,6 +101,11 @@ export interface TailwindV4Options {
101
101
  cssEntries?: string[]
102
102
  /** Overrides the content sources scanned by the oxide scanner. */
103
103
  sources?: SourceEntry[]
104
+ /** Enables UnoCSS-style bare arbitrary values such as `p-10%` and `p-2.5px`. */
105
+ bareArbitraryValues?: boolean | {
106
+ /** Unit allow-list used when detecting bare arbitrary values. */
107
+ units?: string[]
108
+ } | undefined
104
109
  }
105
110
 
106
111
  /**
@@ -185,6 +190,9 @@ export interface NormalizedTailwindV4Options {
185
190
  cssEntries: string[]
186
191
  sources: SourceEntry[]
187
192
  hasUserDefinedSources: boolean
193
+ bareArbitraryValues: false | {
194
+ units?: string[]
195
+ } | undefined
188
196
  }
189
197
 
190
198
  /**
@@ -83,6 +83,7 @@ export async function collectClassesFromTailwindV4(
83
83
  base: firstBase,
84
84
  baseFallbacks: designSystemBases.slice(1),
85
85
  css,
86
+ ...(v4Options.bareArbitraryValues === undefined ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues }),
86
87
  ...(sources === undefined ? {} : { sources }),
87
88
  }
88
89
  const candidates = await extractValidCandidates(extractOptions)
@@ -99,6 +100,7 @@ export async function collectClassesFromTailwindV4(
99
100
  const extractOptions = {
100
101
  cwd: options.projectRoot,
101
102
  base: baseForCss,
103
+ ...(v4Options.bareArbitraryValues === undefined ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues }),
102
104
  ...(v4Options.css === undefined ? {} : { css: v4Options.css }),
103
105
  ...(sources === undefined ? {} : { sources }),
104
106
  }