Files
Notes/scripts/check-syntax.js
2026-09-12 14:15:26 +08:00

87 lines
3.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// 语法体检:对 main / preload / scripts / src 全部 JS 文件跑 `node --check`。
// 比 ESLint 严格度低,但能捕获**最基础的**语法错误(漏括号、错引号等)
// 在文件压根还没被 linted 之前的快速反馈。
const { execFileSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const ROOT = path.resolve(__dirname, '..');
/** @type {string[]} */
const targets = [
'main.js',
'preload.js',
'scripts/launch.js',
// src/ 下用 ESM import/exportnode --check 会按 ESM 解析package.json 无 "type" 字段 → CJS
// 因此 src/**/*.js 必须以 .mjs 单独跑。
];
const esmTargets = [
'src',
];
function walk(dir, out) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
// audit fix (Q3.7):跳过 symlink 文件 + 目录。
// 之前只用 isFile()/isDirectory() 判定fs.readdirSync withFileTypes 不会跟随 symlink
// 但 isSymbolicLink() 会同步报告出来 —— 显式跳过避免后续 stat 跟着 symlink 跑到
// 项目外文件。实际风险低(项目里没有 symlink但保留防御。
if (entry.isSymbolicLink()) continue;
if (entry.isDirectory()) {
if (entry.name === 'node_modules' || entry.name === 'dist') continue;
walk(full, out);
} else if (entry.isFile() && entry.name.endsWith('.js')) {
out.push(full);
}
}
}
/** @type {string[]} */
const allCjs = targets.map((p) => path.join(ROOT, p)).filter(fs.existsSync);
/** @type {string[]} */
const allEsm = [];
for (const dir of esmTargets) {
const abs = path.join(ROOT, dir);
if (fs.existsSync(abs)) walk(abs, allEsm);
}
let failed = false;
for (const file of allCjs) {
try {
// audit fix (Q3.7):改用 execFileSync 不带 shell。
// 之前用 ``execSync(`node --check "${file}"`, { stdio: 'pipe' })`` 把路径拼进
// shell 字符串 —— 文件名里出现 `; rm -rf ~ #` 就会被 cmd.exe / sh 解释执行。
// 项目内的文件名目前可信,但这是审计工具自身表面的隐患,留着没意义。
execFileSync('node', ['--check', file], { stdio: 'pipe' });
} catch (err) {
failed = true;
console.error(`[check-syntax] CJS 失败: ${path.relative(ROOT, file)}`);
if (err.stderr) console.error(err.stderr.toString());
}
}
for (const file of allEsm) {
try {
// audit fix (Q3.7):同样改用 execFileSync。
// ESM 走 stdin 重定向:用 { input: fs.readFileSync(file) } 把文件内容喂给子进程
// stdin避开 shell 重定向 / 文件名展开。
const source = fs.readFileSync(file);
execFileSync('node', ['--check', '--input-type=module'], { stdio: ['pipe', 'pipe', 'pipe'], input: source });
} catch (err) {
failed = true;
console.error(`[check-syntax] ESM 失败: ${path.relative(ROOT, file)}`);
if (err.stderr) console.error(err.stderr.toString());
}
}
if (failed) {
console.error('[check-syntax] 失败');
process.exit(1);
}
console.log(`[check-syntax] 通过 (${allCjs.length} CJS + ${allEsm.length} ESM)`);