@travetto/cli 6.0.1 → 7.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,32 @@
1
1
  import { Env } from '@travetto/runtime';
2
+ import { IsPrivate } from '@travetto/schema';
2
3
 
3
- import { CliCommand } from '../src/decorators.ts';
4
- import { CliCommandSchema, CliCommandShape, CliValidationError } from '../src/types.ts';
5
- import { CliCommandRegistry } from '../src/registry.ts';
6
- import { CliCommandSchemaUtil } from '../src/schema.ts';
4
+ import { CliCommand } from '../src/registry/decorator.ts';
5
+ import { CliCommandShape, CliValidationError } from '../src/types.ts';
6
+ import { CliCommandRegistryIndex } from '../src/registry/registry-index.ts';
7
7
  import { CliUtil } from '../src/util.ts';
8
+ import { CliSchemaExportUtil } from '../src/schema-export.ts';
9
+
8
10
 
9
11
  /**
10
12
  * Generates the schema for all CLI operations
11
13
  */
12
- @CliCommand({ hidden: true })
14
+ @CliCommand()
15
+ @IsPrivate()
13
16
  export class CliSchemaCommand implements CliCommandShape {
14
17
 
15
- async #getSchema(name: string): Promise<CliCommandSchema> {
16
- const inst = await CliCommandRegistry.getInstance(name);
17
- return CliCommandSchemaUtil.getSchema(inst!);
18
- }
18
+ async validate(names?: string[]): Promise<CliValidationError | undefined> {
19
+ if (!names || names.length === 0) {
20
+ return;
21
+ }
22
+ const resolved = await CliCommandRegistryIndex.load(names);
23
+ const invalid = names.find(x => !resolved.find(r => r.command === x));
19
24
 
20
- async validate(names: string[]): Promise<CliValidationError | undefined> {
21
- for (const name of names ?? []) {
22
- if (name && !CliCommandRegistry.getCommandMapping().has(name)) {
23
- return {
24
- source: 'arg',
25
- message: `name: ${name} is not a valid cli command`
26
- };
27
- }
25
+ if (invalid) {
26
+ return {
27
+ source: 'arg',
28
+ message: `name: ${invalid} is not a valid cli command`
29
+ };
28
30
  }
29
31
  }
30
32
 
@@ -33,10 +35,11 @@ export class CliSchemaCommand implements CliCommandShape {
33
35
  }
34
36
 
35
37
  async main(names?: string[]): Promise<void> {
36
- if (!names?.length) {
37
- names = [...CliCommandRegistry.getCommandMapping().keys()];
38
- }
39
- const resolved = await Promise.all(names.map(x => this.#getSchema(x)));
40
- await CliUtil.writeAndEnsureComplete(resolved);
38
+ const resolved = await CliCommandRegistryIndex.load(names);
39
+
40
+ const output = resolved
41
+ .map(x => CliSchemaExportUtil.exportSchema(x.config.cls));
42
+
43
+ await CliUtil.writeAndEnsureComplete(output);
41
44
  }
42
45
  }
@@ -1,11 +1,12 @@
1
1
  import { Runtime } from '@travetto/runtime';
2
2
  import { CliCommandShape, CliCommand, CliValidationError, ParsedState } from '@travetto/cli';
3
- import { Ignore } from '@travetto/schema';
3
+ import { Ignore, IsPrivate } from '@travetto/schema';
4
4
 
5
5
  /**
6
6
  * Allows for running of main entry points
7
7
  */
8
- @CliCommand({ hidden: true })
8
+ @CliCommand()
9
+ @IsPrivate()
9
10
  export class MainCommand implements CliCommandShape {
10
11
 
11
12
  @Ignore()
package/src/decorators.ts DELETED
@@ -1,154 +0,0 @@
1
- import { Class, ClassInstance, Env, Runtime, RuntimeIndex, describeFunction } from '@travetto/runtime';
2
- import { FieldConfig, SchemaRegistry } from '@travetto/schema';
3
-
4
- import { CliCommandShape, CliCommandShapeFields } from './types.ts';
5
- import { CliCommandRegistry } from './registry.ts';
6
- import { CliModuleUtil } from './module.ts';
7
- import { CliParseUtil } from './parse.ts';
8
- import { CliUtil } from './util.ts';
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
-
79
- /**
80
- * Decorator to register a CLI command
81
- * @augments `@travetto/schema:Schema`
82
- * @augments `@travetto/cli:CliCommand`
83
- * @schemaMethods `main`
84
- */
85
- export function CliCommand(cfg: CliCommandConfigOptions = {}) {
86
- return function <T extends CliCommandShape>(target: Class<T>): void {
87
- if (!target.Ⲑid || describeFunction(target)?.abstract) {
88
- return;
89
- }
90
-
91
- const VALID_FIELDS = FIELD_CONFIG.filter(f => !!cfg.with?.[f.name]);
92
-
93
- const { commandModule } = CliCommandRegistry.registerClass(target, {
94
- hidden: cfg.hidden,
95
- runTarget: cfg.runTarget,
96
- preMain: async (cmd: Cmd) => {
97
- for (const field of VALID_FIELDS) {
98
- await field.run(cmd);
99
- }
100
- }
101
- });
102
-
103
- const pendingCls = SchemaRegistry.getOrCreatePending(target);
104
-
105
- for (const { name, field: { type, ...field } } of VALID_FIELDS) {
106
- SchemaRegistry.registerPendingFieldConfig(target, name, type!, field);
107
- }
108
-
109
- const runtimeModule = cfg.runtimeModule ?? (cfg.with?.module ? 'current' : undefined);
110
-
111
- if (runtimeModule) { // Validate module
112
- (pendingCls.validators ??= []).push(async ({ module: mod }: Partial<CliCommandShapeFields>) => {
113
- const runModule = (runtimeModule === 'command' ? commandModule : mod) || Runtime.main.name;
114
-
115
- // If we need to run as a specific module
116
- if (runModule !== Runtime.main.name) {
117
- try {
118
- RuntimeIndex.reinitForModule(runModule);
119
- } catch {
120
- return { source: 'flag', message: `${runModule} is an unknown module`, kind: 'custom', path: '.' };
121
- }
122
- }
123
-
124
- if (!(await CliModuleUtil.moduleHasDependency(runModule, commandModule))) {
125
- return { source: 'flag', message: `${runModule} does not have ${commandModule} as a dependency`, kind: 'custom', path: '.' };
126
- }
127
- });
128
- }
129
- };
130
- }
131
-
132
- /**
133
- * Decorator to register a CLI command flag
134
- */
135
- export function CliFlag(cfg: { name?: string, short?: string, desc?: string, fileExtensions?: string[], envVars?: string[] }) {
136
- return function (target: ClassInstance, prop: string | symbol): void {
137
- const aliases: string[] = [];
138
- if (cfg.name) {
139
- aliases.push(cfg.name.startsWith('-') ? cfg.name : `--${cfg.name}`);
140
- }
141
- if (cfg.short) {
142
- aliases.push(cfg.short.startsWith('-') ? cfg.short : `-${cfg.short}`);
143
- }
144
- if (cfg.envVars) {
145
- aliases.push(...cfg.envVars.map(CliParseUtil.toEnvField));
146
- }
147
- if (typeof prop === 'string') {
148
- SchemaRegistry.registerPendingFieldFacet(target.constructor, prop, {
149
- aliases, description: cfg.desc,
150
- specifiers: cfg.fileExtensions?.length ? ['file', ...cfg.fileExtensions.map(x => `ext:${x.replace(/[*.]/g, '')}`)] : undefined
151
- });
152
- }
153
- };
154
- }
package/src/registry.ts DELETED
@@ -1,96 +0,0 @@
1
- import { asConstructable, Class, classConstruct, describeFunction, Runtime, RuntimeIndex } from '@travetto/runtime';
2
-
3
- import { CliCommandConfig, CliCommandShape } from './types.ts';
4
- import { CliUnknownCommandError } from './error.ts';
5
-
6
- const CLI_FILE_REGEX = /\/cli[.](?<name>.{0,100}?)([.]tsx?)?$/;
7
- const getName = (s: string): string => (s.match(CLI_FILE_REGEX)?.groups?.name ?? s).replaceAll('_', ':');
8
-
9
- class $CliCommandRegistry {
10
- #commands = new Map<Class, CliCommandConfig>();
11
- #fileMapping: Map<string, string>;
12
-
13
- getByClass(cls: Class): CliCommandConfig | undefined {
14
- return this.#commands.get(cls);
15
- }
16
-
17
- getClass(cmd: CliCommandShape): Class<CliCommandShape> {
18
- return asConstructable<CliCommandShape>(cmd).constructor;
19
- }
20
-
21
- /**
22
- * Get list of all commands available
23
- */
24
- getCommandMapping(): Map<string, string> {
25
- if (!this.#fileMapping) {
26
- const all = new Map<string, string>();
27
- for (const e of RuntimeIndex.find({
28
- module: m => !Runtime.production || m.prod,
29
- folder: f => f === 'support',
30
- file: f => f.role === 'std' && CLI_FILE_REGEX.test(f.sourceFile)
31
- })) {
32
- all.set(getName(e.sourceFile), e.import);
33
- }
34
- this.#fileMapping = all;
35
- }
36
- return this.#fileMapping;
37
- }
38
-
39
- /**
40
- * Registers a cli command
41
- */
42
- registerClass<T extends CliCommandShape>(cls: Class<T>, cfg: Partial<CliCommandConfig>): CliCommandConfig {
43
- const meta = describeFunction(cls);
44
- this.#commands.set(cls, {
45
- cls,
46
- name: getName(meta.import),
47
- commandModule: meta.module,
48
- ...cfg,
49
- });
50
- return this.#commands.get(cls)!;
51
- }
52
-
53
- /**
54
- * Get config for a given instance
55
- */
56
- getConfig(cmd: CliCommandShape): CliCommandConfig {
57
- return this.getByClass(this.getClass(cmd))!;
58
- }
59
-
60
- /**
61
- * Get the name of a command from a given instance
62
- */
63
- getName(cmd: CliCommandShape, withModule = false): string | undefined {
64
- const cfg = this.getConfig(cmd);
65
- const prefix = withModule ? `${cfg.commandModule}:` : '';
66
- return `${prefix}${cfg.name}`;
67
- }
68
-
69
- /**
70
- * Import command into an instance
71
- */
72
- getInstance(name: string, failOnMissing: true): Promise<CliCommandShape>;
73
- async getInstance(name: string): Promise<CliCommandShape | undefined>;
74
- async getInstance(name: string, failOnMissing = false): Promise<CliCommandShape | undefined> {
75
- const found = this.getCommandMapping().get(name);
76
- if (found) {
77
- const values = Object.values(await Runtime.importFrom<Record<string, Class>>(found));
78
- for (const v of values) {
79
- const cfg = this.getByClass(v);
80
- if (cfg) {
81
- const inst = classConstruct(cfg.cls);
82
- if (!inst.isActive || inst.isActive()) {
83
- inst._cfg = this.getConfig(inst);
84
- return inst;
85
- }
86
- }
87
- }
88
- if (!failOnMissing) {
89
- return undefined;
90
- }
91
- }
92
- throw new CliUnknownCommandError(name);
93
- }
94
- }
95
-
96
- export const CliCommandRegistry = new $CliCommandRegistry();