tailwindcss-patch 9.1.0 → 9.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const require_chunk = require("./chunk-8l464Juk.js");
2
- const require_validate = require("./validate-BuhhSYBe.js");
3
- const require_index_bundle = require("./index.bundle-C4Y53Ygf.js");
2
+ const require_validate = require("./validate-Bo-Ua7Kg.js");
3
+ const require_index_bundle = require("./index.bundle-BkqJb7rw.js");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_chunk.__toESM(node_process);
6
6
  //#region src/cli.bundle.ts
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { k as logger, r as ValidateCommandError } from "./validate-4FCU-Ql3.mjs";
2
- import { t as createTailwindcssPatchCli } from "./index.bundle-0Fe7Jx8V.mjs";
1
+ import { k as logger, r as ValidateCommandError } from "./validate-CceFRP7h.mjs";
2
+ import { t as createTailwindcssPatchCli } from "./index.bundle-Dnh0a6WS.mjs";
3
3
  import process from "node:process";
4
4
  //#region src/cli.bundle.ts
5
5
  async function main() {
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../chunk-8l464Juk.js");
3
- const require_validate = require("../validate-BuhhSYBe.js");
3
+ const require_validate = require("../validate-Bo-Ua7Kg.js");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_chunk.__toESM(node_process);
6
6
  let fs_extra = require("fs-extra");
@@ -1,4 +1,4 @@
1
- import { E as loadWorkspaceConfigModule, T as loadPatchOptionsForWorkspace, a as tailwindcssPatchCommands, b as groupTokensByFile, i as classifyValidateError, k as logger, n as VALIDATE_FAILURE_REASONS, o as migrateConfigFiles, r as ValidateCommandError, s as restoreConfigFiles, t as VALIDATE_EXIT_CODES, u as TailwindcssPatcher } from "../validate-4FCU-Ql3.mjs";
1
+ import { E as loadWorkspaceConfigModule, T as loadPatchOptionsForWorkspace, a as tailwindcssPatchCommands, b as groupTokensByFile, i as classifyValidateError, k as logger, n as VALIDATE_FAILURE_REASONS, o as migrateConfigFiles, r as ValidateCommandError, s as restoreConfigFiles, t as VALIDATE_EXIT_CODES, u as TailwindcssPatcher } from "../validate-CceFRP7h.mjs";
2
2
  import process from "node:process";
3
3
  import fs from "fs-extra";
4
4
  import path from "pathe";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-8l464Juk.js");
2
- const require_validate = require("./validate-BuhhSYBe.js");
2
+ const require_validate = require("./validate-Bo-Ua7Kg.js");
3
3
  let node_module = require("node:module");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_chunk.__toESM(node_process);
@@ -7,13 +7,20 @@ let pathe = require("pathe");
7
7
  pathe = require_chunk.__toESM(pathe);
8
8
  let node_fs = require("node:fs");
9
9
  //#region src/v4/engine.ts
10
- async function collectRawCandidates(source, options) {
10
+ function resolveScanSources(options, compiledSources) {
11
+ if (Array.isArray(options?.scanSources)) return options.scanSources;
12
+ if (options?.scanSources === true) return compiledSources;
13
+ return [];
14
+ }
15
+ async function collectRawCandidates(source, options, compiledSources = []) {
11
16
  const rawCandidates = /* @__PURE__ */ new Set();
12
17
  for (const candidate of options?.candidates ?? []) rawCandidates.add(candidate);
13
18
  for (const candidateSource of options?.sources ?? []) {
14
19
  const candidates = await require_validate.extractRawCandidatesWithPositions(candidateSource.content, candidateSource.extension);
15
20
  for (const candidate of candidates) rawCandidates.add(candidate.rawCandidate);
16
21
  }
22
+ const filesystemSources = resolveScanSources(options, compiledSources);
23
+ if (filesystemSources.length > 0) for (const candidate of await require_validate.extractRawCandidates(filesystemSources)) rawCandidates.add(candidate);
17
24
  const inlineSources = require_validate.extractTailwindV4InlineSourceCandidates(source.css);
18
25
  for (const candidate of inlineSources.included) rawCandidates.add(candidate);
19
26
  for (const candidate of inlineSources.excluded) rawCandidates.delete(candidate);
@@ -29,11 +36,11 @@ function createTailwindV4Engine(source) {
29
36
  return require_validate.resolveValidTailwindV4Candidates(await require_validate.loadTailwindV4DesignSystem(source), candidates);
30
37
  },
31
38
  async generate(options) {
32
- const rawCandidates = await collectRawCandidates(source, options);
39
+ const { compiled, dependencies } = await require_validate.compileTailwindV4Source(source);
40
+ const rawCandidates = await collectRawCandidates(source, options, compiled.sources);
33
41
  const classSet = require_validate.resolveValidTailwindV4Candidates(await require_validate.loadTailwindV4DesignSystem(source), rawCandidates);
34
42
  const inlineSources = require_validate.extractTailwindV4InlineSourceCandidates(source.css);
35
43
  for (const candidate of inlineSources.excluded) classSet.delete(candidate);
36
- const { compiled, dependencies } = await require_validate.compileTailwindV4Source(source);
37
44
  return {
38
45
  css: compiled.build(Array.from(classSet)),
39
46
  classSet,
@@ -1,16 +1,23 @@
1
- import { C as extractTailwindV4InlineSourceCandidates, D as normalizeOptions, S as loadTailwindV4DesignSystem, v as extractRawCandidatesWithPositions, w as resolveValidTailwindV4Candidates, x as compileTailwindV4Source } from "./validate-4FCU-Ql3.mjs";
1
+ import { C as extractTailwindV4InlineSourceCandidates, D as normalizeOptions, S as loadTailwindV4DesignSystem, _ as extractRawCandidates, v as extractRawCandidatesWithPositions, w as resolveValidTailwindV4Candidates, x as compileTailwindV4Source } from "./validate-CceFRP7h.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import process from "node:process";
4
4
  import path from "pathe";
5
5
  import { promises } from "node:fs";
6
6
  //#region src/v4/engine.ts
7
- async function collectRawCandidates(source, options) {
7
+ function resolveScanSources(options, compiledSources) {
8
+ if (Array.isArray(options?.scanSources)) return options.scanSources;
9
+ if (options?.scanSources === true) return compiledSources;
10
+ return [];
11
+ }
12
+ async function collectRawCandidates(source, options, compiledSources = []) {
8
13
  const rawCandidates = /* @__PURE__ */ new Set();
9
14
  for (const candidate of options?.candidates ?? []) rawCandidates.add(candidate);
10
15
  for (const candidateSource of options?.sources ?? []) {
11
16
  const candidates = await extractRawCandidatesWithPositions(candidateSource.content, candidateSource.extension);
12
17
  for (const candidate of candidates) rawCandidates.add(candidate.rawCandidate);
13
18
  }
19
+ const filesystemSources = resolveScanSources(options, compiledSources);
20
+ if (filesystemSources.length > 0) for (const candidate of await extractRawCandidates(filesystemSources)) rawCandidates.add(candidate);
14
21
  const inlineSources = extractTailwindV4InlineSourceCandidates(source.css);
15
22
  for (const candidate of inlineSources.included) rawCandidates.add(candidate);
16
23
  for (const candidate of inlineSources.excluded) rawCandidates.delete(candidate);
@@ -26,11 +33,11 @@ function createTailwindV4Engine(source) {
26
33
  return resolveValidTailwindV4Candidates(await loadTailwindV4DesignSystem(source), candidates);
27
34
  },
28
35
  async generate(options) {
29
- const rawCandidates = await collectRawCandidates(source, options);
36
+ const { compiled, dependencies } = await compileTailwindV4Source(source);
37
+ const rawCandidates = await collectRawCandidates(source, options, compiled.sources);
30
38
  const classSet = resolveValidTailwindV4Candidates(await loadTailwindV4DesignSystem(source), rawCandidates);
31
39
  const inlineSources = extractTailwindV4InlineSourceCandidates(source.css);
32
40
  for (const candidate of inlineSources.excluded) classSet.delete(candidate);
33
- const { compiled, dependencies } = await compileTailwindV4Source(source);
34
41
  return {
35
42
  css: compiled.build(Array.from(classSet)),
36
43
  classSet,
package/dist/index.d.mts CHANGED
@@ -101,6 +101,13 @@ interface TailwindV4CandidateSource {
101
101
  interface TailwindV4GenerateOptions {
102
102
  candidates?: Iterable<string>;
103
103
  sources?: TailwindV4CandidateSource[];
104
+ /**
105
+ * 扫描文件系统 source entries 中的候选类名。
106
+ *
107
+ * - `true`:使用 Tailwind v4 编译入口解析出的 `@source` 列表。
108
+ * - `TailwindV4SourcePattern[]`:使用调用方显式传入的 source 列表。
109
+ */
110
+ scanSources?: boolean | TailwindV4SourcePattern[];
104
111
  }
105
112
  interface TailwindV4SourcePattern {
106
113
  base: string;
package/dist/index.d.ts CHANGED
@@ -101,6 +101,13 @@ interface TailwindV4CandidateSource {
101
101
  interface TailwindV4GenerateOptions {
102
102
  candidates?: Iterable<string>;
103
103
  sources?: TailwindV4CandidateSource[];
104
+ /**
105
+ * 扫描文件系统 source entries 中的候选类名。
106
+ *
107
+ * - `true`:使用 Tailwind v4 编译入口解析出的 `@source` 列表。
108
+ * - `TailwindV4SourcePattern[]`:使用调用方显式传入的 source 列表。
109
+ */
110
+ scanSources?: boolean | TailwindV4SourcePattern[];
104
111
  }
105
112
  interface TailwindV4SourcePattern {
106
113
  base: string;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_validate = require("./validate-BuhhSYBe.js");
3
- const require_index_bundle = require("./index.bundle-C4Y53Ygf.js");
2
+ const require_validate = require("./validate-Bo-Ua7Kg.js");
3
+ const require_index_bundle = require("./index.bundle-BkqJb7rw.js");
4
4
  exports.CacheStore = require_validate.CacheStore;
5
5
  exports.MIGRATION_REPORT_KIND = require_validate.MIGRATION_REPORT_KIND;
6
6
  exports.MIGRATION_REPORT_SCHEMA_VERSION = require_validate.MIGRATION_REPORT_SCHEMA_VERSION;
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { D as normalizeOptions, O as CacheStore, S as loadTailwindV4DesignSystem, _ as extractRawCandidates, a as tailwindcssPatchCommands, b as groupTokensByFile, c as MIGRATION_REPORT_KIND, d as getPatchStatusReport, f as runTailwindBuild, g as extractProjectCandidatesWithPositions, h as collectClassesFromTailwindV4, k as logger, l as MIGRATION_REPORT_SCHEMA_VERSION, m as collectClassesFromContexts, n as VALIDATE_FAILURE_REASONS, o as migrateConfigFiles, p as loadRuntimeContexts, r as ValidateCommandError, s as restoreConfigFiles, t as VALIDATE_EXIT_CODES, u as TailwindcssPatcher, v as extractRawCandidatesWithPositions, w as resolveValidTailwindV4Candidates, y as extractValidCandidates } from "./validate-4FCU-Ql3.mjs";
2
- import { a as resolveTailwindV4SourceFromPatchOptions, i as resolveTailwindV4Source, n as defineConfig, o as createTailwindV4Engine, r as mountTailwindcssPatchCommands, t as createTailwindcssPatchCli } from "./index.bundle-0Fe7Jx8V.mjs";
1
+ import { D as normalizeOptions, O as CacheStore, S as loadTailwindV4DesignSystem, _ as extractRawCandidates, a as tailwindcssPatchCommands, b as groupTokensByFile, c as MIGRATION_REPORT_KIND, d as getPatchStatusReport, f as runTailwindBuild, g as extractProjectCandidatesWithPositions, h as collectClassesFromTailwindV4, k as logger, l as MIGRATION_REPORT_SCHEMA_VERSION, m as collectClassesFromContexts, n as VALIDATE_FAILURE_REASONS, o as migrateConfigFiles, p as loadRuntimeContexts, r as ValidateCommandError, s as restoreConfigFiles, t as VALIDATE_EXIT_CODES, u as TailwindcssPatcher, v as extractRawCandidatesWithPositions, w as resolveValidTailwindV4Candidates, y as extractValidCandidates } from "./validate-CceFRP7h.mjs";
2
+ import { a as resolveTailwindV4SourceFromPatchOptions, i as resolveTailwindV4Source, n as defineConfig, o as createTailwindV4Engine, r as mountTailwindcssPatchCommands, t as createTailwindcssPatchCli } from "./index.bundle-Dnh0a6WS.mjs";
3
3
  export { CacheStore, MIGRATION_REPORT_KIND, MIGRATION_REPORT_SCHEMA_VERSION, TailwindcssPatcher, VALIDATE_EXIT_CODES, VALIDATE_FAILURE_REASONS, ValidateCommandError, collectClassesFromContexts, collectClassesFromTailwindV4, createTailwindV4Engine, createTailwindcssPatchCli, defineConfig, extractProjectCandidatesWithPositions, extractRawCandidates, extractRawCandidatesWithPositions, extractValidCandidates, getPatchStatusReport, groupTokensByFile, loadRuntimeContexts, loadTailwindV4DesignSystem, logger, migrateConfigFiles, mountTailwindcssPatchCommands, normalizeOptions, resolveTailwindV4Source, resolveTailwindV4SourceFromPatchOptions, resolveValidTailwindV4Candidates, restoreConfigFiles, runTailwindBuild, tailwindcssPatchCommands };
@@ -23,7 +23,7 @@ _babel_traverse = require_chunk.__toESM(_babel_traverse);
23
23
  let _babel_parser = require("@babel/parser");
24
24
  let tailwindcss_config = require("tailwindcss-config");
25
25
  //#region package.json
26
- var version = "9.1.0";
26
+ var version = "9.2.1";
27
27
  //#endregion
28
28
  //#region src/constants.ts
29
29
  const pkgName = "tailwindcss-patch";
@@ -1592,6 +1592,31 @@ function unique(values) {
1592
1592
  function createRequireBase(base) {
1593
1593
  return pathe.default.join(base, "package.json");
1594
1594
  }
1595
+ function isRelativeSpecifier(id) {
1596
+ return id.startsWith("./") || id.startsWith("../") || id === "." || id === "..";
1597
+ }
1598
+ function isAbsoluteSpecifier(id) {
1599
+ return pathe.default.isAbsolute(id);
1600
+ }
1601
+ function isCssSpecifier(id) {
1602
+ return pathe.default.extname(id) === ".css";
1603
+ }
1604
+ function createCssResolutionCandidates(id) {
1605
+ if (isCssSpecifier(id)) return [id];
1606
+ return [`${id}/index.css`, id];
1607
+ }
1608
+ function createFallbackCssResolver(baseCandidates) {
1609
+ const bases = unique(baseCandidates);
1610
+ return async (id) => {
1611
+ if (isRelativeSpecifier(id) || isAbsoluteSpecifier(id)) return;
1612
+ for (const base of bases) {
1613
+ const requireFromBase = (0, node_module.createRequire)(createRequireBase(base));
1614
+ for (const candidate of createCssResolutionCandidates(id)) try {
1615
+ return requireFromBase.resolve(candidate);
1616
+ } catch {}
1617
+ }
1618
+ };
1619
+ }
1595
1620
  async function importResolvedModule(resolved) {
1596
1621
  return import((0, node_url.pathToFileURL)(resolved).href);
1597
1622
  }
@@ -1656,31 +1681,53 @@ async function loadTailwindV4DesignSystem(source) {
1656
1681
  return promise;
1657
1682
  }
1658
1683
  async function compileTailwindV4Source(source) {
1659
- const node = await loadTailwindV4NodeModule([
1660
- source.projectRoot,
1661
- source.base,
1662
- ...source.baseFallbacks
1663
- ]);
1664
- const dependencies = new Set(source.dependencies);
1665
- return {
1666
- compiled: await node.compile(source.css, {
1667
- base: source.base,
1668
- onDependency(dependency) {
1669
- dependencies.add(pathe.default.resolve(dependency));
1670
- }
1671
- }),
1672
- dependencies
1673
- };
1684
+ const bases = unique([source.base, ...source.baseFallbacks]);
1685
+ if (bases.length === 0) throw new Error("No base directories provided for Tailwind CSS v4 compiler.");
1686
+ const node = await loadTailwindV4NodeModule([source.projectRoot, ...bases]);
1687
+ let lastError;
1688
+ for (const base of bases) {
1689
+ const dependencies = new Set(source.dependencies);
1690
+ try {
1691
+ return {
1692
+ compiled: await node.compile(source.css, {
1693
+ base,
1694
+ customCssResolver: createFallbackCssResolver([source.projectRoot, ...bases]),
1695
+ onDependency(dependency) {
1696
+ dependencies.add(pathe.default.resolve(dependency));
1697
+ }
1698
+ }),
1699
+ dependencies
1700
+ };
1701
+ } catch (error) {
1702
+ lastError = error;
1703
+ }
1704
+ }
1705
+ if (lastError instanceof Error) throw lastError;
1706
+ throw new Error("Failed to compile Tailwind CSS v4 source.");
1674
1707
  }
1675
1708
  //#endregion
1676
1709
  //#region src/extraction/candidate-extractor.ts
1677
1710
  let oxideImportPromise;
1678
1711
  const designSystemCandidateCache = /* @__PURE__ */ new Map();
1712
+ function createOxideRuntimeDependencyError(cause) {
1713
+ return new Error([
1714
+ "tailwindcss-patch could not load @tailwindcss/oxide, which is required for source candidate scanning.",
1715
+ "This dependency should be installed automatically by tailwindcss-patch.",
1716
+ "Reinstall dependencies without disabling optional dependencies, or install @tailwindcss/oxide@^4.2.4 manually if your package manager omitted it."
1717
+ ].join(" "), { cause });
1718
+ }
1679
1719
  async function importOxide() {
1680
- return import("@tailwindcss/oxide");
1720
+ try {
1721
+ return await import("@tailwindcss/oxide");
1722
+ } catch (error) {
1723
+ throw createOxideRuntimeDependencyError(error);
1724
+ }
1681
1725
  }
1682
1726
  function getOxideModule() {
1683
1727
  oxideImportPromise ??= importOxide();
1728
+ oxideImportPromise.catch(() => {
1729
+ oxideImportPromise = void 0;
1730
+ });
1684
1731
  return oxideImportPromise;
1685
1732
  }
1686
1733
  async function extractRawCandidatesWithPositions(content, extension = "html") {
@@ -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.1.0";
19
+ var version = "9.2.1";
20
20
  //#endregion
21
21
  //#region src/constants.ts
22
22
  const pkgName = "tailwindcss-patch";
@@ -1590,6 +1590,31 @@ function unique(values) {
1590
1590
  function createRequireBase(base) {
1591
1591
  return path.join(base, "package.json");
1592
1592
  }
1593
+ function isRelativeSpecifier(id) {
1594
+ return id.startsWith("./") || id.startsWith("../") || id === "." || id === "..";
1595
+ }
1596
+ function isAbsoluteSpecifier(id) {
1597
+ return path.isAbsolute(id);
1598
+ }
1599
+ function isCssSpecifier(id) {
1600
+ return path.extname(id) === ".css";
1601
+ }
1602
+ function createCssResolutionCandidates(id) {
1603
+ if (isCssSpecifier(id)) return [id];
1604
+ return [`${id}/index.css`, id];
1605
+ }
1606
+ function createFallbackCssResolver(baseCandidates) {
1607
+ const bases = unique(baseCandidates);
1608
+ return async (id) => {
1609
+ if (isRelativeSpecifier(id) || isAbsoluteSpecifier(id)) return;
1610
+ for (const base of bases) {
1611
+ const requireFromBase = createRequire(createRequireBase(base));
1612
+ for (const candidate of createCssResolutionCandidates(id)) try {
1613
+ return requireFromBase.resolve(candidate);
1614
+ } catch {}
1615
+ }
1616
+ };
1617
+ }
1593
1618
  async function importResolvedModule(resolved) {
1594
1619
  return import(pathToFileURL(resolved).href);
1595
1620
  }
@@ -1654,31 +1679,53 @@ async function loadTailwindV4DesignSystem(source) {
1654
1679
  return promise;
1655
1680
  }
1656
1681
  async function compileTailwindV4Source(source) {
1657
- const node = await loadTailwindV4NodeModule([
1658
- source.projectRoot,
1659
- source.base,
1660
- ...source.baseFallbacks
1661
- ]);
1662
- const dependencies = new Set(source.dependencies);
1663
- return {
1664
- compiled: await node.compile(source.css, {
1665
- base: source.base,
1666
- onDependency(dependency) {
1667
- dependencies.add(path.resolve(dependency));
1668
- }
1669
- }),
1670
- dependencies
1671
- };
1682
+ const bases = unique([source.base, ...source.baseFallbacks]);
1683
+ if (bases.length === 0) throw new Error("No base directories provided for Tailwind CSS v4 compiler.");
1684
+ const node = await loadTailwindV4NodeModule([source.projectRoot, ...bases]);
1685
+ let lastError;
1686
+ for (const base of bases) {
1687
+ const dependencies = new Set(source.dependencies);
1688
+ try {
1689
+ return {
1690
+ compiled: await node.compile(source.css, {
1691
+ base,
1692
+ customCssResolver: createFallbackCssResolver([source.projectRoot, ...bases]),
1693
+ onDependency(dependency) {
1694
+ dependencies.add(path.resolve(dependency));
1695
+ }
1696
+ }),
1697
+ dependencies
1698
+ };
1699
+ } catch (error) {
1700
+ lastError = error;
1701
+ }
1702
+ }
1703
+ if (lastError instanceof Error) throw lastError;
1704
+ throw new Error("Failed to compile Tailwind CSS v4 source.");
1672
1705
  }
1673
1706
  //#endregion
1674
1707
  //#region src/extraction/candidate-extractor.ts
1675
1708
  let oxideImportPromise;
1676
1709
  const designSystemCandidateCache = /* @__PURE__ */ new Map();
1710
+ function createOxideRuntimeDependencyError(cause) {
1711
+ return new Error([
1712
+ "tailwindcss-patch could not load @tailwindcss/oxide, which is required for source candidate scanning.",
1713
+ "This dependency should be installed automatically by tailwindcss-patch.",
1714
+ "Reinstall dependencies without disabling optional dependencies, or install @tailwindcss/oxide@^4.2.4 manually if your package manager omitted it."
1715
+ ].join(" "), { cause });
1716
+ }
1677
1717
  async function importOxide() {
1678
- return import("@tailwindcss/oxide");
1718
+ try {
1719
+ return await import("@tailwindcss/oxide");
1720
+ } catch (error) {
1721
+ throw createOxideRuntimeDependencyError(error);
1722
+ }
1679
1723
  }
1680
1724
  function getOxideModule() {
1681
1725
  oxideImportPromise ??= importOxide();
1726
+ oxideImportPromise.catch(() => {
1727
+ oxideImportPromise = void 0;
1728
+ });
1682
1729
  return oxideImportPromise;
1683
1730
  }
1684
1731
  async function extractRawCandidatesWithPositions(content, extension = "html") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwindcss-patch",
3
- "version": "9.1.0",
3
+ "version": "9.2.1",
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",
@@ -14,12 +14,31 @@ import { getTailwindV4DesignSystemCacheKey, loadTailwindV4DesignSystem } from '.
14
14
  let oxideImportPromise: ReturnType<typeof importOxide> | undefined
15
15
  const designSystemCandidateCache = new Map<string, Map<string, boolean>>()
16
16
 
17
+ function createOxideRuntimeDependencyError(cause: unknown) {
18
+ return new Error(
19
+ [
20
+ 'tailwindcss-patch could not load @tailwindcss/oxide, which is required for source candidate scanning.',
21
+ 'This dependency should be installed automatically by tailwindcss-patch.',
22
+ 'Reinstall dependencies without disabling optional dependencies, or install @tailwindcss/oxide@^4.2.4 manually if your package manager omitted it.',
23
+ ].join(' '),
24
+ { cause },
25
+ )
26
+ }
27
+
17
28
  async function importOxide() {
18
- return import('@tailwindcss/oxide')
29
+ try {
30
+ return await import('@tailwindcss/oxide')
31
+ }
32
+ catch (error) {
33
+ throw createOxideRuntimeDependencyError(error)
34
+ }
19
35
  }
20
36
 
21
37
  function getOxideModule() {
22
38
  oxideImportPromise ??= importOxide()
39
+ oxideImportPromise.catch(() => {
40
+ oxideImportPromise = undefined
41
+ })
23
42
  return oxideImportPromise
24
43
  }
25
44
 
package/src/v4/engine.ts CHANGED
@@ -3,12 +3,30 @@ import type {
3
3
  TailwindV4GenerateOptions,
4
4
  TailwindV4GenerateResult,
5
5
  TailwindV4ResolvedSource,
6
+ TailwindV4SourcePattern,
6
7
  } from './types'
7
- import { extractRawCandidatesWithPositions } from '../extraction/candidate-extractor'
8
+ import { extractRawCandidates, extractRawCandidatesWithPositions } from '../extraction/candidate-extractor'
8
9
  import { extractTailwindV4InlineSourceCandidates, resolveValidTailwindV4Candidates } from './candidates'
9
10
  import { compileTailwindV4Source, loadTailwindV4DesignSystem } from './node-adapter'
10
11
 
11
- async function collectRawCandidates(source: TailwindV4ResolvedSource, options: TailwindV4GenerateOptions | undefined) {
12
+ function resolveScanSources(
13
+ options: TailwindV4GenerateOptions | undefined,
14
+ compiledSources: TailwindV4SourcePattern[],
15
+ ) {
16
+ if (Array.isArray(options?.scanSources)) {
17
+ return options.scanSources
18
+ }
19
+ if (options?.scanSources === true) {
20
+ return compiledSources
21
+ }
22
+ return []
23
+ }
24
+
25
+ async function collectRawCandidates(
26
+ source: TailwindV4ResolvedSource,
27
+ options: TailwindV4GenerateOptions | undefined,
28
+ compiledSources: TailwindV4SourcePattern[] = [],
29
+ ) {
12
30
  const rawCandidates = new Set<string>()
13
31
 
14
32
  for (const candidate of options?.candidates ?? []) {
@@ -22,6 +40,13 @@ async function collectRawCandidates(source: TailwindV4ResolvedSource, options: T
22
40
  }
23
41
  }
24
42
 
43
+ const filesystemSources = resolveScanSources(options, compiledSources)
44
+ if (filesystemSources.length > 0) {
45
+ for (const candidate of await extractRawCandidates(filesystemSources)) {
46
+ rawCandidates.add(candidate)
47
+ }
48
+ }
49
+
25
50
  const inlineSources = extractTailwindV4InlineSourceCandidates(source.css)
26
51
  for (const candidate of inlineSources.included) {
27
52
  rawCandidates.add(candidate)
@@ -44,17 +69,15 @@ export function createTailwindV4Engine(source: TailwindV4ResolvedSource): Tailwi
44
69
  return resolveValidTailwindV4Candidates(designSystem, candidates)
45
70
  },
46
71
  async generate(options): Promise<TailwindV4GenerateResult> {
47
- const rawCandidates = await collectRawCandidates(source, options)
72
+ const { compiled, dependencies } = await compileTailwindV4Source(source)
73
+ const rawCandidates = await collectRawCandidates(source, options, compiled.sources)
48
74
  const designSystem = await loadTailwindV4DesignSystem(source)
49
75
  const classSet = resolveValidTailwindV4Candidates(designSystem, rawCandidates)
50
76
  const inlineSources = extractTailwindV4InlineSourceCandidates(source.css)
51
- // TODO: Non-inline `@source not "..."` is surfaced through `compiled.sources`;
52
- // apply it here if generate() grows filesystem SourceEntry scanning.
53
77
  for (const candidate of inlineSources.excluded) {
54
78
  classSet.delete(candidate)
55
79
  }
56
80
 
57
- const { compiled, dependencies } = await compileTailwindV4Source(source)
58
81
  const css = compiled.build(Array.from(classSet))
59
82
 
60
83
  return {
@@ -20,6 +20,7 @@ interface TailwindV4NodeModule {
20
20
  compile: (css: string, options: {
21
21
  base: string
22
22
  onDependency: (dependency: string) => void
23
+ customCssResolver?: (id: string, base: string) => Promise<string | false | undefined>
23
24
  }) => Promise<TailwindV4CompiledSource>
24
25
  __unstable__loadDesignSystem: (css: string, options: { base: string }) => Promise<TailwindV4DesignSystem>
25
26
  }
@@ -35,6 +36,45 @@ function createRequireBase(base: string) {
35
36
  return path.join(base, 'package.json')
36
37
  }
37
38
 
39
+ function isRelativeSpecifier(id: string) {
40
+ return id.startsWith('./') || id.startsWith('../') || id === '.' || id === '..'
41
+ }
42
+
43
+ function isAbsoluteSpecifier(id: string) {
44
+ return path.isAbsolute(id)
45
+ }
46
+
47
+ function isCssSpecifier(id: string) {
48
+ return path.extname(id) === '.css'
49
+ }
50
+
51
+ function createCssResolutionCandidates(id: string) {
52
+ if (isCssSpecifier(id)) {
53
+ return [id]
54
+ }
55
+ return [`${id}/index.css`, id]
56
+ }
57
+
58
+ function createFallbackCssResolver(baseCandidates: string[]) {
59
+ const bases = unique(baseCandidates)
60
+ return async (id: string) => {
61
+ if (isRelativeSpecifier(id) || isAbsoluteSpecifier(id)) {
62
+ return undefined
63
+ }
64
+
65
+ for (const base of bases) {
66
+ const requireFromBase = createRequire(createRequireBase(base))
67
+ for (const candidate of createCssResolutionCandidates(id)) {
68
+ try {
69
+ return requireFromBase.resolve(candidate)
70
+ }
71
+ catch {}
72
+ }
73
+ }
74
+ return undefined
75
+ }
76
+ }
77
+
38
78
  async function importResolvedModule(resolved: string): Promise<TailwindV4NodeModule> {
39
79
  return import(pathToFileURL(resolved).href) as unknown as Promise<TailwindV4NodeModule>
40
80
  }
@@ -133,17 +173,37 @@ export async function loadTailwindV4DesignSystem(source: TailwindV4ResolvedSourc
133
173
  }
134
174
 
135
175
  export async function compileTailwindV4Source(source: TailwindV4ResolvedSource) {
136
- const node = await loadTailwindV4NodeModule([source.projectRoot, source.base, ...source.baseFallbacks])
137
- const dependencies = new Set(source.dependencies)
138
- const compiled = await node.compile(source.css, {
139
- base: source.base,
140
- onDependency(dependency) {
141
- dependencies.add(path.resolve(dependency))
142
- },
143
- })
176
+ const bases = unique([source.base, ...source.baseFallbacks])
177
+ if (bases.length === 0) {
178
+ throw new Error('No base directories provided for Tailwind CSS v4 compiler.')
179
+ }
180
+
181
+ const node = await loadTailwindV4NodeModule([source.projectRoot, ...bases])
182
+ let lastError: unknown
183
+
184
+ for (const base of bases) {
185
+ const dependencies = new Set(source.dependencies)
186
+ try {
187
+ const compiled = await node.compile(source.css, {
188
+ base,
189
+ customCssResolver: createFallbackCssResolver([source.projectRoot, ...bases]),
190
+ onDependency(dependency) {
191
+ dependencies.add(path.resolve(dependency))
192
+ },
193
+ })
194
+
195
+ return {
196
+ compiled,
197
+ dependencies,
198
+ }
199
+ }
200
+ catch (error) {
201
+ lastError = error
202
+ }
203
+ }
144
204
 
145
- return {
146
- compiled,
147
- dependencies,
205
+ if (lastError instanceof Error) {
206
+ throw lastError
148
207
  }
208
+ throw new Error('Failed to compile Tailwind CSS v4 source.')
149
209
  }
package/src/v4/types.ts CHANGED
@@ -24,6 +24,13 @@ export interface TailwindV4CandidateSource {
24
24
  export interface TailwindV4GenerateOptions {
25
25
  candidates?: Iterable<string>
26
26
  sources?: TailwindV4CandidateSource[]
27
+ /**
28
+ * 扫描文件系统 source entries 中的候选类名。
29
+ *
30
+ * - `true`:使用 Tailwind v4 编译入口解析出的 `@source` 列表。
31
+ * - `TailwindV4SourcePattern[]`:使用调用方显式传入的 source 列表。
32
+ */
33
+ scanSources?: boolean | TailwindV4SourcePattern[]
27
34
  }
28
35
 
29
36
  export interface TailwindV4SourcePattern {