complete first
This commit is contained in:
@ -1,8 +1,11 @@
|
||||
/* eslint-disable max-lines-per-function */
|
||||
/* eslint-disable complexity */
|
||||
/* eslint-disable max-statements */
|
||||
/* eslint-disable no-process-env */
|
||||
import { Persistent } from '@scode/modelling';
|
||||
import fs from 'fs-extra';
|
||||
import yargs from 'yargs';
|
||||
import { Confirm, Input } from 'enquirer';
|
||||
|
||||
enum OptionType {
|
||||
string = 'string',
|
||||
@ -20,6 +23,8 @@ interface Option {
|
||||
default?: unknown;
|
||||
alias?: string;
|
||||
env?: string;
|
||||
description?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
interface OptionProcess extends Option {
|
||||
@ -45,22 +50,24 @@ export class InteractiveOptions extends Persistent {
|
||||
}
|
||||
|
||||
public async parse (): Promise<void> {
|
||||
this.get_env_options ();
|
||||
this.get_args_options ();
|
||||
await this.get_env_options ();
|
||||
await this.get_args_options ();
|
||||
await this.get_interactive_options ();
|
||||
}
|
||||
|
||||
private get unfilled (): Array<OptionProcess> {
|
||||
return this.options.filter ((o) => !o.filled);
|
||||
}
|
||||
|
||||
private async assign_arg (opt: OptionProcess, value: unknown): Promise<void> {
|
||||
if (opt.type === OptionType.string) {
|
||||
opt.value = value;
|
||||
opt.value = String (value);
|
||||
opt.filled = true;
|
||||
return;
|
||||
}
|
||||
if (opt.type === OptionType.number) {
|
||||
if (![
|
||||
'string',
|
||||
'number'
|
||||
].includes (typeof value))
|
||||
return;
|
||||
|
||||
const as_num = parseInt (value);
|
||||
const is_num = !isNaN (as_num);
|
||||
if (is_num) {
|
||||
@ -70,10 +77,20 @@ export class InteractiveOptions extends Persistent {
|
||||
return;
|
||||
}
|
||||
if (opt.type === OptionType.boolean) {
|
||||
const is_boo = (/^(?:true|false)$/ui).test (value);
|
||||
if (is_boo) {
|
||||
const as_boo = (/true/ui).test (value);
|
||||
opt.value = as_boo;
|
||||
if (![
|
||||
'string',
|
||||
'boolean',
|
||||
'number'
|
||||
].includes (typeof value))
|
||||
return;
|
||||
|
||||
const is_bool = [
|
||||
0,
|
||||
1
|
||||
].includes (value) || (/^(?:true|false)$/ui).test (value);
|
||||
if (is_bool) {
|
||||
const as_bool = value === 1 || (/true/ui).test (value as string);
|
||||
opt.value = as_bool;
|
||||
opt.filled = true;
|
||||
}
|
||||
return;
|
||||
@ -83,7 +100,7 @@ export class InteractiveOptions extends Persistent {
|
||||
|| opt.type === OptionType.file
|
||||
|| opt.type === OptionType.folder
|
||||
) {
|
||||
if (!await fs.pathExists (value))
|
||||
if (typeof value !== 'string' || !await fs.pathExists (value))
|
||||
return;
|
||||
if (opt.type === OptionType.path) {
|
||||
opt.value = value;
|
||||
@ -98,7 +115,7 @@ export class InteractiveOptions extends Persistent {
|
||||
}
|
||||
}
|
||||
|
||||
private async get_env_options (): void {
|
||||
private async get_env_options (): Promise<void> {
|
||||
await Promise.all (this.options.map ((opt) => {
|
||||
if (
|
||||
typeof opt.env !== 'undefined'
|
||||
@ -109,11 +126,64 @@ export class InteractiveOptions extends Persistent {
|
||||
}));
|
||||
}
|
||||
|
||||
private async get_args_options (): void {
|
||||
private async get_args_options (): Promise<void> {
|
||||
const yargs_config = {
|
||||
quiet: {
|
||||
alias: 'q',
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
describe: 'do not ask for options interactively'
|
||||
}
|
||||
};
|
||||
for (const opt of this.options) {
|
||||
yargs_config[opt.name] = {
|
||||
alias: opt.alias,
|
||||
default: opt.default,
|
||||
type: opt.type === OptionType.boolean ? 'boolean' : 'string',
|
||||
describe: opt.description
|
||||
};
|
||||
}
|
||||
const argv = yargs.options (yargs_config);
|
||||
await Promise.all (this.options.map ((opt) => {
|
||||
if (typeof argv[opt.name] !== 'undefined')
|
||||
return this.assign_arg (opt, argv[opt.name]);
|
||||
return Promise.resolve ();
|
||||
}));
|
||||
}
|
||||
|
||||
private async prompt (opt: OptionProcess): Promise<void> {
|
||||
if (opt.filled)
|
||||
return;
|
||||
if (
|
||||
opt.type === OptionType.string
|
||||
|| opt.type === OptionType.file
|
||||
|| opt.type === OptionType.folder
|
||||
|| opt.type === OptionType.path
|
||||
|| opt.type === OptionType.number
|
||||
) {
|
||||
const value = await new Input ({
|
||||
message: opt.message,
|
||||
default: opt.default
|
||||
})
|
||||
.run ();
|
||||
await this.assign_arg (opt, value);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
opt.type === OptionType.boolean
|
||||
) {
|
||||
const value = await new Confirm ({
|
||||
message: opt.message,
|
||||
default: opt.default
|
||||
})
|
||||
.run ();
|
||||
await this.assign_arg (opt, value);
|
||||
}
|
||||
}
|
||||
|
||||
private async get_interactive_options (): Promise<void> {
|
||||
|
||||
for (const opt of this.options)
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.prompt (opt);
|
||||
}
|
||||
}
|
||||
|
1
lib/enquirer.d.ts
vendored
Normal file
1
lib/enquirer.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module 'enquirer';
|
Reference in New Issue
Block a user