2026-08-04 23:28:10 +08:00
|
|
|
import path from "node:path";
|
|
|
|
|
import {
|
|
|
|
|
readTextFileIfSmall,
|
|
|
|
|
relative,
|
|
|
|
|
walkFiles,
|
|
|
|
|
} from "./repository-files.mjs";
|
|
|
|
|
|
|
|
|
|
const checkedExtensions = new Set([
|
|
|
|
|
".css",
|
|
|
|
|
".html",
|
|
|
|
|
".js",
|
|
|
|
|
".json",
|
|
|
|
|
".kt",
|
|
|
|
|
".kts",
|
|
|
|
|
".md",
|
|
|
|
|
".mjs",
|
|
|
|
|
".properties",
|
2026-08-08 09:19:40 +08:00
|
|
|
".ps1",
|
2026-08-04 23:28:10 +08:00
|
|
|
".toml",
|
|
|
|
|
".xml",
|
|
|
|
|
".yaml",
|
|
|
|
|
".yml",
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const failures = [];
|
|
|
|
|
let checked = 0;
|
|
|
|
|
for (const file of await walkFiles()) {
|
|
|
|
|
if (!checkedExtensions.has(path.extname(file).toLowerCase())) continue;
|
|
|
|
|
const content = await readTextFileIfSmall(file);
|
|
|
|
|
if (content === null) continue;
|
|
|
|
|
checked += 1;
|
|
|
|
|
|
|
|
|
|
if (content.length > 0 && !content.endsWith("\n")) {
|
|
|
|
|
failures.push(`${relative(file)} must end with a newline`);
|
|
|
|
|
}
|
|
|
|
|
content.split(/\r?\n/u).forEach((line, index) => {
|
|
|
|
|
if (/[ \t]+$/u.test(line)) failures.push(`${relative(file)}:${index + 1} has trailing whitespace`);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (file.endsWith(".json")) {
|
|
|
|
|
try {
|
|
|
|
|
JSON.parse(content);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
failures.push(`${relative(file)} is invalid JSON: ${error.message}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (failures.length > 0) {
|
|
|
|
|
console.error("Formatting verification failed:");
|
|
|
|
|
for (const failure of failures) console.error(`- ${failure}`);
|
|
|
|
|
process.exitCode = 1;
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`Formatting verification passed (${checked} text files).`);
|
|
|
|
|
}
|