@travetto/cli 3.1.0-rc.0 → 3.1.0-rc.10
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 +98 -35
- package/package.json +3 -4
- package/src/decorators.ts +9 -7
- package/src/execute.ts +1 -2
- package/src/help.ts +23 -7
- package/src/module.ts +2 -142
- package/src/schema.ts +23 -15
- package/src/scm.ts +1 -1
- package/src/types.ts +4 -7
- package/src/util.ts +1 -1
- package/support/cli.cli_schema.ts +2 -3
- package/support/cli.main.ts +1 -5
- package/support/cli.exec.ts +0 -55
- package/support/cli.list.ts +0 -51
- package/support/cli.version-sync.ts +0 -16
package/README.md
CHANGED
|
@@ -22,32 +22,32 @@ $ trv --help
|
|
|
22
22
|
Usage: [options] [command]
|
|
23
23
|
|
|
24
24
|
Commands:
|
|
25
|
-
doc
|
|
26
|
-
doc:angular
|
|
27
|
-
doc:mapping
|
|
28
|
-
email:compile
|
|
29
|
-
email:editor
|
|
30
|
-
|
|
31
|
-
lint
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
pack
|
|
39
|
-
pack:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
repo:publish
|
|
43
|
-
repo:version
|
|
44
|
-
|
|
45
|
-
run:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
test
|
|
50
|
-
|
|
25
|
+
doc Command line support for generating module docs.
|
|
26
|
+
doc:angular Generate documentation into the angular webapp under related/travetto.github.io
|
|
27
|
+
doc:mapping Generate module mapping for @travetto/doc
|
|
28
|
+
email:compile CLI Entry point for running the email server
|
|
29
|
+
email:editor The email editor compilation service and output serving
|
|
30
|
+
lint Command line support for linting
|
|
31
|
+
lint:register Writes the lint configuration file
|
|
32
|
+
model:export Exports model schemas
|
|
33
|
+
model:install Installing models
|
|
34
|
+
openapi:client CLI for generating the cli client
|
|
35
|
+
openapi:spec CLI for outputting the open api spec to a local file
|
|
36
|
+
pack Standard pack support
|
|
37
|
+
pack:docker Standard docker support for pack
|
|
38
|
+
pack:lambda Standard lambda support for pack
|
|
39
|
+
pack:zip Standard zip support for pack
|
|
40
|
+
repo:exec Repo execution
|
|
41
|
+
repo:list Allows for listing of modules
|
|
42
|
+
repo:publish Publish all pending modules
|
|
43
|
+
repo:version Version all changed dependencies
|
|
44
|
+
repo:version-sync Enforces all packages to write out their versions and dependencies
|
|
45
|
+
run:double Doubles a number
|
|
46
|
+
run:rest Run a rest server as an application
|
|
47
|
+
scaffold Command to run scaffolding
|
|
48
|
+
service Allows for running services
|
|
49
|
+
test Launch test framework and execute tests
|
|
50
|
+
test:watch Invoke the test watcher
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
This listing is from the [Travetto](https://travetto.dev) monorepo, and represents the majority of tools that can be invoked from the command line.
|
|
@@ -130,9 +130,9 @@ HELLO
|
|
|
130
130
|
|
|
131
131
|
The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L20) supports the following data types for flags:
|
|
132
132
|
* Boolean values
|
|
133
|
-
* Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
134
|
-
* String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
135
|
-
* Date values. The [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
133
|
+
* 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.
|
|
134
|
+
* 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
|
|
135
|
+
* Date values. The [@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 lists. Same as String, but allowing multiple values.
|
|
137
137
|
* Numeric lists. Same as Number, but allowing multiple values.
|
|
138
138
|
|
|
@@ -168,7 +168,7 @@ Options:
|
|
|
168
168
|
$ trv basic:arg 20
|
|
169
169
|
|
|
170
170
|
Execution failed:
|
|
171
|
-
* volume is bigger than (10)
|
|
171
|
+
* Argument volume is bigger than (10)
|
|
172
172
|
|
|
173
173
|
Usage: doc/cli.basic:arg [options] [volume:number]
|
|
174
174
|
|
|
@@ -231,7 +231,7 @@ $ trv basic:arglist 10 5 3 9 8 1
|
|
|
231
231
|
$ trv basic:arglist 10 5 3 9 20 1
|
|
232
232
|
|
|
233
233
|
Execution failed:
|
|
234
|
-
* volumes[4] is bigger than (10)
|
|
234
|
+
* Argument volumes[4] is bigger than (10)
|
|
235
235
|
|
|
236
236
|
Usage: doc/cli.basic:arglist [options] <volumes...:number>
|
|
237
237
|
|
|
@@ -358,7 +358,7 @@ CuStOm
|
|
|
358
358
|
```
|
|
359
359
|
|
|
360
360
|
## VSCode Integration
|
|
361
|
-
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).
|
|
361
|
+
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#L20) decorator. This means the target will be visible within the editor tooling.
|
|
362
362
|
|
|
363
363
|
**Code: Simple Run Target**
|
|
364
364
|
```typescript
|
|
@@ -376,9 +376,43 @@ export class RunCommand {
|
|
|
376
376
|
}
|
|
377
377
|
```
|
|
378
378
|
|
|
379
|
-
Also, any command name that starts with `run:` (i.e. `support/cli.run_*.ts`), will be opted-in to the run behavior unless explicitly disabled.
|
|
380
|
-
|
|
381
379
|
## Advanced Usage
|
|
380
|
+
|
|
381
|
+
**Code: Anatomy of a Command**
|
|
382
|
+
```typescript
|
|
383
|
+
export interface CliCommandShape {
|
|
384
|
+
/**
|
|
385
|
+
* Action target of the command
|
|
386
|
+
*/
|
|
387
|
+
main(...args: unknown[]): OrProm<RunResponse>;
|
|
388
|
+
/**
|
|
389
|
+
* Setup environment before command runs
|
|
390
|
+
*/
|
|
391
|
+
envInit?(): OrProm<GlobalEnvConfig>;
|
|
392
|
+
/**
|
|
393
|
+
* Extra help
|
|
394
|
+
*/
|
|
395
|
+
help?(): OrProm<string[]>;
|
|
396
|
+
/**
|
|
397
|
+
* Is the command active/eligible for usage
|
|
398
|
+
*/
|
|
399
|
+
isActive?(): boolean;
|
|
400
|
+
/**
|
|
401
|
+
* Run before binding occurs
|
|
402
|
+
*/
|
|
403
|
+
initialize?(): OrProm<void>;
|
|
404
|
+
/**
|
|
405
|
+
* Run before validation occurs
|
|
406
|
+
*/
|
|
407
|
+
finalize?(unknownArgs: string[]): OrProm<void>;
|
|
408
|
+
/**
|
|
409
|
+
* Validation method
|
|
410
|
+
*/
|
|
411
|
+
validate?(...unknownArgs: unknown[]): OrProm<CliValidationError | CliValidationError[] | undefined>;
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Dependency Injection
|
|
382
416
|
If the goal is to run a more complex application, which may include depending on [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."), we can take a look at [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.")'s target:
|
|
383
417
|
|
|
384
418
|
**Code: Simple Run Target**
|
|
@@ -390,7 +424,7 @@ import { ServerHandle } from '../src/types';
|
|
|
390
424
|
/**
|
|
391
425
|
* Run a rest server as an application
|
|
392
426
|
*/
|
|
393
|
-
@CliCommand({ fields: ['module', 'env', 'profile'] })
|
|
427
|
+
@CliCommand({ runTarget: true, fields: ['module', 'env', 'profile'] })
|
|
394
428
|
export class RunRestCommand {
|
|
395
429
|
|
|
396
430
|
/** Port to run on */
|
|
@@ -410,3 +444,32 @@ export class RunRestCommand {
|
|
|
410
444
|
As noted in the example above, `fields` is specified in this execution, with support for `module`, `env`, and `profile`. These env and profile flags are directly tied to the GlobalEnv flags defined in the [Base](https://github.com/travetto/travetto/tree/main/module/base#readme "Environment config and common utilities for travetto applications.") module.
|
|
411
445
|
|
|
412
446
|
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.
|
|
447
|
+
|
|
448
|
+
### Custom Validation
|
|
449
|
+
In addition to dependency injection, the command contract also allows for a custom validation function, which will have access to bound command (flags, and args) as well as the unknown arguments. When a command implements this method, any [CliValidationError](https://github.com/travetto/travetto/tree/main/module/cli/src/types.ts#L10) errors that are returned will be shared with the user, and fail to invoke the `main` method.
|
|
450
|
+
|
|
451
|
+
**Code: CliValidationError**
|
|
452
|
+
```typescript
|
|
453
|
+
export type CliValidationError = {
|
|
454
|
+
/**
|
|
455
|
+
* The error message
|
|
456
|
+
*/
|
|
457
|
+
message: string;
|
|
458
|
+
/**
|
|
459
|
+
* Source of validation
|
|
460
|
+
*/
|
|
461
|
+
source?: 'flag' | 'arg' | 'custom';
|
|
462
|
+
};
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
A simple example of the validation can be found in the `doc` command:
|
|
466
|
+
|
|
467
|
+
**Code: Simple Validation Example**
|
|
468
|
+
```typescript
|
|
469
|
+
async validate(...args: unknown[]): Promise<CliValidationError | undefined> {
|
|
470
|
+
const docFile = path.resolve(this.input);
|
|
471
|
+
if (!(await fs.stat(docFile).catch(() => false))) {
|
|
472
|
+
return { message: `input: ${this.input} does not exist`, source: 'flag' };
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cli",
|
|
3
|
-
"version": "3.1.0-rc.
|
|
3
|
+
"version": "3.1.0-rc.10",
|
|
4
4
|
"description": "CLI infrastructure for Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
"directory": "module/cli"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@travetto/schema": "^3.1.0-rc.
|
|
28
|
-
"@travetto/terminal": "^3.1.0-rc.
|
|
29
|
-
"@travetto/worker": "^3.1.0-rc.0"
|
|
27
|
+
"@travetto/schema": "^3.1.0-rc.6",
|
|
28
|
+
"@travetto/terminal": "^3.1.0-rc.1"
|
|
30
29
|
},
|
|
31
30
|
"travetto": {
|
|
32
31
|
"displayName": "Command Line Interface"
|
package/src/decorators.ts
CHANGED
|
@@ -35,12 +35,12 @@ export function CliCommand(cfg: { fields?: ExtraFields[], runTarget?: boolean, h
|
|
|
35
35
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
36
36
|
cls: target as ConcreteClass<T>,
|
|
37
37
|
hidden: cfg.hidden,
|
|
38
|
-
runTarget: cfg.runTarget
|
|
38
|
+
runTarget: cfg.runTarget,
|
|
39
39
|
preMain: (cmd: CliCommandShape & { env?: string, profile?: string[], module?: string }) => {
|
|
40
40
|
if (addEnv) { defineGlobalEnv({ envName: cmd.env }); }
|
|
41
41
|
if (addProfile) { defineGlobalEnv({ profiles: cmd.profile }); }
|
|
42
42
|
if (addEnv || addProfile) { ConsoleManager.setDebugFromEnv(); }
|
|
43
|
-
if (addModule && cmd.module && cmd.module !== RootIndex.
|
|
43
|
+
if (addModule && cmd.module && cmd.module !== RootIndex.mainModuleName) { // Mono-repo support
|
|
44
44
|
RootIndex.reinitForModule(cmd.module); // Reinit with specified module
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -72,10 +72,11 @@ export function CliCommand(cfg: { fields?: ExtraFields[], runTarget?: boolean, h
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
// Register validator for module
|
|
75
|
-
(pendingCls.validators ??= []).push(item =>
|
|
75
|
+
(pendingCls.validators ??= []).push(async item => {
|
|
76
76
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
77
|
-
CliModuleUtil.validateCommandModule(getMod(target), item as { module?: string })
|
|
78
|
-
|
|
77
|
+
const res = await CliModuleUtil.validateCommandModule(getMod(target), item as { module?: string });
|
|
78
|
+
return res ? { ...res, kind: 'custom', path: '.' } : res;
|
|
79
|
+
});
|
|
79
80
|
}
|
|
80
81
|
};
|
|
81
82
|
}
|
|
@@ -83,7 +84,7 @@ export function CliCommand(cfg: { fields?: ExtraFields[], runTarget?: boolean, h
|
|
|
83
84
|
/**
|
|
84
85
|
* Decorator to register a CLI command flag
|
|
85
86
|
*/
|
|
86
|
-
export function CliFlag(cfg: { name?: string, short?: string, desc?: string,
|
|
87
|
+
export function CliFlag(cfg: { name?: string, short?: string, desc?: string, fileExtensions?: string[], envVars?: string[] }) {
|
|
87
88
|
return function (target: ClassInstance, prop: string | symbol): void {
|
|
88
89
|
const aliases: string[] = [];
|
|
89
90
|
if (cfg.name) {
|
|
@@ -97,7 +98,8 @@ export function CliFlag(cfg: { name?: string, short?: string, desc?: string, fil
|
|
|
97
98
|
}
|
|
98
99
|
if (typeof prop === 'string') {
|
|
99
100
|
SchemaRegistry.registerPendingFieldFacet(target.constructor, prop, {
|
|
100
|
-
aliases, description: cfg.desc,
|
|
101
|
+
aliases, description: cfg.desc,
|
|
102
|
+
specifiers: cfg.fileExtensions?.length ? ['file', ...cfg.fileExtensions.map(x => `ext:${x.replace(/[*.]/g, '')}`)] : undefined
|
|
101
103
|
});
|
|
102
104
|
}
|
|
103
105
|
};
|
package/src/execute.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { ConsoleManager, defineGlobalEnv, ShutdownManager } from '@travetto/base
|
|
|
7
7
|
import { HelpUtil } from './help';
|
|
8
8
|
import { CliCommandShape, CliValidationResultError } from './types';
|
|
9
9
|
import { CliCommandRegistry } from './registry';
|
|
10
|
-
import { cliTpl } from './color';
|
|
11
10
|
import { CliCommandSchemaUtil } from './schema';
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -116,7 +115,7 @@ export class ExecutionManager {
|
|
|
116
115
|
console.error(await HelpUtil.renderValidationError(command, err));
|
|
117
116
|
console.error!(await HelpUtil.renderHelp(command));
|
|
118
117
|
} else {
|
|
119
|
-
console.error!(
|
|
118
|
+
console.error!(err);
|
|
120
119
|
console.error!();
|
|
121
120
|
}
|
|
122
121
|
}
|
package/src/help.ts
CHANGED
|
@@ -6,6 +6,12 @@ import { CliCommandShape, CliValidationResultError } from './types';
|
|
|
6
6
|
import { CliCommandRegistry } from './registry';
|
|
7
7
|
import { CliCommandSchemaUtil } from './schema';
|
|
8
8
|
|
|
9
|
+
const validationSourceMap = {
|
|
10
|
+
custom: '',
|
|
11
|
+
arg: 'Argument',
|
|
12
|
+
flag: 'Flag'
|
|
13
|
+
};
|
|
14
|
+
|
|
9
15
|
/**
|
|
10
16
|
* Utilities for showing help
|
|
11
17
|
*/
|
|
@@ -93,12 +99,20 @@ export class HelpUtil {
|
|
|
93
99
|
const maxWidth = keys.reduce((a, b) => Math.max(a, stripAnsiCodes(b).length), 0);
|
|
94
100
|
|
|
95
101
|
for (const cmd of keys) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
try {
|
|
103
|
+
const inst = await CliCommandRegistry.getInstance(cmd);
|
|
104
|
+
if (inst) {
|
|
105
|
+
const cfg = await CliCommandRegistry.getConfig(inst);
|
|
106
|
+
if (!cfg.hidden) {
|
|
107
|
+
const schema = await CliCommandSchemaUtil.getSchema(inst);
|
|
108
|
+
rows.push(cliTpl` ${{ param: cmd.padEnd(maxWidth, ' ') }} ${{ title: schema.title }}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (err instanceof Error) {
|
|
113
|
+
rows.push(cliTpl` ${{ param: cmd.padEnd(maxWidth, ' ') }} ${{ failure: err.message.split(/\n/)[0] }}`);
|
|
114
|
+
} else {
|
|
115
|
+
throw err;
|
|
102
116
|
}
|
|
103
117
|
}
|
|
104
118
|
}
|
|
@@ -124,7 +138,9 @@ export class HelpUtil {
|
|
|
124
138
|
static renderValidationError(cmd: CliCommandShape, err: CliValidationResultError): string {
|
|
125
139
|
return [
|
|
126
140
|
cliTpl`${{ failure: 'Execution failed' }}:`,
|
|
127
|
-
...err.errors.map(e =>
|
|
141
|
+
...err.errors.map(e => e.source && e.source !== 'custom' ?
|
|
142
|
+
cliTpl` * ${{ identifier: validationSourceMap[e.source] }} ${{ subtitle: e.message }}` :
|
|
143
|
+
cliTpl` * ${{ failure: e.message }}`),
|
|
128
144
|
'',
|
|
129
145
|
].join('\n');
|
|
130
146
|
}
|
package/src/module.ts
CHANGED
|
@@ -1,78 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Env, ExecutionOptions, ExecutionResult, ExecutionState, TypedObject } from '@travetto/base';
|
|
3
|
-
import { IndexedModule, PackageUtil, RootIndex } from '@travetto/manifest';
|
|
4
|
-
import { IterableWorkSet, WorkPool, type Worker } from '@travetto/worker';
|
|
1
|
+
import { IndexedModule, RootIndex } from '@travetto/manifest';
|
|
5
2
|
|
|
6
3
|
import { CliScmUtil } from './scm';
|
|
7
4
|
import { CliValidationError } from './types';
|
|
8
5
|
import { CliUtil } from './util';
|
|
9
6
|
|
|
10
|
-
type ModuleRunConfig<T = ExecutionResult> = {
|
|
11
|
-
progressMessage?: (mod: IndexedModule | undefined) => string;
|
|
12
|
-
filter?: (mod: IndexedModule) => boolean | Promise<boolean>;
|
|
13
|
-
transformResult?: (mod: IndexedModule, result: ExecutionResult) => T;
|
|
14
|
-
workerCount?: number;
|
|
15
|
-
progressPosition?: TermLinePosition;
|
|
16
|
-
prefixOutput?: boolean;
|
|
17
|
-
showStdout?: boolean;
|
|
18
|
-
showStderr?: boolean;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
7
|
type ModuleGraphEntry = { children: Set<string>, name: string, active: Set<string>, parents?: string[] };
|
|
22
8
|
|
|
23
|
-
const
|
|
24
|
-
.map(k => [k, ColorDefineUtil.defineColor(k).hsl] as const)
|
|
25
|
-
.filter(([, [, s, l]]) => l > .5 && l < .8 && s > .8)
|
|
26
|
-
.map(([k]) => GlobalTerminal.colorer(k));
|
|
27
|
-
|
|
28
|
-
const colorize = (val: string, idx: number): string => COLORS[idx % COLORS.length](val);
|
|
29
|
-
|
|
30
|
-
const modError = (message: string): CliValidationError => ({ kind: 'required', path: 'module', message });
|
|
9
|
+
const modError = (message: string): CliValidationError => ({ source: 'flag', message: `module: ${message}` });
|
|
31
10
|
|
|
32
11
|
/**
|
|
33
12
|
* Simple utilities for understanding modules for CLI use cases
|
|
34
13
|
*/
|
|
35
14
|
export class CliModuleUtil {
|
|
36
15
|
|
|
37
|
-
/**
|
|
38
|
-
* Generate execution options for running on modules
|
|
39
|
-
*/
|
|
40
|
-
static #buildExecutionOptions<T = ExecutionState>(
|
|
41
|
-
mod: IndexedModule,
|
|
42
|
-
config: ModuleRunConfig<T>,
|
|
43
|
-
prefixes: Record<string, string>,
|
|
44
|
-
stdoutTerm: Terminal,
|
|
45
|
-
stderrTerm: Terminal
|
|
46
|
-
): ExecutionOptions {
|
|
47
|
-
const folder = mod.sourceFolder;
|
|
48
|
-
const opts: ExecutionOptions = {
|
|
49
|
-
stdio: ['ignore', 'pipe', 'pipe', 'ignore'],
|
|
50
|
-
outputMode: 'text',
|
|
51
|
-
catchAsResult: true,
|
|
52
|
-
cwd: folder,
|
|
53
|
-
env: { TRV_MANIFEST: '', TRV_MODULE: mod.name },
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
if (config.showStdout) {
|
|
57
|
-
opts.onStdOutLine = (line: string): unknown => stdoutTerm.writeLines(`${prefixes[folder] ?? ''}${line}`);
|
|
58
|
-
}
|
|
59
|
-
if (config.showStderr) {
|
|
60
|
-
opts.onStdErrorLine = (line: string): unknown => stderrTerm.writeLines(`${prefixes[folder] ?? ''}${line}`);
|
|
61
|
-
}
|
|
62
|
-
return opts;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Build equal sized prefix labels for outputting
|
|
67
|
-
* @param mods
|
|
68
|
-
* @returns
|
|
69
|
-
*/
|
|
70
|
-
static #buildPrefixes(mods: IndexedModule[]): Record<string, string> {
|
|
71
|
-
const folders = mods.map(x => x.sourceFolder);
|
|
72
|
-
const maxWidth = Math.max(...folders.map(x => x.length));
|
|
73
|
-
return Object.fromEntries(folders.map((x, i) => [x, colorize(x.padStart(maxWidth, ' ').padEnd(maxWidth + 1), i)]));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
16
|
/**
|
|
77
17
|
* Find modules that changed, and the dependent modules
|
|
78
18
|
* @param hash
|
|
@@ -115,86 +55,6 @@ export class CliModuleUtil {
|
|
|
115
55
|
).filter(x => x.sourcePath !== RootIndex.manifest.workspacePath);
|
|
116
56
|
}
|
|
117
57
|
|
|
118
|
-
/**
|
|
119
|
-
* Synchronize all workspace modules to have the correct versions from the current packages
|
|
120
|
-
*/
|
|
121
|
-
static async synchronizeModuleVersions(): Promise<Record<string, string>> {
|
|
122
|
-
const versions = {};
|
|
123
|
-
await PackageUtil.syncVersions((await this.findModules('all')).map(x => x.sourcePath), versions);
|
|
124
|
-
return versions;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Run on all modules
|
|
129
|
-
*/
|
|
130
|
-
static async execOnModules<T = ExecutionResult>(
|
|
131
|
-
mode: 'all' | 'changed',
|
|
132
|
-
operation: (mod: IndexedModule, options: ExecutionOptions) => ExecutionState,
|
|
133
|
-
config: ModuleRunConfig<T> = {}
|
|
134
|
-
): Promise<Map<IndexedModule, T>> {
|
|
135
|
-
|
|
136
|
-
config.showStdout = config.showStdout ?? (Env.isSet('DEBUG') && !Env.isFalse('DEBUG'));
|
|
137
|
-
config.showStderr = config.showStderr ?? true;
|
|
138
|
-
|
|
139
|
-
const workerCount = config.workerCount ?? WorkPool.DEFAULT_SIZE;
|
|
140
|
-
|
|
141
|
-
const mods = await CliModuleUtil.findModules(mode);
|
|
142
|
-
const results = new Map<IndexedModule, T>();
|
|
143
|
-
const processes = new Map<IndexedModule, ExecutionState>();
|
|
144
|
-
|
|
145
|
-
const prefixes = config.prefixOutput !== false ? this.#buildPrefixes(mods) : {};
|
|
146
|
-
const stdoutTerm = await Terminal.for({ output: process.stdout });
|
|
147
|
-
const stderrTerm = await Terminal.for({ output: process.stderr });
|
|
148
|
-
|
|
149
|
-
let id = 1;
|
|
150
|
-
const pool = new WorkPool(async () => {
|
|
151
|
-
const worker: Worker<IndexedModule> & { mod?: IndexedModule } = {
|
|
152
|
-
id: id += 1,
|
|
153
|
-
mod: undefined,
|
|
154
|
-
active: false,
|
|
155
|
-
async destroy() {
|
|
156
|
-
this.active = false;
|
|
157
|
-
processes.get(this.mod!)?.process.kill('SIGKILL');
|
|
158
|
-
},
|
|
159
|
-
async execute(mod: IndexedModule) {
|
|
160
|
-
try {
|
|
161
|
-
this.mod = mod;
|
|
162
|
-
this.active = true;
|
|
163
|
-
|
|
164
|
-
if (await config.filter?.(mod) === false) {
|
|
165
|
-
this.active = false;
|
|
166
|
-
} else {
|
|
167
|
-
const opts = CliModuleUtil.#buildExecutionOptions(mod, config, prefixes, stdoutTerm, stderrTerm);
|
|
168
|
-
|
|
169
|
-
const result = await operation(mod, opts).result;
|
|
170
|
-
|
|
171
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
172
|
-
const output = (config.transformResult ? config.transformResult(mod, result) : result) as T;
|
|
173
|
-
results.set(mod, output);
|
|
174
|
-
}
|
|
175
|
-
} finally {
|
|
176
|
-
this.active = false;
|
|
177
|
-
delete this.mod;
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
return worker;
|
|
182
|
-
}, { max: workerCount, min: workerCount });
|
|
183
|
-
|
|
184
|
-
const work = pool.iterateProcess(new IterableWorkSet(mods));
|
|
185
|
-
|
|
186
|
-
if (config.progressMessage) {
|
|
187
|
-
const cfg = { position: config.progressPosition ?? 'bottom' } as const;
|
|
188
|
-
await stdoutTerm.trackProgress(work, ev => ({ ...ev, text: config.progressMessage!(ev.value) }), cfg);
|
|
189
|
-
} else {
|
|
190
|
-
for await (const _ of work) {
|
|
191
|
-
// Ensure its all consumed
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return results;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
58
|
/**
|
|
199
59
|
* Get module dependency graph, fully collapsed
|
|
200
60
|
*/
|
package/src/schema.ts
CHANGED
|
@@ -5,23 +5,29 @@ import { CliCommandRegistry } from './registry';
|
|
|
5
5
|
import { CliCommandInput, CliCommandSchema, CliCommandShape, CliValidationResultError } from './types';
|
|
6
6
|
|
|
7
7
|
function fieldToInput(x: FieldConfig): CliCommandInput {
|
|
8
|
+
const type = x.type === Date ? 'date' :
|
|
9
|
+
x.type === Boolean ? 'boolean' :
|
|
10
|
+
x.type === String ? (x.specifiers?.includes('file') ? 'file' : 'string') :
|
|
11
|
+
x.type === Number ? 'number' :
|
|
12
|
+
x.type === RegExp ? 'regex' : 'string';
|
|
8
13
|
return ({
|
|
9
14
|
name: x.name,
|
|
10
15
|
description: x.description,
|
|
11
16
|
array: x.array,
|
|
12
17
|
required: x.required?.active,
|
|
13
18
|
choices: x.enum?.values,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
x.type === String ? (x.specifier === 'file' ? 'file' : 'string') :
|
|
17
|
-
x.type === Number ? 'number' :
|
|
18
|
-
x.type === RegExp ? 'regex' : 'string',
|
|
19
|
+
fileExtensions: type === 'file' ? x.specifiers?.filter(s => s.startsWith('ext:')).map(s => s.split('ext:')[1]) : undefined,
|
|
20
|
+
type,
|
|
19
21
|
default: x.default,
|
|
20
22
|
flagNames: (x.aliases ?? []).slice(0).filter(v => !v.startsWith('env.')),
|
|
21
23
|
envVars: (x.aliases ?? []).slice(0).filter(v => v.startsWith('env.')).map(v => v.replace('env.', ''))
|
|
22
24
|
});
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
const VALID_FLAG = /^-{1,2}[a-z]/i;
|
|
28
|
+
const LONG_FLAG = /^--[a-z]/i;
|
|
29
|
+
const SHORT_FLAG = /^-[a-z]/i;
|
|
30
|
+
|
|
25
31
|
const isBoolFlag = (x: CliCommandInput): boolean => x.type === 'boolean' && !x.array;
|
|
26
32
|
|
|
27
33
|
/**
|
|
@@ -55,7 +61,7 @@ export class CliCommandSchemaUtil {
|
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
const schema = await SchemaRegistry.getViewSchema(cls);
|
|
58
|
-
const flags =
|
|
64
|
+
const flags = Object.values(schema.schema).map(fieldToInput);
|
|
59
65
|
|
|
60
66
|
// Add help command
|
|
61
67
|
flags.push({ name: 'help', flagNames: ['h'], description: 'display help for command', type: 'boolean' });
|
|
@@ -64,13 +70,13 @@ export class CliCommandSchemaUtil {
|
|
|
64
70
|
|
|
65
71
|
const used = new Set(flags
|
|
66
72
|
.flatMap(f => f.flagNames ?? [])
|
|
67
|
-
.filter(x =>
|
|
73
|
+
.filter(x => SHORT_FLAG.test(x) || x.replaceAll('-', '').length < 3)
|
|
68
74
|
.map(x => x.replace(/^-+/, ''))
|
|
69
75
|
);
|
|
70
76
|
|
|
71
77
|
for (const flag of flags) {
|
|
72
|
-
let short = (flag.flagNames ?? []).find(x =>
|
|
73
|
-
const long = (flag.flagNames ?? []).find(x =>
|
|
78
|
+
let short = (flag.flagNames ?? []).find(x => SHORT_FLAG.test(x) || x.replaceAll('-', '').length < 3)?.replace(/^-+/, '');
|
|
79
|
+
const long = (flag.flagNames ?? []).find(x => LONG_FLAG.test(x) || x.replaceAll('-', '').length > 2)?.replace(/^-+/, '') ??
|
|
74
80
|
flag.name.replace(/([a-z])([A-Z])/g, (_, l, r: string) => `${l}-${r.toLowerCase()}`);
|
|
75
81
|
const aliases: string[] = flag.flagNames = [];
|
|
76
82
|
|
|
@@ -123,7 +129,7 @@ export class CliCommandSchemaUtil {
|
|
|
123
129
|
|
|
124
130
|
for (const el of schema.args) {
|
|
125
131
|
// Siphon off unrecognized flags, in order
|
|
126
|
-
while (i < copy.length && copy[i]
|
|
132
|
+
while (i < copy.length && VALID_FLAG.test(copy[i])) {
|
|
127
133
|
i += 1;
|
|
128
134
|
}
|
|
129
135
|
|
|
@@ -132,7 +138,7 @@ export class CliCommandSchemaUtil {
|
|
|
132
138
|
} else if (el.array) {
|
|
133
139
|
const sub: string[] = [];
|
|
134
140
|
while (i < copy.length) {
|
|
135
|
-
if (!copy[i]
|
|
141
|
+
if (!VALID_FLAG.test(copy[i])) {
|
|
136
142
|
sub.push(copy[i]);
|
|
137
143
|
found[i] = true;
|
|
138
144
|
}
|
|
@@ -196,7 +202,7 @@ export class CliCommandSchemaUtil {
|
|
|
196
202
|
} else if (isBoolFlag(input)) {
|
|
197
203
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
198
204
|
template[key] = !arg.startsWith('--no') as T[typeof key];
|
|
199
|
-
} else if (next === undefined ||
|
|
205
|
+
} else if (next === undefined || VALID_FLAG.test(next)) {
|
|
200
206
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
201
207
|
template[key] = null as T[typeof key];
|
|
202
208
|
} else if (input.array) {
|
|
@@ -232,16 +238,18 @@ export class CliCommandSchemaUtil {
|
|
|
232
238
|
async (): Promise<void> => {
|
|
233
239
|
const res = await cmd.validate?.(...args);
|
|
234
240
|
if (res) {
|
|
235
|
-
throw new
|
|
241
|
+
throw new CliValidationResultError(Array.isArray(res) ? res : [res]);
|
|
236
242
|
}
|
|
237
243
|
},
|
|
238
244
|
];
|
|
239
245
|
|
|
246
|
+
const SOURCES = ['flag', 'arg', 'custom'] as const;
|
|
247
|
+
|
|
240
248
|
const results = validators.map((x, i) => x().catch(err => {
|
|
241
|
-
if (!(err instanceof ValidationResultError)) {
|
|
249
|
+
if (!(err instanceof CliValidationResultError) && !(err instanceof ValidationResultError)) {
|
|
242
250
|
throw err;
|
|
243
251
|
}
|
|
244
|
-
return err.errors.map(v => ({
|
|
252
|
+
return err.errors.map(v => ({ source: SOURCES[i], ...v }));
|
|
245
253
|
}));
|
|
246
254
|
|
|
247
255
|
const errors = (await Promise.all(results)).flatMap(x => (x ?? []));
|
package/src/scm.ts
CHANGED
|
@@ -47,7 +47,7 @@ export class CliScmUtil {
|
|
|
47
47
|
*/
|
|
48
48
|
static async findChangedModulesSince(hash: string): Promise<IndexedModule[]> {
|
|
49
49
|
const ws = RootIndex.manifest.workspacePath;
|
|
50
|
-
const res = await ExecUtil.spawn('git', ['diff', '--name-only', `HEAD..${hash}
|
|
50
|
+
const res = await ExecUtil.spawn('git', ['diff', '--name-only', `HEAD..${hash}`, ':!**/DOC.tsx', ':!**/DOC.html', ':!**/README.md'], { cwd: ws }).result;
|
|
51
51
|
const out = new Set<IndexedModule>();
|
|
52
52
|
for (const line of res.stdout.split(/\n/g)) {
|
|
53
53
|
const mod = RootIndex.getFromSource(path.resolve(ws, line));
|
package/src/types.ts
CHANGED
|
@@ -13,13 +13,9 @@ export type CliValidationError = {
|
|
|
13
13
|
*/
|
|
14
14
|
message: string;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Source of validation
|
|
17
17
|
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The kind of validation
|
|
21
|
-
*/
|
|
22
|
-
kind: string;
|
|
18
|
+
source?: 'flag' | 'arg' | 'custom';
|
|
23
19
|
};
|
|
24
20
|
|
|
25
21
|
/**
|
|
@@ -65,7 +61,7 @@ export interface CliCommandShape {
|
|
|
65
61
|
/**
|
|
66
62
|
* Validation method
|
|
67
63
|
*/
|
|
68
|
-
validate?(...
|
|
64
|
+
validate?(...unknownArgs: unknown[]): OrProm<CliValidationError | CliValidationError[] | undefined>;
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
/**
|
|
@@ -75,6 +71,7 @@ export type CliCommandInput = {
|
|
|
75
71
|
name: string;
|
|
76
72
|
description?: string;
|
|
77
73
|
type: 'string' | 'file' | 'number' | 'boolean' | 'date' | 'regex';
|
|
74
|
+
fileExtensions?: string[];
|
|
78
75
|
choices?: unknown[];
|
|
79
76
|
required?: boolean;
|
|
80
77
|
array?: boolean;
|
package/src/util.ts
CHANGED
|
@@ -12,7 +12,7 @@ export class CliUtil {
|
|
|
12
12
|
* Get a simplified version of a module name
|
|
13
13
|
* @returns
|
|
14
14
|
*/
|
|
15
|
-
static getSimpleModuleName(name = RootIndex.
|
|
15
|
+
static getSimpleModuleName(name = RootIndex.mainModuleName): string {
|
|
16
16
|
return name.replace(/[\/]/, '_').replace(/@/, '');
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -17,9 +17,8 @@ export class CliSchemaCommand {
|
|
|
17
17
|
async validate(name?: string): Promise<CliValidationError | undefined> {
|
|
18
18
|
if (name && !CliCommandRegistry.getCommandMapping().has(name)) {
|
|
19
19
|
return {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
message: `${name} is not a valid cli command`
|
|
20
|
+
source: 'arg',
|
|
21
|
+
message: `name: ${name} is not a valid cli command`
|
|
23
22
|
};
|
|
24
23
|
}
|
|
25
24
|
}
|
package/support/cli.main.ts
CHANGED
|
@@ -25,11 +25,7 @@ export class MainCommand implements CliCommandShape {
|
|
|
25
25
|
async validate(fileOrImport: string): Promise<CliValidationError | undefined> {
|
|
26
26
|
const imp = await this.#getImport(fileOrImport);
|
|
27
27
|
if (!imp) {
|
|
28
|
-
return {
|
|
29
|
-
message: `Unknown file: ${fileOrImport}`,
|
|
30
|
-
kind: 'required',
|
|
31
|
-
path: 'fileOrImport'
|
|
32
|
-
};
|
|
28
|
+
return { message: `Unknown file: ${fileOrImport}` };
|
|
33
29
|
}
|
|
34
30
|
}
|
|
35
31
|
|
package/support/cli.exec.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { CliCommand, CliCommandShape, CliModuleUtil } from '@travetto/cli';
|
|
2
|
-
import { WorkPool } from '@travetto/worker';
|
|
3
|
-
import { RootIndex } from '@travetto/manifest';
|
|
4
|
-
import { ExecUtil, GlobalEnvConfig } from '@travetto/base';
|
|
5
|
-
import { Max, Min } from '@travetto/schema';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Repo execution
|
|
9
|
-
*/
|
|
10
|
-
@CliCommand()
|
|
11
|
-
export class RepoExecCommand implements CliCommandShape {
|
|
12
|
-
|
|
13
|
-
#unknownArgs?: string[];
|
|
14
|
-
|
|
15
|
-
/** Only changed modules */
|
|
16
|
-
changed = true;
|
|
17
|
-
|
|
18
|
-
/** Number of concurrent workers */
|
|
19
|
-
@Min(1) @Max(WorkPool.MAX_SIZE)
|
|
20
|
-
workers = WorkPool.DEFAULT_SIZE;
|
|
21
|
-
|
|
22
|
-
/** Prefix output by folder */
|
|
23
|
-
prefixOutput = true;
|
|
24
|
-
|
|
25
|
-
/** Show stdout */
|
|
26
|
-
showStdout = true;
|
|
27
|
-
|
|
28
|
-
isActive(): boolean {
|
|
29
|
-
return !!RootIndex.manifest.monoRepo;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
envInit(): GlobalEnvConfig {
|
|
33
|
-
return { debug: false };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
finalize(unknownArgs?: string[] | undefined): void | Promise<void> {
|
|
37
|
-
this.#unknownArgs = unknownArgs;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async main(cmd: string, args: string[]): Promise<void> {
|
|
41
|
-
const finalArgs = [...args, ...this.#unknownArgs ?? []];
|
|
42
|
-
|
|
43
|
-
await CliModuleUtil.execOnModules(
|
|
44
|
-
this.changed ? 'changed' : 'all',
|
|
45
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
46
|
-
(mod, opts) => ExecUtil.spawn(cmd, finalArgs, opts),
|
|
47
|
-
{
|
|
48
|
-
progressMessage: mod => `Running '${cmd} ${finalArgs.join(' ')}' [%idx/%total] ${mod?.sourceFolder ?? ''}`,
|
|
49
|
-
showStdout: this.showStdout,
|
|
50
|
-
prefixOutput: this.prefixOutput,
|
|
51
|
-
workerCount: this.workers,
|
|
52
|
-
}
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
}
|
package/support/cli.list.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { CliCommandShape, CliCommand, CliModuleUtil } from '@travetto/cli';
|
|
2
|
-
import { RootIndex } from '@travetto/manifest';
|
|
3
|
-
|
|
4
|
-
const write = (line: string): Promise<void> => new Promise(r => process.stdout.write(`${line}\n`, () => r()));
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Allows for listing of modules
|
|
8
|
-
*/
|
|
9
|
-
@CliCommand()
|
|
10
|
-
export class ListModuleCommand implements CliCommandShape {
|
|
11
|
-
|
|
12
|
-
/** Only show changed modules */
|
|
13
|
-
changed = false;
|
|
14
|
-
|
|
15
|
-
/** Output format */
|
|
16
|
-
format: 'graph' | 'json' | 'list' = 'list';
|
|
17
|
-
|
|
18
|
-
isActive(): boolean {
|
|
19
|
-
return !!RootIndex.manifest.monoRepo;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async main(): Promise<void> {
|
|
23
|
-
|
|
24
|
-
const mods = await CliModuleUtil.findModules(this.changed ? 'changed' : 'all');
|
|
25
|
-
switch (this.format) {
|
|
26
|
-
case 'list': {
|
|
27
|
-
for (const mod of mods.map(x => x.sourceFolder).sort()) {
|
|
28
|
-
await write(mod);
|
|
29
|
-
}
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
case 'json': {
|
|
33
|
-
const outputMap = CliModuleUtil.getDependencyGraph(mods);
|
|
34
|
-
await write(JSON.stringify(Object.entries(outputMap).map(([name, children]) => ({ name, children })), null, 2));
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
case 'graph': {
|
|
38
|
-
await write('digraph g {');
|
|
39
|
-
for (const el of mods) {
|
|
40
|
-
for (const dep of el.parents) {
|
|
41
|
-
if (dep !== RootIndex.mainPackage.name) {
|
|
42
|
-
await write(` "${dep}" -> "${el.name}";`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
await write('}');
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { CliCommandShape, CliCommand, CliModuleUtil } from '@travetto/cli';
|
|
2
|
-
import { RootIndex } from '@travetto/manifest';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Enforces all packages to write out their versions and dependencies
|
|
6
|
-
*/
|
|
7
|
-
@CliCommand()
|
|
8
|
-
export class VersionSyncCommand implements CliCommandShape {
|
|
9
|
-
isActive(): boolean {
|
|
10
|
-
return !!RootIndex.manifest.monoRepo;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async main(): Promise<void> {
|
|
14
|
-
await CliModuleUtil.synchronizeModuleVersions();
|
|
15
|
-
}
|
|
16
|
-
}
|