hydrogen/packages/cli/src/ui/ui.ts
2021-11-04 15:22:30 -07:00

124 lines
3.2 KiB
TypeScript

import inquirer from 'inquirer';
import chalk from 'chalk';
import {FileResult} from '../fs';
interface BaseOptions {
validate?: (input: string) => boolean | string;
default?: string | number | boolean;
name?: string;
}
interface ChoiceQuestionOptions<T = {value: string}> extends BaseOptions {
choices: T[];
}
interface BooleanQuestionOptions extends BaseOptions {
boolean: true;
}
interface CheckboxQuestionOptions<T = {value: any}> extends BaseOptions {
choices: T[];
multiple: true;
}
type CombinedOptions<T> = Partial<BooleanQuestionOptions> &
Partial<ChoiceQuestionOptions<T>> &
Partial<CheckboxQuestionOptions<T>>;
interface ConstructorOptions {
debug?: boolean;
}
interface SayOptions {
error?: boolean;
}
export interface Ui {
ask<T>(message: string, options: CheckboxQuestionOptions<T>): Promise<T[]>;
ask<T = string>(
message: string,
options: ChoiceQuestionOptions<T>
): Promise<T>;
ask(message: string, options: BooleanQuestionOptions): Promise<boolean>;
ask<T = string>(message: string, options?: BaseOptions): Promise<T>;
say(message: string | [string, string][], options?: SayOptions): void;
}
export class Cli implements Ui {
debug: boolean;
readonly indent = ` `;
readonly prefix = chalk.bold.yellow.underline`h2`;
constructor({debug}: ConstructorOptions | undefined = {}) {
this.debug = debug || false;
}
async ask<T = string>(
message: string,
options: CheckboxQuestionOptions<T>
): Promise<T[]>;
async ask<T = string>(
message: string,
options: ChoiceQuestionOptions<T>
): Promise<T>;
async ask(message: string, options: BooleanQuestionOptions): Promise<boolean>;
async ask<T = string>(message: string, options?: BaseOptions): Promise<T>;
async ask<T>(message: string, options: CombinedOptions<T> = {}) {
const name = options.name ?? 'question';
const normalizedQuestion: any = {
message,
name,
validate: options.validate,
default: options.default,
};
if (options?.choices) {
normalizedQuestion.choices = options.choices;
normalizedQuestion.type = options.multiple ? 'checkbox' : 'list';
}
if (options?.boolean) {
normalizedQuestion.type = 'confirm';
}
const result = await inquirer.prompt<{
[key: string]: any;
}>(normalizedQuestion);
return result[name];
}
say(message: string | [string, string?][], options: SayOptions = {}) {
if (Array.isArray(message)) {
message.forEach((msg) => {
const [label, ...values] = msg;
const spacer = [this.indent, this.indent].join('');
console.log(chalk.cyan([spacer, label, ...values].join(spacer)));
});
return;
}
const styledMessage = options.error ? chalk.redBright`${message}` : message;
const type = options.error ? chalk.black.bgRedBright` error ` : '';
console.log(
[
this.indent,
this.prefix,
this.indent,
type,
this.indent,
styledMessage,
].join('')
);
}
printFile({path, overwritten}: FileResult) {
const overwrote = overwritten
? chalk.redBright``
: chalk.greenBright``;
this.say([overwrote, path].join(''));
}
}