@travetto/cli 3.3.5 → 3.4.0-rc.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/README.md +14 -2
- package/bin/trv.js +6 -0
- package/package.json +8 -3
- package/src/decorators.ts +6 -7
- package/src/error.ts +63 -0
- package/src/execute.ts +16 -33
- package/src/help.ts +12 -11
- package/src/registry.ts +8 -33
- package/src/schema.ts +3 -2
- package/src/types.ts +0 -12
- package/src/util.ts +59 -1
- package/support/cli.cli_schema.ts +5 -2
- /package/support/{entry.cli.ts → entry.trv.ts} +0 -0
package/README.md
CHANGED
|
@@ -420,7 +420,9 @@ If the goal is to run a more complex application, which may include depending on
|
|
|
420
420
|
**Code: Simple Run Target**
|
|
421
421
|
```typescript
|
|
422
422
|
import { DependencyRegistry } from '@travetto/di';
|
|
423
|
-
import { CliCommand } from '@travetto/cli';
|
|
423
|
+
import { CliCommand, CliUtil } from '@travetto/cli';
|
|
424
|
+
import { GlobalEnv } from '@travetto/base';
|
|
425
|
+
|
|
424
426
|
import { ServerHandle } from '../src/types';
|
|
425
427
|
|
|
426
428
|
/**
|
|
@@ -429,6 +431,12 @@ import { ServerHandle } from '../src/types';
|
|
|
429
431
|
@CliCommand({ runTarget: true, fields: ['module', 'env', 'profile'] })
|
|
430
432
|
export class RunRestCommand {
|
|
431
433
|
|
|
434
|
+
/** IPC debug is enabled */
|
|
435
|
+
debugIpc = true;
|
|
436
|
+
|
|
437
|
+
/** Should the server be able to run with restart*/
|
|
438
|
+
canRestart = GlobalEnv.devMode;
|
|
439
|
+
|
|
432
440
|
/** Port to run on */
|
|
433
441
|
port?: number;
|
|
434
442
|
|
|
@@ -436,7 +444,11 @@ export class RunRestCommand {
|
|
|
436
444
|
return this.port ? { REST_PORT: `${this.port}` } : {};
|
|
437
445
|
}
|
|
438
446
|
|
|
439
|
-
async main(): Promise<ServerHandle> {
|
|
447
|
+
async main(): Promise<ServerHandle | void> {
|
|
448
|
+
if (await CliUtil.debugIfIpc(this) || await CliUtil.runWithRestart(this)) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
440
452
|
const { RestApplication } = await import('../src/application/rest.js');
|
|
441
453
|
return DependencyRegistry.runInstance(RestApplication);
|
|
442
454
|
}
|
package/bin/trv.js
ADDED
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0-rc.0",
|
|
4
4
|
"description": "CLI infrastructure for Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
7
7
|
"travetto",
|
|
8
8
|
"typescript"
|
|
9
9
|
],
|
|
10
|
+
"type": "module",
|
|
10
11
|
"homepage": "https://travetto.io",
|
|
11
12
|
"license": "MIT",
|
|
12
13
|
"author": {
|
|
@@ -15,17 +16,21 @@
|
|
|
15
16
|
},
|
|
16
17
|
"files": [
|
|
17
18
|
"__index__.ts",
|
|
19
|
+
"bin",
|
|
18
20
|
"src",
|
|
19
21
|
"support"
|
|
20
22
|
],
|
|
21
23
|
"main": "__index__.ts",
|
|
24
|
+
"bin": {
|
|
25
|
+
"trv": "bin/trv.js"
|
|
26
|
+
},
|
|
22
27
|
"repository": {
|
|
23
28
|
"url": "https://github.com/travetto/travetto.git",
|
|
24
29
|
"directory": "module/cli"
|
|
25
30
|
},
|
|
26
31
|
"dependencies": {
|
|
27
|
-
"@travetto/schema": "^3.
|
|
28
|
-
"@travetto/terminal": "^3.
|
|
32
|
+
"@travetto/schema": "^3.4.0-rc.0",
|
|
33
|
+
"@travetto/terminal": "^3.4.0-rc.0"
|
|
29
34
|
},
|
|
30
35
|
"travetto": {
|
|
31
36
|
"displayName": "Command Line Interface"
|
package/src/decorators.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { RootIndex } from '@travetto/manifest';
|
|
|
3
3
|
import { SchemaRegistry } from '@travetto/schema';
|
|
4
4
|
|
|
5
5
|
import { CliCommandShape } from './types';
|
|
6
|
-
import { CliCommandRegistry } from './registry';
|
|
6
|
+
import { CliCommandRegistry, CliCommandConfigOptions } from './registry';
|
|
7
7
|
import { CliModuleUtil } from './module';
|
|
8
8
|
import { CliUtil } from './util';
|
|
9
9
|
|
|
@@ -17,7 +17,7 @@ const getMod = (cls: Class): string => RootIndex.getModuleFromSource(RootIndex.g
|
|
|
17
17
|
* @augments `@travetto/schema:Schema`
|
|
18
18
|
* @augments `@travetto/cli:CliCommand`
|
|
19
19
|
*/
|
|
20
|
-
export function CliCommand(cfg: { fields?: ExtraFields[]
|
|
20
|
+
export function CliCommand({ fields, ...cfg }: { fields?: ExtraFields[] } & CliCommandConfigOptions = {}) {
|
|
21
21
|
return function <T extends CliCommandShape>(target: Class<T>): void {
|
|
22
22
|
const meta = RootIndex.getFunctionMetadata(target);
|
|
23
23
|
if (!meta || meta.abstract) {
|
|
@@ -25,17 +25,16 @@ export function CliCommand(cfg: { fields?: ExtraFields[], runTarget?: boolean, h
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const name = getName(meta.source);
|
|
28
|
-
const addEnv =
|
|
29
|
-
const addProfile =
|
|
30
|
-
const addModule =
|
|
28
|
+
const addEnv = fields?.includes('env');
|
|
29
|
+
const addProfile = fields?.includes('profile');
|
|
30
|
+
const addModule = fields?.includes('module');
|
|
31
31
|
|
|
32
32
|
CliCommandRegistry.registerClass({
|
|
33
33
|
module: getMod(target),
|
|
34
34
|
name,
|
|
35
35
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
36
36
|
cls: target as ConcreteClass<T>,
|
|
37
|
-
|
|
38
|
-
runTarget: cfg.runTarget,
|
|
37
|
+
...cfg,
|
|
39
38
|
preMain: (cmd: CliCommandShape & { env?: string, profile?: string[], module?: string }) => {
|
|
40
39
|
if (addEnv || addProfile) {
|
|
41
40
|
defineGlobalEnv({
|
package/src/error.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { RootIndex } from '@travetto/manifest';
|
|
2
|
+
|
|
3
|
+
import { cliTpl } from './color';
|
|
4
|
+
import { CliValidationError } from './types';
|
|
5
|
+
|
|
6
|
+
const COMMAND_PACKAGE = [
|
|
7
|
+
[/^test(:watch)?$/, 'test', false],
|
|
8
|
+
[/^service$/, 'command', true],
|
|
9
|
+
[/^lint(:register)?$/, 'lint', true],
|
|
10
|
+
[/^model:(install|export)$/, 'model', true],
|
|
11
|
+
[/^openapi:(spec|client)$/, 'openapi', true],
|
|
12
|
+
[/^email:(compile|editor)$/, 'email-compiler', false],
|
|
13
|
+
[/^pack(:zip|:docker)?$/, 'pack', false],
|
|
14
|
+
[/^run:rest$/, 'rest', false],
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Provides a contract for unknown commands
|
|
19
|
+
*/
|
|
20
|
+
export class CliUnknownCommandError extends Error {
|
|
21
|
+
|
|
22
|
+
#getMissingCommandHelp(cmd: string): string | undefined {
|
|
23
|
+
const matchedCfg = COMMAND_PACKAGE.find(([re]) => re.test(cmd));
|
|
24
|
+
if (matchedCfg) {
|
|
25
|
+
const [, pkg, prod] = matchedCfg;
|
|
26
|
+
let install: string;
|
|
27
|
+
switch (RootIndex.manifest.packageManager) {
|
|
28
|
+
case 'npm': install = `npm i ${prod ? '' : '--save-dev '}@travetto/${pkg}`; break;
|
|
29
|
+
case 'yarn': install = `yarn add ${prod ? '' : '--dev '}@travetto/${pkg}`; break;
|
|
30
|
+
}
|
|
31
|
+
return cliTpl`
|
|
32
|
+
${{ title: 'Missing Package' }}\n${'-'.repeat(20)}\nTo use ${{ input: cmd }} please run:\n
|
|
33
|
+
${{ identifier: install }}
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
help?: string;
|
|
39
|
+
cmd: string;
|
|
40
|
+
|
|
41
|
+
constructor(cmd: string) {
|
|
42
|
+
super(`Unknown command: ${cmd}`);
|
|
43
|
+
this.cmd = cmd;
|
|
44
|
+
this.help = this.#getMissingCommandHelp(cmd);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get defaultMessage(): string {
|
|
48
|
+
return cliTpl`${{ subtitle: 'Unknown command' }}: ${{ input: this.cmd }}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Provides a basic error wrapper for cli validation issues
|
|
55
|
+
*/
|
|
56
|
+
export class CliValidationResultError extends Error {
|
|
57
|
+
errors: CliValidationError[];
|
|
58
|
+
|
|
59
|
+
constructor(errors: CliValidationError[]) {
|
|
60
|
+
super('');
|
|
61
|
+
this.errors = errors;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/execute.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { appendFile, mkdir } from 'fs/promises';
|
|
2
|
-
|
|
3
1
|
import { GlobalTerminal } from '@travetto/terminal';
|
|
4
|
-
import { path } from '@travetto/manifest';
|
|
5
2
|
import { ConsoleManager, defineGlobalEnv, ShutdownManager, GlobalEnv } from '@travetto/base';
|
|
6
3
|
|
|
7
4
|
import { HelpUtil } from './help';
|
|
8
|
-
import { CliCommandShape
|
|
5
|
+
import { CliCommandShape } from './types';
|
|
9
6
|
import { CliCommandRegistry } from './registry';
|
|
10
7
|
import { CliCommandSchemaUtil } from './schema';
|
|
8
|
+
import { CliUnknownCommandError, CliValidationResultError } from './error';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* Execution manager
|
|
@@ -30,13 +28,6 @@ export class ExecutionManager {
|
|
|
30
28
|
return known;
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
static #getAction(cmd: CliCommandShape, args: string[]): 'help' | 'ipc' | 'command' {
|
|
34
|
-
const cfg = CliCommandRegistry.getConfig(cmd);
|
|
35
|
-
return args.find(a => /^(-h|--help)$/.test(a)) ?
|
|
36
|
-
'help' :
|
|
37
|
-
(process.env.TRV_CLI_IPC && cfg.runTarget) ? 'ipc' : 'command';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
31
|
/**
|
|
41
32
|
* Run help
|
|
42
33
|
*/
|
|
@@ -46,24 +37,6 @@ export class ExecutionManager {
|
|
|
46
37
|
console.log!(await HelpUtil.renderHelp(cmd));
|
|
47
38
|
}
|
|
48
39
|
|
|
49
|
-
/**
|
|
50
|
-
* Append IPC payload to provided file
|
|
51
|
-
*/
|
|
52
|
-
static async ipc(cmd: CliCommandShape, args: string[]): Promise<void> {
|
|
53
|
-
await this.#bindAndValidateArgs(cmd, args);
|
|
54
|
-
const file = process.env.TRV_CLI_IPC!;
|
|
55
|
-
const cfg = CliCommandRegistry.getConfig(cmd);
|
|
56
|
-
const payload = JSON.stringify({
|
|
57
|
-
type: '@travetto/cli:run', data: {
|
|
58
|
-
name: cfg.name,
|
|
59
|
-
module: cfg.module,
|
|
60
|
-
args: process.argv.slice(3)
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
64
|
-
await appendFile(file, `${payload}\n`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
40
|
/**
|
|
68
41
|
* Run the given command object with the given arguments
|
|
69
42
|
*/
|
|
@@ -103,15 +76,25 @@ export class ExecutionManager {
|
|
|
103
76
|
try {
|
|
104
77
|
// Load a single command
|
|
105
78
|
command = (await CliCommandRegistry.getInstance(cmd, true));
|
|
106
|
-
|
|
107
|
-
|
|
79
|
+
if (args.some(a => /^(-h|--help)$/.test(a))) {
|
|
80
|
+
await this.help(command, args);
|
|
81
|
+
} else {
|
|
82
|
+
await this.command(command, args);
|
|
83
|
+
}
|
|
108
84
|
} catch (err) {
|
|
109
85
|
process.exitCode ||= 1; // Trigger error state
|
|
110
86
|
if (!(err instanceof Error)) {
|
|
111
87
|
throw err;
|
|
112
|
-
} else if (
|
|
113
|
-
console.error(await HelpUtil.renderValidationError(command
|
|
88
|
+
} else if (err instanceof CliValidationResultError) {
|
|
89
|
+
console.error!(await HelpUtil.renderValidationError(command!, err));
|
|
114
90
|
console.error!(await HelpUtil.renderHelp(command));
|
|
91
|
+
} else if (err instanceof CliUnknownCommandError) {
|
|
92
|
+
if (err.help) {
|
|
93
|
+
console.error!(err.help);
|
|
94
|
+
} else {
|
|
95
|
+
console.error!(err.defaultMessage, '\n');
|
|
96
|
+
console.error!(await HelpUtil.renderAllHelp(''));
|
|
97
|
+
}
|
|
115
98
|
} else {
|
|
116
99
|
console.error!(err);
|
|
117
100
|
console.error!();
|
package/src/help.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { Primitive } from '@travetto/base';
|
|
|
2
2
|
import { stripAnsiCodes } from '@travetto/terminal';
|
|
3
3
|
|
|
4
4
|
import { cliTpl } from './color';
|
|
5
|
-
import { CliCommandShape
|
|
5
|
+
import { CliCommandShape } from './types';
|
|
6
6
|
import { CliCommandRegistry } from './registry';
|
|
7
7
|
import { CliCommandSchemaUtil } from './schema';
|
|
8
|
+
import { CliValidationResultError } from './error';
|
|
8
9
|
|
|
9
10
|
const validationSourceMap = {
|
|
10
11
|
custom: '',
|
|
@@ -21,7 +22,7 @@ export class HelpUtil {
|
|
|
21
22
|
* Render command-specific help
|
|
22
23
|
* @param command
|
|
23
24
|
*/
|
|
24
|
-
static async
|
|
25
|
+
static async renderCommandHelp(command: CliCommandShape): Promise<string> {
|
|
25
26
|
const commandName = CliCommandRegistry.getName(command);
|
|
26
27
|
|
|
27
28
|
command.initialize?.();
|
|
@@ -93,7 +94,7 @@ export class HelpUtil {
|
|
|
93
94
|
/**
|
|
94
95
|
* Render help listing of all commands
|
|
95
96
|
*/
|
|
96
|
-
static async
|
|
97
|
+
static async renderAllHelp(title?: string): Promise<string> {
|
|
97
98
|
const rows: string[] = [];
|
|
98
99
|
const keys = [...CliCommandRegistry.getCommandMapping().keys()].sort((a, b) => a.localeCompare(b));
|
|
99
100
|
const maxWidth = keys.reduce((a, b) => Math.max(a, stripAnsiCodes(b).length), 0);
|
|
@@ -116,20 +117,20 @@ export class HelpUtil {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
120
|
+
|
|
121
|
+
const lines = [cliTpl`${{ title: 'Commands:' }}`, ...rows, ''];
|
|
122
|
+
|
|
123
|
+
if (title === undefined || title) {
|
|
124
|
+
lines.unshift(title ? cliTpl`${{ title }}` : cliTpl`${{ title: 'Usage:' }} ${{ param: '[options]' }} ${{ param: '[command]' }}`, '');
|
|
125
|
+
}
|
|
126
|
+
return lines.map(x => x.trimEnd()).join('\n');
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
/**
|
|
129
130
|
* Render help
|
|
130
131
|
*/
|
|
131
132
|
static async renderHelp(command?: CliCommandShape): Promise<string> {
|
|
132
|
-
return command ? this
|
|
133
|
+
return command ? this.renderCommandHelp(command) : this.renderAllHelp();
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
/**
|
package/src/registry.ts
CHANGED
|
@@ -1,52 +1,27 @@
|
|
|
1
1
|
import { Class, ConcreteClass } from '@travetto/base';
|
|
2
2
|
import { RootIndex } from '@travetto/manifest';
|
|
3
3
|
|
|
4
|
-
import { cliTpl } from './color';
|
|
5
4
|
import { CliCommandShape } from './types';
|
|
5
|
+
import { CliUnknownCommandError } from './error';
|
|
6
6
|
|
|
7
|
-
type
|
|
7
|
+
export type CliCommandConfigOptions = {
|
|
8
|
+
runTarget?: boolean;
|
|
9
|
+
hidden?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type CliCommandConfig = CliCommandConfigOptions & {
|
|
8
13
|
name: string;
|
|
9
14
|
module: string;
|
|
10
15
|
cls: ConcreteClass<CliCommandShape>;
|
|
11
|
-
runTarget?: boolean;
|
|
12
|
-
hidden?: boolean;
|
|
13
16
|
preMain?: (cmd: CliCommandShape) => void | Promise<void>;
|
|
14
17
|
};
|
|
15
18
|
|
|
16
|
-
const COMMAND_PACKAGE = [
|
|
17
|
-
[/^test(:watch)?$/, 'test', false],
|
|
18
|
-
[/^service$/, 'command', true],
|
|
19
|
-
[/^lint(:register)?$/, 'lint', true],
|
|
20
|
-
[/^model:(install|export)$/, 'model', true],
|
|
21
|
-
[/^openapi:(spec|client)$/, 'openapi', true],
|
|
22
|
-
[/^email:(compile|editor)$/, 'email-compiler', false],
|
|
23
|
-
[/^pack(:zip|:docker)?$/, 'pack', false],
|
|
24
|
-
] as const;
|
|
25
|
-
|
|
26
19
|
const CLI_REGEX = /\/cli[.]([^.]+)[.][^.]+?$/;
|
|
27
20
|
|
|
28
21
|
class $CliCommandRegistry {
|
|
29
22
|
#commands = new Map<Class, CliCommandConfig>();
|
|
30
23
|
#fileMapping: Map<string, string>;
|
|
31
24
|
|
|
32
|
-
#getMissingCommandHelp(cmd: string): string {
|
|
33
|
-
const matchedCfg = COMMAND_PACKAGE.find(([re]) => re.test(cmd));
|
|
34
|
-
if (matchedCfg) {
|
|
35
|
-
const [, pkg, prod] = matchedCfg;
|
|
36
|
-
let install: string;
|
|
37
|
-
switch (RootIndex.manifest.packageManager) {
|
|
38
|
-
case 'npm': install = `npm i ${prod ? '' : '--save-dev '}@travetto/${pkg}`; break;
|
|
39
|
-
case 'yarn': install = `yarn add ${prod ? '' : '--dev '}@travetto/${pkg}`; break;
|
|
40
|
-
}
|
|
41
|
-
return cliTpl`
|
|
42
|
-
${{ title: 'Missing Package' }}\n${'-'.repeat(20)}\nTo use ${{ input: cmd }} please run:\n
|
|
43
|
-
${{ identifier: install }}
|
|
44
|
-
`;
|
|
45
|
-
} else {
|
|
46
|
-
return `Unknown command: ${cmd}`;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
25
|
#get(cls: Class): CliCommandConfig | undefined {
|
|
51
26
|
return this.#commands.get(cls);
|
|
52
27
|
}
|
|
@@ -115,7 +90,7 @@ ${{ identifier: install }}
|
|
|
115
90
|
return undefined;
|
|
116
91
|
}
|
|
117
92
|
}
|
|
118
|
-
throw new
|
|
93
|
+
throw new CliUnknownCommandError(name);
|
|
119
94
|
}
|
|
120
95
|
}
|
|
121
96
|
|
package/src/schema.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Class, ConsoleManager, GlobalEnv } from '@travetto/base';
|
|
2
2
|
import { BindUtil, FieldConfig, SchemaRegistry, SchemaValidator, ValidationResultError } from '@travetto/schema';
|
|
3
|
-
import { CliCommandRegistry } from './registry';
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import { CliCommandRegistry } from './registry';
|
|
5
|
+
import { CliCommandInput, CliCommandSchema, CliCommandShape } from './types';
|
|
6
|
+
import { CliValidationResultError } from './error';
|
|
6
7
|
|
|
7
8
|
function fieldToInput(x: FieldConfig): CliCommandInput {
|
|
8
9
|
const type = x.type === Date ? 'date' :
|
package/src/types.ts
CHANGED
|
@@ -18,18 +18,6 @@ export type CliValidationError = {
|
|
|
18
18
|
source?: 'flag' | 'arg' | 'custom';
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
* Provides a basic error wrapper for internal try/catch instanceof
|
|
23
|
-
*/
|
|
24
|
-
export class CliValidationResultError extends Error {
|
|
25
|
-
errors: CliValidationError[];
|
|
26
|
-
|
|
27
|
-
constructor(errors: CliValidationError[]) {
|
|
28
|
-
super('');
|
|
29
|
-
this.errors = errors;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
21
|
/**
|
|
34
22
|
* CLI Command Contract
|
|
35
23
|
*/
|
package/src/util.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
import { Env, ExecUtil } from '@travetto/base';
|
|
4
|
+
import { path, RootIndex } from '@travetto/manifest';
|
|
5
|
+
|
|
6
|
+
import { CliCommandShape } from './types';
|
|
7
|
+
import { CliCommandRegistry } from './registry';
|
|
2
8
|
|
|
3
9
|
export class CliUtil {
|
|
4
10
|
/**
|
|
@@ -15,4 +21,56 @@ export class CliUtil {
|
|
|
15
21
|
static getSimpleModuleName(name = RootIndex.mainModuleName): string {
|
|
16
22
|
return name.replace(/[\/]/, '_').replace(/@/, '');
|
|
17
23
|
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Run a command as restartable, linking into self
|
|
27
|
+
*/
|
|
28
|
+
static runWithRestart<T extends CliCommandShape & { canRestart?: boolean }>(cmd: T): Promise<unknown> | undefined {
|
|
29
|
+
if (cmd.canRestart !== false && Env.isFalse('TRV_CAN_RESTART')) {
|
|
30
|
+
delete process.env.TRV_CAN_RESTART;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
return ExecUtil.spawnWithRestart(process.argv0, process.argv.slice(1), {
|
|
34
|
+
env: { TRV_DYNAMIC: '1', TRV_CAN_RESTART: '0' },
|
|
35
|
+
stdio: [0, 1, 2, 'ipc']
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Dispatch IPC payload
|
|
41
|
+
*/
|
|
42
|
+
static async triggerIpc<T extends CliCommandShape>(action: 'run', cmd: T): Promise<boolean> {
|
|
43
|
+
const file = process.env.TRV_CLI_IPC;
|
|
44
|
+
|
|
45
|
+
if (!file) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const cfg = CliCommandRegistry.getConfig(cmd);
|
|
50
|
+
const payload = JSON.stringify({
|
|
51
|
+
type: `@travetto/cli:${action}`, data: {
|
|
52
|
+
name: cfg.name,
|
|
53
|
+
commandModule: cfg.module,
|
|
54
|
+
module: RootIndex.manifest.mainModule,
|
|
55
|
+
args: process.argv.slice(3)
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
59
|
+
await fs.appendFile(file, `${payload}\n`);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Debug if IPC available
|
|
65
|
+
*/
|
|
66
|
+
static async debugIfIpc<T extends CliCommandShape & { debugIpc?: boolean }>(cmd: T): Promise<boolean> {
|
|
67
|
+
return cmd.debugIpc !== false && this.triggerIpc('run', cmd);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Write data to channel and ensure its flushed before continuing
|
|
72
|
+
*/
|
|
73
|
+
static async writeAndEnsureComplete(data: unknown, channel: 'stdout' | 'stderr' = 'stdout'): Promise<void> {
|
|
74
|
+
return await new Promise(r => process[channel].write(typeof data === 'string' ? data : JSON.stringify(data, null, 2), () => r()));
|
|
75
|
+
}
|
|
18
76
|
}
|
|
@@ -2,6 +2,7 @@ import { CliCommand } from '../src/decorators';
|
|
|
2
2
|
import { CliCommandSchema, CliValidationError } from '../src/types';
|
|
3
3
|
import { CliCommandRegistry } from '../src/registry';
|
|
4
4
|
import { CliCommandSchemaUtil } from '../src/schema';
|
|
5
|
+
import { CliUtil } from '../src/util';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Generates the schema for all CLI operations
|
|
@@ -24,12 +25,14 @@ export class CliSchemaCommand {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
async main(name?: string): Promise<void> {
|
|
28
|
+
let output: unknown = undefined;
|
|
27
29
|
if (name) {
|
|
28
|
-
|
|
30
|
+
output = await this.#getSchema(name);
|
|
29
31
|
} else {
|
|
30
32
|
const names = [...CliCommandRegistry.getCommandMapping().keys()];
|
|
31
33
|
const schemas = await Promise.all(names.map(x => this.#getSchema(x)));
|
|
32
|
-
|
|
34
|
+
output = schemas;
|
|
33
35
|
}
|
|
36
|
+
await CliUtil.writeAndEnsureComplete(output);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
File without changes
|