From 08192b9c277a2bf658b534a3d7e508ba3ebb0736 Mon Sep 17 00:00:00 2001 From: Chen Asraf Date: Mon, 4 Dec 2023 21:04:31 +0200 Subject: [PATCH] fix: detect the correct flag syntax in all cases --- src/command.ts | 16 +++++++++--- src/help.ts | 2 +- src/option.ts | 68 ++++++++++++++++++++++++++------------------------ 3 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/command.ts b/src/command.ts index f82a4c4..ef85182 100644 --- a/src/command.ts +++ b/src/command.ts @@ -11,6 +11,7 @@ import { NEGATE_FULL_PREFIX, OPT_SHORT_PREFIX, NEGATE_SHORT_PREFIX, + Prefixes, } from './option' import { DeepRequired, setOrPush, deepMerge } from './utils' import { MassargExample, ExampleConfig } from './example' @@ -97,6 +98,15 @@ export class MassargCommand { this.parent = parent } + get optionPrefixes(): Prefixes { + return { + optionPrefix: this.optionPrefix, + aliasPrefix: this.optionAliasPrefix, + negateFlagPrefix: this.negateFlagPrefix, + negateAliasPrefix: this.negateAliasPrefix, + } + } + get helpConfig(): DeepRequired { if (this.parent) { return deepMerge(this.parent.helpConfig, this._helpConfig) @@ -310,10 +320,10 @@ export class MassargCommand { } private parseOption(arg: string, argv: string[]) { - const option = this.options.find((o) => o._match(arg)) + const option = this.options.find((o) => o._match(arg, this.optionPrefixes)) if (!option) { throw new ValidationError({ - path: [MassargOption.getName(arg)], + path: [MassargOption.findNameInArg(arg, this.optionPrefixes)], code: 'unknown_option', message: 'Unknown option', }) @@ -367,7 +377,7 @@ export class MassargCommand { while (_argv.length) { const arg = _argv.shift()! // make sure option exists - const found = this.options.some((o) => o._isOption(arg)) + const found = this.options.some((o) => o._isOption(arg, this.optionPrefixes)) if (found) { _argv = this.parseOption(arg, _argv) _args = { ..._args, ...this.args } diff --git a/src/help.ts b/src/help.ts index 7a17e45..2756b2b 100644 --- a/src/help.ts +++ b/src/help.ts @@ -254,7 +254,7 @@ export class HelpGenerator { usageText ? strConcat( format('Usage:', this.config.usageStyle.prefix), - format(usageText, this.config.usageStyle.command), + format(usageText, this.config.usageStyle.main), ) : [ format(`Usage:`, this.config.usageStyle.prefix), diff --git a/src/option.ts b/src/option.ts index 77fac40..b72d96c 100644 --- a/src/option.ts +++ b/src/option.ts @@ -50,6 +50,14 @@ export type OptionConfig = z. ReturnType> > +export const FlagConfig = OptionConfig(z.any()).merge( + z.object({ + /** Whether the flag can be negated, e.g. `--no-verbose` */ + negatable: z.boolean().optional(), + }), +) +export type FlagConfig = z.infer + export type Parser = ( x: string, y: Args, @@ -94,6 +102,13 @@ export const OPT_SHORT_PREFIX = '-' export const NEGATE_FULL_PREFIX = 'no-' export const NEGATE_SHORT_PREFIX = '^' +export type Prefixes = { + optionPrefix: string + aliasPrefix: string + negateFlagPrefix: string + negateAliasPrefix: string +} + /** @internal */ export type ArgvValue = { argv: string[]; value: T; key: string } @@ -191,29 +206,11 @@ export class MassargOption' } - _isOption(arg: string): boolean { + _isOption(arg: string, prefixes: Prefixes): boolean { return ( arg.startsWith(OPT_FULL_PREFIX) || arg.startsWith(OPT_SHORT_PREFIX) || @@ -221,21 +218,25 @@ export class MassargOption' } @@ -300,7 +301,7 @@ export class MassargNumber extends MassargOption { * * A flag can be negated by prefixing it with `no-`. For example, `--no-verbose`, * or by prefixing the alias with `^` instead of `-`. This is configurable via the command's - * configuration. + * configuration. To turn this behavior on, set `negatable: true` in the flag's configuration. * * @example * ```ts @@ -313,11 +314,14 @@ export class MassargNumber extends MassargOption { * ``` */ export class MassargFlag extends MassargOption { - constructor(options: Omit, 'parse'>) { + negatable: boolean + + constructor(options: FlagConfig) { super({ ...options, parse: () => true as any, }) + this.negatable = options.negatable ?? false } _parseDetails(argv: string[]): ArgvValue {