( value: string, cursor: TokenCursor, context: CommandContext, state: ParseState )
| 779 | * - Report error if command expects subcommand but got unknown value |
| 780 | */ |
| 781 | const resolveFirstValue = ( |
| 782 | value: string, |
| 783 | cursor: TokenCursor, |
| 784 | context: CommandContext, |
| 785 | state: ParseState |
| 786 | ): FirstValueResult => { |
| 787 | const { command, commandPath, inheritedFlagRegistry, localFlagNames } = context |
| 788 | const subIndex = buildSubcommandIndex(command.subcommands) |
| 789 | const sub = subIndex.get(value) |
| 790 | |
| 791 | if (sub) { |
| 792 | const selectedPath = [...commandPath, sub.name] |
| 793 | |
| 794 | // Local flags are not inherited by subcommands. |
| 795 | const parentFlags = state.flags.snapshot() |
| 796 | for (const localFlagName of localFlagNames) { |
| 797 | const values = parentFlags[localFlagName] |
| 798 | if (values !== undefined && values.length > 0) { |
| 799 | state.errors.push( |
| 800 | new CliError.UnrecognizedOption({ |
| 801 | option: `--${localFlagName}`, |
| 802 | suggestions: [], |
| 803 | command: selectedPath |
| 804 | }) |
| 805 | ) |
| 806 | } |
| 807 | } |
| 808 | |
| 809 | // npm-style: inherited parent flags can appear after subcommand name |
| 810 | const tail = consumeKnownFlags(cursor.rest(), inheritedFlagRegistry) |
| 811 | state.flags.merge(tail.flagMap) |
| 812 | state.errors.push(...tail.errors) |
| 813 | |
| 814 | return { |
| 815 | _tag: "Subcommand", |
| 816 | result: { |
| 817 | _tag: "Sub", |
| 818 | flags: state.flags.snapshot(), |
| 819 | sub, |
| 820 | childTokens: tail.remainder, |
| 821 | errors: state.errors |
| 822 | } |
| 823 | } |
| 824 | } |
| 825 | |
| 826 | // Not a subcommand. Check if this looks like a typo. |
| 827 | const expectsArgs = toImpl(command).config.arguments.length > 0 |
| 828 | if (!expectsArgs && subIndex.size > 0) { |
| 829 | // Exclude unlisted subcommands so a typo cannot reveal a subcommand name |
| 830 | // that was intentionally kept out of --help. Unlisted commands still |
| 831 | // resolve via subIndex when invoked by exact name. |
| 832 | const visibleKeys: Array<string> = [] |
| 833 | for (const [key, sub] of subIndex) { |
| 834 | if (!sub.unlisted) visibleKeys.push(key) |
| 835 | } |
| 836 | const suggestions = suggest(value, visibleKeys) |
| 837 | state.errors.push( |
| 838 | new CliError.UnknownSubcommand({ |
no test coverage detected