weapp-tailwindcss
Version:
把 tailwindcss 原子化样式思想,带给小程序开发者们! bring tailwindcss to miniprogram developers!
343 lines (342 loc) • 13.1 kB
JavaScript
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(` ${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 };