pipe-arguments.js 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import {normalizeParameters} from '../methods/parameters.js';
  2. import {getStartTime} from '../return/duration.js';
  3. import {SUBPROCESS_OPTIONS, getToStream, getFromStream} from '../arguments/fd-options.js';
  4. import {isDenoExecPath} from '../arguments/file-url.js';
  5. // Normalize and validate arguments passed to `source.pipe(destination)`
  6. export const normalizePipeArguments = ({source, sourcePromise, boundOptions, createNested}, ...pipeArguments) => {
  7. const startTime = getStartTime();
  8. const {
  9. destination,
  10. destinationStream,
  11. destinationError,
  12. from,
  13. unpipeSignal,
  14. } = getDestinationStream(boundOptions, createNested, pipeArguments);
  15. const {sourceStream, sourceError} = getSourceStream(source, from);
  16. const {options: sourceOptions, fileDescriptors} = SUBPROCESS_OPTIONS.get(source);
  17. return {
  18. sourcePromise,
  19. sourceStream,
  20. sourceOptions,
  21. sourceError,
  22. destination,
  23. destinationStream,
  24. destinationError,
  25. unpipeSignal,
  26. fileDescriptors,
  27. startTime,
  28. };
  29. };
  30. const getDestinationStream = (boundOptions, createNested, pipeArguments) => {
  31. try {
  32. const {
  33. destination,
  34. pipeOptions: {from, to, unpipeSignal} = {},
  35. } = getDestination(boundOptions, createNested, ...pipeArguments);
  36. const destinationStream = getToStream(destination, to);
  37. return {
  38. destination,
  39. destinationStream,
  40. from,
  41. unpipeSignal,
  42. };
  43. } catch (error) {
  44. return {destinationError: error};
  45. }
  46. };
  47. // Piping subprocesses can use three syntaxes:
  48. // - source.pipe('command', commandArguments, pipeOptionsOrDestinationOptions)
  49. // - source.pipe`command commandArgument` or source.pipe(pipeOptionsOrDestinationOptions)`command commandArgument`
  50. // - source.pipe(execa(...), pipeOptions)
  51. const getDestination = (boundOptions, createNested, firstArgument, ...pipeArguments) => {
  52. if (Array.isArray(firstArgument)) {
  53. const destination = createNested(mapDestinationArguments, boundOptions)(firstArgument, ...pipeArguments);
  54. return {destination, pipeOptions: boundOptions};
  55. }
  56. if (typeof firstArgument === 'string' || firstArgument instanceof URL || isDenoExecPath(firstArgument)) {
  57. if (Object.keys(boundOptions).length > 0) {
  58. throw new TypeError('Please use .pipe("file", ..., options) or .pipe(execa("file", ..., options)) instead of .pipe(options)("file", ...).');
  59. }
  60. const [rawFile, rawArguments, rawOptions] = normalizeParameters(firstArgument, ...pipeArguments);
  61. const destination = createNested(mapDestinationArguments)(rawFile, rawArguments, rawOptions);
  62. return {destination, pipeOptions: rawOptions};
  63. }
  64. if (SUBPROCESS_OPTIONS.has(firstArgument)) {
  65. if (Object.keys(boundOptions).length > 0) {
  66. throw new TypeError('Please use .pipe(options)`command` or .pipe($(options)`command`) instead of .pipe(options)($`command`).');
  67. }
  68. return {destination: firstArgument, pipeOptions: pipeArguments[0]};
  69. }
  70. throw new TypeError(`The first argument must be a template string, an options object, or an Execa subprocess: ${firstArgument}`);
  71. };
  72. // Force `stdin: 'pipe'` with the destination subprocess
  73. const mapDestinationArguments = ({options}) => ({options: {...options, stdin: 'pipe', piped: true}});
  74. const getSourceStream = (source, from) => {
  75. try {
  76. const sourceStream = getFromStream(source, from);
  77. return {sourceStream};
  78. } catch (error) {
  79. return {sourceError: error};
  80. }
  81. };