easy-cli-framework
Version:
A framework for building CLI applications that are robust and easy to maintain. Supports theming, configuration files, interactive prompts, and more.
137 lines (123 loc) • 4.42 kB
text/typescript
import { CommandSetupOptions, EasyCLICommand } from './command';
import { EasyCLIConfigFile } from '../config-files';
import { EasyCLITheme } from '../themes';
/**
* Options for the init command
* @interface InitCommandOptions
*
* @template TGlobalParams The global params for the CLI
* @template TParams The params for the command
*
* @property {boolean} failOnExists Should the command fail if the config file already exists?
* @property {(keyof TGlobalParams)[]} globalKeysToUse Any global keys that should be stored in the config file
* @property {(keyof TGlobalParams)[]} globalKeysToPrompt Any global keys that should be prompted for.
* @property {Partial<TGlobalParams & TParams>} defaults The default values to use
* @property {(params: TGlobalParams & TParams) => any} transformer How to transform the params before saving
* @property {string} configFlag The name of the variable to use for the config file
*
* @extends CommandSetupOptions
*
* @example
* ```typescript
* {
* failOnExists?: boolean; // Should the command fail if the config file already exists?
* globalKeysToUse?: string[]; // What key(s) are you setting?
* defaults?: Partial<TGlobalParams & TParams>; // The default values to use
* configFlag?: string; // The name of the variable to use for the config file
* }
* ```
*/
export type InitCommandOptions<TGlobalParams, TParams> = CommandSetupOptions<
TGlobalParams,
TParams
> & {
failOnExists?: boolean; // Should the command fail if the config file already exists?
globalKeysToUse?: (keyof TGlobalParams)[]; // What key(s) are you setting?
defaults?: Partial<TGlobalParams & TParams>; // The default values to use
configFlag?: string; // The name of the variable to use for the config file
callback?: (params: TGlobalParams & TParams) => void; // The callback to run after the command is executed, this is useful if you want to add additional functionality to the command ie. Copying a file
};
/**
* A command to add an init command to the CLI that will save the configuration
*
* @template TParams The params for the command
* @template TGlobalParams The global params for the CLI
*
* @extends EasyCLICommand
*
* @example
* ```typescript
* new EasyCLIInitCommand(config, 'init', {
* globalKeysToUse: ['verbose'],
* prompts: {
* env: {
* describe: 'What environent are you setting?',
* type: 'string',
* prompt: 'always',
* demandOption: true,
* },
* },
* });
* ```
*/
export class EasyCLIInitCommand<
TParams extends Record<string, any> = Record<string, any>,
TGlobalParams extends Record<string, any> = Record<string, any>
> extends EasyCLICommand<TParams, TGlobalParams> {
/**
* Creates a new init command
* @param {EasyCLIConfigFile} config The configuration file to use to save the config
* @param {string} [name='init'] The name of the command
* @param {InitCommandOptions<TGlobalParams, TParams>} [options={}] The options for the command
*/
constructor(
config: EasyCLIConfigFile,
name: string = 'init',
options: InitCommandOptions<TGlobalParams, TParams> = {}
) {
if (!config) {
throw new Error('EasyCLIConfigFile is required for init command');
}
const {
globalKeysToUse = [],
defaults = {},
configFlag = 'config',
callback,
...commandOptions
} = options;
const handler = async (
params: TGlobalParams & TParams,
theme?: EasyCLITheme
) => {
const logger = theme?.getLogger();
const keys = [...this.getKeys(), ...globalKeysToUse];
const clean = Object.entries(params as any).reduce(
(acc, [key, value]) => {
if (keys.includes(key as string)) {
acc[key] = value;
}
return acc;
},
defaults as any
);
logger?.success('Saving config file');
await config.save(clean);
if (callback) {
await callback(params);
}
};
const validator = async (params: TGlobalParams & TParams) => {
if (
options.failOnExists &&
config.fileExists((params as any)?.[configFlag] ?? null)
) {
throw new Error('Config file already exists');
}
if (commandOptions.validator) {
return commandOptions.validator(params);
}
return true;
};
super(name, handler, { ...commandOptions, validator });
}
}