@oclif/core 2.10.0 → 2.11.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/lib/command.js +1 -1
- package/lib/config/config.d.ts +11 -0
- package/lib/config/config.js +15 -2
- package/lib/interfaces/config.d.ts +1 -0
- package/lib/interfaces/parser.d.ts +6 -3
- package/lib/parser/parse.js +70 -14
- package/lib/util.d.ts +1 -0
- package/lib/util.js +7 -1
- package/package.json +1 -1
package/lib/command.js
CHANGED
|
@@ -112,7 +112,7 @@ class Command {
|
|
|
112
112
|
let result;
|
|
113
113
|
try {
|
|
114
114
|
// remove redirected env var to allow subsessions to run autoupdated client
|
|
115
|
-
|
|
115
|
+
this.config.scopedEnvVarKeys('REDIRECTED').map(key => delete process.env[key]);
|
|
116
116
|
await this.init();
|
|
117
117
|
result = await this.run();
|
|
118
118
|
}
|
package/lib/config/config.d.ts
CHANGED
|
@@ -50,7 +50,18 @@ export declare class Config implements IConfig {
|
|
|
50
50
|
runCommand<T = unknown>(id: string, argv?: string[], cachedCommand?: Command.Loadable | null): Promise<T>;
|
|
51
51
|
scopedEnvVar(k: string): string | undefined;
|
|
52
52
|
scopedEnvVarTrue(k: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* this DOES NOT account for bin aliases, use scopedEnvVarKeys instead which will account for bin aliases
|
|
55
|
+
* @param {string} k, the unscoped key you want to get the value for
|
|
56
|
+
* @returns {string} returns the env var key
|
|
57
|
+
*/
|
|
53
58
|
scopedEnvVarKey(k: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* gets the scoped env var keys for a given key, including bin aliases
|
|
61
|
+
* @param {string} k, the env key e.g. 'debug'
|
|
62
|
+
* @returns {string[]} e.g. ['SF_DEBUG', 'SFDX_DEBUG']
|
|
63
|
+
*/
|
|
64
|
+
scopedEnvVarKeys(k: string): string[];
|
|
54
65
|
findCommand(id: string, opts: {
|
|
55
66
|
must: true;
|
|
56
67
|
}): Command.Loadable;
|
package/lib/config/config.js
CHANGED
|
@@ -333,18 +333,31 @@ class Config {
|
|
|
333
333
|
return result;
|
|
334
334
|
}
|
|
335
335
|
scopedEnvVar(k) {
|
|
336
|
-
return process.env[this.
|
|
336
|
+
return process.env[this.scopedEnvVarKeys(k).find(k => process.env[k])];
|
|
337
337
|
}
|
|
338
338
|
scopedEnvVarTrue(k) {
|
|
339
|
-
const v = process.env[this.
|
|
339
|
+
const v = process.env[this.scopedEnvVarKeys(k).find(k => process.env[k])];
|
|
340
340
|
return v === '1' || v === 'true';
|
|
341
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* this DOES NOT account for bin aliases, use scopedEnvVarKeys instead which will account for bin aliases
|
|
344
|
+
* @param {string} k, the unscoped key you want to get the value for
|
|
345
|
+
* @returns {string} returns the env var key
|
|
346
|
+
*/
|
|
342
347
|
scopedEnvVarKey(k) {
|
|
343
348
|
return [this.bin, k]
|
|
344
349
|
.map(p => p.replace(/@/g, '').replace(/[/-]/g, '_'))
|
|
345
350
|
.join('_')
|
|
346
351
|
.toUpperCase();
|
|
347
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* gets the scoped env var keys for a given key, including bin aliases
|
|
355
|
+
* @param {string} k, the env key e.g. 'debug'
|
|
356
|
+
* @returns {string[]} e.g. ['SF_DEBUG', 'SFDX_DEBUG']
|
|
357
|
+
*/
|
|
358
|
+
scopedEnvVarKeys(k) {
|
|
359
|
+
return [this.bin, ...this.binAliases ?? []].map(alias => [alias.replace(/@/g, '').replace(/[/-]/g, '_'), k].join('_').toUpperCase());
|
|
360
|
+
}
|
|
348
361
|
findCommand(id, opts = {}) {
|
|
349
362
|
const lookupId = this.getCmdLookupId(id);
|
|
350
363
|
const command = this._commands.get(lookupId);
|
|
@@ -130,6 +130,7 @@ export interface Config {
|
|
|
130
130
|
findMatches(id: string, argv: string[]): Command.Loadable[];
|
|
131
131
|
scopedEnvVar(key: string): string | undefined;
|
|
132
132
|
scopedEnvVarKey(key: string): string;
|
|
133
|
+
scopedEnvVarKeys(key: string): string[];
|
|
133
134
|
scopedEnvVarTrue(key: string): boolean;
|
|
134
135
|
s3Url(key: string): string;
|
|
135
136
|
s3Key(type: 'versioned' | 'unversioned', ext: '.tar.gz' | '.tar.xz', options?: Config.s3Key.Options): string;
|
|
@@ -284,7 +284,7 @@ export type BooleanFlag<T> = FlagProps & BooleanFlagProps & {
|
|
|
284
284
|
* specifying a default of false is the same as not specifying a default
|
|
285
285
|
*/
|
|
286
286
|
default?: FlagDefault<boolean>;
|
|
287
|
-
parse: (input: boolean, context:
|
|
287
|
+
parse: (input: boolean, context: FlagParserContext, opts: FlagProps & BooleanFlagProps) => Promise<T>;
|
|
288
288
|
};
|
|
289
289
|
export type OptionFlagDefaults<T, P = CustomOptions, M = false> = FlagProps & OptionFlagProps & {
|
|
290
290
|
parse: FlagParser<T, string, P>;
|
|
@@ -327,7 +327,7 @@ export type Input<TFlags extends FlagOutput, BFlags extends FlagOutput, AFlags e
|
|
|
327
327
|
baseFlags?: FlagInput<BFlags>;
|
|
328
328
|
args?: ArgInput<AFlags>;
|
|
329
329
|
strict?: boolean;
|
|
330
|
-
context?:
|
|
330
|
+
context?: ParserContext;
|
|
331
331
|
'--'?: boolean;
|
|
332
332
|
};
|
|
333
333
|
export type ParserInput = {
|
|
@@ -335,9 +335,12 @@ export type ParserInput = {
|
|
|
335
335
|
flags: FlagInput<any>;
|
|
336
336
|
args: ArgInput<any>;
|
|
337
337
|
strict: boolean;
|
|
338
|
-
context:
|
|
338
|
+
context: ParserContext | undefined;
|
|
339
339
|
'--'?: boolean;
|
|
340
340
|
};
|
|
341
|
+
export type ParserContext = Command & {
|
|
342
|
+
token?: FlagToken | ArgToken;
|
|
343
|
+
};
|
|
341
344
|
export type CompletionContext = {
|
|
342
345
|
args?: {
|
|
343
346
|
[name: string]: string;
|
package/lib/parser/parse.js
CHANGED
|
@@ -13,16 +13,41 @@ try {
|
|
|
13
13
|
catch {
|
|
14
14
|
debug = () => { };
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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;
|
|
19
33
|
if (stdin.isTTY)
|
|
20
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 () => {
|
|
43
|
+
const { stdin, stdout } = process;
|
|
21
44
|
// process.stdin.isTTY is true whenever it's running in a terminal.
|
|
22
45
|
// process.stdin.isTTY is undefined when it's running in a pipe, e.g. echo 'foo' | my-cli command
|
|
23
46
|
// process.stdin.isTTY is undefined when it's running in a spawned process, even if there's no pipe.
|
|
24
47
|
// This means that reading from stdin could hang indefinitely while waiting for a non-existent pipe.
|
|
25
48
|
// Because of this, we have to set a timeout to prevent the process from hanging.
|
|
49
|
+
if (stdin.isTTY)
|
|
50
|
+
return null;
|
|
26
51
|
return new Promise(resolve => {
|
|
27
52
|
let result = '';
|
|
28
53
|
const ac = new AbortController();
|
|
@@ -49,6 +74,13 @@ const readStdin = async () => {
|
|
|
49
74
|
}, { once: true });
|
|
50
75
|
});
|
|
51
76
|
};
|
|
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 ? readStdinLegacy() : readStdinWithTimeout();
|
|
83
|
+
};
|
|
52
84
|
function isNegativeNumber(input) {
|
|
53
85
|
return /^-\d/g.test(input);
|
|
54
86
|
}
|
|
@@ -56,7 +88,7 @@ class Parser {
|
|
|
56
88
|
constructor(input) {
|
|
57
89
|
this.input = input;
|
|
58
90
|
this.raw = [];
|
|
59
|
-
this.context = input.context
|
|
91
|
+
this.context = input.context ?? {};
|
|
60
92
|
this.argv = [...input.argv];
|
|
61
93
|
this._setNames();
|
|
62
94
|
this.booleanFlags = (0, util_1.pickBy)(input.flags, f => f.type === 'boolean');
|
|
@@ -180,14 +212,23 @@ class Parser {
|
|
|
180
212
|
throw new errors_1.FlagInvalidOptionError(flag, input);
|
|
181
213
|
return input;
|
|
182
214
|
};
|
|
183
|
-
const parseFlagOrThrowError = async (input, flag,
|
|
215
|
+
const parseFlagOrThrowError = async (input, flag, context, token) => {
|
|
184
216
|
if (!flag.parse)
|
|
185
217
|
return input;
|
|
218
|
+
const ctx = {
|
|
219
|
+
...context,
|
|
220
|
+
token,
|
|
221
|
+
error: context?.error,
|
|
222
|
+
exit: context?.exit,
|
|
223
|
+
log: context?.log,
|
|
224
|
+
logToStderr: context?.logToStderr,
|
|
225
|
+
warn: context?.warn,
|
|
226
|
+
};
|
|
186
227
|
try {
|
|
187
228
|
if (flag.type === 'boolean') {
|
|
188
|
-
return await flag.parse(input,
|
|
229
|
+
return await flag.parse(input, ctx, flag);
|
|
189
230
|
}
|
|
190
|
-
return await flag.parse(input,
|
|
231
|
+
return await flag.parse(input, ctx, flag);
|
|
191
232
|
}
|
|
192
233
|
catch (error) {
|
|
193
234
|
error.message = `Parsing --${flag.name} \n\t${error.message}\nSee more help with --help`;
|
|
@@ -202,8 +243,11 @@ class Parser {
|
|
|
202
243
|
// user provided some input
|
|
203
244
|
if (tokenLength) {
|
|
204
245
|
// boolean
|
|
205
|
-
if (fws.inputFlag.flag.type === 'boolean' && fws.tokens
|
|
206
|
-
return {
|
|
246
|
+
if (fws.inputFlag.flag.type === 'boolean' && (0, util_1.last)(fws.tokens)?.input) {
|
|
247
|
+
return {
|
|
248
|
+
...fws,
|
|
249
|
+
valueFunction: async (i) => parseFlagOrThrowError((0, util_1.last)(i.tokens)?.input !== `--no-${i.inputFlag.name}`, i.inputFlag.flag, this.context, (0, util_1.last)(i.tokens)),
|
|
250
|
+
};
|
|
207
251
|
}
|
|
208
252
|
// multiple with custom delimiter
|
|
209
253
|
if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.delimiter && fws.inputFlag.flag.multiple) {
|
|
@@ -211,26 +255,38 @@ class Parser {
|
|
|
211
255
|
...fws, valueFunction: async (i) => (await Promise.all(((i.tokens ?? []).flatMap(token => token.input.split(i.inputFlag.flag.delimiter)))
|
|
212
256
|
// trim, and remove surrounding doubleQuotes (which would hav been needed if the elements contain spaces)
|
|
213
257
|
.map(v => v.trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1'))
|
|
214
|
-
.map(async (v) => parseFlagOrThrowError(v, i.inputFlag.flag, { ...i.tokens
|
|
258
|
+
.map(async (v) => parseFlagOrThrowError(v, i.inputFlag.flag, this.context, { ...(0, util_1.last)(i.tokens), input: v })))).map(v => validateOptions(i.inputFlag.flag, v)),
|
|
215
259
|
};
|
|
216
260
|
}
|
|
217
261
|
// multiple in the oclif-core style
|
|
218
262
|
if (fws.inputFlag.flag.type === 'option' && fws.inputFlag.flag.multiple) {
|
|
219
|
-
return {
|
|
263
|
+
return {
|
|
264
|
+
...fws,
|
|
265
|
+
valueFunction: async (i) => Promise.all((fws.tokens ?? []).map(token => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, token.input), i.inputFlag.flag, this.context, token))),
|
|
266
|
+
};
|
|
220
267
|
}
|
|
221
268
|
// simple option flag
|
|
222
269
|
if (fws.inputFlag.flag.type === 'option') {
|
|
223
|
-
return {
|
|
270
|
+
return {
|
|
271
|
+
...fws,
|
|
272
|
+
valueFunction: async (i) => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, (0, util_1.last)(fws.tokens)?.input), i.inputFlag.flag, this.context, (0, util_1.last)(fws.tokens)),
|
|
273
|
+
};
|
|
224
274
|
}
|
|
225
275
|
}
|
|
226
276
|
// no input: env flags
|
|
227
277
|
if (fws.inputFlag.flag.env && process.env[fws.inputFlag.flag.env]) {
|
|
228
278
|
const valueFromEnv = process.env[fws.inputFlag.flag.env];
|
|
229
279
|
if (fws.inputFlag.flag.type === 'option' && valueFromEnv) {
|
|
230
|
-
return {
|
|
280
|
+
return {
|
|
281
|
+
...fws,
|
|
282
|
+
valueFunction: async (i) => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, valueFromEnv), i.inputFlag.flag, this.context),
|
|
283
|
+
};
|
|
231
284
|
}
|
|
232
285
|
if (fws.inputFlag.flag.type === 'boolean') {
|
|
233
|
-
return {
|
|
286
|
+
return {
|
|
287
|
+
...fws,
|
|
288
|
+
valueFunction: async (i) => Promise.resolve((0, util_1.isTruthy)(process.env[i.inputFlag.flag.env] ?? 'false')),
|
|
289
|
+
};
|
|
234
290
|
}
|
|
235
291
|
}
|
|
236
292
|
// no input, but flag has default value
|
package/lib/util.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare function pickBy<T extends {
|
|
|
5
5
|
} | ArrayLike<T[keyof T]>>(obj: T, fn: (i: T[keyof T]) => boolean): Partial<T>;
|
|
6
6
|
export declare function compact<T>(a: (T | undefined)[]): T[];
|
|
7
7
|
export declare function uniqBy<T>(arr: T[], fn: (cur: T) => any): T[];
|
|
8
|
+
export declare function last<T>(arr?: T[]): T | undefined;
|
|
8
9
|
type SortTypes = string | number | undefined | boolean;
|
|
9
10
|
export declare function sortBy<T>(arr: T[], fn: (i: T) => SortTypes | SortTypes[]): T[];
|
|
10
11
|
export declare function castArray<T>(input?: T | T[]): T[];
|
package/lib/util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensureArgObject = exports.requireJson = exports.isNotFalsy = exports.isTruthy = exports.fileExists = exports.dirExists = exports.capitalize = exports.sumBy = exports.maxBy = exports.isProd = exports.castArray = exports.sortBy = exports.uniqBy = exports.compact = exports.pickBy = void 0;
|
|
3
|
+
exports.ensureArgObject = exports.requireJson = exports.isNotFalsy = exports.isTruthy = exports.fileExists = exports.dirExists = exports.capitalize = exports.sumBy = exports.maxBy = exports.isProd = exports.castArray = exports.sortBy = exports.last = exports.uniqBy = exports.compact = exports.pickBy = void 0;
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
function pickBy(obj, fn) {
|
|
@@ -23,6 +23,12 @@ function uniqBy(arr, fn) {
|
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
exports.uniqBy = uniqBy;
|
|
26
|
+
function last(arr) {
|
|
27
|
+
if (!arr)
|
|
28
|
+
return;
|
|
29
|
+
return arr.slice(-1)[0];
|
|
30
|
+
}
|
|
31
|
+
exports.last = last;
|
|
26
32
|
function sortBy(arr, fn) {
|
|
27
33
|
function compare(a, b) {
|
|
28
34
|
a = a === undefined ? 0 : a;
|