mirror of
https://github.com/chenasraf/massarg.git
synced 2026-05-18 01:39:05 +00:00
fix: detect the correct flag syntax in all cases
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
|||||||
NEGATE_FULL_PREFIX,
|
NEGATE_FULL_PREFIX,
|
||||||
OPT_SHORT_PREFIX,
|
OPT_SHORT_PREFIX,
|
||||||
NEGATE_SHORT_PREFIX,
|
NEGATE_SHORT_PREFIX,
|
||||||
|
Prefixes,
|
||||||
} from './option'
|
} from './option'
|
||||||
import { DeepRequired, setOrPush, deepMerge } from './utils'
|
import { DeepRequired, setOrPush, deepMerge } from './utils'
|
||||||
import { MassargExample, ExampleConfig } from './example'
|
import { MassargExample, ExampleConfig } from './example'
|
||||||
@@ -97,6 +98,15 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get optionPrefixes(): Prefixes {
|
||||||
|
return {
|
||||||
|
optionPrefix: this.optionPrefix,
|
||||||
|
aliasPrefix: this.optionAliasPrefix,
|
||||||
|
negateFlagPrefix: this.negateFlagPrefix,
|
||||||
|
negateAliasPrefix: this.negateAliasPrefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get helpConfig(): DeepRequired<HelpConfig> {
|
get helpConfig(): DeepRequired<HelpConfig> {
|
||||||
if (this.parent) {
|
if (this.parent) {
|
||||||
return deepMerge(this.parent.helpConfig, this._helpConfig)
|
return deepMerge(this.parent.helpConfig, this._helpConfig)
|
||||||
@@ -310,10 +320,10 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private parseOption(arg: string, argv: string[]) {
|
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) {
|
if (!option) {
|
||||||
throw new ValidationError({
|
throw new ValidationError({
|
||||||
path: [MassargOption.getName(arg)],
|
path: [MassargOption.findNameInArg(arg, this.optionPrefixes)],
|
||||||
code: 'unknown_option',
|
code: 'unknown_option',
|
||||||
message: 'Unknown option',
|
message: 'Unknown option',
|
||||||
})
|
})
|
||||||
@@ -367,7 +377,7 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
|
|||||||
while (_argv.length) {
|
while (_argv.length) {
|
||||||
const arg = _argv.shift()!
|
const arg = _argv.shift()!
|
||||||
// make sure option exists
|
// 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) {
|
if (found) {
|
||||||
_argv = this.parseOption(arg, _argv)
|
_argv = this.parseOption(arg, _argv)
|
||||||
_args = { ..._args, ...this.args }
|
_args = { ..._args, ...this.args }
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ export class HelpGenerator {
|
|||||||
usageText
|
usageText
|
||||||
? strConcat(
|
? strConcat(
|
||||||
format('Usage:', this.config.usageStyle.prefix),
|
format('Usage:', this.config.usageStyle.prefix),
|
||||||
format(usageText, this.config.usageStyle.command),
|
format(usageText, this.config.usageStyle.main),
|
||||||
)
|
)
|
||||||
: [
|
: [
|
||||||
format(`Usage:`, this.config.usageStyle.prefix),
|
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>>
|
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> = (
|
export type Parser<Args extends ArgsObject = ArgsObject, OptionType extends any = any> = (
|
||||||
x: string,
|
x: string,
|
||||||
y: Args,
|
y: Args,
|
||||||
@@ -94,6 +102,13 @@ export const OPT_SHORT_PREFIX = '-'
|
|||||||
export const NEGATE_FULL_PREFIX = 'no-'
|
export const NEGATE_FULL_PREFIX = 'no-'
|
||||||
export const NEGATE_SHORT_PREFIX = '^'
|
export const NEGATE_SHORT_PREFIX = '^'
|
||||||
|
|
||||||
|
export type Prefixes = {
|
||||||
|
optionPrefix: string
|
||||||
|
aliasPrefix: string
|
||||||
|
negateFlagPrefix: string
|
||||||
|
negateAliasPrefix: string
|
||||||
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export type ArgvValue<T> = { argv: string[]; value: T; key: string }
|
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}`
|
return `--${this.name}${aliases} ${this.description}`
|
||||||
}
|
}
|
||||||
|
|
||||||
_match(arg: string): boolean {
|
_match(arg: string, prefixes: Prefixes): boolean {
|
||||||
if (!arg) return false
|
return MassargOption.findNameInArg(arg, prefixes) !== '<blank>'
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_isOption(arg: string): boolean {
|
_isOption(arg: string, prefixes: Prefixes): boolean {
|
||||||
return (
|
return (
|
||||||
arg.startsWith(OPT_FULL_PREFIX) ||
|
arg.startsWith(OPT_FULL_PREFIX) ||
|
||||||
arg.startsWith(OPT_SHORT_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 {
|
static findNameInArg(
|
||||||
if (arg.startsWith(OPT_FULL_PREFIX)) {
|
arg: string,
|
||||||
|
prefixes: Prefixes,
|
||||||
|
): string {
|
||||||
|
const { optionPrefix, aliasPrefix, negateFlagPrefix, negateAliasPrefix } = prefixes
|
||||||
|
if (arg.startsWith(optionPrefix)) {
|
||||||
// negate full prefix
|
// negate full prefix
|
||||||
if (arg.startsWith(`--${NEGATE_FULL_PREFIX}`)) {
|
if (arg.startsWith(`--${negateFlagPrefix}`)) {
|
||||||
return arg.slice(`--${NEGATE_FULL_PREFIX}`.length)
|
return arg.slice(`--${negateFlagPrefix}`.length)
|
||||||
}
|
}
|
||||||
return arg.slice(OPT_FULL_PREFIX.length)
|
return arg.slice(optionPrefix.length)
|
||||||
}
|
}
|
||||||
// short prefix
|
// short prefix
|
||||||
if (arg.startsWith(OPT_SHORT_PREFIX) || arg.startsWith(NEGATE_SHORT_PREFIX)) {
|
if (arg.startsWith(aliasPrefix) || arg.startsWith(negateAliasPrefix)) {
|
||||||
return arg.slice(OPT_SHORT_PREFIX.length)
|
return arg.slice(aliasPrefix.length)
|
||||||
}
|
}
|
||||||
// negate short prefix
|
// negate short prefix
|
||||||
if (arg.startsWith(NEGATE_SHORT_PREFIX)) {
|
if (arg.startsWith(negateAliasPrefix)) {
|
||||||
return arg.slice(NEGATE_SHORT_PREFIX.length)
|
return arg.slice(negateAliasPrefix.length)
|
||||||
}
|
}
|
||||||
return '<blank>'
|
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`,
|
* 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
|
* 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
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
@@ -313,11 +314,14 @@ export class MassargNumber extends MassargOption<number> {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export class MassargFlag extends MassargOption<boolean> {
|
export class MassargFlag extends MassargOption<boolean> {
|
||||||
constructor(options: Omit<OptionConfig<boolean>, 'parse'>) {
|
negatable: boolean
|
||||||
|
|
||||||
|
constructor(options: FlagConfig) {
|
||||||
super({
|
super({
|
||||||
...options,
|
...options,
|
||||||
parse: () => true as any,
|
parse: () => true as any,
|
||||||
})
|
})
|
||||||
|
this.negatable = options.negatable ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
_parseDetails(argv: string[]): ArgvValue<boolean> {
|
_parseDetails(argv: string[]): ArgvValue<boolean> {
|
||||||
|
|||||||
Reference in New Issue
Block a user