@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/cli",
3
- "version": "3.4.9",
3
+ "version": "3.4.10",
4
4
  "description": "CLI infrastructure for Travetto framework",
5
5
  "keywords": [
6
6
  "cli",
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 && 'env' in cmd && typeof cmd.env === 'string') {
30
- defineEnv({ envName: cmd.env });
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 } = await CliParseUtil.getArgs(argv);
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.type === 'boolean' && !flag.array) {
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.type === 'boolean' && !flag.array)) {
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 async getArgs(argv: string[]): Promise<{ cmd?: string, args: string[], help?: boolean }> {
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 fieldToInput(x: FieldConfig): CliCommandInput {
15
- const type = x.type === Date ? 'date' :
16
- x.type === Boolean ? 'boolean' :
17
- x.type === String ? (x.specifiers?.includes('file') ? 'file' : 'string') :
18
- x.type === Number ? 'number' :
19
- x.type === RegExp ? 'regex' : 'string';
20
- return ({
21
- name: x.name,
22
- description: x.description,
23
- array: x.array,
24
- required: x.required?.active,
25
- choices: x.enum?.values,
26
- fileExtensions: type === 'file' ? x.specifiers?.filter(s => s.startsWith('ext:')).map(s => s.split('ext:')[1]) : undefined,
27
- type,
28
- default: Array.isArray(x.default) ? x.default.slice(0) : x.default,
29
- flagNames: (x.aliases ?? []).slice(0).filter(v => !v.startsWith('env.')),
30
- envVars: (x.aliases ?? []).slice(0).filter(v => v.startsWith('env.')).map(v => v.replace('env.', ''))
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;