complete remodelling
This commit is contained in:
parent
742d77d29f
commit
021f55833d
15
lib/OptionType.ts
Normal file
15
lib/OptionType.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Sapphirecode - All Rights Reserved
|
||||||
|
* This file is part of console-app which is released under MIT.
|
||||||
|
* See file 'LICENSE' for full license details.
|
||||||
|
* Created by Timo Hocker <timo@scode.ovh>, May 2020
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type OptionType =
|
||||||
|
'string'
|
||||||
|
| 'number'
|
||||||
|
| 'boolean'
|
||||||
|
| 'file'
|
||||||
|
| 'folder'
|
||||||
|
| 'path'
|
||||||
|
| 'array';
|
@ -1,7 +1,7 @@
|
|||||||
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
||||||
import { BaseOption } from './BaseOption';
|
import { BaseOption } from './BaseOption';
|
||||||
|
|
||||||
export class ArrayOption extends BaseOption {
|
export class ArrayOption extends BaseOption<(string|number|boolean)[]> {
|
||||||
protected get validation ():TypeValidation {
|
protected get validation ():TypeValidation {
|
||||||
return new TypeValidation ('array');
|
return new TypeValidation ('array');
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { ErrorCallback } from '../ErrorCallback';
|
|||||||
import { ArgSource } from '../Sources/ArgSource';
|
import { ArgSource } from '../Sources/ArgSource';
|
||||||
import { ConfigSource } from '../Sources/ConfigSource';
|
import { ConfigSource } from '../Sources/ConfigSource';
|
||||||
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
||||||
|
import { InteractiveSource } from '../Sources/InteractiveSource';
|
||||||
|
|
||||||
export abstract class BaseOption<T> {
|
export abstract class BaseOption<T> {
|
||||||
protected readonly sources: OptionSource[] = [];
|
protected readonly sources: OptionSource[] = [];
|
||||||
@ -44,8 +45,8 @@ export abstract class BaseOption<T> {
|
|||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await source.parse (this._config, val);
|
await source.parse (this._config, val);
|
||||||
|
|
||||||
if (!val.assinged)
|
if (!val.filled)
|
||||||
return this._config.default;
|
return this._config.default as T;
|
||||||
return val.value;
|
return val.value as T;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
||||||
import { BaseOption } from './BaseOption';
|
import { BaseOption } from './BaseOption';
|
||||||
|
|
||||||
export class BooleanOption extends BaseOption {
|
export class BooleanOption extends BaseOption<boolean> {
|
||||||
protected get validation ():TypeValidation {
|
protected get validation ():TypeValidation {
|
||||||
return new TypeValidation ('boolean');
|
return new TypeValidation ('boolean');
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { PathType } from '../TypeValidation/PathType';
|
import { PathType } from '../TypeValidation/PathType';
|
||||||
|
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
||||||
import { StringOption } from './StringOption';
|
import { StringOption } from './StringOption';
|
||||||
|
|
||||||
export class FileOption extends StringOption {
|
export class FileOption extends StringOption {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { PathType } from '../TypeValidation/PathType';
|
import { PathType } from '../TypeValidation/PathType';
|
||||||
|
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
||||||
import { StringOption } from './StringOption';
|
import { StringOption } from './StringOption';
|
||||||
|
|
||||||
export class FolderOption extends StringOption {
|
export class FolderOption extends StringOption {
|
||||||
protected get validation ():TypeValidation {
|
protected get validation (): TypeValidation {
|
||||||
return new PathType ('folder');
|
return new PathType ('folder');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
||||||
import { BaseOption } from './BaseOption';
|
import { BaseOption } from './BaseOption';
|
||||||
|
|
||||||
export class NumberOption extends BaseOption {
|
export class NumberOption extends BaseOption<number> {
|
||||||
protected get validation ():TypeValidation {
|
protected get validation ():TypeValidation {
|
||||||
return new TypeValidation ('number');
|
return new TypeValidation ('number');
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { PathType } from '../TypeValidation/PathType';
|
import { PathType } from '../TypeValidation/PathType';
|
||||||
|
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
||||||
import { StringOption } from './StringOption';
|
import { StringOption } from './StringOption';
|
||||||
|
|
||||||
export class PathOption extends StringOption {
|
export class PathOption extends StringOption {
|
||||||
protected get validation ():TypeValidation {
|
protected get validation (): TypeValidation {
|
||||||
return new PathType ('path');
|
return new PathType ('path');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
import { TypeValidation } from '../TypeValidation/TypeValidation';
|
||||||
import { Option } from '../Option';
|
import { StringOptionConfig } from '../SubConfigs';
|
||||||
import { BaseOption } from './BaseOption';
|
import { BaseOption } from './BaseOption';
|
||||||
|
|
||||||
interface StringOptionConfig extends Option {
|
export class StringOption extends BaseOption<string> {
|
||||||
preset?: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class StringOption extends BaseOption {
|
|
||||||
protected get validation ():TypeValidation {
|
protected get validation ():TypeValidation {
|
||||||
return new TypeValidation ('string');
|
return new TypeValidation ('string');
|
||||||
}
|
}
|
||||||
|
@ -8,68 +8,43 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
/* eslint-disable no-process-exit */
|
/* eslint-disable no-process-exit */
|
||||||
import yargs, { Options } from 'yargs';
|
import yargs, { Options } from 'yargs';
|
||||||
import { OptionProcess } from '../Option';
|
import { Option, OptionValue } from '../Option';
|
||||||
import { OptionSource } from './OptionSource';
|
import { OptionSource } from './OptionSource';
|
||||||
|
|
||||||
export class ArgSource extends OptionSource {
|
export class ArgSource extends OptionSource {
|
||||||
private create_config (options: OptionProcess[]): Record<string, Options> {
|
private create_config (
|
||||||
const yargs_config: Record<string, Options> = {
|
config: Option,
|
||||||
quiet: {
|
persistent_type: string
|
||||||
alias: 'q',
|
): Record<string, Options> {
|
||||||
default: false,
|
return {
|
||||||
type: 'boolean',
|
[config.name]: {
|
||||||
describe: 'do not ask for options interactively'
|
alias: config.alias,
|
||||||
},
|
|
||||||
help: {
|
|
||||||
alias: 'h',
|
|
||||||
default: false,
|
|
||||||
type: 'boolean',
|
|
||||||
describe: ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (const opt of options) {
|
|
||||||
const type = opt.type_validation.persistent_type;
|
|
||||||
yargs_config[opt.name] = {
|
|
||||||
alias: opt.alias,
|
|
||||||
// eslint-disable-next-line no-undefined
|
// eslint-disable-next-line no-undefined
|
||||||
default: type === 'boolean' ? undefined : opt.default,
|
default: undefined,
|
||||||
type,
|
type: persistent_type
|
||||||
describe: opt.description
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return yargs_config;
|
} as Record<string, Options>;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async parse (options: OptionProcess[]): Promise<void> {
|
public async parse (opt: Option, val: OptionValue): Promise<void> {
|
||||||
const yargs_config = this.create_config (options);
|
const yargs_config = this.create_config (
|
||||||
|
opt,
|
||||||
|
val.type_validation.persistent_type
|
||||||
|
);
|
||||||
|
|
||||||
const argv = yargs.options (yargs_config)
|
const argv = yargs.options (yargs_config)
|
||||||
.parse ();
|
.parse ();
|
||||||
if (argv.help) {
|
|
||||||
yargs.options (yargs_config)
|
|
||||||
.showHelp ();
|
|
||||||
process.exit (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all (options.map ((opt) => {
|
|
||||||
if (typeof argv[opt.name] === 'undefined')
|
if (typeof argv[opt.name] === 'undefined')
|
||||||
return Promise.resolve ();
|
return;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
opt.type === 'array'
|
val.type_validation.option_type === 'array'
|
||||||
&& (argv[opt.name] as Array<unknown>)
|
&& (argv[opt.name] as Array<unknown>)
|
||||||
.filter ((v) => typeof v !== 'undefined').length <= 0
|
.filter ((v) => typeof v !== 'undefined').length <= 0
|
||||||
)
|
)
|
||||||
return Promise.resolve ();
|
return;
|
||||||
return this.assign_arg (opt, argv[opt.name]);
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (argv.quiet) {
|
await this.assign_arg (opt, val, argv[opt.name]);
|
||||||
const missing = options.filter ((o) => !o.filled && o.required)
|
|
||||||
.map ((o) => o.name);
|
|
||||||
if (missing.length > 0) {
|
|
||||||
console.error ('missing arguments:');
|
|
||||||
console.error (missing.join (', '));
|
|
||||||
process.exit (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { dirname, join } from 'path';
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import { run_regex } from '@sapphirecode/utilities';
|
import { run_regex } from '@sapphirecode/utilities';
|
||||||
import hjson from 'hjson';
|
import hjson from 'hjson';
|
||||||
import { OptionProcess } from '../Option';
|
import { OptionValue, Option } from '../Option';
|
||||||
import { ErrorCallback } from '../ErrorCallback';
|
import { ErrorCallback } from '../ErrorCallback';
|
||||||
import { OptionSource } from './OptionSource';
|
import { OptionSource } from './OptionSource';
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export class ConfigSource extends OptionSource {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async parse (options: OptionProcess[]): Promise<void> {
|
public async parse (opt: Option, val: OptionValue): Promise<void> {
|
||||||
const data: Record<string, unknown> = {};
|
const data: Record<string, unknown> = {};
|
||||||
for (const f of this._config_files) {
|
for (const f of this._config_files) {
|
||||||
try {
|
try {
|
||||||
@ -63,9 +63,7 @@ export class ConfigSource extends OptionSource {
|
|||||||
|
|
||||||
const keys = Object.keys (data);
|
const keys = Object.keys (data);
|
||||||
|
|
||||||
for (const opt of options) {
|
|
||||||
if (keys.includes (opt.name))
|
if (keys.includes (opt.name))
|
||||||
await this.assign_arg (opt, data[opt.name]);
|
await this.assign_arg (opt, val, data[opt.name]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable no-process-env */
|
/* eslint-disable no-process-env */
|
||||||
import { OptionProcess } from '../Option';
|
import { Option, OptionValue } from '../Option';
|
||||||
import { OptionSource } from './OptionSource';
|
import { OptionSource } from './OptionSource';
|
||||||
|
|
||||||
export class EnvSource extends OptionSource {
|
export class EnvSource extends OptionSource {
|
||||||
public async parse (options: OptionProcess[]): Promise<void> {
|
public async parse (opt: Option, val:OptionValue): Promise<void> {
|
||||||
await Promise.all (options.map ((opt) => {
|
|
||||||
if (
|
if (
|
||||||
typeof opt.env !== 'undefined'
|
typeof opt.env !== 'undefined'
|
||||||
&& typeof process.env[opt.env] !== 'undefined'
|
&& typeof process.env[opt.env] !== 'undefined'
|
||||||
)
|
)
|
||||||
return this.assign_arg (opt, process.env[opt.env]);
|
await this.assign_arg (opt, val, process.env[opt.env]);
|
||||||
return Promise.resolve ();
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
/* eslint-disable no-process-exit */
|
/* eslint-disable no-process-exit */
|
||||||
import { Confirm, Input, List, AutoComplete } from 'enquirer';
|
import { Confirm, Input, List, AutoComplete } from 'enquirer';
|
||||||
import { OptionProcess, Option } from '../Option';
|
|
||||||
import { ErrorCallback } from '../ErrorCallback';
|
import { ErrorCallback } from '../ErrorCallback';
|
||||||
|
import { Option, OptionValue } from '../Option';
|
||||||
|
import { StringOptionConfig } from '../SubConfigs';
|
||||||
import { OptionSource } from './OptionSource';
|
import { OptionSource } from './OptionSource';
|
||||||
|
|
||||||
export class InteractiveSource extends OptionSource {
|
export class InteractiveSource extends OptionSource {
|
||||||
@ -29,18 +30,20 @@ export class InteractiveSource extends OptionSource {
|
|||||||
: opt.message;
|
: opt.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async prompt (opt: OptionProcess): Promise<void> {
|
private async prompt (opt: Option, val:OptionValue): Promise<void> {
|
||||||
if (opt.filled)
|
if (val.filled)
|
||||||
return;
|
return;
|
||||||
let value = null;
|
let value = null;
|
||||||
|
const { option_type } = val.type_validation;
|
||||||
|
const { preset } = opt as StringOptionConfig;
|
||||||
if (
|
if (
|
||||||
opt.type === 'string'
|
option_type === 'string'
|
||||||
|| opt.type === 'file'
|
|| option_type === 'file'
|
||||||
|| opt.type === 'folder'
|
|| option_type === 'folder'
|
||||||
|| opt.type === 'path'
|
|| option_type === 'path'
|
||||||
|| opt.type === 'number'
|
|| option_type === 'number'
|
||||||
) {
|
) {
|
||||||
if (typeof opt.preset === 'undefined') {
|
if (typeof preset === 'undefined') {
|
||||||
value = await new Input ({
|
value = await new Input ({
|
||||||
message: this.get_message (opt),
|
message: this.get_message (opt),
|
||||||
default: opt.default
|
default: opt.default
|
||||||
@ -51,14 +54,14 @@ export class InteractiveSource extends OptionSource {
|
|||||||
value = await new AutoComplete ({
|
value = await new AutoComplete ({
|
||||||
message: this.get_message (opt),
|
message: this.get_message (opt),
|
||||||
default: opt.default,
|
default: opt.default,
|
||||||
choices: opt.preset,
|
choices: preset,
|
||||||
limit: 10
|
limit: 10
|
||||||
})
|
})
|
||||||
.run ();
|
.run ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
opt.type === 'boolean'
|
option_type === 'boolean'
|
||||||
) {
|
) {
|
||||||
value = await new Confirm ({
|
value = await new Confirm ({
|
||||||
message: this.get_message (opt),
|
message: this.get_message (opt),
|
||||||
@ -66,7 +69,7 @@ export class InteractiveSource extends OptionSource {
|
|||||||
})
|
})
|
||||||
.run ();
|
.run ();
|
||||||
}
|
}
|
||||||
if (opt.type === 'array') {
|
if (option_type === 'array') {
|
||||||
value = await new List ({
|
value = await new List ({
|
||||||
message: this.get_message (opt),
|
message: this.get_message (opt),
|
||||||
default: opt.default
|
default: opt.default
|
||||||
@ -76,22 +79,20 @@ export class InteractiveSource extends OptionSource {
|
|||||||
if (value === null)
|
if (value === null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await this.assign_arg (opt, value);
|
await this.assign_arg (opt, val, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async parse (options: OptionProcess[]): Promise<void> {
|
public async parse (opt: Option, val:OptionValue): Promise<void> {
|
||||||
for (const opt of options) {
|
while (!val.filled) {
|
||||||
while (!opt.filled) {
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await this.prompt (opt)
|
await this.prompt (opt, val)
|
||||||
.catch ((e) => {
|
.catch ((e) => {
|
||||||
if (this._exit_on_interrupt)
|
if (this._exit_on_interrupt)
|
||||||
process.exit (0);
|
process.exit (0);
|
||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
if (!opt.filled)
|
if (!val.filled)
|
||||||
console.log (opt.error || 'input was invalid');
|
console.log (opt.error || 'input was invalid');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,13 @@ export abstract class OptionSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async assign_arg (
|
protected async assign_arg (
|
||||||
opt: OptionValue,
|
opt: Option,
|
||||||
|
val: OptionValue,
|
||||||
value: unknown
|
value: unknown
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
opt.value = await opt.type_validation.to_type (value);
|
val.value = await val.type_validation.to_type (value);
|
||||||
opt.filled = true;
|
val.filled = true;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
if (typeof this.error_callback !== 'undefined')
|
if (typeof this.error_callback !== 'undefined')
|
||||||
|
7
lib/SubConfigs.ts
Normal file
7
lib/SubConfigs.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Option } from './Option';
|
||||||
|
|
||||||
|
interface StringOptionConfig extends Option {
|
||||||
|
preset?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export { StringOptionConfig };
|
@ -1,12 +0,0 @@
|
|||||||
import { TypeValidation } from './TypeValidation/TypeValidation';
|
|
||||||
import { PathType } from './TypeValidation/PathType';
|
|
||||||
|
|
||||||
export const types: Record<OptionType, TypeValidation> = {
|
|
||||||
string: new TypeValidation ('string'),
|
|
||||||
number: new TypeValidation ('number'),
|
|
||||||
boolean: new TypeValidation ('boolean'),
|
|
||||||
file: new PathType ('file'),
|
|
||||||
folder: new PathType ('folder'),
|
|
||||||
path: new PathType ('path'),
|
|
||||||
array: new TypeValidation ('array')
|
|
||||||
};
|
|
@ -5,4 +5,10 @@
|
|||||||
* Created by Timo Hocker <timo@scode.ovh>, May 2020
|
* Created by Timo Hocker <timo@scode.ovh>, May 2020
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { InteractiveOptions } from './InteractiveOptions';
|
export { ArrayOption } from './Options/ArrayOption';
|
||||||
|
export { BooleanOption } from './Options/BooleanOption';
|
||||||
|
export { FileOption } from './Options/FileOption';
|
||||||
|
export { FolderOption } from './Options/FolderOption';
|
||||||
|
export { NumberOption } from './Options/NumberOption';
|
||||||
|
export { PathOption } from './Options/PathOption';
|
||||||
|
export { StringOption } from './Options/StringOption';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user