63 lines
2.6 KiB
JavaScript
63 lines
2.6 KiB
JavaScript
// 静态检查:语法 + 类型 + 构建 + 关键标记 tripwire(CI 与 pre-push 共用)
|
||
import { spawnSync } from 'node:child_process';
|
||
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
import url from 'node:url';
|
||
|
||
const root = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '..');
|
||
let failures = 0;
|
||
const step = (label, fn) => {
|
||
process.stdout.write('==> ' + label + ' ... ');
|
||
try { fn(); console.log('OK'); }
|
||
catch (e) { console.log('FAIL\n' + String(e.message || e).slice(-2000)); failures++; }
|
||
};
|
||
|
||
// 1) 所有服务端 .mjs 语法检查(新增文件自动纳入)
|
||
const mjsDirs = ['sync-server/src', 'app/server', 'scripts'];
|
||
const mjsFiles = mjsDirs.flatMap((d) =>
|
||
fs.readdirSync(path.join(root, d)).filter((f) => f.endsWith('.mjs')).map((f) => path.join(d, f)));
|
||
step('node --check × ' + mjsFiles.length + ' 个文件', () => {
|
||
for (const f of mjsFiles) {
|
||
const r = spawnSync(process.execPath, ['--check', path.join(root, f)], { encoding: 'utf8' });
|
||
if (r.status !== 0) throw new Error(f + '\n' + r.stderr);
|
||
}
|
||
});
|
||
|
||
// 2) TypeScript 类型检查
|
||
const tsc = path.join(root, 'app/node_modules/typescript/bin/tsc');
|
||
if (fs.existsSync(tsc)) {
|
||
step('tsc --noEmit(前端类型)', () => {
|
||
const r = spawnSync(process.execPath, [tsc, '--noEmit'], { cwd: path.join(root, 'app'), encoding: 'utf8' });
|
||
if (r.status !== 0) throw new Error(r.stdout || r.stderr);
|
||
});
|
||
} else console.log('==> tsc 跳过(app/node_modules 不存在,先安装依赖)');
|
||
|
||
// 3) 前端构建(确保源码可构建;产物不提交)
|
||
const vite = path.join(root, 'app/node_modules/vite/bin/vite.js');
|
||
if (fs.existsSync(vite)) {
|
||
step('vite build', () => {
|
||
const r = spawnSync(process.execPath, [vite, 'build'], { cwd: path.join(root, 'app'), encoding: 'utf8' });
|
||
if (r.status !== 0) throw new Error(r.stdout || r.stderr);
|
||
});
|
||
} else console.log('==> vite 跳过(app/node_modules 不存在)');
|
||
|
||
// 4) 关键文件 tripwire:防止「改了没提交」类事故再次发生
|
||
step('关键标记 tripwire', () => {
|
||
const musts = [
|
||
['app/server/db.mjs', "case 'demo_seed'"],
|
||
['app/server/db.mjs', 'DEMO_ROWS'],
|
||
['app/public/sw.js', 'fp-static-v3'],
|
||
['sync-server/src/db.mjs', "name='remark'"],
|
||
['sync-server/src/index.mjs', 'demo_seed'],
|
||
];
|
||
for (const [f, needle] of musts) {
|
||
const p = path.join(root, f);
|
||
if (!fs.existsSync(p) || !fs.readFileSync(p, 'utf8').includes(needle)) {
|
||
throw new Error(f + ' 缺少关键标记: ' + needle);
|
||
}
|
||
}
|
||
});
|
||
|
||
if (failures) { console.error('\n✗ check 失败:' + failures + ' 项'); process.exit(1); }
|
||
console.log('\n✓ check 全部通过');
|