@travetto/cli 3.4.0-rc.8 → 3.4.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 +11 -12
- package/package.json +3 -3
- package/src/decorators.ts +28 -27
- package/src/execute.ts +2 -2
- package/src/module.ts +7 -20
- package/src/registry.ts +24 -9
- package/src/schema.ts +3 -1
- package/src/types.ts +25 -3
- package/src/util.ts +21 -23
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ This module also has a tight integration with the [VSCode plugin](https://market
|
|
|
59
59
|
At it's heart, a cli command is the contract defined by what flags, and what arguments the command supports. Within the framework this requires three criteria to be met:
|
|
60
60
|
* The file must be located in the `support/` folder, and have a name that matches `cli.*.ts`
|
|
61
61
|
* The file must be a class that has a main method
|
|
62
|
-
* The class must use the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#
|
|
62
|
+
* The class must use the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) decorator
|
|
63
63
|
|
|
64
64
|
**Code: Basic Command**
|
|
65
65
|
```typescript
|
|
@@ -93,7 +93,7 @@ Examples of mappings:
|
|
|
93
93
|
The pattern is that underscores(_) translate to colons (:), and the `cli.` prefix, and `.ts` suffix are dropped.
|
|
94
94
|
|
|
95
95
|
## Binding Flags
|
|
96
|
-
[@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#
|
|
96
|
+
[@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) is a wrapper for [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L14), and so every class that uses the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) decorator is now a full [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L14) class. The fields of the class represent the flags that are available to the command.
|
|
97
97
|
|
|
98
98
|
**Code: Basic Command with Flag**
|
|
99
99
|
```typescript
|
|
@@ -130,7 +130,7 @@ $ trv basic:flag --loud
|
|
|
130
130
|
HELLO
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#
|
|
133
|
+
The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) supports the following data types for flags:
|
|
134
134
|
* Boolean values
|
|
135
135
|
* Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L172), [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L178), [@Precision](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L166), [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L107) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L117) decorators help provide additional validation.
|
|
136
136
|
* String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L107), [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L117), [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L99) and [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L78) provide additional constraints
|
|
@@ -170,7 +170,7 @@ Options:
|
|
|
170
170
|
$ trv basic:arg 20
|
|
171
171
|
|
|
172
172
|
Execution failed:
|
|
173
|
-
* Argument
|
|
173
|
+
* Argument volume is bigger than (10)
|
|
174
174
|
|
|
175
175
|
Usage: basic:arg [options] [volume:number]
|
|
176
176
|
|
|
@@ -233,7 +233,7 @@ $ trv basic:arglist 10 5 3 9 8 1
|
|
|
233
233
|
$ trv basic:arglist 10 5 3 9 20 1
|
|
234
234
|
|
|
235
235
|
Execution failed:
|
|
236
|
-
* Argument [4] is bigger than (10)
|
|
236
|
+
* Argument volumes[4] is bigger than (10)
|
|
237
237
|
|
|
238
238
|
Usage: basic:arglist [options] <volumes...:number>
|
|
239
239
|
|
|
@@ -360,7 +360,7 @@ CuStOm
|
|
|
360
360
|
```
|
|
361
361
|
|
|
362
362
|
## VSCode Integration
|
|
363
|
-
By default, cli commands do not expose themselves to the VSCode extension, as the majority of them are not intended for that sort of operation. [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") does expose a cli target `run:rest` that will show up, to help run/debug a rest application. Any command can mark itself as being a run target, and will be eligible for running from within the [VSCode plugin](https://marketplace.visualstudio.com/items?itemName=arcsine.travetto-plugin). This is achieved by setting the `runTarget` field on the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#
|
|
363
|
+
By default, cli commands do not expose themselves to the VSCode extension, as the majority of them are not intended for that sort of operation. [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") does expose a cli target `run:rest` that will show up, to help run/debug a rest application. Any command can mark itself as being a run target, and will be eligible for running from within the [VSCode plugin](https://marketplace.visualstudio.com/items?itemName=arcsine.travetto-plugin). This is achieved by setting the `runTarget` field on the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) decorator. This means the target will be visible within the editor tooling.
|
|
364
364
|
|
|
365
365
|
**Code: Simple Run Target**
|
|
366
366
|
```typescript
|
|
@@ -390,7 +390,7 @@ export interface CliCommandShape {
|
|
|
390
390
|
/**
|
|
391
391
|
* Setup environment before command runs
|
|
392
392
|
*/
|
|
393
|
-
envInit?(): OrProm<
|
|
393
|
+
envInit?(): OrProm<EnvInit>;
|
|
394
394
|
/**
|
|
395
395
|
* Extra help
|
|
396
396
|
*/
|
|
@@ -421,21 +421,20 @@ If the goal is to run a more complex application, which may include depending on
|
|
|
421
421
|
```typescript
|
|
422
422
|
import { DependencyRegistry } from '@travetto/di';
|
|
423
423
|
import { CliCommand, CliUtil } from '@travetto/cli';
|
|
424
|
-
import { GlobalEnv } from '@travetto/base';
|
|
425
424
|
|
|
426
425
|
import { ServerHandle } from '../src/types';
|
|
427
426
|
|
|
428
427
|
/**
|
|
429
428
|
* Run a rest server as an application
|
|
430
429
|
*/
|
|
431
|
-
@CliCommand({ runTarget: true,
|
|
430
|
+
@CliCommand({ runTarget: true, addModule: true, addEnv: true })
|
|
432
431
|
export class RunRestCommand {
|
|
433
432
|
|
|
434
433
|
/** IPC debug is enabled */
|
|
435
|
-
debugIpc
|
|
434
|
+
debugIpc?: boolean;
|
|
436
435
|
|
|
437
436
|
/** Should the server be able to run with restart*/
|
|
438
|
-
canRestart
|
|
437
|
+
canRestart?: boolean;
|
|
439
438
|
|
|
440
439
|
/** Port to run on */
|
|
441
440
|
port?: number;
|
|
@@ -455,7 +454,7 @@ export class RunRestCommand {
|
|
|
455
454
|
}
|
|
456
455
|
```
|
|
457
456
|
|
|
458
|
-
As noted in the example above, `fields` is specified in this execution, with support for `module`, and `env`. These env flag is directly tied to the [GlobalEnv](https://github.com/travetto/travetto/tree/main/module/base/src/global-env.ts#
|
|
457
|
+
As noted in the example above, `fields` is specified in this execution, with support for `module`, and `env`. These env flag is directly tied to the [GlobalEnv](https://github.com/travetto/travetto/tree/main/module/base/src/global-env.ts#L9) flags defined in the [Base](https://github.com/travetto/travetto/tree/main/module/base#readme "Environment config and common utilities for travetto applications.") module.
|
|
459
458
|
|
|
460
459
|
The `module` field is slightly more complex, but is geared towards supporting commands within a monorepo context. This flag ensures that a module is specified if running from the root of the monorepo, and that the module provided is real, and can run the desired command. When running from an explicit module folder in the monorepo, the module flag is ignored.
|
|
461
460
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cli",
|
|
3
|
-
"version": "3.4.0
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "CLI infrastructure for Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"directory": "module/cli"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@travetto/schema": "^3.4.0
|
|
33
|
-
"@travetto/terminal": "^3.4.0
|
|
32
|
+
"@travetto/schema": "^3.4.0",
|
|
33
|
+
"@travetto/terminal": "^3.4.0"
|
|
34
34
|
},
|
|
35
35
|
"travetto": {
|
|
36
36
|
"displayName": "Command Line Interface"
|
package/src/decorators.ts
CHANGED
|
@@ -1,47 +1,34 @@
|
|
|
1
|
-
import { Class, ClassInstance,
|
|
1
|
+
import { Class, ClassInstance, ConsoleManager, GlobalEnv, defineEnv } from '@travetto/base';
|
|
2
2
|
import { RootIndex } from '@travetto/manifest';
|
|
3
3
|
import { SchemaRegistry } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
-
import { CliCommandShape } from './types';
|
|
5
|
+
import { CliCommandShape, CliCommandShapeFields } from './types';
|
|
6
6
|
import { CliCommandRegistry, CliCommandConfigOptions } from './registry';
|
|
7
7
|
import { CliModuleUtil } from './module';
|
|
8
8
|
import { CliUtil } from './util';
|
|
9
9
|
|
|
10
|
-
type ExtraFields = 'module' | 'env';
|
|
11
|
-
|
|
12
|
-
const getName = (source: string): string => (source.match(/cli[.](.*)[.]tsx?$/)?.[1] ?? source).replaceAll('_', ':');
|
|
13
|
-
const getMod = (cls: Class): string => RootIndex.getModuleFromSource(RootIndex.getFunctionMetadata(cls)!.source)!.name;
|
|
14
|
-
|
|
15
10
|
/**
|
|
16
11
|
* Decorator to register a CLI command
|
|
17
12
|
* @augments `@travetto/schema:Schema`
|
|
18
13
|
* @augments `@travetto/cli:CliCommand`
|
|
19
14
|
*/
|
|
20
|
-
export function CliCommand(
|
|
15
|
+
export function CliCommand(cfg: CliCommandConfigOptions = {}) {
|
|
21
16
|
return function <T extends CliCommandShape>(target: Class<T>): void {
|
|
22
17
|
const meta = RootIndex.getFunctionMetadata(target);
|
|
23
18
|
if (!meta || meta.abstract) {
|
|
24
19
|
return;
|
|
25
20
|
}
|
|
26
21
|
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
cls: target as ConcreteClass<T>,
|
|
36
|
-
...cfg,
|
|
37
|
-
preMain: (cmd: CliCommandShape & { env?: string, module?: string }) => {
|
|
38
|
-
if (addEnv) {
|
|
39
|
-
defineGlobalEnv({ envName: cmd.env });
|
|
22
|
+
const addModule = cfg.addModule === true || cfg.fields?.includes('module');
|
|
23
|
+
const runtimeModule = cfg.runtimeModule ?? (cfg.addModule ? 'current' : undefined);
|
|
24
|
+
const addEnv = cfg.addEnv ?? cfg.fields?.includes('env');
|
|
25
|
+
const { commandModule } = CliCommandRegistry.registerClass(target, {
|
|
26
|
+
hidden: cfg.hidden,
|
|
27
|
+
preMain: async (cmd) => {
|
|
28
|
+
if (addEnv && 'env' in cmd && typeof cmd.env === 'string') {
|
|
29
|
+
defineEnv({ envName: cmd.env });
|
|
40
30
|
ConsoleManager.setDebug(GlobalEnv.debug, GlobalEnv.devMode);
|
|
41
31
|
}
|
|
42
|
-
if (addModule && cmd.module && cmd.module !== RootIndex.mainModuleName) { // Mono-repo support
|
|
43
|
-
RootIndex.reinitForModule(cmd.module); // Reinit with specified module
|
|
44
|
-
}
|
|
45
32
|
}
|
|
46
33
|
});
|
|
47
34
|
|
|
@@ -61,12 +48,26 @@ export function CliCommand({ fields, ...cfg }: { fields?: ExtraFields[] } & CliC
|
|
|
61
48
|
description: 'Module to run for',
|
|
62
49
|
required: { active: CliUtil.monoRoot }
|
|
63
50
|
});
|
|
51
|
+
}
|
|
64
52
|
|
|
65
|
-
|
|
53
|
+
if (runtimeModule) { // Validate module
|
|
66
54
|
(pendingCls.validators ??= []).push(async item => {
|
|
67
55
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
68
|
-
const
|
|
69
|
-
|
|
56
|
+
const { module: mod } = item as CliCommandShapeFields;
|
|
57
|
+
const runModule = (runtimeModule === 'command' ? commandModule : mod) || RootIndex.mainModuleName;
|
|
58
|
+
|
|
59
|
+
// If we need to run as a specific module
|
|
60
|
+
if (runModule !== RootIndex.mainModuleName) {
|
|
61
|
+
try {
|
|
62
|
+
RootIndex.reinitForModule(runModule);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
return { source: 'flag', message: `${runModule} is an unknown module`, kind: 'custom', path: '.' };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!(await CliModuleUtil.moduleHasDependency(runModule, commandModule))) {
|
|
69
|
+
return { source: 'flag', message: `${runModule} does not have ${commandModule} as a dependency`, kind: 'custom', path: '.' };
|
|
70
|
+
}
|
|
70
71
|
});
|
|
71
72
|
}
|
|
72
73
|
};
|
package/src/execute.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GlobalTerminal } from '@travetto/terminal';
|
|
2
|
-
import { ConsoleManager,
|
|
2
|
+
import { ConsoleManager, defineEnv, ShutdownManager, GlobalEnv } from '@travetto/base';
|
|
3
3
|
|
|
4
4
|
import { HelpUtil } from './help';
|
|
5
5
|
import { CliCommandShape } from './types';
|
|
@@ -14,7 +14,7 @@ export class ExecutionManager {
|
|
|
14
14
|
|
|
15
15
|
static async #envInit(cmd: CliCommandShape): Promise<void> {
|
|
16
16
|
if (cmd.envInit) {
|
|
17
|
-
|
|
17
|
+
defineEnv(await cmd.envInit());
|
|
18
18
|
ConsoleManager.setDebug(GlobalEnv.debug, GlobalEnv.devMode);
|
|
19
19
|
}
|
|
20
20
|
}
|
package/src/module.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { IndexedModule, RootIndex } from '@travetto/manifest';
|
|
2
2
|
|
|
3
3
|
import { CliScmUtil } from './scm';
|
|
4
|
-
import { CliValidationError } from './types';
|
|
5
|
-
import { CliUtil } from './util';
|
|
6
4
|
|
|
7
5
|
type ModuleGraphEntry = { children: Set<string>, name: string, active: Set<string>, parents?: string[] };
|
|
8
6
|
|
|
9
|
-
const modError = (message: string): CliValidationError => ({ source: 'flag', message: `module: ${message}` });
|
|
10
|
-
|
|
11
7
|
/**
|
|
12
8
|
* Simple utilities for understanding modules for CLI use cases
|
|
13
9
|
*/
|
|
@@ -90,23 +86,14 @@ export class CliModuleUtil {
|
|
|
90
86
|
}
|
|
91
87
|
|
|
92
88
|
/**
|
|
93
|
-
*
|
|
89
|
+
* Determine if module has a given dependency
|
|
94
90
|
*/
|
|
95
|
-
static async
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
return modError(`${mod} is an unknown module`);
|
|
99
|
-
} else {
|
|
100
|
-
if (mod !== selfMod) {
|
|
101
|
-
RootIndex.reinitForModule(mod);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const mods = await this.findModules('all');
|
|
105
|
-
const graph = this.getDependencyGraph(mods);
|
|
106
|
-
if (selfMod !== mod && !graph[mod].includes(selfMod)) {
|
|
107
|
-
return modError(`${mod} does not have ${selfMod} as a dependency`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
91
|
+
static async moduleHasDependency(modName: string, depModName: string): Promise<boolean> {
|
|
92
|
+
if (modName === depModName) {
|
|
93
|
+
return true;
|
|
110
94
|
}
|
|
95
|
+
const mods = await this.findModules('all');
|
|
96
|
+
const graph = this.getDependencyGraph(mods);
|
|
97
|
+
return graph[modName].includes(depModName);
|
|
111
98
|
}
|
|
112
99
|
}
|
package/src/registry.ts
CHANGED
|
@@ -5,18 +5,25 @@ import { CliCommandShape } from './types';
|
|
|
5
5
|
import { CliUnknownCommandError } from './error';
|
|
6
6
|
|
|
7
7
|
export type CliCommandConfigOptions = {
|
|
8
|
-
runTarget?: boolean;
|
|
9
8
|
hidden?: boolean;
|
|
9
|
+
runTarget?: boolean;
|
|
10
|
+
addModule?: boolean;
|
|
11
|
+
addEnv?: boolean;
|
|
12
|
+
runtimeModule?: 'current' | 'command';
|
|
13
|
+
/** @deprecated */
|
|
14
|
+
fields?: ('module' | 'env')[];
|
|
10
15
|
};
|
|
11
16
|
|
|
12
|
-
export type CliCommandConfig =
|
|
17
|
+
export type CliCommandConfig = {
|
|
13
18
|
name: string;
|
|
14
|
-
|
|
19
|
+
commandModule: string;
|
|
15
20
|
cls: ConcreteClass<CliCommandShape>;
|
|
21
|
+
hidden?: boolean;
|
|
16
22
|
preMain?: (cmd: CliCommandShape) => void | Promise<void>;
|
|
17
23
|
};
|
|
18
24
|
|
|
19
|
-
const
|
|
25
|
+
const CLI_FILE_REGEX = /\/cli[.](?<name>.*)[.]tsx?$/;
|
|
26
|
+
const getName = (s: string): string => (s.match(CLI_FILE_REGEX)?.groups?.name ?? s).replaceAll('_', ':');
|
|
20
27
|
|
|
21
28
|
class $CliCommandRegistry {
|
|
22
29
|
#commands = new Map<Class, CliCommandConfig>();
|
|
@@ -40,9 +47,9 @@ class $CliCommandRegistry {
|
|
|
40
47
|
for (const e of RootIndex.find({
|
|
41
48
|
module: m => GlobalEnv.devMode || m.prod,
|
|
42
49
|
folder: f => f === 'support',
|
|
43
|
-
file: f => f.role === 'std' &&
|
|
50
|
+
file: f => f.role === 'std' && CLI_FILE_REGEX.test(f.sourceFile)
|
|
44
51
|
})) {
|
|
45
|
-
all.set(e.
|
|
52
|
+
all.set(getName(e.sourceFile), e.import);
|
|
46
53
|
}
|
|
47
54
|
this.#fileMapping = all;
|
|
48
55
|
}
|
|
@@ -52,8 +59,16 @@ class $CliCommandRegistry {
|
|
|
52
59
|
/**
|
|
53
60
|
* Registers a cli command
|
|
54
61
|
*/
|
|
55
|
-
registerClass(cfg: CliCommandConfig):
|
|
56
|
-
|
|
62
|
+
registerClass(cls: Class, cfg: Partial<CliCommandConfig>): CliCommandConfig {
|
|
63
|
+
const source = RootIndex.getFunctionMetadata(cls)!.source;
|
|
64
|
+
this.#commands.set(cls, {
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
66
|
+
cls: cls as ConcreteClass,
|
|
67
|
+
name: getName(source),
|
|
68
|
+
commandModule: RootIndex.getModuleFromSource(source)!.name,
|
|
69
|
+
...cfg,
|
|
70
|
+
});
|
|
71
|
+
return this.#commands.get(cls)!;
|
|
57
72
|
}
|
|
58
73
|
|
|
59
74
|
/**
|
|
@@ -68,7 +83,7 @@ class $CliCommandRegistry {
|
|
|
68
83
|
*/
|
|
69
84
|
getName(cmd: CliCommandShape, withModule = false): string | undefined {
|
|
70
85
|
const cfg = this.getConfig(cmd);
|
|
71
|
-
const prefix = withModule ? `${cfg.
|
|
86
|
+
const prefix = withModule ? `${cfg.commandModule}:` : '';
|
|
72
87
|
return `${prefix}${cfg.name}`;
|
|
73
88
|
}
|
|
74
89
|
|
package/src/schema.ts
CHANGED
|
@@ -237,9 +237,11 @@ export class CliCommandSchemaUtil {
|
|
|
237
237
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
238
238
|
const cls = cmd.constructor as Class<CliCommandShape>;
|
|
239
239
|
|
|
240
|
+
const paramNames = SchemaRegistry.getMethodSchema(cls, 'main').map(x => x.name);
|
|
241
|
+
|
|
240
242
|
const validators = [
|
|
241
243
|
(): Promise<void> => SchemaValidator.validate(cls, cmd).then(() => { }),
|
|
242
|
-
(): Promise<void> => SchemaValidator.validateMethod(cls, 'main', args),
|
|
244
|
+
(): Promise<void> => SchemaValidator.validateMethod(cls, 'main', args, paramNames),
|
|
243
245
|
async (): Promise<void> => {
|
|
244
246
|
const res = await cmd.validate?.(...args);
|
|
245
247
|
if (res) {
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Closeable,
|
|
1
|
+
import { Closeable, EnvInit } from '@travetto/base';
|
|
2
2
|
|
|
3
3
|
type OrProm<T> = T | Promise<T>;
|
|
4
4
|
|
|
@@ -29,7 +29,7 @@ export interface CliCommandShape {
|
|
|
29
29
|
/**
|
|
30
30
|
* Setup environment before command runs
|
|
31
31
|
*/
|
|
32
|
-
envInit?(): OrProm<
|
|
32
|
+
envInit?(): OrProm<EnvInit>;
|
|
33
33
|
/**
|
|
34
34
|
* Extra help
|
|
35
35
|
*/
|
|
@@ -52,6 +52,28 @@ export interface CliCommandShape {
|
|
|
52
52
|
validate?(...unknownArgs: unknown[]): OrProm<CliValidationError | CliValidationError[] | undefined>;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Command shape common fields
|
|
57
|
+
*/
|
|
58
|
+
export type CliCommandShapeFields = {
|
|
59
|
+
/**
|
|
60
|
+
* Environment to run in
|
|
61
|
+
*/
|
|
62
|
+
env?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Should the cli invocation trigger a debug session, via IPC
|
|
65
|
+
*/
|
|
66
|
+
debugIpc?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Should the invocation run with auto-restart
|
|
69
|
+
*/
|
|
70
|
+
canRestart?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* The module to run the command from
|
|
73
|
+
*/
|
|
74
|
+
module?: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
55
77
|
/**
|
|
56
78
|
* CLI Command argument/flag shape
|
|
57
79
|
*/
|
|
@@ -74,7 +96,7 @@ export type CliCommandInput = {
|
|
|
74
96
|
export type CliCommandSchema = {
|
|
75
97
|
name: string;
|
|
76
98
|
title: string;
|
|
77
|
-
|
|
99
|
+
commandModule: string;
|
|
78
100
|
runTarget?: boolean;
|
|
79
101
|
description?: string;
|
|
80
102
|
args: CliCommandInput[];
|
package/src/util.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Env, ExecUtil, GlobalEnv } from '@travetto/base';
|
|
2
2
|
import { RootIndex } from '@travetto/manifest';
|
|
3
3
|
|
|
4
|
-
import { CliCommandShape } from './types';
|
|
4
|
+
import { CliCommandShape, CliCommandShapeFields } from './types';
|
|
5
5
|
import { CliCommandRegistry } from './registry';
|
|
6
6
|
|
|
7
7
|
export class CliUtil {
|
|
@@ -23,7 +23,7 @@ export class CliUtil {
|
|
|
23
23
|
/**
|
|
24
24
|
* Run a command as restartable, linking into self
|
|
25
25
|
*/
|
|
26
|
-
static runWithRestart<T extends
|
|
26
|
+
static runWithRestart<T extends CliCommandShapeFields & CliCommandShape>(cmd: T): Promise<unknown> | undefined {
|
|
27
27
|
const canRestart = cmd.canRestart ??= GlobalEnv.devMode;
|
|
28
28
|
|
|
29
29
|
if (canRestart === false || Env.isFalse('TRV_CAN_RESTART')) {
|
|
@@ -40,50 +40,48 @@ export class CliUtil {
|
|
|
40
40
|
* Dispatch IPC payload
|
|
41
41
|
*/
|
|
42
42
|
static async triggerIpc<T extends CliCommandShape>(action: 'run', cmd: T): Promise<boolean> {
|
|
43
|
-
|
|
43
|
+
const ipcUrl = process.env.TRV_CLI_IPC;
|
|
44
|
+
|
|
45
|
+
if (!ipcUrl) {
|
|
44
46
|
return false;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
const info = await client.getInfo(true);
|
|
49
|
+
const info = await fetch(ipcUrl).catch(() => ({ ok: false }));
|
|
50
50
|
|
|
51
|
-
if (!info) { // Server not running
|
|
51
|
+
if (!info.ok) { // Server not running
|
|
52
52
|
return false;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
const defaultEnvKeys = new Set(Object.keys(info.env ?? {}));
|
|
56
|
-
defaultEnvKeys.add('PS1').add('INIT_CWD').add('COLOR').add('LANGUAGE').add('PROFILEHOME').add('_');
|
|
57
|
-
|
|
58
|
-
const env = Object.fromEntries(
|
|
59
|
-
Object.entries(process.env).filter(([k]) =>
|
|
60
|
-
!defaultEnvKeys.has(k) && !/^(npm_|GTK|GDK|TRV|NODE|GIT|TERM_)/.test(k) && !/VSCODE/.test(k)
|
|
61
|
-
)
|
|
62
|
-
);
|
|
63
|
-
|
|
64
55
|
const cfg = CliCommandRegistry.getConfig(cmd);
|
|
65
56
|
const req = {
|
|
66
57
|
type: `@travetto/cli:${action}`,
|
|
67
|
-
ipc: process.env.TRV_CLI_IPC,
|
|
68
58
|
data: {
|
|
69
59
|
name: cfg.name,
|
|
70
|
-
commandModule: cfg.
|
|
60
|
+
commandModule: cfg.commandModule,
|
|
71
61
|
module: RootIndex.manifest.mainModule,
|
|
72
62
|
args: process.argv.slice(3),
|
|
73
|
-
env
|
|
74
63
|
}
|
|
75
64
|
};
|
|
76
65
|
|
|
77
66
|
console.log('Triggering IPC request', req);
|
|
78
67
|
|
|
79
|
-
|
|
80
|
-
|
|
68
|
+
const defaultEnvKeys = new Set(['PS1', 'INIT_CWD', 'COLOR', 'LANGUAGE', 'PROFILEHOME', '_']);
|
|
69
|
+
const env = Object.fromEntries(
|
|
70
|
+
Object.entries(process.env).filter(([k]) =>
|
|
71
|
+
!defaultEnvKeys.has(k) && !/^(npm_|GTK|GDK|TRV|NODE|GIT|TERM_)/.test(k) && !/VSCODE/.test(k)
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
Object.assign(req.data, { env });
|
|
76
|
+
|
|
77
|
+
const sent = await fetch(ipcUrl, { method: 'POST', body: JSON.stringify(req) });
|
|
78
|
+
return sent.ok;
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
/**
|
|
84
82
|
* Debug if IPC available
|
|
85
83
|
*/
|
|
86
|
-
static async debugIfIpc<T extends
|
|
84
|
+
static async debugIfIpc<T extends CliCommandShapeFields & CliCommandShape>(cmd: T): Promise<boolean> {
|
|
87
85
|
const canDebug = cmd.debugIpc ??= GlobalEnv.devMode;
|
|
88
86
|
return canDebug !== false && this.triggerIpc('run', cmd);
|
|
89
87
|
}
|