1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127//import * as util from 'util';
import { GluegunCommand } from 'gluegun';
import yargs, { Argv } from 'yargs';
import yargsParser from 'yargs-parser';
import { Command } from '../command';
import { IEvents } from '../events';
import { IOptions, Option } from '../options';
import { CLIRuntimeError } from './exceptions';
export type Registrator = (builder: Argv, command: Command, raw: any) => void;
export class Options implements IOptions {
private _raw: any;
private _parser: Argv;
private _parsed?: any;
private _error?: Error;
private _registrators: Registrator[];
constructor(events: IEvents, argv: string[]) {
this._parser = yargs(argv).help(false).version(false);
this._registrators = [];
this._parsed = null;
this._raw = yargsParser(argv);
events.on('command.options.register', (command: Command) => {
const cmd = command as any as GluegunCommand;
const name = cmd.commandPath
?.concat(...[command.settings?.parameters ?? ''])
.filter((p) => p != '')
.join(' ');
this._parser.fail((message, err) => {
this._error = err || new CLIRuntimeError(message);
});
this._parser.showHelpOnFail(false).exitProcess(false);
this._parser.command(
name ?? '$0',
cmd.description ?? '',
(builder) => {
for (const registrator of this._registrators) {
registrator(builder, command, this._raw);
}
return this._parser;
},
(values) => {
this._parsed = values;
}
);
this._parser.argv;
});
}
register(registrator: Registrator): void {
this._registrators.push(registrator);
}
feature(name: string, registrator: Registrator): void {
this._registrators.push((options: Argv, command: Command) => {
const { settings } = command;
if (!settings) {
return;
}
const { features } = settings;
if (!features || !(name in features) || !features[name]) {
return;
}
registrator(options, command, this._raw);
});
}
failed(): boolean {
return !!this._error;
}
error(): Error | undefined {
return this._error;
}
values(): any {
return this._parsed || {};
}
list(): Option[] {
const freeParser = this._parser as any;
const usage = freeParser.getUsageInstance();
const descriptions = usage.getDescriptions();
const keys = Object.keys(descriptions);
const options = freeParser.getOptions() as any;
const positionalGroup = usage.getPositionalGroupName() as any;
const groups = freeParser.getGroups() as any;
return keys.map<Option>((key) => {
const name = key;
const description = descriptions[key];
const value = options.default[key] ?? undefined;
const required = key in options.demandedOptions;
const choices = options.choices[key];
let type = 'string';
if (options.boolean.indexOf(key) >= 0) {
type = 'boolean';
} else if (options.number.indexOf(key) >= 0) {
type = 'number';
}
let positional = false;
if (positionalGroup in groups) {
positional = groups[positionalGroup].indexOf(key) >= 0;
}
return {
name,
description,
type,
required,
choices,
positional,
default: value,
};
});
}
}