Skip to content

Commit 5772298

Browse files
committed
feat: add cli command dispatch
1 parent 93acddb commit 5772298

1 file changed

Lines changed: 127 additions & 0 deletions

File tree

src/cli.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/env node
2+
3+
import { parseArgs } from "node:util";
4+
import { runExtraction } from "./index.js";
5+
import { runSemanticDiff } from "./semantic-diff.js";
6+
import type {
7+
CommandLineOptions,
8+
SemanticDiffCliOptions,
9+
CliOptions,
10+
} from "./types.js";
11+
12+
async function main(): Promise<void> {
13+
const options = parseCli(process.argv.slice(2));
14+
if (options.command === "extract") {
15+
const spec = await runExtraction(options);
16+
if (options.json) {
17+
process.stdout.write(
18+
`${JSON.stringify({ event: "result", routes: spec.routes.length, diagnostics: spec.diagnostics.length })}\n`,
19+
);
20+
} else {
21+
process.stdout.write(
22+
`Extracted ${spec.routes.length} routes with ${spec.diagnostics.length} diagnostics.\n`,
23+
);
24+
}
25+
return;
26+
}
27+
28+
const summary = await runSemanticDiff({
29+
previousPath: options.previousPath,
30+
nextPath: options.nextPath,
31+
jsonOutputPath: options.jsonOutputPath,
32+
markdownOutputPath: options.markdownOutputPath,
33+
});
34+
process.stdout.write(`${JSON.stringify(summary)}\n`);
35+
}
36+
37+
function parseCli(argv: readonly string[]): CommandLineOptions {
38+
const command = argv[0] ?? "extract";
39+
const commandArgs = argv[0] === undefined ? argv : argv.slice(1);
40+
if (command === "extract") {
41+
return parseExtractCli(commandArgs);
42+
}
43+
if (command === "semantic-diff") {
44+
return parseSemanticDiffCli(commandArgs);
45+
}
46+
throw new Error(`Unknown command: ${command}`);
47+
}
48+
function parseExtractCli(argv: readonly string[]): CliOptions {
49+
const { values, positionals } = parseArgs({
50+
args: [...argv],
51+
allowPositionals: true,
52+
options: {
53+
config: { type: "string" },
54+
output: { type: "string" },
55+
"base-url": { type: "string" },
56+
"max-call-depth": { type: "string" },
57+
"fail-on-diagnostics": { type: "boolean" },
58+
"fail-on-unresolved": { type: "boolean" },
59+
verbose: { type: "boolean" },
60+
"debug-route": { type: "string" },
61+
json: { type: "boolean" },
62+
},
63+
strict: true,
64+
});
65+
66+
if (positionals.length > 0) {
67+
throw new Error(`Unknown arguments: ${positionals.join(" ")}`);
68+
}
69+
70+
let maxCallDepth: number | undefined;
71+
if (values["max-call-depth"] !== undefined) {
72+
maxCallDepth = Number.parseInt(values["max-call-depth"], 10);
73+
if (!Number.isFinite(maxCallDepth)) {
74+
throw new Error(
75+
`Invalid value for --max-call-depth: ${values["max-call-depth"]}`,
76+
);
77+
}
78+
}
79+
80+
return {
81+
command: "extract",
82+
configPath: values.config,
83+
outputPath: values.output,
84+
baseUrl: values["base-url"],
85+
maxCallDepth,
86+
failOnDiagnostics: values["fail-on-diagnostics"],
87+
failOnUnresolved: values["fail-on-unresolved"],
88+
verbose: values.verbose ?? false,
89+
debugRoute: values["debug-route"],
90+
json: values.json ?? false,
91+
};
92+
}
93+
94+
function parseSemanticDiffCli(argv: readonly string[]): SemanticDiffCliOptions {
95+
const { values, positionals } = parseArgs({
96+
args: [...argv],
97+
allowPositionals: true,
98+
options: {
99+
previous: { type: "string" },
100+
next: { type: "string" },
101+
"json-output": { type: "string" },
102+
"markdown-output": { type: "string" },
103+
},
104+
strict: true,
105+
});
106+
107+
if (positionals.length > 0) {
108+
throw new Error(`Unknown arguments: ${positionals.join(" ")}`);
109+
}
110+
if (values.previous === undefined || values.next === undefined) {
111+
throw new Error("Both --previous and --next are required.");
112+
}
113+
114+
return {
115+
command: "semantic-diff",
116+
previousPath: values.previous,
117+
nextPath: values.next,
118+
jsonOutputPath: values["json-output"],
119+
markdownOutputPath: values["markdown-output"],
120+
};
121+
}
122+
123+
main().catch((error: unknown) => {
124+
const message = error instanceof Error ? error.message : String(error);
125+
process.stderr.write(`${message}\n`);
126+
process.exitCode = 1;
127+
});

0 commit comments

Comments
 (0)