Skip to content

Commit

Permalink
fix(command): actions on hyphenated options won't run (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
c4spar committed Feb 7, 2021
1 parent 22681f0 commit 1ee0366
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 40 deletions.
54 changes: 30 additions & 24 deletions command/command.ts
Expand Up @@ -705,15 +705,22 @@ export class Command<O = any, A extends Array<any> = any> {

return await this.execute({} as O, ...this.rawArgs as A);
} else {
const { flags, unknown, literal } = this.parseFlags(this.rawArgs);
const { action, flags, unknown, literal } = this.parseFlags(
this.rawArgs,
);

this.literalArgs = literal;

const params = this.parseArguments(unknown, flags);

await this.validateEnvVars();

if (dry) {
if (dry || action) {
if (action) {
// Fix deno 1.2 error:
// deno-lint-ignore no-explicit-any
await (action as any).call(this, flags, ...params);
}
return {
options: flags,
args: params,
Expand Down Expand Up @@ -789,12 +796,6 @@ export class Command<O = any, A extends Array<any> = any> {
* @param args Command arguments.
*/
protected async execute(options: O, ...args: A): Promise<IParseResult<O, A>> {
const actionOption = this.findActionFlag(options);
if (actionOption?.action) {
await actionOption.action.call(this, options, ...args);
return { options, args, cmd: this, literal: this.literalArgs };
}

if (this.fn) {
await this.fn(options, ...args);
} else if (this.defaultCommand) {
Expand Down Expand Up @@ -899,13 +900,22 @@ export class Command<O = any, A extends Array<any> = any> {
* Parse raw command line arguments.
* @param args Raw command line arguments.
*/
protected parseFlags(args: string[]): IFlagsResult<O> {
return parseFlags<O>(args, {
protected parseFlags(
args: string[],
): IFlagsResult<O> & { action?: IAction<O, A> } {
let action: IAction<O, A> | undefined;
const result = parseFlags<O>(args, {
stopEarly: this._stopEarly,
allowEmpty: this._allowEmpty,
flags: this.getOptions(true),
parse: (type: ITypeInfo) => this.parseType(type),
parse: (type: ITypeInfo) => {
if (!action) {
action = this.getOptionAction(type.name);
}
return this.parseType(type);
},
});
return { ...result, action };
}

/** Parse argument type. */
Expand Down Expand Up @@ -1029,21 +1039,17 @@ export class Command<O = any, A extends Array<any> = any> {
}

/**
* Returns the first option which has an action.
* @param flags Command options.
* Returns the action or undefined of the given option.
* @param name Option name.
*/
protected findActionFlag(flags: O): IOption | undefined {
const flagNames = Object.keys(flags);

for (const flag of flagNames) {
const option = this.getOption(flag, true);

if (option?.action) {
return option;
}
private getOptionAction(name: string): IAction<O, A> | undefined {
const option: IOption<O, A> | undefined = this.getOption(
name.replace(/^(-)+/, ""),
true,
);
if (option?.action) {
return option?.action;
}

return;
}

/**
Expand Down
50 changes: 34 additions & 16 deletions command/test/option/action_test.ts
@@ -1,22 +1,14 @@
import { assertEquals } from "../../../dev_deps.ts";
import { assert, assertEquals } from "../../../dev_deps.ts";
import { Command } from "../../command.ts";

interface IStats {
context: null | Command;
options: unknown;
args: unknown;
context?: null | Command;
options?: unknown;
args?: unknown;
}

function createStats(): IStats {
return {
context: null,
options: null,
args: null,
};
}

Deno.test("command optionAction action", async () => {
const stats: IStats = createStats();
Deno.test("command option action", async () => {
const stats: IStats = {};

const cmd = new Command()
.throwErrors()
Expand All @@ -31,15 +23,16 @@ Deno.test("command optionAction action", async () => {

const { options, args } = await cmd.parse(["--foo", "bar", "beep"]);

assert(stats.context, "option action not executed");
assertEquals(stats.context, cmd);
assertEquals(stats.options, { foo: "bar" });
assertEquals(stats.args, ["beep"]);
assertEquals(stats.options, options);
assertEquals(stats.args, args);
});

Deno.test("command optionAction action", async () => {
const stats: IStats = createStats();
Deno.test("sub-command option action", async () => {
const stats: IStats = {};
let subCmd: Command;

const cmd = new Command()
Expand All @@ -59,9 +52,34 @@ Deno.test("command optionAction action", async () => {

const { options, args } = await cmd.parse(["foo", "--bar", "baz", "beep"]);

assert(stats.context, "option action not executed");
assertEquals(stats.context, subCmd);
assertEquals(stats.options, { bar: "baz" });
assertEquals(stats.args, ["beep"]);
assertEquals(stats.options, options);
assertEquals(stats.args, args);
});

Deno.test("option action with dashed option name", async () => {
const stats: IStats = {};

const cmd = new Command()
.throwErrors()
.arguments("[beep:string]")
.option("-f, --foo-bar [value:string]", "action ...", {
action: function (options, ...args) {
stats.context = this;
stats.options = options;
stats.args = args;
},
});

const { options, args } = await cmd.parse(["-f", "beep", "boop"]);

assert(stats.context, "option action not executed");
assertEquals(stats.context, cmd);
assertEquals(stats.options, { fooBar: "beep" });
assertEquals(stats.args, ["boop"]);
assertEquals(stats.options, options);
assertEquals(stats.args, args);
});

0 comments on commit 1ee0366

Please sign in to comment.