@oclif/core 3.0.0-beta.17 → 3.0.0-beta.19
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/lib/args.js +4 -4
- package/lib/cli-ux/action/base.js +8 -7
- package/lib/cli-ux/action/simple.js +1 -4
- package/lib/cli-ux/action/spinner.js +8 -7
- package/lib/cli-ux/action/spinners.js +1 -1
- package/lib/cli-ux/config.js +11 -12
- package/lib/cli-ux/exit.js +3 -0
- package/lib/cli-ux/flush.js +7 -6
- package/lib/cli-ux/index.js +2 -2
- package/lib/cli-ux/list.js +3 -3
- package/lib/cli-ux/prompt.js +8 -3
- package/lib/cli-ux/stream.js +1 -0
- package/lib/cli-ux/styled/json.js +5 -3
- package/lib/cli-ux/styled/object.js +2 -2
- package/lib/cli-ux/styled/table.js +26 -20
- package/lib/cli-ux/styled/tree.js +1 -3
- package/lib/cli-ux/wait.js +1 -1
- package/lib/command.d.ts +6 -14
- package/lib/command.js +86 -73
- package/lib/config/config.d.ts +8 -9
- package/lib/config/config.js +85 -199
- package/lib/config/index.d.ts +0 -1
- package/lib/config/index.js +1 -3
- package/lib/config/plugin-loader.js +12 -11
- package/lib/config/plugin.d.ts +1 -0
- package/lib/config/plugin.js +54 -34
- package/lib/config/ts-node.js +17 -13
- package/lib/config/util.d.ts +0 -6
- package/lib/config/util.js +3 -15
- package/lib/errors/errors/cli.js +4 -1
- package/lib/errors/errors/exit.js +1 -1
- package/lib/errors/errors/module-load.js +1 -1
- package/lib/errors/errors/pretty-print.js +2 -1
- package/lib/errors/handle.js +4 -3
- package/lib/errors/logger.js +5 -4
- package/lib/flags.d.ts +6 -6
- package/lib/flags.js +3 -3
- package/lib/help/command.js +46 -32
- package/lib/help/docopts.js +8 -5
- package/lib/help/formatter.js +19 -8
- package/lib/help/index.d.ts +5 -1
- package/lib/help/index.js +70 -49
- package/lib/help/root.js +7 -9
- package/lib/help/util.d.ts +1 -7
- package/lib/help/util.js +2 -22
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -3
- package/lib/interfaces/hooks.d.ts +3 -3
- package/lib/interfaces/index.d.ts +1 -1
- package/lib/interfaces/parser.d.ts +19 -18
- package/lib/interfaces/pjson.d.ts +1 -1
- package/lib/interfaces/plugin.d.ts +5 -0
- package/lib/module-loader.d.ts +8 -8
- package/lib/module-loader.js +13 -10
- package/lib/parser/errors.d.ts +1 -1
- package/lib/parser/errors.js +15 -9
- package/lib/parser/help.js +2 -3
- package/lib/parser/parse.js +72 -44
- package/lib/parser/validate.js +37 -21
- package/lib/performance.js +20 -9
- 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 +108 -0
- package/lib/util/cache-default-value.d.ts +2 -0
- package/lib/util/cache-default-value.js +28 -0
- package/lib/{util.d.ts → util/index.d.ts} +7 -2
- package/lib/{util.js → util/index.js} +15 -20
- package/package.json +19 -13
package/lib/parser/parse.js
CHANGED
|
@@ -3,13 +3,16 @@ 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
|
|
6
|
+
const index_1 = require("../util/index");
|
|
7
7
|
const node_readline_1 = require("node:readline");
|
|
8
8
|
let debug;
|
|
9
9
|
try {
|
|
10
|
-
debug =
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
debug =
|
|
11
|
+
process.env.CLI_FLAGS_DEBUG === '1'
|
|
12
|
+
? require('debug')('../parser')
|
|
13
|
+
: () => {
|
|
14
|
+
// noop
|
|
15
|
+
};
|
|
13
16
|
}
|
|
14
17
|
catch {
|
|
15
18
|
debug = () => {
|
|
@@ -25,7 +28,7 @@ const readStdin = async () => {
|
|
|
25
28
|
// Because of this, we have to set a timeout to prevent the process from hanging.
|
|
26
29
|
if (stdin.isTTY)
|
|
27
30
|
return null;
|
|
28
|
-
return new Promise(resolve => {
|
|
31
|
+
return new Promise((resolve) => {
|
|
29
32
|
let result = '';
|
|
30
33
|
const ac = new AbortController();
|
|
31
34
|
const { signal } = ac;
|
|
@@ -35,7 +38,7 @@ const readStdin = async () => {
|
|
|
35
38
|
output: stdout,
|
|
36
39
|
terminal: false,
|
|
37
40
|
});
|
|
38
|
-
rl.on('line', line => {
|
|
41
|
+
rl.on('line', (line) => {
|
|
39
42
|
result += line;
|
|
40
43
|
});
|
|
41
44
|
rl.once('close', () => {
|
|
@@ -60,14 +63,20 @@ const validateOptions = (flag, input) => {
|
|
|
60
63
|
return input;
|
|
61
64
|
};
|
|
62
65
|
class Parser {
|
|
66
|
+
input;
|
|
67
|
+
argv;
|
|
68
|
+
raw = [];
|
|
69
|
+
booleanFlags;
|
|
70
|
+
flagAliases;
|
|
71
|
+
context;
|
|
72
|
+
currentFlag;
|
|
63
73
|
constructor(input) {
|
|
64
74
|
this.input = input;
|
|
65
|
-
this.raw = [];
|
|
66
75
|
this.context = input.context ?? {};
|
|
67
76
|
this.argv = [...input.argv];
|
|
68
77
|
this._setNames();
|
|
69
|
-
this.booleanFlags = (0,
|
|
70
|
-
this.flagAliases = Object.fromEntries(Object.values(input.flags).flatMap(flag =>
|
|
78
|
+
this.booleanFlags = (0, index_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])));
|
|
71
80
|
}
|
|
72
81
|
async parse() {
|
|
73
82
|
this._debugInput();
|
|
@@ -88,13 +97,13 @@ class Parser {
|
|
|
88
97
|
}
|
|
89
98
|
const flag = this.input.flags[name];
|
|
90
99
|
if (flag.type === 'option') {
|
|
91
|
-
if (!flag.multiple && this.raw.some(o => o.type === 'flag' && o.flag === name)) {
|
|
100
|
+
if (!flag.multiple && this.raw.some((o) => o.type === 'flag' && o.flag === name)) {
|
|
92
101
|
throw new errors_1.CLIError(`Flag --${name} can only be specified once`);
|
|
93
102
|
}
|
|
94
103
|
this.currentFlag = flag;
|
|
95
104
|
const input = isLong || arg.length < 3 ? this.argv.shift() : arg.slice(arg[2] === '=' ? 3 : 2);
|
|
96
105
|
// if the value ends up being one of the command's flags, the user didn't provide an input
|
|
97
|
-
if (
|
|
106
|
+
if (typeof input !== 'string' || this.findFlag(input).name) {
|
|
98
107
|
throw new errors_1.CLIError(`Flag --${name} expects a value`);
|
|
99
108
|
}
|
|
100
109
|
this.raw.push({ type: 'flag', flag: flag.name, input });
|
|
@@ -181,40 +190,50 @@ class Parser {
|
|
|
181
190
|
}
|
|
182
191
|
};
|
|
183
192
|
/* Could add a valueFunction (if there is a value/env/default) and could metadata.
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
* Value function can be resolved later.
|
|
194
|
+
*/
|
|
186
195
|
const addValueFunction = (fws) => {
|
|
187
196
|
const tokenLength = fws.tokens?.length;
|
|
188
197
|
// user provided some input
|
|
189
198
|
if (tokenLength) {
|
|
190
199
|
// boolean
|
|
191
|
-
if (fws.inputFlag.flag.type === 'boolean' && (0,
|
|
200
|
+
if (fws.inputFlag.flag.type === 'boolean' && (0, index_1.last)(fws.tokens)?.input) {
|
|
192
201
|
return {
|
|
193
202
|
...fws,
|
|
194
|
-
valueFunction: async (i) => parseFlagOrThrowError((0,
|
|
203
|
+
valueFunction: async (i) => parseFlagOrThrowError((0, index_1.last)(i.tokens)?.input !== `--no-${i.inputFlag.name}`, i.inputFlag.flag, this.context, (0, index_1.last)(i.tokens)),
|
|
195
204
|
};
|
|
196
205
|
}
|
|
197
206
|
// multiple with custom delimiter
|
|
198
207
|
if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.delimiter && fws.inputFlag.flag.multiple) {
|
|
199
208
|
return {
|
|
200
|
-
...fws,
|
|
209
|
+
...fws,
|
|
210
|
+
valueFunction: async (i) => (await Promise.all((i.tokens ?? [])
|
|
211
|
+
.flatMap((token) => token.input.split(i.inputFlag.flag.delimiter ?? ','))
|
|
201
212
|
// trim, and remove surrounding doubleQuotes (which would hav been needed if the elements contain spaces)
|
|
202
|
-
.map(v => v
|
|
203
|
-
.
|
|
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, index_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)),
|
|
204
223
|
};
|
|
205
224
|
}
|
|
206
225
|
// multiple in the oclif-core style
|
|
207
226
|
if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.multiple) {
|
|
208
227
|
return {
|
|
209
228
|
...fws,
|
|
210
|
-
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))),
|
|
211
230
|
};
|
|
212
231
|
}
|
|
213
232
|
// simple option flag
|
|
214
233
|
if (fws.inputFlag.flag.type === 'option') {
|
|
215
234
|
return {
|
|
216
235
|
...fws,
|
|
217
|
-
valueFunction: async (i) => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, (0,
|
|
236
|
+
valueFunction: async (i) => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, (0, index_1.last)(fws.tokens)?.input), i.inputFlag.flag, this.context, (0, index_1.last)(fws.tokens)),
|
|
218
237
|
};
|
|
219
238
|
}
|
|
220
239
|
}
|
|
@@ -230,7 +249,7 @@ class Parser {
|
|
|
230
249
|
if (fws.inputFlag.flag.type === 'boolean') {
|
|
231
250
|
return {
|
|
232
251
|
...fws,
|
|
233
|
-
valueFunction: async (i) => (0,
|
|
252
|
+
valueFunction: async (i) => (0, index_1.isTruthy)(process.env[i.inputFlag.flag.env] ?? 'false'),
|
|
234
253
|
};
|
|
235
254
|
}
|
|
236
255
|
}
|
|
@@ -238,7 +257,8 @@ class Parser {
|
|
|
238
257
|
// eslint-disable-next-line no-constant-binary-expression, valid-typeof
|
|
239
258
|
if (typeof fws.inputFlag.flag.default !== undefined) {
|
|
240
259
|
return {
|
|
241
|
-
...fws,
|
|
260
|
+
...fws,
|
|
261
|
+
metadata: { setFromDefault: true },
|
|
242
262
|
valueFunction: typeof fws.inputFlag.flag.default === 'function'
|
|
243
263
|
? (i, allFlags = {}) => fws.inputFlag.flag.default({ options: i.inputFlag.flag, flags: allFlags })
|
|
244
264
|
: async () => fws.inputFlag.flag.default,
|
|
@@ -250,17 +270,19 @@ class Parser {
|
|
|
250
270
|
const addHelpFunction = (fws) => {
|
|
251
271
|
if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.defaultHelp) {
|
|
252
272
|
return {
|
|
253
|
-
...fws,
|
|
254
|
-
|
|
255
|
-
? (i, flags, ...context) =>
|
|
273
|
+
...fws,
|
|
274
|
+
helpFunction: typeof fws.inputFlag.flag.defaultHelp === 'function'
|
|
275
|
+
? (i, flags, ...context) =>
|
|
256
276
|
// @ts-expect-error flag type isn't specific enough to know defaultHelp will definitely be there
|
|
257
|
-
|
|
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,
|
|
258
280
|
};
|
|
259
281
|
}
|
|
260
282
|
return fws;
|
|
261
283
|
};
|
|
262
284
|
const addDefaultHelp = async (fwsArray) => {
|
|
263
|
-
const valueReferenceForHelp = fwsArrayToObject(flagsWithAllValues.filter(fws => !fws.metadata?.setFromDefault));
|
|
285
|
+
const valueReferenceForHelp = fwsArrayToObject(flagsWithAllValues.filter((fws) => !fws.metadata?.setFromDefault));
|
|
264
286
|
return Promise.all(fwsArray.map(async (fws) => {
|
|
265
287
|
try {
|
|
266
288
|
if (fws.helpFunction) {
|
|
@@ -279,26 +301,32 @@ class Parser {
|
|
|
279
301
|
return fws;
|
|
280
302
|
}));
|
|
281
303
|
};
|
|
282
|
-
const fwsArrayToObject = (fwsArray) => Object.fromEntries(fwsArray.filter(fws => fws.value !== undefined)
|
|
283
|
-
.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]));
|
|
284
305
|
const flagTokenMap = this.mapAndValidateFlags();
|
|
285
306
|
const flagsWithValues = await Promise.all(Object.entries(this.input.flags)
|
|
286
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
|
|
287
|
-
.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))
|
|
288
313
|
// match each possible flag to its token, if there is one
|
|
289
314
|
.map(([name, flag]) => ({ inputFlag: { name, flag }, tokens: flagTokenMap.get(name) }))
|
|
290
|
-
.map(fws => addValueFunction(fws))
|
|
291
|
-
.filter(fws => fws.valueFunction !== undefined)
|
|
292
|
-
.map(fws => addHelpFunction(fws))
|
|
315
|
+
.map((fws) => addValueFunction(fws))
|
|
316
|
+
.filter((fws) => fws.valueFunction !== undefined)
|
|
317
|
+
.map((fws) => addHelpFunction(fws))
|
|
293
318
|
// we can't apply the default values until all the other flags are resolved because `flag.default` can reference other flags
|
|
294
319
|
.map(async (fws) => (fws.metadata?.setFromDefault ? fws : { ...fws, value: await fws.valueFunction?.(fws) })));
|
|
295
|
-
const valueReference = fwsArrayToObject(flagsWithValues.filter(fws => !fws.metadata?.setFromDefault));
|
|
296
|
-
const flagsWithAllValues = await Promise.all(flagsWithValues
|
|
297
|
-
|
|
298
|
-
|
|
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;
|
|
299
325
|
return {
|
|
300
326
|
flags: fwsArrayToObject(finalFlags),
|
|
301
|
-
metadata: {
|
|
327
|
+
metadata: {
|
|
328
|
+
flags: Object.fromEntries(finalFlags.filter((fws) => fws.metadata).map((fws) => [fws.inputFlag.name, fws.metadata])),
|
|
329
|
+
},
|
|
302
330
|
};
|
|
303
331
|
}
|
|
304
332
|
async _args() {
|
|
@@ -308,7 +336,7 @@ class Parser {
|
|
|
308
336
|
let stdinRead = false;
|
|
309
337
|
const ctx = this.context;
|
|
310
338
|
for (const [name, arg] of Object.entries(this.input.args)) {
|
|
311
|
-
const token = tokens.find(t => t.arg === name);
|
|
339
|
+
const token = tokens.find((t) => t.arg === name);
|
|
312
340
|
ctx.token = token;
|
|
313
341
|
if (token) {
|
|
314
342
|
if (arg.options && !arg.options.includes(token.input)) {
|
|
@@ -367,11 +395,11 @@ class Parser {
|
|
|
367
395
|
if (Object.keys(this.input.flags).length === 0)
|
|
368
396
|
return;
|
|
369
397
|
debug('available flags: %s', Object.keys(this.input.flags)
|
|
370
|
-
.map(f => `--${f}`)
|
|
398
|
+
.map((f) => `--${f}`)
|
|
371
399
|
.join(' '));
|
|
372
400
|
}
|
|
373
401
|
get _argTokens() {
|
|
374
|
-
return this.raw.filter(o => o.type === 'arg');
|
|
402
|
+
return this.raw.filter((o) => o.type === 'arg');
|
|
375
403
|
}
|
|
376
404
|
_setNames() {
|
|
377
405
|
for (const k of Object.keys(this.input.flags)) {
|
|
@@ -383,7 +411,7 @@ class Parser {
|
|
|
383
411
|
}
|
|
384
412
|
mapAndValidateFlags() {
|
|
385
413
|
const flagTokenMap = new Map();
|
|
386
|
-
for (const token of this.raw.filter(o => o.type === 'flag')) {
|
|
414
|
+
for (const token of this.raw.filter((o) => o.type === 'flag')) {
|
|
387
415
|
// fail fast if there are any invalid flags
|
|
388
416
|
if (!(token.flag in this.input.flags)) {
|
|
389
417
|
throw new errors_1.CLIError(`Unexpected flag ${token.flag}`);
|
|
@@ -411,12 +439,12 @@ class Parser {
|
|
|
411
439
|
if (this.flagAliases[char]) {
|
|
412
440
|
return this.flagAliases[char].name;
|
|
413
441
|
}
|
|
414
|
-
return Object.keys(this.input.flags).find(k =>
|
|
442
|
+
return Object.keys(this.input.flags).find((k) => this.input.flags[k].char === char && char !== undefined && this.input.flags[k].char !== undefined);
|
|
415
443
|
}
|
|
416
444
|
findFlag(arg) {
|
|
417
445
|
const isLong = arg.startsWith('--');
|
|
418
446
|
const short = isLong ? false : arg.startsWith('-');
|
|
419
|
-
const name = isLong ? this.findLongFlag(arg) :
|
|
447
|
+
const name = isLong ? this.findLongFlag(arg) : short ? this.findShortFlag(arg) : undefined;
|
|
420
448
|
return { name, isLong };
|
|
421
449
|
}
|
|
422
450
|
}
|
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
|
|
5
|
+
const index_1 = require("../util/index");
|
|
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,
|
|
85
|
+
const deduped = (0, index_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,13 +141,19 @@ 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
158
|
case 'all': {
|
|
143
159
|
return validateDependsOn(name, relationship.flags);
|
|
@@ -152,7 +168,7 @@ async function validate(parse) {
|
|
|
152
168
|
throw new Error(`Unknown relationship type: ${relationship.type}`);
|
|
153
169
|
}
|
|
154
170
|
}
|
|
155
|
-
})
|
|
171
|
+
});
|
|
156
172
|
}
|
|
157
173
|
validateArgs();
|
|
158
174
|
return validateFlags();
|
package/lib/performance.js
CHANGED
|
@@ -4,10 +4,17 @@ exports.Performance = void 0;
|
|
|
4
4
|
const node_perf_hooks_1 = require("node:perf_hooks");
|
|
5
5
|
const settings_1 = require("./settings");
|
|
6
6
|
class Marker {
|
|
7
|
+
name;
|
|
8
|
+
details;
|
|
9
|
+
module;
|
|
10
|
+
method;
|
|
11
|
+
scope;
|
|
12
|
+
stopped = false;
|
|
13
|
+
startMarker;
|
|
14
|
+
stopMarker;
|
|
7
15
|
constructor(name, details = {}) {
|
|
8
16
|
this.name = name;
|
|
9
17
|
this.details = details;
|
|
10
|
-
this.stopped = false;
|
|
11
18
|
this.startMarker = `${this.name}-start`;
|
|
12
19
|
this.stopMarker = `${this.name}-stop`;
|
|
13
20
|
const [caller, scope] = name.split('#');
|
|
@@ -29,6 +36,9 @@ class Marker {
|
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
class Performance {
|
|
39
|
+
static markers = {};
|
|
40
|
+
static _results = [];
|
|
41
|
+
static _highlights;
|
|
32
42
|
static get enabled() {
|
|
33
43
|
return settings_1.settings.performanceEnabled ?? false;
|
|
34
44
|
}
|
|
@@ -40,7 +50,7 @@ class Performance {
|
|
|
40
50
|
throw new Error('Perf results not available. Did you forget to call await Performance.collect()?');
|
|
41
51
|
}
|
|
42
52
|
static getResult(name) {
|
|
43
|
-
return Performance.results.find(r => r.name === name);
|
|
53
|
+
return Performance.results.find((r) => r.name === name);
|
|
44
54
|
}
|
|
45
55
|
static get highlights() {
|
|
46
56
|
if (!Performance.enabled)
|
|
@@ -76,11 +86,11 @@ class Performance {
|
|
|
76
86
|
const markers = Object.values(Performance.markers);
|
|
77
87
|
if (markers.length === 0)
|
|
78
88
|
return;
|
|
79
|
-
for (const marker of markers.filter(m => !m.stopped)) {
|
|
89
|
+
for (const marker of markers.filter((m) => !m.stopped)) {
|
|
80
90
|
marker.stop();
|
|
81
91
|
}
|
|
82
|
-
return new Promise(resolve => {
|
|
83
|
-
const perfObserver = new node_perf_hooks_1.PerformanceObserver(items => {
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
const perfObserver = new node_perf_hooks_1.PerformanceObserver((items) => {
|
|
84
94
|
for (const entry of items.getEntries()) {
|
|
85
95
|
if (Performance.markers[entry.name]) {
|
|
86
96
|
const marker = Performance.markers[entry.name];
|
|
@@ -94,8 +104,11 @@ class Performance {
|
|
|
94
104
|
});
|
|
95
105
|
}
|
|
96
106
|
}
|
|
97
|
-
const command = Performance.results.find(r => r.name.startsWith('config.runCommand'));
|
|
98
|
-
const commandLoadTime = command
|
|
107
|
+
const command = Performance.results.find((r) => r.name.startsWith('config.runCommand'));
|
|
108
|
+
const commandLoadTime = command
|
|
109
|
+
? Performance.getResult(`plugin.findCommand#${command.details.plugin}.${command.details.command}`)
|
|
110
|
+
?.duration ?? 0
|
|
111
|
+
: 0;
|
|
99
112
|
const pluginLoadTimes = Object.fromEntries(Performance.results
|
|
100
113
|
.filter(({ name }) => name.startsWith('plugin.load#'))
|
|
101
114
|
.sort((a, b) => b.duration - a.duration)
|
|
@@ -185,5 +198,3 @@ class Performance {
|
|
|
185
198
|
}
|
|
186
199
|
}
|
|
187
200
|
exports.Performance = Performance;
|
|
188
|
-
Performance.markers = {};
|
|
189
|
-
Performance._results = [];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.aggregateFlags = void 0;
|
|
4
|
+
const flags_1 = require("../flags");
|
|
5
|
+
const json = (0, flags_1.boolean)({
|
|
6
|
+
description: 'Format output as json.',
|
|
7
|
+
helpGroup: 'GLOBAL',
|
|
8
|
+
});
|
|
9
|
+
function aggregateFlags(flags, baseFlags, enableJsonFlag) {
|
|
10
|
+
const combinedFlags = { ...baseFlags, ...flags };
|
|
11
|
+
return (enableJsonFlag ? { json, ...combinedFlags } : combinedFlags);
|
|
12
|
+
}
|
|
13
|
+
exports.aggregateFlags = aggregateFlags;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cacheCommand = void 0;
|
|
4
|
+
const index_1 = require("./index");
|
|
5
|
+
const aggregate_flags_1 = require("./aggregate-flags");
|
|
6
|
+
const cache_default_value_1 = require("./cache-default-value");
|
|
7
|
+
// In order to collect static properties up the inheritance chain, we need to recursively
|
|
8
|
+
// access the prototypes until there's nothing left. This allows us to combine baseFlags
|
|
9
|
+
// and flags as well as add in the json flag if enableJsonFlag is enabled.
|
|
10
|
+
function mergePrototype(result, cmd) {
|
|
11
|
+
const proto = Object.getPrototypeOf(cmd);
|
|
12
|
+
const filteredProto = (0, index_1.pickBy)(proto, (v) => v !== undefined);
|
|
13
|
+
return Object.keys(proto).length > 0 ? mergePrototype({ ...filteredProto, ...result }, proto) : result;
|
|
14
|
+
}
|
|
15
|
+
async function cacheFlags(cmdFlags, respectNoCacheDefault) {
|
|
16
|
+
const promises = Object.entries(cmdFlags).map(async ([name, flag]) => [
|
|
17
|
+
name,
|
|
18
|
+
{
|
|
19
|
+
name,
|
|
20
|
+
char: flag.char,
|
|
21
|
+
summary: flag.summary,
|
|
22
|
+
hidden: flag.hidden,
|
|
23
|
+
required: flag.required,
|
|
24
|
+
helpLabel: flag.helpLabel,
|
|
25
|
+
helpGroup: flag.helpGroup,
|
|
26
|
+
description: flag.description,
|
|
27
|
+
dependsOn: flag.dependsOn,
|
|
28
|
+
relationships: flag.relationships,
|
|
29
|
+
exclusive: flag.exclusive,
|
|
30
|
+
deprecated: flag.deprecated,
|
|
31
|
+
deprecateAliases: flag.deprecateAliases,
|
|
32
|
+
aliases: flag.aliases,
|
|
33
|
+
charAliases: flag.charAliases,
|
|
34
|
+
noCacheDefault: flag.noCacheDefault,
|
|
35
|
+
...(flag.type === 'boolean'
|
|
36
|
+
? {
|
|
37
|
+
allowNo: flag.allowNo,
|
|
38
|
+
type: flag.type,
|
|
39
|
+
}
|
|
40
|
+
: {
|
|
41
|
+
type: flag.type,
|
|
42
|
+
helpValue: flag.helpValue,
|
|
43
|
+
multiple: flag.multiple,
|
|
44
|
+
options: flag.options,
|
|
45
|
+
delimiter: flag.delimiter,
|
|
46
|
+
default: await (0, cache_default_value_1.cacheDefaultValue)(flag, respectNoCacheDefault),
|
|
47
|
+
hasDynamicHelp: typeof flag.defaultHelp === 'function',
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
return Object.fromEntries(await Promise.all(promises));
|
|
52
|
+
}
|
|
53
|
+
async function cacheArgs(cmdArgs, respectNoCacheDefault) {
|
|
54
|
+
const promises = Object.entries(cmdArgs).map(async ([name, arg]) => [
|
|
55
|
+
name,
|
|
56
|
+
{
|
|
57
|
+
name,
|
|
58
|
+
description: arg.description,
|
|
59
|
+
required: arg.required,
|
|
60
|
+
options: arg.options,
|
|
61
|
+
default: await (0, cache_default_value_1.cacheDefaultValue)(arg, respectNoCacheDefault),
|
|
62
|
+
hidden: arg.hidden,
|
|
63
|
+
noCacheDefault: arg.noCacheDefault,
|
|
64
|
+
},
|
|
65
|
+
]);
|
|
66
|
+
return Object.fromEntries(await Promise.all(promises));
|
|
67
|
+
}
|
|
68
|
+
async function cacheCommand(uncachedCmd, plugin, respectNoCacheDefault = false) {
|
|
69
|
+
const cmd = mergePrototype(uncachedCmd, uncachedCmd);
|
|
70
|
+
const flags = await cacheFlags((0, aggregate_flags_1.aggregateFlags)(cmd.flags, cmd.baseFlags, cmd.enableJsonFlag), respectNoCacheDefault);
|
|
71
|
+
const args = await cacheArgs((0, index_1.ensureArgObject)(cmd.args), respectNoCacheDefault);
|
|
72
|
+
const stdProperties = {
|
|
73
|
+
id: cmd.id,
|
|
74
|
+
summary: cmd.summary,
|
|
75
|
+
description: cmd.description,
|
|
76
|
+
strict: cmd.strict,
|
|
77
|
+
usage: cmd.usage,
|
|
78
|
+
pluginName: plugin && plugin.name,
|
|
79
|
+
pluginAlias: plugin && plugin.alias,
|
|
80
|
+
pluginType: plugin && plugin.type,
|
|
81
|
+
hidden: cmd.hidden,
|
|
82
|
+
state: cmd.state,
|
|
83
|
+
aliases: cmd.aliases || [],
|
|
84
|
+
examples: cmd.examples || cmd.example,
|
|
85
|
+
deprecationOptions: cmd.deprecationOptions,
|
|
86
|
+
deprecateAliases: cmd.deprecateAliases,
|
|
87
|
+
flags,
|
|
88
|
+
args,
|
|
89
|
+
hasDynamicHelp: Object.values(flags).some((f) => f.hasDynamicHelp),
|
|
90
|
+
};
|
|
91
|
+
// do not include these properties in manifest
|
|
92
|
+
const ignoreCommandProperties = [
|
|
93
|
+
'plugin',
|
|
94
|
+
'_flags',
|
|
95
|
+
'_enableJsonFlag',
|
|
96
|
+
'_globalFlags',
|
|
97
|
+
'_baseFlags',
|
|
98
|
+
'baseFlags',
|
|
99
|
+
'_--',
|
|
100
|
+
'_base',
|
|
101
|
+
];
|
|
102
|
+
// Add in any additional properties that are not standard command properties.
|
|
103
|
+
const stdKeysAndIgnored = new Set([...Object.keys(stdProperties), ...ignoreCommandProperties]);
|
|
104
|
+
const keysToAdd = Object.keys(cmd).filter((property) => !stdKeysAndIgnored.has(property));
|
|
105
|
+
const additionalProperties = Object.fromEntries(keysToAdd.map((key) => [key, cmd[key]]));
|
|
106
|
+
return { ...stdProperties, ...additionalProperties };
|
|
107
|
+
}
|
|
108
|
+
exports.cacheCommand = cacheCommand;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cacheDefaultValue = void 0;
|
|
4
|
+
// when no manifest exists, the default is calculated. This may throw, so we need to catch it
|
|
5
|
+
const cacheDefaultValue = async (flagOrArg, respectNoCacheDefault) => {
|
|
6
|
+
if (respectNoCacheDefault && flagOrArg.noCacheDefault)
|
|
7
|
+
return;
|
|
8
|
+
// Prefer the defaultHelp function (returns a friendly string for complex types)
|
|
9
|
+
if (typeof flagOrArg.defaultHelp === 'function') {
|
|
10
|
+
try {
|
|
11
|
+
return await flagOrArg.defaultHelp({ options: flagOrArg, flags: {} });
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// if not specified, try the default function
|
|
18
|
+
if (typeof flagOrArg.default === 'function') {
|
|
19
|
+
try {
|
|
20
|
+
return await flagOrArg.default({ options: flagOrArg, flags: {} });
|
|
21
|
+
}
|
|
22
|
+
catch { }
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return flagOrArg.default;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.cacheDefaultValue = cacheDefaultValue;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { ArgInput } from '
|
|
3
|
-
import { Command } from '
|
|
2
|
+
import { ArgInput } from '../interfaces/parser';
|
|
3
|
+
import { Command } from '../command';
|
|
4
4
|
export declare function pickBy<T extends {
|
|
5
5
|
[s: string]: T[keyof T];
|
|
6
6
|
} | ArrayLike<T[keyof T]>>(obj: T, fn: (i: T[keyof T]) => boolean): Partial<T>;
|
|
@@ -52,4 +52,9 @@ export declare function getPlatform(): NodeJS.Platform;
|
|
|
52
52
|
export declare function readJson<T = unknown>(path: string): Promise<T>;
|
|
53
53
|
export declare function readJsonSync(path: string, parse: false): string;
|
|
54
54
|
export declare function readJsonSync<T = unknown>(path: string, parse?: true): T;
|
|
55
|
+
export declare function mapValues<T extends Record<string, any>, TResult>(obj: {
|
|
56
|
+
[P in keyof T]: T[P];
|
|
57
|
+
}, fn: (i: T[keyof T], k: keyof T) => TResult): {
|
|
58
|
+
[P in keyof T]: TResult;
|
|
59
|
+
};
|
|
55
60
|
export {};
|