fix: 收紧CSS所有权与历史兼容门禁
This commit is contained in:
@@ -5,6 +5,8 @@ import process from 'node:process';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import postcss from 'postcss';
|
||||
import { broadBusinessSelectors, verifyOwnership } from './css-policy.mjs';
|
||||
export { broadBusinessSelectors } from './css-policy.mjs';
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
||||
|
||||
@@ -24,18 +26,6 @@ export function analyzeCss(css) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function broadBusinessSelectors(css) {
|
||||
const findings = [];
|
||||
postcss.parse(css).walkRules((rule) => {
|
||||
for (const selector of rule.selectors ?? [rule.selector]) {
|
||||
if (/^(?:div|section|article|button|table|input|select|textarea)(?:\b|[ >+~:[.#])/.test(selector.trim())) {
|
||||
findings.push(selector);
|
||||
}
|
||||
}
|
||||
});
|
||||
return findings;
|
||||
}
|
||||
|
||||
export function verifyGlobalCssAbsent(repoRoot) {
|
||||
const legacy = path.join(repoRoot, 'src/styles/global.css');
|
||||
if (fs.existsSync(legacy)) throw new Error('src/styles/global.css 已完成迁移,禁止重新创建');
|
||||
@@ -45,13 +35,6 @@ function read(relativePath) {
|
||||
return fs.readFileSync(path.join(root, relativePath), 'utf8');
|
||||
}
|
||||
|
||||
function currentCssFiles(directory) {
|
||||
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
||||
const target = path.join(directory, entry.name);
|
||||
return entry.isDirectory() ? currentCssFiles(target) : entry.name.endsWith('.css') ? [target] : [];
|
||||
});
|
||||
}
|
||||
|
||||
function changedCssFiles() {
|
||||
if (!fs.existsSync(path.join(root, '.git'))) return { base: 'archive', files: [] };
|
||||
const configuredBase = process.env.QUALITY_BASE_REF;
|
||||
@@ -89,6 +72,12 @@ function baseCss(base, file) {
|
||||
|
||||
function verify() {
|
||||
verifyGlobalCssAbsent(root);
|
||||
const policy = JSON.parse(read('tools/quality/css-ownership.json'));
|
||||
const { graph } = verifyOwnership(root, policy);
|
||||
const mainStyles = graph.get('src/main.tsx').filter((file) => file.endsWith('.css'));
|
||||
if (JSON.stringify(mainStyles) !== JSON.stringify(policy.mainImportOrder)) {
|
||||
throw new Error('应用入口 CSS 层级与已评审基线不一致');
|
||||
}
|
||||
const baseline = JSON.parse(read('tools/quality/css-governance-baseline.json'));
|
||||
const entry = read(baseline.entry);
|
||||
const imports = [...entry.matchAll(/@import\s+['"](.+?)['"]/g)].map((match) =>
|
||||
@@ -132,22 +121,6 @@ function verify() {
|
||||
}
|
||||
}
|
||||
|
||||
const sourceText = currentCssFiles(path.join(root, 'src'))
|
||||
.map((file) => fs.readFileSync(file, 'utf8'))
|
||||
.join('\n');
|
||||
const codeText = fs
|
||||
.readdirSync(path.join(root, 'src'), { recursive: true })
|
||||
.filter((name) => /\.(?:ts|tsx)$/.test(String(name)))
|
||||
.map((name) => fs.readFileSync(path.join(root, 'src', String(name)), 'utf8'))
|
||||
.join('\n');
|
||||
for (const file of currentCssFiles(path.join(root, 'src'))) {
|
||||
const relative = path.relative(path.join(root, 'src'), file).replace(/\\/g, '/');
|
||||
if (relative === 'styles/tokens.css' || relative === 'styles/reset.css') continue;
|
||||
if (!sourceText.includes(path.basename(file)) && !codeText.includes(path.basename(file))) {
|
||||
throw new Error(`${relative} 没有明确 import 所有者`);
|
||||
}
|
||||
}
|
||||
|
||||
const duplicateCount = selectorList.length - new Set(selectorList).size;
|
||||
const digest = crypto.createHash('sha256').update(selectorList.join('\n')).digest('hex').slice(0, 12);
|
||||
console.log(
|
||||
|
||||
Reference in New Issue
Block a user