@black-flag/core
Version:
A declarative framework for building fluent, deeply hierarchical command line interfaces with yargs
301 lines • 16.2 kB
TypeScript
import type { Promisable } from 'type-fest';
import type { ConfigurationHooks } from "./types/configure.js";
import type { Arguments, NullArguments, PreExecutionContext } from "./types/program.js";
/**
* @internal
*/
export type RunProgramParametersWithAny = [commandModulesPath: string] | [commandModulesPath: string, configurationHooks: Promisable<ConfigurationHooks<any>>] | [commandModulesPath: string, preExecutionContext: Promisable<PreExecutionContext>] | [commandModulesPath: string, argv: string | string[]] | [commandModulesPath: string, argv: string | string[], configurationHooks: Promisable<ConfigurationHooks<any>>] | [commandModulesPath: string, argv: string | string[], preExecutionContext: Promisable<PreExecutionContext>];
/**
* The available call signature parameters of the {@link runProgram} function.
*
* The first parameter is always the required `commandModulesPath` string,
* optionally followed by `argv` string/array, and then either a * {@link ConfigurationHooks} or {@link PreExecutionContext} instance (or a
* promise that returns them).
*/
export type RunProgramParameters = RunProgramParametersWithAny | [commandModulesPath: string, configurationHooks: Promisable<ConfigurationHooks>] | [commandModulesPath: string, argv: string | string[], configurationHooks: Promisable<ConfigurationHooks>];
/**
* The return type of the {@link runProgram} function.
*/
export type RunProgramReturnType<CustomCliArguments extends Record<string, unknown>> = Promise<NullArguments | Arguments<CustomCliArguments> | undefined>;
/**
* The available call signature parameters of the low-order function returned by
* {@link makeRunner}.
*
* This is the same thing as {@link RunProgramParameters} but with the first
* parameter (i.e. the `commandModulesPath` string) omitted.
*/
export type FactoriedRunProgramParameters = RunProgramParametersWithAny extends [infer _, ...infer Tail] ? Tail : [];
/**
* The options accepted by the {@link makeRunner} function.
*/
export type MakeRunnerOptions = {
/**
* @see {@link runProgram}
*/
commandModulesPath: string;
/**
* This is a special option exclusive to `makeRunner` that determines how
* errors will be surfaced, which can be useful during testing.
*
* In production and during testing, Black Flag surfaces errors via
* `process.stderr` (e.g. `console.error`), or whichever error handling
* method was implemented in {@link ConfigureErrorHandlingEpilogue}. This is
* the default behavior, and is what end-users will see and experience.
*
* However, by setting this option to `'throw'` instead of `'default'`,
* exceptions that would normally cause {@link runProgram} to return
* `undefined` (including framework errors) or `NullArguments` (such as
* `GracefulEarlyExitError`) will instead be thrown after they are handled
* by Black Flag.
*
* **Asserting expectations against how the CLI will actually behave in
* production, which is what end-users will actually experience, should be
* preferred over testing against an artificially surfaced error**. However,
* surfacing errors in test cases that are failing unexpectedly can be
* crucial when debugging. Discretion is advised.
*
* @default 'default'
*/
errorHandlingBehavior?: 'default' | 'throw';
/**
* The {@link ConfigurationHooks} to be used by each low-order
* invocation by default. Each low-order function can provide its own
* {@link ConfigurationHooks} argument, which will be merged on top of
* this option. A low-order function supplying a
* {@link PreExecutionContext} argument instead will completely override
* this option.
*
* Note: this option cannot be used with `preExecutionContext`.
*
* @see {@link runProgram}
*/
configurationHooks?: Promisable<ConfigurationHooks<any>>;
/**
* The {@link PreExecutionContext} to be used by each low-order
* invocation. Each low-order function can provide its own
* {@link PreExecutionContext} or {@link ConfigurationHooks} argument,
* both of which which will completely override this option.
*
* Note: this option cannot be used with `configurationHooks`.
*
* @see {@link runProgram}
*/
preExecutionContext?: Promisable<PreExecutionContext>;
} & ({
configurationHooks?: Promisable<ConfigurationHooks<any>>;
preExecutionContext?: undefined;
} | {
preExecutionContext?: Promisable<PreExecutionContext>;
configurationHooks?: undefined;
});
/**
* Create and return a {@link PreExecutionContext} containing fully-configured
* {@link Program} instances and an {@link Executor} entry point function.
*
* Command auto-discovery will occur at `commandModulesPath`. An exception will
* occur if no commands are loadable from the given `commandModulesPath`.
*
* **This function throws whenever an exception occurs**, making it not ideal as
* an entry point for a CLI, but perhaps useful during testing. See
* {@link runProgram} for a wrapper function that handles exceptions and sets
* the exit code automatically.
*/
export declare function configureProgram(
/**
* Command auto-discovery will occur at `commandModulesPath`. An exception will
* occur if no commands are loadable from the given `commandModulesPath`.
*
* `'file://...'`-style URLs are also accepted.
*/
commandModulesPath: string, configurationHooks?: Promisable<ConfigurationHooks<any>>): Promise<PreExecutionContext>;
/**
* A "high-order" factory function that returns a "low-order" curried
* {@link runProgram} function that can be called multiple times while only
* having to provide a subset of the required parameters.
*
* This is useful when unit/integration testing a CLI, which will likely require
* multiple calls to `runProgram(...)`.
*
* **BE AWARE**: when an exception (e.g. bad CLI arguments) occurs in the
* low-order function, `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned. Otherwise, upon success, `Arguments` is returned.
*
* In other words: **the promise returned by the low-order function will never
* reject and no exception will ever be thrown.** Keep this in mind when writing
* unit tests and see {@link runProgram} for more details.
*
* Ideally, testing for the presence of errors should be done by capturing
* output sent to `process.stderr` (e.g. `console.error`)—or by interrogating
* whichever error handling method was implemented in
* {@link ConfigureErrorHandlingEpilogue}—since that is what end-users will see
* and experience. That said, if it would be more optimal to test against an
* actual thrown error, set `makeRunner`'s `errorHandlingBehavior` option to
* `'throw'`.
*/
export declare function makeRunner<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(options: MakeRunnerOptions): (...args: FactoriedRunProgramParameters) => RunProgramReturnType<CustomCliArguments>;
/**
* Invokes the dynamically imported
* `configureProgram(commandModulesPath).execute()` function.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* Note: It is always safe to invoke this form of `runProgram` as many times as
* desired.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(commandModulesPath: string): RunProgramReturnType<CustomCliArguments>;
/**
* Invokes the dynamically imported `configureProgram(commandModulesPath,
* configurationHooks).execute()` function.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* Note: It is always safe to invoke this form of `runProgram` as many times as
* desired.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(commandModulesPath: string, configurationHooks: Promisable<ConfigurationHooks>): RunProgramReturnType<CustomCliArguments>;
/**
* Invokes the dynamically imported `configureProgram(commandModulesPath,
* configurationHooks).execute()` function.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* Note: It is always safe to invoke this form of `runProgram` as many times as
* desired.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(commandModulesPath: string, configurationHooks: Promisable<ConfigurationHooks<any>>): RunProgramReturnType<CustomCliArguments>;
/**
* Invokes the `preExecutionContext.execute()` function.
*
* **WARNING: reusing the same `preExecutionContext` with multiple invocations
* of `runProgram` will cause successive invocations to fail.** This is because
* yargs does not support calling `yargs::parseAsync` more than once. If this is
* unacceptable, do not pass `runProgram` a `preExecutionContext` property.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(commandModulesPath: string, preExecutionContext: Promisable<PreExecutionContext>): RunProgramReturnType<CustomCliArguments>;
/**
* Invokes the dynamically imported
* `configureProgram(commandModulesPath).execute(argv)` function. If `argv` is a
* string, `argv = argv.split(' ')` is applied first.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* Note: It is always safe to invoke this form of `runProgram` as many times as
* desired.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(commandModulesPath: string, argv: string | string[]): RunProgramReturnType<CustomCliArguments>;
/**
* Invokes the dynamically imported `configureProgram(commandModulesPath,
* configurationHooks).execute(argv)` function. If `argv` is a string, `argv =
* argv.split(' ')` is applied first.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* Note: It is always safe to invoke this form of `runProgram` as many times as
* desired.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(commandModulesPath: string, argv: string | string[], configurationHooks: Promisable<ConfigurationHooks>): RunProgramReturnType<CustomCliArguments>;
/**
* Invokes the dynamically imported `configureProgram(commandModulesPath,
* configurationHooks).execute(argv)` function. If `argv` is a string, `argv =
* argv.split(' ')` is applied first.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* Note: It is always safe to invoke this form of `runProgram` as many times as
* desired.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(commandModulesPath: string, argv: string | string[], configurationHooks: Promisable<ConfigurationHooks<any>>): RunProgramReturnType<CustomCliArguments>;
/**
* Invokes the `preExecutionContext.execute(argv)` function. If `argv` is a
* string, `argv = argv.split(' ')` is applied first.
*
* **WARNING: reusing the same `preExecutionContext` with multiple invocations
* of `runProgram` will cause successive invocations to fail.** This is because
* yargs does not support calling `yargs::parseAsync` more than once. If this is
* unacceptable, do not pass `runProgram` a `preExecutionContext` property.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(commandModulesPath: string, argv: string | string[], preExecutionContext: Promisable<PreExecutionContext>): RunProgramReturnType<CustomCliArguments>;
/**
* Run the given program with the configuration given in `args`.
*
* This function is suitable for a CLI entry point since it will **never throw
* or reject no matter what.** Instead, when an exception occurs,
* `process.exitCode` is set to the appropriate value, the
* {@link ConfigureErrorHandlingEpilogue} hook is triggered, and either
* `NullArguments` (only if `GracefulEarlyExitError` was thrown) or `undefined`
* is returned.
*
* @returns `NullArguments` if `GracefulEarlyExitError` is thrown, `undefined`
* if any other exception occurs, or `Arguments` otherwise.
*/
export declare function runProgram<CustomCliArguments extends Record<string, unknown> = Record<string, unknown>>(...args: RunProgramParameters): RunProgramReturnType<CustomCliArguments>;