@karmaniverous/get-dotenv 4.5.0 → 4.5.2

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