-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Command.awaitHook(), Argument.chainArgParserCalls() and Option.chainArgParserCalls() #1902
Changes from 1 commit
4061bff
4a6e07c
4738e3c
c0b7e74
200b31d
2114a72
7d1038e
dbca267
6f795f7
e632f84
b96474c
6c44e2b
ca48392
287cdc7
1830e14
a451ac5
3a956b5
96b9fc2
62700a3
9a2a2b5
51d460c
94cb5ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -412,6 +412,27 @@ Expecting one of '${allowedValues.join("', '")}'`); | |||
return this; | ||||
} | ||||
|
||||
/** | ||||
* Add hook to await argument and option values before calling action handlers for this command and its nested subcommands. | ||||
* Useful for asynchronous custom processing (parseArg) of arguments and option-arguments. | ||||
*/ | ||||
|
||||
awaitHook() { | ||||
this.hook('preAction', async function awaitHook(thisCommand, actionCommand) { | ||||
actionCommand.processedArgs = await Promise.all( | ||||
actionCommand.processedArgs | ||||
); | ||||
|
||||
for (const [key, value] of Object.entries(actionCommand.opts())) { | ||||
actionCommand.setOptionValueWithSource( | ||||
key, await value, actionCommand._optionValueSources[key] | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is an existing test for Line 1192 in 4ef19fa
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
); | ||||
} | ||||
}); | ||||
|
||||
return this; | ||||
} | ||||
|
||||
/** | ||||
* Register callback to use as replacement for calling process.exit. | ||||
* | ||||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
const commander = require('../'); | ||
|
||
describe('awaitHook with arguments', () => { | ||
test('when awaitHook and arguments with custom processing then .processedArgs and actioon arguments resolved from callback', async() => { | ||
const resolvedValues = [3, 4]; | ||
const awaited = [ | ||
{ then: (fn) => fn(resolvedValues[0]) }, | ||
resolvedValues[1] | ||
]; | ||
const mockCoercions = awaited.map( | ||
value => jest.fn().mockImplementation(() => value) | ||
); | ||
|
||
let actionValues; | ||
const program = new commander.Command(); | ||
program | ||
.argument('<arg>', 'desc', mockCoercions[0]) | ||
.argument('[arg]', 'desc', mockCoercions[1]) | ||
.awaitHook() | ||
.action((...args) => { | ||
actionValues = args.slice(0, resolvedValues.length); | ||
}); | ||
|
||
const result = program.parseAsync(['1', '2'], { from: 'user' }); | ||
expect(program.processedArgs).toEqual(awaited); | ||
await result; | ||
expect(program.processedArgs).toEqual(resolvedValues); | ||
expect(actionValues).toEqual(resolvedValues); | ||
}); | ||
|
||
test('when awaitHook and arguments not specified with default values then .processedArgs and actioon arguments resolved from default values', async() => { | ||
const resolvedValues = [1, 2]; | ||
const awaited = [ | ||
{ then: (fn) => fn(resolvedValues[0]) }, | ||
resolvedValues[1] | ||
]; | ||
|
||
let actionValues; | ||
const program = new commander.Command(); | ||
program | ||
.argument('[arg]', 'desc', awaited[0]) | ||
.argument('[arg]', 'desc', awaited[1]) | ||
.awaitHook() | ||
.action((...args) => { | ||
actionValues = args.slice(0, resolvedValues.length); | ||
}); | ||
|
||
const result = program.parseAsync([], { from: 'user' }); | ||
expect(program.processedArgs).toEqual(awaited); | ||
await result; | ||
expect(program.processedArgs).toEqual(resolvedValues); | ||
expect(actionValues).toEqual(resolvedValues); | ||
}); | ||
}); | ||
|
||
describe('awaitHook with options', () => { | ||
test('when awaitHook and options with custom processing then .opts() resolved from callback', async() => { | ||
const resolvedValues = { a: 3, b: 4 }; | ||
const awaited = { | ||
a: { then: (fn) => fn(resolvedValues.a) }, | ||
b: resolvedValues.b | ||
}; | ||
const mockCoercions = Object.entries(awaited).reduce((acc, [key, value]) => { | ||
acc[key] = jest.fn().mockImplementation(() => value); | ||
return acc; | ||
}, {}); | ||
|
||
const program = new commander.Command(); | ||
program | ||
.option('-a <arg>', 'desc', mockCoercions.a) | ||
.option('-b [arg]', 'desc', mockCoercions.b) | ||
.awaitHook() | ||
.action(() => {}); | ||
|
||
const result = program.parseAsync(['-a', '1', '-b', '2'], { from: 'user' }); | ||
expect(program.opts()).toEqual(awaited); | ||
await result; | ||
expect(program.opts()).toEqual(resolvedValues); | ||
expect(program.getOptionValueSource('a')).toEqual('cli'); | ||
expect(program.getOptionValueSource('b')).toEqual('cli'); | ||
}); | ||
|
||
test('when awaitHook and options not specified with default values then .opts() resolved from default values', async() => { | ||
const resolvedValues = { a: 1, b: 2 }; | ||
const awaited = { | ||
a: { then: (fn) => fn(resolvedValues.a) }, | ||
b: resolvedValues.b | ||
}; | ||
|
||
const program = new commander.Command(); | ||
program | ||
.option('-a <arg>', 'desc', awaited.a) | ||
.option('-b [arg]', 'desc', awaited.b) | ||
.awaitHook() | ||
.action(() => {}); | ||
|
||
const result = program.parseAsync([], { from: 'user' }); | ||
expect(program.opts()).toEqual(awaited); | ||
await result; | ||
expect(program.opts()).toEqual(resolvedValues); | ||
expect(program.getOptionValueSource('a')).toEqual('default'); | ||
expect(program.getOptionValueSource('b')).toEqual('default'); | ||
}); | ||
|
||
test('when awaitHook and implied option values then .opts() resolved from implied values', async() => { | ||
const resolvedValues = { a: 1, b: 2 }; | ||
const awaited = { | ||
a: { then: (fn) => fn(resolvedValues.a) }, | ||
b: resolvedValues.b | ||
}; | ||
|
||
const option = new commander.Option('-c').implies(awaited); | ||
const program = new commander.Command(); | ||
program | ||
.option('-a <arg>') | ||
.option('-b [arg]') | ||
.addOption(option) | ||
.awaitHook() | ||
.action(() => {}); | ||
|
||
const result = program.parseAsync(['-c'], { from: 'user' }); | ||
expect(program.opts()).toEqual({ ...awaited, c: true }); | ||
await result; | ||
expect(program.opts()).toEqual({ ...resolvedValues, c: true }); | ||
expect(program.getOptionValueSource('a')).toEqual('implied'); | ||
expect(program.getOptionValueSource('b')).toEqual('implied'); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
processedArgs
may include an element which is an array for a variadic argument. I am guessing this is not supported byPromise.all
so might need more complex processing.