Files
rea/tests/boundary/cli/cliOptionAliases.test.ts
T
Kaoru0822andClaude Opus 5.5 498cec042a fix(cli): stop advertising multi-letter single-dash option aliases (#1395)
Incur's `alias` map registers one-letter short flags and already accepts
kebab-case spellings of camelCase options. Twenty options registered their
kebab-case long name as an "alias", so help and completions advertised
flags such as `-case-sensitive` and `-relative-path` that the parser reads
as stacked short flags and rejects with "Unknown flag: -c".

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:29:20 -07:00

72 lines
2.3 KiB
TypeScript

import { Cli } from "incur";
import { describe, expect, it } from "vitest";
import { createCli } from "../../../src/cli.js";
import { createCliInventory } from "../../../scripts/lib/catalog-cli.mjs";
type CliInstance = ReturnType<typeof createCli>;
type CommandMap = NonNullable<ReturnType<typeof Cli.toCommands.get>>;
const registeredCommands: WeakMap<object, CommandMap> = Cli.toCommands;
const serve = async (
cli: CliInstance,
argv: readonly string[],
): Promise<{ readonly stdout: string; readonly exitCode: number }> => {
let stdout = "";
let exitCode = 0;
await cli.serve([...argv], {
env: {},
exit: (code) => {
exitCode = code;
},
stdout: (text) => {
stdout += text;
},
});
return { stdout, exitCode };
};
const registeredAliases = (
cli: CliInstance,
command: string,
): Record<string, unknown> => {
const entry = registeredCommands.get(cli)?.get(command);
const alias: unknown =
entry === undefined ? undefined : Reflect.get(entry, "alias");
return typeof alias === "object" && alias !== null ? { ...alias } : {};
};
describe("CLI option aliases", () => {
it("register only single-character short flags", async () => {
const cli = createCli({});
const invalid: string[] = [];
for (const command of createCliInventory(cli).primary) {
for (const [option, short] of Object.entries(
registeredAliases(cli, command),
))
if (typeof short !== "string" || short.length !== 1)
invalid.push(`${command} ${option}: ${String(short)}`);
// Incur parses `-abc` as stacked one-letter flags, so help must never
// advertise a multi-letter single-dash spelling.
const help = await serve(cli, [command, "--help"]);
for (const match of help.stdout.matchAll(/(?:^|[\s,])-([a-z][\w-]+)/gmu))
invalid.push(`${command} help: -${match[1] ?? ""}`);
}
expect(invalid).toEqual([]);
});
it("accepts kebab-case long options without registered aliases", async () => {
const cli = createCli({});
const result = await serve(cli, [
"inspect-plist",
"/nonexistent/REA.app",
"--relative-path",
"Contents/Info.plist",
"--json",
]);
expect(result.stdout).not.toContain("Unknown flag");
expect(result.stdout).toContain("/nonexistent/REA.app");
});
});