refactoring
This commit is contained in:
parent
0ff924d2dd
commit
469afeb777
15
AppTest.js
Normal file
15
AppTest.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// eslint-disable-next-line id-match
|
||||||
|
const { InteractiveOptions } = require ('./dist/lib/index.js');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const reader = new InteractiveOptions ([
|
||||||
|
{ name: 'str', type: 'string' },
|
||||||
|
{ name: 'bool', type: 'boolean' },
|
||||||
|
{ name: 'num', type: 'number' }
|
||||||
|
]);
|
||||||
|
await reader.parse ();
|
||||||
|
console.log (reader.serialize (true));
|
||||||
|
}) ();
|
@ -12,242 +12,73 @@
|
|||||||
/* eslint-disable max-statements */
|
/* eslint-disable max-statements */
|
||||||
/* eslint-disable no-process-env */
|
/* eslint-disable no-process-env */
|
||||||
import { Persistent } from '@sapphirecode/modelling';
|
import { Persistent } from '@sapphirecode/modelling';
|
||||||
import fs from 'fs-extra';
|
import { TypeValidation } from './Types/TypeValidation';
|
||||||
import yargs, { Options } from 'yargs';
|
import { PathType } from './Types/PathType';
|
||||||
import { Confirm, Input } from 'enquirer';
|
import { Option, OptionProcess, OptionType } from './Types';
|
||||||
|
import { OptionSource } from './Sources/OptionSource';
|
||||||
|
import { EnvSource } from './Sources/EnvSource';
|
||||||
|
import { ArgSource } from './Sources/ArgSource';
|
||||||
|
import { InteractiveSource } from './Sources/InteractiveSource';
|
||||||
|
|
||||||
type OptionType =
|
const types: Record<OptionType, TypeValidation> = {
|
||||||
'string'
|
string: new TypeValidation ('string'),
|
||||||
| 'number'
|
number: new TypeValidation ('number'),
|
||||||
| 'boolean'
|
boolean: new TypeValidation ('boolean'),
|
||||||
| 'file'
|
file: new PathType ('file'),
|
||||||
| 'folder'
|
folder: new PathType ('folder'),
|
||||||
| 'path';
|
path: new PathType ('path')
|
||||||
|
};
|
||||||
|
|
||||||
interface Option {
|
interface SourceConfig {
|
||||||
name: string;
|
env: boolean;
|
||||||
type: OptionType;
|
args: boolean;
|
||||||
required?: boolean;
|
interactive: boolean;
|
||||||
default?: unknown;
|
|
||||||
alias?: string;
|
|
||||||
env?: string;
|
|
||||||
description?: string;
|
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OptionProcess extends Option {
|
|
||||||
filled: boolean;
|
|
||||||
value?: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_string_type (type: OptionType): 'string'|'number'|'boolean' {
|
|
||||||
if ([
|
|
||||||
'string',
|
|
||||||
'number',
|
|
||||||
'boolean'
|
|
||||||
].includes (type))
|
|
||||||
return type as ('string'|'number'|'boolean');
|
|
||||||
if ([
|
|
||||||
'file',
|
|
||||||
'folder',
|
|
||||||
'path'
|
|
||||||
].includes (type))
|
|
||||||
return 'string';
|
|
||||||
throw new Error (`unknown option type ${type}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InteractiveOptions extends Persistent {
|
export class InteractiveOptions extends Persistent {
|
||||||
protected options: Array<OptionProcess>;
|
protected options: Array<OptionProcess>;
|
||||||
protected quiet = false;
|
protected quiet = false;
|
||||||
|
protected sources: OptionSource[] = [];
|
||||||
|
|
||||||
public constructor (options: Array<Option>) {
|
public constructor (
|
||||||
|
options: Array<Option>,
|
||||||
|
source_config: SourceConfig = { args: true, env: true, interactive: true }
|
||||||
|
) {
|
||||||
super ();
|
super ();
|
||||||
this.options = options
|
this.options = options
|
||||||
.map ((v) => ({ filled: false, ...v } as OptionProcess));
|
.map ((v) => ({
|
||||||
|
filled: false,
|
||||||
|
type_validation: types[v.type],
|
||||||
|
...v
|
||||||
|
} as OptionProcess));
|
||||||
for (const option of this.options) {
|
for (const option of this.options) {
|
||||||
if (
|
if (
|
||||||
typeof option.default !== 'undefined'
|
typeof option.default !== 'undefined'
|
||||||
&& typeof option.default !== get_string_type (option.type)
|
&& typeof option.default !== option.type_validation.string_type
|
||||||
) {
|
) {
|
||||||
throw new Error (
|
throw new Error (
|
||||||
`default does not match option type on ${option.name}`
|
`default does not match option type on ${option.name}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.properties[option.name] = get_string_type (option.type);
|
this.properties[option.name] = option.type_validation.string_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (source_config.env)
|
||||||
|
this.sources.push (new EnvSource);
|
||||||
|
if (source_config.args)
|
||||||
|
this.sources.push (new ArgSource);
|
||||||
|
if (source_config.interactive)
|
||||||
|
this.sources.push (new InteractiveSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async parse (): Promise<Record<string, unknown>> {
|
public async parse (): Promise<Record<string, unknown>> {
|
||||||
await this.get_env_options ();
|
for (const src of this.sources)
|
||||||
await this.get_args_options ();
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await this.get_interactive_options ();
|
await src.parse (this.options);
|
||||||
for (const opt of this.options)
|
for (const opt of this.options)
|
||||||
this.set (opt.name, opt.value);
|
this.set (opt.name, opt.value);
|
||||||
|
|
||||||
return this.to_object ();
|
return this.to_object ();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async assign_arg (opt: OptionProcess, value: unknown): Promise<void> {
|
|
||||||
if (opt.type === 'string') {
|
|
||||||
opt.value = String (value);
|
|
||||||
opt.filled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt.type === 'number') {
|
|
||||||
if (![
|
|
||||||
'string',
|
|
||||||
'number'
|
|
||||||
].includes (typeof value))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const as_num = parseInt (String (value));
|
|
||||||
const is_num = !isNaN (as_num);
|
|
||||||
if (is_num) {
|
|
||||||
opt.value = as_num;
|
|
||||||
opt.filled = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (opt.type === 'boolean') {
|
|
||||||
if (![
|
|
||||||
'string',
|
|
||||||
'boolean',
|
|
||||||
'number'
|
|
||||||
].includes (typeof value))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const is_bool = [
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
].includes (parseInt (String (value)))
|
|
||||||
|| (/^(?:true|false)$/ui).test (value as string);
|
|
||||||
if (is_bool) {
|
|
||||||
const as_bool = value === 1 || (/true/ui).test (value as string);
|
|
||||||
opt.value = as_bool;
|
|
||||||
opt.filled = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
opt.type === 'path'
|
|
||||||
|| opt.type === 'file'
|
|
||||||
|| opt.type === 'folder'
|
|
||||||
) {
|
|
||||||
if (typeof value !== 'string' || !await fs.pathExists (value))
|
|
||||||
return;
|
|
||||||
if (opt.type === 'path') {
|
|
||||||
opt.value = value;
|
|
||||||
opt.filled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const stat = await fs.stat (value);
|
|
||||||
if (stat.isDirectory () === (opt.type === 'folder')) {
|
|
||||||
opt.value = value;
|
|
||||||
opt.filled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async get_env_options (): Promise<void> {
|
|
||||||
await Promise.all (this.options.map ((opt) => {
|
|
||||||
if (
|
|
||||||
typeof opt.env !== 'undefined'
|
|
||||||
&& typeof process.env[opt.env] !== 'undefined'
|
|
||||||
)
|
|
||||||
return this.assign_arg (opt, process.env[opt.env]);
|
|
||||||
return Promise.resolve ();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async get_args_options (): Promise<void> {
|
|
||||||
const yargs_config: Record<string, Options> = {
|
|
||||||
quiet: {
|
|
||||||
alias: 'q',
|
|
||||||
default: false,
|
|
||||||
type: 'boolean',
|
|
||||||
describe: 'do not ask for options interactively'
|
|
||||||
},
|
|
||||||
help: {
|
|
||||||
alias: 'h',
|
|
||||||
default: false,
|
|
||||||
type: 'boolean',
|
|
||||||
describe: ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (const opt of this.options) {
|
|
||||||
yargs_config[opt.name] = {
|
|
||||||
alias: opt.alias,
|
|
||||||
default: opt.default,
|
|
||||||
type: get_string_type (opt.type),
|
|
||||||
describe: opt.description
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const argv = yargs.options (yargs_config)
|
|
||||||
.parse ();
|
|
||||||
if (argv.help) {
|
|
||||||
yargs.options (yargs_config)
|
|
||||||
.showHelp ();
|
|
||||||
process.exit (0);
|
|
||||||
}
|
|
||||||
this.quiet = argv.quiet as boolean;
|
|
||||||
|
|
||||||
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 === 'string'
|
|
||||||
|| opt.type === 'file'
|
|
||||||
|| opt.type === 'folder'
|
|
||||||
|| opt.type === 'path'
|
|
||||||
|| opt.type === 'number'
|
|
||||||
) {
|
|
||||||
const value = await new Input ({
|
|
||||||
message: typeof opt.message === 'undefined'
|
|
||||||
? `input ${opt.name}`
|
|
||||||
: opt.message,
|
|
||||||
default: opt.default
|
|
||||||
})
|
|
||||||
.run ();
|
|
||||||
await this.assign_arg (opt, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
opt.type === '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> {
|
|
||||||
if (this.quiet) {
|
|
||||||
const missing = this.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const opt of this.options) {
|
|
||||||
while (!opt.filled) {
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
await this.prompt (opt);
|
|
||||||
if (!opt.filled)
|
|
||||||
console.log ('input was invalid');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
55
lib/Sources/ArgSource.ts
Normal file
55
lib/Sources/ArgSource.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
/* eslint-disable no-process-exit */
|
||||||
|
import yargs, { Options } from 'yargs';
|
||||||
|
import { OptionProcess } from '../Types';
|
||||||
|
import { OptionSource } from './OptionSource';
|
||||||
|
|
||||||
|
export class ArgSource extends OptionSource {
|
||||||
|
public async parse (options: OptionProcess[]): Promise<void> {
|
||||||
|
const yargs_config: Record<string, Options> = {
|
||||||
|
quiet: {
|
||||||
|
alias: 'q',
|
||||||
|
default: false,
|
||||||
|
type: 'boolean',
|
||||||
|
describe: 'do not ask for options interactively'
|
||||||
|
},
|
||||||
|
help: {
|
||||||
|
alias: 'h',
|
||||||
|
default: false,
|
||||||
|
type: 'boolean',
|
||||||
|
describe: ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const opt of options) {
|
||||||
|
yargs_config[opt.name] = {
|
||||||
|
alias: opt.alias,
|
||||||
|
default: opt.default,
|
||||||
|
type: opt.type_validation.string_type,
|
||||||
|
describe: opt.description
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const argv = yargs.options (yargs_config)
|
||||||
|
.parse ();
|
||||||
|
if (argv.help) {
|
||||||
|
yargs.options (yargs_config)
|
||||||
|
.showHelp ();
|
||||||
|
process.exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all (options.map ((opt) => {
|
||||||
|
if (typeof argv[opt.name] !== 'undefined')
|
||||||
|
return this.assign_arg (opt, argv[opt.name]);
|
||||||
|
return Promise.resolve ();
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (argv.quiet) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
lib/Sources/EnvSource.ts
Normal file
16
lib/Sources/EnvSource.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* eslint-disable no-process-env */
|
||||||
|
import { OptionProcess } from '../Types';
|
||||||
|
import { OptionSource } from './OptionSource';
|
||||||
|
|
||||||
|
export class EnvSource extends OptionSource {
|
||||||
|
public async parse (options: OptionProcess[]): Promise<void> {
|
||||||
|
await Promise.all (options.map ((opt) => {
|
||||||
|
if (
|
||||||
|
typeof opt.env !== 'undefined'
|
||||||
|
&& typeof process.env[opt.env] !== 'undefined'
|
||||||
|
)
|
||||||
|
return this.assign_arg (opt, process.env[opt.env]);
|
||||||
|
return Promise.resolve ();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
50
lib/Sources/InteractiveSource.ts
Normal file
50
lib/Sources/InteractiveSource.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
/* eslint-disable no-process-exit */
|
||||||
|
import { Confirm, Input } from 'enquirer';
|
||||||
|
import { OptionProcess } from '../Types';
|
||||||
|
import { OptionSource } from './OptionSource';
|
||||||
|
|
||||||
|
export class InteractiveSource extends OptionSource {
|
||||||
|
private async prompt (opt: OptionProcess): Promise<void> {
|
||||||
|
if (opt.filled)
|
||||||
|
return;
|
||||||
|
if (
|
||||||
|
opt.type === 'string'
|
||||||
|
|| opt.type === 'file'
|
||||||
|
|| opt.type === 'folder'
|
||||||
|
|| opt.type === 'path'
|
||||||
|
|| opt.type === 'number'
|
||||||
|
) {
|
||||||
|
const value = await new Input ({
|
||||||
|
message: typeof opt.message === 'undefined'
|
||||||
|
? `input ${opt.name}`
|
||||||
|
: opt.message,
|
||||||
|
default: opt.default
|
||||||
|
})
|
||||||
|
.run ();
|
||||||
|
await this.assign_arg (opt, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
opt.type === 'boolean'
|
||||||
|
) {
|
||||||
|
const value = await new Confirm ({
|
||||||
|
message: opt.message,
|
||||||
|
default: opt.default
|
||||||
|
})
|
||||||
|
.run ();
|
||||||
|
await this.assign_arg (opt, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async parse (options: OptionProcess[]): Promise<void> {
|
||||||
|
for (const opt of options) {
|
||||||
|
while (!opt.filled) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await this.prompt (opt);
|
||||||
|
if (!opt.filled)
|
||||||
|
console.log ('input was invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
lib/Sources/OptionSource.ts
Normal file
18
lib/Sources/OptionSource.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { OptionProcess } from '../Types';
|
||||||
|
|
||||||
|
export abstract class OptionSource {
|
||||||
|
public abstract async parse(opt: OptionProcess[]): Promise<void>;
|
||||||
|
|
||||||
|
protected async assign_arg (
|
||||||
|
opt: OptionProcess,
|
||||||
|
value: unknown
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
opt.value = await opt.type_validation.to_type (value);
|
||||||
|
opt.filled = true;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// could not assing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
lib/Types.ts
Normal file
28
lib/Types.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { TypeValidation } from './Types/TypeValidation';
|
||||||
|
|
||||||
|
type OptionType =
|
||||||
|
'string'
|
||||||
|
| 'number'
|
||||||
|
| 'boolean'
|
||||||
|
| 'file'
|
||||||
|
| 'folder'
|
||||||
|
| 'path';
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
name: string;
|
||||||
|
type: OptionType;
|
||||||
|
required?: boolean;
|
||||||
|
default?: unknown;
|
||||||
|
alias?: string;
|
||||||
|
env?: string;
|
||||||
|
description?: string;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OptionProcess extends Option {
|
||||||
|
filled: boolean;
|
||||||
|
value?: unknown;
|
||||||
|
type_validation: TypeValidation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { OptionType, Option, OptionProcess };
|
23
lib/Types/PathType.ts
Normal file
23
lib/Types/PathType.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import fs from 'fs-extra';
|
||||||
|
import { TypeValidation } from './TypeValidation';
|
||||||
|
|
||||||
|
export class PathType extends TypeValidation {
|
||||||
|
public get string_type (): 'string'|'number'|'boolean'|'array' {
|
||||||
|
return 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
public async to_type (value: unknown): Promise<unknown> {
|
||||||
|
if (typeof value !== 'string')
|
||||||
|
throw new Error (`invalid type for ${this.general_type}`);
|
||||||
|
if (!await fs.pathExists (value))
|
||||||
|
throw new Error ('path does not exist');
|
||||||
|
if (this.general_type === 'path')
|
||||||
|
return value;
|
||||||
|
|
||||||
|
const stat = await fs.stat (value);
|
||||||
|
if (stat.isDirectory () === (this.general_type === 'folder'))
|
||||||
|
return value;
|
||||||
|
|
||||||
|
throw new Error ('cannot assign folder to file');
|
||||||
|
}
|
||||||
|
}
|
44
lib/Types/TypeValidation.ts
Normal file
44
lib/Types/TypeValidation.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
export class TypeValidation {
|
||||||
|
private readonly _general_type: string;
|
||||||
|
|
||||||
|
public get general_type (): string {
|
||||||
|
return this._general_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get string_type (): 'string'|'number'|'boolean'|'array' {
|
||||||
|
return this._general_type as 'string'|'number'|'boolean'|'array';
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor (type: string) {
|
||||||
|
this._general_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public validate_type (value: unknown): boolean {
|
||||||
|
return typeof value === this.general_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public to_type (value: unknown): Promise<unknown> {
|
||||||
|
if (this.general_type === 'string')
|
||||||
|
return Promise.resolve (String (value));
|
||||||
|
|
||||||
|
if (this.general_type === 'number') {
|
||||||
|
const as_num = parseInt (String (value));
|
||||||
|
if (isNaN (as_num))
|
||||||
|
throw new Error ('value is not a number');
|
||||||
|
return Promise.resolve (as_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.general_type === 'boolean') {
|
||||||
|
const as_num = parseInt (String (value));
|
||||||
|
if (
|
||||||
|
as_num !== 1 && as_num !== 0
|
||||||
|
&& !(/^(?:true|false)$/iu).test (String (value))
|
||||||
|
)
|
||||||
|
throw new Error ('value is not a boolean');
|
||||||
|
return Promise.resolve (
|
||||||
|
as_num === 1 || (/true/iu).test (String (value))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw new Error ('unknown type');
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@sapphirecode/console-app",
|
"name": "@sapphirecode/console-app",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "dist/index.js",
|
"main": "dist/lib/index.js",
|
||||||
"author": "Timo Hocker <timo@sapphirecode.ovh>",
|
"author": "Timo Hocker <timo@sapphirecode.ovh>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -9,20 +9,20 @@
|
|||||||
"@sapphirecode/eslint-config-ts": "^1.0.31",
|
"@sapphirecode/eslint-config-ts": "^1.0.31",
|
||||||
"@types/fs-extra": "^8.1.0",
|
"@types/fs-extra": "^8.1.0",
|
||||||
"@types/yargs": "^15.0.4",
|
"@types/yargs": "^15.0.4",
|
||||||
"ava": "^3.8.1",
|
"ava": "^3.8.2",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"nyc": "^15.0.1",
|
"nyc": "^15.0.1",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue,.mjs",
|
"lint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue,.mjs",
|
||||||
"test": "echo \"no test\"",
|
"test": "tsc && nyc ava",
|
||||||
"compile": "tsc",
|
"compile": "tsc",
|
||||||
"ci": "yarn && node jenkins.js"
|
"ci": "yarn && node jenkins.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"/dist/"
|
"/dist/lib/"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sapphirecode/modelling": "^1.0.26",
|
"@sapphirecode/modelling": "^1.0.26",
|
||||||
|
24
test/.eslintrc.js
Normal file
24
test/.eslintrc.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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@sapphirecode.ovh>, May 2020
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
commonjs: true,
|
||||||
|
es6: true,
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'@sapphirecode/eslint-config-ts'
|
||||||
|
],
|
||||||
|
globals: {
|
||||||
|
Atomics: 'readonly',
|
||||||
|
SharedArrayBuffer: 'readonly'
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2018
|
||||||
|
}
|
||||||
|
}
|
39
test/PathType.ts
Normal file
39
test/PathType.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import test from 'ava';
|
||||||
|
import { PathType } from '../lib/Types/PathType';
|
||||||
|
|
||||||
|
test ('no file', async (t) => {
|
||||||
|
const validator = new PathType ('file');
|
||||||
|
await t.throwsAsync (
|
||||||
|
() => validator.to_type ('test'),
|
||||||
|
{ message: 'cannot assign folder to file' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test ('file', async (t) => {
|
||||||
|
const validator = new PathType ('file');
|
||||||
|
const res = await validator.to_type ('package.json');
|
||||||
|
t.is (res, 'package.json');
|
||||||
|
});
|
||||||
|
test ('no folder', async (t) => {
|
||||||
|
const validator = new PathType ('folder');
|
||||||
|
await t.throwsAsync (
|
||||||
|
() => validator.to_type ('package.json'),
|
||||||
|
{ message: 'cannot assign folder to file' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test ('folder', async (t) => {
|
||||||
|
const validator = new PathType ('folder');
|
||||||
|
const res = await validator.to_type ('test');
|
||||||
|
t.is (res, 'test');
|
||||||
|
});
|
||||||
|
test ('no path', async (t) => {
|
||||||
|
const validator = new PathType ('path');
|
||||||
|
await t.throwsAsync (
|
||||||
|
() => validator.to_type ('doesnotexist.file'),
|
||||||
|
{ message: 'path does not exist' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test ('path', async (t) => {
|
||||||
|
const validator = new PathType ('path');
|
||||||
|
const res = await validator.to_type ('test');
|
||||||
|
t.is (res, 'test');
|
||||||
|
});
|
41
test/TypeValidation.ts
Normal file
41
test/TypeValidation.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import test from 'ava';
|
||||||
|
import { TypeValidation } from '../lib/Types/TypeValidation';
|
||||||
|
|
||||||
|
test ('string', async (t) => {
|
||||||
|
const validator = new TypeValidation ('string');
|
||||||
|
const res = await validator.to_type ('foo');
|
||||||
|
t.is (res, 'foo');
|
||||||
|
});
|
||||||
|
test ('no number', (t) => {
|
||||||
|
const validator = new TypeValidation ('number');
|
||||||
|
t.throws (
|
||||||
|
() => validator.to_type ('foo'),
|
||||||
|
{ message: 'value is not a number' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test ('number', async (t) => {
|
||||||
|
const validator = new TypeValidation ('number');
|
||||||
|
const res = await validator.to_type ('123');
|
||||||
|
t.is (res, 123);
|
||||||
|
});
|
||||||
|
test ('no boolean', (t) => {
|
||||||
|
const validator = new TypeValidation ('boolean');
|
||||||
|
t.throws (
|
||||||
|
() => validator.to_type ('foo'),
|
||||||
|
{ message: 'value is not a boolean' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test ('boolean', async (t) => {
|
||||||
|
const validator = new TypeValidation ('boolean');
|
||||||
|
const r1 = await validator.to_type ('false');
|
||||||
|
const r2 = await validator.to_type ('true');
|
||||||
|
t.is (r1, false);
|
||||||
|
t.is (r2, true);
|
||||||
|
});
|
||||||
|
test ('boolean number', async (t) => {
|
||||||
|
const validator = new TypeValidation ('boolean');
|
||||||
|
const r1 = await validator.to_type (0);
|
||||||
|
const r2 = await validator.to_type (1);
|
||||||
|
t.is (r1, false);
|
||||||
|
t.is (r2, true);
|
||||||
|
});
|
@ -3,7 +3,7 @@
|
|||||||
"target": "es5",
|
"target": "es5",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"rootDir": "./lib",
|
"rootDir": "./",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
57
yarn.lock
57
yarn.lock
@ -269,9 +269,16 @@
|
|||||||
eslint-plugin-sort-requires-by-path "^1.0.2"
|
eslint-plugin-sort-requires-by-path "^1.0.2"
|
||||||
|
|
||||||
"@sapphirecode/modelling@^1.0.26":
|
"@sapphirecode/modelling@^1.0.26":
|
||||||
version "1.0.31"
|
version "1.0.32"
|
||||||
resolved "https://registry.yarnpkg.com/@sapphirecode/modelling/-/modelling-1.0.31.tgz#7d5e0d823dc7eedc9fd1e11bd1d4be562227c2b4"
|
resolved "https://registry.yarnpkg.com/@sapphirecode/modelling/-/modelling-1.0.32.tgz#d7871a464e619aa64892b27f44230327f714c2f1"
|
||||||
integrity sha512-YqG46seI6ewsyW2n16pUowue/BDV4aRgLvR3TBu4UMPaWUEl8G5Ezdx223QK2sWbilQbDxk70HdmQkgawzcc/g==
|
integrity sha512-iZrPI5/oguIGKvLz8rbAaz8InEYntGbuFQmb27ozp2RhjLCZMMSXRU4XZEOs4QyUe6TpmNWRXF44K/P9F4AqYg==
|
||||||
|
dependencies:
|
||||||
|
"@sapphirecode/utilities" "^1.0.39"
|
||||||
|
|
||||||
|
"@sapphirecode/utilities@^1.0.39":
|
||||||
|
version "1.0.39"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sapphirecode/utilities/-/utilities-1.0.39.tgz#12fdc564065102ea156188541941f92fbb126238"
|
||||||
|
integrity sha512-8IgL9Cw43ipUOIqyqXvgmvyR/s+4Mf1h75jIBwOniNM3x7EU2jJiE01ePDrdsNraaGNn6o4GnFMx6ymp07U2SQ==
|
||||||
|
|
||||||
"@sindresorhus/is@^0.14.0":
|
"@sindresorhus/is@^0.14.0":
|
||||||
version "0.14.0"
|
version "0.14.0"
|
||||||
@ -402,9 +409,9 @@ acorn-walk@^7.1.1:
|
|||||||
integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==
|
integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==
|
||||||
|
|
||||||
acorn@^7.1.1:
|
acorn@^7.1.1:
|
||||||
version "7.1.1"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe"
|
||||||
integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==
|
integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==
|
||||||
|
|
||||||
aggregate-error@^3.0.0:
|
aggregate-error@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
@ -567,10 +574,10 @@ at-least-node@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||||
|
|
||||||
ava@^3.8.1:
|
ava@^3.8.2:
|
||||||
version "3.8.1"
|
version "3.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/ava/-/ava-3.8.1.tgz#ec50814f8e6c769b8ed0dcc64bca990cd06bb2d1"
|
resolved "https://registry.yarnpkg.com/ava/-/ava-3.8.2.tgz#877c9eb861763a185bbabd54f359e1fbe57d1754"
|
||||||
integrity sha512-OPWrTxcf1EbtAaGGFQPLbx4AaVqPrFMumKOKn2SzIRo+RTKb33lF2aoVnWqBeZaJ68uSc9R6jqIE7qkG6O33uQ==
|
integrity sha512-sph3oUsVTGsq4qbgeWys03QKCmXjkZUO3oPnFWXEW6g1SReCY9vuONGghMgw1G6VOzkg1k+niqJsOzwfO8h9Ng==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@concordance/react" "^2.0.0"
|
"@concordance/react" "^2.0.0"
|
||||||
acorn "^7.1.1"
|
acorn "^7.1.1"
|
||||||
@ -603,7 +610,7 @@ ava@^3.8.1:
|
|||||||
indent-string "^4.0.0"
|
indent-string "^4.0.0"
|
||||||
is-error "^2.2.2"
|
is-error "^2.2.2"
|
||||||
is-plain-object "^3.0.0"
|
is-plain-object "^3.0.0"
|
||||||
is-promise "^3.0.0"
|
is-promise "^4.0.0"
|
||||||
lodash "^4.17.15"
|
lodash "^4.17.15"
|
||||||
matcher "^3.0.0"
|
matcher "^3.0.0"
|
||||||
md5-hex "^3.0.1"
|
md5-hex "^3.0.1"
|
||||||
@ -614,12 +621,12 @@ ava@^3.8.1:
|
|||||||
picomatch "^2.2.2"
|
picomatch "^2.2.2"
|
||||||
pkg-conf "^3.1.0"
|
pkg-conf "^3.1.0"
|
||||||
plur "^4.0.0"
|
plur "^4.0.0"
|
||||||
pretty-ms "^6.0.1"
|
pretty-ms "^7.0.0"
|
||||||
read-pkg "^5.2.0"
|
read-pkg "^5.2.0"
|
||||||
resolve-cwd "^3.0.0"
|
resolve-cwd "^3.0.0"
|
||||||
slash "^3.0.0"
|
slash "^3.0.0"
|
||||||
source-map-support "^0.5.19"
|
source-map-support "^0.5.19"
|
||||||
stack-utils "^2.0.1"
|
stack-utils "^2.0.2"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
supertap "^1.0.0"
|
supertap "^1.0.0"
|
||||||
temp-dir "^2.0.0"
|
temp-dir "^2.0.0"
|
||||||
@ -639,9 +646,9 @@ binary-extensions@^2.0.0:
|
|||||||
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
|
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
|
||||||
|
|
||||||
blueimp-md5@^2.10.0:
|
blueimp-md5@^2.10.0:
|
||||||
version "2.14.0"
|
version "2.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.14.0.tgz#ac45786ede87d2a985478040d2ac38a9af7a16ac"
|
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.15.0.tgz#ae945f87ca6c2c11e2562983e11450b0545f9bb3"
|
||||||
integrity sha512-fhX8JsIgugJ39g9MUJ4Y0S+WYd/1HATNVzW4nEVknP5uJU1mA7LZCV3OuVH9OvxpuYQXu6ttst0IYIlAyVfBQg==
|
integrity sha512-Zc6sowqlCWu3+V0bocZwdaPPXlRv14EHtYcQDCOghj9EdyKLMkAOODBh3HHAx5r7QRylDYCOaXa/b/edgBLDpA==
|
||||||
|
|
||||||
boxen@^4.2.0:
|
boxen@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
@ -1867,10 +1874,10 @@ is-plain-object@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isobject "^4.0.0"
|
isobject "^4.0.0"
|
||||||
|
|
||||||
is-promise@^3.0.0:
|
is-promise@^4.0.0:
|
||||||
version "3.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-3.0.0.tgz#1f88031af842d9203dc1777cba40411e848f9beb"
|
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3"
|
||||||
integrity sha512-aTHJ4BvETyySzLhguH+7sL4b8765eecqq7ZrHVuhZr3FjCL/IV+LsvisEeH+9d0AkChYny3ad1KEL+mKy4ot7A==
|
integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==
|
||||||
|
|
||||||
is-regex@^1.0.5:
|
is-regex@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
@ -2643,10 +2650,10 @@ prepend-http@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||||
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
||||||
|
|
||||||
pretty-ms@^6.0.1:
|
pretty-ms@^7.0.0:
|
||||||
version "6.0.1"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-6.0.1.tgz#03ec6cfee20329f142645e63efad96bb775d3da4"
|
resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.0.tgz#45781273110caf35f55cab21a8a9bd403a233dc0"
|
||||||
integrity sha512-ke4njoVmlotekHlHyCZ3wI/c5AMT8peuHs8rKJqekj/oR5G8lND2dVpicFlUz5cbZgE290vvkMuDwfj/OcW1kw==
|
integrity sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg==
|
||||||
dependencies:
|
dependencies:
|
||||||
parse-ms "^2.1.0"
|
parse-ms "^2.1.0"
|
||||||
|
|
||||||
@ -2999,7 +3006,7 @@ sprintf-js@~1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||||
|
|
||||||
stack-utils@^2.0.1:
|
stack-utils@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593"
|
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593"
|
||||||
integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==
|
integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==
|
||||||
|
Loading…
x
Reference in New Issue
Block a user