mirror of
https://github.com/chenasraf/massarg.git
synced 2026-05-17 17:38:09 +00:00
fix: detect the correct flag syntax in all cases
This commit is contained in:
@@ -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<Args extends ArgsObject = ArgsObject> {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
get optionPrefixes(): Prefixes {
|
||||
return {
|
||||
optionPrefix: this.optionPrefix,
|
||||
aliasPrefix: this.optionAliasPrefix,
|
||||
negateFlagPrefix: this.negateFlagPrefix,
|
||||
negateAliasPrefix: this.negateAliasPrefix,
|
||||
}
|
||||
}
|
||||
|
||||
get helpConfig(): DeepRequired<HelpConfig> {
|
||||
if (this.parent) {
|
||||
return deepMerge(this.parent.helpConfig, this._helpConfig)
|
||||
@@ -310,10 +320,10 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
||||
}
|
||||
|
||||
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<Args extends ArgsObject = ArgsObject> {
|
||||
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 }
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -50,6 +50,14 @@ export type OptionConfig<T = unknown, Args extends ArgsObject = ArgsObject> = z.
|
||||
ReturnType<typeof OptionConfig<T, Args>>
|
||||
>
|
||||
|
||||
export const FlagConfig = OptionConfig<boolean>(z.any()).merge(
|
||||
z.object({
|
||||
/** Whether the flag can be negated, e.g. `--no-verbose` */
|
||||
negatable: z.boolean().optional(),
|
||||
}),
|
||||
)
|
||||
export type FlagConfig = z.infer<typeof FlagConfig>
|
||||
|
||||
export type Parser<Args extends ArgsObject = ArgsObject, OptionType extends any = any> = (
|
||||
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<T> = { argv: string[]; value: T; key: string }
|
||||
|
||||
@@ -191,29 +206,11 @@ export class MassargOption<OptionType extends any = unknown, Args extends ArgsOb
|
||||
return `--${this.name}${aliases} ${this.description}`
|
||||
}
|
||||
|
||||
_match(arg: string): boolean {
|
||||
if (!arg) return false
|
||||
// full prefix
|
||||
if (arg.startsWith(OPT_FULL_PREFIX)) {
|
||||
// negate full prefix
|
||||
if (arg.startsWith(`--${NEGATE_FULL_PREFIX}`)) {
|
||||
return this.name === arg.slice(`--${NEGATE_FULL_PREFIX}`.length)
|
||||
}
|
||||
return this.name === arg.slice(OPT_FULL_PREFIX.length)
|
||||
}
|
||||
// short prefix
|
||||
if (arg.startsWith(OPT_SHORT_PREFIX) || arg.startsWith(NEGATE_SHORT_PREFIX)) {
|
||||
return this.aliases.includes(arg.slice(OPT_SHORT_PREFIX.length))
|
||||
}
|
||||
// negate short prefix
|
||||
if (arg.startsWith(NEGATE_SHORT_PREFIX)) {
|
||||
return this.aliases.includes(arg.slice(NEGATE_SHORT_PREFIX.length))
|
||||
}
|
||||
// no prefix
|
||||
return false
|
||||
_match(arg: string, prefixes: Prefixes): boolean {
|
||||
return MassargOption.findNameInArg(arg, prefixes) !== '<blank>'
|
||||
}
|
||||
|
||||
_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<OptionType extends any = unknown, Args extends ArgsOb
|
||||
)
|
||||
}
|
||||
|
||||
static getName(arg: string): string {
|
||||
if (arg.startsWith(OPT_FULL_PREFIX)) {
|
||||
static findNameInArg(
|
||||
arg: string,
|
||||
prefixes: Prefixes,
|
||||
): string {
|
||||
const { optionPrefix, aliasPrefix, negateFlagPrefix, negateAliasPrefix } = prefixes
|
||||
if (arg.startsWith(optionPrefix)) {
|
||||
// negate full prefix
|
||||
if (arg.startsWith(`--${NEGATE_FULL_PREFIX}`)) {
|
||||
return arg.slice(`--${NEGATE_FULL_PREFIX}`.length)
|
||||
if (arg.startsWith(`--${negateFlagPrefix}`)) {
|
||||
return arg.slice(`--${negateFlagPrefix}`.length)
|
||||
}
|
||||
return arg.slice(OPT_FULL_PREFIX.length)
|
||||
return arg.slice(optionPrefix.length)
|
||||
}
|
||||
// short prefix
|
||||
if (arg.startsWith(OPT_SHORT_PREFIX) || arg.startsWith(NEGATE_SHORT_PREFIX)) {
|
||||
return arg.slice(OPT_SHORT_PREFIX.length)
|
||||
if (arg.startsWith(aliasPrefix) || arg.startsWith(negateAliasPrefix)) {
|
||||
return arg.slice(aliasPrefix.length)
|
||||
}
|
||||
// negate short prefix
|
||||
if (arg.startsWith(NEGATE_SHORT_PREFIX)) {
|
||||
return arg.slice(NEGATE_SHORT_PREFIX.length)
|
||||
if (arg.startsWith(negateAliasPrefix)) {
|
||||
return arg.slice(negateAliasPrefix.length)
|
||||
}
|
||||
return '<blank>'
|
||||
}
|
||||
@@ -300,7 +301,7 @@ export class MassargNumber extends MassargOption<number> {
|
||||
*
|
||||
* 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<number> {
|
||||
* ```
|
||||
*/
|
||||
export class MassargFlag extends MassargOption<boolean> {
|
||||
constructor(options: Omit<OptionConfig<boolean>, 'parse'>) {
|
||||
negatable: boolean
|
||||
|
||||
constructor(options: FlagConfig) {
|
||||
super({
|
||||
...options,
|
||||
parse: () => true as any,
|
||||
})
|
||||
this.negatable = options.negatable ?? false
|
||||
}
|
||||
|
||||
_parseDetails(argv: string[]): ArgvValue<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user