@travetto/cli 3.4.9 → 3.4.10
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/package.json +1 -1
- package/src/decorators.ts +4 -2
- package/src/execute.ts +2 -1
- package/src/help.ts +3 -2
- package/src/parse.ts +21 -26
- package/src/schema.ts +30 -20
- package/src/types.ts +1 -1
package/package.json
CHANGED
package/src/decorators.ts
CHANGED
|
@@ -26,8 +26,9 @@ export function CliCommand(cfg: CliCommandConfigOptions = {}) {
|
|
|
26
26
|
const { commandModule } = CliCommandRegistry.registerClass(target, {
|
|
27
27
|
hidden: cfg.hidden,
|
|
28
28
|
preMain: async (cmd) => {
|
|
29
|
-
if (addEnv
|
|
30
|
-
|
|
29
|
+
if (addEnv) {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
31
|
+
defineEnv({ envName: (cmd as { env?: string }).env ?? 'dev' });
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
});
|
|
@@ -47,6 +48,7 @@ export function CliCommand(cfg: CliCommandConfigOptions = {}) {
|
|
|
47
48
|
SchemaRegistry.registerPendingFieldConfig(target, 'module', String, {
|
|
48
49
|
aliases: ['m', CliParseUtil.toEnvField('TRV_MODULE')],
|
|
49
50
|
description: 'Module to run for',
|
|
51
|
+
specifiers: ['module'],
|
|
50
52
|
required: { active: CliUtil.monoRoot }
|
|
51
53
|
});
|
|
52
54
|
}
|
package/src/execute.ts
CHANGED
|
@@ -19,6 +19,7 @@ export class ExecutionManager {
|
|
|
19
19
|
*/
|
|
20
20
|
static async #runCommand(cmd: CliCommandShape, args: string[]): Promise<RunResponse> {
|
|
21
21
|
const schema = await CliCommandSchemaUtil.getSchema(cmd);
|
|
22
|
+
args = await CliParseUtil.expandArgs(schema, args);
|
|
22
23
|
cmd._parsed = await CliParseUtil.parse(schema, args);
|
|
23
24
|
const cfg = CliCommandRegistry.getConfig(cmd);
|
|
24
25
|
|
|
@@ -73,7 +74,7 @@ export class ExecutionManager {
|
|
|
73
74
|
|
|
74
75
|
let command: CliCommandShape | undefined;
|
|
75
76
|
try {
|
|
76
|
-
const { cmd, args, help } =
|
|
77
|
+
const { cmd, args, help } = CliParseUtil.getArgs(argv);
|
|
77
78
|
|
|
78
79
|
if (!cmd) {
|
|
79
80
|
console.info!(await HelpUtil.renderAllHelp());
|
package/src/help.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { CliCommandShape } from './types';
|
|
|
6
6
|
import { CliCommandRegistry } from './registry';
|
|
7
7
|
import { CliCommandSchemaUtil } from './schema';
|
|
8
8
|
import { CliValidationResultError } from './error';
|
|
9
|
+
import { isBoolFlag } from './parse';
|
|
9
10
|
|
|
10
11
|
const validationSourceMap = {
|
|
11
12
|
custom: '',
|
|
@@ -47,7 +48,7 @@ export class HelpUtil {
|
|
|
47
48
|
const flagVal = command[key] as unknown as Exclude<Primitive, Error>;
|
|
48
49
|
|
|
49
50
|
let aliases = flag.flagNames ?? [];
|
|
50
|
-
if (flag
|
|
51
|
+
if (isBoolFlag(flag)) {
|
|
51
52
|
if (flagVal === true) {
|
|
52
53
|
aliases = (flag.flagNames ?? []).filter(x => !/^[-][^-]/.test(x));
|
|
53
54
|
} else {
|
|
@@ -55,7 +56,7 @@ export class HelpUtil {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
const param = [cliTpl`${{ param: aliases.join(', ') }}`];
|
|
58
|
-
if (!(flag
|
|
59
|
+
if (!isBoolFlag(flag)) {
|
|
59
60
|
const type = flag.type === 'string' && flag.choices && flag.choices.length <= 3 ? flag.choices?.join('|') : flag.type;
|
|
60
61
|
param.push(cliTpl`${{ type: `<${type}>` }}`);
|
|
61
62
|
}
|
package/src/parse.ts
CHANGED
|
@@ -12,22 +12,8 @@ const LONG_FLAG_WITH_EQ = /^--[a-z][^= ]+=\S+/i;
|
|
|
12
12
|
const CONFIG_PRE = '+=';
|
|
13
13
|
const ENV_PRE = 'env.';
|
|
14
14
|
const SPACE = new Set([32, 7, 13, 10]);
|
|
15
|
-
const MODULE_FLAG = '--module';
|
|
16
|
-
const MODULE_SHORT = '-m';
|
|
17
|
-
|
|
18
|
-
const getModuleValue = (arr: string[]): string => {
|
|
19
|
-
for (let i = 0; i < arr.length; i += 1) {
|
|
20
|
-
const x = arr[i];
|
|
21
|
-
if (x.startsWith(`${MODULE_FLAG}=`)) {
|
|
22
|
-
return x.split('=')[1];
|
|
23
|
-
} else if (!x.startsWith('-') && (arr[i - 1] === MODULE_SHORT || arr[i - 1] === MODULE_FLAG)) {
|
|
24
|
-
return x;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return process.env.TRV_MODULE || RootIndex.mainModuleName;
|
|
28
|
-
};
|
|
29
15
|
|
|
30
|
-
const isBoolFlag = (x?: CliCommandInput): boolean => x?.type === 'boolean' && !x.array;
|
|
16
|
+
export const isBoolFlag = (x?: CliCommandInput): boolean => x?.type === 'boolean' && !x.array;
|
|
31
17
|
|
|
32
18
|
const getInput = (cfg: { field?: CliCommandInput, rawText?: string, input: string, index?: number, value?: string }): ParsedInput => {
|
|
33
19
|
const { field, input, rawText = input, value, index } = cfg;
|
|
@@ -46,6 +32,7 @@ const getInput = (cfg: { field?: CliCommandInput, rawText?: string, input: strin
|
|
|
46
32
|
}
|
|
47
33
|
};
|
|
48
34
|
|
|
35
|
+
|
|
49
36
|
/**
|
|
50
37
|
* Parsing support for the cli
|
|
51
38
|
*/
|
|
@@ -130,7 +117,7 @@ export class CliParseUtil {
|
|
|
130
117
|
* Parse args to extract command from argv along with other params. Will skip
|
|
131
118
|
* argv[0] and argv[1] if equal to process.argv[0:2]
|
|
132
119
|
*/
|
|
133
|
-
static
|
|
120
|
+
static getArgs(argv: string[]): { cmd?: string, args: string[], help?: boolean } {
|
|
134
121
|
let offset = 0;
|
|
135
122
|
if (argv[0] === process.argv[0] && argv[1] === process.argv[1]) {
|
|
136
123
|
offset = 2;
|
|
@@ -138,22 +125,30 @@ export class CliParseUtil {
|
|
|
138
125
|
const out = argv.slice(offset);
|
|
139
126
|
const max = out.includes(RAW_SEP) ? out.indexOf(RAW_SEP) : out.length;
|
|
140
127
|
const valid = out.slice(0, max);
|
|
141
|
-
const mod = getModuleValue(valid);
|
|
142
128
|
const cmd = valid.length > 0 && !valid[0].startsWith('-') ? valid[0] : undefined;
|
|
143
129
|
const helpIdx = valid.findIndex(x => HELP_FLAG.test(x));
|
|
144
|
-
const args =
|
|
145
|
-
for (const item of out.slice(cmd ? 1 : 0)) {
|
|
146
|
-
if (item.startsWith(CONFIG_PRE)) {
|
|
147
|
-
args.push(...await this.readFlagFile(item, mod));
|
|
148
|
-
} else {
|
|
149
|
-
args.push(item);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
args.push(...out.slice(max));
|
|
130
|
+
const args = out.slice(cmd ? 1 : 0);
|
|
153
131
|
const res = { cmd, args, help: helpIdx >= 0 };
|
|
154
132
|
return res;
|
|
155
133
|
}
|
|
156
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Expand flag arguments into full argument list
|
|
137
|
+
*/
|
|
138
|
+
static async expandArgs(schema: CliCommandSchema, args: string[]): Promise<string[]> {
|
|
139
|
+
const SEP = args.includes(RAW_SEP) ? args.indexOf(RAW_SEP) : args.length;
|
|
140
|
+
const input = schema.flags.find(x => x.type === 'module');
|
|
141
|
+
const ENV_KEY = input?.flagNames?.filter(x => x.startsWith(ENV_PRE)).map(x => x.replace(ENV_PRE, ''))[0] ?? '';
|
|
142
|
+
const flags = new Set(input?.flagNames ?? []);
|
|
143
|
+
const check = (k?: string, v?: string): string | undefined => flags.has(k!) ? v : undefined;
|
|
144
|
+
const mod = args.reduce(
|
|
145
|
+
(m, x, i, arr) =>
|
|
146
|
+
(i < SEP ? check(arr[i - 1], x) ?? check(...x.split('=')) : undefined) ?? m,
|
|
147
|
+
process.env[ENV_KEY] || RootIndex.mainModuleName
|
|
148
|
+
);
|
|
149
|
+
return (await Promise.all(args.map((x, i) => x.startsWith(CONFIG_PRE) && (i < SEP || SEP < 0) ? this.readFlagFile(x, mod) : x))).flat();
|
|
150
|
+
}
|
|
151
|
+
|
|
157
152
|
/**
|
|
158
153
|
* Parse inputs to command
|
|
159
154
|
*/
|
package/src/schema.ts
CHANGED
|
@@ -2,35 +2,45 @@ import { Class, ConsoleManager, GlobalEnv } from '@travetto/base';
|
|
|
2
2
|
import { BindUtil, FieldConfig, SchemaRegistry, SchemaValidator, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
import { CliCommandRegistry } from './registry';
|
|
5
|
-
import { CliCommandInput, CliCommandSchema, CliCommandShape } from './types';
|
|
5
|
+
import { ParsedState, CliCommandInput, CliCommandSchema, CliCommandShape } from './types';
|
|
6
6
|
import { CliValidationResultError } from './error';
|
|
7
|
-
import { ParsedState } from './parse';
|
|
8
7
|
|
|
9
8
|
const LONG_FLAG = /^--[a-z][^= ]+/i;
|
|
10
9
|
const SHORT_FLAG = /^-[a-z]/i;
|
|
11
10
|
|
|
12
11
|
const isBoolFlag = (x?: CliCommandInput): boolean => x?.type === 'boolean' && !x.array;
|
|
13
12
|
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
13
|
+
function baseType(x: FieldConfig): Pick<CliCommandInput, 'type' | 'fileExtensions'> {
|
|
14
|
+
switch (x.type) {
|
|
15
|
+
case Date: return { type: 'date' };
|
|
16
|
+
case Boolean: return { type: 'boolean' };
|
|
17
|
+
case Number: return { type: 'number' };
|
|
18
|
+
case RegExp: return { type: 'regex' };
|
|
19
|
+
case String: {
|
|
20
|
+
switch (true) {
|
|
21
|
+
case x.specifiers?.includes('module'): return { type: 'module' };
|
|
22
|
+
case x.specifiers?.includes('file'): return {
|
|
23
|
+
type: 'file',
|
|
24
|
+
fileExtensions: x.specifiers?.map(s => s.split('ext:')[1]).filter(s => !!s)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { type: 'string' };
|
|
32
30
|
}
|
|
33
31
|
|
|
32
|
+
const fieldToInput = (x: FieldConfig): CliCommandInput => ({
|
|
33
|
+
...baseType(x),
|
|
34
|
+
name: x.name,
|
|
35
|
+
description: x.description,
|
|
36
|
+
array: x.array,
|
|
37
|
+
required: x.required?.active,
|
|
38
|
+
choices: x.enum?.values,
|
|
39
|
+
default: Array.isArray(x.default) ? x.default.slice(0) : x.default,
|
|
40
|
+
flagNames: (x.aliases ?? []).slice(0).filter(v => !v.startsWith('env.')),
|
|
41
|
+
envVars: (x.aliases ?? []).slice(0).filter(v => v.startsWith('env.')).map(v => v.replace('env.', ''))
|
|
42
|
+
});
|
|
43
|
+
|
|
34
44
|
/**
|
|
35
45
|
* Allows binding describing/binding inputs for commands
|
|
36
46
|
*/
|
package/src/types.ts
CHANGED
|
@@ -100,7 +100,7 @@ export type CliCommandShapeFields = {
|
|
|
100
100
|
export type CliCommandInput = {
|
|
101
101
|
name: string;
|
|
102
102
|
description?: string;
|
|
103
|
-
type: 'string' | 'file' | 'number' | 'boolean' | 'date' | 'regex';
|
|
103
|
+
type: 'string' | 'file' | 'number' | 'boolean' | 'date' | 'regex' | 'module';
|
|
104
104
|
fileExtensions?: string[];
|
|
105
105
|
choices?: unknown[];
|
|
106
106
|
required?: boolean;
|