UNPKG

weapp-tailwindcss

Version:

把 tailwindcss 原子化样式思想,带给小程序开发者们! bring tailwindcss to miniprogram developers!

1,282 lines 64.6 kB
import { C as findWorkspacePackageDir, Ct as expandTailwindSourceEntries, D as resolveTailwindcssOptions, Dt as resolveCssSourceEntries, E as normalizeTailwindcssRuntimeOptions, Et as parseConfigParam, Lt as loadTailwindV4DesignSystem, Nt as resolveCssMacroTailwindV4Source, O as normalizeStringListOption, S as findNearestPackageRoot, St as createTailwindSourceEntryMatcher, T as normalizeExtendLengthUnits, Tt as normalizeLegacyContentEntries, b as isTailwindV4CssEntry, d as resolveTailwindV4SourceFromRuntime, h as hasConfiguredTailwindV4CssRoots, i as normalizeWeappTailwindcssGeneratorOptions, kt as resolveTailwindV4CssSourceBase, m as filterTailwindV4CssSourceRoots, p as resolveTailwindV4SourceOptionsFromRuntime, v as groupCssEntriesByBase, w as findWorkspaceRoot, x as normalizeCssEntries, xt as FULL_SOURCE_SCAN_PATTERN, y as guessBasedirFromEntries } from "./generator-DYeAa2-5.js"; import { t as omitUndefined } from "./object-Bvy6-w9r.js"; import { n as defuOverrideArray$1 } from "./utils-MVwpU07P.js"; import { createRequire } from "node:module"; import { existsSync, readFileSync, statSync } from "node:fs"; import path from "node:path"; import process from "node:process"; import { extractProjectCandidatesWithPositions, resolveValidTailwindV4Candidates } from "@tailwindcss-mangle/engine"; import { postcss } from "@weapp-tailwindcss/postcss"; import { stat } from "node:fs/promises"; import { defuOverrideArray } from "@weapp-tailwindcss/shared"; import fg from "fast-glob"; import { loadConfig } from "tailwindcss-config"; import { logger } from "@weapp-tailwindcss/logger"; import { fileURLToPath } from "node:url"; //#region src/tailwindcss/source-scan/inline-source.ts const NUMERICAL_RANGE_RE = /^(-?\d+)\.\.(-?\d+)(?:\.\.(-?\d+))?$/; function segmentTopLevel(input, separator, options = {}) { const parts = []; const stack = []; let lastPos = 0; let quote; for (let index = 0; index < input.length; index++) { const char = input[index]; if (char === "\\") { index += 1; continue; } if (quote) { if (char === quote) quote = void 0; continue; } if (char === "\"" || char === "'") { quote = char; continue; } if (char === "(") { stack.push(")"); continue; } if (char === "[") { stack.push("]"); continue; } if (char === "{") { stack.push("}"); continue; } if (stack.length > 0 && char === stack[stack.length - 1]) { stack.pop(); continue; } if (stack.length === 0 && char === separator) { const part = input.slice(lastPos, index); if (part || options.keepEmpty) parts.push(part); lastPos = index + 1; } } const part = input.slice(lastPos); if (part || options.keepEmpty) parts.push(part); return parts; } function isSequence(value) { return NUMERICAL_RANGE_RE.test(value); } function expandSequence(value) { const match = value.match(NUMERICAL_RANGE_RE); if (!match) return [value]; const [, start, end, stepValue] = match; if (start === void 0 || end === void 0) return [value]; let step = stepValue ? Number.parseInt(stepValue, 10) : void 0; const startNumber = Number.parseInt(start, 10); const endNumber = Number.parseInt(end, 10); const increasing = startNumber < endNumber; if (step === void 0) step = increasing ? 1 : -1; if (step === 0) return []; if (increasing && step < 0) step = -step; if (!increasing && step > 0) step = -step; const result = []; for (let value = startNumber; increasing ? value <= endNumber : value >= endNumber; value += step) result.push(String(value)); return result; } function expandInlineSourceCandidatePattern(pattern) { const index = pattern.indexOf("{"); if (index === -1) return [pattern]; const prefix = pattern.slice(0, index); const rest = pattern.slice(index); let depth = 0; let endIndex = -1; for (let index = 0; index < rest.length; index++) { const char = rest[index]; if (char === "{") depth += 1; else if (char === "}") { depth -= 1; if (depth === 0) { endIndex = index; break; } } } if (endIndex === -1) return [pattern]; const inner = rest.slice(1, endIndex); const suffix = rest.slice(endIndex + 1); const parts = (isSequence(inner) ? expandSequence(inner) : segmentTopLevel(inner, ",", { keepEmpty: true })).flatMap((part) => expandInlineSourceCandidatePattern(part)); return expandInlineSourceCandidatePattern(suffix).flatMap((suffix) => parts.map((part) => `${prefix}${part}${suffix}`)); } function parseSourceInlineParam(params) { let value = params.trim(); const negated = value.startsWith("not "); if (negated) value = value.slice(4).trim(); if (!value.startsWith("inline(") || !value.endsWith(")")) return; const inlineValue = value.slice(7, -1).trim(); const match = /^(['"])([\s\S]*)\1$/.exec(inlineValue); if (!match) return; const source = match[2]; if (source === void 0) return; return { negated, source }; } function collectCssInlineSourceCandidates(root) { const included = /* @__PURE__ */ new Set(); const excluded = /* @__PURE__ */ new Set(); root.walkAtRules("source", (rule) => { const parsed = parseSourceInlineParam(rule.params); if (!parsed) return; const target = parsed.negated ? excluded : included; for (const source of segmentTopLevel(parsed.source, " ")) { const trimmed = source.trim(); if (!trimmed) continue; for (const candidate of expandInlineSourceCandidatePattern(trimmed)) { const normalized = candidate.trim(); if (normalized) target.add(normalized); } } }); for (const candidate of excluded) included.delete(candidate); return { included, excluded }; } //#endregion //#region src/tailwindcss/v4/preflight.ts function parseCssImportSpecifier(params) { const value = params.trim(); const quoted = /^(['"])(.*?)\1/.exec(value); if (quoted) return quoted[2]; const url = /^url\(\s*(?:(['"])(.*?)\1|([^'")\s]+))\s*\)/.exec(value); return url?.[2] ?? url?.[3]; } function normalizeTailwindImportSpecifier(params) { return parseCssImportSpecifier(params)?.replaceAll("\\", "/"); } function isTailwindV4CssImportParam(params) { const specifier = normalizeTailwindImportSpecifier(params); return specifier === "tailwindcss" || specifier === "tailwindcss4" || specifier?.startsWith("tailwindcss/") === true || specifier?.startsWith("tailwindcss4/") === true || specifier?.endsWith("/tailwindcss/index.css") === true; } function isTailwindV4PreflightImportParam(params) { const specifier = normalizeTailwindImportSpecifier(params); return specifier === "tailwindcss" || specifier === "tailwindcss4" || specifier === "tailwindcss/preflight" || specifier === "tailwindcss/preflight.css" || specifier === "tailwindcss4/preflight" || specifier === "tailwindcss4/preflight.css" || specifier?.endsWith("/tailwindcss/index.css") === true || specifier?.endsWith("/tailwindcss/preflight.css") === true; } function includesTailwindV4PreflightDirective(css) { try { const root = postcss.parse(css); let includesPreflight = false; root.walkAtRules("import", (rule) => { includesPreflight || (includesPreflight = isTailwindV4PreflightImportParam(rule.params)); }); root.walkAtRules("tailwind", (rule) => { includesPreflight || (includesPreflight = rule.params.trim() === "base"); }); return includesPreflight; } catch { return /@import\s+(?:url\(\s*)?["']?tailwindcss4?(?:\/(?:preflight(?:\.css)?|index\.css))?["')\s;]/.test(css) || /@tailwind\s+base\b/.test(css); } } //#endregion //#region src/bundlers/shared/static-config-content.ts function skipWhitespaceAndComments(source, start) { let index = start; while (index < source.length) { const char = source[index]; if (/\s/.test(char ?? "")) { index += 1; continue; } if (char === "/" && source[index + 1] === "/") { index += 2; while (index < source.length && source[index] !== "\n") index += 1; continue; } if (char === "/" && source[index + 1] === "*") { index += 2; while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) index += 1; index = Math.min(index + 2, source.length); continue; } break; } return index; } function readQuotedString(source, start) { const quote = source[start]; if (quote !== "\"" && quote !== "'") return; let value = ""; for (let index = start + 1; index < source.length; index++) { const char = source[index]; if (char === "\\") { const next = source[index + 1]; if (next === void 0) return; value += next; index += 1; continue; } if (char === quote) return { end: index + 1, value }; value += char; } } function readIdentifier(source, start) { return /^[A-Z_$][\w$]*/i.exec(source.slice(start))?.[0]; } function findMatchingBracket(source, start, open, close) { let depth = 0; let quote; for (let index = start; index < source.length; index++) { const char = source[index]; if (char === "\\") { index += 1; continue; } if (quote) { if (char === quote) quote = void 0; continue; } if (char === "\"" || char === "'") { quote = char; continue; } if (char === "`") return; if (char === open) { depth += 1; continue; } if (char === close) { depth -= 1; if (depth === 0) return index; } } } function findContentPropertyValue(source) { let index = 0; while (index < source.length) { const nextIndex = source.indexOf("content", index); if (nextIndex === -1) return; const previous = source[nextIndex - 1]; const next = source[nextIndex + 7]; if (previous && /[\w$]/.test(previous) || next && /[\w$]/.test(next)) { index = nextIndex + 7; continue; } let colonIndex = skipWhitespaceAndComments(source, nextIndex + 7); if (source[colonIndex] !== ":") { index = nextIndex + 7; continue; } colonIndex = skipWhitespaceAndComments(source, colonIndex + 1); return { start: colonIndex }; } } function parseStaticContentArray(source, start) { if (source[start] !== "[") return; const value = []; let index = skipWhitespaceAndComments(source, start + 1); while (index < source.length) { if (source[index] === "]") return { end: index + 1, value }; const parsed = parseStaticContentValue(source, index); if (!parsed) return; value.push(parsed.value); index = skipWhitespaceAndComments(source, parsed.end); if (source[index] === ",") { index = skipWhitespaceAndComments(source, index + 1); continue; } if (source[index] === "]") continue; return; } } function parseStaticContentObject(source, start) { if (source[start] !== "{") return; const end = findMatchingBracket(source, start, "{", "}"); if (end === void 0) return; let index = skipWhitespaceAndComments(source, start + 1); let files; while (index < end) { let key; const quotedKey = readQuotedString(source, index); if (quotedKey) { key = quotedKey.value; index = quotedKey.end; } else { key = readIdentifier(source, index); if (!key) return; index += key.length; } index = skipWhitespaceAndComments(source, index); if (source[index] !== ":") return; index = skipWhitespaceAndComments(source, index + 1); const parsedValue = parseStaticContentValue(source, index); if (!parsedValue) return; if (key === "files") files = parsedValue.value; index = skipWhitespaceAndComments(source, parsedValue.end); if (source[index] === ",") { index = skipWhitespaceAndComments(source, index + 1); continue; } if (index < end) return; } return files === void 0 ? void 0 : { end: end + 1, value: { files } }; } function parseStaticContentValue(source, start) { const index = skipWhitespaceAndComments(source, start); const quoted = readQuotedString(source, index); if (quoted) return quoted; if (source[index] === "[") return parseStaticContentArray(source, index); if (source[index] === "{") return parseStaticContentObject(source, index); } function readStaticConfigContent(configPath) { let source; try { source = readFileSync(configPath, "utf8"); } catch { return; } const contentProperty = findContentPropertyValue(source); if (!contentProperty) return; return parseStaticContentValue(source, contentProperty.start)?.value; } //#endregion //#region src/bundlers/shared/source-scan/css-entries.ts const SOURCE_CANDIDATE_PATTERN = FULL_SOURCE_SCAN_PATTERN; const TAILWIND_CSS_ENTRY_PATTERN = "**/*.css"; const tailwindV4CssEntriesCache = /* @__PURE__ */ new Map(); function parseImportSourceParam(params) { const match = /\bsource\(\s*(none|(['"])(.*?)\2)\s*\)/.exec(params); if (!match) return; return { none: match[1] === "none", sourcePath: match[3] }; } function resolveSourceBase(base, sourcePath) { return path.isAbsolute(sourcePath) ? sourcePath : path.resolve(base, sourcePath); } function resolveConfigPath(base, configPath) { if (path.isAbsolute(configPath)) return path.resolve(configPath); return path.resolve(base, configPath); } function createCssEntriesCacheKey(css, base, dependencies) { return JSON.stringify({ base: path.resolve(base), css, dependencies }); } async function statConfigDependency(file) { try { const stats = await stat(file); return { file, mtimeMs: stats.mtimeMs, size: stats.size }; } catch { return { file, mtimeMs: -1, size: -1 }; } } async function collectConfigDependencySignatures(root, base) { const configPaths = /* @__PURE__ */ new Set(); root.walkAtRules("config", (rule) => { const configPath = parseConfigParam(rule.params); if (configPath) configPaths.add(resolveConfigPath(base, configPath)); }); return Promise.all([...configPaths].sort().map(statConfigDependency)); } function mergeTailwindInlineSourceCandidates(allInlineCandidates) { const merged = { included: /* @__PURE__ */ new Set(), excluded: /* @__PURE__ */ new Set() }; for (const inlineCandidates of allInlineCandidates) { if (!inlineCandidates) continue; for (const candidate of inlineCandidates.included) if (!merged.excluded.has(candidate)) merged.included.add(candidate); for (const candidate of inlineCandidates.excluded) { merged.excluded.add(candidate); merged.included.delete(candidate); } } return merged.included.size > 0 || merged.excluded.size > 0 ? merged : void 0; } async function resolveConfigContentEntries(root, base) { const configPaths = /* @__PURE__ */ new Set(); root.walkAtRules("config", (rule) => { const configPath = parseConfigParam(rule.params); if (configPath) configPaths.add(resolveConfigPath(base, configPath)); }); const entries = []; for (const configPath of configPaths) { const staticContent = readStaticConfigContent(configPath); if (staticContent !== void 0) { entries.push(...normalizeLegacyContentEntries(staticContent, path.dirname(configPath), { relativeBase: path.dirname(configPath) })); continue; } try { const loaded = await loadConfig({ config: configPath, cwd: path.dirname(configPath) }); entries.push(...normalizeLegacyContentEntries(loaded?.config.content, path.dirname(configPath), { relativeBase: path.dirname(configPath) })); } catch {} } return { dependencies: [...configPaths], entries }; } async function resolveTailwindV4EntriesFromCss(css, base) { let root; try { root = postcss.parse(css); } catch { return; } let importSourceBase; let hasSourceNone = false; let hasTailwindCssImport = false; let includesPreflight = false; const [sourceEntries, configEntries] = await Promise.all([resolveCssSourceEntries(root, base, SOURCE_CANDIDATE_PATTERN), resolveConfigContentEntries(root, base)]); const entries = [...configEntries.entries, ...sourceEntries]; const hasPositiveEntries = entries.some((entry) => !entry.negated); const inlineCandidates = collectCssInlineSourceCandidates(root); root.walkAtRules("import", (rule) => { if (!isTailwindV4CssImportParam(rule.params)) return; hasTailwindCssImport = true; includesPreflight || (includesPreflight = isTailwindV4PreflightImportParam(rule.params)); const sourceParam = parseImportSourceParam(rule.params); if (sourceParam?.none) hasSourceNone = true; if (sourceParam?.sourcePath) importSourceBase = resolveSourceBase(base, sourceParam.sourcePath); }); root.walkAtRules("tailwind", (rule) => { includesPreflight || (includesPreflight = rule.params.trim() === "base"); }); if (importSourceBase) return { entries: [{ base: importSourceBase, negated: false, pattern: SOURCE_CANDIDATE_PATTERN }, ...entries], explicit: true, includesPreflight, inlineCandidates, dependencies: configEntries.dependencies }; if (hasSourceNone) return { entries, explicit: true, includesPreflight, inlineCandidates, dependencies: configEntries.dependencies }; if (hasPositiveEntries) return { entries, explicit: true, includesPreflight, inlineCandidates, dependencies: configEntries.dependencies }; if (inlineCandidates.included.size > 0 || inlineCandidates.excluded.size > 0) return { entries: [], explicit: true, includesPreflight, inlineCandidates, dependencies: configEntries.dependencies }; return hasTailwindCssImport ? { entries, explicit: false, includesPreflight, inlineCandidates, dependencies: configEntries.dependencies } : void 0; } async function resolveTailwindV4CssDependencies(css, base) { return (await resolveTailwindV4EntriesFromCss(css, base))?.dependencies ?? []; } function collectExistingCssEntries(options) { return [ ...options.cssEntries ?? [], ...options.tailwindcss?.v4?.cssEntries ?? [], ...options.tailwindcssRuntimeOptions?.tailwindcss?.v4?.cssEntries ?? [] ].filter((item) => typeof item === "string" && item.length > 0).filter(isTailwindV4CssEntry).map((item) => path.resolve(item)).filter((item) => existsSync(item)); } async function pathExistsAsFile(file) { try { return (await stat(file)).isFile(); } catch { return false; } } async function resolveTailwindV4EntriesFromCssCached(css, base) { let root; try { root = postcss.parse(css); } catch { return; } const cacheKey = createCssEntriesCacheKey(css, base, await collectConfigDependencySignatures(root, base)); const cached = tailwindV4CssEntriesCache.get(cacheKey); if (cached) return cached; const task = resolveTailwindV4EntriesFromCss(css, base).catch((error) => { tailwindV4CssEntriesCache.delete(cacheKey); throw error; }); tailwindV4CssEntriesCache.set(cacheKey, task); return task; } async function discoverTailwindV4CssEntries(root, outDir) { const resolvedRoot = path.resolve(root); const ignore = ["**/node_modules/**", "**/.git/**"]; const resolvedOutDir = outDir ? path.resolve(resolvedRoot, outDir) : void 0; if (resolvedOutDir) { const relativeOutDir = path.relative(resolvedRoot, resolvedOutDir); if (relativeOutDir && !relativeOutDir.startsWith("..") && !path.isAbsolute(relativeOutDir)) ignore.push(`${relativeOutDir.split(path.sep).join("/")}/**`); } const candidates = await fg(TAILWIND_CSS_ENTRY_PATTERN, { absolute: true, cwd: resolvedRoot, ignore, onlyFiles: true, unique: true }); const entries = []; for (const file of candidates) { if (!await pathExistsAsFile(file)) continue; try { const css = readFileSync(file, "utf8"); if (css.includes("tailwindcss") || css.includes("@source") || css.includes("@config")) { if (await resolveTailwindV4EntriesFromCssCached(css, path.dirname(file))) entries.push(file); } } catch {} } return entries; } function collectConfiguredCssSources(options) { return filterTailwindV4CssSourceRoots([...options.tailwindcss?.v4?.cssSources ?? [], ...options.tailwindcssRuntimeOptions?.tailwindcss?.v4?.cssSources ?? []]) ?? []; } //#endregion //#region src/bundlers/shared/source-scan/dependencies.ts function addSourceScanDependency(dependencies, file) { if (typeof file === "string" && file.length > 0) dependencies.add(path.resolve(file)); } function addSourceScanDependencies(dependencies, files) { for (const file of files ?? []) addSourceScanDependency(dependencies, file); } //#endregion //#region src/bundlers/shared/source-scan.ts function createResolvedSourceScan(input, dependencies) { return { ...input, ...dependencies.size > 0 ? { dependencies: [...dependencies].sort() } : {} }; } function createMergedCssEntrySourceScanEntries(entries, options) { if (options.sourceCount <= 1) return entries; const positiveEntries = entries.filter((entry) => !entry.negated); return positiveEntries.length > 0 ? positiveEntries : entries; } function resolveExplicitSourceScanEntries(entries, explicit) { if (!explicit) return entries.length > 0 ? entries : void 0; return entries.some((entry) => !entry.negated) ? entries : []; } function createResolvedV4CssScanInput(entries, inlineCandidates, explicit) { return { entries: resolveExplicitSourceScanEntries(entries, explicit), explicit, inlineCandidates }; } async function resolveSourceScanEntries(options, runtime, scanOptions = {}) { const sourceOptions = resolveTailwindV4SourceOptionsFromRuntime(runtime); const cssEntries = collectExistingCssEntries(options); if (cssEntries.length === 0 && !sourceOptions.css && !sourceOptions.cssSources?.length) { const scanRoot = scanOptions.root; const sourceProjectRoot = sourceOptions.projectRoot; if (scanRoot && sourceProjectRoot && path.resolve(scanRoot) === path.resolve(sourceProjectRoot)) { const discoveredCssEntries = await discoverTailwindV4CssEntries(scanRoot, scanOptions.outDir); cssEntries.push(...discoveredCssEntries); } } const entries = []; const cssInlineCandidates = []; const dependencies = /* @__PURE__ */ new Set(); let explicit = false; let readableCssEntryCount = 0; for (const cssEntry of cssEntries) { addSourceScanDependency(dependencies, cssEntry); let css; try { css = readFileSync(cssEntry, "utf8"); } catch { continue; } readableCssEntryCount++; const resolved = await resolveTailwindV4EntriesFromCssCached(css, path.dirname(cssEntry)); if (resolved) { entries.push(...resolved.entries); cssInlineCandidates.push(resolved.inlineCandidates); addSourceScanDependencies(dependencies, resolved.dependencies); explicit || (explicit = resolved.explicit); } } const inlineCandidates = mergeTailwindInlineSourceCandidates(cssInlineCandidates); const scanEntries = createMergedCssEntrySourceScanEntries(entries, { sourceCount: readableCssEntryCount }); if (scanEntries.length > 0 || inlineCandidates || explicit || readableCssEntryCount > 0) return createResolvedSourceScan({ entries: resolveExplicitSourceScanEntries(scanEntries, explicit), explicit, inlineCandidates }, dependencies); if (typeof sourceOptions.css === "string" && sourceOptions.css.length > 0) { const resolved = await resolveTailwindV4EntriesFromCssCached(sourceOptions.css, sourceOptions.base ?? sourceOptions.projectRoot ?? process.cwd()); return resolved ? createResolvedSourceScan(createResolvedV4CssScanInput(resolved.entries, resolved.inlineCandidates, resolved.explicit), new Set(resolved.dependencies)) : void 0; } const sourceOptionBase = sourceOptions.base ?? sourceOptions.projectRoot ?? process.cwd(); const configuredCssSources = collectConfiguredCssSources(options); for (const cssSource of [...configuredCssSources, ...sourceOptions.cssSources ?? []]) { if (typeof cssSource.css !== "string" || cssSource.css.length === 0) continue; addSourceScanDependency(dependencies, cssSource.file); addSourceScanDependencies(dependencies, cssSource.dependencies); const resolved = await resolveTailwindV4EntriesFromCssCached(cssSource.css, resolveTailwindV4CssSourceBase(cssSource, sourceOptionBase)); if (resolved) { entries.push(...resolved.entries); cssInlineCandidates.push(resolved.inlineCandidates); addSourceScanDependencies(dependencies, resolved.dependencies); explicit || (explicit = resolved.explicit); } } const cssSourceInlineCandidates = mergeTailwindInlineSourceCandidates(cssInlineCandidates); const cssSourceCount = (sourceOptions.cssSources?.length ?? 0) + configuredCssSources.length; const cssSourceScanEntries = createMergedCssEntrySourceScanEntries(entries, { sourceCount: cssSourceCount }); if (cssSourceScanEntries.length > 0 || cssSourceInlineCandidates || explicit) return createResolvedSourceScan({ entries: resolveExplicitSourceScanEntries(cssSourceScanEntries, explicit), explicit, inlineCandidates: cssSourceInlineCandidates }, dependencies); if (cssSourceCount > 0) return; const source = await resolveTailwindV4SourceFromRuntime(runtime); addSourceScanDependency(dependencies, source.file); addSourceScanDependencies(dependencies, source.dependencies); const resolved = await resolveTailwindV4EntriesFromCssCached(source.css, source.base); return resolved ? createResolvedSourceScan(createResolvedV4CssScanInput(resolved.entries.length > 0 ? resolved.entries : [], resolved.inlineCandidates, resolved.entries.length > 0 ? resolved.explicit : false), /* @__PURE__ */ new Set([...dependencies, ...resolved.dependencies])) : void 0; } function createSourceScanMatcher(entries) { if (entries?.length === 0) return () => false; return createTailwindSourceEntryMatcher(entries); } //#endregion //#region src/tailwindcss/runtime/cache.ts const runtimeClassSetCache = /* @__PURE__ */ new WeakMap(); const runtimeFileSignatureCache = /* @__PURE__ */ new Map(); const runtimeTrackedSourceFilesCache = /* @__PURE__ */ new Map(); let runtimeFileSignatureCacheClearTimer; const runtimeSignatureRuntimesSymbol = Symbol.for("weapp-tailwindcss.runtimeSignatureRuntimes"); function getCacheEntry(tailwindRuntime) { let entry = runtimeClassSetCache.get(tailwindRuntime); if (!entry) { entry = {}; runtimeClassSetCache.set(tailwindRuntime, entry); } return entry; } function scheduleRuntimeConfigSignatureCacheClear() { if (runtimeFileSignatureCacheClearTimer) return; runtimeFileSignatureCacheClearTimer = setTimeout(() => { runtimeFileSignatureCache.clear(); runtimeTrackedSourceFilesCache.clear(); runtimeFileSignatureCacheClearTimer = void 0; }, 0); runtimeFileSignatureCacheClearTimer.unref?.(); } function getFileSignature(filePath) { const cached = runtimeFileSignatureCache.get(filePath); if (cached !== void 0) return cached; let signature; try { const stats = statSync(filePath); signature = `${filePath}:${stats.size}:${stats.mtimeMs}`; } catch { signature = `${filePath}:missing`; } runtimeFileSignatureCache.set(filePath, signature); scheduleRuntimeConfigSignatureCacheClear(); return signature; } function getTailwindTrackedFiles(tailwindRuntime) { const tailwindOptions = resolveTailwindcssOptions(tailwindRuntime.options); const tracked = /* @__PURE__ */ new Set(); const configPath = tailwindOptions?.config; if (typeof configPath === "string" && configPath.length > 0) tracked.add(configPath); for (const entry of tailwindOptions?.v4?.cssEntries ?? []) if (typeof entry === "string" && entry.length > 0) tracked.add(entry); for (const source of tailwindOptions?.v4?.cssSources ?? []) { if (typeof source.file === "string" && source.file.length > 0) tracked.add(source.file); for (const dependency of source.dependencies ?? []) if (typeof dependency === "string" && dependency.length > 0) tracked.add(dependency); } return tracked; } function normalizeTrackedSourceSignature(cssEntries, cssSources) { return normalizeSignatureValue({ cssEntries: cssEntries?.map((entry) => { if (!existsSync(entry)) return `${entry}:missing`; return getFileSignature(entry); }), cssSources }); } async function collectTailwindV4TrackedSourceFiles(tailwindRuntime) { const tailwindOptions = resolveTailwindcssOptions(tailwindRuntime.options); const signature = normalizeTrackedSourceSignature(tailwindOptions?.v4?.cssEntries, tailwindOptions?.v4?.cssSources); const cached = runtimeTrackedSourceFilesCache.get(signature); if (cached) return cached; const files = /* @__PURE__ */ new Set(); for (const cssEntry of tailwindOptions?.v4?.cssEntries ?? []) { if (!existsSync(cssEntry)) continue; const resolved = await resolveTailwindV4EntriesFromCssCached(readFileSync(cssEntry, "utf8"), path.dirname(cssEntry)); const expanded = resolved?.entries?.length ? await expandTailwindSourceEntries(resolved.entries) : []; for (const file of expanded) files.add(file); } for (const cssSource of tailwindOptions?.v4?.cssSources ?? []) { if (typeof cssSource.css !== "string" || cssSource.css.length === 0) continue; const base = typeof cssSource.file === "string" && cssSource.file.length > 0 ? path.dirname(cssSource.file) : tailwindOptions?.v4?.base ?? tailwindOptions?.cwd ?? tailwindRuntime.options?.projectRoot ?? process.cwd(); const resolved = await resolveTailwindV4EntriesFromCssCached(cssSource.css, base); const expanded = resolved?.entries?.length ? await expandTailwindSourceEntries(resolved.entries) : []; for (const file of expanded) files.add(file); } const result = [...files].sort((a, b) => a.localeCompare(b)); runtimeTrackedSourceFilesCache.set(signature, result); return result; } function normalizeSignatureValue(value) { if (value == null) return "null"; if (typeof value === "string") return value; if (typeof value === "number" || typeof value === "boolean") return String(value); if (Array.isArray(value)) return `[${value.map((item) => normalizeSignatureValue(item)).join(",")}]`; if (typeof value === "object") return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${key}:${normalizeSignatureValue(item)}`).join(",")}}`; return String(value); } function readOptionalProperty(value, key) { if (typeof value !== "object" || value === null || !(key in value)) return; return value[key]; } function getTailwindOptionsSignature(tailwindRuntime) { const options = tailwindRuntime.options; const tailwindOptions = resolveTailwindcssOptions(options); return normalizeSignatureValue({ projectRoot: options?.projectRoot, packageName: tailwindOptions?.packageName, versionHint: readOptionalProperty(tailwindOptions, "versionHint"), cwd: tailwindOptions?.cwd, config: tailwindOptions?.config, v4: { base: tailwindOptions?.v4?.base, configuredBase: readOptionalProperty(tailwindOptions?.v4, "configuredBase"), css: tailwindOptions?.v4?.css, cssEntries: tailwindOptions?.v4?.cssEntries, cssSources: tailwindOptions?.v4?.cssSources, hasUserDefinedSources: readOptionalProperty(tailwindOptions?.v4, "hasUserDefinedSources"), sources: tailwindOptions?.v4?.sources } }); } function getRuntimeTargetSignature(tailwindRuntime) { const packageInfo = tailwindRuntime.packageInfo; return [ packageInfo?.name ?? "missing", packageInfo?.rootPath ?? "missing", packageInfo?.version ?? "unknown", tailwindRuntime.majorVersion ?? "unknown", getTailwindOptionsSignature(tailwindRuntime) ].join(":"); } function getNestedRuntimes(tailwindRuntime) { const nested = tailwindRuntime[runtimeSignatureRuntimesSymbol]; return Array.isArray(nested) && nested.length > 0 ? nested : void 0; } function getOwnRuntimeClassSetSignature(tailwindRuntime) { const trackedFiles = [...getTailwindTrackedFiles(tailwindRuntime)].sort((a, b) => a.localeCompare(b)).map(getFileSignature); return `${trackedFiles.length > 0 ? trackedFiles.join("|") : "files:missing"}|runtime:${getRuntimeTargetSignature(tailwindRuntime)}`; } function invalidateRuntimeClassSet(tailwindRuntime) { if (!tailwindRuntime) return; const nestedRuntimes = getNestedRuntimes(tailwindRuntime); if (nestedRuntimes) for (const runtime of nestedRuntimes) invalidateRuntimeClassSet(runtime); for (const trackedFile of getTailwindTrackedFiles(tailwindRuntime)) runtimeFileSignatureCache.delete(trackedFile); runtimeTrackedSourceFilesCache.clear(); runtimeClassSetCache.delete(tailwindRuntime); } function getRuntimeClassSetCacheEntry(tailwindRuntime) { return getCacheEntry(tailwindRuntime); } function getRuntimeClassSetSignature(tailwindRuntime) { const nestedRuntimes = getNestedRuntimes(tailwindRuntime); if (nestedRuntimes) return nestedRuntimes.map(getOwnRuntimeClassSetSignature).sort((a, b) => a.localeCompare(b)).join("||"); return getOwnRuntimeClassSetSignature(tailwindRuntime); } async function getRuntimeClassSetSignatureWithSources(tailwindRuntime) { const baseSignature = getRuntimeClassSetSignature(tailwindRuntime); const trackedSourceFiles = await collectTailwindV4TrackedSourceFiles(tailwindRuntime); if (trackedSourceFiles.length === 0) return baseSignature; return [baseSignature, trackedSourceFiles.map(getFileSignature).join("|")].join("|sources:"); } //#endregion //#region src/tailwindcss/v4/config.ts const DEFAULT_CSS_CALC_CUSTOM_PROPERTIES = []; function includesToken(list, token) { return list.some((candidate) => { if (typeof token === "string") { if (typeof candidate === "string") return candidate === token; candidate.lastIndex = 0; return candidate.test(token); } if (typeof candidate === "string") { token.lastIndex = 0; return token.test(candidate); } return candidate.source === token.source && candidate.flags === token.flags; }); } function ensureDefaultsIncluded(value) { if (value === true) return { includeCustomProperties: [...DEFAULT_CSS_CALC_CUSTOM_PROPERTIES] }; if (Array.isArray(value)) { if (!DEFAULT_CSS_CALC_CUSTOM_PROPERTIES.length) return value; const missing = DEFAULT_CSS_CALC_CUSTOM_PROPERTIES.filter((token) => !includesToken(value, token)); return missing.length > 0 ? [...value, ...missing] : value; } if (value && typeof value === "object") { const include = value.includeCustomProperties; if (!Array.isArray(include)) return { ...value, includeCustomProperties: [...DEFAULT_CSS_CALC_CUSTOM_PROPERTIES] }; if (!DEFAULT_CSS_CALC_CUSTOM_PROPERTIES.length) return value; const missing = DEFAULT_CSS_CALC_CUSTOM_PROPERTIES.filter((token) => !includesToken(include, token)); return missing.length > 0 ? { ...value, includeCustomProperties: [...include, ...missing] } : value; } return value; } function normalizeCssEntriesConfig(entries) { const normalized = normalizeStringListOption(entries)?.filter(isTailwindV4CssEntry); return normalized && normalized.length > 0 ? normalized : void 0; } function hasConfiguredCssEntries(ctx) { if (normalizeCssEntriesConfig(ctx.cssEntries)) return true; if (normalizeCssEntriesConfig(ctx.tailwindcss?.v4?.cssEntries)) return true; const runtimeOptions = ctx.tailwindcssRuntimeOptions; if (runtimeOptions) { if (normalizeCssEntriesConfig(runtimeOptions.tailwindcss?.v4?.cssEntries)) return true; } return false; } let hasWarnedMissingCssEntries = false; function warnMissingCssEntries(ctx, tailwindRuntime) { if (hasWarnedMissingCssEntries) return; if (!tailwindRuntime) return; if (hasConfiguredCssEntries(ctx) || hasConfiguredTailwindV4CssRoots(ctx)) return; hasWarnedMissingCssEntries = true; logger.warn("[tailwindcss@4] 未检测到 cssEntries 配置。请传入包含 tailwindcss 引用的 CSS 绝对路径,例如 cssEntries: [\"/absolute/path/to/src/app.css\"],否则 tailwindcss 生成的类名不会参与转译。"); } function applyV4CssCalcDefaults(cssCalc, tailwindRuntime) { const cssCalcOptions = cssCalc ?? false; if (tailwindRuntime && cssCalcOptions) return ensureDefaultsIncluded(cssCalcOptions); return cssCalcOptions; } //#endregion //#region src/tailwindcss/runtime-resolve.ts const GENERIC_RELATIVE_SPECIFIERS = [".", ".."]; const DEFAULT_TAILWIND_CONFIG_SPECIFIERS = ["stubs/config.full.js", "defaultConfig.js"]; const TAILWIND_CONFIG_FILES = [ "tailwind.config.js", "tailwind.config.cjs", "tailwind.config.mjs", "tailwind.config.ts", "tailwind.config.cts", "tailwind.config.mts" ]; function isPathSpecifier(specifier) { if (!specifier) return false; if (specifier.startsWith("file://")) return true; if (path.isAbsolute(specifier)) return true; return GENERIC_RELATIVE_SPECIFIERS.some((prefix) => specifier.startsWith(`${prefix}/`) || specifier.startsWith(`${prefix}\\`)); } function resolveModuleFromPaths(specifier, paths) { if (!specifier || isPathSpecifier(specifier) || paths.length === 0) return; try { return createRequire(import.meta.url).resolve(specifier, { paths }); } catch { return; } } function resolveTailwindConfigFallback(packageName, paths) { if (!packageName) return; for (const suffix of DEFAULT_TAILWIND_CONFIG_SPECIFIERS) { const resolved = resolveModuleFromPaths(`${packageName}/${suffix}`, paths); if (resolved) return resolved; } } function appendNodeModules(paths, dir) { if (!dir) return; const nodeModulesDir = path.join(dir, "node_modules"); if (existsSync(nodeModulesDir)) paths.add(nodeModulesDir); } function findTailwindConfig(searchRoots) { for (const root of searchRoots) for (const file of TAILWIND_CONFIG_FILES) { const candidate = path.resolve(root, file); if (existsSync(candidate)) return candidate; } } function createDefaultResolvePaths(basedir) { const paths = /* @__PURE__ */ new Set(); let fallbackCandidates = []; if (basedir) { const resolvedBase = path.resolve(basedir); appendNodeModules(paths, resolvedBase); fallbackCandidates.push(resolvedBase); const packageRoot = findNearestPackageRoot(resolvedBase); if (packageRoot) { appendNodeModules(paths, packageRoot); fallbackCandidates.push(packageRoot); } const workspaceRoot = findWorkspaceRoot(resolvedBase); if (workspaceRoot) { appendNodeModules(paths, workspaceRoot); fallbackCandidates.push(workspaceRoot); } } const cwd = process.cwd(); appendNodeModules(paths, cwd); try { const modulePath = fileURLToPath(import.meta.url); const candidate = existsSync(modulePath) && !path.extname(modulePath) ? modulePath : path.dirname(modulePath); paths.add(candidate); } catch { paths.add(import.meta.url); } if (paths.size === 0) { fallbackCandidates = fallbackCandidates.filter(Boolean); if (fallbackCandidates.length === 0) fallbackCandidates.push(cwd); for (const candidate of fallbackCandidates) paths.add(candidate); } return [...paths]; } //#endregion //#region src/tailwindcss/version.ts function normalizeSupportedTailwindcssMajorVersion(version) { return version === 4 ? version : void 0; } function readPackageJson(packageJsonPath) { try { return JSON.parse(readFileSync(packageJsonPath, "utf8")); } catch { return; } } function findPackageJsonDeclaringPackage(packageName, base) { let current = path.resolve(base); while (true) { const pkgPath = path.join(current, "package.json"); if (existsSync(pkgPath)) { const pkg = readPackageJson(pkgPath); if (readDeclaredPackageVersion(packageName, pkg)) return pkgPath; if (pkg?.name !== "weapp-tailwindcss") return; } const parent = path.dirname(current); if (parent === current) return; current = parent; } } function readDeclaredPackageVersion(packageName, pkg) { return pkg?.dependencies?.[packageName] ?? pkg?.devDependencies?.[packageName] ?? pkg?.peerDependencies?.[packageName] ?? pkg?.optionalDependencies?.[packageName]; } function readDeclaredPackageMajorVersion(version) { const match = version?.match(/(?:^|\D)(4)(?:\.|\b)/); return normalizeSupportedTailwindcssMajorVersion(match ? Number(match[1]) : void 0); } function readInstalledPackageMajorVersion(packageName, base) { const packageJsonPath = findPackageJsonDeclaringPackage(packageName, base); if (!packageJsonPath) return; const declaredVersion = readDeclaredPackageVersion(packageName, readPackageJson(packageJsonPath)); if (!declaredVersion) return; try { const pkg = createRequire(packageJsonPath)(`${packageName}/package.json`); return normalizeSupportedTailwindcssMajorVersion(Number(pkg.version?.split(".")[0])); } catch { return readDeclaredPackageMajorVersion(declaredVersion); } } //#endregion //#region src/tailwindcss/runtime-factory.ts const require = createRequire(import.meta.url); function createPackageInfo(tailwindOptions) { const packageName = tailwindOptions?.packageName ?? "tailwindcss"; const resolvePaths = tailwindOptions?.resolve?.paths; let rootPath; const cwdPackageJsonPath = tailwindOptions?.cwd ? path.join(tailwindOptions.cwd, "package.json") : void 0; if (cwdPackageJsonPath) try { if (require(cwdPackageJsonPath).name === packageName) rootPath = cwdPackageJsonPath; } catch {} rootPath ?? (rootPath = resolveModuleFromPaths(`${packageName}/package.json`, resolvePaths ?? [])); if (!rootPath) for (const resolvePath of [tailwindOptions?.cwd, ...resolvePaths ?? []]) { if (!resolvePath) continue; try { rootPath = createRequire(path.join(resolvePath, "package.json")).resolve(`${packageName}/package.json`); break; } catch {} } if (rootPath) { const packageJsonPath = rootPath; const packageRoot = path.dirname(packageJsonPath); try { const packageJson = require(packageJsonPath); return { name: packageName, version: packageJson.version, rootPath: packageRoot, packageJsonPath, packageJson }; } catch { return { name: packageName, version: void 0, rootPath: packageRoot, packageJsonPath, packageJson: {} }; } } return { name: packageName, version: void 0, rootPath: "", packageJsonPath: "", packageJson: {} }; } function resolveMajorVersion(tailwindOptions, packageInfo) { if (tailwindOptions?.version === 4) return tailwindOptions.version; if (packageInfo.version?.startsWith("4.")) return 4; return 4; } function createFallbackTailwindcssRuntime(options) { const packageInfo = createPackageInfo(options?.tailwindcss); return { packageInfo, majorVersion: resolveMajorVersion(options?.tailwindcss, packageInfo), options, async getClassSet() { return /* @__PURE__ */ new Set(); }, async extract(_options) { return { classList: [], classSet: /* @__PURE__ */ new Set() }; }, async collectContentTokens() { return { entries: [], filesScanned: 0, sources: [], skippedFiles: [] }; } }; } function createEngineTailwindcssRuntime(options) { const tailwindOptions = options.tailwindcss; const packageInfo = createPackageInfo(tailwindOptions); const majorVersion = resolveMajorVersion(tailwindOptions, packageInfo); let classSetCache; let runtime; function applyClassSetFilter(classSet) { if (typeof options.filter !== "function") return classSet; return new Set([...classSet].filter((className) => options.filter?.(className) !== false)); } function hasTailwindV4ApplyContext(css) { if (!css.includes("@apply")) return false; try { const root = postcss.parse(css); let hasContext = false; root.walkAtRules((rule) => { if (rule.name === "reference" || rule.name === "import" || rule.name === "tailwind" && rule.params.trim() === "utilities") hasContext = true; }); return hasContext; } catch { return false; } } async function collectClassSet() { const report = await collectContentTokens(); const rawCandidates = new Set(report.entries.map((entry) => { return typeof entry === "string" ? entry : entry.rawCandidate; }).filter((entry) => typeof entry === "string" && entry.length > 0)); const source = resolveCssMacroTailwindV4Source(await resolveTailwindV4SourceFromRuntime(runtime)); const designSystem = await loadTailwindV4DesignSystem(source); const candidates = new Set(resolveValidTailwindV4Candidates(designSystem, rawCandidates, { ...source.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: source.bareArbitraryValues } })); await collectTailwindV4CssCandidates(candidates); return applyClassSetFilter(candidates); } async function collectTailwindV4CssCandidates(candidates) { const source = resolveCssMacroTailwindV4Source(await resolveTailwindV4SourceFromRuntime(runtime)); const cssList = [source.css, ...(source.cssSources ?? []).map((cssSource) => cssSource.css).filter((css) => typeof css === "string")]; for (const css of cssList) { if (!hasTailwindV4ApplyContext(css)) continue; try { postcss.parse(css).walkAtRules("apply", (rule) => { for (const candidate of rule.params.split(/\s+/)) { const normalized = candidate.replace(/!important$/, "").trim(); if (normalized) candidates.add(normalized); } }); } catch {} } } async function collectContentTokens() { const source = resolveCssMacroTailwindV4Source(await resolveTailwindV4SourceFromRuntime(runtime)); const report = await extractProjectCandidatesWithPositions({ base: source.base, baseFallbacks: source.baseFallbacks, css: source.css, cwd: source.projectRoot, ...source.sources === void 0 ? {} : { sources: source.sources } }); return { entries: report.entries, filesScanned: report.filesScanned, sources: source.sources ?? [], skippedFiles: report.skippedFiles }; } async function extract(options) { let classSet = await collectClassSet(); const filter = options?.removeUniversalSelector === true ? (className) => className !== "*" : void 0; if (filter) classSet = new Set([...classSet].filter(filter)); if (typeof options?.write === "boolean") {} classSetCache = classSet; return { classList: [...classSet], classSet }; } runtime = { packageInfo, majorVersion, options, async getClassSet() { return (await extract({ write: false })).classSet; }, getClassSetSync() { return classSetCache ?? /* @__PURE__ */ new Set(); }, extract, collectContentTokens }; return runtime; } let hasLoggedMissingTailwind = false; const TAILWINDCSS_NOT_FOUND_RE = /tailwindcss not found/i; const UNABLE_TO_LOCATE_TAILWINDCSS_RE = /unable to locate tailwind css package/i; function createTailwindcssRuntime(options) { const { basedir, cacheDir, supportCustomLengthUnits, tailwindcss, tailwindcssRuntimeOptions } = options || {}; const cache = { enabled: true, driver: "memory" }; const normalizedBasedir = basedir ? path.resolve(basedir) : void 0; const cacheRoot = findNearestPackageRoot(normalizedBasedir) ?? normalizedBasedir ?? process.cwd(); if (cacheDir) if (path.isAbsolute(cacheDir)) cache.dir = cacheDir; else if (normalizedBasedir) cache.dir = path.resolve(normalizedBasedir, cacheDir); else cache.dir = path.resolve(process.cwd(), cacheDir); else cache.dir = path.join(cacheRoot, "node_modules", ".cache", "@tailwindcss-mangle", "engine"); if (normalizedBasedir) cache.cwd = normalizedBasedir; const resolvePaths = createDefaultResolvePaths(cache.cwd ?? normalizedBasedir ?? process.cwd()); const normalizedUserOptions = normalizeTailwindcssRuntimeOptions(tailwindcssRuntimeOptions); const extendLengthUnits = normalizeExtendLengthUnits(supportCustomLengthUnits ?? true); const baseTailwindOptions = defuOverrideArray(tailwindcss ?? {}, omitUndefined({ cwd: normalizedBasedir, resolve: { paths: resolvePaths } })); baseTailwindOptions.packageName = tailwindcss?.packageName ?? normalizedUserOptions?.tailwindcss?.packageName ?? baseTailwindOptions.packageName ?? "tailwindcss"; if (!baseTailwindOptions.postcssPlugin) baseTailwindOptions.postcssPlugin = "tailwindcss"; if (typeof baseTailwindOptions.postcssPlugin === "string") { const resolvedPlugin = resolveModuleFromPaths(baseTailwindOptions.postcssPlugin, resolvePaths); if (resolvedPlugin) baseTailwindOptions.postcssPlugin = resolvedPlugin; } const baseOptions = omitUndefined({ projectRoot: normalizedBasedir, cache, tailwindcss: baseTailwindOptions, apply: omitUndefined({ exposeContext: true, extendLengthUnits }) }); const resolvedOptions = defuOverrideArray(normalizedUserOptions ?? {}, baseOptions); const resolvedTailwindOptions = resolvedOptions.tailwindcss; if (resolvedTailwindOptions) { const existingResolve = resolvedTailwindOptions.resolve ?? {}; const sourcePaths = Array.isArray(existingResolve.paths) && existingResolve.paths.length > 0 ? [...existingResolve.paths ?? [], ...resolvePaths] : resolvePaths; resolvedTailwindOptions.resolve = { ...existingResolve, paths: [...new Set(sourcePaths)] }; logger.debug("Tailwind resolve config %O", { packageName: resolvedTailwindOptions.packageName, version: resolvedTailwindOptions.version, resolve: resolvedTailwindOptions.resolve, cwd: resolvedTailwindOptions.cwd }); if (typeof resolvedTailwindOptions.postcssPlugin === "string") { const resolvedPlugin = resolveModuleFromPaths(resolvedTailwindOptions.postcssPlugin, resolvedTailwindOptions.resolve?.paths ?? resolvePaths); if (resolvedPlugin) resolvedTailwindOptions.postcssPlugin = resolvedPlugin; } const searchRoots = /* @__PURE__ */ new Set(); if (resolvedTailwindOptions.cwd) searchRoots.add(resolvedTailwindOptions.cwd); for (const resolvePath of resolvedTailwindOptions.resolve?.paths ?? []) { const parentDir = path.dirname(resolvePath); searchRoots.add(parentDir); } const configPath = findTailwindConfig(searchRoots); if (!resolvedTailwindOptions.config) if (configPath) resolvedTailwindOptions.config = configPath; else { const fallbackConfig = resolveTailwindConfigFallback(resolvedTailwindOptions.packageName, resolvedTailwindOptions.resolve.paths ?? resolvePaths); if (fallbackConfig) resolvedTailwindOptions.config = fallbackConfig; } if (!resolvedTailwindOptions.cwd && configPath) resolvedTailwindOptions.cwd = path.dirname(configPath); resolvedOptions.tailwindcss = resolvedTailwindOptions; } try { return createEngineTailwindcssRuntime(resolvedOptions); } catch (error) { const searchPaths = resolvedOptions.tailwindcss?.resolve?.paths; if (error instanceof Error && TAILWINDCSS_NOT_FOUND_RE.test(error.message)) { if (!hasLoggedMissingTailwind) { logger.warn("Tailwind CSS 未安装,已跳过 Tailwind 运行时能力。若需使用 Tailwind 能力,请安装 tailwindcss。"); hasLoggedMissingTailwind = true; } return createFallbackTailwindcssRuntime(resolvedOptions); } if (error instanceof Error && UNABLE_TO_LOCATE_TAILWINDCSS_RE.test(error.message)) logger.error("无法定位 Tailwind CSS 包 \"%s\",已尝试路径: %O", resolvedOptions.tailwindcss?.packageName, searchPaths); throw error; } } //#endregion //#region src/tailwindcss/v4/multi-runtime.ts function createMultiTailwindcssRuntime(runtimes) { if (ru