/** * 后端接口预留审计:找出「函数体里没有留下任何后端调用痕迹」的接口函数。 * * 背景:本项目当前是 mock 阶段,数据要能展示,但每个函数都要把真实请求写好、注释起来, * 后端就绪时**放开注释**即可接入(见 `src/api/api.ts` 顶部的「后端接口预留」约定)。 * 判断标准是「函数体里出现过 fetch(」—— 注释着的也算,因此不依赖注释的写法。 * * 豁免:确实没有后端接口、本就以本地存储为唯一真相源的函数,在函数体内任意位置写 * `backend-stub-ok` 注释即可跳过(约定见 api.ts 顶部)。 * * 用法:node scripts/check-backend-stubs.mjs # 列出全部 * node scripts/check-backend-stubs.mjs --error # 有命中则 exit 1(可用于 CI) */ import { readFileSync } from 'node:fs'; const FILE = 'src/api/api.ts'; /** 有意不接后端的函数在函数体内标这个,避免与「忘了留接口」混在一起 */ const EXEMPT_MARKER = 'backend-stub-ok'; const lines = readFileSync(FILE, 'utf8').split('\n'); /** 顶层 `export async function` 的起始行;同步函数不涉及请求,不参与审计 */ const starts = []; lines.forEach((line, index) => { const match = line.match(/^export async function (\w+)/); if (match) starts.push({ name: match[1], index }); }); starts.push({ name: null, index: lines.length }); const findings = []; for (let i = 0; i < starts.length - 1; i += 1) { const { name, index } = starts[i]; const body = lines.slice(index, starts[i + 1].index).join('\n'); if (body.includes('fetch(') || body.includes(EXEMPT_MARKER)) continue; findings.push({ name, line: index + 1 }); } if (findings.length === 0) { console.log(`✅ ${starts.length - 1} 个接口函数均已预留后端调用(注释版)`); } else { console.log(`=== 未预留后端调用的接口函数 (${findings.length}) ===`); for (const f of findings) { console.log(`${FILE}:${f.line} ${f.name}`); } console.log('请补上注释版真实请求,或在不接后端的函数里标注 backend-stub-ok'); if (process.argv.includes('--error')) process.exitCode = 1; }