UNPKG

weapp-tailwindcss

Version:

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

343 lines (342 loc) 13.1 kB
import { n as _defineProperty } from "./object-Bvy6-w9r.js"; import { g as babelParse, i as analyzeSource, m as replaceWxml, n as generateCode, o as JsTokenUpdater, r as createAttributeMatcher, u as isClassContextLiteralPath } from "./wxml-IBzZSaQC.js"; import { splitCandidateTokens } from "@tailwindcss-mangle/engine"; import MagicString from "magic-string"; import { NodeTypes, parse } from "@vue/compiler-dom"; //#region src/uni-app-x/component-local-style.ts const EXPRESSION_WRAPPER_PREFIX = "(\n"; const EXPRESSION_WRAPPER_SUFFIX = "\n)"; const COMPONENT_RE = /(?:^|[/\\])components(?:[/\\].+)?\.(?:uvue|nvue)$/; const PAGE_RE = /(?:^|[/\\])pages(?:[/\\].+)?\.(?:uvue|nvue)$/; function createStableHash(input) { let hash = 2166136261; for (let i = 0; i < input.length; i++) { hash ^= input.charCodeAt(i); hash = Math.imul(hash, 16777619); } return (hash >>> 0).toString(36); } function extractLiteralValue(path) { if (path.isStringLiteral()) return { literal: path.node.value, offset: 1 }; return { literal: typeof path.node.value === "string" ? path.node.value : path.node.value.raw, offset: 0 }; } function createAlias(fileId, utility, index) { return `wtu-${createStableHash(`${fileId}:${utility}`)}-${index.toString(36)}`; } function isRuntimeCandidate(candidate, runtimeSet) { if (!runtimeSet || runtimeSet.size === 0) return false; return runtimeSet.has(candidate) || runtimeSet.has(replaceWxml(candidate)); } function hasTopLevelVariant(candidate) { let bracketDepth = 0; let parenthesisDepth = 0; let quote = ""; let escaped = false; for (const char of candidate) { if (escaped) { escaped = false; continue; } if (char === "\\") { escaped = true; continue; } if (quote) { if (char === quote) quote = ""; continue; } if (char === "\"" || char === "'") { quote = char; continue; } if (char === "[") { bracketDepth += 1; continue; } if (char === "]") { bracketDepth = Math.max(0, bracketDepth - 1); continue; } if (char === "(") { parenthesisDepth += 1; continue; } if (char === ")") { parenthesisDepth = Math.max(0, parenthesisDepth - 1); continue; } if (char === ":" && bracketDepth === 0 && parenthesisDepth === 0) return true; } return false; } function shouldEnableComponentLocalStyle(id) { return COMPONENT_RE.test(id); } function shouldEnablePageLocalStyle(id) { return PAGE_RE.test(id); } var UniAppXComponentLocalStyleCollector = class { constructor(fileId, runtimeSet) { this.fileId = fileId; this.runtimeSet = runtimeSet; _defineProperty(this, "aliasByUtility", /* @__PURE__ */ new Map()); _defineProperty(this, "aliasByLookup", /* @__PURE__ */ new Map()); } ensureAlias(utility) { const cached = this.aliasByUtility.get(utility); if (cached) return cached; const alias = createAlias(this.fileId, utility, this.aliasByUtility.size); this.aliasByUtility.set(utility, alias); this.aliasByLookup.set(utility, alias); this.aliasByLookup.set(replaceWxml(utility), alias); return alias; } rewriteLiteral(literal) { const candidates = splitCandidateTokens(literal); if (candidates.length === 0) return literal; let rewritten = literal; for (const candidate of candidates) { if (!isRuntimeCandidate(candidate, this.runtimeSet)) continue; rewritten = rewritten.replace(candidate, hasTopLevelVariant(candidate) ? replaceWxml(candidate) : this.ensureAlias(candidate)); } return rewritten; } collectAndRewriteStaticClass(literal) { return this.rewriteLiteral(literal); } collectRuntimeClasses(rawSource, options = {}) { const wrapped = options.wrapExpression ? `${EXPRESSION_WRAPPER_PREFIX}${rawSource}${EXPRESSION_WRAPPER_SUFFIX}` : rawSource; try { const analysis = analyzeSource(babelParse(wrapped, { plugins: ["typescript"], sourceType: options.wrapExpression ? "module" : "unambiguous" }), {}, void 0, false); for (const path of analysis.targetPaths) { const { literal } = extractLiteralValue(path); const candidates = splitCandidateTokens(literal); const classContext = options.wrapExpression || isClassContextLiteralPath(path); for (const candidate of candidates) { if (!candidate || !classContext && !isRuntimeCandidate(candidate, this.runtimeSet)) continue; if (isRuntimeCandidate(candidate, this.runtimeSet) && !hasTopLevelVariant(candidate)) this.ensureAlias(candidate); } } } catch {} } rewriteTransformedCode(rawSource, options = {}) { if (this.aliasByLookup.size === 0) return rawSource; const wrapped = options.wrapExpression ? `${EXPRESSION_WRAPPER_PREFIX}${rawSource}${EXPRESSION_WRAPPER_SUFFIX}` : rawSource; try { const analysis = analyzeSource(babelParse(wrapped, { plugins: ["typescript"], sourceType: options.wrapExpression ? "module" : "unambiguous" }), {}, void 0, false); if (analysis.targetPaths.length === 0) return rawSource; const updater = new JsTokenUpdater(); for (const path of analysis.targetPaths) { const { literal, offset } = extractLiteralValue(path); const candidates = splitCandidateTokens(literal); if (candidates.length === 0) continue; let rewritten = literal; let mutated = false; for (const candidate of candidates) { const alias = this.aliasByLookup.get(candidate); if (!alias) continue; const replaced = rewritten.replace(candidate, alias); if (replaced !== rewritten) { rewritten = replaced; mutated = true; } } if (!mutated || typeof path.node.start !== "number" || typeof path.node.end !== "number") continue; updater.addToken({ start: path.node.start + offset, end: path.node.end - offset, value: rewritten, path }); } if (updater.length === 0) return rawSource; const ms = new MagicString(wrapped); updater.updateMagicString(ms); if (options.wrapExpression) { ms.remove(0, 2); ms.remove(wrapped.length - 2, wrapped.length); } return ms.toString(); } catch { return rawSource; } } hasStyles() { return this.aliasByUtility.size > 0; } toStyleBlock() { if (!this.hasStyles()) return ""; const lines = ["<style scoped>"]; for (const [utility, alias] of this.aliasByUtility) { lines.push(`.${alias} {`); lines.push(` @apply ${utility};`); lines.push("}"); } lines.push("</style>"); return `${lines.join("\n")}\n`; } }; //#endregion //#region src/uni-app-x/transform.ts function traverse(node, visitor) { visitor(node); if (Array.isArray(node.children)) { for (const child of node.children) if (child && typeof child === "object" && "type" in child) traverse(child, visitor); } } function updateStaticAttribute(ms, prop, offset, content = prop.value?.content) { if (!prop.value) return; const start = offset + prop.value.loc.start.offset + 1; const end = offset + prop.value.loc.end.offset - 1; if (start < end) ms.update(start, end, replaceWxml(content ?? "")); } function updateStaticAttributeWithLocalStyle(ms, prop, offset, collector, content = prop.value?.content) { if (!prop.value) return; const start = offset + prop.value.loc.start.offset + 1; const end = offset + prop.value.loc.end.offset - 1; if (start < end) ms.update(start, end, collector.collectAndRewriteStaticClass(content ?? "")); } function updateDirectiveExpression(ms, prop, offset, jsHandler, runtimeSet) { if (prop.exp?.type !== NodeTypes.SIMPLE_EXPRESSION) return; const expression = prop.exp.content; const start = offset + prop.exp.loc.start.offset; const end = offset + prop.exp.loc.end.offset; if (start >= end) return; const generated = generateCode(expression, { jsHandler, runtimeSet, wrapExpression: true }); ms.update(start, end, generated); } function updateDirectiveExpressionWithLocalStyle(ms, prop, offset, jsHandler, collector, runtimeSet) { if (prop.exp?.type !== NodeTypes.SIMPLE_EXPRESSION) return; const expression = prop.exp.content; const start = offset + prop.exp.loc.start.offset; const end = offset + prop.exp.loc.end.offset; if (start >= end) return; collector.collectRuntimeClasses(expression, { wrapExpression: true }); const generated = generateCode(expression, { jsHandler, runtimeSet, wrapExpression: true }); ms.update(start, end, collector.rewriteTransformedCode(generated, { wrapExpression: true })); } function shouldEnableLocalStyle(id, options) { if (options.enableComponentLocalStyle && shouldEnableComponentLocalStyle(id)) return true; if (options.enablePageLocalStyle && shouldEnablePageLocalStyle(id)) return true; return false; } function shouldHandleAttribute(tag, attrName, disabledDefaultTemplateHandler, matchCustomAttribute) { const lowerName = attrName.toLowerCase(); const shouldHandleDefault = !disabledDefaultTemplateHandler && lowerName === "class"; const shouldHandleCustom = matchCustomAttribute?.(tag, attrName) ?? false; return { shouldHandleDefault, shouldHandleCustom, shouldHandle: shouldHandleDefault || shouldHandleCustom }; } const defaultCreateJsHandlerOptions = { babelParserOptions: { plugins: ["typescript"] } }; const UVUE_NVUE_RE = /\.(?:uvue|nvue)(?:\?.*)?$/; const SFC_BLOCK_RE = /<(template|script)\b([^>]*)>([\s\S]*?)<\/\1>/gi; const SCRIPT_SETUP_RE = /(?:^|\s)setup(?:\s|=|$)/; function parseSfc(code) { const descriptor = { errors: [] }; for (const match of code.matchAll(SFC_BLOCK_RE)) { const type = match[1]; const attrs = match[2] ?? ""; const content = match[3] ?? ""; const full = match[0]; const contentStart = (match.index ?? 0) + full.indexOf(">") + 1; const block = { content, start: contentStart, end: contentStart + content.length, attrs }; if (type === "template" && !descriptor.template) { try { descriptor.template = { ...block, ast: parse(content) }; } catch (error) { descriptor.errors.push(error); descriptor.template = block; } continue; } if (type === "script") if (SCRIPT_SETUP_RE.test(attrs)) descriptor.scriptSetup ?? (descriptor.scriptSetup = block); else descriptor.script ?? (descriptor.script = block); } return descriptor; } function transformUVue(code, id, jsHandler, runtimeSet, options = {}) { if (!UVUE_NVUE_RE.test(id)) return; const { customAttributesEntities, disabledDefaultTemplateHandler = false } = options; const matchCustomAttribute = createAttributeMatcher(customAttributesEntities); const ms = new MagicString(code); const descriptor = parseSfc(code); const localStyleCollector = shouldEnableLocalStyle(id, options) ? new UniAppXComponentLocalStyleCollector(id, runtimeSet) : void 0; if (descriptor.errors.length === 0) { if (descriptor.template?.ast) { const templateOffset = descriptor.template.start; traverse(descriptor.template.ast, (node) => { if (node.type !== NodeTypes.ELEMENT) return; const tag = node.tag; for (const prop of node.props) if (prop.type === NodeTypes.ATTRIBUTE) { const { shouldHandle, shouldHandleDefault } = shouldHandleAttribute(tag, prop.name, disabledDefaultTemplateHandler, matchCustomAttribute); if (!shouldHandle) continue; if (shouldHandleDefault && localStyleCollector) updateStaticAttributeWithLocalStyle(ms, prop, templateOffset, localStyleCollector); else updateStaticAttribute(ms, prop, templateOffset); if (shouldHandleDefault) continue; } else if (prop.type === NodeTypes.DIRECTIVE && prop.name === "bind" && prop.arg?.type === NodeTypes.SIMPLE_EXPRESSION && prop.arg.isStatic) { const attrName = prop.arg.content; const { shouldHandle } = shouldHandleAttribute(tag, attrName, disabledDefaultTemplateHandler, matchCustomAttribute); if (!shouldHandle) continue; if (attrName.toLowerCase() === "class" && localStyleCollector) updateDirectiveExpressionWithLocalStyle(ms, prop, templateOffset, jsHandler, localStyleCollector, runtimeSet); else updateDirectiveExpression(ms, prop, templateOffset, jsHandler, runtimeSet); } }); } if (descriptor.script) { localStyleCollector?.collectRuntimeClasses(descriptor.script.content); const { code } = jsHandler(descriptor.script.content, runtimeSet ?? /* @__PURE__ */ new Set(), defaultCreateJsHandlerOptions); ms.update(descriptor.script.start, descriptor.script.end, localStyleCollector ? localStyleCollector.rewriteTransformedCode(code) : code); } if (descriptor.scriptSetup) { localStyleCollector?.collectRuntimeClasses(descriptor.scriptSetup.content); const { code } = jsHandler(descriptor.scriptSetup.content, runtimeSet ?? /* @__PURE__ */ new Set(), defaultCreateJsHandlerOptions); ms.update(descriptor.scriptSetup.start, descriptor.scriptSetup.end, localStyleCollector ? localStyleCollector.rewriteTransformedCode(code) : code); } if (localStyleCollector?.hasStyles()) ms.append(`\n${localStyleCollector.toStyleBlock()}`); } const result = { code: ms.toString(), map: null }; Object.defineProperty(result, "map", { configurable: true, enumerable: true, get() { return ms.generateMap(); } }); return result; } //#endregion export { transformUVue };