contents.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import {setImmediate} from 'node:timers/promises';
  2. import getStream, {getStreamAsArrayBuffer, getStreamAsArray} from 'get-stream';
  3. import {isArrayBuffer} from '../utils/uint-array.js';
  4. import {shouldLogOutput, logLines} from '../verbose/output.js';
  5. import {iterateForResult} from './iterate.js';
  6. import {handleMaxBuffer} from './max-buffer.js';
  7. import {getStripFinalNewline} from './strip-newline.js';
  8. // Retrieve `result.stdout|stderr|all|stdio[*]`
  9. export const getStreamOutput = async ({stream, onStreamEnd, fdNumber, encoding, buffer, maxBuffer, lines, allMixed, stripFinalNewline, verboseInfo, streamInfo}) => {
  10. const logPromise = logOutputAsync({
  11. stream,
  12. onStreamEnd,
  13. fdNumber,
  14. encoding,
  15. allMixed,
  16. verboseInfo,
  17. streamInfo,
  18. });
  19. if (!buffer) {
  20. await Promise.all([resumeStream(stream), logPromise]);
  21. return;
  22. }
  23. const stripFinalNewlineValue = getStripFinalNewline(stripFinalNewline, fdNumber);
  24. const iterable = iterateForResult({
  25. stream,
  26. onStreamEnd,
  27. lines,
  28. encoding,
  29. stripFinalNewline: stripFinalNewlineValue,
  30. allMixed,
  31. });
  32. const [output] = await Promise.all([
  33. getStreamContents({
  34. stream,
  35. iterable,
  36. fdNumber,
  37. encoding,
  38. maxBuffer,
  39. lines,
  40. }),
  41. logPromise,
  42. ]);
  43. return output;
  44. };
  45. const logOutputAsync = async ({stream, onStreamEnd, fdNumber, encoding, allMixed, verboseInfo, streamInfo: {fileDescriptors}}) => {
  46. if (!shouldLogOutput({
  47. stdioItems: fileDescriptors[fdNumber]?.stdioItems,
  48. encoding,
  49. verboseInfo,
  50. fdNumber,
  51. })) {
  52. return;
  53. }
  54. const linesIterable = iterateForResult({
  55. stream,
  56. onStreamEnd,
  57. lines: true,
  58. encoding,
  59. stripFinalNewline: true,
  60. allMixed,
  61. });
  62. await logLines(linesIterable, stream, fdNumber, verboseInfo);
  63. };
  64. // When using `buffer: false`, users need to read `subprocess.stdout|stderr|all` right away
  65. // See https://github.com/sindresorhus/execa/issues/730 and https://github.com/sindresorhus/execa/pull/729#discussion_r1465496310
  66. const resumeStream = async stream => {
  67. await setImmediate();
  68. if (stream.readableFlowing === null) {
  69. stream.resume();
  70. }
  71. };
  72. const getStreamContents = async ({stream, stream: {readableObjectMode}, iterable, fdNumber, encoding, maxBuffer, lines}) => {
  73. try {
  74. if (readableObjectMode || lines) {
  75. return await getStreamAsArray(iterable, {maxBuffer});
  76. }
  77. if (encoding === 'buffer') {
  78. return new Uint8Array(await getStreamAsArrayBuffer(iterable, {maxBuffer}));
  79. }
  80. return await getStream(iterable, {maxBuffer});
  81. } catch (error) {
  82. return handleBufferedData(handleMaxBuffer({
  83. error,
  84. stream,
  85. readableObjectMode,
  86. lines,
  87. encoding,
  88. fdNumber,
  89. }));
  90. }
  91. };
  92. // On failure, `result.stdout|stderr|all` should contain the currently buffered stream
  93. // They are automatically closed and flushed by Node.js when the subprocess exits
  94. // When `buffer` is `false`, `streamPromise` is `undefined` and there is no buffered data to retrieve
  95. export const getBufferedData = async streamPromise => {
  96. try {
  97. return await streamPromise;
  98. } catch (error) {
  99. return handleBufferedData(error);
  100. }
  101. };
  102. // Ensure we are returning Uint8Arrays when using `encoding: 'buffer'`
  103. const handleBufferedData = ({bufferedData}) => isArrayBuffer(bufferedData)
  104. ? new Uint8Array(bufferedData)
  105. : bufferedData;