@oclif/core 3.0.0-beta.2 → 3.0.0-beta.21
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 +10 -6
- package/flush.js +1 -1
- package/handle.js +1 -1
- package/lib/args.d.ts +1 -1
- package/lib/args.js +17 -18
- package/lib/cli-ux/action/base.d.ts +3 -5
- package/lib/cli-ux/action/base.js +32 -26
- package/lib/cli-ux/action/simple.js +13 -18
- package/lib/cli-ux/action/spinner.d.ts +4 -2
- package/lib/cli-ux/action/spinner.js +27 -19
- package/lib/cli-ux/action/spinners.js +1 -1
- package/lib/cli-ux/action/types.d.ts +5 -0
- package/lib/cli-ux/action/types.js +2 -0
- package/lib/cli-ux/config.d.ts +0 -1
- package/lib/cli-ux/config.js +17 -21
- package/lib/cli-ux/exit.d.ts +1 -1
- package/lib/cli-ux/exit.js +4 -1
- package/lib/cli-ux/flush.d.ts +1 -0
- package/lib/cli-ux/flush.js +28 -0
- package/lib/cli-ux/index.d.ts +10 -30
- package/lib/cli-ux/index.js +32 -75
- package/lib/cli-ux/list.js +3 -3
- package/lib/cli-ux/prompt.js +32 -22
- package/lib/cli-ux/stream.js +1 -0
- package/lib/cli-ux/styled/index.d.ts +5 -6
- package/lib/cli-ux/styled/index.js +11 -11
- package/lib/cli-ux/styled/json.js +8 -5
- package/lib/cli-ux/styled/object.js +7 -9
- package/lib/cli-ux/styled/table.d.ts +4 -4
- package/lib/cli-ux/styled/table.js +61 -64
- package/lib/cli-ux/styled/tree.js +1 -3
- package/lib/cli-ux/wait.js +3 -5
- package/lib/command.d.ts +15 -19
- package/lib/command.js +117 -96
- package/lib/config/config.d.ts +16 -23
- package/lib/config/config.js +180 -334
- package/lib/config/index.d.ts +1 -1
- package/lib/config/index.js +1 -2
- package/lib/config/plugin-loader.d.ts +30 -0
- package/lib/config/plugin-loader.js +145 -0
- package/lib/config/plugin.d.ts +6 -11
- package/lib/config/plugin.js +112 -78
- package/lib/config/ts-node.d.ts +2 -1
- package/lib/config/ts-node.js +64 -51
- package/lib/config/util.d.ts +1 -11
- package/lib/config/util.js +6 -59
- package/lib/errors/config.js +1 -1
- package/lib/errors/errors/cli.d.ts +1 -1
- package/lib/errors/errors/cli.js +18 -14
- package/lib/errors/errors/exit.d.ts +0 -3
- package/lib/errors/errors/exit.js +1 -1
- package/lib/errors/errors/module-load.d.ts +0 -3
- package/lib/errors/errors/module-load.js +1 -1
- package/lib/errors/errors/pretty-print.js +11 -9
- package/lib/errors/handle.d.ts +12 -2
- package/lib/errors/handle.js +28 -18
- package/lib/errors/index.d.ts +2 -2
- package/lib/errors/index.js +20 -19
- package/lib/errors/logger.js +9 -8
- package/lib/execute.d.ts +49 -0
- package/lib/execute.js +63 -0
- package/lib/flags.d.ts +102 -31
- package/lib/flags.js +81 -46
- package/lib/help/command.d.ts +2 -0
- package/lib/help/command.js +68 -53
- package/lib/help/docopts.js +9 -13
- package/lib/help/formatter.d.ts +1 -1
- package/lib/help/formatter.js +35 -24
- package/lib/help/index.d.ts +7 -3
- package/lib/help/index.js +77 -55
- package/lib/help/root.js +7 -9
- package/lib/help/util.d.ts +1 -7
- package/lib/help/util.js +8 -28
- package/lib/index.d.ts +19 -18
- package/lib/index.js +36 -48
- package/lib/interfaces/config.d.ts +30 -30
- package/lib/interfaces/errors.d.ts +1 -1
- package/lib/interfaces/hooks.d.ts +3 -3
- package/lib/interfaces/index.d.ts +14 -14
- package/lib/interfaces/parser.d.ts +188 -116
- package/lib/interfaces/pjson.d.ts +2 -1
- package/lib/interfaces/plugin.d.ts +10 -1
- package/lib/main.d.ts +0 -48
- package/lib/main.js +11 -66
- package/lib/module-loader.d.ts +68 -79
- package/lib/module-loader.js +183 -150
- package/lib/parser/errors.d.ts +3 -3
- package/lib/parser/errors.js +17 -10
- package/lib/parser/help.js +5 -5
- package/lib/parser/parse.d.ts +3 -0
- package/lib/parser/parse.js +114 -115
- package/lib/parser/validate.js +45 -25
- package/lib/performance.d.ts +5 -1
- package/lib/performance.js +40 -19
- package/lib/util/aggregate-flags.d.ts +2 -0
- package/lib/util/aggregate-flags.js +13 -0
- package/lib/util/cache-command.d.ts +3 -0
- package/lib/util/cache-command.js +109 -0
- package/lib/util/cache-default-value.d.ts +2 -0
- package/lib/util/cache-default-value.js +28 -0
- package/lib/util/ensure-arg-object.d.ts +12 -0
- package/lib/util/ensure-arg-object.js +14 -0
- package/lib/util/fs.d.ts +7 -0
- package/lib/util/fs.js +54 -0
- package/lib/util/os.d.ts +19 -0
- package/lib/util/os.js +28 -0
- package/lib/{util.d.ts → util/util.d.ts} +6 -15
- package/lib/util/util.js +98 -0
- package/package.json +32 -34
- package/lib/cli-ux/action/pride-spinner.d.ts +0 -4
- package/lib/cli-ux/action/pride-spinner.js +0 -30
- package/lib/util.js +0 -126
package/lib/parser/parse.d.ts
CHANGED
package/lib/parser/parse.js
CHANGED
|
@@ -3,43 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Parser = void 0;
|
|
4
4
|
/* eslint-disable no-await-in-loop */
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const util_1 = require("../util/util");
|
|
7
|
+
const node_readline_1 = require("node:readline");
|
|
8
8
|
let debug;
|
|
9
9
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
debug =
|
|
11
|
+
process.env.CLI_FLAGS_DEBUG === '1'
|
|
12
|
+
? require('debug')('../parser')
|
|
13
|
+
: () => {
|
|
14
|
+
// noop
|
|
15
|
+
};
|
|
12
16
|
}
|
|
13
17
|
catch {
|
|
14
|
-
debug = () => {
|
|
18
|
+
debug = () => {
|
|
19
|
+
// noop
|
|
20
|
+
};
|
|
15
21
|
}
|
|
16
|
-
|
|
17
|
-
* Support reading from stdin in Node 14 and older.
|
|
18
|
-
*
|
|
19
|
-
* This generally works for Node 14 and older EXCEPT when it's being
|
|
20
|
-
* run from another process, in which case it will hang indefinitely. Because
|
|
21
|
-
* of that issue we updated this to use AbortController but since AbortController
|
|
22
|
-
* is only available in Node 16 and newer, we have to keep this legacy version.
|
|
23
|
-
*
|
|
24
|
-
* See these issues for more details on the hanging indefinitely bug:
|
|
25
|
-
* https://github.com/oclif/core/issues/330
|
|
26
|
-
* https://github.com/oclif/core/pull/363
|
|
27
|
-
*
|
|
28
|
-
* @returns Promise<string | null>
|
|
29
|
-
*/
|
|
30
|
-
const readStdinLegacy = async () => {
|
|
31
|
-
const { stdin } = process;
|
|
32
|
-
let result;
|
|
33
|
-
if (stdin.isTTY)
|
|
34
|
-
return null;
|
|
35
|
-
result = '';
|
|
36
|
-
stdin.setEncoding('utf8');
|
|
37
|
-
for await (const chunk of stdin) {
|
|
38
|
-
result += chunk;
|
|
39
|
-
}
|
|
40
|
-
return result;
|
|
41
|
-
};
|
|
42
|
-
const readStdinWithTimeout = async () => {
|
|
22
|
+
const readStdin = async () => {
|
|
43
23
|
const { stdin, stdout } = process;
|
|
44
24
|
// process.stdin.isTTY is true whenever it's running in a terminal.
|
|
45
25
|
// process.stdin.isTTY is undefined when it's running in a pipe, e.g. echo 'foo' | my-cli command
|
|
@@ -48,17 +28,17 @@ const readStdinWithTimeout = async () => {
|
|
|
48
28
|
// Because of this, we have to set a timeout to prevent the process from hanging.
|
|
49
29
|
if (stdin.isTTY)
|
|
50
30
|
return null;
|
|
51
|
-
return new Promise(resolve => {
|
|
31
|
+
return new Promise((resolve) => {
|
|
52
32
|
let result = '';
|
|
53
33
|
const ac = new AbortController();
|
|
54
|
-
const signal = ac
|
|
34
|
+
const { signal } = ac;
|
|
55
35
|
const timeout = setTimeout(() => ac.abort(), 100);
|
|
56
|
-
const rl =
|
|
36
|
+
const rl = (0, node_readline_1.createInterface)({
|
|
57
37
|
input: stdin,
|
|
58
38
|
output: stdout,
|
|
59
39
|
terminal: false,
|
|
60
40
|
});
|
|
61
|
-
rl.on('line', line => {
|
|
41
|
+
rl.on('line', (line) => {
|
|
62
42
|
result += line;
|
|
63
43
|
});
|
|
64
44
|
rl.once('close', () => {
|
|
@@ -74,58 +54,34 @@ const readStdinWithTimeout = async () => {
|
|
|
74
54
|
}, { once: true });
|
|
75
55
|
});
|
|
76
56
|
};
|
|
77
|
-
const readStdin = async () => {
|
|
78
|
-
const { stdin, version } = process;
|
|
79
|
-
debug('stdin.isTTY', stdin.isTTY);
|
|
80
|
-
const nodeMajorVersion = Number(version.split('.')[0].replace(/^v/, ''));
|
|
81
|
-
debug('node version', nodeMajorVersion);
|
|
82
|
-
return nodeMajorVersion > 14 ? readStdinWithTimeout() : readStdinLegacy();
|
|
83
|
-
};
|
|
84
57
|
function isNegativeNumber(input) {
|
|
85
58
|
return /^-\d/g.test(input);
|
|
86
59
|
}
|
|
60
|
+
const validateOptions = (flag, input) => {
|
|
61
|
+
if (flag.options && !flag.options.includes(input))
|
|
62
|
+
throw new errors_1.FlagInvalidOptionError(flag, input);
|
|
63
|
+
return input;
|
|
64
|
+
};
|
|
87
65
|
class Parser {
|
|
66
|
+
input;
|
|
67
|
+
argv;
|
|
68
|
+
raw = [];
|
|
69
|
+
booleanFlags;
|
|
70
|
+
flagAliases;
|
|
71
|
+
context;
|
|
72
|
+
currentFlag;
|
|
88
73
|
constructor(input) {
|
|
89
74
|
this.input = input;
|
|
90
|
-
this.raw = [];
|
|
91
75
|
this.context = input.context ?? {};
|
|
92
76
|
this.argv = [...input.argv];
|
|
93
77
|
this._setNames();
|
|
94
|
-
this.booleanFlags = (0, util_1.pickBy)(input.flags, f => f.type === 'boolean');
|
|
95
|
-
this.flagAliases = Object.fromEntries(Object.values(input.flags).flatMap(flag =>
|
|
96
|
-
return (flag.aliases ?? []).map(a => [a, flag]);
|
|
97
|
-
}));
|
|
78
|
+
this.booleanFlags = (0, util_1.pickBy)(input.flags, (f) => f.type === 'boolean');
|
|
79
|
+
this.flagAliases = Object.fromEntries(Object.values(input.flags).flatMap((flag) => [...(flag.aliases ?? []), ...(flag.charAliases ?? [])].map((a) => [a, flag])));
|
|
98
80
|
}
|
|
99
81
|
async parse() {
|
|
100
82
|
this._debugInput();
|
|
101
|
-
const findLongFlag = (arg) => {
|
|
102
|
-
const name = arg.slice(2);
|
|
103
|
-
if (this.input.flags[name]) {
|
|
104
|
-
return name;
|
|
105
|
-
}
|
|
106
|
-
if (this.flagAliases[name]) {
|
|
107
|
-
return this.flagAliases[name].name;
|
|
108
|
-
}
|
|
109
|
-
if (arg.startsWith('--no-')) {
|
|
110
|
-
const flag = this.booleanFlags[arg.slice(5)];
|
|
111
|
-
if (flag && flag.allowNo)
|
|
112
|
-
return flag.name;
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
const findShortFlag = ([_, char]) => {
|
|
116
|
-
if (this.flagAliases[char]) {
|
|
117
|
-
return this.flagAliases[char].name;
|
|
118
|
-
}
|
|
119
|
-
return Object.keys(this.input.flags).find(k => (this.input.flags[k].char === char && char !== undefined && this.input.flags[k].char !== undefined));
|
|
120
|
-
};
|
|
121
|
-
const findFlag = (arg) => {
|
|
122
|
-
const isLong = arg.startsWith('--');
|
|
123
|
-
const short = isLong ? false : arg.startsWith('-');
|
|
124
|
-
const name = isLong ? findLongFlag(arg) : (short ? findShortFlag(arg) : undefined);
|
|
125
|
-
return { name, isLong };
|
|
126
|
-
};
|
|
127
83
|
const parseFlag = (arg) => {
|
|
128
|
-
const { name, isLong } = findFlag(arg);
|
|
84
|
+
const { name, isLong } = this.findFlag(arg);
|
|
129
85
|
if (!name) {
|
|
130
86
|
const i = arg.indexOf('=');
|
|
131
87
|
if (i !== -1) {
|
|
@@ -141,13 +97,16 @@ class Parser {
|
|
|
141
97
|
}
|
|
142
98
|
const flag = this.input.flags[name];
|
|
143
99
|
if (flag.type === 'option') {
|
|
100
|
+
if (!flag.multiple && this.raw.some((o) => o.type === 'flag' && o.flag === name)) {
|
|
101
|
+
throw new errors_1.CLIError(`Flag --${name} can only be specified once`);
|
|
102
|
+
}
|
|
144
103
|
this.currentFlag = flag;
|
|
145
104
|
const input = isLong || arg.length < 3 ? this.argv.shift() : arg.slice(arg[2] === '=' ? 3 : 2);
|
|
146
105
|
// if the value ends up being one of the command's flags, the user didn't provide an input
|
|
147
|
-
if (
|
|
106
|
+
if (typeof input !== 'string' || this.findFlag(input).name) {
|
|
148
107
|
throw new errors_1.CLIError(`Flag --${name} expects a value`);
|
|
149
108
|
}
|
|
150
|
-
this.raw.push({ type: 'flag', flag: flag.name, input
|
|
109
|
+
this.raw.push({ type: 'flag', flag: flag.name, input });
|
|
151
110
|
}
|
|
152
111
|
else {
|
|
153
112
|
this.raw.push({ type: 'flag', flag: flag.name, input: arg });
|
|
@@ -207,11 +166,6 @@ class Parser {
|
|
|
207
166
|
};
|
|
208
167
|
}
|
|
209
168
|
async _flags() {
|
|
210
|
-
const validateOptions = (flag, input) => {
|
|
211
|
-
if (flag.options && !flag.options.includes(input))
|
|
212
|
-
throw new errors_1.FlagInvalidOptionError(flag, input);
|
|
213
|
-
return input;
|
|
214
|
-
};
|
|
215
169
|
const parseFlagOrThrowError = async (input, flag, context, token) => {
|
|
216
170
|
if (!flag.parse)
|
|
217
171
|
return input;
|
|
@@ -236,8 +190,8 @@ class Parser {
|
|
|
236
190
|
}
|
|
237
191
|
};
|
|
238
192
|
/* Could add a valueFunction (if there is a value/env/default) and could metadata.
|
|
239
|
-
|
|
240
|
-
|
|
193
|
+
* Value function can be resolved later.
|
|
194
|
+
*/
|
|
241
195
|
const addValueFunction = (fws) => {
|
|
242
196
|
const tokenLength = fws.tokens?.length;
|
|
243
197
|
// user provided some input
|
|
@@ -252,17 +206,27 @@ class Parser {
|
|
|
252
206
|
// multiple with custom delimiter
|
|
253
207
|
if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.delimiter && fws.inputFlag.flag.multiple) {
|
|
254
208
|
return {
|
|
255
|
-
...fws,
|
|
209
|
+
...fws,
|
|
210
|
+
valueFunction: async (i) => (await Promise.all((i.tokens ?? [])
|
|
211
|
+
.flatMap((token) => token.input.split(i.inputFlag.flag.delimiter ?? ','))
|
|
256
212
|
// trim, and remove surrounding doubleQuotes (which would hav been needed if the elements contain spaces)
|
|
257
|
-
.map(v => v
|
|
258
|
-
.
|
|
213
|
+
.map((v) => v
|
|
214
|
+
.trim()
|
|
215
|
+
.replace(/^"(.*)"$/, '$1')
|
|
216
|
+
.replace(/^'(.*)'$/, '$1'))
|
|
217
|
+
.map(async (v) => parseFlagOrThrowError(v, i.inputFlag.flag, this.context, {
|
|
218
|
+
...(0, util_1.last)(i.tokens),
|
|
219
|
+
input: v,
|
|
220
|
+
}))))
|
|
221
|
+
// eslint-disable-next-line unicorn/no-await-expression-member
|
|
222
|
+
.map((v) => validateOptions(i.inputFlag.flag, v)),
|
|
259
223
|
};
|
|
260
224
|
}
|
|
261
225
|
// multiple in the oclif-core style
|
|
262
226
|
if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.multiple) {
|
|
263
227
|
return {
|
|
264
228
|
...fws,
|
|
265
|
-
valueFunction: async (i) => Promise.all((fws.tokens ?? []).map(token => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, token.input), i.inputFlag.flag, this.context, token))),
|
|
229
|
+
valueFunction: async (i) => Promise.all((fws.tokens ?? []).map((token) => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, token.input), i.inputFlag.flag, this.context, token))),
|
|
266
230
|
};
|
|
267
231
|
}
|
|
268
232
|
// simple option flag
|
|
@@ -285,17 +249,19 @@ class Parser {
|
|
|
285
249
|
if (fws.inputFlag.flag.type === 'boolean') {
|
|
286
250
|
return {
|
|
287
251
|
...fws,
|
|
288
|
-
valueFunction: async (i) =>
|
|
252
|
+
valueFunction: async (i) => (0, util_1.isTruthy)(process.env[i.inputFlag.flag.env] ?? 'false'),
|
|
289
253
|
};
|
|
290
254
|
}
|
|
291
255
|
}
|
|
292
256
|
// no input, but flag has default value
|
|
257
|
+
// eslint-disable-next-line no-constant-binary-expression, valid-typeof
|
|
293
258
|
if (typeof fws.inputFlag.flag.default !== undefined) {
|
|
294
259
|
return {
|
|
295
|
-
...fws,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
260
|
+
...fws,
|
|
261
|
+
metadata: { setFromDefault: true },
|
|
262
|
+
valueFunction: typeof fws.inputFlag.flag.default === 'function'
|
|
263
|
+
? (i, allFlags = {}) => fws.inputFlag.flag.default({ options: i.inputFlag.flag, flags: allFlags })
|
|
264
|
+
: async () => fws.inputFlag.flag.default,
|
|
299
265
|
};
|
|
300
266
|
}
|
|
301
267
|
// base case (no value function)
|
|
@@ -304,17 +270,19 @@ class Parser {
|
|
|
304
270
|
const addHelpFunction = (fws) => {
|
|
305
271
|
if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.defaultHelp) {
|
|
306
272
|
return {
|
|
307
|
-
...fws,
|
|
308
|
-
|
|
309
|
-
(i, flags, ...context) =>
|
|
273
|
+
...fws,
|
|
274
|
+
helpFunction: typeof fws.inputFlag.flag.defaultHelp === 'function'
|
|
275
|
+
? (i, flags, ...context) =>
|
|
310
276
|
// @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there
|
|
311
|
-
|
|
277
|
+
i.inputFlag.flag.defaultHelp({ options: i.inputFlag, flags }, ...context)
|
|
278
|
+
: // @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there
|
|
279
|
+
(i) => i.inputFlag.flag.defaultHelp,
|
|
312
280
|
};
|
|
313
281
|
}
|
|
314
282
|
return fws;
|
|
315
283
|
};
|
|
316
284
|
const addDefaultHelp = async (fwsArray) => {
|
|
317
|
-
const valueReferenceForHelp = fwsArrayToObject(flagsWithAllValues.filter(fws => !fws.metadata?.setFromDefault));
|
|
285
|
+
const valueReferenceForHelp = fwsArrayToObject(flagsWithAllValues.filter((fws) => !fws.metadata?.setFromDefault));
|
|
318
286
|
return Promise.all(fwsArray.map(async (fws) => {
|
|
319
287
|
try {
|
|
320
288
|
if (fws.helpFunction) {
|
|
@@ -333,27 +301,32 @@ class Parser {
|
|
|
333
301
|
return fws;
|
|
334
302
|
}));
|
|
335
303
|
};
|
|
336
|
-
const fwsArrayToObject = (fwsArray) => Object.fromEntries(fwsArray.filter(fws => fws.value !== undefined)
|
|
337
|
-
.map(fws => [fws.inputFlag.name, fws.value]));
|
|
304
|
+
const fwsArrayToObject = (fwsArray) => Object.fromEntries(fwsArray.filter((fws) => fws.value !== undefined).map((fws) => [fws.inputFlag.name, fws.value]));
|
|
338
305
|
const flagTokenMap = this.mapAndValidateFlags();
|
|
339
306
|
const flagsWithValues = await Promise.all(Object.entries(this.input.flags)
|
|
340
307
|
// we check them if they have a token, or might have env, default, or defaultHelp. Also include booleans so they get their default value
|
|
341
|
-
.filter(([name, flag]) => flag.type === 'boolean' ||
|
|
308
|
+
.filter(([name, flag]) => flag.type === 'boolean' ||
|
|
309
|
+
flag.env ||
|
|
310
|
+
flag.default !== undefined ||
|
|
311
|
+
'defaultHelp' in flag ||
|
|
312
|
+
flagTokenMap.has(name))
|
|
342
313
|
// match each possible flag to its token, if there is one
|
|
343
314
|
.map(([name, flag]) => ({ inputFlag: { name, flag }, tokens: flagTokenMap.get(name) }))
|
|
344
|
-
.map(fws => addValueFunction(fws))
|
|
345
|
-
.filter(fws => fws.valueFunction !== undefined)
|
|
346
|
-
.map(fws => addHelpFunction(fws))
|
|
315
|
+
.map((fws) => addValueFunction(fws))
|
|
316
|
+
.filter((fws) => fws.valueFunction !== undefined)
|
|
317
|
+
.map((fws) => addHelpFunction(fws))
|
|
347
318
|
// we can't apply the default values until all the other flags are resolved because `flag.default` can reference other flags
|
|
348
319
|
.map(async (fws) => (fws.metadata?.setFromDefault ? fws : { ...fws, value: await fws.valueFunction?.(fws) })));
|
|
349
|
-
const valueReference = fwsArrayToObject(flagsWithValues.filter(fws => !fws.metadata?.setFromDefault));
|
|
350
|
-
const flagsWithAllValues = await Promise.all(flagsWithValues
|
|
351
|
-
|
|
352
|
-
|
|
320
|
+
const valueReference = fwsArrayToObject(flagsWithValues.filter((fws) => !fws.metadata?.setFromDefault));
|
|
321
|
+
const flagsWithAllValues = await Promise.all(flagsWithValues.map(async (fws) => fws.metadata?.setFromDefault ? { ...fws, value: await fws.valueFunction?.(fws, valueReference) } : fws));
|
|
322
|
+
const finalFlags = flagsWithAllValues.some((fws) => typeof fws.helpFunction === 'function')
|
|
323
|
+
? await addDefaultHelp(flagsWithAllValues)
|
|
324
|
+
: flagsWithAllValues;
|
|
353
325
|
return {
|
|
354
|
-
// @ts-ignore original version returned an any. Not sure how to get to the return type for `flags` prop
|
|
355
326
|
flags: fwsArrayToObject(finalFlags),
|
|
356
|
-
metadata: {
|
|
327
|
+
metadata: {
|
|
328
|
+
flags: Object.fromEntries(finalFlags.filter((fws) => fws.metadata).map((fws) => [fws.inputFlag.name, fws.metadata])),
|
|
329
|
+
},
|
|
357
330
|
};
|
|
358
331
|
}
|
|
359
332
|
async _args() {
|
|
@@ -363,7 +336,7 @@ class Parser {
|
|
|
363
336
|
let stdinRead = false;
|
|
364
337
|
const ctx = this.context;
|
|
365
338
|
for (const [name, arg] of Object.entries(this.input.args)) {
|
|
366
|
-
const token = tokens.find(t => t.arg === name);
|
|
339
|
+
const token = tokens.find((t) => t.arg === name);
|
|
367
340
|
ctx.token = token;
|
|
368
341
|
if (token) {
|
|
369
342
|
if (arg.options && !arg.options.includes(token.input)) {
|
|
@@ -400,7 +373,7 @@ class Parser {
|
|
|
400
373
|
continue;
|
|
401
374
|
argv.push(token.input);
|
|
402
375
|
}
|
|
403
|
-
return { argv, args
|
|
376
|
+
return { argv, args };
|
|
404
377
|
}
|
|
405
378
|
_debugOutput(args, flags, argv) {
|
|
406
379
|
if (argv.length > 0) {
|
|
@@ -422,11 +395,11 @@ class Parser {
|
|
|
422
395
|
if (Object.keys(this.input.flags).length === 0)
|
|
423
396
|
return;
|
|
424
397
|
debug('available flags: %s', Object.keys(this.input.flags)
|
|
425
|
-
.map(f => `--${f}`)
|
|
398
|
+
.map((f) => `--${f}`)
|
|
426
399
|
.join(' '));
|
|
427
400
|
}
|
|
428
401
|
get _argTokens() {
|
|
429
|
-
return this.raw.filter(o => o.type === 'arg');
|
|
402
|
+
return this.raw.filter((o) => o.type === 'arg');
|
|
430
403
|
}
|
|
431
404
|
_setNames() {
|
|
432
405
|
for (const k of Object.keys(this.input.flags)) {
|
|
@@ -438,7 +411,7 @@ class Parser {
|
|
|
438
411
|
}
|
|
439
412
|
mapAndValidateFlags() {
|
|
440
413
|
const flagTokenMap = new Map();
|
|
441
|
-
for (const token of this.raw.filter(o => o.type === 'flag')) {
|
|
414
|
+
for (const token of this.raw.filter((o) => o.type === 'flag')) {
|
|
442
415
|
// fail fast if there are any invalid flags
|
|
443
416
|
if (!(token.flag in this.input.flags)) {
|
|
444
417
|
throw new errors_1.CLIError(`Unexpected flag ${token.flag}`);
|
|
@@ -448,5 +421,31 @@ class Parser {
|
|
|
448
421
|
}
|
|
449
422
|
return flagTokenMap;
|
|
450
423
|
}
|
|
424
|
+
findLongFlag(arg) {
|
|
425
|
+
const name = arg.slice(2);
|
|
426
|
+
if (this.input.flags[name]) {
|
|
427
|
+
return name;
|
|
428
|
+
}
|
|
429
|
+
if (this.flagAliases[name]) {
|
|
430
|
+
return this.flagAliases[name].name;
|
|
431
|
+
}
|
|
432
|
+
if (arg.startsWith('--no-')) {
|
|
433
|
+
const flag = this.booleanFlags[arg.slice(5)];
|
|
434
|
+
if (flag && flag.allowNo)
|
|
435
|
+
return flag.name;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
findShortFlag([_, char]) {
|
|
439
|
+
if (this.flagAliases[char]) {
|
|
440
|
+
return this.flagAliases[char].name;
|
|
441
|
+
}
|
|
442
|
+
return Object.keys(this.input.flags).find((k) => this.input.flags[k].char === char && char !== undefined && this.input.flags[k].char !== undefined);
|
|
443
|
+
}
|
|
444
|
+
findFlag(arg) {
|
|
445
|
+
const isLong = arg.startsWith('--');
|
|
446
|
+
const short = isLong ? false : arg.startsWith('-');
|
|
447
|
+
const name = isLong ? this.findLongFlag(arg) : short ? this.findShortFlag(arg) : undefined;
|
|
448
|
+
return { name, isLong };
|
|
449
|
+
}
|
|
451
450
|
}
|
|
452
451
|
exports.Parser = Parser;
|
package/lib/parser/validate.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validate = void 0;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
|
-
const util_1 = require("../
|
|
5
|
+
const util_1 = require("../util/util");
|
|
6
6
|
async function validate(parse) {
|
|
7
7
|
let cachedResolvedFlags;
|
|
8
8
|
function validateArgs() {
|
|
@@ -40,10 +40,10 @@ async function validate(parse) {
|
|
|
40
40
|
const promises = Object.entries(parse.input.flags).flatMap(([name, flag]) => {
|
|
41
41
|
if (parse.output.flags[name] !== undefined) {
|
|
42
42
|
return [
|
|
43
|
-
...flag.relationships ? validateRelationships(name, flag) : [],
|
|
44
|
-
...flag.dependsOn ? [validateDependsOn(name, flag.dependsOn)] : [],
|
|
45
|
-
...flag.exclusive ? [validateExclusive(name, flag.exclusive)] : [],
|
|
46
|
-
...flag.exactlyOne ? [validateExactlyOne(name, flag.exactlyOne)] : [],
|
|
43
|
+
...(flag.relationships ? validateRelationships(name, flag) : []),
|
|
44
|
+
...(flag.dependsOn ? [validateDependsOn(name, flag.dependsOn)] : []),
|
|
45
|
+
...(flag.exclusive ? [validateExclusive(name, flag.exclusive)] : []),
|
|
46
|
+
...(flag.exactlyOne ? [validateExactlyOne(name, flag.exactlyOne)] : []),
|
|
47
47
|
];
|
|
48
48
|
}
|
|
49
49
|
if (flag.required) {
|
|
@@ -54,8 +54,8 @@ async function validate(parse) {
|
|
|
54
54
|
}
|
|
55
55
|
return [];
|
|
56
56
|
});
|
|
57
|
-
const results =
|
|
58
|
-
const failed = results.filter(r => r.status === 'failed');
|
|
57
|
+
const results = await Promise.all(promises);
|
|
58
|
+
const failed = results.filter((r) => r.status === 'failed');
|
|
59
59
|
if (failed.length > 0)
|
|
60
60
|
throw new errors_1.FailedFlagValidationError({ parse, failed });
|
|
61
61
|
}
|
|
@@ -70,19 +70,19 @@ async function validate(parse) {
|
|
|
70
70
|
return result ? [flag.name, parse.output.flags[flag.name]] : null;
|
|
71
71
|
});
|
|
72
72
|
const resolved = await Promise.all(promises);
|
|
73
|
-
cachedResolvedFlags = Object.fromEntries(resolved.filter(r => r !== null));
|
|
73
|
+
cachedResolvedFlags = Object.fromEntries(resolved.filter((r) => r !== null));
|
|
74
74
|
return cachedResolvedFlags;
|
|
75
75
|
}
|
|
76
|
-
const getPresentFlags = (flags) => Object.keys(flags).filter(key => key !== undefined);
|
|
76
|
+
const getPresentFlags = (flags) => Object.keys(flags).filter((key) => key !== undefined);
|
|
77
77
|
function validateAcrossFlags(flag) {
|
|
78
78
|
const base = { name: flag.name, validationFn: 'validateAcrossFlags' };
|
|
79
79
|
const intersection = Object.entries(parse.input.flags)
|
|
80
|
-
.map(entry => entry[0]) // array of flag names
|
|
81
|
-
.filter(flagName => parse.output.flags[flagName] !== undefined) // with values
|
|
82
|
-
.filter(flagName => flag.exactlyOne && flag.exactlyOne.includes(flagName)); // and in the exactlyOne list
|
|
80
|
+
.map((entry) => entry[0]) // array of flag names
|
|
81
|
+
.filter((flagName) => parse.output.flags[flagName] !== undefined) // with values
|
|
82
|
+
.filter((flagName) => flag.exactlyOne && flag.exactlyOne.includes(flagName)); // and in the exactlyOne list
|
|
83
83
|
if (intersection.length === 0) {
|
|
84
84
|
// the command's exactlyOne may or may not include itself, so we'll use Set to add + de-dupe
|
|
85
|
-
const deduped = (0, util_1.uniq)(flag.exactlyOne?.map(flag => `--${flag}`) ?? []).join(', ');
|
|
85
|
+
const deduped = (0, util_1.uniq)(flag.exactlyOne?.map((flag) => `--${flag}`) ?? []).join(', ');
|
|
86
86
|
const reason = `Exactly one of the following must be provided: ${deduped}`;
|
|
87
87
|
return { ...base, status: 'failed', reason };
|
|
88
88
|
}
|
|
@@ -100,7 +100,11 @@ async function validate(parse) {
|
|
|
100
100
|
continue;
|
|
101
101
|
if (parse.output.flags[flag] !== undefined) {
|
|
102
102
|
const flagValue = parse.output.metadata.flags?.[flag]?.defaultHelp ?? parse.output.flags[flag];
|
|
103
|
-
return {
|
|
103
|
+
return {
|
|
104
|
+
...base,
|
|
105
|
+
status: 'failed',
|
|
106
|
+
reason: `--${flag}=${flagValue} cannot also be provided when using --${name}`,
|
|
107
|
+
};
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
110
|
return { ...base, status: 'success' };
|
|
@@ -119,10 +123,16 @@ async function validate(parse) {
|
|
|
119
123
|
async function validateDependsOn(name, flags) {
|
|
120
124
|
const base = { name, validationFn: 'validateDependsOn' };
|
|
121
125
|
const resolved = await resolveFlags(flags);
|
|
122
|
-
const foundAll = Object.values(resolved).every(val => val !== undefined);
|
|
126
|
+
const foundAll = Object.values(resolved).every((val) => val !== undefined);
|
|
123
127
|
if (!foundAll) {
|
|
124
|
-
const formattedFlags = Object.keys(resolved)
|
|
125
|
-
|
|
128
|
+
const formattedFlags = Object.keys(resolved)
|
|
129
|
+
.map((f) => `--${f}`)
|
|
130
|
+
.join(', ');
|
|
131
|
+
return {
|
|
132
|
+
...base,
|
|
133
|
+
status: 'failed',
|
|
134
|
+
reason: `All of the following must be provided when using --${name}: ${formattedFlags}`,
|
|
135
|
+
};
|
|
126
136
|
}
|
|
127
137
|
return { ...base, status: 'success' };
|
|
128
138
|
}
|
|
@@ -131,24 +141,34 @@ async function validate(parse) {
|
|
|
131
141
|
const resolved = await resolveFlags(flags);
|
|
132
142
|
const foundAtLeastOne = Object.values(resolved).some(Boolean);
|
|
133
143
|
if (!foundAtLeastOne) {
|
|
134
|
-
const formattedFlags = Object.keys(resolved)
|
|
135
|
-
|
|
144
|
+
const formattedFlags = Object.keys(resolved)
|
|
145
|
+
.map((f) => `--${f}`)
|
|
146
|
+
.join(', ');
|
|
147
|
+
return {
|
|
148
|
+
...base,
|
|
149
|
+
status: 'failed',
|
|
150
|
+
reason: `One of the following must be provided when using --${name}: ${formattedFlags}`,
|
|
151
|
+
};
|
|
136
152
|
}
|
|
137
153
|
return { ...base, status: 'success' };
|
|
138
154
|
}
|
|
139
155
|
function validateRelationships(name, flag) {
|
|
140
|
-
return (
|
|
156
|
+
return (flag.relationships ?? []).map((relationship) => {
|
|
141
157
|
switch (relationship.type) {
|
|
142
|
-
case 'all':
|
|
158
|
+
case 'all': {
|
|
143
159
|
return validateDependsOn(name, relationship.flags);
|
|
144
|
-
|
|
160
|
+
}
|
|
161
|
+
case 'some': {
|
|
145
162
|
return validateSome(name, relationship.flags);
|
|
146
|
-
|
|
163
|
+
}
|
|
164
|
+
case 'none': {
|
|
147
165
|
return validateExclusive(name, relationship.flags);
|
|
148
|
-
|
|
166
|
+
}
|
|
167
|
+
default: {
|
|
149
168
|
throw new Error(`Unknown relationship type: ${relationship.type}`);
|
|
169
|
+
}
|
|
150
170
|
}
|
|
151
|
-
})
|
|
171
|
+
});
|
|
152
172
|
}
|
|
153
173
|
validateArgs();
|
|
154
174
|
return validateFlags();
|
package/lib/performance.d.ts
CHANGED
|
@@ -12,7 +12,11 @@ type PerfHighlights = {
|
|
|
12
12
|
runTime: number;
|
|
13
13
|
initTime: number;
|
|
14
14
|
commandLoadTime: number;
|
|
15
|
-
|
|
15
|
+
commandRunTime: number;
|
|
16
|
+
pluginLoadTimes: Record<string, {
|
|
17
|
+
duration: number;
|
|
18
|
+
details: Details;
|
|
19
|
+
}>;
|
|
16
20
|
corePluginsLoadTime: number;
|
|
17
21
|
userPluginsLoadTime: number;
|
|
18
22
|
linkedPluginsLoadTime: number;
|