Files
mihomo-patchbay/scripts/roundtrip.ts
T
im0o f7db96fb0e 搭起项目骨架,验证配置文件往返
核心是确认「改配置而不弄乱文件」这件事成立:新增一路入站之后,其余 52 行逐字节不变。

搬进来的改写引擎用默认输出参数时会重排整个文件——行尾注释的对齐空格消失、流式数组被加上内边距——
连根本没改动的段落也一起波及。不修的话「改一处只动一处」这个承诺直接落空,所以关掉了它的自动排版,
并在写回之后补了一步逐行还原。
2026-08-10 02:43:04 +08:00

80 lines
3.0 KiB
TypeScript

/**
* 往返验证:读一份真实配置 → 改一处 → 写回,看未改动的部分保住了多少。
*
* 这是整个项目最先要回答的问题。如果写回之后文件面目全非,界面做得再好也没人敢用。
*
* 用法:
* 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<Record<string, unknown>>
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`)
}