@travetto/cli 8.0.0-alpha.8 → 8.0.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.
package/src/help.ts CHANGED
@@ -1,72 +1,73 @@
1
1
  import util from 'node:util';
2
2
 
3
- import { castKey, getClass, JSONUtil, Runtime } from '@travetto/runtime';
3
+ import { CodecUtil, castKey, getClass, JSONUtil, Runtime } from '@travetto/runtime';
4
4
  import { SchemaRegistryIndex, ValidationResultError } from '@travetto/schema';
5
5
 
6
6
  import { cliTpl } from './color.ts';
7
- import type { CliCommandShape } from './types.ts';
8
7
  import { CliCommandRegistryIndex, UNKNOWN_COMMAND } from './registry/registry-index.ts';
9
8
  import { CliSchemaExportUtil } from './schema-export.ts';
9
+ import { type CliCommandShape, HELP_FLAG } from './types.ts';
10
10
 
11
- const validationSourceMap: Record<string, string> = {
12
- arg: 'Argument',
13
- flag: 'Flag'
14
- };
11
+ const validationSourceMap: Record<string, string> = { arg: 'Argument', flag: 'Flag' };
15
12
 
16
13
  const ifDefined = <T>(value: T | null | '' | undefined): T | undefined =>
17
- (value === null || value === '' || value === undefined) ? undefined : value;
18
-
19
- const toItem = (name: string, pkg: string, prod?: boolean) => [name, Runtime.getInstallCommand(pkg, prod)] as const;
20
-
21
- const INSTALL_COMMANDS = new Map<string, string>([
22
- ...['test', 'test:watch', 'test:direct'].map(item => toItem(item, '@travetto/test')),
23
- ...['lint', 'lint:register', 'eslint', 'eslint:register'].map(item => toItem(item, '@travetto/eslint')),
24
- ...['model:install', 'model:export'].map(item => toItem(item, '@travetto/model', true)),
25
- ...['openapi:spec', 'openapi:client'].map(item => toItem(item, '@travetto/openapi', true)),
26
- ...['email:compile', 'email:test', 'email:editor'].map(item => toItem(item, '@travetto/email-compiler')),
27
- ...['pack', 'pack:zip', 'pack:docker'].map(item => toItem(item, '@travetto/pack')),
28
- ...['repo:publish', 'repo:version', 'repo:exec', 'repo:list', 'repo:version-sync'].map(item => toItem(item, '@travetto/repo')),
29
- toItem('web:http', '@travetto/web-http', true),
30
- toItem('doc', '@travetto/doc'),
31
- toItem('web:rpc-client', '@travetto/web-rpc', true),
32
- ]);
14
+ value === null || value === '' || value === undefined ? undefined : value;
15
+
16
+ const MODULE_TO_COMMAND = {
17
+ '@travetto/doc': ['doc'],
18
+ '@travetto/email-compiler': ['email:compile', 'email:test', 'email:editor'],
19
+ '@travetto/lint': ['lint', 'lint:register'],
20
+ '@travetto/model': ['model:install', 'model:export'],
21
+ '@travetto/openapi': ['openapi:spec', 'openapi:client'],
22
+ '@travetto/pack': ['pack', 'pack:zip', 'pack:docker'],
23
+ '@travetto/repo': ['repo:publish', 'repo:version', 'repo:exec', 'repo:list'],
24
+ '@travetto/test': ['test', 'test:watch', 'test:direct'],
25
+ '@travetto/web-http': ['web:http'],
26
+ '@travetto/web-rpc': ['web:rpc-client']
27
+ };
28
+
29
+ const COMMAND_TO_MODULE = Object.fromEntries(Object.entries(MODULE_TO_COMMAND).flatMap(([k, v]) => v.map(sv => [sv, k])));
33
30
 
34
31
  /**
35
32
  * Utilities for showing help
36
33
  */
37
34
  export class HelpUtil {
38
-
39
- /** Render the unknown command message */
40
- static renderUnknownCommandMessage(cmd: string): string {
41
- const install = INSTALL_COMMANDS.get(cmd);
42
- if (install) {
43
- return cliTpl`
44
- ${{ title: 'Missing Package' }}\n${'-'.repeat(20)}\nTo use ${{ input: cmd }} please run:\n
45
- ${{ identifier: install }}
46
- `;
47
- } else {
48
- return cliTpl`${{ subtitle: 'Unknown command' }}: ${{ input: cmd }}`;
49
- }
50
- }
51
-
52
- /**
53
- * Render command-specific help
54
- * @param command
55
- */
56
- static async renderCommandHelp(command: CliCommandShape): Promise<string> {
35
+ /** Get usage help for a command */
36
+ static getUsageMessage(command: CliCommandShape): string[] {
57
37
  const schema = SchemaRegistryIndex.getConfig(getClass(command));
58
38
  const { name: commandName } = CliCommandRegistryIndex.get(getClass(command));
59
- const args = schema.methods.main?.parameters ?? [];
60
39
 
61
- // Ensure finalized
40
+ const usage: string[] = [];
41
+
42
+ usage.push(cliTpl`${{ title: 'Usage:' }} ${{ param: commandName }} ${{ input: '[options]' }}`);
62
43
 
63
- const usage: string[] = [cliTpl`${{ title: 'Usage:' }} ${{ param: commandName }} ${{ input: '[options]' }}`,];
64
- for (const field of args) {
65
- const type = field.type === String && field.enum && field.enum?.values.length <= 7 ? field.enum?.values?.join('|') : field.type.name.toLowerCase();
44
+ // Ensure finalized
45
+ for (const field of schema.methods.main?.parameters ?? []) {
46
+ const type =
47
+ field.type === String && field.enum && field.enum?.values.length <= 7
48
+ ? field.enum?.values?.join('|')
49
+ : field.type.name.toLowerCase();
66
50
  const arg = `${field.name}${field.array ? '...' : ''}:${type}`;
67
51
  usage.push(cliTpl`${{ input: field.required?.active !== false ? `<${arg}>` : `[${arg}]` }}`);
68
52
  }
69
53
 
54
+ return [usage.join(' '), ''];
55
+ }
56
+
57
+ /** Get description help for a command */
58
+ static getDescriptionMessage(command: CliCommandShape): string[] {
59
+ const schema = SchemaRegistryIndex.getConfig(getClass(command));
60
+ const description: string[] = [];
61
+
62
+ if (schema.description) {
63
+ description.push(cliTpl`${{ title: 'Description:' }}`, ...schema.description.split('\n').map(line => ` ${line}`), '');
64
+ }
65
+ return description;
66
+ }
67
+
68
+ /** Get options help for a command */
69
+ static getOptionsMessage(command: CliCommandShape): string[] {
70
+ const schema = SchemaRegistryIndex.getConfig(getClass(command));
70
71
  const params: string[] = [];
71
72
  const descriptions: string[] = [];
72
73
 
@@ -75,9 +76,7 @@ ${{ identifier: install }}
75
76
  const defaultValue = ifDefined(command[key]) ?? ifDefined(field.default);
76
77
  const aliases = (field.aliases ?? [])
77
78
  .filter(flag => flag.startsWith('-'))
78
- .filter(flag =>
79
- (field.type !== Boolean) || ((defaultValue !== true || field.name === 'help') ? !flag.startsWith('--no-') : flag.startsWith('--'))
80
- );
79
+ .filter(flag => field.type !== Boolean || (defaultValue !== true ? !flag.startsWith('--no-') : flag.startsWith('--')));
81
80
  let type: string | undefined;
82
81
 
83
82
  if (field.type === String && field.enum && field.enum.values.length <= 3) {
@@ -86,41 +85,93 @@ ${{ identifier: install }}
86
85
  ({ type } = CliSchemaExportUtil.baseInputType(field));
87
86
  }
88
87
 
89
- const param = [
90
- cliTpl`${{ param: aliases.join(', ') }}`,
91
- ...(type ? [cliTpl`${{ type: `<${type}>` }}`] : []),
92
- ];
88
+ const parameter = [cliTpl`${{ param: aliases.join(', ') }}`, ...(type ? [cliTpl`${{ type: `<${type}>` }}`] : [])];
93
89
 
94
- params.push(param.join(' '));
95
- const desc = [cliTpl`${{ title: field.description }}`];
90
+ params.push(parameter.join(' '));
91
+ const parts = [cliTpl`${{ title: field.description }}`];
96
92
 
97
- if (key !== 'help' && defaultValue !== undefined) {
98
- desc.push(cliTpl`(default: ${{ input: JSONUtil.toUTF8(defaultValue) }})`);
93
+ if (defaultValue !== undefined) {
94
+ parts.push(cliTpl`(default: ${{ input: JSONUtil.toUTF8(defaultValue) }})`);
99
95
  }
100
- descriptions.push(desc.join(' '));
96
+ descriptions.push(parts.join(' '));
101
97
  }
102
98
 
99
+ params.push(cliTpl`${{ param: HELP_FLAG }}`);
100
+ descriptions.push('display help for command');
101
+
103
102
  const paramWidths = params.map(item => util.stripVTControlCharacters(item).length);
104
103
  const descWidths = descriptions.map(item => util.stripVTControlCharacters(item).length);
105
104
 
106
105
  const paramWidth = Math.max(...paramWidths);
107
106
  const descWidth = Math.max(...descWidths);
108
107
 
109
- const helpText = await (command.help?.() ?? []);
110
- if (helpText.length && helpText.at(-1) !== '') {
111
- helpText.push('');
108
+ const options: string[] = [
109
+ cliTpl`${{ title: 'Options:' }}`,
110
+ ...params.map(
111
+ (_, i) =>
112
+ ` ${params[i]}${' '.repeat(paramWidth - paramWidths[i])} ${descriptions[i].padEnd(descWidth)}${' '.repeat(descWidth - descWidths[i])}`
113
+ ),
114
+ ''
115
+ ];
116
+ return options;
117
+ }
118
+
119
+ /** Get extended help for a command */
120
+ static async getExtendedHelpMessage(command: CliCommandShape): Promise<string[]> {
121
+ const extendedHelpText = await (command.help?.() ?? []);
122
+ if (extendedHelpText.length && extendedHelpText.at(-1) !== '') {
123
+ extendedHelpText.push('');
124
+ }
125
+ return extendedHelpText;
126
+ }
127
+
128
+ /** Get examples for a command */
129
+ static getExamplesMessage(command: CliCommandShape): string[] {
130
+ const schema = SchemaRegistryIndex.getConfig(getClass(command));
131
+ const examples: string[] = [];
132
+ if (schema.examples) {
133
+ examples.push(cliTpl`${{ title: 'Examples:' }}`);
134
+ for (const example of schema.examples) {
135
+ for (const line of example.split('\n')) {
136
+ examples.push(
137
+ line.trim().startsWith('>')
138
+ ? cliTpl` ${{ input: line.substring(line.indexOf('> ') + 2).trim() }}`
139
+ : cliTpl` ${{ subtitle: line.trim() }}`
140
+ );
141
+ }
142
+ }
143
+ examples.push('');
144
+ }
145
+ return examples;
146
+ }
147
+
148
+ /** Render the unknown command message */
149
+ static renderUnknownCommandMessage(command: string): string {
150
+ const module = COMMAND_TO_MODULE[command];
151
+ if (module) {
152
+ return cliTpl`
153
+ ${{ title: 'Missing Package' }}\n${'-'.repeat(20)}\nTo use ${{ input: command }} please run:\n
154
+ ${{ identifier: Runtime.getInstallCommand(module) }}
155
+ `;
156
+ } else {
157
+ return cliTpl`${{ subtitle: 'Unknown command' }}: ${{ input: command }}`;
112
158
  }
159
+ }
113
160
 
161
+ /**
162
+ * Render command-specific help
163
+ * @param command
164
+ */
165
+ static async renderCommandHelp(command: CliCommandShape): Promise<string> {
114
166
  return [
115
- usage.join(' '),
116
- '',
117
- cliTpl`${{ title: 'Options:' }}`,
118
- ...params.map((_, i) =>
119
- ` ${params[i]}${' '.repeat((paramWidth - paramWidths[i]))} ${descriptions[i].padEnd(descWidth)}${' '.repeat((descWidth - descWidths[i]))}`
120
- ),
121
- '',
122
- ...helpText
123
- ].map(line => line.trimEnd()).join('\n');
167
+ ...this.getUsageMessage(command),
168
+ ...this.getDescriptionMessage(command),
169
+ ...this.getOptionsMessage(command),
170
+ ...(await this.getExtendedHelpMessage(command)),
171
+ ...this.getExamplesMessage(command)
172
+ ]
173
+ .map(line => line.trimEnd())
174
+ .join('\n');
124
175
  }
125
176
 
126
177
  /**
@@ -136,11 +187,13 @@ ${{ identifier: install }}
136
187
  for (const { command: cmd, schema } of resolved) {
137
188
  try {
138
189
  if (schema && !schema.private) {
139
- rows.push(cliTpl` ${{ param: cmd.padEnd(maxWidth, ' ') }} ${{ title: schema.description || '' }}`);
190
+ const description = CodecUtil.readFirstLine(schema.description, '');
191
+ rows.push(cliTpl` ${{ param: cmd.padEnd(maxWidth, ' ') }} ${{ title: description }}`);
140
192
  }
141
193
  } catch (error) {
142
194
  if (error instanceof Error) {
143
- rows.push(cliTpl` ${{ param: cmd.padEnd(maxWidth, ' ') }} ${{ failure: error.message.split(/\n/)[0] }}`);
195
+ const failure = CodecUtil.readFirstLine(error.message);
196
+ rows.push(cliTpl` ${{ param: cmd.padEnd(maxWidth, ' ') }} ${{ failure }}`);
144
197
  } else {
145
198
  throw error;
146
199
  }
@@ -166,7 +219,7 @@ ${{ identifier: install }}
166
219
  }
167
220
  return cliTpl` * ${{ failure: error.message }}`;
168
221
  }),
169
- '',
222
+ ''
170
223
  ].join('\n');
171
224
  }
172
225
 
@@ -185,4 +238,4 @@ ${{ identifier: install }}
185
238
  }
186
239
  console.error!();
187
240
  }
188
- }
241
+ }
package/src/module.ts CHANGED
@@ -1,15 +1,14 @@
1
- import { Runtime, RuntimeIndex } from '@travetto/runtime';
2
1
  import type { IndexedModule } from '@travetto/manifest';
2
+ import { Runtime, RuntimeIndex } from '@travetto/runtime';
3
3
 
4
4
  import { CliScmUtil } from './scm.ts';
5
5
 
6
- type ModuleGraphEntry = { children: Set<string>, name: string, active: Set<string>, parents?: string[] };
6
+ type ModuleGraphEntry = { children: Set<string>; name: string; active: Set<string>; parents?: string[] };
7
7
 
8
8
  /**
9
9
  * Simple utilities for understanding modules for CLI use cases
10
10
  */
11
11
  export class CliModuleUtil {
12
-
13
12
  /**
14
13
  * Find modules that changed, and the dependent modules
15
14
  * @param fromHash
@@ -34,8 +33,7 @@ export class CliModuleUtil {
34
33
  }
35
34
  }
36
35
 
37
- return [...out.values()]
38
- .toSorted((a, b) => a.name.localeCompare(b.name));
36
+ return [...out.values()].toSorted((a, b) => a.name.localeCompare(b.name));
39
37
  }
40
38
 
41
39
  /**
@@ -45,9 +43,10 @@ export class CliModuleUtil {
45
43
  * @returns
46
44
  */
47
45
  static async findModules(mode: 'all' | 'changed' | 'workspace', fromHash?: string, toHash?: string): Promise<IndexedModule[]> {
48
- return (mode === 'changed' ?
49
- await this.findChangedModulesRecursive(fromHash, toHash, true) :
50
- [...RuntimeIndex.getModuleList(mode)].map(name => RuntimeIndex.getModule(name)!)
46
+ return (
47
+ mode === 'changed'
48
+ ? await this.findChangedModulesRecursive(fromHash, toHash, true)
49
+ : [...RuntimeIndex.getModuleList(mode)].map(name => RuntimeIndex.getModule(name)!)
51
50
  ).filter(module => module.sourcePath !== Runtime.workspace.path);
52
51
  }
53
52
 
@@ -103,7 +102,7 @@ export class CliModuleUtil {
103
102
  /**
104
103
  * Find changed paths, either files between two git commits, or all folders for changed modules
105
104
  */
106
- static async findChangedPaths(config: { since?: string, changed?: boolean, logError?: boolean } = {}): Promise<string[]> {
105
+ static async findChangedPaths(config: { since?: string; changed?: boolean; logError?: boolean } = {}): Promise<string[]> {
107
106
  if (config.since) {
108
107
  try {
109
108
  const files = await CliScmUtil.findChangedFiles(config.since, 'HEAD');
@@ -119,4 +118,4 @@ export class CliModuleUtil {
119
118
  return modules.map(module => module.sourcePath);
120
119
  }
121
120
  }
122
- }
121
+ }
package/src/parse.ts CHANGED
@@ -4,13 +4,12 @@ import path from 'node:path';
4
4
  import { Runtime } from '@travetto/runtime';
5
5
  import type { SchemaClassConfig, SchemaFieldConfig, SchemaInputConfig } from '@travetto/schema';
6
6
 
7
- import type { ParsedState } from './types.ts';
7
+ import { HELP_FLAG, type ParsedState } from './types.ts';
8
8
 
9
9
  type ParsedInput = ParsedState['all'][number];
10
10
 
11
11
  const RAW_SEPARATOR = '--';
12
12
  const VALID_FLAG = /^-{1,2}[a-z]/i;
13
- const HELP_FLAG = /^(-h|--help)$/;
14
13
  const LONG_FLAG_WITH_EQ = /^--[a-z][^= ]+=\S+/i;
15
14
  const CONFIG_PREFIX = '+=';
16
15
  const SPACE = new Set([32, 7, 13, 10]);
@@ -26,32 +25,35 @@ const STATE_SYMBOL = Symbol();
26
25
  * Parsing support for the cli
27
26
  */
28
27
  export class CliParseUtil {
29
-
30
28
  static toEnvField(key: string): string {
31
29
  return key.startsWith(ENV_PREFIX) ? key : `${ENV_PREFIX}${key}`;
32
30
  }
33
31
 
34
- static readToken(text: string, start = 0): { next: number, value?: string } {
32
+ static readToken(text: string, start = 0): { next: number; value?: string } {
35
33
  const collected: number[] = [];
36
34
  let i = start;
37
35
  let done = false;
38
36
  let quote: number | undefined;
39
37
  let escaped = false;
40
- outer: for (; i < text.length; i += 1) {
38
+ for (; i < text.length; i += 1) {
41
39
  const ch = text.charCodeAt(i);
42
40
  const space = SPACE.has(ch);
43
41
  if (escaped) {
44
42
  escaped = false;
45
43
  collected.push(ch);
46
44
  } else if (done && !space) {
47
- break outer;
45
+ break;
48
46
  } else if (!quote && space) {
49
47
  done = true;
50
48
  } else {
51
49
  switch (ch) {
52
- case 92: /* Backslash */ escaped = true; break;
53
- case 39: /* Single quote */ case 34: /* Double quote */
54
- if (quote === ch) { // End quote
50
+ case 92:
51
+ /* Backslash */ escaped = true;
52
+ break;
53
+ case 39: /* Single quote */
54
+ case 34 /* Double quote */:
55
+ if (quote === ch) {
56
+ // End quote
55
57
  quote = undefined;
56
58
  } else if (!quote) {
57
59
  quote = ch;
@@ -59,7 +61,8 @@ export class CliParseUtil {
59
61
  collected.push(ch);
60
62
  }
61
63
  break;
62
- default: collected.push(ch);
64
+ default:
65
+ collected.push(ch);
63
66
  }
64
67
  }
65
68
  }
@@ -74,10 +77,9 @@ export class CliParseUtil {
74
77
  const input = Object.values(schema.fields).find(config => config.specifiers?.includes('module'));
75
78
  const envKey = input?.aliases?.filter(alias => alias.startsWith(ENV_PREFIX)).map(alias => alias.replace(ENV_PREFIX, ''))[0] ?? '';
76
79
  const flags = new Set(input?.aliases ?? []);
77
- const check = (key?: string, value?: string): string | undefined => flags.has(key!) ? value : undefined;
80
+ const check = (key?: string, value?: string): string | undefined => (flags.has(key!) ? value : undefined);
78
81
  return args.reduce(
79
- (name, value, i, values) =>
80
- (i < separatorIndex ? check(values[i - 1], value) ?? check(...value.split('=')) : undefined) ?? name,
82
+ (name, value, i, values) => (i < separatorIndex ? (check(values[i - 1], value) ?? check(...value.split('='))) : undefined) ?? name,
81
83
  process.env[envKey]
82
84
  );
83
85
  }
@@ -90,18 +92,20 @@ export class CliParseUtil {
90
92
  const overrides = { '@': module ?? Runtime.main.name };
91
93
 
92
94
  // We have a file
93
- const relativePath = (key.includes('/') ? key : `@#support/pack.${key}.flags`)
94
- .replace(/^(@[^#]*)#(.*)$/, (_, imp, rest) => `${Runtime.modulePath(imp, overrides)}/${rest}`);
95
+ const relativePath = (key.includes('/') ? key : `@#support/pack.${key}.flags`).replace(
96
+ /^(@[^#]*)#(.*)$/,
97
+ (_, imp, rest) => `${Runtime.modulePath(imp, overrides)}/${rest}`
98
+ );
95
99
 
96
100
  const file = path.resolve(relativePath);
97
101
 
98
- if (!await fs.stat(file, { throwIfNoEntry: false })) {
102
+ if (!(await fs.stat(file, { throwIfNoEntry: false }))) {
99
103
  throw new Error(`Missing flag file: ${key}, unable to proceed`);
100
104
  }
101
105
 
102
106
  const data = await fs.readFile(file, 'utf8');
103
107
  const args: string[] = [];
104
- let token: { next: number, value?: string } = { next: 0 };
108
+ let token: { next: number; value?: string } = { next: 0 };
105
109
  while (token.next < data.length) {
106
110
  token = this.readToken(data, token.next);
107
111
  if (token.value !== undefined) {
@@ -115,7 +119,7 @@ export class CliParseUtil {
115
119
  * Parse args to extract command from argv along with other params. Will skip
116
120
  * argv[0] and argv[1] if equal to process.argv[0:2]
117
121
  */
118
- static getArgs(argv: string[]): { cmd?: string, args: string[], help?: boolean } {
122
+ static getArgs(argv: string[]): { cmd?: string; args: string[]; help?: boolean } {
119
123
  let offset = 0;
120
124
  if (argv[0] === process.argv[0] && argv[1] === process.argv[1]) {
121
125
  offset = 2;
@@ -124,9 +128,9 @@ export class CliParseUtil {
124
128
  const max = out.includes(RAW_SEPARATOR) ? out.indexOf(RAW_SEPARATOR) : out.length;
125
129
  const valid = out.slice(0, max);
126
130
  const cmd = valid.length > 0 && !valid[0].startsWith('-') ? valid[0] : undefined;
127
- const helpIdx = valid.findIndex(flag => HELP_FLAG.test(flag));
131
+ const help = valid.includes(HELP_FLAG);
128
132
  const args = out.slice(cmd ? 1 : 0);
129
- const result = { cmd, args, help: helpIdx >= 0 };
133
+ const result = { cmd, args, help };
130
134
  return result;
131
135
  }
132
136
 
@@ -136,11 +140,12 @@ export class CliParseUtil {
136
140
  static async expandArgs(schema: SchemaClassConfig, args: string[]): Promise<string[]> {
137
141
  const separatorIndex = args.includes(RAW_SEPARATOR) ? args.indexOf(RAW_SEPARATOR) : args.length;
138
142
  const module = this.getSpecifiedModule(schema, args);
139
- return Promise
140
- .all(args.map(async (arg, i) =>
141
- await (arg.startsWith(CONFIG_PREFIX) && (i < separatorIndex || separatorIndex < 0) ? this.readFlagFile(arg, module) : arg))
143
+ return Promise.all(
144
+ args.map(
145
+ async (arg, i) =>
146
+ await (arg.startsWith(CONFIG_PREFIX) && (i < separatorIndex || separatorIndex < 0) ? this.readFlagFile(arg, module) : arg)
142
147
  )
143
- .then(expanded => expanded.flat());
148
+ ).then(expanded => expanded.flat());
144
149
  }
145
150
 
146
151
  /**
@@ -160,7 +165,9 @@ export class CliParseUtil {
160
165
  if (simple in process.env) {
161
166
  const value: string = process.env[simple]!;
162
167
  if (field.array) {
163
- out.push(...value.split(/\s*,\s*/g).map(item => ({ type: 'flag', fieldName: field.name, input: envName, value: item }) as const));
168
+ out.push(
169
+ ...value.split(/\s*,\s*/g).map(item => ({ type: 'flag', fieldName: field.name, input: envName, value: item }) as const)
170
+ );
164
171
  } else {
165
172
  out.push({ type: 'flag', fieldName: field.name, input: envName, value });
166
173
  }
@@ -173,7 +180,8 @@ export class CliParseUtil {
173
180
  for (let i = 0; i < inputs.length; i += 1) {
174
181
  const input = inputs[i];
175
182
 
176
- if (input === RAW_SEPARATOR) { // Raw separator
183
+ if (input === RAW_SEPARATOR) {
184
+ // Raw separator
177
185
  out.push(...inputs.slice(i + 1).map((arg, idx) => ({ type: 'unknown', input: arg, index: argIdx + idx }) as const));
178
186
  break;
179
187
  } else if (LONG_FLAG_WITH_EQ.test(input)) {
@@ -184,7 +192,8 @@ export class CliParseUtil {
184
192
  } else {
185
193
  out.push({ type: 'unknown', input });
186
194
  }
187
- } else if (VALID_FLAG.test(input)) { // Flag
195
+ } else if (VALID_FLAG.test(input)) {
196
+ // Flag
188
197
  const field = flagMap.get(input);
189
198
  if (!field) {
190
199
  out.push({ type: 'unknown', input });
@@ -225,27 +234,33 @@ export class CliParseUtil {
225
234
  * Parse aliases into categories for registration
226
235
  */
227
236
  static parseAliases(aliases: string[]): AliasesParseResult {
228
- return aliases.reduce<AliasesParseResult>((result, alias) => {
229
- if (VALID_FLAG.test(alias)) {
230
- if (alias.startsWith('--')) {
231
- result.long.push(alias);
237
+ return aliases.reduce<AliasesParseResult>(
238
+ (result, alias) => {
239
+ if (VALID_FLAG.test(alias)) {
240
+ if (alias.startsWith('--')) {
241
+ result.long.push(alias);
242
+ } else {
243
+ result.short.push(alias);
244
+ }
245
+ } else if (alias.startsWith(ENV_PREFIX)) {
246
+ result.env.push(alias);
232
247
  } else {
233
- result.short.push(alias);
248
+ result.raw.push(alias);
234
249
  }
235
- } else if (alias.startsWith(ENV_PREFIX)) {
236
- result.env.push(alias);
237
- } else {
238
- result.raw.push(alias);
239
- }
240
- return result;
241
- }, { long: [], short: [], raw: [], env: [] });
250
+ return result;
251
+ },
252
+ { long: [], short: [], raw: [], env: [] }
253
+ );
242
254
  }
243
255
 
244
256
  /**
245
257
  * Build aliases for a schema config
246
258
  */
247
- static buildAliases(config: { full?: string, short?: string, envVars?: string[] }, ...extraEnvVars: string[]): Partial<SchemaFieldConfig> {
248
- const envVars = [...config.envVars ?? [], ...extraEnvVars];
259
+ static buildAliases(
260
+ config: { full?: string; short?: string; envVars?: string[] },
261
+ ...extraEnvVars: string[]
262
+ ): Partial<SchemaFieldConfig> {
263
+ const envVars = [...(config.envVars ?? []), ...extraEnvVars];
249
264
  return {
250
265
  aliases: [
251
266
  ...(config.full ? [config.full.startsWith('-') ? config.full : `--${config.full}`] : []),
@@ -270,4 +285,4 @@ export class CliParseUtil {
270
285
  const local: T & { [STATE_SYMBOL]?: ParsedState } = item;
271
286
  local[STATE_SYMBOL] = state;
272
287
  }
273
- }
288
+ }