diff --git a/docs/spikes/extract-frontend-calls.ts b/docs/spikes/extract-frontend-calls.ts new file mode 100644 index 0000000..215b008 --- /dev/null +++ b/docs/spikes/extract-frontend-calls.ts @@ -0,0 +1,58 @@ +#!/usr/bin/env bun +// 从 ojnext 提取前端实际发起的 API 调用 +import { readdirSync, readFileSync, statSync } from "node:fs" +import { join, relative } from "node:path" + +const ROOT = process.argv[2] ?? "/home/xuyue/Projects/OJ/ojnext" +const SRC = join(ROOT, "src") + +type Call = { file: string; fn: string; method: string; path: string } + +function walkFiles(dir: string, out: string[] = []): string[] { + for (const e of readdirSync(dir)) { + const p = join(dir, e) + if (statSync(p).isDirectory()) walkFiles(p, out) + else if (/\.(ts|vue)$/.test(p)) out.push(p) + } + return out +} + +// 同时吃 get("x")、get(`x/${id}`),以及带泛型的 get("x") +// (http.get() 在 api.ts 里大量使用,泛型不吃会漏掉三分之一的调用点) +const CALL_RE = /\b(get|post|put|delete)(?:<[^>]*>)?\(\s*(["'`])([^"'`]*)\2/g +// 就近向上找所属的导出函数名 +const FN_RE = /export\s+(?:async\s+)?function\s+(\w+)|export\s+const\s+(\w+)\s*=/g + +function normalize(p: string): string { + return p.replace(/\$\{[^}]*\}/g, ":param").replace(/^\/+/, "") +} + +const calls: Call[] = [] +for (const file of walkFiles(SRC)) { + const src = readFileSync(file, "utf8") + + // 先建立 "偏移量 -> 函数名" 的索引 + const fns: { at: number; name: string }[] = [] + FN_RE.lastIndex = 0 + let f: RegExpExecArray | null + while ((f = FN_RE.exec(src)) !== null) fns.push({ at: f.index, name: f[1] ?? f[2] }) + + CALL_RE.lastIndex = 0 + let m: RegExpExecArray | null + while ((m = CALL_RE.exec(src)) !== null) { + const path = normalize(m[3]) + if (!path || path.startsWith("http")) continue // 跳过外部 URL 和空串 + const owner = fns.filter((x) => x.at < m!.index).pop() + calls.push({ + file: relative(ROOT, file), + fn: owner?.name ?? "", + method: m[1], + path, + }) + } +} + +const uniq = new Set(calls.map((c) => `${c.method} ${c.path}`)) +console.log(`前端调用点 ${calls.length} 处,去重后 ${uniq.size} 条`) +await Bun.write("endpoints-frontend.json", JSON.stringify(calls, null, 2)) +console.log("→ endpoints-frontend.json")