@oclif/core 2.9.5 → 2.10.1
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/interfaces/parser.d.ts +6 -3
- package/lib/module-loader.js +3 -1
- 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
|
@@ -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/module-loader.js
CHANGED
|
@@ -100,10 +100,12 @@ class ModuleLoader {
|
|
|
100
100
|
const extension = path.extname(filePath).toLowerCase();
|
|
101
101
|
switch (extension) {
|
|
102
102
|
case '.js':
|
|
103
|
-
|
|
103
|
+
case '.jsx':
|
|
104
104
|
case '.ts':
|
|
105
|
+
case '.tsx':
|
|
105
106
|
return getPackageType.sync(filePath) === 'module';
|
|
106
107
|
case '.mjs':
|
|
108
|
+
case '.mts':
|
|
107
109
|
return true;
|
|
108
110
|
default:
|
|
109
111
|
return false;
|
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;
|