@travetto/cli 2.1.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.ts +1 -1
- package/package.json +2 -2
- package/src/color.ts +15 -10
- package/src/execute.ts +10 -7
- package/src/help.ts +5 -5
- package/src/plugin-base.ts +25 -22
- package/src/plugin.ts +5 -4
- package/src/util.ts +20 -18
package/bin/cli.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { EnvUtil } from '@travetto/boot';
|
|
|
6
6
|
/**
|
|
7
7
|
* Entry point
|
|
8
8
|
*/
|
|
9
|
-
export async function main() {
|
|
9
|
+
export async function main(): Promise<void> {
|
|
10
10
|
if (!EnvUtil.isFalse('TRV_CLI_LOCAL') && !PathUtil.toUnix(__filename).includes(PathUtil.cwd)) { // If the current file is not under the working directory
|
|
11
11
|
console.error(`
|
|
12
12
|
The @travetto/cli is not intended to be installed globally. Please install it within your local project
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cli",
|
|
3
3
|
"displayName": "Command Line Interface",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"description": "CLI infrastructure for travetto framework",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"cli",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"directory": "module/cli"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/base": "^2.
|
|
30
|
+
"@travetto/base": "^2.2.0",
|
|
31
31
|
"commander": "^9.4.0"
|
|
32
32
|
},
|
|
33
33
|
"docDependencies": {
|
package/src/color.ts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { ColorUtil } from '@travetto/boot/src/color';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* Colorize a string, as a string interpolation
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```
|
|
8
|
-
* color`${{title: 'Main Title'}} is ${{subtitle: 'Sub Title}}`
|
|
9
|
-
* ```
|
|
10
|
-
*/
|
|
11
|
-
export const color = ColorUtil.makeTemplate({
|
|
3
|
+
const colorSet = {
|
|
12
4
|
input: ColorUtil.makeColorer('yellow'),
|
|
13
5
|
output: ColorUtil.makeColorer('magenta'),
|
|
14
6
|
path: ColorUtil.makeColorer('cyan'),
|
|
@@ -21,4 +13,17 @@ export const color = ColorUtil.makeTemplate({
|
|
|
21
13
|
identifier: ColorUtil.makeColorer('blue', 'bold'),
|
|
22
14
|
subtitle: ColorUtil.makeColorer('white'),
|
|
23
15
|
subsubtitle: ColorUtil.makeColorer('white', 'faint')
|
|
24
|
-
}
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Colorize a string, as a string interpolation
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```
|
|
23
|
+
* color`${{title: 'Main Title'}} is ${{subtitle: 'Sub Title}}`
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export const color = ColorUtil.makeTemplate(colorSet);
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export type ColoredElement = keyof typeof colorSet;
|
package/src/execute.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { program as commander } from 'commander';
|
|
2
2
|
|
|
3
3
|
import { CliUtil } from './util';
|
|
4
4
|
import { CompletionConfig } from './types';
|
|
@@ -14,10 +14,10 @@ export class ExecutionManager {
|
|
|
14
14
|
/**
|
|
15
15
|
* Run tab completion given the full args list
|
|
16
16
|
*/
|
|
17
|
-
static async runCompletion(args: string[]) {
|
|
18
|
-
const
|
|
19
|
-
await PluginManager.loadAllPlugins(x => x.setupCompletion(
|
|
20
|
-
const res = await CliUtil.getCompletion(
|
|
17
|
+
static async runCompletion(args: string[]): Promise<void> {
|
|
18
|
+
const cfg: CompletionConfig = { all: [], task: {} };
|
|
19
|
+
await PluginManager.loadAllPlugins(x => x.setupCompletion(cfg));
|
|
20
|
+
const res = await CliUtil.getCompletion(cfg, args.slice(3));
|
|
21
21
|
console.log(res.join(' '));
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
@@ -25,7 +25,7 @@ export class ExecutionManager {
|
|
|
25
25
|
/**
|
|
26
26
|
* Run plugin
|
|
27
27
|
*/
|
|
28
|
-
static async runPlugin(args: string[]) {
|
|
28
|
+
static async runPlugin(args: string[]): Promise<void> {
|
|
29
29
|
const cmd = args[2];
|
|
30
30
|
|
|
31
31
|
let plugin;
|
|
@@ -45,6 +45,9 @@ export class ExecutionManager {
|
|
|
45
45
|
commander.parse(args);
|
|
46
46
|
}
|
|
47
47
|
} catch (err) {
|
|
48
|
+
if (!(err instanceof Error)) {
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
48
51
|
return plugin.showHelp(err);
|
|
49
52
|
}
|
|
50
53
|
}
|
|
@@ -53,7 +56,7 @@ export class ExecutionManager {
|
|
|
53
56
|
* Execute the command line
|
|
54
57
|
* @param args argv
|
|
55
58
|
*/
|
|
56
|
-
static async run(args: string[]) {
|
|
59
|
+
static async run(args: string[]): Promise<void> {
|
|
57
60
|
const width = +(process.env.TRV_CONSOLE_WIDTH ?? process.stdout.columns ?? 120);
|
|
58
61
|
commander
|
|
59
62
|
.version(version)
|
package/src/help.ts
CHANGED
|
@@ -11,7 +11,7 @@ export class HelpUtil {
|
|
|
11
11
|
* @param text Source text
|
|
12
12
|
* @param key
|
|
13
13
|
*/
|
|
14
|
-
static extractValue(text: string, key: string) {
|
|
14
|
+
static extractValue(text: string, key: string): readonly [string, string] {
|
|
15
15
|
let sub = '';
|
|
16
16
|
if (text.includes(key)) {
|
|
17
17
|
const start = text.indexOf(key);
|
|
@@ -28,7 +28,7 @@ export class HelpUtil {
|
|
|
28
28
|
/**
|
|
29
29
|
* Colorize Usage
|
|
30
30
|
*/
|
|
31
|
-
static colorizeOptions(option: string) {
|
|
31
|
+
static colorizeOptions(option: string): string {
|
|
32
32
|
return option.replace(/(\s*)(-[^, ]+)(,?\s*)(--\S+)?((\s+)?((?:\[[^\]]+\])|(?:\<[^>]+>)))?((\s+)(.*))?/g, (
|
|
33
33
|
p: string, spacing: string,
|
|
34
34
|
simpleParam: string, pSep: string,
|
|
@@ -59,7 +59,7 @@ export class HelpUtil {
|
|
|
59
59
|
/**
|
|
60
60
|
* Colorize command section
|
|
61
61
|
*/
|
|
62
|
-
static colorizeCommands(commands: string) {
|
|
62
|
+
static colorizeCommands(commands: string): string {
|
|
63
63
|
return commands
|
|
64
64
|
.replace(/\s([^\[\]]\S+)/g, param => color`${{ param }}`)
|
|
65
65
|
.replace(/(\s*[^\x1b]\[[^\]]+\])/g, input => color`${{ input }}`) // eslint-disable-line no-control-regex
|
|
@@ -69,14 +69,14 @@ export class HelpUtil {
|
|
|
69
69
|
/**
|
|
70
70
|
* Colorize usage
|
|
71
71
|
*/
|
|
72
|
-
static colorizeUsage(usage: string) {
|
|
72
|
+
static colorizeUsage(usage: string): string {
|
|
73
73
|
return usage.replace(/Usage:/, title => color`${{ title }}`);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Get full help text
|
|
78
78
|
*/
|
|
79
|
-
static getHelpText(text: string, extraText?: string) {
|
|
79
|
+
static getHelpText(text: string, extraText?: string): string {
|
|
80
80
|
const [usage, text2] = this.extractValue(text, 'Usage:');
|
|
81
81
|
const [options, text3] = this.extractValue(text2, 'Options:');
|
|
82
82
|
const [commands, textFinal] = this.extractValue(text3, 'Commands:');
|
package/src/plugin-base.ts
CHANGED
|
@@ -6,7 +6,9 @@ import { CliUtil } from './util';
|
|
|
6
6
|
|
|
7
7
|
type Completion = Record<string, string[]>;
|
|
8
8
|
|
|
9
|
-
type
|
|
9
|
+
type ParamPrimitive = string | number | boolean | string[] | number[];
|
|
10
|
+
|
|
11
|
+
type ParamConfig<K extends ParamPrimitive = ParamPrimitive> = {
|
|
10
12
|
type?: Function;
|
|
11
13
|
key?: string;
|
|
12
14
|
short?: string | false;
|
|
@@ -14,12 +16,12 @@ type ParamConfig<K> = {
|
|
|
14
16
|
desc: string;
|
|
15
17
|
completion?: boolean;
|
|
16
18
|
def?: K;
|
|
17
|
-
choices?: K[];
|
|
19
|
+
choices?: K[] | readonly K[];
|
|
18
20
|
combine?: (v: string, curr: K) => K;
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
-
type ParamMap<T = any> = { [key in keyof T]: ParamConfig<T[key]> };
|
|
24
|
+
type ParamMap<T = any> = { [key in keyof T]: T[key] extends ParamPrimitive ? ParamConfig<T[key]> : never };
|
|
23
25
|
|
|
24
26
|
type Shape<M extends ParamMap> = { [k in keyof M]: Exclude<M[k]['def'], undefined> };
|
|
25
27
|
|
|
@@ -82,14 +84,16 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
82
84
|
/**
|
|
83
85
|
* Define option
|
|
84
86
|
*/
|
|
85
|
-
choiceOption<K>({ choices, ...cfg }: ParamConfig<
|
|
86
|
-
|
|
87
|
+
choiceOption<K extends string | number>({ choices, ...cfg }: ParamConfig<K> & { choices: K[] | readonly K[] }): ParamConfig<K> {
|
|
88
|
+
const config: ParamConfig<K> = {
|
|
87
89
|
type: String,
|
|
88
|
-
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
91
|
+
combine: (v: string, acc: K): K => choices.includes(v as K) ? v as K : acc,
|
|
89
92
|
choices,
|
|
90
93
|
completion: true,
|
|
91
94
|
...cfg
|
|
92
|
-
}
|
|
95
|
+
};
|
|
96
|
+
return config;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
/**
|
|
@@ -131,7 +135,7 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
131
135
|
/**
|
|
132
136
|
* Pre-compile on every cli execution
|
|
133
137
|
*/
|
|
134
|
-
async build() {
|
|
138
|
+
async build(): Promise<void> {
|
|
135
139
|
await (await import('@travetto/base/bin/lib/'))
|
|
136
140
|
.BuildUtil.build();
|
|
137
141
|
}
|
|
@@ -139,14 +143,15 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
139
143
|
/**
|
|
140
144
|
* Expose configuration as constrained typed object
|
|
141
145
|
*/
|
|
142
|
-
get cmd() {
|
|
146
|
+
get cmd(): Shape<ReturnType<Exclude<this['getOptions'], undefined>>> {
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
143
148
|
return this.#cmd.opts() as Shape<ReturnType<Exclude<this['getOptions'], undefined>>>;
|
|
144
149
|
}
|
|
145
150
|
|
|
146
151
|
/**
|
|
147
152
|
* Expose command line arguments
|
|
148
153
|
*/
|
|
149
|
-
get args() {
|
|
154
|
+
get args(): string[] {
|
|
150
155
|
return this.#cmd.args;
|
|
151
156
|
}
|
|
152
157
|
|
|
@@ -164,11 +169,11 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
164
169
|
* Process all options into final set before registering with commander
|
|
165
170
|
* @returns
|
|
166
171
|
*/
|
|
167
|
-
async finalizeOptions() {
|
|
168
|
-
const opts = this.getOptions?.()
|
|
172
|
+
async finalizeOptions(): Promise<ParamConfig[]> {
|
|
173
|
+
const opts = this.getOptions?.();
|
|
169
174
|
const used = new Set();
|
|
170
175
|
|
|
171
|
-
return Object.entries(opts
|
|
176
|
+
return (opts ? Object.entries(opts) : []).map(([k, cfg]) => {
|
|
172
177
|
cfg.key = k;
|
|
173
178
|
cfg.name ??= k.replace(/([a-z])([A-Z])/g, (_, l, r: string) => `${l}-${r.toLowerCase()}`);
|
|
174
179
|
if (cfg.short === undefined) {
|
|
@@ -185,7 +190,7 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
185
190
|
/**
|
|
186
191
|
* Receive the commander object, and process
|
|
187
192
|
*/
|
|
188
|
-
async setup(cmd: commander.Command) {
|
|
193
|
+
async setup(cmd: commander.Command): Promise<commander.Command> {
|
|
189
194
|
cmd = cmd.command(this.name);
|
|
190
195
|
if (this.allowUnknownOptions) {
|
|
191
196
|
cmd = cmd.allowUnknownOption(true);
|
|
@@ -201,7 +206,7 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
201
206
|
if (cfg.type !== Boolean || cfg.def) {
|
|
202
207
|
key = `${key} <${cfg.name}>`;
|
|
203
208
|
}
|
|
204
|
-
cmd = cfg.combine ? cmd.option(key, cfg.desc, cfg.combine, cfg.def) : cmd.option(key, cfg.desc, cfg.def);
|
|
209
|
+
cmd = cfg.combine ? cmd.option(key, cfg.desc, cfg.combine, cfg.def) : cmd.option(key, cfg.desc, (cur, acc) => cur, cfg.def);
|
|
205
210
|
}
|
|
206
211
|
|
|
207
212
|
cmd = cmd.action(this.runAction.bind(this));
|
|
@@ -211,7 +216,7 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
211
216
|
/**
|
|
212
217
|
* Runs the action at execution time
|
|
213
218
|
*/
|
|
214
|
-
async runAction(...args: unknown[]) {
|
|
219
|
+
async runAction(...args: unknown[]): Promise<void> {
|
|
215
220
|
await this.envInit?.();
|
|
216
221
|
await this.build();
|
|
217
222
|
return await this.action(...args);
|
|
@@ -220,7 +225,7 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
220
225
|
/**
|
|
221
226
|
* Collection tab completion information
|
|
222
227
|
*/
|
|
223
|
-
async setupCompletion(config: CompletionConfig) {
|
|
228
|
+
async setupCompletion(config: CompletionConfig): Promise<void> {
|
|
224
229
|
const task = await this.complete();
|
|
225
230
|
config.all = [...config.all, this.name];
|
|
226
231
|
if (task) {
|
|
@@ -232,16 +237,14 @@ export abstract class BasePlugin<V extends ParamMap = ParamMap> {
|
|
|
232
237
|
* Return tab completion information
|
|
233
238
|
*/
|
|
234
239
|
async complete(): Promise<Completion | void> {
|
|
235
|
-
const out: Completion = {
|
|
236
|
-
'': [] as string[]
|
|
237
|
-
};
|
|
240
|
+
const out: Completion = { '': [] };
|
|
238
241
|
for (const el of await this.finalizeOptions()) {
|
|
239
242
|
if (el.completion) {
|
|
240
243
|
out[''] = [...out['']!, `--${el.name} `];
|
|
241
244
|
if (el.choices) {
|
|
242
|
-
out[`--${el.name} `] = el.choices;
|
|
245
|
+
out[`--${el.name} `] = el.choices.map(x => `${x}`);
|
|
243
246
|
if (el.short) {
|
|
244
|
-
out[`- ${el.short} `] = el.choices;
|
|
247
|
+
out[`- ${el.short} `] = el.choices.map(x => `${x}`);
|
|
245
248
|
}
|
|
246
249
|
}
|
|
247
250
|
}
|
package/src/plugin.ts
CHANGED
|
@@ -12,7 +12,7 @@ const PLUGIN_PACKAGE = [
|
|
|
12
12
|
[/^openapi:(spec|client)$/, 'openapi', true],
|
|
13
13
|
[/^email:(compile|dev)$/, 'email-template', false],
|
|
14
14
|
[/^pack(:assemble|:zip|:docker)?$/, 'pack', false],
|
|
15
|
-
] as
|
|
15
|
+
] as const;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Manages loading and finding all plugins
|
|
@@ -22,7 +22,7 @@ export class PluginManager {
|
|
|
22
22
|
/**
|
|
23
23
|
* Get list of all plugins available
|
|
24
24
|
*/
|
|
25
|
-
static getPluginMapping() {
|
|
25
|
+
static getPluginMapping(): Map<string, string> {
|
|
26
26
|
const all = new Map<string, string>();
|
|
27
27
|
for (const { file } of SourceIndex.find({ folder: 'bin', filter: /bin\/cli-/ })) {
|
|
28
28
|
all.set(file.replace(/^.*\/bin\/.+?-(.*?)[.][^.]*$/, (_, f) => f), file);
|
|
@@ -47,7 +47,8 @@ ${{ identifier: `npm i ${prod ? '' : '--save-dev '}@travetto/${pkg}` }}`);
|
|
|
47
47
|
}
|
|
48
48
|
throw new Error(`Unknown command: ${cmd}`);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
const values = Object.values<{ new(...args: unknown[]): unknown }>(await import(f));
|
|
51
|
+
for (const v of values) {
|
|
51
52
|
try {
|
|
52
53
|
const inst = new v();
|
|
53
54
|
if (inst instanceof BasePlugin) {
|
|
@@ -64,7 +65,7 @@ ${{ identifier: `npm i ${prod ? '' : '--save-dev '}@travetto/${pkg}` }}`);
|
|
|
64
65
|
/**
|
|
65
66
|
* Load all available plugins
|
|
66
67
|
*/
|
|
67
|
-
static async loadAllPlugins(op?: (p: BasePlugin) => unknown | Promise<unknown>) {
|
|
68
|
+
static async loadAllPlugins(op?: (p: BasePlugin) => unknown | Promise<unknown>): Promise<BasePlugin[]> {
|
|
68
69
|
return Promise.all(
|
|
69
70
|
[...this.getPluginMapping().keys()]
|
|
70
71
|
.sort((a, b) => a.localeCompare(b))
|
package/src/util.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
|
+
import { Writable } from 'stream';
|
|
3
|
+
|
|
2
4
|
import { CompletionConfig } from './types';
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -8,20 +10,20 @@ export class CliUtil {
|
|
|
8
10
|
|
|
9
11
|
static #waitState = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'.split('');
|
|
10
12
|
|
|
11
|
-
static isBoolean(x: string) {
|
|
13
|
+
static isBoolean(x: string): boolean {
|
|
12
14
|
return /^(1|0|yes|no|on|off|auto|true|false)$/i.test(x);
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
static toBool(x: string | boolean, def: boolean): boolean;
|
|
16
18
|
static toBool(x?: string | boolean, def?: boolean): boolean | undefined;
|
|
17
|
-
static toBool(x?: string | boolean, def?: boolean) {
|
|
19
|
+
static toBool(x?: string | boolean, def?: boolean): boolean | undefined {
|
|
18
20
|
return x === undefined ? true :
|
|
19
21
|
(typeof x === 'boolean' ? x :
|
|
20
22
|
(this.isBoolean(x) ? /^(1|yes|on|true)$/i.test(x) :
|
|
21
23
|
def));
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
static toInt(l: number | undefined, u: number | undefined, v: string, d: number) {
|
|
26
|
+
static toInt(l: number | undefined, u: number | undefined, v: string, d: number): number {
|
|
25
27
|
let n = +v;
|
|
26
28
|
if (l === undefined && u === undefined) {
|
|
27
29
|
return n;
|
|
@@ -38,7 +40,7 @@ export class CliUtil {
|
|
|
38
40
|
/**
|
|
39
41
|
* Get code completion values
|
|
40
42
|
*/
|
|
41
|
-
static async getCompletion(
|
|
43
|
+
static async getCompletion(cfg: CompletionConfig, args: string[]): Promise<string[]> {
|
|
42
44
|
args = args.slice(0); // Copy as we mutate
|
|
43
45
|
|
|
44
46
|
const cmd = args.shift()!;
|
|
@@ -47,27 +49,27 @@ export class CliUtil {
|
|
|
47
49
|
let opts: string[] = [];
|
|
48
50
|
|
|
49
51
|
// List all commands
|
|
50
|
-
if (!
|
|
51
|
-
opts =
|
|
52
|
+
if (!cfg.task[cmd]) {
|
|
53
|
+
opts = cfg.all;
|
|
52
54
|
} else {
|
|
53
55
|
// Look available sub commands
|
|
54
56
|
last = args.pop() ?? '';
|
|
55
57
|
const second = args.pop() ?? '';
|
|
56
58
|
let flag = '';
|
|
57
59
|
|
|
58
|
-
if (last in
|
|
60
|
+
if (last in cfg.task[cmd]) {
|
|
59
61
|
flag = last;
|
|
60
62
|
last = '';
|
|
61
|
-
} else if (second in
|
|
63
|
+
} else if (second in cfg.task[cmd]) {
|
|
62
64
|
// Look for available flags
|
|
63
|
-
if (
|
|
65
|
+
if (cfg.task[cmd][second].includes(last)) {
|
|
64
66
|
flag = '';
|
|
65
67
|
last = '';
|
|
66
68
|
} else {
|
|
67
69
|
flag = second;
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
|
-
opts =
|
|
72
|
+
opts = cfg.task[cmd][flag];
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
return last ? opts.filter(x => x.startsWith(last)) : opts.filter(x => !x.startsWith('-'));
|
|
@@ -79,7 +81,7 @@ export class CliUtil {
|
|
|
79
81
|
* @param text Text, if desired
|
|
80
82
|
* @param clear Should the entire line be cleared?
|
|
81
83
|
*/
|
|
82
|
-
static async rewriteLine(stream:
|
|
84
|
+
static async rewriteLine(stream: Writable, text?: string, clear = false): Promise<void> {
|
|
83
85
|
await new Promise<void>(r => readline.cursorTo(stream, 0, undefined, () => {
|
|
84
86
|
if (clear) {
|
|
85
87
|
readline.clearLine(stream, 0);
|
|
@@ -92,7 +94,7 @@ export class CliUtil {
|
|
|
92
94
|
}));
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
static sleep(ms: number) {
|
|
97
|
+
static sleep(ms: number): Promise<void> {
|
|
96
98
|
return new Promise(r => setTimeout(r, ms));
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -103,8 +105,8 @@ export class CliUtil {
|
|
|
103
105
|
* @param delay Delay duration
|
|
104
106
|
*/
|
|
105
107
|
static async waiting<T>(message: string, work: Promise<T> | (() => Promise<T>),
|
|
106
|
-
config: { completion?: string, delay?: number, stream?:
|
|
107
|
-
) {
|
|
108
|
+
config: { completion?: string, delay?: number, stream?: Writable } = {}
|
|
109
|
+
): Promise<T> {
|
|
108
110
|
const { stream, delay, completion } = { delay: 1000, stream: process.stderr, ...config };
|
|
109
111
|
|
|
110
112
|
const writeLine = this.rewriteLine.bind(this, stream);
|
|
@@ -120,10 +122,10 @@ export class CliUtil {
|
|
|
120
122
|
let i = -1;
|
|
121
123
|
let done = false;
|
|
122
124
|
let value: T | undefined;
|
|
123
|
-
let
|
|
125
|
+
let capturedError: Error | undefined;
|
|
124
126
|
const final = work
|
|
125
127
|
.then(res => value = res)
|
|
126
|
-
.catch(
|
|
128
|
+
.catch(err => capturedError = err)
|
|
127
129
|
.finally(() => done = true);
|
|
128
130
|
|
|
129
131
|
if (delay) {
|
|
@@ -138,8 +140,8 @@ export class CliUtil {
|
|
|
138
140
|
if (i >= 0) {
|
|
139
141
|
await writeLine(completion ? `${message} ${completion}\n` : '', true);
|
|
140
142
|
}
|
|
141
|
-
if (
|
|
142
|
-
throw
|
|
143
|
+
if (capturedError) {
|
|
144
|
+
throw capturedError;
|
|
143
145
|
} else {
|
|
144
146
|
return value!;
|
|
145
147
|
}
|