@travetto/cli 3.1.0-rc.0 → 3.1.0-rc.2
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 +72 -9
- package/package.json +2 -2
- package/src/decorators.ts +4 -4
- package/src/help.ts +23 -7
- package/src/module.ts +1 -1
- package/src/schema.ts +16 -10
- package/src/types.ts +3 -7
- package/support/cli.cli_schema.ts +2 -3
- package/support/cli.list.ts +1 -1
- package/support/cli.main.ts +1 -5
package/README.md
CHANGED
|
@@ -130,9 +130,9 @@ HELLO
|
|
|
130
130
|
|
|
131
131
|
The [@CliCommand](https://github.com/travetto/travetto/tree/main/module/cli/src/decorators.ts#L20) supports the following data types for flags:
|
|
132
132
|
* Boolean values
|
|
133
|
-
* Number values. The [@Integer](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
134
|
-
* String values. [@MinLength](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
135
|
-
* Date values. The [@Min](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#
|
|
133
|
+
* 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.
|
|
134
|
+
* 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
|
|
135
|
+
* Date values. The [@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 lists. Same as String, but allowing multiple values.
|
|
137
137
|
* Numeric lists. Same as Number, but allowing multiple values.
|
|
138
138
|
|
|
@@ -168,7 +168,7 @@ Options:
|
|
|
168
168
|
$ trv basic:arg 20
|
|
169
169
|
|
|
170
170
|
Execution failed:
|
|
171
|
-
* volume is bigger than (10)
|
|
171
|
+
* Argument volume is bigger than (10)
|
|
172
172
|
|
|
173
173
|
Usage: doc/cli.basic:arg [options] [volume:number]
|
|
174
174
|
|
|
@@ -231,7 +231,7 @@ $ trv basic:arglist 10 5 3 9 8 1
|
|
|
231
231
|
$ trv basic:arglist 10 5 3 9 20 1
|
|
232
232
|
|
|
233
233
|
Execution failed:
|
|
234
|
-
* volumes[4] is bigger than (10)
|
|
234
|
+
* Argument volumes[4] is bigger than (10)
|
|
235
235
|
|
|
236
236
|
Usage: doc/cli.basic:arglist [options] <volumes...:number>
|
|
237
237
|
|
|
@@ -358,7 +358,7 @@ CuStOm
|
|
|
358
358
|
```
|
|
359
359
|
|
|
360
360
|
## VSCode Integration
|
|
361
|
-
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).
|
|
361
|
+
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#L20) decorator. This means the target will be visible within the editor tooling.
|
|
362
362
|
|
|
363
363
|
**Code: Simple Run Target**
|
|
364
364
|
```typescript
|
|
@@ -376,9 +376,43 @@ export class RunCommand {
|
|
|
376
376
|
}
|
|
377
377
|
```
|
|
378
378
|
|
|
379
|
-
Also, any command name that starts with `run:` (i.e. `support/cli.run_*.ts`), will be opted-in to the run behavior unless explicitly disabled.
|
|
380
|
-
|
|
381
379
|
## Advanced Usage
|
|
380
|
+
|
|
381
|
+
**Code: Anatomy of a Command**
|
|
382
|
+
```typescript
|
|
383
|
+
export interface CliCommandShape {
|
|
384
|
+
/**
|
|
385
|
+
* Action target of the command
|
|
386
|
+
*/
|
|
387
|
+
main(...args: unknown[]): OrProm<RunResponse>;
|
|
388
|
+
/**
|
|
389
|
+
* Setup environment before command runs
|
|
390
|
+
*/
|
|
391
|
+
envInit?(): OrProm<GlobalEnvConfig>;
|
|
392
|
+
/**
|
|
393
|
+
* Extra help
|
|
394
|
+
*/
|
|
395
|
+
help?(): OrProm<string[]>;
|
|
396
|
+
/**
|
|
397
|
+
* Is the command active/eligible for usage
|
|
398
|
+
*/
|
|
399
|
+
isActive?(): boolean;
|
|
400
|
+
/**
|
|
401
|
+
* Run before binding occurs
|
|
402
|
+
*/
|
|
403
|
+
initialize?(): OrProm<void>;
|
|
404
|
+
/**
|
|
405
|
+
* Run before validation occurs
|
|
406
|
+
*/
|
|
407
|
+
finalize?(unknownArgs: string[]): OrProm<void>;
|
|
408
|
+
/**
|
|
409
|
+
* Validation method
|
|
410
|
+
*/
|
|
411
|
+
validate?(...unknownArgs: unknown[]): OrProm<CliValidationError | CliValidationError[] | undefined>;
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Dependency Injection
|
|
382
416
|
If the goal is to run a more complex application, which may include depending on [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."), we can take a look at [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.")'s target:
|
|
383
417
|
|
|
384
418
|
**Code: Simple Run Target**
|
|
@@ -390,7 +424,7 @@ import { ServerHandle } from '../src/types';
|
|
|
390
424
|
/**
|
|
391
425
|
* Run a rest server as an application
|
|
392
426
|
*/
|
|
393
|
-
@CliCommand({ fields: ['module', 'env', 'profile'] })
|
|
427
|
+
@CliCommand({ runTarget: true, fields: ['module', 'env', 'profile'] })
|
|
394
428
|
export class RunRestCommand {
|
|
395
429
|
|
|
396
430
|
/** Port to run on */
|
|
@@ -410,3 +444,32 @@ export class RunRestCommand {
|
|
|
410
444
|
As noted in the example above, `fields` is specified in this execution, with support for `module`, `env`, and `profile`. These env and profile flags are directly tied to the GlobalEnv flags defined in the [Base](https://github.com/travetto/travetto/tree/main/module/base#readme "Environment config and common utilities for travetto applications.") module.
|
|
411
445
|
|
|
412
446
|
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.
|
|
447
|
+
|
|
448
|
+
### Custom Validation
|
|
449
|
+
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#L10) errors that are returned will be shared with the user, and fail to invoke the `main` method.
|
|
450
|
+
|
|
451
|
+
**Code: CliValidationError**
|
|
452
|
+
```typescript
|
|
453
|
+
export type CliValidationError = {
|
|
454
|
+
/**
|
|
455
|
+
* The error message
|
|
456
|
+
*/
|
|
457
|
+
message: string;
|
|
458
|
+
/**
|
|
459
|
+
* Source of validation
|
|
460
|
+
*/
|
|
461
|
+
source?: 'flag' | 'arg' | 'custom';
|
|
462
|
+
};
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
A simple example of the validation can be found in the `doc` command:
|
|
466
|
+
|
|
467
|
+
**Code: Simple Validation Example**
|
|
468
|
+
```typescript
|
|
469
|
+
async validate(...args: unknown[]): Promise<CliValidationError | undefined> {
|
|
470
|
+
const docFile = path.resolve(this.input);
|
|
471
|
+
if (!(await fs.stat(docFile).catch(() => false))) {
|
|
472
|
+
return { message: `input: ${this.input} does not exist`, source: 'flag' };
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cli",
|
|
3
|
-
"version": "3.1.0-rc.
|
|
3
|
+
"version": "3.1.0-rc.2",
|
|
4
4
|
"description": "CLI infrastructure for Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"directory": "module/cli"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@travetto/schema": "^3.1.0-rc.
|
|
27
|
+
"@travetto/schema": "^3.1.0-rc.2",
|
|
28
28
|
"@travetto/terminal": "^3.1.0-rc.0",
|
|
29
29
|
"@travetto/worker": "^3.1.0-rc.0"
|
|
30
30
|
},
|
package/src/decorators.ts
CHANGED
|
@@ -35,7 +35,6 @@ export function CliCommand(cfg: { fields?: ExtraFields[], runTarget?: boolean, h
|
|
|
35
35
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
36
36
|
cls: target as ConcreteClass<T>,
|
|
37
37
|
hidden: cfg.hidden,
|
|
38
|
-
runTarget: cfg.runTarget ?? name.startsWith('run:'),
|
|
39
38
|
preMain: (cmd: CliCommandShape & { env?: string, profile?: string[], module?: string }) => {
|
|
40
39
|
if (addEnv) { defineGlobalEnv({ envName: cmd.env }); }
|
|
41
40
|
if (addProfile) { defineGlobalEnv({ profiles: cmd.profile }); }
|
|
@@ -72,10 +71,11 @@ export function CliCommand(cfg: { fields?: ExtraFields[], runTarget?: boolean, h
|
|
|
72
71
|
});
|
|
73
72
|
|
|
74
73
|
// Register validator for module
|
|
75
|
-
(pendingCls.validators ??= []).push(item =>
|
|
74
|
+
(pendingCls.validators ??= []).push(async item => {
|
|
76
75
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
77
|
-
CliModuleUtil.validateCommandModule(getMod(target), item as { module?: string })
|
|
78
|
-
|
|
76
|
+
const res = await CliModuleUtil.validateCommandModule(getMod(target), item as { module?: string });
|
|
77
|
+
return res ? { ...res, kind: 'custom', path: '.' } : res;
|
|
78
|
+
});
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
81
|
}
|
package/src/help.ts
CHANGED
|
@@ -6,6 +6,12 @@ import { CliCommandShape, CliValidationResultError } from './types';
|
|
|
6
6
|
import { CliCommandRegistry } from './registry';
|
|
7
7
|
import { CliCommandSchemaUtil } from './schema';
|
|
8
8
|
|
|
9
|
+
const validationSourceMap = {
|
|
10
|
+
custom: '',
|
|
11
|
+
arg: 'Argument',
|
|
12
|
+
flag: 'Flag'
|
|
13
|
+
};
|
|
14
|
+
|
|
9
15
|
/**
|
|
10
16
|
* Utilities for showing help
|
|
11
17
|
*/
|
|
@@ -93,12 +99,20 @@ export class HelpUtil {
|
|
|
93
99
|
const maxWidth = keys.reduce((a, b) => Math.max(a, stripAnsiCodes(b).length), 0);
|
|
94
100
|
|
|
95
101
|
for (const cmd of keys) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
try {
|
|
103
|
+
const inst = await CliCommandRegistry.getInstance(cmd);
|
|
104
|
+
if (inst) {
|
|
105
|
+
const cfg = await CliCommandRegistry.getConfig(inst);
|
|
106
|
+
if (!cfg.hidden) {
|
|
107
|
+
const schema = await CliCommandSchemaUtil.getSchema(inst);
|
|
108
|
+
rows.push(cliTpl` ${{ param: cmd.padEnd(maxWidth, ' ') }} ${{ title: schema.title }}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (err instanceof Error) {
|
|
113
|
+
rows.push(cliTpl` ${{ param: cmd.padEnd(maxWidth, ' ') }} ${{ failure: err.message.split(/\n/)[0] }}`);
|
|
114
|
+
} else {
|
|
115
|
+
throw err;
|
|
102
116
|
}
|
|
103
117
|
}
|
|
104
118
|
}
|
|
@@ -124,7 +138,9 @@ export class HelpUtil {
|
|
|
124
138
|
static renderValidationError(cmd: CliCommandShape, err: CliValidationResultError): string {
|
|
125
139
|
return [
|
|
126
140
|
cliTpl`${{ failure: 'Execution failed' }}:`,
|
|
127
|
-
...err.errors.map(e =>
|
|
141
|
+
...err.errors.map(e => e.source && e.source !== 'custom' ?
|
|
142
|
+
cliTpl` * ${{ identifier: validationSourceMap[e.source] }} ${{ subtitle: e.message }}` :
|
|
143
|
+
cliTpl` * ${{ failure: e.message }}`),
|
|
128
144
|
'',
|
|
129
145
|
].join('\n');
|
|
130
146
|
}
|
package/src/module.ts
CHANGED
|
@@ -27,7 +27,7 @@ const COLORS = TypedObject.keys(NAMED_COLORS)
|
|
|
27
27
|
|
|
28
28
|
const colorize = (val: string, idx: number): string => COLORS[idx % COLORS.length](val);
|
|
29
29
|
|
|
30
|
-
const modError = (message: string): CliValidationError => ({
|
|
30
|
+
const modError = (message: string): CliValidationError => ({ source: 'flag', message: `module: ${message}` });
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Simple utilities for understanding modules for CLI use cases
|
package/src/schema.ts
CHANGED
|
@@ -22,6 +22,10 @@ function fieldToInput(x: FieldConfig): CliCommandInput {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
const VALID_FLAG = /^-{1,2}[a-z]/i;
|
|
26
|
+
const LONG_FLAG = /^--[a-z]/i;
|
|
27
|
+
const SHORT_FLAG = /^-[a-z]/i;
|
|
28
|
+
|
|
25
29
|
const isBoolFlag = (x: CliCommandInput): boolean => x.type === 'boolean' && !x.array;
|
|
26
30
|
|
|
27
31
|
/**
|
|
@@ -55,7 +59,7 @@ export class CliCommandSchemaUtil {
|
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
const schema = await SchemaRegistry.getViewSchema(cls);
|
|
58
|
-
const flags =
|
|
62
|
+
const flags = Object.values(schema.schema).map(fieldToInput);
|
|
59
63
|
|
|
60
64
|
// Add help command
|
|
61
65
|
flags.push({ name: 'help', flagNames: ['h'], description: 'display help for command', type: 'boolean' });
|
|
@@ -64,13 +68,13 @@ export class CliCommandSchemaUtil {
|
|
|
64
68
|
|
|
65
69
|
const used = new Set(flags
|
|
66
70
|
.flatMap(f => f.flagNames ?? [])
|
|
67
|
-
.filter(x =>
|
|
71
|
+
.filter(x => SHORT_FLAG.test(x) || x.replaceAll('-', '').length < 3)
|
|
68
72
|
.map(x => x.replace(/^-+/, ''))
|
|
69
73
|
);
|
|
70
74
|
|
|
71
75
|
for (const flag of flags) {
|
|
72
|
-
let short = (flag.flagNames ?? []).find(x =>
|
|
73
|
-
const long = (flag.flagNames ?? []).find(x =>
|
|
76
|
+
let short = (flag.flagNames ?? []).find(x => SHORT_FLAG.test(x) || x.replaceAll('-', '').length < 3)?.replace(/^-+/, '');
|
|
77
|
+
const long = (flag.flagNames ?? []).find(x => LONG_FLAG.test(x) || x.replaceAll('-', '').length > 2)?.replace(/^-+/, '') ??
|
|
74
78
|
flag.name.replace(/([a-z])([A-Z])/g, (_, l, r: string) => `${l}-${r.toLowerCase()}`);
|
|
75
79
|
const aliases: string[] = flag.flagNames = [];
|
|
76
80
|
|
|
@@ -123,7 +127,7 @@ export class CliCommandSchemaUtil {
|
|
|
123
127
|
|
|
124
128
|
for (const el of schema.args) {
|
|
125
129
|
// Siphon off unrecognized flags, in order
|
|
126
|
-
while (i < copy.length && copy[i]
|
|
130
|
+
while (i < copy.length && VALID_FLAG.test(copy[i])) {
|
|
127
131
|
i += 1;
|
|
128
132
|
}
|
|
129
133
|
|
|
@@ -132,7 +136,7 @@ export class CliCommandSchemaUtil {
|
|
|
132
136
|
} else if (el.array) {
|
|
133
137
|
const sub: string[] = [];
|
|
134
138
|
while (i < copy.length) {
|
|
135
|
-
if (!copy[i]
|
|
139
|
+
if (!VALID_FLAG.test(copy[i])) {
|
|
136
140
|
sub.push(copy[i]);
|
|
137
141
|
found[i] = true;
|
|
138
142
|
}
|
|
@@ -196,7 +200,7 @@ export class CliCommandSchemaUtil {
|
|
|
196
200
|
} else if (isBoolFlag(input)) {
|
|
197
201
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
198
202
|
template[key] = !arg.startsWith('--no') as T[typeof key];
|
|
199
|
-
} else if (next === undefined ||
|
|
203
|
+
} else if (next === undefined || VALID_FLAG.test(next)) {
|
|
200
204
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
201
205
|
template[key] = null as T[typeof key];
|
|
202
206
|
} else if (input.array) {
|
|
@@ -232,16 +236,18 @@ export class CliCommandSchemaUtil {
|
|
|
232
236
|
async (): Promise<void> => {
|
|
233
237
|
const res = await cmd.validate?.(...args);
|
|
234
238
|
if (res) {
|
|
235
|
-
throw new
|
|
239
|
+
throw new CliValidationResultError(Array.isArray(res) ? res : [res]);
|
|
236
240
|
}
|
|
237
241
|
},
|
|
238
242
|
];
|
|
239
243
|
|
|
244
|
+
const SOURCES = ['flag', 'arg', 'custom'] as const;
|
|
245
|
+
|
|
240
246
|
const results = validators.map((x, i) => x().catch(err => {
|
|
241
|
-
if (!(err instanceof ValidationResultError)) {
|
|
247
|
+
if (!(err instanceof CliValidationResultError) && !(err instanceof ValidationResultError)) {
|
|
242
248
|
throw err;
|
|
243
249
|
}
|
|
244
|
-
return err.errors.map(v => ({
|
|
250
|
+
return err.errors.map(v => ({ source: SOURCES[i], ...v }));
|
|
245
251
|
}));
|
|
246
252
|
|
|
247
253
|
const errors = (await Promise.all(results)).flatMap(x => (x ?? []));
|
package/src/types.ts
CHANGED
|
@@ -13,13 +13,9 @@ export type CliValidationError = {
|
|
|
13
13
|
*/
|
|
14
14
|
message: string;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Source of validation
|
|
17
17
|
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The kind of validation
|
|
21
|
-
*/
|
|
22
|
-
kind: string;
|
|
18
|
+
source?: 'flag' | 'arg' | 'custom';
|
|
23
19
|
};
|
|
24
20
|
|
|
25
21
|
/**
|
|
@@ -65,7 +61,7 @@ export interface CliCommandShape {
|
|
|
65
61
|
/**
|
|
66
62
|
* Validation method
|
|
67
63
|
*/
|
|
68
|
-
validate?(...
|
|
64
|
+
validate?(...unknownArgs: unknown[]): OrProm<CliValidationError | CliValidationError[] | undefined>;
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
/**
|
|
@@ -17,9 +17,8 @@ export class CliSchemaCommand {
|
|
|
17
17
|
async validate(name?: string): Promise<CliValidationError | undefined> {
|
|
18
18
|
if (name && !CliCommandRegistry.getCommandMapping().has(name)) {
|
|
19
19
|
return {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
message: `${name} is not a valid cli command`
|
|
20
|
+
source: 'arg',
|
|
21
|
+
message: `name: ${name} is not a valid cli command`
|
|
23
22
|
};
|
|
24
23
|
}
|
|
25
24
|
}
|
package/support/cli.list.ts
CHANGED
|
@@ -31,7 +31,7 @@ export class ListModuleCommand implements CliCommandShape {
|
|
|
31
31
|
}
|
|
32
32
|
case 'json': {
|
|
33
33
|
const outputMap = CliModuleUtil.getDependencyGraph(mods);
|
|
34
|
-
await write(JSON.stringify(Object.entries(outputMap).map(([name, children]) => ({ name, children })), null, 2));
|
|
34
|
+
await write(JSON.stringify(Object.entries(outputMap).map(([name, children]) => ({ name, children, local: RootIndex.getModule(name)?.local })), null, 2));
|
|
35
35
|
break;
|
|
36
36
|
}
|
|
37
37
|
case 'graph': {
|
package/support/cli.main.ts
CHANGED
|
@@ -25,11 +25,7 @@ export class MainCommand implements CliCommandShape {
|
|
|
25
25
|
async validate(fileOrImport: string): Promise<CliValidationError | undefined> {
|
|
26
26
|
const imp = await this.#getImport(fileOrImport);
|
|
27
27
|
if (!imp) {
|
|
28
|
-
return {
|
|
29
|
-
message: `Unknown file: ${fileOrImport}`,
|
|
30
|
-
kind: 'required',
|
|
31
|
-
path: 'fileOrImport'
|
|
32
|
-
};
|
|
28
|
+
return { message: `Unknown file: ${fileOrImport}` };
|
|
33
29
|
}
|
|
34
30
|
}
|
|
35
31
|
|