Skip to content

Commit 3d1eae7

Browse files
dmealingclaude
andcommitted
fix(cli): surface a broken metaobjects.config.ts in meta docs instead of silently swallowing
Only attempt config load when the file exists (absence = expected config-less case, stay silent); a config that EXISTS but fails to load now warns with the real error rather than degrading silently to provider-less docs — otherwise a custom-type project would later fail with a cryptic unknown-subtype error. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 24e9884 commit 3d1eae7

2 files changed

Lines changed: 52 additions & 7 deletions

File tree

server/typescript/packages/cli/src/commands/docs.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,24 @@ export async function docsCommand(args: string[], cwd: string): Promise<number>
9696
// back to defaults; the loader still surfaces a stable unknown-subtype error
9797
// if the metadata genuinely uses an unregistered type.
9898
let configProviders: NonNullable<Awaited<ReturnType<typeof loadMetaobjectsConfig>>["providers"]> | undefined;
99-
try {
100-
// The config lives alongside metaobjects/ at the metadata root (metaRoot);
101-
// projectRoot only diverges when --templates overrides the template lookup.
102-
const forgeConfig = await loadMetaobjectsConfig(metaRoot);
103-
configProviders = forgeConfig.providers;
104-
} catch {
105-
configProviders = undefined;
99+
// The config lives alongside metaobjects/ at the metadata root (metaRoot);
100+
// projectRoot only diverges when --templates overrides the template lookup.
101+
// Only attempt the load when the file is actually present: absence is the
102+
// expected config-less case (stay silent), but a config that EXISTS yet fails
103+
// to load is surfaced as a warning rather than silently degrading to
104+
// provider-less docs — otherwise a custom-type project would later fail with a
105+
// cryptic unknown-subtype error instead of the real config error.
106+
if (existsSync(join(metaRoot, "metaobjects.config.ts"))) {
107+
try {
108+
const forgeConfig = await loadMetaobjectsConfig(metaRoot);
109+
configProviders = forgeConfig.providers;
110+
} catch (err) {
111+
log.warn(
112+
`docs: metaobjects.config.ts failed to load (${(err as Error).message}); ` +
113+
`generating docs without its providers`,
114+
);
115+
configProviders = undefined;
116+
}
106117
}
107118

108119
// Load metadata standalone — same loader path as migrate/gen. Threads any

server/typescript/packages/cli/test/docs-command.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,40 @@ describe("meta docs — standalone neutral metadata docs", () => {
196196
expect(files).toContain("Place.md");
197197
});
198198

199+
test("a broken metaobjects.config.ts is surfaced (not silently swallowed), docs still generate", async () => {
200+
// Basic metadata (no custom types) + a config that THROWS on load. Docs must
201+
// still succeed config-less, but the real config error must be surfaced as a
202+
// warning rather than silently degrading to provider-less docs.
203+
const root = await project();
204+
await writeFile(
205+
join(root, "metaobjects.config.ts"),
206+
"throw new Error('broken config boom');\n",
207+
"utf8",
208+
);
209+
const out = join(root, "out-brokencfg");
210+
211+
const errLogged: string[] = [];
212+
const origErr = console.error;
213+
console.error = (...args: unknown[]) => {
214+
errLogged.push(args.map(String).join(" "));
215+
};
216+
let code: number;
217+
try {
218+
code = await docsCommand([root, "--out", out], root);
219+
} finally {
220+
console.error = origErr;
221+
}
222+
223+
expect(code).toBe(0); // config-less generation still works
224+
const files = await readdir(out);
225+
expect(files.length).toBeGreaterThan(0);
226+
// The config failure is SURFACED (warning on stderr), not silently swallowed,
227+
// and carries the loader's real diagnostic so the user can fix it.
228+
const stderr = errLogged.join("\n");
229+
expect(stderr).toContain("metaobjects.config.ts failed to load");
230+
expect(stderr).toContain("generating docs without its providers");
231+
});
232+
199233
test("exits non-zero with a clear message when metadata is missing", async () => {
200234
const empty = await mkdtemp(join(tmpdir(), "meta-docs-empty-"));
201235
dirs.push(empty);

0 commit comments

Comments
 (0)