73 lines
2.8 KiB
JavaScript
73 lines
2.8 KiB
JavaScript
import fs from "node:fs";
|
||
import path from "node:path";
|
||
import postcss from "postcss";
|
||
|
||
/**
|
||
* 旧浏览器(麒麟 Chromium ~80)CSS 产物守卫:构建后运行,防止回归。
|
||
* 迁移后的结构性保证(见 tailwind.config.ts / postcss.config.js):
|
||
* - Tailwind v3 输出无 @layer(Chrome 99+)与 @property(Chrome 85+);
|
||
* - dark: 变体经 postcss-dark-descendant 改写为 .dark 后代形式,全库 :is( 为 0;
|
||
* - 媒体查询 range 语法(Chrome 104+)由 lightningcss 按 target 转译;
|
||
* - 手写 CSS 不使用 :is()(复杂参数无法安全降级,源头展开为逗号选择器);
|
||
* - color-mix()/oklch() 仅允许出现在 @supports 守护内或带前置回退声明。
|
||
* :has()(Chrome 105+)与 :where()(Chrome 88+)按渐进增强放行,不在检查范围。
|
||
*/
|
||
const unsupportedColor = /(?:color-mix|oklch|oklab|lab|lch)\(/i;
|
||
const assetsDir = path.resolve("dist/assets");
|
||
|
||
if (!fs.existsSync(assetsDir)) {
|
||
throw new Error("dist/assets 不存在,请先运行 pnpm build");
|
||
}
|
||
|
||
const cssFiles = fs.readdirSync(assetsDir).filter((f) => f.endsWith(".css"));
|
||
if (cssFiles.length === 0) throw new Error("dist/assets 中没有 CSS 构建产物");
|
||
|
||
const failures = [];
|
||
const hardTokens = [
|
||
[/@layer\b/, "@layer(Chrome 99+)"],
|
||
[/@property\b/, "@property(Chrome 85+)"],
|
||
[/:is\(/, ":is()(Chrome 88+)"],
|
||
[/\(width\s*>=|\(width\s*<=|\(height\s*>=/, "媒体查询 range 语法(Chrome 104+)"],
|
||
];
|
||
let guardedColors = 0;
|
||
|
||
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}`);
|
||
}
|
||
|
||
const root = postcss.parse(raw, { from: file });
|
||
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)}`);
|
||
}
|
||
});
|
||
}
|
||
|
||
if (failures.length > 0) {
|
||
console.error("旧浏览器 CSS 兼容检查失败:\n");
|
||
console.error(failures.slice(0, 50).join("\n"));
|
||
if (failures.length > 50) console.error(`... 及另外 ${failures.length - 50} 处`);
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(
|
||
`旧浏览器(麒麟 Chromium ~80)CSS 兼容检查通过(${guardedColors} 处守护的现代颜色声明)。`,
|
||
);
|