@oclif/core 2.10.0 → 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.
@@ -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: Command, opts: FlagProps & BooleanFlagProps) => Promise<T>;
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?: Command;
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: Command | undefined;
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;
@@ -13,16 +13,41 @@ try {
13
13
  catch {
14
14
  debug = () => { };
15
15
  }
16
- const readStdin = async () => {
17
- const { stdin, stdout } = process;
18
- debug('stdin.isTTY', stdin.isTTY);
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, token, context = {}) => {
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, { ...context, token }, flag);
229
+ return await flag.parse(input, ctx, flag);
189
230
  }
190
- return await flag.parse(input, { ...context, token }, flag);
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?.at(-1)?.input) {
206
- return { ...fws, valueFunction: async (i) => parseFlagOrThrowError(i.tokens?.at(-1)?.input !== `--no-${i.inputFlag.name}`, i.inputFlag.flag, i.tokens?.at(-1), this.context) };
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?.at(-1), input: v }, this.context)))).map(v => validateOptions(i.inputFlag.flag, v)),
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 { ...fws, valueFunction: async (i) => Promise.all((fws.tokens ?? []).map(token => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, token.input), i.inputFlag.flag, token, this.context))) };
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 { ...fws, valueFunction: async (i) => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, fws.tokens?.at(-1)?.input), i.inputFlag.flag, fws.tokens?.at(-1), this.context) };
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 { ...fws, valueFunction: async (i) => parseFlagOrThrowError(validateOptions(i.inputFlag.flag, valueFromEnv), i.inputFlag.flag, this.context) };
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 { ...fws, valueFunction: async (i) => Promise.resolve((0, util_1.isTruthy)(process.env[i.inputFlag.flag.env] ?? 'false')) };
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;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oclif/core",
3
3
  "description": "base library for oclif CLIs",
4
- "version": "2.10.0",
4
+ "version": "2.10.1",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {