tailwindcss-patch 9.3.1 → 9.3.3

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.
@@ -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.3";
20
20
  //#endregion
21
21
  //#region src/constants.ts
22
22
  const pkgName = "tailwindcss-patch";
@@ -1323,6 +1323,13 @@ function normalizeExtendLengthUnitsOptions(extend) {
1323
1323
  function normalizeTailwindV4Options(v4, fallbackBase) {
1324
1324
  const configuredBase = v4?.base ? path.resolve(v4.base) : void 0;
1325
1325
  const base = configuredBase ?? fallbackBase;
1326
+ const resolveV4Path = (value) => path.isAbsolute(value) ? path.resolve(value) : path.resolve(fallbackBase, value);
1327
+ const cssSources = Array.isArray(v4?.cssSources) ? v4.cssSources.filter((source) => typeof source?.css === "string").map((source) => ({
1328
+ css: source.css,
1329
+ ...source.base === void 0 ? {} : { base: resolveV4Path(source.base) },
1330
+ ...source.file === void 0 ? {} : { file: resolveV4Path(source.file) },
1331
+ ...source.dependencies === void 0 ? {} : { dependencies: source.dependencies.filter(Boolean).map(resolveV4Path) }
1332
+ })) : [];
1326
1333
  const cssEntries = Array.isArray(v4?.cssEntries) ? v4.cssEntries.filter((entry) => Boolean(entry)).map((entry) => path.resolve(entry)) : [];
1327
1334
  const userSources = v4?.sources;
1328
1335
  const hasUserDefinedSources = Boolean(userSources?.length);
@@ -1335,6 +1342,7 @@ function normalizeTailwindV4Options(v4, fallbackBase) {
1335
1342
  base,
1336
1343
  ...configuredBase === void 0 ? {} : { configuredBase },
1337
1344
  ...v4?.css === void 0 ? {} : { css: v4.css },
1345
+ cssSources,
1338
1346
  cssEntries,
1339
1347
  sources,
1340
1348
  hasUserDefinedSources,
@@ -1477,7 +1485,8 @@ const DEFAULT_BARE_ARBITRARY_VALUE_UNITS = [
1477
1485
  "ms"
1478
1486
  ];
1479
1487
  const NUMBER_RE = /^-?(?:\d+|\d*\.\d+)$/;
1480
- const FUNCTION_VALUE_RE = /^[a-zA-Z_-][a-zA-Z0-9_-]*\(/;
1488
+ const FUNCTION_VALUE_RE = /^[a-z_-][\w-]*\(/i;
1489
+ const HEX_ESCAPE_RE = /^[\da-f]$/i;
1481
1490
  function splitVariantPrefix(candidate) {
1482
1491
  let depth = 0;
1483
1492
  let quote;
@@ -1543,8 +1552,38 @@ function isBalancedFunctionValue(value) {
1543
1552
  }
1544
1553
  return depth === 0 && quote === void 0;
1545
1554
  }
1555
+ function isEscapedAt(value, index) {
1556
+ let slashCount = 0;
1557
+ for (let slashIndex = index - 1; slashIndex >= 0 && value[slashIndex] === "\\"; slashIndex--) slashCount++;
1558
+ return slashCount % 2 === 1;
1559
+ }
1560
+ function isBalancedBareArbitraryBody(value) {
1561
+ let depth = 0;
1562
+ let quote;
1563
+ for (let index = 0; index < value.length; index++) {
1564
+ const character = value[index];
1565
+ if (isEscapedAt(value, index)) continue;
1566
+ if (quote) {
1567
+ if (character === quote) quote = void 0;
1568
+ continue;
1569
+ }
1570
+ if (character === "\"" || character === "'") {
1571
+ quote = character;
1572
+ continue;
1573
+ }
1574
+ if (character === "(" || character === "{") {
1575
+ depth++;
1576
+ continue;
1577
+ }
1578
+ if (character === ")" || character === "}") {
1579
+ depth--;
1580
+ if (depth < 0) return false;
1581
+ }
1582
+ }
1583
+ return depth === 0 && quote === void 0;
1584
+ }
1546
1585
  function isHexColorValue(value) {
1547
- return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6,8})$/.test(value);
1586
+ return /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6,8})$/i.test(value);
1548
1587
  }
1549
1588
  function isQuotedValue(value) {
1550
1589
  const quote = value[0];
@@ -1567,29 +1606,61 @@ function normalizeBareArbitraryValueOptions(options) {
1567
1606
  if (normalizedUnits.length === 0) return;
1568
1607
  return { units: normalizedUnits.sort((a, b) => b.length - a.length) };
1569
1608
  }
1609
+ function normalizeEscapedValue(value) {
1610
+ let result = "";
1611
+ for (let index = 0; index < value.length; index++) {
1612
+ const character = value[index];
1613
+ if (character !== "\\") {
1614
+ result += character;
1615
+ continue;
1616
+ }
1617
+ const nextCharacter = value[index + 1];
1618
+ if (nextCharacter === void 0) {
1619
+ result += character;
1620
+ continue;
1621
+ }
1622
+ if (HEX_ESCAPE_RE.test(nextCharacter)) {
1623
+ let hex = "";
1624
+ let nextIndex = index + 1;
1625
+ while (nextIndex < value.length && hex.length < 6) {
1626
+ const hexCharacter = value[nextIndex];
1627
+ if (hexCharacter === void 0 || !HEX_ESCAPE_RE.test(hexCharacter)) break;
1628
+ hex += hexCharacter;
1629
+ nextIndex++;
1630
+ }
1631
+ if (/[\t\n\f\r ]/.test(value[nextIndex] ?? "")) nextIndex++;
1632
+ const decoded = String.fromCodePoint(Number.parseInt(hex, 16));
1633
+ result += decoded === "_" ? "\\_" : decoded;
1634
+ index = nextIndex - 1;
1635
+ continue;
1636
+ }
1637
+ result += nextCharacter === "_" ? "\\_" : nextCharacter;
1638
+ index++;
1639
+ }
1640
+ return result;
1641
+ }
1570
1642
  function resolveValueWithUnit(body, units) {
1643
+ const value = normalizeEscapedValue(body);
1571
1644
  for (const unit of units) {
1572
- if (!body.endsWith(unit)) continue;
1573
- const numberPart = body.slice(0, -unit.length);
1645
+ if (!value.endsWith(unit)) continue;
1646
+ const numberPart = value.slice(0, -unit.length);
1574
1647
  if (NUMBER_RE.test(numberPart)) return `${numberPart}${unit}`;
1575
1648
  }
1576
1649
  }
1577
1650
  function resolveArbitraryValue(body, units) {
1578
- const withUnit = resolveValueWithUnit(body, units);
1651
+ const value = normalizeEscapedValue(body);
1652
+ const withUnit = resolveValueWithUnit(value, units);
1579
1653
  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;
1654
+ if (isHexColorValue(value)) return value;
1655
+ if (isQuotedValue(value)) return value;
1656
+ if (FUNCTION_VALUE_RE.test(value) && value.endsWith(")") && isBalancedFunctionValue(value)) return value;
1583
1657
  }
1584
1658
  function resolveUtilityAndValue(body, units) {
1585
1659
  let depth = 0;
1586
1660
  let quote;
1587
- for (let index = 1; index < body.length; index++) {
1661
+ for (let index = body.length - 1; index > 0; index--) {
1588
1662
  const character = body[index];
1589
- if (character === "\\") {
1590
- index++;
1591
- continue;
1592
- }
1663
+ if (isEscapedAt(body, index)) continue;
1593
1664
  if (quote) {
1594
1665
  if (character === quote) quote = void 0;
1595
1666
  continue;
@@ -1598,11 +1669,11 @@ function resolveUtilityAndValue(body, units) {
1598
1669
  quote = character;
1599
1670
  continue;
1600
1671
  }
1601
- if (character === "(" || character === "{") {
1672
+ if (character === ")" || character === "}") {
1602
1673
  depth++;
1603
1674
  continue;
1604
1675
  }
1605
- if (character === ")" || character === "}") {
1676
+ if (character === "(" || character === "{") {
1606
1677
  depth = Math.max(0, depth - 1);
1607
1678
  continue;
1608
1679
  }
@@ -1625,6 +1696,7 @@ function resolveBareArbitraryValueCandidate(candidate, options) {
1625
1696
  let normalizedBody = important ? body.slice(1) : body;
1626
1697
  const negative = normalizedBody.startsWith("-") ? "-" : "";
1627
1698
  if (negative) normalizedBody = normalizedBody.slice(1);
1699
+ if (!isBalancedBareArbitraryBody(normalizedBody)) return;
1628
1700
  const resolved = resolveUtilityAndValue(normalizedBody, normalizedOptions.units);
1629
1701
  if (!resolved) return;
1630
1702
  return {
@@ -1658,23 +1730,32 @@ function escapeCssClassName(value) {
1658
1730
  function resolveValidTailwindV4Candidates(designSystem, candidates, options) {
1659
1731
  const validCandidates = /* @__PURE__ */ new Set();
1660
1732
  const parsedCandidates = [];
1661
- const originalCandidateByCanonical = /* @__PURE__ */ new Map();
1733
+ const originalCandidatesByCanonical = /* @__PURE__ */ new Map();
1662
1734
  for (const candidate of candidates) {
1663
1735
  if (!candidate) continue;
1664
1736
  const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options?.bareArbitraryValues);
1665
1737
  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);
1738
+ if (bareArbitrary) {
1739
+ const originalCandidates = originalCandidatesByCanonical.get(candidateToCheck) ?? /* @__PURE__ */ new Set();
1740
+ originalCandidates.add(candidate);
1741
+ originalCandidatesByCanonical.set(candidateToCheck, originalCandidates);
1670
1742
  }
1743
+ if (parsedCandidates.includes(candidateToCheck)) continue;
1744
+ if (designSystem.parseCandidate(candidateToCheck).length > 0) parsedCandidates.push(candidateToCheck);
1671
1745
  }
1672
1746
  if (parsedCandidates.length === 0) return validCandidates;
1673
1747
  const cssByCandidate = designSystem.candidatesToCss(parsedCandidates);
1674
1748
  for (let index = 0; index < parsedCandidates.length; index++) {
1675
1749
  const candidate = parsedCandidates[index];
1676
1750
  const candidateCss = cssByCandidate[index];
1677
- if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) validCandidates.add(originalCandidateByCanonical.get(candidate) ?? candidate);
1751
+ if (candidate && typeof candidateCss === "string" && candidateCss.trim().length > 0) {
1752
+ const originalCandidates = originalCandidatesByCanonical.get(candidate);
1753
+ if (originalCandidates) {
1754
+ for (const originalCandidate of originalCandidates) validCandidates.add(originalCandidate);
1755
+ continue;
1756
+ }
1757
+ validCandidates.add(candidate);
1758
+ }
1678
1759
  }
1679
1760
  return validCandidates;
1680
1761
  }
@@ -1683,16 +1764,34 @@ function createSelectorAliasMap(candidates, options) {
1683
1764
  for (const candidate of candidates) {
1684
1765
  const bareArbitrary = resolveBareArbitraryValueCandidate(candidate, options);
1685
1766
  if (!bareArbitrary) continue;
1686
- aliases.set(escapeCssClassName(bareArbitrary.canonicalCandidate), escapeCssClassName(bareArbitrary.candidate));
1767
+ const canonicalSelector = escapeCssClassName(bareArbitrary.canonicalCandidate);
1768
+ const bareSelectors = aliases.get(canonicalSelector) ?? /* @__PURE__ */ new Set();
1769
+ bareSelectors.add(escapeCssClassName(bareArbitrary.candidate));
1770
+ aliases.set(canonicalSelector, bareSelectors);
1687
1771
  }
1688
1772
  return aliases;
1689
1773
  }
1690
1774
  function replaceBareArbitraryValueSelectors(css, candidates, options) {
1691
1775
  const aliases = createSelectorAliasMap(candidates, options);
1692
1776
  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;
1777
+ if (Array.from(aliases.values()).every((bareSelectors) => bareSelectors.size === 1)) {
1778
+ let result = css;
1779
+ for (const [canonicalSelector, bareSelectors] of aliases) {
1780
+ const bareSelector = Array.from(bareSelectors)[0];
1781
+ if (bareSelector !== void 0) result = result.replaceAll(canonicalSelector, bareSelector);
1782
+ }
1783
+ return result;
1784
+ }
1785
+ const root = postcss.parse(css);
1786
+ root.walkRules((rule) => {
1787
+ let selectors = rule.selectors;
1788
+ for (const [canonicalSelector, bareSelectors] of aliases) selectors = selectors.flatMap((selector) => {
1789
+ if (!selector.includes(canonicalSelector)) return selector;
1790
+ return Array.from(bareSelectors, (bareSelector) => selector.replaceAll(canonicalSelector, bareSelector));
1791
+ });
1792
+ rule.selectors = selectors;
1793
+ });
1794
+ return root.toString();
1696
1795
  }
1697
1796
  function canonicalizeBareArbitraryValueCandidates(candidates, options) {
1698
1797
  return Array.from(candidates, (candidate) => {
@@ -2224,6 +2323,25 @@ async function collectClassesFromTailwindV4(options) {
2224
2323
  negated: source.negated
2225
2324
  }));
2226
2325
  };
2326
+ const addCandidates = async (extractOptions) => {
2327
+ const candidates = await extractValidCandidates(extractOptions);
2328
+ for (const candidate of candidates) if (options.filter(candidate)) set.add(candidate);
2329
+ };
2330
+ if (v4Options.cssSources.length > 0) for (const source of v4Options.cssSources) {
2331
+ const sourceFile = toAbsolute(source.file);
2332
+ const sourceBase = toAbsolute(source.base) ?? (sourceFile ? path.dirname(sourceFile) : resolvedDefaultBase);
2333
+ const designSystemBases = resolvedConfiguredBase && resolvedConfiguredBase !== sourceBase ? [sourceBase, resolvedConfiguredBase] : [sourceBase];
2334
+ const sources = resolveSources(sourceBase);
2335
+ const firstBase = designSystemBases[0] ?? sourceBase;
2336
+ await addCandidates({
2337
+ cwd: options.projectRoot,
2338
+ base: firstBase,
2339
+ baseFallbacks: designSystemBases.slice(1),
2340
+ css: source.css,
2341
+ ...v4Options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues },
2342
+ ...sources === void 0 ? {} : { sources }
2343
+ });
2344
+ }
2227
2345
  if (v4Options.cssEntries.length > 0) for (const entry of v4Options.cssEntries) {
2228
2346
  const filePath = path.isAbsolute(entry) ? entry : path.resolve(options.projectRoot, entry);
2229
2347
  if (!await fs.pathExists(filePath)) continue;
@@ -2232,7 +2350,7 @@ async function collectClassesFromTailwindV4(options) {
2232
2350
  const designSystemBases = resolvedConfiguredBase && resolvedConfiguredBase !== entryDir ? [entryDir, resolvedConfiguredBase] : [entryDir];
2233
2351
  const sources = resolveSources(resolvedConfiguredBase ?? entryDir);
2234
2352
  const firstBase = designSystemBases[0] ?? entryDir;
2235
- const candidates = await extractValidCandidates({
2353
+ await addCandidates({
2236
2354
  cwd: options.projectRoot,
2237
2355
  base: firstBase,
2238
2356
  baseFallbacks: designSystemBases.slice(1),
@@ -2240,19 +2358,17 @@ async function collectClassesFromTailwindV4(options) {
2240
2358
  ...v4Options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues },
2241
2359
  ...sources === void 0 ? {} : { sources }
2242
2360
  });
2243
- for (const candidate of candidates) if (options.filter(candidate)) set.add(candidate);
2244
2361
  }
2245
- else {
2362
+ else if (v4Options.cssSources.length === 0) {
2246
2363
  const baseForCss = resolvedConfiguredBase ?? resolvedDefaultBase;
2247
2364
  const sources = resolveSources(baseForCss);
2248
- const candidates = await extractValidCandidates({
2365
+ await addCandidates({
2249
2366
  cwd: options.projectRoot,
2250
2367
  base: baseForCss,
2251
2368
  ...v4Options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues },
2252
2369
  ...v4Options.css === void 0 ? {} : { css: v4Options.css },
2253
2370
  ...sources === void 0 ? {} : { sources }
2254
2371
  });
2255
- for (const candidate of candidates) if (options.filter(candidate)) set.add(candidate);
2256
2372
  }
2257
2373
  return set;
2258
2374
  }
@@ -1,9 +1,10 @@
1
- import { PackageInfo, PackageResolvingOptions } from "local-pkg";
2
- import * as _$consola from "consola";
3
- import { Node, Rule } from "postcss";
4
1
  import { CAC, Command } from "cac";
2
+ import { PackageInfo, PackageResolvingOptions } from "local-pkg";
5
3
  import { SourceEntry } from "@tailwindcss/oxide";
4
+ import { Node, Rule } from "postcss";
6
5
  import { Config } from "tailwindcss";
6
+ import * as _$consola from "consola";
7
+
7
8
  //#region src/cache/types.d.ts
8
9
  declare const CACHE_SCHEMA_VERSION = 2;
9
10
  declare const CACHE_FINGERPRINT_VERSION = 1;
@@ -158,6 +159,78 @@ interface PatchStatusReport {
158
159
  entries: PatchStatusEntry[];
159
160
  }
160
161
  //#endregion
162
+ //#region src/v4/types.d.ts
163
+ interface TailwindV4SourceOptions {
164
+ projectRoot?: string;
165
+ cwd?: string;
166
+ base?: string;
167
+ baseFallbacks?: string[];
168
+ css?: string;
169
+ cssSources?: TailwindV4CssSource[];
170
+ cssEntries?: string[];
171
+ packageName?: string;
172
+ }
173
+ interface TailwindV4CssSource {
174
+ css: string;
175
+ base?: string;
176
+ file?: string;
177
+ dependencies?: string[];
178
+ }
179
+ interface TailwindV4ResolvedSource {
180
+ projectRoot: string;
181
+ base: string;
182
+ baseFallbacks: string[];
183
+ css: string;
184
+ dependencies: string[];
185
+ }
186
+ interface TailwindV4CandidateSource {
187
+ content: string;
188
+ extension?: string;
189
+ }
190
+ interface TailwindV4GenerateOptions {
191
+ candidates?: Iterable<string>;
192
+ sources?: TailwindV4CandidateSource[];
193
+ /**
194
+ * Enables UnoCSS-style bare arbitrary values such as `p-10%` and `p-2.5px`.
195
+ */
196
+ bareArbitraryValues?: boolean | {
197
+ units?: string[];
198
+ };
199
+ /**
200
+ * 扫描文件系统 source entries 中的候选类名。
201
+ *
202
+ * - `true`:使用 Tailwind v4 编译入口解析出的 `@source` 列表。
203
+ * - `TailwindV4SourcePattern[]`:使用调用方显式传入的 source 列表。
204
+ */
205
+ scanSources?: boolean | TailwindV4SourcePattern[];
206
+ }
207
+ interface TailwindV4SourcePattern {
208
+ base: string;
209
+ pattern: string;
210
+ negated: boolean;
211
+ }
212
+ interface TailwindV4GenerateResult {
213
+ css: string;
214
+ classSet: Set<string>;
215
+ rawCandidates: Set<string>;
216
+ dependencies: string[];
217
+ sources: TailwindV4SourcePattern[];
218
+ root: null | 'none' | {
219
+ base: string;
220
+ pattern: string;
221
+ };
222
+ }
223
+ interface TailwindV4DesignSystem {
224
+ parseCandidate: (candidate: string) => unknown[];
225
+ candidatesToCss: (candidates: string[]) => Array<string | null | undefined>;
226
+ }
227
+ interface TailwindV4Engine {
228
+ source: TailwindV4ResolvedSource;
229
+ loadDesignSystem: () => Promise<TailwindV4DesignSystem>;
230
+ validateCandidates: (candidates: Iterable<string>) => Promise<Set<string>>;
231
+ generate: (options?: TailwindV4GenerateOptions) => Promise<TailwindV4GenerateResult>;
232
+ }
233
+ //#endregion
161
234
  //#region src/options/types.d.ts
162
235
  type CacheStrategy = 'merge' | 'overwrite';
163
236
  type CacheDriver = 'file' | 'memory' | 'noop';
@@ -245,6 +318,8 @@ interface TailwindV4Options {
245
318
  base?: string;
246
319
  /** Raw CSS passed directly to the v4 design system. */
247
320
  css?: string;
321
+ /** 构建器在 CSS 落盘前捕获的内存 CSS 入口。 */
322
+ cssSources?: TailwindV4CssSource[];
248
323
  /** Set of CSS entry files that should be scanned for `@config` directives. */
249
324
  cssEntries?: string[];
250
325
  /** Overrides the content sources scanned by the oxide scanner. */
@@ -327,6 +402,7 @@ interface NormalizedTailwindV4Options {
327
402
  base: string;
328
403
  configuredBase?: string;
329
404
  css?: string;
405
+ cssSources: TailwindV4CssSource[];
330
406
  cssEntries: string[];
331
407
  sources: SourceEntry[];
332
408
  hasUserDefinedSources: boolean;
@@ -698,4 +774,4 @@ declare class ValidateCommandError extends Error {
698
774
  constructor(summary: ValidateFailureSummary, options?: ErrorOptions);
699
775
  }
700
776
  //#endregion
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 };
777
+ export { TailwindV4SourceOptions 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, TailwindV4DesignSystem as J, TailwindV4CandidateSource as K, ExtendLengthUnitsOptions as L, normalizeOptions as M, ApplyOptions as N, extractRawCandidatesWithPositions as O, CacheOptions as P, TailwindV4ResolvedSource as Q, ExtractOptions as R, MIGRATION_REPORT_KIND as S, TailwindcssPatcher as T, TailwindV2Options as U, TailwindCssOptions as V, TailwindV3Options as W, TailwindV4GenerateOptions as X, TailwindV4Engine as Y, TailwindV4GenerateResult as Z, ConfigFileMigrationEntry as _, CacheContextMetadata as _t, ValidateFailureSummary as a, PatchStatusReport as at, RestoreConfigFilesOptions as b, CacheReadResult as bt, TailwindcssPatchCliMountOptions as c, TailwindTokenFileKey as ct, TailwindcssPatchCommandContext as d, TailwindcssClassCache as dt, ExtractResult as et, TailwindcssPatchCommandHandler as f, TailwindcssRuntimeContext as ft, tailwindcssPatchCommands as g, CacheContextDescriptor as gt, TailwindcssPatchCommandOptions as h, CacheClearScope as ht, ValidateFailureReason as i, PatchStatusEntry as it, BareArbitraryValueOptions as j, extractValidCandidates as k, TailwindcssPatchCliOptions as l, TailwindTokenLocation as lt, TailwindcssPatchCommandOptionDefinition as m, CacheClearResult as mt, VALIDATE_FAILURE_REASONS as n, PatchCheckStatus as nt, ValidateJsonFailurePayload as o, TailwindPatchRuntime as ot, TailwindcssPatchCommandHandlerMap as p, CacheClearOptions as pt, TailwindV4CssSource as q, ValidateCommandError as r, PatchName as rt, ValidateJsonSuccessPayload as s, TailwindTokenByFileMap as st, VALIDATE_EXIT_CODES as t, ILengthUnitsPatchOptions as tt, TailwindcssPatchCommand as u, TailwindTokenReport as ut, ConfigFileMigrationReport as v, CacheIndexFileV2 as vt, logger as w, RestoreConfigFilesResult as x, MigrateConfigFilesOptions as y, CacheReadMeta as yt, NormalizedCacheOptions as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwindcss-patch",
3
- "version": "9.3.1",
3
+ "version": "9.3.3",
4
4
  "description": "patch tailwindcss for exposing context and extract classes",
5
5
  "author": "ice breaker <1324318532@qq.com>",
6
6
  "license": "MIT",
@@ -105,6 +105,7 @@ export type { TailwindCssPatchOptions } from './config'
105
105
  export * from './types'
106
106
  export type {
107
107
  TailwindV4CandidateSource,
108
+ TailwindV4CssSource,
108
109
  TailwindV4DesignSystem,
109
110
  TailwindV4Engine,
110
111
  TailwindV4GenerateOptions,
package/src/index.ts CHANGED
@@ -60,6 +60,7 @@ export {
60
60
  } from './v4'
61
61
  export type {
62
62
  TailwindV4CandidateSource,
63
+ TailwindV4CssSource,
63
64
  TailwindV4DesignSystem,
64
65
  TailwindV4Engine,
65
66
  TailwindV4GenerateOptions,
@@ -148,6 +148,19 @@ function normalizeTailwindV4Options(
148
148
  ): NormalizedTailwindV4Options {
149
149
  const configuredBase = v4?.base ? path.resolve(v4.base) : undefined
150
150
  const base = configuredBase ?? fallbackBase
151
+ const resolveV4Path = (value: string) => path.isAbsolute(value) ? path.resolve(value) : path.resolve(fallbackBase, value)
152
+ const cssSources = Array.isArray(v4?.cssSources)
153
+ ? v4!.cssSources
154
+ .filter(source => typeof source?.css === 'string')
155
+ .map(source => ({
156
+ css: source.css,
157
+ ...(source.base === undefined ? {} : { base: resolveV4Path(source.base) }),
158
+ ...(source.file === undefined ? {} : { file: resolveV4Path(source.file) }),
159
+ ...(source.dependencies === undefined
160
+ ? {}
161
+ : { dependencies: source.dependencies.filter(Boolean).map(resolveV4Path) }),
162
+ }))
163
+ : []
151
164
  const cssEntries = Array.isArray(v4?.cssEntries)
152
165
  ? v4!.cssEntries.filter((entry): entry is string => Boolean(entry)).map(entry => path.resolve(entry))
153
166
  : []
@@ -167,6 +180,7 @@ function normalizeTailwindV4Options(
167
180
  base,
168
181
  ...(configuredBase === undefined ? {} : { configuredBase }),
169
182
  ...(v4?.css === undefined ? {} : { css: v4.css }),
183
+ cssSources,
170
184
  cssEntries,
171
185
  sources,
172
186
  hasUserDefinedSources,
@@ -1,6 +1,7 @@
1
1
  import type { SourceEntry } from '@tailwindcss/oxide'
2
2
  import type { PackageResolvingOptions } from 'local-pkg'
3
3
  import type { ILengthUnitsPatchOptions } from '../types'
4
+ import type { TailwindV4CssSource } from '../v4/types'
4
5
 
5
6
  export type CacheStrategy = 'merge' | 'overwrite'
6
7
  export type CacheDriver = 'file' | 'memory' | 'noop'
@@ -97,6 +98,8 @@ export interface TailwindV4Options {
97
98
  base?: string
98
99
  /** Raw CSS passed directly to the v4 design system. */
99
100
  css?: string
101
+ /** 构建器在 CSS 落盘前捕获的内存 CSS 入口。 */
102
+ cssSources?: TailwindV4CssSource[]
100
103
  /** Set of CSS entry files that should be scanned for `@config` directives. */
101
104
  cssEntries?: string[]
102
105
  /** Overrides the content sources scanned by the oxide scanner. */
@@ -187,6 +190,7 @@ export interface NormalizedTailwindV4Options {
187
190
  base: string
188
191
  configuredBase?: string
189
192
  css?: string
193
+ cssSources: TailwindV4CssSource[]
190
194
  cssEntries: string[]
191
195
  sources: SourceEntry[]
192
196
  hasUserDefinedSources: boolean
@@ -1,3 +1,4 @@
1
+ import type { ExtractValidCandidatesOption } from '../extraction/candidate-extractor'
1
2
  import type { NormalizedTailwindCssPatchOptions } from '../options/types'
2
3
  import type { TailwindcssRuntimeContext } from '../types'
3
4
  import process from 'node:process'
@@ -63,6 +64,35 @@ export async function collectClassesFromTailwindV4(
63
64
  negated: source.negated,
64
65
  }))
65
66
  }
67
+ const addCandidates = async (extractOptions: ExtractValidCandidatesOption) => {
68
+ const candidates = await extractValidCandidates(extractOptions)
69
+ for (const candidate of candidates) {
70
+ if (options.filter(candidate)) {
71
+ set.add(candidate)
72
+ }
73
+ }
74
+ }
75
+
76
+ if (v4Options.cssSources.length > 0) {
77
+ for (const source of v4Options.cssSources) {
78
+ const sourceFile = toAbsolute(source.file)
79
+ const sourceBase = toAbsolute(source.base)
80
+ ?? (sourceFile ? path.dirname(sourceFile) : resolvedDefaultBase)
81
+ const designSystemBases = resolvedConfiguredBase && resolvedConfiguredBase !== sourceBase
82
+ ? [sourceBase, resolvedConfiguredBase]
83
+ : [sourceBase]
84
+ const sources = resolveSources(sourceBase)
85
+ const firstBase = designSystemBases[0] ?? sourceBase
86
+ await addCandidates({
87
+ cwd: options.projectRoot,
88
+ base: firstBase,
89
+ baseFallbacks: designSystemBases.slice(1),
90
+ css: source.css,
91
+ ...(v4Options.bareArbitraryValues === undefined ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues }),
92
+ ...(sources === undefined ? {} : { sources }),
93
+ })
94
+ }
95
+ }
66
96
 
67
97
  if (v4Options.cssEntries.length > 0) {
68
98
  for (const entry of v4Options.cssEntries) {
@@ -86,15 +116,10 @@ export async function collectClassesFromTailwindV4(
86
116
  ...(v4Options.bareArbitraryValues === undefined ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues }),
87
117
  ...(sources === undefined ? {} : { sources }),
88
118
  }
89
- const candidates = await extractValidCandidates(extractOptions)
90
- for (const candidate of candidates) {
91
- if (options.filter(candidate)) {
92
- set.add(candidate)
93
- }
94
- }
119
+ await addCandidates(extractOptions)
95
120
  }
96
121
  }
97
- else {
122
+ else if (v4Options.cssSources.length === 0) {
98
123
  const baseForCss = resolvedConfiguredBase ?? resolvedDefaultBase
99
124
  const sources = resolveSources(baseForCss)
100
125
  const extractOptions = {
@@ -104,12 +129,7 @@ export async function collectClassesFromTailwindV4(
104
129
  ...(v4Options.css === undefined ? {} : { css: v4Options.css }),
105
130
  ...(sources === undefined ? {} : { sources }),
106
131
  }
107
- const candidates = await extractValidCandidates(extractOptions)
108
- for (const candidate of candidates) {
109
- if (options.filter(candidate)) {
110
- set.add(candidate)
111
- }
112
- }
132
+ await addCandidates(extractOptions)
113
133
  }
114
134
 
115
135
  return set