@karmaniverous/get-dotenv 4.5.0 → 4.5.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.
@@ -6,8 +6,8 @@ import fs$v, { writeFileSync as writeFileSync$1, statSync as statSync$1, readFil
6
6
  import process$3, { platform as platform$1, hrtime, execPath, execArgv } from 'node:process';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import { StringDecoder } from 'node:string_decoder';
9
- import tty from 'node:tty';
10
9
  import { debuglog, stripVTControlCharacters, inspect, promisify as promisify$1, callbackify, aborted } from 'node:util';
10
+ import tty from 'node:tty';
11
11
  import require$$0$3 from 'child_process';
12
12
  import require$$0$2, { join } from 'path';
13
13
  import require$$0$1 from 'fs';
@@ -4120,56 +4120,220 @@ const getSubprocessResult = ({stdout}) => {
4120
4120
  throw new TypeError(`Unexpected "${typeof stdout}" stdout in template expression`);
4121
4121
  };
4122
4122
 
4123
- // eslint-disable-next-line no-warning-comments
4124
- // TODO: Use a better method when it's added to Node.js (https://github.com/nodejs/node/pull/40240)
4125
- const hasColors = tty.WriteStream.prototype.hasColors();
4123
+ const isStandardStream = stream => STANDARD_STREAMS.includes(stream);
4124
+ const STANDARD_STREAMS = [process$3.stdin, process$3.stdout, process$3.stderr];
4125
+ const STANDARD_STREAMS_ALIASES = ['stdin', 'stdout', 'stderr'];
4126
+ const getStreamName = fdNumber => STANDARD_STREAMS_ALIASES[fdNumber] ?? `stdio[${fdNumber}]`;
4126
4127
 
4127
- // Intentionally not using template literal for performance.
4128
- const format = (startCode, endCode) => hasColors ? string => '\u001B[' + startCode + 'm' + string + '\u001B[' + endCode + 'm' : string => string;
4129
- const bold = format(1, 22);
4130
- const gray = format(90, 39);
4128
+ // Some options can have different values for `stdout`/`stderr`/`fd3`.
4129
+ // This normalizes those to array of values.
4130
+ // For example, `{verbose: {stdout: 'none', stderr: 'full'}}` becomes `{verbose: ['none', 'none', 'full']}`
4131
+ const normalizeFdSpecificOptions = options => {
4132
+ const optionsCopy = {...options};
4131
4133
 
4132
- const redBright = format(91, 39);
4133
- const yellowBright = format(93, 39);
4134
+ for (const optionName of FD_SPECIFIC_OPTIONS) {
4135
+ optionsCopy[optionName] = normalizeFdSpecificOption(options, optionName);
4136
+ }
4137
+
4138
+ return optionsCopy;
4139
+ };
4140
+
4141
+ const normalizeFdSpecificOption = (options, optionName) => {
4142
+ const optionBaseArray = Array.from({length: getStdioLength(options) + 1});
4143
+ const optionArray = normalizeFdSpecificValue(options[optionName], optionBaseArray, optionName);
4144
+ return addDefaultValue$1(optionArray, optionName);
4145
+ };
4146
+
4147
+ const getStdioLength = ({stdio}) => Array.isArray(stdio)
4148
+ ? Math.max(stdio.length, STANDARD_STREAMS_ALIASES.length)
4149
+ : STANDARD_STREAMS_ALIASES.length;
4150
+
4151
+ const normalizeFdSpecificValue = (optionValue, optionArray, optionName) => isPlainObject(optionValue)
4152
+ ? normalizeOptionObject(optionValue, optionArray, optionName)
4153
+ : optionArray.fill(optionValue);
4154
+
4155
+ const normalizeOptionObject = (optionValue, optionArray, optionName) => {
4156
+ for (const fdName of Object.keys(optionValue).sort(compareFdName)) {
4157
+ for (const fdNumber of parseFdName(fdName, optionName, optionArray)) {
4158
+ optionArray[fdNumber] = optionValue[fdName];
4159
+ }
4160
+ }
4161
+
4162
+ return optionArray;
4163
+ };
4164
+
4165
+ // Ensure priority order when setting both `stdout`/`stderr`, `fd1`/`fd2`, and `all`
4166
+ const compareFdName = (fdNameA, fdNameB) => getFdNameOrder(fdNameA) < getFdNameOrder(fdNameB) ? 1 : -1;
4167
+
4168
+ const getFdNameOrder = fdName => {
4169
+ if (fdName === 'stdout' || fdName === 'stderr') {
4170
+ return 0;
4171
+ }
4172
+
4173
+ return fdName === 'all' ? 2 : 1;
4174
+ };
4175
+
4176
+ const parseFdName = (fdName, optionName, optionArray) => {
4177
+ if (fdName === 'ipc') {
4178
+ return [optionArray.length - 1];
4179
+ }
4180
+
4181
+ const fdNumber = parseFd(fdName);
4182
+ if (fdNumber === undefined || fdNumber === 0) {
4183
+ throw new TypeError(`"${optionName}.${fdName}" is invalid.
4184
+ It must be "${optionName}.stdout", "${optionName}.stderr", "${optionName}.all", "${optionName}.ipc", or "${optionName}.fd3", "${optionName}.fd4" (and so on).`);
4185
+ }
4186
+
4187
+ if (fdNumber >= optionArray.length) {
4188
+ throw new TypeError(`"${optionName}.${fdName}" is invalid: that file descriptor does not exist.
4189
+ Please set the "stdio" option to ensure that file descriptor exists.`);
4190
+ }
4191
+
4192
+ return fdNumber === 'all' ? [1, 2] : [fdNumber];
4193
+ };
4194
+
4195
+ // Use the same syntax for fd-specific options and the `from`/`to` options
4196
+ const parseFd = fdName => {
4197
+ if (fdName === 'all') {
4198
+ return fdName;
4199
+ }
4200
+
4201
+ if (STANDARD_STREAMS_ALIASES.includes(fdName)) {
4202
+ return STANDARD_STREAMS_ALIASES.indexOf(fdName);
4203
+ }
4204
+
4205
+ const regexpResult = FD_REGEXP.exec(fdName);
4206
+ if (regexpResult !== null) {
4207
+ return Number(regexpResult[1]);
4208
+ }
4209
+ };
4210
+
4211
+ const FD_REGEXP = /^fd(\d+)$/;
4212
+
4213
+ const addDefaultValue$1 = (optionArray, optionName) => optionArray.map(optionValue => optionValue === undefined
4214
+ ? DEFAULT_OPTIONS[optionName]
4215
+ : optionValue);
4134
4216
 
4135
4217
  // Default value for the `verbose` option
4136
4218
  const verboseDefault = debuglog('execa').enabled ? 'full' : 'none';
4137
4219
 
4138
- // Information computed before spawning, used by the `verbose` option
4139
- const getVerboseInfo = verbose => {
4140
- const verboseId = isVerbose(verbose) ? VERBOSE_ID++ : undefined;
4141
- validateVerbose(verbose);
4142
- return {verbose, verboseId};
4220
+ const DEFAULT_OPTIONS = {
4221
+ lines: false,
4222
+ buffer: true,
4223
+ maxBuffer: 1000 * 1000 * 100,
4224
+ verbose: verboseDefault,
4225
+ stripFinalNewline: true,
4143
4226
  };
4144
4227
 
4145
- // Prepending the `pid` is useful when multiple commands print their output at the same time.
4146
- // However, we cannot use the real PID since this is not available with `child_process.spawnSync()`.
4147
- // Also, we cannot use the real PID if we want to print it before `child_process.spawn()` is run.
4148
- // As a pro, it is shorter than a normal PID and never re-uses the same id.
4149
- // As a con, it cannot be used to send signals.
4150
- let VERBOSE_ID = 0n;
4228
+ // List of options which can have different values for `stdout`/`stderr`
4229
+ const FD_SPECIFIC_OPTIONS = ['lines', 'buffer', 'maxBuffer', 'verbose', 'stripFinalNewline'];
4230
+
4231
+ // Retrieve fd-specific option
4232
+ const getFdSpecificValue = (optionArray, fdNumber) => fdNumber === 'ipc'
4233
+ ? optionArray.at(-1)
4234
+ : optionArray[fdNumber];
4151
4235
 
4152
4236
  // The `verbose` option can have different values for `stdout`/`stderr`
4153
- const isVerbose = verbose => verbose.some(fdVerbose => fdVerbose !== 'none');
4237
+ const isVerbose = ({verbose}, fdNumber) => getFdVerbose(verbose, fdNumber) !== 'none';
4154
4238
 
4155
- const validateVerbose = verbose => {
4156
- for (const verboseItem of verbose) {
4157
- if (verboseItem === false) {
4158
- throw new TypeError('The "verbose: false" option was renamed to "verbose: \'none\'".');
4159
- }
4239
+ // Whether IPC and output and logged
4240
+ const isFullVerbose = ({verbose}, fdNumber) => !['none', 'short'].includes(getFdVerbose(verbose, fdNumber));
4160
4241
 
4161
- if (verboseItem === true) {
4162
- throw new TypeError('The "verbose: true" option was renamed to "verbose: \'short\'".');
4163
- }
4242
+ // The `verbose` option can be a function to customize logging
4243
+ const getVerboseFunction = ({verbose}, fdNumber) => {
4244
+ const fdVerbose = getFdVerbose(verbose, fdNumber);
4245
+ return isVerboseFunction(fdVerbose) ? fdVerbose : undefined;
4246
+ };
4164
4247
 
4165
- if (!VERBOSE_VALUES.has(verboseItem)) {
4166
- const allowedValues = [...VERBOSE_VALUES].map(allowedValue => `'${allowedValue}'`).join(', ');
4167
- throw new TypeError(`The "verbose" option must not be ${verboseItem}. Allowed values are: ${allowedValues}.`);
4168
- }
4248
+ // When using `verbose: {stdout, stderr, fd3, ipc}`:
4249
+ // - `verbose.stdout|stderr|fd3` is used for 'output'
4250
+ // - `verbose.ipc` is only used for 'ipc'
4251
+ // - highest `verbose.*` value is used for 'command', 'error' and 'duration'
4252
+ const getFdVerbose = (verbose, fdNumber) => fdNumber === undefined
4253
+ ? getFdGenericVerbose(verbose)
4254
+ : getFdSpecificValue(verbose, fdNumber);
4255
+
4256
+ // When using `verbose: {stdout, stderr, fd3, ipc}` and logging is not specific to a file descriptor.
4257
+ // We then use the highest `verbose.*` value, using the following order:
4258
+ // - function > 'full' > 'short' > 'none'
4259
+ // - if several functions are defined: stdout > stderr > fd3 > ipc
4260
+ const getFdGenericVerbose = verbose => verbose.find(fdVerbose => isVerboseFunction(fdVerbose))
4261
+ ?? VERBOSE_VALUES.findLast(fdVerbose => verbose.includes(fdVerbose));
4262
+
4263
+ // Whether the `verbose` option is customized using a function
4264
+ const isVerboseFunction = fdVerbose => typeof fdVerbose === 'function';
4265
+
4266
+ const VERBOSE_VALUES = ['none', 'short', 'full'];
4267
+
4268
+ // Compute `result.command` and `result.escapedCommand`
4269
+ const joinCommand = (filePath, rawArguments) => {
4270
+ const fileAndArguments = [filePath, ...rawArguments];
4271
+ const command = fileAndArguments.join(' ');
4272
+ const escapedCommand = fileAndArguments
4273
+ .map(fileAndArgument => quoteString(escapeControlCharacters(fileAndArgument)))
4274
+ .join(' ');
4275
+ return {command, escapedCommand};
4276
+ };
4277
+
4278
+ // Remove ANSI sequences and escape control characters and newlines
4279
+ const escapeLines = lines => stripVTControlCharacters(lines)
4280
+ .split('\n')
4281
+ .map(line => escapeControlCharacters(line))
4282
+ .join('\n');
4283
+
4284
+ const escapeControlCharacters = line => line.replaceAll(SPECIAL_CHAR_REGEXP, character => escapeControlCharacter(character));
4285
+
4286
+ const escapeControlCharacter = character => {
4287
+ const commonEscape = COMMON_ESCAPES[character];
4288
+ if (commonEscape !== undefined) {
4289
+ return commonEscape;
4290
+ }
4291
+
4292
+ const codepoint = character.codePointAt(0);
4293
+ const codepointHex = codepoint.toString(16);
4294
+ return codepoint <= ASTRAL_START
4295
+ ? `\\u${codepointHex.padStart(4, '0')}`
4296
+ : `\\U${codepointHex}`;
4297
+ };
4298
+
4299
+ // Characters that would create issues when printed are escaped using the \u or \U notation.
4300
+ // Those include control characters and newlines.
4301
+ // The \u and \U notation is Bash specific, but there is no way to do this in a shell-agnostic way.
4302
+ // Some shells do not even have a way to print those characters in an escaped fashion.
4303
+ // Therefore, we prioritize printing those safely, instead of allowing those to be copy-pasted.
4304
+ // List of Unicode character categories: https://www.fileformat.info/info/unicode/category/index.htm
4305
+ const SPECIAL_CHAR_REGEXP = /\p{Separator}|\p{Other}/gu;
4306
+
4307
+ // Accepted by $'...' in Bash.
4308
+ // Exclude \a \e \v which are accepted in Bash but not in JavaScript (except \v) and JSON.
4309
+ const COMMON_ESCAPES = {
4310
+ ' ': ' ',
4311
+ '\b': '\\b',
4312
+ '\f': '\\f',
4313
+ '\n': '\\n',
4314
+ '\r': '\\r',
4315
+ '\t': '\\t',
4316
+ };
4317
+
4318
+ // Up until that codepoint, \u notation can be used instead of \U
4319
+ const ASTRAL_START = 65_535;
4320
+
4321
+ // Some characters are shell-specific, i.e. need to be escaped when the command is copy-pasted then run.
4322
+ // Escaping is shell-specific. We cannot know which shell is used: `process.platform` detection is not enough.
4323
+ // For example, Windows users could be using `cmd.exe`, Powershell or Bash for Windows which all use different escaping.
4324
+ // We use '...' on Unix, which is POSIX shell compliant and escape all characters but ' so this is fairly safe.
4325
+ // On Windows, we assume cmd.exe is used and escape with "...", which also works with Powershell.
4326
+ const quoteString = escapedArgument => {
4327
+ if (NO_ESCAPE_REGEXP.test(escapedArgument)) {
4328
+ return escapedArgument;
4169
4329
  }
4330
+
4331
+ return platform$1 === 'win32'
4332
+ ? `"${escapedArgument.replaceAll('"', '""')}"`
4333
+ : `'${escapedArgument.replaceAll('\'', '\'\\\'\'')}'`;
4170
4334
  };
4171
4335
 
4172
- const VERBOSE_VALUES = new Set(['none', 'short', 'full']);
4336
+ const NO_ESCAPE_REGEXP = /^[\w./-]+$/;
4173
4337
 
4174
4338
  function isUnicodeSupported() {
4175
4339
  if (process$3.platform !== 'win32') {
@@ -4463,120 +4627,126 @@ const fallbackSymbols = {...common$7, ...specialFallbackSymbols};
4463
4627
  const shouldUseMain = isUnicodeSupported();
4464
4628
  const figures = shouldUseMain ? mainSymbols : fallbackSymbols;
4465
4629
 
4466
- // Compute `result.command` and `result.escapedCommand`
4467
- const joinCommand = (filePath, rawArguments) => {
4468
- const fileAndArguments = [filePath, ...rawArguments];
4469
- const command = fileAndArguments.join(' ');
4470
- const escapedCommand = fileAndArguments
4471
- .map(fileAndArgument => quoteString(escapeControlCharacters(fileAndArgument)))
4472
- .join(' ');
4473
- return {command, escapedCommand};
4630
+ // eslint-disable-next-line no-warning-comments
4631
+ // TODO: Use a better method when it's added to Node.js (https://github.com/nodejs/node/pull/40240)
4632
+ const hasColors = tty.WriteStream.prototype.hasColors();
4633
+
4634
+ // Intentionally not using template literal for performance.
4635
+ const format = (startCode, endCode) => hasColors ? string => '\u001B[' + startCode + 'm' + string + '\u001B[' + endCode + 'm' : string => string;
4636
+ const bold = format(1, 22);
4637
+ const gray = format(90, 39);
4638
+
4639
+ const redBright = format(91, 39);
4640
+ const yellowBright = format(93, 39);
4641
+
4642
+ // Default when `verbose` is not a function
4643
+ const defaultVerboseFunction = ({
4644
+ type,
4645
+ message,
4646
+ timestamp,
4647
+ piped,
4648
+ commandId,
4649
+ result: {failed = false} = {},
4650
+ options: {reject = true},
4651
+ }) => {
4652
+ const timestampString = serializeTimestamp(timestamp);
4653
+ const icon = ICONS[type]({failed, reject, piped});
4654
+ const color = COLORS[type]({reject});
4655
+ return `${gray(`[${timestampString}]`)} ${gray(`[${commandId}]`)} ${color(icon)} ${color(message)}`;
4474
4656
  };
4475
4657
 
4476
- // Remove ANSI sequences and escape control characters and newlines
4477
- const escapeLines = lines => stripVTControlCharacters(lines)
4478
- .split('\n')
4479
- .map(line => escapeControlCharacters(line))
4480
- .join('\n');
4658
+ // Prepending the timestamp allows debugging the slow paths of a subprocess
4659
+ const serializeTimestamp = timestamp => `${padField(timestamp.getHours(), 2)}:${padField(timestamp.getMinutes(), 2)}:${padField(timestamp.getSeconds(), 2)}.${padField(timestamp.getMilliseconds(), 3)}`;
4481
4660
 
4482
- const escapeControlCharacters = line => line.replaceAll(SPECIAL_CHAR_REGEXP, character => escapeControlCharacter(character));
4661
+ const padField = (field, padding) => String(field).padStart(padding, '0');
4483
4662
 
4484
- const escapeControlCharacter = character => {
4485
- const commonEscape = COMMON_ESCAPES[character];
4486
- if (commonEscape !== undefined) {
4487
- return commonEscape;
4663
+ const getFinalIcon = ({failed, reject}) => {
4664
+ if (!failed) {
4665
+ return figures.tick;
4488
4666
  }
4489
4667
 
4490
- const codepoint = character.codePointAt(0);
4491
- const codepointHex = codepoint.toString(16);
4492
- return codepoint <= ASTRAL_START
4493
- ? `\\u${codepointHex.padStart(4, '0')}`
4494
- : `\\U${codepointHex}`;
4668
+ return reject ? figures.cross : figures.warning;
4495
4669
  };
4496
4670
 
4497
- // Characters that would create issues when printed are escaped using the \u or \U notation.
4498
- // Those include control characters and newlines.
4499
- // The \u and \U notation is Bash specific, but there is no way to do this in a shell-agnostic way.
4500
- // Some shells do not even have a way to print those characters in an escaped fashion.
4501
- // Therefore, we prioritize printing those safely, instead of allowing those to be copy-pasted.
4502
- // List of Unicode character categories: https://www.fileformat.info/info/unicode/category/index.htm
4503
- const SPECIAL_CHAR_REGEXP = /\p{Separator}|\p{Other}/gu;
4671
+ const ICONS = {
4672
+ command: ({piped}) => piped ? '|' : '$',
4673
+ output: () => ' ',
4674
+ ipc: () => '*',
4675
+ error: getFinalIcon,
4676
+ duration: getFinalIcon,
4677
+ };
4504
4678
 
4505
- // Accepted by $'...' in Bash.
4506
- // Exclude \a \e \v which are accepted in Bash but not in JavaScript (except \v) and JSON.
4507
- const COMMON_ESCAPES = {
4508
- ' ': ' ',
4509
- '\b': '\\b',
4510
- '\f': '\\f',
4511
- '\n': '\\n',
4512
- '\r': '\\r',
4513
- '\t': '\\t',
4679
+ const identity$1 = string => string;
4680
+
4681
+ const COLORS = {
4682
+ command: () => bold,
4683
+ output: () => identity$1,
4684
+ ipc: () => identity$1,
4685
+ error: ({reject}) => reject ? redBright : yellowBright,
4686
+ duration: () => gray,
4514
4687
  };
4515
4688
 
4516
- // Up until that codepoint, \u notation can be used instead of \U
4517
- const ASTRAL_START = 65_535;
4689
+ // Apply the `verbose` function on each line
4690
+ const applyVerboseOnLines = (printedLines, verboseInfo, fdNumber) => {
4691
+ const verboseFunction = getVerboseFunction(verboseInfo, fdNumber);
4692
+ return printedLines
4693
+ .map(({verboseLine, verboseObject}) => applyVerboseFunction(verboseLine, verboseObject, verboseFunction))
4694
+ .filter(printedLine => printedLine !== undefined)
4695
+ .map(printedLine => appendNewline(printedLine))
4696
+ .join('');
4697
+ };
4518
4698
 
4519
- // Some characters are shell-specific, i.e. need to be escaped when the command is copy-pasted then run.
4520
- // Escaping is shell-specific. We cannot know which shell is used: `process.platform` detection is not enough.
4521
- // For example, Windows users could be using `cmd.exe`, Powershell or Bash for Windows which all use different escaping.
4522
- // We use '...' on Unix, which is POSIX shell compliant and escape all characters but ' so this is fairly safe.
4523
- // On Windows, we assume cmd.exe is used and escape with "...", which also works with Powershell.
4524
- const quoteString = escapedArgument => {
4525
- if (NO_ESCAPE_REGEXP.test(escapedArgument)) {
4526
- return escapedArgument;
4699
+ const applyVerboseFunction = (verboseLine, verboseObject, verboseFunction) => {
4700
+ if (verboseFunction === undefined) {
4701
+ return verboseLine;
4527
4702
  }
4528
4703
 
4529
- return platform$1 === 'win32'
4530
- ? `"${escapedArgument.replaceAll('"', '""')}"`
4531
- : `'${escapedArgument.replaceAll('\'', '\'\\\'\'')}'`;
4704
+ const printedLine = verboseFunction(verboseLine, verboseObject);
4705
+ if (typeof printedLine === 'string') {
4706
+ return printedLine;
4707
+ }
4532
4708
  };
4533
4709
 
4534
- const NO_ESCAPE_REGEXP = /^[\w./-]+$/;
4710
+ const appendNewline = printedLine => printedLine.endsWith('\n')
4711
+ ? printedLine
4712
+ : `${printedLine}\n`;
4535
4713
 
4536
4714
  // Write synchronously to ensure lines are properly ordered and not interleaved with `stdout`
4537
- const verboseLog = (string, verboseId, icon, color) => {
4538
- const prefixedLines = addPrefix(string, verboseId, icon, color);
4539
- writeFileSync$1(STDERR_FD, `${prefixedLines}\n`);
4715
+ const verboseLog = ({type, verboseMessage, fdNumber, verboseInfo, result}) => {
4716
+ const verboseObject = getVerboseObject({type, result, verboseInfo});
4717
+ const printedLines = getPrintedLines(verboseMessage, verboseObject);
4718
+ const finalLines = applyVerboseOnLines(printedLines, verboseInfo, fdNumber);
4719
+ writeFileSync$1(STDERR_FD, finalLines);
4540
4720
  };
4541
4721
 
4542
- const STDERR_FD = 2;
4543
-
4544
- const addPrefix = (string, verboseId, icon, color) => string.includes('\n')
4545
- ? string
4546
- .split('\n')
4547
- .map(line => addPrefixToLine(line, verboseId, icon, color))
4548
- .join('\n')
4549
- : addPrefixToLine(string, verboseId, icon, color);
4550
-
4551
- const addPrefixToLine = (line, verboseId, icon, color = identity$1) => [
4552
- gray(`[${getTimestamp()}]`),
4553
- gray(`[${verboseId}]`),
4554
- color(ICONS[icon]),
4555
- color(line),
4556
- ].join(' ');
4722
+ const getVerboseObject = ({
4723
+ type,
4724
+ result,
4725
+ verboseInfo: {escapedCommand, commandId, rawOptions: {piped = false, ...options}},
4726
+ }) => ({
4727
+ type,
4728
+ escapedCommand,
4729
+ commandId: `${commandId}`,
4730
+ timestamp: new Date(),
4731
+ piped,
4732
+ result,
4733
+ options,
4734
+ });
4557
4735
 
4558
- const identity$1 = string => string;
4736
+ const getPrintedLines = (verboseMessage, verboseObject) => verboseMessage
4737
+ .split('\n')
4738
+ .map(message => getPrintedLine({...verboseObject, message}));
4559
4739
 
4560
- // Prepending the timestamp allows debugging the slow paths of a subprocess
4561
- const getTimestamp = () => {
4562
- const date = new Date();
4563
- return `${padField(date.getHours(), 2)}:${padField(date.getMinutes(), 2)}:${padField(date.getSeconds(), 2)}.${padField(date.getMilliseconds(), 3)}`;
4740
+ const getPrintedLine = verboseObject => {
4741
+ const verboseLine = defaultVerboseFunction(verboseObject);
4742
+ return {verboseLine, verboseObject};
4564
4743
  };
4565
4744
 
4566
- const padField = (field, padding) => String(field).padStart(padding, '0');
4567
-
4568
- const ICONS = {
4569
- command: '$',
4570
- pipedCommand: '|',
4571
- output: ' ',
4572
- ipc: '*',
4573
- error: figures.cross,
4574
- warning: figures.warning,
4575
- success: figures.tick,
4576
- };
4745
+ // Unless a `verbose` function is used, print all logs on `stderr`
4746
+ const STDERR_FD = 2;
4577
4747
 
4578
4748
  // Serialize any type to a line string, for logging
4579
- const serializeLogMessage = message => {
4749
+ const serializeVerboseMessage = message => {
4580
4750
  const messageString = typeof message === 'string' ? message : inspect(message);
4581
4751
  const escapedMessage = escapeLines(messageString);
4582
4752
  return escapedMessage.replaceAll('\t', ' '.repeat(TAB_SIZE));
@@ -4585,134 +4755,71 @@ const serializeLogMessage = message => {
4585
4755
  // Same as `util.inspect()`
4586
4756
  const TAB_SIZE = 2;
4587
4757
 
4588
- // When `verbose` is `short|full`, print each command
4589
- const logCommand = (escapedCommand, {verbose, verboseId}, {piped = false}) => {
4590
- if (!isVerbose(verbose)) {
4758
+ // When `verbose` is `short|full|custom`, print each command
4759
+ const logCommand = (escapedCommand, verboseInfo) => {
4760
+ if (!isVerbose(verboseInfo)) {
4591
4761
  return;
4592
4762
  }
4593
4763
 
4594
- const icon = piped ? 'pipedCommand' : 'command';
4595
- verboseLog(escapedCommand, verboseId, icon, bold);
4596
- };
4597
-
4598
- // Start counting time before spawning the subprocess
4599
- const getStartTime = () => hrtime.bigint();
4600
-
4601
- // Compute duration after the subprocess ended.
4602
- // Printed by the `verbose` option.
4603
- const getDurationMs = startTime => Number(hrtime.bigint() - startTime) / 1e6;
4604
-
4605
- const isStandardStream = stream => STANDARD_STREAMS.includes(stream);
4606
- const STANDARD_STREAMS = [process$3.stdin, process$3.stdout, process$3.stderr];
4607
- const STANDARD_STREAMS_ALIASES = ['stdin', 'stdout', 'stderr'];
4608
- const getStreamName = fdNumber => STANDARD_STREAMS_ALIASES[fdNumber] ?? `stdio[${fdNumber}]`;
4609
-
4610
- // Some options can have different values for `stdout`/`stderr`/`fd3`.
4611
- // This normalizes those to array of values.
4612
- // For example, `{verbose: {stdout: 'none', stderr: 'full'}}` becomes `{verbose: ['none', 'none', 'full']}`
4613
- const normalizeFdSpecificOptions = options => {
4614
- const optionsCopy = {...options};
4615
-
4616
- for (const optionName of FD_SPECIFIC_OPTIONS) {
4617
- optionsCopy[optionName] = normalizeFdSpecificOption(options, optionName);
4618
- }
4619
-
4620
- return optionsCopy;
4764
+ verboseLog({
4765
+ type: 'command',
4766
+ verboseMessage: escapedCommand,
4767
+ verboseInfo,
4768
+ });
4621
4769
  };
4622
4770
 
4623
- const normalizeFdSpecificOption = (options, optionName) => {
4624
- const optionBaseArray = Array.from({length: getStdioLength(options) + 1});
4625
- const optionArray = normalizeFdSpecificValue(options[optionName], optionBaseArray, optionName);
4626
- return addDefaultValue$1(optionArray, optionName);
4771
+ // Information computed before spawning, used by the `verbose` option
4772
+ const getVerboseInfo = (verbose, escapedCommand, rawOptions) => {
4773
+ validateVerbose(verbose);
4774
+ const commandId = getCommandId(verbose);
4775
+ return {
4776
+ verbose,
4777
+ escapedCommand,
4778
+ commandId,
4779
+ rawOptions,
4780
+ };
4627
4781
  };
4628
4782
 
4629
- const getStdioLength = ({stdio}) => Array.isArray(stdio)
4630
- ? Math.max(stdio.length, STANDARD_STREAMS_ALIASES.length)
4631
- : STANDARD_STREAMS_ALIASES.length;
4783
+ const getCommandId = verbose => isVerbose({verbose}) ? COMMAND_ID++ : undefined;
4632
4784
 
4633
- const normalizeFdSpecificValue = (optionValue, optionArray, optionName) => isPlainObject(optionValue)
4634
- ? normalizeOptionObject(optionValue, optionArray, optionName)
4635
- : optionArray.fill(optionValue);
4785
+ // Prepending the `pid` is useful when multiple commands print their output at the same time.
4786
+ // However, we cannot use the real PID since this is not available with `child_process.spawnSync()`.
4787
+ // Also, we cannot use the real PID if we want to print it before `child_process.spawn()` is run.
4788
+ // As a pro, it is shorter than a normal PID and never re-uses the same id.
4789
+ // As a con, it cannot be used to send signals.
4790
+ let COMMAND_ID = 0n;
4636
4791
 
4637
- const normalizeOptionObject = (optionValue, optionArray, optionName) => {
4638
- for (const fdName of Object.keys(optionValue).sort(compareFdName)) {
4639
- for (const fdNumber of parseFdName(fdName, optionName, optionArray)) {
4640
- optionArray[fdNumber] = optionValue[fdName];
4792
+ const validateVerbose = verbose => {
4793
+ for (const fdVerbose of verbose) {
4794
+ if (fdVerbose === false) {
4795
+ throw new TypeError('The "verbose: false" option was renamed to "verbose: \'none\'".');
4641
4796
  }
4642
- }
4643
-
4644
- return optionArray;
4645
- };
4646
4797
 
4647
- // Ensure priority order when setting both `stdout`/`stderr`, `fd1`/`fd2`, and `all`
4648
- const compareFdName = (fdNameA, fdNameB) => getFdNameOrder(fdNameA) < getFdNameOrder(fdNameB) ? 1 : -1;
4649
-
4650
- const getFdNameOrder = fdName => {
4651
- if (fdName === 'stdout' || fdName === 'stderr') {
4652
- return 0;
4653
- }
4654
-
4655
- return fdName === 'all' ? 2 : 1;
4656
- };
4657
-
4658
- const parseFdName = (fdName, optionName, optionArray) => {
4659
- if (fdName === 'ipc') {
4660
- return [optionArray.length - 1];
4661
- }
4662
-
4663
- const fdNumber = parseFd(fdName);
4664
- if (fdNumber === undefined || fdNumber === 0) {
4665
- throw new TypeError(`"${optionName}.${fdName}" is invalid.
4666
- It must be "${optionName}.stdout", "${optionName}.stderr", "${optionName}.all", "${optionName}.ipc", or "${optionName}.fd3", "${optionName}.fd4" (and so on).`);
4667
- }
4668
-
4669
- if (fdNumber >= optionArray.length) {
4670
- throw new TypeError(`"${optionName}.${fdName}" is invalid: that file descriptor does not exist.
4671
- Please set the "stdio" option to ensure that file descriptor exists.`);
4672
- }
4673
-
4674
- return fdNumber === 'all' ? [1, 2] : [fdNumber];
4675
- };
4676
-
4677
- // Use the same syntax for fd-specific options and the `from`/`to` options
4678
- const parseFd = fdName => {
4679
- if (fdName === 'all') {
4680
- return fdName;
4681
- }
4682
-
4683
- if (STANDARD_STREAMS_ALIASES.includes(fdName)) {
4684
- return STANDARD_STREAMS_ALIASES.indexOf(fdName);
4685
- }
4798
+ if (fdVerbose === true) {
4799
+ throw new TypeError('The "verbose: true" option was renamed to "verbose: \'short\'".');
4800
+ }
4686
4801
 
4687
- const regexpResult = FD_REGEXP.exec(fdName);
4688
- if (regexpResult !== null) {
4689
- return Number(regexpResult[1]);
4802
+ if (!VERBOSE_VALUES.includes(fdVerbose) && !isVerboseFunction(fdVerbose)) {
4803
+ const allowedValues = VERBOSE_VALUES.map(allowedValue => `'${allowedValue}'`).join(', ');
4804
+ throw new TypeError(`The "verbose" option must not be ${fdVerbose}. Allowed values are: ${allowedValues} or a function.`);
4805
+ }
4690
4806
  }
4691
4807
  };
4692
4808
 
4693
- const FD_REGEXP = /^fd(\d+)$/;
4694
-
4695
- const addDefaultValue$1 = (optionArray, optionName) => optionArray.map(optionValue => optionValue === undefined
4696
- ? DEFAULT_OPTIONS[optionName]
4697
- : optionValue);
4698
-
4699
- const DEFAULT_OPTIONS = {
4700
- lines: false,
4701
- buffer: true,
4702
- maxBuffer: 1000 * 1000 * 100,
4703
- verbose: verboseDefault,
4704
- stripFinalNewline: true,
4705
- };
4809
+ // Start counting time before spawning the subprocess
4810
+ const getStartTime = () => hrtime.bigint();
4706
4811
 
4707
- // List of options which can have different values for `stdout`/`stderr`
4708
- const FD_SPECIFIC_OPTIONS = ['lines', 'buffer', 'maxBuffer', 'verbose', 'stripFinalNewline'];
4812
+ // Compute duration after the subprocess ended.
4813
+ // Printed by the `verbose` option.
4814
+ const getDurationMs = startTime => Number(hrtime.bigint() - startTime) / 1e6;
4709
4815
 
4710
4816
  // Compute `result.command`, `result.escapedCommand` and `verbose`-related information
4711
4817
  const handleCommand = (filePath, rawArguments, rawOptions) => {
4712
4818
  const startTime = getStartTime();
4713
4819
  const {command, escapedCommand} = joinCommand(filePath, rawArguments);
4714
- const verboseInfo = getVerboseInfo(normalizeFdSpecificOption(rawOptions, 'verbose'));
4715
- logCommand(escapedCommand, verboseInfo, rawOptions);
4820
+ const verbose = normalizeFdSpecificOption(rawOptions, 'verbose');
4821
+ const verboseInfo = getVerboseInfo(verbose, escapedCommand, {...rawOptions});
4822
+ logCommand(escapedCommand, verboseInfo);
4716
4823
  return {
4717
4824
  command,
4718
4825
  escapedCommand,
@@ -6577,7 +6684,7 @@ const hasMessageListeners = (anyProcess, ipcEmitter) => ipcEmitter.listenerCount
6577
6684
  // When `buffer` is `false`, we set up a `message` listener that should be ignored.
6578
6685
  // That listener is only meant to intercept `strict` acknowledgement responses.
6579
6686
  const getMinListenerCount = anyProcess => SUBPROCESS_OPTIONS.has(anyProcess)
6580
- && !SUBPROCESS_OPTIONS.get(anyProcess).options.buffer.at(-1)
6687
+ && !getFdSpecificValue(SUBPROCESS_OPTIONS.get(anyProcess).options.buffer, 'ipc')
6581
6688
  ? 1
6582
6689
  : 0;
6583
6690
 
@@ -7622,11 +7729,12 @@ const getMaxBufferInfo = (error, maxBuffer) => {
7622
7729
  const {maxBufferInfo: {fdNumber, unit}} = error;
7623
7730
  delete error.maxBufferInfo;
7624
7731
 
7732
+ const threshold = getFdSpecificValue(maxBuffer, fdNumber);
7625
7733
  if (fdNumber === 'ipc') {
7626
- return {streamName: 'IPC output', threshold: maxBuffer.at(-1), unit: 'messages'};
7734
+ return {streamName: 'IPC output', threshold, unit: 'messages'};
7627
7735
  }
7628
7736
 
7629
- return {streamName: getStreamName(fdNumber), threshold: maxBuffer[fdNumber], unit};
7737
+ return {streamName: getStreamName(fdNumber), threshold, unit};
7630
7738
  };
7631
7739
 
7632
7740
  // The only way to apply `maxBuffer` with `spawnSync()` is to use the native `maxBuffer` option Node.js provides.
@@ -8159,71 +8267,42 @@ function prettyMilliseconds(milliseconds, options) {
8159
8267
  return result.join(separator);
8160
8268
  }
8161
8269
 
8162
- // When `verbose` is `short|full`, print each command's error when it fails
8163
- const logError = ({message, failed, reject, verboseId, icon}) => {
8164
- if (!failed) {
8165
- return;
8270
+ // When `verbose` is `short|full|custom`, print each command's error when it fails
8271
+ const logError = (result, verboseInfo) => {
8272
+ if (result.failed) {
8273
+ verboseLog({
8274
+ type: 'error',
8275
+ verboseMessage: result.shortMessage,
8276
+ verboseInfo,
8277
+ result,
8278
+ });
8166
8279
  }
8167
-
8168
- const color = reject ? redBright : yellowBright;
8169
- verboseLog(message, verboseId, icon, color);
8170
8280
  };
8171
8281
 
8172
- // When `verbose` is `short|full`, print each command's completion, duration and error
8173
- const logFinalResult = ({shortMessage, failed, durationMs}, reject, verboseInfo) => {
8174
- logResult({
8175
- message: shortMessage,
8176
- failed,
8177
- reject,
8178
- durationMs,
8179
- verboseInfo,
8180
- });
8181
- };
8182
-
8183
- // Same but for early validation errors
8184
- const logEarlyResult = (error, startTime, verboseInfo) => {
8185
- logResult({
8186
- message: escapeLines(String(error)),
8187
- failed: true,
8188
- reject: true,
8189
- durationMs: getDurationMs(startTime),
8190
- verboseInfo,
8191
- });
8192
- };
8193
-
8194
- const logResult = ({message, failed, reject, durationMs, verboseInfo: {verbose, verboseId}}) => {
8195
- if (!isVerbose(verbose)) {
8282
+ // When `verbose` is `short|full|custom`, print each command's completion, duration and error
8283
+ const logResult = (result, verboseInfo) => {
8284
+ if (!isVerbose(verboseInfo)) {
8196
8285
  return;
8197
8286
  }
8198
8287
 
8199
- const icon = getIcon(failed, reject);
8200
- logError({
8201
- message,
8202
- failed,
8203
- reject,
8204
- verboseId,
8205
- icon,
8206
- });
8207
- logDuration(durationMs, verboseId, icon);
8208
- };
8209
-
8210
- const logDuration = (durationMs, verboseId, icon) => {
8211
- const durationMessage = `(done in ${prettyMilliseconds(durationMs)})`;
8212
- verboseLog(durationMessage, verboseId, icon, gray);
8288
+ logError(result, verboseInfo);
8289
+ logDuration(result, verboseInfo);
8213
8290
  };
8214
8291
 
8215
- const getIcon = (failed, reject) => {
8216
- if (!failed) {
8217
- return 'success';
8218
- }
8219
-
8220
- return reject ? 'error' : 'warning';
8292
+ const logDuration = (result, verboseInfo) => {
8293
+ const verboseMessage = `(done in ${prettyMilliseconds(result.durationMs)})`;
8294
+ verboseLog({
8295
+ type: 'duration',
8296
+ verboseMessage,
8297
+ verboseInfo,
8298
+ result,
8299
+ });
8221
8300
  };
8222
8301
 
8223
8302
  // Applies the `reject` option.
8224
8303
  // Also print the final log line with `verbose`.
8225
8304
  const handleResult = (result, verboseInfo, {reject}) => {
8226
- logFinalResult(result, reject, verboseInfo);
8305
+ logResult(result, verboseInfo);
8227
8306
 
8228
8307
  if (result.failed && reject) {
8229
8308
  throw result;
@@ -8623,10 +8702,10 @@ const normalizeIpcStdioArray = (stdioArray, ipc) => ipc && !stdioArray.includes(
8623
8702
 
8624
8703
  // Add support for `stdin`/`stdout`/`stderr` as an alias for `stdio`.
8625
8704
  // Also normalize the `stdio` option.
8626
- const normalizeStdioOption = ({stdio, ipc, buffer, verbose, ...options}, isSync) => {
8705
+ const normalizeStdioOption = ({stdio, ipc, buffer, ...options}, verboseInfo, isSync) => {
8627
8706
  const stdioArray = getStdioArray(stdio, options).map((stdioOption, fdNumber) => addDefaultValue(stdioOption, fdNumber));
8628
8707
  return isSync
8629
- ? normalizeStdioSync(stdioArray, buffer, verbose)
8708
+ ? normalizeStdioSync(stdioArray, buffer, verboseInfo)
8630
8709
  : normalizeIpcStdioArray(stdioArray, ipc);
8631
8710
  };
8632
8711
 
@@ -8667,10 +8746,10 @@ const addDefaultValue = (stdioOption, fdNumber) => {
8667
8746
 
8668
8747
  // Using `buffer: false` with synchronous methods implies `stdout`/`stderr`: `ignore`.
8669
8748
  // Unless the output is needed, e.g. due to `verbose: 'full'` or to redirecting to a file.
8670
- const normalizeStdioSync = (stdioArray, buffer, verbose) => stdioArray.map((stdioOption, fdNumber) =>
8749
+ const normalizeStdioSync = (stdioArray, buffer, verboseInfo) => stdioArray.map((stdioOption, fdNumber) =>
8671
8750
  !buffer[fdNumber]
8672
8751
  && fdNumber !== 0
8673
- && verbose[fdNumber] !== 'full'
8752
+ && !isFullVerbose(verboseInfo, fdNumber)
8674
8753
  && isOutputPipeOnly(stdioOption)
8675
8754
  ? 'ignore'
8676
8755
  : stdioOption);
@@ -8939,7 +9018,7 @@ const throwOnDuplicateStream = (stdioItem, optionName, type) => {
8939
9018
  // They are converted into an array of `fileDescriptors`.
8940
9019
  // Each `fileDescriptor` is normalized, validated and contains all information necessary for further handling.
8941
9020
  const handleStdio = (addProperties, options, verboseInfo, isSync) => {
8942
- const stdio = normalizeStdioOption(options, isSync);
9021
+ const stdio = normalizeStdioOption(options, verboseInfo, isSync);
8943
9022
  const initialFileDescriptors = stdio.map((stdioOption, fdNumber) => getFileDescriptor({
8944
9023
  stdioOption,
8945
9024
  fdNumber,
@@ -9645,8 +9724,8 @@ const validateSerializable = newContents => {
9645
9724
  // `inherit` would result in double printing.
9646
9725
  // They can also lead to double printing when passing file descriptor integers or `process.std*`.
9647
9726
  // This only leaves with `pipe` and `overlapped`.
9648
- const shouldLogOutput = ({stdioItems, encoding, verboseInfo: {verbose}, fdNumber}) => fdNumber !== 'all'
9649
- && verbose[fdNumber] === 'full'
9727
+ const shouldLogOutput = ({stdioItems, encoding, verboseInfo, fdNumber}) => fdNumber !== 'all'
9728
+ && isFullVerbose(verboseInfo, fdNumber)
9650
9729
  && !BINARY_ENCODINGS.has(encoding)
9651
9730
  && fdUsesVerbose(fdNumber)
9652
9731
  && (stdioItems.some(({type, value}) => type === 'native' && PIPED_STDIO_VALUES.has(value))
@@ -9661,18 +9740,18 @@ const fdUsesVerbose = fdNumber => fdNumber === 1 || fdNumber === 2;
9661
9740
  const PIPED_STDIO_VALUES = new Set(['pipe', 'overlapped']);
9662
9741
 
9663
9742
  // `verbose: 'full'` printing logic with async methods
9664
- const logLines = async (linesIterable, stream, verboseInfo) => {
9743
+ const logLines = async (linesIterable, stream, fdNumber, verboseInfo) => {
9665
9744
  for await (const line of linesIterable) {
9666
9745
  if (!isPipingStream(stream)) {
9667
- logLine(line, verboseInfo);
9746
+ logLine(line, fdNumber, verboseInfo);
9668
9747
  }
9669
9748
  }
9670
9749
  };
9671
9750
 
9672
9751
  // `verbose: 'full'` printing logic with sync methods
9673
- const logLinesSync = (linesArray, verboseInfo) => {
9752
+ const logLinesSync = (linesArray, fdNumber, verboseInfo) => {
9674
9753
  for (const line of linesArray) {
9675
- logLine(line, verboseInfo);
9754
+ logLine(line, fdNumber, verboseInfo);
9676
9755
  }
9677
9756
  };
9678
9757
 
@@ -9686,8 +9765,14 @@ const logLinesSync = (linesArray, verboseInfo) => {
9686
9765
  const isPipingStream = stream => stream._readableState.pipes.length > 0;
9687
9766
 
9688
9767
  // When `verbose` is `full`, print stdout|stderr
9689
- const logLine = (line, {verboseId}) => {
9690
- verboseLog(serializeLogMessage(line), verboseId, 'output');
9768
+ const logLine = (line, fdNumber, verboseInfo) => {
9769
+ const verboseMessage = serializeVerboseMessage(line);
9770
+ verboseLog({
9771
+ type: 'output',
9772
+ verboseMessage,
9773
+ fdNumber,
9774
+ verboseInfo,
9775
+ });
9691
9776
  };
9692
9777
 
9693
9778
  // Apply `stdout`/`stderr` options, after spawning, in sync mode
@@ -9732,15 +9817,15 @@ const transformOutputResultSync = (
9732
9817
  fdNumber,
9733
9818
  });
9734
9819
 
9735
- if (shouldLogOutput({
9736
- stdioItems,
9737
- encoding,
9738
- verboseInfo,
9820
+ logOutputSync({
9821
+ serializedResult,
9739
9822
  fdNumber,
9740
- })) {
9741
- const linesArray = splitLinesSync(serializedResult, false, objectMode);
9742
- logLinesSync(linesArray, verboseInfo);
9743
- }
9823
+ state,
9824
+ verboseInfo,
9825
+ encoding,
9826
+ stdioItems,
9827
+ objectMode,
9828
+ });
9744
9829
 
9745
9830
  const returnedResult = buffer[fdNumber] ? finalResult : undefined;
9746
9831
 
@@ -9786,6 +9871,25 @@ const serializeChunks = ({chunks, objectMode, encoding, lines, stripFinalNewline
9786
9871
  return {serializedResult};
9787
9872
  };
9788
9873
 
9874
+ const logOutputSync = ({serializedResult, fdNumber, state, verboseInfo, encoding, stdioItems, objectMode}) => {
9875
+ if (!shouldLogOutput({
9876
+ stdioItems,
9877
+ encoding,
9878
+ verboseInfo,
9879
+ fdNumber,
9880
+ })) {
9881
+ return;
9882
+ }
9883
+
9884
+ const linesArray = splitLinesSync(serializedResult, false, objectMode);
9885
+
9886
+ try {
9887
+ logLinesSync(linesArray, fdNumber, verboseInfo);
9888
+ } catch (error) {
9889
+ state.error ??= error;
9890
+ }
9891
+ };
9892
+
9789
9893
  // When the `std*` target is a file path/URL or a file descriptor
9790
9894
  const writeToFiles = (serializedResult, stdioItems, outputFiles) => {
9791
9895
  for (const {path} of stdioItems.filter(({type}) => FILE_TYPES.has(type))) {
@@ -9923,26 +10027,20 @@ const execaCoreSync = (rawFile, rawArguments, rawOptions) => {
9923
10027
  // Compute arguments to pass to `child_process.spawnSync()`
9924
10028
  const handleSyncArguments = (rawFile, rawArguments, rawOptions) => {
9925
10029
  const {command, escapedCommand, startTime, verboseInfo} = handleCommand(rawFile, rawArguments, rawOptions);
9926
-
9927
- try {
9928
- const syncOptions = normalizeSyncOptions(rawOptions);
9929
- const {file, commandArguments, options} = normalizeOptions$2(rawFile, rawArguments, syncOptions);
9930
- validateSyncOptions(options);
9931
- const fileDescriptors = handleStdioSync(options, verboseInfo);
9932
- return {
9933
- file,
9934
- commandArguments,
9935
- command,
9936
- escapedCommand,
9937
- startTime,
9938
- verboseInfo,
9939
- options,
9940
- fileDescriptors,
9941
- };
9942
- } catch (error) {
9943
- logEarlyResult(error, startTime, verboseInfo);
9944
- throw error;
9945
- }
10030
+ const syncOptions = normalizeSyncOptions(rawOptions);
10031
+ const {file, commandArguments, options} = normalizeOptions$2(rawFile, rawArguments, syncOptions);
10032
+ validateSyncOptions(options);
10033
+ const fileDescriptors = handleStdioSync(options, verboseInfo);
10034
+ return {
10035
+ file,
10036
+ commandArguments,
10037
+ command,
10038
+ escapedCommand,
10039
+ startTime,
10040
+ verboseInfo,
10041
+ options,
10042
+ fileDescriptors,
10043
+ };
9946
10044
  };
9947
10045
 
9948
10046
  // Options normalization logic specific to sync methods
@@ -11459,26 +11557,19 @@ const getGenerators = ({binary, shouldEncode, encoding, shouldSplit, preserveNew
11459
11557
  ].filter(Boolean);
11460
11558
 
11461
11559
  // Retrieve `result.stdout|stderr|all|stdio[*]`
11462
- const getStreamOutput = async ({stream, onStreamEnd, fdNumber, encoding, buffer, maxBuffer, lines, allMixed, stripFinalNewline, verboseInfo, streamInfo: {fileDescriptors}}) => {
11463
- if (shouldLogOutput({
11464
- stdioItems: fileDescriptors[fdNumber]?.stdioItems,
11560
+ const getStreamOutput = async ({stream, onStreamEnd, fdNumber, encoding, buffer, maxBuffer, lines, allMixed, stripFinalNewline, verboseInfo, streamInfo}) => {
11561
+ const logPromise = logOutputAsync({
11562
+ stream,
11563
+ onStreamEnd,
11564
+ fdNumber,
11465
11565
  encoding,
11566
+ allMixed,
11466
11567
  verboseInfo,
11467
- fdNumber,
11468
- })) {
11469
- const linesIterable = iterateForResult({
11470
- stream,
11471
- onStreamEnd,
11472
- lines: true,
11473
- encoding,
11474
- stripFinalNewline: true,
11475
- allMixed,
11476
- });
11477
- logLines(linesIterable, stream, verboseInfo);
11478
- }
11568
+ streamInfo,
11569
+ });
11479
11570
 
11480
11571
  if (!buffer) {
11481
- await resumeStream(stream);
11572
+ await Promise.all([resumeStream(stream), logPromise]);
11482
11573
  return;
11483
11574
  }
11484
11575
 
@@ -11491,14 +11582,39 @@ const getStreamOutput = async ({stream, onStreamEnd, fdNumber, encoding, buffer,
11491
11582
  stripFinalNewline: stripFinalNewlineValue,
11492
11583
  allMixed,
11493
11584
  });
11494
- return getStreamContents({
11495
- stream,
11496
- iterable,
11585
+ const [output] = await Promise.all([
11586
+ getStreamContents({
11587
+ stream,
11588
+ iterable,
11589
+ fdNumber,
11590
+ encoding,
11591
+ maxBuffer,
11592
+ lines,
11593
+ }),
11594
+ logPromise,
11595
+ ]);
11596
+ return output;
11597
+ };
11598
+
11599
+ const logOutputAsync = async ({stream, onStreamEnd, fdNumber, encoding, allMixed, verboseInfo, streamInfo: {fileDescriptors}}) => {
11600
+ if (!shouldLogOutput({
11601
+ stdioItems: fileDescriptors[fdNumber]?.stdioItems,
11602
+ encoding,
11603
+ verboseInfo,
11497
11604
  fdNumber,
11605
+ })) {
11606
+ return;
11607
+ }
11608
+
11609
+ const linesIterable = iterateForResult({
11610
+ stream,
11611
+ onStreamEnd,
11612
+ lines: true,
11498
11613
  encoding,
11499
- maxBuffer,
11500
- lines,
11614
+ stripFinalNewline: true,
11615
+ allMixed,
11501
11616
  });
11617
+ await logLines(linesIterable, stream, fdNumber, verboseInfo);
11502
11618
  };
11503
11619
 
11504
11620
  // When using `buffer: false`, users need to read `subprocess.stdout|stderr|all` right away
@@ -11734,10 +11850,16 @@ const getAllMixed = ({all, stdout, stderr}) => all
11734
11850
  && stdout.readableObjectMode !== stderr.readableObjectMode;
11735
11851
 
11736
11852
  // When `verbose` is `'full'`, print IPC messages from the subprocess
11737
- const shouldLogIpc = ({verbose}) => verbose.at(-1) === 'full';
11738
-
11739
- const logIpcOutput = (message, {verboseId}) => {
11740
- verboseLog(serializeLogMessage(message), verboseId, 'ipc');
11853
+ const shouldLogIpc = verboseInfo => isFullVerbose(verboseInfo, 'ipc');
11854
+
11855
+ const logIpcOutput = (message, verboseInfo) => {
11856
+ const verboseMessage = serializeVerboseMessage(message);
11857
+ verboseLog({
11858
+ type: 'ipc',
11859
+ verboseMessage,
11860
+ fdNumber: 'ipc',
11861
+ verboseInfo,
11862
+ });
11741
11863
  };
11742
11864
 
11743
11865
  // Iterate through IPC messages sent by the subprocess
@@ -11754,8 +11876,8 @@ const waitForIpcOutput = async ({
11754
11876
  }
11755
11877
 
11756
11878
  const isVerbose = shouldLogIpc(verboseInfo);
11757
- const buffer = bufferArray.at(-1);
11758
- const maxBuffer = maxBufferArray.at(-1);
11879
+ const buffer = getFdSpecificValue(bufferArray, 'ipc');
11880
+ const maxBuffer = getFdSpecificValue(maxBufferArray, 'ipc');
11759
11881
 
11760
11882
  for await (const message of loopOnMessages({
11761
11883
  anyProcess: subprocess,
@@ -12308,25 +12430,19 @@ const execaCoreAsync = (rawFile, rawArguments, rawOptions, createNested) => {
12308
12430
  // Compute arguments to pass to `child_process.spawn()`
12309
12431
  const handleAsyncArguments = (rawFile, rawArguments, rawOptions) => {
12310
12432
  const {command, escapedCommand, startTime, verboseInfo} = handleCommand(rawFile, rawArguments, rawOptions);
12311
-
12312
- try {
12313
- const {file, commandArguments, options: normalizedOptions} = normalizeOptions$2(rawFile, rawArguments, rawOptions);
12314
- const options = handleAsyncOptions(normalizedOptions);
12315
- const fileDescriptors = handleStdioAsync(options, verboseInfo);
12316
- return {
12317
- file,
12318
- commandArguments,
12319
- command,
12320
- escapedCommand,
12321
- startTime,
12322
- verboseInfo,
12323
- options,
12324
- fileDescriptors,
12325
- };
12326
- } catch (error) {
12327
- logEarlyResult(error, startTime, verboseInfo);
12328
- throw error;
12329
- }
12433
+ const {file, commandArguments, options: normalizedOptions} = normalizeOptions$2(rawFile, rawArguments, rawOptions);
12434
+ const options = handleAsyncOptions(normalizedOptions);
12435
+ const fileDescriptors = handleStdioAsync(options, verboseInfo);
12436
+ return {
12437
+ file,
12438
+ commandArguments,
12439
+ command,
12440
+ escapedCommand,
12441
+ startTime,
12442
+ verboseInfo,
12443
+ options,
12444
+ fileDescriptors,
12445
+ };
12330
12446
  };
12331
12447
 
12332
12448
  // Options normalization logic specific to async methods.
@@ -32886,7 +33002,8 @@ const getDotenv = async (options = {}) => {
32886
33002
  for (const key in dynamic)
32887
33003
  Object.assign(dotenv, {
32888
33004
  [key]: _.isFunction(dynamic[key])
32889
- ? dynamic[key](dotenv, env ?? defaultEnv)
33005
+ ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
33006
+ dynamic[key](dotenv, env ?? defaultEnv)
32890
33007
  : dynamic[key],
32891
33008
  });
32892
33009
  }
@@ -32920,7 +33037,8 @@ const resolveCommand = (scripts, command) => (scripts && _.isObject(scripts[comm
32920
33037
  ? scripts[command].cmd
32921
33038
  : scripts?.[command] ?? command);
32922
33039
  const resolveShell = (scripts, command, shell) => scripts && _.isObject(scripts[command])
32923
- ? scripts[command].shell
33040
+ ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
33041
+ scripts[command].shell
32924
33042
  : shell;
32925
33043
 
32926
33044
  /*