@travetto/cli 8.0.0-alpha.3 → 8.0.0-alpha.30
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 +108 -86
- package/__index__.ts +10 -9
- package/bin/trv.js +3 -1
- package/package.json +3 -3
- package/src/color.ts +1 -1
- package/src/execute.ts +38 -20
- package/src/help.ts +129 -76
- package/src/module.ts +9 -10
- package/src/parse.ts +57 -42
- package/src/registry/decorator.ts +52 -47
- package/src/registry/registry-adapter.ts +22 -25
- package/src/registry/registry-index.ts +30 -26
- package/src/schema-export.ts +25 -16
- package/src/schema.ts +15 -10
- package/src/scm.ts +19 -10
- package/src/service.ts +34 -33
- package/src/types.ts +9 -7
- package/src/util.ts +25 -26
- package/support/cli.cli_schema.ts +8 -7
- package/support/cli.main.ts +12 -8
- package/support/cli.service.ts +25 -19
- package/support/entry.trv.ts +3 -2
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
import { type Class, type ClassInstance,
|
|
1
|
+
import { type Class, type ClassInstance, castTo, describeFunction, Env, getClass, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
2
2
|
import { SchemaRegistryIndex, type ValidationError } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import type { CliCommandShape } from '../types.ts';
|
|
5
|
-
import { CliCommandRegistryIndex } from './registry-index.ts';
|
|
6
4
|
import { CliModuleUtil } from '../module.ts';
|
|
7
5
|
import { CliParseUtil } from '../parse.ts';
|
|
6
|
+
import type { CliCommandShape } from '../types.ts';
|
|
8
7
|
import { CliUtil } from '../util.ts';
|
|
8
|
+
import { CliCommandRegistryIndex } from './registry-index.ts';
|
|
9
9
|
|
|
10
10
|
type CliCommandConfigOptions = { runTarget?: boolean };
|
|
11
|
-
type CliFlagOptions = { full?: string
|
|
12
|
-
|
|
13
|
-
function runBeforeMain<T>(cls: Class, handler: (item: T) => (unknown | Promise<unknown>), runTarget?: boolean): void {
|
|
14
|
-
CliCommandRegistryIndex.getForRegister(cls).register({
|
|
15
|
-
runTarget,
|
|
16
|
-
preMain: [async (cmd): Promise<void> => { await handler(castTo(cmd)); }]
|
|
17
|
-
});
|
|
18
|
-
}
|
|
11
|
+
type CliFlagOptions = { full?: string; short?: string; envVars?: string[] };
|
|
19
12
|
|
|
20
13
|
/**
|
|
21
14
|
* Decorator to register a CLI command
|
|
@@ -39,8 +32,7 @@ export function CliCommand(config: CliCommandConfigOptions = {}) {
|
|
|
39
32
|
*/
|
|
40
33
|
export function CliFlag(config: CliFlagOptions) {
|
|
41
34
|
return function (instance: ClassInstance, property: string): void {
|
|
42
|
-
SchemaRegistryIndex.getForRegister(getClass(instance))
|
|
43
|
-
.registerField(property, CliParseUtil.buildAliases(config));
|
|
35
|
+
SchemaRegistryIndex.getForRegister(getClass(instance)).registerField(property, CliParseUtil.buildAliases(config));
|
|
44
36
|
};
|
|
45
37
|
}
|
|
46
38
|
|
|
@@ -72,11 +64,9 @@ export function CliProfilesFlag(config: CliFlagOptions = {}) {
|
|
|
72
64
|
description: 'Application profiles'
|
|
73
65
|
});
|
|
74
66
|
|
|
75
|
-
|
|
76
|
-
Env.TRV_PROFILES.set([...cmd[property] ?? [], ...(Env.TRV_PROFILES.list ?? [])])
|
|
77
|
-
);
|
|
67
|
+
CliCommandRegistryIndex.registerPreMain<typeof instance>(cls, 1, cmd => Env.TRV_PROFILES.add(...(cmd[property] ?? [])));
|
|
78
68
|
};
|
|
79
|
-
}
|
|
69
|
+
}
|
|
80
70
|
|
|
81
71
|
/**
|
|
82
72
|
* Registers a flag to support targeting a specific module
|
|
@@ -93,28 +83,40 @@ export function CliModuleFlag(config: CliFlagOptions & { scope?: 'current' | 'co
|
|
|
93
83
|
...CliParseUtil.buildAliases(config, Env.TRV_MODULE.key),
|
|
94
84
|
description: 'Module to run for',
|
|
95
85
|
specifiers: ['module'],
|
|
96
|
-
required: { active: Runtime.monoRoot }
|
|
86
|
+
required: { active: Runtime.monoRoot && config.scope !== 'command' }
|
|
97
87
|
});
|
|
98
88
|
|
|
99
89
|
SchemaRegistryIndex.getForRegister(cls).register({
|
|
100
|
-
validators: [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
RuntimeIndex.reinitForModule(runModule);
|
|
109
|
-
} catch {
|
|
90
|
+
validators: [
|
|
91
|
+
async (cmd: CliCommandShape): Promise<ValidationError | undefined> => {
|
|
92
|
+
const typed: typeof cmd & { [property]?: string } = castTo(cmd);
|
|
93
|
+
const providedModule = typed[property];
|
|
94
|
+
const runModule = (config.scope === 'command' ? commandModule : providedModule) || Runtime.main.name;
|
|
95
|
+
|
|
96
|
+
// If we need to run as a specific module
|
|
97
|
+
if (runModule !== Runtime.main.name && RuntimeIndex.getModule(runModule) === undefined) {
|
|
110
98
|
return { source: 'flag', message: `${runModule} is an unknown module`, kind: 'custom', path: property };
|
|
111
99
|
}
|
|
112
|
-
}
|
|
113
100
|
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
if (!(await CliModuleUtil.moduleHasDependency(runModule, commandModule))) {
|
|
102
|
+
return {
|
|
103
|
+
source: 'flag',
|
|
104
|
+
message: `${runModule} does not have ${commandModule} as a dependency`,
|
|
105
|
+
kind: 'custom',
|
|
106
|
+
path: property
|
|
107
|
+
};
|
|
108
|
+
}
|
|
116
109
|
}
|
|
117
|
-
|
|
110
|
+
]
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
CliCommandRegistryIndex.registerPreMain<typeof instance>(cls, 5, cmd => {
|
|
114
|
+
const typed: typeof cmd & { [property]?: string } = castTo(cmd);
|
|
115
|
+
const providedModule = typed[property];
|
|
116
|
+
const runModule = (config.scope === 'command' ? commandModule : providedModule) || Runtime.main.name;
|
|
117
|
+
if (runModule !== Runtime.main.name) {
|
|
118
|
+
RuntimeIndex.reinitForModule(runModule);
|
|
119
|
+
}
|
|
118
120
|
});
|
|
119
121
|
};
|
|
120
122
|
}
|
|
@@ -127,14 +129,17 @@ export function CliModuleFlag(config: CliFlagOptions & { scope?: 'current' | 'co
|
|
|
127
129
|
export function CliRestartOnChangeFlag(config: CliFlagOptions = {}) {
|
|
128
130
|
return function <K extends string, T extends Partial<Record<K, boolean>>>(instance: T, property: K): void {
|
|
129
131
|
const cls = getClass(instance);
|
|
132
|
+
if (Runtime.production) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
130
136
|
SchemaRegistryIndex.getForRegister(cls).registerField(property, {
|
|
131
137
|
...CliParseUtil.buildAliases(config),
|
|
132
|
-
description: 'Should the invocation automatically restart on source changes'
|
|
133
|
-
default: Runtime.localDevelopment,
|
|
134
|
-
required: { active: false },
|
|
138
|
+
description: 'Should the invocation automatically restart on source changes'
|
|
135
139
|
});
|
|
136
140
|
|
|
137
|
-
|
|
141
|
+
CliCommandRegistryIndex.getForRegister(cls).register({ runTarget: true });
|
|
142
|
+
CliCommandRegistryIndex.registerPreMain<typeof instance>(cls, 20, cmd => CliUtil.runWithRestartOnChange(cmd[property]));
|
|
138
143
|
};
|
|
139
144
|
}
|
|
140
145
|
|
|
@@ -145,20 +150,20 @@ export function CliRestartOnChangeFlag(config: CliFlagOptions = {}) {
|
|
|
145
150
|
*/
|
|
146
151
|
export function CliDebugIpcFlag(config: CliFlagOptions = {}) {
|
|
147
152
|
return function <K extends string, T extends Partial<Record<K, boolean>>>(instance: T, property: K): void {
|
|
153
|
+
if (Runtime.production) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
148
157
|
const cls = getClass(instance);
|
|
149
158
|
SchemaRegistryIndex.getForRegister(cls).registerField(property, {
|
|
150
159
|
...CliParseUtil.buildAliases(config, Env.TRV_DEBUG_IPC.key),
|
|
151
|
-
description: 'Should the invocation
|
|
152
|
-
default: Runtime.localDevelopment,
|
|
153
|
-
required: { active: false },
|
|
160
|
+
description: 'Should the invocation support debugging via IPC (e.g. from VSCode)'
|
|
154
161
|
});
|
|
155
162
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
true
|
|
162
|
-
);
|
|
163
|
+
CliCommandRegistryIndex.getForRegister(cls).register({ runTarget: true });
|
|
164
|
+
CliCommandRegistryIndex.registerPreMain<typeof instance>(cls, 10, cmd => {
|
|
165
|
+
const cliConfig = CliCommandRegistryIndex.get(cls);
|
|
166
|
+
return cmd[property] && CliUtil.runWithDebugIpc(cliConfig.name);
|
|
167
|
+
});
|
|
163
168
|
};
|
|
164
|
-
}
|
|
169
|
+
}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import { type Class, classConstruct, describeFunction } from '@travetto/runtime';
|
|
2
1
|
import type { RegistryAdapter } from '@travetto/registry';
|
|
2
|
+
import { type Class, classConstruct, describeFunction } from '@travetto/runtime';
|
|
3
3
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
-
import type { CliCommandConfig, CliCommandShape } from '../types.ts';
|
|
6
5
|
import { CliParseUtil, ENV_PREFIX } from '../parse.ts';
|
|
6
|
+
import type { CliCommandConfig, CliCommandShape } from '../types.ts';
|
|
7
7
|
|
|
8
8
|
const CLI_FILE_REGEX = /\/cli[.](?<name>.{0,100}?)([.]tsx?)?$/;
|
|
9
9
|
|
|
10
10
|
const getName = (name: string): string => (name.match(CLI_FILE_REGEX)?.groups?.name ?? name).replaceAll('_', ':');
|
|
11
11
|
const stripDashes = (flag?: string): string | undefined => flag?.replace(/^-+/, '');
|
|
12
|
-
const toFlagName = (field: string): string =>
|
|
12
|
+
const toFlagName = (field: string): string =>
|
|
13
|
+
field.replace(/([a-z])([A-Z])/g, (_, left: string, right: string) => `${left}-${right.toLowerCase()}`);
|
|
13
14
|
|
|
14
15
|
function combineClasses(base: CliCommandConfig, ...configs: Partial<CliCommandConfig>[]): CliCommandConfig {
|
|
15
16
|
for (const config of configs) {
|
|
16
17
|
base.runTarget = config.runTarget ?? base.runTarget;
|
|
17
18
|
if (config.preMain) {
|
|
18
|
-
base.preMain = [...base.preMain ?? [], ...config.preMain ?? []];
|
|
19
|
+
base.preMain = [...(base.preMain ?? []), ...(config.preMain ?? [])];
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
return base;
|
|
@@ -29,26 +30,15 @@ export class CliCommandRegistryAdapter implements RegistryAdapter<CliCommandConf
|
|
|
29
30
|
this.#cls = cls;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
// TODO: handle when aliases overlap/conflict
|
|
34
|
+
finalize(parent?: CliCommandConfig): void {
|
|
33
35
|
// Add help command
|
|
34
36
|
const schema = SchemaRegistryIndex.getConfig(this.#cls);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class: this.#cls,
|
|
41
|
-
description: 'display help for command',
|
|
42
|
-
required: { active: false },
|
|
43
|
-
default: false,
|
|
44
|
-
access: 'readonly',
|
|
45
|
-
aliases: ['-h', '--help']
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const used = new Set(Object.values(schema.fields)
|
|
49
|
-
.flatMap(field => field.aliases ?? [])
|
|
50
|
-
.filter(alias => !alias.startsWith(ENV_PREFIX))
|
|
51
|
-
.map(stripDashes)
|
|
37
|
+
const used = new Set(
|
|
38
|
+
Object.values(schema.fields)
|
|
39
|
+
.flatMap(field => field.aliases ?? [])
|
|
40
|
+
.filter(alias => !alias.startsWith(ENV_PREFIX))
|
|
41
|
+
.map(stripDashes)
|
|
52
42
|
);
|
|
53
43
|
|
|
54
44
|
for (const field of Object.values(schema.fields)) {
|
|
@@ -57,7 +47,7 @@ export class CliCommandRegistryAdapter implements RegistryAdapter<CliCommandConf
|
|
|
57
47
|
|
|
58
48
|
let short = stripDashes(shortAliases?.[0]) ?? rawAliases.find(alias => alias.length <= 2);
|
|
59
49
|
const long = stripDashes(longAliases?.[0]) ?? rawAliases.find(alias => alias.length >= 3) ?? toFlagName(fieldName);
|
|
60
|
-
const aliases: string[] = field.aliases = [...envAliases];
|
|
50
|
+
const aliases: string[] = (field.aliases = [...envAliases]);
|
|
61
51
|
|
|
62
52
|
if (short === undefined) {
|
|
63
53
|
short = fieldName.charAt(0);
|
|
@@ -75,6 +65,13 @@ export class CliCommandRegistryAdapter implements RegistryAdapter<CliCommandConf
|
|
|
75
65
|
aliases.push(`--no-${long}`);
|
|
76
66
|
}
|
|
77
67
|
}
|
|
68
|
+
|
|
69
|
+
if (parent) {
|
|
70
|
+
this.#config.preMain = [...this.#config.preMain, ...(parent?.preMain ?? [])];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Sort
|
|
74
|
+
this.#config.preMain = this.#config.preMain.toSorted((left, right) => left.priority - right.priority);
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
get(): CliCommandConfig {
|
|
@@ -86,7 +83,7 @@ export class CliCommandRegistryAdapter implements RegistryAdapter<CliCommandConf
|
|
|
86
83
|
*/
|
|
87
84
|
register(...configs: Partial<CliCommandConfig>[]): CliCommandConfig {
|
|
88
85
|
const metadata = describeFunction(this.#cls);
|
|
89
|
-
this.#config ??= { cls: this.#cls, name: getName(metadata.import), preMain: [], runTarget:
|
|
86
|
+
this.#config ??= { cls: this.#cls, name: getName(metadata.import), preMain: [], runTarget: false };
|
|
90
87
|
return combineClasses(this.#config, ...configs);
|
|
91
88
|
}
|
|
92
89
|
|
|
@@ -96,4 +93,4 @@ export class CliCommandRegistryAdapter implements RegistryAdapter<CliCommandConf
|
|
|
96
93
|
getInstance(): CliCommandShape {
|
|
97
94
|
return classConstruct(this.#cls);
|
|
98
95
|
}
|
|
99
|
-
}
|
|
96
|
+
}
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type
|
|
1
|
+
import { Registry, type RegistryAdapter, type RegistryIndex, RegistryIndexStore } from '@travetto/registry';
|
|
2
|
+
import { type Any, type Class, getClass, getParentClass, isClass, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
3
3
|
import { type SchemaClassConfig, SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
-
import type { CliCommandConfig, CliCommandShape } from '../types.ts';
|
|
5
|
+
import type { CliCommandConfig, CliCommandShape, PreMainHandler } from '../types.ts';
|
|
6
6
|
import { CliCommandRegistryAdapter } from './registry-adapter.ts';
|
|
7
7
|
|
|
8
8
|
const CLI_FILE_REGEX = /\/cli[.](?<name>.{0,100}?)([.]tsx?)?$/;
|
|
9
9
|
const getName = (field: string): string => (field.match(CLI_FILE_REGEX)?.groups?.name ?? field).replaceAll('_', ':');
|
|
10
10
|
|
|
11
|
-
type CliCommandLoadResult = { command: string
|
|
11
|
+
type CliCommandLoadResult = { command: string; config: CliCommandConfig; instance: CliCommandShape; schema: SchemaClassConfig };
|
|
12
12
|
|
|
13
13
|
export const UNKNOWN_COMMAND = Symbol();
|
|
14
14
|
|
|
15
15
|
export class CliCommandRegistryIndex implements RegistryIndex {
|
|
16
|
-
|
|
17
16
|
static #instance = Registry.registerIndex(this);
|
|
18
17
|
|
|
19
18
|
static getForRegister(cls: Class): RegistryAdapter<CliCommandConfig> {
|
|
@@ -28,12 +27,18 @@ export class CliCommandRegistryIndex implements RegistryIndex {
|
|
|
28
27
|
return this.#instance.load(names);
|
|
29
28
|
}
|
|
30
29
|
|
|
30
|
+
static registerPreMain<T = Any>(cls: Class, priority: number, handler: PreMainHandler<T>['handler']): void {
|
|
31
|
+
CliCommandRegistryIndex.getForRegister(cls).register({ preMain: [{ handler, priority }] });
|
|
32
|
+
}
|
|
33
|
+
|
|
31
34
|
#fileMapping: Map<string, string>;
|
|
32
35
|
#instanceMapping: Map<string, CliCommandShape> = new Map();
|
|
33
36
|
|
|
34
37
|
store = new RegistryIndexStore(CliCommandRegistryAdapter);
|
|
35
38
|
|
|
36
|
-
/** @private */ constructor(source: unknown) {
|
|
39
|
+
/** @private */ constructor(source: unknown) {
|
|
40
|
+
Registry.validateConstructor(source);
|
|
41
|
+
}
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
44
|
* Get list of all commands available
|
|
@@ -67,19 +72,16 @@ export class CliCommandRegistryIndex implements RegistryIndex {
|
|
|
67
72
|
|
|
68
73
|
const found = this.#commandMapping.get(name)!;
|
|
69
74
|
const values = Object.values(await Runtime.importFrom<Record<string, Class>>(found));
|
|
70
|
-
const filtered = values
|
|
71
|
-
|
|
72
|
-
.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const uninitialized = filtered
|
|
82
|
-
.filter(cls => !this.store.finalized(cls));
|
|
75
|
+
const filtered = values.filter(isClass).reduce<Class[]>((classes, cls) => {
|
|
76
|
+
const parent = getParentClass(cls);
|
|
77
|
+
if (parent && !classes.includes(parent)) {
|
|
78
|
+
classes.push(parent);
|
|
79
|
+
}
|
|
80
|
+
classes.push(cls);
|
|
81
|
+
return classes;
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const uninitialized = filtered.filter(cls => !this.store.finalized(cls));
|
|
83
85
|
|
|
84
86
|
// Initialize any uninitialized commands
|
|
85
87
|
if (uninitialized.length) {
|
|
@@ -106,13 +108,15 @@ export class CliCommandRegistryIndex implements RegistryIndex {
|
|
|
106
108
|
async load(names?: string[]): Promise<CliCommandLoadResult[]> {
|
|
107
109
|
const keys = names ?? [...this.#commandMapping.keys()];
|
|
108
110
|
|
|
109
|
-
const list = await Promise.all(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
const list = await Promise.all(
|
|
112
|
+
keys.map(async key => {
|
|
113
|
+
const instance = await this.#getInstance(key);
|
|
114
|
+
const config = this.store.get(getClass(instance)).get();
|
|
115
|
+
const schema = SchemaRegistryIndex.getConfig(getClass(instance));
|
|
116
|
+
return { command: key, instance, config, schema };
|
|
117
|
+
})
|
|
118
|
+
);
|
|
115
119
|
|
|
116
120
|
return list.sort((a, b) => a.command.localeCompare(b.command));
|
|
117
121
|
}
|
|
118
|
-
}
|
|
122
|
+
}
|
package/src/schema-export.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Class, castTo, describeFunction } from '@travetto/runtime';
|
|
2
2
|
import { type SchemaInputConfig, SchemaRegistryIndex } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { CliCommandRegistryIndex } from '
|
|
4
|
+
import { CliCommandRegistryIndex } from './registry/registry-index.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* CLI Command argument/flag shape
|
|
@@ -32,24 +32,30 @@ export interface CliCommandSchema<K extends string = string> {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export class CliSchemaExportUtil {
|
|
35
|
-
|
|
36
35
|
/**
|
|
37
36
|
* Get the base type for a CLI command input
|
|
38
37
|
*/
|
|
39
38
|
static baseInputType(config: SchemaInputConfig): Pick<CliCommandInput, 'type' | 'fileExtensions'> {
|
|
40
39
|
switch (castTo<Function>(config.type)) {
|
|
41
|
-
case Date:
|
|
42
|
-
|
|
43
|
-
case
|
|
44
|
-
|
|
45
|
-
case
|
|
40
|
+
case Date:
|
|
41
|
+
return { type: 'date' };
|
|
42
|
+
case Boolean:
|
|
43
|
+
return { type: 'boolean' };
|
|
44
|
+
case Number:
|
|
45
|
+
return { type: 'number' };
|
|
46
|
+
case RegExp:
|
|
47
|
+
return { type: 'regex' };
|
|
48
|
+
case BigInt:
|
|
49
|
+
return { type: 'bigint' };
|
|
46
50
|
case String: {
|
|
47
51
|
switch (true) {
|
|
48
|
-
case config.specifiers?.includes('module'):
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
case config.specifiers?.includes('module'):
|
|
53
|
+
return { type: 'module' };
|
|
54
|
+
case config.specifiers?.includes('file'):
|
|
55
|
+
return {
|
|
56
|
+
type: 'file',
|
|
57
|
+
fileExtensions: config.specifiers?.map(specifier => specifier.split('ext:')[1]).filter(specifier => !!specifier)
|
|
58
|
+
};
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
}
|
|
@@ -62,14 +68,17 @@ export class CliSchemaExportUtil {
|
|
|
62
68
|
static processInput(config: SchemaInputConfig): CliCommandInput {
|
|
63
69
|
return {
|
|
64
70
|
...this.baseInputType(config),
|
|
65
|
-
...(
|
|
71
|
+
...('name' in config && typeof config.name === 'string' ? { name: config.name } : { name: '' }),
|
|
66
72
|
description: config.description,
|
|
67
73
|
array: config.array,
|
|
68
74
|
required: config.required?.active !== false,
|
|
69
75
|
choices: config.enum?.values,
|
|
70
76
|
default: Array.isArray(config.default) ? config.default.slice(0) : config.default,
|
|
71
77
|
flagNames: (config.aliases ?? []).slice(0).filter(value => !value.startsWith('env.')),
|
|
72
|
-
envVars: (config.aliases ?? [])
|
|
78
|
+
envVars: (config.aliases ?? [])
|
|
79
|
+
.slice(0)
|
|
80
|
+
.filter(value => value.startsWith('env.'))
|
|
81
|
+
.map(value => value.replace('env.', ''))
|
|
73
82
|
};
|
|
74
83
|
}
|
|
75
84
|
|
|
@@ -87,4 +96,4 @@ export class CliSchemaExportUtil {
|
|
|
87
96
|
runTarget: config.runTarget ?? false
|
|
88
97
|
};
|
|
89
98
|
}
|
|
90
|
-
}
|
|
99
|
+
}
|
package/src/schema.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { castKey, castTo, getClass } from '@travetto/runtime';
|
|
2
|
-
import { BindUtil, SchemaRegistryIndex, SchemaValidator,
|
|
2
|
+
import { BindUtil, SchemaRegistryIndex, SchemaValidator, type ValidationError, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { CliCommandShape, ParsedState } from './types.ts';
|
|
5
5
|
|
|
6
6
|
const getSource = (source: string | undefined, defaultSource: ValidationError['source']): ValidationError['source'] => {
|
|
7
7
|
switch (source) {
|
|
8
8
|
case 'custom':
|
|
9
9
|
case 'arg':
|
|
10
|
-
case 'flag':
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
case 'flag':
|
|
11
|
+
return source;
|
|
12
|
+
case undefined:
|
|
13
|
+
return defaultSource;
|
|
14
|
+
default:
|
|
15
|
+
return 'custom';
|
|
13
16
|
}
|
|
14
17
|
};
|
|
15
18
|
|
|
@@ -41,7 +44,7 @@ export class CliCommandSchemaUtil {
|
|
|
41
44
|
const key = castKey<T>(item.fieldName);
|
|
42
45
|
const value = item.value!;
|
|
43
46
|
if (item.array) {
|
|
44
|
-
castTo<unknown[]>(template[key] ??= castTo([])).push(value);
|
|
47
|
+
castTo<unknown[]>((template[key] ??= castTo([]))).push(value);
|
|
45
48
|
} else {
|
|
46
49
|
template[key] = castTo(value);
|
|
47
50
|
}
|
|
@@ -49,7 +52,7 @@ export class CliCommandSchemaUtil {
|
|
|
49
52
|
}
|
|
50
53
|
case 'arg': {
|
|
51
54
|
if (item.array) {
|
|
52
|
-
castTo<unknown[]>(bound[item.index] ??= []).push(item.input);
|
|
55
|
+
castTo<unknown[]>((bound[item.index] ??= [])).push(item.input);
|
|
53
56
|
} else {
|
|
54
57
|
bound[item.index] = item.input;
|
|
55
58
|
}
|
|
@@ -67,11 +70,13 @@ export class CliCommandSchemaUtil {
|
|
|
67
70
|
*/
|
|
68
71
|
static async validate(command: CliCommandShape, args: unknown[]): Promise<typeof command> {
|
|
69
72
|
const cls = getClass(command);
|
|
70
|
-
const paramNames = SchemaRegistryIndex.get(cls)
|
|
73
|
+
const paramNames = SchemaRegistryIndex.get(cls)
|
|
74
|
+
.getMethod('main')
|
|
75
|
+
.parameters.map(config => config.name!);
|
|
71
76
|
|
|
72
77
|
const results = await Promise.all([
|
|
73
78
|
SchemaValidator.validate(cls, command).then(() => [], transformFlagErrors),
|
|
74
|
-
SchemaValidator.validateMethod(cls, 'main', args, paramNames).then(() => [], transformArgErrors)
|
|
79
|
+
SchemaValidator.validateMethod(cls, 'main', args, paramNames).then(() => [], transformArgErrors)
|
|
75
80
|
]);
|
|
76
81
|
|
|
77
82
|
const errors = results.flat();
|
|
@@ -80,4 +85,4 @@ export class CliCommandSchemaUtil {
|
|
|
80
85
|
}
|
|
81
86
|
return command;
|
|
82
87
|
}
|
|
83
|
-
}
|
|
88
|
+
}
|
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 { RuntimeError, ExecUtil, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
6
5
|
import type { IndexedModule } from '@travetto/manifest';
|
|
6
|
+
import { ExecUtil, Runtime, RuntimeError, RuntimeIndex } from '@travetto/runtime';
|
|
7
7
|
|
|
8
8
|
export class CliScmUtil {
|
|
9
9
|
/**
|
|
@@ -12,17 +12,20 @@ export class CliScmUtil {
|
|
|
12
12
|
* @returns
|
|
13
13
|
*/
|
|
14
14
|
static isRepoRoot(folder: string): Promise<boolean> {
|
|
15
|
-
return fs.stat(path.resolve(folder, '.git')).then(
|
|
15
|
+
return fs.stat(path.resolve(folder, '.git')).then(
|
|
16
|
+
() => true,
|
|
17
|
+
() => false
|
|
18
|
+
);
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* Get author information
|
|
20
23
|
* @returns
|
|
21
24
|
*/
|
|
22
|
-
static async getAuthor(): Promise<{ name?: string
|
|
25
|
+
static async getAuthor(): Promise<{ name?: string; email: string }> {
|
|
23
26
|
const [name, email] = await Promise.all([
|
|
24
27
|
ExecUtil.getResult(spawn('git', ['config', 'user.name']), { catch: true }),
|
|
25
|
-
ExecUtil.getResult(spawn('git', ['config', 'user.email']))
|
|
28
|
+
ExecUtil.getResult(spawn('git', ['config', 'user.email']))
|
|
26
29
|
]);
|
|
27
30
|
return {
|
|
28
31
|
name: (name.valid ? name.stdout.trim() : '') || process.env.USER,
|
|
@@ -38,7 +41,8 @@ export class CliScmUtil {
|
|
|
38
41
|
const result = await ExecUtil.getResult(spawn('git', ['log', '--pretty=oneline'], { cwd: Runtime.workspace.path }));
|
|
39
42
|
return result.stdout
|
|
40
43
|
.split(/\n/)
|
|
41
|
-
.find(line => /Publish /.test(line))
|
|
44
|
+
.find(line => /Publish /.test(line))
|
|
45
|
+
?.split(/\s+/)?.[0];
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
/**
|
|
@@ -48,9 +52,15 @@ export class CliScmUtil {
|
|
|
48
52
|
*/
|
|
49
53
|
static async findChangedFiles(fromHash: string, toHash: string = 'HEAD'): Promise<string[]> {
|
|
50
54
|
const rootPath = Runtime.workspace.path;
|
|
51
|
-
const result = await ExecUtil.getResult(
|
|
55
|
+
const result = await ExecUtil.getResult(
|
|
56
|
+
spawn('git', ['diff', '--name-only', `${fromHash}..${toHash}`, ':!**/DOC.*', ':!**/README.*'], { cwd: rootPath }),
|
|
57
|
+
{ catch: true }
|
|
58
|
+
);
|
|
52
59
|
if (!result.valid) {
|
|
53
|
-
throw new RuntimeError('Unable to detect changes between', {
|
|
60
|
+
throw new RuntimeError('Unable to detect changes between', {
|
|
61
|
+
category: 'data',
|
|
62
|
+
details: { fromHash, toHash, output: result.stderr || result.stdout }
|
|
63
|
+
});
|
|
54
64
|
}
|
|
55
65
|
const out = new Set<string>();
|
|
56
66
|
for (const line of result.stdout.split(/\n/g)) {
|
|
@@ -76,8 +86,7 @@ export class CliScmUtil {
|
|
|
76
86
|
.map(file => RuntimeIndex.getModule(file.module))
|
|
77
87
|
.filter(module => !!module);
|
|
78
88
|
|
|
79
|
-
return [...new Set(modules)]
|
|
80
|
-
.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
89
|
+
return [...new Set(modules)].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
/**
|
|
@@ -103,4 +112,4 @@ export class CliScmUtil {
|
|
|
103
112
|
const res2 = await ExecUtil.getResult(spawn('git', ['diff', '--quiet', '--exit-code', '--cached']), { catch: true });
|
|
104
113
|
return !res1.valid || !res2.valid;
|
|
105
114
|
}
|
|
106
|
-
}
|
|
115
|
+
}
|