UNPKG

weapp-tailwindcss

Version:

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

556 lines (555 loc) 24 kB
const require_rolldown_runtime = require("./rolldown-runtime-CVvi-lCc.cjs"); const require_generator = require("./generator-BE34PYSC.cjs"); const require_v4_generation_core = require("./v4-generation-core-CDI6ChiH.cjs"); const require_wxml = require("./wxml-DSMNuUaP.cjs"); const require_tailwindcss = require("./tailwindcss-DcpiKvmJ.cjs"); let node_process = require("node:process"); node_process = require_rolldown_runtime.__toESM(node_process, 1); let _tailwindcss_mangle_engine = require("@tailwindcss-mangle/engine"); let _weapp_core_escape = require("@weapp-core/escape"); //#region src/compiler/runtime-build-state.ts function invertLinkedByEntry(linkedByEntry) { const dependentsByLinkedFile = /* @__PURE__ */ new Map(); for (const [entryFile, linkedFiles] of linkedByEntry.entries()) for (const linkedFile of linkedFiles) { let dependents = dependentsByLinkedFile.get(linkedFile); if (!dependents) { dependents = /* @__PURE__ */ new Set(); dependentsByLinkedFile.set(linkedFile, dependents); } dependents.add(entryFile); } return dependentsByLinkedFile; } function replaceMapEntries(target, source) { target.clear(); for (const [key, value] of source) target.set(key, value); return target; } function mergeMapEntries(target, source) { for (const [key, value] of source) target.set(key, value); return target; } function createRuntimeCompilationBuildState() { return { iteration: 0, sourceHashByFile: /* @__PURE__ */ new Map(), runtimeAffectingSignatureByFile: /* @__PURE__ */ new Map(), runtimeAffectingHashByFile: /* @__PURE__ */ new Map(), linkedByEntry: /* @__PURE__ */ new Map(), dependentsByLinkedFile: /* @__PURE__ */ new Map(), bundleMarkupCandidatesByFile: /* @__PURE__ */ new Map(), generatorCandidateSignature: void 0 }; } function updateRuntimeCompilationBuildState(state, snapshot, linkedByEntry, options = {}) { const incremental = options.incremental === true; removeRuntimeCompilationBuildStateFiles(state, snapshot.removedFiles); state.iteration += 1; state.sourceHashByFile = incremental ? mergeMapEntries(state.sourceHashByFile, snapshot.sourceHashByFile) : replaceMapEntries(state.sourceHashByFile, snapshot.sourceHashByFile); state.runtimeAffectingSignatureByFile.clear(); state.runtimeAffectingHashByFile = incremental ? mergeMapEntries(state.runtimeAffectingHashByFile, snapshot.runtimeAffectingHashByFile) : replaceMapEntries(state.runtimeAffectingHashByFile, snapshot.runtimeAffectingHashByFile); state.linkedByEntry = incremental ? mergeMapEntries(state.linkedByEntry, linkedByEntry) : replaceMapEntries(state.linkedByEntry, linkedByEntry); state.dependentsByLinkedFile = invertLinkedByEntry(state.linkedByEntry); } function removeRuntimeCompilationBuildStateFiles(state, files) { const removedFiles = new Set(files); if (removedFiles.size === 0) return; for (const file of removedFiles) { state.sourceHashByFile.delete(file); state.runtimeAffectingSignatureByFile.delete(file); state.runtimeAffectingHashByFile.delete(file); state.bundleMarkupCandidatesByFile.delete(file); state.linkedByEntry.delete(file); } for (const linkedFiles of state.linkedByEntry.values()) for (const file of removedFiles) linkedFiles.delete(file); state.dependentsByLinkedFile = invertLinkedByEntry(state.linkedByEntry); } function resetRuntimeCompilationBuildState(state) { state.iteration = 0; state.sourceHashByFile.clear(); state.runtimeAffectingSignatureByFile.clear(); state.runtimeAffectingHashByFile.clear(); state.linkedByEntry.clear(); state.dependentsByLinkedFile.clear(); state.bundleMarkupCandidatesByFile.clear(); state.generatorCandidateSignature = void 0; } function createRuntimeCompilationAffectingSignature(snapshot, computeHash) { const entries = [...snapshot.runtimeAffectingHashByFile].sort(([first], [second]) => first.localeCompare(second)).map(([file, hash]) => `${file}:${hash}`); return entries.length === 0 ? "runtime-affecting:0" : `runtime-affecting:1:${computeHash(entries.join("\n"))}`; } //#endregion //#region src/compiler/runtime-snapshot.ts function createChangedByType() { return { html: /* @__PURE__ */ new Set(), js: /* @__PURE__ */ new Set(), css: /* @__PURE__ */ new Set(), other: /* @__PURE__ */ new Set() }; } function createProcessFiles() { return { html: /* @__PURE__ */ new Set(), js: /* @__PURE__ */ new Set(), css: /* @__PURE__ */ new Set() }; } function markProcessFile(type, file, processFiles) { if (type === "html" || type === "js" || type === "css") processFiles[type].add(file); } function markFilesByEntryType(files, entriesByFile, target) { for (const file of files ?? []) { const entry = entriesByFile.get(file); if (entry) target[entry.type].add(file); } } function createRuntimeCompilationSnapshot(entries, options = {}) { const changedByType = createChangedByType(); const runtimeAffectingChangedByType = createChangedByType(); const processFiles = createProcessFiles(); const entriesByFile = new Map(entries.map((entry) => [entry.file, entry])); markFilesByEntryType(options.changedFiles, entriesByFile, changedByType); markFilesByEntryType(options.runtimeAffectingChangedFiles, entriesByFile, runtimeAffectingChangedByType); for (const file of options.processFiles ?? []) { const entry = entriesByFile.get(file); if (entry) markProcessFile(entry.type, file, processFiles); } return { entries, sourceHashByFile: /* @__PURE__ */ new Map(), runtimeAffectingSignatureByFile: /* @__PURE__ */ new Map(), runtimeAffectingHashByFile: /* @__PURE__ */ new Map(), hasOmittedKnownFiles: options.hasOmittedKnownFiles === true, removedFiles: new Set([...options.removedFiles ?? []].filter((file) => !entriesByFile.has(file))), changedByType, runtimeAffectingChangedByType, processFiles, linkedImpactsByEntry: /* @__PURE__ */ new Map() }; } function buildRuntimeCompilationSnapshot(entries, state, options) { const snapshot = createRuntimeCompilationSnapshot(entries, { hasOmittedKnownFiles: options.hasOmittedKnownFiles, removedFiles: options.removedFiles }); const firstRun = state.iteration === 0 && state.sourceHashByFile.size === 0; for (const entry of entries) { const { file, source, type } = entry; const hash = options.computeHash(source); snapshot.sourceHashByFile.set(file, hash); const previousHash = state.sourceHashByFile.get(file); const changed = previousHash == null || previousHash !== hash; const previousRuntimeAffectingHash = state.runtimeAffectingHashByFile.get(file); const runtimeAffectingHash = !changed && previousRuntimeAffectingHash != null ? previousRuntimeAffectingHash : (() => { const signature = options.createRuntimeAffectingSignature(source, type); snapshot.runtimeAffectingSignatureByFile.set(file, signature); return options.computeHash(signature); })(); snapshot.runtimeAffectingHashByFile.set(file, runtimeAffectingHash); if (changed) snapshot.changedByType[type].add(file); if (previousRuntimeAffectingHash == null || previousRuntimeAffectingHash !== runtimeAffectingHash) snapshot.runtimeAffectingChangedByType[type].add(file); if (options.forceAll || firstRun) markProcessFile(type, file, snapshot.processFiles); else if (type === "html") snapshot.processFiles.html.add(file); else if (changed && (type === "js" || type === "css")) snapshot.processFiles[type].add(file); } if (!options.forceAll && !firstRun) for (const changedFile of snapshot.changedByType.js) { const dependents = state.dependentsByLinkedFile.get(changedFile); if (!dependents) continue; for (const entryFile of dependents) { snapshot.processFiles.js.add(entryFile); let impacts = snapshot.linkedImpactsByEntry.get(entryFile); if (!impacts) { impacts = /* @__PURE__ */ new Set(); snapshot.linkedImpactsByEntry.set(entryFile, impacts); } impacts.add(changedFile); } } return snapshot; } //#endregion //#region src/compiler/shadow-gate.ts const COMPILER_SHADOW_GATE_ENV = "WEAPP_TAILWINDCSS_COMPILER_SHADOW_GATE"; function resolveCompilerShadowGateMode(env = node_process.default.env) { const value = env[COMPILER_SHADOW_GATE_ENV]?.trim().toLowerCase(); return value === "report" || value === "error" ? value : "off"; } function evaluateCompilerShadowGate(snapshot, mode = "off") { const issues = []; if (snapshot.summary.mismatched > 0) issues.push("semantic-mismatch"); if (snapshot.summary.truncated > 0) issues.push("truncated-report"); if (snapshot.summary.dropped > 0) issues.push("dropped-report"); return { mode, passed: issues.length === 0, issues, snapshot }; } function emitCompilerShadowGateResult(payload) { node_process.default.stdout.write(`[weapp-tailwindcss:compiler-shadow] ${JSON.stringify(payload)}\n`); } function finalizeCompilerShadowRun(owner, options = {}) { const env = options.env ?? node_process.default.env; if (require_v4_generation_core.resolveCompilerMode(env) !== "shadow") return; const snapshot = require_v4_generation_core.completeCompilerShadowReportRun(owner, options.revision); if (!snapshot) return; const result = evaluateCompilerShadowGate(snapshot, resolveCompilerShadowGateMode(env)); if (result.mode !== "off") { const payload = { mode: result.mode, passed: result.passed, issues: result.issues, revision: snapshot.revision, summary: snapshot.summary }; if (options.emit) options.emit(payload); else emitCompilerShadowGateResult(payload); } if (result.mode === "error" && !result.passed) throw new Error(`weapp-tailwindcss shadow gate failed: ${result.issues.join(", ")}`); return result; } //#endregion //#region src/bundlers/shared/runtime-class-set/entries.ts const EXTENSION_DOT_PREFIX_RE = /^\./; function createRuntimeEntries(snapshot) { return snapshot.entries.filter((entry) => entry.runtimeCandidate !== false); } function collectChangedRuntimeFiles(snapshot) { return /* @__PURE__ */ new Set([...snapshot.runtimeAffectingChangedByType.html, ...snapshot.runtimeAffectingChangedByType.js]); } function resolveEntryExtension(entry) { if (entry.type === "html") return "html"; const ext = entry.file.split(/[?#]/, 1)[0]?.split(".").pop()?.replace(EXTENSION_DOT_PREFIX_RE, "") ?? ""; if (ext.length > 0) return ext; return "js"; } //#endregion //#region src/bundlers/shared/runtime-class-set.ts const debug = require_wxml.createDebug("[runtime-set] "); function createExtractOptions(context, source, bareArbitraryValues) { return { cwd: context.projectRoot, base: context.base, baseFallbacks: context.baseFallbacks, css: context.css, content: source, extension: "html", ...bareArbitraryValues === void 0 ? {} : { bareArbitraryValues } }; } function createCandidateValidationSource(candidates) { return [...new Set(candidates)].sort().join("\n"); } function removeCandidateSet(candidateCountByClass, candidates) { for (const className of candidates) { const count = candidateCountByClass.get(className); if (count == null) continue; if (count <= 1) { candidateCountByClass.delete(className); continue; } candidateCountByClass.set(className, count - 1); } } function addCandidateSet(candidateCountByClass, candidates) { for (const className of candidates) { const nextCount = (candidateCountByClass.get(className) ?? 0) + 1; candidateCountByClass.set(className, nextCount); } } function createRuntimeClassSet(baseClassSet, candidateCountByClass) { return /* @__PURE__ */ new Set([...baseClassSet, ...candidateCountByClass.keys()]); } function createNonSourceBaseClassSet(baseClassSet, candidateCountByClass) { const nextBaseClassSet = new Set(baseClassSet); for (const candidate of candidateCountByClass.keys()) nextBaseClassSet.delete(candidate); return nextBaseClassSet; } function createRuntimeClassSetManager(options = {}) { const customExtractCandidates = options.extractCandidates; const extractCandidates = customExtractCandidates ?? _tailwindcss_mangle_engine.extractValidCandidates; const extractRawCandidates = options.extractRawCandidates ?? _tailwindcss_mangle_engine.extractRawCandidatesWithPositions; const escapeMap = options.escapeMap ?? _weapp_core_escape.MappingChars2String; const escapeFragments = require_v4_generation_core.createEscapeFragments(escapeMap); let baseClassSet = /* @__PURE__ */ new Set(); const candidateCountByClass = /* @__PURE__ */ new Map(); const candidatesByFile = /* @__PURE__ */ new Map(); const candidateValidityCache = /* @__PURE__ */ new Map(); let runtimeSignature; let validationContext; let designSystemPromise; async function reset() { baseClassSet = /* @__PURE__ */ new Set(); candidateCountByClass.clear(); candidatesByFile.clear(); candidateValidityCache.clear(); runtimeSignature = void 0; validationContext = void 0; designSystemPromise = void 0; } async function resolveValidationContextCached(runtime) { if (!validationContext) validationContext = await require_generator.resolveTailwindV4SourceFromRuntime(runtime); return validationContext; } async function loadDesignSystem(context) { if (!designSystemPromise) designSystemPromise = (0, _tailwindcss_mangle_engine.loadTailwindV4DesignSystem)(context); return designSystemPromise; } function populateCandidateValidityCacheFromDesignSystem(designSystem, unknownCandidates) { const validCandidates = (0, _tailwindcss_mangle_engine.resolveValidTailwindV4Candidates)(designSystem, unknownCandidates, { ...options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: options.bareArbitraryValues } }); for (const candidate of unknownCandidates) candidateValidityCache.set(candidate, validCandidates.has(candidate)); } async function validateUnknownCandidates(runtime, unknownCandidates) { if (unknownCandidates.size === 0) return; const context = await resolveValidationContextCached(runtime); if (!customExtractCandidates) try { populateCandidateValidityCacheFromDesignSystem(await loadDesignSystem(context), unknownCandidates); return; } catch (error) { debug("incremental design-system validation failed: %O", error); designSystemPromise = void 0; throw error; } const source = createCandidateValidationSource(unknownCandidates); const validCandidates = new Set(await extractCandidates(createExtractOptions(context, source, options.bareArbitraryValues))); for (const candidate of unknownCandidates) candidateValidityCache.set(candidate, validCandidates.has(candidate)); } async function extractEntryRawCandidates(entry, _runtime, _knownSourceCandidates) { const extension = resolveEntryExtension(entry); const matches = options.bareArbitraryValues === void 0 || options.bareArbitraryValues === false ? await extractRawCandidates(entry.source, extension) : await extractRawCandidates(entry.source, extension, { bareArbitraryValues: options.bareArbitraryValues }); const candidates = /* @__PURE__ */ new Set(); for (const match of matches) { const candidate = match?.rawCandidate; if (typeof candidate === "string" && candidate.length > 0) candidates.add(candidate); } for (const candidate of require_v4_generation_core.collectEscapedRuntimeCandidates(entry.source, escapeMap, escapeFragments)) candidates.add(candidate); return candidates; } async function sync(runtime, snapshot, options = {}) { const nextSignature = require_tailwindcss.getRuntimeClassSetSignature(runtime) ?? "runtime:missing"; const runtimeEntries = createRuntimeEntries(snapshot); const runtimeEntriesByFile = new Map(runtimeEntries.map((entry) => [entry.file, entry])); const currentRuntimeFiles = new Set(runtimeEntriesByFile.keys()); const hadTrackedRuntimeFiles = candidatesByFile.size > 0; const fullRebuild = runtimeSignature !== nextSignature || candidatesByFile.size === 0; if (runtimeSignature !== nextSignature) { debug("runtime signature changed, reset incremental runtime set: %s", nextSignature); await reset(); } runtimeSignature = nextSignature; const nextBaseClassSet = options.baseClassSet; const canUseBaseWithoutInitialFullScan = Boolean(fullRebuild && !hadTrackedRuntimeFiles && options.skipInitialFullScanWithBase === true && nextBaseClassSet && nextBaseClassSet.size > 0); for (const [file, previousCandidates] of candidatesByFile) { if (!snapshot.removedFiles.has(file) && (currentRuntimeFiles.has(file) || snapshot.hasOmittedKnownFiles)) continue; removeCandidateSet(candidateCountByClass, previousCandidates); candidatesByFile.delete(file); } const changedRuntimeFiles = canUseBaseWithoutInitialFullScan ? [...collectChangedRuntimeFiles(snapshot)] : fullRebuild ? [...runtimeEntriesByFile.keys()] : [...collectChangedRuntimeFiles(snapshot)]; if (changedRuntimeFiles.length === 0) { if (nextBaseClassSet) baseClassSet = canUseBaseWithoutInitialFullScan ? new Set(nextBaseClassSet) : createNonSourceBaseClassSet(nextBaseClassSet, candidateCountByClass); return createRuntimeClassSet(baseClassSet, candidateCountByClass); } const rawCandidatesByFile = /* @__PURE__ */ new Map(); const unknownCandidates = /* @__PURE__ */ new Set(); for (const file of changedRuntimeFiles) { const entry = runtimeEntriesByFile.get(file); if (!entry) continue; const candidates = await extractEntryRawCandidates(entry, runtime, nextBaseClassSet); rawCandidatesByFile.set(file, candidates); for (const candidate of candidates) if (!candidateValidityCache.has(candidate)) unknownCandidates.add(candidate); } await validateUnknownCandidates(runtime, unknownCandidates); let rawCandidateCount = 0; for (const file of changedRuntimeFiles) { const nextRawCandidates = rawCandidatesByFile.get(file); const previousCandidates = candidatesByFile.get(file); if (previousCandidates) removeCandidateSet(candidateCountByClass, previousCandidates); if (!nextRawCandidates || nextRawCandidates.size === 0) { candidatesByFile.delete(file); continue; } rawCandidateCount += nextRawCandidates.size; const nextCandidates = new Set([...nextRawCandidates].filter((candidate) => candidateValidityCache.get(candidate) === true)); if (nextCandidates.size === 0) { candidatesByFile.delete(file); continue; } addCandidateSet(candidateCountByClass, nextCandidates); candidatesByFile.set(file, nextCandidates); } if (nextBaseClassSet) baseClassSet = canUseBaseWithoutInitialFullScan ? new Set(nextBaseClassSet) : createNonSourceBaseClassSet(nextBaseClassSet, candidateCountByClass); const runtimeSet = createRuntimeClassSet(baseClassSet, candidateCountByClass); debug("incremental runtime set synced, changedFiles=%d rawCandidates=%d validateMisses=%d runtimeSize=%d trackedFiles=%d", changedRuntimeFiles.length, rawCandidateCount, unknownCandidates.size, runtimeSet.size, candidatesByFile.size); return new Set(runtimeSet); } return { sync, reset }; } //#endregion //#region src/bundlers/shared/cache.ts async function processCachedTask({ cache, cacheKey, hashKey = cacheKey, rawSource, hash, readCache, applyResult, transform, onCacheHit }) { let cacheHit = false; const result = await cache.process({ key: cacheKey, hashKey, rawSource, hash, resolveCache: readCache, async onCacheHit(value) { cacheHit = true; await applyResult(value, { cacheHit: true }); onCacheHit?.(); }, transform }); if (!cacheHit) await applyResult(result, { cacheHit: false }); } //#endregion //#region src/bundlers/shared/hmr-timing.ts function shouldEmitHmrTiming() { return node_process.default.env["WEAPP_TW_WATCH_REGRESSION"] === "1" || node_process.default.env["WEAPP_TW_HMR_TIMING"] === "1"; } function shouldEmitHumanReadableTiming() { return node_process.default.env["WEAPP_TW_HMR_TIMING"] === "1" && node_process.default.env["WEAPP_TW_HMR_TIMING_LOG"] !== "0"; } function emitHmrTiming(bundler, phase, durationMs, details = {}) { if (!shouldEmitHmrTiming()) return; const serializableDetails = { ...details }; delete serializableDetails.emit; const payload = { bundler, phase, durationMs: Math.max(0, Math.round(durationMs)), ...serializableDetails, ...typeof details.wallMs === "number" ? { wallMs: Math.max(0, Math.round(details.wallMs)) } : {} }; node_process.default.stdout.write(`[weapp-tailwindcss:hmr] ${JSON.stringify(payload)}\n`); if (shouldEmitHumanReadableTiming()) { const fileSuffix = details.file ? ` file=${details.file}` : ""; if (details.metric === "total") { const hooks = details.hooks ? Object.entries(details.hooks).map(([hook, summary]) => `${hook}=${Math.max(0, Math.round(summary.durationMs))}ms/${summary.count}`).join(", ") : ""; const hookSuffix = hooks ? ` (${hooks})` : ""; const wallSuffix = typeof payload.wallMs === "number" ? ` wall=${payload.wallMs}ms` : ""; node_process.default.stdout.write(`[weapp-tailwindcss] ${bundler}:weapp-tailwindcss 总耗时 ${payload.durationMs}ms${wallSuffix}${hookSuffix}\n`); return; } node_process.default.stdout.write(`[weapp-tailwindcss] ${bundler}:${phase} 耗时 ${payload.durationMs}ms${fileSuffix}\n`); } } function createHmrTimingRecorder(bundler) { const session = { hooks: {}, totalMs: 0 }; const record = (phase, durationMs, details = {}) => { const roundedDuration = Math.max(0, Math.round(durationMs)); if (session.startedAt === void 0) session.startedAt = performance.now() - Math.max(0, durationMs); session.totalMs += Math.max(0, durationMs); const current = session.hooks[phase] ?? { count: 0, durationMs: 0, maxMs: 0 }; current.count += 1; current.durationMs += roundedDuration; current.maxMs = Math.max(current.maxMs, roundedDuration); session.hooks[phase] = current; if (details.emit !== false) emitHmrTiming(bundler, phase, durationMs, details); }; const measure = async (phase, task, details = {}) => { const startedAt = performance.now(); try { return await task(); } finally { record(phase, performance.now() - startedAt, details); } }; const emitTotal = (phase = "total") => { if (session.totalMs <= 0) return; const wallMs = session.startedAt === void 0 ? session.totalMs : performance.now() - session.startedAt; emitHmrTiming(bundler, phase, session.totalMs, { hooks: session.hooks, metric: "total", wallMs }); session.hooks = {}; delete session.startedAt; session.totalMs = 0; }; return { emitTotal, measure, record }; } //#endregion Object.defineProperty(exports, "buildRuntimeCompilationSnapshot", { enumerable: true, get: function() { return buildRuntimeCompilationSnapshot; } }); Object.defineProperty(exports, "createHmrTimingRecorder", { enumerable: true, get: function() { return createHmrTimingRecorder; } }); Object.defineProperty(exports, "createRuntimeClassSetManager", { enumerable: true, get: function() { return createRuntimeClassSetManager; } }); Object.defineProperty(exports, "createRuntimeCompilationAffectingSignature", { enumerable: true, get: function() { return createRuntimeCompilationAffectingSignature; } }); Object.defineProperty(exports, "createRuntimeCompilationBuildState", { enumerable: true, get: function() { return createRuntimeCompilationBuildState; } }); Object.defineProperty(exports, "emitHmrTiming", { enumerable: true, get: function() { return emitHmrTiming; } }); Object.defineProperty(exports, "finalizeCompilerShadowRun", { enumerable: true, get: function() { return finalizeCompilerShadowRun; } }); Object.defineProperty(exports, "processCachedTask", { enumerable: true, get: function() { return processCachedTask; } }); Object.defineProperty(exports, "removeRuntimeCompilationBuildStateFiles", { enumerable: true, get: function() { return removeRuntimeCompilationBuildStateFiles; } }); Object.defineProperty(exports, "resetRuntimeCompilationBuildState", { enumerable: true, get: function() { return resetRuntimeCompilationBuildState; } }); Object.defineProperty(exports, "updateRuntimeCompilationBuildState", { enumerable: true, get: function() { return updateRuntimeCompilationBuildState; } });