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