output.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
  2. import {TRANSFORM_TYPES} from '../stdio/type.js';
  3. import {verboseLog, serializeVerboseMessage} from './log.js';
  4. import {isFullVerbose} from './values.js';
  5. // `ignore` opts-out of `verbose` for a specific stream.
  6. // `ipc` cannot use piping.
  7. // `inherit` would result in double printing.
  8. // They can also lead to double printing when passing file descriptor integers or `process.std*`.
  9. // This only leaves with `pipe` and `overlapped`.
  10. export const shouldLogOutput = ({stdioItems, encoding, verboseInfo, fdNumber}) => fdNumber !== 'all'
  11. && isFullVerbose(verboseInfo, fdNumber)
  12. && !BINARY_ENCODINGS.has(encoding)
  13. && fdUsesVerbose(fdNumber)
  14. && (stdioItems.some(({type, value}) => type === 'native' && PIPED_STDIO_VALUES.has(value))
  15. || stdioItems.every(({type}) => TRANSFORM_TYPES.has(type)));
  16. // Printing input streams would be confusing.
  17. // Files and streams can produce big outputs, which we don't want to print.
  18. // We could print `stdio[3+]` but it often is redirected to files and streams, with the same issue.
  19. // So we only print stdout and stderr.
  20. const fdUsesVerbose = fdNumber => fdNumber === 1 || fdNumber === 2;
  21. const PIPED_STDIO_VALUES = new Set(['pipe', 'overlapped']);
  22. // `verbose: 'full'` printing logic with async methods
  23. export const logLines = async (linesIterable, stream, fdNumber, verboseInfo) => {
  24. for await (const line of linesIterable) {
  25. if (!isPipingStream(stream)) {
  26. logLine(line, fdNumber, verboseInfo);
  27. }
  28. }
  29. };
  30. // `verbose: 'full'` printing logic with sync methods
  31. export const logLinesSync = (linesArray, fdNumber, verboseInfo) => {
  32. for (const line of linesArray) {
  33. logLine(line, fdNumber, verboseInfo);
  34. }
  35. };
  36. // When `subprocess.stdout|stderr.pipe()` is called, `verbose` becomes a noop.
  37. // This prevents the following problems:
  38. // - `.pipe()` achieves the same result as using `stdout: 'inherit'`, `stdout: stream`, etc. which also make `verbose` a noop.
  39. // For example, `subprocess.stdout.pipe(process.stdin)` would print each line twice.
  40. // - When chaining subprocesses with `subprocess.pipe(otherSubprocess)`, only the last one should print its output.
  41. // Detecting whether `.pipe()` is impossible without monkey-patching it, so we use the following undocumented property.
  42. // This is not a critical behavior since changes of the following property would only make `verbose` more verbose.
  43. const isPipingStream = stream => stream._readableState.pipes.length > 0;
  44. // When `verbose` is `full`, print stdout|stderr
  45. const logLine = (line, fdNumber, verboseInfo) => {
  46. const verboseMessage = serializeVerboseMessage(line);
  47. verboseLog({
  48. type: 'output',
  49. verboseMessage,
  50. fdNumber,
  51. verboseInfo,
  52. });
  53. };