| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -135,6 +135,8 @@ export interface HydratedNamespaceMetadata<C extends ICommand<C, N, M, I, O>, N | |||
| 135 | 135 | export interface IExecutor<C extends ICommand<C, N, M, I, O>, N extends INamespace<C, N, M, I, O>, M extends CommandMetadata<I, O>, I extends CommandMetadataInput, O extends CommandMetadataOption> { | |
| 136 | 136 | readonly namespace: N; | |
| 137 | 137 | ||
| 138 | + locate(argv: readonly string[], options?: NamespaceLocateOptions): Promise<NamespaceLocateResult<C, N, M, I, O>>; | ||
| 139 | + execute(location: NamespaceLocateResult<C, N, M, I, O>): Promise<void>; | ||
| 138 | 140 | execute(argv: readonly string[], env: NodeJS.ProcessEnv): Promise<void>; | |
| 139 | 141 | run(command: C, cmdargs: readonly string[], runinfo?: Partial<CommandInstanceInfo<C, N, M, I, O>>): Promise<void>; | |
| 140 | 142 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,7 @@ | |||
| 1 | 1 | import { EventEmitter } from 'events'; | |
| 2 | 2 | import * as lodash from 'lodash'; | |
| 3 | 3 | ||
| 4 | - import { CommandInstanceInfo, CommandMetadata, CommandMetadataInput, CommandMetadataOption, ICommand, IExecutor, INamespace, NamespaceLocateResult } from '../definitions'; | ||
| 4 | + import { CommandInstanceInfo, CommandMetadata, CommandMetadataInput, CommandMetadataOption, ICommand, IExecutor, INamespace, NamespaceLocateOptions, NamespaceLocateResult } from '../definitions'; | ||
| 5 | 5 | import { BaseError, InputValidationError } from '../errors'; | |
| 6 | 6 | import { isCommand, isNamespace } from '../guards'; | |
| 7 | 7 | ||
@@ -20,11 +20,23 @@ export const EXECUTOR_OPS: ExecutorOperations = Object.freeze({ | |||
| 20 | 20 | RPC: '📡', | |
| 21 | 21 | }); | |
| 22 | 22 | ||
| 23 | + export const HELP_FLAGS = ['--help', '-?']; | ||
| 24 | + | ||
| 23 | 25 | export abstract class AbstractExecutor<C extends ICommand<C, N, M, I, O>, N extends INamespace<C, N, M, I, O>, M extends CommandMetadata<I, O>, I extends CommandMetadataInput, O extends CommandMetadataOption> extends EventEmitter implements IExecutor<C, N, M, I, O> { | |
| 24 | 26 | abstract readonly namespace: N; | |
| 25 | 27 | ||
| 28 | + abstract locate(argv: readonly string[], options?: NamespaceLocateOptions): Promise<NamespaceLocateResult<C, N, M, I, O>>; | ||
| 29 | + abstract execute(location: NamespaceLocateResult<C, N, M, I, O>): Promise<void>; | ||
| 26 | 30 | abstract execute(argv: readonly string[], env: NodeJS.ProcessEnv): Promise<void>; | |
| 27 | 31 | abstract run(command: C, cmdargs: readonly string[], runinfo?: Partial<CommandInstanceInfo<C, N, M, I, O>>): Promise<void>; | |
| 32 | + | ||
| 33 | + async resolveExecuteInput(argvOrLocation: readonly string[] | NamespaceLocateResult<C, N, M, I, O>): Promise<[NamespaceLocateResult<C, N, M, I, O>, string[]]> { | ||
| 34 | + if ('obj' in argvOrLocation) { | ||
| 35 | + return [argvOrLocation, [...argvOrLocation.args]]; | ||
| 36 | + } else { | ||
| 37 | + return [await this.locate(argvOrLocation), [...argvOrLocation]]; | ||
| 38 | + } | ||
| 39 | + } | ||
| 28 | 40 | } | |
| 29 | 41 | ||
| 30 | 42 | export interface BaseExecutorFormatHelpOptions { | |
@@ -57,6 +69,20 @@ export class BaseExecutor<C extends ICommand<C, N, M, I, O>, N extends INamespac | |||
| 57 | 69 | this.stderr = stderr ? stderr : process.stderr; | |
| 58 | 70 | } | |
| 59 | 71 | ||
| 72 | + /** | ||
| 73 | + * Locate a command or namespace given an array of positional arguments | ||
| 74 | + * (argv). | ||
| 75 | + * | ||
| 76 | + * @param argv Command arguments sliced to the root for the namespace of this | ||
| 77 | + * executor. Usually, this means `process.argv.slice(2)`. | ||
| 78 | + */ | ||
| 79 | + async locate(argv: readonly string[]): Promise<NamespaceLocateResult<C, N, M, I, O>> { | ||
| 80 | + const parsedArgs = stripOptions(argv, {}); | ||
| 81 | + const location = await this.namespace.locate(parsedArgs); | ||
| 82 | + | ||
| 83 | + return location; | ||
| 84 | + } | ||
| 85 | + | ||
| 60 | 86 | /** | |
| 61 | 87 | * Locate and execute a command given an array of positional command | |
| 62 | 88 | * arguments (argv) and a set of environment variables. | |
@@ -68,32 +94,21 @@ export class BaseExecutor<C extends ICommand<C, N, M, I, O>, N extends INamespac | |||
| 68 | 94 | * executor. Usually, this means `process.argv.slice(2)`. | |
| 69 | 95 | * @param env Environment variables for this execution. | |
| 70 | 96 | */ | |
| 71 | - async execute(argv: readonly string[], env: NodeJS.ProcessEnv): Promise<void> { | ||
| 72 | - if (argv[0] === EXECUTOR_OPS.RPC) { | ||
| 97 | + async execute(argvOrLocation: readonly string[] | NamespaceLocateResult<C, N, M, I, O>, env?: NodeJS.ProcessEnv): Promise<void> { | ||
| 98 | + if (Array.isArray(argvOrLocation) && argvOrLocation[0] === EXECUTOR_OPS.RPC) { | ||
| 73 | 99 | return this.rpc(); | |
| 74 | 100 | } | |
| 75 | 101 | ||
| 76 | - const parsedArgs = stripOptions(argv, { includeSeparated: false }); | ||
| 77 | - const location = await this.namespace.locate(parsedArgs); | ||
| 102 | + const [ location, argv ] = await this.resolveExecuteInput(argvOrLocation); | ||
| 78 | 103 | ||
| 79 | - if (argv.find(arg => arg === '--help' || arg === '-?') || isNamespace(location.obj)) { | ||
| 80 | - const cmdoptions = parseArgs([...argv]); | ||
| 104 | + if (lodash.intersection(HELP_FLAGS, argv).length > 0 || isNamespace(location.obj)) { | ||
| 105 | + const cmdoptions = parseArgs(argv); | ||
| 81 | 106 | this.stdout.write(await this.formatHelp(location, { format: cmdoptions['json'] ? 'json' : 'terminal' })); | |
| 82 | 107 | } else { | |
| 83 | 108 | const cmd = location.obj; | |
| 84 | - const cmdargs = lodash.drop(argv, location.path.length - 1); | ||
| 85 | - | ||
| 86 | - try { | ||
| 87 | - await this.run(cmd, cmdargs, { location, env, executor: this }); | ||
| 88 | - } catch (e) { | ||
| 89 | - if (e instanceof BaseError) { | ||
| 90 | - this.stderr.write(`Error: ${e.message}`); | ||
| 91 | - process.exitCode = typeof e.exitCode === 'undefined' ? 1 : e.exitCode; | ||
| 92 | - return; | ||
| 93 | - } | ||
| 109 | + const cmdargs = location.args; | ||
| 94 | 110 | ||
| 95 | - throw e; | ||
| 96 | - } | ||
| 111 | + await this.run(cmd, cmdargs, { location, env, executor: this }); | ||
| 97 | 112 | } | |
| 98 | 113 | } | |
| 99 | 114 | ||
@@ -166,5 +181,16 @@ export class Executor extends BaseExecutor<Command, Namespace, CommandMetadata, | |||
| 166 | 181 | ||
| 167 | 182 | export async function execute<C extends ICommand<C, N, M, I, O>, N extends INamespace<C, N, M, I, O>, M extends CommandMetadata<I, O>, I extends CommandMetadataInput, O extends CommandMetadataOption>({ namespace, argv, env, ...rest }: { namespace: N; argv: string[]; env: NodeJS.ProcessEnv } & Partial<BaseExecutorDeps<C, N, M, I, O>>) { | |
| 168 | 183 | const executor = new BaseExecutor<C, N, M, I, O>({ namespace, ...rest }); | |
| 169 | - await executor.execute(argv, env); | ||
| 184 | + | ||
| 185 | + try { | ||
| 186 | + await executor.execute(argv, env); | ||
| 187 | + } catch (e) { | ||
| 188 | + if (e instanceof BaseError) { | ||
| 189 | + executor.stderr.write(`Error: ${e.message}`); | ||
| 190 | + process.exitCode = typeof e.exitCode === 'undefined' ? 1 : e.exitCode; | ||
| 191 | + return; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + throw e; | ||
| 195 | + } | ||
| 170 | 196 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments