Skip to content

Commit e591b3f

Browse files
committedDec 31, 2019
Change argv pipeline to init -> applyConfig -> prepareContext
1 parent 1d8f5e8 commit e591b3f

8 files changed

+89
-70
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- Removed `Command#normalize()` method (use `createOptionValues()` instead)
2323
- Changed `Option` to store params info as `Option#params`, it always an object even if no params
2424
- Allowed a number for options's short name
25+
- Changed argv parse handlers to [`init()` -> `applyConfig()` -> `prepareContext()`]+ -> `action()`
2526
- Changed exports
2627
- Added `getCommandHelp()` function
2728
- Added `Params` class

‎README.md

+17-12
Original file line numberDiff line numberDiff line change
@@ -107,25 +107,30 @@ Where `options`:
107107
value: any, // default value
108108
normalize: (value, oldValue) => { ... }, // any value for option is passing through this function and its result stores as option value
109109
shortcut: (value, oldValue) => { ... }, // for shortcut options, the handler is executed after the value is set, and its result (an object) is used as a source of values for other options
110-
action: () => { ... } // for an action option, which breaks regular args processing and preform and action (e.g. show help or version)
110+
action: () => { ... }, // for an action option, which breaks regular args processing and preform and action (e.g. show help or version)
111+
config: boolean // mark option is about config and should be applied before `applyConfig()`
111112
}
112113
```
113114

114-
### Args processing
115+
### Argv processing
115116

116-
- init(command, context) // before arguments parsing
117-
- invoke action option and exit if any
118-
- apply option values
119-
- prepare(context) // after arguments parsing
117+
- `init(command, context)` // before arguments parsing
118+
- invoke action option and exit if any
119+
- apply **config** options
120+
- `applyConfig(context)`
121+
- apply all the rest options
122+
- `prepareContext(context)` // after arguments parsing
120123
- switch to next command -> command is prescending
121-
- init(command, context)
122-
- invoke action option and exit if any
123-
- apply option values
124-
- prepare(context) // after arguments parsing
124+
- `init(command, context)`
125+
- invoke action option and exit if any
126+
- apply **config** options
127+
- `applyConfig(context)`
128+
- apply all the rest options
129+
- `prepareContext(context)` // after arguments parsing
125130
- switch to next command
126131
- ...
127-
- action(context) -> command is target
128-
- action(context) -> command is target
132+
- `action(context)` -> command is target
133+
- `action(context)` -> command is target
129134

130135
## License
131136

‎lib/command.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const defaultHelpAction = (instance, _, { commandPath }) => instance.outputHelp(
99
const defaultVersionAction = instance => console.log(instance.meta.version);
1010
const lastCommandHost = new WeakMap();
1111

12-
const handlers = ['init', 'prepare', 'action'].reduce((res, name) => {
12+
const handlers = ['init', 'applyConfig', 'finishContext', 'action'].reduce((res, name) => {
1313
res.initial[name] = name === 'action' ? self : noop;
1414
res.setters[name] = function(fn) {
1515
this.handlers[name] = fn.bind(null);

‎lib/option.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ module.exports = class Option {
1515
defValue: !ensureFunction(raw.action) ? raw.value : undefined,
1616
normalize: ensureFunction(raw.normalize, self),
1717
shortcut: ensureFunction(raw.shortcut),
18-
action: ensureFunction(raw.action)
18+
action: ensureFunction(raw.action),
19+
config: Boolean(raw.config)
1920
};
2021
}
2122

‎lib/parse-argv.js

+32-18
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function consumeOptionParams(option, rawOptions, argv, index, suggestPoint) {
5050
module.exports = function parseArgv(command, argv, context, suggestMode) {
5151
const suggestPoint = suggestMode ? argv.length - 1 : -1;
5252
const rawOptions = [];
53-
const resultChunk = {
53+
const result = {
5454
context,
5555
action: null,
5656
next: null
@@ -124,8 +124,8 @@ module.exports = function parseArgv(command, argv, context, suggestMode) {
124124

125125
if (subcommand !== null &&
126126
context.args.length >= command.params.minCount) {
127-
// finalize command
128-
resultChunk.next = {
127+
// set next command and rest argv
128+
result.next = {
129129
command: subcommand,
130130
argv: argv.slice(i + 1)
131131
};
@@ -142,34 +142,48 @@ module.exports = function parseArgv(command, argv, context, suggestMode) {
142142
}
143143

144144
// final checks
145-
if (suggestMode && !resultChunk.next) {
145+
if (suggestMode && !result.next) {
146146
return findVariants(command, '');
147147
} else if (context.args.length < command.params.minCount) {
148148
throw new CliError(`Missed required argument(s) for command "${command.name}"`);
149149
}
150150

151+
// create new option values storage
152+
context.options = command.createOptionValues();
153+
151154
// process action option
152-
const firstActionOption = rawOptions.find(({ option }) => option.action);
153-
if (firstActionOption) {
154-
const { option, value } = firstActionOption;
155-
resultChunk.action = (() => option.action(command, value, context));
156-
resultChunk.next = null;
157-
return resultChunk;
155+
const actionOption = rawOptions.find(({ option }) => option.action);
156+
if (actionOption) {
157+
const { option, value } = actionOption;
158+
result.action = () => option.action(command, value, context);
159+
result.next = null;
160+
return result;
158161
}
159162

160-
// apply options
161-
context.options = command.createOptionValues();
163+
// apply config options
162164
for (const { option, value } of rawOptions) {
163-
context.options[option.name] = value;
165+
if (option.config) {
166+
context.options[option.name] = value;
167+
}
168+
}
169+
170+
// run apply config handler
171+
command.handlers.applyConfig(context);
172+
173+
// apply regular options
174+
for (const { option, value } of rawOptions) {
175+
if (!option.config) {
176+
context.options[option.name] = value;
177+
}
164178
}
165179

166-
// run prepare handler
167-
command.handlers.prepare(context);
180+
// run context finish handler
181+
command.handlers.finishContext(context);
168182

169183
// set action if no rest argv
170-
if (!resultChunk.next) {
171-
resultChunk.action = command.handlers.action;
184+
if (!result.next) {
185+
result.action = command.handlers.action;
172186
}
173187

174-
return resultChunk;
188+
return result;
175189
};

‎test/command-init-prepare.js

-32
This file was deleted.

‎test/command-parse-handlers.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const assert = require('assert');
2+
const cli = require('../lib');
3+
4+
describe('init()/applyConfig()/finishContext()', function() {
5+
let command;
6+
let calls;
7+
8+
beforeEach(function() {
9+
calls = [];
10+
command = cli.command('test', '[arg1]')
11+
.init(() => calls.push('init'))
12+
.applyConfig(() => calls.push('applyConfig'))
13+
.finishContext(() => calls.push('finishContext'));
14+
command.command('nested', '[arg2] [arg3]')
15+
.init(() => calls.push('nested init'))
16+
.applyConfig(() => calls.push('nested applyConfig'))
17+
.finishContext(() => calls.push('nested finishContext'));
18+
});
19+
20+
it('with no arguments should init/finishContext top level command only', function() {
21+
command.run([]);
22+
assert.deepEqual(calls, ['init', 'applyConfig', 'finishContext']);
23+
});
24+
25+
it('with one argument should init and finishContext top level command', function() {
26+
command.run(['foo']);
27+
assert.deepEqual(calls, ['init', 'applyConfig', 'finishContext']);
28+
});
29+
30+
it('with first argument as command should init/finishContext both commands', function() {
31+
command.run(['nested']);
32+
assert.deepEqual(calls, ['init', 'applyConfig', 'finishContext', 'nested init', 'nested applyConfig', 'nested finishContext']);
33+
});
34+
});

‎test/option-one-arg.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ describe('one arg options', function() {
4444

4545
const command = cli.command()
4646
.option('--option <arg>', 'description')
47-
.prepare(function({ options }) {
48-
values = options;
49-
})
47+
.finishContext(({ options }) => values = options)
5048
.command('test')
5149
.action(() => ok = true)
5250
.end();
@@ -144,9 +142,7 @@ describe('one arg options', function() {
144142

145143
const command = cli.command()
146144
.option('--option [arg]', 'description')
147-
.prepare(function({ options }) {
148-
values = options;
149-
})
145+
.finishContext(({ options }) => values = options)
150146
.command('test')
151147
.action(() => ok = true)
152148
.end();

0 commit comments

Comments
 (0)
Please sign in to comment.