@travetto/cli 3.0.0-rc.1 → 3.0.0-rc.11
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/README.md +10 -16
- package/__index__.ts +7 -0
- package/package.json +11 -10
- package/src/color.ts +17 -27
- package/src/command-manager.ts +50 -29
- package/src/command.ts +52 -62
- package/src/execute.ts +47 -33
- package/src/help.ts +123 -38
- package/src/module.ts +189 -0
- package/src/scm.ts +76 -0
- package/support/cli.exec.ts +53 -0
- package/support/cli.list.ts +47 -0
- package/support/cli.ts +3 -0
- package/support/cli.version-sync.ts +17 -0
- package/bin/bash-complete.sh +0 -18
- package/bin/cli.ts +0 -28
- package/bin/trv.js +0 -3
- package/src/types.ts +0 -17
- package/src/util.ts +0 -146
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- This file was generated by @travetto/doc and should not be modified directly -->
|
|
2
|
-
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/cli/
|
|
2
|
+
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/cli/DOC.ts and execute "npx trv doc" to rebuild -->
|
|
3
3
|
# Command Line Interface
|
|
4
4
|
## CLI infrastructure for travetto framework
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ npm install @travetto/cli
|
|
|
10
10
|
|
|
11
11
|
The cli is the primary structure for interacting with the external requirements of the framework. This can range from running tests, to running applications, to generating email templates. The main executable can be installed globally or locally. If installed globally and locally, it will defer to the local installation for execution.
|
|
12
12
|
|
|
13
|
-
As is the custom, modules are able to register their own cli extensions as scripts, whose name starts with `cli
|
|
13
|
+
As is the custom, modules are able to register their own cli extensions as scripts, whose name starts with `cli.`. These scripts are then picked up at runtime and all available options are provided when viewing the help documentation. The following are all the supported cli operations and the various settings they allow.
|
|
14
14
|
|
|
15
15
|
## General
|
|
16
16
|
|
|
@@ -21,17 +21,12 @@ $ trv --help
|
|
|
21
21
|
Usage: [options] [command]
|
|
22
22
|
|
|
23
23
|
Options:
|
|
24
|
-
-V, --version
|
|
25
|
-
-h, --help
|
|
24
|
+
-V, --version output the version number
|
|
25
|
+
-h, --help display help for command
|
|
26
26
|
|
|
27
27
|
Commands:
|
|
28
|
-
build [options]
|
|
29
|
-
clean [options]
|
|
30
|
-
doc [options]
|
|
31
28
|
echo [options] [args...]
|
|
32
|
-
|
|
33
|
-
test [options] [regexes...]
|
|
34
|
-
help [command] display help for command
|
|
29
|
+
help [command] display help for command
|
|
35
30
|
```
|
|
36
31
|
|
|
37
32
|
This will show all the available options/choices that are exposed given the currently installed modules.
|
|
@@ -42,8 +37,7 @@ Extending the `cli` is fairly straightforward. It is built upon [commander](htt
|
|
|
42
37
|
|
|
43
38
|
**Code: Echo Command**
|
|
44
39
|
```typescript
|
|
45
|
-
import '@travetto/
|
|
46
|
-
import { CliCommand } from '@travetto/cli/src/command';
|
|
40
|
+
import { CliCommand } from '@travetto/cli';
|
|
47
41
|
|
|
48
42
|
/**
|
|
49
43
|
* `npx trv echo`
|
|
@@ -54,7 +48,7 @@ export class CliEchoCommand extends CliCommand {
|
|
|
54
48
|
name = 'echo';
|
|
55
49
|
|
|
56
50
|
getOptions() {
|
|
57
|
-
return { uppercase: this.boolOption({ desc: 'Upper case'
|
|
51
|
+
return { uppercase: this.boolOption({ desc: 'Upper case' }) };
|
|
58
52
|
}
|
|
59
53
|
|
|
60
54
|
getArgs() {
|
|
@@ -65,7 +59,7 @@ export class CliEchoCommand extends CliCommand {
|
|
|
65
59
|
if (this.cmd.uppercase) {
|
|
66
60
|
args = args.map(x => x.toUpperCase());
|
|
67
61
|
}
|
|
68
|
-
console.log(args);
|
|
62
|
+
console.log!(args);
|
|
69
63
|
}
|
|
70
64
|
}
|
|
71
65
|
```
|
|
@@ -79,7 +73,7 @@ $ trv echo --help
|
|
|
79
73
|
Usage: echo [options] [args...]
|
|
80
74
|
|
|
81
75
|
Options:
|
|
82
|
-
-u, --uppercase Upper case
|
|
76
|
+
-u, --uppercase Upper case
|
|
83
77
|
-h, --help display help for command
|
|
84
78
|
```
|
|
85
79
|
|
|
@@ -89,5 +83,5 @@ And actually using it:
|
|
|
89
83
|
```bash
|
|
90
84
|
$ trv echo -u bOb rOb DRoP
|
|
91
85
|
|
|
92
|
-
[ '
|
|
86
|
+
[ 'BOB', 'ROB', 'DROP' ]
|
|
93
87
|
```
|
package/__index__.ts
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cli",
|
|
3
|
-
"
|
|
4
|
-
"version": "3.0.0-rc.1",
|
|
3
|
+
"version": "3.0.0-rc.11",
|
|
5
4
|
"description": "CLI infrastructure for travetto framework",
|
|
6
5
|
"keywords": [
|
|
7
6
|
"cli",
|
|
@@ -15,23 +14,25 @@
|
|
|
15
14
|
"name": "Travetto Framework"
|
|
16
15
|
},
|
|
17
16
|
"files": [
|
|
17
|
+
"__index__.ts",
|
|
18
18
|
"src",
|
|
19
|
+
"support",
|
|
19
20
|
"bin"
|
|
20
21
|
],
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"trv": "bin/trv.js"
|
|
24
|
-
},
|
|
22
|
+
"bin": "trv.js",
|
|
23
|
+
"main": "__index__.ts",
|
|
25
24
|
"repository": {
|
|
26
25
|
"url": "https://github.com/travetto/travetto.git",
|
|
27
26
|
"directory": "module/cli"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
|
-
"@travetto/base": "^3.0.0-rc.
|
|
31
|
-
"
|
|
29
|
+
"@travetto/base": "^3.0.0-rc.10",
|
|
30
|
+
"@travetto/terminal": "^3.0.0-rc.6",
|
|
31
|
+
"@travetto/worker": "^3.0.0-rc.10",
|
|
32
|
+
"commander": "^10.0.0"
|
|
32
33
|
},
|
|
33
|
-
"
|
|
34
|
-
"
|
|
34
|
+
"travetto": {
|
|
35
|
+
"displayName": "Command Line Interface"
|
|
35
36
|
},
|
|
36
37
|
"publishConfig": {
|
|
37
38
|
"access": "public"
|
package/src/color.ts
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Util } from '@travetto/base';
|
|
2
|
+
import { GlobalTerminal } from '@travetto/terminal';
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
input:
|
|
5
|
-
output:
|
|
6
|
-
path:
|
|
7
|
-
success:
|
|
8
|
-
failure:
|
|
9
|
-
param:
|
|
10
|
-
type:
|
|
11
|
-
description:
|
|
12
|
-
title:
|
|
13
|
-
identifier:
|
|
14
|
-
subtitle:
|
|
15
|
-
subsubtitle:
|
|
16
|
-
}
|
|
4
|
+
const tplFn = GlobalTerminal.templateFunction({
|
|
5
|
+
input: 'oliveDrab',
|
|
6
|
+
output: 'pink',
|
|
7
|
+
path: 'teal',
|
|
8
|
+
success: 'green',
|
|
9
|
+
failure: 'red',
|
|
10
|
+
param: 'yellow',
|
|
11
|
+
type: 'cyan',
|
|
12
|
+
description: 'white',
|
|
13
|
+
title: 'brightWhite',
|
|
14
|
+
identifier: 'dodgerBlue',
|
|
15
|
+
subtitle: 'lightGray',
|
|
16
|
+
subsubtitle: 'darkGray'
|
|
17
|
+
});
|
|
17
18
|
|
|
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;
|
|
19
|
+
export const cliTpl = Util.makeTemplate(tplFn);
|
package/src/command-manager.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ShutdownManager } from '@travetto/base';
|
|
2
|
+
import { RootIndex } from '@travetto/manifest';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { cliTpl } from './color';
|
|
4
5
|
import { CliCommand } from './command';
|
|
5
6
|
|
|
6
7
|
const COMMAND_PACKAGE = [
|
|
7
8
|
[/^run$/, 'app', true],
|
|
8
|
-
[/^compile$/, 'compiler', true],
|
|
9
9
|
[/^test$/, 'test', false],
|
|
10
|
-
[/^
|
|
10
|
+
[/^service$/, 'command', true],
|
|
11
11
|
[/^model:(install|export)$/, 'model', true],
|
|
12
12
|
[/^openapi:(spec|client)$/, 'openapi', true],
|
|
13
13
|
[/^email:(compile|dev)$/, 'email-template', false],
|
|
@@ -24,8 +24,8 @@ export class CliCommandManager {
|
|
|
24
24
|
*/
|
|
25
25
|
static getCommandMapping(): Map<string, string> {
|
|
26
26
|
const all = new Map<string, string>();
|
|
27
|
-
for (const {
|
|
28
|
-
all.set(
|
|
27
|
+
for (const { outputFile: output, import: imp } of RootIndex.findSupport({ filter: /\/cli[.]/, checkProfile: false })) {
|
|
28
|
+
all.set(output.replace(/^.*\/cli[.](.*?)[.][^.]+$/, (_, f) => f), imp);
|
|
29
29
|
}
|
|
30
30
|
return all;
|
|
31
31
|
}
|
|
@@ -33,43 +33,64 @@ export class CliCommandManager {
|
|
|
33
33
|
/**
|
|
34
34
|
* Load command
|
|
35
35
|
*/
|
|
36
|
-
static async loadCommand(
|
|
36
|
+
static async loadCommand(
|
|
37
|
+
cmd: string,
|
|
38
|
+
cfg: {
|
|
39
|
+
filter?: (p: CliCommand) => boolean;
|
|
40
|
+
failOnMissing?: boolean;
|
|
41
|
+
} = {}
|
|
42
|
+
): Promise<CliCommand | undefined> {
|
|
37
43
|
const command = cmd.replace(/:/g, '_');
|
|
38
|
-
const
|
|
39
|
-
if (!
|
|
40
|
-
const
|
|
41
|
-
if (
|
|
42
|
-
const [, pkg, prod] =
|
|
43
|
-
console.error(
|
|
44
|
+
const found = this.getCommandMapping().get(command)!;
|
|
45
|
+
if (!found) {
|
|
46
|
+
const matchedCfg = COMMAND_PACKAGE.find(([re]) => re.test(cmd));
|
|
47
|
+
if (matchedCfg) {
|
|
48
|
+
const [, pkg, prod] = matchedCfg;
|
|
49
|
+
console.error!(cliTpl`
|
|
44
50
|
${{ title: 'Missing Package' }}\n${'-'.repeat(20)}\nTo use ${{ input: cmd }} please run:\n
|
|
45
|
-
${{ identifier: `npm i ${prod ? '' : '--save-dev '}@travetto/${pkg}` }}
|
|
46
|
-
|
|
51
|
+
${{ identifier: `npm i ${prod ? '' : '--save-dev '}@travetto/${pkg}` }}
|
|
52
|
+
`);
|
|
53
|
+
await ShutdownManager.exit(1);
|
|
47
54
|
}
|
|
48
55
|
throw new Error(`Unknown command: ${cmd}`);
|
|
49
56
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
+
try {
|
|
58
|
+
const values = Object.values<{ new(...args: unknown[]): unknown }>(await import(found));
|
|
59
|
+
for (const v of values) {
|
|
60
|
+
try {
|
|
61
|
+
const inst = new v();
|
|
62
|
+
if (inst instanceof CliCommand && (!cfg.filter || cfg.filter(inst))) {
|
|
63
|
+
return inst;
|
|
57
64
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
} catch { }
|
|
66
|
+
}
|
|
67
|
+
} catch (importErr) {
|
|
68
|
+
console.error(`Import error: ${cmd}`, importErr);
|
|
69
|
+
}
|
|
70
|
+
if (cfg.failOnMissing ?? false) {
|
|
71
|
+
throw new Error(`Not a valid command: ${cmd}`);
|
|
61
72
|
}
|
|
62
|
-
throw new Error(`Not a valid command: ${cmd}`);
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
/**
|
|
66
76
|
* Load all available commands
|
|
67
77
|
*/
|
|
68
78
|
static async loadAllCommands(op?: (p: CliCommand) => unknown | Promise<unknown>): Promise<CliCommand[]> {
|
|
69
|
-
|
|
79
|
+
const commands = await Promise.all(
|
|
70
80
|
[...this.getCommandMapping().keys()]
|
|
71
|
-
.
|
|
72
|
-
|
|
81
|
+
.map(k => this.loadCommand(k, {
|
|
82
|
+
filter(cmd: CliCommand) {
|
|
83
|
+
return RootIndex.getFunctionMetadata(cmd.constructor)?.abstract !== true && cmd.isActive?.() !== false;
|
|
84
|
+
}
|
|
85
|
+
}))
|
|
73
86
|
);
|
|
87
|
+
|
|
88
|
+
return await Promise.all(commands
|
|
89
|
+
.filter((x): x is Exclude<typeof x, undefined> => !!x)
|
|
90
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
91
|
+
.map(async x => {
|
|
92
|
+
await op?.(x);
|
|
93
|
+
return x;
|
|
94
|
+
}));
|
|
74
95
|
}
|
|
75
96
|
}
|
package/src/command.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { appendFile } from 'fs/promises';
|
|
2
|
-
import
|
|
1
|
+
import { appendFile, mkdir } from 'fs/promises';
|
|
2
|
+
import type commander from 'commander';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { CliUtil } from './util';
|
|
4
|
+
import { path } from '@travetto/manifest';
|
|
5
|
+
import { ConsoleManager, DataUtil, defineGlobalEnv, GlobalEnvConfig, ShutdownManager } from '@travetto/base';
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
import { HelpUtil } from './help';
|
|
9
8
|
|
|
10
9
|
type OptionPrimitive = string | number | boolean;
|
|
11
10
|
|
|
@@ -29,10 +28,16 @@ export type ListOptionConfig<K extends OptionPrimitive = OptionPrimitive> = Core
|
|
|
29
28
|
type AllOptionConfig<K extends OptionPrimitive = OptionPrimitive> = OptionConfig<K> | ListOptionConfig<K>;
|
|
30
29
|
|
|
31
30
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
-
type OptionMap<T = any> = { [key in keyof T]: T[key] extends OptionPrimitive ? AllOptionConfig<T[key]> : never };
|
|
31
|
+
export type OptionMap<T = any> = { [key in keyof T]: T[key] extends OptionPrimitive ? AllOptionConfig<T[key]> : never };
|
|
33
32
|
|
|
34
33
|
type Shape<M extends OptionMap> = { [k in keyof M]: Exclude<M[k]['def'], undefined> };
|
|
35
34
|
|
|
35
|
+
function clamp(v: number, l?: number, u?: number): number | undefined {
|
|
36
|
+
if (l !== undefined && v < l) { return; }
|
|
37
|
+
if (u !== undefined && v > u) { return; }
|
|
38
|
+
return v;
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
/**
|
|
37
42
|
* Base command
|
|
38
43
|
*/
|
|
@@ -61,7 +66,7 @@ export abstract class CliCommand<V extends OptionMap = OptionMap> {
|
|
|
61
66
|
/**
|
|
62
67
|
* Setup environment before command runs
|
|
63
68
|
*/
|
|
64
|
-
envInit?(): Promise<
|
|
69
|
+
envInit?(): Promise<GlobalEnvConfig> | GlobalEnvConfig;
|
|
65
70
|
/**
|
|
66
71
|
* Get Options for commander
|
|
67
72
|
*/
|
|
@@ -69,7 +74,7 @@ export abstract class CliCommand<V extends OptionMap = OptionMap> {
|
|
|
69
74
|
/**
|
|
70
75
|
* Get args
|
|
71
76
|
*/
|
|
72
|
-
getArgs?(): string;
|
|
77
|
+
getArgs?(): string | undefined;
|
|
73
78
|
/**
|
|
74
79
|
* Initialization code for adding options
|
|
75
80
|
*/
|
|
@@ -82,7 +87,10 @@ export abstract class CliCommand<V extends OptionMap = OptionMap> {
|
|
|
82
87
|
* Supports JSON IPC?
|
|
83
88
|
*/
|
|
84
89
|
jsonIpc?(...args: unknown[]): Promise<unknown>;
|
|
85
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Is the command active/eligible for usage
|
|
92
|
+
*/
|
|
93
|
+
isActive?(): boolean;
|
|
86
94
|
/**
|
|
87
95
|
* Define option
|
|
88
96
|
*/
|
|
@@ -127,7 +135,8 @@ export abstract class CliCommand<V extends OptionMap = OptionMap> {
|
|
|
127
135
|
boolOption(cfg: OptionConfig<boolean>): OptionConfig<boolean> {
|
|
128
136
|
return {
|
|
129
137
|
type: Boolean,
|
|
130
|
-
combine:
|
|
138
|
+
combine: (val, curr): boolean =>
|
|
139
|
+
(val !== undefined ? DataUtil.coerceType(val, Boolean, false) : undefined) ?? curr,
|
|
131
140
|
completion: true,
|
|
132
141
|
...cfg
|
|
133
142
|
};
|
|
@@ -139,19 +148,11 @@ export abstract class CliCommand<V extends OptionMap = OptionMap> {
|
|
|
139
148
|
intOption({ lower, upper, ...cfg }: OptionConfig<number> & { lower?: number, upper?: number }): OptionConfig<number> {
|
|
140
149
|
return {
|
|
141
150
|
type: Number,
|
|
142
|
-
combine:
|
|
151
|
+
combine: (val, curr): number => clamp(DataUtil.coerceType(val, Number, false), lower, upper) ?? curr,
|
|
143
152
|
...cfg
|
|
144
153
|
};
|
|
145
154
|
}
|
|
146
155
|
|
|
147
|
-
/**
|
|
148
|
-
* Pre-compile on every cli execution
|
|
149
|
-
*/
|
|
150
|
-
async build(): Promise<void> {
|
|
151
|
-
await (await import('@travetto/base/bin/lib/'))
|
|
152
|
-
.BuildUtil.build();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
156
|
/**
|
|
156
157
|
* Expose configuration as constrained typed object
|
|
157
158
|
*/
|
|
@@ -166,14 +167,21 @@ export abstract class CliCommand<V extends OptionMap = OptionMap> {
|
|
|
166
167
|
return this.#cmd.args;
|
|
167
168
|
}
|
|
168
169
|
|
|
170
|
+
exit(code = 0): Promise<void> {
|
|
171
|
+
return ShutdownManager.exit(code);
|
|
172
|
+
}
|
|
173
|
+
|
|
169
174
|
/**
|
|
170
175
|
* Render help with additional message or extra text
|
|
171
176
|
*/
|
|
172
|
-
async showHelp(err?: string | Error, extra?: string): Promise<
|
|
177
|
+
async showHelp(err?: string | Error, extra?: string, exitOnError = true): Promise<void> {
|
|
173
178
|
if (err && typeof err !== 'string') {
|
|
174
179
|
err = err.message;
|
|
175
180
|
}
|
|
176
181
|
HelpUtil.showHelp(this.#cmd, err, extra || (await this.help?.()) || '');
|
|
182
|
+
if (exitOnError) {
|
|
183
|
+
return this.exit(err ? 1 : 0);
|
|
184
|
+
}
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
/**
|
|
@@ -209,19 +217,27 @@ export abstract class CliCommand<V extends OptionMap = OptionMap> {
|
|
|
209
217
|
if (this.init) {
|
|
210
218
|
cmd = await this.init?.(cmd);
|
|
211
219
|
}
|
|
212
|
-
|
|
213
|
-
|
|
220
|
+
const args = this.getArgs?.();
|
|
221
|
+
if (args) {
|
|
222
|
+
cmd = cmd.arguments(args);
|
|
214
223
|
}
|
|
215
224
|
for (const cfg of await this.finalizeOptions()) {
|
|
216
|
-
|
|
217
|
-
if (cfg.type
|
|
218
|
-
|
|
225
|
+
const pre = cfg.short ? `-${cfg.short}, ` : '';
|
|
226
|
+
if (cfg.type === Boolean) {
|
|
227
|
+
if (cfg.def) {
|
|
228
|
+
cmd.option(`${pre}--no-${cfg.name}`, `Disables: ${cfg.desc}`);
|
|
229
|
+
} else {
|
|
230
|
+
cmd.option(`${pre}--${cfg.name}`, cfg.desc);
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
const key = `${pre}--${cfg.name} <${cfg.name}>`;
|
|
234
|
+
// @ts-expect-error
|
|
235
|
+
cmd = cmd.option(key, cfg.desc, cfg.combine ?? ((cur, acc): unknown => cur), cfg.def);
|
|
219
236
|
}
|
|
220
|
-
// @ts-expect-error
|
|
221
|
-
cmd = cfg.combine ? cmd.option(key, cfg.desc, cfg.combine, cfg.def) : cmd.option(key, cfg.desc, (cur, acc) => cur, cfg.def);
|
|
222
237
|
}
|
|
223
238
|
|
|
224
239
|
cmd = cmd.action(this.runAction.bind(this));
|
|
240
|
+
|
|
225
241
|
return this.#cmd = cmd;
|
|
226
242
|
}
|
|
227
243
|
|
|
@@ -229,46 +245,20 @@ export abstract class CliCommand<V extends OptionMap = OptionMap> {
|
|
|
229
245
|
* Runs the action at execution time
|
|
230
246
|
*/
|
|
231
247
|
async runAction(...args: unknown[]): Promise<void> {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
248
|
+
if (this.envInit) {
|
|
249
|
+
defineGlobalEnv(await this.envInit());
|
|
250
|
+
ConsoleManager.setDebugFromEnv();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (process.env.TRV_CLI_IPC && this.jsonIpc) {
|
|
235
254
|
const data = await this.jsonIpc(...args);
|
|
236
255
|
if (data !== undefined) {
|
|
237
256
|
const payload = JSON.stringify({ type: this.name, data });
|
|
238
|
-
await
|
|
257
|
+
await mkdir(path.dirname(process.env.TRV_CLI_IPC), { recursive: true });
|
|
258
|
+
await appendFile(process.env.TRV_CLI_IPC, `${payload}\n`);
|
|
239
259
|
return;
|
|
240
260
|
}
|
|
241
261
|
}
|
|
242
262
|
return await this.action(...args);
|
|
243
263
|
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Collection tab completion information
|
|
247
|
-
*/
|
|
248
|
-
async setupCompletion(config: CompletionConfig): Promise<void> {
|
|
249
|
-
const task = await this.complete();
|
|
250
|
-
config.all = [...config.all, this.name];
|
|
251
|
-
if (task) {
|
|
252
|
-
config.task[this.name] = task;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Return tab completion information
|
|
258
|
-
*/
|
|
259
|
-
async complete(): Promise<Completion | void> {
|
|
260
|
-
const out: Completion = { '': [] };
|
|
261
|
-
for (const el of await this.finalizeOptions()) {
|
|
262
|
-
if (el.completion) {
|
|
263
|
-
out[''] = [...out['']!, `--${el.name} `];
|
|
264
|
-
if ('choices' in el && el.choices) {
|
|
265
|
-
out[`--${el.name} `] = el.choices.map(x => `${x}`);
|
|
266
|
-
if (el.short) {
|
|
267
|
-
out[`- ${el.short} `] = el.choices.map(x => `${x}`);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return out;
|
|
273
|
-
}
|
|
274
264
|
}
|
package/src/execute.ts
CHANGED
|
@@ -1,38 +1,27 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
1
2
|
import { program as commander } from 'commander';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { PackageUtil, path, RootIndex } from '@travetto/manifest';
|
|
5
|
+
import { GlobalTerminal } from '@travetto/terminal';
|
|
6
|
+
import { ShutdownManager } from '@travetto/base';
|
|
7
|
+
|
|
5
8
|
import { CliCommandManager } from './command-manager';
|
|
6
9
|
import { HelpUtil } from './help';
|
|
7
|
-
import { version } from '../package.json';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Execution manager
|
|
11
13
|
*/
|
|
12
14
|
export class ExecutionManager {
|
|
13
15
|
|
|
14
|
-
/**
|
|
15
|
-
* Run tab completion given the full args list
|
|
16
|
-
*/
|
|
17
|
-
static async runCompletion(args: string[]): Promise<void> {
|
|
18
|
-
const cfg: CompletionConfig = { all: [], task: {} };
|
|
19
|
-
await CliCommandManager.loadAllCommands(x => x.setupCompletion(cfg));
|
|
20
|
-
const res = await CliUtil.getCompletion(cfg, args.slice(3));
|
|
21
|
-
console.log(res.join(' '));
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
16
|
/**
|
|
26
17
|
* Run command
|
|
27
18
|
*/
|
|
28
|
-
static async runCommand(args: string[]): Promise<void> {
|
|
29
|
-
const cmd = args[2];
|
|
30
|
-
|
|
19
|
+
static async runCommand(cmd: string, args: string[]): Promise<void> {
|
|
31
20
|
let command;
|
|
32
21
|
|
|
33
22
|
try {
|
|
34
23
|
// Load a single command
|
|
35
|
-
command = await CliCommandManager.loadCommand(cmd)
|
|
24
|
+
command = (await CliCommandManager.loadCommand(cmd, { failOnMissing: true }))!;
|
|
36
25
|
await command.setup(commander);
|
|
37
26
|
} catch (err) {
|
|
38
27
|
return HelpUtil.showHelp(commander, `Unknown command ${cmd}`);
|
|
@@ -42,38 +31,63 @@ export class ExecutionManager {
|
|
|
42
31
|
if (args.includes('-h') || args.includes('--help')) {
|
|
43
32
|
return command.showHelp();
|
|
44
33
|
} else {
|
|
45
|
-
commander.
|
|
34
|
+
await commander.parseAsync([process.argv[0], process.argv[1], cmd, ...args]);
|
|
46
35
|
}
|
|
47
36
|
} catch (err) {
|
|
48
37
|
if (!(err instanceof Error)) {
|
|
49
38
|
throw err;
|
|
39
|
+
} else {
|
|
40
|
+
console.error(err);
|
|
50
41
|
}
|
|
51
42
|
return command.showHelp(err);
|
|
52
43
|
}
|
|
53
44
|
}
|
|
54
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Run file expecting a main method
|
|
48
|
+
*/
|
|
49
|
+
static async runMain(file: string, args: string[]): Promise<void> {
|
|
50
|
+
try {
|
|
51
|
+
// If referenced file exists
|
|
52
|
+
if (await (fs.stat(path.resolve(file)).then(() => true, () => false))) {
|
|
53
|
+
file = path.join(RootIndex.manifest.mainModule, file);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const imp = RootIndex.getFromImport(file)?.import;
|
|
57
|
+
if (!imp) {
|
|
58
|
+
throw new Error(`Unknown file: ${file}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const mod = await import(imp);
|
|
62
|
+
await ShutdownManager.exitWithResponse(await mod.main(...args));
|
|
63
|
+
} catch (err) {
|
|
64
|
+
await ShutdownManager.exitWithResponse(err, true);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
55
68
|
/**
|
|
56
69
|
* Execute the command line
|
|
57
70
|
* @param args
|
|
58
71
|
*/
|
|
59
|
-
static async run(
|
|
60
|
-
const
|
|
72
|
+
static async run(argv: string[]): Promise<void> {
|
|
73
|
+
const { init } = await import('@travetto/base/support/init.js');
|
|
74
|
+
await init();
|
|
75
|
+
|
|
76
|
+
const width = GlobalTerminal.width;
|
|
61
77
|
commander
|
|
62
|
-
.version(
|
|
78
|
+
.version(PackageUtil.getFrameworkVersion())
|
|
63
79
|
.configureOutput({ getOutHelpWidth: () => width, getErrHelpWidth: () => width });
|
|
64
80
|
|
|
65
|
-
const cmd =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
await this.
|
|
81
|
+
const [, , cmd, ...args] = argv;
|
|
82
|
+
if (cmd === 'main') {
|
|
83
|
+
const [file, ...rest] = args;
|
|
84
|
+
await this.runMain(file, rest);
|
|
85
|
+
} else if (cmd && !cmd.startsWith('-')) {
|
|
86
|
+
await this.runCommand(cmd, args);
|
|
69
87
|
} else {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Load all commands
|
|
74
|
-
await CliCommandManager.loadAllCommands(x => x.setup(commander));
|
|
75
|
-
HelpUtil.showHelp(commander);
|
|
76
|
-
}
|
|
88
|
+
// Load all commands
|
|
89
|
+
await CliCommandManager.loadAllCommands(x => x.setup(commander));
|
|
90
|
+
HelpUtil.showHelp(commander);
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
}
|