@travetto/cli 4.0.0-rc.0 → 4.0.0-rc.1

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#L16) decorator
62
+ * The class must use the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) decorator
63
63
 
64
64
  **Code: Basic Command**
65
65
  ```typescript
@@ -93,7 +93,7 @@ Examples of mappings:
93
93
  The pattern is that underscores(_) translate to colons (:), and the `cli.` prefix, and `.ts` suffix are dropped.
94
94
 
95
95
  ## Binding Flags
96
- [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L16) 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#L16) 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#L15) is a wrapper for [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L14), and so every class that uses the [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) decorator is now a full [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L14) class. The fields of the class represent the flags that are available to the command.
97
97
 
98
98
  **Code: Basic Command with Flag**
99
99
  ```typescript
@@ -130,7 +130,7 @@ $ trv basic:flag --loud
130
130
  HELLO
131
131
  ```
132
132
 
133
- The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L16) supports the following data types for flags:
133
+ The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L15) supports the following data types for flags:
134
134
  * Boolean values
135
135
  * Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L172), [@Float](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L178), [@Precision](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L166), [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L107) and [@Max](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L117) decorators help provide additional validation.
136
136
  * String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L107), [@MaxLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L117), [@Match](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L99) and [@Enum](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L78) provide additional constraints
@@ -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#L16) 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#L15) decorator. This means the target will be visible within the editor tooling.
392
392
 
393
393
  **Code: Simple Run Target**
394
394
  ```typescript
@@ -496,12 +496,12 @@ export class RunRestCommand implements CliCommandShape {
496
496
  }
497
497
  ```
498
498
 
499
- 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/base/src/env.ts#L104) `name` defined in the [Base](https://github.com/travetto/travetto/tree/main/module/base#readme "Environment config and common utilities for travetto applications.") module.
499
+ 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/base/src/env.ts#L105) `name` defined in the [Base](https://github.com/travetto/travetto/tree/main/module/base#readme "Environment config and common utilities for travetto applications.") module.
500
500
 
501
501
  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.
502
502
 
503
503
  ### Custom Validation
504
- 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#L36) errors that are returned will be shared with the user, and fail to invoke the `main` method.
504
+ 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#L37) errors that are returned will be shared with the user, and fail to invoke the `main` method.
505
505
 
506
506
  **Code: CliValidationError**
507
507
  ```typescript
package/bin/trv.js CHANGED
@@ -1,7 +1,6 @@
1
- #!/usr/bin/env -S node --disable-proto=delete --enable-source-maps
2
- process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS ?? ''} --enable-source-maps --disable-proto=delete`;
1
+ #!/usr/bin/env node
3
2
 
4
3
  // @ts-check
5
4
  import { getEntry } from '@travetto/compiler/bin/common.js';
6
5
 
7
- getEntry().then(ops => ops.compile('run')).then(load => load('@travetto/cli/support/entry.trv.js'));
6
+ getEntry().then(ops => ops.getLoader()).then(load => load('@travetto/cli/support/entry.trv.js'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/cli",
3
- "version": "4.0.0-rc.0",
3
+ "version": "4.0.0-rc.1",
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": "^4.0.0-rc.0",
33
- "@travetto/terminal": "^4.0.0-rc.0"
32
+ "@travetto/schema": "^4.0.0-rc.1",
33
+ "@travetto/terminal": "^4.0.0-rc.1"
34
34
  },
35
35
  "travetto": {
36
36
  "displayName": "Command Line Interface"
package/src/decorators.ts CHANGED
@@ -5,7 +5,6 @@ import { SchemaRegistry } from '@travetto/schema';
5
5
  import { CliCommandShape, CliCommandShapeFields } from './types';
6
6
  import { CliCommandRegistry, CliCommandConfigOptions } from './registry';
7
7
  import { CliModuleUtil } from './module';
8
- import { CliUtil } from './util';
9
8
  import { CliParseUtil } from './parse';
10
9
 
11
10
  /**
@@ -48,7 +47,7 @@ export function CliCommand(cfg: CliCommandConfigOptions = {}) {
48
47
  aliases: ['m', CliParseUtil.toEnvField(Env.TRV_MODULE.key)],
49
48
  description: 'Module to run for',
50
49
  specifiers: ['module'],
51
- required: { active: CliUtil.monoRoot }
50
+ required: { active: RuntimeContext.monoRoot }
52
51
  });
53
52
  }
54
53
 
package/src/module.ts CHANGED
@@ -20,14 +20,14 @@ export class CliModuleUtil {
20
20
  fromHash ??= await CliScmUtil.findLastRelease();
21
21
 
22
22
  if (!fromHash) {
23
- return RuntimeIndex.getLocalModules();
23
+ return RuntimeIndex.getWorkspaceModules();
24
24
  }
25
25
 
26
26
  const out = new Map<string, IndexedModule>();
27
27
  for (const mod of await CliScmUtil.findChangedModules(fromHash, toHash)) {
28
28
  out.set(mod.name, mod);
29
29
  if (transitive) {
30
- for (const sub of await RuntimeIndex.getDependentModules(mod)) {
30
+ for (const sub of await RuntimeIndex.getDependentModules(mod, 'parents')) {
31
31
  out.set(sub.name, sub);
32
32
  }
33
33
  }
package/src/scm.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { spawn } from 'node:child_process';
1
2
  import fs from 'node:fs/promises';
2
3
 
3
4
  import { ExecUtil } from '@travetto/base';
@@ -19,8 +20,8 @@ export class CliScmUtil {
19
20
  */
20
21
  static async getAuthor(): Promise<{ name?: string, email: string }> {
21
22
  const [name, email] = await Promise.all([
22
- await ExecUtil.spawn('git', ['config', 'user.name'], { catchAsResult: true }).result,
23
- await ExecUtil.spawn('git', ['config', 'user.email']).result,
23
+ ExecUtil.getResult(spawn('git', ['config', 'user.name']), { catch: true }),
24
+ ExecUtil.getResult(spawn('git', ['config', 'user.email'])),
24
25
  ]);
25
26
  return {
26
27
  name: (name.valid ? name.stdout.trim() : '') || process.env.USER,
@@ -33,8 +34,8 @@ export class CliScmUtil {
33
34
  * @returns
34
35
  */
35
36
  static async findLastRelease(): Promise<string | undefined> {
36
- const { result } = ExecUtil.spawn('git', ['log', '--pretty=oneline'], { cwd: RuntimeContext.workspace.path });
37
- return (await result).stdout
37
+ const result = await ExecUtil.getResult(spawn('git', ['log', '--pretty=oneline'], { cwd: RuntimeContext.workspace.path }));
38
+ return result.stdout
38
39
  .split(/\n/)
39
40
  .find(x => /Publish /.test(x))?.split(/\s+/)?.[0];
40
41
  }
@@ -46,7 +47,7 @@ export class CliScmUtil {
46
47
  */
47
48
  static async findChangedFiles(fromHash: string, toHash: string = 'HEAD'): Promise<string[]> {
48
49
  const ws = RuntimeContext.workspace.path;
49
- const res = await ExecUtil.spawn('git', ['diff', '--name-only', `${fromHash}..${toHash}`, ':!**/DOC.*', ':!**/README.*'], { cwd: ws }).result;
50
+ const res = await ExecUtil.getResult(spawn('git', ['diff', '--name-only', `${fromHash}..${toHash}`, ':!**/DOC.*', ':!**/README.*'], { cwd: ws }));
50
51
  const out = new Set<string>();
51
52
  for (const line of res.stdout.split(/\n/g)) {
52
53
  const entry = RuntimeIndex.getEntry(path.resolve(ws, line));
@@ -79,15 +80,15 @@ export class CliScmUtil {
79
80
  * Create a commit
80
81
  */
81
82
  static createCommit(message: string): Promise<string> {
82
- return ExecUtil.spawn('git', ['commit', '.', '-m', message]).result.then(r => r.stdout);
83
+ return ExecUtil.getResult(spawn('git', ['commit', '.', '-m', message])).then(r => r.stdout);
83
84
  }
84
85
 
85
86
  /**
86
87
  * Verify if workspace is dirty
87
88
  */
88
89
  static async isWorkspaceDirty(): Promise<boolean> {
89
- const res1 = await ExecUtil.spawn('git', ['diff', '--quiet', '--exit-code'], { catchAsResult: true }).result;
90
- const res2 = await ExecUtil.spawn('git', ['diff', '--quiet', '--exit-code', '--cached'], { catchAsResult: true }).result;
90
+ const res1 = await ExecUtil.getResult(spawn('git', ['diff', '--quiet', '--exit-code']), { catch: true });
91
+ const res2 = await ExecUtil.getResult(spawn('git', ['diff', '--quiet', '--exit-code', '--cached']), { catch: true });
91
92
  return !res1.valid || !res2.valid;
92
93
  }
93
94
  }
package/src/util.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import { spawn } from 'node:child_process';
2
+
1
3
  import { Env, ExecUtil, ShutdownManager } from '@travetto/base';
2
- import { RuntimeIndex, RuntimeContext } from '@travetto/manifest';
4
+ import { RuntimeContext } from '@travetto/manifest';
3
5
 
4
6
  import { CliCommandShape, CliCommandShapeFields, RunResponse } from './types';
5
7
 
@@ -8,13 +10,6 @@ const IPC_INVALID_ENV = new Set(['PS1', 'INIT_CWD', 'COLOR', 'LANGUAGE', 'PROFIL
8
10
  const validEnv = (k: string): boolean => IPC_ALLOWED_ENV.has(k) || (!IPC_INVALID_ENV.has(k) && !/^(npm_|GTK|GDK|TRV|NODE|GIT|TERM_)/.test(k) && !/VSCODE/.test(k));
9
11
 
10
12
  export class CliUtil {
11
- /**
12
- * Are we running from a mono-root?
13
- */
14
- static get monoRoot(): boolean {
15
- return !!RuntimeContext.workspace.mono && RuntimeIndex.mainModule.sourcePath === RuntimeContext.workspace.path;
16
- }
17
-
18
13
  /**
19
14
  * Get a simplified version of a module name
20
15
  * @returns
@@ -23,7 +18,7 @@ export class CliUtil {
23
18
  const simple = (module ?? RuntimeContext.main.name).replace(/[\/]/, '_').replace(/@/, '');
24
19
  if (!simple) {
25
20
  return placeholder;
26
- } else if (!module && this.monoRoot) {
21
+ } else if (!module && RuntimeContext.monoRoot) {
27
22
  return placeholder;
28
23
  } else {
29
24
  return placeholder.replace('<module>', simple);
@@ -38,13 +33,13 @@ export class CliUtil {
38
33
  Env.TRV_CAN_RESTART.clear();
39
34
  return;
40
35
  }
41
- return ExecUtil.spawnWithRestart(process.argv0, process.argv.slice(1), {
36
+ return ExecUtil.withRestart(() => spawn(process.argv0, process.argv.slice(1), {
42
37
  env: {
43
- ...Env.TRV_DYNAMIC.export(true),
38
+ ...process.env,
44
39
  ...Env.TRV_CAN_RESTART.export(false)
45
40
  },
46
41
  stdio: [0, 1, 2, 'ipc']
47
- });
42
+ }));
48
43
  }
49
44
 
50
45
  /**
@@ -1,6 +1,5 @@
1
1
  import fs from 'node:fs/promises';
2
2
 
3
- import { ExecUtil } from '@travetto/base';
4
3
  import { CliCommandShape, CliCommand, CliValidationError, ParsedState } from '@travetto/cli';
5
4
  import { path, RuntimeIndex, RuntimeContext } from '@travetto/manifest';
6
5
  import { Ignore } from '@travetto/schema';
@@ -32,13 +31,20 @@ export class MainCommand implements CliCommandShape {
32
31
  }
33
32
 
34
33
  async main(fileOrImport: string, args: string[] = []): Promise<void> {
34
+ let res: unknown;
35
35
  try {
36
36
  const imp = await this.#getImport(fileOrImport);
37
37
  const mod = await import(imp!);
38
-
39
- ExecUtil.sendResponse(await mod.main(...args, ...this._parsed.unknown));
38
+ res = await mod.main(...args, ...this._parsed.unknown);
40
39
  } catch (err) {
41
- ExecUtil.sendResponse(err, true);
40
+ res = err;
41
+ process.exitCode = Math.max(process.exitCode ?? 1, 1);
42
+ }
43
+
44
+ if (res !== undefined) {
45
+ if (process.connected) { process.send?.(res); }
46
+ const payload = typeof res === 'string' ? res : (res instanceof Error ? res.stack : JSON.stringify(res));
47
+ process[process.exitCode ? 'stderr' : 'stdout'].write(`${payload}\n`);
42
48
  }
43
49
  }
44
50
  }