@travetto/cli 5.0.0-rc.1 → 5.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 +13 -21
- package/package.json +3 -3
- package/src/decorators.ts +87 -32
- package/src/error.ts +2 -2
- package/src/execute.ts +2 -2
- package/src/help.ts +7 -9
- package/src/module.ts +5 -5
- package/src/parse.ts +5 -14
- package/src/registry.ts +13 -26
- package/src/schema.ts +8 -16
- package/src/scm.ts +7 -8
- package/src/trv.d.ts +1 -1
- package/src/types.ts +8 -7
- package/src/util.ts +6 -6
- package/support/cli.cli_schema.ts +1 -1
- package/support/cli.main.ts +5 -19
- package/support/transformer.cli.ts +3 -3
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#L85) 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#L85) 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#L85) 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,11 +130,11 @@ $ 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#L85) supports the following data types for flags:
|
|
134
134
|
* Boolean values
|
|
135
|
-
* Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
136
|
-
* String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
137
|
-
* Date values. The [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
135
|
+
* Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L171), [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L177), [@Precision](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L165), [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L116) decorators help provide additional validation.
|
|
136
|
+
* String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106), [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L116), [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L98) and [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L77) provide additional constraints
|
|
137
|
+
* Date values. The [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L106) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L116) decorators help provide additional validation.
|
|
138
138
|
* String lists. Same as String, but allowing multiple values.
|
|
139
139
|
* Numeric lists. Same as Number, but allowing multiple values.
|
|
140
140
|
|
|
@@ -388,7 +388,7 @@ npx trv call:db --host localhost --port 3306 --username app --password <custom>
|
|
|
388
388
|
```
|
|
389
389
|
|
|
390
390
|
## VSCode Integration
|
|
391
|
-
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#
|
|
391
|
+
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#L85) decorator. This means the target will be visible within the editor tooling.
|
|
392
392
|
|
|
393
393
|
**Code: Simple Run Target**
|
|
394
394
|
```typescript
|
|
@@ -411,6 +411,7 @@ export class RunCommand {
|
|
|
411
411
|
**Code: Anatomy of a Command**
|
|
412
412
|
```typescript
|
|
413
413
|
export interface CliCommandShape<T extends unknown[] = unknown[]> {
|
|
414
|
+
|
|
414
415
|
/**
|
|
415
416
|
* Parsed state
|
|
416
417
|
*/
|
|
@@ -459,9 +460,9 @@ If the goal is to run a more complex application, which may include depending on
|
|
|
459
460
|
|
|
460
461
|
**Code: Simple Run Target**
|
|
461
462
|
```typescript
|
|
462
|
-
import {
|
|
463
|
+
import { Runtime } from '@travetto/runtime';
|
|
463
464
|
import { DependencyRegistry } from '@travetto/di';
|
|
464
|
-
import { CliCommand, CliCommandShape
|
|
465
|
+
import { CliCommand, CliCommandShape } from '@travetto/cli';
|
|
465
466
|
|
|
466
467
|
import { ServerHandle } from '../src/types';
|
|
467
468
|
import { RestNetUtil } from '../src/util/net';
|
|
@@ -469,15 +470,9 @@ import { RestNetUtil } from '../src/util/net';
|
|
|
469
470
|
/**
|
|
470
471
|
* Run a rest server as an application
|
|
471
472
|
*/
|
|
472
|
-
@CliCommand({ runTarget: true,
|
|
473
|
+
@CliCommand({ runTarget: true, with: { debugIpc: true, canRestart: true, module: true, env: true } })
|
|
473
474
|
export class RunRestCommand implements CliCommandShape {
|
|
474
475
|
|
|
475
|
-
/** IPC debug is enabled */
|
|
476
|
-
debugIpc?: boolean;
|
|
477
|
-
|
|
478
|
-
/** Should the server be able to run with restart*/
|
|
479
|
-
canRestart?: boolean;
|
|
480
|
-
|
|
481
476
|
/** Port to run on */
|
|
482
477
|
port?: number;
|
|
483
478
|
|
|
@@ -491,14 +486,11 @@ export class RunRestCommand implements CliCommandShape {
|
|
|
491
486
|
}
|
|
492
487
|
|
|
493
488
|
async main(): Promise<ServerHandle | void> {
|
|
494
|
-
if (await CliUtil.debugIfIpc(this) || await CliUtil.runWithRestart(this)) {
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
489
|
const { RestApplication } = await import('../src/application/rest');
|
|
498
490
|
try {
|
|
499
491
|
return await DependencyRegistry.runInstance(RestApplication);
|
|
500
492
|
} catch (err) {
|
|
501
|
-
if (RestNetUtil.isInuseError(err) && !
|
|
493
|
+
if (RestNetUtil.isInuseError(err) && !Runtime.production && this.killConflict) {
|
|
502
494
|
await RestNetUtil.freePort(err.port);
|
|
503
495
|
return await DependencyRegistry.runInstance(RestApplication);
|
|
504
496
|
}
|
|
@@ -508,7 +500,7 @@ export class RunRestCommand implements CliCommandShape {
|
|
|
508
500
|
}
|
|
509
501
|
```
|
|
510
502
|
|
|
511
|
-
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 [Runtime](https://github.com/travetto/travetto/tree/main/module/
|
|
503
|
+
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 [Runtime](https://github.com/travetto/travetto/tree/main/module/runtime/src/env.ts#L109) `name` defined in the [Runtime](https://github.com/travetto/travetto/tree/main/module/runtime#readme "Runtime for travetto applications.") module.
|
|
512
504
|
|
|
513
505
|
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.
|
|
514
506
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cli",
|
|
3
|
-
"version": "5.0.0-rc.
|
|
3
|
+
"version": "5.0.0-rc.11",
|
|
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": "^5.0.0-rc.
|
|
33
|
-
"@travetto/terminal": "^5.0.0-rc.
|
|
32
|
+
"@travetto/schema": "^5.0.0-rc.11",
|
|
33
|
+
"@travetto/terminal": "^5.0.0-rc.10"
|
|
34
34
|
},
|
|
35
35
|
"travetto": {
|
|
36
36
|
"displayName": "Command Line Interface",
|
package/src/decorators.ts
CHANGED
|
@@ -1,11 +1,81 @@
|
|
|
1
|
-
import { Class, ClassInstance, Env,
|
|
2
|
-
import {
|
|
3
|
-
import { SchemaRegistry } from '@travetto/schema';
|
|
1
|
+
import { Class, ClassInstance, Env, Runtime, RuntimeIndex, describeFunction } from '@travetto/runtime';
|
|
2
|
+
import { FieldConfig, SchemaRegistry } from '@travetto/schema';
|
|
4
3
|
|
|
5
4
|
import { CliCommandShape, CliCommandShapeFields } from './types';
|
|
6
|
-
import { CliCommandRegistry
|
|
5
|
+
import { CliCommandRegistry } from './registry';
|
|
7
6
|
import { CliModuleUtil } from './module';
|
|
8
7
|
import { CliParseUtil } from './parse';
|
|
8
|
+
import { CliUtil } from './util';
|
|
9
|
+
|
|
10
|
+
type Cmd = CliCommandShape & { env?: string };
|
|
11
|
+
|
|
12
|
+
type CliCommandConfigOptions = {
|
|
13
|
+
hidden?: boolean;
|
|
14
|
+
runTarget?: boolean;
|
|
15
|
+
runtimeModule?: 'current' | 'command';
|
|
16
|
+
with?: {
|
|
17
|
+
/** Application environment */
|
|
18
|
+
env?: boolean;
|
|
19
|
+
/** Module to run for */
|
|
20
|
+
module?: boolean;
|
|
21
|
+
/** Should debug invocation trigger via ipc */
|
|
22
|
+
debugIpc?: boolean;
|
|
23
|
+
/** Should the invocation automatically restart on exit */
|
|
24
|
+
canRestart?: boolean;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const FIELD_CONFIG: {
|
|
29
|
+
name: keyof Exclude<CliCommandConfigOptions['with'], undefined>;
|
|
30
|
+
field: Partial<FieldConfig>;
|
|
31
|
+
run: (cmd: Cmd) => (Promise<unknown> | unknown);
|
|
32
|
+
}[] =
|
|
33
|
+
[
|
|
34
|
+
{
|
|
35
|
+
name: 'env',
|
|
36
|
+
run: cmd => Env.TRV_ENV.set(cmd.env || Runtime.env),
|
|
37
|
+
field: {
|
|
38
|
+
type: String,
|
|
39
|
+
aliases: ['e', CliParseUtil.toEnvField(Env.TRV_ENV.key)],
|
|
40
|
+
description: 'Application environment',
|
|
41
|
+
required: { active: false },
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'module',
|
|
46
|
+
run: (): void => { },
|
|
47
|
+
field: {
|
|
48
|
+
type: String,
|
|
49
|
+
aliases: ['m', CliParseUtil.toEnvField(Env.TRV_MODULE.key)],
|
|
50
|
+
description: 'Module to run for',
|
|
51
|
+
specifiers: ['module'],
|
|
52
|
+
required: { active: Runtime.monoRoot },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'debugIpc',
|
|
57
|
+
run: cmd => CliUtil.debugIfIpc(cmd).then((v) => v && process.exit(0)),
|
|
58
|
+
field: {
|
|
59
|
+
type: Boolean,
|
|
60
|
+
aliases: ['di'],
|
|
61
|
+
description: 'Should debug invocation trigger via ipc',
|
|
62
|
+
default: true,
|
|
63
|
+
required: { active: false },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'canRestart',
|
|
68
|
+
run: cmd => CliUtil.runWithRestart(cmd)?.then((v) => v && process.exit(0)),
|
|
69
|
+
field: {
|
|
70
|
+
type: Boolean,
|
|
71
|
+
aliases: ['cr'],
|
|
72
|
+
description: 'Should the invocation automatically restart on exit',
|
|
73
|
+
default: false,
|
|
74
|
+
required: { active: false },
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
|
|
9
79
|
|
|
10
80
|
/**
|
|
11
81
|
* Decorator to register a CLI command
|
|
@@ -14,54 +84,39 @@ import { CliParseUtil } from './parse';
|
|
|
14
84
|
*/
|
|
15
85
|
export function CliCommand(cfg: CliCommandConfigOptions = {}) {
|
|
16
86
|
return function <T extends CliCommandShape>(target: Class<T>): void {
|
|
17
|
-
|
|
18
|
-
if (!meta || meta.abstract) {
|
|
87
|
+
if (!target.Ⲑid || describeFunction(target)?.abstract) {
|
|
19
88
|
return;
|
|
20
89
|
}
|
|
21
90
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const addEnv = cfg.addEnv ?? cfg.fields?.includes('env');
|
|
91
|
+
const VALID_FIELDS = FIELD_CONFIG.filter(f => !!cfg.with?.[f.name]);
|
|
92
|
+
|
|
25
93
|
const { commandModule } = CliCommandRegistry.registerClass(target, {
|
|
26
94
|
hidden: cfg.hidden,
|
|
27
95
|
runTarget: cfg.runTarget,
|
|
28
|
-
preMain: async (cmd:
|
|
29
|
-
|
|
30
|
-
|
|
96
|
+
preMain: async (cmd: Cmd) => {
|
|
97
|
+
for (const field of VALID_FIELDS) {
|
|
98
|
+
await field.run(cmd);
|
|
31
99
|
}
|
|
32
100
|
}
|
|
33
101
|
});
|
|
34
102
|
|
|
35
103
|
const pendingCls = SchemaRegistry.getOrCreatePending(target);
|
|
36
104
|
|
|
37
|
-
|
|
38
|
-
SchemaRegistry.registerPendingFieldConfig(target,
|
|
39
|
-
aliases: ['e', CliParseUtil.toEnvField(Env.TRV_ENV.key)],
|
|
40
|
-
description: 'Application environment',
|
|
41
|
-
required: { active: false }
|
|
42
|
-
});
|
|
105
|
+
for (const { name, field: { type, ...field } } of VALID_FIELDS) {
|
|
106
|
+
SchemaRegistry.registerPendingFieldConfig(target, name, type!, field);
|
|
43
107
|
}
|
|
44
108
|
|
|
45
|
-
|
|
46
|
-
SchemaRegistry.registerPendingFieldConfig(target, 'module', String, {
|
|
47
|
-
aliases: ['m', CliParseUtil.toEnvField(Env.TRV_MODULE.key)],
|
|
48
|
-
description: 'Module to run for',
|
|
49
|
-
specifiers: ['module'],
|
|
50
|
-
required: { active: RuntimeContext.monoRoot }
|
|
51
|
-
});
|
|
52
|
-
}
|
|
109
|
+
const runtimeModule = cfg.runtimeModule ?? (cfg.with?.module ? 'current' : undefined);
|
|
53
110
|
|
|
54
111
|
if (runtimeModule) { // Validate module
|
|
55
|
-
(pendingCls.validators ??= []).push(async
|
|
56
|
-
|
|
57
|
-
const { module: mod } = item as CliCommandShapeFields;
|
|
58
|
-
const runModule = (runtimeModule === 'command' ? commandModule : mod) || RuntimeContext.main.name;
|
|
112
|
+
(pendingCls.validators ??= []).push(async ({ module: mod }: Partial<CliCommandShapeFields>) => {
|
|
113
|
+
const runModule = (runtimeModule === 'command' ? commandModule : mod) || Runtime.main.name;
|
|
59
114
|
|
|
60
115
|
// If we need to run as a specific module
|
|
61
|
-
if (runModule !==
|
|
116
|
+
if (runModule !== Runtime.main.name) {
|
|
62
117
|
try {
|
|
63
118
|
RuntimeIndex.reinitForModule(runModule);
|
|
64
|
-
} catch
|
|
119
|
+
} catch {
|
|
65
120
|
return { source: 'flag', message: `${runModule} is an unknown module`, kind: 'custom', path: '.' };
|
|
66
121
|
}
|
|
67
122
|
}
|
package/src/error.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppError,
|
|
1
|
+
import { AppError, Runtime } from '@travetto/runtime';
|
|
2
2
|
import { PackageUtil } from '@travetto/manifest';
|
|
3
3
|
import { cliTpl } from './color';
|
|
4
4
|
import { CliValidationError, CliCommandShape } from './types';
|
|
@@ -23,7 +23,7 @@ export class CliUnknownCommandError extends Error {
|
|
|
23
23
|
const matchedCfg = COMMAND_PACKAGE.find(([re]) => re.test(cmd));
|
|
24
24
|
if (matchedCfg) {
|
|
25
25
|
const [, pkg, prod] = matchedCfg;
|
|
26
|
-
const install = PackageUtil.getInstallCommand(
|
|
26
|
+
const install = PackageUtil.getInstallCommand(Runtime, `@travetto/${pkg}`, prod);
|
|
27
27
|
return cliTpl`
|
|
28
28
|
${{ title: 'Missing Package' }}\n${'-'.repeat(20)}\nTo use ${{ input: cmd }} please run:\n
|
|
29
29
|
${{ identifier: install }}
|
package/src/execute.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConsoleManager,
|
|
1
|
+
import { ConsoleManager, Runtime, ShutdownManager } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import { HelpUtil } from './help';
|
|
4
4
|
import { CliCommandRegistry } from './registry';
|
|
@@ -54,7 +54,7 @@ export class ExecutionManager {
|
|
|
54
54
|
await command._cfg!.preMain?.(command);
|
|
55
55
|
await command.preMain?.();
|
|
56
56
|
|
|
57
|
-
ConsoleManager.debug(
|
|
57
|
+
ConsoleManager.debug(Runtime.debug);
|
|
58
58
|
const result = await command.main(...boundArgs);
|
|
59
59
|
await CliUtil.listenForResponse(result);
|
|
60
60
|
}
|
package/src/help.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import util from 'node:util';
|
|
2
2
|
|
|
3
|
-
import { Primitive } from '@travetto/
|
|
3
|
+
import { castKey, castTo, Primitive } from '@travetto/runtime';
|
|
4
4
|
|
|
5
5
|
import { cliTpl } from './color';
|
|
6
6
|
import { CliCommandShape } from './types';
|
|
@@ -41,13 +41,11 @@ export class HelpUtil {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const params: string[] = [];
|
|
44
|
-
const
|
|
44
|
+
const descriptions: string[] = [];
|
|
45
45
|
|
|
46
46
|
for (const flag of flags) {
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
50
|
-
const flagVal = command[key] as unknown as Primitive;
|
|
47
|
+
const key = castKey<CliCommandShape>(flag.name);
|
|
48
|
+
const flagVal: Primitive = castTo(command[key]);
|
|
51
49
|
|
|
52
50
|
let aliases = flag.flagNames ?? [];
|
|
53
51
|
if (isBoolFlag(flag)) {
|
|
@@ -68,11 +66,11 @@ export class HelpUtil {
|
|
|
68
66
|
if (key !== 'help' && flagVal !== null && flagVal !== undefined && flagVal !== '') {
|
|
69
67
|
desc.push(cliTpl`(default: ${{ input: JSON.stringify(flagVal) }})`);
|
|
70
68
|
}
|
|
71
|
-
|
|
69
|
+
descriptions.push(desc.join(' '));
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
const paramWidths = params.map(x => util.stripVTControlCharacters(x).length);
|
|
75
|
-
const descWidths =
|
|
73
|
+
const descWidths = descriptions.map(x => util.stripVTControlCharacters(x).length);
|
|
76
74
|
|
|
77
75
|
const paramWidth = Math.max(...paramWidths);
|
|
78
76
|
const descWidth = Math.max(...descWidths);
|
|
@@ -87,7 +85,7 @@ export class HelpUtil {
|
|
|
87
85
|
'',
|
|
88
86
|
cliTpl`${{ title: 'Options:' }}`,
|
|
89
87
|
...params.map((_, i) =>
|
|
90
|
-
` ${params[i]}${' '.repeat((paramWidth - paramWidths[i]))} ${
|
|
88
|
+
` ${params[i]}${' '.repeat((paramWidth - paramWidths[i]))} ${descriptions[i].padEnd(descWidth)}${' '.repeat((descWidth - descWidths[i]))}`
|
|
91
89
|
),
|
|
92
90
|
'',
|
|
93
91
|
...helpText
|
package/src/module.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
2
|
+
import type { IndexedModule } from '@travetto/manifest';
|
|
3
3
|
|
|
4
4
|
import { CliScmUtil } from './scm';
|
|
5
5
|
|
|
@@ -44,11 +44,11 @@ export class CliModuleUtil {
|
|
|
44
44
|
* @param transitive
|
|
45
45
|
* @returns
|
|
46
46
|
*/
|
|
47
|
-
static async findModules(mode: 'all' | 'changed', fromHash?: string, toHash?: string
|
|
47
|
+
static async findModules(mode: 'all' | 'changed', fromHash?: string, toHash?: string): Promise<IndexedModule[]> {
|
|
48
48
|
return (mode === 'changed' ?
|
|
49
|
-
await this.findChangedModulesRecursive(fromHash, toHash,
|
|
49
|
+
await this.findChangedModulesRecursive(fromHash, toHash, true) :
|
|
50
50
|
[...RuntimeIndex.getModuleList('all')].map(x => RuntimeIndex.getModule(x)!)
|
|
51
|
-
).filter(x => x.sourcePath !==
|
|
51
|
+
).filter(x => x.sourcePath !== Runtime.workspace.path);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
package/src/parse.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { RuntimeIndex } from '@travetto/manifest';
|
|
4
|
+
import { Runtime } from '@travetto/runtime';
|
|
6
5
|
import { CliCommandInput, CliCommandSchema, ParsedState } from './types';
|
|
7
6
|
|
|
8
7
|
type ParsedInput = ParsedState['all'][number];
|
|
@@ -86,16 +85,8 @@ export class CliParseUtil {
|
|
|
86
85
|
const key = flag.replace(CONFIG_PRE, '');
|
|
87
86
|
|
|
88
87
|
// We have a file
|
|
89
|
-
const rel = (key.includes('/') ? key :
|
|
90
|
-
.replace(
|
|
91
|
-
.replace('@/', `${mod}/`)
|
|
92
|
-
.replace(/^(@[^\/]+\/[^\/]+)(\/.*)$/, (_, imp, rest) => {
|
|
93
|
-
const val = RuntimeIndex.getModule(imp);
|
|
94
|
-
if (!val) {
|
|
95
|
-
throw new Error(`Unknown module file: ${_}, unable to proceed`);
|
|
96
|
-
}
|
|
97
|
-
return `${val.sourcePath}${rest}`;
|
|
98
|
-
});
|
|
88
|
+
const rel = (key.includes('/') ? key : `@#support/pack.${key}.flags`)
|
|
89
|
+
.replace(/^(@[^#]*)#(.*)$/, (_, imp, rest) => `${Runtime.modulePath(imp)}/${rest}`);
|
|
99
90
|
|
|
100
91
|
const file = path.resolve(rel);
|
|
101
92
|
|
|
@@ -146,7 +137,7 @@ export class CliParseUtil {
|
|
|
146
137
|
const mod = args.reduce(
|
|
147
138
|
(m, x, i, arr) =>
|
|
148
139
|
(i < SEP ? check(arr[i - 1], x) ?? check(...x.split('=')) : undefined) ?? m,
|
|
149
|
-
process.env[ENV_KEY] ||
|
|
140
|
+
process.env[ENV_KEY] || Runtime.main.name
|
|
150
141
|
);
|
|
151
142
|
return (await Promise.all(args.map((x, i) =>
|
|
152
143
|
x.startsWith(CONFIG_PRE) && (i < SEP || SEP < 0) ? this.readFlagFile(x, mod) : x))).flat();
|
|
@@ -200,7 +191,7 @@ export class CliParseUtil {
|
|
|
200
191
|
} else {
|
|
201
192
|
const field = schema.args[argIdx];
|
|
202
193
|
out.push(getInput({ field, input, index: argIdx }));
|
|
203
|
-
// Move argIdx along if not in a
|
|
194
|
+
// Move argIdx along if not in a var arg situation
|
|
204
195
|
if (!field?.array) {
|
|
205
196
|
argIdx += 1;
|
|
206
197
|
}
|
package/src/registry.ts
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import { Class,
|
|
2
|
-
import { RuntimeIndex } from '@travetto/manifest';
|
|
1
|
+
import { asConstructable, Class, classConstruct, describeFunction, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
3
2
|
|
|
4
3
|
import { CliCommandConfig, CliCommandShape } from './types';
|
|
5
4
|
import { CliUnknownCommandError } from './error';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
hidden?: boolean;
|
|
9
|
-
runTarget?: boolean;
|
|
10
|
-
addModule?: boolean;
|
|
11
|
-
addEnv?: boolean;
|
|
12
|
-
runtimeModule?: 'current' | 'command';
|
|
13
|
-
/** @deprecated */
|
|
14
|
-
fields?: ('module' | 'env')[];
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const CLI_FILE_REGEX = /\/cli[.](?<name>.*)[.]tsx?$/;
|
|
6
|
+
const CLI_FILE_REGEX = /\/cli[.](?<name>.*?)([.]tsx?)?$/;
|
|
18
7
|
const getName = (s: string): string => (s.match(CLI_FILE_REGEX)?.groups?.name ?? s).replaceAll('_', ':');
|
|
19
8
|
|
|
20
9
|
class $CliCommandRegistry {
|
|
@@ -25,9 +14,8 @@ class $CliCommandRegistry {
|
|
|
25
14
|
return this.#commands.get(cls);
|
|
26
15
|
}
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return cmd.constructor as Class;
|
|
17
|
+
getClass(cmd: CliCommandShape): Class<CliCommandShape> {
|
|
18
|
+
return asConstructable<CliCommandShape>(cmd).constructor;
|
|
31
19
|
}
|
|
32
20
|
|
|
33
21
|
/**
|
|
@@ -37,7 +25,7 @@ class $CliCommandRegistry {
|
|
|
37
25
|
if (!this.#fileMapping) {
|
|
38
26
|
const all = new Map<string, string>();
|
|
39
27
|
for (const e of RuntimeIndex.find({
|
|
40
|
-
module: m => !
|
|
28
|
+
module: m => !Runtime.production || m.prod,
|
|
41
29
|
folder: f => f === 'support',
|
|
42
30
|
file: f => f.role === 'std' && CLI_FILE_REGEX.test(f.sourceFile)
|
|
43
31
|
})) {
|
|
@@ -51,13 +39,12 @@ class $CliCommandRegistry {
|
|
|
51
39
|
/**
|
|
52
40
|
* Registers a cli command
|
|
53
41
|
*/
|
|
54
|
-
registerClass(cls: Class
|
|
55
|
-
const
|
|
42
|
+
registerClass<T extends CliCommandShape>(cls: Class<T>, cfg: Partial<CliCommandConfig>): CliCommandConfig {
|
|
43
|
+
const meta = describeFunction(cls);
|
|
56
44
|
this.#commands.set(cls, {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
commandModule: RuntimeIndex.getModuleFromSource(source)!.name,
|
|
45
|
+
cls,
|
|
46
|
+
name: getName(meta.import),
|
|
47
|
+
commandModule: meta.module,
|
|
61
48
|
...cfg,
|
|
62
49
|
});
|
|
63
50
|
return this.#commands.get(cls)!;
|
|
@@ -67,7 +54,7 @@ class $CliCommandRegistry {
|
|
|
67
54
|
* Get config for a given instance
|
|
68
55
|
*/
|
|
69
56
|
getConfig(cmd: CliCommandShape): CliCommandConfig {
|
|
70
|
-
return this.getByClass(this
|
|
57
|
+
return this.getByClass(this.getClass(cmd))!;
|
|
71
58
|
}
|
|
72
59
|
|
|
73
60
|
/**
|
|
@@ -87,11 +74,11 @@ class $CliCommandRegistry {
|
|
|
87
74
|
async getInstance(name: string, failOnMissing = false): Promise<CliCommandShape | undefined> {
|
|
88
75
|
const found = this.getCommandMapping().get(name);
|
|
89
76
|
if (found) {
|
|
90
|
-
const values = Object.values
|
|
77
|
+
const values = Object.values(await Runtime.importFrom<Record<string, Class>>(found));
|
|
91
78
|
for (const v of values) {
|
|
92
79
|
const cfg = this.getByClass(v);
|
|
93
80
|
if (cfg) {
|
|
94
|
-
const inst =
|
|
81
|
+
const inst = classConstruct(cfg.cls);
|
|
95
82
|
if (!inst.isActive || inst.isActive()) {
|
|
96
83
|
inst._cfg = this.getConfig(inst);
|
|
97
84
|
return inst;
|
package/src/schema.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Class } from '@travetto/
|
|
1
|
+
import { castKey, castTo, Class } from '@travetto/runtime';
|
|
2
2
|
import { BindUtil, FieldConfig, SchemaRegistry, SchemaValidator, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
import { CliCommandRegistry } from './registry';
|
|
@@ -52,8 +52,7 @@ export class CliCommandSchemaUtil {
|
|
|
52
52
|
* Get schema for a given command
|
|
53
53
|
*/
|
|
54
54
|
static async getSchema(src: Class | CliCommandShape): Promise<CliCommandSchema> {
|
|
55
|
-
|
|
56
|
-
const cls = 'main' in src ? src.constructor as Class : src;
|
|
55
|
+
const cls = 'main' in src ? CliCommandRegistry.getClass(src) : src;
|
|
57
56
|
if (this.#schemas.has(cls)) {
|
|
58
57
|
return this.#schemas.get(cls)!;
|
|
59
58
|
}
|
|
@@ -127,22 +126,18 @@ export class CliCommandSchemaUtil {
|
|
|
127
126
|
for (const arg of state.all) {
|
|
128
127
|
switch (arg.type) {
|
|
129
128
|
case 'flag': {
|
|
130
|
-
|
|
131
|
-
const key = arg.fieldName as keyof T;
|
|
129
|
+
const key = castKey<T>(arg.fieldName);
|
|
132
130
|
const value = arg.value!;
|
|
133
131
|
if (arg.array) {
|
|
134
|
-
|
|
135
|
-
((template[key] as unknown[]) ??= []).push(value);
|
|
132
|
+
castTo<unknown[]>(template[key] ??= castTo([])).push(value);
|
|
136
133
|
} else {
|
|
137
|
-
|
|
138
|
-
template[key] = value as unknown as T[typeof key];
|
|
134
|
+
template[key] = castTo(value);
|
|
139
135
|
}
|
|
140
136
|
break;
|
|
141
137
|
}
|
|
142
138
|
case 'arg': {
|
|
143
139
|
if (arg.array) {
|
|
144
|
-
|
|
145
|
-
((bound[arg.index] ??= []) as unknown[]).push(arg.input);
|
|
140
|
+
castTo<unknown[]>(bound[arg.index] ??= []).push(arg.input);
|
|
146
141
|
} else {
|
|
147
142
|
bound[arg.index] = arg.input;
|
|
148
143
|
}
|
|
@@ -150,8 +145,7 @@ export class CliCommandSchemaUtil {
|
|
|
150
145
|
}
|
|
151
146
|
}
|
|
152
147
|
|
|
153
|
-
|
|
154
|
-
const cls = cmd.constructor as Class<CliCommandShape>;
|
|
148
|
+
const cls = CliCommandRegistry.getClass(cmd);
|
|
155
149
|
BindUtil.bindSchemaToObject(cls, cmd, template);
|
|
156
150
|
return BindUtil.coerceMethodParams(cls, 'main', bound);
|
|
157
151
|
}
|
|
@@ -160,9 +154,7 @@ export class CliCommandSchemaUtil {
|
|
|
160
154
|
* Validate command shape with the given arguments
|
|
161
155
|
*/
|
|
162
156
|
static async validate(cmd: CliCommandShape, args: unknown[]): Promise<typeof cmd> {
|
|
163
|
-
|
|
164
|
-
const cls = cmd.constructor as Class<CliCommandShape>;
|
|
165
|
-
|
|
157
|
+
const cls = CliCommandRegistry.getClass(cmd);
|
|
166
158
|
const paramNames = SchemaRegistry.getMethodSchema(cls, 'main').map(x => x.name);
|
|
167
159
|
|
|
168
160
|
const validators = [
|
package/src/scm.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
|
-
import { ExecUtil,
|
|
6
|
-
import {
|
|
5
|
+
import { AppError, ExecUtil, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
6
|
+
import type { IndexedModule } from '@travetto/manifest';
|
|
7
7
|
|
|
8
8
|
export class CliScmUtil {
|
|
9
9
|
/**
|
|
@@ -35,7 +35,7 @@ export class CliScmUtil {
|
|
|
35
35
|
* @returns
|
|
36
36
|
*/
|
|
37
37
|
static async findLastRelease(): Promise<string | undefined> {
|
|
38
|
-
const result = await ExecUtil.getResult(spawn('git', ['log', '--pretty=oneline'], { cwd:
|
|
38
|
+
const result = await ExecUtil.getResult(spawn('git', ['log', '--pretty=oneline'], { cwd: Runtime.workspace.path }));
|
|
39
39
|
return result.stdout
|
|
40
40
|
.split(/\n/)
|
|
41
41
|
.find(x => /Publish /.test(x))?.split(/\s+/)?.[0];
|
|
@@ -46,12 +46,11 @@ export class CliScmUtil {
|
|
|
46
46
|
* @param fromHash
|
|
47
47
|
* @returns
|
|
48
48
|
*/
|
|
49
|
-
static async findChangedFiles(fromHash: string, toHash: string = 'HEAD'
|
|
50
|
-
const ws =
|
|
49
|
+
static async findChangedFiles(fromHash: string, toHash: string = 'HEAD'): Promise<string[]> {
|
|
50
|
+
const ws = Runtime.workspace.path;
|
|
51
51
|
const res = await ExecUtil.getResult(spawn('git', ['diff', '--name-only', `${fromHash}..${toHash}`, ':!**/DOC.*', ':!**/README.*'], { cwd: ws }), { catch: true });
|
|
52
|
-
if (!res.valid
|
|
53
|
-
|
|
54
|
-
return [];
|
|
52
|
+
if (!res.valid) {
|
|
53
|
+
throw new AppError('Unable to detect changes between', 'data', { fromHash, toHash, output: (res.stderr || res.stdout) });
|
|
55
54
|
}
|
|
56
55
|
const out = new Set<string>();
|
|
57
56
|
for (const line of res.stdout.split(/\n/g)) {
|
package/src/trv.d.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
type OrProm<T> = T | Promise<T>;
|
|
4
4
|
|
|
@@ -19,7 +19,7 @@ export type CliCommandConfig = {
|
|
|
19
19
|
name: string;
|
|
20
20
|
commandModule: string;
|
|
21
21
|
runTarget?: boolean;
|
|
22
|
-
cls:
|
|
22
|
+
cls: Class<CliCommandShape>;
|
|
23
23
|
hidden?: boolean;
|
|
24
24
|
preMain?: (cmd: CliCommandShape) => void | Promise<void>;
|
|
25
25
|
};
|
|
@@ -49,6 +49,7 @@ export type CliValidationError = {
|
|
|
49
49
|
* CLI Command Contract
|
|
50
50
|
*/
|
|
51
51
|
export interface CliCommandShape<T extends unknown[] = unknown[]> {
|
|
52
|
+
|
|
52
53
|
/**
|
|
53
54
|
* Parsed state
|
|
54
55
|
*/
|
|
@@ -116,7 +117,7 @@ export type CliCommandShapeFields = {
|
|
|
116
117
|
/**
|
|
117
118
|
* CLI Command argument/flag shape
|
|
118
119
|
*/
|
|
119
|
-
export type CliCommandInput = {
|
|
120
|
+
export type CliCommandInput<K extends string = string> = {
|
|
120
121
|
name: string;
|
|
121
122
|
description?: string;
|
|
122
123
|
type: 'string' | 'file' | 'number' | 'boolean' | 'date' | 'regex' | 'module';
|
|
@@ -125,19 +126,19 @@ export type CliCommandInput = {
|
|
|
125
126
|
required?: boolean;
|
|
126
127
|
array?: boolean;
|
|
127
128
|
default?: unknown;
|
|
128
|
-
flagNames?:
|
|
129
|
+
flagNames?: K[];
|
|
129
130
|
envVars?: string[];
|
|
130
131
|
};
|
|
131
132
|
|
|
132
133
|
/**
|
|
133
134
|
* CLI Command schema shape
|
|
134
135
|
*/
|
|
135
|
-
export
|
|
136
|
+
export interface CliCommandSchema<K extends string = string> {
|
|
136
137
|
name: string;
|
|
137
138
|
title: string;
|
|
138
139
|
commandModule: string;
|
|
139
140
|
runTarget?: boolean;
|
|
140
141
|
description?: string;
|
|
141
142
|
args: CliCommandInput[];
|
|
142
|
-
flags: CliCommandInput[];
|
|
143
|
-
}
|
|
143
|
+
flags: CliCommandInput<K>[];
|
|
144
|
+
}
|
package/src/util.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
|
|
3
|
-
import { Env, ExecUtil, ShutdownManager,
|
|
3
|
+
import { Env, ExecUtil, ShutdownManager, Runtime } from '@travetto/runtime';
|
|
4
4
|
|
|
5
5
|
import { CliCommandShape, CliCommandShapeFields, RunResponse } from './types';
|
|
6
6
|
|
|
@@ -14,10 +14,10 @@ export class CliUtil {
|
|
|
14
14
|
* @returns
|
|
15
15
|
*/
|
|
16
16
|
static getSimpleModuleName(placeholder: string, module?: string): string {
|
|
17
|
-
const simple = (module ??
|
|
17
|
+
const simple = (module ?? Runtime.main.name).replace(/[\/]/, '_').replace(/@/, '');
|
|
18
18
|
if (!simple) {
|
|
19
19
|
return placeholder;
|
|
20
|
-
} else if (!module &&
|
|
20
|
+
} else if (!module && Runtime.monoRoot) {
|
|
21
21
|
return placeholder;
|
|
22
22
|
} else {
|
|
23
23
|
return placeholder.replace('<module>', simple);
|
|
@@ -31,7 +31,7 @@ export class CliUtil {
|
|
|
31
31
|
if (ipc && process.connected) {
|
|
32
32
|
process.once('disconnect', () => process.exit());
|
|
33
33
|
}
|
|
34
|
-
if (Env.TRV_CAN_RESTART.isFalse || !(cmd.canRestart ?? !
|
|
34
|
+
if (Env.TRV_CAN_RESTART.isFalse || !(cmd.canRestart ?? !Runtime.production)) {
|
|
35
35
|
Env.TRV_CAN_RESTART.clear();
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
@@ -64,7 +64,7 @@ export class CliUtil {
|
|
|
64
64
|
data: {
|
|
65
65
|
name: cmd._cfg!.name, env,
|
|
66
66
|
commandModule: cmd._cfg!.commandModule,
|
|
67
|
-
module:
|
|
67
|
+
module: Runtime.main.name,
|
|
68
68
|
args: process.argv.slice(3),
|
|
69
69
|
}
|
|
70
70
|
};
|
|
@@ -79,7 +79,7 @@ export class CliUtil {
|
|
|
79
79
|
* Debug if IPC available
|
|
80
80
|
*/
|
|
81
81
|
static async debugIfIpc<T extends CliCommandShapeFields & CliCommandShape>(cmd: T): Promise<boolean> {
|
|
82
|
-
return (cmd.debugIpc ?? !
|
|
82
|
+
return (cmd.debugIpc ?? !Runtime.production) && this.triggerIpc('run', cmd);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
package/support/cli.main.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { RuntimeContext } from '@travetto/base';
|
|
1
|
+
import { Runtime } from '@travetto/runtime';
|
|
5
2
|
import { CliCommandShape, CliCommand, CliValidationError, ParsedState } from '@travetto/cli';
|
|
6
|
-
import { RuntimeIndex } from '@travetto/manifest';
|
|
7
3
|
import { Ignore } from '@travetto/schema';
|
|
8
4
|
|
|
9
5
|
/**
|
|
@@ -15,19 +11,10 @@ export class MainCommand implements CliCommandShape {
|
|
|
15
11
|
@Ignore()
|
|
16
12
|
_parsed: ParsedState;
|
|
17
13
|
|
|
18
|
-
async #getImport(fileOrImport: string): Promise<string | undefined> {
|
|
19
|
-
// If referenced file exists
|
|
20
|
-
let file = fileOrImport;
|
|
21
|
-
if (await (fs.stat(path.resolve(fileOrImport)).then(() => true, () => false))) {
|
|
22
|
-
file = path.join(RuntimeContext.main.name, fileOrImport);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return RuntimeIndex.getFromImport(file)?.import;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
14
|
async validate(fileOrImport: string): Promise<CliValidationError | undefined> {
|
|
29
|
-
|
|
30
|
-
|
|
15
|
+
try {
|
|
16
|
+
await Runtime.importFrom(fileOrImport);
|
|
17
|
+
} catch {
|
|
31
18
|
return { message: `Unknown file: ${fileOrImport}` };
|
|
32
19
|
}
|
|
33
20
|
}
|
|
@@ -35,8 +22,7 @@ export class MainCommand implements CliCommandShape {
|
|
|
35
22
|
async main(fileOrImport: string, args: string[] = []): Promise<void> {
|
|
36
23
|
let res: unknown;
|
|
37
24
|
try {
|
|
38
|
-
const
|
|
39
|
-
const mod = await import(imp!);
|
|
25
|
+
const mod = await Runtime.importFrom<{ main(..._: unknown[]): Promise<unknown> }>(fileOrImport);
|
|
40
26
|
res = await mod.main(...args, ...this._parsed.unknown);
|
|
41
27
|
} catch (err) {
|
|
42
28
|
res = err;
|
|
@@ -20,16 +20,16 @@ export class CliCommandTransformer {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// Find runnable method
|
|
23
|
-
const
|
|
23
|
+
const mainMethod = node.members
|
|
24
24
|
.find((x): x is ts.MethodDeclaration =>
|
|
25
25
|
ts.isMethodDeclaration(x) && x.name!.getText() === 'main'
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
-
if (!
|
|
28
|
+
if (!mainMethod) {
|
|
29
29
|
return node;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const members = node.members.map(x => ts.isMethodDeclaration(x) && x ===
|
|
32
|
+
const members = node.members.map(x => ts.isMethodDeclaration(x) && x === mainMethod ?
|
|
33
33
|
state.factory.updateMethodDeclaration(
|
|
34
34
|
x,
|
|
35
35
|
x.modifiers,
|