@travetto/cli 5.0.0-rc.2 → 5.0.0-rc.4

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 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#L14) decorator
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#L14) 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#L14) 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.
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,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#L14) supports the following data types for flags:
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
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
@@ -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#L14) decorator. This means the target will be visible within the editor tooling.
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
@@ -461,7 +461,7 @@ If the goal is to run a more complex application, which may include depending on
461
461
  ```typescript
462
462
  import { Runtime } from '@travetto/runtime';
463
463
  import { DependencyRegistry } from '@travetto/di';
464
- import { CliCommand, CliCommandShape, CliUtil } from '@travetto/cli';
464
+ import { CliCommand, CliCommandShape } from '@travetto/cli';
465
465
 
466
466
  import { ServerHandle } from '../src/types';
467
467
  import { RestNetUtil } from '../src/util/net';
@@ -469,15 +469,9 @@ import { RestNetUtil } from '../src/util/net';
469
469
  /**
470
470
  * Run a rest server as an application
471
471
  */
472
- @CliCommand({ runTarget: true, addModule: true, addEnv: true })
472
+ @CliCommand({ runTarget: true, with: { debugIpc: true, canRestart: true, module: true, env: true } })
473
473
  export class RunRestCommand implements CliCommandShape {
474
474
 
475
- /** IPC debug is enabled */
476
- debugIpc?: boolean;
477
-
478
- /** Should the server be able to run with restart*/
479
- canRestart?: boolean;
480
-
481
475
  /** Port to run on */
482
476
  port?: number;
483
477
 
@@ -491,9 +485,6 @@ export class RunRestCommand implements CliCommandShape {
491
485
  }
492
486
 
493
487
  async main(): Promise<ServerHandle | void> {
494
- if (await CliUtil.debugIfIpc(this) || await CliUtil.runWithRestart(this)) {
495
- return;
496
- }
497
488
  const { RestApplication } = await import('../src/application/rest');
498
489
  try {
499
490
  return await DependencyRegistry.runInstance(RestApplication);
@@ -508,7 +499,7 @@ export class RunRestCommand implements CliCommandShape {
508
499
  }
509
500
  ```
510
501
 
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/runtime/src/env.ts#L109) `name` defined in the [Base](https://github.com/travetto/travetto/tree/main/module/runtime#readme "Environment config and common utilities for travetto applications.") module.
502
+ 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#L108) `name` defined in the [Base](https://github.com/travetto/travetto/tree/main/module/runtime#readme "Environment config and common utilities for travetto applications.") module.
512
503
 
513
504
  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
505
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/cli",
3
- "version": "5.0.0-rc.2",
3
+ "version": "5.0.0-rc.4",
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.2",
33
- "@travetto/terminal": "^5.0.0-rc.2"
32
+ "@travetto/schema": "^5.0.0-rc.4",
33
+ "@travetto/terminal": "^5.0.0-rc.4"
34
34
  },
35
35
  "travetto": {
36
36
  "displayName": "Command Line Interface",
package/src/decorators.ts CHANGED
@@ -1,10 +1,81 @@
1
1
  import { Class, ClassInstance, Env, Runtime, RuntimeIndex, describeFunction } from '@travetto/runtime';
2
- import { SchemaRegistry } from '@travetto/schema';
2
+ import { FieldConfig, SchemaRegistry } from '@travetto/schema';
3
3
 
4
4
  import { CliCommandShape, CliCommandShapeFields } from './types';
5
- import { CliCommandRegistry, CliCommandConfigOptions } from './registry';
5
+ import { CliCommandRegistry } from './registry';
6
6
  import { CliModuleUtil } from './module';
7
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.envName),
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
+
8
79
 
9
80
  /**
10
81
  * Decorator to register a CLI command
@@ -17,37 +88,25 @@ export function CliCommand(cfg: CliCommandConfigOptions = {}) {
17
88
  return;
18
89
  }
19
90
 
20
- const addModule = cfg.addModule === true || cfg.fields?.includes('module');
21
- const runtimeModule = cfg.runtimeModule ?? (cfg.addModule ? 'current' : undefined);
22
- const addEnv = cfg.addEnv ?? cfg.fields?.includes('env');
91
+ const VALID_FIELDS = FIELD_CONFIG.filter(f => !!cfg.with?.[f.name]);
92
+
23
93
  const { commandModule } = CliCommandRegistry.registerClass(target, {
24
94
  hidden: cfg.hidden,
25
95
  runTarget: cfg.runTarget,
26
- preMain: async (cmd: CliCommandShape & { env?: string }) => {
27
- if (addEnv) {
28
- Env.TRV_ENV.set(cmd.env || Runtime.name);
96
+ preMain: async (cmd: Cmd) => {
97
+ for (const field of VALID_FIELDS) {
98
+ await field.run(cmd);
29
99
  }
30
100
  }
31
101
  });
32
102
 
33
103
  const pendingCls = SchemaRegistry.getOrCreatePending(target);
34
104
 
35
- if (addEnv) {
36
- SchemaRegistry.registerPendingFieldConfig(target, 'env', String, {
37
- aliases: ['e', CliParseUtil.toEnvField(Env.TRV_ENV.key)],
38
- description: 'Application environment',
39
- required: { active: false }
40
- });
105
+ for (const { name, field: { type, ...field } } of VALID_FIELDS) {
106
+ SchemaRegistry.registerPendingFieldConfig(target, name, type!, field);
41
107
  }
42
108
 
43
- if (addModule) {
44
- SchemaRegistry.registerPendingFieldConfig(target, 'module', String, {
45
- aliases: ['m', CliParseUtil.toEnvField(Env.TRV_MODULE.key)],
46
- description: 'Module to run for',
47
- specifiers: ['module'],
48
- required: { active: Runtime.monoRoot }
49
- });
50
- }
109
+ const runtimeModule = cfg.runtimeModule ?? (cfg.with?.module ? 'current' : undefined);
51
110
 
52
111
  if (runtimeModule) { // Validate module
53
112
  (pendingCls.validators ??= []).push(async item => {
package/src/help.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import util from 'node:util';
2
2
 
3
- import { Primitive } from '@travetto/schema';
3
+ import { Primitive } from '@travetto/runtime';
4
4
 
5
5
  import { cliTpl } from './color';
6
6
  import { CliCommandShape } from './types';
package/src/registry.ts CHANGED
@@ -1,19 +1,9 @@
1
- import { Class, ConcreteClass, Runtime, RuntimeIndex } from '@travetto/runtime';
1
+ import { Class, ConcreteClass, describeFunction, Runtime, RuntimeIndex } from '@travetto/runtime';
2
2
 
3
3
  import { CliCommandConfig, CliCommandShape } from './types';
4
4
  import { CliUnknownCommandError } from './error';
5
5
 
6
- export type CliCommandConfigOptions = {
7
- hidden?: boolean;
8
- runTarget?: boolean;
9
- addModule?: boolean;
10
- addEnv?: boolean;
11
- runtimeModule?: 'current' | 'command';
12
- /** @deprecated */
13
- fields?: ('module' | 'env')[];
14
- };
15
-
16
- const CLI_FILE_REGEX = /\/cli[.](?<name>.*)[.]tsx?$/;
6
+ const CLI_FILE_REGEX = /\/cli[.](?<name>.*?)([.]tsx?)?$/;
17
7
  const getName = (s: string): string => (s.match(CLI_FILE_REGEX)?.groups?.name ?? s).replaceAll('_', ':');
18
8
 
19
9
  class $CliCommandRegistry {
@@ -51,12 +41,12 @@ class $CliCommandRegistry {
51
41
  * Registers a cli command
52
42
  */
53
43
  registerClass(cls: Class, cfg: Partial<CliCommandConfig>): CliCommandConfig {
54
- const source = Runtime.getSource(cls);
44
+ const meta = describeFunction(cls);
55
45
  this.#commands.set(cls, {
56
46
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
57
47
  cls: cls as ConcreteClass,
58
- name: getName(source),
59
- commandModule: RuntimeIndex.getModuleFromSource(source)!.name,
48
+ name: getName(meta.import),
49
+ commandModule: meta.module,
60
50
  ...cfg,
61
51
  });
62
52
  return this.#commands.get(cls)!;
@@ -20,16 +20,16 @@ export class CliCommandTransformer {
20
20
  }
21
21
 
22
22
  // Find runnable method
23
- const runMethod = node.members
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 (!runMethod) {
28
+ if (!mainMethod) {
29
29
  return node;
30
30
  }
31
31
 
32
- const members = node.members.map(x => ts.isMethodDeclaration(x) && x === runMethod ?
32
+ const members = node.members.map(x => ts.isMethodDeclaration(x) && x === mainMethod ?
33
33
  state.factory.updateMethodDeclaration(
34
34
  x,
35
35
  x.modifiers,