/** * 关键工具类抽查:把产物里指定类的实际声明打出来,用于人工核对数值是否与设计稿一致。 * 用法:node scripts/audit-css.mjs [distDir] */ import fs from "node:fs"; import path from "node:path"; const ROOT = path.resolve(process.argv[2] || "dist/build/mp-weixin"); const css = fs.readFileSync(path.join(ROOT, "app.wxss"), "utf8"); /** 取出某个类选择器(含伪元素/伪类变体)的规则体 */ function rule(cls, extra = "") { const escaped = cls.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\/g, "\\\\"); const re = new RegExp(`\\.${escaped}${extra ? extra.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : ""}\\s*\\{([^}]*)\\}`, "g"); const out = []; let m; while ((m = re.exec(css))) out.push(m[1].replace(/\s+/g, " ").trim()); return out; } /** 找任意「含该子串的选择器」的规则(用于看被重写后的名字) */ function ruleByFragment(frag) { const out = []; const re = /([^{}@/]+)\{([^}]*)\}/g; let m; while ((m = re.exec(css))) { if (m[1].includes(frag)) out.push([m[1].trim().slice(0, 90), m[2].replace(/\s+/g, " ").trim().slice(0, 260)]); } return out; } const groups = [ ["① 全局变量与骨架", ["page", "button"]], ["② 刻度换算(验证 --spacing 与 rem2rpx)", []], ["③ 自定义 @utility:页面底 / 渐变 / 遮罩", []], ["④ 伪元素装饰", []], ["⑤ 设计 Token 生成的原子", ["bg-brand", "text-ink2", "text-ink3", "rounded-card", "rounded-hero", "rounded-panel", "rounded-dialog", "shadow-elev2", "shadow-ccard", "shadow-wcard"]], ["⑥ 字号刻度", ["text-hint", "text-cap", "text-body", "text-title", "text-h1", "text-display"]], ["⑦ 组合类(自定义 radius token)", []], ["⑧ 滤镜 / 背景模糊 / 文字阴影", []], ]; for (const [title] of groups) console.log(`\n===== ${title} =====`); console.log("--- page ---"); rule("page").forEach((r) => console.log(" " + r.slice(0, 400))); console.log("--- button::after ---"); rule("button", ":after").forEach((r) => console.log(" " + r)); console.log("\n--- --spacing 定义 ---"); ruleByFragment("--spacing:").slice(0, 3).forEach(([s, b]) => console.log(" " + s + " { " + b.slice(0, 120) + " }")); console.log("--- 数值刻度样例 gap-2 / px-4 / h-14 / w-11 / py-3 ---"); for (const c of ["gap-2", "px-4", "h-14", "w-11", "py-3", "h-7", "gap-3", "mt-auto"]) { console.log(" ." + c + " -> " + (rule(c)[0] || "【缺】")); } console.log("\n--- 自定义工具类 ---"); for (const c of ["skin-home", "skin-detail", "grad-brand", "grad-cardside", "gradtext-accent", "gradtext-brand", "scrim-tile", "scrim-rec", "deco-notch", "deco-titledot", "deco-underline", "deco-hairline", "deco-handle", "deco-mark", "btn-reset", "grad-divider", "grad-call"]) { const r = rule(c); console.log(" ." + c + " -> " + (r.length ? r.join(" | ").slice(0, 220) : "【缺】")); } console.log(" 伪元素变体:"); for (const frag of ["deco-notch:before", "deco-underline:after", "btn-reset:after"]) { ruleByFragment(frag).forEach(([s, b]) => console.log(" " + s + " { " + b.slice(0, 150) + " }")); } console.log("\n--- 组合 radius token(rounded-l-card / rounded-r-card / rounded-t-panel)---"); for (const c of ["rounded-l-card", "rounded-r-card", "rounded-t-panel", "rounded-t-dialog"]) { console.log(" ." + c + " -> " + (rule(c)[0] || "【缺】")); } console.log("\n--- 滤镜 / 模糊 / 文字阴影 ---"); ruleByFragment("grayscale").slice(0, 4).forEach(([s, b]) => console.log(" " + s + " { " + b.slice(0, 200) + " }")); ruleByFragment("brightness").slice(0, 3).forEach(([s, b]) => console.log(" " + s + " { " + b.slice(0, 200) + " }")); ruleByFragment("backdrop-blur").slice(0, 3).forEach(([s, b]) => console.log(" " + s + " { " + b.slice(0, 260) + " }")); ruleByFragment("text-shadow").slice(0, 3).forEach(([s, b]) => console.log(" " + s + " { " + b.slice(0, 200) + " }")); ruleByFragment("rotate").slice(0, 3).forEach(([s, b]) => console.log(" " + s + " { " + b.slice(0, 160) + " }")); console.log("\n--- --tw-* 变量兜底初始化(weapp-tailwindcss 注入)---"); const idx = css.indexOf("--tw-border-style"); console.log(" " + css.slice(css.lastIndexOf("}", idx) + 1, idx + 700).replace(/\s+/g, " ").slice(0, 700));