@guanghechen/commander 4.4.0 → 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/lib/cjs/index.cjs +67 -45
- package/lib/esm/index.mjs +67 -45
- package/lib/types/index.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 4.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fix(commander): centralize option policy and enforce version flag semantics — subcommands with
|
|
8
|
+
their own `version` now correctly expose `--version`; commands without `version` reject
|
|
9
|
+
`--version` instead of treating it as a boolean option.
|
|
10
|
+
|
|
3
11
|
## 4.4.0
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
package/lib/cjs/index.cjs
CHANGED
|
@@ -368,30 +368,29 @@ class Command {
|
|
|
368
368
|
const routeResult = this.#route(processedArgv);
|
|
369
369
|
const { chain, remaining } = routeResult;
|
|
370
370
|
const leafCommand = chain[chain.length - 1];
|
|
371
|
-
const rootCommand = chain[0];
|
|
372
371
|
const tokenizeResult = tokenize(remaining, leafCommand.#getCommandPath());
|
|
373
372
|
const { optionTokens, restArgs } = tokenizeResult;
|
|
374
|
-
const
|
|
375
|
-
const
|
|
376
|
-
if (
|
|
377
|
-
const helpColor = leafCommand.#resolveHelpColorOption(optionTokens, envs);
|
|
373
|
+
const optionPolicyMap = this.#buildOptionPolicyMap(chain);
|
|
374
|
+
const leafPolicy = this.#mustGetOptionPolicy(optionPolicyMap, leafCommand);
|
|
375
|
+
if (leafPolicy.enableBuiltinHelp && this.#hasFlag(optionTokens, 'help', 'h')) {
|
|
376
|
+
const helpColor = leafCommand.#resolveHelpColorOption(optionTokens, envs, leafPolicy);
|
|
378
377
|
console.log(leafCommand.#formatHelpForDisplay({ color: helpColor }));
|
|
379
378
|
return;
|
|
380
379
|
}
|
|
381
|
-
if (
|
|
380
|
+
if (leafPolicy.enableBuiltinVersion) {
|
|
382
381
|
if (this.#hasFlag(optionTokens, 'version', 'V')) {
|
|
383
382
|
console.log(leafCommand.#version);
|
|
384
383
|
return;
|
|
385
384
|
}
|
|
386
385
|
}
|
|
387
|
-
const resolveResult = this.#resolve(chain, optionTokens);
|
|
386
|
+
const resolveResult = this.#resolve(chain, optionTokens, optionPolicyMap);
|
|
388
387
|
const ctx = {
|
|
389
388
|
cmd: leafCommand,
|
|
390
389
|
envs,
|
|
391
390
|
reporter: reporter$1 ?? this.#reporter ?? new reporter.Reporter(),
|
|
392
391
|
argv,
|
|
393
392
|
};
|
|
394
|
-
const parseResult = this.#parse(chain, resolveResult, ctx, restArgs);
|
|
393
|
+
const parseResult = this.#parse(chain, resolveResult, optionPolicyMap, ctx, restArgs);
|
|
395
394
|
const actionParams = {
|
|
396
395
|
ctx: parseResult.ctx,
|
|
397
396
|
opts: parseResult.opts,
|
|
@@ -402,7 +401,7 @@ class Command {
|
|
|
402
401
|
await leafCommand.#runAction(actionParams);
|
|
403
402
|
}
|
|
404
403
|
else if (leafCommand.#subcommandsList.length > 0) {
|
|
405
|
-
const helpColor = leafCommand.#resolveHelpColorOption(optionTokens, envs);
|
|
404
|
+
const helpColor = leafCommand.#resolveHelpColorOption(optionTokens, envs, leafPolicy);
|
|
406
405
|
console.log(leafCommand.#formatHelpForDisplay({ color: helpColor }));
|
|
407
406
|
}
|
|
408
407
|
else {
|
|
@@ -426,14 +425,15 @@ class Command {
|
|
|
426
425
|
const leafCommand = chain[chain.length - 1];
|
|
427
426
|
const tokenizeResult = tokenize(remaining, leafCommand.#getCommandPath());
|
|
428
427
|
const { optionTokens, restArgs } = tokenizeResult;
|
|
429
|
-
const
|
|
428
|
+
const optionPolicyMap = this.#buildOptionPolicyMap(chain);
|
|
429
|
+
const resolveResult = this.#resolve(chain, optionTokens, optionPolicyMap);
|
|
430
430
|
const ctx = {
|
|
431
431
|
cmd: leafCommand,
|
|
432
432
|
envs,
|
|
433
433
|
reporter: reporter$1 ?? this.#reporter ?? new reporter.Reporter(),
|
|
434
434
|
argv,
|
|
435
435
|
};
|
|
436
|
-
return this.#parse(chain, resolveResult, ctx, restArgs);
|
|
436
|
+
return this.#parse(chain, resolveResult, optionPolicyMap, ctx, restArgs);
|
|
437
437
|
}
|
|
438
438
|
formatHelp() {
|
|
439
439
|
return this.#renderHelpPlain(this.#buildHelpData());
|
|
@@ -450,7 +450,7 @@ class Command {
|
|
|
450
450
|
return color && process.stdout.isTTY === true;
|
|
451
451
|
}
|
|
452
452
|
#buildHelpData() {
|
|
453
|
-
const allOptions = this.#
|
|
453
|
+
const allOptions = this.#resolveOptionPolicy().mergedOptions;
|
|
454
454
|
const commandPath = this.#getCommandPath();
|
|
455
455
|
let usage = `Usage: ${commandPath}`;
|
|
456
456
|
if (allOptions.length > 0)
|
|
@@ -587,7 +587,7 @@ class Command {
|
|
|
587
587
|
return lines.join('\n');
|
|
588
588
|
}
|
|
589
589
|
getCompletionMeta() {
|
|
590
|
-
const allOptions = this.#
|
|
590
|
+
const allOptions = this.#resolveOptionPolicy().mergedOptions;
|
|
591
591
|
const options = [];
|
|
592
592
|
for (const opt of allOptions) {
|
|
593
593
|
options.push({
|
|
@@ -675,14 +675,14 @@ class Command {
|
|
|
675
675
|
}
|
|
676
676
|
return { chain, remaining: argv.slice(idx) };
|
|
677
677
|
}
|
|
678
|
-
#resolve(chain, tokens) {
|
|
678
|
+
#resolve(chain, tokens, optionPolicyMap) {
|
|
679
679
|
const consumedTokens = new Map();
|
|
680
680
|
let remaining = [...tokens];
|
|
681
681
|
const shadowed = new Set();
|
|
682
682
|
for (let i = chain.length - 1; i >= 0; i--) {
|
|
683
683
|
const cmd = chain[i];
|
|
684
|
-
const
|
|
685
|
-
const result = cmd.#shift(remaining, shadowed,
|
|
684
|
+
const policy = this.#mustGetOptionPolicy(optionPolicyMap, cmd);
|
|
685
|
+
const result = cmd.#shift(remaining, shadowed, policy.mergedOptions);
|
|
686
686
|
consumedTokens.set(cmd, result.consumed);
|
|
687
687
|
remaining = result.remaining;
|
|
688
688
|
for (const opt of cmd.#options) {
|
|
@@ -699,8 +699,7 @@ class Command {
|
|
|
699
699
|
}
|
|
700
700
|
return { consumedTokens, argTokens };
|
|
701
701
|
}
|
|
702
|
-
#shift(tokens, shadowed,
|
|
703
|
-
const allOptions = this.#getMergedOptions(includeVersion);
|
|
702
|
+
#shift(tokens, shadowed, allOptions) {
|
|
704
703
|
const effectiveOptions = allOptions.filter(o => !shadowed.has(o.long));
|
|
705
704
|
const optionByLong = new Map();
|
|
706
705
|
const optionByShort = new Map();
|
|
@@ -768,18 +767,17 @@ class Command {
|
|
|
768
767
|
}
|
|
769
768
|
return { consumed, remaining };
|
|
770
769
|
}
|
|
771
|
-
#parse(chain, resolveResult, ctx, restArgs) {
|
|
770
|
+
#parse(chain, resolveResult, optionPolicyMap, ctx, restArgs) {
|
|
772
771
|
const { consumedTokens, argTokens } = resolveResult;
|
|
773
772
|
const leafCommand = chain[chain.length - 1];
|
|
774
|
-
this.#validateMergedShortOptions(chain);
|
|
773
|
+
this.#validateMergedShortOptions(chain, optionPolicyMap);
|
|
775
774
|
const optsMap = new Map();
|
|
776
|
-
for (
|
|
777
|
-
const
|
|
778
|
-
const includeVersion = i === 0;
|
|
775
|
+
for (const cmd of chain) {
|
|
776
|
+
const policy = this.#mustGetOptionPolicy(optionPolicyMap, cmd);
|
|
779
777
|
const tokens = consumedTokens.get(cmd) ?? [];
|
|
780
|
-
const opts = cmd.#parseOptions(tokens,
|
|
778
|
+
const opts = cmd.#parseOptions(tokens, policy.mergedOptions, ctx.envs);
|
|
781
779
|
optsMap.set(cmd, opts);
|
|
782
|
-
for (const opt of
|
|
780
|
+
for (const opt of policy.mergedOptions) {
|
|
783
781
|
if (opt.apply && opts[opt.long] !== undefined) {
|
|
784
782
|
opt.apply(opts[opt.long], ctx);
|
|
785
783
|
}
|
|
@@ -793,8 +791,7 @@ class Command {
|
|
|
793
791
|
const { args, rawArgs } = leafCommand.#parseArguments(rawArgStrings);
|
|
794
792
|
return { ctx, opts: mergedOpts, args, rawArgs };
|
|
795
793
|
}
|
|
796
|
-
#parseOptions(tokens,
|
|
797
|
-
const allOptions = this.#getMergedOptions(includeVersion);
|
|
794
|
+
#parseOptions(tokens, allOptions, envs) {
|
|
798
795
|
const opts = {};
|
|
799
796
|
let sawColorToken = false;
|
|
800
797
|
for (const opt of allOptions) {
|
|
@@ -976,22 +973,30 @@ class Command {
|
|
|
976
973
|
}
|
|
977
974
|
return raw;
|
|
978
975
|
}
|
|
979
|
-
#
|
|
976
|
+
#hasUserOption(long) {
|
|
977
|
+
return this.#options.some(option => option.long === long);
|
|
978
|
+
}
|
|
979
|
+
#canUseBuiltinVersion() {
|
|
980
|
+
return this.#version !== undefined;
|
|
981
|
+
}
|
|
982
|
+
#resolveOptionPolicy() {
|
|
980
983
|
const optionMap = new Map();
|
|
981
|
-
const hasUserColor = this.#
|
|
982
|
-
const hasUserHelp = this.#
|
|
983
|
-
const hasUserVersion = this.#
|
|
984
|
-
const hasUserLogLevel = this.#
|
|
985
|
-
const hasUserSilent = this.#
|
|
986
|
-
const hasUserLogDate = this.#
|
|
987
|
-
const hasUserLogColorful = this.#
|
|
984
|
+
const hasUserColor = this.#hasUserOption('color');
|
|
985
|
+
const hasUserHelp = this.#hasUserOption('help');
|
|
986
|
+
const hasUserVersion = this.#hasUserOption('version');
|
|
987
|
+
const hasUserLogLevel = this.#hasUserOption('logLevel');
|
|
988
|
+
const hasUserSilent = this.#hasUserOption('silent');
|
|
989
|
+
const hasUserLogDate = this.#hasUserOption('logDate');
|
|
990
|
+
const hasUserLogColorful = this.#hasUserOption('logColorful');
|
|
991
|
+
const enableBuiltinHelp = !hasUserHelp;
|
|
992
|
+
const enableBuiltinVersion = !hasUserVersion && this.#canUseBuiltinVersion();
|
|
988
993
|
if (this.#builtin.option.color && !hasUserColor) {
|
|
989
994
|
optionMap.set('color', BUILTIN_COLOR_OPTION);
|
|
990
995
|
}
|
|
991
|
-
if (
|
|
996
|
+
if (enableBuiltinHelp) {
|
|
992
997
|
optionMap.set('help', BUILTIN_HELP_OPTION);
|
|
993
998
|
}
|
|
994
|
-
if (
|
|
999
|
+
if (enableBuiltinVersion) {
|
|
995
1000
|
optionMap.set('version', BUILTIN_VERSION_OPTION);
|
|
996
1001
|
}
|
|
997
1002
|
if (this.#builtin.option.logLevel && !hasUserLogLevel) {
|
|
@@ -1009,14 +1014,31 @@ class Command {
|
|
|
1009
1014
|
for (const opt of this.#options) {
|
|
1010
1015
|
optionMap.set(opt.long, opt);
|
|
1011
1016
|
}
|
|
1012
|
-
return
|
|
1017
|
+
return {
|
|
1018
|
+
mergedOptions: Array.from(optionMap.values()),
|
|
1019
|
+
enableBuiltinHelp,
|
|
1020
|
+
enableBuiltinVersion,
|
|
1021
|
+
};
|
|
1013
1022
|
}
|
|
1014
|
-
#
|
|
1023
|
+
#buildOptionPolicyMap(chain) {
|
|
1024
|
+
const optionPolicyMap = new Map();
|
|
1025
|
+
for (const cmd of chain) {
|
|
1026
|
+
optionPolicyMap.set(cmd, cmd.#resolveOptionPolicy());
|
|
1027
|
+
}
|
|
1028
|
+
return optionPolicyMap;
|
|
1029
|
+
}
|
|
1030
|
+
#mustGetOptionPolicy(optionPolicyMap, cmd) {
|
|
1031
|
+
const policy = optionPolicyMap.get(cmd);
|
|
1032
|
+
if (policy !== undefined) {
|
|
1033
|
+
return policy;
|
|
1034
|
+
}
|
|
1035
|
+
throw new CommanderError('ConfigurationError', `missing option policy for command "${cmd.#getCommandPath()}"`, this.#getCommandPath());
|
|
1036
|
+
}
|
|
1037
|
+
#validateMergedShortOptions(chain, optionPolicyMap) {
|
|
1015
1038
|
const mergedByLong = new Map();
|
|
1016
|
-
for (
|
|
1017
|
-
const
|
|
1018
|
-
const
|
|
1019
|
-
for (const opt of cmd.#getMergedOptions(includeVersion)) {
|
|
1039
|
+
for (const cmd of chain) {
|
|
1040
|
+
const policy = this.#mustGetOptionPolicy(optionPolicyMap, cmd);
|
|
1041
|
+
for (const opt of policy.mergedOptions) {
|
|
1020
1042
|
mergedByLong.set(opt.long, opt);
|
|
1021
1043
|
}
|
|
1022
1044
|
}
|
|
@@ -1112,8 +1134,8 @@ class Command {
|
|
|
1112
1134
|
process.exit(1);
|
|
1113
1135
|
}
|
|
1114
1136
|
}
|
|
1115
|
-
#resolveHelpColorOption(tokens, envs) {
|
|
1116
|
-
const colorOption =
|
|
1137
|
+
#resolveHelpColorOption(tokens, envs, policy = this.#resolveOptionPolicy()) {
|
|
1138
|
+
const colorOption = policy.mergedOptions.find(opt => opt.long === 'color');
|
|
1117
1139
|
let color = !isNoColorEnabled(envs);
|
|
1118
1140
|
if (!colorOption || colorOption.type !== 'boolean' || colorOption.args !== 'none') {
|
|
1119
1141
|
return color;
|
package/lib/esm/index.mjs
CHANGED
|
@@ -346,30 +346,29 @@ class Command {
|
|
|
346
346
|
const routeResult = this.#route(processedArgv);
|
|
347
347
|
const { chain, remaining } = routeResult;
|
|
348
348
|
const leafCommand = chain[chain.length - 1];
|
|
349
|
-
const rootCommand = chain[0];
|
|
350
349
|
const tokenizeResult = tokenize(remaining, leafCommand.#getCommandPath());
|
|
351
350
|
const { optionTokens, restArgs } = tokenizeResult;
|
|
352
|
-
const
|
|
353
|
-
const
|
|
354
|
-
if (
|
|
355
|
-
const helpColor = leafCommand.#resolveHelpColorOption(optionTokens, envs);
|
|
351
|
+
const optionPolicyMap = this.#buildOptionPolicyMap(chain);
|
|
352
|
+
const leafPolicy = this.#mustGetOptionPolicy(optionPolicyMap, leafCommand);
|
|
353
|
+
if (leafPolicy.enableBuiltinHelp && this.#hasFlag(optionTokens, 'help', 'h')) {
|
|
354
|
+
const helpColor = leafCommand.#resolveHelpColorOption(optionTokens, envs, leafPolicy);
|
|
356
355
|
console.log(leafCommand.#formatHelpForDisplay({ color: helpColor }));
|
|
357
356
|
return;
|
|
358
357
|
}
|
|
359
|
-
if (
|
|
358
|
+
if (leafPolicy.enableBuiltinVersion) {
|
|
360
359
|
if (this.#hasFlag(optionTokens, 'version', 'V')) {
|
|
361
360
|
console.log(leafCommand.#version);
|
|
362
361
|
return;
|
|
363
362
|
}
|
|
364
363
|
}
|
|
365
|
-
const resolveResult = this.#resolve(chain, optionTokens);
|
|
364
|
+
const resolveResult = this.#resolve(chain, optionTokens, optionPolicyMap);
|
|
366
365
|
const ctx = {
|
|
367
366
|
cmd: leafCommand,
|
|
368
367
|
envs,
|
|
369
368
|
reporter: reporter ?? this.#reporter ?? new Reporter(),
|
|
370
369
|
argv,
|
|
371
370
|
};
|
|
372
|
-
const parseResult = this.#parse(chain, resolveResult, ctx, restArgs);
|
|
371
|
+
const parseResult = this.#parse(chain, resolveResult, optionPolicyMap, ctx, restArgs);
|
|
373
372
|
const actionParams = {
|
|
374
373
|
ctx: parseResult.ctx,
|
|
375
374
|
opts: parseResult.opts,
|
|
@@ -380,7 +379,7 @@ class Command {
|
|
|
380
379
|
await leafCommand.#runAction(actionParams);
|
|
381
380
|
}
|
|
382
381
|
else if (leafCommand.#subcommandsList.length > 0) {
|
|
383
|
-
const helpColor = leafCommand.#resolveHelpColorOption(optionTokens, envs);
|
|
382
|
+
const helpColor = leafCommand.#resolveHelpColorOption(optionTokens, envs, leafPolicy);
|
|
384
383
|
console.log(leafCommand.#formatHelpForDisplay({ color: helpColor }));
|
|
385
384
|
}
|
|
386
385
|
else {
|
|
@@ -404,14 +403,15 @@ class Command {
|
|
|
404
403
|
const leafCommand = chain[chain.length - 1];
|
|
405
404
|
const tokenizeResult = tokenize(remaining, leafCommand.#getCommandPath());
|
|
406
405
|
const { optionTokens, restArgs } = tokenizeResult;
|
|
407
|
-
const
|
|
406
|
+
const optionPolicyMap = this.#buildOptionPolicyMap(chain);
|
|
407
|
+
const resolveResult = this.#resolve(chain, optionTokens, optionPolicyMap);
|
|
408
408
|
const ctx = {
|
|
409
409
|
cmd: leafCommand,
|
|
410
410
|
envs,
|
|
411
411
|
reporter: reporter ?? this.#reporter ?? new Reporter(),
|
|
412
412
|
argv,
|
|
413
413
|
};
|
|
414
|
-
return this.#parse(chain, resolveResult, ctx, restArgs);
|
|
414
|
+
return this.#parse(chain, resolveResult, optionPolicyMap, ctx, restArgs);
|
|
415
415
|
}
|
|
416
416
|
formatHelp() {
|
|
417
417
|
return this.#renderHelpPlain(this.#buildHelpData());
|
|
@@ -428,7 +428,7 @@ class Command {
|
|
|
428
428
|
return color && process.stdout.isTTY === true;
|
|
429
429
|
}
|
|
430
430
|
#buildHelpData() {
|
|
431
|
-
const allOptions = this.#
|
|
431
|
+
const allOptions = this.#resolveOptionPolicy().mergedOptions;
|
|
432
432
|
const commandPath = this.#getCommandPath();
|
|
433
433
|
let usage = `Usage: ${commandPath}`;
|
|
434
434
|
if (allOptions.length > 0)
|
|
@@ -565,7 +565,7 @@ class Command {
|
|
|
565
565
|
return lines.join('\n');
|
|
566
566
|
}
|
|
567
567
|
getCompletionMeta() {
|
|
568
|
-
const allOptions = this.#
|
|
568
|
+
const allOptions = this.#resolveOptionPolicy().mergedOptions;
|
|
569
569
|
const options = [];
|
|
570
570
|
for (const opt of allOptions) {
|
|
571
571
|
options.push({
|
|
@@ -653,14 +653,14 @@ class Command {
|
|
|
653
653
|
}
|
|
654
654
|
return { chain, remaining: argv.slice(idx) };
|
|
655
655
|
}
|
|
656
|
-
#resolve(chain, tokens) {
|
|
656
|
+
#resolve(chain, tokens, optionPolicyMap) {
|
|
657
657
|
const consumedTokens = new Map();
|
|
658
658
|
let remaining = [...tokens];
|
|
659
659
|
const shadowed = new Set();
|
|
660
660
|
for (let i = chain.length - 1; i >= 0; i--) {
|
|
661
661
|
const cmd = chain[i];
|
|
662
|
-
const
|
|
663
|
-
const result = cmd.#shift(remaining, shadowed,
|
|
662
|
+
const policy = this.#mustGetOptionPolicy(optionPolicyMap, cmd);
|
|
663
|
+
const result = cmd.#shift(remaining, shadowed, policy.mergedOptions);
|
|
664
664
|
consumedTokens.set(cmd, result.consumed);
|
|
665
665
|
remaining = result.remaining;
|
|
666
666
|
for (const opt of cmd.#options) {
|
|
@@ -677,8 +677,7 @@ class Command {
|
|
|
677
677
|
}
|
|
678
678
|
return { consumedTokens, argTokens };
|
|
679
679
|
}
|
|
680
|
-
#shift(tokens, shadowed,
|
|
681
|
-
const allOptions = this.#getMergedOptions(includeVersion);
|
|
680
|
+
#shift(tokens, shadowed, allOptions) {
|
|
682
681
|
const effectiveOptions = allOptions.filter(o => !shadowed.has(o.long));
|
|
683
682
|
const optionByLong = new Map();
|
|
684
683
|
const optionByShort = new Map();
|
|
@@ -746,18 +745,17 @@ class Command {
|
|
|
746
745
|
}
|
|
747
746
|
return { consumed, remaining };
|
|
748
747
|
}
|
|
749
|
-
#parse(chain, resolveResult, ctx, restArgs) {
|
|
748
|
+
#parse(chain, resolveResult, optionPolicyMap, ctx, restArgs) {
|
|
750
749
|
const { consumedTokens, argTokens } = resolveResult;
|
|
751
750
|
const leafCommand = chain[chain.length - 1];
|
|
752
|
-
this.#validateMergedShortOptions(chain);
|
|
751
|
+
this.#validateMergedShortOptions(chain, optionPolicyMap);
|
|
753
752
|
const optsMap = new Map();
|
|
754
|
-
for (
|
|
755
|
-
const
|
|
756
|
-
const includeVersion = i === 0;
|
|
753
|
+
for (const cmd of chain) {
|
|
754
|
+
const policy = this.#mustGetOptionPolicy(optionPolicyMap, cmd);
|
|
757
755
|
const tokens = consumedTokens.get(cmd) ?? [];
|
|
758
|
-
const opts = cmd.#parseOptions(tokens,
|
|
756
|
+
const opts = cmd.#parseOptions(tokens, policy.mergedOptions, ctx.envs);
|
|
759
757
|
optsMap.set(cmd, opts);
|
|
760
|
-
for (const opt of
|
|
758
|
+
for (const opt of policy.mergedOptions) {
|
|
761
759
|
if (opt.apply && opts[opt.long] !== undefined) {
|
|
762
760
|
opt.apply(opts[opt.long], ctx);
|
|
763
761
|
}
|
|
@@ -771,8 +769,7 @@ class Command {
|
|
|
771
769
|
const { args, rawArgs } = leafCommand.#parseArguments(rawArgStrings);
|
|
772
770
|
return { ctx, opts: mergedOpts, args, rawArgs };
|
|
773
771
|
}
|
|
774
|
-
#parseOptions(tokens,
|
|
775
|
-
const allOptions = this.#getMergedOptions(includeVersion);
|
|
772
|
+
#parseOptions(tokens, allOptions, envs) {
|
|
776
773
|
const opts = {};
|
|
777
774
|
let sawColorToken = false;
|
|
778
775
|
for (const opt of allOptions) {
|
|
@@ -954,22 +951,30 @@ class Command {
|
|
|
954
951
|
}
|
|
955
952
|
return raw;
|
|
956
953
|
}
|
|
957
|
-
#
|
|
954
|
+
#hasUserOption(long) {
|
|
955
|
+
return this.#options.some(option => option.long === long);
|
|
956
|
+
}
|
|
957
|
+
#canUseBuiltinVersion() {
|
|
958
|
+
return this.#version !== undefined;
|
|
959
|
+
}
|
|
960
|
+
#resolveOptionPolicy() {
|
|
958
961
|
const optionMap = new Map();
|
|
959
|
-
const hasUserColor = this.#
|
|
960
|
-
const hasUserHelp = this.#
|
|
961
|
-
const hasUserVersion = this.#
|
|
962
|
-
const hasUserLogLevel = this.#
|
|
963
|
-
const hasUserSilent = this.#
|
|
964
|
-
const hasUserLogDate = this.#
|
|
965
|
-
const hasUserLogColorful = this.#
|
|
962
|
+
const hasUserColor = this.#hasUserOption('color');
|
|
963
|
+
const hasUserHelp = this.#hasUserOption('help');
|
|
964
|
+
const hasUserVersion = this.#hasUserOption('version');
|
|
965
|
+
const hasUserLogLevel = this.#hasUserOption('logLevel');
|
|
966
|
+
const hasUserSilent = this.#hasUserOption('silent');
|
|
967
|
+
const hasUserLogDate = this.#hasUserOption('logDate');
|
|
968
|
+
const hasUserLogColorful = this.#hasUserOption('logColorful');
|
|
969
|
+
const enableBuiltinHelp = !hasUserHelp;
|
|
970
|
+
const enableBuiltinVersion = !hasUserVersion && this.#canUseBuiltinVersion();
|
|
966
971
|
if (this.#builtin.option.color && !hasUserColor) {
|
|
967
972
|
optionMap.set('color', BUILTIN_COLOR_OPTION);
|
|
968
973
|
}
|
|
969
|
-
if (
|
|
974
|
+
if (enableBuiltinHelp) {
|
|
970
975
|
optionMap.set('help', BUILTIN_HELP_OPTION);
|
|
971
976
|
}
|
|
972
|
-
if (
|
|
977
|
+
if (enableBuiltinVersion) {
|
|
973
978
|
optionMap.set('version', BUILTIN_VERSION_OPTION);
|
|
974
979
|
}
|
|
975
980
|
if (this.#builtin.option.logLevel && !hasUserLogLevel) {
|
|
@@ -987,14 +992,31 @@ class Command {
|
|
|
987
992
|
for (const opt of this.#options) {
|
|
988
993
|
optionMap.set(opt.long, opt);
|
|
989
994
|
}
|
|
990
|
-
return
|
|
995
|
+
return {
|
|
996
|
+
mergedOptions: Array.from(optionMap.values()),
|
|
997
|
+
enableBuiltinHelp,
|
|
998
|
+
enableBuiltinVersion,
|
|
999
|
+
};
|
|
991
1000
|
}
|
|
992
|
-
#
|
|
1001
|
+
#buildOptionPolicyMap(chain) {
|
|
1002
|
+
const optionPolicyMap = new Map();
|
|
1003
|
+
for (const cmd of chain) {
|
|
1004
|
+
optionPolicyMap.set(cmd, cmd.#resolveOptionPolicy());
|
|
1005
|
+
}
|
|
1006
|
+
return optionPolicyMap;
|
|
1007
|
+
}
|
|
1008
|
+
#mustGetOptionPolicy(optionPolicyMap, cmd) {
|
|
1009
|
+
const policy = optionPolicyMap.get(cmd);
|
|
1010
|
+
if (policy !== undefined) {
|
|
1011
|
+
return policy;
|
|
1012
|
+
}
|
|
1013
|
+
throw new CommanderError('ConfigurationError', `missing option policy for command "${cmd.#getCommandPath()}"`, this.#getCommandPath());
|
|
1014
|
+
}
|
|
1015
|
+
#validateMergedShortOptions(chain, optionPolicyMap) {
|
|
993
1016
|
const mergedByLong = new Map();
|
|
994
|
-
for (
|
|
995
|
-
const
|
|
996
|
-
const
|
|
997
|
-
for (const opt of cmd.#getMergedOptions(includeVersion)) {
|
|
1017
|
+
for (const cmd of chain) {
|
|
1018
|
+
const policy = this.#mustGetOptionPolicy(optionPolicyMap, cmd);
|
|
1019
|
+
for (const opt of policy.mergedOptions) {
|
|
998
1020
|
mergedByLong.set(opt.long, opt);
|
|
999
1021
|
}
|
|
1000
1022
|
}
|
|
@@ -1090,8 +1112,8 @@ class Command {
|
|
|
1090
1112
|
process.exit(1);
|
|
1091
1113
|
}
|
|
1092
1114
|
}
|
|
1093
|
-
#resolveHelpColorOption(tokens, envs) {
|
|
1094
|
-
const colorOption =
|
|
1115
|
+
#resolveHelpColorOption(tokens, envs, policy = this.#resolveOptionPolicy()) {
|
|
1116
|
+
const colorOption = policy.mergedOptions.find(opt => opt.long === 'color');
|
|
1095
1117
|
let color = !isNoColorEnabled(envs);
|
|
1096
1118
|
if (!colorOption || colorOption.type !== 'boolean' || colorOption.args !== 'none') {
|
|
1097
1119
|
return color;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -129,7 +129,7 @@ interface ICommandConfig {
|
|
|
129
129
|
name?: string;
|
|
130
130
|
/** Command description */
|
|
131
131
|
desc: string;
|
|
132
|
-
/** Version (for
|
|
132
|
+
/** Version (for built-in --version on this command) */
|
|
133
133
|
version?: string;
|
|
134
134
|
/** Built-in features configuration */
|
|
135
135
|
builtin?: boolean | ICommandBuiltinConfig;
|