Files
2026-07-13 13:00:08 +08:00

141 lines
2.9 KiB
JavaScript

import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const frontendRoot = path.resolve(scriptDir, "..");
const targets = process.argv.slice(2);
const files = targets.length > 0 ? targets : ["src/styles.css"];
let failed = false;
for (const file of files) {
const fullPath = path.resolve(frontendRoot, file);
const source = fs.readFileSync(fullPath, "utf8");
const result = checkCssDelimiters(source);
if (result.ok) {
console.log(`CSS syntax check passed: ${file}`);
continue;
}
failed = true;
console.error(`CSS syntax check failed: ${file}:${result.line}:${result.column}`);
console.error(result.message);
}
if (failed) {
process.exit(1);
}
function checkCssDelimiters(source) {
const stack = [];
let state = "normal";
let line = 1;
let column = 0;
let tokenLine = 1;
let tokenColumn = 1;
for (let i = 0; i < source.length; i += 1) {
const char = source[i];
const next = source[i + 1];
if (char === "\n") {
line += 1;
column = 0;
} else {
column += 1;
}
if (state === "comment") {
if (char === "*" && next === "/") {
i += 1;
column += 1;
state = "normal";
}
continue;
}
if (state === "single" || state === "double") {
if (char === "\\") {
i += 1;
column += 1;
continue;
}
if ((state === "single" && char === "'") || (state === "double" && char === '"')) {
state = "normal";
}
continue;
}
if (char === "/" && next === "*") {
tokenLine = line;
tokenColumn = column;
i += 1;
column += 1;
state = "comment";
continue;
}
if (char === "'") {
tokenLine = line;
tokenColumn = column;
state = "single";
continue;
}
if (char === '"') {
tokenLine = line;
tokenColumn = column;
state = "double";
continue;
}
if (char === "{") {
stack.push({ line, column });
continue;
}
if (char === "}") {
if (stack.length === 0) {
return {
ok: false,
line,
column,
message: "Found a closing brace without a matching opening brace.",
};
}
stack.pop();
}
}
if (state === "comment") {
return {
ok: false,
line: tokenLine,
column: tokenColumn,
message: "Found an unterminated CSS comment.",
};
}
if (state === "single" || state === "double") {
return {
ok: false,
line: tokenLine,
column: tokenColumn,
message: "Found an unterminated CSS string.",
};
}
if (stack.length > 0) {
const opener = stack[stack.length - 1];
return {
ok: false,
line: opener.line,
column: opener.column,
message: "Found an opening brace without a matching closing brace.",
};
}
return { ok: true };
}