/** * 往返验证:读一份真实配置 → 改一处 → 写回,看未改动的部分保住了多少。 * * 这是整个项目最先要回答的问题。如果写回之后文件面目全非,界面做得再好也没人敢用。 * * 用法: * bun run scripts/roundtrip.ts <源配置> <输出目录> * * 产出三个文件,各对应一种改动,再用 git diff --no-index 跟源文件比对。 */ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { join } from 'node:path' import { replaceDocumentData, openDocument } from '../vendor/config-editor/index.ts' import type { ConfigObject } from '../vendor/config-editor/index.ts' import { restoreInlineCommentAlignment } from '../shared/yaml-cosmetics.ts' const [sourcePath, outDir] = process.argv.slice(2) if (!sourcePath || !outDir) { console.error('用法:bun run scripts/roundtrip.ts <源配置> <输出目录>') process.exit(1) } const source = readFileSync(sourcePath, 'utf8') const doc = openDocument(source) mkdirSync(outDir, { recursive: true }) const clone = (v: unknown) => JSON.parse(JSON.stringify(v)) as ConfigObject /** 每个场景改一处,模拟界面上一次真实操作。 */ const scenarios: Array<{ file: string; what: string; mutate: (d: ConfigObject) => void }> = [ { file: 'A-加一路入站.yaml', what: '新增命名入站 7778,出站固定走 AUTO(本项目的核心操作)', mutate: (d) => { d.listeners = [ { name: '容器A', type: 'mixed', port: 7778, listen: '0.0.0.0', proxy: 'AUTO' }, ] }, }, { file: 'B-改一条规则.yaml', what: '把最后一条兜底规则的出站从 PROXY 换成 AUTO', mutate: (d) => { const rules = d.rules as string[] rules[rules.length - 1] = 'MATCH,AUTO' }, }, { file: 'C-改一个策略组.yaml', what: '给 PROXY 组的成员列表加一个 REJECT', mutate: (d) => { const groups = d['proxy-groups'] as Array> const proxy = groups.find((g) => g.name === 'PROXY')! proxy.proxies = [...(proxy.proxies as string[]), 'REJECT'] }, }, ] const countComments = (yaml: string) => yaml.split('\n').filter((line) => line.trim().startsWith('#') || / #\s/.test(line)).length console.log(`源文件 ${sourcePath}`) console.log(` ${source.split('\n').length} 行,其中带注释 ${countComments(source)} 行\n`) for (const scenario of scenarios) { const draft = clone(doc.data) scenario.mutate(draft) const result = replaceDocumentData(doc, draft) const written = restoreInlineCommentAlignment(source, result.yaml) const outPath = join(outDir, scenario.file) writeFileSync(outPath, written, 'utf8') const before = countComments(source) const after = countComments(written) console.log(`${scenario.file}`) console.log(` 改动:${scenario.what}`) console.log(` 注释行 ${before} → ${after}${after < before ? `(丢了 ${before - after} 行)` : '(没丢)'}`) console.log(` 写到 ${outPath}\n`) }