Files
deerflow-code/frontend-web/scripts/check-firefox52-css.mjs
T

99 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import fs from "node:fs";
import path from "node:path";
import postcss from "postcss";
/**
* Firefox 52 CSS 产物守卫:构建后运行,防止回归。
* 迁移后的结构性保证(见 tailwind.config.ts / postcss.config.js):
* - Tailwind v3 输出无 @layer(Chrome 99+)与 @property(Chrome 85+);
* - dark: 变体经 postcss-dark-descendant 改写为 .dark 后代形式;
* - :where() 由 Firefox 专用 PostCSS 插件展开;
* - 媒体查询 range 语法(Chrome 104+)由 lightningcss 按 target 转译;
* - 手写 CSS 不使用 :is()(复杂参数无法安全降级,源头展开为逗号选择器);
* - color-mix()/oklch() 仅允许出现在 @supports 守护内或带前置回退声明。
* :has() 按渐进增强放行,关键输入框布局由显式 class 和 :focus-within 兜底。
*/
const unsupportedColor = /(?:color-mix|oklch|oklab|lab|lch)\(/i;
const assetsDir = path.resolve("dist-firefox52/assets");
if (!fs.existsSync(assetsDir)) {
throw new Error("dist-firefox52/assets 不存在,请先运行 pnpm build:firefox52:fast");
}
const cssFiles = fs.readdirSync(assetsDir).filter((f) => f.endsWith(".css"));
if (cssFiles.length === 0) throw new Error("dist-firefox52/assets 中没有 CSS 构建产物");
const failures = [];
const hardTokens = [
[/@layer\b/, "@layer(Chrome 99+)"],
[/@property\b/, "@property(Chrome 85+)"],
[/:is\(/, ":is()(Chrome 88+)"],
[/:where\(/, ":where()(Firefox 78+)"],
[/\(width\s*>=|\(width\s*<=|\(height\s*>=/, "媒体查询 range 语法(Chrome 104+)"],
[/rgb\(\s*var\(/i, "rgb(var(...)) 现代通道语法"],
[/rgba\([^;{}]*,\s*calc\(/i, "rgba() 的 calc alpha(Firefox 52 不解析)"],
[/rgba\([^;{}]*,\s*[\d.]+%\s*\)/i, "rgba() 百分比 alpha(Firefox 52 不解析)"],
];
let guardedColors = 0;
let progressiveHas = 0;
const requiredComposerWidths = new Map([
[".max-w-\\[var\\(--container-width-md\\)\\]", "816px"],
[".max-w-\\[calc\\(var\\(--container-width-md\\)\\+150px\\)\\]", "966px"],
[".max-w-\\[var\\(--container-width-lg\\)\\]", "1024px"],
]);
const foundComposerWidths = new Map();
for (const file of cssFiles) {
const raw = fs.readFileSync(path.join(assetsDir, file), "utf8");
for (const [re, label] of hardTokens) {
const hits = raw.match(new RegExp(re.source, "g")) || [];
if (hits.length > 0) failures.push(`${file}: ${hits.length} 处 ${label}`);
}
progressiveHas += (raw.match(/:has\(/g) || []).length;
const root = postcss.parse(raw, { from: file });
root.walkRules((rule) => {
for (const [selector, expectedWidth] of requiredComposerWidths) {
if (!rule.selector?.includes(selector)) continue;
const width = rule.nodes?.find((node) => node.type === "decl" && node.prop === "max-width");
if (width?.value === expectedWidth) foundComposerWidths.set(selector, true);
}
});
root.walkDecls((decl) => {
if (!unsupportedColor.test(decl.value)) return;
guardedColors += 1;
let anc = decl.parent;
while (anc) {
if (anc.type === "atrule" && anc.name === "supports" && unsupportedColor.test(anc.params)) return;
anc = anc.parent;
}
let prev = decl.prev();
while (prev?.type === "comment") prev = prev.prev();
const hasFallback =
!decl.prop.startsWith("--") &&
prev?.type === "decl" &&
prev.prop === decl.prop &&
!unsupportedColor.test(prev.value);
if (!hasFallback) {
failures.push(`${file}: 未守护的现代颜色 ${decl.prop}: ${decl.value.slice(0, 60)}`);
}
});
}
for (const selector of requiredComposerWidths.keys()) {
if (!foundComposerWidths.has(selector)) {
failures.push(`缺少 Firefox 52 输入区宽度回退:${selector}`);
}
}
if (failures.length > 0) {
console.error("Firefox 52 CSS 兼容检查失败:\n");
console.error(failures.slice(0, 50).join("\n"));
if (failures.length > 50) console.error(`... 及另外 ${failures.length - 50} 处`);
process.exit(1);
}
console.log(
`Firefox 52 CSS 兼容检查通过(${guardedColors} 处守护的现代颜色声明,${progressiveHas} 处渐进式 :has())。`,
);