Merge pull request #2063 from heygen-com/vi/figma-mapper-fixes

fix(core,cli): parent-relative figma child geometry; groups stop rejecting subcommand flags
This commit is contained in:
Vance Ingalls
2026-07-09 01:18:04 -07:00
committed by GitHub
10 changed files with 678 additions and 12 deletions
@@ -39,6 +39,60 @@ describe("trackCommandFailures", () => {
expect(onFailure).not.toHaveBeenCalled();
});
it("rejects an unknown flag on a leaf command", async () => {
const wrapped = trackCommandFailures(
() =>
Promise.resolve({
meta: { name: "leaf" },
args: { out: { type: "string" } },
run: () => Promise.resolve(),
} as CommandDef),
vi.fn(),
);
const cmd = await wrapped();
await expect(
(cmd.run as (ctx: unknown) => Promise<unknown>)({ rawArgs: ["--bogus", "x"] }),
).rejects.toThrow(/--bogus/);
});
it("skips the unknown-flag check when a command group delegates to a subcommand", async () => {
// `figma component <ref> --name x`: --name belongs to the subcommand's
// table; the group (subCommands + fallback-help run) must not reject it.
const run = vi.fn(() => Promise.resolve());
const wrapped = trackCommandFailures(
() =>
Promise.resolve({
meta: { name: "figma" },
subCommands: { component: () => Promise.resolve({ meta: { name: "component" } }) },
run,
} as unknown as CommandDef),
vi.fn(),
);
const cmd = await wrapped();
await expect(
(cmd.run as (ctx: unknown) => Promise<unknown>)({
rawArgs: ["component", "KEY:1-2", "--name", "hero"],
}),
).resolves.toBeUndefined();
expect(run).toHaveBeenCalled();
});
it("still rejects an unknown flag when the group is NOT delegating", async () => {
const wrapped = trackCommandFailures(
() =>
Promise.resolve({
meta: { name: "figma" },
subCommands: { component: () => Promise.resolve({ meta: { name: "component" } }) },
run: () => Promise.resolve(),
} as unknown as CommandDef),
vi.fn(),
);
const cmd = await wrapped();
await expect(
(cmd.run as (ctx: unknown) => Promise<unknown>)({ rawArgs: ["--bogus"] }),
).rejects.toThrow(/--bogus/);
});
it("passes through a command with no run() untouched", async () => {
const onFailure = vi.fn();
const parent: CommandDef = { meta: { name: "parent" } };
@@ -54,7 +54,23 @@ function wrapCommand(
// Reject unknown flags before the command body: citty silently ignores
// them otherwise, dropping the value (e.g. `render --out x` fell back to
// the default output path). Inside the try so the rejection is reported.
assertKnownFlags(cmd, ctx?.rawArgs ?? []);
//
// A command group (subCommands + fallback-help run) delegating to a
// subcommand must NOT assert here: the flags belong to the subcommand's
// table, not the group's, and would be falsely rejected (e.g.
// `figma component <ref> --name x`). The wrapped subcommand loaders
// below assert the leaf's own table, so typo protection is preserved.
// Heuristic caveat: "first non-dash token names a subcommand" is sound
// only while command groups declare no flags of their own — if a group
// grows a flag whose value could match a subcommand name, replace this
// with a real argv parse.
const rawArgs = ctx?.rawArgs ?? [];
const firstPositional = rawArgs.find((tok) => tok && !tok.startsWith("-"));
const delegatesToSub =
cmd.subCommands != null &&
firstPositional != null &&
Object.prototype.hasOwnProperty.call(cmd.subCommands, firstPositional);
if (!delegatesToSub) assertKnownFlags(cmd, rawArgs);
return await run(ctx);
} catch (err) {
try {