refactor(config): simplify includes with class-based processor

- Replace free functions with IncludeProcessor class
- Simplify IncludeResolver interface: { readFile, parseJson }
- Break down loadFile into focused private methods
- Use reduce() for array include merging
- Cleaner separation of concerns
This commit is contained in:
sheeek
2026-01-11 15:48:40 +01:00
committed by Peter Steinberger
parent e6400b0b0f
commit 53d3134fe8
3 changed files with 174 additions and 180 deletions

View File

@@ -3,26 +3,20 @@ import { describe, expect, it } from "vitest";
import {
CircularIncludeError,
ConfigIncludeError,
type IncludeResolver,
resolveConfigIncludes,
} from "./includes.js";
function createMockResolver(files: Record<string, unknown>) {
const fsModule = {
readFileSync: (filePath: string) => {
function createMockResolver(files: Record<string, unknown>): IncludeResolver {
return {
readFile: (filePath: string) => {
if (filePath in files) {
return JSON.stringify(files[filePath]);
}
const err = new Error(`ENOENT: no such file: ${filePath}`);
(err as NodeJS.ErrnoException).code = "ENOENT";
throw err;
throw new Error(`ENOENT: no such file: ${filePath}`);
},
} as typeof import("node:fs");
const json5Module = {
parse: JSON.parse,
} as typeof import("json5");
return { fsModule, json5Module };
parseJson: JSON.parse,
};
}
function resolve(
@@ -124,9 +118,9 @@ describe("resolveConfigIncludes", () => {
});
it("throws ConfigIncludeError for invalid JSON", () => {
const resolver = {
fsModule: { readFileSync: () => "{ invalid json }" } as typeof import("node:fs"),
json5Module: { parse: JSON.parse } as typeof import("json5"),
const resolver: IncludeResolver = {
readFile: () => "{ invalid json }",
parseJson: JSON.parse,
};
const obj = { $include: "./bad.json" };
expect(() => resolveConfigIncludes(obj, "/config/clawdbot.json", resolver))
@@ -136,19 +130,17 @@ describe("resolveConfigIncludes", () => {
});
it("throws CircularIncludeError for circular includes", () => {
const resolver = {
fsModule: {
readFileSync: (filePath: string) => {
if (filePath === "/config/a.json") {
return JSON.stringify({ $include: "./b.json" });
}
if (filePath === "/config/b.json") {
return JSON.stringify({ $include: "./a.json" });
}
throw new Error(`Unknown file: ${filePath}`);
},
} as typeof import("node:fs"),
json5Module: { parse: JSON.parse } as typeof import("json5"),
const resolver: IncludeResolver = {
readFile: (filePath: string) => {
if (filePath === "/config/a.json") {
return JSON.stringify({ $include: "./b.json" });
}
if (filePath === "/config/b.json") {
return JSON.stringify({ $include: "./a.json" });
}
throw new Error(`Unknown file: ${filePath}`);
},
parseJson: JSON.parse,
};
const obj = { $include: "./a.json" };
expect(() => resolveConfigIncludes(obj, "/config/clawdbot.json", resolver))