返回 DeepSeek-Reasonix
check-css-syntax.mjs
根目录 / desktop / frontend / scripts / check-css-syntax.mjs
1 import fs from "node:fs";
2 import path from "node:path";
3 import { fileURLToPath } from "node:url";
4
5 const scriptDir = path.dirname(fileURLToPath(import.meta.url));
6 const frontendRoot = path.resolve(scriptDir, "..");
7 const targets = process.argv.slice(2);
8 const files = targets.length > 0 ? targets : ["src/styles.css"];
9
10 let failed = false;
11
12 for (const file of files) {
13 const fullPath = path.resolve(frontendRoot, file);
14 const source = fs.readFileSync(fullPath, "utf8");
15 const result = checkCssDelimiters(source);
16 if (result.ok) {
17 console.log(`CSS syntax check passed: ${file}`);
18 continue;
19 }
20
21 failed = true;
22 console.error(`CSS syntax check failed: ${file}:${result.line}:${result.column}`);
23 console.error(result.message);
24 }
25
26 if (failed) {
27 process.exit(1);
28 }
29
30 function checkCssDelimiters(source) {
31 const stack = [];
32 let state = "normal";
33 let line = 1;
34 let column = 0;
35 let tokenLine = 1;
36 let tokenColumn = 1;
37
38 for (let i = 0; i < source.length; i += 1) {
39 const char = source[i];
40 const next = source[i + 1];
41
42 if (char === "\n") {
43 line += 1;
44 column = 0;
45 } else {
46 column += 1;
47 }
48
49 if (state === "comment") {
50 if (char === "*" && next === "/") {
51 i += 1;
52 column += 1;
53 state = "normal";
54 }
55 continue;
56 }
57
58 if (state === "single" || state === "double") {
59 if (char === "\\") {
60 i += 1;
61 column += 1;
62 continue;
63 }
64 if ((state === "single" && char === "'") || (state === "double" && char === '"')) {
65 state = "normal";
66 }
67 continue;
68 }
69
70 if (char === "/" && next === "*") {
71 tokenLine = line;
72 tokenColumn = column;
73 i += 1;
74 column += 1;
75 state = "comment";
76 continue;
77 }
78
79 if (char === "'") {
80 tokenLine = line;
81 tokenColumn = column;
82 state = "single";
83 continue;
84 }
85
86 if (char === '"') {
87 tokenLine = line;
88 tokenColumn = column;
89 state = "double";
90 continue;
91 }
92
93 if (char === "{") {
94 stack.push({ line, column });
95 continue;
96 }
97
98 if (char === "}") {
99 if (stack.length === 0) {
100 return {
101 ok: false,
102 line,
103 column,
104 message: "Found a closing brace without a matching opening brace.",
105 };
106 }
107 stack.pop();
108 }
109 }
110
111 if (state === "comment") {
112 return {
113 ok: false,
114 line: tokenLine,
115 column: tokenColumn,
116 message: "Found an unterminated CSS comment.",
117 };
118 }
119
120 if (state === "single" || state === "double") {
121 return {
122 ok: false,
123 line: tokenLine,
124 column: tokenColumn,
125 message: "Found an unterminated CSS string.",
126 };
127 }
128
129 if (stack.length > 0) {
130 const opener = stack[stack.length - 1];
131 return {
132 ok: false,
133 line: opener.line,
134 column: opener.column,
135 message: "Found an opening brace without a matching closing brace.",
136 };
137 }
138
139 return { ok: true };
140 }
141
141 lines Plain Text