feat: 新增 validateConfig 校验 depend 引用、自依赖和循环依赖

This commit is contained in:
2026-06-09 10:40:07 +08:00
parent 566a9d7255
commit 0d2b117680
2 changed files with 136 additions and 3 deletions

View File

@@ -3,6 +3,7 @@ import { readFile } from "node:fs/promises";
import { join, dirname } from "node:path";
import { parse as parseYaml } from "yaml";
import { defaultConfig } from "../defaults/config.ts";
import { ConfigError } from "../cli/errors.ts";
import type { RuneConfig } from "../types.ts";
import { RUNE_DIR, CONFIG_FILE, CHANGES_DIR, ARCHIVE_DIR } from "../types.ts";
@@ -22,12 +23,68 @@ export function findProjectRoot(
export async function loadConfig(projectRoot: string): Promise<RuneConfig> {
const configPath = join(projectRoot, RUNE_DIR, CONFIG_FILE);
let merged: RuneConfig;
try {
const content = await readFile(configPath, "utf-8");
const userConfig = parseYaml(content) as Partial<RuneConfig> | null;
return mergeConfig(userConfig ?? {});
merged = mergeConfig(userConfig ?? {});
} catch {
return mergeConfig({});
merged = mergeConfig({});
}
validateConfig(merged);
return merged;
}
export function validateConfig(config: RuneConfig): void {
const plan = config.stages.plan;
if (!plan) return;
const docNames = new Set(plan.documents.map((d) => d.name));
for (const doc of plan.documents) {
if (!doc.depend || doc.depend.length === 0) continue;
for (const dep of doc.depend) {
if (dep === doc.name) {
throw new ConfigError(`文档 "${doc.name}" 不能依赖自身`);
}
if (!docNames.has(dep)) {
throw new ConfigError(
`文档 "${doc.name}" 依赖 "${dep}" 不存在于 plan.documents 中`,
);
}
}
}
const visited = new Set<string>();
const path: string[] = [];
function hasCycle(name: string): boolean {
if (path.includes(name)) {
path.push(name);
return true;
}
if (visited.has(name)) return false;
visited.add(name);
path.push(name);
const doc = plan!.documents.find((d) => d.name === name);
if (doc?.depend) {
for (const dep of doc.depend) {
if (hasCycle(dep)) return true;
}
}
path.pop();
return false;
}
for (const doc of plan.documents) {
path.length = 0;
if (hasCycle(doc.name)) {
throw new ConfigError(
`文档间存在循环依赖:${path.join(" → ")}`,
);
}
}
}