Skip to content

Commit 203adba

Browse files
committed
fix: let help flag ignore requirements
1 parent cc87438 commit 203adba

5 files changed

Lines changed: 58 additions & 29 deletions

File tree

src/command.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,13 +318,17 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
318318
* To parse the arguments without running any commands and only get the output args,
319319
* use `getArgs` instead.
320320
*/
321-
parse(argv: string[], args?: Partial<Args>, parent?: MassargCommand<Args>): Promise<void> | void {
321+
parse(
322+
argv = process.argv.slice(2),
323+
args?: Partial<Args>,
324+
parent?: MassargCommand<Args>,
325+
): Promise<void> | void {
322326
this.getArgs(argv, args, parent, true)
323327
}
324328

325329
private parseOption(arg: string, argv: string[]) {
326330
const prefixes = { ...this.optionPrefixes }
327-
const option = this.options.find((o) => o._match(arg, prefixes))
331+
const option = this.options.find((o) => o.isMatch(arg, prefixes))
328332
if (!option) {
329333
throw new ValidationError({
330334
path: [MassargOption.findNameInArg(arg, prefixes)],
@@ -375,8 +379,9 @@ export class MassargCommand<Args extends ArgsObject = ArgsObject> {
375379
// parse options
376380
while (_argv.length) {
377381
const arg = _argv.shift()!
382+
378383
// make sure option exists
379-
const found = this.options.find((o) => o._isOption(arg, { ...this.optionPrefixes }))
384+
const found = this.options.find((o) => o.isMatch(arg, this.optionPrefixes))
380385
if (found) {
381386
if (this.helpConfig.bindOption && found.name === 'help') {
382387
if (parseCommands) {

src/option.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export class MassargOption<OptionType extends any = unknown, Args extends ArgsOb
177177
parseDetails(argv: string[], options: ArgsObject, prefixes: Prefixes): ArgvValue<OptionType> {
178178
let input = ''
179179
try {
180-
if (!this._match(argv[0], prefixes)) {
180+
if (!this.isMatch(argv[0], prefixes)) {
181181
throw new ParseError({
182182
path: [this.name],
183183
code: 'invalid_option',
@@ -207,20 +207,17 @@ export class MassargOption<OptionType extends any = unknown, Args extends ArgsOb
207207
return `--${this.name}${aliases} ${this.description}`
208208
}
209209

210-
_match(arg: string, prefixes: Prefixes): boolean {
210+
/** Returns true if the flag (including any prefixes) matches the name or aliases */
211+
isMatch(arg: string, prefixes: Prefixes): boolean {
211212
const name = MassargOption.findNameInArg(arg, prefixes)
212213
return name === this.name || this.aliases.includes(name)
213214
}
214215

215-
_isOption(arg: string, prefixes: Prefixes): boolean {
216-
return (
217-
arg.startsWith(prefixes.optionPrefix) ||
218-
arg.startsWith(prefixes.aliasPrefix) ||
219-
arg.startsWith(prefixes.negateFlagPrefix) ||
220-
arg.startsWith(prefixes.negateAliasPrefix)
221-
)
222-
}
223-
216+
/**
217+
* Returns the name of the flag, removing any prefixes. It is discriminate of if the option
218+
* exists, as it is a static method; it only returns the name of the flag if it matches the
219+
* prefixes format.
220+
*/
224221
static findNameInArg(arg: string, prefixes: Prefixes): string {
225222
const { optionPrefix, aliasPrefix, negateFlagPrefix, negateAliasPrefix } = prefixes
226223
// negate full prefix
@@ -337,7 +334,7 @@ export class MassargFlag extends MassargOption<boolean> {
337334
received: JSON.stringify(argv[0]),
338335
})
339336
}
340-
if (!this._match(argv[0], prefixes)) {
337+
if (!this.isMatch(argv[0], prefixes)) {
341338
throw new ParseError({
342339
path: [this.name],
343340
code: 'invalid_option',

src/utils.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,15 @@ export function deepMerge<T1, T2>(obj1: T1, obj2: T2): NonNullable<T1> & NonNull
9191
* regular spaced strings.
9292
*/
9393
export function splitWords(str: string): string[] {
94-
return (
95-
str
96-
.replace(/([a-z])([A-Z])/g, '$1 $2')
97-
// .replace(/([a-zA-Z])([0-9])/g, '$1 $2')
98-
.replace(/([0-9])([a-zA-Z])/g, '$1 $2')
99-
.replace(/([a-z])([_-])/g, '$1 $2')
100-
.replace(/([_-])([a-zA-Z])/g, '$1 $2')
101-
.split(/[_-]/)
102-
.map((s) => s.trim())
103-
.filter(Boolean)
104-
)
94+
return str
95+
.replace(/([a-z])([A-Z])/g, '$1 $2')
96+
.replace(/([a-zA-Z])([0-9])/g, '$1 $2')
97+
.replace(/([0-9])([a-zA-Z])/g, '$1 $2')
98+
.replace(/([a-z])([_-])/g, '$1 $2')
99+
.replace(/([_-])([a-zA-Z])/g, '$1 $2')
100+
.split(/[_\- ]/)
101+
.map((s) => s.trim())
102+
.filter(Boolean)
105103
}
106104

107105
export function toCamelCase(str: string): string {

test/help.test.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ test('prints help from command', () => {
3838
const command = massarg(opts).help({
3939
bindCommand: true,
4040
})
41-
const log = jest.spyOn(console, 'log').mockImplementation(() => { })
41+
const log = jest.spyOn(console, 'log').mockImplementation(() => {})
4242
command.parse(['help'])
4343
expect(log).toHaveBeenCalled()
44+
log.mockRestore()
4445
})
4546

4647
test('binds option', () => {
@@ -52,12 +53,26 @@ test('binds option', () => {
5253
expect(command.options.find((o) => o.name === 'help')).toBeTruthy()
5354
})
5455

56+
test('overrides required options', () => {
57+
const command = massarg(opts)
58+
.option({
59+
name: 'required',
60+
aliases: ['r'],
61+
description: 'required',
62+
required: true,
63+
})
64+
.help({
65+
bindOption: true,
66+
})
67+
expect(() => command.getArgs(['--help'])).not.toThrow()
68+
})
69+
5570
describe('prints help from option', () => {
5671
test('when no main command', () => {
5772
const command = massarg(opts).help({
5873
bindOption: true,
5974
})
60-
const log = jest.spyOn(console, 'log').mockImplementation(() => { })
75+
const log = jest.spyOn(console, 'log').mockImplementation(() => {})
6176
command.parse(['--help'])
6277
expect(log).toHaveBeenCalled()
6378
})
@@ -80,7 +95,7 @@ test('help string', () => {
8095
})
8196

8297
test('print help', () => {
83-
const log = jest.spyOn(console, 'log').mockImplementation(() => { })
98+
const log = jest.spyOn(console, 'log').mockImplementation(() => {})
8499
const command = massarg(opts)
85100
command.printHelp()
86101
expect(log).toHaveBeenCalled()

test/utils.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { toCamelCase } from '../src/utils'
2+
3+
describe('toCamelCase', () => {
4+
test('works', () => {
5+
expect(toCamelCase('foo')).toBe('foo')
6+
expect(toCamelCase('foo-bar')).toBe('fooBar')
7+
expect(toCamelCase('foo-bar baz')).toBe('fooBarBaz')
8+
expect(toCamelCase('foo-bar baz-qux')).toBe('fooBarBazQux')
9+
10+
expect(toCamelCase('foo123')).toBe('foo123')
11+
expect(toCamelCase('foo-123')).toBe('foo123')
12+
expect(toCamelCase('foo-123 bar')).toBe('foo123Bar')
13+
})
14+
})

0 commit comments

Comments
 (0)