@@ -12,6 +12,8 @@ const { suggestSimilar } = require('./suggestSimilar');
12
12
13
13
// @ts -check
14
14
15
+ const PRODUCTION = process . env . NODE_ENV === 'production' ;
16
+
15
17
class Command extends EventEmitter {
16
18
/**
17
19
* Initialize a new `Command`.
@@ -77,6 +79,9 @@ class Command extends EventEmitter {
77
79
this . _helpCommandnameAndArgs = 'help [command]' ;
78
80
this . _helpCommandDescription = 'display help for command' ;
79
81
this . _helpConfiguration = { } ;
82
+
83
+ /** @type {boolean | undefined } */
84
+ this . _asyncParsing = undefined ;
80
85
}
81
86
82
87
/**
@@ -265,6 +270,10 @@ class Command extends EventEmitter {
265
270
throw new Error ( `Command passed to .addCommand() must have a name
266
271
- specify the name in Command constructor or using .name()` ) ;
267
272
}
273
+ if ( cmd . parent ) {
274
+ throw new Error ( `'${ cmd . _name } ' cannot be added using .addCommand()
275
+ - it already has a parent command` ) ;
276
+ }
268
277
269
278
opts = opts || { } ;
270
279
if ( opts . isDefault ) this . _defaultCommandName = cmd . _name ;
@@ -887,6 +896,28 @@ Expecting one of '${allowedValues.join("', '")}'`);
887
896
return userArgs ;
888
897
}
889
898
899
+ /**
900
+ * @param {boolean } async
901
+ * @param {Function } userArgsCallback
902
+ * @param {string[] } [argv]
903
+ * @param {Object } [parseOptions]
904
+ * @param {string } [parseOptions.from]
905
+ * @return {Command|Promise }
906
+ * @api private
907
+ */
908
+
909
+ _parseSubroutine ( async , userArgsCallback , argv , parseOptions ) {
910
+ this . _asyncParsing = async ;
911
+ const methodName = async ? 'parseAsync' : 'parse' ;
912
+ if ( ! PRODUCTION && this . parent ) {
913
+ console . warn ( `Called .${ methodName } () on subcommand '${ this . _name } '.
914
+ Call on top-level command instead` ) ;
915
+ }
916
+
917
+ const userArgs = this . _prepareUserArgs ( argv , parseOptions ) ;
918
+ return userArgsCallback ( userArgs ) ;
919
+ }
920
+
890
921
/**
891
922
* Parse `argv`, setting options and invoking commands when defined.
892
923
*
@@ -905,10 +936,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
905
936
*/
906
937
907
938
parse ( argv , parseOptions ) {
908
- const userArgs = this . _prepareUserArgs ( argv , parseOptions ) ;
909
- this . _parseCommand ( [ ] , userArgs ) ;
910
-
911
- return this ;
939
+ return this . _parseSubroutine ( false , ( userArgs ) => {
940
+ this . _parseCommand ( [ ] , userArgs ) ;
941
+ return this ;
942
+ } , argv , parseOptions ) ;
912
943
}
913
944
914
945
/**
@@ -931,10 +962,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
931
962
*/
932
963
933
964
async parseAsync ( argv , parseOptions ) {
934
- const userArgs = this . _prepareUserArgs ( argv , parseOptions ) ;
935
- await this . _parseCommand ( [ ] , userArgs ) ;
936
-
937
- return this ;
965
+ return this . _parseSubroutine ( true , async ( userArgs ) => {
966
+ await this . _parseCommand ( [ ] , userArgs ) ;
967
+ return this ;
968
+ } , argv , parseOptions ) ;
938
969
}
939
970
940
971
/**
@@ -1071,6 +1102,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1071
1102
_dispatchSubcommand ( commandName , operands , unknown ) {
1072
1103
const subCommand = this . _findCommand ( commandName ) ;
1073
1104
if ( ! subCommand ) this . help ( { error : true } ) ;
1105
+ subCommand . _asyncParsing = this . _asyncParsing ;
1074
1106
1075
1107
let hookResult ;
1076
1108
hookResult = this . _chainOrCallSubCommandHook ( hookResult , subCommand , 'preSubcommand' ) ;
@@ -1189,12 +1221,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
1189
1221
1190
1222
_chainOrCall ( promise , fn ) {
1191
1223
// thenable
1192
- if ( promise && promise . then && typeof promise . then === 'function' ) {
1224
+ if ( isThenable ( promise ) ) {
1193
1225
// already have a promise, chain callback
1194
1226
return promise . then ( ( ) => fn ( ) ) ;
1195
1227
}
1228
+
1196
1229
// callback might return a promise
1197
- return fn ( ) ;
1230
+ const result = fn ( ) ;
1231
+ if ( ! PRODUCTION && ! this . _asyncParsing && isThenable ( result ) ) {
1232
+ console . warn ( `.parse() is incompatible with async argParsers, hooks and actions.
1233
+ Use .parseAsync() instead.` ) ;
1234
+ }
1235
+ return result ;
1198
1236
}
1199
1237
1200
1238
/**
@@ -2193,4 +2231,14 @@ function getCommandAndParents(startCommand) {
2193
2231
return result ;
2194
2232
}
2195
2233
2234
+ /**
2235
+ * @param {* } value
2236
+ * @returns {boolean }
2237
+ * @api private
2238
+ */
2239
+
2240
+ function isThenable ( value ) {
2241
+ return typeof value ?. then === 'function' ;
2242
+ }
2243
+
2196
2244
exports . Command = Command ;
0 commit comments