Repository: sindresorhus/execa Branch: main Commit: f3a2e8481a1e Files: 619 Total size: 1.7 MB Directory structure: gitextract_y9phr28c/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── codecov.yml │ ├── security.md │ └── workflows/ │ └── main.yml ├── .gitignore ├── .npmrc ├── docs/ │ ├── api.md │ ├── bash.md │ ├── binary.md │ ├── debugging.md │ ├── environment.md │ ├── errors.md │ ├── escaping.md │ ├── execution.md │ ├── input.md │ ├── ipc.md │ ├── lines.md │ ├── node.md │ ├── output.md │ ├── pipe.md │ ├── scripts.md │ ├── shell.md │ ├── small.md │ ├── streams.md │ ├── termination.md │ ├── transform.md │ ├── typescript.md │ └── windows.md ├── index.d.ts ├── index.js ├── lib/ │ ├── arguments/ │ │ ├── command.js │ │ ├── cwd.js │ │ ├── encoding-option.js │ │ ├── escape.js │ │ ├── fd-options.js │ │ ├── file-url.js │ │ ├── options.js │ │ ├── shell.js │ │ └── specific.js │ ├── convert/ │ │ ├── add.js │ │ ├── concurrent.js │ │ ├── duplex.js │ │ ├── iterable.js │ │ ├── readable.js │ │ ├── shared.js │ │ └── writable.js │ ├── io/ │ │ ├── contents.js │ │ ├── input-sync.js │ │ ├── iterate.js │ │ ├── max-buffer.js │ │ ├── output-async.js │ │ ├── output-sync.js │ │ ├── pipeline.js │ │ └── strip-newline.js │ ├── ipc/ │ │ ├── array.js │ │ ├── buffer-messages.js │ │ ├── forward.js │ │ ├── get-each.js │ │ ├── get-one.js │ │ ├── graceful.js │ │ ├── incoming.js │ │ ├── ipc-input.js │ │ ├── methods.js │ │ ├── outgoing.js │ │ ├── reference.js │ │ ├── send.js │ │ ├── strict.js │ │ └── validation.js │ ├── methods/ │ │ ├── bind.js │ │ ├── command.js │ │ ├── create.js │ │ ├── main-async.js │ │ ├── main-sync.js │ │ ├── node.js │ │ ├── parameters.js │ │ ├── promise.js │ │ ├── script.js │ │ └── template.js │ ├── pipe/ │ │ ├── abort.js │ │ ├── pipe-arguments.js │ │ ├── sequence.js │ │ ├── setup.js │ │ ├── streaming.js │ │ └── throw.js │ ├── resolve/ │ │ ├── all-async.js │ │ ├── all-sync.js │ │ ├── exit-async.js │ │ ├── exit-sync.js │ │ ├── stdio.js │ │ ├── wait-stream.js │ │ └── wait-subprocess.js │ ├── return/ │ │ ├── duration.js │ │ ├── early-error.js │ │ ├── final-error.js │ │ ├── message.js │ │ ├── reject.js │ │ └── result.js │ ├── stdio/ │ │ ├── direction.js │ │ ├── duplicate.js │ │ ├── handle-async.js │ │ ├── handle-sync.js │ │ ├── handle.js │ │ ├── input-option.js │ │ ├── native.js │ │ ├── stdio-option.js │ │ └── type.js │ ├── terminate/ │ │ ├── cancel.js │ │ ├── cleanup.js │ │ ├── graceful.js │ │ ├── kill.js │ │ ├── signal.js │ │ └── timeout.js │ ├── transform/ │ │ ├── encoding-transform.js │ │ ├── generator.js │ │ ├── normalize.js │ │ ├── object-mode.js │ │ ├── run-async.js │ │ ├── run-sync.js │ │ ├── split.js │ │ └── validate.js │ ├── utils/ │ │ ├── abort-signal.js │ │ ├── deferred.js │ │ ├── max-listeners.js │ │ ├── standard-stream.js │ │ └── uint-array.js │ └── verbose/ │ ├── complete.js │ ├── custom.js │ ├── default.js │ ├── error.js │ ├── info.js │ ├── ipc.js │ ├── log.js │ ├── output.js │ ├── start.js │ └── values.js ├── license ├── media/ │ └── logo.sketch ├── package.json ├── readme.md ├── test/ │ ├── arguments/ │ │ ├── cwd.js │ │ ├── encoding-option.js │ │ ├── env.js │ │ ├── escape-no-icu.js │ │ ├── escape.js │ │ ├── fd-options.js │ │ ├── local.js │ │ ├── shell.js │ │ └── specific.js │ ├── convert/ │ │ ├── concurrent.js │ │ ├── duplex.js │ │ ├── iterable.js │ │ ├── readable.js │ │ ├── shared.js │ │ └── writable.js │ ├── fixtures/ │ │ ├── all-fail.js │ │ ├── all.js │ │ ├── command with space.js │ │ ├── delay.js │ │ ├── detach.js │ │ ├── echo-fail.js │ │ ├── echo.js │ │ ├── empty.js │ │ ├── environment.js │ │ ├── exit.js │ │ ├── fail.js │ │ ├── fast-exit-darwin │ │ ├── fast-exit-linux │ │ ├── forever.js │ │ ├── graceful-disconnect.js │ │ ├── graceful-echo.js │ │ ├── graceful-listener.js │ │ ├── graceful-none.js │ │ ├── graceful-print.js │ │ ├── graceful-ref.js │ │ ├── graceful-send-echo.js │ │ ├── graceful-send-fast.js │ │ ├── graceful-send-print.js │ │ ├── graceful-send-string.js │ │ ├── graceful-send-twice.js │ │ ├── graceful-send.js │ │ ├── graceful-twice.js │ │ ├── graceful-wait.js │ │ ├── hello.cmd │ │ ├── hello.sh │ │ ├── ipc-any.js │ │ ├── ipc-disconnect-get.js │ │ ├── ipc-disconnect.js │ │ ├── ipc-echo-fail.js │ │ ├── ipc-echo-filter.js │ │ ├── ipc-echo-item.js │ │ ├── ipc-echo-twice-fail.js │ │ ├── ipc-echo-twice-wait.js │ │ ├── ipc-echo-twice.js │ │ ├── ipc-echo-wait.js │ │ ├── ipc-echo.js │ │ ├── ipc-get-filter-throw.js │ │ ├── ipc-get-io-error.js │ │ ├── ipc-get-ref.js │ │ ├── ipc-get-send-get.js │ │ ├── ipc-get-unref.js │ │ ├── ipc-get.js │ │ ├── ipc-iterate-back-serial.js │ │ ├── ipc-iterate-back.js │ │ ├── ipc-iterate-break.js │ │ ├── ipc-iterate-error.js │ │ ├── ipc-iterate-io-error.js │ │ ├── ipc-iterate-print.js │ │ ├── ipc-iterate-ref.js │ │ ├── ipc-iterate-send.js │ │ ├── ipc-iterate-throw.js │ │ ├── ipc-iterate-twice.js │ │ ├── ipc-iterate-unref.js │ │ ├── ipc-iterate.js │ │ ├── ipc-once-disconnect-get.js │ │ ├── ipc-once-disconnect-send.js │ │ ├── ipc-once-disconnect.js │ │ ├── ipc-once-message-get.js │ │ ├── ipc-once-message-send.js │ │ ├── ipc-once-message.js │ │ ├── ipc-print-many-each.js │ │ ├── ipc-print-many.js │ │ ├── ipc-process-error.js │ │ ├── ipc-process-send-get.js │ │ ├── ipc-process-send-send.js │ │ ├── ipc-process-send.js │ │ ├── ipc-replay.js │ │ ├── ipc-send-argv.js │ │ ├── ipc-send-disconnect.js │ │ ├── ipc-send-echo-strict.js │ │ ├── ipc-send-echo-wait.js │ │ ├── ipc-send-error.js │ │ ├── ipc-send-fail.js │ │ ├── ipc-send-forever.js │ │ ├── ipc-send-get.js │ │ ├── ipc-send-io-error.js │ │ ├── ipc-send-json.js │ │ ├── ipc-send-many.js │ │ ├── ipc-send-native.js │ │ ├── ipc-send-pid.js │ │ ├── ipc-send-print.js │ │ ├── ipc-send-repeat.js │ │ ├── ipc-send-strict-catch.js │ │ ├── ipc-send-strict-get.js │ │ ├── ipc-send-strict-listen.js │ │ ├── ipc-send-strict.js │ │ ├── ipc-send-twice.js │ │ ├── ipc-send-wait-print.js │ │ ├── ipc-send.js │ │ ├── max-buffer.js │ │ ├── nested/ │ │ │ ├── custom-event.js │ │ │ ├── custom-json.js │ │ │ ├── custom-object-stdout.js │ │ │ ├── custom-option.js │ │ │ ├── custom-print-function.js │ │ │ ├── custom-print-multiple.js │ │ │ ├── custom-print.js │ │ │ ├── custom-result.js │ │ │ ├── custom-return.js │ │ │ ├── custom-throw.js │ │ │ ├── custom-uppercase.js │ │ │ ├── file-url.js │ │ │ ├── generator-big-array.js │ │ │ ├── generator-duplex.js │ │ │ ├── generator-object.js │ │ │ ├── generator-string-object.js │ │ │ ├── generator-uppercase.js │ │ │ ├── writable-web.js │ │ │ └── writable.js │ │ ├── nested-double.js │ │ ├── nested-fail.js │ │ ├── nested-inherit.js │ │ ├── nested-multiple-stdin.js │ │ ├── nested-multiple-stdio-output.js │ │ ├── nested-node.js │ │ ├── nested-pipe-file.js │ │ ├── nested-pipe-script.js │ │ ├── nested-pipe-stream.js │ │ ├── nested-pipe-subprocess.js │ │ ├── nested-pipe-subprocesses.js │ │ ├── nested-pipe-verbose.js │ │ ├── nested-stdio.js │ │ ├── nested-sync-tty.js │ │ ├── nested-write.js │ │ ├── nested.js │ │ ├── no-await.js │ │ ├── no-killable.js │ │ ├── non-executable.js │ │ ├── noop-132.js │ │ ├── noop-both-fail-strict.js │ │ ├── noop-both-fail.js │ │ ├── noop-both.js │ │ ├── noop-continuous.js │ │ ├── noop-delay.js │ │ ├── noop-fail.js │ │ ├── noop-fd-ipc.js │ │ ├── noop-fd.js │ │ ├── noop-forever.js │ │ ├── noop-progressive.js │ │ ├── noop-repeat.js │ │ ├── noop-stdin-double.js │ │ ├── noop-stdin-fail.js │ │ ├── noop-stdin-fd.js │ │ ├── noop-verbose.js │ │ ├── noop.js │ │ ├── stdin-both.js │ │ ├── stdin-fail.js │ │ ├── stdin-fd-both.js │ │ ├── stdin-fd.js │ │ ├── stdin-script.js │ │ ├── stdin-twice-both.js │ │ ├── stdin.js │ │ ├── verbose-script.js │ │ ├── wait-fail.js │ │ └── worker.js │ ├── helpers/ │ │ ├── convert.js │ │ ├── duplex.js │ │ ├── early-error.js │ │ ├── encoding.js │ │ ├── file-path.js │ │ ├── fixtures-directory.js │ │ ├── fs.js │ │ ├── generator.js │ │ ├── graceful.js │ │ ├── input.js │ │ ├── ipc.js │ │ ├── lines.js │ │ ├── listeners.js │ │ ├── map.js │ │ ├── max-buffer.js │ │ ├── nested.js │ │ ├── node-version.js │ │ ├── override-promise.js │ │ ├── parallel.js │ │ ├── pipe.js │ │ ├── run.js │ │ ├── stdio.js │ │ ├── stream.js │ │ ├── verbose.js │ │ ├── wait.js │ │ └── web-transform.js │ ├── io/ │ │ ├── input-option.js │ │ ├── input-sync.js │ │ ├── iterate.js │ │ ├── max-buffer.js │ │ ├── output-async.js │ │ ├── output-sync.js │ │ ├── pipeline.js │ │ └── strip-newline.js │ ├── ipc/ │ │ ├── buffer-messages.js │ │ ├── forward.js │ │ ├── get-each.js │ │ ├── get-one.js │ │ ├── graceful.js │ │ ├── incoming.js │ │ ├── ipc-input.js │ │ ├── outgoing.js │ │ ├── pending.js │ │ ├── reference.js │ │ ├── send.js │ │ ├── strict.js │ │ └── validation.js │ ├── methods/ │ │ ├── bind.js │ │ ├── command.js │ │ ├── create.js │ │ ├── main-async.js │ │ ├── node.js │ │ ├── override-promise.js │ │ ├── parameters-args.js │ │ ├── parameters-command.js │ │ ├── parameters-options.js │ │ ├── promise.js │ │ ├── script.js │ │ └── template.js │ ├── pipe/ │ │ ├── abort.js │ │ ├── pipe-arguments.js │ │ ├── sequence.js │ │ ├── setup.js │ │ ├── streaming.js │ │ └── throw.js │ ├── resolve/ │ │ ├── all.js │ │ ├── buffer-end.js │ │ ├── exit.js │ │ ├── no-buffer.js │ │ ├── stdio.js │ │ ├── wait-abort.js │ │ ├── wait-epipe.js │ │ ├── wait-error.js │ │ └── wait-subprocess.js │ ├── return/ │ │ ├── duration.js │ │ ├── early-error.js │ │ ├── final-error.js │ │ ├── message.js │ │ ├── output.js │ │ ├── reject.js │ │ └── result.js │ ├── stdio/ │ │ ├── direction.js │ │ ├── duplex.js │ │ ├── duplicate.js │ │ ├── file-descriptor.js │ │ ├── file-path-error.js │ │ ├── file-path-main.js │ │ ├── file-path-mixed.js │ │ ├── handle-invalid.js │ │ ├── handle-normal.js │ │ ├── handle-options.js │ │ ├── iterable.js │ │ ├── lines-main.js │ │ ├── lines-max-buffer.js │ │ ├── lines-mixed.js │ │ ├── lines-noop.js │ │ ├── native-fd.js │ │ ├── native-inherit-pipe.js │ │ ├── native-redirect.js │ │ ├── node-stream-custom.js │ │ ├── node-stream-native.js │ │ ├── stdio-option.js │ │ ├── type-invalid.js │ │ ├── type-undefined.js │ │ ├── typed-array.js │ │ ├── web-stream.js │ │ └── web-transform.js │ ├── terminate/ │ │ ├── cancel.js │ │ ├── cleanup.js │ │ ├── graceful.js │ │ ├── kill-error.js │ │ ├── kill-force.js │ │ ├── kill-signal.js │ │ ├── signal.js │ │ └── timeout.js │ ├── transform/ │ │ ├── encoding-final.js │ │ ├── encoding-ignored.js │ │ ├── encoding-multibyte.js │ │ ├── encoding-transform.js │ │ ├── generator-all.js │ │ ├── generator-error.js │ │ ├── generator-final.js │ │ ├── generator-input.js │ │ ├── generator-main.js │ │ ├── generator-mixed.js │ │ ├── generator-output.js │ │ ├── generator-return.js │ │ ├── normalize-transform.js │ │ ├── split-binary.js │ │ ├── split-lines.js │ │ ├── split-newline.js │ │ ├── split-transform.js │ │ └── validate.js │ └── verbose/ │ ├── complete.js │ ├── custom-command.js │ ├── custom-common.js │ ├── custom-complete.js │ ├── custom-error.js │ ├── custom-event.js │ ├── custom-id.js │ ├── custom-ipc.js │ ├── custom-options.js │ ├── custom-output.js │ ├── custom-reject.js │ ├── custom-result.js │ ├── custom-start.js │ ├── custom-throw.js │ ├── error.js │ ├── info.js │ ├── ipc.js │ ├── log.js │ ├── output-buffer.js │ ├── output-enable.js │ ├── output-mixed.js │ ├── output-noop.js │ ├── output-pipe.js │ ├── output-progressive.js │ └── start.js ├── test-d/ │ ├── arguments/ │ │ ├── encoding-option.test-d.ts │ │ ├── env.test-d.ts │ │ ├── options.test-d.ts │ │ └── specific.test-d.ts │ ├── convert/ │ │ ├── duplex.test-d.ts │ │ ├── iterable.test-d.ts │ │ ├── readable.test-d.ts │ │ └── writable.test-d.ts │ ├── ipc/ │ │ ├── get-each.test-d.ts │ │ ├── get-one.test-d.ts │ │ ├── graceful.ts │ │ ├── message.test-d.ts │ │ └── send.test-d.ts │ ├── methods/ │ │ ├── command.test-d.ts │ │ ├── list.test-d.ts │ │ ├── main-async.test-d.ts │ │ ├── main-sync.test-d.ts │ │ ├── node.test-d.ts │ │ ├── script-s.test-d.ts │ │ ├── script-sync.test-d.ts │ │ ├── script.test-d.ts │ │ └── template.test-d.ts │ ├── pipe.test-d.ts │ ├── return/ │ │ ├── ignore-option.test-d.ts │ │ ├── ignore-other.test-d.ts │ │ ├── lines-main.test-d.ts │ │ ├── lines-specific.test-d.ts │ │ ├── no-buffer-main.test-d.ts │ │ ├── no-buffer-specific.test-d.ts │ │ ├── result-all.test-d.ts │ │ ├── result-ipc.ts │ │ ├── result-main.test-d.ts │ │ ├── result-reject.test-d.ts │ │ └── result-stdio.test-d.ts │ ├── stdio/ │ │ ├── array.test-d.ts │ │ ├── direction.test-d.ts │ │ └── option/ │ │ ├── array-binary.test-d.ts │ │ ├── array-object.test-d.ts │ │ ├── array-string.test-d.ts │ │ ├── duplex-invalid.test-d.ts │ │ ├── duplex-object.test-d.ts │ │ ├── duplex-transform.test-d.ts │ │ ├── duplex.test-d.ts │ │ ├── fd-integer-0.test-d.ts │ │ ├── fd-integer-1.test-d.ts │ │ ├── fd-integer-2.test-d.ts │ │ ├── fd-integer-3.test-d.ts │ │ ├── file-append-invalid.test-d.ts │ │ ├── file-append.test-d.ts │ │ ├── file-object-invalid.test-d.ts │ │ ├── file-object.test-d.ts │ │ ├── file-url.test-d.ts │ │ ├── final-async-full.test-d.ts │ │ ├── final-invalid-full.test-d.ts │ │ ├── final-object-full.test-d.ts │ │ ├── final-unknown-full.test-d.ts │ │ ├── generator-async-full.test-d.ts │ │ ├── generator-async.test-d.ts │ │ ├── generator-binary-invalid.test-d.ts │ │ ├── generator-binary.test-d.ts │ │ ├── generator-boolean-full.test-d.ts │ │ ├── generator-boolean.test-d.ts │ │ ├── generator-empty.test-d.ts │ │ ├── generator-invalid-full.test-d.ts │ │ ├── generator-invalid.test-d.ts │ │ ├── generator-object-full.test-d.ts │ │ ├── generator-object-mode-invalid.test-d.ts │ │ ├── generator-object-mode.test-d.ts │ │ ├── generator-object.test-d.ts │ │ ├── generator-only-binary.test-d.ts │ │ ├── generator-only-final.test-d.ts │ │ ├── generator-only-object-mode.test-d.ts │ │ ├── generator-only-preserve.test-d.ts │ │ ├── generator-preserve-invalid.test-d.ts │ │ ├── generator-preserve.test-d.ts │ │ ├── generator-string-full.test-d.ts │ │ ├── generator-string.test-d.ts │ │ ├── generator-unknown-full.test-d.ts │ │ ├── generator-unknown.test-d.ts │ │ ├── ignore.test-d.ts │ │ ├── inherit.test-d.ts │ │ ├── ipc.test-d.ts │ │ ├── iterable-async-binary.test-d.ts │ │ ├── iterable-async-object.test-d.ts │ │ ├── iterable-async-string.test-d.ts │ │ ├── iterable-binary.test-d.ts │ │ ├── iterable-object.test-d.ts │ │ ├── iterable-string.test-d.ts │ │ ├── null.test-d.ts │ │ ├── overlapped.test-d.ts │ │ ├── pipe-inherit.test-d.ts │ │ ├── pipe-undefined.test-d.ts │ │ ├── pipe-wide.test-d.ts │ │ ├── pipe.test-d.ts │ │ ├── process-stderr.test-d.ts │ │ ├── process-stdin.test-d.ts │ │ ├── process-stdout.test-d.ts │ │ ├── readable-stream.test-d.ts │ │ ├── readable.test-d.ts │ │ ├── uint-array.test-d.ts │ │ ├── undefined.test-d.ts │ │ ├── unknown.test-d.ts │ │ ├── web-transform-instance.test-d.ts │ │ ├── web-transform-invalid.test-d.ts │ │ ├── web-transform-object.test-d.ts │ │ ├── web-transform.test-d.ts │ │ ├── writable-stream.test-d.ts │ │ └── writable.test-d.ts │ ├── subprocess/ │ │ ├── all.test-d.ts │ │ ├── stdio.test-d.ts │ │ └── subprocess.test-d.ts │ ├── transform/ │ │ └── object-mode.test-d.ts │ └── verbose.test-d.ts ├── tsconfig.json └── types/ ├── arguments/ │ ├── encoding-option.d.ts │ ├── fd-options.d.ts │ ├── options.d.ts │ └── specific.d.ts ├── convert.d.ts ├── ipc.d.ts ├── methods/ │ ├── command.d.ts │ ├── main-async.d.ts │ ├── main-sync.d.ts │ ├── node.d.ts │ ├── script.d.ts │ └── template.d.ts ├── pipe.d.ts ├── return/ │ ├── final-error.d.ts │ ├── ignore.d.ts │ ├── result-all.d.ts │ ├── result-ipc.d.ts │ ├── result-stdio.d.ts │ ├── result-stdout.d.ts │ └── result.d.ts ├── stdio/ │ ├── array.d.ts │ ├── direction.d.ts │ ├── option.d.ts │ └── type.d.ts ├── subprocess/ │ ├── all.d.ts │ ├── stdio.d.ts │ ├── stdout.d.ts │ └── subprocess.d.ts ├── transform/ │ ├── normalize.d.ts │ └── object-mode.d.ts ├── utils.d.ts └── verbose.d.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.yml] indent_style = space indent_size = 2 ================================================ FILE: .gitattributes ================================================ * text=auto eol=lf ================================================ FILE: .github/codecov.yml ================================================ codecov: notify: after_n_builds: 6 ================================================ FILE: .github/security.md ================================================ # Security Policy To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. ================================================ FILE: .github/workflows/main.yml ================================================ name: CI on: - push - pull_request jobs: test: name: Node.js ${{ matrix.node-version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }}-latest strategy: fail-fast: false matrix: node-version: - 24 - 18 os: - ubuntu - macos - windows steps: - uses: actions/cache@v4 with: path: .lycheecache key: cache-lychee-${{ github.sha }} restore-keys: cache-lychee- - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install - uses: lycheeverse/lychee-action@v1 with: args: --cache --verbose --no-progress --include-fragments --exclude packagephobia --exclude /pull/ --exclude linkedin --exclude stackoverflow --exclude stackexchange --exclude github.com/nodejs/node --exclude file:///test --exclude invalid.com '*.md' 'docs/*.md' '.github/**/*.md' '*.json' '*.js' 'lib/**/*.js' 'test/**/*.js' '*.ts' 'test-d/**/*.ts' fail: true if: ${{ matrix.os == 'ubuntu' && matrix.node-version == 24 }} - run: npm run lint - run: npm run type - run: npm run unit - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} flags: '${{ matrix.os }}, node-${{ matrix.node-version }}' fail_ci_if_error: false verbose: true ================================================ FILE: .gitignore ================================================ node_modules yarn.lock .nyc_output coverage ================================================ FILE: .npmrc ================================================ package-lock=false ================================================ FILE: docs/api.md ================================================ execa logo
# 📔 API reference This lists all available [methods](#methods) and their [options](#options-1). This also describes the properties of the [subprocess](#subprocess), [result](#result) and [error](#execaerror) they return. ## Methods ### execa(file, arguments?, options?) `file`: `string | URL`\ `arguments`: `string[]`\ `options`: [`Options`](#options-1)\ _Returns_: [`ResultPromise`](#return-value) Executes a command using `file ...arguments`. More info on the [syntax](execution.md#array-syntax) and [escaping](escaping.md#array-syntax). ### $(file, arguments?, options?) `file`: `string | URL`\ `arguments`: `string[]`\ `options`: [`Options`](#options-1)\ _Returns_: [`ResultPromise`](#return-value) Same as [`execa()`](#execafile-arguments-options) but using [script-friendly default options](scripts.md#script-files). This is the preferred method when executing multiple commands in a script file. [More info.](scripts.md) ### execaNode(scriptPath, arguments?, options?) `scriptPath`: `string | URL`\ `arguments`: `string[]`\ `options`: [`Options`](#options-1)\ _Returns_: [`ResultPromise`](#return-value) Same as [`execa()`](#execafile-arguments-options) but using the [`node: true`](#optionsnode) option. Executes a Node.js file using `node scriptPath ...arguments`. This is the preferred method when executing Node.js files. [More info.](node.md) ### execaSync(file, arguments?, options?) ### $.sync(file, arguments?, options?) ### $.s(file, arguments?, options?) `file`: `string | URL`\ `arguments`: `string[]`\ `options`: [`SyncOptions`](#options-1)\ _Returns_: [`SyncResult`](#return-value) Same as [`execa()`](#execafile-arguments-options) and [`$`](#file-arguments-options) but synchronous. Returns a subprocess [`result`](#result) or throws an [`error`](#execasyncerror). The [`subprocess`](#subprocess) is not returned: its methods and properties are not available. Those methods are discouraged as they hold the CPU and lack multiple features. [More info.](execution.md#synchronous-execution) ### execa\`command\` ### $\`command\` ### execaNode\`command\` ### execaSync\`command\` ### $.sync\`command\` ### $.s\`command\` `command`: `string`\ _Returns_: [`ResultPromise`](#return-value), [`SyncResult`](#return-value) Same as [`execa()`](#execafile-arguments-options), [`$()`](#file-arguments-options), [`execaNode()`](#execanodescriptpath-arguments-options) and [`execaSync()`](#execasyncfile-arguments-options) but using a [template string](execution.md#template-string-syntax). `command` includes both the `file` and its `arguments`. More info on the [syntax](execution.md#template-string-syntax) and [escaping](escaping.md#template-string-syntax). ### execa(options)\`command\` ### $(options)\`command\` ### execaNode(options)\`command\` ### execaSync(options)\`command\` ### $.sync(options)\`command\` ### $.s(options)\`command\` `command`: `string`\ `options`: [`Options`](#options-1), [`SyncOptions`](#options-1)\ _Returns_: [`ResultPromise`](#return-value), [`SyncResult`](#return-value) Same as [```execa`command` ```](#execacommand) but with [options](#options-1). [More info.](execution.md#template-string-syntax) ### execa(options) ### $(options) ### execaNode(options) ### execaSync(options) ### $.sync(options) ### $.s(options) `options`: [`Options`](#options-1), [`SyncOptions`](#options-1)\ _Returns_: [`ExecaMethod`](#execafile-arguments-options), [`ExecaScriptMethod`](#file-arguments-options), [`ExecaNodeMethod`](#execanodescriptpath-arguments-options), [`ExecaSyncMethod`](#execasyncfile-arguments-options), [`ExecaScriptSyncMethod`](#syncfile-arguments-options) Returns a new instance of those methods but with different default [`options`](#options-1). Consecutive calls are merged to previous ones. [More info.](execution.md#globalshared-options) ### parseCommandString(command) `command`: `string`\ _Returns_: `string[]` Split a `command` string into an array. For example, `'npm run build'` returns `['npm', 'run', 'build']` and `'argument otherArgument'` returns `['argument', 'otherArgument']`. [More info.](escaping.md#user-defined-input) ### sendMessage(message, sendMessageOptions?) `message`: [`Message`](ipc.md#message-type)\ `sendMessageOptions`: [`SendMessageOptions`](#sendmessageoptions)\ _Returns_: `Promise` Send a `message` to the parent process. This requires the [`ipc`](#optionsipc) option to be `true`. The [type](ipc.md#message-type) of `message` depends on the [`serialization`](#optionsserialization) option. [More info.](ipc.md#exchanging-messages) #### sendMessageOptions _Type_: `object` #### sendMessageOptions.strict _Type_: `boolean`\ _Default_: `false` Throw when the other process is not receiving or listening to messages. [More info.](ipc.md#ensure-messages-are-received) ### getOneMessage(getOneMessageOptions?) `getOneMessageOptions`: [`GetOneMessageOptions`](#getonemessageoptions)\ _Returns_: [`Promise`](ipc.md#message-type) Receive a single `message` from the parent process. This requires the [`ipc`](#optionsipc) option to be `true`. The [type](ipc.md#message-type) of `message` depends on the [`serialization`](#optionsserialization) option. [More info.](ipc.md#exchanging-messages) #### getOneMessageOptions _Type_: `object` #### getOneMessageOptions.filter _Type_: [`(Message) => boolean`](ipc.md#message-type) Ignore any `message` that returns `false`. [More info.](ipc.md#filter-messages) #### getOneMessageOptions.reference _Type_: `boolean`\ _Default_: `true` Keep the subprocess alive while `getOneMessage()` is waiting. [More info.](ipc.md#keeping-the-subprocess-alive) ### getEachMessage(getEachMessageOptions?) `getEachMessageOptions`: [`GetEachMessageOptions`](#geteachmessageoptions)\ _Returns_: [`AsyncIterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols) Iterate over each `message` from the parent process. This requires the [`ipc`](#optionsipc) option to be `true`. The [type](ipc.md#message-type) of `message` depends on the [`serialization`](#optionsserialization) option. [More info.](ipc.md#listening-to-messages) #### getEachMessageOptions _Type_: `object` #### getEachMessageOptions.reference _Type_: `boolean`\ _Default_: `true` Keep the subprocess alive while `getEachMessage()` is waiting. [More info.](ipc.md#keeping-the-subprocess-alive) ### getCancelSignal() _Returns_: [`Promise`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) Retrieves the [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) shared by the [`cancelSignal`](#optionscancelsignal) option. This can only be called inside a subprocess. This requires the [`gracefulCancel`](#optionsgracefulcancel) option to be `true`. [More info.](termination.md#graceful-termination) ## Return value _TypeScript:_ [`ResultPromise`](typescript.md)\ _Type:_ `Promise | Subprocess` The return value of all [asynchronous methods](#methods) is both: - the [subprocess](#subprocess). - a `Promise` either resolving with its successful [`result`](#result), or rejecting with its [`error`](#execaerror). [More info.](execution.md#subprocess) ## Subprocess _TypeScript:_ [`Subprocess`](typescript.md) [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with the following methods and properties. ### subprocess\[Symbol.asyncIterator\]() _Returns_: [`AsyncIterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols) Subprocesses are [async iterables](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator). They iterate over each output line. [More info.](lines.md#progressive-splitting) ### subprocess.iterable(readableOptions?) `readableOptions`: [`ReadableOptions`](#readableoptions)\ _Returns_: [`AsyncIterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols) Same as [`subprocess[Symbol.asyncIterator]`](#subprocesssymbolasynciterator) except [options](#readableoptions) can be provided. [More info.](lines.md#progressive-splitting) ### subprocess.pipe(file, arguments?, options?) `file`: `string | URL`\ `arguments`: `string[]`\ `options`: [`Options`](#options-1) and [`PipeOptions`](#pipeoptions)\ _Returns_: [`Promise`](#result) [Pipe](https://nodejs.org/api/stream.html#readablepipedestination-options) the subprocess' [`stdout`](#subprocessstdout) to a second Execa subprocess' [`stdin`](#subprocessstdin). This resolves with that second subprocess' [result](#result). If either subprocess is rejected, this is rejected with that subprocess' [error](#execaerror) instead. This follows the same syntax as [`execa(file, arguments?, options?)`](#execafile-arguments-options) except both [regular options](#options-1) and [pipe-specific options](#pipeoptions) can be specified. [More info.](pipe.md#array-syntax) ### subprocess.pipe\`command\` ### subprocess.pipe(options)\`command\` `command`: `string`\ `options`: [`Options`](#options-1) and [`PipeOptions`](#pipeoptions)\ _Returns_: [`Promise`](#result) Like [`subprocess.pipe(file, arguments?, options?)`](#subprocesspipefile-arguments-options) but using a [`command` template string](execution.md#template-string-syntax) instead. This follows the same syntax as `execa` [template strings](execution.md#template-string-syntax). [More info.](pipe.md#template-string-syntax) ### subprocess.pipe(secondSubprocess, pipeOptions?) `secondSubprocess`: [`ResultPromise`](#return-value)\ `pipeOptions`: [`PipeOptions`](#pipeoptions)\ _Returns_: [`Promise`](#result) Like [`subprocess.pipe(file, arguments?, options?)`](#subprocesspipefile-arguments-options) but using the [return value](#return-value) of another [`execa()`](#execafile-arguments-options) call instead. [More info.](pipe.md#advanced-syntax) #### pipeOptions _Type:_ `object` #### pipeOptions.from _Type:_ `"stdout" | "stderr" | "all" | "fd3" | "fd4" | ...`\ _Default:_ `"stdout"` Which stream to pipe from the source subprocess. A [file descriptor](https://en.wikipedia.org/wiki/File_descriptor) like `"fd3"` can also be passed. `"all"` pipes both [`stdout`](#subprocessstdout) and [`stderr`](#subprocessstderr). This requires the [`all`](#optionsall) option to be `true`. [More info.](pipe.md#source-file-descriptor) #### pipeOptions.to _Type:_ `"stdin" | "fd3" | "fd4" | ...`\ _Default:_ `"stdin"` Which [stream](#subprocessstdin) to pipe to the destination subprocess. A [file descriptor](https://en.wikipedia.org/wiki/File_descriptor) like `"fd3"` can also be passed. [More info.](pipe.md#destination-file-descriptor) #### pipeOptions.unpipeSignal _Type:_ [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) Unpipe the subprocess when the signal aborts. [More info.](pipe.md#unpipe) ### subprocess.kill(signal, error?) ### subprocess.kill(error?) `signal`: `string | number`\ `error`: `Error`\ _Returns_: `boolean` Sends a [signal](https://nodejs.org/api/os.html#signal-constants) to the subprocess. The default signal is the [`killSignal`](#optionskillsignal) option. `killSignal` defaults to `SIGTERM`, which [terminates](#erroristerminated) the subprocess. This returns `false` when the signal could not be sent, for example when the subprocess has already exited. When an error is passed as argument, it is set to the subprocess' [`error.cause`](#errorcause). The subprocess is then terminated with the default signal. This does not emit the [`error` event](https://nodejs.org/api/child_process.html#event-error). [More info.](termination.md) ### subprocess.pid _Type:_ `number | undefined` Process identifier ([PID](https://en.wikipedia.org/wiki/Process_identifier)). This is `undefined` if the subprocess failed to spawn. [More info.](termination.md#inter-process-termination) ### subprocess.sendMessage(message, sendMessageOptions) `message`: [`Message`](ipc.md#message-type)\ `sendMessageOptions`: [`SendMessageOptions`](#sendmessageoptions)\ _Returns_: `Promise` Send a `message` to the subprocess. This requires the [`ipc`](#optionsipc) option to be `true`. The [type](ipc.md#message-type) of `message` depends on the [`serialization`](#optionsserialization) option. [More info.](ipc.md#exchanging-messages) ### subprocess.getOneMessage(getOneMessageOptions?) `getOneMessageOptions`: [`GetOneMessageOptions`](#getonemessageoptions)\ _Returns_: [`Promise`](ipc.md#message-type) Receive a single `message` from the subprocess. This requires the [`ipc`](#optionsipc) option to be `true`. The [type](ipc.md#message-type) of `message` depends on the [`serialization`](#optionsserialization) option. [More info.](ipc.md#exchanging-messages) ### subprocess.getEachMessage(getEachMessageOptions?) `getEachMessageOptions`: [`GetEachMessageOptions`](#geteachmessageoptions)\ _Returns_: [`AsyncIterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols) Iterate over each `message` from the subprocess. This requires the [`ipc`](#optionsipc) option to be `true`. The [type](ipc.md#message-type) of `message` depends on the [`serialization`](#optionsserialization) option. [More info.](ipc.md#listening-to-messages) ### subprocess.stdin _Type:_ [`Writable | null`](https://nodejs.org/api/stream.html#class-streamwritable) The subprocess [`stdin`](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)) as a stream. This is `null` if the [`stdin`](#optionsstdin) option is set to [`'inherit'`](input.md#terminal-input), [`'ignore'`](input.md#ignore-input), [`Readable`](streams.md#input) or [`integer`](input.md#terminal-input). [More info.](streams.md#manual-streaming) ### subprocess.stdout _Type:_ [`Readable | null`](https://nodejs.org/api/stream.html#class-streamreadable) The subprocess [`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)) as a stream. This is `null` if the [`stdout`](#optionsstdout) option is set to [`'inherit'`](output.md#terminal-output), [`'ignore'`](output.md#ignore-output), [`Writable`](streams.md#output) or [`integer`](output.md#terminal-output), or if the [`buffer`](#optionsbuffer) option is `false`. [More info.](streams.md#manual-streaming) ### subprocess.stderr _Type:_ [`Readable | null`](https://nodejs.org/api/stream.html#class-streamreadable) The subprocess [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)) as a stream. This is `null` if the [`stderr`](#optionsstdout) option is set to [`'inherit'`](output.md#terminal-output), [`'ignore'`](output.md#ignore-output), [`Writable`](streams.md#output) or [`integer`](output.md#terminal-output), or if the [`buffer`](#optionsbuffer) option is `false`. [More info.](streams.md#manual-streaming) ### subprocess.all _Type:_ [`Readable | undefined`](https://nodejs.org/api/stream.html#class-streamreadable) Stream combining/interleaving [`subprocess.stdout`](#subprocessstdout) and [`subprocess.stderr`](#subprocessstderr). This requires the [`all`](#optionsall) option to be `true`. This is `undefined` if [`stdout`](#optionsstdout) and [`stderr`](#optionsstderr) options are set to [`'inherit'`](output.md#terminal-output), [`'ignore'`](output.md#ignore-output), [`Writable`](streams.md#output) or [`integer`](output.md#terminal-output), or if the [`buffer`](#optionsbuffer) option is `false`. More info on [interleaving](output.md#interleaved-output) and [streaming](streams.md#manual-streaming). ### subprocess.stdio _Type:_ [`[Writable | null, Readable | null, Readable | null, ...Array]`](https://nodejs.org/api/stream.html#class-streamreadable) The subprocess [`stdin`](#subprocessstdin), [`stdout`](#subprocessstdout), [`stderr`](#subprocessstderr) and [other files descriptors](#optionsstdio) as an array of streams. Each array item is `null` if the corresponding [`stdin`](#optionsstdin), [`stdout`](#optionsstdout), [`stderr`](#optionsstderr) or [`stdio`](#optionsstdio) option is set to [`'inherit'`](output.md#terminal-output), [`'ignore'`](output.md#ignore-output), [`Stream`](streams.md#output) or [`integer`](output.md#terminal-output), or if the [`buffer`](#optionsbuffer) option is `false`. [More info.](streams.md#manual-streaming) ### subprocess.readable(readableOptions?) `readableOptions`: [`ReadableOptions`](#readableoptions)\ _Returns_: [`Readable`](https://nodejs.org/api/stream.html#class-streamreadable) Node.js stream Converts the subprocess to a readable stream. [More info.](streams.md#converting-a-subprocess-to-a-stream) #### readableOptions _Type:_ `object` #### readableOptions.from _Type:_ `"stdout" | "stderr" | "all" | "fd3" | "fd4" | ...`\ _Default:_ `"stdout"` Which stream to read from the subprocess. A [file descriptor](https://en.wikipedia.org/wiki/File_descriptor) like `"fd3"` can also be passed. `"all"` reads both [`stdout`](#subprocessstdout) and [`stderr`](#subprocessstderr). This requires the [`all`](#optionsall) option to be `true`. [More info.](streams.md#different-file-descriptor) #### readableOptions.binary _Type:_ `boolean`\ _Default:_ `false` with [`subprocess.iterable()`](#subprocessiterablereadableoptions), `true` with [`subprocess.readable()`](#subprocessreadablereadableoptions)/[`subprocess.duplex()`](#subprocessduplexduplexoptions) If `false`, iterates over lines. Each line is a string. If `true`, iterates over arbitrary chunks of data. Each line is an [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) (with [`subprocess.iterable()`](#subprocessiterablereadableoptions)) or a [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer) (with [`subprocess.readable()`](#subprocessreadablereadableoptions)/[`subprocess.duplex()`](#subprocessduplexduplexoptions)). This is always `true` when the [`encoding`](#optionsencoding) option is binary. More info for [iterables](binary.md#iterable) and [streams](binary.md#streams). #### readableOptions.preserveNewlines _Type:_ `boolean`\ _Default:_ `false` with [`subprocess.iterable()`](#subprocessiterablereadableoptions), `true` with [`subprocess.readable()`](#subprocessreadablereadableoptions)/[`subprocess.duplex()`](#subprocessduplexduplexoptions) If both this option and the [`binary`](#readableoptionsbinary) option is `false`, [newlines](https://en.wikipedia.org/wiki/Newline) are stripped from each line. [More info.](lines.md#iterable) ### subprocess.writable(writableOptions?) `writableOptions`: [`WritableOptions`](#writableoptions)\ _Returns_: [`Writable`](https://nodejs.org/api/stream.html#class-streamwritable) Node.js stream Converts the subprocess to a writable stream. [More info.](streams.md#converting-a-subprocess-to-a-stream) #### writableOptions _Type:_ `object` #### writableOptions.to _Type:_ `"stdin" | "fd3" | "fd4" | ...`\ _Default:_ `"stdin"` Which [stream](#subprocessstdin) to write to the subprocess. A [file descriptor](https://en.wikipedia.org/wiki/File_descriptor) like `"fd3"` can also be passed. [More info.](streams.md#different-file-descriptor) ### subprocess.duplex(duplexOptions?) `duplexOptions`: [`ReadableOptions | WritableOptions`](#readableoptions)\ _Returns_: [`Duplex`](https://nodejs.org/api/stream.html#class-streamduplex) Node.js stream Converts the subprocess to a duplex stream. [More info.](streams.md#converting-a-subprocess-to-a-stream) ## Result _TypeScript:_ [`Result`](typescript.md) or [`SyncResult`](typescript.md)\ _Type:_ `object` [Result](execution.md#result) of a subprocess successful execution. When the subprocess [fails](errors.md#subprocess-failure), it is rejected with an [`ExecaError`](#execaerror) instead. ### result.stdout _Type:_ `string | Uint8Array | string[] | Uint8Array[] | unknown[] | undefined` The output of the subprocess on [`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)). This is `undefined` if the [`stdout`](#optionsstdout) option is set to only [`'inherit'`](output.md#terminal-output), [`'ignore'`](output.md#ignore-output), [`Writable`](streams.md#output) or [`integer`](output.md#terminal-output), or if the [`buffer`](#optionsbuffer) option is `false`. This is an array if the [`lines`](#optionslines) option is `true`, or if the `stdout` option is a [transform in object mode](transform.md#object-mode). [More info.](output.md#stdout-and-stderr) ### result.stderr _Type:_ `string | Uint8Array | string[] | Uint8Array[] | unknown[] | undefined` The output of the subprocess on [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). This is `undefined` if the [`stderr`](#optionsstderr) option is set to only [`'inherit'`](output.md#terminal-output), [`'ignore'`](output.md#ignore-output), [`Writable`](streams.md#output) or [`integer`](output.md#terminal-output), or if the [`buffer`](#optionsbuffer) option is `false`. This is an array if the [`lines`](#optionslines) option is `true`, or if the `stderr` option is a [transform in object mode](transform.md#object-mode). [More info.](output.md#stdout-and-stderr) ### result.all _Type:_ `string | Uint8Array | string[] | Uint8Array[] | unknown[] | undefined` The output of the subprocess with [`result.stdout`](#resultstdout) and [`result.stderr`](#resultstderr) interleaved. This requires the [`all`](#optionsall) option to be `true`. This is `undefined` if both [`stdout`](#optionsstdout) and [`stderr`](#optionsstderr) options are set to only [`'inherit'`](output.md#terminal-output), [`'ignore'`](output.md#ignore-output), [`Writable`](streams.md#output) or [`integer`](output.md#terminal-output), or if the [`buffer`](#optionsbuffer) option is `false`. This is an array if the [`lines`](#optionslines) option is `true`, or if either the `stdout` or `stderr` option is a [transform in object mode](transform.md#object-mode). [More info.](output.md#interleaved-output) ### result.stdio _Type:_ `Array` The output of the subprocess on [`stdin`](#optionsstdin), [`stdout`](#optionsstdout), [`stderr`](#optionsstderr) and [other file descriptors](#optionsstdio). Items are `undefined` when their corresponding [`stdio`](#optionsstdio) option is set to [`'inherit'`](output.md#terminal-output), [`'ignore'`](output.md#ignore-output), [`Writable`](streams.md#output) or [`integer`](output.md#terminal-output), or if the [`buffer`](#optionsbuffer) option is `false`. Items are arrays when their corresponding `stdio` option is a [transform in object mode](transform.md#object-mode). [More info.](output.md#additional-file-descriptors) ### result.ipcOutput _Type_: [`Message[]`](ipc.md#message-type) All the messages [sent by the subprocess](#sendmessagemessage-sendmessageoptions) to the current process. This is empty unless the [`ipc`](#optionsipc) option is `true`. Also, this is empty if the [`buffer`](#optionsbuffer) option is `false`. [More info.](ipc.md#retrieve-all-messages) ### result.pipedFrom _Type:_ [`Array`](#result) [Results](#result) of the other subprocesses that were piped into this subprocess. This array is initially empty and is populated each time the [`subprocess.pipe()`](#subprocesspipefile-arguments-options) method resolves. [More info.](pipe.md#result) ### result.command _Type:_ `string` The file and [arguments](input.md#command-arguments) that were run. [More info.](debugging.md#command) ### result.escapedCommand _Type:_ `string` Same as [`command`](#resultcommand) but escaped. [More info.](debugging.md#command) ### result.cwd _Type:_ `string` The [current directory](#optionscwd) in which the command was run. [More info.](environment.md#current-directory) ### result.durationMs _Type:_ `number` Duration of the subprocess, in milliseconds. [More info.](debugging.md#duration) ### result.failed _Type:_ `boolean` Whether the subprocess failed to run. When this is `true`, the result is an [`ExecaError`](#execaerror) instance with additional error-related properties. [More info.](errors.md#subprocess-failure) ## ExecaError ## ExecaSyncError _Type:_ `Error` Result of a subprocess [failed execution](errors.md#subprocess-failure). This error is thrown as an exception. If the [`reject`](#optionsreject) option is false, it is returned instead. This has the same shape as [successful results](#result), with the following additional properties. [More info.](errors.md) ### error.message _Type:_ `string` Error message when the subprocess [failed](errors.md#subprocess-failure) to run. [More info.](errors.md#error-message) ### error.shortMessage _Type:_ `string` This is the same as [`error.message`](#errormessage) except it does not include the subprocess [output](output.md). [More info.](errors.md#error-message) ### error.originalMessage _Type:_ `string | undefined` Original error message. This is the same as [`error.message`](#errormessage) excluding the subprocess [output](output.md) and some additional information added by Execa. [More info.](errors.md#error-message) ### error.cause _Type:_ `unknown | undefined` Underlying error, if there is one. For example, this is set by [`subprocess.kill(error)`](#subprocesskillerror). This is usually an `Error` instance. [More info.](termination.md#error-message-and-stack-trace) ### error.code _Type:_ `string | undefined` Node.js-specific [error code](https://nodejs.org/api/errors.html#errorcode), when available. ### error.timedOut _Type:_ `boolean` Whether the subprocess timed out due to the [`timeout`](#optionstimeout) option. [More info.](termination.md#timeout) ### error.isCanceled _Type:_ `boolean` Whether the subprocess was canceled using the [`cancelSignal`](#optionscancelsignal) option. [More info.](termination.md#canceling) ### error.isGracefullyCanceled _Type:_ `boolean` Whether the subprocess was canceled using both the [`cancelSignal`](#optionscancelsignal) and the [`gracefulCancel`](#optionsgracefulcancel) options. [More info.](termination.md#graceful-termination) ### error.isMaxBuffer _Type:_ `boolean` Whether the subprocess failed because its output was larger than the [`maxBuffer`](#optionsmaxbuffer) option. [More info.](output.md#big-output) ### error.isTerminated _Type:_ `boolean` Whether the subprocess was terminated by a [signal](termination.md#signal-termination) (like [`SIGTERM`](termination.md#sigterm)) sent by either: - The current process. - [Another process](termination.md#inter-process-termination). This case is [not supported on Windows](https://nodejs.org/api/process.html#signal-events). [More info.](termination.md#signal-name-and-description) ### error.isForcefullyTerminated _Type:_ `boolean` Whether the subprocess was terminated by the [`SIGKILL`](termination.md#sigkill) signal sent by the [`forceKillAfterDelay`](#optionsforcekillafterdelay) option. [More info.](termination.md#forceful-termination) ### error.exitCode _Type:_ `number | undefined` The numeric [exit code](https://en.wikipedia.org/wiki/Exit_status) of the subprocess that was run. This is `undefined` when the subprocess could not be spawned or was terminated by a [signal](#errorsignal). [More info.](errors.md#exit-code) ### error.signal _Type:_ `string | undefined` The name of the [signal](termination.md#signal-termination) (like [`SIGTERM`](termination.md#sigterm)) that terminated the subprocess, sent by either: - The current process. - [Another process](termination.md#inter-process-termination). This case is [not supported on Windows](https://nodejs.org/api/process.html#signal-events). If a signal terminated the subprocess, this property is defined and included in the [error message](#errormessage). Otherwise it is `undefined`. [More info.](termination.md#signal-name-and-description) ### error.signalDescription _Type:_ `string | undefined` A human-friendly description of the [signal](termination.md#signal-termination) that was used to terminate the subprocess. If a signal terminated the subprocess, this property is defined and included in the error message. Otherwise it is `undefined`. It is also `undefined` when the signal is very uncommon which should seldomly happen. [More info.](termination.md#signal-name-and-description) ## Options _TypeScript:_ [`Options`](typescript.md) or [`SyncOptions`](typescript.md)\ _Type:_ `object` This lists all options for [`execa()`](#execafile-arguments-options) and the [other methods](#methods). The following options [can specify different values](output.md#stdoutstderr-specific-options) for [`stdout`](#optionsstdout) and [`stderr`](#optionsstderr): [`verbose`](#optionsverbose), [`lines`](#optionslines), [`stripFinalNewline`](#optionsstripfinalnewline), [`buffer`](#optionsbuffer), [`maxBuffer`](#optionsmaxbuffer). ### options.preferLocal _Type:_ `boolean`\ _Default:_ `true` with [`$`](#file-arguments-options), `false` otherwise Prefer locally installed binaries when looking for a binary to execute. [More info.](environment.md#local-binaries) ### options.localDir _Type:_ `string | URL`\ _Default:_ [`cwd`](#optionscwd) option Preferred path to find locally installed binaries, when using the [`preferLocal`](#optionspreferlocal) option. [More info.](environment.md#local-binaries) ### options.node _Type:_ `boolean`\ _Default:_ `true` with [`execaNode()`](#execanodescriptpath-arguments-options), `false` otherwise If `true`, runs with Node.js. The first argument must be a Node.js file. The subprocess inherits the current Node.js [CLI flags](https://nodejs.org/api/cli.html#options) and version. This can be overridden using the [`nodeOptions`](#optionsnodeoptions) and [`nodePath`](#optionsnodepath) options. [More info.](node.md) ### options.nodeOptions _Type:_ `string[]`\ _Default:_ [`process.execArgv`](https://nodejs.org/api/process.html#process_process_execargv) (current Node.js CLI flags) List of [CLI flags](https://nodejs.org/api/cli.html#cli_options) passed to the [Node.js executable](#optionsnodepath). Requires the [`node`](#optionsnode) option to be `true`. [More info.](node.md#nodejs-cli-flags) ### options.nodePath _Type:_ `string | URL`\ _Default:_ [`process.execPath`](https://nodejs.org/api/process.html#process_process_execpath) (current Node.js executable) Path to the Node.js executable. Requires the [`node`](#optionsnode) option to be `true`. [More info.](node.md#nodejs-version) ### options.shell _Type:_ `boolean | string | URL`\ _Default:_ `false` If `true`, runs the command inside of a [shell](https://en.wikipedia.org/wiki/Shell_(computing)). Uses [`/bin/sh`](https://en.wikipedia.org/wiki/Unix_shell) on UNIX and [`cmd.exe`](https://en.wikipedia.org/wiki/Cmd.exe) on Windows. A different shell can be specified as a string. The shell should understand the `-c` switch on UNIX or `/d /s /c` on Windows. We [recommend against](shell.md#avoiding-shells) using this option. [More info.](shell.md) ### options.cwd _Type:_ `string | URL`\ _Default:_ `process.cwd()` Current [working directory](https://en.wikipedia.org/wiki/Working_directory) of the subprocess. This is also used to resolve the [`nodePath`](#optionsnodepath) option when it is a relative path. [More info.](environment.md#current-directory) ### options.env _Type:_ `object`\ _Default:_ [`process.env`](https://nodejs.org/api/process.html#processenv) [Environment variables](https://en.wikipedia.org/wiki/Environment_variable). Unless the [`extendEnv`](#optionsextendenv) option is `false`, the subprocess also uses the current process' environment variables ([`process.env`](https://nodejs.org/api/process.html#processenv)). [More info.](input.md#environment-variables) ### options.extendEnv _Type:_ `boolean`\ _Default:_ `true` If `true`, the subprocess uses both the [`env`](#optionsenv) option and the current process' environment variables ([`process.env`](https://nodejs.org/api/process.html#processenv)). If `false`, only the `env` option is used, not `process.env`. [More info.](input.md#environment-variables) ### options.input _Type:_ `string | Uint8Array | stream.Readable` Write some input to the subprocess' [`stdin`](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). See also the [`inputFile`](#optionsinputfile) and [`stdin`](#optionsstdin) options. [More info.](input.md#string-input) ### options.inputFile _Type:_ `string | URL` Use a file as input to the subprocess' [`stdin`](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). See also the [`input`](#optionsinput) and [`stdin`](#optionsstdin) options. [More info.](input.md#file-input) ### options.stdin _TypeScript:_ [`StdinOption`](typescript.md) or [`StdinSyncOption`](typescript.md)\ _Type:_ `string | number | stream.Readable | ReadableStream | TransformStream | URL | {file: string} | Uint8Array | Iterable | AsyncIterable | GeneratorFunction | AsyncGeneratorFunction | {transform: GeneratorFunction | AsyncGeneratorFunction | Duplex | TransformStream}` (or a tuple of those types)\ _Default:_ `'inherit'` with [`$`](#file-arguments-options), `'pipe'` otherwise How to setup the subprocess' [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). This can be [`'pipe'`](streams.md#manual-streaming), [`'overlapped'`](windows.md#asynchronous-io), [`'ignore`](input.md#ignore-input), [`'inherit'`](input.md#terminal-input), a [file descriptor integer](input.md#terminal-input), a [Node.js `Readable` stream](streams.md#input), a web [`ReadableStream`](streams.md#web-streams), a [`{ file: 'path' }` object](input.md#file-input), a [file URL](input.md#file-input), an [`Iterable`](streams.md#iterables-as-input) (including an [array of strings](input.md#string-input)), an [`AsyncIterable`](streams.md#iterables-as-input), an [`Uint8Array`](binary.md#binary-input), a [generator function](transform.md), a [`Duplex`](transform.md#duplextransform-streams) or a web [`TransformStream`](transform.md#duplextransform-streams). This can be an [array of values](output.md#multiple-targets) such as `['inherit', 'pipe']` or `[fileUrl, 'pipe']`. More info on [available values](input.md), [streaming](streams.md) and [transforms](transform.md). ### options.stdout _TypeScript:_ [`StdoutStderrOption`](typescript.md) or [`StdoutStderrSyncOption`](typescript.md)\ _Type:_ `string | number | stream.Writable | WritableStream | TransformStream | URL | {file: string} | GeneratorFunction | AsyncGeneratorFunction | {transform: GeneratorFunction | AsyncGeneratorFunction | Duplex | TransformStream}` (or a tuple of those types)\ _Default:_ `pipe` How to setup the subprocess' [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). This can be [`'pipe'`](output.md#stdout-and-stderr), [`'overlapped'`](windows.md#asynchronous-io), [`'ignore`](output.md#ignore-output), [`'inherit'`](output.md#terminal-output), a [file descriptor integer](output.md#terminal-output), a [Node.js `Writable` stream](streams.md#output), a web [`WritableStream`](streams.md#web-streams), a [`{ file: 'path' }` object](output.md#file-output), a [file URL](output.md#file-output), a [generator function](transform.md), a [`Duplex`](transform.md#duplextransform-streams) or a web [`TransformStream`](transform.md#duplextransform-streams). This can be an [array of values](output.md#multiple-targets) such as `['inherit', 'pipe']` or `[fileUrl, 'pipe']`. More info on [available values](output.md), [streaming](streams.md) and [transforms](transform.md). ### options.stderr _TypeScript:_ [`StdoutStderrOption`](typescript.md) or [`StdoutStderrSyncOption`](typescript.md)\ _Type:_ `string | number | stream.Writable | WritableStream | TransformStream | URL | {file: string} | GeneratorFunction | AsyncGeneratorFunction | {transform: GeneratorFunction | AsyncGeneratorFunction | Duplex | TransformStream}` (or a tuple of those types)\ _Default:_ `pipe` How to setup the subprocess' [standard error](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). This can be [`'pipe'`](output.md#stdout-and-stderr), [`'overlapped'`](windows.md#asynchronous-io), [`'ignore`](output.md#ignore-output), [`'inherit'`](output.md#terminal-output), a [file descriptor integer](output.md#terminal-output), a [Node.js `Writable` stream](streams.md#output), a web [`WritableStream`](streams.md#web-streams), a [`{ file: 'path' }` object](output.md#file-output), a [file URL](output.md#file-output), a [generator function](transform.md), a [`Duplex`](transform.md#duplextransform-streams) or a web [`TransformStream`](transform.md#duplextransform-streams). This can be an [array of values](output.md#multiple-targets) such as `['inherit', 'pipe']` or `[fileUrl, 'pipe']`. More info on [available values](output.md), [streaming](streams.md) and [transforms](transform.md). ### options.stdio _TypeScript:_ [`Options['stdio']`](typescript.md) or [`SyncOptions['stdio']`](typescript.md)\ _Type:_ `string | Array | Iterable | Iterable | AsyncIterable | GeneratorFunction | AsyncGeneratorFunction | {transform: GeneratorFunction | AsyncGeneratorFunction | Duplex | TransformStream}>` (or a tuple of those types)\ _Default:_ `pipe` Like the [`stdin`](#optionsstdin), [`stdout`](#optionsstdout) and [`stderr`](#optionsstderr) options but for all [file descriptors](https://en.wikipedia.org/wiki/File_descriptor) at once. For example, `{stdio: ['ignore', 'pipe', 'pipe']}` is the same as `{stdin: 'ignore', stdout: 'pipe', stderr: 'pipe'}`. A single string can be used [as a shortcut](output.md#shortcut). The array can have more than 3 items, to create [additional file descriptors](output.md#additional-file-descriptors) beyond [`stdin`](#optionsstdin)/[`stdout`](#optionsstdout)/[`stderr`](#optionsstderr). More info on [available values](output.md), [streaming](streams.md) and [transforms](transform.md). ### options.all _Type:_ `boolean`\ _Default:_ `false` Add a [`subprocess.all`](#subprocessall) stream and a [`result.all`](#resultall) property. [More info.](output.md#interleaved-output) ### options.encoding _Type:_ `'utf8' | 'utf16le' | 'buffer' | 'hex' | 'base64' | 'base64url' | 'latin1' | 'ascii'`\ _Default:_ `'utf8'` If the subprocess outputs text, specifies its character encoding, either [`'utf8'`](https://en.wikipedia.org/wiki/UTF-8) or [`'utf16le'`](https://en.wikipedia.org/wiki/UTF-16). If it outputs binary data instead, this should be either: - `'buffer'`: returns the binary output as an [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). - [`'hex'`](https://en.wikipedia.org/wiki/Hexadecimal), [`'base64'`](https://en.wikipedia.org/wiki/Base64), [`'base64url'`](https://en.wikipedia.org/wiki/Base64#URL_applications), [`'latin1'`](https://nodejs.org/api/buffer.html#buffers-and-character-encodings) or [`'ascii'`](https://nodejs.org/api/buffer.html#buffers-and-character-encodings): encodes the binary output as a string. The output is available with [`result.stdout`](#resultstdout), [`result.stderr`](#resultstderr) and [`result.stdio`](#resultstdio). [More info.](binary.md) ### options.lines _Type:_ `boolean`\ _Default:_ `false` Set [`result.stdout`](#resultstdout), [`result.stderr`](#resultstdout), [`result.all`](#resultall) and [`result.stdio`](#resultstdio) as arrays of strings, splitting the subprocess' output into lines. This cannot be used if the [`encoding`](#optionsencoding) option is [binary](binary.md#binary-output). By default, this applies to both `stdout` and `stderr`, but [different values can also be passed](output.md#stdoutstderr-specific-options). [More info.](lines.md#simple-splitting) ### options.stripFinalNewline _Type:_ `boolean`\ _Default:_ `true` Strip the final [newline character](https://en.wikipedia.org/wiki/Newline) from the output. If the [`lines`](#optionslines) option is true, this applies to each output line instead. By default, this applies to both `stdout` and `stderr`, but [different values can also be passed](output.md#stdoutstderr-specific-options). [More info.](lines.md#newlines) ### options.maxBuffer _Type:_ `number`\ _Default:_ `100_000_000` Largest amount of data allowed on [`stdout`](#resultstdout), [`stderr`](#resultstderr) and [`stdio`](#resultstdio). By default, this applies to both `stdout` and `stderr`, but [different values can also be passed](output.md#stdoutstderr-specific-options). When reached, [`error.isMaxBuffer`](#errorismaxbuffer) becomes `true`. [More info.](output.md#big-output) ### options.buffer _Type:_ `boolean`\ _Default:_ `true` When `buffer` is `false`, the [`result.stdout`](#resultstdout), [`result.stderr`](#resultstderr), [`result.all`](#resultall) and [`result.stdio`](#resultstdio) properties are not set. By default, this applies to both `stdout` and `stderr`, but [different values can also be passed](output.md#stdoutstderr-specific-options). [More info.](output.md#low-memory) ### options.ipc _Type:_ `boolean`\ _Default:_ `true` if the [`node`](#optionsnode), [`ipcInput`](#optionsipcinput) or [`gracefulCancel`](#optionsgracefulcancel) option is set, `false` otherwise Enables exchanging messages with the subprocess using [`subprocess.sendMessage(message)`](#subprocesssendmessagemessage-sendmessageoptions), [`subprocess.getOneMessage()`](#subprocessgetonemessagegetonemessageoptions) and [`subprocess.getEachMessage()`](#subprocessgeteachmessagegeteachmessageoptions). The subprocess must be a Node.js file. [More info.](ipc.md) ### options.serialization _Type:_ `'json' | 'advanced'`\ _Default:_ `'advanced'` Specify the kind of serialization used for sending messages between subprocesses when using the [`ipc`](#optionsipc) option. [More info.](ipc.md#message-type) ### options.ipcInput _Type_: [`Message`](ipc.md#message-type) Sends an IPC message when the subprocess starts. The subprocess must be a [Node.js file](#optionsnode). The value's [type](ipc.md#message-type) depends on the [`serialization`](#optionsserialization) option. More info [here](ipc.md#send-an-initial-message) and [there](input.md#any-input-type). ### options.verbose _Type:_ `'none' | 'short' | 'full' | Function`\ _Default:_ `'none'` If `verbose` is `'short'`, prints the command on [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)): its file, arguments, duration and (if it failed) error message. If `verbose` is `'full'` or a function, the command's [`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)), `stderr` and [IPC messages](ipc.md) are also printed. A [function](#verbose-function) can be passed to customize logging. Please see [this page](debugging.md#custom-logging) for more information. By default, this applies to both `stdout` and `stderr`, but [different values can also be passed](output.md#stdoutstderr-specific-options). [More info.](debugging.md#verbose-mode) ### options.reject _Type:_ `boolean`\ _Default:_ `true` Setting this to `false` resolves the [result's promise](#return-value) with the [error](#execaerror) instead of rejecting it. [More info.](errors.md#preventing-exceptions) ### options.timeout _Type:_ `number`\ _Default:_ `0` If `timeout` is greater than `0`, the subprocess will be [terminated](#optionskillsignal) if it runs for longer than that amount of milliseconds. On timeout, [`error.timedOut`](#errortimedout) becomes `true`. [More info.](termination.md#timeout) ### options.cancelSignal _Type:_ [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) When the `cancelSignal` is [aborted](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort), terminate the subprocess using a `SIGTERM` signal. When aborted, [`error.isCanceled`](#erroriscanceled) becomes `true`. [More info.](termination.md#canceling) ### options.gracefulCancel _Type:_ `boolean`\ _Default:_: `false` When the [`cancelSignal`](#optionscancelsignal) option is [aborted](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort), do not send any [`SIGTERM`](termination.md#canceling). Instead, abort the [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) returned by [`getCancelSignal()`](#getcancelsignal). The subprocess should use it to terminate gracefully. The subprocess must be a [Node.js file](#optionsnode). When aborted, [`error.isGracefullyCanceled`](#errorisgracefullycanceled) becomes `true`. [More info.](termination.md#graceful-termination) ### options.forceKillAfterDelay _Type:_ `number | false`\ _Default:_ `5000` If the subprocess is terminated but does not exit, forcefully exit it by sending [`SIGKILL`](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGKILL). When this happens, [`error.isForcefullyTerminated`](#errorisforcefullyterminated) becomes `true`. [More info.](termination.md#forceful-termination) ### options.killSignal _Type:_ `string | number`\ _Default:_ `'SIGTERM'` Default [signal](https://en.wikipedia.org/wiki/Signal_(IPC)) used to terminate the subprocess. This can be either a name (like [`'SIGTERM'`](termination.md#sigterm)) or a number (like `9`). [More info.](termination.md#default-signal) ### options.detached _Type:_ `boolean`\ _Default:_ `false` Run the subprocess independently from the current process. [More info.](environment.md#background-subprocess) ### options.cleanup _Type:_ `boolean`\ _Default:_ `true` Kill the subprocess when the current process exits. [More info.](termination.md#current-process-exit) ### options.uid _Type:_ `number`\ _Default:_ current user identifier Sets the [user identifier](https://en.wikipedia.org/wiki/User_identifier) of the subprocess. [More info.](windows.md#uid-and-gid) ### options.gid _Type:_ `number`\ _Default:_ current group identifier Sets the [group identifier](https://en.wikipedia.org/wiki/Group_identifier) of the subprocess. [More info.](windows.md#uid-and-gid) ### options.argv0 _Type:_ `string`\ _Default:_ file being executed Value of [`argv[0]`](https://nodejs.org/api/process.html#processargv0) sent to the subprocess. ### options.windowsHide _Type:_ `boolean`\ _Default:_ `true` On Windows, do not create a new console window. [More info.](windows.md#console-window) ### options.windowsVerbatimArguments _Type:_ `boolean`\ _Default:_ `true` if the [`shell`](#optionsshell) option is `true`, `false` otherwise If `false`, escapes the command arguments on Windows. [More info.](windows.md#escaping) ## Verbose function _Type_: `(string, VerboseObject) => string | undefined` Function passed to the [`verbose`](#optionsverbose) option to customize logging. [More info.](debugging.md#custom-logging) ### Verbose object _Type_: `VerboseObject` or `SyncVerboseObject` Subprocess event object, for logging purpose, using the [`verbose`](#optionsverbose) option. #### verboseObject.type _Type_: `string` Event type. This can be: - `'command'`: subprocess start - `'output'`: `stdout`/`stderr` [output](output.md#stdout-and-stderr) - `'ipc'`: IPC [output](ipc.md#retrieve-all-messages) - `'error'`: subprocess [failure](errors.md#subprocess-failure) - `'duration'`: subprocess success or failure #### verboseObject.message _Type_: `string` Depending on [`verboseObject.type`](#verboseobjecttype), this is: - `'command'`: the [`result.escapedCommand`](#resultescapedcommand) - `'output'`: one line from [`result.stdout`](#resultstdout) or [`result.stderr`](#resultstderr) - `'ipc'`: one IPC message from [`result.ipcOutput`](#resultipcoutput) - `'error'`: the [`error.shortMessage`](#errorshortmessage) - `'duration'`: the [`result.durationMs`](#resultdurationms) #### verboseObject.escapedCommand _Type_: `string` The file and [arguments](input.md#command-arguments) that were run. This is the same as [`result.escapedCommand`](#resultescapedcommand). #### verboseObject.options _Type_: [`Options`](#options-1) or [`SyncOptions`](#options-1) The [options](#options-1) passed to the subprocess. #### verboseObject.commandId _Type_: `string` Serial number identifying the subprocess within the current process. It is incremented from `'0'`. This is helpful when multiple subprocesses are running at the same time. This is similar to a [PID](https://en.wikipedia.org/wiki/Process_identifier) except it has no maximum limit, which means it never repeats. Also, it is usually shorter. #### verboseObject.timestamp _Type_: `Date` Event date/time. #### verboseObject.result _Type_: [`Result`](#result), [`SyncResult`](#result) or `undefined` Subprocess [result](#result). This is `undefined` if [`verboseObject.type`](#verboseobjecttype) is `'command'`, `'output'` or `'ipc'`. #### verboseObject.piped _Type_: `boolean` Whether another subprocess is [piped](pipe.md) into this subprocess. This is `false` when [`result.pipedFrom`](#resultfailed) is empty. ## Transform options A transform or an [array of transforms](transform.md#combining) can be passed to the [`stdin`](#optionsstdin), [`stdout`](#optionsstdout), [`stderr`](#optionsstderr) or [`stdio`](#optionsstdio) option. A transform is either a [generator function](#transformoptionstransform) or a plain object with the following members. [More info.](transform.md) ### transformOptions.transform _Type:_ `GeneratorFunction` | `AsyncGeneratorFunction` Map or [filter](transform.md#filtering) the [input](input.md) or [output](output.md) of the subprocess. More info [here](transform.md#summary) and [there](transform.md#sharing-state). ### transformOptions.final _Type:_ `GeneratorFunction` | `AsyncGeneratorFunction` Create additional lines after the last one. [More info.](transform.md#finalizing) ### transformOptions.binary _Type:_ `boolean`\ _Default:_ `false` If `true`, iterate over arbitrary chunks of `Uint8Array`s instead of line `string`s. [More info.](binary.md#transforms) ### transformOptions.preserveNewlines _Type:_ `boolean`\ _Default:_ `false` If `true`, keep newlines in each `line` argument. Also, this allows multiple `yield`s to produces a single line. [More info.](lines.md#transforms) ### transformOptions.objectMode _Type:_ `boolean`\ _Default:_ `false` If `true`, allow [`transformOptions.transform`](#transformoptionstransform) and [`transformOptions.final`](#transformoptionsfinal) to return any type, not just `string` or `Uint8Array`. [More info.](transform.md#object-mode)
[**Previous**: 🔍 Differences with Bash and zx](bash.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/bash.md ================================================ execa logo
# 🔍 Differences with Bash and zx This page describes the differences between [Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)), Execa, and [zx](https://github.com/google/zx). Execa intends to be more: - [Simple](#simplicity): minimalistic API, no [globals](#global-variables), no [binary](#main-binary), no builtin CLI utilities. - [Cross-platform](#shell): [no shell](shell.md) is used, only JavaScript. - [Secure](#escaping): no shell injection. - [Featureful](#simplicity): all Execa features are available ([text lines iteration](#iterate-over-output-lines), [advanced piping](#piping-stdout-to-another-command), [simple IPC](#ipc), [passing any input type](#pass-any-input-type), [returning any output type](#return-any-output-type), [transforms](#transforms), [web streams](#web-streams), [convert to Duplex stream](#convert-to-duplex-stream), [cleanup on exit](termination.md#current-process-exit), [graceful termination](#graceful-termination), [forceful termination](termination.md#forceful-termination), and [more](../readme.md#documentation)). - [Easy to debug](#debugging): [verbose mode](#verbose-mode-single-command), [detailed errors](#detailed-errors), [messages and stack traces](#cancelation), stateless API. - [Performant](#performance) ## Flexibility Unlike shell languages like Bash, libraries like Execa and zx enable you to write scripts with a more featureful programming language (JavaScript). This allows complex logic (such as [parallel execution](#parallel-commands)) to be expressed easily. This also lets you use any Node.js package. ## Shell The main difference between Execa and zx is that Execa does not require any shell. Shell-specific keywords and features are [written in JavaScript](#variable-substitution) instead. This is more cross-platform. For example, your code works the same on Windows machines without Bash installed. Also, there is no shell syntax to remember: everything is just plain JavaScript. If you really need a shell though, the [`shell`](shell.md) option can be used. ## Simplicity Execa's scripting API mostly consists of only two methods: [`` $`command` ``](shell.md) and [`$(options)`](execution.md#globalshared-options). [No special binary](#main-binary) is recommended, no [global variable](#global-variables) is injected: scripts are regular Node.js files. Execa is a thin wrapper around the core Node.js [`child_process` module](https://nodejs.org/api/child_process.html). It lets you use any of its native features. ## Modularity zx includes many builtin utilities: [`fetch()`](#http-requests), [`question()`](#cli-prompts), [`sleep()`](#sleep), [`echo()`](#printing-to-stdout), [`stdin()`](#retrieve-stdin), [`retry()`](#retry-on-error), [`spinner()`](#cli-spinner), [`globby`](#globbing), [`chalk`](https://github.com/chalk/chalk), [`fs`](https://github.com/jprichardson/node-fs-extra), [`os`](https://nodejs.org/api/os.html), [`path`](https://nodejs.org/api/path.html), [`yaml`](https://github.com/eemeli/yaml), [`which`](https://github.com/npm/node-which), [`ps`](https://github.com/webpod/ps), [`tmpfile()`](#temporary-file), [`argv`](#cli-arguments), Markdown scripts, remote scripts. Execa does not include any utility: it focuses on being small and modular instead. [Any Node.js package](https://github.com/sindresorhus/awesome-nodejs#command-line-utilities) can be used in your scripts. ## Performance Spawning a shell for every command comes at a performance cost, which Execa avoids. ## Debugging Subprocesses can be hard to debug, which is why Execa includes a [`verbose`](#verbose-mode-single-command) option. It includes [more information](debugging.md#full-mode) than zx: timestamps, command completion and duration, interleaved commands, IPC messages. Also, Execa's error messages and [properties](#detailed-errors) are very detailed to make it clear to determine why a subprocess failed. Error messages and stack traces can be set with [`subprocess.kill(error)`](termination.md#error-message-and-stack-trace). Finally, unlike Bash and zx, which are stateful (options, current directory, etc.), Execa is [purely functional](#current-directory), which also helps with debugging. ## Examples ### Main binary ```sh # Bash bash file.sh ``` ```js // zx zx file.js // or a shebang can be used: // #!/usr/bin/env zx ``` ```js // Execa scripts are just regular Node.js files node file.js ``` ### Global variables ```js // zx await $`npm run build`; ``` ```js // Execa import {$} from 'execa'; await $`npm run build`; ``` [More info.](execution.md) ### Command execution ```sh # Bash npm run build ``` ```js // zx await $`npm run build`; ``` ```js // Execa await $`npm run build`; ``` [More info.](execution.md) ### Multiline commands ```sh # Bash npm run build \ --example-flag-one \ --example-flag-two ``` ```js // zx await $`npm run build ${[ '--example-flag-one', '--example-flag-two', ]}`; ``` ```js // Execa await $`npm run build --example-flag-one --example-flag-two`; ``` [More info.](execution.md#multiple-lines) ### Concatenation ```sh # Bash tmpDirectory="/tmp" mkdir "$tmpDirectory/filename" ``` ```js // zx const tmpDirectory = '/tmp' await $`mkdir ${tmpDirectory}/filename`; ``` ```js // Execa const tmpDirectory = '/tmp' await $`mkdir ${tmpDirectory}/filename`; ``` [More info.](execution.md#concatenation) ### Variable substitution ```sh # Bash echo $LANG ``` ```js // zx await $`echo $LANG`; ``` ```js // Execa await $`echo ${process.env.LANG}`; ``` [More info.](input.md#environment-variables) ### Escaping ```sh # Bash echo 'one two' ``` ```js // zx await $`echo ${'one two'}`; ``` ```js // Execa await $`echo ${'one two'}`; ``` [More info.](escaping.md) ### Escaping multiple arguments ```sh # Bash echo 'one two' '$' ``` ```js // zx await $`echo ${['one two', '$']}`; ``` ```js // Execa await $`echo ${['one two', '$']}`; ``` [More info.](execution.md#multiple-arguments) ### Subcommands ```sh # Bash echo "$(npm run build)" ``` ```js // zx const result = await $`npm run build`; await $`echo ${result}`; ``` ```js // Execa const result = await $`npm run build`; await $`echo ${result}`; ``` [More info.](execution.md#subcommands) ### Serial commands ```sh # Bash npm run build && npm run test ``` ```js // zx await $`npm run build && npm run test`; ``` ```js // Execa await $`npm run build`; await $`npm run test`; ``` ### Parallel commands ```sh # Bash npm run build & npm run test & ``` ```js // zx await Promise.all([$`npm run build`, $`npm run test`]); ``` ```js // Execa await Promise.all([$`npm run build`, $`npm run test`]); ``` ### Global/shared options ```sh # Bash options="timeout 5" $options npm run init $options npm run build $options npm run test ``` ```js // zx const $$ = $({verbose: true}); await $$`npm run init`; await $$`npm run build`; await $$`npm run test`; ``` ```js // Execa import {$ as $_} from 'execa'; const $ = $_({verbose: true}); await $`npm run init`; await $`npm run build`; await $`npm run test`; ``` [More info.](execution.md#globalshared-options) ### Environment variables ```sh # Bash EXAMPLE=1 npm run build ``` ```js // zx await $({env: {EXAMPLE: '1'}})`npm run build`; ``` ```js // Execa await $({env: {EXAMPLE: '1'}})`npm run build`; ``` [More info.](input.md#environment-variables) ### Local binaries ```sh # Bash npx tsc --version ``` ```js // zx await $({preferLocal: true})`tsc --version`; ``` ```js // Execa await $({preferLocal: true})`tsc --version`; ``` [More info.](environment.md#local-binaries) ### Retrieve stdin ```sh # Bash read content ``` ```js // zx const content = await stdin(); ``` ```js // Execa import getStdin from 'get-stdin'; const content = await getStdin(); ``` [More info.](https://github.com/sindresorhus/get-stdin) ### Pass input to stdin ```sh # Bash cat <<<"example" ``` ```js // zx $({input: 'example'})`cat`; ``` ```js // Execa $({input: 'example'})`cat`; ``` ### Pass any input type ```sh # Bash only allows passing strings as input ``` ```js // zx only allows passing specific input types ``` ```js // Execa - main.js const ipcInput = [ {task: 'lint', ignore: /test\.js/}, {task: 'copy', files: new Set(['main.js', 'index.js']), }]; await $({ipcInput})`node build.js`; ``` ```js // Execa - build.js import {getOneMessage} from 'execa'; const ipcInput = await getOneMessage(); ``` [More info.](ipc.md#send-an-initial-message) ### Return any output type ```sh # Bash only allows returning strings as output ``` ```js // zx only allows returning specific output types ``` ```js // Execa - main.js const {ipcOutput} = await $({ipc: true})`node build.js`; console.log(ipcOutput[0]); // {kind: 'start', timestamp: date} console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date} ``` ```js // Execa - build.js import {sendMessage} from 'execa'; await sendMessage({kind: 'start', timestamp: new Date()}); await runBuild(); await sendMessage({kind: 'stop', timestamp: new Date()}); ``` [More info.](ipc.md#retrieve-all-messages) ### Printing to stdout ```sh # Bash echo example ``` ```js // zx echo`example`; ``` ```js // Execa console.log('example'); ``` ### Silent stdout ```sh # Bash npm run build > /dev/null ``` ```js // zx await $`npm run build`.quiet(); ``` ```js // Execa does not print stdout by default await $`npm run build`; ``` ### Binary output ```sh # Bash usually requires redirecting binary output zip -r - input.txt > output.txt ``` ```js // zx const stdout = await $`zip -r - input.txt`.buffer(); ``` ```js // Execa const {stdout} = await $({encoding: 'buffer'})`zip -r - input.txt`; ``` [More info.](binary.md#binary-output) ### Verbose mode (single command) ```sh # Bash set -v npm run build set +v ``` ```js // zx await $`npm run build`.verbose(); ``` ```js // Execa await $({verbose: 'full'})`npm run build`; ``` [More info.](debugging.md#verbose-mode) ### Verbose mode (global) ```sh # Bash set -v npm run build ``` ``` // zx $ zx --verbose file.js $ npm run build Building... Done. ``` ``` $ NODE_DEBUG=execa node file.js [19:49:00.360] [0] $ npm run build [19:49:00.360] [0] Building... [19:49:00.360] [0] Done. [19:49:00.383] [0] √ (done in 23ms) ``` [More info.](debugging.md#global-mode) ### Piping stdout to another command ```sh # Bash echo npm run build | sort | head -n2 ``` ```js // zx await $`npm run build` .pipe($`sort`) .pipe($`head -n2`); ``` ```js // Execa await $`npm run build` .pipe`sort` .pipe`head -n2`; ``` [More info.](pipe.md) ### Piping stdout and stderr to another command ```sh # Bash npm run build |& cat ``` ```js // zx const subprocess = $`npm run build`; const cat = $`cat`; subprocess.pipe(cat); subprocess.stderr.pipe(cat.stdin); await Promise.all([subprocess, cat]); ``` ```js // Execa await $({all: true})`npm run build` .pipe({from: 'all'})`cat`; ``` [More info.](pipe.md#source-file-descriptor) ### Piping stdout to a file ```sh # Bash npm run build > output.txt ``` ```js // zx import {createWriteStream} from 'node:fs'; await $`npm run build`.pipe(createWriteStream('output.txt')); ``` ```js // Execa await $({stdout: {file: 'output.txt'}})`npm run build`; ``` [More info.](output.md#file-output) ### Append stdout to a file ```sh # Bash npm run build >> output.txt ``` ```js // zx import {createWriteStream} from 'node:fs'; await $`npm run build`.pipe(createWriteStream('output.txt', {flags: 'a'})); ``` ```js // Execa await $({stdout: {file: 'output.txt', append: true}})`npm run build`; ``` [More info.](output.md#file-output) ### Piping interleaved stdout and stderr to a file ```sh # Bash npm run build &> output.txt ``` ```js // zx import {createWriteStream} from 'node:fs'; const subprocess = $`npm run build`; const fileStream = createWriteStream('output.txt'); subprocess.pipe(fileStream); subprocess.stderr.pipe(fileStream); await subprocess; ``` ```js // Execa const output = {file: 'output.txt'}; await $({stdout: output, stderr: output})`npm run build`; ``` [More info.](output.md#file-output) ### Piping stdin from a file ```sh # Bash cat < input.txt ``` ```js // zx const cat = $`cat`; fs.createReadStream('input.txt').pipe(cat.stdin); await cat; ``` ```js // Execa await $({inputFile: 'input.txt'})`cat`; ``` [More info.](input.md#file-input) ### Web streams ```js // zx does not support web streams ``` ```js // Execa const response = await fetch('https://example.com'); await $({stdin: response.body})`npm run build`; ``` [More info.](streams.md#web-streams) ### Convert to Duplex stream ```js // zx does not support converting subprocesses to streams ``` ```js // Execa import {pipeline} from 'node:stream/promises'; import {createReadStream, createWriteStream} from 'node:fs'; await pipeline( createReadStream('./input.txt'), $`node ./transform.js`.duplex(), createWriteStream('./output.txt'), ); ``` [More info.](streams.md#convert) ### Handle pipeline errors ```sh # Bash set -e npm run crash | sort | head -n2 ``` ```js // zx try { await $`npm run crash` .pipe($`sort`) .pipe($`head -n2`); // This is never reached. // The process crashes instead. } catch (error) { console.error(error); } ``` ```js // Execa try { await $`npm run build` .pipe`sort` .pipe`head -n2`; } catch (error) { console.error(error); } ``` [More info.](pipe.md#errors) ### Return all pipeline results ```sh # Bash only allows returning each command's exit code npm run crash | sort | head -n2 # 1 0 0 echo "${PIPESTATUS[@]}" ``` ```js // zx only returns the last command's result ``` ```js // Execa const destinationResult = await execa`npm run build` .pipe`head -n 2`; console.log(destinationResult.stdout); // First 2 lines of `npm run build` const sourceResult = destinationResult.pipedFrom[0]; console.log(sourceResult.stdout); // Full output of `npm run build` ``` [More info.](pipe.md#result) ### Split output into lines ```sh # Bash npm run build | IFS='\n' read -ra lines ``` ```js // zx const lines = await $`npm run build`.lines(); ``` ```js // Execa const lines = await $({lines: true})`npm run build`; ``` [More info.](lines.md#simple-splitting) ### Iterate over output lines ```sh # Bash while read do if [[ "$REPLY" == *ERROR* ]] then echo "$REPLY" fi done < <(npm run build) ``` ```js // zx does not allow easily iterating over output lines. // Also, the iteration does not handle subprocess errors. ``` ```js // Execa for await (const line of $`npm run build`) { if (line.includes('ERROR')) { console.log(line); } } ``` [More info.](lines.md#progressive-splitting) ### Detailed errors ```sh # Bash communicates errors only through the exit code and stderr timeout 1 sleep 2 echo $? ``` ```js // zx await $`sleep 2`.timeout('1ms'); // Error: // at file:///home/me/Desktop/example.js:6:12 // exit code: null // signal: SIGTERM ``` ```js // Execa await $({timeout: 1})`sleep 2`; // ExecaError: Command timed out after 1 milliseconds: sleep 2 // at file:///home/me/Desktop/example.js:2:20 // at ... { // shortMessage: 'Command timed out after 1 milliseconds: sleep 2\nTimed out', // originalMessage: '', // command: 'sleep 2', // escapedCommand: 'sleep 2', // cwd: '/path/to/cwd', // durationMs: 19.95693, // failed: true, // timedOut: true, // isCanceled: false, // isTerminated: true, // isMaxBuffer: false, // signal: 'SIGTERM', // signalDescription: 'Termination', // stdout: '', // stderr: '', // stdio: [undefined, '', ''], // pipedFrom: [] // } ``` [More info.](errors.md) ### Exit codes ```sh # Bash npm run build echo $? ``` ```js // zx const {exitCode} = await $`npm run build`.nothrow(); ``` ```js // Execa const {exitCode} = await $({reject: false})`npm run build`; ``` [More info.](errors.md#exit-code) ### Timeouts ```sh # Bash timeout 5 npm run build ``` ```js // zx await $`npm run build`.timeout('5s'); ``` ```js // Execa await $({timeout: 5000})`npm run build`; ``` [More info.](termination.md#timeout) ### Current filename ```sh # Bash echo "$(basename "$0")" ``` ```js // zx await $`echo ${__filename}`; ``` ```js // Execa await $`echo ${import.meta.filename}`; ``` ### Current directory ```sh # Bash cd project ``` ```js // zx const $$ = $({cwd: 'project'}); // Or: cd('project'); ``` ```js // Execa const $$ = $({cwd: 'project'}); ``` [More info.](environment.md#current-directory) ### Background subprocess ```sh # Bash npm run build & ``` ```js // zx await $({detached: true})`npm run build`; ``` ```js // Execa await $({detached: true})`npm run build`; ``` [More info.](environment.md#background-subprocess) ### IPC ```sh # Bash does not allow simple IPC ``` ```js // zx does not allow simple IPC ``` ```js // Execa const subprocess = $({node: true})`script.js`; for await (const message of subprocess.getEachMessage()) { if (message === 'ping') { await subprocess.sendMessage('pong'); } }); ``` [More info.](ipc.md) ### Transforms ```sh # Bash does not allow transforms ``` ```js // zx does not allow transforms ``` ```js // Execa const transform = function * (line) { if (!line.includes('secret')) { yield line; } }; await $({stdout: [transform, 'inherit']})`echo ${'This is a secret.'}`; ``` [More info.](transform.md) ### Signal termination ```sh # Bash kill $PID ``` ```js // zx subprocess.kill(); ``` ```js // Execa subprocess.kill(); // Or with an error message and stack trace: subprocess.kill(error); ``` [More info.](termination.md#signal-termination) ### Default signal ```sh # Bash does not allow changing the default termination signal ``` ```js // zx only allows changing the signal used for timeouts const $$ = $({timeoutSignal: 'SIGINT'}); ``` ```js // Execa const $ = $_({killSignal: 'SIGINT'}); ``` [More info.](termination.md#default-signal) ### Cancelation ```sh # Bash kill $PID ``` ```js // zx const controller = new AbortController(); await $({signal: controller.signal})`node long-script.js`; ``` ```js // Execa const controller = new AbortController(); await $({cancelSignal: controller.signal})`node long-script.js`; ``` [More info.](termination.md#canceling) ### Graceful termination ```sh # Bash trap cleanup SIGTERM ``` ```js // zx // This does not work on Windows process.on('SIGTERM', () => { // ... }); ``` ```js // Execa - main.js const controller = new AbortController(); await $({ cancelSignal: controller.signal, gracefulCancel: true, })`node build.js`; ``` ```js // Execa - build.js import {getCancelSignal} from 'execa'; const cancelSignal = await getCancelSignal(); await fetch('https://example.com', {signal: cancelSignal}); ``` ### Interleaved output ```sh # Bash prints stdout and stderr interleaved ``` ```js // zx const all = String(await $`node example.js`); ``` ```js // Execa const {all} = await $({all: true})`node example.js`; ``` [More info.](output.md#interleaved-output) ### PID ```sh # Bash npm run build & echo $! ``` ```js // zx does not return `subprocess.pid` ``` ```js // Execa const {pid} = $`npm run build`; ``` [More info.](termination.md#inter-process-termination) ### CLI arguments ```js // zx const {myCliFlag} = argv; ``` ```js // Execa import {parseArgs} from 'node:util'; const {myCliFlag} = parseArgs({strict: false}).values; ``` [More info.](https://nodejs.org/api/util.html#utilparseargsconfig) ### CLI prompts ```sh # Bash read -p "Question? " answer ``` ```js // zx const answer = await question('Question? '); ``` ```js // Execa import input from '@inquirer/input'; const answer = await input({message: 'Question?'}); ``` [More info.](https://github.com/SBoudrias/Inquirer.js) ### CLI spinner ```sh # Bash does not provide with a builtin spinner ``` ```js // zx await spinner(() => $`node script.js`); ``` ```js // Execa import {oraPromise} from 'ora'; await oraPromise($`node script.js`); ``` [More info.](https://github.com/sindresorhus/ora) ### Sleep ```sh # Bash sleep 5 ``` ```js // zx await sleep(5000); ``` ```js // Execa import {setTimeout} from 'node:timers/promises'; await setTimeout(5000); ``` [More info.](https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options) ### Globbing ```sh # Bash ls packages/* ``` ```js // zx const files = await glob(['packages/*']); ``` ```js // Execa import {glob} from 'node:fs/promises'; const files = await Array.fromAsync(glob('packages/*')); ``` [More info.](https://nodejs.org/api/fs.html#fspromisesglobpattern-options) ### Temporary file ```js // zx const filePath = tmpfile(); ``` ```js // Execa import tempfile from 'tempfile'; const filePath = tempfile(); ``` [More info.](https://github.com/sindresorhus/tempfile) ### HTTP requests ```sh # Bash curl https://github.com ``` ```js // zx await fetch('https://github.com'); ``` ```js // Execa await fetch('https://github.com'); ``` [More info.](https://nodejs.org/api/globals.html#fetch) ### Retry on error ```js // zx await retry( 5, () => $`curl -sSL https://sindresorhus.com/unicorn`, ) ``` ```js // Execa import pRetry from 'p-retry'; await pRetry( () => $`curl -sSL https://sindresorhus.com/unicorn`, {retries: 5}, ); ``` [More info.](https://github.com/sindresorhus/p-retry)
[**Next**: 🐭 Small packages](small.md)\ [**Previous**: 📎 Windows](windows.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/binary.md ================================================ execa logo
# 🤖 Binary data ## Binary input There are multiple ways to pass binary input using the [`stdin`](api.md#optionsstdin), [`input`](api.md#optionsinput) or [`inputFile`](api.md#optionsinputfile) options: `Uint8Array`s, [files](input.md#file-input), [streams](streams.md) or [other subprocesses](pipe.md). This is required if the subprocess input includes [null bytes](https://en.wikipedia.org/wiki/Null_character). ```js import {execa} from 'execa'; const binaryData = new Uint8Array([/* ... */]); await execa({stdin: binaryData})`hexdump`; ``` ## Binary output By default, the subprocess [output](api.md#resultstdout) is a [UTF8](https://en.wikipedia.org/wiki/UTF-8) string. If it is binary, the [`encoding`](api.md#optionsencoding) option should be set to `'buffer'` instead. The output will be an [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). ```js const {stdout} = await execa({encoding: 'buffer'})`zip -r - input.txt`; console.log(stdout.byteLength); ``` ## Encoding When the output is binary, the [`encoding`](api.md#optionsencoding) option can also be set to [`'hex'`](https://en.wikipedia.org/wiki/Hexadecimal), [`'base64'`](https://en.wikipedia.org/wiki/Base64) or [`'base64url'`](https://en.wikipedia.org/wiki/Base64#URL_applications). The output will be a string then. ```js const {stdout} = await execa({encoding: 'hex'})`zip -r - input.txt`; console.log(stdout); // Hexadecimal string ``` ## Iterable By default, the subprocess [iterates](lines.md#progressive-splitting) over line strings. However, if the [`encoding`](api.md#optionsencoding) subprocess option is binary, or if the [`binary`](api.md#readableoptionsbinary) iterable option is `true`, it iterates over arbitrary chunks of [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) data instead. ```js for await (const data of execa({encoding: 'buffer'})`zip -r - input.txt`) { /* ... */ } ``` ## Transforms The same applies to transforms. When the [`encoding`](api.md#optionsencoding) subprocess option is binary, or when the [`binary`](api.md#transformoptionsbinary) transform option is `true`, it iterates over arbitrary chunks of [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) data instead. However, transforms can always `yield` either a `string` or an `Uint8Array`, regardless of whether the output is binary or not. ```js const transform = function * (data) { /* ... */ } await execa({stdout: {transform, binary: true}})`zip -r - input.txt`; ``` ## Streams [Streams produced](streams.md#converting-a-subprocess-to-a-stream) by [`subprocess.readable()`](api.md#subprocessreadablereadableoptions) and [`subprocess.duplex()`](api.md#subprocessduplexduplexoptions) are binary by default, which means they iterate over arbitrary [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer) chunks. However, if the [`binary`](api.md#readableoptionsbinary) option is `false`, they iterate over line strings instead, and the stream is [in object mode](https://nodejs.org/api/stream.html#object-mode). ```js const readable = execa`npm run build`.readable({binary: false}); readable.on('data', lineString => { /* ... */ }); ```
[**Next**: 🧙 Transforms](transform.md)\ [**Previous**: 📃 Text lines](lines.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/debugging.md ================================================ execa logo
# 🐛 Debugging ## Command [`error.command`](api.md#resultcommand) contains the file and [arguments](input.md#command-arguments) that were run. It is intended for logging or debugging. [`error.escapedCommand`](api.md#resultescapedcommand) is the same, except control characters are escaped. This makes it safe to either print or copy and paste in a terminal, for debugging purposes. Since the escaping is fairly basic, neither `error.command` nor `error.escapedCommand` should be executed directly, including using [`execa()`](api.md#execafile-arguments-options) or [`parseCommandString()`](api.md#parsecommandstringcommand). ```js import {execa} from 'execa'; try { await execa`npm run build\ntask`; } catch (error) { console.error(error.command); // "npm run build\ntask" console.error(error.escapedCommand); // "npm run 'build\\ntask'" throw error; } ``` ## Duration ```js try { const result = await execa`npm run build`; console.log('Command duration:', result.durationMs); // 150 } catch (error) { console.error('Command duration:', error.durationMs); // 150 throw error; } ``` ## Verbose mode ### Short mode When the [`verbose`](api.md#optionsverbose) option is `'short'`, the [command](#command), [duration](#duration) and [error messages](errors.md#error-message) are printed on [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). ```js // build.js await execa({verbose: 'short'})`npm run build`; ``` ``` $ node build.js [20:36:11.043] [0] $ npm run build [20:36:11.885] [0] ✔ (done in 842ms) ``` ### Full mode When the [`verbose`](api.md#optionsverbose) option is `'full'`, the subprocess' [`stdout`, `stderr`](output.md) and [IPC messages](ipc.md) are also logged. They are all printed on [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). The output is not logged if either: - The [`stdout`](api.md#optionsstdout)/[`stderr`](api.md#optionsstderr) option is [`'ignore'`](output.md#ignore-output) or [`'inherit'`](output.md#terminal-output). - The `stdout`/`stderr` is redirected to [a stream](streams.md#output), [a file](output.md#file-output), [a file descriptor](output.md#terminal-output), or [another subprocess](pipe.md). - The [`encoding`](api.md#optionsencoding) option is [binary](binary.md#binary-output). ```js // build.js await execa({verbose: 'full'})`npm run build`; await execa({verbose: 'full'})`npm run test`; ``` ``` $ node build.js [00:57:44.581] [0] $ npm run build [00:57:44.653] [0] Building application... [00:57:44.653] [0] Done building. [00:57:44.658] [0] ✔ (done in 78ms) [00:57:44.658] [1] $ npm run test [00:57:44.740] [1] Running tests... [00:57:44.740] [1] Error: the entrypoint is invalid. [00:57:44.747] [1] ✘ Command failed with exit code 1: npm run test [00:57:44.747] [1] ✘ (done in 89ms) ``` ### Global mode When the `NODE_DEBUG=execa` [environment variable](https://en.wikipedia.org/wiki/Environment_variable) is set, the [`verbose`](api.md#optionsverbose) option defaults to `'full'` for all commands. ```js // build.js // This is logged by default await execa`npm run build`; // This is not logged await execa({verbose: 'none'})`npm run test`; ``` ``` $ NODE_DEBUG=execa node build.js ``` ### Colors When printed to a terminal, the verbose mode uses colors. execa verbose output ## Custom logging ### Verbose function The [`verbose`](api.md#optionsverbose) option can be a function to customize logging. It is called once per log line. The first argument is the default log line string. The second argument is the same information but as an object instead (documented [here](api.md#verbose-object)). If a string is returned, it is printed on `stderr`. If `undefined` is returned, nothing is printed. ### Filter logs ```js import {execa as execa_} from 'execa'; // Only print log lines showing the subprocess duration const execa = execa_({ verbose(verboseLine, {type}) { return type === 'duration' ? verboseLine : undefined; }, }); ``` ### Transform logs ```js import {execa as execa_} from 'execa'; // Prepend current process' PID const execa = execa_({ verbose(verboseLine) { return `[${process.pid}] ${verboseLine}`; }, }); ``` ### Custom log format ```js import {execa as execa_} from 'execa'; // Use a different format for the timestamp const execa = execa_({ verbose(verboseLine, {timestamp}) { return verboseLine.replace(timestampRegExp, timestamp.toISOString()); }, }); // Timestamp at the start of each log line const timestampRegExp = /\d{2}:\d{2}:\d{2}\.\d{3}/; ``` ### JSON logging ```js import {execa as execa_} from 'execa'; const execa = execa_({ verbose(verboseLine, verboseObject) { return JSON.stringify(verboseObject); }, }); ``` ### Advanced logging ```js import {execa as execa_} from 'execa'; import {createLogger, transports} from 'winston'; // Log to a file using Winston const transport = new transports.File({filename: 'logs.txt'}); const logger = createLogger({transports: [transport]}); const LOG_LEVELS = { command: 'info', output: 'verbose', ipc: 'verbose', error: 'error', duration: 'info', }; const execa = execa_({ verbose(verboseLine, {message, ...verboseObject}) { const level = LOG_LEVELS[verboseObject.type]; logger[level](message, verboseObject); }, }); ```
[**Next**: 📎 Windows](windows.md)\ [**Previous**: 📞 Inter-process communication](ipc.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/environment.md ================================================ execa logo
# 🌐 Environment ## Current directory The [current directory](https://en.wikipedia.org/wiki/Working_directory) when running the command can be set with the [`cwd`](api.md#optionscwd) option. ```js import {execa} from 'execa'; await execa({cwd: '/path/to/cwd'})`npm run build`; ``` And be retrieved with the [`result.cwd`](api.md#resultcwd) property. ```js const {cwd} = await execa`npm run build`; ``` ## Local binaries Package managers like `npm` install local binaries in `./node_modules/.bin`. ```sh $ npm install -D eslint ``` ```js await execa('./node_modules/.bin/eslint'); ``` The [`preferLocal`](api.md#optionspreferlocal) option can be used to execute those local binaries. ```js await execa({preferLocal: true})`eslint`; ``` Those are searched in the current or any parent directory. The [`localDir`](api.md#optionslocaldir) option can select a different directory. ```js await execa({preferLocal: true, localDir: '/path/to/dir'})`eslint`; ``` ## Current package's binary Execa can be combined with [`get-bin-path`](https://github.com/ehmicky/get-bin-path) to test the current package's binary. As opposed to hard-coding the path to the binary, this validates that the `package.json` [`bin`](https://docs.npmjs.com/cli/v10/configuring-npm/package-json#bin) field is correctly set up. ```js import {execa} from 'execa'; import {getBinPath} from 'get-bin-path'; const binPath = await getBinPath(); await execa(binPath); ``` ## Background subprocess When the [`detached`](api.md#optionsdetached) option is `true`, the subprocess [runs independently](https://en.wikipedia.org/wiki/Background_process) from the current process. Specific behavior depends on the platform. [More info.](https://nodejs.org/api/child_process.html#child_process_options_detached) ```js await execa({detached: true})`npm run start`; ```
[**Next**: ❌ Errors](errors.md)\ [**Previous**: 🐢 Node.js files](node.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/errors.md ================================================ execa logo
# ❌ Errors ## Subprocess failure When the subprocess fails, the promise returned by [`execa()`](api.md#execafile-arguments-options) is rejected with an [`ExecaError`](api.md#execaerror) instance. The `error` has the same shape as successful [results](api.md#result), with a few additional [error-specific fields](api.md#execaerror). [`error.failed`](api.md#resultfailed) is always `true`. ```js import {execa, ExecaError} from 'execa'; try { const result = await execa`npm run build`; console.log(result.failed); // false } catch (error) { if (error instanceof ExecaError) { console.error(error.failed); // true } } ``` ## Preventing exceptions When the [`reject`](api.md#optionsreject) option is `false`, the `error` is returned instead. ```js const resultOrError = await execa({reject: false})`npm run build`; if (resultOrError.failed) { console.error(resultOrError); } ``` ## Exit code The subprocess fails when its [exit code](https://en.wikipedia.org/wiki/Exit_status) is not `0`. The exit code is available as [`error.exitCode`](api.md#errorexitcode). It is `undefined` when the subprocess fails to spawn or when it was [terminated by a signal](termination.md#signal-termination). ```js try { await execa`npm run build`; } catch (error) { // Either non-0 integer or undefined console.error(error.exitCode); } ``` ## Failure reason The subprocess can fail for other reasons. Some of them can be detected using a specific boolean property: - [`error.timedOut`](api.md#errortimedout): [`timeout`](termination.md#timeout) option. - [`error.isCanceled`](api.md#erroriscanceled): [`cancelSignal`](termination.md#canceling) option. - [`error.isGracefullyCanceled`](api.md#errorisgracefullycanceled): `cancelSignal` option, if the [`gracefulCancel`](termination.md#graceful-termination) option is `true`. - [`error.isMaxBuffer`](api.md#errorismaxbuffer): [`maxBuffer`](output.md#big-output) option. - [`error.isTerminated`](api.md#erroristerminated): [signal termination](termination.md#signal-termination). This includes the [`timeout`](termination.md#timeout) and [`forceKillAfterDelay`](termination.md#forceful-termination) options since those terminate the subprocess with a [signal](termination.md#default-signal). This also includes the [`cancelSignal`](termination.md#canceling) option unless the [`gracefulCancel`](termination.md#graceful-termination) option is `true`. This does not include the [`maxBuffer`](output.md#big-output) option. Otherwise, the subprocess failed because either: - An exception was thrown in a [stream](streams.md) or [transform](transform.md). - The command's executable file was not found. - An invalid [option](api.md#options-1) was passed. - There was not enough memory or too many subprocesses. ```js try { await execa`npm run build`; } catch (error) { if (error.timedOut) { handleTimeout(error); } throw error; } ``` ## Error message For better [debugging](debugging.md), [`error.message`](api.md#errormessage) includes both: - The command and the [reason it failed](#failure-reason). - Its [`stdout`, `stderr`](output.md#stdout-and-stderr), [other file descriptors'](output.md#additional-file-descriptors) output and [IPC messages](ipc.md), separated with newlines and not [interleaved](output.md#interleaved-output). [`error.shortMessage`](api.md#errorshortmessage) is the same but without `stdout`, `stderr` nor IPC messages. [`error.originalMessage`](api.md#errororiginalmessage) is the same but also without the command. This exists only in specific instances, such as when calling [`subprocess.kill(error)`](termination.md#error-message-and-stack-trace), using the [`cancelSignal`](termination.md#canceling) option, passing an invalid command or [option](api.md#options-1), or throwing an exception in a [stream](streams.md) or [transform](transform.md). ```js try { await execa`npm run build`; } catch (error) { console.error(error.originalMessage); // The task "build" does not exist. console.error(error.shortMessage); // Command failed with exit code 3: npm run build // The task "build" does not exist. console.error(error.message); // Command failed with exit code 3: npm run build // The task "build" does not exist. // [stderr contents...] // [stdout contents...] } ```
[**Next**: 🏁 Termination](termination.md)\ [**Previous**: 🌐 Environment](environment.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/escaping.md ================================================ execa logo
# 💬 Escaping/quoting ## Array syntax When using the [array syntax](execution.md#array-syntax), arguments are automatically escaped. They can contain any character, including spaces, tabs and newlines. However, they cannot contain [null bytes](https://en.wikipedia.org/wiki/Null_character): [binary inputs](binary.md#binary-input) should be used instead. ```js import {execa} from 'execa'; await execa('npm', ['run', 'task with space']); ``` ## Template string syntax The same applies when using the [template string syntax](execution.md#template-string-syntax). However, spaces, tabs and newlines must use `${}`. ```js await execa`npm run ${'task with space'}`; ``` ## User-defined input The above syntaxes allow the file and its arguments to be user-defined by passing a variable. ```js import {execa} from 'execa'; const file = 'npm'; const commandArguments = ['run', 'task with space']; await execa`${file} ${commandArguments}`; await execa(file, commandArguments); ``` If the file and/or multiple arguments are supplied as a single string, [`parseCommandString()`](api.md#parsecommandstringcommand) can split it into an array. ```js import {execa, parseCommandString} from 'execa'; const commandString = 'npm run task'; const commandArray = parseCommandString(commandString); await execa`${commandArray}`; const [file, ...commandArguments] = commandArray; await execa(file, commandArguments); ``` Spaces are used as delimiters. They can be escaped with a backslash. ```js await execa`${parseCommandString('npm run task\\ with\\ space')}`; ``` ## Shells [Shells](shell.md) ([Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)), [cmd.exe](https://en.wikipedia.org/wiki/Cmd.exe), etc.) are not used unless the [`shell`](api.md#optionsshell) option is set. This means shell-specific syntax has no special meaning and does not need to be escaped: - Quotes: `"value"`, `'value'`, `$'value'` - Characters: `$variable`, `&&`, `||`, `;`, `|` - Globbing: `*`, `**` - Expressions: `$?`, `~` ```js // This prints `$TASK_NAME`, not `build` await execa({env: {TASK_NAME: 'build'}})`echo $TASK_NAME`; ``` If you do set the `shell` option, arguments will not be automatically escaped anymore. Instead, they will be concatenated as a single string using spaces as delimiters. ```js await execa({shell: true})`npm ${'run'} ${'task with space'}`; // Is the same as: await execa({shell: true})`npm run task with space`; ``` Therefore, you need to manually quote the arguments, using the shell-specific syntax. ```js await execa({shell: true})`npm ${'run'} ${'"task with space"'}`; // Is the same as: await execa({shell: true})`npm run "task with space"`; ``` Sometimes a shell command is passed as argument to an executable that runs it indirectly. In that case, that shell command must quote its own arguments. ```js const command = 'npm run "task with space"'; await execa`ssh host ${command}`; ```
[**Next**: 💻 Shell](shell.md)\ [**Previous**: ️▶️ Basic execution](execution.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/execution.md ================================================ execa logo
# ▶️ Basic execution ## Array syntax ```js import {execa} from 'execa'; await execa('npm', ['run', 'build']); ``` ## Template string syntax All [available methods](api.md#methods) can use either the [array syntax](#array-syntax) or the template string syntax, which are equivalent. ```js await execa`npm run build`; ``` ### String argument ```js await execa`npm run ${'task with space'}`; ``` ### Number argument ```js await execa`npm run build --concurrency ${2}`; ``` ### Subcommands ```js const result = await execa`get-concurrency`; // Uses `result.stdout` await execa`npm run build --concurrency ${result}`; ``` ### Concatenation ```js const tmpDirectory = '/tmp'; await execa`mkdir ${tmpDirectory}/filename`; ``` ### Multiple arguments ```js const result = await execa`get-concurrency`; await execa`npm ${['run', 'build', '--concurrency', result]}`; ``` ### No arguments ```js await execa`npm run build ${[]}`; // Same as: await execa('npm', ['run', 'build']); ``` ### Empty string argument ```js await execa`npm run build ${''}`; // Same as: await execa('npm', ['run', 'build', '']); ``` ### Conditional argument ```js const commandArguments = failFast ? ['--fail-fast'] : []; await execa`npm run build ${commandArguments}`; ``` ### Multiple lines ```js await execa`npm run build --concurrency 2 --fail-fast`; ``` ### Shells By default, any shell-specific syntax has no special meaning and does not need to be escaped. This prevents [shell injections](https://en.wikipedia.org/wiki/Code_injection#Shell_injection). [More info.](escaping.md#shells) ```js // This prints `$TASK_NAME`, not `build` await execa({env: {TASK_NAME: 'build'}})`echo $TASK_NAME`; ``` ## Options [Options](api.md#options-1) can be passed to influence the execution's behavior. ### Array syntax ```js await execa('npm', ['run', 'build'], {timeout: 5000}); ``` ### Template string syntax ```js await execa({timeout: 5000})`npm run build`; ``` ### Global/shared options ```js const timedExeca = execa({timeout: 5000}); await timedExeca('npm', ['run', 'build']); await timedExeca`npm run test`; ``` ## Return value ### Subprocess The subprocess is returned as soon as it is spawned. It is a [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with [additional methods and properties](api.md#subprocess). ```js const subprocess = execa`npm run build`; console.log(subprocess.pid); ``` ### Result The subprocess is also a `Promise` that resolves with the [`result`](api.md#result). ```js const {stdout} = await execa`npm run build`; ``` ### Synchronous execution [`execaSync()`](api.md#execasyncfile-arguments-options) and [`$.sync()`](api.md#syncfile-arguments-options) return the [`result`](api.md#result) without needing to `await`. The [`subprocess`](#subprocess) is not returned: its methods and properties are not available. ```js import {execaSync} from 'execa'; const {stdout} = execaSync`npm run build`; ``` Synchronous execution is generally discouraged as it holds the CPU and prevents parallelization. Also, the following features cannot be used: - Streams: [`subprocess.stdin`](api.md#subprocessstdin), [`subprocess.stdout`](api.md#subprocessstdout), [`subprocess.stderr`](api.md#subprocessstderr), [`subprocess.readable()`](api.md#subprocessreadablereadableoptions), [`subprocess.writable()`](api.md#subprocesswritablewritableoptions), [`subprocess.duplex()`](api.md#subprocessduplexduplexoptions). - The [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout), [`stderr`](api.md#optionsstderr) and [`stdio`](api.md#optionsstdio) options cannot be [`'overlapped'`](api.md#optionsstdout), an [async iterable](lines.md#progressive-splitting), an async [transform](transform.md), a [`Duplex`](transform.md#duplextransform-streams), nor a [web stream](streams.md#web-streams). Node.js streams can be passed but only if either they [have a file descriptor](streams.md#file-descriptors), or the [`input`](api.md#optionsinput) option is used. - Signal termination: [`subprocess.kill()`](api.md#subprocesskillerror), [`subprocess.pid`](api.md#subprocesspid), [`cleanup`](api.md#optionscleanup) option, [`cancelSignal`](api.md#optionscancelsignal) option, [`forceKillAfterDelay`](api.md#optionsforcekillafterdelay) option. - Piping multiple subprocesses: [`subprocess.pipe()`](api.md#subprocesspipefile-arguments-options). - [`subprocess.iterable()`](lines.md#progressive-splitting). - [IPC](ipc.md): [`sendMessage()`](api.md#sendmessagemessage-sendmessageoptions), [`getOneMessage()`](api.md#getonemessagegetonemessageoptions), [`getEachMessage()`](api.md#geteachmessagegeteachmessageoptions), [`result.ipcOutput`](output.md#any-output-type), [`ipc`](api.md#optionsipc) option, [`serialization`](api.md#optionsserialization) option, [`ipcInput`](input.md#any-input-type) option. - [`result.all`](api.md#resultall) is not interleaved. - [`detached`](api.md#optionsdetached) option. - The [`maxBuffer`](api.md#optionsmaxbuffer) option is always measured in bytes, not in characters, [lines](api.md#optionslines) nor [objects](transform.md#object-mode). Also, it ignores transforms and the [`encoding`](api.md#optionsencoding) option.
[**Next**: 💬 Escaping/quoting](escaping.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/input.md ================================================ execa logo
# 🎹 Input ## Command arguments The simplest way to pass input to a subprocess is to use command arguments. ```js import {execa} from 'execa'; const commandArgument = 'build'; await execa`node child.js ${commandArgument}`; ``` If the subprocess is a Node.js file, those are available using [`process.argv`](https://nodejs.org/api/process.html#processargv). ```js // child.js import process from 'node:process'; const commandArgument = process.argv[2]; ``` ## Environment variables Unlike [command arguments](#command-arguments), [environment variables](https://en.wikipedia.org/wiki/Environment_variable) have names. They are commonly used to configure applications. If the subprocess spawns its own subprocesses, they inherit environment variables. To isolate subprocesses from each other, either command arguments or [`stdin`](#string-input) should be preferred instead. ```js // Keep the current process' environment variables, and set `NO_COLOR` await execa({env: {NO_COLOR: 'true'}})`node child.js`; // Discard the current process' environment variables, only pass `NO_COLOR` await execa({env: {NO_COLOR: 'true'}, extendEnv: false})`node child.js`; ``` If the subprocess is a Node.js file, environment variables are available using [`process.env`](https://nodejs.org/api/process.html#processenv). ```js // child.js import process from 'node:process'; console.log(process.env.NO_COLOR); ``` ## String input Alternatively, input can be provided to [`stdin`](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). Unlike [command arguments](#command-arguments) and [environment variables](#environment-variables) which have [size](https://unix.stackexchange.com/questions/120642/what-defines-the-maximum-size-for-a-command-single-argument) [limits](https://stackoverflow.com/questions/1078031/what-is-the-maximum-size-of-a-linux-environment-variable-value), `stdin` works when the input is big. Also, the input can be redirected from the [terminal](#terminal-input), a [file](#file-input), another [subprocess](pipe.md) or a [stream](streams.md#manual-streaming). Finally, this is required when the input might contain [null bytes](https://en.wikipedia.org/wiki/Null_character), for example when it might be [binary](binary.md#binary-input). If the input is already available as a string, it can be passed directly to the [`input`](api.md#optionsinput) option. ```js await execa({input: 'stdinInput'})`npm run scaffold`; ``` The [`stdin`](api.md#optionsstdin) option can also be used, although the string must be wrapped in two arrays for [syntax reasons](output.md#multiple-targets). ```js await execa({stdin: [['stdinInput']]})`npm run scaffold`; ``` ## Ignore input ```js const subprocess = execa({stdin: 'ignore'})`npm run scaffold`; console.log(subprocess.stdin); // undefined await subprocess; ``` ## File input ```js await execa({inputFile: 'input.txt'})`npm run scaffold`; // Or: await execa({stdin: {file: 'input.txt'}})`npm run scaffold`; // Or: await execa({stdin: new URL('file:///path/to/input.txt')})`npm run scaffold`; ``` ## Terminal input The parent process' input can be re-used in the subprocess by passing `'inherit'`. This is especially useful to receive interactive input in command line applications. ```js await execa({stdin: 'inherit'})`npm run scaffold`; ``` ## Any input type If the subprocess [uses Node.js](node.md), [almost any type](ipc.md#message-type) can be passed to the subprocess using the [`ipcInput`](ipc.md#send-an-initial-message) option. The subprocess retrieves that input using [`getOneMessage()`](api.md#getonemessagegetonemessageoptions). ```js // main.js import {execaNode} from 'execa'; const ipcInput = [ {task: 'lint', ignore: /test\.js/}, {task: 'copy', files: new Set(['main.js', 'index.js']), }]; await execaNode({ipcInput})`build.js`; ``` ```js // build.js import {getOneMessage} from 'execa'; const ipcInput = await getOneMessage(); ``` ## Multiple inputs The input can come from multiple sources by setting the [`stdin`](api.md#optionsstdin) option to an array of values. Alternatively the [`input`](api.md#optionsinput), [`inputFile`](api.md#optionsinputfile) and [`stdin`](api.md#optionsstdin) options can be combined. The following example redirects `stdin` from both the [terminal](#terminal-input) and an `input.txt` [file](#file-input). ```js await execa({stdin: ['inherit', {file: 'input.txt'}]})`npm run scaffold`; ``` __Loss of TTY control:__ Please note that when a file descriptor is configured with a combination of `'inherit'` and other values, this file descriptor will never refer to a TTY in the subprocess, even if in the current process it does. ## Additional file descriptors The [`stdio`](api.md#optionsstdio) option can be used to pass some input to any [file descriptor](https://en.wikipedia.org/wiki/File_descriptor), as opposed to only [`stdin`](api.md#optionsstdin). ```js // Pass input to the file descriptor number 3 await execa({ stdio: ['pipe', 'pipe', 'pipe', new Uint8Array([/* ... */])], })`npm run build`; ```
[**Next**: 📢 Output](output.md)\ [**Previous**: 🏁 Termination](termination.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/ipc.md ================================================ execa logo
# 📞 Inter-process communication ## Exchanging messages When the [`ipc`](api.md#optionsipc) option is `true`, the current process and subprocess can exchange messages. This only works if the subprocess is a [Node.js file](node.md). The `ipc` option defaults to `true` when using [`execaNode()`](node.md#run-nodejs-files) or the [`node`](node.md#run-nodejs-files) option. Please note that unlike `node:child_process` [`spawn()`](https://nodejs.org/api/child_process.html#optionsstdio), the [`stdio` option](api.md#optionsstdio) does not support the `'ipc'` value. Instead, the [`ipc`](api.md#optionsipc) option must be set to `true`. The current process sends messages with [`subprocess.sendMessage(message)`](api.md#subprocesssendmessagemessage-sendmessageoptions) and receives them with [`subprocess.getOneMessage()`](api.md#subprocessgetonemessagegetonemessageoptions). The subprocess uses [`sendMessage(message)`](api.md#sendmessagemessage-sendmessageoptions) and [`getOneMessage()`](api.md#getonemessagegetonemessageoptions). Those are the same methods, but imported directly from the `'execa'` module. ```js // parent.js import {execaNode} from 'execa'; const subprocess = execaNode`child.js`; await subprocess.sendMessage('Hello from parent'); const message = await subprocess.getOneMessage(); console.log(message); // 'Hello from child' await subprocess; ``` ```js // child.js import {getOneMessage, sendMessage} from 'execa'; const message = await getOneMessage(); // 'Hello from parent' const newMessage = message.replace('parent', 'child'); // 'Hello from child' await sendMessage(newMessage); ``` ## Listening to messages The methods described above read a single message. On the other hand, [`subprocess.getEachMessage()`](api.md#subprocessgeteachmessagegeteachmessageoptions) and [`getEachMessage()`](api.md#geteachmessagegeteachmessageoptions) return an [async iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols). This should be preferred when listening to multiple messages. [`subprocess.getEachMessage()`](api.md#subprocessgeteachmessagegeteachmessageoptions) waits for the subprocess to end (even when using [`break`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break) or [`return`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return)). It throws if the subprocess [fails](api.md#result). This means you do not need to `await` the subprocess' [promise](execution.md#result). ```js // parent.js import {execaNode} from 'execa'; const subprocess = execaNode`child.js`; await subprocess.sendMessage(0); // This loop ends when the subprocess exits. // It throws if the subprocess fails. for await (const message of subprocess.getEachMessage()) { console.log(message); // 1, 3, 5, 7, 9 await subprocess.sendMessage(message + 1); } ``` ```js // child.js import {sendMessage, getEachMessage} from 'execa'; // The subprocess exits when hitting `break` for await (const message of getEachMessage()) { if (message === 10) { break; } console.log(message); // 0, 2, 4, 6, 8 await sendMessage(message + 1); } ``` ## Filter messages ```js import {getOneMessage} from 'execa'; const startMessage = await getOneMessage({ filter: message => message.type === 'start', }); ``` ```js import {getEachMessage} from 'execa'; for await (const message of getEachMessage()) { if (message.type === 'start') { // ... } } ``` ## Ensure messages are received When a message is sent by one process, the other process must receive it using [`getOneMessage()`](#exchanging-messages), [`getEachMessage()`](#listening-to-messages), or automatically with [`result.ipcOutput`](api.md#resultipcoutput). If not, that message is silently discarded. If the [`strict: true`](api.md#sendmessageoptionsstrict) option is passed to [`subprocess.sendMessage(message)`](api.md#subprocesssendmessagemessage-sendmessageoptions) or [`sendMessage(message)`](api.md#sendmessagemessage-sendmessageoptions), an error is thrown instead. This helps identifying subtle race conditions like the following example. ```js // main.js import {execaNode} from 'execa'; const subprocess = execaNode`build.js`; // This `build` message is received await subprocess.sendMessage('build', {strict: true}); // This `lint` message is not received, so it throws await subprocess.sendMessage('lint', {strict: true}); await subprocess; ``` ```js // build.js import {getOneMessage} from 'execa'; // Receives the 'build' message const task = await getOneMessage(); // The `lint` message is sent while `runTask()` is ongoing // Therefore the `lint` message is discarded await runTask(task); // Does not receive the `lint` message // Without `strict`, this would wait forever const secondTask = await getOneMessage(); await runTask(secondTask); ``` ## Retrieve all messages The [`result.ipcOutput`](api.md#resultipcoutput) array contains all the messages sent by the subprocess. In many situations, this is simpler than using [`subprocess.getOneMessage()`](api.md#subprocessgetonemessagegetonemessageoptions) and [`subprocess.getEachMessage()`](api.md#subprocessgeteachmessagegeteachmessageoptions). ```js // main.js import {execaNode} from 'execa'; const {ipcOutput} = await execaNode`build.js`; console.log(ipcOutput[0]); // {kind: 'start', timestamp: date} console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date} ``` ```js // build.js import {sendMessage} from 'execa'; await sendMessage({kind: 'start', timestamp: new Date()}); await runBuild(); await sendMessage({kind: 'stop', timestamp: new Date()}); ``` ## Send an initial message The [`ipcInput`](api.md#optionsipcinput) option sends a message to the [Node.js subprocess](node.md) when it starts. ```js // main.js import {execaNode} from 'execa'; const ipcInput = [ {task: 'lint', ignore: /test\.js/}, {task: 'copy', files: new Set(['main.js', 'index.js']), }]; await execaNode({ipcInput})`build.js`; ``` ```js // build.js import {getOneMessage} from 'execa'; const ipcInput = await getOneMessage(); ``` ## Message type By default, messages are serialized using [`structuredClone()`](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This supports most types including objects, arrays, `Error`, `Date`, `RegExp`, `Map`, `Set`, `bigint`, `Uint8Array`, and circular references. This throws when passing functions, symbols or promises (including inside an object or array). To limit messages to JSON instead, the [`serialization`](api.md#optionsserialization) option can be set to `'json'`. ```js import {execaNode} from 'execa'; await execaNode({serialization: 'json'})`child.js`; ``` ## Messages order The messages are always received in the same order they were sent. Even when sent all at once. ```js import {sendMessage} from 'execa'; await Promise.all([ sendMessage('first'), sendMessage('second'), sendMessage('third'), ]); ``` ## Keeping the subprocess alive By default, the subprocess is kept alive as long as [`getOneMessage()`](api.md#getonemessagegetonemessageoptions) or [`getEachMessage()`](api.md#geteachmessagegeteachmessageoptions) is waiting. This is recommended if you're sure the current process will send a message, as this prevents the subprocess from exiting too early. However, if you don't know whether a message will be sent, this can leave the subprocess hanging forever. In that case, the [`reference: false`](api.md#geteachmessageoptionsreference) option can be set. ```js import {getEachMessage} from 'execa'; // {type: 'gracefulExit'} is sometimes received, but not always for await (const message of getEachMessage({reference: false})) { if (message.type === 'gracefulExit') { gracefulExit(); } } ``` ## Debugging When the [`verbose`](api.md#optionsverbose) option is `'full'`, the IPC messages sent by the subprocess to the current process are [printed on the console](debugging.md#full-mode). Also, when the subprocess [failed](errors.md#subprocess-failure), [`error.ipcOutput`](api.md) contains all the messages sent by the subprocess. Those are also shown at the end of the [error message](errors.md#error-message).
[**Next**: 🐛 Debugging](debugging.md)\ [**Previous**: ⏳️ Streams](streams.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/lines.md ================================================ execa logo
# 📃 Text lines ## Simple splitting If the [`lines`](api.md#optionslines) option is `true`, the output is split into lines, as an array of strings. ```js import {execa} from 'execa'; const lines = await execa({lines: true})`npm run build`; console.log(lines.join('\n')); ``` ## Iteration ### Progressive splitting The subprocess' return value is an [async iterable](api.md#subprocesssymbolasynciterator). It iterates over the output's lines while the subprocess is still running. ```js for await (const line of execa`npm run build`) { if (line.includes('ERROR')) { console.log(line); } } ``` Alternatively, [`subprocess.iterable()`](api.md#subprocessiterablereadableoptions) can be called to pass [iterable options](api.md#readableoptions). The iteration waits for the subprocess to end (even when using [`break`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break) or [`return`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return)). It throws if the subprocess [fails](api.md#result). This means you do not need to `await` the subprocess' [promise](execution.md#result). ```js for await (const line of execa`npm run build`.iterable())) { /* ... */ } ``` ### Stdout/stderr By default, the subprocess' [`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)) is used. The [`from`](api.md#readableoptionsfrom) iterable option can select a different file descriptor, such as [`'stderr'`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)), [`'all'`](output.md#interleaved-output) or [`'fd3'`](output.md#additional-file-descriptors). ```js for await (const stderrLine of execa`npm run build`.iterable({from: 'stderr'})) { /* ... */ } ``` ## Newlines ### Final newline The final newline is stripped from the output's last line, unless the [`stripFinalNewline`](api.md#optionsstripfinalnewline) option is `false`. ```js const {stdout} = await execa({stripFinalNewline: false})`npm run build`; console.log(stdout.endsWith('\n')); // true ``` ### Array of lines When using the [`lines`](#simple-splitting) option, newlines are stripped from each line, unless the [`stripFinalNewline`](api.md#optionsstripfinalnewline) option is `false`. ```js // Each line now ends with '\n'. // The last `line` might or might not end with '\n', depending on the output. const lines = await execa({lines: true, stripFinalNewline: false})`npm run build`; console.log(lines.join('')); ``` ### Iterable When [iterating](#progressive-splitting) over lines, newlines are stripped from each line, unless the [`preserveNewlines`](api.md#readableoptionspreservenewlines) iterable option is `true`. This option can also be used with [streams produced](streams.md#converting-a-subprocess-to-a-stream) by [`subprocess.readable()`](api.md#subprocessreadablereadableoptions) or [`subprocess.duplex()`](api.md#subprocessduplexduplexoptions), providing the [`binary`](binary.md#streams) option is `false`. ```js // `line` now ends with '\n'. // The last `line` might or might not end with '\n', depending on the output. for await (const line of execa`npm run build`.iterable({preserveNewlines: true})) { /* ... */ } ``` ### Transforms When using [transforms](transform.md), newlines are stripped from each `line` argument, unless the [`preserveNewlines`](api.md#transformoptionspreservenewlines) transform option is `true`. ```js // `line` now ends with '\n'. // The last `line` might or might not end with '\n', depending on the output. const transform = function * (line) { /* ... */ }; await execa({stdout: {transform, preserveNewlines: true}})`npm run build`; ``` Each `yield` produces at least one line. Calling `yield` multiple times or calling `yield *` produces multiples lines. ```js const transform = function * (line) { yield 'Important note:'; yield 'Read the comments below.'; // Or: yield * [ 'Important note:', 'Read the comments below.', ]; // Is the same as: yield 'Important note:\nRead the comments below.\n'; yield line; }; await execa({stdout: transform})`npm run build`; ``` However, if the [`preserveNewlines`](api.md#transformoptionspreservenewlines) transform option is `true`, multiple `yield`s produce a single line instead. ```js const transform = function * (line) { yield 'Important note: '; yield 'Read the comments below.\n'; // Is the same as: yield 'Important note: Read the comments below.\n'; yield line; }; await execa({stdout: {transform, preserveNewlines: true}})`npm run build`; ```
[**Next**: 🤖 Binary data](binary.md)\ [**Previous**: 📢 Output](output.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/node.md ================================================ execa logo
# 🐢 Node.js files ## Run Node.js files ```js import {execaNode, execa} from 'execa'; await execaNode`file.js argument`; // Is the same as: await execa({node: true})`file.js argument`; // Or: await execa`node file.js argument`; ``` ## Node.js CLI flags When using the [`node`](api.md#optionsnode) option or [`execaNode()`](api.md#execanodescriptpath-arguments-options), the current Node.js [CLI flags](https://nodejs.org/api/cli.html#options) are inherited. For example, the subprocess will use [`--allow-fs-read`](https://nodejs.org/api/cli.html#--allow-fs-read) if the current process does. The [`nodeOptions`](api.md#optionsnodeoptions) option can be used to set different CLI flags. ```js await execaNode({nodeOptions: ['--allow-fs-write']})`file.js argument`; ``` ## Node.js version The same applies to the Node.js version, which is inherited too. [`get-node`](https://github.com/ehmicky/get-node) and the [`nodePath`](api.md#optionsnodepath) option can be used to run a specific Node.js version. Alternatively, [`nvexeca`](https://github.com/ehmicky/nvexeca) or [`nve`](https://github.com/ehmicky/nve) can be used. ```js import {execaNode} from 'execa'; import getNode from 'get-node'; const {path: nodePath} = await getNode('16.2.0'); await execaNode({nodePath})`file.js argument`; ```
[**Next**: 🌐 Environment](environment.md)\ [**Previous**: 📜 Scripts](scripts.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/output.md ================================================ execa logo
# 📢 Output ## Stdout and stderr The [`stdout`](api.md#optionsstdout) and [`stderr`](api.md#optionsstderr) options redirect the subprocess output. They default to `'pipe'`, which returns the output using [`result.stdout`](api.md#resultstdout) and [`result.stderr`](api.md#resultstderr). ```js import {execa} from 'execa'; const {stdout, stderr} = await execa`npm run build`; console.log(stdout); console.log(stderr); ``` ## Ignore output ```js const {stdout, stderr} = await execa({stdout: 'ignore'})`npm run build`; console.log(stdout); // undefined console.log(stderr); // string with errors ``` ## File output ```js await execa({stdout: {file: 'output.txt'}})`npm run build`; // Or: await execa({stdout: new URL('file:///path/to/output.txt')})`npm run build`; ``` ```js // Redirect interleaved stdout and stderr to same file const output = {file: 'output.txt'}; await execa({stdout: output, stderr: output})`npm run build`; ``` ```js // Append instead of overwriting await execa({stdout: {file: 'output.txt', append: true}})`npm run build`; ``` ## Terminal output The parent process' output can be re-used in the subprocess by passing `'inherit'`. This is especially useful to print to the terminal in command line applications. ```js await execa({stdout: 'inherit', stderr: 'inherit'})`npm run build`; ``` To redirect from/to a different [file descriptor](https://en.wikipedia.org/wiki/File_descriptor), pass its [number](https://en.wikipedia.org/wiki/Standard_streams) or [`process.stdout`](https://nodejs.org/api/process.html#processstdout)/[`process.stderr`](https://nodejs.org/api/process.html#processstderr). ```js // Print both stdout/stderr to the parent stdout await execa({stdout: process.stdout, stderr: process.stdout})`npm run build`; // Or: await execa({stdout: 1, stderr: 1})`npm run build`; ``` ## Any output type If the subprocess uses Node.js, [IPC](ipc.md) can be used to return [almost any type](ipc.md#message-type) from the subprocess. The [`result.ipcOutput`](api.md#resultipcoutput) array contains all the messages sent by the subprocess. ```js // main.js import {execaNode} from 'execa'; const {ipcOutput} = await execaNode`build.js`; console.log(ipcOutput[0]); // {kind: 'start', timestamp: date} console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date} ``` ```js // build.js import {sendMessage} from 'execa'; await sendMessage({kind: 'start', timestamp: new Date()}); await runBuild(); await sendMessage({kind: 'stop', timestamp: new Date()}); ``` ## Multiple targets The output can be redirected to multiple targets by setting the [`stdout`](api.md#optionsstdout) or [`stderr`](api.md#optionsstderr) option to an array of values. The following example redirects `stdout` to both the [terminal](#terminal-output) and an `output.txt` [file](#file-output), while also retrieving its value [programmatically](#stdout-and-stderr). ```js const {stdout} = await execa({stdout: ['inherit', {file: 'output.txt'}, 'pipe']})`npm run build`; console.log(stdout); ``` __Loss of TTY control:__ Please note that when a file descriptor is configured with a combination of 'inherit' and other values, this file descriptor will never refer to a TTY in the subprocess, even if in the current process it does. ## Interleaved output If the [`all`](api.md#optionsall) option is `true`, [`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)) and [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)) are combined: - [`result.all`](api.md#resultall): [`result.stdout`](api.md#resultstdout) + [`result.stderr`](api.md#resultstderr) - [`subprocess.all`](api.md#subprocessall): [`subprocess.stdout`](api.md#subprocessstdout) + [`subprocess.stderr`](api.md#subprocessstderr) `stdout` and `stderr` are guaranteed to interleave. However, for performance reasons, the subprocess might buffer and merge multiple simultaneous writes to `stdout` or `stderr`. This can prevent proper interleaving. For example, this prints `1 3 2` instead of `1 2 3` because both `console.log()` are merged into a single write. ```js const {all} = await execa({all: true})`node example.js`; ``` ```js // example.js console.log('1'); // writes to stdout console.error('2'); // writes to stderr console.log('3'); // writes to stdout ``` This can be worked around by using `setTimeout()`. ```js import {setTimeout} from 'timers/promises'; console.log('1'); console.error('2'); await setTimeout(0); console.log('3'); ``` ## Stdout/stderr-specific options Some options are related to the subprocess output: [`verbose`](api.md#optionsverbose), [`lines`](api.md#optionslines), [`stripFinalNewline`](api.md#optionsstripfinalnewline), [`buffer`](api.md#optionsbuffer), [`maxBuffer`](api.md#optionsmaxbuffer). By default, those options apply to all [file descriptors](https://en.wikipedia.org/wiki/File_descriptor) ([`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)), [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)), and [others](#additional-file-descriptors)) and [IPC messages](ipc.md). A plain object can be passed instead to apply them to only `stdout`, `stderr`, `all` (both stdout and stderr), [`ipc`](ipc.md), [`fd3`](#additional-file-descriptors), etc. ```js // Same value for stdout and stderr await execa({verbose: 'full'})`npm run build`; // Different values for stdout and stderr await execa({verbose: {stdout: 'none', stderr: 'full'}})`npm run build`; ``` ## Additional file descriptors The [`stdio`](api.md#optionsstdio) option is an array combining [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout), [`stderr`](api.md#optionsstderr) and any other file descriptor. It is useful when using additional [file descriptors](https://en.wikipedia.org/wiki/File_descriptor) beyond the [standard ones](https://en.wikipedia.org/wiki/Standard_streams), either for [input](input.md#additional-file-descriptors) or output. [`result.stdio`](api.md#resultstdio) can be used to retrieve some output from any file descriptor, as opposed to only [`stdout`](api.md#optionsstdout) and [`stderr`](api.md#optionsstderr). ```js // Retrieve output from file descriptor number 3 const {stdio} = await execa({ stdio: ['pipe', 'pipe', 'pipe', 'pipe'], })`npm run build`; console.log(stdio[3]); ``` ## Shortcut The [`stdio`](api.md#optionsstdio) option can also be a single value [`'pipe'`](#stdout-and-stderr), [`'overlapped'`](windows.md#asynchronous-io), [`'ignore'`](#ignore-output) or [`'inherit'`](#terminal-output). This is a shortcut for setting that same value with the [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout) and [`stderr`](api.md#optionsstderr) options. ```js await execa({stdio: 'ignore'})`npm run build`; // Same as: await execa({stdin: 'ignore', stdout: 'ignore', stderr: 'ignore'})`npm run build`; ``` ## Big output To prevent high memory consumption, a maximum output size can be set using the [`maxBuffer`](api.md#optionsmaxbuffer) option. It defaults to 100MB. When this threshold is hit, the subprocess fails and [`error.isMaxBuffer`](api.md#errorismaxbuffer) becomes `true`. The truncated output is still available using [`error.stdout`](api.md#resultstdout), [`error.stderr`](api.md#resultstderr) and [`error.ipcOutput`](api.md#resultipcoutput). This is measured: - By default: in [characters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length). - If the [`encoding`](binary.md#encoding) option is `'buffer'`: in bytes. - If the [`lines`](lines.md#simple-splitting) option is `true`: in lines. - If a [transform in object mode](transform.md#object-mode) is used: in objects. - With [`error.ipcOutput`](ipc.md#retrieve-all-messages): in messages. ```js try { await execa({maxBuffer: 1_000_000})`npm run build`; } catch (error) { if (error.isMaxBuffer) { console.error('Error: output larger than 1MB.'); console.error(error.stdout); console.error(error.stderr); } throw error; } ``` ## Low memory When the [`buffer`](api.md#optionsbuffer) option is `false`, [`result.stdout`](api.md#resultstdout), [`result.stderr`](api.md#resultstderr), [`result.all`](api.md#resultall), [`result.stdio[*]`](api.md#resultstdio) and [`result.ipcOutput`](api.md#resultipcoutput) properties are empty. This prevents high memory consumption when the output is big. However, the output must be either ignored, [redirected](#file-output), [streamed](streams.md) or [listened to](ipc.md#listening-to-messages). If streamed, this should be done right away to avoid missing any data.
[**Next**: 📃 Text lines](lines.md)\ [**Previous**: 🎹 Input](input.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/pipe.md ================================================ execa logo
# 🔀 Piping multiple subprocesses ## Array syntax A subprocess' [output](output.md) can be [piped](https://en.wikipedia.org/wiki/Pipeline_(Unix)) to another subprocess' [input](input.md). The syntax is the same as [`execa(file, arguments?, options?)`](execution.md#array-syntax). ```js import {execa} from 'execa'; // Similar to `npm run build | head -n 2` in shells const {stdout} = await execa('npm', ['run', 'build']) .pipe('head', ['-n', '2']); ``` ## Template string syntax ```js const {stdout} = await execa`npm run build` .pipe`head -n 2`; ``` ## Advanced syntax ```js const subprocess = execa`head -n 2`; const {stdout} = await execa`npm run build` .pipe(subprocess); ``` ## Options [Options](api.md#options-1) can be passed to either the source or the destination subprocess. Some [pipe-specific options](api.md#pipeoptions) can also be set by the destination subprocess. ```js const {stdout} = await execa('npm', ['run', 'build'], subprocessOptions) .pipe('head', ['-n', '2'], subprocessOrPipeOptions); ``` ```js const {stdout} = await execa(subprocessOptions)`npm run build` .pipe(subprocessOrPipeOptions)`head -n 2`; ``` ```js const subprocess = execa(subprocessOptions)`head -n 2`; const {stdout} = await execa(subprocessOptions)`npm run build` .pipe(subprocess, pipeOptions); ``` ## Result When both subprocesses succeed, the [`result`](api.md#result) of the destination subprocess is returned. The [`result`](api.md#result) of the source subprocess is available in a [`result.pipedFrom`](api.md#resultpipedfrom) array. ```js const destinationResult = await execa`npm run build` .pipe`head -n 2`; console.log(destinationResult.stdout); // First 2 lines of `npm run build` const sourceResult = destinationResult.pipedFrom[0]; console.log(sourceResult.stdout); // Full output of `npm run build` ``` ## Errors When either subprocess fails, `subprocess.pipe()` is rejected with that subprocess' error. If the destination subprocess fails, [`error.pipedFrom`](api.md#resultpipedfrom) includes the source subprocess' result, which is useful for debugging. ```js try { await execa`npm run build` .pipe`head -n 2`; } catch (error) { if (error.pipedFrom.length === 0) { // `npm run build` failure console.error(error); } else { // `head -n 2` failure console.error(error); // `npm run build` output console.error(error.pipedFrom[0].stdout); } throw error; } ``` ## Series of subprocesses ```js await execa`npm run build` .pipe`sort` .pipe`head -n 2`; ``` ## 1 source, multiple destinations ```js const subprocess = execa`npm run build`; const [sortedResult, truncatedResult] = await Promise.all([ subprocess.pipe`sort`, subprocess.pipe`head -n 2`, ]); ``` ## Multiple sources, 1 destination ```js const destination = execa`./log-remotely.js`; await Promise.all([ execa`npm run build`.pipe(destination), execa`npm run test`.pipe(destination), ]); ``` ## Source file descriptor By default, the source's [`stdout`](api.md#subprocessstdout) is used, but this can be changed using the [`from`](api.md#pipeoptionsfrom) piping option. ```js await execa`npm run build` .pipe({from: 'stderr'})`head -n 2`; ``` ## Destination file descriptor By default, the destination's [`stdin`](api.md#subprocessstdin) is used, but this can be changed using the [`to`](api.md#pipeoptionsto) piping option. ```js await execa`npm run build` .pipe({to: 'fd3'})`./log-remotely.js`; ``` ## Unpipe Piping can be stopped using the [`unpipeSignal`](api.md#pipeoptionsunpipesignal) piping option. The [`subprocess.pipe()`](api.md#subprocesspipefile-arguments-options) method will be rejected with a cancelation error. However, each subprocess will keep running. ```js const abortController = new AbortController(); process.on('SIGUSR1', () => { abortController.abort(); }); // If the process receives SIGUSR1, `npm run build` stopped being logged remotely. // However, it keeps running successfully. try { await execa`npm run build` .pipe({unpipeSignal: abortController.signal})`./log-remotely.js`; } catch (error) { if (!abortController.signal.aborted) { throw error; } } ```
[**Next**: ⏳️ Streams](streams.md)\ [**Previous**: 🧙 Transforms](transform.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/scripts.md ================================================ execa logo
# 📜 Scripts ## Script files [Scripts](https://en.wikipedia.org/wiki/Shell_script) are Node.js files executing a series of commands. While those used to be written with a shell language like [Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)), libraries like Execa provide with a better, modern experience. Scripts use [`$`](api.md#file-arguments-options) instead of [`execa`](api.md#execafile-arguments-options). The only difference is that `$` includes script-friendly default options: [`stdin: 'inherit'`](input.md#terminal-input) and [`preferLocal: true`](environment.md#local-binaries). [More info about the difference between Execa, Bash and zx.](bash.md) ```js import {$} from 'execa'; const {stdout: name} = await $`cat package.json`.pipe`grep name`; console.log(name); const branch = await $`git branch --show-current`; await $`dep deploy --branch=${branch}`; await Promise.all([ $`sleep 1`, $`sleep 2`, $`sleep 3`, ]); const directoryName = 'foo bar'; await $`mkdir /tmp/${directoryName}`; ``` ## Template string syntax Just like [`execa`](api.md#execacommand), [`$`](api.md#command) can use either the [template string syntax](execution.md#template-string-syntax) or the [array syntax](execution.md#array-syntax). Conversely, the template string syntax can be used outside of script files: `$` is not required to use that syntax. For example, `execa` [can use it too](execution.md#template-string-syntax). ```js import {execa, $} from 'execa'; const branch = await execa`git branch --show-current`; await $('dep', ['deploy', `--branch=${branch}`]); ```
[**Next**: 🐢 Node.js files](node.md)\ [**Previous**: 💻 Shell](shell.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/shell.md ================================================ execa logo
# 💻 Shell ## Avoiding shells In general, [shells](https://en.wikipedia.org/wiki/Shell_(computing)) should be avoided because they are: - Not cross-platform, encouraging shell-specific syntax. - Slower, because of the additional shell interpretation. - Unsafe, potentially allowing [command injection](https://en.wikipedia.org/wiki/Code_injection#Shell_injection) (see the [escaping section](escaping.md#shells)). In almost all cases, plain JavaScript is a better alternative to shells. The [following page](bash.md) shows how to convert Bash into JavaScript. ## Specific shell ```js import {execa} from 'execa'; await execa({shell: '/bin/bash'})`npm run "$TASK" && npm run test`; ``` ## OS-specific shell When the [`shell`](api.md#optionsshell) option is `true`, `sh` is used on Unix and [`cmd.exe`](https://en.wikipedia.org/wiki/Cmd.exe) is used on Windows. `sh` and `cmd.exe` syntaxes are very different. Therefore, this is usually not useful. ```js await execa({shell: true})`npm run build`; ```
[**Next**: 📜 Scripts](scripts.md)\ [**Previous**: 💬 Escaping/quoting](escaping.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/small.md ================================================ execa logo
# 🐭 Small packages ## `nano-spawn` Execa aims to be the best way to run commands on Node.js. It is [very widely used](https://github.com/sindresorhus/execa/network/dependents), [battle-tested](https://github.com/sindresorhus/execa/graphs/contributors) and has a bunch of [features](../readme.md#features). However, this means it has a relatively big package size: [![Install size](https://packagephobia.com/badge?p=execa)](https://packagephobia.com/result?p=execa). This should not be a problem in a server-side context, such as a script, a server, or an app. But you might be in an environment requiring small packages, such as a library or a serverless function. If so, you can use [nano-spawn](https://github.com/sindresorhus/nano-spawn). It is similar, is maintained by the [same people](https://github.com/sindresorhus/nano-spawn#maintainers), has no dependencies, and a smaller package size: ![npm package minzipped size](https://img.shields.io/bundlejs/size/nano-spawn) [![Install size](https://packagephobia.com/badge?p=nano-spawn)](https://packagephobia.com/result?p=nano-spawn). On the other hand, please note `nano-spawn` lacks many features from Execa: [scripts](scripts.md), [template string syntax](execution.md#template-string-syntax), [synchronous execution](execution.md#synchronous-execution), [file input/output](output.md#file-output), [binary input/output](binary.md), [advanced piping](pipe.md), [verbose mode](debugging.md#verbose-mode), [graceful](termination.md#graceful-termination) or [forceful termination](termination.md#forceful-termination), [IPC](ipc.md), [shebangs on Windows](windows.md), [and much more](https://github.com/sindresorhus/nano-spawn/issues/14). ```js import spawn from 'nano-spawn'; const result = await spawn('npm', ['run', 'build']); ``` ### `node:child_process` Both Execa and nano-spawn are built on top of the [`node:child_process`](https://nodejs.org/api/child_process.html) core module. If you'd prefer avoiding adding any dependency, you may use `node:child_process` directly. However, you might miss some basic [features](https://github.com/sindresorhus/nano-spawn#features) that both Execa and nano-spawn provide: [proper error handling](https://github.com/sindresorhus/nano-spawn#subprocesserror), [full Windows support](https://github.com/sindresorhus/nano-spawn#windows-support), [local binaries](https://github.com/sindresorhus/nano-spawn#optionspreferlocal), [piping](https://github.com/sindresorhus/nano-spawn#subprocesspipefile-arguments-options), [lines iteration](https://github.com/sindresorhus/nano-spawn#subprocesssymbolasynciterator), [interleaved output](https://github.com/sindresorhus/nano-spawn#resultoutput), [and more](https://github.com/sindresorhus/nano-spawn#features). ```js import {execFile} from 'node:child_process'; import {promisify} from 'node:util'; const pExecFile = promisify(execFile); const result = await pExecFile('npm', ['run', 'build']); ```
[**Next**: 🤓 TypeScript](typescript.md)\ [**Previous**: 🔍 Differences with Bash and zx](bash.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/streams.md ================================================ execa logo
# ⏳️ Streams ## Node.js streams ### Input ```js import {createReadStream} from 'node:fs'; import {once} from 'node:events'; import {execa} from 'execa'; const readable = createReadStream('input.txt'); await once(readable, 'open'); await execa({stdin: readable})`npm run scaffold`; ``` ### Output ```js import {createWriteStream} from 'node:fs'; import {once} from 'node:events'; import {execa} from 'execa'; const writable = createWriteStream('output.txt'); await once(writable, 'open'); await execa({stdout: writable})`npm run build`; ``` ### File descriptors When passing a Node.js stream to the [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout) or [`stderr`](api.md#optionsstderr) option, that stream must have an underlying file or socket, such as the streams created by the [`fs`](https://nodejs.org/api/fs.html#filehandlecreatereadstreamoptions), [`net`](https://nodejs.org/api/net.html#new-netsocketoptions) or [`http`](https://nodejs.org/api/http.html#class-httpincomingmessage) core modules. Otherwise the following error is thrown. ``` TypeError [ERR_INVALID_ARG_VALUE]: The argument 'stdio' is invalid. ``` This limitation can be worked around by either: - Using the [`input`](api.md#optionsinput) option instead of the [`stdin`](api.md#optionsstdin) option. - Passing a [web stream](#web-streams). - Passing [`[nodeStream, 'pipe']`](output.md#multiple-targets) instead of `nodeStream`. ## Web streams [Web streams](https://nodejs.org/api/webstreams.html) ([`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) or [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream)) can be used instead of [Node.js streams](https://nodejs.org/api/stream.html). ```js const response = await fetch('https://example.com'); await execa({stdin: response.body})`npm run build`; ``` ## Iterables as input ```js const getReplInput = async function * () { for await (const replLine of getReplLines()) { yield replLine; } }; await execa({stdin: getReplInput()})`npm run scaffold`; ``` ## Manual streaming [`subprocess.stdin`](api.md#subprocessstdin) is a Node.js [`Readable`](https://nodejs.org/api/stream.html#class-streamreadable) stream and [`subprocess.stdout`](api.md#subprocessstdout)/[`subprocess.stderr`](api.md#subprocessstderr)/[`subprocess.all`](api.md#subprocessall) are Node.js [`Writable`](https://nodejs.org/api/stream.html#class-streamwritable) streams. They can be used to stream input/output manually. This is intended for advanced situations. In most cases, the following simpler solutions can be used instead: - [`result.stdout`](output.md#stdout-and-stderr), [`result.stderr`](output.md#stdout-and-stderr) or [`result.stdio`](output.md#additional-file-descriptors). - The [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout), [`stderr`](api.md#optionsstderr) or [`stdio`](api.md#optionsstdio) options. - [`subprocess.iterable()`](lines.md#progressive-splitting). - [`subprocess.pipe()`](pipe.md). ## Converting a subprocess to a stream ### Convert The [`subprocess.readable()`](api.md#subprocessreadablereadableoptions), [`subprocess.writable()`](api.md#subprocesswritablewritableoptions) and [`subprocess.duplex()`](api.md#subprocessduplexduplexoptions) methods convert the subprocess to a Node.js [`Readable`](https://nodejs.org/api/stream.html#class-streamreadable), [`Writable`](https://nodejs.org/api/stream.html#class-streamwritable) and [`Duplex`](https://nodejs.org/api/stream.html#class-streamduplex) stream. This is useful when using a library or API that expects Node.js streams as arguments. In every other situation, the simpler solutions described [above](#manual-streaming) can be used instead. ```js const readable = execa`npm run scaffold`.readable(); const writable = execa`npm run scaffold`.writable(); const duplex = execa`npm run scaffold`.duplex(); ``` ### Different file descriptor By default, [`subprocess.readable()`](api.md#subprocessreadablereadableoptions), [`subprocess.writable()`](api.md#subprocesswritablewritableoptions) and [`subprocess.duplex()`](api.md#subprocessduplexduplexoptions) methods use [`stdin`](api.md#subprocessstdin) and [`stdout`](api.md#subprocessstdout). This can be changed using the [`from`](api.md#readableoptionsfrom) and [`to`](api.md#writableoptionsto) options. ```js const readable = execa`npm run scaffold`.readable({from: 'stderr'}); const writable = execa`npm run scaffold`.writable({to: 'fd3'}); const duplex = execa`npm run scaffold`.duplex({from: 'stderr', to: 'fd3'}); ``` ### Error handling When using [`subprocess.readable()`](api.md#subprocessreadablereadableoptions), [`subprocess.writable()`](api.md#subprocesswritablewritableoptions) or [`subprocess.duplex()`](api.md#subprocessduplexduplexoptions), the stream waits for the subprocess to end, and emits an [`error`](https://nodejs.org/api/stream.html#event-error) event if the subprocess [fails](errors.md). This differs from [`subprocess.stdin`](api.md#subprocessstdin), [`subprocess.stdout`](api.md#subprocessstdout) and [`subprocess.stderr`](api.md#subprocessstderr)'s behavior. This means you do not need to `await` the subprocess' [promise](execution.md#result). On the other hand, you (or the library using the stream) do need to both consume the stream, and handle its `error` event. This can be done by using [`await finished(stream)`](https://nodejs.org/api/stream.html#streamfinishedstream-options), [`await pipeline(..., stream, ...)`](https://nodejs.org/api/stream.html#streampipelinesource-transforms-destination-options) or [`await text(stream)`](https://nodejs.org/api/webstreams.html#streamconsumerstextstream) which throw an exception when the stream errors.
[**Next**: 📞 Inter-process communication](ipc.md)\ [**Previous**: 🔀 Piping multiple subprocesses](pipe.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/termination.md ================================================ execa logo
# 🏁 Termination ## Alternatives Terminating a subprocess ends it abruptly. This prevents rolling back the subprocess' operations and leaves them incomplete. Ideally subprocesses should end on their own. If that's not possible, [graceful termination](#graceful-termination) should be preferred. ## Canceling The [`cancelSignal`](api.md#optionscancelsignal) option can be used to cancel a subprocess. When it is [aborted](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort), a [`SIGTERM` signal](#default-signal) is sent to the subprocess. ```js import {execaNode} from 'execa'; const controller = new AbortController(); const cancelSignal = controller.signal; setTimeout(() => { controller.abort(); }, 5000); try { await execaNode({cancelSignal})`build.js`; } catch (error) { if (error.isCanceled) { console.error('Canceled by cancelSignal.'); } throw error; } ``` ## Graceful termination ### Share a `cancelSignal` When the [`gracefulCancel`](api.md#optionsgracefulcancel) option is `true`, the [`cancelSignal`](api.md#optionscancelsignal) option does not send any [`SIGTERM`](#sigterm). Instead, the subprocess calls [`getCancelSignal()`](api.md#getcancelsignal) to retrieve and handle the [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). This allows the subprocess to properly clean up and abort operations. This option only works with Node.js files. This is cross-platform. If you do not need to support Windows, [signal handlers](#handling-signals) can also be used. ```js // main.js import {execaNode} from 'execa'; const controller = new AbortController(); const cancelSignal = controller.signal; setTimeout(() => { controller.abort(); }, 5000); try { await execaNode({cancelSignal, gracefulCancel: true})`build.js`; } catch (error) { if (error.isGracefullyCanceled) { console.error('Cancelled gracefully.'); } throw error; } ``` ```js // build.js import {getCancelSignal} from 'execa'; const cancelSignal = await getCancelSignal(); ``` ### Abort operations The [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) returned by [`getCancelSignal()`](api.md#getcancelsignal) can be passed to most long-running Node.js methods: [`setTimeout()`](https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options), [`setInterval()`](https://nodejs.org/api/timers.html#timerspromisessetintervaldelay-value-options), [events](https://nodejs.org/api/events.html#eventsonemitter-eventname-options), [streams](https://nodejs.org/api/stream.html#new-streamreadableoptions), [REPL](https://nodejs.org/api/readline.html#rlquestionquery-options), HTTP/TCP [requests](https://nodejs.org/api/http.html#httprequesturl-options-callback) or [servers](https://nodejs.org/api/net.html#serverlistenoptions-callback), [reading](https://nodejs.org/api/fs.html#fspromisesreadfilepath-options) / [writing](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) / [watching](https://nodejs.org/api/fs.html#fspromiseswatchfilename-options) files, or spawning another subprocess. When aborted, those methods throw the `Error` instance which was passed to [`abortController.abort(error)`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort). Since those methods keep the subprocess alive, aborting them makes the subprocess end on its own. ```js import {getCancelSignal} from 'execa'; import {watch} from 'node:fs/promises'; const cancelSignal = await getCancelSignal(); try { for await (const fileChange of watch('./src', {signal: cancelSignal})) { onFileChange(fileChange); } } catch (error) { if (error.isGracefullyCanceled) { console.log(error.cause === cancelSignal.reason); // true } } ``` ### Cleanup logic For other kinds of operations, the [`abort`](https://nodejs.org/api/globals.html#event-abort) event should be listened to. Although [`cancelSignal.addEventListener('abort')`](https://nodejs.org/api/events.html#eventtargetaddeventlistenertype-listener-options) can be used, [`events.addAbortListener(cancelSignal)`](https://nodejs.org/api/events.html#eventsaddabortlistenersignal-listener) is preferred since it works even if the `cancelSignal` is already aborted. ### Graceful exit We recommend explicitly [stopping](#abort-operations) each pending operation when the subprocess is aborted. This allows it to end on its own. ```js import {getCancelSignal} from 'execa'; import {addAbortListener} from 'node:events'; const cancelSignal = await getCancelSignal(); addAbortListener(cancelSignal, async () => { await cleanup(); process.exitCode = 1; }); ``` However, if any operation is still ongoing, the subprocess will keep running. It can be forcefully ended using [`process.exit(exitCode)`](https://nodejs.org/api/process.html#processexitcode) instead of [`process.exitCode`](https://nodejs.org/api/process.html#processexitcode_1). If the subprocess is still alive after 5 seconds, it is forcefully terminated with [`SIGKILL`](#sigkill). This can be [configured or disabled](#forceful-termination) using the [`forceKillAfterDelay`](api.md#optionsforcekillafterdelay) option. ## Timeout ### Execution timeout If the subprocess lasts longer than the [`timeout`](api.md#optionstimeout) option, a [`SIGTERM` signal](#default-signal) is sent to it. ```js try { await execa({timeout: 5000})`npm run build`; } catch (error) { if (error.timedOut) { console.error('Timed out.'); } throw error; } ``` ### Inactivity timeout To terminate a subprocess when it becomes inactive, the [`cancelSignal`](#canceling) option can be combined with [transforms](transform.md) and some [debouncing logic](https://github.com/sindresorhus/debounce-fn). The following example terminates the subprocess if it has not printed to [`stdout`](api.md#resultstdout)/[`stderr`](api.md#resultstderr) in the last minute. ```js import {execa} from 'execa'; import debounceFn from 'debounce-fn'; // 1 minute const wait = 60_000; const getInactivityOptions = () => { const controller = new AbortController(); const cancelSignal = controller.signal; // Delay and debounce `cancelSignal` each time `controller.abort()` is called const scheduleAbort = debounceFn(controller.abort.bind(controller), {wait}); const onOutput = { * transform(data) { // When anything is printed, debounce `controller.abort()` scheduleAbort(); // Keep the output as is yield data; }, // Debounce even if the output does not include any newline binary: true, }; // Start debouncing scheduleAbort(); return { cancelSignal, stdout: onOutput, stderr: onOutput, }; }; const options = getInactivityOptions(); await execa(options)`npm run build`; ``` ## Current process exit If the current process exits, the subprocess is automatically [terminated](#default-signal) unless either: - The [`cleanup`](api.md#optionscleanup) option is `false`. - The subprocess is run in the background using the [`detached`](api.md#optionsdetached) option. - The current process was terminated abruptly, for example, with [`SIGKILL`](#sigkill) as opposed to [`SIGTERM`](#sigterm) or a successful exit. ## Signal termination [`subprocess.kill()`](api.md#subprocesskillsignal-error) sends a [signal](https://en.wikipedia.org/wiki/Signal_(IPC)) to the subprocess. This is an inter-process message handled by the OS. Most (but [not all](https://github.com/ehmicky/human-signals#action)) signals terminate the subprocess. [More info.](https://nodejs.org/api/child_process.html#subprocesskillsignal) ### SIGTERM [`SIGTERM`](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGTERM) is the default signal. It terminates the subprocess. On Unix, it can [be handled](#handling-signals) to run some cleanup logic. ```js const subprocess = execa`npm run build`; subprocess.kill(); // Is the same as: subprocess.kill('SIGTERM'); ``` ### SIGINT [`SIGINT`](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGINT) terminates the process. Its [handler](#handling-signals) is triggered on `CTRL-C`. ```js subprocess.kill('SIGINT'); ``` ### SIGKILL [`SIGKILL`](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGKILL) forcefully terminates the subprocess. It [cannot be handled](#handling-signals). ```js subprocess.kill('SIGKILL'); ``` ### SIGQUIT [`SIGQUIT`](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGQUIT) terminates the process. On Unix, it creates a [core dump](https://en.wikipedia.org/wiki/Core_dump). ```js subprocess.kill('SIGQUIT'); ``` ### Other signals Other signals can be passed as argument. However, most other signals do not fully [work on Windows](https://github.com/ehmicky/cross-platform-node-guide/blob/main/docs/6_networking_ipc/signals.md#cross-platform-signals). ### Default signal The [`killSignal`](api.md#optionskillsignal) option sets the default signal used by [`subprocess.kill()`](api.md#subprocesskillsignal-error) and the following options: [`cancelSignal`](#canceling), [`timeout`](#timeout), [`maxBuffer`](output.md#big-output) and [`cleanup`](#current-process-exit). It is [`SIGTERM`](#sigterm) by default. ```js const subprocess = execa({killSignal: 'SIGKILL'})`npm run build`; subprocess.kill(); // Forceful termination ``` ### Handling signals On Unix, most signals (not [`SIGKILL`](#sigkill)) can be intercepted to perform a graceful exit. ```js process.on('SIGTERM', () => { cleanup(); process.exit(1); }) ``` Unfortunately this [usually does not work](https://github.com/ehmicky/cross-platform-node-guide/blob/main/docs/6_networking_ipc/signals.md#cross-platform-signals) on Windows. The only signal that is somewhat cross-platform is [`SIGINT`](#sigint): on Windows, its handler is triggered when the user types `CTRL-C` in the terminal. However `subprocess.kill('SIGINT')` is only handled on Unix. Execa provides the [`gracefulCancel`](#graceful-termination) option as a cross-platform alternative to signal handlers. ### Signal name and description When a subprocess was terminated by a signal, [`error.isTerminated`](api.md#erroristerminated) is `true`. Also, [`error.signal`](api.md#errorsignal) and [`error.signalDescription`](api.md#errorsignaldescription) indicate the signal's name and [human-friendly description](https://github.com/ehmicky/human-signals). On Windows, those are only set if the current process terminated the subprocess, as opposed to [another process](#inter-process-termination). ```js try { await execa`npm run build`; } catch (error) { if (error.isTerminated) { console.error(error.signal); // SIGFPE console.error(error.signalDescription); // 'Floating point arithmetic error' } throw error; } ``` ## Forceful termination If the subprocess is terminated but does not exit, [`SIGKILL`](#sigkill) is automatically sent to forcefully terminate it. The grace period is set by the [`forceKillAfterDelay`](api.md#optionsforcekillafterdelay) option, which is 5 seconds by default. This feature can be disabled with `false`. The [`error.isForcefullyTerminated`](api.md#errorisforcefullyterminated) boolean property can be used to check whether a subprocess was forcefully terminated by the `forceKillAfterDelay` option. This works when the subprocess is terminated by either: - Calling [`subprocess.kill()`](api.md#subprocesskillsignal-error) with no arguments. - The [`cancelSignal`](#canceling), [`timeout`](#timeout), [`maxBuffer`](output.md#big-output) or [`cleanup`](#current-process-exit) option. This does not work when the subprocess is terminated by either: - Calling [`subprocess.kill()`](api.md#subprocesskillsignal-error) with a specific signal. - Calling [`process.kill(subprocess.pid)`](api.md#subprocesspid). - Sending a termination signal [from another process](#inter-process-termination). Also, this does not work on Windows, because Windows [doesn't support signals](https://nodejs.org/api/process.html#process_signal_events): `SIGKILL` and `SIGTERM` both terminate the subprocess immediately. Other packages (such as [`taskkill`](https://github.com/sindresorhus/taskkill)) can be used to achieve fail-safe termination on Windows. ```js // No forceful termination const subprocess = execa({forceKillAfterDelay: false})`npm run build`; subprocess.kill(); ``` ## Inter-process termination [`subprocess.kill()`](api.md#subprocesskillsignal-error) only works when the current process terminates the subprocess. To terminate the subprocess from a different process, its [`subprocess.pid`](api.md#subprocesspid) can be used instead. ```js const subprocess = execa`npm run build`; console.log('PID:', subprocess.pid); // PID: 6513 await subprocess; ``` For example, from a terminal: ```sh $ kill -SIGTERM 6513 ``` Or from a different Node.js process: ```js import process from 'node:process'; process.kill(subprocessPid); ``` ## Error message and stack trace When terminating a subprocess, it is possible to include an error message and stack trace by using [`subprocess.kill(error)`](api.md#subprocesskillerror). The `error` argument will be available at [`error.cause`](api.md#errorcause). ```js try { const subprocess = execa`npm run build`; setTimeout(() => { subprocess.kill(new Error('Timed out after 5 seconds.')); }, 5000); await subprocess; } catch (error) { if (error.isTerminated) { console.error(error.cause); // new Error('Timed out after 5 seconds.') console.error(error.cause.stack); // Stack trace from `error.cause` console.error(error.originalMessage); // 'Timed out after 5 seconds.' } throw error; } ```
[**Next**: 🎹 Input](input.md)\ [**Previous**: ❌ Errors](errors.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/transform.md ================================================ execa logo
# 🧙 Transforms ## Summary Transforms map or filter the input or output of a subprocess. They are defined by passing a [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) or a [transform options object](api.md#transform-options) to the [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout), [`stderr`](api.md#optionsstderr) or [`stdio`](api.md#optionsstdio) option. It can be [`async`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*). ```js import {execa} from 'execa'; const transform = function * (line) { const prefix = line.includes('error') ? 'ERROR' : 'INFO'; yield `${prefix}: ${line}`; }; const {stdout} = await execa({stdout: transform})`echo HELLO`; console.log(stdout); // INFO: HELLO ``` ## Difference with iteration Transforms operate one `line` at a time, just like [`subprocess.iterable()`](lines.md#progressive-splitting). However, unlike iteration, transforms: - Modify the subprocess' [output](api.md#resultstdout) and [streams](api.md#subprocessstdout). - Can apply to the subprocess' input. - Are defined using a [generator function](#summary), [`Duplex`](#duplextransform-streams) stream, Node.js [`Transform`](#duplextransform-streams) stream or web [`TransformStream`](#duplextransform-streams). ## Filtering `yield` can be called 0, 1 or multiple times. Not calling `yield` enables filtering a specific line. ```js const transform = function * (line) { if (!line.includes('secret')) { yield line; } }; const {stdout} = await execa({stdout: transform})`echo ${'This is a secret'}`; console.log(stdout); // '' ``` ## Object mode By default, [`stdout`](api.md#optionsstdout) and [`stderr`](api.md#optionsstderr)'s transforms must return a string or an `Uint8Array`. However, if the [`objectMode`](api.md#transformoptionsobjectmode) transform option is `true`, any type can be returned instead, except `null` or `undefined`. The subprocess' [`result.stdout`](api.md#resultstdout)/[`result.stderr`](api.md#resultstderr) will be an array of values. ```js const transform = function * (line) { yield JSON.parse(line); }; const {stdout} = await execa({stdout: {transform, objectMode: true}})`node jsonlines-output.js`; for (const data of stdout) { console.log(stdout); // {...object} } ``` [`stdin`](api.md#optionsstdin) can also use `objectMode: true`. ```js const transform = function * (line) { yield JSON.stringify(line); }; const input = [{event: 'example'}, {event: 'otherExample'}]; await execa({stdin: [input, {transform, objectMode: true}]})`node jsonlines-input.js`; ``` ## Sharing state State can be shared between calls of the [`transform`](api.md#transformoptionstransform) and [`final`](api.md#transformoptionsfinal) functions. ```js let count = 0; // Prefix line number const transform = function * (line) { yield `[${count++}] ${line}`; }; ``` ## Finalizing To create additional lines after the last one, a [`final`](api.md#transformoptionsfinal) generator function can be used. ```js let count = 0; const transform = function * (line) { count += 1; yield line; }; const final = function * () { yield `Number of lines: ${count}`; }; const {stdout} = await execa({stdout: {transform, final}})`npm run build`; console.log(stdout); // Ends with: 'Number of lines: 54' ``` ## Duplex/Transform streams A [`Duplex`](https://nodejs.org/api/stream.html#class-streamduplex) stream, Node.js [`Transform`](https://nodejs.org/api/stream.html#class-streamtransform) stream or web [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) can be used instead of a generator function. Like generator functions, web `TransformStream` can be passed either directly or as a [`{transform}` plain object](api.md#transform-options). But `Duplex` and `Transform` must always be passed as a `{transform}` plain object. The [`objectMode`](#object-mode) transform option can be used, but not the [`binary`](api.md#transformoptionsbinary) nor [`preserveNewlines`](api.md#transformoptionspreservenewlines) options. ```js import {createGzip} from 'node:zlib'; import {execa} from 'execa'; const {stdout} = await execa({ stdout: {transform: createGzip()}, encoding: 'buffer', })`npm run build`; console.log(stdout); // `stdout` is compressed with gzip ``` ```js const {stdout} = await execa({ stdout: new CompressionStream('gzip'), encoding: 'buffer', })`npm run build`; console.log(stdout); // `stdout` is compressed with gzip ``` ## Combining The [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout), [`stderr`](api.md#optionsstderr) and [`stdio`](api.md#optionsstdio) options can accept [an array of values](output.md#multiple-targets). While this is not specific to transforms, this can be useful with them too. For example, the following transform impacts the value printed by `'inherit'`. ```js await execa({stdout: [transform, 'inherit']})`npm run build`; ``` This also allows using multiple transforms. ```js await execa({stdout: [transform, otherTransform]})`npm run build`; ``` Or saving to archives. ```js await execa({stdout: [new CompressionStream('gzip'), {file: './output.gz'}]})`npm run build`; ```
[**Next**: 🔀 Piping multiple subprocesses](pipe.md)\ [**Previous**: 🤖 Binary data](binary.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/typescript.md ================================================ execa logo
# 🤓 TypeScript ## Available types The following types can be imported: [`ResultPromise`](api.md#return-value), [`Subprocess`](api.md#subprocess), [`Result`](api.md#result), [`ExecaError`](api.md#execaerror), [`Options`](api.md#options-1), [`StdinOption`](api.md#optionsstdin), [`StdoutStderrOption`](api.md#optionsstdout), [`TemplateExpression`](api.md#execacommand), [`Message`](api.md#subprocesssendmessagemessage-sendmessageoptions), [`VerboseObject`](api.md#verbose-object), [`ExecaMethod`](api.md#execaoptions), [`ExecaNodeMethod`](api.md#execanodeoptions) and [`ExecaScriptMethod`](api.md#options). ```ts import { execa as execa_, ExecaError, type ResultPromise, type Result, type Options, type StdinOption, type StdoutStderrOption, type TemplateExpression, type Message, type VerboseObject, type ExecaMethod, } from 'execa'; const execa: ExecaMethod = execa_({preferLocal: true}); const options: Options = { stdin: 'inherit' satisfies StdinOption, stdout: 'pipe' satisfies StdoutStderrOption, stderr: 'pipe' satisfies StdoutStderrOption, timeout: 1000, ipc: true, verbose(verboseLine: string, verboseObject: VerboseObject) { return verboseObject.type === 'duration' ? verboseLine : undefined; }, }; const task: TemplateExpression = 'build'; const message: Message = 'hello world'; try { const subprocess: ResultPromise = execa(options)`npm run ${task}`; await subprocess.sendMessage?.(message); const result: Result = await subprocess; console.log(result.stdout); } catch (error) { if (error instanceof ExecaError) { console.error(error); } } ``` ## Synchronous execution Their [synchronous](#synchronous-execution) counterparts are [`SyncResult`](api.md#result), [`ExecaSyncError`](api.md#execasyncerror), [`SyncOptions`](api.md#options-1), [`StdinSyncOption`](api.md#optionsstdin), [`StdoutStderrSyncOption`](api.md#optionsstdout), [`TemplateExpression`](api.md#execacommand), [`SyncVerboseObject`](api.md#verbose-object), [`ExecaSyncMethod`](api.md#execasyncoptions) and [`ExecaScriptSyncMethod`](api.md#syncoptions). ```ts import { execaSync as execaSync_, ExecaSyncError, type SyncResult, type SyncOptions, type StdinSyncOption, type StdoutStderrSyncOption, type TemplateExpression, type SyncVerboseObject, type ExecaSyncMethod, } from 'execa'; const execaSync: ExecaSyncMethod = execaSync_({preferLocal: true}); const options: SyncOptions = { stdin: 'inherit' satisfies StdinSyncOption, stdout: 'pipe' satisfies StdoutStderrSyncOption, stderr: 'pipe' satisfies StdoutStderrSyncOption, timeout: 1000, verbose(verboseLine: string, verboseObject: SyncVerboseObject) { return verboseObject.type === 'duration' ? verboseLine : undefined; }, }; const task: TemplateExpression = 'build'; try { const result: SyncResult = execaSync(options)`npm run ${task}`; console.log(result.stdout); } catch (error) { if (error instanceof ExecaSyncError) { console.error(error); } } ``` ## Type inference The above examples demonstrate those types. However, types are automatically inferred. Therefore, explicit types are only needed when defining functions that take those values as parameters. ```ts import { execa as execa_, ExecaError, type Result, type VerboseObject, } from 'execa'; const execa = execa_({preferLocal: true}); const printResultStdout = (result: Result) => { console.log('Stdout', result.stdout); }; const options = { stdin: 'inherit', stdout: 'pipe', stderr: 'pipe', timeout: 1000, ipc: true, verbose(verboseLine: string, verboseObject: VerboseObject) { return verboseObject.type === 'duration' ? verboseLine : undefined; }, } as const; const task = 'build'; const message = 'hello world'; try { const subprocess = execa(options)`npm run ${task}`; await subprocess.sendMessage(message); const result = await subprocess; printResultStdout(result); } catch (error) { if (error instanceof ExecaError) { console.error(error); } } ``` ## Troubleshooting ### Supported version The minimum supported TypeScript version is [`5.1.6`](https://github.com/microsoft/TypeScript/releases/tag/v5.1.6). ### ES modules This package uses pure ES modules. Therefore the TypeScript's `--module` compiler option must be set to [`nodenext`](https://www.typescriptlang.org/docs/handbook/modules/reference.html#node16-nodenext) or [`preserve`](https://www.typescriptlang.org/docs/handbook/modules/reference.html#preserve). [More info.](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) Otherwise, transpilation will work, but running the transpiled file will throw the following runtime error: ``` Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported. ``` Or: ``` ReferenceError: exports is not defined in ES module scope ``` ### Strict unions Several options are typed as unions of strings: [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout), [`stderr`](api.md#optionsstderr), [`encoding`](api.md#optionsencoding), [`serialization`](api.md#optionsserialization), [`verbose`](api.md#optionsverbose), [`killSignal`](api.md#optionskillsignal), [`from`](api.md#pipeoptionsfrom) and [`to`](api.md#pipeoptionsto). For example, the `serialization` option's type is `'advanced' | 'json'`, not `string`. Therefore the following example fails: ```ts import {execa} from 'execa'; // Type error: "No overload matches this call" const spawnSubprocess = (serialization: string) => execa({serialization})`npm run build`; // Without `as const`, `options.serialization` is typed as `string`, not `'json'` const options = {serialization: 'json'}; // Type error: "No overload matches this call" await execa(options)`npm run build`; ``` But this works: ```ts import {execa, type Options} from 'execa'; const spawnSubprocess = (serialization: Options['serialization']) => execa({serialization})`npm run build`; const options = {serialization: 'json'} as const; await execa(options)`npm run build`; ```
[**Next**: 📔 API reference](api.md)\ [**Previous**: 🐭 Small packages](small.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: docs/windows.md ================================================ execa logo
# 📎 Windows Although each OS implements subprocesses very differently, Execa makes them cross-platform, except in a few instances. ## Shebang On Unix, executable files can use [shebangs](https://en.wikipedia.org/wiki/Shebang_(Unix)). ```js import {execa} from 'execa'; // If script.js starts with #!/usr/bin/env node await execa`./script.js`; // Then, the above is a shortcut for: await execa`node ./script.js`; ``` Although Windows does not natively support shebangs, Execa adds support for them. ## Signals Only few [signals](termination.md#other-signals) work on Windows with Node.js: [`SIGTERM`](termination.md#sigterm), [`SIGKILL`](termination.md#sigkill), [`SIGINT`](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGINT) and [`SIGQUIT`](termination.md#sigquit). Also, sending signals from other processes is [not supported](termination.md#signal-name-and-description). Finally, the [`forceKillAfterDelay`](api.md#optionsforcekillafterdelay) option [is a noop](termination.md#forceful-termination) on Windows. ## Asynchronous I/O The default value for the [`stdin`](api.md#optionsstdin), [`stdout`](api.md#optionsstdout) and [`stderr`](api.md#optionsstderr) options is [`'pipe'`](output.md#stdout-and-stderr). This returns the output as [`result.stdout`](api.md#resultstdout) and [`result.stderr`](api.md#resultstderr) and allows for [manual streaming](streams.md#manual-streaming). Instead of `'pipe'`, `'overlapped'` can be used instead to use [asynchronous I/O](https://learn.microsoft.com/en-us/windows/win32/fileio/synchronous-and-asynchronous-i-o) under-the-hood on Windows, instead of the default behavior which is synchronous. On other platforms, asynchronous I/O is always used, so `'overlapped'` behaves the same way as `'pipe'`. ## Escaping Windows requires files and arguments to be quoted when they contain spaces, tabs, backslashes or double quotes. Unlike Unix, this is needed even when no [shell](shell.md) is used. When not using any shell, Execa performs that quoting automatically. This ensures files and arguments are split correctly. ```js await execa`npm run ${'task with space'}`; ``` When using a [shell](shell.md), the user must manually perform shell-specific quoting, on both Unix and Windows. When the [`shell`](api.md#optionsshell) option is `true`, [`cmd.exe`](https://en.wikipedia.org/wiki/Cmd.exe) is used on Windows and `sh` on Unix. Unfortunately, both shells use different quoting rules. With `cmd.exe`, this mostly involves double quoting arguments and prepending double quotes with a backslash. ```js if (isWindows) { await execa({shell: true})`npm run ${'"task with space"'}`; } else { await execa({shell: true})`npm run ${'\'task with space\''}`; } ``` When using other Windows shells (such as PowerShell or WSL), Execa performs `cmd.exe`-specific automatic quoting by default. This is a problem since Powershell uses different quoting rules. This can be disabled using the [`windowsVerbatimArguments: true`](api.md#optionswindowsverbatimarguments) option. ```js if (isWindows) { await execa({windowsVerbatimArguments: true})`wsl ...`; } ``` ## Console window If the [`windowsHide`](api.md#optionswindowshide) option is `false`, the subprocess is run in a new console window. This is necessary to make [`SIGINT` work](https://github.com/nodejs/node/issues/29837) on Windows, and to prevent subprocesses not being cleaned up in [some specific situations](https://github.com/sindresorhus/execa/issues/433). ## UID and GID By default, subprocesses are run using the current [user](https://en.wikipedia.org/wiki/User_identifier) and [group](https://en.wikipedia.org/wiki/Group_identifier). The [`uid`](api.md#optionsuid) and [`gid`](api.md#optionsgid) options can be used to set a different user or group. However, since Windows uses a different permission model, those options throw.
[**Next**: 🔍 Differences with Bash and zx](bash.md)\ [**Previous**: 🐛 Debugging](debugging.md)\ [**Top**: Table of contents](../readme.md#documentation) ================================================ FILE: index.d.ts ================================================ export type { StdinOption, StdinSyncOption, StdoutStderrOption, StdoutStderrSyncOption, } from './types/stdio/type.js'; export type {Options, SyncOptions} from './types/arguments/options.js'; export type {TemplateExpression} from './types/methods/template.js'; export type {Result, SyncResult} from './types/return/result.js'; export type {ResultPromise, Subprocess} from './types/subprocess/subprocess.js'; export {ExecaError, ExecaSyncError} from './types/return/final-error.js'; export {execa, type ExecaMethod} from './types/methods/main-async.js'; export {execaSync, type ExecaSyncMethod} from './types/methods/main-sync.js'; export {execaCommand, execaCommandSync, parseCommandString} from './types/methods/command.js'; export {$, type ExecaScriptMethod, type ExecaScriptSyncMethod} from './types/methods/script.js'; export {execaNode, type ExecaNodeMethod} from './types/methods/node.js'; export { sendMessage, getOneMessage, getEachMessage, getCancelSignal, type Message, } from './types/ipc.js'; export type {VerboseObject, SyncVerboseObject} from './types/verbose.js'; ================================================ FILE: index.js ================================================ import {createExeca} from './lib/methods/create.js'; import {mapCommandAsync, mapCommandSync} from './lib/methods/command.js'; import {mapNode} from './lib/methods/node.js'; import {mapScriptAsync, setScriptSync, deepScriptOptions} from './lib/methods/script.js'; import {getIpcExport} from './lib/ipc/methods.js'; export {parseCommandString} from './lib/methods/command.js'; export {ExecaError, ExecaSyncError} from './lib/return/final-error.js'; export const execa = createExeca(() => ({})); export const execaSync = createExeca(() => ({isSync: true})); export const execaCommand = createExeca(mapCommandAsync); export const execaCommandSync = createExeca(mapCommandSync); export const execaNode = createExeca(mapNode); export const $ = createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync); const { sendMessage, getOneMessage, getEachMessage, getCancelSignal, } = getIpcExport(); export { sendMessage, getOneMessage, getEachMessage, getCancelSignal, }; ================================================ FILE: lib/arguments/command.js ================================================ import {logCommand} from '../verbose/start.js'; import {getVerboseInfo} from '../verbose/info.js'; import {getStartTime} from '../return/duration.js'; import {joinCommand} from './escape.js'; import {normalizeFdSpecificOption} from './specific.js'; // Compute `result.command`, `result.escapedCommand` and `verbose`-related information export const handleCommand = (filePath, rawArguments, rawOptions) => { const startTime = getStartTime(); const {command, escapedCommand} = joinCommand(filePath, rawArguments); const verbose = normalizeFdSpecificOption(rawOptions, 'verbose'); const verboseInfo = getVerboseInfo(verbose, escapedCommand, {...rawOptions}); logCommand(escapedCommand, verboseInfo); return { command, escapedCommand, startTime, verboseInfo, }; }; ================================================ FILE: lib/arguments/cwd.js ================================================ import {statSync} from 'node:fs'; import path from 'node:path'; import process from 'node:process'; import {safeNormalizeFileUrl} from './file-url.js'; // Normalize `cwd` option export const normalizeCwd = (cwd = getDefaultCwd()) => { const cwdString = safeNormalizeFileUrl(cwd, 'The "cwd" option'); return path.resolve(cwdString); }; const getDefaultCwd = () => { try { return process.cwd(); } catch (error) { error.message = `The current directory does not exist.\n${error.message}`; throw error; } }; // When `cwd` option has an invalid value, provide with a better error message export const fixCwdError = (originalMessage, cwd) => { if (cwd === getDefaultCwd()) { return originalMessage; } let cwdStat; try { cwdStat = statSync(cwd); } catch (error) { return `The "cwd" option is invalid: ${cwd}.\n${error.message}\n${originalMessage}`; } if (!cwdStat.isDirectory()) { return `The "cwd" option is not a directory: ${cwd}.\n${originalMessage}`; } return originalMessage; }; ================================================ FILE: lib/arguments/encoding-option.js ================================================ // Validate `encoding` option export const validateEncoding = ({encoding}) => { if (ENCODINGS.has(encoding)) { return; } const correctEncoding = getCorrectEncoding(encoding); if (correctEncoding !== undefined) { throw new TypeError(`Invalid option \`encoding: ${serializeEncoding(encoding)}\`. Please rename it to ${serializeEncoding(correctEncoding)}.`); } const correctEncodings = [...ENCODINGS].map(correctEncoding => serializeEncoding(correctEncoding)).join(', '); throw new TypeError(`Invalid option \`encoding: ${serializeEncoding(encoding)}\`. Please rename it to one of: ${correctEncodings}.`); }; const TEXT_ENCODINGS = new Set(['utf8', 'utf16le']); export const BINARY_ENCODINGS = new Set(['buffer', 'hex', 'base64', 'base64url', 'latin1', 'ascii']); const ENCODINGS = new Set([...TEXT_ENCODINGS, ...BINARY_ENCODINGS]); const getCorrectEncoding = encoding => { if (encoding === null) { return 'buffer'; } if (typeof encoding !== 'string') { return; } const lowerEncoding = encoding.toLowerCase(); if (lowerEncoding in ENCODING_ALIASES) { return ENCODING_ALIASES[lowerEncoding]; } if (ENCODINGS.has(lowerEncoding)) { return lowerEncoding; } }; const ENCODING_ALIASES = { // eslint-disable-next-line unicorn/text-encoding-identifier-case 'utf-8': 'utf8', 'utf-16le': 'utf16le', 'ucs-2': 'utf16le', ucs2: 'utf16le', binary: 'latin1', }; const serializeEncoding = encoding => typeof encoding === 'string' ? `"${encoding}"` : String(encoding); ================================================ FILE: lib/arguments/escape.js ================================================ import {platform} from 'node:process'; import {stripVTControlCharacters} from 'node:util'; // Compute `result.command` and `result.escapedCommand` export const joinCommand = (filePath, rawArguments) => { const fileAndArguments = [filePath, ...rawArguments]; const command = fileAndArguments.join(' '); const escapedCommand = fileAndArguments .map(fileAndArgument => quoteString(escapeControlCharacters(fileAndArgument))) .join(' '); return {command, escapedCommand}; }; // Remove ANSI sequences and escape control characters and newlines export const escapeLines = lines => stripVTControlCharacters(lines) .split('\n') .map(line => escapeControlCharacters(line)) .join('\n'); const escapeControlCharacters = line => line.replaceAll(SPECIAL_CHAR_REGEXP, character => escapeControlCharacter(character)); const escapeControlCharacter = character => { const commonEscape = COMMON_ESCAPES[character]; if (commonEscape !== undefined) { return commonEscape; } const codepoint = character.codePointAt(0); const codepointHex = codepoint.toString(16); return codepoint <= ASTRAL_START ? `\\u${codepointHex.padStart(4, '0')}` : `\\U${codepointHex}`; }; // Characters that would create issues when printed are escaped using the \u or \U notation. // Those include control characters and newlines. // The \u and \U notation is Bash specific, but there is no way to do this in a shell-agnostic way. // Some shells do not even have a way to print those characters in an escaped fashion. // Therefore, we prioritize printing those safely, instead of allowing those to be copy-pasted. // List of Unicode character categories: https://www.fileformat.info/info/unicode/category/index.htm const getSpecialCharRegExp = () => { try { // This throws when using Node.js without ICU support. // When using a RegExp literal, this would throw at parsing-time, instead of runtime. // eslint-disable-next-line prefer-regex-literals return new RegExp('\\p{Separator}|\\p{Other}', 'gu'); } catch { // Similar to the above RegExp, but works even when Node.js has been built without ICU support. // Unlike the above RegExp, it only covers whitespaces and C0/C1 control characters. // It does not cover some edge cases, such as Unicode reserved characters. // See https://github.com/sindresorhus/execa/issues/1143 // eslint-disable-next-line no-control-regex return /[\s\u0000-\u001F\u007F-\u009F\u00AD]/g; } }; const SPECIAL_CHAR_REGEXP = getSpecialCharRegExp(); // Accepted by $'...' in Bash. // Exclude \a \e \v which are accepted in Bash but not in JavaScript (except \v) and JSON. const COMMON_ESCAPES = { ' ': ' ', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t', }; // Up until that codepoint, \u notation can be used instead of \U const ASTRAL_START = 65_535; // Some characters are shell-specific, i.e. need to be escaped when the command is copy-pasted then run. // Escaping is shell-specific. We cannot know which shell is used: `process.platform` detection is not enough. // For example, Windows users could be using `cmd.exe`, Powershell or Bash for Windows which all use different escaping. // We use '...' on Unix, which is POSIX shell compliant and escape all characters but ' so this is fairly safe. // On Windows, we assume cmd.exe is used and escape with "...", which also works with Powershell. const quoteString = escapedArgument => { if (NO_ESCAPE_REGEXP.test(escapedArgument)) { return escapedArgument; } return platform === 'win32' ? `"${escapedArgument.replaceAll('"', '""')}"` : `'${escapedArgument.replaceAll('\'', '\'\\\'\'')}'`; }; const NO_ESCAPE_REGEXP = /^[\w./-]+$/; ================================================ FILE: lib/arguments/fd-options.js ================================================ import {parseFd} from './specific.js'; // Retrieve stream targeted by the `to` option export const getToStream = (destination, to = 'stdin') => { const isWritable = true; const {options, fileDescriptors} = SUBPROCESS_OPTIONS.get(destination); const fdNumber = getFdNumber(fileDescriptors, to, isWritable); const destinationStream = destination.stdio[fdNumber]; if (destinationStream === null) { throw new TypeError(getInvalidStdioOptionMessage(fdNumber, to, options, isWritable)); } return destinationStream; }; // Retrieve stream targeted by the `from` option export const getFromStream = (source, from = 'stdout') => { const isWritable = false; const {options, fileDescriptors} = SUBPROCESS_OPTIONS.get(source); const fdNumber = getFdNumber(fileDescriptors, from, isWritable); const sourceStream = fdNumber === 'all' ? source.all : source.stdio[fdNumber]; if (sourceStream === null || sourceStream === undefined) { throw new TypeError(getInvalidStdioOptionMessage(fdNumber, from, options, isWritable)); } return sourceStream; }; // Keeps track of the options passed to each Execa call export const SUBPROCESS_OPTIONS = new WeakMap(); const getFdNumber = (fileDescriptors, fdName, isWritable) => { const fdNumber = parseFdNumber(fdName, isWritable); validateFdNumber(fdNumber, fdName, isWritable, fileDescriptors); return fdNumber; }; const parseFdNumber = (fdName, isWritable) => { const fdNumber = parseFd(fdName); if (fdNumber !== undefined) { return fdNumber; } const {validOptions, defaultValue} = isWritable ? {validOptions: '"stdin"', defaultValue: 'stdin'} : {validOptions: '"stdout", "stderr", "all"', defaultValue: 'stdout'}; throw new TypeError(`"${getOptionName(isWritable)}" must not be "${fdName}". It must be ${validOptions} or "fd3", "fd4" (and so on). It is optional and defaults to "${defaultValue}".`); }; const validateFdNumber = (fdNumber, fdName, isWritable, fileDescriptors) => { const fileDescriptor = fileDescriptors[getUsedDescriptor(fdNumber)]; if (fileDescriptor === undefined) { throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. That file descriptor does not exist. Please set the "stdio" option to ensure that file descriptor exists.`); } if (fileDescriptor.direction === 'input' && !isWritable) { throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. It must be a readable stream, not writable.`); } if (fileDescriptor.direction !== 'input' && isWritable) { throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. It must be a writable stream, not readable.`); } }; const getInvalidStdioOptionMessage = (fdNumber, fdName, options, isWritable) => { if (fdNumber === 'all' && !options.all) { return 'The "all" option must be true to use "from: \'all\'".'; } const {optionName, optionValue} = getInvalidStdioOption(fdNumber, options); return `The "${optionName}: ${serializeOptionValue(optionValue)}" option is incompatible with using "${getOptionName(isWritable)}: ${serializeOptionValue(fdName)}". Please set this option with "pipe" instead.`; }; const getInvalidStdioOption = (fdNumber, {stdin, stdout, stderr, stdio}) => { const usedDescriptor = getUsedDescriptor(fdNumber); if (usedDescriptor === 0 && stdin !== undefined) { return {optionName: 'stdin', optionValue: stdin}; } if (usedDescriptor === 1 && stdout !== undefined) { return {optionName: 'stdout', optionValue: stdout}; } if (usedDescriptor === 2 && stderr !== undefined) { return {optionName: 'stderr', optionValue: stderr}; } return {optionName: `stdio[${usedDescriptor}]`, optionValue: stdio[usedDescriptor]}; }; const getUsedDescriptor = fdNumber => fdNumber === 'all' ? 1 : fdNumber; const getOptionName = isWritable => isWritable ? 'to' : 'from'; export const serializeOptionValue = value => { if (typeof value === 'string') { return `'${value}'`; } return typeof value === 'number' ? `${value}` : 'Stream'; }; ================================================ FILE: lib/arguments/file-url.js ================================================ import {fileURLToPath} from 'node:url'; // Allow some arguments/options to be either a file path string or a file URL export const safeNormalizeFileUrl = (file, name) => { const fileString = normalizeFileUrl(normalizeDenoExecPath(file)); if (typeof fileString !== 'string') { throw new TypeError(`${name} must be a string or a file URL: ${fileString}.`); } return fileString; }; // In Deno node:process execPath is a special object, not just a string: // https://github.com/denoland/deno/blob/f460188e583f00144000aa0d8ade08218d47c3c1/ext/node/polyfills/process.ts#L344 const normalizeDenoExecPath = file => isDenoExecPath(file) ? file.toString() : file; export const isDenoExecPath = file => typeof file !== 'string' && file && Object.getPrototypeOf(file) === String.prototype; // Same but also allows other values, e.g. `boolean` for the `shell` option export const normalizeFileUrl = file => file instanceof URL ? fileURLToPath(file) : file; ================================================ FILE: lib/arguments/options.js ================================================ import path from 'node:path'; import process from 'node:process'; import crossSpawn from 'cross-spawn'; import {npmRunPathEnv} from 'npm-run-path'; import {normalizeForceKillAfterDelay} from '../terminate/kill.js'; import {normalizeKillSignal} from '../terminate/signal.js'; import {validateCancelSignal} from '../terminate/cancel.js'; import {validateGracefulCancel} from '../terminate/graceful.js'; import {validateTimeout} from '../terminate/timeout.js'; import {handleNodeOption} from '../methods/node.js'; import {validateIpcInputOption} from '../ipc/ipc-input.js'; import {validateEncoding, BINARY_ENCODINGS} from './encoding-option.js'; import {normalizeCwd} from './cwd.js'; import {normalizeFileUrl} from './file-url.js'; import {normalizeFdSpecificOptions} from './specific.js'; // Normalize the options object, and sometimes also the file paths and arguments. // Applies default values, validate allowed options, normalize them. export const normalizeOptions = (filePath, rawArguments, rawOptions) => { // Prevent prototype pollution by copying only own properties to a null-prototype object const sanitizedOptions = {__proto__: null, ...rawOptions}; sanitizedOptions.cwd = normalizeCwd(sanitizedOptions.cwd); const [processedFile, processedArguments, processedOptions] = handleNodeOption(filePath, rawArguments, sanitizedOptions); const {command: file, args: commandArguments, options: initialOptions} = crossSpawn._parse(processedFile, processedArguments, processedOptions); const fdOptions = normalizeFdSpecificOptions(initialOptions); const options = addDefaultOptions(fdOptions); validateTimeout(options); validateEncoding(options); validateIpcInputOption(options); validateCancelSignal(options); validateGracefulCancel(options); options.shell = normalizeFileUrl(options.shell); options.env = getEnv(options); options.killSignal = normalizeKillSignal(options.killSignal); options.forceKillAfterDelay = normalizeForceKillAfterDelay(options.forceKillAfterDelay); options.lines = options.lines.map((lines, fdNumber) => lines && !BINARY_ENCODINGS.has(options.encoding) && options.buffer[fdNumber]); if (process.platform === 'win32' && path.basename(file, '.exe') === 'cmd') { // #116 commandArguments.unshift('/q'); } return {file, commandArguments, options}; }; // Use null prototype to prevent prototype pollution from leaking through const addDefaultOptions = ({ extendEnv = true, preferLocal = false, cwd, localDir: localDirectory = cwd, encoding = 'utf8', reject = true, cleanup = true, all = false, windowsHide = true, killSignal = 'SIGTERM', forceKillAfterDelay = true, gracefulCancel = false, ipcInput, ipc = ipcInput !== undefined || gracefulCancel, serialization = 'advanced', ...options }) => ({ __proto__: null, ...options, extendEnv, preferLocal, cwd, localDirectory, encoding, reject, cleanup, all, windowsHide, killSignal, forceKillAfterDelay, gracefulCancel, ipcInput, ipc, serialization, }); const getEnv = ({env: envOption, extendEnv, preferLocal, node, localDirectory, nodePath}) => { const env = extendEnv ? {...process.env, ...envOption} : envOption; if (preferLocal || node) { return npmRunPathEnv({ env, cwd: localDirectory, execPath: nodePath, preferLocal, addExecPath: node, }); } return env; }; ================================================ FILE: lib/arguments/shell.js ================================================ // When the `shell` option is set, any command argument is concatenated as a single string by Node.js: // https://github.com/nodejs/node/blob/e38ce27f3ca0a65f68a31cedd984cddb927d4002/lib/child_process.js#L614-L624 // However, since Node 24, it also prints a deprecation warning. // To avoid this warning, we perform that same operation before calling `node:child_process`. // Shells only understand strings, which is why Node.js performs that concatenation. // However, we rely on users splitting command arguments as an array. // For example, this allows us to easily detect which arguments are passed. // So we do want users to pass array of arguments even with `shell: true`, but we also want to avoid any warning. export const concatenateShell = (file, commandArguments, options) => options.shell && commandArguments.length > 0 ? [[file, ...commandArguments].join(' '), [], options] : [file, commandArguments, options]; ================================================ FILE: lib/arguments/specific.js ================================================ import {debuglog} from 'node:util'; import isPlainObject from 'is-plain-obj'; import {STANDARD_STREAMS_ALIASES} from '../utils/standard-stream.js'; // Some options can have different values for `stdout`/`stderr`/`fd3`. // This normalizes those to array of values. // For example, `{verbose: {stdout: 'none', stderr: 'full'}}` becomes `{verbose: ['none', 'none', 'full']}` export const normalizeFdSpecificOptions = options => { const optionsCopy = {...options}; for (const optionName of FD_SPECIFIC_OPTIONS) { optionsCopy[optionName] = normalizeFdSpecificOption(options, optionName); } return optionsCopy; }; export const normalizeFdSpecificOption = (options, optionName) => { const optionBaseArray = Array.from({length: getStdioLength(options) + 1}); const optionArray = normalizeFdSpecificValue(options[optionName], optionBaseArray, optionName); return addDefaultValue(optionArray, optionName); }; const getStdioLength = ({stdio}) => Array.isArray(stdio) ? Math.max(stdio.length, STANDARD_STREAMS_ALIASES.length) : STANDARD_STREAMS_ALIASES.length; const normalizeFdSpecificValue = (optionValue, optionArray, optionName) => isPlainObject(optionValue) ? normalizeOptionObject(optionValue, optionArray, optionName) : optionArray.fill(optionValue); const normalizeOptionObject = (optionValue, optionArray, optionName) => { for (const fdName of Object.keys(optionValue).sort(compareFdName)) { for (const fdNumber of parseFdName(fdName, optionName, optionArray)) { optionArray[fdNumber] = optionValue[fdName]; } } return optionArray; }; // Ensure priority order when setting both `stdout`/`stderr`, `fd1`/`fd2`, and `all` const compareFdName = (fdNameA, fdNameB) => getFdNameOrder(fdNameA) < getFdNameOrder(fdNameB) ? 1 : -1; const getFdNameOrder = fdName => { if (fdName === 'stdout' || fdName === 'stderr') { return 0; } return fdName === 'all' ? 2 : 1; }; const parseFdName = (fdName, optionName, optionArray) => { if (fdName === 'ipc') { return [optionArray.length - 1]; } const fdNumber = parseFd(fdName); if (fdNumber === undefined || fdNumber === 0) { throw new TypeError(`"${optionName}.${fdName}" is invalid. It must be "${optionName}.stdout", "${optionName}.stderr", "${optionName}.all", "${optionName}.ipc", or "${optionName}.fd3", "${optionName}.fd4" (and so on).`); } if (fdNumber >= optionArray.length) { throw new TypeError(`"${optionName}.${fdName}" is invalid: that file descriptor does not exist. Please set the "stdio" option to ensure that file descriptor exists.`); } return fdNumber === 'all' ? [1, 2] : [fdNumber]; }; // Use the same syntax for fd-specific options and the `from`/`to` options export const parseFd = fdName => { if (fdName === 'all') { return fdName; } if (STANDARD_STREAMS_ALIASES.includes(fdName)) { return STANDARD_STREAMS_ALIASES.indexOf(fdName); } const regexpResult = FD_REGEXP.exec(fdName); if (regexpResult !== null) { return Number(regexpResult[1]); } }; const FD_REGEXP = /^fd(\d+)$/; const addDefaultValue = (optionArray, optionName) => optionArray.map(optionValue => optionValue === undefined ? DEFAULT_OPTIONS[optionName] : optionValue); // Default value for the `verbose` option const verboseDefault = debuglog('execa').enabled ? 'full' : 'none'; const DEFAULT_OPTIONS = { lines: false, buffer: true, maxBuffer: 1000 * 1000 * 100, verbose: verboseDefault, stripFinalNewline: true, }; // List of options which can have different values for `stdout`/`stderr` export const FD_SPECIFIC_OPTIONS = ['lines', 'buffer', 'maxBuffer', 'verbose', 'stripFinalNewline']; // Retrieve fd-specific option export const getFdSpecificValue = (optionArray, fdNumber) => fdNumber === 'ipc' ? optionArray.at(-1) : optionArray[fdNumber]; ================================================ FILE: lib/convert/add.js ================================================ import {initializeConcurrentStreams} from './concurrent.js'; import {createReadable} from './readable.js'; import {createWritable} from './writable.js'; import {createDuplex} from './duplex.js'; import {createIterable} from './iterable.js'; // Add methods to convert the subprocess to a stream or iterable export const addConvertedStreams = (subprocess, {encoding}) => { const concurrentStreams = initializeConcurrentStreams(); subprocess.readable = createReadable.bind(undefined, {subprocess, concurrentStreams, encoding}); subprocess.writable = createWritable.bind(undefined, {subprocess, concurrentStreams}); subprocess.duplex = createDuplex.bind(undefined, {subprocess, concurrentStreams, encoding}); subprocess.iterable = createIterable.bind(undefined, subprocess, encoding); subprocess[Symbol.asyncIterator] = createIterable.bind(undefined, subprocess, encoding, {}); }; ================================================ FILE: lib/convert/concurrent.js ================================================ import {createDeferred} from '../utils/deferred.js'; // When using multiple `.readable()`/`.writable()`/`.duplex()`, `final` and `destroy` should wait for other streams export const initializeConcurrentStreams = () => ({ readableDestroy: new WeakMap(), writableFinal: new WeakMap(), writableDestroy: new WeakMap(), }); // Each file descriptor + `waitName` has its own array of promises. // Each promise is a single `.readable()`/`.writable()`/`.duplex()` call. export const addConcurrentStream = (concurrentStreams, stream, waitName) => { const weakMap = concurrentStreams[waitName]; if (!weakMap.has(stream)) { weakMap.set(stream, []); } const promises = weakMap.get(stream); const promise = createDeferred(); promises.push(promise); const resolve = promise.resolve.bind(promise); return {resolve, promises}; }; // Wait for other streams, but stop waiting when subprocess ends export const waitForConcurrentStreams = async ({resolve, promises}, subprocess) => { resolve(); const [isSubprocessExit] = await Promise.race([ Promise.allSettled([true, subprocess]), Promise.all([false, ...promises]), ]); return !isSubprocessExit; }; ================================================ FILE: lib/convert/duplex.js ================================================ import {Duplex} from 'node:stream'; import {callbackify} from 'node:util'; import {BINARY_ENCODINGS} from '../arguments/encoding-option.js'; import { getSubprocessStdout, getReadableOptions, getReadableMethods, onStdoutFinished, onReadableDestroy, } from './readable.js'; import { getSubprocessStdin, getWritableMethods, onStdinFinished, onWritableDestroy, } from './writable.js'; // Create a `Duplex` stream combining both `subprocess.readable()` and `subprocess.writable()` export const createDuplex = ({subprocess, concurrentStreams, encoding}, {from, to, binary: binaryOption = true, preserveNewlines = true} = {}) => { const binary = binaryOption || BINARY_ENCODINGS.has(encoding); const {subprocessStdout, waitReadableDestroy} = getSubprocessStdout(subprocess, from, concurrentStreams); const {subprocessStdin, waitWritableFinal, waitWritableDestroy} = getSubprocessStdin(subprocess, to, concurrentStreams); const {readableEncoding, readableObjectMode, readableHighWaterMark} = getReadableOptions(subprocessStdout, binary); const {read, onStdoutDataDone} = getReadableMethods({ subprocessStdout, subprocess, binary, encoding, preserveNewlines, }); const duplex = new Duplex({ read, ...getWritableMethods(subprocessStdin, subprocess, waitWritableFinal), destroy: callbackify(onDuplexDestroy.bind(undefined, { subprocessStdout, subprocessStdin, subprocess, waitReadableDestroy, waitWritableFinal, waitWritableDestroy, })), readableHighWaterMark, writableHighWaterMark: subprocessStdin.writableHighWaterMark, readableObjectMode, writableObjectMode: subprocessStdin.writableObjectMode, encoding: readableEncoding, }); onStdoutFinished({ subprocessStdout, onStdoutDataDone, readable: duplex, subprocess, subprocessStdin, }); onStdinFinished(subprocessStdin, duplex, subprocessStdout); return duplex; }; const onDuplexDestroy = async ({subprocessStdout, subprocessStdin, subprocess, waitReadableDestroy, waitWritableFinal, waitWritableDestroy}, error) => { await Promise.all([ onReadableDestroy({subprocessStdout, subprocess, waitReadableDestroy}, error), onWritableDestroy({ subprocessStdin, subprocess, waitWritableFinal, waitWritableDestroy, }, error), ]); }; ================================================ FILE: lib/convert/iterable.js ================================================ import {BINARY_ENCODINGS} from '../arguments/encoding-option.js'; import {getFromStream} from '../arguments/fd-options.js'; import {iterateOnSubprocessStream} from '../io/iterate.js'; // Convert the subprocess to an async iterable export const createIterable = (subprocess, encoding, { from, binary: binaryOption = false, preserveNewlines = false, } = {}) => { const binary = binaryOption || BINARY_ENCODINGS.has(encoding); const subprocessStdout = getFromStream(subprocess, from); const onStdoutData = iterateOnSubprocessStream({ subprocessStdout, subprocess, binary, shouldEncode: true, encoding, preserveNewlines, }); return iterateOnStdoutData(onStdoutData, subprocessStdout, subprocess); }; const iterateOnStdoutData = async function * (onStdoutData, subprocessStdout, subprocess) { try { yield * onStdoutData; } finally { if (subprocessStdout.readable) { subprocessStdout.destroy(); } await subprocess; } }; ================================================ FILE: lib/convert/readable.js ================================================ import {Readable} from 'node:stream'; import {callbackify} from 'node:util'; import {BINARY_ENCODINGS} from '../arguments/encoding-option.js'; import {getFromStream} from '../arguments/fd-options.js'; import {iterateOnSubprocessStream, DEFAULT_OBJECT_HIGH_WATER_MARK} from '../io/iterate.js'; import {createDeferred} from '../utils/deferred.js'; import {addConcurrentStream, waitForConcurrentStreams} from './concurrent.js'; import { safeWaitForSubprocessStdin, waitForSubprocessStdout, waitForSubprocess, destroyOtherStream, } from './shared.js'; // Create a `Readable` stream that forwards from `stdout` and awaits the subprocess export const createReadable = ({subprocess, concurrentStreams, encoding}, {from, binary: binaryOption = true, preserveNewlines = true} = {}) => { const binary = binaryOption || BINARY_ENCODINGS.has(encoding); const {subprocessStdout, waitReadableDestroy} = getSubprocessStdout(subprocess, from, concurrentStreams); const {readableEncoding, readableObjectMode, readableHighWaterMark} = getReadableOptions(subprocessStdout, binary); const {read, onStdoutDataDone} = getReadableMethods({ subprocessStdout, subprocess, binary, encoding, preserveNewlines, }); const readable = new Readable({ read, destroy: callbackify(onReadableDestroy.bind(undefined, {subprocessStdout, subprocess, waitReadableDestroy})), highWaterMark: readableHighWaterMark, objectMode: readableObjectMode, encoding: readableEncoding, }); onStdoutFinished({ subprocessStdout, onStdoutDataDone, readable, subprocess, }); return readable; }; // Retrieve `stdout` (or other stream depending on `from`) export const getSubprocessStdout = (subprocess, from, concurrentStreams) => { const subprocessStdout = getFromStream(subprocess, from); const waitReadableDestroy = addConcurrentStream(concurrentStreams, subprocessStdout, 'readableDestroy'); return {subprocessStdout, waitReadableDestroy}; }; export const getReadableOptions = ({readableEncoding, readableObjectMode, readableHighWaterMark}, binary) => binary ? {readableEncoding, readableObjectMode, readableHighWaterMark} : {readableEncoding, readableObjectMode: true, readableHighWaterMark: DEFAULT_OBJECT_HIGH_WATER_MARK}; export const getReadableMethods = ({subprocessStdout, subprocess, binary, encoding, preserveNewlines}) => { const onStdoutDataDone = createDeferred(); const onStdoutData = iterateOnSubprocessStream({ subprocessStdout, subprocess, binary, shouldEncode: !binary, encoding, preserveNewlines, }); return { read() { onRead(this, onStdoutData, onStdoutDataDone); }, onStdoutDataDone, }; }; // Forwards data from `stdout` to `readable` const onRead = async (readable, onStdoutData, onStdoutDataDone) => { try { const {value, done} = await onStdoutData.next(); if (done) { onStdoutDataDone.resolve(); } else { readable.push(value); } } catch {} }; // When `subprocess.stdout` ends/aborts/errors, do the same on `readable`. // Await the subprocess, for the same reason as above. export const onStdoutFinished = async ({subprocessStdout, onStdoutDataDone, readable, subprocess, subprocessStdin}) => { try { await waitForSubprocessStdout(subprocessStdout); await subprocess; await safeWaitForSubprocessStdin(subprocessStdin); await onStdoutDataDone; if (readable.readable) { readable.push(null); } } catch (error) { await safeWaitForSubprocessStdin(subprocessStdin); destroyOtherReadable(readable, error); } }; // When `readable` aborts/errors, do the same on `subprocess.stdout` export const onReadableDestroy = async ({subprocessStdout, subprocess, waitReadableDestroy}, error) => { if (await waitForConcurrentStreams(waitReadableDestroy, subprocess)) { destroyOtherReadable(subprocessStdout, error); await waitForSubprocess(subprocess, error); } }; const destroyOtherReadable = (stream, error) => { destroyOtherStream(stream, stream.readable, error); }; ================================================ FILE: lib/convert/shared.js ================================================ import {finished} from 'node:stream/promises'; import {isStreamAbort} from '../resolve/wait-stream.js'; export const safeWaitForSubprocessStdin = async subprocessStdin => { if (subprocessStdin === undefined) { return; } try { await waitForSubprocessStdin(subprocessStdin); } catch {} }; export const safeWaitForSubprocessStdout = async subprocessStdout => { if (subprocessStdout === undefined) { return; } try { await waitForSubprocessStdout(subprocessStdout); } catch {} }; export const waitForSubprocessStdin = async subprocessStdin => { await finished(subprocessStdin, {cleanup: true, readable: false, writable: true}); }; export const waitForSubprocessStdout = async subprocessStdout => { await finished(subprocessStdout, {cleanup: true, readable: true, writable: false}); }; // When `readable` or `writable` aborts/errors, awaits the subprocess, for the reason mentioned above export const waitForSubprocess = async (subprocess, error) => { await subprocess; if (error) { throw error; } }; export const destroyOtherStream = (stream, isOpen, error) => { if (error && !isStreamAbort(error)) { stream.destroy(error); } else if (isOpen) { stream.destroy(); } }; ================================================ FILE: lib/convert/writable.js ================================================ import {Writable} from 'node:stream'; import {callbackify} from 'node:util'; import {getToStream} from '../arguments/fd-options.js'; import {addConcurrentStream, waitForConcurrentStreams} from './concurrent.js'; import { safeWaitForSubprocessStdout, waitForSubprocessStdin, waitForSubprocess, destroyOtherStream, } from './shared.js'; // Create a `Writable` stream that forwards to `stdin` and awaits the subprocess export const createWritable = ({subprocess, concurrentStreams}, {to} = {}) => { const {subprocessStdin, waitWritableFinal, waitWritableDestroy} = getSubprocessStdin(subprocess, to, concurrentStreams); const writable = new Writable({ ...getWritableMethods(subprocessStdin, subprocess, waitWritableFinal), destroy: callbackify(onWritableDestroy.bind(undefined, { subprocessStdin, subprocess, waitWritableFinal, waitWritableDestroy, })), highWaterMark: subprocessStdin.writableHighWaterMark, objectMode: subprocessStdin.writableObjectMode, }); onStdinFinished(subprocessStdin, writable); return writable; }; // Retrieve `stdin` (or other stream depending on `to`) export const getSubprocessStdin = (subprocess, to, concurrentStreams) => { const subprocessStdin = getToStream(subprocess, to); const waitWritableFinal = addConcurrentStream(concurrentStreams, subprocessStdin, 'writableFinal'); const waitWritableDestroy = addConcurrentStream(concurrentStreams, subprocessStdin, 'writableDestroy'); return {subprocessStdin, waitWritableFinal, waitWritableDestroy}; }; export const getWritableMethods = (subprocessStdin, subprocess, waitWritableFinal) => ({ write: onWrite.bind(undefined, subprocessStdin), final: callbackify(onWritableFinal.bind(undefined, subprocessStdin, subprocess, waitWritableFinal)), }); // Forwards data from `writable` to `stdin` const onWrite = (subprocessStdin, chunk, encoding, done) => { if (subprocessStdin.write(chunk, encoding)) { done(); } else { subprocessStdin.once('drain', done); } }; // Ensures that the writable `final` and readable `end` events awaits the subprocess. // Like this, any subprocess failure is propagated as a stream `error` event, instead of being lost. // The user does not need to `await` the subprocess anymore, but now needs to await the stream completion or error. // When multiple writables are targeting the same stream, they wait for each other, unless the subprocess ends first. const onWritableFinal = async (subprocessStdin, subprocess, waitWritableFinal) => { if (await waitForConcurrentStreams(waitWritableFinal, subprocess)) { if (subprocessStdin.writable) { subprocessStdin.end(); } await subprocess; } }; // When `subprocess.stdin` ends/aborts/errors, do the same on `writable`. export const onStdinFinished = async (subprocessStdin, writable, subprocessStdout) => { try { await waitForSubprocessStdin(subprocessStdin); if (writable.writable) { writable.end(); } } catch (error) { await safeWaitForSubprocessStdout(subprocessStdout); destroyOtherWritable(writable, error); } }; // When `writable` aborts/errors, do the same on `subprocess.stdin` export const onWritableDestroy = async ({subprocessStdin, subprocess, waitWritableFinal, waitWritableDestroy}, error) => { await waitForConcurrentStreams(waitWritableFinal, subprocess); if (await waitForConcurrentStreams(waitWritableDestroy, subprocess)) { destroyOtherWritable(subprocessStdin, error); await waitForSubprocess(subprocess, error); } }; const destroyOtherWritable = (stream, error) => { destroyOtherStream(stream, stream.writable, error); }; ================================================ FILE: lib/io/contents.js ================================================ import {setImmediate} from 'node:timers/promises'; import getStream, {getStreamAsArrayBuffer, getStreamAsArray} from 'get-stream'; import {isArrayBuffer} from '../utils/uint-array.js'; import {shouldLogOutput, logLines} from '../verbose/output.js'; import {iterateForResult} from './iterate.js'; import {handleMaxBuffer} from './max-buffer.js'; import {getStripFinalNewline} from './strip-newline.js'; // Retrieve `result.stdout|stderr|all|stdio[*]` export const getStreamOutput = async ({stream, onStreamEnd, fdNumber, encoding, buffer, maxBuffer, lines, allMixed, stripFinalNewline, verboseInfo, streamInfo}) => { const logPromise = logOutputAsync({ stream, onStreamEnd, fdNumber, encoding, allMixed, verboseInfo, streamInfo, }); if (!buffer) { await Promise.all([resumeStream(stream), logPromise]); return; } const stripFinalNewlineValue = getStripFinalNewline(stripFinalNewline, fdNumber); const iterable = iterateForResult({ stream, onStreamEnd, lines, encoding, stripFinalNewline: stripFinalNewlineValue, allMixed, }); const [output] = await Promise.all([ getStreamContents({ stream, iterable, fdNumber, encoding, maxBuffer, lines, }), logPromise, ]); return output; }; const logOutputAsync = async ({stream, onStreamEnd, fdNumber, encoding, allMixed, verboseInfo, streamInfo: {fileDescriptors}}) => { if (!shouldLogOutput({ stdioItems: fileDescriptors[fdNumber]?.stdioItems, encoding, verboseInfo, fdNumber, })) { return; } const linesIterable = iterateForResult({ stream, onStreamEnd, lines: true, encoding, stripFinalNewline: true, allMixed, }); await logLines(linesIterable, stream, fdNumber, verboseInfo); }; // When using `buffer: false`, users need to read `subprocess.stdout|stderr|all` right away // See https://github.com/sindresorhus/execa/issues/730 and https://github.com/sindresorhus/execa/pull/729#discussion_r1465496310 const resumeStream = async stream => { await setImmediate(); if (stream.readableFlowing === null) { stream.resume(); } }; const getStreamContents = async ({stream, stream: {readableObjectMode}, iterable, fdNumber, encoding, maxBuffer, lines}) => { try { if (readableObjectMode || lines) { return await getStreamAsArray(iterable, {maxBuffer}); } if (encoding === 'buffer') { return new Uint8Array(await getStreamAsArrayBuffer(iterable, {maxBuffer})); } return await getStream(iterable, {maxBuffer}); } catch (error) { return handleBufferedData(handleMaxBuffer({ error, stream, readableObjectMode, lines, encoding, fdNumber, })); } }; // On failure, `result.stdout|stderr|all` should contain the currently buffered stream // They are automatically closed and flushed by Node.js when the subprocess exits // When `buffer` is `false`, `streamPromise` is `undefined` and there is no buffered data to retrieve export const getBufferedData = async streamPromise => { try { return await streamPromise; } catch (error) { return handleBufferedData(error); } }; // Ensure we are returning Uint8Arrays when using `encoding: 'buffer'` const handleBufferedData = ({bufferedData}) => isArrayBuffer(bufferedData) ? new Uint8Array(bufferedData) : bufferedData; ================================================ FILE: lib/io/input-sync.js ================================================ import {runGeneratorsSync} from '../transform/generator.js'; import {joinToUint8Array, isUint8Array} from '../utils/uint-array.js'; import {TYPE_TO_MESSAGE} from '../stdio/type.js'; // Apply `stdin`/`input`/`inputFile` options, before spawning, in sync mode, by converting it to the `input` option export const addInputOptionsSync = (fileDescriptors, options) => { for (const fdNumber of getInputFdNumbers(fileDescriptors)) { addInputOptionSync(fileDescriptors, fdNumber, options); } }; const getInputFdNumbers = fileDescriptors => new Set(Object.entries(fileDescriptors) .filter(([, {direction}]) => direction === 'input') .map(([fdNumber]) => Number(fdNumber))); const addInputOptionSync = (fileDescriptors, fdNumber, options) => { const {stdioItems} = fileDescriptors[fdNumber]; const allStdioItems = stdioItems.filter(({contents}) => contents !== undefined); if (allStdioItems.length === 0) { return; } if (fdNumber !== 0) { const [{type, optionName}] = allStdioItems; throw new TypeError(`Only the \`stdin\` option, not \`${optionName}\`, can be ${TYPE_TO_MESSAGE[type]} with synchronous methods.`); } const allContents = allStdioItems.map(({contents}) => contents); const transformedContents = allContents.map(contents => applySingleInputGeneratorsSync(contents, stdioItems)); options.input = joinToUint8Array(transformedContents); }; const applySingleInputGeneratorsSync = (contents, stdioItems) => { const newContents = runGeneratorsSync(contents, stdioItems, 'utf8', true); validateSerializable(newContents); return joinToUint8Array(newContents); }; const validateSerializable = newContents => { const invalidItem = newContents.find(item => typeof item !== 'string' && !isUint8Array(item)); if (invalidItem !== undefined) { throw new TypeError(`The \`stdin\` option is invalid: when passing objects as input, a transform must be used to serialize them to strings or Uint8Arrays: ${invalidItem}.`); } }; ================================================ FILE: lib/io/iterate.js ================================================ import {on} from 'node:events'; import {getDefaultHighWaterMark} from 'node:stream'; import {getEncodingTransformGenerator} from '../transform/encoding-transform.js'; import {getSplitLinesGenerator} from '../transform/split.js'; import {transformChunkSync, finalChunksSync} from '../transform/run-sync.js'; // Iterate over lines of `subprocess.stdout`, used by `subprocess.readable|duplex|iterable()` export const iterateOnSubprocessStream = ({subprocessStdout, subprocess, binary, shouldEncode, encoding, preserveNewlines}) => { const controller = new AbortController(); stopReadingOnExit(subprocess, controller); return iterateOnStream({ stream: subprocessStdout, controller, binary, shouldEncode: !subprocessStdout.readableObjectMode && shouldEncode, encoding, shouldSplit: !subprocessStdout.readableObjectMode, preserveNewlines, }); }; const stopReadingOnExit = async (subprocess, controller) => { try { await subprocess; } catch {} finally { controller.abort(); } }; // Iterate over lines of `subprocess.stdout`, used by `result.stdout` and the `verbose: 'full'` option. // Applies the `lines` and `encoding` options. export const iterateForResult = ({stream, onStreamEnd, lines, encoding, stripFinalNewline, allMixed}) => { const controller = new AbortController(); stopReadingOnStreamEnd(onStreamEnd, controller, stream); const objectMode = stream.readableObjectMode && !allMixed; return iterateOnStream({ stream, controller, binary: encoding === 'buffer', shouldEncode: !objectMode, encoding, shouldSplit: !objectMode && lines, preserveNewlines: !stripFinalNewline, }); }; const stopReadingOnStreamEnd = async (onStreamEnd, controller, stream) => { try { await onStreamEnd; } catch { stream.destroy(); } finally { controller.abort(); } }; const iterateOnStream = ({stream, controller, binary, shouldEncode, encoding, shouldSplit, preserveNewlines}) => { const onStdoutChunk = on(stream, 'data', { signal: controller.signal, highWaterMark: HIGH_WATER_MARK, // Backward compatibility with older name for this option // See https://github.com/nodejs/node/pull/52080#discussion_r1525227861 // @todo Remove after removing support for Node 21 highWatermark: HIGH_WATER_MARK, }); return iterateOnData({ onStdoutChunk, controller, binary, shouldEncode, encoding, shouldSplit, preserveNewlines, }); }; export const DEFAULT_OBJECT_HIGH_WATER_MARK = getDefaultHighWaterMark(true); // The `highWaterMark` of `events.on()` is measured in number of events, not in bytes. // Not knowing the average amount of bytes per `data` event, we use the same heuristic as streams in objectMode, since they have the same issue. // Therefore, we use the value of `getDefaultHighWaterMark(true)`. // Note: this option does not exist on Node 18, but this is ok since the logic works without it. It just consumes more memory. const HIGH_WATER_MARK = DEFAULT_OBJECT_HIGH_WATER_MARK; const iterateOnData = async function * ({onStdoutChunk, controller, binary, shouldEncode, encoding, shouldSplit, preserveNewlines}) { const generators = getGenerators({ binary, shouldEncode, encoding, shouldSplit, preserveNewlines, }); try { for await (const [chunk] of onStdoutChunk) { yield * transformChunkSync(chunk, generators, 0); } } catch (error) { if (!controller.signal.aborted) { throw error; } } finally { yield * finalChunksSync(generators); } }; const getGenerators = ({binary, shouldEncode, encoding, shouldSplit, preserveNewlines}) => [ getEncodingTransformGenerator(binary, encoding, !shouldEncode), getSplitLinesGenerator(binary, preserveNewlines, !shouldSplit, {}), ].filter(Boolean); ================================================ FILE: lib/io/max-buffer.js ================================================ import {MaxBufferError} from 'get-stream'; import {getStreamName} from '../utils/standard-stream.js'; import {getFdSpecificValue} from '../arguments/specific.js'; // When the `maxBuffer` option is hit, a MaxBufferError is thrown. // The stream is aborted, then specific information is kept for the error message. export const handleMaxBuffer = ({error, stream, readableObjectMode, lines, encoding, fdNumber}) => { if (!(error instanceof MaxBufferError)) { throw error; } if (fdNumber === 'all') { return error; } const unit = getMaxBufferUnit(readableObjectMode, lines, encoding); error.maxBufferInfo = {fdNumber, unit}; stream.destroy(); throw error; }; const getMaxBufferUnit = (readableObjectMode, lines, encoding) => { if (readableObjectMode) { return 'objects'; } if (lines) { return 'lines'; } if (encoding === 'buffer') { return 'bytes'; } return 'characters'; }; // Check the `maxBuffer` option with `result.ipcOutput` export const checkIpcMaxBuffer = (subprocess, ipcOutput, maxBuffer) => { if (ipcOutput.length !== maxBuffer) { return; } const error = new MaxBufferError(); error.maxBufferInfo = {fdNumber: 'ipc'}; throw error; }; // Error message when `maxBuffer` is hit export const getMaxBufferMessage = (error, maxBuffer) => { const {streamName, threshold, unit} = getMaxBufferInfo(error, maxBuffer); return `Command's ${streamName} was larger than ${threshold} ${unit}`; }; const getMaxBufferInfo = (error, maxBuffer) => { if (error?.maxBufferInfo === undefined) { return {streamName: 'output', threshold: maxBuffer[1], unit: 'bytes'}; } const {maxBufferInfo: {fdNumber, unit}} = error; delete error.maxBufferInfo; const threshold = getFdSpecificValue(maxBuffer, fdNumber); if (fdNumber === 'ipc') { return {streamName: 'IPC output', threshold, unit: 'messages'}; } return {streamName: getStreamName(fdNumber), threshold, unit}; }; // The only way to apply `maxBuffer` with `spawnSync()` is to use the native `maxBuffer` option Node.js provides. // However, this has multiple limitations, and cannot behave the exact same way as the async behavior. // When the `maxBuffer` is hit, a `ENOBUFS` error is thrown. export const isMaxBufferSync = (resultError, output, maxBuffer) => resultError?.code === 'ENOBUFS' && output !== null && output.some(result => result !== null && result.length > getMaxBufferSync(maxBuffer)); // When `maxBuffer` is hit, ensure the result is truncated export const truncateMaxBufferSync = (result, isMaxBuffer, maxBuffer) => { if (!isMaxBuffer) { return result; } const maxBufferValue = getMaxBufferSync(maxBuffer); return result.length > maxBufferValue ? result.slice(0, maxBufferValue) : result; }; // `spawnSync()` does not allow differentiating `maxBuffer` per file descriptor, so we always use `stdout` export const getMaxBufferSync = ([, stdoutMaxBuffer]) => stdoutMaxBuffer; ================================================ FILE: lib/io/output-async.js ================================================ import mergeStreams from '@sindresorhus/merge-streams'; import {isStandardStream} from '../utils/standard-stream.js'; import {incrementMaxListeners} from '../utils/max-listeners.js'; import {TRANSFORM_TYPES} from '../stdio/type.js'; import {pipeStreams} from './pipeline.js'; // Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, after spawning, in async mode // When multiple input streams are used, we merge them to ensure the output stream ends only once each input stream has ended export const pipeOutputAsync = (subprocess, fileDescriptors, controller) => { const pipeGroups = new Map(); for (const [fdNumber, {stdioItems, direction}] of Object.entries(fileDescriptors)) { for (const {stream} of stdioItems.filter(({type}) => TRANSFORM_TYPES.has(type))) { pipeTransform(subprocess, stream, direction, fdNumber); } for (const {stream} of stdioItems.filter(({type}) => !TRANSFORM_TYPES.has(type))) { pipeStdioItem({ subprocess, stream, direction, fdNumber, pipeGroups, controller, }); } } for (const [outputStream, inputStreams] of pipeGroups.entries()) { const inputStream = inputStreams.length === 1 ? inputStreams[0] : mergeStreams(inputStreams); pipeStreams(inputStream, outputStream); } }; // When using transforms, `subprocess.stdin|stdout|stderr|stdio` is directly mutated const pipeTransform = (subprocess, stream, direction, fdNumber) => { if (direction === 'output') { pipeStreams(subprocess.stdio[fdNumber], stream); } else { pipeStreams(stream, subprocess.stdio[fdNumber]); } const streamProperty = SUBPROCESS_STREAM_PROPERTIES[fdNumber]; if (streamProperty !== undefined) { subprocess[streamProperty] = stream; } subprocess.stdio[fdNumber] = stream; }; const SUBPROCESS_STREAM_PROPERTIES = ['stdin', 'stdout', 'stderr']; // Most `std*` option values involve piping `subprocess.std*` to a stream. // The stream is either passed by the user or created internally. const pipeStdioItem = ({subprocess, stream, direction, fdNumber, pipeGroups, controller}) => { if (stream === undefined) { return; } setStandardStreamMaxListeners(stream, controller); const [inputStream, outputStream] = direction === 'output' ? [stream, subprocess.stdio[fdNumber]] : [subprocess.stdio[fdNumber], stream]; const outputStreams = pipeGroups.get(inputStream) ?? []; pipeGroups.set(inputStream, [...outputStreams, outputStream]); }; // Multiple subprocesses might be piping from/to `process.std*` at the same time. // This is not necessarily an error and should not print a `maxListeners` warning. const setStandardStreamMaxListeners = (stream, {signal}) => { if (isStandardStream(stream)) { incrementMaxListeners(stream, MAX_LISTENERS_INCREMENT, signal); } }; // `source.pipe(destination)` adds at most 1 listener for each event. // If `stdin` option is an array, the values might be combined with `merge-streams`. // That library also listens for `source` end, which adds 1 more listener. const MAX_LISTENERS_INCREMENT = 2; ================================================ FILE: lib/io/output-sync.js ================================================ import {writeFileSync, appendFileSync} from 'node:fs'; import {shouldLogOutput, logLinesSync} from '../verbose/output.js'; import {runGeneratorsSync} from '../transform/generator.js'; import {splitLinesSync} from '../transform/split.js'; import {joinToString, joinToUint8Array, bufferToUint8Array} from '../utils/uint-array.js'; import {FILE_TYPES} from '../stdio/type.js'; import {truncateMaxBufferSync} from './max-buffer.js'; // Apply `stdout`/`stderr` options, after spawning, in sync mode export const transformOutputSync = ({fileDescriptors, syncResult: {output}, options, isMaxBuffer, verboseInfo}) => { if (output === null) { return {output: Array.from({length: 3})}; } const state = {}; const outputFiles = new Set([]); const transformedOutput = output.map((result, fdNumber) => transformOutputResultSync({ result, fileDescriptors, fdNumber, state, outputFiles, isMaxBuffer, verboseInfo, }, options)); return {output: transformedOutput, ...state}; }; const transformOutputResultSync = ( {result, fileDescriptors, fdNumber, state, outputFiles, isMaxBuffer, verboseInfo}, {buffer, encoding, lines, stripFinalNewline, maxBuffer}, ) => { if (result === null) { return; } const truncatedResult = truncateMaxBufferSync(result, isMaxBuffer, maxBuffer); const uint8ArrayResult = bufferToUint8Array(truncatedResult); const {stdioItems, objectMode} = fileDescriptors[fdNumber]; const chunks = runOutputGeneratorsSync([uint8ArrayResult], stdioItems, encoding, state); const {serializedResult, finalResult = serializedResult} = serializeChunks({ chunks, objectMode, encoding, lines, stripFinalNewline, fdNumber, }); logOutputSync({ serializedResult, fdNumber, state, verboseInfo, encoding, stdioItems, objectMode, }); const returnedResult = buffer[fdNumber] ? finalResult : undefined; try { if (state.error === undefined) { writeToFiles(serializedResult, stdioItems, outputFiles); } return returnedResult; } catch (error) { state.error = error; return returnedResult; } }; // Applies transform generators to `stdout`/`stderr` const runOutputGeneratorsSync = (chunks, stdioItems, encoding, state) => { try { return runGeneratorsSync(chunks, stdioItems, encoding, false); } catch (error) { state.error = error; return chunks; } }; // The contents is converted to three stages: // - serializedResult: used when the target is a file path/URL or a file descriptor (including 'inherit') // - finalResult/returnedResult: returned as `result.std*` const serializeChunks = ({chunks, objectMode, encoding, lines, stripFinalNewline, fdNumber}) => { if (objectMode) { return {serializedResult: chunks}; } if (encoding === 'buffer') { return {serializedResult: joinToUint8Array(chunks)}; } const serializedResult = joinToString(chunks, encoding); if (lines[fdNumber]) { return {serializedResult, finalResult: splitLinesSync(serializedResult, !stripFinalNewline[fdNumber], objectMode)}; } return {serializedResult}; }; const logOutputSync = ({serializedResult, fdNumber, state, verboseInfo, encoding, stdioItems, objectMode}) => { if (!shouldLogOutput({ stdioItems, encoding, verboseInfo, fdNumber, })) { return; } const linesArray = splitLinesSync(serializedResult, false, objectMode); try { logLinesSync(linesArray, fdNumber, verboseInfo); } catch (error) { state.error ??= error; } }; // When the `std*` target is a file path/URL or a file descriptor const writeToFiles = (serializedResult, stdioItems, outputFiles) => { for (const {path, append} of stdioItems.filter(({type}) => FILE_TYPES.has(type))) { const pathString = typeof path === 'string' ? path : path.toString(); if (append || outputFiles.has(pathString)) { appendFileSync(path, serializedResult); } else { outputFiles.add(pathString); writeFileSync(path, serializedResult); } } }; ================================================ FILE: lib/io/pipeline.js ================================================ import {finished} from 'node:stream/promises'; import {isStandardStream} from '../utils/standard-stream.js'; // Similar to `Stream.pipeline(source, destination)`, but does not destroy standard streams export const pipeStreams = (source, destination) => { source.pipe(destination); onSourceFinish(source, destination); onDestinationFinish(source, destination); }; // `source.pipe(destination)` makes `destination` end when `source` ends. // But it does not propagate aborts or errors. This function does it. const onSourceFinish = async (source, destination) => { if (isStandardStream(source) || isStandardStream(destination)) { return; } try { await finished(source, {cleanup: true, readable: true, writable: false}); } catch {} endDestinationStream(destination); }; export const endDestinationStream = destination => { if (destination.writable) { destination.end(); } }; // We do the same thing in the other direction as well. const onDestinationFinish = async (source, destination) => { if (isStandardStream(source) || isStandardStream(destination)) { return; } try { await finished(destination, {cleanup: true, readable: false, writable: true}); } catch {} abortSourceStream(source); }; export const abortSourceStream = source => { if (source.readable) { source.destroy(); } }; ================================================ FILE: lib/io/strip-newline.js ================================================ import stripFinalNewlineFunction from 'strip-final-newline'; // Apply `stripFinalNewline` option, which applies to `result.stdout|stderr|all|stdio[*]`. // If the `lines` option is used, it is applied on each line, but using a different function. export const stripNewline = (value, {stripFinalNewline}, fdNumber) => getStripFinalNewline(stripFinalNewline, fdNumber) && value !== undefined && !Array.isArray(value) ? stripFinalNewlineFunction(value) : value; // Retrieve `stripFinalNewline` option value, including with `subprocess.all` export const getStripFinalNewline = (stripFinalNewline, fdNumber) => fdNumber === 'all' ? stripFinalNewline[1] || stripFinalNewline[2] : stripFinalNewline[fdNumber]; ================================================ FILE: lib/ipc/array.js ================================================ // The `ipc` option adds an `ipc` item to the `stdio` option export const normalizeIpcStdioArray = (stdioArray, ipc) => ipc && !stdioArray.includes('ipc') ? [...stdioArray, 'ipc'] : stdioArray; ================================================ FILE: lib/ipc/buffer-messages.js ================================================ import {checkIpcMaxBuffer} from '../io/max-buffer.js'; import {shouldLogIpc, logIpcOutput} from '../verbose/ipc.js'; import {getFdSpecificValue} from '../arguments/specific.js'; import {loopOnMessages} from './get-each.js'; // Iterate through IPC messages sent by the subprocess export const waitForIpcOutput = async ({ subprocess, buffer: bufferArray, maxBuffer: maxBufferArray, ipc, ipcOutput, verboseInfo, }) => { if (!ipc) { return ipcOutput; } const isVerbose = shouldLogIpc(verboseInfo); const buffer = getFdSpecificValue(bufferArray, 'ipc'); const maxBuffer = getFdSpecificValue(maxBufferArray, 'ipc'); for await (const message of loopOnMessages({ anyProcess: subprocess, channel: subprocess.channel, isSubprocess: false, ipc, shouldAwait: false, reference: true, })) { if (buffer) { checkIpcMaxBuffer(subprocess, ipcOutput, maxBuffer); ipcOutput.push(message); } if (isVerbose) { logIpcOutput(message, verboseInfo); } } return ipcOutput; }; export const getBufferedIpcOutput = async (ipcOutputPromise, ipcOutput) => { await Promise.allSettled([ipcOutputPromise]); return ipcOutput; }; ================================================ FILE: lib/ipc/forward.js ================================================ import {EventEmitter} from 'node:events'; import {onMessage, onDisconnect} from './incoming.js'; import {undoAddedReferences} from './reference.js'; // Forward the `message` and `disconnect` events from the process and subprocess to a proxy emitter. // This prevents the `error` event from stopping IPC. // This also allows debouncing the `message` event. export const getIpcEmitter = (anyProcess, channel, isSubprocess) => { if (IPC_EMITTERS.has(anyProcess)) { return IPC_EMITTERS.get(anyProcess); } // Use an `EventEmitter`, like the `process` that is being proxied // eslint-disable-next-line unicorn/prefer-event-target const ipcEmitter = new EventEmitter(); ipcEmitter.connected = true; IPC_EMITTERS.set(anyProcess, ipcEmitter); forwardEvents({ ipcEmitter, anyProcess, channel, isSubprocess, }); return ipcEmitter; }; const IPC_EMITTERS = new WeakMap(); // The `message` and `disconnect` events are buffered in the subprocess until the first listener is setup. // However, unbuffering happens after one tick, so this give enough time for the caller to setup the listener on the proxy emitter first. // See https://github.com/nodejs/node/blob/2aaeaa863c35befa2ebaa98fb7737ec84df4d8e9/lib/internal/child_process.js#L721 const forwardEvents = ({ipcEmitter, anyProcess, channel, isSubprocess}) => { const boundOnMessage = onMessage.bind(undefined, { anyProcess, channel, isSubprocess, ipcEmitter, }); anyProcess.on('message', boundOnMessage); anyProcess.once('disconnect', onDisconnect.bind(undefined, { anyProcess, channel, isSubprocess, ipcEmitter, boundOnMessage, })); undoAddedReferences(channel, isSubprocess); }; // Check whether there might still be some `message` events to receive export const isConnected = anyProcess => { const ipcEmitter = IPC_EMITTERS.get(anyProcess); return ipcEmitter === undefined ? anyProcess.channel !== null : ipcEmitter.connected; }; ================================================ FILE: lib/ipc/get-each.js ================================================ import {once, on} from 'node:events'; import {validateIpcMethod, disconnect, getStrictResponseError} from './validation.js'; import {getIpcEmitter, isConnected} from './forward.js'; import {addReference, removeReference} from './reference.js'; // Like `[sub]process.on('message')` but promise-based export const getEachMessage = ({anyProcess, channel, isSubprocess, ipc}, {reference = true} = {}) => loopOnMessages({ anyProcess, channel, isSubprocess, ipc, shouldAwait: !isSubprocess, reference, }); // Same but used internally export const loopOnMessages = ({anyProcess, channel, isSubprocess, ipc, shouldAwait, reference}) => { validateIpcMethod({ methodName: 'getEachMessage', isSubprocess, ipc, isConnected: isConnected(anyProcess), }); addReference(channel, reference); const ipcEmitter = getIpcEmitter(anyProcess, channel, isSubprocess); const controller = new AbortController(); const state = {}; stopOnDisconnect(anyProcess, ipcEmitter, controller); abortOnStrictError({ ipcEmitter, isSubprocess, controller, state, }); return iterateOnMessages({ anyProcess, channel, ipcEmitter, isSubprocess, shouldAwait, controller, state, reference, }); }; const stopOnDisconnect = async (anyProcess, ipcEmitter, controller) => { try { await once(ipcEmitter, 'disconnect', {signal: controller.signal}); controller.abort(); } catch {} }; const abortOnStrictError = async ({ipcEmitter, isSubprocess, controller, state}) => { try { const [error] = await once(ipcEmitter, 'strict:error', {signal: controller.signal}); state.error = getStrictResponseError(error, isSubprocess); controller.abort(); } catch {} }; const iterateOnMessages = async function * ({anyProcess, channel, ipcEmitter, isSubprocess, shouldAwait, controller, state, reference}) { try { for await (const [message] of on(ipcEmitter, 'message', {signal: controller.signal})) { throwIfStrictError(state); yield message; } } catch { throwIfStrictError(state); } finally { controller.abort(); removeReference(channel, reference); if (!isSubprocess) { disconnect(anyProcess); } if (shouldAwait) { await anyProcess; } } }; const throwIfStrictError = ({error}) => { if (error) { throw error; } }; ================================================ FILE: lib/ipc/get-one.js ================================================ import {once, on} from 'node:events'; import { validateIpcMethod, throwOnEarlyDisconnect, disconnect, getStrictResponseError, } from './validation.js'; import {getIpcEmitter, isConnected} from './forward.js'; import {addReference, removeReference} from './reference.js'; // Like `[sub]process.once('message')` but promise-based export const getOneMessage = ({anyProcess, channel, isSubprocess, ipc}, {reference = true, filter} = {}) => { validateIpcMethod({ methodName: 'getOneMessage', isSubprocess, ipc, isConnected: isConnected(anyProcess), }); return getOneMessageAsync({ anyProcess, channel, isSubprocess, filter, reference, }); }; const getOneMessageAsync = async ({anyProcess, channel, isSubprocess, filter, reference}) => { addReference(channel, reference); const ipcEmitter = getIpcEmitter(anyProcess, channel, isSubprocess); const controller = new AbortController(); try { return await Promise.race([ getMessage(ipcEmitter, filter, controller), throwOnDisconnect(ipcEmitter, isSubprocess, controller), throwOnStrictError(ipcEmitter, isSubprocess, controller), ]); } catch (error) { disconnect(anyProcess); throw error; } finally { controller.abort(); removeReference(channel, reference); } }; const getMessage = async (ipcEmitter, filter, {signal}) => { if (filter === undefined) { const [message] = await once(ipcEmitter, 'message', {signal}); return message; } for await (const [message] of on(ipcEmitter, 'message', {signal})) { if (filter(message)) { return message; } } }; const throwOnDisconnect = async (ipcEmitter, isSubprocess, {signal}) => { await once(ipcEmitter, 'disconnect', {signal}); throwOnEarlyDisconnect(isSubprocess); }; const throwOnStrictError = async (ipcEmitter, isSubprocess, {signal}) => { const [error] = await once(ipcEmitter, 'strict:error', {signal}); throw getStrictResponseError(error, isSubprocess); }; ================================================ FILE: lib/ipc/graceful.js ================================================ import {scheduler} from 'node:timers/promises'; import {sendOneMessage} from './send.js'; import {getIpcEmitter} from './forward.js'; import {validateConnection, getAbortDisconnectError, throwOnMissingParent} from './validation.js'; // Send an IPC message so the subprocess performs a graceful termination export const sendAbort = (subprocess, message) => { const methodName = 'cancelSignal'; validateConnection(methodName, false, subprocess.connected); return sendOneMessage({ anyProcess: subprocess, methodName, isSubprocess: false, wrappedMessage: {type: GRACEFUL_CANCEL_TYPE, message}, message, }); }; // When the signal is being used, start listening for incoming messages. // Unbuffering messages takes one microtask to complete, so this must be async. export const getCancelSignal = async ({anyProcess, channel, isSubprocess, ipc}) => { await startIpc({ anyProcess, channel, isSubprocess, ipc, }); return cancelController.signal; }; const startIpc = async ({anyProcess, channel, isSubprocess, ipc}) => { if (cancelListening) { return; } cancelListening = true; if (!ipc) { throwOnMissingParent(); return; } if (channel === null) { abortOnDisconnect(); return; } getIpcEmitter(anyProcess, channel, isSubprocess); await scheduler.yield(); }; let cancelListening = false; // Reception of IPC message to perform a graceful termination export const handleAbort = wrappedMessage => { if (wrappedMessage?.type !== GRACEFUL_CANCEL_TYPE) { return false; } cancelController.abort(wrappedMessage.message); return true; }; const GRACEFUL_CANCEL_TYPE = 'execa:ipc:cancel'; // When the current process disconnects early, the subprocess `cancelSignal` is aborted. // Otherwise, the signal would never be able to be aborted later on. export const abortOnDisconnect = () => { cancelController.abort(getAbortDisconnectError()); }; const cancelController = new AbortController(); ================================================ FILE: lib/ipc/incoming.js ================================================ import {once} from 'node:events'; import {scheduler} from 'node:timers/promises'; import {waitForOutgoingMessages} from './outgoing.js'; import {redoAddedReferences} from './reference.js'; import {handleStrictRequest, handleStrictResponse} from './strict.js'; import {handleAbort, abortOnDisconnect} from './graceful.js'; // By default, Node.js buffers `message` events. // - Buffering happens when there is a `message` event is emitted but there is no handler. // - As soon as a `message` event handler is set, all buffered `message` events are emitted, emptying the buffer. // - This happens both in the current process and the subprocess. // - See https://github.com/nodejs/node/blob/501546e8f37059cd577041e23941b640d0d4d406/lib/internal/child_process.js#L719 // This is helpful. Notably, this allows sending messages to a subprocess that's still initializing. // However, it has several problems. // - This works with `events.on()` but not `events.once()` since all buffered messages are emitted at once. // For example, users cannot call `await getOneMessage()`/`getEachMessage()` multiple times in a row. // - When a user intentionally starts listening to `message` at a specific point in time, past `message` events are replayed, which might be unexpected. // - Buffering is unlimited, which might lead to an out-of-memory crash. // - This does not work well with multiple consumers. // For example, Execa consumes events with both `result.ipcOutput` and manual IPC calls like `getOneMessage()`. // Since `result.ipcOutput` reads all incoming messages, no buffering happens for manual IPC calls. // - Forgetting to setup a `message` listener, or setting it up too late, is a programming mistake. // The default behavior does not allow users to realize they made that mistake. // To solve those problems, instead of buffering messages, we debounce them. // The `message` event so it is emitted at most once per macrotask. export const onMessage = async ({anyProcess, channel, isSubprocess, ipcEmitter}, wrappedMessage) => { if (handleStrictResponse(wrappedMessage) || handleAbort(wrappedMessage)) { return; } if (!INCOMING_MESSAGES.has(anyProcess)) { INCOMING_MESSAGES.set(anyProcess, []); } const incomingMessages = INCOMING_MESSAGES.get(anyProcess); incomingMessages.push(wrappedMessage); if (incomingMessages.length > 1) { return; } while (incomingMessages.length > 0) { // eslint-disable-next-line no-await-in-loop await waitForOutgoingMessages(anyProcess, ipcEmitter, wrappedMessage); // eslint-disable-next-line no-await-in-loop await scheduler.yield(); // eslint-disable-next-line no-await-in-loop const message = await handleStrictRequest({ wrappedMessage: incomingMessages[0], anyProcess, channel, isSubprocess, ipcEmitter, }); incomingMessages.shift(); ipcEmitter.emit('message', message); ipcEmitter.emit('message:done'); } }; // If the `message` event is currently debounced, the `disconnect` event must wait for it export const onDisconnect = async ({anyProcess, channel, isSubprocess, ipcEmitter, boundOnMessage}) => { abortOnDisconnect(); const incomingMessages = INCOMING_MESSAGES.get(anyProcess); while (incomingMessages?.length > 0) { // eslint-disable-next-line no-await-in-loop await once(ipcEmitter, 'message:done'); } anyProcess.removeListener('message', boundOnMessage); redoAddedReferences(channel, isSubprocess); ipcEmitter.connected = false; ipcEmitter.emit('disconnect'); }; const INCOMING_MESSAGES = new WeakMap(); ================================================ FILE: lib/ipc/ipc-input.js ================================================ import {serialize} from 'node:v8'; // Validate the `ipcInput` option export const validateIpcInputOption = ({ipcInput, ipc, serialization}) => { if (ipcInput === undefined) { return; } if (!ipc) { throw new Error('The `ipcInput` option cannot be set unless the `ipc` option is `true`.'); } validateIpcInput[serialization](ipcInput); }; const validateAdvancedInput = ipcInput => { try { serialize(ipcInput); } catch (error) { throw new Error('The `ipcInput` option is not serializable with a structured clone.', {cause: error}); } }; const validateJsonInput = ipcInput => { try { JSON.stringify(ipcInput); } catch (error) { throw new Error('The `ipcInput` option is not serializable with JSON.', {cause: error}); } }; const validateIpcInput = { advanced: validateAdvancedInput, json: validateJsonInput, }; // When the `ipcInput` option is set, it is sent as an initial IPC message to the subprocess export const sendIpcInput = async (subprocess, ipcInput) => { if (ipcInput === undefined) { return; } await subprocess.sendMessage(ipcInput); }; ================================================ FILE: lib/ipc/methods.js ================================================ import process from 'node:process'; import {sendMessage} from './send.js'; import {getOneMessage} from './get-one.js'; import {getEachMessage} from './get-each.js'; import {getCancelSignal} from './graceful.js'; // Add promise-based IPC methods in current process export const addIpcMethods = (subprocess, {ipc}) => { Object.assign(subprocess, getIpcMethods(subprocess, false, ipc)); }; // Get promise-based IPC in the subprocess export const getIpcExport = () => { const anyProcess = process; const isSubprocess = true; const ipc = process.channel !== undefined; return { ...getIpcMethods(anyProcess, isSubprocess, ipc), getCancelSignal: getCancelSignal.bind(undefined, { anyProcess, channel: anyProcess.channel, isSubprocess, ipc, }), }; }; // Retrieve the `ipc` shared by both the current process and the subprocess const getIpcMethods = (anyProcess, isSubprocess, ipc) => ({ sendMessage: sendMessage.bind(undefined, { anyProcess, channel: anyProcess.channel, isSubprocess, ipc, }), getOneMessage: getOneMessage.bind(undefined, { anyProcess, channel: anyProcess.channel, isSubprocess, ipc, }), getEachMessage: getEachMessage.bind(undefined, { anyProcess, channel: anyProcess.channel, isSubprocess, ipc, }), }); ================================================ FILE: lib/ipc/outgoing.js ================================================ import {createDeferred} from '../utils/deferred.js'; import {getFdSpecificValue} from '../arguments/specific.js'; import {SUBPROCESS_OPTIONS} from '../arguments/fd-options.js'; import {validateStrictDeadlock} from './strict.js'; // When `sendMessage()` is ongoing, any `message` being received waits before being emitted. // This allows calling one or multiple `await sendMessage()` followed by `await getOneMessage()`/`await getEachMessage()`. // Without running into a race condition when the other process sends a response too fast, before the current process set up a listener. export const startSendMessage = (anyProcess, wrappedMessage, strict) => { if (!OUTGOING_MESSAGES.has(anyProcess)) { OUTGOING_MESSAGES.set(anyProcess, new Set()); } const outgoingMessages = OUTGOING_MESSAGES.get(anyProcess); const onMessageSent = createDeferred(); const id = strict ? wrappedMessage.id : undefined; const outgoingMessage = {onMessageSent, id}; outgoingMessages.add(outgoingMessage); return {outgoingMessages, outgoingMessage}; }; export const endSendMessage = ({outgoingMessages, outgoingMessage}) => { outgoingMessages.delete(outgoingMessage); outgoingMessage.onMessageSent.resolve(); }; // Await while `sendMessage()` is ongoing, unless there is already a `message` listener export const waitForOutgoingMessages = async (anyProcess, ipcEmitter, wrappedMessage) => { while (!hasMessageListeners(anyProcess, ipcEmitter) && OUTGOING_MESSAGES.get(anyProcess)?.size > 0) { const outgoingMessages = [...OUTGOING_MESSAGES.get(anyProcess)]; validateStrictDeadlock(outgoingMessages, wrappedMessage); // eslint-disable-next-line no-await-in-loop await Promise.all(outgoingMessages.map(({onMessageSent}) => onMessageSent)); } }; const OUTGOING_MESSAGES = new WeakMap(); // Whether any `message` listener is setup export const hasMessageListeners = (anyProcess, ipcEmitter) => ipcEmitter.listenerCount('message') > getMinListenerCount(anyProcess); // When `buffer` is `false`, we set up a `message` listener that should be ignored. // That listener is only meant to intercept `strict` acknowledgement responses. const getMinListenerCount = anyProcess => SUBPROCESS_OPTIONS.has(anyProcess) && !getFdSpecificValue(SUBPROCESS_OPTIONS.get(anyProcess).options.buffer, 'ipc') ? 1 : 0; ================================================ FILE: lib/ipc/reference.js ================================================ // By default, Node.js keeps the subprocess alive while it has a `message` or `disconnect` listener. // We replicate the same logic for the events that we proxy. // This ensures the subprocess is kept alive while `getOneMessage()` and `getEachMessage()` are ongoing. // This is not a problem with `sendMessage()` since Node.js handles that method automatically. // We do not use `anyProcess.channel.ref()` since this would prevent the automatic `.channel.refCounted()` Node.js is doing. // We keep a reference to `anyProcess.channel` since it might be `null` while `getOneMessage()` or `getEachMessage()` is still processing debounced messages. // See https://github.com/nodejs/node/blob/2aaeaa863c35befa2ebaa98fb7737ec84df4d8e9/lib/internal/child_process.js#L547 export const addReference = (channel, reference) => { if (reference) { addReferenceCount(channel); } }; const addReferenceCount = channel => { channel.refCounted(); }; export const removeReference = (channel, reference) => { if (reference) { removeReferenceCount(channel); } }; const removeReferenceCount = channel => { channel.unrefCounted(); }; // To proxy events, we setup some global listeners on the `message` and `disconnect` events. // Those should not keep the subprocess alive, so we remove the automatic counting that Node.js is doing. // See https://github.com/nodejs/node/blob/1b965270a9c273d4cf70e8808e9d28b9ada7844f/lib/child_process.js#L180 export const undoAddedReferences = (channel, isSubprocess) => { if (isSubprocess) { removeReferenceCount(channel); removeReferenceCount(channel); } }; // Reverse it during `disconnect` export const redoAddedReferences = (channel, isSubprocess) => { if (isSubprocess) { addReferenceCount(channel); addReferenceCount(channel); } }; ================================================ FILE: lib/ipc/send.js ================================================ import {promisify} from 'node:util'; import { validateIpcMethod, handleEpipeError, handleSerializationError, disconnect, } from './validation.js'; import {startSendMessage, endSendMessage} from './outgoing.js'; import {handleSendStrict, waitForStrictResponse} from './strict.js'; // Like `[sub]process.send()` but promise-based. // We do not `await subprocess` during `.sendMessage()` nor `.getOneMessage()` since those methods are transient. // Users would still need to `await subprocess` after the method is done. // Also, this would prevent `unhandledRejection` event from being emitted, making it silent. export const sendMessage = ({anyProcess, channel, isSubprocess, ipc}, message, {strict = false} = {}) => { const methodName = 'sendMessage'; validateIpcMethod({ methodName, isSubprocess, ipc, isConnected: anyProcess.connected, }); return sendMessageAsync({ anyProcess, channel, methodName, isSubprocess, message, strict, }); }; const sendMessageAsync = async ({anyProcess, channel, methodName, isSubprocess, message, strict}) => { const wrappedMessage = handleSendStrict({ anyProcess, channel, isSubprocess, message, strict, }); const outgoingMessagesState = startSendMessage(anyProcess, wrappedMessage, strict); try { await sendOneMessage({ anyProcess, methodName, isSubprocess, wrappedMessage, message, }); } catch (error) { disconnect(anyProcess); throw error; } finally { endSendMessage(outgoingMessagesState); } }; // Used internally by `cancelSignal` export const sendOneMessage = async ({anyProcess, methodName, isSubprocess, wrappedMessage, message}) => { const sendMethod = getSendMethod(anyProcess); try { await Promise.all([ waitForStrictResponse(wrappedMessage, anyProcess, isSubprocess), sendMethod(wrappedMessage), ]); } catch (error) { handleEpipeError({error, methodName, isSubprocess}); handleSerializationError({ error, methodName, isSubprocess, message, }); throw error; } }; // [sub]process.send() promisified, memoized const getSendMethod = anyProcess => { if (PROCESS_SEND_METHODS.has(anyProcess)) { return PROCESS_SEND_METHODS.get(anyProcess); } const sendMethod = promisify(anyProcess.send.bind(anyProcess)); PROCESS_SEND_METHODS.set(anyProcess, sendMethod); return sendMethod; }; const PROCESS_SEND_METHODS = new WeakMap(); ================================================ FILE: lib/ipc/strict.js ================================================ import {once} from 'node:events'; import {createDeferred} from '../utils/deferred.js'; import {incrementMaxListeners} from '../utils/max-listeners.js'; import {sendMessage} from './send.js'; import {throwOnMissingStrict, throwOnStrictDisconnect, throwOnStrictDeadlockError} from './validation.js'; import {getIpcEmitter} from './forward.js'; import {hasMessageListeners} from './outgoing.js'; // When using the `strict` option, wrap the message with metadata during `sendMessage()` export const handleSendStrict = ({anyProcess, channel, isSubprocess, message, strict}) => { if (!strict) { return message; } const ipcEmitter = getIpcEmitter(anyProcess, channel, isSubprocess); const hasListeners = hasMessageListeners(anyProcess, ipcEmitter); return { id: count++, type: REQUEST_TYPE, message, hasListeners, }; }; let count = 0n; // Handles when both processes are calling `sendMessage()` with `strict` at the same time. // If neither process is listening, this would create a deadlock. We detect it and throw. export const validateStrictDeadlock = (outgoingMessages, wrappedMessage) => { if (wrappedMessage?.type !== REQUEST_TYPE || wrappedMessage.hasListeners) { return; } for (const {id} of outgoingMessages) { if (id !== undefined) { STRICT_RESPONSES[id].resolve({isDeadlock: true, hasListeners: false}); } } }; // The other process then sends the acknowledgment back as a response export const handleStrictRequest = async ({wrappedMessage, anyProcess, channel, isSubprocess, ipcEmitter}) => { if (wrappedMessage?.type !== REQUEST_TYPE || !anyProcess.connected) { return wrappedMessage; } const {id, message} = wrappedMessage; const response = {id, type: RESPONSE_TYPE, message: hasMessageListeners(anyProcess, ipcEmitter)}; try { await sendMessage({ anyProcess, channel, isSubprocess, ipc: true, }, response); } catch (error) { ipcEmitter.emit('strict:error', error); } return message; }; // Reception of the acknowledgment response export const handleStrictResponse = wrappedMessage => { if (wrappedMessage?.type !== RESPONSE_TYPE) { return false; } const {id, message: hasListeners} = wrappedMessage; STRICT_RESPONSES[id]?.resolve({isDeadlock: false, hasListeners}); return true; }; // Wait for the other process to receive the message from `sendMessage()` export const waitForStrictResponse = async (wrappedMessage, anyProcess, isSubprocess) => { if (wrappedMessage?.type !== REQUEST_TYPE) { return; } const deferred = createDeferred(); STRICT_RESPONSES[wrappedMessage.id] = deferred; const controller = new AbortController(); try { const {isDeadlock, hasListeners} = await Promise.race([ deferred, throwOnDisconnect(anyProcess, isSubprocess, controller), ]); if (isDeadlock) { throwOnStrictDeadlockError(isSubprocess); } if (!hasListeners) { throwOnMissingStrict(isSubprocess); } } finally { controller.abort(); delete STRICT_RESPONSES[wrappedMessage.id]; } }; const STRICT_RESPONSES = {}; const throwOnDisconnect = async (anyProcess, isSubprocess, {signal}) => { incrementMaxListeners(anyProcess, 1, signal); await once(anyProcess, 'disconnect', {signal}); throwOnStrictDisconnect(isSubprocess); }; const REQUEST_TYPE = 'execa:ipc:request'; const RESPONSE_TYPE = 'execa:ipc:response'; ================================================ FILE: lib/ipc/validation.js ================================================ // Validate the IPC channel is connected before receiving/sending messages export const validateIpcMethod = ({methodName, isSubprocess, ipc, isConnected}) => { validateIpcOption(methodName, isSubprocess, ipc); validateConnection(methodName, isSubprocess, isConnected); }; // Better error message when forgetting to set `ipc: true` and using the IPC methods const validateIpcOption = (methodName, isSubprocess, ipc) => { if (!ipc) { throw new Error(`${getMethodName(methodName, isSubprocess)} can only be used if the \`ipc\` option is \`true\`.`); } }; // Better error message when one process does not send/receive messages once the other process has disconnected. // This also makes it clear that any buffered messages are lost once either process has disconnected. // Also when aborting `cancelSignal` after disconnecting the IPC. export const validateConnection = (methodName, isSubprocess, isConnected) => { if (!isConnected) { throw new Error(`${getMethodName(methodName, isSubprocess)} cannot be used: the ${getOtherProcessName(isSubprocess)} has already exited or disconnected.`); } }; // When `getOneMessage()` could not complete due to an early disconnection export const throwOnEarlyDisconnect = isSubprocess => { throw new Error(`${getMethodName('getOneMessage', isSubprocess)} could not complete: the ${getOtherProcessName(isSubprocess)} exited or disconnected.`); }; // When both processes use `sendMessage()` with `strict` at the same time export const throwOnStrictDeadlockError = isSubprocess => { throw new Error(`${getMethodName('sendMessage', isSubprocess)} failed: the ${getOtherProcessName(isSubprocess)} is sending a message too, instead of listening to incoming messages. This can be fixed by both sending a message and listening to incoming messages at the same time: const [receivedMessage] = await Promise.all([ ${getMethodName('getOneMessage', isSubprocess)}, ${getMethodName('sendMessage', isSubprocess, 'message, {strict: true}')}, ]);`); }; // When the other process used `strict` but the current process had I/O error calling `sendMessage()` for the response export const getStrictResponseError = (error, isSubprocess) => new Error(`${getMethodName('sendMessage', isSubprocess)} failed when sending an acknowledgment response to the ${getOtherProcessName(isSubprocess)}.`, {cause: error}); // When using `strict` but the other process was not listening for messages export const throwOnMissingStrict = isSubprocess => { throw new Error(`${getMethodName('sendMessage', isSubprocess)} failed: the ${getOtherProcessName(isSubprocess)} is not listening to incoming messages.`); }; // When using `strict` but the other process disconnected before receiving the message export const throwOnStrictDisconnect = isSubprocess => { throw new Error(`${getMethodName('sendMessage', isSubprocess)} failed: the ${getOtherProcessName(isSubprocess)} exited without listening to incoming messages.`); }; // When the current process disconnects while the subprocess is listening to `cancelSignal` export const getAbortDisconnectError = () => new Error(`\`cancelSignal\` aborted: the ${getOtherProcessName(true)} disconnected.`); // When the subprocess uses `cancelSignal` but not the current process export const throwOnMissingParent = () => { throw new Error('`getCancelSignal()` cannot be used without setting the `cancelSignal` subprocess option.'); }; // EPIPE can happen when sending a message to a subprocess that is closing but has not disconnected yet export const handleEpipeError = ({error, methodName, isSubprocess}) => { if (error.code === 'EPIPE') { throw new Error(`${getMethodName(methodName, isSubprocess)} cannot be used: the ${getOtherProcessName(isSubprocess)} is disconnecting.`, {cause: error}); } }; // Better error message when sending messages which cannot be serialized. // Works with both `serialization: 'advanced'` and `serialization: 'json'`. export const handleSerializationError = ({error, methodName, isSubprocess, message}) => { if (isSerializationError(error)) { throw new Error(`${getMethodName(methodName, isSubprocess)}'s argument type is invalid: the message cannot be serialized: ${String(message)}.`, {cause: error}); } }; const isSerializationError = ({code, message}) => SERIALIZATION_ERROR_CODES.has(code) || SERIALIZATION_ERROR_MESSAGES.some(serializationErrorMessage => message.includes(serializationErrorMessage)); // `error.code` set by Node.js when it failed to serialize the message const SERIALIZATION_ERROR_CODES = new Set([ // Message is `undefined` 'ERR_MISSING_ARGS', // Message is a function, a bigint, a symbol 'ERR_INVALID_ARG_TYPE', ]); // `error.message` set by Node.js when it failed to serialize the message const SERIALIZATION_ERROR_MESSAGES = [ // Message is a promise or a proxy, with `serialization: 'advanced'` 'could not be cloned', // Message has cycles, with `serialization: 'json'` 'circular structure', // Message has cycles inside toJSON(), with `serialization: 'json'` 'call stack size exceeded', ]; const getMethodName = (methodName, isSubprocess, parameters = '') => methodName === 'cancelSignal' ? '`cancelSignal`\'s `controller.abort()`' : `${getNamespaceName(isSubprocess)}${methodName}(${parameters})`; const getNamespaceName = isSubprocess => isSubprocess ? '' : 'subprocess.'; const getOtherProcessName = isSubprocess => isSubprocess ? 'parent process' : 'subprocess'; // When any error arises, we disconnect the IPC. // Otherwise, it is likely that one of the processes will stop sending/receiving messages. // This would leave the other process hanging. export const disconnect = anyProcess => { if (anyProcess.connected) { anyProcess.disconnect(); } }; ================================================ FILE: lib/methods/bind.js ================================================ import isPlainObject from 'is-plain-obj'; import {FD_SPECIFIC_OPTIONS} from '../arguments/specific.js'; // Deep merge specific options like `env`. Shallow merge the other ones. // Use spread (which only copies own properties) to safely read from boundOptions without prototype pollution export const mergeOptions = (boundOptions, options) => { const safeBoundOptions = {__proto__: null, ...boundOptions}; const mergedOptions = Object.fromEntries( Object.entries(options).map(([optionName, optionValue]) => [ optionName, mergeOption(optionName, safeBoundOptions[optionName], optionValue), ]), ); return {...safeBoundOptions, ...mergedOptions}; }; const mergeOption = (optionName, boundOptionValue, optionValue) => { if (DEEP_OPTIONS.has(optionName) && isPlainObject(boundOptionValue) && isPlainObject(optionValue)) { return {...boundOptionValue, ...optionValue}; } return optionValue; }; const DEEP_OPTIONS = new Set(['env', ...FD_SPECIFIC_OPTIONS]); ================================================ FILE: lib/methods/command.js ================================================ // Main logic for `execaCommand()` export const mapCommandAsync = ({file, commandArguments}) => parseCommand(file, commandArguments); // Main logic for `execaCommandSync()` export const mapCommandSync = ({file, commandArguments}) => ({...parseCommand(file, commandArguments), isSync: true}); // Convert `execaCommand(command)` into `execa(file, ...commandArguments)` const parseCommand = (command, unusedArguments) => { if (unusedArguments.length > 0) { throw new TypeError(`The command and its arguments must be passed as a single string: ${command} ${unusedArguments}.`); } const [file, ...commandArguments] = parseCommandString(command); return {file, commandArguments}; }; // Convert `command` string into an array of file or arguments to pass to $`${...fileOrCommandArguments}` export const parseCommandString = command => { if (typeof command !== 'string') { throw new TypeError(`The command must be a string: ${String(command)}.`); } const trimmedCommand = command.trim(); if (trimmedCommand === '') { return []; } const tokens = []; for (const token of trimmedCommand.split(SPACES_REGEXP)) { // Allow spaces to be escaped by a backslash if not meant as a delimiter const previousToken = tokens.at(-1); if (previousToken && previousToken.endsWith('\\')) { // Merge previous token with current one tokens[tokens.length - 1] = `${previousToken.slice(0, -1)} ${token}`; } else { tokens.push(token); } } return tokens; }; const SPACES_REGEXP = / +/g; ================================================ FILE: lib/methods/create.js ================================================ import isPlainObject from 'is-plain-obj'; import {normalizeParameters} from './parameters.js'; import {isTemplateString, parseTemplates} from './template.js'; import {execaCoreSync} from './main-sync.js'; import {execaCoreAsync} from './main-async.js'; import {mergeOptions} from './bind.js'; // Wraps every exported methods to provide the following features: // - template string syntax: execa`command argument` // - options binding: boundExeca = execa(options) // - optional argument/options: execa(file), execa(file, args), execa(file, options), execa(file, args, options) // `mapArguments()` and `setBoundExeca()` allows for method-specific logic. export const createExeca = (mapArguments, boundOptions, deepOptions, setBoundExeca) => { const createNested = (mapArguments, boundOptions, setBoundExeca) => createExeca(mapArguments, boundOptions, deepOptions, setBoundExeca); const boundExeca = (...execaArguments) => callBoundExeca({ mapArguments, deepOptions, boundOptions, setBoundExeca, createNested, }, ...execaArguments); if (setBoundExeca !== undefined) { setBoundExeca(boundExeca, createNested, boundOptions); } return boundExeca; }; const callBoundExeca = ({mapArguments, deepOptions = {}, boundOptions = {}, setBoundExeca, createNested}, firstArgument, ...nextArguments) => { if (isPlainObject(firstArgument)) { return createNested(mapArguments, mergeOptions(boundOptions, firstArgument), setBoundExeca); } const {file, commandArguments, options, isSync} = parseArguments({ mapArguments, firstArgument, nextArguments, deepOptions, boundOptions, }); return isSync ? execaCoreSync(file, commandArguments, options) : execaCoreAsync(file, commandArguments, options, createNested); }; const parseArguments = ({mapArguments, firstArgument, nextArguments, deepOptions, boundOptions}) => { const callArguments = isTemplateString(firstArgument) ? parseTemplates(firstArgument, nextArguments) : [firstArgument, ...nextArguments]; const [initialFile, initialArguments, initialOptions] = normalizeParameters(...callArguments); const mergedOptions = mergeOptions(mergeOptions(deepOptions, boundOptions), initialOptions); const { file = initialFile, commandArguments = initialArguments, options = mergedOptions, isSync = false, } = mapArguments({file: initialFile, commandArguments: initialArguments, options: mergedOptions}); return { file, commandArguments, options, isSync, }; }; ================================================ FILE: lib/methods/main-async.js ================================================ import {setMaxListeners} from 'node:events'; import {spawn} from 'node:child_process'; import {MaxBufferError} from 'get-stream'; import {handleCommand} from '../arguments/command.js'; import {normalizeOptions} from '../arguments/options.js'; import {SUBPROCESS_OPTIONS} from '../arguments/fd-options.js'; import {concatenateShell} from '../arguments/shell.js'; import {addIpcMethods} from '../ipc/methods.js'; import {makeError, makeSuccessResult} from '../return/result.js'; import {handleResult} from '../return/reject.js'; import {handleEarlyError} from '../return/early-error.js'; import {handleStdioAsync} from '../stdio/handle-async.js'; import {stripNewline} from '../io/strip-newline.js'; import {pipeOutputAsync} from '../io/output-async.js'; import {subprocessKill} from '../terminate/kill.js'; import {cleanupOnExit} from '../terminate/cleanup.js'; import {pipeToSubprocess} from '../pipe/setup.js'; import {makeAllStream} from '../resolve/all-async.js'; import {waitForSubprocessResult} from '../resolve/wait-subprocess.js'; import {addConvertedStreams} from '../convert/add.js'; import {createDeferred} from '../utils/deferred.js'; import {mergePromise} from './promise.js'; // Main shared logic for all async methods: `execa()`, `$`, `execaNode()` export const execaCoreAsync = (rawFile, rawArguments, rawOptions, createNested) => { const {file, commandArguments, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors} = handleAsyncArguments(rawFile, rawArguments, rawOptions); const {subprocess, promise} = spawnSubprocessAsync({ file, commandArguments, options, startTime, verboseInfo, command, escapedCommand, fileDescriptors, }); subprocess.pipe = pipeToSubprocess.bind(undefined, { source: subprocess, sourcePromise: promise, boundOptions: {}, createNested, }); mergePromise(subprocess, promise); SUBPROCESS_OPTIONS.set(subprocess, {options, fileDescriptors}); return subprocess; }; // Compute arguments to pass to `child_process.spawn()` const handleAsyncArguments = (rawFile, rawArguments, rawOptions) => { const {command, escapedCommand, startTime, verboseInfo} = handleCommand(rawFile, rawArguments, rawOptions); const {file, commandArguments, options: normalizedOptions} = normalizeOptions(rawFile, rawArguments, rawOptions); const options = handleAsyncOptions(normalizedOptions); const fileDescriptors = handleStdioAsync(options, verboseInfo); return { file, commandArguments, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors, }; }; // Options normalization logic specific to async methods. // Prevent passing the `timeout` option directly to `child_process.spawn()`. const handleAsyncOptions = ({timeout, signal, ...options}) => { if (signal !== undefined) { throw new TypeError('The "signal" option has been renamed to "cancelSignal" instead.'); } return {...options, timeoutDuration: timeout}; }; const spawnSubprocessAsync = ({file, commandArguments, options, startTime, verboseInfo, command, escapedCommand, fileDescriptors}) => { let subprocess; try { subprocess = spawn(...concatenateShell(file, commandArguments, options)); } catch (error) { return handleEarlyError({ error, command, escapedCommand, fileDescriptors, options, startTime, verboseInfo, }); } const controller = new AbortController(); setMaxListeners(Number.POSITIVE_INFINITY, controller.signal); const originalStreams = [...subprocess.stdio]; pipeOutputAsync(subprocess, fileDescriptors, controller); cleanupOnExit(subprocess, options, controller); const context = {}; const onInternalError = createDeferred(); subprocess.kill = subprocessKill.bind(undefined, { kill: subprocess.kill.bind(subprocess), options, onInternalError, context, controller, }); subprocess.all = makeAllStream(subprocess, options); addConvertedStreams(subprocess, options); addIpcMethods(subprocess, options); const promise = handlePromise({ subprocess, options, startTime, verboseInfo, fileDescriptors, originalStreams, command, escapedCommand, context, onInternalError, controller, }); return {subprocess, promise}; }; // Asynchronous logic, as opposed to the previous logic which can be run synchronously, i.e. can be returned to user right away const handlePromise = async ({subprocess, options, startTime, verboseInfo, fileDescriptors, originalStreams, command, escapedCommand, context, onInternalError, controller}) => { const [ errorInfo, [exitCode, signal], stdioResults, allResult, ipcOutput, ] = await waitForSubprocessResult({ subprocess, options, context, verboseInfo, fileDescriptors, originalStreams, onInternalError, controller, }); controller.abort(); onInternalError.resolve(); const stdio = stdioResults.map((stdioResult, fdNumber) => stripNewline(stdioResult, options, fdNumber)); const all = stripNewline(allResult, options, 'all'); const result = getAsyncResult({ errorInfo, exitCode, signal, stdio, all, ipcOutput, context, options, command, escapedCommand, startTime, }); return handleResult(result, verboseInfo, options); }; const getAsyncResult = ({errorInfo, exitCode, signal, stdio, all, ipcOutput, context, options, command, escapedCommand, startTime}) => 'error' in errorInfo ? makeError({ error: errorInfo.error, command, escapedCommand, timedOut: context.terminationReason === 'timeout', isCanceled: context.terminationReason === 'cancel' || context.terminationReason === 'gracefulCancel', isGracefullyCanceled: context.terminationReason === 'gracefulCancel', isMaxBuffer: errorInfo.error instanceof MaxBufferError, isForcefullyTerminated: context.isForcefullyTerminated, exitCode, signal, stdio, all, ipcOutput, options, startTime, isSync: false, }) : makeSuccessResult({ command, escapedCommand, stdio, all, ipcOutput, options, startTime, }); ================================================ FILE: lib/methods/main-sync.js ================================================ import {spawnSync} from 'node:child_process'; import {handleCommand} from '../arguments/command.js'; import {normalizeOptions} from '../arguments/options.js'; import {concatenateShell} from '../arguments/shell.js'; import {makeError, makeEarlyError, makeSuccessResult} from '../return/result.js'; import {handleResult} from '../return/reject.js'; import {handleStdioSync} from '../stdio/handle-sync.js'; import {stripNewline} from '../io/strip-newline.js'; import {addInputOptionsSync} from '../io/input-sync.js'; import {transformOutputSync} from '../io/output-sync.js'; import {getMaxBufferSync} from '../io/max-buffer.js'; import {getAllSync} from '../resolve/all-sync.js'; import {getExitResultSync} from '../resolve/exit-sync.js'; // Main shared logic for all sync methods: `execaSync()`, `$.sync()` export const execaCoreSync = (rawFile, rawArguments, rawOptions) => { const {file, commandArguments, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors} = handleSyncArguments(rawFile, rawArguments, rawOptions); const result = spawnSubprocessSync({ file, commandArguments, options, command, escapedCommand, verboseInfo, fileDescriptors, startTime, }); return handleResult(result, verboseInfo, options); }; // Compute arguments to pass to `child_process.spawnSync()` const handleSyncArguments = (rawFile, rawArguments, rawOptions) => { const {command, escapedCommand, startTime, verboseInfo} = handleCommand(rawFile, rawArguments, rawOptions); const syncOptions = normalizeSyncOptions(rawOptions); const {file, commandArguments, options} = normalizeOptions(rawFile, rawArguments, syncOptions); validateSyncOptions(options); const fileDescriptors = handleStdioSync(options, verboseInfo); return { file, commandArguments, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors, }; }; // Options normalization logic specific to sync methods const normalizeSyncOptions = options => options.node && !options.ipc ? {...options, ipc: false} : options; // Options validation logic specific to sync methods const validateSyncOptions = ({ipc, ipcInput, detached, cancelSignal}) => { if (ipcInput) { throwInvalidSyncOption('ipcInput'); } if (ipc) { throwInvalidSyncOption('ipc: true'); } if (detached) { throwInvalidSyncOption('detached: true'); } if (cancelSignal) { throwInvalidSyncOption('cancelSignal'); } }; const throwInvalidSyncOption = value => { throw new TypeError(`The "${value}" option cannot be used with synchronous methods.`); }; const spawnSubprocessSync = ({file, commandArguments, options, command, escapedCommand, verboseInfo, fileDescriptors, startTime}) => { const syncResult = runSubprocessSync({ file, commandArguments, options, command, escapedCommand, fileDescriptors, startTime, }); if (syncResult.failed) { return syncResult; } const {resultError, exitCode, signal, timedOut, isMaxBuffer} = getExitResultSync(syncResult, options); const {output, error = resultError} = transformOutputSync({ fileDescriptors, syncResult, options, isMaxBuffer, verboseInfo, }); const stdio = output.map((stdioOutput, fdNumber) => stripNewline(stdioOutput, options, fdNumber)); const all = stripNewline(getAllSync(output, options), options, 'all'); return getSyncResult({ error, exitCode, signal, timedOut, isMaxBuffer, stdio, all, options, command, escapedCommand, startTime, }); }; const runSubprocessSync = ({file, commandArguments, options, command, escapedCommand, fileDescriptors, startTime}) => { try { addInputOptionsSync(fileDescriptors, options); const normalizedOptions = normalizeSpawnSyncOptions(options); return spawnSync(...concatenateShell(file, commandArguments, normalizedOptions)); } catch (error) { return makeEarlyError({ error, command, escapedCommand, fileDescriptors, options, startTime, isSync: true, }); } }; // The `encoding` option is handled by Execa, not by `child_process.spawnSync()` const normalizeSpawnSyncOptions = ({encoding, maxBuffer, ...options}) => ({...options, encoding: 'buffer', maxBuffer: getMaxBufferSync(maxBuffer)}); const getSyncResult = ({error, exitCode, signal, timedOut, isMaxBuffer, stdio, all, options, command, escapedCommand, startTime}) => error === undefined ? makeSuccessResult({ command, escapedCommand, stdio, all, ipcOutput: [], options, startTime, }) : makeError({ error, command, escapedCommand, timedOut, isCanceled: false, isGracefullyCanceled: false, isMaxBuffer, isForcefullyTerminated: false, exitCode, signal, stdio, all, ipcOutput: [], options, startTime, isSync: true, }); ================================================ FILE: lib/methods/node.js ================================================ import {execPath, execArgv} from 'node:process'; import path from 'node:path'; import {safeNormalizeFileUrl} from '../arguments/file-url.js'; // `execaNode()` is a shortcut for `execa(..., {node: true})` export const mapNode = ({options}) => { if (options.node === false) { throw new TypeError('The "node" option cannot be false with `execaNode()`.'); } return {options: {...options, node: true}}; }; // Applies the `node: true` option, and the related `nodePath`/`nodeOptions` options. // Modifies the file commands/arguments to ensure the same Node binary and flags are re-used. // Also adds `ipc: true` and `shell: false`. export const handleNodeOption = (file, commandArguments, { node: shouldHandleNode = false, nodePath = execPath, nodeOptions = execArgv.filter(nodeOption => !nodeOption.startsWith('--inspect')), cwd, execPath: formerNodePath, ...options }) => { if (formerNodePath !== undefined) { throw new TypeError('The "execPath" option has been removed. Please use the "nodePath" option instead.'); } const normalizedNodePath = safeNormalizeFileUrl(nodePath, 'The "nodePath" option'); const resolvedNodePath = path.resolve(cwd, normalizedNodePath); // Use spread (which only copies own properties) to safely get shell without reading polluted prototype const newOptions = { __proto__: null, shell: false, ...options, nodePath: resolvedNodePath, node: shouldHandleNode, cwd, }; if (!shouldHandleNode) { return [file, commandArguments, newOptions]; } if (path.basename(file, '.exe') === 'node') { throw new TypeError('When the "node" option is true, the first argument does not need to be "node".'); } return [ resolvedNodePath, [ ...nodeOptions, file, ...commandArguments, ], { __proto__: null, ipc: true, ...newOptions, shell: false, }, ]; }; ================================================ FILE: lib/methods/parameters.js ================================================ import isPlainObject from 'is-plain-obj'; import {safeNormalizeFileUrl} from '../arguments/file-url.js'; // The command `arguments` and `options` are both optional. // This also does basic validation on them and on the command file. export const normalizeParameters = (rawFile, rawArguments = [], rawOptions = {}) => { const filePath = safeNormalizeFileUrl(rawFile, 'First argument'); const [commandArguments, options] = isPlainObject(rawArguments) ? [[], rawArguments] : [rawArguments, rawOptions]; if (!Array.isArray(commandArguments)) { throw new TypeError(`Second argument must be either an array of arguments or an options object: ${commandArguments}`); } if (commandArguments.some(commandArgument => typeof commandArgument === 'object' && commandArgument !== null)) { throw new TypeError(`Second argument must be an array of strings: ${commandArguments}`); } const normalizedArguments = commandArguments.map(String); const nullByteArgument = normalizedArguments.find(normalizedArgument => normalizedArgument.includes('\0')); if (nullByteArgument !== undefined) { throw new TypeError(`Arguments cannot contain null bytes ("\\0"): ${nullByteArgument}`); } if (!isPlainObject(options)) { throw new TypeError(`Last argument must be an options object: ${options}`); } // Prevent prototype pollution by copying only own properties to a null-prototype object return [filePath, normalizedArguments, {__proto__: null, ...options}]; }; ================================================ FILE: lib/methods/promise.js ================================================ // The return value is a mixin of `subprocess` and `Promise` export const mergePromise = (subprocess, promise) => { for (const [property, descriptor] of descriptors) { const value = descriptor.value.bind(promise); Reflect.defineProperty(subprocess, property, {...descriptor, value}); } }; // eslint-disable-next-line unicorn/prefer-top-level-await const nativePromisePrototype = (async () => {})().constructor.prototype; const descriptors = ['then', 'catch', 'finally'].map(property => [ property, Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property), ]); ================================================ FILE: lib/methods/script.js ================================================ // Sets `$.sync` and `$.s` export const setScriptSync = (boundExeca, createNested, boundOptions) => { boundExeca.sync = createNested(mapScriptSync, boundOptions); boundExeca.s = boundExeca.sync; }; // Main logic for `$` export const mapScriptAsync = ({options}) => getScriptOptions(options); // Main logic for `$.sync` const mapScriptSync = ({options}) => ({...getScriptOptions(options), isSync: true}); // `$` is like `execa` but with script-friendly options: `{stdin: 'inherit', preferLocal: true}` const getScriptOptions = options => ({options: {...getScriptStdinOption(options), ...options}}); const getScriptStdinOption = ({input, inputFile, stdio}) => input === undefined && inputFile === undefined && stdio === undefined ? {stdin: 'inherit'} : {}; // When using $(...).pipe(...), most script-friendly options should apply to both commands. // However, some options (like `stdin: 'inherit'`) would create issues with piping, i.e. cannot be deep. export const deepScriptOptions = {preferLocal: true}; ================================================ FILE: lib/methods/template.js ================================================ import {ChildProcess} from 'node:child_process'; import isPlainObject from 'is-plain-obj'; import {isUint8Array, uint8ArrayToString} from '../utils/uint-array.js'; // Check whether the template string syntax is being used export const isTemplateString = templates => Array.isArray(templates) && Array.isArray(templates.raw); // Convert execa`file ...commandArguments` to execa(file, commandArguments) export const parseTemplates = (templates, expressions) => { let tokens = []; for (const [index, template] of templates.entries()) { tokens = parseTemplate({ templates, expressions, tokens, index, template, }); } if (tokens.length === 0) { throw new TypeError('Template script must not be empty'); } const [file, ...commandArguments] = tokens; return [file, commandArguments, {}]; }; const parseTemplate = ({templates, expressions, tokens, index, template}) => { if (template === undefined) { throw new TypeError(`Invalid backslash sequence: ${templates.raw[index]}`); } const {nextTokens, leadingWhitespaces, trailingWhitespaces} = splitByWhitespaces(template, templates.raw[index]); const newTokens = concatTokens(tokens, nextTokens, leadingWhitespaces); if (index === expressions.length) { return newTokens; } const expression = expressions[index]; const expressionTokens = Array.isArray(expression) ? expression.map(expression => parseExpression(expression)) : [parseExpression(expression)]; return concatTokens(newTokens, expressionTokens, trailingWhitespaces); }; // Like `string.split(/[ \t\r\n]+/)` except newlines and tabs are: // - ignored when input as a backslash sequence like: `echo foo\n bar` // - not ignored when input directly // The only way to distinguish those in JavaScript is to use a tagged template and compare: // - the first array argument, which does not escape backslash sequences // - its `raw` property, which escapes them const splitByWhitespaces = (template, rawTemplate) => { if (rawTemplate.length === 0) { return {nextTokens: [], leadingWhitespaces: false, trailingWhitespaces: false}; } const nextTokens = []; let templateStart = 0; const leadingWhitespaces = DELIMITERS.has(rawTemplate[0]); for ( let templateIndex = 0, rawIndex = 0; templateIndex < template.length; templateIndex += 1, rawIndex += 1 ) { const rawCharacter = rawTemplate[rawIndex]; if (DELIMITERS.has(rawCharacter)) { if (templateStart !== templateIndex) { nextTokens.push(template.slice(templateStart, templateIndex)); } templateStart = templateIndex + 1; } else if (rawCharacter === '\\') { const nextRawCharacter = rawTemplate[rawIndex + 1]; if (nextRawCharacter === '\n') { // Handles escaped newlines in templates templateIndex -= 1; rawIndex += 1; } else if (nextRawCharacter === 'u' && rawTemplate[rawIndex + 2] === '{') { rawIndex = rawTemplate.indexOf('}', rawIndex + 3); } else { rawIndex += ESCAPE_LENGTH[nextRawCharacter] ?? 1; } } } const trailingWhitespaces = templateStart === template.length; if (!trailingWhitespaces) { nextTokens.push(template.slice(templateStart)); } return {nextTokens, leadingWhitespaces, trailingWhitespaces}; }; const DELIMITERS = new Set([' ', '\t', '\r', '\n']); // Number of characters in backslash escape sequences: \0 \xXX or \uXXXX // \cX is allowed in RegExps but not in strings // Octal sequences are not allowed in strict mode const ESCAPE_LENGTH = {x: 3, u: 5}; const concatTokens = (tokens, nextTokens, isSeparated) => isSeparated || tokens.length === 0 || nextTokens.length === 0 ? [...tokens, ...nextTokens] : [ ...tokens.slice(0, -1), `${tokens.at(-1)}${nextTokens[0]}`, ...nextTokens.slice(1), ]; // Handle `${expression}` inside the template string syntax const parseExpression = expression => { const typeOfExpression = typeof expression; if (typeOfExpression === 'string') { return expression; } if (typeOfExpression === 'number') { return String(expression); } if (isPlainObject(expression) && ('stdout' in expression || 'isMaxBuffer' in expression)) { return getSubprocessResult(expression); } if (expression instanceof ChildProcess || Object.prototype.toString.call(expression) === '[object Promise]') { // eslint-disable-next-line no-template-curly-in-string throw new TypeError('Unexpected subprocess in template expression. Please use ${await subprocess} instead of ${subprocess}.'); } throw new TypeError(`Unexpected "${typeOfExpression}" in template expression`); }; const getSubprocessResult = ({stdout}) => { if (typeof stdout === 'string') { return stdout; } if (isUint8Array(stdout)) { return uint8ArrayToString(stdout); } if (stdout === undefined) { throw new TypeError('Missing result.stdout in template expression. This is probably due to the previous subprocess\' "stdout" option.'); } throw new TypeError(`Unexpected "${typeof stdout}" stdout in template expression`); }; ================================================ FILE: lib/pipe/abort.js ================================================ import {aborted} from 'node:util'; import {createNonCommandError} from './throw.js'; // When passing an `unpipeSignal` option, abort piping when the signal is aborted. // However, do not terminate the subprocesses. export const unpipeOnAbort = (unpipeSignal, unpipeContext) => unpipeSignal === undefined ? [] : [unpipeOnSignalAbort(unpipeSignal, unpipeContext)]; const unpipeOnSignalAbort = async (unpipeSignal, {sourceStream, mergedStream, fileDescriptors, sourceOptions, startTime}) => { await aborted(unpipeSignal, sourceStream); await mergedStream.remove(sourceStream); const error = new Error('Pipe canceled by `unpipeSignal` option.'); throw createNonCommandError({ error, fileDescriptors, sourceOptions, startTime, }); }; ================================================ FILE: lib/pipe/pipe-arguments.js ================================================ import {normalizeParameters} from '../methods/parameters.js'; import {getStartTime} from '../return/duration.js'; import {SUBPROCESS_OPTIONS, getToStream, getFromStream} from '../arguments/fd-options.js'; import {isDenoExecPath} from '../arguments/file-url.js'; // Normalize and validate arguments passed to `source.pipe(destination)` export const normalizePipeArguments = ({source, sourcePromise, boundOptions, createNested}, ...pipeArguments) => { const startTime = getStartTime(); const { destination, destinationStream, destinationError, from, unpipeSignal, } = getDestinationStream(boundOptions, createNested, pipeArguments); const {sourceStream, sourceError} = getSourceStream(source, from); const {options: sourceOptions, fileDescriptors} = SUBPROCESS_OPTIONS.get(source); return { sourcePromise, sourceStream, sourceOptions, sourceError, destination, destinationStream, destinationError, unpipeSignal, fileDescriptors, startTime, }; }; const getDestinationStream = (boundOptions, createNested, pipeArguments) => { try { const { destination, pipeOptions: {from, to, unpipeSignal} = {}, } = getDestination(boundOptions, createNested, ...pipeArguments); const destinationStream = getToStream(destination, to); return { destination, destinationStream, from, unpipeSignal, }; } catch (error) { return {destinationError: error}; } }; // Piping subprocesses can use three syntaxes: // - source.pipe('command', commandArguments, pipeOptionsOrDestinationOptions) // - source.pipe`command commandArgument` or source.pipe(pipeOptionsOrDestinationOptions)`command commandArgument` // - source.pipe(execa(...), pipeOptions) const getDestination = (boundOptions, createNested, firstArgument, ...pipeArguments) => { if (Array.isArray(firstArgument)) { const destination = createNested(mapDestinationArguments, boundOptions)(firstArgument, ...pipeArguments); return {destination, pipeOptions: boundOptions}; } if (typeof firstArgument === 'string' || firstArgument instanceof URL || isDenoExecPath(firstArgument)) { if (Object.keys(boundOptions).length > 0) { throw new TypeError('Please use .pipe("file", ..., options) or .pipe(execa("file", ..., options)) instead of .pipe(options)("file", ...).'); } const [rawFile, rawArguments, rawOptions] = normalizeParameters(firstArgument, ...pipeArguments); const destination = createNested(mapDestinationArguments)(rawFile, rawArguments, rawOptions); return {destination, pipeOptions: rawOptions}; } if (SUBPROCESS_OPTIONS.has(firstArgument)) { if (Object.keys(boundOptions).length > 0) { throw new TypeError('Please use .pipe(options)`command` or .pipe($(options)`command`) instead of .pipe(options)($`command`).'); } return {destination: firstArgument, pipeOptions: pipeArguments[0]}; } throw new TypeError(`The first argument must be a template string, an options object, or an Execa subprocess: ${firstArgument}`); }; // Force `stdin: 'pipe'` with the destination subprocess const mapDestinationArguments = ({options}) => ({options: {...options, stdin: 'pipe', piped: true}}); const getSourceStream = (source, from) => { try { const sourceStream = getFromStream(source, from); return {sourceStream}; } catch (error) { return {sourceError: error}; } }; ================================================ FILE: lib/pipe/sequence.js ================================================ // Like Bash, we await both subprocesses. This is unlike some other shells which only await the destination subprocess. // Like Bash with the `pipefail` option, if either subprocess fails, the whole pipe fails. // Like Bash, if both subprocesses fail, we return the failure of the destination. // This ensures both subprocesses' errors are present, using `error.pipedFrom`. export const waitForBothSubprocesses = async subprocessPromises => { const [ {status: sourceStatus, reason: sourceReason, value: sourceResult = sourceReason}, {status: destinationStatus, reason: destinationReason, value: destinationResult = destinationReason}, ] = await subprocessPromises; if (!destinationResult.pipedFrom.includes(sourceResult)) { destinationResult.pipedFrom.push(sourceResult); } if (destinationStatus === 'rejected') { throw destinationResult; } if (sourceStatus === 'rejected') { throw sourceResult; } return destinationResult; }; ================================================ FILE: lib/pipe/setup.js ================================================ import isPlainObject from 'is-plain-obj'; import {normalizePipeArguments} from './pipe-arguments.js'; import {handlePipeArgumentsError} from './throw.js'; import {waitForBothSubprocesses} from './sequence.js'; import {pipeSubprocessStream} from './streaming.js'; import {unpipeOnAbort} from './abort.js'; // Pipe a subprocess' `stdout`/`stderr`/`stdio` into another subprocess' `stdin` export const pipeToSubprocess = (sourceInfo, ...pipeArguments) => { if (isPlainObject(pipeArguments[0])) { return pipeToSubprocess.bind(undefined, { ...sourceInfo, boundOptions: {...sourceInfo.boundOptions, ...pipeArguments[0]}, }); } const {destination, ...normalizedInfo} = normalizePipeArguments(sourceInfo, ...pipeArguments); const promise = handlePipePromise({...normalizedInfo, destination}); promise.pipe = pipeToSubprocess.bind(undefined, { ...sourceInfo, source: destination, sourcePromise: promise, boundOptions: {}, }); return promise; }; // Asynchronous logic when piping subprocesses const handlePipePromise = async ({ sourcePromise, sourceStream, sourceOptions, sourceError, destination, destinationStream, destinationError, unpipeSignal, fileDescriptors, startTime, }) => { const subprocessPromises = getSubprocessPromises(sourcePromise, destination); handlePipeArgumentsError({ sourceStream, sourceError, destinationStream, destinationError, fileDescriptors, sourceOptions, startTime, }); const maxListenersController = new AbortController(); try { const mergedStream = pipeSubprocessStream(sourceStream, destinationStream, maxListenersController); return await Promise.race([ waitForBothSubprocesses(subprocessPromises), ...unpipeOnAbort(unpipeSignal, { sourceStream, mergedStream, sourceOptions, fileDescriptors, startTime, }), ]); } finally { maxListenersController.abort(); } }; // `.pipe()` awaits the subprocess promises. // When invalid arguments are passed to `.pipe()`, we throw an error, which prevents awaiting them. // We need to ensure this does not create unhandled rejections. const getSubprocessPromises = (sourcePromise, destination) => Promise.allSettled([sourcePromise, destination]); ================================================ FILE: lib/pipe/streaming.js ================================================ import {finished} from 'node:stream/promises'; import mergeStreams from '@sindresorhus/merge-streams'; import {incrementMaxListeners} from '../utils/max-listeners.js'; import {pipeStreams} from '../io/pipeline.js'; // The piping behavior is like Bash. // In particular, when one subprocess exits, the other is not terminated by a signal. // Instead, its stdout (for the source) or stdin (for the destination) closes. // If the subprocess uses it, it will make it error with SIGPIPE or EPIPE (for the source) or end (for the destination). // If it does not use it, it will continue running. // This allows for subprocesses to gracefully exit and lower the coupling between subprocesses. export const pipeSubprocessStream = (sourceStream, destinationStream, maxListenersController) => { const mergedStream = MERGED_STREAMS.has(destinationStream) ? pipeMoreSubprocessStream(sourceStream, destinationStream) : pipeFirstSubprocessStream(sourceStream, destinationStream); incrementMaxListeners(sourceStream, SOURCE_LISTENERS_PER_PIPE, maxListenersController.signal); incrementMaxListeners(destinationStream, DESTINATION_LISTENERS_PER_PIPE, maxListenersController.signal); cleanupMergedStreamsMap(destinationStream); return mergedStream; }; // We use `merge-streams` to allow for multiple sources to pipe to the same destination. const pipeFirstSubprocessStream = (sourceStream, destinationStream) => { const mergedStream = mergeStreams([sourceStream]); pipeStreams(mergedStream, destinationStream); MERGED_STREAMS.set(destinationStream, mergedStream); return mergedStream; }; const pipeMoreSubprocessStream = (sourceStream, destinationStream) => { const mergedStream = MERGED_STREAMS.get(destinationStream); mergedStream.add(sourceStream); return mergedStream; }; const cleanupMergedStreamsMap = async destinationStream => { try { await finished(destinationStream, {cleanup: true, readable: false, writable: true}); } catch {} MERGED_STREAMS.delete(destinationStream); }; const MERGED_STREAMS = new WeakMap(); // Number of listeners set up on `sourceStream` by each `sourceStream.pipe(destinationStream)` // Those are added by `merge-streams` const SOURCE_LISTENERS_PER_PIPE = 2; // Number of listeners set up on `destinationStream` by each `sourceStream.pipe(destinationStream)` // Those are added by `finished()` in `cleanupMergedStreamsMap()` const DESTINATION_LISTENERS_PER_PIPE = 1; ================================================ FILE: lib/pipe/throw.js ================================================ import {makeEarlyError} from '../return/result.js'; import {abortSourceStream, endDestinationStream} from '../io/pipeline.js'; // When passing invalid arguments to `source.pipe()`, throw asynchronously. // We also abort both subprocesses. export const handlePipeArgumentsError = ({ sourceStream, sourceError, destinationStream, destinationError, fileDescriptors, sourceOptions, startTime, }) => { const error = getPipeArgumentsError({ sourceStream, sourceError, destinationStream, destinationError, }); if (error !== undefined) { throw createNonCommandError({ error, fileDescriptors, sourceOptions, startTime, }); } }; const getPipeArgumentsError = ({sourceStream, sourceError, destinationStream, destinationError}) => { if (sourceError !== undefined && destinationError !== undefined) { return destinationError; } if (destinationError !== undefined) { abortSourceStream(sourceStream); return destinationError; } if (sourceError !== undefined) { endDestinationStream(destinationStream); return sourceError; } }; // Specific error return value when passing invalid arguments to `subprocess.pipe()` or when using `unpipeSignal` export const createNonCommandError = ({error, fileDescriptors, sourceOptions, startTime}) => makeEarlyError({ error, command: PIPE_COMMAND_MESSAGE, escapedCommand: PIPE_COMMAND_MESSAGE, fileDescriptors, options: sourceOptions, startTime, isSync: false, }); const PIPE_COMMAND_MESSAGE = 'source.pipe(destination)'; ================================================ FILE: lib/resolve/all-async.js ================================================ import mergeStreams from '@sindresorhus/merge-streams'; import {waitForSubprocessStream} from './stdio.js'; // `all` interleaves `stdout` and `stderr` export const makeAllStream = ({stdout, stderr}, {all}) => all && (stdout || stderr) ? mergeStreams([stdout, stderr].filter(Boolean)) : undefined; // Read the contents of `subprocess.all` and|or wait for its completion export const waitForAllStream = ({subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo}) => waitForSubprocessStream({ ...getAllStream(subprocess, buffer), fdNumber: 'all', encoding, maxBuffer: maxBuffer[1] + maxBuffer[2], lines: lines[1] || lines[2], allMixed: getAllMixed(subprocess), stripFinalNewline, verboseInfo, streamInfo, }); const getAllStream = ({stdout, stderr, all}, [, bufferStdout, bufferStderr]) => { const buffer = bufferStdout || bufferStderr; if (!buffer) { return {stream: all, buffer}; } if (!bufferStdout) { return {stream: stderr, buffer}; } if (!bufferStderr) { return {stream: stdout, buffer}; } return {stream: all, buffer}; }; // When `subprocess.stdout` is in objectMode but not `subprocess.stderr` (or the opposite), we need to use both: // - `getStreamAsArray()` for the chunks in objectMode, to return as an array without changing each chunk // - `getStreamAsArrayBuffer()` or `getStream()` for the chunks not in objectMode, to convert them from Buffers to string or Uint8Array // We do this by emulating the Buffer -> string|Uint8Array conversion performed by `get-stream` with our own, which is identical. const getAllMixed = ({all, stdout, stderr}) => all && stdout && stderr && stdout.readableObjectMode !== stderr.readableObjectMode; ================================================ FILE: lib/resolve/all-sync.js ================================================ import {isUint8Array, concatUint8Arrays} from '../utils/uint-array.js'; import {stripNewline} from '../io/strip-newline.js'; // Retrieve `result.all` with synchronous methods export const getAllSync = ([, stdout, stderr], options) => { if (!options.all) { return; } if (stdout === undefined) { return stderr; } if (stderr === undefined) { return stdout; } if (Array.isArray(stdout)) { return Array.isArray(stderr) ? [...stdout, ...stderr] : [...stdout, stripNewline(stderr, options, 'all')]; } if (Array.isArray(stderr)) { return [stripNewline(stdout, options, 'all'), ...stderr]; } if (isUint8Array(stdout) && isUint8Array(stderr)) { return concatUint8Arrays([stdout, stderr]); } return `${stdout}${stderr}`; }; ================================================ FILE: lib/resolve/exit-async.js ================================================ import {once} from 'node:events'; import {DiscardedError} from '../return/final-error.js'; // If `error` is emitted before `spawn`, `exit` will never be emitted. // However, `error` might be emitted after `spawn`. // In that case, `exit` will still be emitted. // Since the `exit` event contains the signal name, we want to make sure we are listening for it. // This function also takes into account the following unlikely cases: // - `exit` being emitted in the same microtask as `spawn` // - `error` being emitted multiple times export const waitForExit = async (subprocess, context) => { const [exitCode, signal] = await waitForExitOrError(subprocess); context.isForcefullyTerminated ??= false; return [exitCode, signal]; }; const waitForExitOrError = async subprocess => { const [spawnPayload, exitPayload] = await Promise.allSettled([ once(subprocess, 'spawn'), once(subprocess, 'exit'), ]); if (spawnPayload.status === 'rejected') { return []; } return exitPayload.status === 'rejected' ? waitForSubprocessExit(subprocess) : exitPayload.value; }; const waitForSubprocessExit = async subprocess => { try { return await once(subprocess, 'exit'); } catch { return waitForSubprocessExit(subprocess); } }; // Retrieve the final exit code and|or signal name export const waitForSuccessfulExit = async exitPromise => { const [exitCode, signal] = await exitPromise; if (!isSubprocessErrorExit(exitCode, signal) && isFailedExit(exitCode, signal)) { throw new DiscardedError(); } return [exitCode, signal]; }; // When the subprocess fails due to an `error` event const isSubprocessErrorExit = (exitCode, signal) => exitCode === undefined && signal === undefined; // When the subprocess fails due to a non-0 exit code or to a signal termination export const isFailedExit = (exitCode, signal) => exitCode !== 0 || signal !== null; ================================================ FILE: lib/resolve/exit-sync.js ================================================ import {DiscardedError} from '../return/final-error.js'; import {isMaxBufferSync} from '../io/max-buffer.js'; import {isFailedExit} from './exit-async.js'; // Retrieve exit code, signal name and error information, with synchronous methods export const getExitResultSync = ({error, status: exitCode, signal, output}, {maxBuffer}) => { const resultError = getResultError(error, exitCode, signal); const timedOut = resultError?.code === 'ETIMEDOUT'; const isMaxBuffer = isMaxBufferSync(resultError, output, maxBuffer); return { resultError, exitCode, signal, timedOut, isMaxBuffer, }; }; const getResultError = (error, exitCode, signal) => { if (error !== undefined) { return error; } return isFailedExit(exitCode, signal) ? new DiscardedError() : undefined; }; ================================================ FILE: lib/resolve/stdio.js ================================================ import {getStreamOutput} from '../io/contents.js'; import {waitForStream, isInputFileDescriptor} from './wait-stream.js'; // Read the contents of `subprocess.std*` and|or wait for its completion export const waitForStdioStreams = ({subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo}) => subprocess.stdio.map((stream, fdNumber) => waitForSubprocessStream({ stream, fdNumber, encoding, buffer: buffer[fdNumber], maxBuffer: maxBuffer[fdNumber], lines: lines[fdNumber], allMixed: false, stripFinalNewline, verboseInfo, streamInfo, })); // Read the contents of `subprocess.std*` or `subprocess.all` and|or wait for its completion export const waitForSubprocessStream = async ({stream, fdNumber, encoding, buffer, maxBuffer, lines, allMixed, stripFinalNewline, verboseInfo, streamInfo}) => { if (!stream) { return; } const onStreamEnd = waitForStream(stream, fdNumber, streamInfo); if (isInputFileDescriptor(streamInfo, fdNumber)) { await onStreamEnd; return; } const [output] = await Promise.all([ getStreamOutput({ stream, onStreamEnd, fdNumber, encoding, buffer, maxBuffer, lines, allMixed, stripFinalNewline, verboseInfo, streamInfo, }), onStreamEnd, ]); return output; }; ================================================ FILE: lib/resolve/wait-stream.js ================================================ import {finished} from 'node:stream/promises'; // Wraps `finished(stream)` to handle the following case: // - When the subprocess exits, Node.js automatically calls `subprocess.stdin.destroy()`, which we need to ignore. // - However, we still need to throw if `subprocess.stdin.destroy()` is called before subprocess exit. export const waitForStream = async (stream, fdNumber, streamInfo, {isSameDirection, stopOnExit = false} = {}) => { const state = handleStdinDestroy(stream, streamInfo); const abortController = new AbortController(); try { await Promise.race([ ...(stopOnExit ? [streamInfo.exitPromise] : []), finished(stream, {cleanup: true, signal: abortController.signal}), ]); } catch (error) { if (!state.stdinCleanedUp) { handleStreamError(error, fdNumber, streamInfo, isSameDirection); } } finally { abortController.abort(); } }; // If `subprocess.stdin` is destroyed before being fully written to, it is considered aborted and should throw an error. // This can happen for example when user called `subprocess.stdin.destroy()` before `subprocess.stdin.end()`. // However, Node.js calls `subprocess.stdin.destroy()` on exit for cleanup purposes. // https://github.com/nodejs/node/blob/0b4cdb4b42956cbd7019058e409e06700a199e11/lib/internal/child_process.js#L278 // This is normal and should not throw an error. // Therefore, we need to differentiate between both situations to know whether to throw an error. // Unfortunately, events (`close`, `error`, `end`, `exit`) cannot be used because `.destroy()` can take an arbitrary amount of time. // For example, `stdin: 'pipe'` is implemented as a TCP socket, and its `.destroy()` method waits for TCP disconnection. // Therefore `.destroy()` might end before or after subprocess exit, based on OS speed and load. // The only way to detect this is to spy on `subprocess.stdin._destroy()` by wrapping it. // If `subprocess.exitCode` or `subprocess.signalCode` is set, it means `.destroy()` is being called by Node.js itself. const handleStdinDestroy = (stream, {originalStreams: [originalStdin], subprocess}) => { const state = {stdinCleanedUp: false}; if (stream === originalStdin) { spyOnStdinDestroy(stream, subprocess, state); } return state; }; const spyOnStdinDestroy = (subprocessStdin, subprocess, state) => { const {_destroy} = subprocessStdin; subprocessStdin._destroy = (...destroyArguments) => { setStdinCleanedUp(subprocess, state); _destroy.call(subprocessStdin, ...destroyArguments); }; }; const setStdinCleanedUp = ({exitCode, signalCode}, state) => { if (exitCode !== null || signalCode !== null) { state.stdinCleanedUp = true; } }; // We ignore EPIPEs on writable streams and aborts on readable streams since those can happen normally. // When one stream errors, the error is propagated to the other streams on the same file descriptor. // Those other streams might have a different direction due to the above. // When this happens, the direction of both the initial stream and the others should then be taken into account. // Therefore, we keep track of whether a stream error is currently propagating. const handleStreamError = (error, fdNumber, streamInfo, isSameDirection) => { if (!shouldIgnoreStreamError(error, fdNumber, streamInfo, isSameDirection)) { throw error; } }; const shouldIgnoreStreamError = (error, fdNumber, streamInfo, isSameDirection = true) => { if (streamInfo.propagating) { return isStreamEpipe(error) || isStreamAbort(error); } streamInfo.propagating = true; return isInputFileDescriptor(streamInfo, fdNumber) === isSameDirection ? isStreamEpipe(error) : isStreamAbort(error); }; // Unfortunately, we cannot use the stream's class or properties to know whether it is readable or writable. // For example, `subprocess.stdin` is technically a Duplex, but can only be used as a writable. // Therefore, we need to use the file descriptor's direction (`stdin` is input, `stdout` is output, etc.). // However, while `subprocess.std*` and transforms follow that direction, any stream passed the `std*` option has the opposite direction. // For example, `subprocess.stdin` is a writable, but the `stdin` option is a readable. export const isInputFileDescriptor = ({fileDescriptors}, fdNumber) => fdNumber !== 'all' && fileDescriptors[fdNumber].direction === 'input'; // When `stream.destroy()` is called without an `error` argument, stream is aborted. // This is the only way to abort a readable stream, which can be useful in some instances. // Therefore, we ignore this error on readable streams. export const isStreamAbort = error => error?.code === 'ERR_STREAM_PREMATURE_CLOSE'; // When `stream.write()` is called but the underlying source has been closed, `EPIPE` is emitted. // When piping subprocesses, the source subprocess usually decides when to stop piping. // However, there are some instances when the destination does instead, such as `... | head -n1`. // It notifies the source by using `EPIPE`. // Therefore, we ignore this error on writable streams. const isStreamEpipe = error => error?.code === 'EPIPE'; ================================================ FILE: lib/resolve/wait-subprocess.js ================================================ import {once} from 'node:events'; import {isStream as isNodeStream} from 'is-stream'; import {throwOnTimeout} from '../terminate/timeout.js'; import {throwOnCancel} from '../terminate/cancel.js'; import {throwOnGracefulCancel} from '../terminate/graceful.js'; import {isStandardStream} from '../utils/standard-stream.js'; import {TRANSFORM_TYPES} from '../stdio/type.js'; import {getBufferedData} from '../io/contents.js'; import {waitForIpcOutput, getBufferedIpcOutput} from '../ipc/buffer-messages.js'; import {sendIpcInput} from '../ipc/ipc-input.js'; import {waitForAllStream} from './all-async.js'; import {waitForStdioStreams} from './stdio.js'; import {waitForExit, waitForSuccessfulExit} from './exit-async.js'; import {waitForStream} from './wait-stream.js'; // Retrieve result of subprocess: exit code, signal, error, streams (stdout/stderr/all) export const waitForSubprocessResult = async ({ subprocess, options: { encoding, buffer, maxBuffer, lines, timeoutDuration: timeout, cancelSignal, gracefulCancel, forceKillAfterDelay, stripFinalNewline, ipc, ipcInput, }, context, verboseInfo, fileDescriptors, originalStreams, onInternalError, controller, }) => { const exitPromise = waitForExit(subprocess, context); const streamInfo = { originalStreams, fileDescriptors, subprocess, exitPromise, propagating: false, }; const stdioPromises = waitForStdioStreams({ subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo, }); const allPromise = waitForAllStream({ subprocess, encoding, buffer, maxBuffer, lines, stripFinalNewline, verboseInfo, streamInfo, }); const ipcOutput = []; const ipcOutputPromise = waitForIpcOutput({ subprocess, buffer, maxBuffer, ipc, ipcOutput, verboseInfo, }); const originalPromises = waitForOriginalStreams(originalStreams, subprocess, streamInfo); const customStreamsEndPromises = waitForCustomStreamsEnd(fileDescriptors, streamInfo); try { return await Promise.race([ Promise.all([ {}, waitForSuccessfulExit(exitPromise), Promise.all(stdioPromises), allPromise, ipcOutputPromise, sendIpcInput(subprocess, ipcInput), ...originalPromises, ...customStreamsEndPromises, ]), onInternalError, throwOnSubprocessError(subprocess, controller), ...throwOnTimeout(subprocess, timeout, context, controller), ...throwOnCancel({ subprocess, cancelSignal, gracefulCancel, context, controller, }), ...throwOnGracefulCancel({ subprocess, cancelSignal, gracefulCancel, forceKillAfterDelay, context, controller, }), ]); } catch (error) { context.terminationReason ??= 'other'; return Promise.all([ {error}, exitPromise, Promise.all(stdioPromises.map(stdioPromise => getBufferedData(stdioPromise))), getBufferedData(allPromise), getBufferedIpcOutput(ipcOutputPromise, ipcOutput), Promise.allSettled(originalPromises), Promise.allSettled(customStreamsEndPromises), ]); } }; // Transforms replace `subprocess.std*`, which means they are not exposed to users. // However, we still want to wait for their completion. const waitForOriginalStreams = (originalStreams, subprocess, streamInfo) => originalStreams.map((stream, fdNumber) => stream === subprocess.stdio[fdNumber] ? undefined : waitForStream(stream, fdNumber, streamInfo)); // Some `stdin`/`stdout`/`stderr` options create a stream, e.g. when passing a file path. // The `.pipe()` method automatically ends that stream when `subprocess` ends. // This makes sure we wait for the completion of those streams, in order to catch any error. const waitForCustomStreamsEnd = (fileDescriptors, streamInfo) => fileDescriptors.flatMap(({stdioItems}, fdNumber) => stdioItems .filter(({value, stream = value}) => isNodeStream(stream, {checkOpen: false}) && !isStandardStream(stream)) .map(({type, value, stream = value}) => waitForStream(stream, fdNumber, streamInfo, { isSameDirection: TRANSFORM_TYPES.has(type), stopOnExit: type === 'native', }))); // Fails when the subprocess emits an `error` event const throwOnSubprocessError = async (subprocess, {signal}) => { const [error] = await once(subprocess, 'error', {signal}); throw error; }; ================================================ FILE: lib/return/duration.js ================================================ import {hrtime} from 'node:process'; // Start counting time before spawning the subprocess export const getStartTime = () => hrtime.bigint(); // Compute duration after the subprocess ended. // Printed by the `verbose` option. export const getDurationMs = startTime => Number(hrtime.bigint() - startTime) / 1e6; ================================================ FILE: lib/return/early-error.js ================================================ import {ChildProcess} from 'node:child_process'; import { PassThrough, Readable, Writable, Duplex, } from 'node:stream'; import {cleanupCustomStreams} from '../stdio/handle.js'; import {makeEarlyError} from './result.js'; import {handleResult} from './reject.js'; // When the subprocess fails to spawn. // We ensure the returned error is always both a promise and a subprocess. export const handleEarlyError = ({error, command, escapedCommand, fileDescriptors, options, startTime, verboseInfo}) => { cleanupCustomStreams(fileDescriptors); const subprocess = new ChildProcess(); createDummyStreams(subprocess, fileDescriptors); Object.assign(subprocess, {readable, writable, duplex}); const earlyError = makeEarlyError({ error, command, escapedCommand, fileDescriptors, options, startTime, isSync: false, }); const promise = handleDummyPromise(earlyError, verboseInfo, options); return {subprocess, promise}; }; const createDummyStreams = (subprocess, fileDescriptors) => { const stdin = createDummyStream(); const stdout = createDummyStream(); const stderr = createDummyStream(); const extraStdio = Array.from({length: fileDescriptors.length - 3}, createDummyStream); const all = createDummyStream(); const stdio = [stdin, stdout, stderr, ...extraStdio]; Object.assign(subprocess, { stdin, stdout, stderr, all, stdio, }); }; const createDummyStream = () => { const stream = new PassThrough(); stream.end(); return stream; }; const readable = () => new Readable({read() {}}); const writable = () => new Writable({write() {}}); const duplex = () => new Duplex({read() {}, write() {}}); const handleDummyPromise = async (error, verboseInfo, options) => handleResult(error, verboseInfo, options); ================================================ FILE: lib/return/final-error.js ================================================ // When the subprocess fails, this is the error instance being returned. // If another error instance is being thrown, it is kept as `error.cause`. export const getFinalError = (originalError, message, isSync) => { const ErrorClass = isSync ? ExecaSyncError : ExecaError; const options = originalError instanceof DiscardedError ? {} : {cause: originalError}; return new ErrorClass(message, options); }; // Indicates that the error is used only to interrupt control flow, but not in the return value export class DiscardedError extends Error {} // Proper way to set `error.name`: it should be inherited and non-enumerable const setErrorName = (ErrorClass, value) => { Object.defineProperty(ErrorClass.prototype, 'name', { value, writable: true, enumerable: false, configurable: true, }); Object.defineProperty(ErrorClass.prototype, execaErrorSymbol, { value: true, writable: false, enumerable: false, configurable: false, }); }; // Unlike `instanceof`, this works across realms export const isExecaError = error => isErrorInstance(error) && execaErrorSymbol in error; const execaErrorSymbol = Symbol('isExecaError'); export const isErrorInstance = value => Object.prototype.toString.call(value) === '[object Error]'; // We use two different Error classes for async/sync methods since they have slightly different shape and types export class ExecaError extends Error {} setErrorName(ExecaError, ExecaError.name); export class ExecaSyncError extends Error {} setErrorName(ExecaSyncError, ExecaSyncError.name); ================================================ FILE: lib/return/message.js ================================================ import {inspect} from 'node:util'; import stripFinalNewline from 'strip-final-newline'; import {isUint8Array, uint8ArrayToString} from '../utils/uint-array.js'; import {fixCwdError} from '../arguments/cwd.js'; import {escapeLines} from '../arguments/escape.js'; import {getMaxBufferMessage} from '../io/max-buffer.js'; import {getSignalDescription} from '../terminate/signal.js'; import {DiscardedError, isExecaError} from './final-error.js'; // Computes `error.message`, `error.shortMessage` and `error.originalMessage` export const createMessages = ({ stdio, all, ipcOutput, originalError, signal, signalDescription, exitCode, escapedCommand, timedOut, isCanceled, isGracefullyCanceled, isMaxBuffer, isForcefullyTerminated, forceKillAfterDelay, killSignal, maxBuffer, timeout, cwd, }) => { const errorCode = originalError?.code; const prefix = getErrorPrefix({ originalError, timedOut, timeout, isMaxBuffer, maxBuffer, errorCode, signal, signalDescription, exitCode, isCanceled, isGracefullyCanceled, isForcefullyTerminated, forceKillAfterDelay, killSignal, }); const originalMessage = getOriginalMessage(originalError, cwd); const suffix = originalMessage === undefined ? '' : `\n${originalMessage}`; const shortMessage = `${prefix}: ${escapedCommand}${suffix}`; const messageStdio = all === undefined ? [stdio[2], stdio[1]] : [all]; const message = [ shortMessage, ...messageStdio, ...stdio.slice(3), ipcOutput.map(ipcMessage => serializeIpcMessage(ipcMessage)).join('\n'), ] .map(messagePart => escapeLines(stripFinalNewline(serializeMessagePart(messagePart)))) .filter(Boolean) .join('\n\n'); return {originalMessage, shortMessage, message}; }; const getErrorPrefix = ({ originalError, timedOut, timeout, isMaxBuffer, maxBuffer, errorCode, signal, signalDescription, exitCode, isCanceled, isGracefullyCanceled, isForcefullyTerminated, forceKillAfterDelay, killSignal, }) => { const forcefulSuffix = getForcefulSuffix(isForcefullyTerminated, forceKillAfterDelay); if (timedOut) { return `Command timed out after ${timeout} milliseconds${forcefulSuffix}`; } if (isGracefullyCanceled) { if (signal === undefined) { return `Command was gracefully canceled with exit code ${exitCode}`; } return isForcefullyTerminated ? `Command was gracefully canceled${forcefulSuffix}` : `Command was gracefully canceled with ${signal} (${signalDescription})`; } if (isCanceled) { return `Command was canceled${forcefulSuffix}`; } if (isMaxBuffer) { return `${getMaxBufferMessage(originalError, maxBuffer)}${forcefulSuffix}`; } if (errorCode !== undefined) { return `Command failed with ${errorCode}${forcefulSuffix}`; } if (isForcefullyTerminated) { return `Command was killed with ${killSignal} (${getSignalDescription(killSignal)})${forcefulSuffix}`; } if (signal !== undefined) { return `Command was killed with ${signal} (${signalDescription})`; } if (exitCode !== undefined) { return `Command failed with exit code ${exitCode}`; } return 'Command failed'; }; const getForcefulSuffix = (isForcefullyTerminated, forceKillAfterDelay) => isForcefullyTerminated ? ` and was forcefully terminated after ${forceKillAfterDelay} milliseconds` : ''; const getOriginalMessage = (originalError, cwd) => { if (originalError instanceof DiscardedError) { return; } const originalMessage = isExecaError(originalError) ? originalError.originalMessage : String(originalError?.message ?? originalError); const escapedOriginalMessage = escapeLines(fixCwdError(originalMessage, cwd)); return escapedOriginalMessage === '' ? undefined : escapedOriginalMessage; }; const serializeIpcMessage = ipcMessage => typeof ipcMessage === 'string' ? ipcMessage : inspect(ipcMessage); const serializeMessagePart = messagePart => Array.isArray(messagePart) ? messagePart.map(messageItem => stripFinalNewline(serializeMessageItem(messageItem))).filter(Boolean).join('\n') : serializeMessageItem(messagePart); const serializeMessageItem = messageItem => { if (typeof messageItem === 'string') { return messageItem; } if (isUint8Array(messageItem)) { return uint8ArrayToString(messageItem); } return ''; }; ================================================ FILE: lib/return/reject.js ================================================ import {logResult} from '../verbose/complete.js'; // Applies the `reject` option. // Also print the final log line with `verbose`. export const handleResult = (result, verboseInfo, {reject}) => { logResult(result, verboseInfo); if (result.failed && reject) { throw result; } return result; }; ================================================ FILE: lib/return/result.js ================================================ import {getSignalDescription} from '../terminate/signal.js'; import {getDurationMs} from './duration.js'; import {getFinalError} from './final-error.js'; import {createMessages} from './message.js'; // Object returned on subprocess success export const makeSuccessResult = ({ command, escapedCommand, stdio, all, ipcOutput, options: {cwd}, startTime, }) => omitUndefinedProperties({ command, escapedCommand, cwd, durationMs: getDurationMs(startTime), failed: false, timedOut: false, isCanceled: false, isGracefullyCanceled: false, isTerminated: false, isMaxBuffer: false, isForcefullyTerminated: false, exitCode: 0, stdout: stdio[1], stderr: stdio[2], all, stdio, ipcOutput, pipedFrom: [], }); // Object returned on subprocess failure before spawning export const makeEarlyError = ({ error, command, escapedCommand, fileDescriptors, options, startTime, isSync, }) => makeError({ error, command, escapedCommand, startTime, timedOut: false, isCanceled: false, isGracefullyCanceled: false, isMaxBuffer: false, isForcefullyTerminated: false, stdio: Array.from({length: fileDescriptors.length}), ipcOutput: [], options, isSync, }); // Object returned on subprocess failure export const makeError = ({ error: originalError, command, escapedCommand, startTime, timedOut, isCanceled, isGracefullyCanceled, isMaxBuffer, isForcefullyTerminated, exitCode: rawExitCode, signal: rawSignal, stdio, all, ipcOutput, options: { timeoutDuration, timeout = timeoutDuration, forceKillAfterDelay, killSignal, cwd, maxBuffer, }, isSync, }) => { const {exitCode, signal, signalDescription} = normalizeExitPayload(rawExitCode, rawSignal); const {originalMessage, shortMessage, message} = createMessages({ stdio, all, ipcOutput, originalError, signal, signalDescription, exitCode, escapedCommand, timedOut, isCanceled, isGracefullyCanceled, isMaxBuffer, isForcefullyTerminated, forceKillAfterDelay, killSignal, maxBuffer, timeout, cwd, }); const error = getFinalError(originalError, message, isSync); Object.assign(error, getErrorProperties({ error, command, escapedCommand, startTime, timedOut, isCanceled, isGracefullyCanceled, isMaxBuffer, isForcefullyTerminated, exitCode, signal, signalDescription, stdio, all, ipcOutput, cwd, originalMessage, shortMessage, })); return error; }; const getErrorProperties = ({ error, command, escapedCommand, startTime, timedOut, isCanceled, isGracefullyCanceled, isMaxBuffer, isForcefullyTerminated, exitCode, signal, signalDescription, stdio, all, ipcOutput, cwd, originalMessage, shortMessage, }) => omitUndefinedProperties({ shortMessage, originalMessage, command, escapedCommand, cwd, durationMs: getDurationMs(startTime), failed: true, timedOut, isCanceled, isGracefullyCanceled, isTerminated: signal !== undefined, isMaxBuffer, isForcefullyTerminated, exitCode, signal, signalDescription, code: error.cause?.code, stdout: stdio[1], stderr: stdio[2], all, stdio, ipcOutput, pipedFrom: [], }); const omitUndefinedProperties = result => Object.fromEntries(Object.entries(result).filter(([, value]) => value !== undefined)); // `signal` and `exitCode` emitted on `subprocess.on('exit')` event can be `null`. // We normalize them to `undefined` const normalizeExitPayload = (rawExitCode, rawSignal) => { const exitCode = rawExitCode === null ? undefined : rawExitCode; const signal = rawSignal === null ? undefined : rawSignal; const signalDescription = signal === undefined ? undefined : getSignalDescription(rawSignal); return {exitCode, signal, signalDescription}; }; ================================================ FILE: lib/stdio/direction.js ================================================ import process from 'node:process'; import { isStream as isNodeStream, isReadableStream as isNodeReadableStream, isWritableStream as isNodeWritableStream, } from 'is-stream'; import {isWritableStream} from './type.js'; // For `stdio[fdNumber]` beyond stdin/stdout/stderr, we need to guess whether the value passed is intended for inputs or outputs. // This allows us to know whether to pipe _into_ or _from_ the stream. // When `stdio[fdNumber]` is a single value, this guess is fairly straightforward. // However, when it is an array instead, we also need to make sure the different values are not incompatible with each other. export const getStreamDirection = (stdioItems, fdNumber, optionName) => { const directions = stdioItems.map(stdioItem => getStdioItemDirection(stdioItem, fdNumber)); if (directions.includes('input') && directions.includes('output')) { throw new TypeError(`The \`${optionName}\` option must not be an array of both readable and writable values.`); } return directions.find(Boolean) ?? DEFAULT_DIRECTION; }; const getStdioItemDirection = ({type, value}, fdNumber) => KNOWN_DIRECTIONS[fdNumber] ?? guessStreamDirection[type](value); // `stdin`/`stdout`/`stderr` have a known direction const KNOWN_DIRECTIONS = ['input', 'output', 'output']; const anyDirection = () => undefined; const alwaysInput = () => 'input'; // `string` can only be added through the `input` option, i.e. does not need to be handled here const guessStreamDirection = { generator: anyDirection, asyncGenerator: anyDirection, fileUrl: anyDirection, filePath: anyDirection, iterable: alwaysInput, asyncIterable: alwaysInput, uint8Array: alwaysInput, webStream: value => isWritableStream(value) ? 'output' : 'input', nodeStream(value) { if (!isNodeReadableStream(value, {checkOpen: false})) { return 'output'; } return isNodeWritableStream(value, {checkOpen: false}) ? undefined : 'input'; }, webTransform: anyDirection, duplex: anyDirection, native(value) { const standardStreamDirection = getStandardStreamDirection(value); if (standardStreamDirection !== undefined) { return standardStreamDirection; } if (isNodeStream(value, {checkOpen: false})) { return guessStreamDirection.nodeStream(value); } }, }; const getStandardStreamDirection = value => { if ([0, process.stdin].includes(value)) { return 'input'; } if ([1, 2, process.stdout, process.stderr].includes(value)) { return 'output'; } }; // When ambiguous, we initially keep the direction as `undefined`. // This allows arrays of `stdio` values to resolve the ambiguity. // For example, `stdio[3]: DuplexStream` is ambiguous, but `stdio[3]: [DuplexStream, WritableStream]` is not. // When the ambiguity remains, we default to `output` since it is the most common use case for additional file descriptors. const DEFAULT_DIRECTION = 'output'; ================================================ FILE: lib/stdio/duplicate.js ================================================ import { SPECIAL_DUPLICATE_TYPES_SYNC, SPECIAL_DUPLICATE_TYPES, FORBID_DUPLICATE_TYPES, TYPE_TO_MESSAGE, } from './type.js'; // Duplicates in the same file descriptor is most likely an error. // However, this can be useful with generators. export const filterDuplicates = stdioItems => stdioItems.filter((stdioItemOne, indexOne) => stdioItems.every((stdioItemTwo, indexTwo) => stdioItemOne.value !== stdioItemTwo.value || indexOne >= indexTwo || stdioItemOne.type === 'generator' || stdioItemOne.type === 'asyncGenerator')); // Check if two file descriptors are sharing the same target. // For example `{stdout: {file: './output.txt'}, stderr: {file: './output.txt'}}`. export const getDuplicateStream = ({stdioItem: {type, value, optionName}, direction, fileDescriptors, isSync}) => { const otherStdioItems = getOtherStdioItems(fileDescriptors, type); if (otherStdioItems.length === 0) { return; } if (isSync) { validateDuplicateStreamSync({ otherStdioItems, type, value, optionName, direction, }); return; } if (SPECIAL_DUPLICATE_TYPES.has(type)) { return getDuplicateStreamInstance({ otherStdioItems, type, value, optionName, direction, }); } if (FORBID_DUPLICATE_TYPES.has(type)) { validateDuplicateTransform({ otherStdioItems, type, value, optionName, }); } }; // Values shared by multiple file descriptors const getOtherStdioItems = (fileDescriptors, type) => fileDescriptors .flatMap(({direction, stdioItems}) => stdioItems .filter(stdioItem => stdioItem.type === type) .map((stdioItem => ({...stdioItem, direction})))); // With `execaSync()`, do not allow setting a file path both in input and output const validateDuplicateStreamSync = ({otherStdioItems, type, value, optionName, direction}) => { if (SPECIAL_DUPLICATE_TYPES_SYNC.has(type)) { getDuplicateStreamInstance({ otherStdioItems, type, value, optionName, direction, }); } }; // When two file descriptors share the file or stream, we need to re-use the same underlying stream. // Otherwise, the stream would be closed twice when piping ends. // This is only an issue with output file descriptors. // This is not a problem with generator functions since those create a new instance for each file descriptor. // We also forbid input and output file descriptors sharing the same file or stream, since that does not make sense. const getDuplicateStreamInstance = ({otherStdioItems, type, value, optionName, direction}) => { const duplicateStdioItems = otherStdioItems.filter(stdioItem => hasSameValue(stdioItem, value)); if (duplicateStdioItems.length === 0) { return; } const differentStdioItem = duplicateStdioItems.find(stdioItem => stdioItem.direction !== direction); throwOnDuplicateStream(differentStdioItem, optionName, type); return direction === 'output' ? duplicateStdioItems[0].stream : undefined; }; const hasSameValue = ({type, value}, secondValue) => { if (type === 'filePath') { return value.file === secondValue.file; } if (type === 'fileUrl') { return value.href === secondValue.href; } return value === secondValue; }; // We do not allow two file descriptors to share the same Duplex or TransformStream. // This is because those are set directly to `subprocess.std*`. // For example, this could result in `subprocess.stdout` and `subprocess.stderr` being the same value. // This means reading from either would get data from both stdout and stderr. const validateDuplicateTransform = ({otherStdioItems, type, value, optionName}) => { const duplicateStdioItem = otherStdioItems.find(({value: {transform}}) => transform === value.transform); throwOnDuplicateStream(duplicateStdioItem, optionName, type); }; const throwOnDuplicateStream = (stdioItem, optionName, type) => { if (stdioItem !== undefined) { throw new TypeError(`The \`${stdioItem.optionName}\` and \`${optionName}\` options must not target ${TYPE_TO_MESSAGE[type]} that is the same.`); } }; ================================================ FILE: lib/stdio/handle-async.js ================================================ import {createReadStream, createWriteStream} from 'node:fs'; import {Buffer} from 'node:buffer'; import {Readable, Writable, Duplex} from 'node:stream'; import {generatorToStream} from '../transform/generator.js'; import {handleStdio} from './handle.js'; import {TYPE_TO_MESSAGE} from './type.js'; // Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in async mode export const handleStdioAsync = (options, verboseInfo) => handleStdio(addPropertiesAsync, options, verboseInfo, false); const forbiddenIfAsync = ({type, optionName}) => { throw new TypeError(`The \`${optionName}\` option cannot be ${TYPE_TO_MESSAGE[type]}.`); }; // Create streams used internally for piping when using specific values for the `std*` options, in async mode. // For example, `stdout: {file}` creates a file stream, which is piped from/to. const addProperties = { fileNumber: forbiddenIfAsync, generator: generatorToStream, asyncGenerator: generatorToStream, nodeStream: ({value}) => ({stream: value}), webTransform({value: {transform, writableObjectMode, readableObjectMode}}) { const objectMode = writableObjectMode || readableObjectMode; const stream = Duplex.fromWeb(transform, {objectMode}); return {stream}; }, duplex: ({value: {transform}}) => ({stream: transform}), native() {}, }; const addPropertiesAsync = { input: { ...addProperties, fileUrl: ({value}) => ({stream: createReadStream(value)}), filePath: ({value: {file}}) => ({stream: createReadStream(file)}), webStream: ({value}) => ({stream: Readable.fromWeb(value)}), iterable: ({value}) => ({stream: Readable.from(value)}), asyncIterable: ({value}) => ({stream: Readable.from(value)}), string: ({value}) => ({stream: Readable.from(value)}), uint8Array: ({value}) => ({stream: Readable.from(Buffer.from(value))}), }, output: { ...addProperties, fileUrl: ({value}) => ({stream: createWriteStream(value)}), filePath: ({value: {file, append}}) => ({stream: createWriteStream(file, append ? {flags: 'a'} : {})}), webStream: ({value}) => ({stream: Writable.fromWeb(value)}), iterable: forbiddenIfAsync, asyncIterable: forbiddenIfAsync, string: forbiddenIfAsync, uint8Array: forbiddenIfAsync, }, }; ================================================ FILE: lib/stdio/handle-sync.js ================================================ import {readFileSync} from 'node:fs'; import {bufferToUint8Array} from '../utils/uint-array.js'; import {handleStdio} from './handle.js'; import {TYPE_TO_MESSAGE} from './type.js'; // Normalize `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in sync mode export const handleStdioSync = (options, verboseInfo) => handleStdio(addPropertiesSync, options, verboseInfo, true); const forbiddenIfSync = ({type, optionName}) => { throwInvalidSyncValue(optionName, TYPE_TO_MESSAGE[type]); }; const forbiddenNativeIfSync = ({optionName, value}) => { if (value === 'ipc' || value === 'overlapped') { throwInvalidSyncValue(optionName, `"${value}"`); } return {}; }; const throwInvalidSyncValue = (optionName, value) => { throw new TypeError(`The \`${optionName}\` option cannot be ${value} with synchronous methods.`); }; // Create streams used internally for redirecting when using specific values for the `std*` options, in sync mode. // For example, `stdin: {file}` reads the file synchronously, then passes it as the `input` option. const addProperties = { generator() {}, asyncGenerator: forbiddenIfSync, webStream: forbiddenIfSync, nodeStream: forbiddenIfSync, webTransform: forbiddenIfSync, duplex: forbiddenIfSync, asyncIterable: forbiddenIfSync, native: forbiddenNativeIfSync, }; const addPropertiesSync = { input: { ...addProperties, fileUrl: ({value}) => ({contents: [bufferToUint8Array(readFileSync(value))]}), filePath: ({value: {file}}) => ({contents: [bufferToUint8Array(readFileSync(file))]}), fileNumber: forbiddenIfSync, iterable: ({value}) => ({contents: [...value]}), string: ({value}) => ({contents: [value]}), uint8Array: ({value}) => ({contents: [value]}), }, output: { ...addProperties, fileUrl: ({value}) => ({path: value}), filePath: ({value: {file, append}}) => ({path: file, append}), fileNumber: ({value}) => ({path: value}), iterable: forbiddenIfSync, string: forbiddenIfSync, uint8Array: forbiddenIfSync, }, }; ================================================ FILE: lib/stdio/handle.js ================================================ import {getStreamName, isStandardStream} from '../utils/standard-stream.js'; import {normalizeTransforms} from '../transform/normalize.js'; import {getFdObjectMode} from '../transform/object-mode.js'; import { getStdioItemType, isRegularUrl, isUnknownStdioString, FILE_TYPES, } from './type.js'; import {getStreamDirection} from './direction.js'; import {normalizeStdioOption} from './stdio-option.js'; import {handleNativeStream} from './native.js'; import {handleInputOptions} from './input-option.js'; import {filterDuplicates, getDuplicateStream} from './duplicate.js'; // Handle `input`, `inputFile`, `stdin`, `stdout` and `stderr` options, before spawning, in async/sync mode // They are converted into an array of `fileDescriptors`. // Each `fileDescriptor` is normalized, validated and contains all information necessary for further handling. export const handleStdio = (addProperties, options, verboseInfo, isSync) => { const stdio = normalizeStdioOption(options, verboseInfo, isSync); const initialFileDescriptors = stdio.map((stdioOption, fdNumber) => getFileDescriptor({ stdioOption, fdNumber, options, isSync, })); const fileDescriptors = getFinalFileDescriptors({ initialFileDescriptors, addProperties, options, isSync, }); options.stdio = fileDescriptors.map(({stdioItems}) => forwardStdio(stdioItems)); return fileDescriptors; }; const getFileDescriptor = ({stdioOption, fdNumber, options, isSync}) => { const optionName = getStreamName(fdNumber); const {stdioItems: initialStdioItems, isStdioArray} = initializeStdioItems({ stdioOption, fdNumber, options, optionName, }); const direction = getStreamDirection(initialStdioItems, fdNumber, optionName); const stdioItems = initialStdioItems.map(stdioItem => handleNativeStream({ stdioItem, isStdioArray, fdNumber, direction, isSync, })); const normalizedStdioItems = normalizeTransforms(stdioItems, optionName, direction, options); const objectMode = getFdObjectMode(normalizedStdioItems, direction); validateFileObjectMode(normalizedStdioItems, objectMode); return {direction, objectMode, stdioItems: normalizedStdioItems}; }; // We make sure passing an array with a single item behaves the same as passing that item without an array. // This is what users would expect. // For example, `stdout: ['ignore']` behaves the same as `stdout: 'ignore'`. const initializeStdioItems = ({stdioOption, fdNumber, options, optionName}) => { const values = Array.isArray(stdioOption) ? stdioOption : [stdioOption]; const initialStdioItems = [ ...values.map(value => initializeStdioItem(value, optionName)), ...handleInputOptions(options, fdNumber), ]; const stdioItems = filterDuplicates(initialStdioItems); const isStdioArray = stdioItems.length > 1; validateStdioArray(stdioItems, isStdioArray, optionName); validateStreams(stdioItems); return {stdioItems, isStdioArray}; }; const initializeStdioItem = (value, optionName) => ({ type: getStdioItemType(value, optionName), value, optionName, }); const validateStdioArray = (stdioItems, isStdioArray, optionName) => { if (stdioItems.length === 0) { throw new TypeError(`The \`${optionName}\` option must not be an empty array.`); } if (!isStdioArray) { return; } for (const {value, optionName} of stdioItems) { if (INVALID_STDIO_ARRAY_OPTIONS.has(value)) { throw new Error(`The \`${optionName}\` option must not include \`${value}\`.`); } } }; // Using those `stdio` values together with others for the same stream does not make sense, so we make it fail. // However, we do allow it if the array has a single item. const INVALID_STDIO_ARRAY_OPTIONS = new Set(['ignore', 'ipc']); const validateStreams = stdioItems => { for (const stdioItem of stdioItems) { validateFileStdio(stdioItem); } }; const validateFileStdio = ({type, value, optionName}) => { if (isRegularUrl(value)) { throw new TypeError(`The \`${optionName}: URL\` option must use the \`file:\` scheme. For example, you can use the \`pathToFileURL()\` method of the \`url\` core module.`); } if (isUnknownStdioString(type, value)) { throw new TypeError(`The \`${optionName}: { file: '...' }\` option must be used instead of \`${optionName}: '...'\`.`); } }; const validateFileObjectMode = (stdioItems, objectMode) => { if (!objectMode) { return; } const fileStdioItem = stdioItems.find(({type}) => FILE_TYPES.has(type)); if (fileStdioItem !== undefined) { throw new TypeError(`The \`${fileStdioItem.optionName}\` option cannot use both files and transforms in objectMode.`); } }; // Some `stdio` values require Execa to create streams. // For example, file paths create file read/write streams. // Those transformations are specified in `addProperties`, which is both direction-specific and type-specific. const getFinalFileDescriptors = ({initialFileDescriptors, addProperties, options, isSync}) => { const fileDescriptors = []; try { for (const fileDescriptor of initialFileDescriptors) { fileDescriptors.push(getFinalFileDescriptor({ fileDescriptor, fileDescriptors, addProperties, options, isSync, })); } return fileDescriptors; } catch (error) { cleanupCustomStreams(fileDescriptors); throw error; } }; const getFinalFileDescriptor = ({ fileDescriptor: {direction, objectMode, stdioItems}, fileDescriptors, addProperties, options, isSync, }) => { const finalStdioItems = stdioItems.map(stdioItem => addStreamProperties({ stdioItem, addProperties, direction, options, fileDescriptors, isSync, })); return {direction, objectMode, stdioItems: finalStdioItems}; }; const addStreamProperties = ({stdioItem, addProperties, direction, options, fileDescriptors, isSync}) => { const duplicateStream = getDuplicateStream({ stdioItem, direction, fileDescriptors, isSync, }); if (duplicateStream !== undefined) { return {...stdioItem, stream: duplicateStream}; } return { ...stdioItem, ...addProperties[direction][stdioItem.type](stdioItem, options), }; }; // The stream error handling is performed by the piping logic above, which cannot be performed before subprocess spawning. // If the subprocess spawning fails (e.g. due to an invalid command), the streams need to be manually destroyed. // We need to create those streams before subprocess spawning, in case their creation fails, e.g. when passing an invalid generator as argument. // Like this, an exception would be thrown, which would prevent spawning a subprocess. export const cleanupCustomStreams = fileDescriptors => { for (const {stdioItems} of fileDescriptors) { for (const {stream} of stdioItems) { if (stream !== undefined && !isStandardStream(stream)) { stream.destroy(); } } } }; // When the `std*: Iterable | WebStream | URL | filePath`, `input` or `inputFile` option is used, we pipe to `subprocess.std*`. // When the `std*: Array` option is used, we emulate some of the native values ('inherit', Node.js stream and file descriptor integer). To do so, we also need to pipe to `subprocess.std*`. // Therefore the `std*` options must be either `pipe` or `overlapped`. Other values do not set `subprocess.std*`. const forwardStdio = stdioItems => { if (stdioItems.length > 1) { return stdioItems.some(({value}) => value === 'overlapped') ? 'overlapped' : 'pipe'; } const [{type, value}] = stdioItems; return type === 'native' ? value : 'pipe'; }; ================================================ FILE: lib/stdio/input-option.js ================================================ import {isReadableStream} from 'is-stream'; import {isUint8Array} from '../utils/uint-array.js'; import {isUrl, isFilePathString} from './type.js'; // Append the `stdin` option with the `input` and `inputFile` options export const handleInputOptions = ({input, inputFile}, fdNumber) => fdNumber === 0 ? [ ...handleInputOption(input), ...handleInputFileOption(inputFile), ] : []; const handleInputOption = input => input === undefined ? [] : [{ type: getInputType(input), value: input, optionName: 'input', }]; const getInputType = input => { if (isReadableStream(input, {checkOpen: false})) { return 'nodeStream'; } if (typeof input === 'string') { return 'string'; } if (isUint8Array(input)) { return 'uint8Array'; } throw new Error('The `input` option must be a string, a Uint8Array or a Node.js Readable stream.'); }; const handleInputFileOption = inputFile => inputFile === undefined ? [] : [{ ...getInputFileType(inputFile), optionName: 'inputFile', }]; const getInputFileType = inputFile => { if (isUrl(inputFile)) { return {type: 'fileUrl', value: inputFile}; } if (isFilePathString(inputFile)) { return {type: 'filePath', value: {file: inputFile}}; } throw new Error('The `inputFile` option must be a file path string or a file URL.'); }; ================================================ FILE: lib/stdio/native.js ================================================ import {readFileSync} from 'node:fs'; import tty from 'node:tty'; import {isStream as isNodeStream} from 'is-stream'; import {STANDARD_STREAMS} from '../utils/standard-stream.js'; import {bufferToUint8Array} from '../utils/uint-array.js'; import {serializeOptionValue} from '../arguments/fd-options.js'; // When we use multiple `stdio` values for the same streams, we pass 'pipe' to `child_process.spawn()`. // We then emulate the piping done by core Node.js. // To do so, we transform the following values: // - Node.js streams are marked as `type: nodeStream` // - 'inherit' becomes `process.stdin|stdout|stderr` // - any file descriptor integer becomes `process.stdio[fdNumber]` // All of the above transformations tell Execa to perform manual piping. export const handleNativeStream = ({stdioItem, stdioItem: {type}, isStdioArray, fdNumber, direction, isSync}) => { if (!isStdioArray || type !== 'native') { return stdioItem; } return isSync ? handleNativeStreamSync({stdioItem, fdNumber, direction}) : handleNativeStreamAsync({stdioItem, fdNumber}); }; // Synchronous methods use a different logic. // 'inherit', file descriptors and process.std* are handled by readFileSync()/writeFileSync(). const handleNativeStreamSync = ({stdioItem, stdioItem: {value, optionName}, fdNumber, direction}) => { const targetFd = getTargetFd({ value, optionName, fdNumber, direction, }); if (targetFd !== undefined) { return targetFd; } if (isNodeStream(value, {checkOpen: false})) { throw new TypeError(`The \`${optionName}: Stream\` option cannot both be an array and include a stream with synchronous methods.`); } return stdioItem; }; const getTargetFd = ({value, optionName, fdNumber, direction}) => { const targetFdNumber = getTargetFdNumber(value, fdNumber); if (targetFdNumber === undefined) { return; } if (direction === 'output') { return {type: 'fileNumber', value: targetFdNumber, optionName}; } if (tty.isatty(targetFdNumber)) { throw new TypeError(`The \`${optionName}: ${serializeOptionValue(value)}\` option is invalid: it cannot be a TTY with synchronous methods.`); } return {type: 'uint8Array', value: bufferToUint8Array(readFileSync(targetFdNumber)), optionName}; }; const getTargetFdNumber = (value, fdNumber) => { if (value === 'inherit') { return fdNumber; } if (typeof value === 'number') { return value; } const standardStreamIndex = STANDARD_STREAMS.indexOf(value); if (standardStreamIndex !== -1) { return standardStreamIndex; } }; const handleNativeStreamAsync = ({stdioItem, stdioItem: {value, optionName}, fdNumber}) => { if (value === 'inherit') { return {type: 'nodeStream', value: getStandardStream(fdNumber, value, optionName), optionName}; } if (typeof value === 'number') { return {type: 'nodeStream', value: getStandardStream(value, value, optionName), optionName}; } if (isNodeStream(value, {checkOpen: false})) { return {type: 'nodeStream', value, optionName}; } return stdioItem; }; // Node.js does not allow to easily retrieve file descriptors beyond stdin/stdout/stderr as streams. // - `fs.createReadStream()`/`fs.createWriteStream()` with the `fd` option do not work with character devices that use blocking reads/writes (such as interactive TTYs). // - Using a TCP `Socket` would work but be rather complex to implement. // Since this is an edge case, we simply throw an error message. // See https://github.com/sindresorhus/execa/pull/643#discussion_r1435905707 const getStandardStream = (fdNumber, value, optionName) => { const standardStream = STANDARD_STREAMS[fdNumber]; if (standardStream === undefined) { throw new TypeError(`The \`${optionName}: ${value}\` option is invalid: no such standard stream.`); } return standardStream; }; ================================================ FILE: lib/stdio/stdio-option.js ================================================ import {STANDARD_STREAMS_ALIASES} from '../utils/standard-stream.js'; import {normalizeIpcStdioArray} from '../ipc/array.js'; import {isFullVerbose} from '../verbose/values.js'; // Add support for `stdin`/`stdout`/`stderr` as an alias for `stdio`. // Also normalize the `stdio` option. export const normalizeStdioOption = ({stdio, ipc, buffer, ...options}, verboseInfo, isSync) => { const stdioArray = getStdioArray(stdio, options).map((stdioOption, fdNumber) => addDefaultValue(stdioOption, fdNumber)); return isSync ? normalizeStdioSync(stdioArray, buffer, verboseInfo) : normalizeIpcStdioArray(stdioArray, ipc); }; const getStdioArray = (stdio, options) => { if (stdio === undefined) { return STANDARD_STREAMS_ALIASES.map(alias => options[alias]); } if (hasAlias(options)) { throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${STANDARD_STREAMS_ALIASES.map(alias => `\`${alias}\``).join(', ')}`); } if (typeof stdio === 'string') { return [stdio, stdio, stdio]; } if (!Array.isArray(stdio)) { throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``); } const length = Math.max(stdio.length, STANDARD_STREAMS_ALIASES.length); return Array.from({length}, (_, fdNumber) => stdio[fdNumber]); }; const hasAlias = options => STANDARD_STREAMS_ALIASES.some(alias => options[alias] !== undefined); const addDefaultValue = (stdioOption, fdNumber) => { if (Array.isArray(stdioOption)) { return stdioOption.map(item => addDefaultValue(item, fdNumber)); } if (stdioOption === null || stdioOption === undefined) { return fdNumber >= STANDARD_STREAMS_ALIASES.length ? 'ignore' : 'pipe'; } return stdioOption; }; // Using `buffer: false` with synchronous methods implies `stdout`/`stderr`: `ignore`. // Unless the output is needed, e.g. due to `verbose: 'full'` or to redirecting to a file. const normalizeStdioSync = (stdioArray, buffer, verboseInfo) => stdioArray.map((stdioOption, fdNumber) => !buffer[fdNumber] && fdNumber !== 0 && !isFullVerbose(verboseInfo, fdNumber) && isOutputPipeOnly(stdioOption) ? 'ignore' : stdioOption); const isOutputPipeOnly = stdioOption => stdioOption === 'pipe' || (Array.isArray(stdioOption) && stdioOption.every(item => item === 'pipe')); ================================================ FILE: lib/stdio/type.js ================================================ import {isStream as isNodeStream, isDuplexStream} from 'is-stream'; import isPlainObj from 'is-plain-obj'; import {isUint8Array} from '../utils/uint-array.js'; // The `stdin`/`stdout`/`stderr` option can be of many types. This detects it. export const getStdioItemType = (value, optionName) => { if (isAsyncGenerator(value)) { return 'asyncGenerator'; } if (isSyncGenerator(value)) { return 'generator'; } if (isUrl(value)) { return 'fileUrl'; } if (isFilePathObject(value)) { return 'filePath'; } if (isWebStream(value)) { return 'webStream'; } if (isNodeStream(value, {checkOpen: false})) { return 'native'; } if (isUint8Array(value)) { return 'uint8Array'; } if (isAsyncIterableObject(value)) { return 'asyncIterable'; } if (isIterableObject(value)) { return 'iterable'; } if (isTransformStream(value)) { return getTransformStreamType({transform: value}, optionName); } if (isTransformOptions(value)) { return getTransformObjectType(value, optionName); } return 'native'; }; const getTransformObjectType = (value, optionName) => { if (isDuplexStream(value.transform, {checkOpen: false})) { return getDuplexType(value, optionName); } if (isTransformStream(value.transform)) { return getTransformStreamType(value, optionName); } return getGeneratorObjectType(value, optionName); }; const getDuplexType = (value, optionName) => { validateNonGeneratorType(value, optionName, 'Duplex stream'); return 'duplex'; }; const getTransformStreamType = (value, optionName) => { validateNonGeneratorType(value, optionName, 'web TransformStream'); return 'webTransform'; }; const validateNonGeneratorType = ({final, binary, objectMode}, optionName, typeName) => { checkUndefinedOption(final, `${optionName}.final`, typeName); checkUndefinedOption(binary, `${optionName}.binary`, typeName); checkBooleanOption(objectMode, `${optionName}.objectMode`); }; const checkUndefinedOption = (value, optionName, typeName) => { if (value !== undefined) { throw new TypeError(`The \`${optionName}\` option can only be defined when using a generator, not a ${typeName}.`); } }; const getGeneratorObjectType = ({transform, final, binary, objectMode}, optionName) => { if (transform !== undefined && !isGenerator(transform)) { throw new TypeError(`The \`${optionName}.transform\` option must be a generator, a Duplex stream or a web TransformStream.`); } if (isDuplexStream(final, {checkOpen: false})) { throw new TypeError(`The \`${optionName}.final\` option must not be a Duplex stream.`); } if (isTransformStream(final)) { throw new TypeError(`The \`${optionName}.final\` option must not be a web TransformStream.`); } if (final !== undefined && !isGenerator(final)) { throw new TypeError(`The \`${optionName}.final\` option must be a generator.`); } checkBooleanOption(binary, `${optionName}.binary`); checkBooleanOption(objectMode, `${optionName}.objectMode`); return isAsyncGenerator(transform) || isAsyncGenerator(final) ? 'asyncGenerator' : 'generator'; }; const checkBooleanOption = (value, optionName) => { if (value !== undefined && typeof value !== 'boolean') { throw new TypeError(`The \`${optionName}\` option must use a boolean.`); } }; const isGenerator = value => isAsyncGenerator(value) || isSyncGenerator(value); export const isAsyncGenerator = value => Object.prototype.toString.call(value) === '[object AsyncGeneratorFunction]'; const isSyncGenerator = value => Object.prototype.toString.call(value) === '[object GeneratorFunction]'; const isTransformOptions = value => isPlainObj(value) && (value.transform !== undefined || value.final !== undefined); export const isUrl = value => Object.prototype.toString.call(value) === '[object URL]'; export const isRegularUrl = value => isUrl(value) && value.protocol !== 'file:'; const isFilePathObject = value => isPlainObj(value) && Object.keys(value).length > 0 && Object.keys(value).every(key => FILE_PATH_KEYS.has(key)) && isFilePathString(value.file); const FILE_PATH_KEYS = new Set(['file', 'append']); export const isFilePathString = file => typeof file === 'string'; export const isUnknownStdioString = (type, value) => type === 'native' && typeof value === 'string' && !KNOWN_STDIO_STRINGS.has(value); const KNOWN_STDIO_STRINGS = new Set(['ipc', 'ignore', 'inherit', 'overlapped', 'pipe']); const isReadableStream = value => Object.prototype.toString.call(value) === '[object ReadableStream]'; export const isWritableStream = value => Object.prototype.toString.call(value) === '[object WritableStream]'; const isWebStream = value => isReadableStream(value) || isWritableStream(value); const isTransformStream = value => isReadableStream(value?.readable) && isWritableStream(value?.writable); const isAsyncIterableObject = value => isObject(value) && typeof value[Symbol.asyncIterator] === 'function'; const isIterableObject = value => isObject(value) && typeof value[Symbol.iterator] === 'function'; const isObject = value => typeof value === 'object' && value !== null; // Types which modify `subprocess.std*` export const TRANSFORM_TYPES = new Set(['generator', 'asyncGenerator', 'duplex', 'webTransform']); // Types which write to a file or a file descriptor export const FILE_TYPES = new Set(['fileUrl', 'filePath', 'fileNumber']); // When two file descriptors of this type share the same target, we need to do some special logic export const SPECIAL_DUPLICATE_TYPES_SYNC = new Set(['fileUrl', 'filePath']); export const SPECIAL_DUPLICATE_TYPES = new Set([...SPECIAL_DUPLICATE_TYPES_SYNC, 'webStream', 'nodeStream']); // Do not allow two file descriptors of this type sharing the same target export const FORBID_DUPLICATE_TYPES = new Set(['webTransform', 'duplex']); // Convert types to human-friendly strings for error messages export const TYPE_TO_MESSAGE = { generator: 'a generator', asyncGenerator: 'an async generator', fileUrl: 'a file URL', filePath: 'a file path string', fileNumber: 'a file descriptor number', webStream: 'a web stream', nodeStream: 'a Node.js stream', webTransform: 'a web TransformStream', duplex: 'a Duplex stream', native: 'any value', iterable: 'an iterable', asyncIterable: 'an async iterable', string: 'a string', uint8Array: 'a Uint8Array', }; ================================================ FILE: lib/terminate/cancel.js ================================================ import {onAbortedSignal} from '../utils/abort-signal.js'; // Validate the `cancelSignal` option export const validateCancelSignal = ({cancelSignal}) => { if (cancelSignal !== undefined && Object.prototype.toString.call(cancelSignal) !== '[object AbortSignal]') { throw new Error(`The \`cancelSignal\` option must be an AbortSignal: ${String(cancelSignal)}`); } }; // Terminate the subprocess when aborting the `cancelSignal` option and `gracefulSignal` is `false` export const throwOnCancel = ({subprocess, cancelSignal, gracefulCancel, context, controller}) => cancelSignal === undefined || gracefulCancel ? [] : [terminateOnCancel(subprocess, cancelSignal, context, controller)]; const terminateOnCancel = async (subprocess, cancelSignal, context, {signal}) => { await onAbortedSignal(cancelSignal, signal); context.terminationReason ??= 'cancel'; subprocess.kill(); throw cancelSignal.reason; }; ================================================ FILE: lib/terminate/cleanup.js ================================================ import {addAbortListener} from 'node:events'; import {onExit} from 'signal-exit'; // If the `cleanup` option is used, call `subprocess.kill()` when the parent process exits export const cleanupOnExit = (subprocess, {cleanup, detached}, {signal}) => { if (!cleanup || detached) { return; } const removeExitHandler = onExit(() => { subprocess.kill(); }); addAbortListener(signal, () => { removeExitHandler(); }); }; ================================================ FILE: lib/terminate/graceful.js ================================================ import {onAbortedSignal} from '../utils/abort-signal.js'; import {sendAbort} from '../ipc/graceful.js'; import {killOnTimeout} from './kill.js'; // Validate the `gracefulCancel` option export const validateGracefulCancel = ({gracefulCancel, cancelSignal, ipc, serialization}) => { if (!gracefulCancel) { return; } if (cancelSignal === undefined) { throw new Error('The `cancelSignal` option must be defined when setting the `gracefulCancel` option.'); } if (!ipc) { throw new Error('The `ipc` option cannot be false when setting the `gracefulCancel` option.'); } if (serialization === 'json') { throw new Error('The `serialization` option cannot be \'json\' when setting the `gracefulCancel` option.'); } }; // Send abort reason to the subprocess when aborting the `cancelSignal` option and `gracefulCancel` is `true` export const throwOnGracefulCancel = ({ subprocess, cancelSignal, gracefulCancel, forceKillAfterDelay, context, controller, }) => gracefulCancel ? [sendOnAbort({ subprocess, cancelSignal, forceKillAfterDelay, context, controller, })] : []; const sendOnAbort = async ({subprocess, cancelSignal, forceKillAfterDelay, context, controller: {signal}}) => { await onAbortedSignal(cancelSignal, signal); const reason = getReason(cancelSignal); await sendAbort(subprocess, reason); killOnTimeout({ kill: subprocess.kill, forceKillAfterDelay, context, controllerSignal: signal, }); context.terminationReason ??= 'gracefulCancel'; throw cancelSignal.reason; }; // The default `reason` is a DOMException, which is not serializable with V8 // See https://github.com/nodejs/node/issues/53225 const getReason = ({reason}) => { if (!(reason instanceof DOMException)) { return reason; } const error = new Error(reason.message); Object.defineProperty(error, 'stack', { value: reason.stack, enumerable: false, configurable: true, writable: true, }); return error; }; ================================================ FILE: lib/terminate/kill.js ================================================ import {setTimeout} from 'node:timers/promises'; import {isErrorInstance} from '../return/final-error.js'; import {normalizeSignalArgument} from './signal.js'; // Normalize the `forceKillAfterDelay` option export const normalizeForceKillAfterDelay = forceKillAfterDelay => { if (forceKillAfterDelay === false) { return forceKillAfterDelay; } if (forceKillAfterDelay === true) { return DEFAULT_FORCE_KILL_TIMEOUT; } if (!Number.isFinite(forceKillAfterDelay) || forceKillAfterDelay < 0) { throw new TypeError(`Expected the \`forceKillAfterDelay\` option to be a non-negative integer, got \`${forceKillAfterDelay}\` (${typeof forceKillAfterDelay})`); } return forceKillAfterDelay; }; const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5; // Monkey-patches `subprocess.kill()` to add `forceKillAfterDelay` behavior and `.kill(error)` export const subprocessKill = ( {kill, options: {forceKillAfterDelay, killSignal}, onInternalError, context, controller}, signalOrError, errorArgument, ) => { const {signal, error} = parseKillArguments(signalOrError, errorArgument, killSignal); emitKillError(error, onInternalError); const killResult = kill(signal); setKillTimeout({ kill, signal, forceKillAfterDelay, killSignal, killResult, context, controller, }); return killResult; }; const parseKillArguments = (signalOrError, errorArgument, killSignal) => { const [signal = killSignal, error] = isErrorInstance(signalOrError) ? [undefined, signalOrError] : [signalOrError, errorArgument]; if (typeof signal !== 'string' && !Number.isInteger(signal)) { throw new TypeError(`The first argument must be an error instance or a signal name string/integer: ${String(signal)}`); } if (error !== undefined && !isErrorInstance(error)) { throw new TypeError(`The second argument is optional. If specified, it must be an error instance: ${error}`); } return {signal: normalizeSignalArgument(signal), error}; }; // Fails right away when calling `subprocess.kill(error)`. // Does not wait for actual signal termination. // Uses a deferred promise instead of the `error` event on the subprocess, as this is less intrusive. const emitKillError = (error, onInternalError) => { if (error !== undefined) { onInternalError.reject(error); } }; const setKillTimeout = async ({kill, signal, forceKillAfterDelay, killSignal, killResult, context, controller}) => { if (signal === killSignal && killResult) { killOnTimeout({ kill, forceKillAfterDelay, context, controllerSignal: controller.signal, }); } }; // Forcefully terminate a subprocess after a timeout export const killOnTimeout = async ({kill, forceKillAfterDelay, context, controllerSignal}) => { if (forceKillAfterDelay === false) { return; } try { await setTimeout(forceKillAfterDelay, undefined, {signal: controllerSignal}); if (kill('SIGKILL')) { context.isForcefullyTerminated ??= true; } } catch {} }; ================================================ FILE: lib/terminate/signal.js ================================================ import {constants} from 'node:os'; import {signalsByName} from 'human-signals'; // Normalize signals for comparison purpose. // Also validate the signal exists. export const normalizeKillSignal = killSignal => { const optionName = 'option `killSignal`'; if (killSignal === 0) { throw new TypeError(`Invalid ${optionName}: 0 cannot be used.`); } return normalizeSignal(killSignal, optionName); }; export const normalizeSignalArgument = signal => signal === 0 ? signal : normalizeSignal(signal, '`subprocess.kill()`\'s argument'); const normalizeSignal = (signalNameOrInteger, optionName) => { if (Number.isInteger(signalNameOrInteger)) { return normalizeSignalInteger(signalNameOrInteger, optionName); } if (typeof signalNameOrInteger === 'string') { return normalizeSignalName(signalNameOrInteger, optionName); } throw new TypeError(`Invalid ${optionName} ${String(signalNameOrInteger)}: it must be a string or an integer.\n${getAvailableSignals()}`); }; const normalizeSignalInteger = (signalInteger, optionName) => { if (signalsIntegerToName.has(signalInteger)) { return signalsIntegerToName.get(signalInteger); } throw new TypeError(`Invalid ${optionName} ${signalInteger}: this signal integer does not exist.\n${getAvailableSignals()}`); }; const getSignalsIntegerToName = () => new Map(Object.entries(constants.signals) .reverse() .map(([signalName, signalInteger]) => [signalInteger, signalName])); const signalsIntegerToName = getSignalsIntegerToName(); const normalizeSignalName = (signalName, optionName) => { if (signalName in constants.signals) { return signalName; } if (signalName.toUpperCase() in constants.signals) { throw new TypeError(`Invalid ${optionName} '${signalName}': please rename it to '${signalName.toUpperCase()}'.`); } throw new TypeError(`Invalid ${optionName} '${signalName}': this signal name does not exist.\n${getAvailableSignals()}`); }; const getAvailableSignals = () => `Available signal names: ${getAvailableSignalNames()}. Available signal numbers: ${getAvailableSignalIntegers()}.`; const getAvailableSignalNames = () => Object.keys(constants.signals) .sort() .map(signalName => `'${signalName}'`) .join(', '); const getAvailableSignalIntegers = () => [...new Set(Object.values(constants.signals) .sort((signalInteger, signalIntegerTwo) => signalInteger - signalIntegerTwo))] .join(', '); // Human-friendly description of a signal export const getSignalDescription = signal => signalsByName[signal].description; ================================================ FILE: lib/terminate/timeout.js ================================================ import {setTimeout} from 'node:timers/promises'; import {DiscardedError} from '../return/final-error.js'; // Validate `timeout` option export const validateTimeout = ({timeout}) => { if (timeout !== undefined && (!Number.isFinite(timeout) || timeout < 0)) { throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`); } }; // Fails when the `timeout` option is exceeded export const throwOnTimeout = (subprocess, timeout, context, controller) => timeout === 0 || timeout === undefined ? [] : [killAfterTimeout(subprocess, timeout, context, controller)]; const killAfterTimeout = async (subprocess, timeout, context, {signal}) => { await setTimeout(timeout, undefined, {signal}); context.terminationReason ??= 'timeout'; subprocess.kill(); throw new DiscardedError(); }; ================================================ FILE: lib/transform/encoding-transform.js ================================================ import {Buffer} from 'node:buffer'; import {StringDecoder} from 'node:string_decoder'; import {isUint8Array, bufferToUint8Array} from '../utils/uint-array.js'; /* When using binary encodings, add an internal generator that converts chunks from `Buffer` to `string` or `Uint8Array`. Chunks might be Buffer, Uint8Array or strings since: - `subprocess.stdout|stderr` emits Buffers - `subprocess.stdin.write()` accepts Buffer, Uint8Array or string - Previous generators might return Uint8Array or string However, those are converted to Buffer: - on writes: `Duplex.writable` `decodeStrings: true` default option - on reads: `Duplex.readable` `readableEncoding: null` default option */ export const getEncodingTransformGenerator = (binary, encoding, skipped) => { if (skipped) { return; } if (binary) { return {transform: encodingUint8ArrayGenerator.bind(undefined, new TextEncoder())}; } const stringDecoder = new StringDecoder(encoding); return { transform: encodingStringGenerator.bind(undefined, stringDecoder), final: encodingStringFinal.bind(undefined, stringDecoder), }; }; const encodingUint8ArrayGenerator = function * (textEncoder, chunk) { if (Buffer.isBuffer(chunk)) { yield bufferToUint8Array(chunk); } else if (typeof chunk === 'string') { yield textEncoder.encode(chunk); } else { yield chunk; } }; const encodingStringGenerator = function * (stringDecoder, chunk) { yield isUint8Array(chunk) ? stringDecoder.write(chunk) : chunk; }; const encodingStringFinal = function * (stringDecoder) { const lastChunk = stringDecoder.end(); if (lastChunk !== '') { yield lastChunk; } }; ================================================ FILE: lib/transform/generator.js ================================================ import {Transform, getDefaultHighWaterMark} from 'node:stream'; import {isAsyncGenerator} from '../stdio/type.js'; import {getSplitLinesGenerator, getAppendNewlineGenerator} from './split.js'; import {getValidateTransformInput, getValidateTransformReturn} from './validate.js'; import {getEncodingTransformGenerator} from './encoding-transform.js'; import { pushChunks, transformChunk, finalChunks, destroyTransform, } from './run-async.js'; import { pushChunksSync, transformChunkSync, finalChunksSync, runTransformSync, } from './run-sync.js'; /* Generators can be used to transform/filter standard streams. Generators have a simple syntax, yet allows all of the following: - Sharing `state` between chunks - Flushing logic, by using a `final` function - Asynchronous logic - Emitting multiple chunks from a single source chunk, even if spaced in time, by using multiple `yield` - Filtering, by using no `yield` Therefore, there is no need to allow Node.js or web transform streams. The `highWaterMark` is kept as the default value, since this is what `subprocess.std*` uses. Chunks are currently processed serially. We could add a `concurrency` option to parallelize in the future. Transform an array of generator functions into a `Transform` stream. `Duplex.from(generator)` cannot be used because it does not allow setting the `objectMode` and `highWaterMark`. */ export const generatorToStream = ({ value, value: {transform, final, writableObjectMode, readableObjectMode}, optionName, }, {encoding}) => { const state = {}; const generators = addInternalGenerators(value, encoding, optionName); const transformAsync = isAsyncGenerator(transform); const finalAsync = isAsyncGenerator(final); const transformMethod = transformAsync ? pushChunks.bind(undefined, transformChunk, state) : pushChunksSync.bind(undefined, transformChunkSync); const finalMethod = transformAsync || finalAsync ? pushChunks.bind(undefined, finalChunks, state) : pushChunksSync.bind(undefined, finalChunksSync); const destroyMethod = transformAsync || finalAsync ? destroyTransform.bind(undefined, state) : undefined; const stream = new Transform({ writableObjectMode, writableHighWaterMark: getDefaultHighWaterMark(writableObjectMode), readableObjectMode, readableHighWaterMark: getDefaultHighWaterMark(readableObjectMode), transform(chunk, encoding, done) { transformMethod([chunk, generators, 0], this, done); }, flush(done) { finalMethod([generators], this, done); }, destroy: destroyMethod, }); return {stream}; }; // Applies transform generators in sync mode export const runGeneratorsSync = (chunks, stdioItems, encoding, isInput) => { const generators = stdioItems.filter(({type}) => type === 'generator'); const reversedGenerators = isInput ? generators.reverse() : generators; for (const {value, optionName} of reversedGenerators) { const generators = addInternalGenerators(value, encoding, optionName); chunks = runTransformSync(generators, chunks); } return chunks; }; // Generators used internally to convert the chunk type, validate it, and split into lines const addInternalGenerators = ( {transform, final, binary, writableObjectMode, readableObjectMode, preserveNewlines}, encoding, optionName, ) => { const state = {}; return [ {transform: getValidateTransformInput(writableObjectMode, optionName)}, getEncodingTransformGenerator(binary, encoding, writableObjectMode), getSplitLinesGenerator(binary, preserveNewlines, writableObjectMode, state), {transform, final}, {transform: getValidateTransformReturn(readableObjectMode, optionName)}, getAppendNewlineGenerator({ binary, preserveNewlines, readableObjectMode, state, }), ].filter(Boolean); }; ================================================ FILE: lib/transform/normalize.js ================================================ import isPlainObj from 'is-plain-obj'; import {BINARY_ENCODINGS} from '../arguments/encoding-option.js'; import {TRANSFORM_TYPES} from '../stdio/type.js'; import {getTransformObjectModes} from './object-mode.js'; // Transforms generators/duplex/TransformStream can have multiple shapes. // This normalizes it and applies default values. export const normalizeTransforms = (stdioItems, optionName, direction, options) => [ ...stdioItems.filter(({type}) => !TRANSFORM_TYPES.has(type)), ...getTransforms(stdioItems, optionName, direction, options), ]; const getTransforms = (stdioItems, optionName, direction, {encoding}) => { const transforms = stdioItems.filter(({type}) => TRANSFORM_TYPES.has(type)); const newTransforms = Array.from({length: transforms.length}); for (const [index, stdioItem] of Object.entries(transforms)) { newTransforms[index] = normalizeTransform({ stdioItem, index: Number(index), newTransforms, optionName, direction, encoding, }); } return sortTransforms(newTransforms, direction); }; const normalizeTransform = ({stdioItem, stdioItem: {type}, index, newTransforms, optionName, direction, encoding}) => { if (type === 'duplex') { return normalizeDuplex({stdioItem, optionName}); } if (type === 'webTransform') { return normalizeTransformStream({ stdioItem, index, newTransforms, direction, }); } return normalizeGenerator({ stdioItem, index, newTransforms, direction, encoding, }); }; const normalizeDuplex = ({ stdioItem, stdioItem: { value: { transform, transform: {writableObjectMode, readableObjectMode}, objectMode = readableObjectMode, }, }, optionName, }) => { if (objectMode && !readableObjectMode) { throw new TypeError(`The \`${optionName}.objectMode\` option can only be \`true\` if \`new Duplex({objectMode: true})\` is used.`); } if (!objectMode && readableObjectMode) { throw new TypeError(`The \`${optionName}.objectMode\` option cannot be \`false\` if \`new Duplex({objectMode: true})\` is used.`); } return { ...stdioItem, value: {transform, writableObjectMode, readableObjectMode}, }; }; const normalizeTransformStream = ({stdioItem, stdioItem: {value}, index, newTransforms, direction}) => { const {transform, objectMode} = isPlainObj(value) ? value : {transform: value}; const {writableObjectMode, readableObjectMode} = getTransformObjectModes(objectMode, index, newTransforms, direction); return ({ ...stdioItem, value: {transform, writableObjectMode, readableObjectMode}, }); }; const normalizeGenerator = ({stdioItem, stdioItem: {value}, index, newTransforms, direction, encoding}) => { const { transform, final, binary: binaryOption = false, preserveNewlines = false, objectMode, } = isPlainObj(value) ? value : {transform: value}; const binary = binaryOption || BINARY_ENCODINGS.has(encoding); const {writableObjectMode, readableObjectMode} = getTransformObjectModes(objectMode, index, newTransforms, direction); return { ...stdioItem, value: { transform, final, binary, preserveNewlines, writableObjectMode, readableObjectMode, }, }; }; const sortTransforms = (newTransforms, direction) => direction === 'input' ? newTransforms.reverse() : newTransforms; ================================================ FILE: lib/transform/object-mode.js ================================================ import {TRANSFORM_TYPES} from '../stdio/type.js'; /* Retrieve the `objectMode`s of a single transform. `objectMode` determines the return value's type, i.e. the `readableObjectMode`. The chunk argument's type is based on the previous generator's return value, i.e. the `writableObjectMode` is based on the previous `readableObjectMode`. The last input's generator is read by `subprocess.stdin` which: - should not be in `objectMode` for performance reasons. - can only be strings, Buffers and Uint8Arrays. Therefore its `readableObjectMode` must be `false`. The same applies to the first output's generator's `writableObjectMode`. */ export const getTransformObjectModes = (objectMode, index, newTransforms, direction) => direction === 'output' ? getOutputObjectModes(objectMode, index, newTransforms) : getInputObjectModes(objectMode, index, newTransforms); const getOutputObjectModes = (objectMode, index, newTransforms) => { const writableObjectMode = index !== 0 && newTransforms[index - 1].value.readableObjectMode; const readableObjectMode = objectMode ?? writableObjectMode; return {writableObjectMode, readableObjectMode}; }; const getInputObjectModes = (objectMode, index, newTransforms) => { const writableObjectMode = index === 0 ? objectMode === true : newTransforms[index - 1].value.readableObjectMode; const readableObjectMode = index !== newTransforms.length - 1 && (objectMode ?? writableObjectMode); return {writableObjectMode, readableObjectMode}; }; // Retrieve the `objectMode` of a file descriptor, e.g. `stdout` or `stderr` export const getFdObjectMode = (stdioItems, direction) => { const lastTransform = stdioItems.findLast(({type}) => TRANSFORM_TYPES.has(type)); if (lastTransform === undefined) { return false; } return direction === 'input' ? lastTransform.value.writableObjectMode : lastTransform.value.readableObjectMode; }; ================================================ FILE: lib/transform/run-async.js ================================================ import {callbackify} from 'node:util'; // Applies a series of generator functions asynchronously export const pushChunks = callbackify(async (getChunks, state, getChunksArguments, transformStream) => { state.currentIterable = getChunks(...getChunksArguments); try { for await (const chunk of state.currentIterable) { transformStream.push(chunk); } } finally { delete state.currentIterable; } }); // For each new chunk, apply each `transform()` method export const transformChunk = async function * (chunk, generators, index) { if (index === generators.length) { yield chunk; return; } const {transform = identityGenerator} = generators[index]; for await (const transformedChunk of transform(chunk)) { yield * transformChunk(transformedChunk, generators, index + 1); } }; // At the end, apply each `final()` method, followed by the `transform()` method of the next transforms export const finalChunks = async function * (generators) { for (const [index, {final}] of Object.entries(generators)) { yield * generatorFinalChunks(final, Number(index), generators); } }; const generatorFinalChunks = async function * (final, index, generators) { if (final === undefined) { return; } for await (const finalChunk of final()) { yield * transformChunk(finalChunk, generators, index + 1); } }; // Cancel any ongoing async generator when the Transform is destroyed, e.g. when the subprocess errors export const destroyTransform = callbackify(async ({currentIterable}, error) => { if (currentIterable !== undefined) { await (error ? currentIterable.throw(error) : currentIterable.return()); return; } if (error) { throw error; } }); const identityGenerator = function * (chunk) { yield chunk; }; ================================================ FILE: lib/transform/run-sync.js ================================================ // Duplicate the code from `run-async.js` but as synchronous functions export const pushChunksSync = (getChunksSync, getChunksArguments, transformStream, done) => { try { for (const chunk of getChunksSync(...getChunksArguments)) { transformStream.push(chunk); } done(); } catch (error) { done(error); } }; // Run synchronous generators with `execaSync()` export const runTransformSync = (generators, chunks) => [ ...chunks.flatMap(chunk => [...transformChunkSync(chunk, generators, 0)]), ...finalChunksSync(generators), ]; export const transformChunkSync = function * (chunk, generators, index) { if (index === generators.length) { yield chunk; return; } const {transform = identityGenerator} = generators[index]; for (const transformedChunk of transform(chunk)) { yield * transformChunkSync(transformedChunk, generators, index + 1); } }; export const finalChunksSync = function * (generators) { for (const [index, {final}] of Object.entries(generators)) { yield * generatorFinalChunksSync(final, Number(index), generators); } }; const generatorFinalChunksSync = function * (final, index, generators) { if (final === undefined) { return; } for (const finalChunk of final()) { yield * transformChunkSync(finalChunk, generators, index + 1); } }; const identityGenerator = function * (chunk) { yield chunk; }; ================================================ FILE: lib/transform/split.js ================================================ // Split chunks line-wise for generators passed to the `std*` options export const getSplitLinesGenerator = (binary, preserveNewlines, skipped, state) => binary || skipped ? undefined : initializeSplitLines(preserveNewlines, state); // Same but for synchronous methods export const splitLinesSync = (chunk, preserveNewlines, objectMode) => objectMode ? chunk.flatMap(item => splitLinesItemSync(item, preserveNewlines)) : splitLinesItemSync(chunk, preserveNewlines); const splitLinesItemSync = (chunk, preserveNewlines) => { const {transform, final} = initializeSplitLines(preserveNewlines, {}); return [...transform(chunk), ...final()]; }; const initializeSplitLines = (preserveNewlines, state) => { state.previousChunks = ''; return { transform: splitGenerator.bind(undefined, state, preserveNewlines), final: linesFinal.bind(undefined, state), }; }; // This imperative logic is much faster than using `String.split()` and uses very low memory. const splitGenerator = function * (state, preserveNewlines, chunk) { if (typeof chunk !== 'string') { yield chunk; return; } let {previousChunks} = state; let start = -1; for (let end = 0; end < chunk.length; end += 1) { if (chunk[end] === '\n') { const newlineLength = getNewlineLength(chunk, end, preserveNewlines, state); let line = chunk.slice(start + 1, end + 1 - newlineLength); if (previousChunks.length > 0) { line = concatString(previousChunks, line); previousChunks = ''; } yield line; start = end; } } if (start !== chunk.length - 1) { previousChunks = concatString(previousChunks, chunk.slice(start + 1)); } state.previousChunks = previousChunks; }; const getNewlineLength = (chunk, end, preserveNewlines, state) => { if (preserveNewlines) { return 0; } state.isWindowsNewline = end !== 0 && chunk[end - 1] === '\r'; return state.isWindowsNewline ? 2 : 1; }; const linesFinal = function * ({previousChunks}) { if (previousChunks.length > 0) { yield previousChunks; } }; // Unless `preserveNewlines: true` is used, we strip the newline of each line. // This re-adds them after the user `transform` code has run. export const getAppendNewlineGenerator = ({binary, preserveNewlines, readableObjectMode, state}) => binary || preserveNewlines || readableObjectMode ? undefined : {transform: appendNewlineGenerator.bind(undefined, state)}; const appendNewlineGenerator = function * ({isWindowsNewline = false}, chunk) { const {unixNewline, windowsNewline, LF, concatBytes} = typeof chunk === 'string' ? linesStringInfo : linesUint8ArrayInfo; if (chunk.at(-1) === LF) { yield chunk; return; } const newline = isWindowsNewline ? windowsNewline : unixNewline; yield concatBytes(chunk, newline); }; const concatString = (firstChunk, secondChunk) => `${firstChunk}${secondChunk}`; const linesStringInfo = { windowsNewline: '\r\n', unixNewline: '\n', LF: '\n', concatBytes: concatString, }; const concatUint8Array = (firstChunk, secondChunk) => { const chunk = new Uint8Array(firstChunk.length + secondChunk.length); chunk.set(firstChunk, 0); chunk.set(secondChunk, firstChunk.length); return chunk; }; const linesUint8ArrayInfo = { windowsNewline: new Uint8Array([0x0D, 0x0A]), unixNewline: new Uint8Array([0x0A]), LF: 0x0A, concatBytes: concatUint8Array, }; ================================================ FILE: lib/transform/validate.js ================================================ import {Buffer} from 'node:buffer'; import {isUint8Array} from '../utils/uint-array.js'; // Validate the type of chunk argument passed to transform generators export const getValidateTransformInput = (writableObjectMode, optionName) => writableObjectMode ? undefined : validateStringTransformInput.bind(undefined, optionName); const validateStringTransformInput = function * (optionName, chunk) { if (typeof chunk !== 'string' && !isUint8Array(chunk) && !Buffer.isBuffer(chunk)) { throw new TypeError(`The \`${optionName}\` option's transform must use "objectMode: true" to receive as input: ${typeof chunk}.`); } yield chunk; }; // Validate the type of the value returned by transform generators export const getValidateTransformReturn = (readableObjectMode, optionName) => readableObjectMode ? validateObjectTransformReturn.bind(undefined, optionName) : validateStringTransformReturn.bind(undefined, optionName); const validateObjectTransformReturn = function * (optionName, chunk) { validateEmptyReturn(optionName, chunk); yield chunk; }; const validateStringTransformReturn = function * (optionName, chunk) { validateEmptyReturn(optionName, chunk); if (typeof chunk !== 'string' && !isUint8Array(chunk)) { throw new TypeError(`The \`${optionName}\` option's function must yield a string or an Uint8Array, not ${typeof chunk}.`); } yield chunk; }; const validateEmptyReturn = (optionName, chunk) => { if (chunk === null || chunk === undefined) { throw new TypeError(`The \`${optionName}\` option's function must not call \`yield ${chunk}\`. Instead, \`yield\` should either be called with a value, or not be called at all. For example: if (condition) { yield value; }`); } }; ================================================ FILE: lib/utils/abort-signal.js ================================================ import {once} from 'node:events'; // Combines `util.aborted()` and `events.addAbortListener()`: promise-based and cleaned up with a stop signal export const onAbortedSignal = async (mainSignal, stopSignal) => { if (!mainSignal.aborted) { await once(mainSignal, 'abort', {signal: stopSignal}); } }; ================================================ FILE: lib/utils/deferred.js ================================================ export const createDeferred = () => { const methods = {}; const promise = new Promise((resolve, reject) => { Object.assign(methods, {resolve, reject}); }); return Object.assign(promise, methods); }; ================================================ FILE: lib/utils/max-listeners.js ================================================ import {addAbortListener} from 'node:events'; // Temporarily increase the maximum number of listeners on an eventEmitter export const incrementMaxListeners = (eventEmitter, maxListenersIncrement, signal) => { const maxListeners = eventEmitter.getMaxListeners(); if (maxListeners === 0 || maxListeners === Number.POSITIVE_INFINITY) { return; } eventEmitter.setMaxListeners(maxListeners + maxListenersIncrement); addAbortListener(signal, () => { eventEmitter.setMaxListeners(eventEmitter.getMaxListeners() - maxListenersIncrement); }); }; ================================================ FILE: lib/utils/standard-stream.js ================================================ import process from 'node:process'; export const isStandardStream = stream => STANDARD_STREAMS.includes(stream); export const STANDARD_STREAMS = [process.stdin, process.stdout, process.stderr]; export const STANDARD_STREAMS_ALIASES = ['stdin', 'stdout', 'stderr']; export const getStreamName = fdNumber => STANDARD_STREAMS_ALIASES[fdNumber] ?? `stdio[${fdNumber}]`; ================================================ FILE: lib/utils/uint-array.js ================================================ import {StringDecoder} from 'node:string_decoder'; const {toString: objectToString} = Object.prototype; export const isArrayBuffer = value => objectToString.call(value) === '[object ArrayBuffer]'; // Is either Uint8Array or Buffer export const isUint8Array = value => objectToString.call(value) === '[object Uint8Array]'; export const bufferToUint8Array = buffer => new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); const textEncoder = new TextEncoder(); const stringToUint8Array = string => textEncoder.encode(string); const textDecoder = new TextDecoder(); export const uint8ArrayToString = uint8Array => textDecoder.decode(uint8Array); export const joinToString = (uint8ArraysOrStrings, encoding) => { const strings = uint8ArraysToStrings(uint8ArraysOrStrings, encoding); return strings.join(''); }; const uint8ArraysToStrings = (uint8ArraysOrStrings, encoding) => { if (encoding === 'utf8' && uint8ArraysOrStrings.every(uint8ArrayOrString => typeof uint8ArrayOrString === 'string')) { return uint8ArraysOrStrings; } const decoder = new StringDecoder(encoding); const strings = uint8ArraysOrStrings .map(uint8ArrayOrString => typeof uint8ArrayOrString === 'string' ? stringToUint8Array(uint8ArrayOrString) : uint8ArrayOrString) .map(uint8Array => decoder.write(uint8Array)); const finalString = decoder.end(); return finalString === '' ? strings : [...strings, finalString]; }; export const joinToUint8Array = uint8ArraysOrStrings => { if (uint8ArraysOrStrings.length === 1 && isUint8Array(uint8ArraysOrStrings[0])) { return uint8ArraysOrStrings[0]; } return concatUint8Arrays(stringsToUint8Arrays(uint8ArraysOrStrings)); }; const stringsToUint8Arrays = uint8ArraysOrStrings => uint8ArraysOrStrings.map(uint8ArrayOrString => typeof uint8ArrayOrString === 'string' ? stringToUint8Array(uint8ArrayOrString) : uint8ArrayOrString); export const concatUint8Arrays = uint8Arrays => { const result = new Uint8Array(getJoinLength(uint8Arrays)); let index = 0; for (const uint8Array of uint8Arrays) { result.set(uint8Array, index); index += uint8Array.length; } return result; }; const getJoinLength = uint8Arrays => { let joinLength = 0; for (const uint8Array of uint8Arrays) { joinLength += uint8Array.length; } return joinLength; }; ================================================ FILE: lib/verbose/complete.js ================================================ import prettyMs from 'pretty-ms'; import {isVerbose} from './values.js'; import {verboseLog} from './log.js'; import {logError} from './error.js'; // When `verbose` is `short|full|custom`, print each command's completion, duration and error export const logResult = (result, verboseInfo) => { if (!isVerbose(verboseInfo)) { return; } logError(result, verboseInfo); logDuration(result, verboseInfo); }; const logDuration = (result, verboseInfo) => { const verboseMessage = `(done in ${prettyMs(result.durationMs)})`; verboseLog({ type: 'duration', verboseMessage, verboseInfo, result, }); }; ================================================ FILE: lib/verbose/custom.js ================================================ import {getVerboseFunction} from './values.js'; // Apply the `verbose` function on each line export const applyVerboseOnLines = (printedLines, verboseInfo, fdNumber) => { const verboseFunction = getVerboseFunction(verboseInfo, fdNumber); return printedLines .map(({verboseLine, verboseObject}) => applyVerboseFunction(verboseLine, verboseObject, verboseFunction)) .filter(printedLine => printedLine !== undefined) .map(printedLine => appendNewline(printedLine)) .join(''); }; const applyVerboseFunction = (verboseLine, verboseObject, verboseFunction) => { if (verboseFunction === undefined) { return verboseLine; } const printedLine = verboseFunction(verboseLine, verboseObject); if (typeof printedLine === 'string') { return printedLine; } }; const appendNewline = printedLine => printedLine.endsWith('\n') ? printedLine : `${printedLine}\n`; ================================================ FILE: lib/verbose/default.js ================================================ import figures from 'figures'; import { gray, bold, redBright, yellowBright, } from 'yoctocolors'; // Default when `verbose` is not a function export const defaultVerboseFunction = ({ type, message, timestamp, piped, commandId, result: {failed = false} = {}, options: {reject = true}, }) => { const timestampString = serializeTimestamp(timestamp); const icon = ICONS[type]({failed, reject, piped}); const color = COLORS[type]({reject}); return `${gray(`[${timestampString}]`)} ${gray(`[${commandId}]`)} ${color(icon)} ${color(message)}`; }; // Prepending the timestamp allows debugging the slow paths of a subprocess const serializeTimestamp = timestamp => `${padField(timestamp.getHours(), 2)}:${padField(timestamp.getMinutes(), 2)}:${padField(timestamp.getSeconds(), 2)}.${padField(timestamp.getMilliseconds(), 3)}`; const padField = (field, padding) => String(field).padStart(padding, '0'); const getFinalIcon = ({failed, reject}) => { if (!failed) { return figures.tick; } return reject ? figures.cross : figures.warning; }; const ICONS = { command: ({piped}) => piped ? '|' : '$', output: () => ' ', ipc: () => '*', error: getFinalIcon, duration: getFinalIcon, }; const identity = string => string; const COLORS = { command: () => bold, output: () => identity, ipc: () => identity, error: ({reject}) => reject ? redBright : yellowBright, duration: () => gray, }; ================================================ FILE: lib/verbose/error.js ================================================ import {verboseLog} from './log.js'; // When `verbose` is `short|full|custom`, print each command's error when it fails export const logError = (result, verboseInfo) => { if (result.failed) { verboseLog({ type: 'error', verboseMessage: result.shortMessage, verboseInfo, result, }); } }; ================================================ FILE: lib/verbose/info.js ================================================ import {isVerbose, VERBOSE_VALUES, isVerboseFunction} from './values.js'; // Information computed before spawning, used by the `verbose` option export const getVerboseInfo = (verbose, escapedCommand, rawOptions) => { validateVerbose(verbose); const commandId = getCommandId(verbose); return { verbose, escapedCommand, commandId, rawOptions, }; }; const getCommandId = verbose => isVerbose({verbose}) ? COMMAND_ID++ : undefined; // Prepending the `pid` is useful when multiple commands print their output at the same time. // However, we cannot use the real PID since this is not available with `child_process.spawnSync()`. // Also, we cannot use the real PID if we want to print it before `child_process.spawn()` is run. // As a pro, it is shorter than a normal PID and never re-uses the same id. // As a con, it cannot be used to send signals. let COMMAND_ID = 0n; const validateVerbose = verbose => { for (const fdVerbose of verbose) { if (fdVerbose === false) { throw new TypeError('The "verbose: false" option was renamed to "verbose: \'none\'".'); } if (fdVerbose === true) { throw new TypeError('The "verbose: true" option was renamed to "verbose: \'short\'".'); } if (!VERBOSE_VALUES.includes(fdVerbose) && !isVerboseFunction(fdVerbose)) { const allowedValues = VERBOSE_VALUES.map(allowedValue => `'${allowedValue}'`).join(', '); throw new TypeError(`The "verbose" option must not be ${fdVerbose}. Allowed values are: ${allowedValues} or a function.`); } } }; ================================================ FILE: lib/verbose/ipc.js ================================================ import {verboseLog, serializeVerboseMessage} from './log.js'; import {isFullVerbose} from './values.js'; // When `verbose` is `'full'`, print IPC messages from the subprocess export const shouldLogIpc = verboseInfo => isFullVerbose(verboseInfo, 'ipc'); export const logIpcOutput = (message, verboseInfo) => { const verboseMessage = serializeVerboseMessage(message); verboseLog({ type: 'ipc', verboseMessage, fdNumber: 'ipc', verboseInfo, }); }; ================================================ FILE: lib/verbose/log.js ================================================ import {inspect} from 'node:util'; import {escapeLines} from '../arguments/escape.js'; import {defaultVerboseFunction} from './default.js'; import {applyVerboseOnLines} from './custom.js'; // This prints on stderr. // If the subprocess prints on stdout and is using `stdout: 'inherit'`, // there is a chance both writes will compete (introducing a race condition). // This means their respective order is not deterministic. // In particular, this means the verbose command lines might be after the start of the subprocess output. // Using synchronous I/O does not solve this problem. // However, this only seems to happen when the stdout/stderr target // (e.g. a terminal) is being written to by many subprocesses at once, which is unlikely in real scenarios. export const verboseLog = ({type, verboseMessage, fdNumber, verboseInfo, result}) => { const verboseObject = getVerboseObject({type, result, verboseInfo}); const printedLines = getPrintedLines(verboseMessage, verboseObject); const finalLines = applyVerboseOnLines(printedLines, verboseInfo, fdNumber); if (finalLines !== '') { console.warn(finalLines.slice(0, -1)); } }; const getVerboseObject = ({ type, result, verboseInfo: {escapedCommand, commandId, rawOptions: {piped = false, ...options}}, }) => ({ type, escapedCommand, commandId: `${commandId}`, timestamp: new Date(), piped, result, options, }); const getPrintedLines = (verboseMessage, verboseObject) => verboseMessage .split('\n') .map(message => getPrintedLine({...verboseObject, message})); const getPrintedLine = verboseObject => { const verboseLine = defaultVerboseFunction(verboseObject); return {verboseLine, verboseObject}; }; // Serialize any type to a line string, for logging export const serializeVerboseMessage = message => { const messageString = typeof message === 'string' ? message : inspect(message); const escapedMessage = escapeLines(messageString); return escapedMessage.replaceAll('\t', ' '.repeat(TAB_SIZE)); }; // Same as `util.inspect()` const TAB_SIZE = 2; ================================================ FILE: lib/verbose/output.js ================================================ import {BINARY_ENCODINGS} from '../arguments/encoding-option.js'; import {TRANSFORM_TYPES} from '../stdio/type.js'; import {verboseLog, serializeVerboseMessage} from './log.js'; import {isFullVerbose} from './values.js'; // `ignore` opts-out of `verbose` for a specific stream. // `ipc` cannot use piping. // `inherit` would result in double printing. // They can also lead to double printing when passing file descriptor integers or `process.std*`. // This only leaves with `pipe` and `overlapped`. export const shouldLogOutput = ({stdioItems, encoding, verboseInfo, fdNumber}) => fdNumber !== 'all' && isFullVerbose(verboseInfo, fdNumber) && !BINARY_ENCODINGS.has(encoding) && fdUsesVerbose(fdNumber) && (stdioItems.some(({type, value}) => type === 'native' && PIPED_STDIO_VALUES.has(value)) || stdioItems.every(({type}) => TRANSFORM_TYPES.has(type))); // Printing input streams would be confusing. // Files and streams can produce big outputs, which we don't want to print. // We could print `stdio[3+]` but it often is redirected to files and streams, with the same issue. // So we only print stdout and stderr. const fdUsesVerbose = fdNumber => fdNumber === 1 || fdNumber === 2; const PIPED_STDIO_VALUES = new Set(['pipe', 'overlapped']); // `verbose: 'full'` printing logic with async methods export const logLines = async (linesIterable, stream, fdNumber, verboseInfo) => { for await (const line of linesIterable) { if (!isPipingStream(stream)) { logLine(line, fdNumber, verboseInfo); } } }; // `verbose: 'full'` printing logic with sync methods export const logLinesSync = (linesArray, fdNumber, verboseInfo) => { for (const line of linesArray) { logLine(line, fdNumber, verboseInfo); } }; // When `subprocess.stdout|stderr.pipe()` is called, `verbose` becomes a noop. // This prevents the following problems: // - `.pipe()` achieves the same result as using `stdout: 'inherit'`, `stdout: stream`, etc. which also make `verbose` a noop. // For example, `subprocess.stdout.pipe(process.stdin)` would print each line twice. // - When chaining subprocesses with `subprocess.pipe(otherSubprocess)`, only the last one should print its output. // Detecting whether `.pipe()` is impossible without monkey-patching it, so we use the following undocumented property. // This is not a critical behavior since changes of the following property would only make `verbose` more verbose. const isPipingStream = stream => stream._readableState.pipes.length > 0; // When `verbose` is `full`, print stdout|stderr const logLine = (line, fdNumber, verboseInfo) => { const verboseMessage = serializeVerboseMessage(line); verboseLog({ type: 'output', verboseMessage, fdNumber, verboseInfo, }); }; ================================================ FILE: lib/verbose/start.js ================================================ import {isVerbose} from './values.js'; import {verboseLog} from './log.js'; // When `verbose` is `short|full|custom`, print each command export const logCommand = (escapedCommand, verboseInfo) => { if (!isVerbose(verboseInfo)) { return; } verboseLog({ type: 'command', verboseMessage: escapedCommand, verboseInfo, }); }; ================================================ FILE: lib/verbose/values.js ================================================ import {getFdSpecificValue} from '../arguments/specific.js'; // The `verbose` option can have different values for `stdout`/`stderr` export const isVerbose = ({verbose}, fdNumber) => getFdVerbose(verbose, fdNumber) !== 'none'; // Whether IPC and output and logged export const isFullVerbose = ({verbose}, fdNumber) => !['none', 'short'].includes(getFdVerbose(verbose, fdNumber)); // The `verbose` option can be a function to customize logging export const getVerboseFunction = ({verbose}, fdNumber) => { const fdVerbose = getFdVerbose(verbose, fdNumber); return isVerboseFunction(fdVerbose) ? fdVerbose : undefined; }; // When using `verbose: {stdout, stderr, fd3, ipc}`: // - `verbose.stdout|stderr|fd3` is used for 'output' // - `verbose.ipc` is only used for 'ipc' // - highest `verbose.*` value is used for 'command', 'error' and 'duration' const getFdVerbose = (verbose, fdNumber) => fdNumber === undefined ? getFdGenericVerbose(verbose) : getFdSpecificValue(verbose, fdNumber); // When using `verbose: {stdout, stderr, fd3, ipc}` and logging is not specific to a file descriptor. // We then use the highest `verbose.*` value, using the following order: // - function > 'full' > 'short' > 'none' // - if several functions are defined: stdout > stderr > fd3 > ipc const getFdGenericVerbose = verbose => verbose.find(fdVerbose => isVerboseFunction(fdVerbose)) ?? VERBOSE_VALUES.findLast(fdVerbose => verbose.includes(fdVerbose)); // Whether the `verbose` option is customized using a function export const isVerboseFunction = fdVerbose => typeof fdVerbose === 'function'; export const VERBOSE_VALUES = ['none', 'short', 'full']; ================================================ FILE: license ================================================ MIT License Copyright (c) Sindre Sorhus (https://sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: package.json ================================================ { "name": "execa", "version": "9.6.1", "description": "Process execution for humans", "license": "MIT", "repository": "sindresorhus/execa", "funding": "https://github.com/sindresorhus/execa?sponsor=1", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, "type": "module", "exports": { "types": "./index.d.ts", "default": "./index.js" }, "sideEffects": false, "engines": { "node": "^18.19.0 || >=20.5.0" }, "scripts": { "test": "npm run lint && npm run unit && npm run type", "lint": "xo", "unit": "c8 --merge-async ava", "type": "tsd && tsc && npx --yes tsd@0.29.0 && npx --yes --package typescript@5.1 tsc" }, "files": [ "index.js", "index.d.ts", "lib/**/*.js", "types/**/*.ts" ], "keywords": [ "exec", "child", "process", "subprocess", "execute", "fork", "execfile", "spawn", "file", "shell", "bin", "binary", "binaries", "npm", "path", "local", "zx" ], "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" }, "devDependencies": { "@types/node": "^22.15.21", "ava": "^6.3.0", "c8": "^10.1.3", "get-node": "^15.0.3", "is-in-ci": "^1.0.0", "is-running": "^2.1.0", "log-process-errors": "^12.0.1", "path-exists": "^5.0.0", "path-key": "^4.0.0", "tempfile": "^5.0.0", "tsd": "^0.32.0", "typescript": "^5.8.3", "which": "^5.0.0", "xo": "^0.60.0" }, "c8": { "reporter": [ "text", "lcov" ], "exclude": [ "**/fixtures/**", "**/test.js", "**/test/**" ] }, "ava": { "workerThreads": false, "concurrency": 1, "timeout": "240s" }, "xo": { "rules": { "unicorn/no-empty-file": "off", "@typescript-eslint/ban-types": "off" } } } ================================================ FILE: readme.md ================================================ execa logo
[![Coverage Status](https://codecov.io/gh/sindresorhus/execa/branch/main/graph/badge.svg)](https://codecov.io/gh/sindresorhus/execa) > Process execution for humans
--- ---
Execa runs commands in your script, application or library. Unlike shells, it is [optimized](docs/bash.md) for programmatic usage. Built on top of the [`child_process`](https://nodejs.org/api/child_process.html) core module. ## Features - [Simple syntax](#simple-syntax): promises and [template strings](docs/execution.md#template-string-syntax), like [`zx`](docs/bash.md). - [Script](#script) interface. - [No escaping](docs/escaping.md) nor quoting needed. No risk of shell injection. - Execute [locally installed binaries](#local-binaries) without `npx`. - Improved [Windows support](docs/windows.md): [shebangs](docs/windows.md#shebang), [`PATHEXT`](https://ss64.com/nt/path.html#pathext), [graceful termination](#graceful-termination), [and more](https://github.com/moxystudio/node-cross-spawn?tab=readme-ov-file#why). - [Detailed errors](#detailed-error), [verbose mode](#verbose-mode) and [custom logging](#custom-logging), for [debugging](docs/debugging.md). - [Pipe multiple subprocesses](#pipe-multiple-subprocesses) better than in shells: retrieve [intermediate results](docs/pipe.md#result), use multiple [sources](docs/pipe.md#multiple-sources-1-destination)/[destinations](docs/pipe.md#1-source-multiple-destinations), [unpipe](docs/pipe.md#unpipe). - [Split](#split-into-text-lines) the output into text lines, or [iterate](#iterate-over-text-lines) progressively over them. - Strip [unnecessary newlines](docs/lines.md#newlines). - Pass any [input](docs/input.md) to the subprocess: [files](#file-input), [strings](#simple-input), [`Uint8Array`s](docs/binary.md#binary-input), [iterables](docs/streams.md#iterables-as-input), [objects](docs/transform.md#object-mode) and almost any [other type](#any-input-type). - Return [almost any type](#any-output-type) from the subprocess, or redirect it to [files](#file-output). - Get [interleaved output](#interleaved-output) from `stdout` and `stderr` similar to what is printed on the terminal. - Retrieve the output [programmatically and print it](#programmatic--terminal-output) on the console at the same time. - [Transform or filter](#transformfilter-output) the input and output with [simple functions](docs/transform.md). - Pass [Node.js streams](docs/streams.md#nodejs-streams) or [web streams](#web-streams) to subprocesses, or [convert](#convert-to-duplex-stream) subprocesses to [a stream](docs/streams.md#converting-a-subprocess-to-a-stream). - [Exchange messages](#exchange-messages) with the subprocess. - Ensure subprocesses exit even when they [intercept termination signals](docs/termination.md#forceful-termination), or when the current process [ends abruptly](docs/termination.md#current-process-exit). ## Install ```sh npm install execa ``` ## Documentation Execution: - ▶️ [Basic execution](docs/execution.md) - 💬 [Escaping/quoting](docs/escaping.md) - 💻 [Shell](docs/shell.md) - 📜 [Scripts](docs/scripts.md) - 🐢 [Node.js files](docs/node.md) - 🌐 [Environment](docs/environment.md) - ❌ [Errors](docs/errors.md) - 🏁 [Termination](docs/termination.md) Input/output: - 🎹 [Input](docs/input.md) - 📢 [Output](docs/output.md) - 📃 [Text lines](docs/lines.md) - 🤖 [Binary data](docs/binary.md) - 🧙 [Transforms](docs/transform.md) Advanced usage: - 🔀 [Piping multiple subprocesses](docs/pipe.md) - ⏳️ [Streams](docs/streams.md) - 📞 [Inter-process communication](docs/ipc.md) - 🐛 [Debugging](docs/debugging.md) - 📎 [Windows](docs/windows.md) - 🔍 [Difference with Bash and zx](docs/bash.md) - 🐭 [Small packages](docs/small.md) - 🤓 [TypeScript](docs/typescript.md) - 📔 [API reference](docs/api.md) ## Examples ### Execution #### Simple syntax ```js import {execa} from 'execa'; const {stdout} = await execa`npm run build`; // Print command's output console.log(stdout); ``` #### Script ```js import {$} from 'execa'; const {stdout: name} = await $`cat package.json`.pipe`grep name`; console.log(name); const branch = await $`git branch --show-current`; await $`dep deploy --branch=${branch}`; await Promise.all([ $`sleep 1`, $`sleep 2`, $`sleep 3`, ]); const directoryName = 'foo bar'; await $`mkdir /tmp/${directoryName}`; ``` #### Local binaries ```sh $ npm install -D eslint ``` ```js await execa({preferLocal: true})`eslint`; ``` #### Pipe multiple subprocesses ```js const {stdout, pipedFrom} = await execa`npm run build` .pipe`sort` .pipe`head -n 2`; // Output of `npm run build | sort | head -n 2` console.log(stdout); // Output of `npm run build | sort` console.log(pipedFrom[0].stdout); // Output of `npm run build` console.log(pipedFrom[0].pipedFrom[0].stdout); ``` ### Input/output #### Interleaved output ```js const {all} = await execa({all: true})`npm run build`; // stdout + stderr, interleaved console.log(all); ``` #### Programmatic + terminal output ```js const {stdout} = await execa({stdout: ['pipe', 'inherit']})`npm run build`; // stdout is also printed to the terminal console.log(stdout); ``` #### Simple input ```js const getInputString = () => { /* ... */ }; const {stdout} = await execa({input: getInputString()})`sort`; console.log(stdout); ``` #### File input ```js // Similar to: npm run build < input.txt await execa({stdin: {file: 'input.txt'}})`npm run build`; ``` #### File output ```js // Similar to: npm run build > output.txt await execa({stdout: {file: 'output.txt'}})`npm run build`; ``` #### Split into text lines ```js const {stdout} = await execa({lines: true})`npm run build`; // Print first 10 lines console.log(stdout.slice(0, 10).join('\n')); ``` ### Streaming #### Iterate over text lines ```js for await (const line of execa`npm run build`) { if (line.includes('WARN')) { console.warn(line); } } ``` #### Transform/filter output ```js let count = 0; // Filter out secret lines, then prepend the line number const transform = function * (line) { if (!line.includes('secret')) { yield `[${count++}] ${line}`; } }; await execa({stdout: transform})`npm run build`; ``` #### Web streams ```js const response = await fetch('https://example.com'); await execa({stdin: response.body})`sort`; ``` #### Convert to Duplex stream ```js import {execa} from 'execa'; import {pipeline} from 'node:stream/promises'; import {createReadStream, createWriteStream} from 'node:fs'; await pipeline( createReadStream('./input.txt'), execa`node ./transform.js`.duplex(), createWriteStream('./output.txt'), ); ``` ### IPC #### Exchange messages ```js // parent.js import {execaNode} from 'execa'; const subprocess = execaNode`child.js`; await subprocess.sendMessage('Hello from parent'); const message = await subprocess.getOneMessage(); console.log(message); // 'Hello from child' ``` ```js // child.js import {getOneMessage, sendMessage} from 'execa'; const message = await getOneMessage(); // 'Hello from parent' const newMessage = message.replace('parent', 'child'); // 'Hello from child' await sendMessage(newMessage); ``` #### Any input type ```js // main.js import {execaNode} from 'execa'; const ipcInput = [ {task: 'lint', ignore: /test\.js/}, {task: 'copy', files: new Set(['main.js', 'index.js']), }]; await execaNode({ipcInput})`build.js`; ``` ```js // build.js import {getOneMessage} from 'execa'; const ipcInput = await getOneMessage(); ``` #### Any output type ```js // main.js import {execaNode} from 'execa'; const {ipcOutput} = await execaNode`build.js`; console.log(ipcOutput[0]); // {kind: 'start', timestamp: date} console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date} ``` ```js // build.js import {sendMessage} from 'execa'; const runBuild = () => { /* ... */ }; await sendMessage({kind: 'start', timestamp: new Date()}); await runBuild(); await sendMessage({kind: 'stop', timestamp: new Date()}); ``` #### Graceful termination ```js // main.js import {execaNode} from 'execa'; const controller = new AbortController(); setTimeout(() => { controller.abort(); }, 5000); await execaNode({ cancelSignal: controller.signal, gracefulCancel: true, })`build.js`; ``` ```js // build.js import {getCancelSignal} from 'execa'; const cancelSignal = await getCancelSignal(); const url = 'https://example.com/build/info'; const response = await fetch(url, {signal: cancelSignal}); ``` ### Debugging #### Detailed error ```js import {execa, ExecaError} from 'execa'; try { await execa`unknown command`; } catch (error) { if (error instanceof ExecaError) { console.log(error); } /* ExecaError: Command failed with ENOENT: unknown command spawn unknown ENOENT at ... at ... { shortMessage: 'Command failed with ENOENT: unknown command\nspawn unknown ENOENT', originalMessage: 'spawn unknown ENOENT', command: 'unknown command', escapedCommand: 'unknown command', cwd: '/path/to/cwd', durationMs: 28.217566, failed: true, timedOut: false, isCanceled: false, isTerminated: false, isMaxBuffer: false, code: 'ENOENT', stdout: '', stderr: '', stdio: [undefined, '', ''], pipedFrom: [] [cause]: Error: spawn unknown ENOENT at ... at ... { errno: -2, code: 'ENOENT', syscall: 'spawn unknown', path: 'unknown', spawnargs: [ 'command' ] } } */ } ``` #### Verbose mode ```js await execa`npm run build`; await execa`npm run test`; ``` execa verbose output #### Custom logging ```js import {execa as execa_} from 'execa'; import {createLogger, transports} from 'winston'; // Log to a file using Winston const transport = new transports.File({filename: 'logs.txt'}); const logger = createLogger({transports: [transport]}); const LOG_LEVELS = { command: 'info', output: 'verbose', ipc: 'verbose', error: 'error', duration: 'info', }; const execa = execa_({ verbose(verboseLine, {message, ...verboseObject}) { const level = LOG_LEVELS[verboseObject.type]; logger[level](message, verboseObject); }, }); await execa`npm run build`; await execa`npm run test`; ``` ## Related - [nano-spawn](https://github.com/sindresorhus/nano-spawn) - Like Execa but [smaller](docs/small.md) - [gulp-execa](https://github.com/ehmicky/gulp-execa) - Gulp plugin for Execa - [nvexeca](https://github.com/ehmicky/nvexeca) - Run Execa using any Node.js version ## Maintainers - [Sindre Sorhus](https://github.com/sindresorhus) - [@ehmicky](https://github.com/ehmicky) ================================================ FILE: test/arguments/cwd.js ================================================ import {mkdir, rmdir} from 'node:fs/promises'; import path from 'node:path'; import process from 'node:process'; import {pathToFileURL, fileURLToPath} from 'node:url'; import tempfile from 'tempfile'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {FIXTURES_DIRECTORY, setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {majorNodeVersion} from '../helpers/node-version.js'; setFixtureDirectory(); const isWindows = process.platform === 'win32'; const testOptionCwdString = async (t, execaMethod) => { const cwd = '/'; const {stdout} = await execaMethod('node', ['-p', 'process.cwd()'], {cwd}); t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); }; test('The "cwd" option can be a string', testOptionCwdString, execa); test('The "cwd" option can be a string - sync', testOptionCwdString, execaSync); const testOptionCwdUrl = async (t, execaMethod) => { const cwd = '/'; const cwdUrl = pathToFileURL(cwd); const {stdout} = await execaMethod('node', ['-p', 'process.cwd()'], {cwd: cwdUrl}); t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); }; test('The "cwd" option can be a URL', testOptionCwdUrl, execa); test('The "cwd" option can be a URL - sync', testOptionCwdUrl, execaSync); const testOptionCwdInvalid = (t, execaMethod) => { t.throws(() => { execaMethod('empty.js', {cwd: true}); }, {message: /The "cwd" option must be a string or a file URL: true/}); }; test('The "cwd" option cannot be an invalid type', testOptionCwdInvalid, execa); test('The "cwd" option cannot be an invalid type - sync', testOptionCwdInvalid, execaSync); const testErrorCwdDefault = async (t, execaMethod) => { const {cwd} = await execaMethod('empty.js'); t.is(cwd, process.cwd()); }; test('The "cwd" option defaults to process.cwd()', testErrorCwdDefault, execa); test('The "cwd" option defaults to process.cwd() - sync', testErrorCwdDefault, execaSync); // Windows does not allow removing a directory used as `cwd` of a running subprocess if (!isWindows) { const testCwdPreSpawn = async (t, execaMethod) => { const currentCwd = process.cwd(); const filePath = tempfile(); await mkdir(filePath); process.chdir(filePath); await rmdir(filePath); try { t.throws(() => { execaMethod('empty.js'); }, {message: /The current directory does not exist/}); } finally { process.chdir(currentCwd); } }; test.serial('The "cwd" option default fails if current cwd is missing', testCwdPreSpawn, execa); test.serial('The "cwd" option default fails if current cwd is missing - sync', testCwdPreSpawn, execaSync); } const cwdNotExisting = {cwd: 'does_not_exist', expectedCode: 'ENOENT', expectedMessage: 'The "cwd" option is invalid'}; const cwdTooLong = {cwd: '.'.repeat(1e5), expectedCode: isWindows && majorNodeVersion >= 20 ? 'ENOENT' : 'ENAMETOOLONG', expectedMessage: 'The "cwd" option is invalid'}; // @todo: use import.meta.dirname after dropping support for Node <20.11.0 const cwdNotDirectory = {cwd: fileURLToPath(import.meta.url), expectedCode: isWindows ? 'ENOENT' : 'ENOTDIR', expectedMessage: 'The "cwd" option is not a directory'}; const testCwdPostSpawn = async (t, {cwd, expectedCode, expectedMessage}, execaMethod) => { const {failed, code, message} = await execaMethod('empty.js', {cwd, reject: false}); t.true(failed); t.is(code, expectedCode); t.true(message.includes(expectedMessage)); t.true(message.includes(cwd)); }; test('The "cwd" option must be an existing file', testCwdPostSpawn, cwdNotExisting, execa); test('The "cwd" option must be an existing file - sync', testCwdPostSpawn, cwdNotExisting, execaSync); test('The "cwd" option must not be too long', testCwdPostSpawn, cwdTooLong, execa); test('The "cwd" option must not be too long - sync', testCwdPostSpawn, cwdTooLong, execaSync); test('The "cwd" option must be a directory', testCwdPostSpawn, cwdNotDirectory, execa); test('The "cwd" option must be a directory - sync', testCwdPostSpawn, cwdNotDirectory, execaSync); const successProperties = {fixtureName: 'empty.js', expectedFailed: false}; const errorProperties = {fixtureName: 'fail.js', expectedFailed: true}; const testErrorCwd = async (t, execaMethod, {fixtureName, expectedFailed}) => { const {failed, cwd} = await execaMethod(fixtureName, {cwd: path.relative('.', FIXTURES_DIRECTORY), reject: false}); t.is(failed, expectedFailed); t.is(cwd, FIXTURES_DIRECTORY); }; test('result.cwd is defined', testErrorCwd, execa, successProperties); test('result.cwd is defined - sync', testErrorCwd, execaSync, successProperties); test('error.cwd is defined', testErrorCwd, execa, errorProperties); test('error.cwd is defined - sync', testErrorCwd, execaSync, errorProperties); ================================================ FILE: test/arguments/encoding-option.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const testInvalidEncoding = (t, encoding, message, execaMethod) => { const error = t.throws(() => { execaMethod('empty.js', {encoding}); }); t.true(error.message.includes(message)); }; const UNKNOWN_ENCODING_MESSAGE = 'Please rename it to one of'; const getCorrectEncodingMessage = correctEncoding => `Please rename it to "${correctEncoding}"`; test('cannot pass unknown encodings', testInvalidEncoding, 'unknown', UNKNOWN_ENCODING_MESSAGE, execa); test('cannot pass unknown encodings, sync', testInvalidEncoding, 'unknown', UNKNOWN_ENCODING_MESSAGE, execaSync); test('cannot pass empty encodings', testInvalidEncoding, '', UNKNOWN_ENCODING_MESSAGE, execa); test('cannot pass encoding: false', testInvalidEncoding, false, UNKNOWN_ENCODING_MESSAGE, execa); test('cannot pass encoding: Symbol', testInvalidEncoding, Symbol('test'), UNKNOWN_ENCODING_MESSAGE, execa); test('cannot pass encoding: null', testInvalidEncoding, null, getCorrectEncodingMessage('buffer'), execa); test('cannot pass encoding: null, sync', testInvalidEncoding, null, getCorrectEncodingMessage('buffer'), execaSync); /* eslint-disable unicorn/text-encoding-identifier-case */ test('cannot pass encoding: utf-8', testInvalidEncoding, 'utf-8', getCorrectEncodingMessage('utf8'), execa); test('cannot pass encoding: utf-8, sync', testInvalidEncoding, 'utf-8', getCorrectEncodingMessage('utf8'), execaSync); test('cannot pass encoding: UTF-8', testInvalidEncoding, 'UTF-8', getCorrectEncodingMessage('utf8'), execa); test('cannot pass encoding: UTF-8, sync', testInvalidEncoding, 'UTF-8', getCorrectEncodingMessage('utf8'), execaSync); test('cannot pass encoding: UTF8', testInvalidEncoding, 'UTF8', getCorrectEncodingMessage('utf8'), execa); test('cannot pass encoding: UTF8, sync', testInvalidEncoding, 'UTF8', getCorrectEncodingMessage('utf8'), execaSync); /* eslint-enable unicorn/text-encoding-identifier-case */ test('cannot pass encoding: utf-16le', testInvalidEncoding, 'utf-16le', getCorrectEncodingMessage('utf16le'), execa); test('cannot pass encoding: UTF-16LE', testInvalidEncoding, 'UTF-16LE', getCorrectEncodingMessage('utf16le'), execa); test('cannot pass encoding: UTF16LE', testInvalidEncoding, 'UTF16LE', getCorrectEncodingMessage('utf16le'), execa); test('cannot pass encoding: ucs2', testInvalidEncoding, 'ucs2', getCorrectEncodingMessage('utf16le'), execa); test('cannot pass encoding: UCS2', testInvalidEncoding, 'UCS2', getCorrectEncodingMessage('utf16le'), execa); test('cannot pass encoding: ucs-2', testInvalidEncoding, 'ucs-2', getCorrectEncodingMessage('utf16le'), execa); test('cannot pass encoding: UCS-2', testInvalidEncoding, 'UCS-2', getCorrectEncodingMessage('utf16le'), execa); test('cannot pass encoding: binary', testInvalidEncoding, 'binary', getCorrectEncodingMessage('latin1'), execa); ================================================ FILE: test/arguments/env.js ================================================ import process from 'node:process'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory, PATH_KEY} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); process.env.FOO = 'foo'; const isWindows = process.platform === 'win32'; test('use environment variables by default', async t => { const {stdout} = await execa('environment.js'); t.deepEqual(stdout.split('\n'), ['foo', 'undefined']); }); test('extend environment variables by default', async t => { const {stdout} = await execa('environment.js', [], {env: {BAR: 'bar', [PATH_KEY]: process.env[PATH_KEY]}}); t.deepEqual(stdout.split('\n'), ['foo', 'bar']); }); test('do not extend environment with `extendEnv: false`', async t => { const {stdout} = await execa('environment.js', [], {env: {BAR: 'bar', [PATH_KEY]: process.env[PATH_KEY]}, extendEnv: false}); t.deepEqual(stdout.split('\n'), ['undefined', 'bar']); }); test('use extend environment with `extendEnv: true` and `shell: true`', async t => { process.env.TEST = 'test'; const command = isWindows ? 'echo %TEST%' : 'echo $TEST'; const {stdout} = await execa(command, {shell: true, env: {}, extendEnv: true}); t.is(stdout, 'test'); delete process.env.TEST; }); ================================================ FILE: test/arguments/escape-no-icu.js ================================================ // Mimics Node.js when built without ICU support // See https://github.com/sindresorhus/execa/issues/1143 globalThis.RegExp = class extends RegExp { constructor(regExpString, flags) { if (flags?.includes('u') && regExpString.includes('\\p{')) { throw new Error('Invalid property name'); } super(regExpString, flags); } static isMocked = true; }; // Execa computes the RegExp when first loaded, so we must delay this import await import('./escape.js'); ================================================ FILE: test/arguments/escape.js ================================================ import {platform} from 'node:process'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const isWindows = platform === 'win32'; const testResultCommand = async (t, expected, ...commandArguments) => { const {command: failCommand} = await t.throwsAsync(execa('fail.js', commandArguments)); t.is(failCommand, `fail.js${expected}`); const {command} = await execa('noop.js', commandArguments); t.is(command, `noop.js${expected}`); }; testResultCommand.title = (message, expected) => `result.command is: ${JSON.stringify(expected)}`; test(testResultCommand, ' foo bar', 'foo', 'bar'); test(testResultCommand, ' baz quz', 'baz', 'quz'); test(testResultCommand, ''); // eslint-disable-next-line max-params const testEscapedCommand = async (t, commandArguments, expectedUnix, expectedWindows, expectedUnixNoIcu = expectedUnix, expectedWindowsNoIcu = expectedWindows) => { const expected = RegExp.isMocked ? (isWindows ? expectedWindowsNoIcu : expectedUnixNoIcu) : (isWindows ? expectedWindows : expectedUnix); t.like( await t.throwsAsync(execa('fail.js', commandArguments)), {escapedCommand: `fail.js ${expected}`}, ); t.like(t.throws(() => { execaSync('fail.js', commandArguments); }), {escapedCommand: `fail.js ${expected}`}); t.like( await execa('noop.js', commandArguments), {escapedCommand: `noop.js ${expected}`}, ); t.like( execaSync('noop.js', commandArguments), {escapedCommand: `noop.js ${expected}`}, ); }; test('result.escapedCommand - foo bar', testEscapedCommand, ['foo', 'bar'], 'foo bar', 'foo bar'); test('result.escapedCommand - foo\\ bar', testEscapedCommand, ['foo bar'], '\'foo bar\'', '"foo bar"'); test('result.escapedCommand - "foo"', testEscapedCommand, ['"foo"'], '\'"foo"\'', '"""foo"""'); test('result.escapedCommand - \'foo\'', testEscapedCommand, ['\'foo\''], '\'\'\\\'\'foo\'\\\'\'\'', '"\'foo\'"'); test('result.escapedCommand - "0"', testEscapedCommand, ['0'], '0', '0'); test('result.escapedCommand - 0', testEscapedCommand, [0], '0', '0'); test('result.escapedCommand - *', testEscapedCommand, ['*'], '\'*\'', '"*"'); test('result.escapedCommand - .', testEscapedCommand, ['.'], '.', '.'); test('result.escapedCommand - -', testEscapedCommand, ['-'], '-', '-'); test('result.escapedCommand - _', testEscapedCommand, ['_'], '_', '_'); test('result.escapedCommand - /', testEscapedCommand, ['/'], '/', '/'); test('result.escapedCommand - ,', testEscapedCommand, [','], '\',\'', '","'); test('result.escapedCommand - :', testEscapedCommand, [':'], '\':\'', '":"'); test('result.escapedCommand - ;', testEscapedCommand, [';'], '\';\'', '";"'); test('result.escapedCommand - ~', testEscapedCommand, ['~'], '\'~\'', '"~"'); test('result.escapedCommand - %', testEscapedCommand, ['%'], '\'%\'', '"%"'); test('result.escapedCommand - $', testEscapedCommand, ['$'], '\'$\'', '"$"'); test('result.escapedCommand - !', testEscapedCommand, ['!'], '\'!\'', '"!"'); test('result.escapedCommand - ?', testEscapedCommand, ['?'], '\'?\'', '"?"'); test('result.escapedCommand - #', testEscapedCommand, ['#'], '\'#\'', '"#"'); test('result.escapedCommand - &', testEscapedCommand, ['&'], '\'&\'', '"&"'); test('result.escapedCommand - =', testEscapedCommand, ['='], '\'=\'', '"="'); test('result.escapedCommand - @', testEscapedCommand, ['@'], '\'@\'', '"@"'); test('result.escapedCommand - ^', testEscapedCommand, ['^'], '\'^\'', '"^"'); test('result.escapedCommand - `', testEscapedCommand, ['`'], '\'`\'', '"`"'); test('result.escapedCommand - |', testEscapedCommand, ['|'], '\'|\'', '"|"'); test('result.escapedCommand - +', testEscapedCommand, ['+'], '\'+\'', '"+"'); test('result.escapedCommand - \\', testEscapedCommand, ['\\'], '\'\\\'', '"\\"'); test('result.escapedCommand - ()', testEscapedCommand, ['()'], '\'()\'', '"()"'); test('result.escapedCommand - {}', testEscapedCommand, ['{}'], '\'{}\'', '"{}"'); test('result.escapedCommand - []', testEscapedCommand, ['[]'], '\'[]\'', '"[]"'); test('result.escapedCommand - <>', testEscapedCommand, ['<>'], '\'<>\'', '"<>"'); test('result.escapedCommand - ã', testEscapedCommand, ['ã'], '\'ã\'', '"ã"'); test('result.escapedCommand - \\a', testEscapedCommand, ['\u0007'], '\'\\u0007\'', '"\\u0007"'); test('result.escapedCommand - \\b', testEscapedCommand, ['\b'], '\'\\b\'', '"\\b"'); test('result.escapedCommand - \\e', testEscapedCommand, ['\u001B'], '\'\\u001b\'', '"\\u001b"'); test('result.escapedCommand - \\f', testEscapedCommand, ['\f'], '\'\\f\'', '"\\f"'); test('result.escapedCommand - \\n', testEscapedCommand, ['\n'], '\'\\n\'', '"\\n"'); test('result.escapedCommand - \\r\\n', testEscapedCommand, ['\r\n'], '\'\\r\\n\'', '"\\r\\n"'); test('result.escapedCommand - \\t', testEscapedCommand, ['\t'], '\'\\t\'', '"\\t"'); test('result.escapedCommand - \\v', testEscapedCommand, ['\v'], '\'\\u000b\'', '"\\u000b"'); test('result.escapedCommand - \\x01', testEscapedCommand, ['\u0001'], '\'\\u0001\'', '"\\u0001"'); test('result.escapedCommand - \\x7f', testEscapedCommand, ['\u007F'], '\'\\u007f\'', '"\\u007f"'); test('result.escapedCommand - \\u0085', testEscapedCommand, ['\u0085'], '\'\\u0085\'', '"\\u0085"'); test('result.escapedCommand - \\u2000', testEscapedCommand, ['\u2000'], '\'\\u2000\'', '"\\u2000"'); test('result.escapedCommand - \\u200E', testEscapedCommand, ['\u200E'], '\'\\u200e\'', '"\\u200e"', '\'\u200E\'', '"\u200E"'); test('result.escapedCommand - \\u2028', testEscapedCommand, ['\u2028'], '\'\\u2028\'', '"\\u2028"'); test('result.escapedCommand - \\u2029', testEscapedCommand, ['\u2029'], '\'\\u2029\'', '"\\u2029"'); test('result.escapedCommand - \\u5555', testEscapedCommand, ['\u5555'], '\'\u5555\'', '"\u5555"'); test('result.escapedCommand - \\uD800', testEscapedCommand, ['\uD800'], '\'\\ud800\'', '"\\ud800"', '\'\uD800\'', '"\uD800"'); test('result.escapedCommand - \\uE000', testEscapedCommand, ['\uE000'], '\'\\ue000\'', '"\\ue000"', '\'\uE000\'', '"\uE000"'); test('result.escapedCommand - \\U1D172', testEscapedCommand, ['\u{1D172}'], '\'\u{1D172}\'', '"\u{1D172}"'); test('result.escapedCommand - \\U1D173', testEscapedCommand, ['\u{1D173}'], '\'\\U1d173\'', '"\\U1d173"', '\'\u{1D173}\'', '"\u{1D173}"'); test('result.escapedCommand - \\U10FFFD', testEscapedCommand, ['\u{10FFFD}'], '\'\\U10fffd\'', '"\\U10fffd"', '\'\u{10FFFD}\'', '"\u{10FFFD}"'); ================================================ FILE: test/arguments/fd-options.js ================================================ import {PassThrough} from 'node:stream'; import {spawn} from 'node:child_process'; import process from 'node:process'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio, getStdio} from '../helpers/stdio.js'; import {getEarlyErrorSubprocess} from '../helpers/early-error.js'; import {assertPipeError} from '../helpers/pipe.js'; setFixtureDirectory(); const getMessage = message => Array.isArray(message) ? `"${message[0]}: ${message[1]}" option is incompatible` : message; const testPipeError = async (t, { message, sourceOptions = {}, destinationOptions = {}, getSource = () => execa('empty.js', sourceOptions), getDestination = () => execa('empty.js', destinationOptions), isScript = false, from, to, }) => { const source = getSource(); const pipePromise = isScript ? source.pipe({from, to})`empty.js` : source.pipe(getDestination(), {from, to}); await assertPipeError(t, pipePromise, getMessage(message)); }; const testNodeStream = async (t, { message, sourceOptions = {}, getSource = () => execa('empty.js', sourceOptions), from, to, writable = to !== undefined, }) => { assertNodeStream({ t, message, getSource, from, to, methodName: writable ? 'writable' : 'readable', }); assertNodeStream({ t, message, getSource, from, to, methodName: 'duplex', }); }; const assertNodeStream = ({t, message, getSource, from, to, methodName}) => { const error = t.throws(() => { getSource()[methodName]({from, to}); }); t.true(error.message.includes(getMessage(message))); }; const testIterable = async (t, { message, sourceOptions = {}, getSource = () => execa('empty.js', sourceOptions), from, }) => { const error = t.throws(() => { getSource().iterable({from}); }); t.true(error.message.includes(getMessage(message))); }; test('Must set "all" option to "true" to use .pipe("all")', testPipeError, { from: 'all', message: '"all" option must be true', }); test('Must set "all" option to "true" to use .duplex("all")', testNodeStream, { from: 'all', message: '"all" option must be true', }); test('Must set "all" option to "true" to use .iterable("all")', testIterable, { from: 'all', message: '"all" option must be true', }); test('.pipe() cannot pipe to non-subprocesses', testPipeError, { getDestination: () => new PassThrough(), message: 'an Execa subprocess', }); test('.pipe() cannot pipe to non-Execa subprocesses', testPipeError, { getDestination: () => spawn('node', ['--version']), message: 'an Execa subprocess', }); test('.pipe() "from" option cannot be "stdin"', testPipeError, { from: 'stdin', message: '"from" must not be', }); test('.duplex() "from" option cannot be "stdin"', testNodeStream, { from: 'stdin', message: '"from" must not be', }); test('.iterable() "from" option cannot be "stdin"', testIterable, { from: 'stdin', message: '"from" must not be', }); test('$.pipe() "from" option cannot be "stdin"', testPipeError, { from: 'stdin', isScript: true, message: '"from" must not be', }); test('.pipe() "to" option cannot be "stdout"', testPipeError, { to: 'stdout', message: '"to" must not be', }); test('.duplex() "to" option cannot be "stdout"', testNodeStream, { to: 'stdout', message: '"to" must not be', }); test('$.pipe() "to" option cannot be "stdout"', testPipeError, { to: 'stdout', isScript: true, message: '"to" must not be', }); test('.pipe() "from" option cannot be any string', testPipeError, { from: 'other', message: 'must be "stdout", "stderr", "all"', }); test('.duplex() "from" option cannot be any string', testNodeStream, { from: 'other', message: 'must be "stdout", "stderr", "all"', }); test('.iterable() "from" option cannot be any string', testIterable, { from: 'other', message: 'must be "stdout", "stderr", "all"', }); test('.pipe() "to" option cannot be any string', testPipeError, { to: 'other', message: 'must be "stdin"', }); test('.duplex() "to" option cannot be any string', testNodeStream, { to: 'other', message: 'must be "stdin"', }); test('.pipe() "from" option cannot be a number without "fd"', testPipeError, { from: '1', message: 'must be "stdout", "stderr", "all"', }); test('.duplex() "from" option cannot be a number without "fd"', testNodeStream, { from: '1', message: 'must be "stdout", "stderr", "all"', }); test('.iterable() "from" option cannot be a number without "fd"', testIterable, { from: '1', message: 'must be "stdout", "stderr", "all"', }); test('.pipe() "to" option cannot be a number without "fd"', testPipeError, { to: '0', message: 'must be "stdin"', }); test('.duplex() "to" option cannot be a number without "fd"', testNodeStream, { to: '0', message: 'must be "stdin"', }); test('.pipe() "from" option cannot be just "fd"', testPipeError, { from: 'fd', message: 'must be "stdout", "stderr", "all"', }); test('.duplex() "from" option cannot be just "fd"', testNodeStream, { from: 'fd', message: 'must be "stdout", "stderr", "all"', }); test('.iterable() "from" option cannot be just "fd"', testIterable, { from: 'fd', message: 'must be "stdout", "stderr", "all"', }); test('.pipe() "to" option cannot be just "fd"', testPipeError, { to: 'fd', message: 'must be "stdin"', }); test('.duplex() "to" option cannot be just "fd"', testNodeStream, { to: 'fd', message: 'must be "stdin"', }); test('.pipe() "from" option cannot be a float', testPipeError, { from: 'fd1.5', message: 'must be "stdout", "stderr", "all"', }); test('.duplex() "from" option cannot be a float', testNodeStream, { from: 'fd1.5', message: 'must be "stdout", "stderr", "all"', }); test('.iterable() "from" option cannot be a float', testIterable, { from: 'fd1.5', message: 'must be "stdout", "stderr", "all"', }); test('.pipe() "to" option cannot be a float', testPipeError, { to: 'fd1.5', message: 'must be "stdin"', }); test('.duplex() "to" option cannot be a float', testNodeStream, { to: 'fd1.5', message: 'must be "stdin"', }); test('.pipe() "from" option cannot be a negative number', testPipeError, { from: 'fd-1', message: 'must be "stdout", "stderr", "all"', }); test('.duplex() "from" option cannot be a negative number', testNodeStream, { from: 'fd-1', message: 'must be "stdout", "stderr", "all"', }); test('.iterable() "from" option cannot be a negative number', testIterable, { from: 'fd-1', message: 'must be "stdout", "stderr", "all"', }); test('.pipe() "to" option cannot be a negative number', testPipeError, { to: 'fd-1', message: 'must be "stdin"', }); test('.duplex() "to" option cannot be a negative number', testNodeStream, { to: 'fd-1', message: 'must be "stdin"', }); test('.pipe() "from" option cannot be a non-existing file descriptor', testPipeError, { from: 'fd3', message: 'file descriptor does not exist', }); test('.duplex() "from" cannot be a non-existing file descriptor', testNodeStream, { from: 'fd3', message: 'file descriptor does not exist', }); test('.iterable() "from" cannot be a non-existing file descriptor', testIterable, { from: 'fd3', message: 'file descriptor does not exist', }); test('.pipe() "to" option cannot be a non-existing file descriptor', testPipeError, { to: 'fd3', message: 'file descriptor does not exist', }); test('.duplex() "to" cannot be a non-existing file descriptor', testNodeStream, { to: 'fd3', message: 'file descriptor does not exist', }); test('.pipe() "from" option cannot be an input file descriptor', testPipeError, { sourceOptions: getStdio(3, new Uint8Array()), from: 'fd3', message: 'must be a readable stream', }); test('.duplex() "from" option cannot be an input file descriptor', testNodeStream, { sourceOptions: getStdio(3, new Uint8Array()), from: 'fd3', message: 'must be a readable stream', }); test('.iterable() "from" option cannot be an input file descriptor', testIterable, { sourceOptions: getStdio(3, new Uint8Array()), from: 'fd3', message: 'must be a readable stream', }); test('.pipe() "to" option cannot be an output file descriptor', testPipeError, { destinationOptions: fullStdio, to: 'fd3', message: 'must be a writable stream', }); test('.duplex() "to" option cannot be an output file descriptor', testNodeStream, { sourceOptions: fullStdio, to: 'fd3', message: 'must be a writable stream', }); test('.pipe() "to" option cannot be "all"', testPipeError, { destinationOptions: fullStdio, to: 'all', message: 'must be a writable stream', }); test('.duplex() "to" option cannot be "all"', testNodeStream, { sourceOptions: fullStdio, to: 'all', message: 'must be a writable stream', }); test('Cannot set "stdout" option to "ignore" to use .pipe()', testPipeError, { sourceOptions: {stdout: 'ignore'}, message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" option to "ignore" to use .duplex()', testNodeStream, { sourceOptions: {stdout: 'ignore'}, message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" option to "ignore" to use .iterable()', testIterable, { sourceOptions: {stdout: 'ignore'}, message: ['stdout', '\'ignore\''], }); test('Cannot set "stdin" option to "ignore" to use .pipe()', testPipeError, { destinationOptions: {stdin: 'ignore'}, message: ['stdin', '\'ignore\''], }); test('Cannot set "stdin" option to "ignore" to use .duplex()', testNodeStream, { sourceOptions: {stdin: 'ignore'}, message: ['stdin', '\'ignore\''], writable: true, }); test('Cannot set "stdout" option to "ignore" to use .pipe(1)', testPipeError, { sourceOptions: {stdout: 'ignore'}, from: 'fd1', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" option to "ignore" to use .duplex(1)', testNodeStream, { sourceOptions: {stdout: 'ignore'}, from: 'fd1', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" option to "ignore" to use .iterable(1)', testIterable, { sourceOptions: {stdout: 'ignore'}, from: 'fd1', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdin" option to "ignore" to use .pipe(0)', testPipeError, { destinationOptions: {stdin: 'ignore'}, message: ['stdin', '\'ignore\''], to: 'fd0', }); test('Cannot set "stdin" option to "ignore" to use .duplex(0)', testNodeStream, { sourceOptions: {stdin: 'ignore'}, message: ['stdin', '\'ignore\''], to: 'fd0', }); test('Cannot set "stdout" option to "ignore" to use .pipe("stdout")', testPipeError, { sourceOptions: {stdout: 'ignore'}, from: 'stdout', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" option to "ignore" to use .duplex("stdout")', testNodeStream, { sourceOptions: {stdout: 'ignore'}, from: 'stdout', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" option to "ignore" to use .iterable("stdout")', testIterable, { sourceOptions: {stdout: 'ignore'}, from: 'stdout', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdin" option to "ignore" to use .pipe("stdin")', testPipeError, { destinationOptions: {stdin: 'ignore'}, message: ['stdin', '\'ignore\''], to: 'stdin', }); test('Cannot set "stdin" option to "ignore" to use .duplex("stdin")', testNodeStream, { sourceOptions: {stdin: 'ignore'}, message: ['stdin', '\'ignore\''], to: 'stdin', }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe()', testPipeError, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex()', testNodeStream, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable()', testIterable, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe(1)', testPipeError, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'fd1', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex(1)', testNodeStream, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'fd1', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable(1)', testIterable, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'fd1', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe("stdout")', testPipeError, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'stdout', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex("stdout")', testNodeStream, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'stdout', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable("stdout")', testIterable, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'stdout', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdio[1]" option to "ignore" to use .pipe()', testPipeError, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[1]" option to "ignore" to use .duplex()', testNodeStream, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[1]" option to "ignore" to use .iterable()', testIterable, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[0]" option to "ignore" to use .pipe()', testPipeError, { destinationOptions: {stdio: ['ignore', 'pipe', 'pipe']}, message: ['stdio[0]', '\'ignore\''], }); test('Cannot set "stdio[0]" option to "ignore" to use .duplex()', testNodeStream, { sourceOptions: {stdio: ['ignore', 'pipe', 'pipe']}, message: ['stdio[0]', '\'ignore\''], writable: true, }); test('Cannot set "stdio[1]" option to "ignore" to use .pipe(1)', testPipeError, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, from: 'fd1', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[1]" option to "ignore" to use .duplex(1)', testNodeStream, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, from: 'fd1', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[1]" option to "ignore" to use .iterable(1)', testIterable, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, from: 'fd1', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[0]" option to "ignore" to use .pipe(0)', testPipeError, { destinationOptions: {stdio: ['ignore', 'pipe', 'pipe']}, message: ['stdio[0]', '\'ignore\''], to: 'fd0', }); test('Cannot set "stdio[0]" option to "ignore" to use .duplex(0)', testNodeStream, { sourceOptions: {stdio: ['ignore', 'pipe', 'pipe']}, message: ['stdio[0]', '\'ignore\''], to: 'fd0', }); test('Cannot set "stdio[1]" option to "ignore" to use .pipe("stdout")', testPipeError, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, from: 'stdout', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[1]" option to "ignore" to use .duplex("stdout")', testNodeStream, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, from: 'stdout', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[1]" option to "ignore" to use .iterable("stdout")', testIterable, { sourceOptions: {stdio: ['pipe', 'ignore', 'pipe']}, from: 'stdout', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[0]" option to "ignore" to use .pipe("stdin")', testPipeError, { destinationOptions: {stdio: ['ignore', 'pipe', 'pipe']}, message: ['stdio[0]', '\'ignore\''], to: 'stdin', }); test('Cannot set "stdio[0]" option to "ignore" to use .duplex("stdin")', testNodeStream, { sourceOptions: {stdio: ['ignore', 'pipe', 'pipe']}, message: ['stdio[0]', '\'ignore\''], to: 'stdin', }); test('Cannot set "stderr" option to "ignore" to use .pipe(2)', testPipeError, { sourceOptions: {stderr: 'ignore'}, from: 'fd2', message: ['stderr', '\'ignore\''], }); test('Cannot set "stderr" option to "ignore" to use .duplex(2)', testNodeStream, { sourceOptions: {stderr: 'ignore'}, from: 'fd2', message: ['stderr', '\'ignore\''], }); test('Cannot set "stderr" option to "ignore" to use .iterable(2)', testIterable, { sourceOptions: {stderr: 'ignore'}, from: 'fd2', message: ['stderr', '\'ignore\''], }); test('Cannot set "stderr" option to "ignore" to use .pipe("stderr")', testPipeError, { sourceOptions: {stderr: 'ignore'}, from: 'stderr', message: ['stderr', '\'ignore\''], }); test('Cannot set "stderr" option to "ignore" to use .duplex("stderr")', testNodeStream, { sourceOptions: {stderr: 'ignore'}, from: 'stderr', message: ['stderr', '\'ignore\''], }); test('Cannot set "stderr" option to "ignore" to use .iterable("stderr")', testIterable, { sourceOptions: {stderr: 'ignore'}, from: 'stderr', message: ['stderr', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe(2)', testPipeError, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'fd2', message: ['stderr', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex(2)', testNodeStream, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'fd2', message: ['stderr', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable(2)', testIterable, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'fd2', message: ['stderr', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe("stderr")', testPipeError, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'stderr', message: ['stderr', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex("stderr")', testNodeStream, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'stderr', message: ['stderr', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable("stderr")', testIterable, { sourceOptions: {stdout: 'ignore', stderr: 'ignore'}, from: 'stderr', message: ['stderr', '\'ignore\''], }); test('Cannot set "stdio[2]" option to "ignore" to use .pipe(2)', testPipeError, { sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, from: 'fd2', message: ['stdio[2]', '\'ignore\''], }); test('Cannot set "stdio[2]" option to "ignore" to use .duplex(2)', testNodeStream, { sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, from: 'fd2', message: ['stdio[2]', '\'ignore\''], }); test('Cannot set "stdio[2]" option to "ignore" to use .iterable(2)', testIterable, { sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, from: 'fd2', message: ['stdio[2]', '\'ignore\''], }); test('Cannot set "stdio[2]" option to "ignore" to use .pipe("stderr")', testPipeError, { sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, from: 'stderr', message: ['stdio[2]', '\'ignore\''], }); test('Cannot set "stdio[2]" option to "ignore" to use .duplex("stderr")', testNodeStream, { sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, from: 'stderr', message: ['stdio[2]', '\'ignore\''], }); test('Cannot set "stdio[2]" option to "ignore" to use .iterable("stderr")', testIterable, { sourceOptions: {stdio: ['pipe', 'pipe', 'ignore']}, from: 'stderr', message: ['stdio[2]', '\'ignore\''], }); test('Cannot set "stdio[3]" option to "ignore" to use .pipe(3)', testPipeError, { sourceOptions: getStdio(3, 'ignore'), from: 'fd3', message: ['stdio[3]', '\'ignore\''], }); test('Cannot set "stdio[3]" option to "ignore" to use .duplex(3)', testNodeStream, { sourceOptions: getStdio(3, 'ignore'), from: 'fd3', message: ['stdio[3]', '\'ignore\''], }); test('Cannot set "stdio[3]" option to "ignore" to use .iterable(3)', testIterable, { sourceOptions: getStdio(3, 'ignore'), from: 'fd3', message: ['stdio[3]', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .pipe("all")', testPipeError, { sourceOptions: {stdout: 'ignore', stderr: 'ignore', all: true}, from: 'all', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .duplex("all")', testNodeStream, { sourceOptions: {stdout: 'ignore', stderr: 'ignore', all: true}, from: 'all', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdout" + "stderr" option to "ignore" to use .iterable("all")', testIterable, { sourceOptions: {stdout: 'ignore', stderr: 'ignore', all: true}, from: 'all', message: ['stdout', '\'ignore\''], }); test('Cannot set "stdio[1]" + "stdio[2]" option to "ignore" to use .pipe("all")', testPipeError, { sourceOptions: {stdio: ['pipe', 'ignore', 'ignore'], all: true}, from: 'all', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[1]" + "stdio[2]" option to "ignore" to use .duplex("all")', testNodeStream, { sourceOptions: {stdio: ['pipe', 'ignore', 'ignore'], all: true}, from: 'all', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdio[1]" + "stdio[2]" option to "ignore" to use .iterable("all")', testIterable, { sourceOptions: {stdio: ['pipe', 'ignore', 'ignore'], all: true}, from: 'all', message: ['stdio[1]', '\'ignore\''], }); test('Cannot set "stdout" option to "inherit" to use .pipe()', testPipeError, { sourceOptions: {stdout: 'inherit'}, message: ['stdout', '\'inherit\''], }); test('Cannot set "stdout" option to "inherit" to use .duplex()', testNodeStream, { sourceOptions: {stdout: 'inherit'}, message: ['stdout', '\'inherit\''], }); test('Cannot set "stdout" option to "inherit" to use .iterable()', testIterable, { sourceOptions: {stdout: 'inherit'}, message: ['stdout', '\'inherit\''], }); test('Cannot set "stdin" option to "inherit" to use .pipe()', testPipeError, { destinationOptions: {stdin: 'inherit'}, message: ['stdin', '\'inherit\''], }); test('Cannot set "stdin" option to "inherit" to use .duplex()', testNodeStream, { sourceOptions: {stdin: 'inherit'}, message: ['stdin', '\'inherit\''], writable: true, }); test('Cannot set "stdout" option to "ipc" to use .pipe()', testPipeError, { sourceOptions: {stdout: 'ipc'}, message: ['stdout', '\'ipc\''], }); test('Cannot set "stdout" option to "ipc" to use .duplex()', testNodeStream, { sourceOptions: {stdout: 'ipc'}, message: ['stdout', '\'ipc\''], }); test('Cannot set "stdout" option to "ipc" to use .iterable()', testIterable, { sourceOptions: {stdout: 'ipc'}, message: ['stdout', '\'ipc\''], }); test('Cannot set "stdin" option to "ipc" to use .pipe()', testPipeError, { destinationOptions: {stdin: 'ipc'}, message: ['stdin', '\'ipc\''], }); test('Cannot set "stdin" option to "ipc" to use .duplex()', testNodeStream, { sourceOptions: {stdin: 'ipc'}, message: ['stdin', '\'ipc\''], writable: true, }); test('Cannot set "stdout" option to file descriptors to use .pipe()', testPipeError, { sourceOptions: {stdout: 1}, message: ['stdout', '1'], }); test('Cannot set "stdout" option to file descriptors to use .duplex()', testNodeStream, { sourceOptions: {stdout: 1}, message: ['stdout', '1'], }); test('Cannot set "stdout" option to file descriptors to use .iterable()', testIterable, { sourceOptions: {stdout: 1}, message: ['stdout', '1'], }); test('Cannot set "stdin" option to file descriptors to use .pipe()', testPipeError, { destinationOptions: {stdin: 0}, message: ['stdin', '0'], }); test('Cannot set "stdin" option to file descriptors to use .duplex()', testNodeStream, { sourceOptions: {stdin: 0}, message: ['stdin', '0'], writable: true, }); test('Cannot set "stdout" option to Node.js streams to use .pipe()', testPipeError, { sourceOptions: {stdout: process.stdout}, message: ['stdout', 'Stream'], }); test('Cannot set "stdout" option to Node.js streams to use .duplex()', testNodeStream, { sourceOptions: {stdout: process.stdout}, message: ['stdout', 'Stream'], }); test('Cannot set "stdout" option to Node.js streams to use .iterable()', testIterable, { sourceOptions: {stdout: process.stdout}, message: ['stdout', 'Stream'], }); test('Cannot set "stdin" option to Node.js streams to use .pipe()', testPipeError, { destinationOptions: {stdin: process.stdin}, message: ['stdin', 'Stream'], }); test('Cannot set "stdin" option to Node.js streams to use .duplex()', testNodeStream, { sourceOptions: {stdin: process.stdin}, message: ['stdin', 'Stream'], writable: true, }); test('Cannot set "stdio[3]" option to Node.js Writable streams to use .pipe()', testPipeError, { sourceOptions: getStdio(3, process.stdout), message: ['stdio[3]', 'Stream'], from: 'fd3', }); test('Cannot set "stdio[3]" option to Node.js Writable streams to use .duplex()', testNodeStream, { sourceOptions: getStdio(3, process.stdout), message: ['stdio[3]', 'Stream'], from: 'fd3', }); test('Cannot set "stdio[3]" option to Node.js Writable streams to use .iterable()', testIterable, { sourceOptions: getStdio(3, process.stdout), message: ['stdio[3]', 'Stream'], from: 'fd3', }); test('Cannot set "stdio[3]" option to Node.js Readable streams to use .pipe()', testPipeError, { destinationOptions: getStdio(3, process.stdin), message: ['stdio[3]', 'Stream'], to: 'fd3', }); test('Cannot set "stdio[3]" option to Node.js Readable streams to use .duplex()', testNodeStream, { sourceOptions: getStdio(3, process.stdin), message: ['stdio[3]', 'Stream'], to: 'fd3', }); test('Sets the right error message when the "all" option is incompatible - execa.$', async t => { await assertPipeError( t, execa('empty.js') .pipe({all: false})`stdin.js` .pipe(execa('empty.js'), {from: 'all'}), '"all" option must be true', ); }); test('Sets the right error message when the "all" option is incompatible - execa.execa', async t => { await assertPipeError( t, execa('empty.js') .pipe(execa('stdin.js', {all: false})) .pipe(execa('empty.js'), {from: 'all'}), '"all" option must be true', ); }); test('Sets the right error message when the "all" option is incompatible - early error', async t => { await assertPipeError( t, getEarlyErrorSubprocess() .pipe(execa('stdin.js', {all: false})) .pipe(execa('empty.js'), {from: 'all'}), '"all" option must be true', ); }); ================================================ FILE: test/arguments/local.js ================================================ import path from 'node:path'; import process from 'node:process'; import {pathToFileURL} from 'node:url'; import test from 'ava'; import {execa, $} from '../../index.js'; import {setFixtureDirectory, PATH_KEY} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); process.env.FOO = 'foo'; const isWindows = process.platform === 'win32'; const ENOENT_REGEXP = isWindows ? /failed with exit code 1/ : /spawn.* ENOENT/; const getPathWithoutLocalDirectory = () => { const newPath = process.env[PATH_KEY] .split(path.delimiter) .filter(pathDirectory => !BIN_DIR_REGEXP.test(pathDirectory)).join(path.delimiter); return {[PATH_KEY]: newPath}; }; const BIN_DIR_REGEXP = /node_modules[\\/]\.bin/; const pathWitoutLocalDirectory = getPathWithoutLocalDirectory(); test('preferLocal: true', async t => { await t.notThrowsAsync(execa('ava', ['--version'], {preferLocal: true, env: pathWitoutLocalDirectory})); }); test('preferLocal: false', async t => { await t.throwsAsync(execa('ava', ['--version'], {preferLocal: false, env: pathWitoutLocalDirectory}), {message: ENOENT_REGEXP}); }); test('preferLocal: undefined', async t => { await t.throwsAsync(execa('ava', ['--version'], {env: pathWitoutLocalDirectory}), {message: ENOENT_REGEXP}); }); test('preferLocal: undefined with $', async t => { await t.notThrowsAsync($('ava', ['--version'], {env: pathWitoutLocalDirectory})); }); test('preferLocal: undefined with $.sync', t => { t.notThrows(() => $.sync('ava', ['--version'], {env: pathWitoutLocalDirectory})); }); test('preferLocal: undefined with execa.pipe`...`', async t => { await t.throwsAsync(() => execa('node', ['--version']).pipe({env: pathWitoutLocalDirectory})`ava --version`); }); test('preferLocal: undefined with $.pipe`...`', async t => { await t.notThrows(() => $('node', ['--version']).pipe({env: pathWitoutLocalDirectory})`ava --version`); }); test('preferLocal: undefined with execa.pipe()', async t => { await t.throwsAsync(() => execa('node', ['--version']).pipe('ava', ['--version'], {env: pathWitoutLocalDirectory})); }); test('preferLocal: undefined with $.pipe()', async t => { await t.notThrows(() => $('node', ['--version']).pipe('ava', ['--version'], {env: pathWitoutLocalDirectory})); }); test('localDir option', async t => { const command = isWindows ? 'echo %PATH%' : 'echo $PATH'; const {stdout} = await execa(command, {shell: true, preferLocal: true, localDir: '/test'}); const envPaths = stdout.split(path.delimiter); t.true(envPaths.some(envPath => envPath.endsWith('.bin'))); }); test('localDir option can be a URL', async t => { const command = isWindows ? 'echo %PATH%' : 'echo $PATH'; const {stdout} = await execa(command, {shell: true, preferLocal: true, localDir: pathToFileURL('/test')}); const envPaths = stdout.split(path.delimiter); t.true(envPaths.some(envPath => envPath.endsWith('.bin'))); }); ================================================ FILE: test/arguments/shell.js ================================================ import process from 'node:process'; import {pathToFileURL} from 'node:url'; import test from 'ava'; import which from 'which'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {identity} from '../helpers/stdio.js'; setFixtureDirectory(); process.env.FOO = 'foo'; const isWindows = process.platform === 'win32'; test('can use `options.shell: true`', async t => { const {stdout} = await execa('node test/fixtures/noop.js foo', {shell: true}); t.is(stdout, 'foo'); }); const testShellPath = async (t, mapPath) => { const shellPath = isWindows ? 'cmd.exe' : 'bash'; const shell = mapPath(await which(shellPath)); const {stdout} = await execa('node test/fixtures/noop.js foo', {shell}); t.is(stdout, 'foo'); }; test('can use `options.shell: string`', testShellPath, identity); test('can use `options.shell: file URL`', testShellPath, pathToFileURL); ================================================ FILE: test/arguments/specific.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testPriorityOrder = async (t, buffer, bufferStdout, bufferStderr, execaMethod) => { const {stdout, stderr} = await execaMethod('noop-both.js', {buffer}); t.is(stdout, bufferStdout ? foobarString : undefined); t.is(stderr, bufferStderr ? foobarString : undefined); }; test('buffer: {stdout, fd1}', testPriorityOrder, {stdout: true, fd1: false}, true, true, execa); test('buffer: {stdout, all}', testPriorityOrder, {stdout: true, all: false}, true, false, execa); test('buffer: {fd1, all}', testPriorityOrder, {fd1: true, all: false}, true, false, execa); test('buffer: {stderr, fd2}', testPriorityOrder, {stderr: true, fd2: false}, true, true, execa); test('buffer: {stderr, all}', testPriorityOrder, {stderr: true, all: false}, false, true, execa); test('buffer: {fd2, all}', testPriorityOrder, {fd2: true, all: false}, false, true, execa); test('buffer: {fd1, stdout}', testPriorityOrder, {fd1: false, stdout: true}, true, true, execa); test('buffer: {all, stdout}', testPriorityOrder, {all: false, stdout: true}, true, false, execa); test('buffer: {all, fd1}', testPriorityOrder, {all: false, fd1: true}, true, false, execa); test('buffer: {fd2, stderr}', testPriorityOrder, {fd2: false, stderr: true}, true, true, execa); test('buffer: {all, stderr}', testPriorityOrder, {all: false, stderr: true}, false, true, execa); test('buffer: {all, fd2}', testPriorityOrder, {all: false, fd2: true}, false, true, execa); test('buffer: {stdout, fd1}, sync', testPriorityOrder, {stdout: true, fd1: false}, true, true, execaSync); test('buffer: {stdout, all}, sync', testPriorityOrder, {stdout: true, all: false}, true, false, execaSync); test('buffer: {fd1, all}, sync', testPriorityOrder, {fd1: true, all: false}, true, false, execaSync); test('buffer: {stderr, fd2}, sync', testPriorityOrder, {stderr: true, fd2: false}, true, true, execaSync); test('buffer: {stderr, all}, sync', testPriorityOrder, {stderr: true, all: false}, false, true, execaSync); test('buffer: {fd2, all}, sync', testPriorityOrder, {fd2: true, all: false}, false, true, execaSync); test('buffer: {fd1, stdout}, sync', testPriorityOrder, {fd1: false, stdout: true}, true, true, execaSync); test('buffer: {all, stdout}, sync', testPriorityOrder, {all: false, stdout: true}, true, false, execaSync); test('buffer: {all, fd1}, sync', testPriorityOrder, {all: false, fd1: true}, true, false, execaSync); test('buffer: {fd2, stderr}, sync', testPriorityOrder, {fd2: false, stderr: true}, true, true, execaSync); test('buffer: {all, stderr}, sync', testPriorityOrder, {all: false, stderr: true}, false, true, execaSync); test('buffer: {all, fd2}, sync', testPriorityOrder, {all: false, fd2: true}, false, true, execaSync); ================================================ FILE: test/convert/concurrent.js ================================================ import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {fullReadableStdio} from '../helpers/stdio.js'; import { finishedStream, assertStreamOutput, assertStreamError, assertStreamReadError, assertSubprocessOutput, assertSubprocessError, getReadWriteSubprocess, } from '../helpers/convert.js'; setFixtureDirectory(); const endStream = async stream => { stream.end(foobarString); await setTimeout(0); }; // eslint-disable-next-line max-params const endSameWritable = async (t, stream, secondStream, subprocess, fdNumber) => { await endStream(stream); t.true(subprocess.stdio[fdNumber].writable); await endStream(secondStream); t.false(subprocess.stdio[fdNumber].writable); }; // eslint-disable-next-line max-params const endDifferentWritable = async (t, stream, secondStream, subprocess, fdNumber = 0, secondFdNumber = 3) => { await endStream(stream); t.false(subprocess.stdio[fdNumber].writable); t.true(subprocess.stdio[secondFdNumber].writable); await endStream(secondStream); t.false(subprocess.stdio[secondFdNumber].writable); }; const testReadableTwice = async (t, fdNumber, from) => { const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString]); const stream = subprocess.readable({from}); const secondStream = subprocess.readable({from}); await Promise.all([ assertStreamOutput(t, stream), assertStreamOutput(t, secondStream), ]); await assertSubprocessOutput(t, subprocess, foobarString, fdNumber); }; test('Can call .readable() twice on same file descriptor', testReadableTwice, 1); test('Can call .readable({from: "stderr"}) twice on same file descriptor', testReadableTwice, 2, 'stderr'); const testWritableTwice = async (t, fdNumber, to, options) => { const subprocess = execa('stdin-fd.js', [`${fdNumber}`], options); const stream = subprocess.writable({to}); const secondStream = subprocess.writable({to}); await Promise.all([ finishedStream(stream), finishedStream(secondStream), endSameWritable(t, stream, secondStream, subprocess, fdNumber), ]); await assertSubprocessOutput(t, subprocess, `${foobarString}${foobarString}`); }; test('Can call .writable() twice on same file descriptor', testWritableTwice, 0, undefined, {}); test('Can call .writable({to: "fd3"}) twice on same file descriptor', testWritableTwice, 3, 'fd3', fullReadableStdio()); const testDuplexTwice = async (t, fdNumber, to, options) => { const subprocess = execa('stdin-fd.js', [`${fdNumber}`], options); const stream = subprocess.duplex({to}); const secondStream = subprocess.duplex({to}); const expectedOutput = `${foobarString}${foobarString}`; await Promise.all([ assertStreamOutput(t, stream, expectedOutput), assertStreamOutput(t, secondStream, expectedOutput), endSameWritable(t, stream, secondStream, subprocess, fdNumber), ]); await assertSubprocessOutput(t, subprocess, expectedOutput); }; test('Can call .duplex() twice on same file descriptor', testDuplexTwice, 0, undefined, {}); test('Can call .duplex({to: "fd3"}) twice on same file descriptor', testDuplexTwice, 3, 'fd3', fullReadableStdio()); test('Can call .duplex() twice on same readable file descriptor but different writable one', async t => { const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); const stream = subprocess.duplex(); const secondStream = subprocess.duplex({to: 'fd3'}); const expectedOutput = `${foobarString}${foobarString}`; await Promise.all([ assertStreamOutput(t, stream, expectedOutput), assertStreamOutput(t, secondStream, expectedOutput), endDifferentWritable(t, stream, secondStream, subprocess), ]); await assertSubprocessOutput(t, subprocess, expectedOutput); }); test('Can call .readable() twice on different file descriptors', async t => { const subprocess = execa('noop-both.js', [foobarString]); const stream = subprocess.readable(); const secondStream = subprocess.readable({from: 'stderr'}); const expectedOutput = `${foobarString}\n`; await Promise.all([ assertStreamOutput(t, stream, expectedOutput), assertStreamOutput(t, secondStream, expectedOutput), ]); await assertSubprocessOutput(t, subprocess); await assertSubprocessOutput(t, subprocess, foobarString, 2); }); test('Can call .writable() twice on different file descriptors', async t => { const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); const stream = subprocess.writable(); const secondStream = subprocess.writable({to: 'fd3'}); await Promise.all([ finishedStream(stream), finishedStream(secondStream), endDifferentWritable(t, stream, secondStream, subprocess), ]); await assertSubprocessOutput(t, subprocess, `${foobarString}${foobarString}`); }); test('Can call .duplex() twice on different file descriptors', async t => { const subprocess = execa('stdin-twice-both.js', ['3'], fullReadableStdio()); const stream = subprocess.duplex(); const secondStream = subprocess.duplex({from: 'stderr', to: 'fd3'}); await Promise.all([ assertStreamOutput(t, stream), assertStreamOutput(t, secondStream), endDifferentWritable(t, stream, secondStream, subprocess), ]); await assertSubprocessOutput(t, subprocess); await assertSubprocessOutput(t, subprocess, foobarString, 2); }); test('Can call .readable() and .writable()', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.writable(); const secondStream = subprocess.readable(); stream.end(foobarString); await Promise.all([ finishedStream(stream), assertStreamOutput(t, secondStream), ]); await assertSubprocessOutput(t, subprocess); }); test('Can call .writable() and .duplex()', async t => { const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); const stream = subprocess.duplex(); const secondStream = subprocess.writable({to: 'fd3'}); const expectedOutput = `${foobarString}${foobarString}`; await Promise.all([ assertStreamOutput(t, stream, expectedOutput), finishedStream(secondStream), endDifferentWritable(t, stream, secondStream, subprocess), ]); await assertSubprocessOutput(t, subprocess, expectedOutput); }); test('Can call .readable() and .duplex()', async t => { const subprocess = execa('stdin-both.js'); const stream = subprocess.duplex(); const secondStream = subprocess.readable({from: 'stderr'}); stream.end(foobarString); await Promise.all([ assertStreamOutput(t, stream), assertStreamOutput(t, secondStream), ]); await assertSubprocessOutput(t, subprocess); await assertSubprocessOutput(t, subprocess, foobarString, 2); }); test('Can error one of two .readable() on same file descriptor', async t => { const subprocess = execa('noop-fd.js', ['1', foobarString]); const stream = subprocess.readable(); const secondStream = subprocess.readable(); const cause = new Error(foobarString); stream.destroy(cause); await Promise.all([ assertStreamReadError(t, stream, cause), assertStreamOutput(t, secondStream), ]); await assertSubprocessOutput(t, subprocess); }); test('Can error both .readable() on same file descriptor', async t => { const subprocess = execa('noop-fd.js', ['1', foobarString]); const stream = subprocess.readable(); const secondStream = subprocess.readable(); const cause = new Error(foobarString); stream.destroy(cause); secondStream.destroy(cause); const [error, secondError] = await Promise.all([ assertStreamReadError(t, stream, {cause}), assertStreamReadError(t, secondStream, {cause}), ]); t.is(error, secondError); await assertSubprocessError(t, subprocess, error); }); test('Can error one of two .readable() on different file descriptors', async t => { const subprocess = execa('noop-both.js', [foobarString]); const stream = subprocess.readable(); const secondStream = subprocess.readable({from: 'stderr'}); const cause = new Error(foobarString); stream.destroy(cause); const [error, secondError] = await Promise.all([ assertStreamReadError(t, stream, {cause}), assertStreamReadError(t, secondStream, {cause}), ]); t.is(error, secondError); t.is(error.stderr, foobarString); await assertSubprocessError(t, subprocess, error); }); test('Can error both .readable() on different file descriptors', async t => { const subprocess = execa('noop-both.js', [foobarString]); const stream = subprocess.readable(); const secondStream = subprocess.readable({from: 'stderr'}); const cause = new Error(foobarString); stream.destroy(cause); secondStream.destroy(cause); const [error, secondError] = await Promise.all([ assertStreamReadError(t, stream, {cause}), assertStreamReadError(t, secondStream, {cause}), ]); t.is(error, secondError); await assertSubprocessError(t, subprocess, error); }); test('Can error one of two .writable() on same file descriptor', async t => { const subprocess = execa('stdin.js'); const stream = subprocess.writable(); const secondStream = subprocess.writable(); const cause = new Error(foobarString); stream.destroy(cause); secondStream.end(foobarString); await Promise.all([ assertStreamError(t, stream, cause), finishedStream(secondStream), ]); await assertSubprocessOutput(t, subprocess); }); test('Can error both .writable() on same file descriptor', async t => { const subprocess = execa('stdin.js'); const stream = subprocess.writable(); const secondStream = subprocess.writable(); const cause = new Error(foobarString); stream.destroy(cause); secondStream.destroy(cause); const [error, secondError] = await Promise.all([ assertStreamError(t, stream, {cause}), assertStreamError(t, secondStream, {cause}), ]); t.is(error, secondError); await assertSubprocessError(t, subprocess, error); }); test('Can error one of two .writable() on different file descriptors', async t => { const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); const stream = subprocess.writable(); const secondStream = subprocess.writable({to: 'fd3'}); const cause = new Error(foobarString); stream.destroy(cause); secondStream.end(foobarString); const [error, secondError] = await Promise.all([ assertStreamError(t, stream, {cause}), assertStreamError(t, secondStream, {cause}), ]); t.is(error, secondError); t.is(error.stdout, foobarString); await assertSubprocessError(t, subprocess, error); }); test('Can error both .writable() on different file descriptors', async t => { const subprocess = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); const stream = subprocess.writable(); const secondStream = subprocess.writable({to: 'fd3'}); const cause = new Error(foobarString); stream.destroy(cause); secondStream.destroy(cause); const [error, secondError] = await Promise.all([ assertStreamError(t, stream, {cause}), assertStreamError(t, secondStream, {cause}), ]); t.is(error, secondError); await assertSubprocessError(t, subprocess, error); }); test('Can error one of two .duplex() on same file descriptor', async t => { const subprocess = execa('stdin.js'); const stream = subprocess.duplex(); const secondStream = subprocess.duplex(); const cause = new Error(foobarString); stream.destroy(cause); secondStream.end(foobarString); await Promise.all([ assertStreamReadError(t, stream, cause), assertStreamOutput(t, secondStream), ]); await assertSubprocessOutput(t, subprocess); }); test('Can error both .duplex() on same file descriptor', async t => { const subprocess = execa('stdin.js'); const stream = subprocess.duplex(); const secondStream = subprocess.duplex(); const cause = new Error(foobarString); stream.destroy(cause); secondStream.destroy(cause); await Promise.all([ assertStreamReadError(t, stream, cause), assertStreamReadError(t, secondStream, cause), ]); await assertSubprocessError(t, subprocess, {cause}); }); test('Can error one of two .duplex() on different file descriptors', async t => { const subprocess = execa('stdin-twice-both.js', ['3'], fullReadableStdio()); const stream = subprocess.duplex(); const secondStream = subprocess.duplex({from: 'stderr', to: 'fd3'}); const cause = new Error(foobarString); stream.destroy(cause); secondStream.end(foobarString); const [error] = await Promise.all([ assertStreamReadError(t, secondStream, {cause}), assertStreamReadError(t, stream, cause), ]); t.is(error.stderr, foobarString); await assertSubprocessError(t, subprocess, error); }); test('Can error both .duplex() on different file descriptors', async t => { const subprocess = execa('stdin-twice-both.js', ['3'], fullReadableStdio()); const stream = subprocess.duplex(); const secondStream = subprocess.duplex({from: 'stderr', to: 'fd3'}); const cause = new Error(foobarString); stream.destroy(cause); secondStream.destroy(cause); await Promise.all([ assertStreamReadError(t, stream, cause), assertStreamReadError(t, secondStream, cause), ]); await assertSubprocessError(t, subprocess, {cause}); }); ================================================ FILE: test/convert/duplex.js ================================================ import { compose, Readable, Writable, PassThrough, } from 'node:stream'; import {pipeline} from 'node:stream/promises'; import {text} from 'node:stream/consumers'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { finishedStream, assertReadableAborted, assertWritableAborted, assertProcessNormalExit, assertStreamOutput, assertStreamError, assertStreamReadError, assertSubprocessOutput, assertSubprocessError, assertPromiseError, getReadWriteSubprocess, } from '../helpers/convert.js'; import {foobarString} from '../helpers/input.js'; import {majorNodeVersion} from '../helpers/node-version.js'; import {prematureClose, fullStdio, fullReadableStdio} from '../helpers/stdio.js'; import {defaultHighWaterMark} from '../helpers/stream.js'; setFixtureDirectory(); test('.duplex() success', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); t.true(stream instanceof Writable); t.true(stream.writable); t.true(stream instanceof Readable); t.true(stream.readable); stream.end(foobarString); await assertStreamOutput(t, stream); await assertSubprocessOutput(t, subprocess); }); // eslint-disable-next-line max-params const testReadableDuplexDefault = async (t, fdNumber, from, options, hasResult) => { const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], options); const stream = subprocess.duplex({from}); stream.end(foobarString); await assertStreamOutput(t, stream, hasResult ? foobarString : ''); await assertSubprocessOutput(t, subprocess, foobarString, fdNumber); }; test('.duplex() can use stdout', testReadableDuplexDefault, 1, 'stdout', {}, true); test('.duplex() can use stderr', testReadableDuplexDefault, 2, 'stderr', {}, true); test('.duplex() can use output stdio[*]', testReadableDuplexDefault, 3, 'fd3', fullStdio, true); test('.duplex() uses stdout by default', testReadableDuplexDefault, 1, undefined, {}, true); test('.duplex() does not use stderr by default', testReadableDuplexDefault, 2, undefined, {}, false); test('.duplex() does not use stdio[*] by default', testReadableDuplexDefault, 3, undefined, fullStdio, false); test('.duplex() uses stdout even if stderr is "ignore"', testReadableDuplexDefault, 1, 'stdout', {stderr: 'ignore'}, true); test('.duplex() uses stderr even if stdout is "ignore"', testReadableDuplexDefault, 2, 'stderr', {stdout: 'ignore'}, true); test('.duplex() uses stdout if "all" is used', testReadableDuplexDefault, 1, 'all', {all: true}, true); test('.duplex() uses stderr if "all" is used', testReadableDuplexDefault, 2, 'all', {all: true}, true); const testWritableDuplexDefault = async (t, fdNumber, to, options) => { const subprocess = execa('stdin-fd.js', [`${fdNumber}`], options); const stream = subprocess.duplex({to}); stream.end(foobarString); await assertStreamOutput(t, stream, foobarString); await assertSubprocessOutput(t, subprocess); }; test('.duplex() can use stdin', testWritableDuplexDefault, 0, 'stdin', {}); test('.duplex() can use input stdio[*]', testWritableDuplexDefault, 3, 'fd3', fullReadableStdio()); test('.duplex() uses stdin by default', testWritableDuplexDefault, 0, undefined, {}); test('.duplex() abort -> subprocess fail', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); const textPromise = text(stream); stream.destroy(); const error = await t.throwsAsync(textPromise); t.like(error, prematureClose); assertProcessNormalExit(t, error); assertWritableAborted(t, subprocess.stdin); assertReadableAborted(t, subprocess.stdout); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }); test('.duplex() error -> subprocess fail', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); const cause = new Error(foobarString); stream.destroy(cause); const error = await assertStreamError(t, stream, {cause}); assertProcessNormalExit(t, error); t.is(subprocess.stdin.errored, cause); t.is(subprocess.stdout.errored, cause); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }); test('.duplex() can be used with Stream.pipeline()', async t => { const subprocess = getReadWriteSubprocess(); const inputStream = Readable.from([foobarString]); const stream = subprocess.duplex(); const outputStream = new PassThrough(); await pipeline(inputStream, stream, outputStream); await finishedStream(inputStream); await finishedStream(stream); await assertStreamOutput(t, outputStream); await assertSubprocessOutput(t, subprocess); }); test('.duplex() can error with Stream.pipeline()', async t => { const subprocess = execa('stdin-fail.js'); const inputStream = Readable.from([foobarString]); const stream = subprocess.duplex(); const outputStream = new PassThrough(); const error = await t.throwsAsync(pipeline(inputStream, stream, outputStream)); assertProcessNormalExit(t, error, 2); t.like(error, {stdout: foobarString}); await finishedStream(inputStream); await assertStreamError(t, stream, error); await assertStreamReadError(t, outputStream, error); await assertSubprocessError(t, subprocess, error); }); test('.duplex() can pipe to errored stream with Stream.pipeline()', async t => { const subprocess = execa('stdin-fail.js'); const inputStream = Readable.from([foobarString]); const stream = subprocess.duplex(); const outputStream = new PassThrough(); const cause = new Error('test'); outputStream.destroy(cause); // Node 23 does not allow calling `stream.pipeline()` with an already errored stream if (majorNodeVersion >= 23) { outputStream.on('error', () => {}); stream.on('error', () => {}); await t.throwsAsync(pipeline(stream, outputStream), {code: 'ERR_STREAM_UNABLE_TO_PIPE'}); stream.end(); } else { await assertPromiseError(t, pipeline(inputStream, stream, outputStream), cause); await t.throwsAsync(finishedStream(stream)); await assertStreamError(t, inputStream, cause); const error = await assertStreamError(t, stream, cause); await assertStreamReadError(t, outputStream, cause); await assertSubprocessError(t, subprocess, {cause: error}); } }); test('.duplex() can be piped to errored stream with Stream.pipeline()', async t => { const subprocess = execa('stdin-fail.js'); const inputStream = Readable.from([foobarString]); const stream = subprocess.duplex(); const outputStream = new PassThrough(); const cause = new Error('test'); inputStream.destroy(cause); await assertPromiseError(t, pipeline(inputStream, stream, outputStream), cause); await t.throwsAsync(finishedStream(stream)); await assertStreamError(t, inputStream, cause); const error = await assertStreamError(t, stream, cause); await assertStreamReadError(t, outputStream, cause); await assertSubprocessError(t, subprocess, {cause: error}); }); test('.duplex() can be used with Stream.compose()', async t => { const subprocess = getReadWriteSubprocess(); const inputStream = Readable.from([foobarString]); const stream = subprocess.duplex(); const outputStream = new PassThrough(); await assertStreamOutput(t, compose(inputStream, stream, outputStream)); await assertSubprocessOutput(t, subprocess); }); test('.duplex() has the right highWaterMark', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); t.is(stream.readableHighWaterMark, defaultHighWaterMark); t.is(stream.writableHighWaterMark, defaultHighWaterMark); stream.end(); await text(stream); }); ================================================ FILE: test/convert/iterable.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {fullStdio, assertEpipe} from '../helpers/stdio.js'; import { arrayFromAsync, assertWritableAborted, assertReadableAborted, assertProcessNormalExit, } from '../helpers/convert.js'; import {simpleFull, noNewlinesChunks} from '../helpers/lines.js'; setFixtureDirectory(); const partialArrayFromAsync = async (asyncIterable, lines = []) => { // eslint-disable-next-line no-unreachable-loop for await (const line of asyncIterable) { lines.push(line); break; } return lines; }; const errorArrayFromAsync = async (t, cause, asyncIterable, lines = []) => { const {value} = await asyncIterable.next(); lines.push(value); await asyncIterable.throw(cause); }; const throwsAsync = async (t, asyncIterable, arrayFromAsyncMethod) => { const lines = []; const error = await t.throwsAsync(arrayFromAsyncMethod(asyncIterable, lines)); return {error, lines}; }; const assertStdoutAbort = (t, subprocess, error, cause) => { assertProcessNormalExit(t, error, 1); assertEpipe(t, error.stderr); assertWritableAborted(t, subprocess.stdin); t.true(subprocess.stderr.readableEnded); if (cause === undefined) { assertReadableAborted(t, subprocess.stdout); } else { t.is(subprocess.stdout.errored, cause); } }; const testSuccess = async (t, fdNumber, from, options = {}) => { const lines = await arrayFromAsync(execa('noop-fd.js', [`${fdNumber}`, simpleFull], options).iterable({from})); t.deepEqual(lines, noNewlinesChunks); }; test('Uses stdout by default', testSuccess, 1, undefined); test('Can iterate successfully on stdout', testSuccess, 1, 'stdout'); test('Can iterate successfully on stderr', testSuccess, 2, 'stderr'); test('Can iterate successfully on stdio[*]', testSuccess, 3, 'fd3', fullStdio); test('Can iterate successfully on all', async t => { const lines = await arrayFromAsync(execa('noop-both.js', [simpleFull], {all: true}).iterable({from: 'all'})); t.deepEqual(lines, [...noNewlinesChunks, ...noNewlinesChunks]); }); test('Can iterate using Symbol.asyncIterator', async t => { const lines = await arrayFromAsync(execa('noop-fd.js', ['1', simpleFull])); t.deepEqual(lines, noNewlinesChunks); }); const assertMultipleCalls = async (t, iterable, iterableTwo) => { t.not(iterable, iterableTwo); const lines = await arrayFromAsync(iterable); const linesTwo = await arrayFromAsync(iterableTwo); t.deepEqual(lines, linesTwo); t.deepEqual(lines, noNewlinesChunks); }; test('Can be called multiple times', async t => { const subprocess = execa('noop-fd.js', ['1', simpleFull]); const iterable = subprocess.iterable(); const iterableTwo = subprocess.iterable(); await assertMultipleCalls(t, iterable, iterableTwo); }); test('Can be called on different file descriptors', async t => { const subprocess = execa('noop-both.js', [simpleFull]); const iterable = subprocess.iterable(); const iterableTwo = subprocess.iterable({from: 'stderr'}); await assertMultipleCalls(t, iterable, iterableTwo); }); test('Wait for the subprocess exit', async t => { const subprocess = execa('noop-delay.js', ['1', simpleFull]); const linesPromise = arrayFromAsync(subprocess); t.is(await Promise.race([linesPromise, subprocess]), await subprocess); t.deepEqual(await linesPromise, noNewlinesChunks); }); test('Wait for the subprocess exit on iterator.return()', async t => { const subprocess = execa('noop-delay.js', ['1', simpleFull]); const linesPromise = partialArrayFromAsync(subprocess); t.is(await Promise.race([linesPromise, subprocess]), await subprocess); t.deepEqual(await linesPromise, [noNewlinesChunks[0]]); }); test('Wait for the subprocess exit on iterator.throw()', async t => { const subprocess = execa('noop-delay.js', ['1', simpleFull]); const cause = new Error(foobarString); const lines = []; const linesPromise = t.throwsAsync(errorArrayFromAsync(t, cause, subprocess.iterable(), lines)); t.is(await Promise.race([linesPromise, subprocess]), await subprocess); t.deepEqual(lines, [noNewlinesChunks[0]]); }); test('Abort stdout on iterator.return()', async t => { const subprocess = execa('noop-repeat.js', ['1', simpleFull]); const {error, lines} = await throwsAsync(t, subprocess, partialArrayFromAsync); t.deepEqual(lines, [noNewlinesChunks[0]]); assertStdoutAbort(t, subprocess, error); t.is(error, await t.throwsAsync(subprocess)); }); test('Abort stdout on iterator.throw()', async t => { const subprocess = execa('noop-repeat.js', ['1', simpleFull]); const cause = new Error(foobarString); const {error, lines} = await throwsAsync(t, subprocess.iterable(), errorArrayFromAsync.bind(undefined, t, cause)); t.deepEqual(lines, [noNewlinesChunks[0]]); assertStdoutAbort(t, subprocess, error); t.is(error, await t.throwsAsync(subprocess)); }); test('Propagate subprocess failure', async t => { const subprocess = execa('noop-fail.js', ['1', simpleFull]); const {error, lines} = await throwsAsync(t, subprocess, arrayFromAsync); t.is(error, await t.throwsAsync(subprocess)); t.deepEqual(lines, noNewlinesChunks); }); const testStdoutError = async (t, destroyStdout, isAbort, cause) => { const subprocess = execa('noop-repeat.js', ['1', simpleFull]); subprocess.stdout.once('data', () => { destroyStdout(subprocess.stdout, cause); }); const {error} = await throwsAsync(t, subprocess, arrayFromAsync); t.is(error.cause, cause); assertStdoutAbort(t, subprocess, error, isAbort ? undefined : cause); t.is(error, await t.throwsAsync(subprocess)); }; test('Propagate stdout abort', testStdoutError, subprocessStdout => subprocessStdout.destroy(), true); test('Propagate stdout error', testStdoutError, (subprocessStdout, cause) => subprocessStdout.destroy(cause), false, new Error(foobarString)); test('Propagate stdout "error" event', testStdoutError, (subprocessStdout, cause) => subprocessStdout.emit('error', cause), true, new Error(foobarString)); ================================================ FILE: test/convert/readable.js ================================================ import {once} from 'node:events'; import { compose, Readable, Writable, PassThrough, } from 'node:stream'; import {pipeline} from 'node:stream/promises'; import {text} from 'node:stream/consumers'; import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { finishedStream, assertReadableAborted, assertWritableAborted, assertProcessNormalExit, assertStreamOutput, assertStreamChunks, assertStreamError, assertStreamReadError, assertSubprocessOutput, assertSubprocessError, assertPromiseError, getReadableSubprocess, getReadWriteSubprocess, } from '../helpers/convert.js'; import {foobarString, foobarBuffer, foobarObject} from '../helpers/input.js'; import {simpleFull} from '../helpers/lines.js'; import {majorNodeVersion} from '../helpers/node-version.js'; import {prematureClose, fullStdio} from '../helpers/stdio.js'; import {outputObjectGenerator, getOutputsAsyncGenerator} from '../helpers/generator.js'; import {defaultHighWaterMark, defaultObjectHighWaterMark} from '../helpers/stream.js'; setFixtureDirectory(); test('.readable() success', async t => { const subprocess = getReadableSubprocess(); const stream = subprocess.readable(); t.false(stream instanceof Writable); t.is(stream.writable, undefined); t.true(stream instanceof Readable); t.true(stream.readable); await assertStreamOutput(t, stream); await assertSubprocessOutput(t, subprocess); }); // eslint-disable-next-line max-params const testReadableDefault = async (t, fdNumber, from, options, hasResult) => { const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString], options); const stream = subprocess.readable({from}); await assertStreamOutput(t, stream, hasResult ? foobarString : ''); await assertSubprocessOutput(t, subprocess, foobarString, fdNumber); }; test('.readable() can use stdout', testReadableDefault, 1, 'stdout', {}, true); test('.readable() can use stderr', testReadableDefault, 2, 'stderr', {}, true); test('.readable() can use stdio[*]', testReadableDefault, 3, 'fd3', fullStdio, true); test('.readable() uses stdout by default', testReadableDefault, 1, undefined, {}, true); test('.readable() does not use stderr by default', testReadableDefault, 2, undefined, {}, false); test('.readable() does not use stdio[*] by default', testReadableDefault, 3, undefined, fullStdio, false); test('.readable() uses stdout even if stderr is "ignore"', testReadableDefault, 1, 'stdout', {stderr: 'ignore'}, true); test('.readable() uses stderr even if stdout is "ignore"', testReadableDefault, 2, 'stderr', {stdout: 'ignore'}, true); test('.readable() uses stdout if "all" is used', testReadableDefault, 1, 'all', {all: true}, true); test('.readable() uses stderr if "all" is used', testReadableDefault, 2, 'all', {all: true}, true); const testBuffering = async (t, methodName) => { const subprocess = execa('noop-stdin-fd.js', ['1'], {buffer: false}); const stream = subprocess[methodName](); subprocess.stdin.write(foobarString); await once(subprocess.stdout, 'readable'); subprocess.stdin.end(); await assertStreamOutput(t, stream); }; test('.readable() buffers until read', testBuffering, 'readable'); test('.duplex() buffers until read', testBuffering, 'duplex'); test('.readable() abort -> subprocess fail', async t => { const subprocess = execa('noop-repeat.js'); const stream = subprocess.readable(); stream.destroy(); const error = await t.throwsAsync(text(stream)); assertProcessNormalExit(t, error, 1); t.true(error.message.includes('EPIPE')); assertWritableAborted(t, subprocess.stdin); assertReadableAborted(t, subprocess.stdout); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }); test('.readable() error -> subprocess fail', async t => { const subprocess = execa('noop-repeat.js'); const stream = subprocess.readable(); const cause = new Error(foobarString); stream.destroy(cause); const error = await assertStreamReadError(t, stream, {cause}); assertProcessNormalExit(t, error, 1); t.true(error.message.includes('EPIPE')); assertWritableAborted(t, subprocess.stdin); t.is(subprocess.stdout.errored, cause); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }); const testStdoutAbort = async (t, methodName) => { const subprocess = execa('ipc-echo.js', {ipc: true}); const stream = subprocess[methodName](); subprocess.stdout.destroy(); await subprocess.sendMessage(foobarString); const [error, message] = await Promise.all([ t.throwsAsync(finishedStream(stream)), subprocess.getOneMessage(), ]); t.like(error, prematureClose); t.is(message, foobarString); assertWritableAborted(t, subprocess.stdin); assertReadableAborted(t, subprocess.stdout); t.true(subprocess.stderr.readableEnded); await assertSubprocessOutput(t, subprocess, ''); }; test('subprocess.stdout abort + no more writes -> .readable() error + subprocess success', testStdoutAbort, 'readable'); test('subprocess.stdout abort + no more writes -> .duplex() error + subprocess success', testStdoutAbort, 'duplex'); const testStdoutError = async (t, methodName) => { const subprocess = execa('ipc-echo.js', {ipc: true}); const stream = subprocess[methodName](); const cause = new Error(foobarString); subprocess.stdout.destroy(cause); await subprocess.sendMessage(foobarString); const [error, message] = await Promise.all([ t.throwsAsync(finishedStream(stream)), subprocess.getOneMessage(), ]); t.is(message, foobarString); t.is(error.cause, cause); assertProcessNormalExit(t, error); t.is(subprocess.stdout.errored, cause); t.true(subprocess.stderr.readableEnded); assertWritableAborted(t, subprocess.stdin); await assertSubprocessError(t, subprocess, error); }; test('subprocess.stdout error + no more writes -> .readable() error + subprocess fail', testStdoutError, 'readable'); test('subprocess.stdout error + no more writes -> .duplex() error + subprocess fail', testStdoutError, 'duplex'); const testStdinAbortWrites = async (t, methodName) => { const subprocess = getReadWriteSubprocess(); const stream = subprocess[methodName](); subprocess.stdout.destroy(); subprocess.stdin.end(foobarString); const error = await t.throwsAsync(finishedStream(stream)); assertProcessNormalExit(t, error, 1); t.true(subprocess.stdin.writableEnded); assertReadableAborted(t, subprocess.stdout); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }; test('subprocess.stdout abort + more writes -> .readable() error + subprocess fail', testStdinAbortWrites, 'readable'); test('subprocess.stdout abort + more writes -> .duplex() error + subprocess fail', testStdinAbortWrites, 'duplex'); const testStdinErrorWrites = async (t, methodName) => { const subprocess = getReadWriteSubprocess(); const stream = subprocess[methodName](); const cause = new Error(foobarString); subprocess.stdout.destroy(cause); subprocess.stdin.end(foobarString); const error = await assertStreamError(t, stream, {cause}); assertProcessNormalExit(t, error, 1); t.true(subprocess.stdin.writableEnded); t.is(subprocess.stdout.errored, cause); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }; test('subprocess.stdout error + more writes -> .readable() error + subprocess fail', testStdinErrorWrites, 'readable'); test('subprocess.stdout error + more writes -> .duplex() error + subprocess fail', testStdinErrorWrites, 'duplex'); test('.readable() can be used with Stream.pipeline()', async t => { const subprocess = getReadableSubprocess(); const stream = subprocess.readable(); const outputStream = new PassThrough(); await pipeline(stream, outputStream); await finishedStream(stream); await assertStreamOutput(t, outputStream); await assertSubprocessOutput(t, subprocess); }); test('.readable() can error with Stream.pipeline()', async t => { const subprocess = execa('noop-fail.js', ['1', foobarString]); const stream = subprocess.readable(); const outputStream = new PassThrough(); const error = await t.throwsAsync(pipeline(stream, outputStream)); assertProcessNormalExit(t, error, 2); t.like(error, {stdout: foobarString}); await assertStreamError(t, stream, error); await assertStreamReadError(t, outputStream, error); await assertSubprocessError(t, subprocess, error); }); test('.readable() can pipe to errored stream with Stream.pipeline()', async t => { const subprocess = getReadableSubprocess(); const stream = subprocess.readable(); const outputStream = new PassThrough(); const cause = new Error('test'); outputStream.destroy(cause); // Node 23 does not allow calling `stream.pipeline()` with an already errored stream if (majorNodeVersion >= 23) { outputStream.on('error', () => {}); await t.throwsAsync(pipeline(stream, outputStream), {code: 'ERR_STREAM_UNABLE_TO_PIPE'}); } else { await assertPromiseError(t, pipeline(stream, outputStream), cause); await t.throwsAsync(finishedStream(stream)); const error = await assertStreamError(t, stream, cause); await assertStreamReadError(t, outputStream, cause); await assertSubprocessError(t, subprocess, {cause: error}); } }); test('.readable() can be used with Stream.compose()', async t => { const subprocess = getReadableSubprocess(); const stream = subprocess.readable(); const outputStream = new PassThrough(); await assertStreamOutput(t, compose(stream, outputStream)); await assertSubprocessOutput(t, subprocess); }); test('.readable() works with objectMode', async t => { const subprocess = execa('noop.js', {stdout: outputObjectGenerator()}); const stream = subprocess.readable(); t.true(stream.readableObjectMode); t.is(stream.readableHighWaterMark, defaultObjectHighWaterMark); await assertStreamChunks(t, stream, [foobarObject]); await assertSubprocessOutput(t, subprocess, [foobarObject]); }); test('.duplex() works with objectMode and reads', async t => { const subprocess = getReadWriteSubprocess({stdout: outputObjectGenerator()}); const stream = subprocess.duplex(); t.true(stream.readableObjectMode); t.is(stream.readableHighWaterMark, defaultObjectHighWaterMark); t.false(stream.writableObjectMode); t.is(stream.writableHighWaterMark, defaultHighWaterMark); stream.end(foobarString); await assertStreamChunks(t, stream, [foobarObject]); await assertSubprocessOutput(t, subprocess, [foobarObject]); }); test('.readable() works with default encoding', async t => { const subprocess = getReadableSubprocess(); const stream = subprocess.readable(); t.is(stream.readableEncoding, null); await assertStreamChunks(t, stream, [foobarBuffer]); await assertSubprocessOutput(t, subprocess, foobarString); }); test('.duplex() works with default encoding', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); t.is(stream.readableEncoding, null); stream.end(foobarString); await assertStreamChunks(t, stream, [foobarBuffer]); await assertSubprocessOutput(t, subprocess, foobarString); }); test('.readable() works with encoding "utf8"', async t => { const subprocess = getReadableSubprocess(); subprocess.stdout.setEncoding('utf8'); const stream = subprocess.readable(); t.is(stream.readableEncoding, 'utf8'); await assertStreamChunks(t, stream, [foobarString]); await assertSubprocessOutput(t, subprocess, foobarString); }); test('.duplex() works with encoding "utf8"', async t => { const subprocess = getReadWriteSubprocess(); subprocess.stdout.setEncoding('utf8'); const stream = subprocess.duplex(); t.is(stream.readableEncoding, 'utf8'); stream.end(foobarBuffer); await assertStreamChunks(t, stream, [foobarString]); await assertSubprocessOutput(t, subprocess, foobarString); }); test('.readable() has the right highWaterMark', async t => { const subprocess = execa('noop.js'); const stream = subprocess.readable(); t.is(stream.readableHighWaterMark, defaultHighWaterMark); await text(stream); }); test('.readable() can iterate over lines', async t => { const subprocess = execa('noop-fd.js', ['1', simpleFull]); const lines = []; for await (const line of subprocess.readable({binary: false, preserveNewlines: false})) { lines.push(line); } const expectedLines = ['aaa', 'bbb', 'ccc']; t.deepEqual(lines, expectedLines); await assertSubprocessOutput(t, subprocess, simpleFull); }); test('.readable() can wait for data', async t => { const subprocess = execa('noop.js', {stdout: getOutputsAsyncGenerator([foobarString, foobarString])(false, true)}); const stream = subprocess.readable(); t.is(stream.read(), null); await once(stream, 'readable'); t.is(stream.read().toString(), foobarString); t.is(stream.read(), null); await once(stream, 'readable'); t.is(stream.read().toString(), foobarString); t.is(stream.read(), null); await once(stream, 'readable'); t.is(stream.read(), null); await finishedStream(stream); await assertSubprocessOutput(t, subprocess, `${foobarString}${foobarString}`); }); const testBufferData = async (t, methodName) => { const chunk = '.'.repeat(defaultHighWaterMark).repeat(2); const subprocess = getReadWriteSubprocess(); const stream = subprocess[methodName](); subprocess.stdin.end(chunk); await assertStreamOutput(t, stream, chunk); await assertSubprocessOutput(t, subprocess, chunk); }; test('.readable() can buffer data', testBufferData, 'readable'); test('.duplex() can buffer data', testBufferData, 'duplex'); const assertDataEvents = async (t, stream, subprocess) => { const [output] = await once(stream, 'data'); t.is(output.toString(), foobarString); await finishedStream(stream); await assertSubprocessOutput(t, subprocess); }; test('.readable() can be read with "data" events', async t => { const subprocess = getReadableSubprocess(); const stream = subprocess.readable(); await assertDataEvents(t, stream, subprocess); }); test('.duplex() can be read with "data" events', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); stream.end(foobarString); await assertDataEvents(t, stream, subprocess); }); const assertPause = async (t, stream, subprocess) => { const onceData = once(stream, 'data'); stream.pause(); t.is(stream.readableLength, 0); do { // eslint-disable-next-line no-await-in-loop await setTimeout(10); } while (stream.readableLength === 0); t.false(await Promise.race([onceData, false])); stream.resume(); const [output] = await onceData; t.is(output.toString(), foobarString); await finishedStream(stream); await assertSubprocessOutput(t, subprocess); }; test('.readable() can be paused', async t => { const subprocess = getReadableSubprocess(); const stream = subprocess.readable(); await assertPause(t, stream, subprocess); }); test('.duplex() can be paused', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); stream.end(foobarString); await assertPause(t, stream, subprocess); }); // This feature does not work on Node 18. // @todo: remove after dropping support for Node 18. if (majorNodeVersion >= 20) { const testHighWaterMark = async (t, methodName) => { const subprocess = execa('stdin.js'); const stream = subprocess[methodName](); let count = 0; const onPause = once(subprocess.stdout, 'pause'); for (; !subprocess.stdout.isPaused(); count += 1) { subprocess.stdin.write('.'); // eslint-disable-next-line no-await-in-loop await Promise.race([onPause, once(subprocess.stdout, 'data')]); } const expectedCount = defaultObjectHighWaterMark + 1; const expectedOutput = '.'.repeat(expectedCount); t.is(count, expectedCount); subprocess.stdin.end(); await assertStreamOutput(t, stream, expectedOutput); await assertSubprocessOutput(t, subprocess, expectedOutput); }; test('.readable() pauses its buffering when too high', testHighWaterMark, 'readable'); test('.duplex() pauses its buffering when too high', testHighWaterMark, 'duplex'); } const testBigOutput = async (t, methodName) => { const bigChunk = '.'.repeat(1e6); const subprocess = execa('stdin.js', {input: bigChunk}); const stream = subprocess[methodName](); await assertStreamOutput(t, stream, bigChunk); await assertSubprocessOutput(t, subprocess, bigChunk); }; test('.readable() with big output', testBigOutput, 'readable'); test('.duplex() with big output', testBigOutput, 'duplex'); ================================================ FILE: test/convert/shared.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { finishedStream, assertWritableAborted, assertStreamError, assertSubprocessError, getReadWriteSubprocess, } from '../helpers/convert.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const testSubprocessFail = async (t, methodName) => { const subprocess = getReadWriteSubprocess(); const stream = subprocess[methodName](); const cause = new Error(foobarString); subprocess.kill(cause); const error = await assertStreamError(t, stream, {cause}); assertWritableAborted(t, subprocess.stdin); t.true(subprocess.stdout.readableEnded); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }; test('subprocess fail -> .readable() error', testSubprocessFail, 'readable'); test('subprocess fail -> .writable() error', testSubprocessFail, 'writable'); test('subprocess fail -> .duplex() error', testSubprocessFail, 'duplex'); const testErrorEvent = async (t, methodName) => { const subprocess = execa('empty.js'); const stream = subprocess[methodName](); t.is(stream.listenerCount('error'), 0); stream.destroy(); await t.throwsAsync(finishedStream(stream)); }; test('.readable() requires listening to "error" event', testErrorEvent, 'readable'); test('.writable() requires listening to "error" event', testErrorEvent, 'writable'); test('.duplex() requires listening to "error" event', testErrorEvent, 'duplex'); const testSubprocessError = async (t, methodName) => { const subprocess = getReadWriteSubprocess(); const stream = subprocess[methodName](); const cause = new Error(foobarString); subprocess.kill(cause); await assertStreamError(t, stream, {cause}); }; test('Do not need to await subprocess with .readable()', testSubprocessError, 'readable'); test('Do not need to await subprocess with .writable()', testSubprocessError, 'writable'); test('Do not need to await subprocess with .duplex()', testSubprocessError, 'duplex'); ================================================ FILE: test/convert/writable.js ================================================ import {once} from 'node:events'; import {compose, Readable, Writable} from 'node:stream'; import {pipeline} from 'node:stream/promises'; import {text} from 'node:stream/consumers'; import {setTimeout, scheduler} from 'node:timers/promises'; import {promisify} from 'node:util'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { finishedStream, assertWritableAborted, assertProcessNormalExit, assertStreamOutput, assertStreamError, assertSubprocessOutput, assertSubprocessError, assertPromiseError, getWritableSubprocess, getReadableSubprocess, getReadWriteSubprocess, } from '../helpers/convert.js'; import { foobarString, foobarBuffer, foobarObject, foobarObjectString, } from '../helpers/input.js'; import {prematureClose, fullReadableStdio} from '../helpers/stdio.js'; import { throwingGenerator, serializeGenerator, noopAsyncGenerator, } from '../helpers/generator.js'; import {defaultHighWaterMark, defaultObjectHighWaterMark} from '../helpers/stream.js'; setFixtureDirectory(); test('.writable() success', async t => { const subprocess = getWritableSubprocess(); const stream = subprocess.writable(); t.true(stream instanceof Writable); t.true(stream.writable); t.false(stream instanceof Readable); t.is(stream.readable, undefined); stream.end(foobarString); await finishedStream(stream); await assertSubprocessOutput(t, subprocess, foobarString, 2); }); const testWritableDefault = async (t, fdNumber, to, options) => { const subprocess = execa('stdin-fd.js', [`${fdNumber}`], options); const stream = subprocess.writable({to}); stream.end(foobarString); await finishedStream(stream); await assertSubprocessOutput(t, subprocess); }; test('.writable() can use stdin', testWritableDefault, 0, 'stdin', {}); test('.writable() can use stdio[*]', testWritableDefault, 3, 'fd3', fullReadableStdio()); test('.writable() uses stdin by default', testWritableDefault, 0, undefined, {}); test('.writable() hangs until ended', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.writable(); stream.write(foobarString); await setTimeout(1e2); stream.end(); await finishedStream(stream); await assertSubprocessOutput(t, subprocess); }); test('.duplex() hangs until ended', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); stream.write(foobarString); await setTimeout(1e2); stream.end(); await assertStreamOutput(t, stream); await assertSubprocessOutput(t, subprocess); }); const testEarlySuccess = async (t, methodName, hasWrites) => { const subprocess = hasWrites ? getReadableSubprocess() : execa('empty.js'); const stream = subprocess[methodName](); const error = await t.throwsAsync(finishedStream(stream)); t.like(error, prematureClose); assertWritableAborted(t, subprocess.stdin); t.true(subprocess.stdout.readableEnded); t.true(subprocess.stderr.readableEnded); await assertSubprocessOutput(t, subprocess, hasWrites ? foobarString : ''); }; test('subprocess early success with no writes -> .writable() abort', testEarlySuccess, 'writable', false); test('subprocess early success with no writes -> .duplex() abort', testEarlySuccess, 'duplex', false); test('subprocess early success with writes -> .writable() abort', testEarlySuccess, 'writable', true); test('subprocess early success with writes -> .duplex() abort', testEarlySuccess, 'duplex', true); test('.writable() abort -> subprocess fail', async t => { const subprocess = getWritableSubprocess(); const stream = subprocess.writable(); stream.destroy(); const error = await t.throwsAsync(finishedStream(stream)); t.like(error, prematureClose); assertProcessNormalExit(t, error); assertWritableAborted(t, subprocess.stdin); t.true(subprocess.stdout.readableEnded); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }); test('.writable() error -> subprocess fail', async t => { const subprocess = getWritableSubprocess(); const stream = subprocess.writable(); const cause = new Error(foobarString); stream.destroy(cause); const error = await assertStreamError(t, stream, {cause}); assertProcessNormalExit(t, error); t.is(subprocess.stdin.errored, cause); t.true(subprocess.stdout.readableEnded); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }); test('.writable() EPIPE error -> subprocess success', async t => { const subprocess = getWritableSubprocess(); const stream = subprocess.writable(); const error = new Error(foobarString); error.code = 'EPIPE'; stream.destroy(error); await assertStreamError(t, stream, error); t.is(subprocess.stdin.errored, error); t.true(subprocess.stdout.readableEnded); t.true(subprocess.stderr.readableEnded); await subprocess; }); test('subprocess.stdin end -> .writable() end + subprocess success', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.writable(); subprocess.stdin.end(foobarString); await finishedStream(stream); await assertSubprocessOutput(t, subprocess); }); test('subprocess.stdin end -> .duplex() end + subprocess success', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); subprocess.stdin.end(foobarString); await assertStreamOutput(t, stream); await assertSubprocessOutput(t, subprocess); }); const testStdinAbort = async (t, methodName) => { const subprocess = getReadWriteSubprocess(); const stream = subprocess[methodName](); subprocess.stdin.destroy(); const error = await t.throwsAsync(finishedStream(stream)); t.like(error, prematureClose); assertProcessNormalExit(t, error); assertWritableAborted(t, subprocess.stdin); t.true(subprocess.stdout.readableEnded); t.true(subprocess.stderr.readableEnded); await assertSubprocessError(t, subprocess, error); }; test('subprocess.stdin abort -> .writable() error + subprocess fail', testStdinAbort, 'writable'); test('subprocess.stdin abort -> .duplex() error + subprocess fail', testStdinAbort, 'duplex'); const testStdinError = async (t, methodName) => { const subprocess = getReadWriteSubprocess(); const stream = subprocess[methodName](); const cause = new Error(foobarString); subprocess.stdin.destroy(cause); const error = await assertStreamError(t, stream, {cause}); assertProcessNormalExit(t, error); t.is(subprocess.stdin.errored, cause); t.true(subprocess.stderr.readableEnded); t.true(subprocess.stdout.readableEnded); await assertSubprocessError(t, subprocess, error); }; test('subprocess.stdin error -> .writable() error + subprocess fail', testStdinError, 'writable'); test('subprocess.stdin error -> .duplex() error + subprocess fail', testStdinError, 'duplex'); test('.writable() can be used with Stream.pipeline()', async t => { const subprocess = getWritableSubprocess(); const inputStream = Readable.from([foobarString]); const stream = subprocess.writable(); await pipeline(inputStream, stream); await finishedStream(inputStream); await finishedStream(stream); await assertSubprocessOutput(t, subprocess, foobarString, 2); }); test('.writable() can error with Stream.pipeline()', async t => { const subprocess = execa('noop-stdin-fail.js', ['2']); const inputStream = Readable.from([foobarString]); const stream = subprocess.writable(); const error = await t.throwsAsync(pipeline(inputStream, stream)); assertProcessNormalExit(t, error, 2); t.is(error.stderr, foobarString); await finishedStream(inputStream); await assertStreamError(t, stream, error); await assertSubprocessError(t, subprocess, error); }); test('.writable() can pipe to errored stream with Stream.pipeline()', async t => { const subprocess = getWritableSubprocess(); const inputStream = Readable.from([foobarString]); const stream = subprocess.writable(); const cause = new Error('test'); inputStream.destroy(cause); await assertPromiseError(t, pipeline(inputStream, stream), cause); await t.throwsAsync(finishedStream(stream)); await assertStreamError(t, inputStream, cause); const error = await assertStreamError(t, stream, cause); await assertSubprocessError(t, subprocess, {cause: error}); }); test('.writable() can be used with Stream.compose()', async t => { const subprocess = getWritableSubprocess(); const inputStream = Readable.from([foobarString]); const stream = subprocess.writable(); await finishedStream(compose(inputStream, stream)); await assertSubprocessOutput(t, subprocess, foobarString, 2); }); test('.writable() works with objectMode', async t => { const subprocess = getReadWriteSubprocess({stdin: serializeGenerator(true, true)}); const stream = subprocess.writable(); t.true(stream.writableObjectMode); t.is(stream.writableHighWaterMark, defaultObjectHighWaterMark); stream.end(foobarObject); await finishedStream(stream); await assertSubprocessOutput(t, subprocess, foobarObjectString); }); test('.duplex() works with objectMode and writes', async t => { const subprocess = getReadWriteSubprocess({stdin: serializeGenerator(true, true)}); const stream = subprocess.duplex(); t.false(stream.readableObjectMode); t.is(stream.readableHighWaterMark, defaultHighWaterMark); t.true(stream.writableObjectMode); t.is(stream.writableHighWaterMark, defaultObjectHighWaterMark); stream.end(foobarObject); await assertStreamOutput(t, stream, foobarObjectString); await assertSubprocessOutput(t, subprocess, foobarObjectString); }); test('.writable() has the right highWaterMark', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.writable(); t.is(stream.writableHighWaterMark, defaultHighWaterMark); stream.end(); await finishedStream(stream); }); const writeUntilFull = async (t, stream, subprocess) => { const size = stream.writableHighWaterMark / 2; const chunk = '.'.repeat(size); t.is(subprocess.stdin.writableLength, 0); t.is(stream.writableLength, 0); t.false(subprocess.stdin.writableNeedDrain); t.false(stream.writableNeedDrain); t.true(stream.write(chunk)); t.is(subprocess.stdin.writableLength, size); t.is(stream.writableLength, 0); t.false(subprocess.stdin.writableNeedDrain); t.false(stream.writableNeedDrain); t.true(stream.write(chunk)); t.is(subprocess.stdin.writableLength, size * 2); t.is(stream.writableLength, size); t.true(subprocess.stdin.writableNeedDrain); t.false(stream.writableNeedDrain); t.false(stream.write(chunk)); t.is(subprocess.stdin.writableLength, size * 2); t.is(stream.writableLength, size * 2); t.true(subprocess.stdin.writableNeedDrain); t.true(stream.writableNeedDrain); await once(stream, 'drain'); stream.end(); return '.'.repeat(size * 3); }; test('.writable() waits when its buffer is full', async t => { const subprocess = getReadWriteSubprocess({stdin: noopAsyncGenerator(false, true)}); const stream = subprocess.writable(); const expectedOutput = await writeUntilFull(t, stream, subprocess); await assertSubprocessOutput(t, subprocess, expectedOutput); }); test('.duplex() waits when its buffer is full', async t => { const subprocess = getReadWriteSubprocess({stdin: noopAsyncGenerator(false, true)}); const stream = subprocess.duplex(); const expectedOutput = await writeUntilFull(t, stream, subprocess); await assertStreamOutput(t, stream, expectedOutput); await assertSubprocessOutput(t, subprocess, expectedOutput); }); const testPropagateError = async (t, methodName) => { const cause = new Error(foobarString); const subprocess = getReadWriteSubprocess({stdin: throwingGenerator(cause)()}); const stream = subprocess[methodName](); stream.end('.'); await assertStreamError(t, stream, {cause}); }; test('.writable() propagates write errors', testPropagateError, 'writable'); test('.duplex() propagates write errors', testPropagateError, 'duplex'); const testWritev = async (t, methodName, waitForStream) => { const subprocess = getReadWriteSubprocess({stdin: noopAsyncGenerator()}); const stream = subprocess[methodName](); const chunk = '.'.repeat(stream.writableHighWaterMark); stream.write(chunk); t.true(stream.writableNeedDrain); const [writeInOneTick] = await Promise.race([ Promise.all([true, promisify(stream.write.bind(stream))(chunk)]), Promise.all([false, scheduler.yield()]), ]); t.true(writeInOneTick); stream.end(); await waitForStream(stream); }; test('.writable() can use .writev()', testWritev, 'writable', finishedStream); test('.duplex() can use .writev()', testWritev, 'duplex', text); test('.writable() can set encoding', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.writable(); stream.end(foobarBuffer.toString('hex'), 'hex'); await finishedStream(stream); await assertSubprocessOutput(t, subprocess); }); test('.duplex() can set encoding', async t => { const subprocess = getReadWriteSubprocess(); const stream = subprocess.duplex(); stream.end(foobarBuffer.toString('hex'), 'hex'); await assertStreamOutput(t, stream); await assertSubprocessOutput(t, subprocess); }); ================================================ FILE: test/fixtures/all-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; console.log('std\nout'); console.error('std\nerr'); process.exitCode = 1; ================================================ FILE: test/fixtures/all.js ================================================ #!/usr/bin/env node console.log('std\nout'); console.error('std\nerr'); ================================================ FILE: test/fixtures/command with space.js ================================================ #!/usr/bin/env node import process from 'node:process'; console.log(process.argv.slice(2).join('\n')); ================================================ FILE: test/fixtures/delay.js ================================================ #!/usr/bin/env node import process from 'node:process'; const delay = Number(process.argv[2]); setTimeout(() => {}, delay); ================================================ FILE: test/fixtures/detach.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {execa} from '../../index.js'; const subprocess = execa('forever.js', {detached: true}); console.log(subprocess.pid); process.exit(0); ================================================ FILE: test/fixtures/echo-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getWriteStream} from '../helpers/fs.js'; console.log('stdout'); console.error('stderr'); getWriteStream(3).write('fd3'); process.exitCode = 1; ================================================ FILE: test/fixtures/echo.js ================================================ #!/usr/bin/env node import process from 'node:process'; console.log(process.argv.slice(2).join('\n')); ================================================ FILE: test/fixtures/empty.js ================================================ #!/usr/bin/env node ================================================ FILE: test/fixtures/environment.js ================================================ #!/usr/bin/env node import process from 'node:process'; console.log(process.env.FOO); console.log(process.env.BAR); ================================================ FILE: test/fixtures/exit.js ================================================ #!/usr/bin/env node import process from 'node:process'; process.exitCode = Number(process.argv[2]); ================================================ FILE: test/fixtures/fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; process.exitCode = 2; ================================================ FILE: test/fixtures/forever.js ================================================ #!/usr/bin/env node setTimeout(() => {}, 1e8); ================================================ FILE: test/fixtures/graceful-disconnect.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {once} from 'node:events'; import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); await Promise.all([ once(process, 'disconnect'), sendMessage(cancelSignal.reason), ]); ================================================ FILE: test/fixtures/graceful-echo.js ================================================ #!/usr/bin/env node import {getCancelSignal, sendMessage, getOneMessage} from 'execa'; await getCancelSignal(); await sendMessage(await getOneMessage()); ================================================ FILE: test/fixtures/graceful-listener.js ================================================ #!/usr/bin/env node import {getCancelSignal, sendMessage} from 'execa'; const id = setTimeout(() => {}, 1e8); const cancelSignal = await getCancelSignal(); // eslint-disable-next-line unicorn/prefer-add-event-listener cancelSignal.onabort = async () => { await sendMessage(cancelSignal.reason); clearTimeout(id); }; await sendMessage('.'); ================================================ FILE: test/fixtures/graceful-none.js ================================================ #!/usr/bin/env node import {getCancelSignal} from 'execa'; await getCancelSignal(); ================================================ FILE: test/fixtures/graceful-print.js ================================================ #!/usr/bin/env node import {getCancelSignal} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); console.log(cancelSignal.reason); ================================================ FILE: test/fixtures/graceful-ref.js ================================================ #!/usr/bin/env node import {getCancelSignal} from 'execa'; const cancelSignal = await getCancelSignal(); cancelSignal.addEventListener('abort', () => {}); ================================================ FILE: test/fixtures/graceful-send-echo.js ================================================ #!/usr/bin/env node import {getCancelSignal, getOneMessage, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; const message = await getOneMessage(); const cancelSignal = await getCancelSignal(); await sendMessage(message); await onAbortedSignal(cancelSignal); await sendMessage(cancelSignal.reason); ================================================ FILE: test/fixtures/graceful-send-fast.js ================================================ #!/usr/bin/env node import {getCancelSignal, sendMessage} from 'execa'; const cancelSignal = await getCancelSignal(); await sendMessage(cancelSignal.aborted); ================================================ FILE: test/fixtures/graceful-send-print.js ================================================ #!/usr/bin/env node import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; const cancelSignal = await getCancelSignal(); await sendMessage(cancelSignal.aborted); await onAbortedSignal(cancelSignal); console.log(cancelSignal.reason); ================================================ FILE: test/fixtures/graceful-send-string.js ================================================ #!/usr/bin/env node import {getCancelSignal, sendMessage} from 'execa'; import {foobarString} from '../helpers/input.js'; await getCancelSignal(); await sendMessage(foobarString); ================================================ FILE: test/fixtures/graceful-send-twice.js ================================================ #!/usr/bin/env node import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; const cancelSignal = await getCancelSignal(); await sendMessage(cancelSignal.aborted); await onAbortedSignal(cancelSignal); await sendMessage(cancelSignal.reason); ================================================ FILE: test/fixtures/graceful-send.js ================================================ #!/usr/bin/env node import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); await sendMessage(cancelSignal.reason); ================================================ FILE: test/fixtures/graceful-twice.js ================================================ #!/usr/bin/env node import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; await getCancelSignal(); const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); await sendMessage(cancelSignal.reason); ================================================ FILE: test/fixtures/graceful-wait.js ================================================ #!/usr/bin/env node import {getCancelSignal} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); ================================================ FILE: test/fixtures/hello.cmd ================================================ ECHO Hello World ================================================ FILE: test/fixtures/hello.sh ================================================ echo Hello World ================================================ FILE: test/fixtures/ipc-any.js ================================================ #!/usr/bin/env node import process from 'node:process'; import * as execaExports from '../../index.js'; const methodName = process.argv[2]; await execaExports[methodName](); ================================================ FILE: test/fixtures/ipc-disconnect-get.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getOneMessage} from '../../index.js'; process.disconnect(); console.log(process.channel); await getOneMessage(); ================================================ FILE: test/fixtures/ipc-disconnect.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {once} from 'node:events'; import * as execaExports from '../../index.js'; const methodName = process.argv[2]; if (process.channel !== null) { await once(process, 'disconnect'); } await execaExports[methodName](); ================================================ FILE: test/fixtures/ipc-echo-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage, getOneMessage} from '../../index.js'; await sendMessage(await getOneMessage()); process.exitCode = 1; ================================================ FILE: test/fixtures/ipc-echo-filter.js ================================================ #!/usr/bin/env node import {sendMessage, getOneMessage} from '../../index.js'; import {foobarArray} from '../helpers/input.js'; const message = await getOneMessage({filter: message => message === foobarArray[1]}); await sendMessage(message); ================================================ FILE: test/fixtures/ipc-echo-item.js ================================================ #!/usr/bin/env node import {sendMessage, getOneMessage} from '../../index.js'; const [message] = await getOneMessage(); await sendMessage(message); ================================================ FILE: test/fixtures/ipc-echo-twice-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage, getOneMessage} from '../../index.js'; const message = await getOneMessage(); await sendMessage(message); await sendMessage(message); process.exitCode = 1; ================================================ FILE: test/fixtures/ipc-echo-twice-wait.js ================================================ #!/usr/bin/env node import {setTimeout} from 'node:timers/promises'; import {sendMessage, getOneMessage} from '../../index.js'; const message = await getOneMessage(); await sendMessage(message); const secondMessage = await getOneMessage(); await sendMessage(secondMessage); await setTimeout(1e3); await sendMessage('.'); ================================================ FILE: test/fixtures/ipc-echo-twice.js ================================================ #!/usr/bin/env node import {sendMessage, getOneMessage} from '../../index.js'; const message = await getOneMessage(); await sendMessage(message); const secondMessage = await getOneMessage(); await sendMessage(secondMessage); ================================================ FILE: test/fixtures/ipc-echo-wait.js ================================================ #!/usr/bin/env node import {setTimeout} from 'node:timers/promises'; import {sendMessage, getOneMessage} from '../../index.js'; await setTimeout(1e3); await sendMessage(await getOneMessage()); ================================================ FILE: test/fixtures/ipc-echo.js ================================================ #!/usr/bin/env node import {sendMessage, getOneMessage} from '../../index.js'; await sendMessage(await getOneMessage()); ================================================ FILE: test/fixtures/ipc-get-filter-throw.js ================================================ #!/usr/bin/env node import {getOneMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await getOneMessage({ filter() { throw new Error(foobarString); }, }); ================================================ FILE: test/fixtures/ipc-get-io-error.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getOneMessage} from '../../index.js'; import {mockSendIoError} from '../helpers/ipc.js'; mockSendIoError(process); console.log(await getOneMessage()); ================================================ FILE: test/fixtures/ipc-get-ref.js ================================================ #!/usr/bin/env node import {getOneMessage} from '../../index.js'; getOneMessage(); ================================================ FILE: test/fixtures/ipc-get-send-get.js ================================================ #!/usr/bin/env node import {argv} from 'node:process'; import {sendMessage, getOneMessage} from '../../index.js'; import {alwaysPass} from '../helpers/ipc.js'; const filter = argv[2] === 'true' ? alwaysPass : undefined; const message = await getOneMessage({filter}); await Promise.all([ getOneMessage({filter}), sendMessage(message), ]); ================================================ FILE: test/fixtures/ipc-get-unref.js ================================================ #!/usr/bin/env node import {getOneMessage} from '../../index.js'; getOneMessage({reference: false}); ================================================ FILE: test/fixtures/ipc-get.js ================================================ #!/usr/bin/env node import {getOneMessage} from '../../index.js'; await getOneMessage(); ================================================ FILE: test/fixtures/ipc-iterate-back-serial.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage, getOneMessage} from '../../index.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; import {alwaysPass, getFirst} from '../helpers/ipc.js'; const filter = process.argv[2] === 'true' ? alwaysPass : undefined; await sendMessage(await getOneMessage({filter})); const promise = sendMessage(1); process.emit('message', '.'); await promise; const messages = Array.from({length: PARALLEL_COUNT}, (_, index) => index + 2); for (const message of messages) { // eslint-disable-next-line no-await-in-loop await sendMessage(message); } const secondPromise = process.argv[3] === 'true' ? getFirst() : getOneMessage({filter}); await sendMessage(await secondPromise); ================================================ FILE: test/fixtures/ipc-iterate-back.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage, getOneMessage} from '../../index.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; import {alwaysPass, getFirst} from '../helpers/ipc.js'; const filter = process.argv[2] === 'true' ? alwaysPass : undefined; await sendMessage(await getOneMessage({filter})); const messages = Array.from({length: PARALLEL_COUNT}, (_, index) => index + 1); await Promise.all([ ...messages.map(message => sendMessage(message)), process.emit('message', '.'), ]); const promise = process.argv[3] === 'true' ? getFirst() : getOneMessage({filter}); await sendMessage(await promise); ================================================ FILE: test/fixtures/ipc-iterate-break.js ================================================ #!/usr/bin/env node import {sendMessage, getEachMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; const iterable = getEachMessage(); await sendMessage(foobarString); // eslint-disable-next-line no-unreachable-loop for await (const _ of iterable) { break; } ================================================ FILE: test/fixtures/ipc-iterate-error.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage, getEachMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; const echoMessages = async () => { for await (const message of getEachMessage()) { if (message === foobarString) { break; } await sendMessage(message); } }; process.on('error', () => {}); // eslint-disable-next-line unicorn/prefer-top-level-await const promise = echoMessages(); process.emit('error', new Error(foobarString)); await promise; ================================================ FILE: test/fixtures/ipc-iterate-io-error.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getEachMessage} from '../../index.js'; import {mockSendIoError} from '../helpers/ipc.js'; mockSendIoError(process); for await (const message of getEachMessage()) { console.log(message); } ================================================ FILE: test/fixtures/ipc-iterate-print.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage, getEachMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; const iterable = getEachMessage(); await sendMessage(foobarString); for await (const message of iterable) { if (message === foobarString) { break; } process.stdout.write(`${message}`); } ================================================ FILE: test/fixtures/ipc-iterate-ref.js ================================================ #!/usr/bin/env node import {getEachMessage} from '../../index.js'; getEachMessage(); ================================================ FILE: test/fixtures/ipc-iterate-send.js ================================================ #!/usr/bin/env node import {sendMessage, getEachMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; const iterable = getEachMessage(); await sendMessage(foobarString); for await (const message of iterable) { console.log(message); } ================================================ FILE: test/fixtures/ipc-iterate-throw.js ================================================ #!/usr/bin/env node import {sendMessage, getEachMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; const iterable = getEachMessage(); await sendMessage(foobarString); // eslint-disable-next-line no-unreachable-loop for await (const message of iterable) { throw new Error(message); } ================================================ FILE: test/fixtures/ipc-iterate-twice.js ================================================ #!/usr/bin/env node import {sendMessage, getEachMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; for (let index = 0; index < 2; index += 1) { // Intentionally not awaiting `sendMessage()` to avoid a race condition sendMessage(foobarString); // eslint-disable-next-line no-await-in-loop for await (const message of getEachMessage()) { if (message === foobarString) { break; } await sendMessage(`${index}${message}`); } } ================================================ FILE: test/fixtures/ipc-iterate-unref.js ================================================ #!/usr/bin/env node import {getEachMessage} from '../../index.js'; getEachMessage({reference: false}); ================================================ FILE: test/fixtures/ipc-iterate.js ================================================ #!/usr/bin/env node import {getEachMessage, sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; for await (const message of getEachMessage()) { if (message === foobarString) { break; } await sendMessage(message); } ================================================ FILE: test/fixtures/ipc-once-disconnect-get.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getOneMessage} from '../../index.js'; await getOneMessage(); process.once('disconnect', () => { console.log('.'); }); process.send('.'); ================================================ FILE: test/fixtures/ipc-once-disconnect-send.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage} from '../../index.js'; await sendMessage('.'); process.once('disconnect', () => { console.log('.'); }); process.send('.'); ================================================ FILE: test/fixtures/ipc-once-disconnect.js ================================================ #!/usr/bin/env node import process from 'node:process'; process.send('.'); process.once('disconnect', () => { console.log('.'); }); ================================================ FILE: test/fixtures/ipc-once-message-get.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getOneMessage} from '../../index.js'; await getOneMessage(); process.once('message', message => { console.log(message); }); process.send('.'); ================================================ FILE: test/fixtures/ipc-once-message-send.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage} from '../../index.js'; await sendMessage('.'); process.once('message', message => { console.log(message); }); ================================================ FILE: test/fixtures/ipc-once-message.js ================================================ #!/usr/bin/env node import process from 'node:process'; process.send('.'); process.once('message', message => { console.log(message); }); ================================================ FILE: test/fixtures/ipc-print-many-each.js ================================================ #!/usr/bin/env node import {argv} from 'node:process'; import {getEachMessage} from '../../index.js'; const count = Number(argv[2]); for (let index = 0; index < count; index += 1) { // eslint-disable-next-line no-await-in-loop, no-unreachable-loop for await (const message of getEachMessage()) { console.log(message); break; } } ================================================ FILE: test/fixtures/ipc-print-many.js ================================================ #!/usr/bin/env node import {argv} from 'node:process'; import {getOneMessage} from '../../index.js'; import {alwaysPass} from '../helpers/ipc.js'; const count = Number(argv[2]); const filter = argv[3] === 'true' ? alwaysPass : undefined; for (let index = 0; index < count; index += 1) { // eslint-disable-next-line no-await-in-loop console.log(await getOneMessage({filter})); } ================================================ FILE: test/fixtures/ipc-process-error.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getOneMessage, sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; import {alwaysPass} from '../helpers/ipc.js'; process.on('error', () => {}); const filter = process.argv[2] === 'true' ? alwaysPass : undefined; const promise = getOneMessage({filter}); process.emit('error', new Error(foobarString)); await sendMessage(await promise); ================================================ FILE: test/fixtures/ipc-process-send-get.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {foobarString} from '../helpers/input.js'; import {getOneMessage} from '../../index.js'; await getOneMessage(); process.send(foobarString, () => { console.log('.'); }); ================================================ FILE: test/fixtures/ipc-process-send-send.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {foobarString} from '../helpers/input.js'; import {sendMessage} from '../../index.js'; await sendMessage('.'); process.send(foobarString, () => { console.log('.'); }); ================================================ FILE: test/fixtures/ipc-process-send.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {foobarString} from '../helpers/input.js'; process.send(foobarString, () => { console.log('.'); }); ================================================ FILE: test/fixtures/ipc-replay.js ================================================ #!/usr/bin/env node import {setTimeout} from 'node:timers/promises'; import {sendMessage, getOneMessage} from '../../index.js'; await sendMessage(await getOneMessage()); await setTimeout(1e3); await sendMessage(await getOneMessage()); ================================================ FILE: test/fixtures/ipc-send-argv.js ================================================ #!/usr/bin/env node import {argv} from 'node:process'; import {sendMessage} from '../../index.js'; const message = argv[2]; await sendMessage(message); ================================================ FILE: test/fixtures/ipc-send-disconnect.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await sendMessage(foobarString); process.disconnect(); ================================================ FILE: test/fixtures/ipc-send-echo-strict.js ================================================ #!/usr/bin/env node import {setTimeout} from 'node:timers/promises'; import {sendMessage, getOneMessage} from '../../index.js'; await sendMessage('.', {strict: true}); await setTimeout(10); await sendMessage(await getOneMessage()); ================================================ FILE: test/fixtures/ipc-send-echo-wait.js ================================================ #!/usr/bin/env node import {setTimeout} from 'node:timers/promises'; import {sendMessage, getOneMessage} from '../../index.js'; await sendMessage('.'); await setTimeout(1e3); await sendMessage(await getOneMessage()); ================================================ FILE: test/fixtures/ipc-send-error.js ================================================ #!/usr/bin/env node import {sendMessage} from '../../index.js'; await sendMessage(0n); ================================================ FILE: test/fixtures/ipc-send-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await sendMessage(foobarString); process.exitCode = 1; ================================================ FILE: test/fixtures/ipc-send-forever.js ================================================ #!/usr/bin/env node import {sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await sendMessage(foobarString); setTimeout(() => {}, 1e8); ================================================ FILE: test/fixtures/ipc-send-get.js ================================================ #!/usr/bin/env node import {sendMessage, getOneMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await Promise.all([ getOneMessage(), sendMessage(foobarString), ]); ================================================ FILE: test/fixtures/ipc-send-io-error.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage} from '../../index.js'; import {mockSendIoError} from '../helpers/ipc.js'; mockSendIoError(process); await sendMessage('.'); ================================================ FILE: test/fixtures/ipc-send-json.js ================================================ #!/usr/bin/env node import {argv} from 'node:process'; import {sendMessage} from '../../index.js'; const message = JSON.parse(argv[2]); await sendMessage(message); ================================================ FILE: test/fixtures/ipc-send-many.js ================================================ #!/usr/bin/env node import {argv} from 'node:process'; import {sendMessage} from '../../index.js'; const count = Number(argv[2]); await Promise.all(Array.from({length: count}, (_, index) => sendMessage(index))); ================================================ FILE: test/fixtures/ipc-send-native.js ================================================ #!/usr/bin/env node import process from 'node:process'; process.send('.'); ================================================ FILE: test/fixtures/ipc-send-pid.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {execa, sendMessage} from '../../index.js'; const cleanup = process.argv[2] === 'true'; const detached = process.argv[3] === 'true'; const subprocess = execa('forever.js', {cleanup, detached}); await sendMessage(subprocess.pid); await subprocess; ================================================ FILE: test/fixtures/ipc-send-print.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage, getOneMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await sendMessage(foobarString); process.stdout.write('.'); await getOneMessage(); ================================================ FILE: test/fixtures/ipc-send-repeat.js ================================================ #!/usr/bin/env node import {argv} from 'node:process'; import {sendMessage} from '../../index.js'; const count = Number(argv[2]); for (let index = 0; index < count; index += 1) { // eslint-disable-next-line no-await-in-loop await sendMessage(index); } ================================================ FILE: test/fixtures/ipc-send-strict-catch.js ================================================ #!/usr/bin/env node import {sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; try { await sendMessage(foobarString, {strict: true}); } catch { await sendMessage(foobarString); } ================================================ FILE: test/fixtures/ipc-send-strict-get.js ================================================ #!/usr/bin/env node import {sendMessage, getOneMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await sendMessage(foobarString, {strict: true}); await sendMessage(await getOneMessage()); ================================================ FILE: test/fixtures/ipc-send-strict-listen.js ================================================ #!/usr/bin/env node import {sendMessage, getOneMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; const [message] = await Promise.all([ getOneMessage(), sendMessage(foobarString, {strict: true}), ]); await sendMessage(message); ================================================ FILE: test/fixtures/ipc-send-strict.js ================================================ #!/usr/bin/env node import {sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await sendMessage(foobarString, {strict: true}); ================================================ FILE: test/fixtures/ipc-send-twice.js ================================================ #!/usr/bin/env node import {sendMessage} from '../../index.js'; import {foobarArray} from '../helpers/input.js'; await sendMessage(foobarArray[0]); await sendMessage(foobarArray[1]); ================================================ FILE: test/fixtures/ipc-send-wait-print.js ================================================ #!/usr/bin/env node import {setTimeout} from 'node:timers/promises'; import {sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; await sendMessage(foobarString); await setTimeout(100); console.log('.'); ================================================ FILE: test/fixtures/ipc-send.js ================================================ #!/usr/bin/env node import {argv} from 'node:process'; import {sendMessage} from '../../index.js'; import {foobarString} from '../helpers/input.js'; const message = argv[2] || foobarString; await sendMessage(message); ================================================ FILE: test/fixtures/max-buffer.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getWriteStream} from '../helpers/fs.js'; const fdNumber = Number(process.argv[2]); const bytes = '.'.repeat(Number(process.argv[3] || 1e7)); getWriteStream(fdNumber).write(bytes); ================================================ FILE: test/fixtures/nested/custom-event.js ================================================ export const getOptions = ({type, eventProperty}) => ({ verbose: (verboseLine, verboseObject) => verboseObject.type === type ? `${verboseObject[eventProperty]}` : undefined, }); ================================================ FILE: test/fixtures/nested/custom-json.js ================================================ export const getOptions = ({type}) => ({ verbose: (verboseLine, verboseObject) => verboseObject.type === type ? JSON.stringify(verboseObject) : undefined, }); ================================================ FILE: test/fixtures/nested/custom-object-stdout.js ================================================ import {foobarObject} from '../../helpers/input.js'; export const getOptions = () => ({ verbose: (verboseLine, {type}) => type === 'output' ? verboseLine : undefined, stdout: { * transform() { yield foobarObject; }, objectMode: true, }, }); ================================================ FILE: test/fixtures/nested/custom-option.js ================================================ export const getOptions = ({type, optionName}) => ({ verbose: (verboseLine, verboseObject) => verboseObject.type === type ? `${verboseObject.options[optionName]}` : undefined, }); ================================================ FILE: test/fixtures/nested/custom-print-function.js ================================================ export const getOptions = ({type, fdNumber, secondFdNumber}) => ({ verbose: { [fdNumber](verboseLine, verboseObject) { if (verboseObject.type === type) { console.warn(verboseLine); } }, [secondFdNumber]: 'none', }, }); ================================================ FILE: test/fixtures/nested/custom-print-multiple.js ================================================ export const getOptions = ({type, fdNumber, secondFdNumber}) => ({ verbose: { [fdNumber](verboseLine, verboseObject) { if (verboseObject.type === type) { console.warn(verboseLine); } }, [secondFdNumber]() {}, }, }); ================================================ FILE: test/fixtures/nested/custom-print.js ================================================ export const getOptions = ({type, fdNumber}) => ({ verbose: setFdSpecific( fdNumber, (verboseLine, verboseObject) => verboseObject.type === type ? verboseLine : undefined, ), }); const setFdSpecific = (fdNumber, option) => fdNumber === undefined ? option : {[fdNumber]: option}; ================================================ FILE: test/fixtures/nested/custom-result.js ================================================ export const getOptions = ({type}) => ({ verbose: (verboseLine, verboseObject) => verboseObject.type === type ? JSON.stringify(verboseObject.result) : undefined, }); ================================================ FILE: test/fixtures/nested/custom-return.js ================================================ export const getOptions = ({verboseOutput}) => ({ verbose(verboseLine, {type}) { return type === 'command' ? verboseOutput : undefined; }, }); ================================================ FILE: test/fixtures/nested/custom-throw.js ================================================ export const getOptions = ({type, errorMessage}) => ({ verbose(verboseLine, verboseObject) { if (verboseObject.type === type) { throw new Error(errorMessage); } }, }); ================================================ FILE: test/fixtures/nested/custom-uppercase.js ================================================ export const getOptions = () => ({ verbose(verboseLine, {type}) { return type === 'command' ? verboseLine.replace('noop', 'NOOP') : undefined; }, }); ================================================ FILE: test/fixtures/nested/file-url.js ================================================ import {pathToFileURL} from 'node:url'; export const getOptions = ({stdout: {file}}) => ({stdout: pathToFileURL(file)}); ================================================ FILE: test/fixtures/nested/generator-big-array.js ================================================ import {getOutputGenerator} from '../../helpers/generator.js'; const bigArray = Array.from({length: 100}, (_, index) => index); export const getOptions = () => ({stdout: getOutputGenerator(bigArray)(true)}); ================================================ FILE: test/fixtures/nested/generator-duplex.js ================================================ import {uppercaseBufferDuplex} from '../../helpers/duplex.js'; export const getOptions = () => ({stdout: uppercaseBufferDuplex()}); ================================================ FILE: test/fixtures/nested/generator-object.js ================================================ import {outputObjectGenerator} from '../../helpers/generator.js'; export const getOptions = () => ({stdout: outputObjectGenerator()}); ================================================ FILE: test/fixtures/nested/generator-string-object.js ================================================ import {getOutputGenerator} from '../../helpers/generator.js'; import {simpleFull} from '../../helpers/lines.js'; export const getOptions = () => ({stdout: getOutputGenerator(simpleFull)(true)}); ================================================ FILE: test/fixtures/nested/generator-uppercase.js ================================================ import {uppercaseGenerator} from '../../helpers/generator.js'; export const getOptions = () => ({stdout: uppercaseGenerator()}); ================================================ FILE: test/fixtures/nested/writable-web.js ================================================ export const getOptions = () => ({stdout: new WritableStream()}); ================================================ FILE: test/fixtures/nested/writable.js ================================================ import process from 'node:process'; export const getOptions = () => ({stdout: process.stdout}); ================================================ FILE: test/fixtures/nested-double.js ================================================ #!/usr/bin/env node import {execa, getOneMessage} from '../../index.js'; const {file, commandArguments, options} = await getOneMessage(); const firstArguments = commandArguments.slice(0, -1); const lastArgument = commandArguments.at(-1); await Promise.all([ execa(file, [...firstArguments, lastArgument], options), execa(file, [...firstArguments, lastArgument.toUpperCase()], options), ]); ================================================ FILE: test/fixtures/nested-fail.js ================================================ #!/usr/bin/env node import {execa, getOneMessage} from '../../index.js'; const {file, commandArguments, options} = await getOneMessage(); const subprocess = execa(file, commandArguments, options); subprocess.kill(new Error(commandArguments[0])); await subprocess; ================================================ FILE: test/fixtures/nested-inherit.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {execa} from '../../index.js'; import {generatorsMap} from '../helpers/map.js'; const type = process.argv[2]; await execa('noop-fd.js', ['1'], {stdout: ['inherit', generatorsMap[type].uppercase()]}); ================================================ FILE: test/fixtures/nested-multiple-stdin.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {execa, execaSync} from '../../index.js'; import {foobarString} from '../helpers/input.js'; import {parseStdioOption} from '../helpers/stdio.js'; const [stdioOption, isSyncString] = process.argv.slice(2); const stdin = parseStdioOption(stdioOption); const execaMethod = isSyncString === 'true' ? execaSync : execa; await execaMethod('stdin.js', {input: foobarString, stdin, stdout: 'inherit'}); ================================================ FILE: test/fixtures/nested-multiple-stdio-output.js ================================================ #!/usr/bin/env node import {Buffer} from 'node:buffer'; import process from 'node:process'; import {execa, execaSync} from '../../index.js'; import {foobarString} from '../helpers/input.js'; import {getStdio, parseStdioOption} from '../helpers/stdio.js'; import {getWriteStream} from '../helpers/fs.js'; const [stdioOption, fdNumber, outerFdNumber, isSyncString, encoding] = process.argv.slice(2); const stdioValue = parseStdioOption(stdioOption); const execaMethod = isSyncString === 'true' ? execaSync : execa; const {stdio} = await execaMethod('noop-fd.js', [fdNumber, foobarString], {...getStdio(Number(fdNumber), stdioValue), encoding}); getWriteStream(Number(outerFdNumber)).write(`nested ${Buffer.from(stdio[fdNumber]).toString()}`); ================================================ FILE: test/fixtures/nested-node.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getWriteStream} from '../helpers/fs.js'; import {execa, execaNode} from '../../index.js'; const [fakeExecArgv, execaMethod, nodeOptions, file, ...commandArguments] = process.argv.slice(2); if (fakeExecArgv !== '') { process.execArgv = [fakeExecArgv]; } const filteredNodeOptions = [nodeOptions].filter(Boolean); const {stdout, stderr} = await (execaMethod === 'execaNode' ? execaNode(file, commandArguments, {nodeOptions: filteredNodeOptions}) : execa(file, commandArguments, {nodeOptions: filteredNodeOptions, node: true})); console.log(stdout); getWriteStream(3).write(stderr); ================================================ FILE: test/fixtures/nested-pipe-file.js ================================================ #!/usr/bin/env node import {execa, getOneMessage} from '../../index.js'; const { file, commandArguments = [], options: { sourceOptions = {}, destinationFile, destinationArguments = [], destinationOptions = {}, }, } = await getOneMessage(); await execa(file, commandArguments, sourceOptions) .pipe(destinationFile, destinationArguments, destinationOptions); ================================================ FILE: test/fixtures/nested-pipe-script.js ================================================ #!/usr/bin/env node import {$, getOneMessage} from '../../index.js'; const { file, commandArguments = [], options: { sourceOptions = {}, destinationFile, destinationArguments = [], destinationOptions = {}, }, } = await getOneMessage(); await $(sourceOptions)`${file} ${commandArguments}` .pipe(destinationOptions)`${destinationFile} ${destinationArguments}`; ================================================ FILE: test/fixtures/nested-pipe-stream.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {execa, getOneMessage} from '../../index.js'; const {file, commandArguments, options: {unpipe, ...options}} = await getOneMessage(); const subprocess = execa(file, commandArguments, options); subprocess.stdout.pipe(process.stdout); if (unpipe) { subprocess.stdout.unpipe(process.stdout); } await subprocess; ================================================ FILE: test/fixtures/nested-pipe-subprocess.js ================================================ #!/usr/bin/env node import {execa, getOneMessage} from '../../index.js'; const {file, commandArguments, options: {unpipe, ...options}} = await getOneMessage(); const source = execa(file, commandArguments, options); const destination = execa('stdin.js'); const controller = new AbortController(); const subprocess = source.pipe(destination, {unpipeSignal: controller.signal}); if (unpipe) { controller.abort(); destination.stdin.end(); } try { await subprocess; } catch {} ================================================ FILE: test/fixtures/nested-pipe-subprocesses.js ================================================ #!/usr/bin/env node import {execa, getOneMessage} from '../../index.js'; const { file, commandArguments = [], options: { sourceOptions = {}, destinationFile, destinationArguments = [], destinationOptions = {}, }, } = await getOneMessage(); await execa(file, commandArguments, sourceOptions) .pipe(execa(destinationFile, destinationArguments, destinationOptions)); ================================================ FILE: test/fixtures/nested-pipe-verbose.js ================================================ #!/usr/bin/env node import {execa, getOneMessage, sendMessage} from '../../index.js'; import {getNestedOptions} from '../helpers/nested.js'; const { file, commandArguments = [], options: {destinationFile, destinationArguments, ...options}, optionsFixture, optionsInput, } = await getOneMessage(); const commandOptions = await getNestedOptions(options, optionsFixture, optionsInput); try { const result = await execa(file, commandArguments, commandOptions) .pipe(destinationFile, destinationArguments, commandOptions); await sendMessage(result); } catch (error) { await sendMessage(error); } ================================================ FILE: test/fixtures/nested-stdio.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {execa, execaSync} from '../../index.js'; import {parseStdioOption} from '../helpers/stdio.js'; const [stdioOption, fdNumber, isSyncString, file, ...commandArguments] = process.argv.slice(2); const optionValue = parseStdioOption(stdioOption); const isSync = isSyncString === 'true'; const stdio = ['ignore', 'inherit', 'inherit']; stdio[fdNumber] = optionValue; const execaMethod = isSync ? execaSync : execa; const returnValue = execaMethod(file, [`${fdNumber}`, ...commandArguments], {stdio}); const shouldPipe = Array.isArray(optionValue) && optionValue.includes('pipe'); const fdReturnValue = returnValue.stdio[fdNumber]; const hasPipe = fdReturnValue !== undefined && fdReturnValue !== null; if (shouldPipe && !hasPipe) { throw new Error(`subprocess.stdio[${fdNumber}] is null.`); } if (!shouldPipe && hasPipe) { throw new Error(`subprocess.stdio[${fdNumber}] should be null.`); } if (!isSync) { await returnValue; } ================================================ FILE: test/fixtures/nested-sync-tty.js ================================================ #!/usr/bin/env node import process from 'node:process'; import tty from 'node:tty'; import {execa, execaSync} from '../../index.js'; const mockIsatty = fdNumber => { tty.isatty = fdNumberArgument => fdNumber === fdNumberArgument; }; const originalIsatty = tty.isatty; const unmockIsatty = () => { tty.isatty = originalIsatty; }; const [options, isSync, file, fdNumber, ...commandArguments] = process.argv.slice(2); mockIsatty(Number(fdNumber)); try { if (isSync === 'true') { execaSync(file, [fdNumber, ...commandArguments], JSON.parse(options)); } else { await execa(file, [fdNumber, ...commandArguments], JSON.parse(options)); } } finally { unmockIsatty(); } ================================================ FILE: test/fixtures/nested-write.js ================================================ #!/usr/bin/env node import {writeFile} from 'node:fs/promises'; import {text} from 'node:stream/consumers'; import process from 'node:process'; const [filePath, bytes] = process.argv.slice(2); const stdinString = await text(process.stdin); await writeFile(filePath, `${stdinString} ${bytes}`); ================================================ FILE: test/fixtures/nested.js ================================================ #!/usr/bin/env node import { execa, execaSync, getOneMessage, sendMessage, } from '../../index.js'; import {getNestedOptions} from '../helpers/nested.js'; const { isSync, file, commandArguments, options, optionsFixture, optionsInput, } = await getOneMessage(); const commandOptions = await getNestedOptions(options, optionsFixture, optionsInput); try { const result = isSync ? execaSync(file, commandArguments, commandOptions) : await execa(file, commandArguments, commandOptions); await sendMessage(result); } catch (error) { await sendMessage(error); } ================================================ FILE: test/fixtures/no-await.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {once} from 'node:events'; import {execa} from '../../index.js'; const [options, file, ...commandArguments] = process.argv.slice(2); execa(file, commandArguments, JSON.parse(options)); const [error] = await once(process, 'unhandledRejection'); console.log(error.shortMessage); ================================================ FILE: test/fixtures/no-killable.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage} from '../../index.js'; const noop = () => {}; process.on('SIGTERM', noop); process.on('SIGINT', noop); await sendMessage(''); console.log('.'); setTimeout(noop, 1e8); ================================================ FILE: test/fixtures/non-executable.js ================================================ #!/usr/bin/env node ================================================ FILE: test/fixtures/noop-132.js ================================================ #!/usr/bin/env node console.log(1); console.warn(2); setTimeout(() => { console.log(3); }, 1000); ================================================ FILE: test/fixtures/noop-both-fail-strict.js ================================================ #!/usr/bin/env node import process from 'node:process'; const stdoutBytes = process.argv[2]; const stderrBytes = process.argv[3]; process.stdout.write(stdoutBytes); process.stderr.write(stderrBytes); process.exitCode = 1; ================================================ FILE: test/fixtures/noop-both-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {foobarString} from '../helpers/input.js'; const bytes = process.argv[2] || foobarString; console.log(bytes); console.error(bytes); process.exitCode = 1; ================================================ FILE: test/fixtures/noop-both.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {foobarString} from '../helpers/input.js'; const bytes = process.argv[2] || foobarString; const bytesStderr = process.argv[3] || bytes; console.log(bytes); console.error(bytesStderr); ================================================ FILE: test/fixtures/noop-continuous.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {setTimeout} from 'node:timers/promises'; const bytes = process.argv[2]; for (const character of bytes) { console.log(character); // eslint-disable-next-line no-await-in-loop await setTimeout(100); } ================================================ FILE: test/fixtures/noop-delay.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {setTimeout} from 'node:timers/promises'; import {getWriteStream} from '../helpers/fs.js'; import {foobarString} from '../helpers/input.js'; const fdNumber = Number(process.argv[2]); const bytes = process.argv[3] || foobarString; getWriteStream(fdNumber).write(bytes); await setTimeout(100); ================================================ FILE: test/fixtures/noop-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getWriteStream} from '../helpers/fs.js'; import {foobarString} from '../helpers/input.js'; const fdNumber = Number(process.argv[2]); const bytes = process.argv[3] || foobarString; getWriteStream(fdNumber).write(bytes); process.exitCode = 2; ================================================ FILE: test/fixtures/noop-fd-ipc.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {promisify} from 'node:util'; import {sendMessage} from '../../index.js'; import {getWriteStream} from '../helpers/fs.js'; import {foobarString} from '../helpers/input.js'; const fdNumber = Number(process.argv[2]); const bytes = process.argv[3] || foobarString; const stream = getWriteStream(fdNumber); await promisify(stream.write.bind(stream))(bytes); await sendMessage(''); ================================================ FILE: test/fixtures/noop-fd.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getWriteStream} from '../helpers/fs.js'; import {foobarString} from '../helpers/input.js'; const fdNumber = Number(process.argv[2]); const bytes = process.argv[3] || foobarString; getWriteStream(fdNumber).write(bytes); ================================================ FILE: test/fixtures/noop-forever.js ================================================ #!/usr/bin/env node import process from 'node:process'; const bytes = process.argv[2]; console.log(bytes); setTimeout(() => {}, 1e8); ================================================ FILE: test/fixtures/noop-progressive.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {setTimeout} from 'node:timers/promises'; const bytes = process.argv[2]; for (const character of bytes) { process.stdout.write(character); // eslint-disable-next-line no-await-in-loop await setTimeout(10); } process.stdout.write('\n'); ================================================ FILE: test/fixtures/noop-repeat.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getWriteStream} from '../helpers/fs.js'; import {foobarString} from '../helpers/input.js'; const fdNumber = Number(process.argv[2]) || 1; const bytes = process.argv[3] || foobarString; setInterval(() => { getWriteStream(fdNumber).write(bytes); }, 10); ================================================ FILE: test/fixtures/noop-stdin-double.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {text} from 'node:stream/consumers'; const bytes = process.argv[2]; const stdinString = await text(process.stdin); console.log(`${stdinString} ${bytes}`); ================================================ FILE: test/fixtures/noop-stdin-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {text} from 'node:stream/consumers'; import {getWriteStream} from '../helpers/fs.js'; const fdNumber = Number(process.argv[2]); const stdinString = await text(process.stdin); getWriteStream(fdNumber).write(stdinString); process.exitCode = 2; ================================================ FILE: test/fixtures/noop-stdin-fd.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getWriteStream} from '../helpers/fs.js'; const fdNumber = Number(process.argv[2]); process.stdin.pipe(getWriteStream(fdNumber)); ================================================ FILE: test/fixtures/noop-verbose.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {sendMessage} from '../../index.js'; const bytes = process.argv[2]; console.log(bytes); try { await sendMessage(bytes); } catch {} process.exitCode = 2; ================================================ FILE: test/fixtures/noop.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {foobarString} from '../helpers/input.js'; const bytes = process.argv[2] || foobarString; console.log(bytes); ================================================ FILE: test/fixtures/stdin-both.js ================================================ #!/usr/bin/env node import process from 'node:process'; process.stdin.pipe(process.stdout); process.stdin.pipe(process.stderr); ================================================ FILE: test/fixtures/stdin-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; process.stdin.pipe(process.stdout); process.exitCode = 2; ================================================ FILE: test/fixtures/stdin-fd-both.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getReadStream} from '../helpers/fs.js'; const fdNumber = Number(process.argv[2]); process.stdin.pipe(process.stdout); getReadStream(fdNumber).pipe(process.stdout); ================================================ FILE: test/fixtures/stdin-fd.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getReadStream} from '../helpers/fs.js'; const fdNumber = Number(process.argv[2]); getReadStream(fdNumber).pipe(process.stdout); ================================================ FILE: test/fixtures/stdin-script.js ================================================ #!/usr/bin/env node import {$} from '../../index.js'; import {FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; await $({stdout: 'inherit'})`node ${`${FIXTURES_DIRECTORY}/stdin.js`}`; ================================================ FILE: test/fixtures/stdin-twice-both.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {getReadStream} from '../helpers/fs.js'; const fdNumber = Number(process.argv[2]); process.stdin.pipe(process.stdout); getReadStream(fdNumber).pipe(process.stderr); ================================================ FILE: test/fixtures/stdin.js ================================================ #!/usr/bin/env node import process from 'node:process'; process.stdin.pipe(process.stdout); ================================================ FILE: test/fixtures/verbose-script.js ================================================ #!/usr/bin/env node import {$} from '../../index.js'; const $$ = $({stdio: 'inherit'}); await $$`node -e console.error(1)`; await $$({reject: false})`node -e process.exit(2)`; ================================================ FILE: test/fixtures/wait-fail.js ================================================ #!/usr/bin/env node import process from 'node:process'; import {setTimeout} from 'node:timers/promises'; await setTimeout(1e3); process.exitCode = 2; ================================================ FILE: test/fixtures/worker.js ================================================ #!/usr/bin/env node import {workerData, parentPort} from 'node:worker_threads'; import {spawnParentProcess} from '../helpers/nested.js'; try { const result = await spawnParentProcess(workerData); parentPort.postMessage(result); } catch (error) { parentPort.postMessage(error); } ================================================ FILE: test/helpers/convert.js ================================================ import {text} from 'node:stream/consumers'; import {finished} from 'node:stream/promises'; import getStream from 'get-stream'; import isPlainObj from 'is-plain-obj'; import {execa} from '../../index.js'; import {foobarString} from '../helpers/input.js'; export const arrayFromAsync = async (asyncIterable, lines = []) => { for await (const line of asyncIterable) { lines.push(line); } return lines; }; export const finishedStream = stream => finished(stream, {cleanup: true}); export const assertWritableAborted = (t, writable) => { t.false(writable.writableEnded); t.is(writable.errored, null); t.false(writable.writable); }; export const assertReadableAborted = (t, readable) => { t.false(readable.readableEnded); t.is(readable.errored, null); t.false(readable.readable); }; export const assertProcessNormalExit = (t, error, exitCode = 0) => { t.is(error.exitCode, exitCode); t.is(error.signal, undefined); }; export const assertStreamOutput = async (t, stream, expectedOutput = foobarString) => { t.is(await text(stream), expectedOutput); }; export const assertStreamDataEvents = async (t, stream, expectedOutput = foobarString) => { t.is(await getStream(stream), expectedOutput); }; export const assertIterableChunks = async (t, asyncIterable, expectedChunks) => { t.deepEqual(await arrayFromAsync(asyncIterable), expectedChunks); }; export const assertStreamChunks = async (t, stream, expectedOutput) => { t.deepEqual(await stream.toArray(), expectedOutput); }; export const assertSubprocessOutput = async (t, subprocess, expectedOutput = foobarString, fdNumber = 1) => { const result = await subprocess; t.deepEqual(result.stdio[fdNumber], expectedOutput); }; export const assertStreamError = (t, stream, error) => assertPromiseError(t, finishedStream(stream), error); export const assertStreamReadError = (t, stream, error) => assertPromiseError(t, text(stream), error); export const assertSubprocessError = (t, subprocess, error) => assertPromiseError(t, subprocess, error); export const assertPromiseError = async (t, promise, error) => { const thrownError = await t.throwsAsync(promise); if (isPlainObj(error) && error.cause !== undefined) { t.is(thrownError.cause, error.cause); } else { t.is(thrownError, error); } return thrownError; }; export const getReadableSubprocess = (output = foobarString, options = {}) => execa('noop-fd.js', ['1', output], options); export const getWritableSubprocess = () => execa('noop-stdin-fd.js', ['2']); export const getReadWriteSubprocess = options => execa('stdin.js', options); ================================================ FILE: test/helpers/duplex.js ================================================ import {Transform} from 'node:stream'; import {setTimeout, scheduler} from 'node:timers/promises'; import {callbackify} from 'node:util'; import {foobarObject, foobarString} from './input.js'; import {casedSuffix, prefix, suffix} from './generator.js'; const getDuplex = (transform, encoding, outerObjectMode) => objectMode => ({ transform: new Transform({ transform: callbackify(async function (value) { return transform.call(this, value); }), objectMode, encoding, }), objectMode: outerObjectMode, }); export const addNoopDuplex = (duplex, addNoopTransform, objectMode) => addNoopTransform ? [duplex, noopDuplex(objectMode)] : [duplex]; export const noopDuplex = getDuplex(value => value); export const serializeDuplex = getDuplex(object => JSON.stringify(object)); export const getOutputDuplex = (input, outerObjectMode) => getDuplex(() => input, undefined, outerObjectMode); export const outputObjectDuplex = () => getOutputDuplex(foobarObject)(true); export const getOutputsDuplex = inputs => getDuplex(function () { for (const input of inputs) { this.push(input); } }); export const noYieldDuplex = getDuplex(() => {}); export const multipleYieldDuplex = getDuplex(async function (line) { this.push(prefix); await scheduler.yield(); this.push(line); await scheduler.yield(); this.push(suffix); }); export const uppercaseEncodingDuplex = (encoding, outerObjectMode) => getDuplex(buffer => buffer.toString().toUpperCase(), encoding, outerObjectMode); export const uppercaseBufferDuplex = uppercaseEncodingDuplex(); export const throwingDuplex = cause => getDuplex(() => { throw cause; }); export const appendDuplex = getDuplex(string => `${string}${casedSuffix}`); export const timeoutDuplex = timeout => getDuplex(async () => { await setTimeout(timeout); return foobarString; }); ================================================ FILE: test/helpers/early-error.js ================================================ import {execa, execaSync} from '../../index.js'; export const earlyErrorOptions = {detached: 'true'}; export const getEarlyErrorSubprocess = options => execa('empty.js', {...earlyErrorOptions, ...options}); export const earlyErrorOptionsSync = {maxBuffer: false}; export const getEarlyErrorSubprocessSync = options => execaSync('empty.js', {...earlyErrorOptionsSync, ...options}); export const expectedEarlyError = {code: 'ERR_INVALID_ARG_TYPE'}; export const expectedEarlyErrorSync = {code: 'ERR_OUT_OF_RANGE'}; ================================================ FILE: test/helpers/encoding.js ================================================ const textEncoder = new TextEncoder(); export const multibyteChar = '\u{1F984}'; export const multibyteString = `${multibyteChar}${multibyteChar}`; export const multibyteUint8Array = textEncoder.encode(multibyteString); export const breakingLength = multibyteUint8Array.length * 0.75; export const brokenSymbol = '\uFFFD'; ================================================ FILE: test/helpers/file-path.js ================================================ import path from 'node:path'; import process from 'node:process'; export const getAbsolutePath = file => ({file}); export const getRelativePath = filePath => ({file: path.relative('.', filePath)}); // Defined as getter so call to toString is not cached export const getDenoNodePath = () => Object.freeze({ __proto__: String.prototype, toString() { return process.execPath; }, get length() { return this.toString().length; }, }); ================================================ FILE: test/helpers/fixtures-directory.js ================================================ import path from 'node:path'; import process from 'node:process'; import {fileURLToPath} from 'node:url'; import logProcessErrors from 'log-process-errors'; import pathKey from 'path-key'; // Make tests fail if any warning (such as a deprecation warning) is emitted logProcessErrors({ onError(error, event) { if (event === 'warning') { throw error; } }, }); export const PATH_KEY = pathKey(); export const FIXTURES_DIRECTORY_URL = new URL('../fixtures/', import.meta.url); // @todo: use import.meta.dirname after dropping support for Node <20.11.0 export const FIXTURES_DIRECTORY = path.resolve(fileURLToPath(FIXTURES_DIRECTORY_URL)); // Add the fixtures directory to PATH so fixtures can be executed without adding // `node`. This is only meant to make writing tests simpler. export const setFixtureDirectory = () => { process.env[PATH_KEY] = FIXTURES_DIRECTORY + path.delimiter + process.env[PATH_KEY]; }; ================================================ FILE: test/helpers/fs.js ================================================ import {createReadStream, createWriteStream} from 'node:fs'; import process from 'node:process'; export const getReadStream = fdNumber => fdNumber === 0 ? process.stdin : createReadStream(undefined, {fd: fdNumber}); export const getWriteStream = fdNumber => { if (fdNumber === 1) { return process.stdout; } if (fdNumber === 2) { return process.stderr; } return createWriteStream(undefined, {fd: fdNumber}); }; ================================================ FILE: test/helpers/generator.js ================================================ import { setImmediate, setInterval, setTimeout, scheduler, } from 'node:timers/promises'; import {foobarObject, foobarString} from './input.js'; const getGenerator = transform => (objectMode, binary, preserveNewlines) => ({ transform, objectMode, binary, preserveNewlines, }); export const addNoopGenerator = (transform, addNoopTransform, objectMode, binary) => addNoopTransform ? [transform, noopGenerator(objectMode, binary)] : [transform]; export const noopGenerator = getGenerator(function * (value) { yield value; }); export const noopAsyncGenerator = getGenerator(async function * (value) { yield value; }); export const serializeGenerator = getGenerator(function * (object) { yield JSON.stringify(object); }); export const getOutputGenerator = input => getGenerator(function * () { yield input; }); export const outputObjectGenerator = () => getOutputGenerator(foobarObject)(true); export const getOutputAsyncGenerator = input => getGenerator(async function * () { yield input; }); export const getOutputsGenerator = inputs => getGenerator(function * () { yield * inputs; }); export const getOutputsAsyncGenerator = inputs => getGenerator(async function * () { for (const input of inputs) { yield input; // eslint-disable-next-line no-await-in-loop await setImmediate(); } }); const noYieldTransform = function * () {}; export const noYieldGenerator = getGenerator(noYieldTransform); export const prefix = '> '; export const suffix = ' <'; export const multipleYieldGenerator = getGenerator(async function * (line = foobarString) { yield prefix; await scheduler.yield(); yield line; await scheduler.yield(); yield suffix; }); export const convertTransformToFinal = (transform, final) => { if (!final) { return transform; } const generatorOptions = typeof transform === 'function' ? {transform} : transform; return ({...generatorOptions, transform: noYieldTransform, final: generatorOptions.transform}); }; export const infiniteGenerator = getGenerator(async function * () { for await (const value of setInterval(100, foobarString)) { yield value; } }); const textDecoder = new TextDecoder(); const textEncoder = new TextEncoder(); export const uppercaseBufferGenerator = getGenerator(function * (buffer) { yield textEncoder.encode(textDecoder.decode(buffer).toUpperCase()); }); export const uppercaseGenerator = getGenerator(function * (string) { yield string.toUpperCase(); }); // eslint-disable-next-line require-yield export const throwingGenerator = error => getGenerator(function * () { throw error; }); export const appendGenerator = getGenerator(function * (string) { yield `${string}${casedSuffix}`; }); export const appendAsyncGenerator = getGenerator(async function * (string) { yield `${string}${casedSuffix}`; }); export const casedSuffix = 'k'; export const resultGenerator = inputs => getGenerator(function * (input) { inputs.push(input); yield input; }); export const timeoutGenerator = timeout => getGenerator(async function * () { await setTimeout(timeout); yield foobarString; }); ================================================ FILE: test/helpers/graceful.js ================================================ import {setTimeout} from 'node:timers/promises'; // Combines `util.aborted()` and `events.addAbortListener()`: promise-based and cleaned up with a stop signal export const onAbortedSignal = async signal => { try { await setTimeout(1e8, undefined, {signal}); } catch {} }; ================================================ FILE: test/helpers/input.js ================================================ import {Buffer} from 'node:buffer'; import {inspect} from 'node:util'; const textEncoder = new TextEncoder(); export const foobarString = 'foobar'; export const foobarArray = ['foo', 'bar']; export const foobarUint8Array = textEncoder.encode(foobarString); export const foobarArrayBuffer = foobarUint8Array.buffer; export const foobarUint16Array = new Uint16Array(foobarArrayBuffer); export const foobarBuffer = Buffer.from(foobarString); const foobarUtf16Buffer = Buffer.from(foobarString, 'utf16le'); export const foobarUtf16Uint8Array = new Uint8Array(foobarUtf16Buffer); export const foobarDataView = new DataView(foobarArrayBuffer); export const foobarHex = foobarBuffer.toString('hex'); export const foobarUppercase = foobarString.toUpperCase(); export const foobarUppercaseUint8Array = textEncoder.encode(foobarUppercase); export const foobarUppercaseHex = Buffer.from(foobarUppercase).toString('hex'); export const foobarObject = {foo: 'bar'}; export const foobarObjectString = JSON.stringify(foobarObject); export const foobarObjectInspect = inspect(foobarObject); ================================================ FILE: test/helpers/ipc.js ================================================ import {getEachMessage} from '../../index.js'; import {foobarString} from './input.js'; // @todo: replace with Array.fromAsync(subprocess.getEachMessage()) after dropping support for Node <22.0.0 export const iterateAllMessages = async subprocess => { const messages = []; for await (const message of subprocess.getEachMessage()) { messages.push(message); } return messages; }; export const subprocessGetFirst = async subprocess => { const [firstMessage] = await iterateAllMessages(subprocess); return firstMessage; }; export const getFirst = async () => { // eslint-disable-next-line no-unreachable-loop for await (const message of getEachMessage()) { return message; } }; export const subprocessGetOne = (subprocess, options) => subprocess.getOneMessage(options); export const alwaysPass = () => true; // `process.send()` can fail due to I/O errors. // However, I/O errors are seldom and hard to trigger predictably. // So we mock them. export const mockSendIoError = anyProcess => { const error = new Error(foobarString); anyProcess.send = () => { throw error; }; return error; }; ================================================ FILE: test/helpers/lines.js ================================================ import {Buffer} from 'node:buffer'; import {execa} from '../../index.js'; const textEncoder = new TextEncoder(); export const stringsToUint8Arrays = strings => strings.map(string => stringToUint8Arrays(string, true)); export const stringToUint8Arrays = (string, isUint8Array) => isUint8Array ? textEncoder.encode(string) : string; export const simpleFull = 'aaa\nbbb\nccc'; export const simpleChunks = [simpleFull]; export const simpleFullUint8Array = textEncoder.encode(simpleFull); export const simpleChunksUint8Array = [simpleFullUint8Array]; const simpleFullBuffer = Buffer.from(simpleFull); export const simpleFullHex = simpleFullBuffer.toString('hex'); export const simpleChunksBuffer = [simpleFullBuffer]; export const simpleFullUtf16Inverted = simpleFullBuffer.toString('utf16le'); const simpleFullUtf16Buffer = Buffer.from(simpleFull, 'utf16le'); export const simpleFullUtf16Uint8Array = new Uint8Array(simpleFullUtf16Buffer); export const simpleFullEnd = `${simpleFull}\n`; const simpleFullEndBuffer = Buffer.from(simpleFullEnd); export const simpleFullEndChunks = [simpleFullEnd]; export const simpleFullEndUtf16Inverted = simpleFullEndBuffer.toString('utf16le'); export const simpleLines = ['aaa\n', 'bbb\n', 'ccc']; export const simpleFullEndLines = ['aaa\n', 'bbb\n', 'ccc\n']; export const noNewlinesFull = 'aaabbbccc'; export const noNewlinesChunks = ['aaa', 'bbb', 'ccc']; export const complexFull = '\naaa\r\nbbb\n\nccc'; const complexFullBuffer = Buffer.from(complexFull); const complexFullUtf16Buffer = Buffer.from(complexFull, 'utf16le'); export const complexFullUtf16 = complexFullUtf16Buffer.toString(); export const complexFullUtf16Uint8Array = new Uint8Array(complexFullUtf16Buffer); export const singleComplexBuffer = [complexFullBuffer]; export const singleComplexUtf16Buffer = [complexFullUtf16Buffer]; export const singleComplexUint8Array = textEncoder.encode(complexFull); export const singleComplexHex = complexFullBuffer.toString('hex'); export const complexChunksEnd = ['\n', 'aaa\r\n', 'bbb\n', '\n', 'ccc']; export const complexChunks = ['', 'aaa', 'bbb', '', 'ccc']; export const singleFull = 'aaa'; export const singleFullEnd = `${singleFull}\n`; export const getSimpleChunkSubprocessAsync = options => getSimpleChunkSubprocess(execa, options); export const getSimpleChunkSubprocess = (execaMethod, options) => execaMethod('noop-fd.js', ['1', simpleFull], {lines: true, ...options}); ================================================ FILE: test/helpers/listeners.js ================================================ import process from 'node:process'; export const assertMaxListeners = t => { let warning; const captureWarning = warningArgument => { warning = warningArgument; }; process.once('warning', captureWarning); return () => { t.is(warning, undefined); process.removeListener('warning', captureWarning); }; }; ================================================ FILE: test/helpers/map.js ================================================ import { addNoopGenerator, noopGenerator, serializeGenerator, uppercaseBufferGenerator, uppercaseGenerator, getOutputGenerator, outputObjectGenerator, getOutputsGenerator, noYieldGenerator, multipleYieldGenerator, throwingGenerator, appendGenerator, timeoutGenerator, } from './generator.js'; import { addNoopDuplex, noopDuplex, serializeDuplex, uppercaseBufferDuplex, getOutputDuplex, outputObjectDuplex, getOutputsDuplex, noYieldDuplex, multipleYieldDuplex, throwingDuplex, appendDuplex, timeoutDuplex, } from './duplex.js'; import { addNoopWebTransform, noopWebTransform, serializeWebTransform, uppercaseBufferWebTransform, getOutputWebTransform, outputObjectWebTransform, getOutputsWebTransform, noYieldWebTransform, multipleYieldWebTransform, throwingWebTransform, appendWebTransform, timeoutWebTransform, } from './web-transform.js'; export const generatorsMap = { generator: { addNoop: addNoopGenerator, noop: noopGenerator, serialize: serializeGenerator, uppercaseBuffer: uppercaseBufferGenerator, uppercase: uppercaseGenerator, getOutput: getOutputGenerator, outputObject: outputObjectGenerator, getOutputs: getOutputsGenerator, noYield: noYieldGenerator, multipleYield: multipleYieldGenerator, throwing: throwingGenerator, append: appendGenerator, timeout: timeoutGenerator, }, duplex: { addNoop: addNoopDuplex, noop: noopDuplex, serialize: serializeDuplex, uppercaseBuffer: uppercaseBufferDuplex, uppercase: uppercaseBufferDuplex, getOutput: getOutputDuplex, outputObject: outputObjectDuplex, getOutputs: getOutputsDuplex, noYield: noYieldDuplex, multipleYield: multipleYieldDuplex, throwing: throwingDuplex, append: appendDuplex, timeout: timeoutDuplex, }, webTransform: { addNoop: addNoopWebTransform, noop: noopWebTransform, serialize: serializeWebTransform, uppercaseBuffer: uppercaseBufferWebTransform, uppercase: uppercaseBufferWebTransform, getOutput: getOutputWebTransform, outputObject: outputObjectWebTransform, getOutputs: getOutputsWebTransform, noYield: noYieldWebTransform, multipleYield: multipleYieldWebTransform, throwing: throwingWebTransform, append: appendWebTransform, timeout: timeoutWebTransform, }, }; ================================================ FILE: test/helpers/max-buffer.js ================================================ import {execa, execaSync} from '../../index.js'; export const maxBuffer = 10; export const assertErrorMessage = (t, shortMessage, {execaMethod = execa, length = maxBuffer, fdNumber = 1, unit = 'characters'} = {}) => { const [expectedStreamName, expectedUnit] = execaMethod === execaSync ? ['output', 'bytes'] : [STREAM_NAMES[fdNumber], unit]; t.true(shortMessage.includes(`${expectedStreamName} was larger than ${length} ${expectedUnit}`)); }; const STREAM_NAMES = ['stdin', 'stdout', 'stderr', 'stdio[3]']; ================================================ FILE: test/helpers/nested.js ================================================ import {once} from 'node:events'; import {Worker} from 'node:worker_threads'; import {execa} from '../../index.js'; import {FIXTURES_DIRECTORY_URL} from './fixtures-directory.js'; const WORKER_URL = new URL('worker.js', FIXTURES_DIRECTORY_URL); const NESTED_URL = new URL('nested/', FIXTURES_DIRECTORY_URL); // Like `execa(file, commandArguments, options)` but spawns inside another parent process. // This is useful when testing logic where Execa modifies the global state. // For example, when it prints to the console, with the `verbose` option. // The parent process calls `execa(options.parentFixture, parentOptions)` // When `options.isSync` is `true`, `execaSync()` is called instead. // When `options.worker` is `true`, the whole flow happens inside a Worker. export const nestedSubprocess = async (file, commandArguments, options, parentOptions) => { const result = await nestedInstance(file, commandArguments, options, parentOptions); const nestedResult = result.ipcOutput[0]; return {...result, nestedResult}; }; export const nestedInstance = (file, commandArguments, options, parentOptions) => { [commandArguments, options = {}, parentOptions = {}] = Array.isArray(commandArguments) ? [commandArguments, options, parentOptions] : [[], commandArguments, options]; const { parentFixture = 'nested.js', worker = false, isSync = false, optionsFixture, optionsInput = {}, ...otherOptions } = options; const normalizedArguments = { parentFixture, parentOptions, isSync, file, commandArguments, options: otherOptions, optionsFixture, optionsInput, }; return worker ? spawnWorker(normalizedArguments) : spawnParentProcess(normalizedArguments); }; const spawnWorker = async workerData => { const worker = new Worker(WORKER_URL, {workerData}); const [result] = await once(worker, 'message'); if (result instanceof Error) { throw result; } return result; }; export const spawnParentProcess = ({parentFixture, parentOptions, ...ipcInput}) => execa(parentFixture, {...parentOptions, ipcInput}); // Some subprocess options cannot be serialized between processes. // For those, we pass a fixture filename instead, which dynamically creates the options. export const getNestedOptions = async (options, optionsFixture, optionsInput) => { if (optionsFixture === undefined) { return options; } const {getOptions} = await import(new URL(optionsFixture, NESTED_URL)); return {...options, ...getOptions({...options, ...optionsInput})}; }; ================================================ FILE: test/helpers/node-version.js ================================================ import {version} from 'node:process'; export const majorNodeVersion = Number(version.split('.')[0].slice(1)); ================================================ FILE: test/helpers/override-promise.js ================================================ // Can't use `test.before`, because `ava` needs `Promise`. const nativePromise = Promise; globalThis.Promise = class BrokenPromise { then() { // eslint-disable-line unicorn/no-thenable throw new Error('error'); } }; export function restorePromise() { globalThis.Promise = nativePromise; } ================================================ FILE: test/helpers/parallel.js ================================================ import isInCi from 'is-in-ci'; export const PARALLEL_COUNT = isInCi ? 10 : 100; ================================================ FILE: test/helpers/pipe.js ================================================ export const assertPipeError = async (t, pipePromise, message) => { const error = await t.throwsAsync(pipePromise); t.is(error.command, 'source.pipe(destination)'); t.is(error.escapedCommand, error.command); t.is(typeof error.cwd, 'string'); t.true(error.failed); t.false(error.timedOut); t.false(error.isCanceled); t.false(error.isTerminated); t.is(error.exitCode, undefined); t.is(error.signal, undefined); t.is(error.signalDescription, undefined); t.is(error.stdout, undefined); t.is(error.stderr, undefined); t.is(error.all, undefined); t.deepEqual(error.stdio, Array.from({length: error.stdio.length})); t.deepEqual(error.pipedFrom, []); t.true(error.shortMessage.includes(`Command failed: ${error.command}`)); t.true(error.shortMessage.includes(error.originalMessage)); t.true(error.message.includes(error.shortMessage)); t.true(error.originalMessage.includes(message)); }; ================================================ FILE: test/helpers/run.js ================================================ import {execa, execaSync, $} from '../../index.js'; export const runExeca = (file, options) => execa(file, options); export const runExecaSync = (file, options) => execaSync(file, options); export const runScript = (file, options) => $(options)`${file}`; export const runScriptSync = (file, options) => $(options).sync`${file}`; ================================================ FILE: test/helpers/stdio.js ================================================ import process, {platform} from 'node:process'; import {noopReadable} from './stream.js'; export const identity = value => value; export const getStdio = (fdNumberOrName, stdioOption, length = 3) => { if (typeof fdNumberOrName === 'string') { return {[fdNumberOrName]: stdioOption}; } const stdio = Array.from({length}).fill('pipe'); stdio[fdNumberOrName] = stdioOption; return {stdio}; }; export const fullStdio = getStdio(3, 'pipe'); export const fullReadableStdio = () => getStdio(3, ['pipe', noopReadable()]); export const STANDARD_STREAMS = [process.stdin, process.stdout, process.stderr]; export const prematureClose = {code: 'ERR_STREAM_PREMATURE_CLOSE'}; const isWindows = platform === 'win32'; export const assertEpipe = (t, stderr, fdNumber = 1) => { if (fdNumber === 1 && !isWindows) { t.true(stderr.includes('EPIPE')); } }; export const parseStdioOption = stdioOption => { const optionValue = JSON.parse(stdioOption); if (typeof optionValue === 'string' && optionValue in process) { return process[optionValue]; } if (Array.isArray(optionValue) && typeof optionValue[0] === 'string' && optionValue[0] in process) { return [process[optionValue[0]], ...optionValue.slice(1)]; } return optionValue; }; ================================================ FILE: test/helpers/stream.js ================================================ import { Readable, Writable, PassThrough, getDefaultHighWaterMark, } from 'node:stream'; import {foobarString} from './input.js'; export const noopReadable = () => new Readable({read() {}}); export const noopWritable = () => new Writable({write() {}}); export const noopDuplex = () => new PassThrough().resume(); export const simpleReadable = () => Readable.from([foobarString]); export const defaultHighWaterMark = getDefaultHighWaterMark(false); export const defaultObjectHighWaterMark = getDefaultHighWaterMark(true); ================================================ FILE: test/helpers/verbose.js ================================================ import {platform} from 'node:process'; import {stripVTControlCharacters} from 'node:util'; import {replaceSymbols} from 'figures'; import {foobarString} from './input.js'; import {nestedSubprocess} from './nested.js'; const isWindows = platform === 'win32'; export const QUOTE = isWindows ? '"' : '\''; export const runErrorSubprocess = async (t, verbose, isSync = false, expectExitCode = true) => { const {stderr, nestedResult} = await nestedSubprocess('noop-fail.js', ['1', foobarString], {verbose, isSync}); t.true(nestedResult instanceof Error); if (expectExitCode) { t.true(stderr.includes('exit code 2')); } return stderr; }; export const runWarningSubprocess = async (t, isSync) => { const {stderr, nestedResult} = await nestedSubprocess('noop-fail.js', ['1', foobarString], {verbose: 'short', reject: false, isSync}); t.true(nestedResult instanceof Error); t.true(stderr.includes('exit code 2')); return stderr; }; export const runEarlyErrorSubprocess = async (t, isSync) => { const {stderr, nestedResult} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short', cwd: true, isSync}); t.true(nestedResult instanceof Error); t.true(nestedResult.message.startsWith('The "cwd" option must')); return stderr; }; export const runVerboseSubprocess = ({ isSync = false, type, eventProperty, optionName, errorMessage, fdNumber, secondFdNumber, optionsFixture = 'custom-event.js', output = '. .', ...options }) => nestedSubprocess('noop-verbose.js', [output], { ipc: !isSync, optionsFixture, optionsInput: { type, eventProperty, optionName, errorMessage, fdNumber, secondFdNumber, }, isSync, ...options, }); export const getCommandLine = stderr => getCommandLines(stderr)[0]; export const getCommandLines = stderr => getNormalizedLines(stderr).filter(line => isCommandLine(line)); const isCommandLine = line => line.includes(' $ ') || line.includes(' | '); export const getOutputLine = stderr => getOutputLines(stderr)[0]; export const getOutputLines = stderr => getNormalizedLines(stderr).filter(line => isOutputLine(line)); const isOutputLine = line => line.includes('] '); export const getIpcLine = stderr => getIpcLines(stderr)[0]; export const getIpcLines = stderr => getNormalizedLines(stderr).filter(line => isIpcLine(line)); const isIpcLine = line => line.includes(' * '); export const getErrorLine = stderr => getErrorLines(stderr)[0]; export const getErrorLines = stderr => getNormalizedLines(stderr).filter(line => isErrorLine(line)); const isErrorLine = line => (line.includes(' × ') || line.includes(' ‼ ')) && !isCompletionLine(line); export const getCompletionLine = stderr => getCompletionLines(stderr)[0]; export const getCompletionLines = stderr => getNormalizedLines(stderr).filter(line => isCompletionLine(line)); const isCompletionLine = line => line.includes('(done in'); export const getNormalizedLine = stderr => getNormalizedLines(stderr)[0]; export const getNormalizedLines = stderr => splitLines(normalizeStderr(stderr)); const splitLines = stderr => stderr.split('\n'); const normalizeStderr = stderr => replaceSymbols(normalizeDuration(normalizeTimestamp(stripVTControlCharacters(stderr))), {useFallback: true}); export const testTimestamp = '[00:00:00.000]'; const normalizeTimestamp = stderr => stderr.replaceAll(/^\[\d{2}:\d{2}:\d{2}.\d{3}]/gm, testTimestamp); const normalizeDuration = stderr => stderr.replaceAll(/\(done in [^)]+\)/g, '(done in 0ms)'); export const getVerboseOption = (isVerbose, verbose = 'short') => ({verbose: isVerbose ? verbose : 'none'}); export const stdoutNoneOption = {stdout: 'none'}; export const stdoutShortOption = {stdout: 'short'}; export const stdoutFullOption = {stdout: 'full'}; export const stderrNoneOption = {stderr: 'none'}; export const stderrShortOption = {stderr: 'short'}; export const stderrFullOption = {stderr: 'full'}; export const fd3NoneOption = {fd3: 'none'}; export const fd3ShortOption = {fd3: 'short'}; export const fd3FullOption = {fd3: 'full'}; export const ipcNoneOption = {ipc: 'none'}; export const ipcShortOption = {ipc: 'short'}; export const ipcFullOption = {ipc: 'full'}; ================================================ FILE: test/helpers/wait.js ================================================ import {getStdio} from '../helpers/stdio.js'; import {noopGenerator} from '../helpers/generator.js'; export const endOptionStream = ({stream}) => { stream.end(); }; export const destroyOptionStream = ({stream, error}) => { stream.destroy(error); }; export const destroySubprocessStream = ({subprocess, fdNumber, error}) => { subprocess.stdio[fdNumber].destroy(error); }; export const getStreamStdio = (fdNumber, stream, useTransform) => getStdio(fdNumber, [stream, useTransform ? noopGenerator(false) : 'pipe']); ================================================ FILE: test/helpers/web-transform.js ================================================ import {setTimeout, scheduler} from 'node:timers/promises'; import {foobarObject, foobarString} from './input.js'; import {casedSuffix, prefix, suffix} from './generator.js'; const getWebTransform = transform => objectMode => ({ transform: new TransformStream({transform}), objectMode, }); export const addNoopWebTransform = (webTransform, addNoopTransform, objectMode) => addNoopTransform ? [webTransform, noopWebTransform(objectMode)] : [webTransform]; export const noopWebTransform = getWebTransform((value, controller) => { controller.enqueue(value); }); export const serializeWebTransform = getWebTransform((object, controller) => { controller.enqueue(JSON.stringify(object)); }); export const getOutputWebTransform = (input, outerObjectMode) => getWebTransform((_, controller) => { controller.enqueue(input); }, undefined, outerObjectMode); export const outputObjectWebTransform = () => getOutputWebTransform(foobarObject)(true); export const getOutputsWebTransform = inputs => getWebTransform((_, controller) => { for (const input of inputs) { controller.enqueue(input); } }); export const noYieldWebTransform = getWebTransform(() => {}); export const multipleYieldWebTransform = getWebTransform(async (line, controller) => { controller.enqueue(prefix); await scheduler.yield(); controller.enqueue(line); await scheduler.yield(); controller.enqueue(suffix); }); export const uppercaseBufferWebTransform = getWebTransform((string, controller) => { controller.enqueue(string.toString().toUpperCase()); }); export const throwingWebTransform = cause => getWebTransform(() => { throw cause; }); export const appendWebTransform = getWebTransform((string, controller) => { controller.enqueue(`${string}${casedSuffix}`); }); export const timeoutWebTransform = timeout => getWebTransform(async (_, controller) => { await setTimeout(timeout); controller.enqueue(foobarString); }); ================================================ FILE: test/io/input-option.js ================================================ import {Writable} from 'node:stream'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { runExeca, runExecaSync, runScript, runScriptSync, } from '../helpers/run.js'; import { foobarUint8Array, foobarBuffer, foobarArrayBuffer, foobarUint16Array, foobarDataView, } from '../helpers/input.js'; setFixtureDirectory(); const testInput = async (t, input, execaMethod) => { const {stdout} = await execaMethod('stdin.js', {input}); t.is(stdout, 'foobar'); }; test('input option can be a String', testInput, 'foobar', runExeca); test('input option can be a Uint8Array', testInput, foobarUint8Array, runExeca); test('input option can be a Buffer', testInput, foobarBuffer, runExeca); test('input option can be a String - sync', testInput, 'foobar', runExecaSync); test('input option can be a Uint8Array - sync', testInput, foobarUint8Array, runExecaSync); test('input option can be a Buffer - sync', testInput, foobarBuffer, runExecaSync); test('input option can be used with $', testInput, 'foobar', runScript); test('input option can be used with $.sync', testInput, 'foobar', runScriptSync); const testInvalidInput = async (t, input, execaMethod) => { t.throws(() => { execaMethod('empty.js', {input}); }, {message: /a string, a Uint8Array/}); }; test('input option cannot be an ArrayBuffer', testInvalidInput, foobarArrayBuffer, execa); test('input option cannot be a DataView', testInvalidInput, foobarDataView, execa); test('input option cannot be a Uint16Array', testInvalidInput, foobarUint16Array, execa); test('input option cannot be 0', testInvalidInput, 0, execa); test('input option cannot be false', testInvalidInput, false, execa); test('input option cannot be null', testInvalidInput, null, execa); test('input option cannot be a non-Readable stream', testInvalidInput, new Writable(), execa); test('input option cannot be an ArrayBuffer - sync', testInvalidInput, foobarArrayBuffer, execaSync); test('input option cannot be a DataView - sync', testInvalidInput, foobarDataView, execaSync); test('input option cannot be a Uint16Array - sync', testInvalidInput, foobarUint16Array, execaSync); test('input option cannot be 0 - sync', testInvalidInput, 0, execaSync); test('input option cannot be false - sync', testInvalidInput, false, execaSync); test('input option cannot be null - sync', testInvalidInput, null, execaSync); test('input option cannot be a non-Readable stream - sync', testInvalidInput, new Writable(), execaSync); ================================================ FILE: test/io/input-sync.js ================================================ import test from 'ava'; import {execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; setFixtureDirectory(); const getFd3InputMessage = type => `not \`stdio[3]\`, can be ${type}`; const testFd3InputSync = (t, stdioOption, expectedMessage) => { const {message} = t.throws(() => { execaSync('empty.js', getStdio(3, stdioOption)); }); t.true(message.includes(expectedMessage)); }; test('Cannot use Uint8Array with stdio[*], sync', testFd3InputSync, new Uint8Array(), getFd3InputMessage('a Uint8Array')); test('Cannot use iterable with stdio[*], sync', testFd3InputSync, [[]], getFd3InputMessage('an iterable')); ================================================ FILE: test/io/iterate.js ================================================ import {once} from 'node:events'; import {getDefaultHighWaterMark} from 'node:stream'; import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { assertStreamOutput, assertIterableChunks, assertStreamChunks, assertSubprocessOutput, getReadableSubprocess, getReadWriteSubprocess, } from '../helpers/convert.js'; import { stringToUint8Arrays, simpleFull, simpleChunks, simpleChunksBuffer, simpleChunksUint8Array, simpleLines, noNewlinesFull, complexFull, complexFullUtf16, complexFullUtf16Uint8Array, singleComplexBuffer, singleComplexUtf16Buffer, singleComplexUint8Array, singleComplexHex, complexChunks, complexChunksEnd, } from '../helpers/lines.js'; import {outputObjectGenerator, getOutputGenerator} from '../helpers/generator.js'; import {foobarString, foobarObject} from '../helpers/input.js'; import { multibyteChar, multibyteUint8Array, breakingLength, brokenSymbol, } from '../helpers/encoding.js'; setFixtureDirectory(); const foobarObjectChunks = [foobarObject, foobarObject, foobarObject]; const getSubprocess = (methodName, output, options) => { if (methodName !== 'duplex') { return getReadableSubprocess(output, options); } const subprocess = getReadWriteSubprocess(options); subprocess.stdin.end(output); return subprocess; }; const assertChunks = async (t, streamOrIterable, expectedChunks, methodName) => { const assertMethod = methodName === 'iterable' ? assertIterableChunks : assertStreamChunks; await assertMethod(t, streamOrIterable, expectedChunks); }; // eslint-disable-next-line max-params const testText = async (t, expectedChunks, methodName, binary, preserveNewlines, encoding) => { const subprocess = getReadWriteSubprocess({encoding}); const input = encoding === 'utf16le' ? complexFullUtf16 : complexFull; subprocess.stdin.end(input); const stream = subprocess[methodName]({binary, preserveNewlines}); await assertChunks(t, stream, expectedChunks, methodName); const expectedOutput = encoding === 'hex' ? singleComplexHex : stringToUint8Arrays(complexFull, encoding === 'buffer'); await assertSubprocessOutput(t, subprocess, expectedOutput); }; test('.iterable() can use "binary: true"', testText, [singleComplexUint8Array], 'iterable', true, undefined, 'utf8'); test('.iterable() can use "binary: true" + "encoding: utf16le"', testText, [complexFullUtf16Uint8Array], 'iterable', true, undefined, 'utf16le'); test('.iterable() can use "binary: true" + "encoding: "buffer"', testText, [singleComplexUint8Array], 'iterable', true, undefined, 'buffer'); test('.iterable() can use "binary: true" + "encoding: "hex"', testText, [singleComplexUint8Array], 'iterable', true, undefined, 'hex'); test('.iterable() can use "binary: undefined"', testText, complexChunks, 'iterable', undefined, undefined, 'utf8'); test('.iterable() can use "binary: undefined" + "encoding: utf16le"', testText, complexChunks, 'iterable', undefined, undefined, 'utf16le'); test('.iterable() can use "binary: undefined" + "encoding: buffer"', testText, [singleComplexUint8Array], 'iterable', undefined, undefined, 'buffer'); test('.iterable() can use "binary: undefined" + "encoding: hex"', testText, [singleComplexUint8Array], 'iterable', undefined, undefined, 'hex'); test('.iterable() can use "binary: false"', testText, complexChunks, 'iterable', false, undefined, 'utf8'); test('.iterable() can use "binary: false" + "encoding: utf16le"', testText, complexChunks, 'iterable', false, undefined, 'utf16le'); test('.iterable() can use "binary: false" + "encoding: buffer"', testText, [singleComplexUint8Array], 'iterable', false, undefined, 'buffer'); test('.iterable() can use "binary: false" + "encoding: hex"', testText, [singleComplexUint8Array], 'iterable', false, undefined, 'hex'); test('.iterable() can use "binary: false" + "preserveNewlines: true"', testText, complexChunksEnd, 'iterable', false, true, 'utf8'); test('.iterable() can use "binary: false" + "preserveNewlines: false"', testText, complexChunks, 'iterable', false, false, 'utf8'); test('.readable() can use "binary: true"', testText, singleComplexBuffer, 'readable', true, undefined, 'utf8'); test('.readable() can use "binary: true" + "encoding: utf16le"', testText, singleComplexUtf16Buffer, 'readable', true, undefined, 'utf16le'); test('.readable() can use "binary: true" + "encoding: buffer"', testText, singleComplexBuffer, 'readable', true, undefined, 'buffer'); test('.readable() can use "binary: true" + "encoding: hex"', testText, singleComplexBuffer, 'readable', true, undefined, 'hex'); test('.readable() can use "binary: undefined"', testText, singleComplexBuffer, 'readable', undefined, undefined, 'utf8'); test('.readable() can use "binary: undefined" + "encoding: utf16le"', testText, singleComplexUtf16Buffer, 'readable', undefined, undefined, 'utf16le'); test('.readable() can use "binary: undefined" + "encoding: buffer"', testText, singleComplexBuffer, 'readable', undefined, undefined, 'buffer'); test('.readable() can use "binary: undefined" + "encoding: hex"', testText, singleComplexBuffer, 'readable', undefined, undefined, 'hex'); test('.readable() can use "binary: false"', testText, complexChunksEnd, 'readable', false, undefined, 'utf8'); test('.readable() can use "binary: false" + "encoding: utf16le"', testText, complexChunksEnd, 'readable', false, undefined, 'utf16le'); test('.readable() can use "binary: false" + "encoding: buffer"', testText, singleComplexBuffer, 'readable', false, undefined, 'buffer'); test('.readable() can use "binary: false" + "encoding: hex"', testText, singleComplexBuffer, 'readable', false, undefined, 'hex'); test('.readable() can use "binary: false" + "preserveNewlines: true"', testText, complexChunksEnd, 'readable', false, true, 'utf8'); test('.readable() can use "binary: false" + "preserveNewlines: false"', testText, complexChunks, 'readable', false, false, 'utf8'); test('.duplex() can use "binary: true"', testText, singleComplexBuffer, 'duplex', true, undefined, 'utf8'); test('.duplex() can use "binary: true" + "encoding: utf16le"', testText, singleComplexUtf16Buffer, 'duplex', true, undefined, 'utf16le'); test('.duplex() can use "binary: true" + "encoding: buffer"', testText, singleComplexBuffer, 'duplex', true, undefined, 'buffer'); test('.duplex() can use "binary: true" + "encoding: hex"', testText, singleComplexBuffer, 'duplex', true, undefined, 'hex'); test('.duplex() can use "binary: undefined"', testText, singleComplexBuffer, 'duplex', undefined, undefined, 'utf8'); test('.duplex() can use "binary: undefined" + "encoding: utf16le"', testText, singleComplexUtf16Buffer, 'duplex', undefined, undefined, 'utf16le'); test('.duplex() can use "binary: undefined" + "encoding: "buffer"', testText, singleComplexBuffer, 'duplex', undefined, undefined, 'buffer'); test('.duplex() can use "binary: undefined" + "encoding: "hex"', testText, singleComplexBuffer, 'duplex', undefined, undefined, 'hex'); test('.duplex() can use "binary: false"', testText, complexChunksEnd, 'duplex', false, undefined, 'utf8'); test('.duplex() can use "binary: false" + "encoding: utf16le"', testText, complexChunksEnd, 'duplex', false, undefined, 'utf16le'); test('.duplex() can use "binary: false" + "encoding: buffer"', testText, singleComplexBuffer, 'duplex', false, undefined, 'buffer'); test('.duplex() can use "binary: false" + "encoding: hex"', testText, singleComplexBuffer, 'duplex', false, undefined, 'hex'); test('.duplex() can use "binary: false" + "preserveNewlines: true"', testText, complexChunksEnd, 'duplex', false, true, 'utf8'); test('.duplex() can use "binary: false" + "preserveNewlines: false"', testText, complexChunks, 'duplex', false, false, 'utf8'); const testTextOutput = async (t, expectedOutput, methodName, preserveNewlines) => { const subprocess = getSubprocess(methodName, complexFull); const stream = subprocess[methodName]({binary: false, preserveNewlines}); await assertStreamOutput(t, stream, expectedOutput); await assertSubprocessOutput(t, subprocess, complexFull); }; test('.readable() "binary: false" keeps output as is', testTextOutput, complexFull, 'readable', undefined); test('.readable() "binary: false" + "preserveNewlines: true" keeps output as is', testTextOutput, complexFull, 'readable', true); test('.readable() "binary: false" + "preserveNewlines: false" removes all newlines', testTextOutput, noNewlinesFull, 'readable', false); test('.duplex() "binary: false" keeps output as is', testTextOutput, complexFull, 'duplex', undefined); test('.duplex() "binary: false" + "preserveNewlines: true" keeps output as is', testTextOutput, complexFull, 'duplex', true); test('.duplex() "binary: false" + "preserveNewlines: false" removes all newlines', testTextOutput, noNewlinesFull, 'duplex', false); // eslint-disable-next-line max-params const testObjectMode = async (t, expectedChunks, methodName, encoding, initialObjectMode, finalObjectMode, binary, options) => { const subprocess = getSubprocess(methodName, simpleFull, options); if (encoding !== null) { subprocess.stdout.setEncoding(encoding); } t.is(subprocess.stdout.readableEncoding, encoding); t.is(subprocess.stdout.readableObjectMode, initialObjectMode); t.is(subprocess.stdout.readableHighWaterMark, getDefaultHighWaterMark(initialObjectMode)); const stream = subprocess[methodName]({binary, preserveNewlines: true}); if (methodName !== 'iterable') { t.is(stream.readableEncoding, encoding); t.is(stream.readableObjectMode, finalObjectMode); t.is(stream.readableHighWaterMark, getDefaultHighWaterMark(finalObjectMode)); } t.is(subprocess.stdout.readableEncoding, encoding); t.is(subprocess.stdout.readableObjectMode, initialObjectMode); t.is(subprocess.stdout.readableHighWaterMark, getDefaultHighWaterMark(initialObjectMode)); await assertChunks(t, stream, expectedChunks, methodName); await subprocess; }; test('.iterable() uses Uint8Arrays with "binary: true"', testObjectMode, simpleChunksUint8Array, 'iterable', null, false, false, true); test('.iterable() uses Uint8Arrays with "binary: true" and .setEncoding("utf8")', testObjectMode, simpleChunksUint8Array, 'iterable', 'utf8', false, false, true); test('.iterable() uses Uint8Arrays with "binary: true", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunksUint8Array, 'iterable', 'utf8', false, false, true, {encoding: 'buffer'}); test('.iterable() uses strings in objectMode with "binary: true" and object transforms', testObjectMode, foobarObjectChunks, 'iterable', null, true, true, true, {stdout: outputObjectGenerator()}); test('.iterable() uses strings in objectMode with "binary: false"', testObjectMode, simpleLines, 'iterable', null, false, true, false); test('.iterable() uses strings in objectMode with "binary: false" and .setEncoding("utf8")', testObjectMode, simpleLines, 'iterable', 'utf8', false, true, false); test('.iterable() uses Uint8Arrays in objectMode with "binary: false", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunksUint8Array, 'iterable', 'utf8', false, true, false, {encoding: 'buffer'}); test('.iterable() uses strings in objectMode with "binary: false" and object transforms', testObjectMode, foobarObjectChunks, 'iterable', null, true, true, false, {stdout: outputObjectGenerator()}); test('.readable() uses Buffers with "binary: true"', testObjectMode, simpleChunksBuffer, 'readable', null, false, false, true); test('.readable() uses strings with "binary: true" and .setEncoding("utf8")', testObjectMode, simpleChunks, 'readable', 'utf8', false, false, true); test('.readable() uses strings with "binary: true", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunks, 'readable', 'utf8', false, false, true, {encoding: 'buffer'}); test('.readable() uses strings in objectMode with "binary: true" and object transforms', testObjectMode, foobarObjectChunks, 'readable', null, true, true, true, {stdout: outputObjectGenerator()}); test('.readable() uses strings in objectMode with "binary: false"', testObjectMode, simpleLines, 'readable', null, false, true, false); test('.readable() uses strings in objectMode with "binary: false" and .setEncoding("utf8")', testObjectMode, simpleLines, 'readable', 'utf8', false, true, false); test('.readable() uses strings in objectMode with "binary: false", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunks, 'readable', 'utf8', false, false, false, {encoding: 'buffer'}); test('.readable() uses strings in objectMode with "binary: false" and object transforms', testObjectMode, foobarObjectChunks, 'readable', null, true, true, false, {stdout: outputObjectGenerator()}); test('.duplex() uses Buffers with "binary: true"', testObjectMode, simpleChunksBuffer, 'duplex', null, false, false, true); test('.duplex() uses strings with "binary: true" and .setEncoding("utf8")', testObjectMode, simpleChunks, 'duplex', 'utf8', false, false, true); test('.duplex() uses strings with "binary: true", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunks, 'duplex', 'utf8', false, false, true, {encoding: 'buffer'}); test('.duplex() uses strings in objectMode with "binary: true" and object transforms', testObjectMode, foobarObjectChunks, 'duplex', null, true, true, true, {stdout: outputObjectGenerator()}); test('.duplex() uses strings in objectMode with "binary: false"', testObjectMode, simpleLines, 'duplex', null, false, true, false); test('.duplex() uses strings in objectMode with "binary: false" and .setEncoding("utf8")', testObjectMode, simpleLines, 'duplex', 'utf8', false, true, false); test('.duplex() uses strings in objectMode with "binary: false", .setEncoding("utf8") and "encoding: buffer"', testObjectMode, simpleChunks, 'duplex', 'utf8', false, false, false, {encoding: 'buffer'}); test('.duplex() uses strings in objectMode with "binary: false" and object transforms', testObjectMode, foobarObjectChunks, 'duplex', null, true, true, false, {stdout: outputObjectGenerator()}); const testObjectSplit = async (t, methodName) => { const subprocess = getSubprocess(methodName, foobarString, {stdout: getOutputGenerator(simpleFull)(true)}); const stream = subprocess[methodName]({binary: false}); await assertChunks(t, stream, [simpleFull], methodName); await subprocess; }; test('.iterable() "binary: false" does not split lines of strings produced by object transforms', testObjectSplit, 'iterable'); test('.readable() "binary: false" does not split lines of strings produced by object transforms', testObjectSplit, 'readable'); test('.duplex() "binary: false" does not split lines of strings produced by object transforms', testObjectSplit, 'duplex'); const testMultibyteCharacters = async (t, methodName) => { const subprocess = getReadWriteSubprocess(); const stream = subprocess[methodName]({binary: false}); const assertPromise = assertChunks(t, stream, [`${multibyteChar}${brokenSymbol}`], methodName); subprocess.stdin.write(multibyteUint8Array.slice(0, breakingLength)); await once(subprocess.stdout, 'data'); subprocess.stdin.end(); await assertPromise; }; test('.iterable() "binary: false" handles partial multibyte characters', testMultibyteCharacters, 'iterable'); test('.readable() "binary: false" handles partial multibyte characters', testMultibyteCharacters, 'readable'); test('.duplex() "binary: false" handles partial multibyte characters', testMultibyteCharacters, 'duplex'); ================================================ FILE: test/io/max-buffer.js ================================================ import {Buffer} from 'node:buffer'; import test from 'ava'; import getStream from 'get-stream'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import {getEarlyErrorSubprocess} from '../helpers/early-error.js'; import {maxBuffer, assertErrorMessage} from '../helpers/max-buffer.js'; import {foobarArray} from '../helpers/input.js'; setFixtureDirectory(); const maxBufferMessage = {message: /maxBuffer exceeded/}; const maxBufferCodeSync = {code: 'ENOBUFS'}; const runMaxBuffer = async (t, execaMethod, fdNumber, options) => { const error = execaMethod === execa ? await t.throwsAsync(getMaxBufferSubprocess(execaMethod, fdNumber, options), maxBufferMessage) : t.throws(() => { getMaxBufferSubprocess(execaMethod, fdNumber, options); }, maxBufferCodeSync); t.true(error.isMaxBuffer); t.is(error.maxBufferInfo, undefined); return error; }; const getMaxBufferSubprocess = (execaMethod, fdNumber, {length = maxBuffer, ...options} = {}) => execaMethod('max-buffer.js', [`${fdNumber}`, `${length + 1}`], {...fullStdio, maxBuffer, ...options}); const getExpectedOutput = (length = maxBuffer) => '.'.repeat(length); const testMaxBufferSuccess = async (t, execaMethod, fdNumber, all) => { const {isMaxBuffer} = await getMaxBufferSubprocess(execaMethod, fdNumber, {all, length: maxBuffer - 1}); t.false(isMaxBuffer); }; test('maxBuffer does not affect stdout if too high', testMaxBufferSuccess, execa, 1, false); test('maxBuffer does not affect stderr if too high', testMaxBufferSuccess, execa, 2, false); test('maxBuffer does not affect stdio[*] if too high', testMaxBufferSuccess, execa, 3, false); test('maxBuffer does not affect all if too high', testMaxBufferSuccess, execa, 1, true); test('maxBuffer does not affect stdout if too high, sync', testMaxBufferSuccess, execaSync, 1, false); test('maxBuffer does not affect stderr if too high, sync', testMaxBufferSuccess, execaSync, 2, false); test('maxBuffer does not affect stdio[*] if too high, sync', testMaxBufferSuccess, execaSync, 3, false); test('maxBuffer does not affect all if too high, sync', testMaxBufferSuccess, execaSync, 1, true); const testGracefulExit = async (t, fixtureName, expectedExitCode) => { const {isMaxBuffer, shortMessage, exitCode, signal, stdout} = await t.throwsAsync( execa(fixtureName, ['1', '.'.repeat(maxBuffer + 1)], {maxBuffer}), maxBufferMessage, ); t.true(isMaxBuffer); assertErrorMessage(t, shortMessage); t.is(exitCode, expectedExitCode); t.is(signal, undefined); t.is(stdout, getExpectedOutput()); }; test('maxBuffer terminates stream gracefully, more writes', testGracefulExit, 'noop-repeat.js', 1); test('maxBuffer terminates stream gracefully, no more writes', testGracefulExit, 'noop-fd.js', 0); const testGracefulExitSync = (t, fixtureName) => { const {isMaxBuffer, shortMessage, exitCode, signal, stdout} = t.throws(() => { execaSync(fixtureName, ['1', '.'.repeat(maxBuffer + 1)], {maxBuffer, killSignal: 'SIGINT'}); }, maxBufferCodeSync); t.true(isMaxBuffer); assertErrorMessage(t, shortMessage, {execaMethod: execaSync}); t.is(exitCode, undefined); t.is(signal, 'SIGINT'); t.is(stdout, getExpectedOutput()); }; test('maxBuffer terminate stream with killSignal, more writes, sync', testGracefulExitSync, 'noop-repeat.js'); test('maxBuffer terminate stream with killSignal, no more writes, sync', testGracefulExitSync, 'noop-fd.js'); const testMaxBufferLimit = async (t, execaMethod, fdNumber, all) => { const length = all && execaMethod === execa ? maxBuffer * 2 : maxBuffer; const {shortMessage, all: allOutput, stdio} = await runMaxBuffer(t, execaMethod, fdNumber, {all, length}); assertErrorMessage(t, shortMessage, {execaMethod, fdNumber}); t.is(all ? allOutput : stdio[fdNumber], getExpectedOutput(length)); }; test('maxBuffer truncates stdout', testMaxBufferLimit, execa, 1, false); test('maxBuffer truncates stderr', testMaxBufferLimit, execa, 2, false); test('maxBuffer truncates stdio[*]', testMaxBufferLimit, execa, 3, false); test('maxBuffer truncates all', testMaxBufferLimit, execa, 1, true); test('maxBuffer truncates stdout, sync', testMaxBufferLimit, execaSync, 1, false); test('maxBuffer truncates stderr, sync', testMaxBufferLimit, execaSync, 2, false); test('maxBuffer truncates stdio[*], sync', testMaxBufferLimit, execaSync, 3, false); test('maxBuffer truncates all, sync', testMaxBufferLimit, execaSync, 1, true); const MAX_BUFFER_DEFAULT = 1e8; const testMaxBufferDefault = async (t, execaMethod, fdNumber, maxBuffer) => { const length = MAX_BUFFER_DEFAULT; const {shortMessage, stdio} = await runMaxBuffer(t, execaMethod, fdNumber, {length: MAX_BUFFER_DEFAULT + 1, maxBuffer}); assertErrorMessage(t, shortMessage, {execaMethod, fdNumber, length}); t.is(stdio[fdNumber], getExpectedOutput(length)); }; test('maxBuffer has a default value with stdout', testMaxBufferDefault, execa, 1, undefined); test('maxBuffer has a default value with stderr', testMaxBufferDefault, execa, 2, undefined); test('maxBuffer has a default value with stdio[*]', testMaxBufferDefault, execa, 3, undefined); test('maxBuffer has a default value with stdout, sync', testMaxBufferDefault, execaSync, 1, undefined); test('maxBuffer has a default value with stderr, sync', testMaxBufferDefault, execaSync, 2, undefined); test('maxBuffer has a default value with stdio[*], sync', testMaxBufferDefault, execaSync, 3, undefined); test('maxBuffer has a default value with stdout with fd-specific options', testMaxBufferDefault, execa, 1, {stderr: 1e9}); test('maxBuffer has a default value with stderr with fd-specific options', testMaxBufferDefault, execa, 2, {stdout: 1e9}); test('maxBuffer has a default value with stdio[*] with fd-specific options', testMaxBufferDefault, execa, 3, {stdout: 1e9}); test('maxBuffer has a default value with stdout with empty fd-specific options', testMaxBufferDefault, execa, 1, {}); const testFdSpecific = async (t, fdNumber, fdName, execaMethod) => { const length = 1; const {shortMessage, stdio} = await runMaxBuffer(t, execaMethod, fdNumber, {maxBuffer: {[fdName]: length}}); assertErrorMessage(t, shortMessage, {execaMethod, fdNumber, length}); t.is(stdio[fdNumber], getExpectedOutput(length)); }; test('maxBuffer truncates file descriptors with fd-specific options, stdout', testFdSpecific, 1, 'stdout', execa); test('maxBuffer truncates file descriptors with fd-specific options, fd1', testFdSpecific, 1, 'fd1', execa); test('maxBuffer truncates file descriptors with fd-specific options, stderr', testFdSpecific, 2, 'stderr', execa); test('maxBuffer truncates file descriptors with fd-specific options, fd2', testFdSpecific, 2, 'fd2', execa); test('maxBuffer truncates file descriptors with fd-specific options, stdout, all', testFdSpecific, 1, 'all', execa); test('maxBuffer truncates file descriptors with fd-specific options, stderr, all', testFdSpecific, 2, 'all', execa); test('maxBuffer truncates file descriptors with fd-specific options, fd3', testFdSpecific, 3, 'fd3', execa); test('maxBuffer.stdout is used for stdout with fd-specific options, stdout, sync', testFdSpecific, 1, 'stdout', execaSync); test('maxBuffer does not affect other file descriptors with fd-specific options', async t => { const {isMaxBuffer} = await getMaxBufferSubprocess(execa, 2, {maxBuffer: {stdout: 1}}); t.false(isMaxBuffer); }); test('maxBuffer.stdout is used for other file descriptors with fd-specific options, sync', async t => { const length = 1; const {shortMessage, stderr} = await runMaxBuffer(t, execaSync, 2, {maxBuffer: {stdout: length}}); assertErrorMessage(t, shortMessage, {execaMethod: execaSync, fdNumber: 2, length}); t.is(stderr, getExpectedOutput(length)); }); const testAll = async (t, shouldFail) => { const difference = shouldFail ? 0 : 1; const maxBufferStdout = 2; const maxBufferStderr = 4 - difference; const {isMaxBuffer, shortMessage, stdout, stderr, all} = await execa( 'noop-both.js', ['\n'.repeat(maxBufferStdout - 1), '\n'.repeat(maxBufferStderr - difference)], { maxBuffer: {stdout: maxBufferStdout, stderr: maxBufferStderr}, all: true, stripFinalNewline: false, reject: false, }, ); t.is(isMaxBuffer, shouldFail); if (shouldFail) { assertErrorMessage(t, shortMessage, {fdNumber: 2, length: maxBufferStderr}); } t.is(stdout, '\n'.repeat(maxBufferStdout)); t.is(stderr, '\n'.repeat(maxBufferStderr)); t.is(all, '\n'.repeat(maxBufferStdout + maxBufferStderr)); }; test('maxBuffer.stdout can differ from maxBuffer.stderr, combined with all, below threshold', testAll, false); test('maxBuffer.stdout can differ from maxBuffer.stderr, combined with all, above threshold', testAll, true); const testInvalidFd = async (t, fdName, execaMethod) => { const {message} = t.throws(() => { execaMethod('empty.js', {maxBuffer: {[fdName]: 0}}); }); t.true(message.includes(`"maxBuffer.${fdName}" is invalid`)); }; test('maxBuffer.stdin is invalid', testInvalidFd, 'stdin', execa); test('maxBuffer.fd0 is invalid', testInvalidFd, 'fd0', execa); test('maxBuffer.other is invalid', testInvalidFd, 'other', execa); test('maxBuffer.fd10 is invalid', testInvalidFd, 'fd10', execa); test('maxBuffer.stdin is invalid, sync', testInvalidFd, 'stdin', execaSync); test('maxBuffer.fd0 is invalid, sync', testInvalidFd, 'fd0', execaSync); test('maxBuffer.other is invalid, sync', testInvalidFd, 'other', execaSync); test('maxBuffer.fd10 is invalid, sync', testInvalidFd, 'fd10', execaSync); const testMaxBufferEncoding = async (t, execaMethod, fdNumber) => { const {shortMessage, stdio} = await runMaxBuffer(t, execaMethod, fdNumber, {encoding: 'buffer'}); assertErrorMessage(t, shortMessage, {execaMethod, fdNumber, unit: 'bytes'}); const stream = stdio[fdNumber]; t.true(stream instanceof Uint8Array); t.is(Buffer.from(stream).toString(), getExpectedOutput()); }; test('maxBuffer works with encoding buffer and stdout', testMaxBufferEncoding, execa, 1); test('maxBuffer works with encoding buffer and stderr', testMaxBufferEncoding, execa, 2); test('maxBuffer works with encoding buffer and stdio[*]', testMaxBufferEncoding, execa, 3); test('maxBuffer works with encoding buffer and stdout, sync', testMaxBufferEncoding, execaSync, 1); test('maxBuffer works with encoding buffer and stderr, sync', testMaxBufferEncoding, execaSync, 2); test('maxBuffer works with encoding buffer and stdio[*], sync', testMaxBufferEncoding, execaSync, 3); const testMaxBufferHex = async (t, fdNumber) => { const length = maxBuffer / 2; const {shortMessage, stdio} = await runMaxBuffer(t, execa, fdNumber, {length, encoding: 'hex'}); assertErrorMessage(t, shortMessage, {fdNumber}); t.is(stdio[fdNumber], Buffer.from(getExpectedOutput(length)).toString('hex')); }; test('maxBuffer works with other encodings and stdout', testMaxBufferHex, 1); test('maxBuffer works with other encodings and stderr', testMaxBufferHex, 2); test('maxBuffer works with other encodings and stdio[*]', testMaxBufferHex, 3); const testMaxBufferHexSync = async (t, fdNumber) => { const length = maxBuffer / 2; const {isMaxBuffer, stdio} = await getMaxBufferSubprocess(execaSync, fdNumber, {length, encoding: 'hex'}); t.false(isMaxBuffer); t.is(stdio[fdNumber], Buffer.from(getExpectedOutput(length + 1)).toString('hex')); }; test('maxBuffer ignores other encodings and stdout, sync', testMaxBufferHexSync, 1); test('maxBuffer ignores other encodings and stderr, sync', testMaxBufferHexSync, 2); test('maxBuffer ignores other encodings and stdio[*], sync', testMaxBufferHexSync, 3); const testNoMaxBuffer = async (t, fdNumber, buffer) => { const subprocess = getMaxBufferSubprocess(execa, fdNumber, {buffer}); const [{isMaxBuffer, stdio}, output] = await Promise.all([ subprocess, getStream(subprocess.stdio[fdNumber]), ]); t.false(isMaxBuffer); t.is(stdio[fdNumber], undefined); t.is(output, getExpectedOutput(maxBuffer + 1)); }; test('do not buffer stdout when `buffer` set to `false`', testNoMaxBuffer, 1, false); test('do not buffer stdout when `buffer` set to `false`, fd-specific', testNoMaxBuffer, 1, {stdout: false}); test('do not buffer stderr when `buffer` set to `false`', testNoMaxBuffer, 2, false); test('do not buffer stderr when `buffer` set to `false`, fd-specific', testNoMaxBuffer, 2, {stderr: false}); test('do not buffer stdio[*] when `buffer` set to `false`', testNoMaxBuffer, 3, false); test('do not buffer stdio[*] when `buffer` set to `false`, fd-specific', testNoMaxBuffer, 3, {fd3: false}); const testNoMaxBufferSync = (t, fdNumber, buffer) => { const {isMaxBuffer, stdio} = getMaxBufferSubprocess(execaSync, fdNumber, {buffer}); t.false(isMaxBuffer); t.is(stdio[fdNumber], undefined); }; // @todo: add tests for fd3 once the following Node.js bug is fixed. // https://github.com/nodejs/node/issues/52422 test('do not buffer stdout when `buffer` set to `false`, sync', testNoMaxBufferSync, 1, false); test('do not buffer stdout when `buffer` set to `false`, fd-specific, sync', testNoMaxBufferSync, 1, {stdout: false}); test('do not buffer stderr when `buffer` set to `false`, sync', testNoMaxBufferSync, 2, false); test('do not buffer stderr when `buffer` set to `false`, fd-specific, sync', testNoMaxBufferSync, 2, {stderr: false}); const testMaxBufferAbort = async (t, fdNumber) => { const subprocess = getMaxBufferSubprocess(execa, fdNumber); const [{isMaxBuffer, shortMessage}] = await Promise.all([ t.throwsAsync(subprocess, maxBufferMessage), t.throwsAsync(getStream(subprocess.stdio[fdNumber]), {code: 'ERR_STREAM_PREMATURE_CLOSE'}), ]); t.true(isMaxBuffer); assertErrorMessage(t, shortMessage, {execaMethod: execa, fdNumber}); }; test('abort stream when hitting maxBuffer with stdout', testMaxBufferAbort, 1); test('abort stream when hitting maxBuffer with stderr', testMaxBufferAbort, 2); test('abort stream when hitting maxBuffer with stdio[*]', testMaxBufferAbort, 3); test('error.isMaxBuffer is false on early errors', async t => { const {failed, isMaxBuffer} = await getEarlyErrorSubprocess({reject: false, maxBuffer: 1}); t.true(failed); t.false(isMaxBuffer); }); test('maxBuffer works with result.ipcOutput', async t => { const { isMaxBuffer, shortMessage, message, stderr, ipcOutput, } = await t.throwsAsync(execa('ipc-send-twice.js', {ipc: true, maxBuffer: {ipc: 1}})); t.true(isMaxBuffer); t.is(shortMessage, 'Command\'s IPC output was larger than 1 messages: ipc-send-twice.js\nmaxBuffer exceeded'); t.true(message.endsWith(`\n\n${foobarArray[0]}`)); t.is(stderr, ''); t.deepEqual(ipcOutput, [foobarArray[0]]); }); test('maxBuffer is ignored with result.ipcOutput if buffer is false', async t => { const {ipcOutput} = await execa('ipc-send-twice.js', {ipc: true, maxBuffer: {ipc: 1}, buffer: false}); t.deepEqual(ipcOutput, []); }); ================================================ FILE: test/io/output-async.js ================================================ import {once, defaultMaxListeners} from 'node:events'; import process from 'node:process'; import {setImmediate} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {STANDARD_STREAMS} from '../helpers/stdio.js'; import {foobarString} from '../helpers/input.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {assertMaxListeners} from '../helpers/listeners.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); const getStandardStreamListeners = stream => Object.fromEntries(stream.eventNames().map(eventName => [eventName, stream.listeners(eventName)])); const getStandardStreamsListeners = () => STANDARD_STREAMS.map(stream => getStandardStreamListeners(stream)); const getComplexStdio = isMultiple => ({ stdin: ['pipe', 'inherit', ...(isMultiple ? [0, process.stdin] : [])], stdout: ['pipe', 'inherit', ...(isMultiple ? [1, process.stdout] : [])], stderr: ['pipe', 'inherit', ...(isMultiple ? [2, process.stderr] : [])], }); const onStdinRemoveListener = () => once(process.stdin, 'removeListener'); const testListenersCleanup = async (t, isMultiple) => { const streamsPreviousListeners = getStandardStreamsListeners(); const subprocess = execa('empty.js', getComplexStdio(isMultiple)); t.notDeepEqual(getStandardStreamsListeners(), streamsPreviousListeners); await Promise.all([subprocess, onStdinRemoveListener()]); if (isMultiple) { await onStdinRemoveListener(); } for (const [fdNumber, streamNewListeners] of Object.entries(getStandardStreamsListeners())) { const defaultListeners = Object.fromEntries(Reflect.ownKeys(streamNewListeners).map(eventName => [eventName, []])); t.deepEqual(streamNewListeners, {...defaultListeners, ...streamsPreviousListeners[fdNumber]}); } }; test.serial('process.std* listeners are cleaned up on success with a single input', testListenersCleanup, false); test.serial('process.std* listeners are cleaned up on success with multiple inputs', testListenersCleanup, true); test.serial('Can spawn many subprocesses in parallel', async t => { const results = await Promise.all( Array.from({length: PARALLEL_COUNT}, () => execa('noop.js', [foobarString])), ); t.true(results.every(({stdout}) => stdout === foobarString)); }); const testMaxListeners = async (t, isMultiple, maxListenersCount) => { const checkMaxListeners = assertMaxListeners(t); for (const standardStream of STANDARD_STREAMS) { standardStream.setMaxListeners(maxListenersCount); } try { const results = await Promise.all( Array.from({length: PARALLEL_COUNT}, () => execa('empty.js', getComplexStdio(isMultiple))), ); t.true(results.every(({exitCode}) => exitCode === 0)); } finally { await setImmediate(); await setImmediate(); checkMaxListeners(); for (const standardStream of STANDARD_STREAMS) { t.is(standardStream.getMaxListeners(), maxListenersCount); standardStream.setMaxListeners(defaultMaxListeners); } } }; test.serial('No warning with maxListeners 1 and ["pipe", "inherit"]', testMaxListeners, false, 1); test.serial('No warning with maxListeners default and ["pipe", "inherit"]', testMaxListeners, false, defaultMaxListeners); test.serial('No warning with maxListeners 100 and ["pipe", "inherit"]', testMaxListeners, false, 100); test.serial('No warning with maxListeners Infinity and ["pipe", "inherit"]', testMaxListeners, false, Number.POSITIVE_INFINITY); test.serial('No warning with maxListeners 0 and ["pipe", "inherit"]', testMaxListeners, false, 0); test.serial('No warning with maxListeners 1 and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, 1); test.serial('No warning with maxListeners default and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, defaultMaxListeners); test.serial('No warning with maxListeners 100 and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, 100); test.serial('No warning with maxListeners Infinity and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, Number.POSITIVE_INFINITY); test.serial('No warning with maxListeners 0 and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, 0); ================================================ FILE: test/io/output-sync.js ================================================ import test from 'ava'; import {execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {throwingGenerator} from '../helpers/generator.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); test('Handles errors with stdout generator, sync', t => { const cause = new Error(foobarString); const error = t.throws(() => { execaSync('noop.js', {stdout: throwingGenerator(cause)()}); }); t.is(error.cause, cause); }); test('Handles errors with stdout generator, spawn failure, sync', t => { const cause = new Error(foobarString); const error = t.throws(() => { execaSync('noop.js', {cwd: 'does_not_exist', stdout: throwingGenerator(cause)()}); }); t.true(error.failed); t.is(error.cause.code, 'ENOENT'); }); test('Handles errors with stdout generator, subprocess failure, sync', t => { const cause = new Error(foobarString); const error = t.throws(() => { execaSync('noop-fail.js', ['1'], {stdout: throwingGenerator(cause)()}); }); t.true(error.failed); t.is(error.cause, cause); }); ================================================ FILE: test/io/pipeline.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {getStdio, STANDARD_STREAMS} from '../helpers/stdio.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getEarlyErrorSubprocess, expectedEarlyError} from '../helpers/early-error.js'; setFixtureDirectory(); const testDestroyStandard = async (t, fdNumber) => { const subprocess = execa('forever.js', {...getStdio(fdNumber, [STANDARD_STREAMS[fdNumber], 'pipe']), timeout: 1}); await t.throwsAsync(subprocess, {message: /timed out/}); t.false(STANDARD_STREAMS[fdNumber].destroyed); }; test('Does not destroy process.stdin on subprocess errors', testDestroyStandard, 0); test('Does not destroy process.stdout on subprocess errors', testDestroyStandard, 1); test('Does not destroy process.stderr on subprocess errors', testDestroyStandard, 2); const testDestroyStandardSpawn = async (t, fdNumber) => { const error = await t.throwsAsync(getEarlyErrorSubprocess(getStdio(fdNumber, [STANDARD_STREAMS[fdNumber], 'pipe']))); t.like(error, expectedEarlyError); t.false(STANDARD_STREAMS[fdNumber].destroyed); }; test('Does not destroy process.stdin on subprocess early errors', testDestroyStandardSpawn, 0); test('Does not destroy process.stdout on subprocess early errors', testDestroyStandardSpawn, 1); test('Does not destroy process.stderr on subprocess early errors', testDestroyStandardSpawn, 2); const testDestroyStandardStream = async (t, fdNumber) => { const subprocess = execa('forever.js', getStdio(fdNumber, [STANDARD_STREAMS[fdNumber], 'pipe'])); const cause = new Error('test'); subprocess.stdio[fdNumber].destroy(cause); subprocess.kill(); t.like(await t.throwsAsync(subprocess), {cause}); t.false(STANDARD_STREAMS[fdNumber].destroyed); }; test('Does not destroy process.stdin on stream subprocess errors', testDestroyStandardStream, 0); test('Does not destroy process.stdout on stream subprocess errors', testDestroyStandardStream, 1); test('Does not destroy process.stderr on stream subprocess errors', testDestroyStandardStream, 2); ================================================ FILE: test/io/strip-newline.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import {noopGenerator} from '../helpers/generator.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testStripFinalNewline = async (t, fdNumber, stripFinalNewline, shouldStrip, execaMethod) => { const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, `${foobarString}\n`], {...fullStdio, stripFinalNewline}); t.is(stdio[fdNumber], `${foobarString}${shouldStrip ? '' : '\n'}`); }; test('stripFinalNewline: default with stdout', testStripFinalNewline, 1, undefined, true, execa); test('stripFinalNewline: true with stdout', testStripFinalNewline, 1, true, true, execa); test('stripFinalNewline: false with stdout', testStripFinalNewline, 1, false, false, execa); test('stripFinalNewline: default with stderr', testStripFinalNewline, 2, undefined, true, execa); test('stripFinalNewline: true with stderr', testStripFinalNewline, 2, true, true, execa); test('stripFinalNewline: false with stderr', testStripFinalNewline, 2, false, false, execa); test('stripFinalNewline: default with stdio[*]', testStripFinalNewline, 3, undefined, true, execa); test('stripFinalNewline: true with stdio[*]', testStripFinalNewline, 3, true, true, execa); test('stripFinalNewline: false with stdio[*]', testStripFinalNewline, 3, false, false, execa); test('stripFinalNewline: default with stdout, fd-specific', testStripFinalNewline, 1, {}, true, execa); test('stripFinalNewline: true with stdout, fd-specific', testStripFinalNewline, 1, {stdout: true}, true, execa); test('stripFinalNewline: false with stdout, fd-specific', testStripFinalNewline, 1, {stdout: false}, false, execa); test('stripFinalNewline: default with stderr, fd-specific', testStripFinalNewline, 2, {}, true, execa); test('stripFinalNewline: true with stderr, fd-specific', testStripFinalNewline, 2, {stderr: true}, true, execa); test('stripFinalNewline: false with stderr, fd-specific', testStripFinalNewline, 2, {stderr: false}, false, execa); test('stripFinalNewline: default with stdio[*], fd-specific', testStripFinalNewline, 3, {}, true, execa); test('stripFinalNewline: true with stdio[*], fd-specific', testStripFinalNewline, 3, {fd3: true}, true, execa); test('stripFinalNewline: false with stdio[*], fd-specific', testStripFinalNewline, 3, {fd3: false}, false, execa); test('stripFinalNewline: default with stdout, sync', testStripFinalNewline, 1, undefined, true, execaSync); test('stripFinalNewline: true with stdout, sync', testStripFinalNewline, 1, true, true, execaSync); test('stripFinalNewline: false with stdout, sync', testStripFinalNewline, 1, false, false, execaSync); test('stripFinalNewline: default with stderr, sync', testStripFinalNewline, 2, undefined, true, execaSync); test('stripFinalNewline: true with stderr, sync', testStripFinalNewline, 2, true, true, execaSync); test('stripFinalNewline: false with stderr, sync', testStripFinalNewline, 2, false, false, execaSync); test('stripFinalNewline: default with stdio[*], sync', testStripFinalNewline, 3, undefined, true, execaSync); test('stripFinalNewline: true with stdio[*], sync', testStripFinalNewline, 3, true, true, execaSync); test('stripFinalNewline: false with stdio[*], sync', testStripFinalNewline, 3, false, false, execaSync); test('stripFinalNewline: default with stdout, fd-specific, sync', testStripFinalNewline, 1, {}, true, execaSync); test('stripFinalNewline: true with stdout, fd-specific, sync', testStripFinalNewline, 1, {stdout: true}, true, execaSync); test('stripFinalNewline: false with stdout, fd-specific, sync', testStripFinalNewline, 1, {stdout: false}, false, execaSync); test('stripFinalNewline: default with stderr, fd-specific, sync', testStripFinalNewline, 2, {}, true, execaSync); test('stripFinalNewline: true with stderr, fd-specific, sync', testStripFinalNewline, 2, {stderr: true}, true, execaSync); test('stripFinalNewline: false with stderr, fd-specific, sync', testStripFinalNewline, 2, {stderr: false}, false, execaSync); test('stripFinalNewline: default with stdio[*], fd-specific, sync', testStripFinalNewline, 3, {}, true, execaSync); test('stripFinalNewline: true with stdio[*], fd-specific, sync', testStripFinalNewline, 3, {fd3: true}, true, execaSync); test('stripFinalNewline: false with stdio[*], fd-specific, sync', testStripFinalNewline, 3, {fd3: false}, false, execaSync); test('stripFinalNewline is not used in objectMode', async t => { const {stdout} = await execa('noop-fd.js', ['1', `${foobarString}\n`], {stripFinalNewline: true, stdout: noopGenerator(true, false, true)}); t.deepEqual(stdout, [`${foobarString}\n`]); }); ================================================ FILE: test/ipc/buffer-messages.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarArray} from '../helpers/input.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); const testResultIpc = async (t, options) => { const {ipcOutput} = await execa('ipc-send-twice.js', {...options, ipc: true}); t.deepEqual(ipcOutput, foobarArray); }; test('Sets result.ipcOutput', testResultIpc, {}); test('Sets result.ipcOutput, fd-specific buffer', testResultIpc, {buffer: {stdout: false}}); const testResultNoBuffer = async (t, options) => { const {ipcOutput} = await execa('ipc-send.js', {...options, ipc: true}); t.deepEqual(ipcOutput, []); }; test('Sets empty result.ipcOutput if buffer is false', testResultNoBuffer, {buffer: false}); test('Sets empty result.ipcOutput if buffer is false, fd-specific buffer', testResultNoBuffer, {buffer: {ipc: false}}); test('Can use IPC methods when buffer is false', async t => { const subprocess = execa('ipc-send.js', {ipc: true, buffer: false}); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, []); }); test('Sets empty result.ipcOutput if ipc is false', async t => { const {ipcOutput} = await execa('empty.js'); t.deepEqual(ipcOutput, []); }); test('Sets empty result.ipcOutput, sync', t => { const {ipcOutput} = execaSync('empty.js'); t.deepEqual(ipcOutput, []); }); const testErrorIpc = async (t, options) => { const {ipcOutput} = await t.throwsAsync(execa('ipc-send-fail.js', {...options, ipc: true})); t.deepEqual(ipcOutput, [foobarString]); }; test('Sets error.ipcOutput', testErrorIpc, {}); test('Sets error.ipcOutput, fd-specific buffer', testErrorIpc, {buffer: {stdout: false}}); const testErrorNoBuffer = async (t, options) => { const {ipcOutput} = await t.throwsAsync(execa('ipc-send-fail.js', {...options, ipc: true})); t.deepEqual(ipcOutput, []); }; test('Sets empty error.ipcOutput if buffer is false', testErrorNoBuffer, {buffer: false}); test('Sets empty error.ipcOutput if buffer is false, fd-specific buffer', testErrorNoBuffer, {buffer: {ipc: false}}); test('Sets empty error.ipcOutput if ipc is false', async t => { const {ipcOutput} = await t.throwsAsync(execa('fail.js')); t.deepEqual(ipcOutput, []); }); test('Sets empty error.ipcOutput, sync', t => { const {ipcOutput} = t.throws(() => execaSync('fail.js')); t.deepEqual(ipcOutput, []); }); test.serial('Can retrieve initial IPC messages under heavy load', async t => { await Promise.all( Array.from({length: PARALLEL_COUNT}, async (_, index) => { const {ipcOutput} = await execa('ipc-send-argv.js', [`${index}`], {ipc: true}); t.deepEqual(ipcOutput, [`${index}`]); }), ); }); ================================================ FILE: test/ipc/forward.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarArray} from '../helpers/input.js'; import {iterateAllMessages, alwaysPass} from '../helpers/ipc.js'; setFixtureDirectory(); const testParentErrorOne = async (t, filter, buffer) => { const subprocess = execa('ipc-send.js', {ipc: true, buffer}); const promise = subprocess.getOneMessage({filter}); const cause = new Error(foobarString); subprocess.emit('error', cause); t.is(await promise, foobarString); const error = await t.throwsAsync(subprocess); t.is(error.exitCode, undefined); t.false(error.isTerminated); t.is(error.cause, cause); if (buffer) { t.deepEqual(error.ipcOutput, [foobarString]); } }; test('"error" event does not interrupt subprocess.getOneMessage(), buffer false', testParentErrorOne, undefined, false); test('"error" event does not interrupt subprocess.getOneMessage(), buffer true', testParentErrorOne, undefined, true); test('"error" event does not interrupt subprocess.getOneMessage(), buffer false, filter', testParentErrorOne, alwaysPass, false); test('"error" event does not interrupt subprocess.getOneMessage(), buffer true, filter', testParentErrorOne, alwaysPass, true); const testSubprocessErrorOne = async (t, filter, buffer) => { const subprocess = execa('ipc-process-error.js', [`${filter}`], {ipc: true, buffer}); await subprocess.sendMessage(foobarString); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; if (buffer) { t.deepEqual(ipcOutput, [foobarString]); } }; test('"error" event does not interrupt exports.getOneMessage(), buffer false', testSubprocessErrorOne, false, false); test('"error" event does not interrupt exports.getOneMessage(), buffer true', testSubprocessErrorOne, false, true); test('"error" event does not interrupt exports.getOneMessage(), buffer false, filter', testSubprocessErrorOne, true, false); test('"error" event does not interrupt exports.getOneMessage(), buffer true, filter', testSubprocessErrorOne, true, true); const testParentErrorEach = async (t, buffer) => { const subprocess = execa('ipc-send-twice.js', {ipc: true, buffer}); const promise = iterateAllMessages(subprocess); const cause = new Error(foobarString); subprocess.emit('error', cause); const error = await t.throwsAsync(subprocess); t.is(error, await t.throwsAsync(promise)); t.is(error.exitCode, undefined); t.false(error.isTerminated); t.is(error.cause, cause); if (buffer) { t.deepEqual(error.ipcOutput, foobarArray); } }; test('"error" event does not interrupt subprocess.getEachMessage(), buffer false', testParentErrorEach, false); test('"error" event does not interrupt subprocess.getEachMessage(), buffer true', testParentErrorEach, true); const testSubprocessErrorEach = async (t, filter, buffer) => { const subprocess = execa('ipc-iterate-error.js', [`${filter}`], {ipc: true, buffer}); await subprocess.sendMessage('.'); t.is(await subprocess.getOneMessage(), '.'); await subprocess.sendMessage(foobarString); const {ipcOutput} = await subprocess; if (buffer) { t.deepEqual(ipcOutput, ['.']); } }; test('"error" event does not interrupt exports.getEachMessage(), buffer false', testSubprocessErrorEach, 'ipc-iterate-error.js', false); test('"error" event does not interrupt exports.getEachMessage(), buffer true', testSubprocessErrorEach, 'ipc-iterate-error.js', true); test('"error" event does not interrupt result.ipcOutput', async t => { const subprocess = execa('ipc-echo-twice.js', {ipcInput: foobarString}); const cause = new Error(foobarString); subprocess.emit('error', cause); t.is(await subprocess.getOneMessage(), foobarString); await subprocess.sendMessage(foobarString); t.is(await subprocess.getOneMessage(), foobarString); const error = await t.throwsAsync(subprocess); t.is(error.exitCode, undefined); t.false(error.isTerminated); t.is(error.cause, cause); t.deepEqual(error.ipcOutput, [foobarString, foobarString]); }); ================================================ FILE: test/ipc/get-each.js ================================================ import {scheduler} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarArray} from '../helpers/input.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; import {iterateAllMessages} from '../helpers/ipc.js'; setFixtureDirectory(); test('Can iterate over IPC messages', async t => { let count = 0; const subprocess = execa('ipc-send-twice.js', {ipc: true}); for await (const message of subprocess.getEachMessage()) { t.is(message, foobarArray[count++]); } const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, foobarArray); }); test('Can iterate over IPC messages in subprocess', async t => { const subprocess = execa('ipc-iterate.js', {ipc: true}); await subprocess.sendMessage('.'); await subprocess.sendMessage('.'); await subprocess.sendMessage(foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, ['.', '.']); }); test('subprocess.getEachMessage() can be called twice at the same time', async t => { const subprocess = execa('ipc-send-twice.js', {ipc: true}); t.deepEqual( await Promise.all([iterateAllMessages(subprocess), iterateAllMessages(subprocess)]), [foobarArray, foobarArray], ); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, foobarArray); }); const iterateAndBreak = async (t, subprocess) => { // eslint-disable-next-line no-unreachable-loop for await (const message of subprocess.getEachMessage()) { t.is(message, foobarString); break; } }; test('Breaking in subprocess.getEachMessage() disconnects', async t => { const subprocess = execa('ipc-iterate-send.js', {ipc: true}); await iterateAndBreak(t, subprocess); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); }); test('Breaking from subprocess.getEachMessage() awaits the subprocess', async t => { const subprocess = execa('ipc-send-wait-print.js', {ipc: true}); await iterateAndBreak(t, subprocess); const {ipcOutput, stdout} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); t.is(stdout, '.'); }); test('Breaking from exports.getEachMessage() disconnects', async t => { const subprocess = execa('ipc-iterate-break.js', {ipc: true}); t.is(await subprocess.getOneMessage(), foobarString); await subprocess.sendMessage(foobarString); const ipcError = await t.throwsAsync(subprocess.getOneMessage()); t.true(ipcError.message.includes('subprocess.getOneMessage() could not complete')); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); }); const iterateAndThrow = async (t, subprocess, cause) => { // eslint-disable-next-line no-unreachable-loop for await (const message of subprocess.getEachMessage()) { t.is(message, foobarString); throw cause; } }; test('Throwing from subprocess.getEachMessage() disconnects', async t => { const subprocess = execa('ipc-iterate-send.js', {ipc: true}); const cause = new Error(foobarString); t.is(await t.throwsAsync(iterateAndThrow(t, subprocess, cause)), cause); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); }); test('Throwing from subprocess.getEachMessage() awaits the subprocess', async t => { const subprocess = execa('ipc-send-wait-print.js', {ipc: true}); const cause = new Error(foobarString); t.is(await t.throwsAsync(iterateAndThrow(t, subprocess, cause)), cause); const {ipcOutput, stdout} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); t.is(stdout, '.'); }); test('Throwing from exports.getEachMessage() disconnects', async t => { const subprocess = execa('ipc-iterate-throw.js', {ipc: true}); t.is(await subprocess.getOneMessage(), foobarString); await subprocess.sendMessage(foobarString); const ipcError = await t.throwsAsync(subprocess.getOneMessage()); t.true(ipcError.message.includes('subprocess.getOneMessage() could not complete')); const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(message.includes(`Error: ${foobarString}`)); t.deepEqual(ipcOutput, [foobarString]); }); test.serial('Can send many messages at once with exports.getEachMessage()', async t => { const subprocess = execa('ipc-iterate.js', {ipc: true}); await Promise.all(Array.from({length: PARALLEL_COUNT}, (_, index) => subprocess.sendMessage(index))); await subprocess.sendMessage(foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, Array.from({length: PARALLEL_COUNT}, (_, index) => index)); }); test('subprocess.getOneMessage() can be called multiple times in a row, buffer true', async t => { const subprocess = execa('ipc-print-many-each.js', [`${PARALLEL_COUNT}`], {ipc: true}); const indexes = Array.from({length: PARALLEL_COUNT}, (_, index) => `${index}`); await Promise.all(indexes.map(index => subprocess.sendMessage(index))); const {stdout} = await subprocess; const expectedOutput = indexes.join('\n'); t.is(stdout, expectedOutput); }); test('Disconnecting in the current process stops exports.getEachMessage()', async t => { const subprocess = execa('ipc-iterate-print.js', {ipc: true}); t.is(await subprocess.getOneMessage(), foobarString); await subprocess.sendMessage('.'); subprocess.disconnect(); const {stdout} = await subprocess; t.is(stdout, '.'); }); test('Disconnecting in the subprocess stops subprocess.getEachMessage()', async t => { const subprocess = execa('ipc-send-disconnect.js', {ipc: true}); for await (const message of subprocess.getEachMessage()) { t.is(message, foobarString); } const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); }); test('Exiting the subprocess stops subprocess.getEachMessage()', async t => { const subprocess = execa('ipc-send.js', {ipc: true}); for await (const message of subprocess.getEachMessage()) { t.is(message, foobarString); } const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); }); const testCleanupListeners = async (t, buffer) => { const subprocess = execa('ipc-send.js', {ipc: true, buffer}); t.is(subprocess.listenerCount('message'), 1); t.is(subprocess.listenerCount('disconnect'), 1); const promise = iterateAllMessages(subprocess); t.is(subprocess.listenerCount('message'), 1); t.is(subprocess.listenerCount('disconnect'), 1); t.deepEqual(await promise, [foobarString]); t.is(subprocess.listenerCount('message'), 0); t.is(subprocess.listenerCount('disconnect'), 0); }; test('Cleans up subprocess.getEachMessage() listeners, buffer false', testCleanupListeners, false); test('Cleans up subprocess.getEachMessage() listeners, buffer true', testCleanupListeners, true); const sendContinuousMessages = async subprocess => { while (subprocess.connected) { for (let index = 0; index < 10; index += 1) { subprocess.emit('message', foobarString); } // eslint-disable-next-line no-await-in-loop await scheduler.yield(); } }; test.serial('Handles buffered messages when disconnecting', async t => { const subprocess = execa('ipc-send-fail.js', {ipc: true, buffer: false}); const promise = subprocess.getOneMessage(); subprocess.emit('message', foobarString); t.is(await promise, foobarString); sendContinuousMessages(subprocess); const {exitCode, isTerminated, ipcOutput} = await t.throwsAsync(iterateAllMessages(subprocess)); t.is(exitCode, 1); t.false(isTerminated); t.deepEqual(ipcOutput, []); }); ================================================ FILE: test/ipc/get-one.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarArray} from '../helpers/input.js'; import {alwaysPass} from '../helpers/ipc.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); test('subprocess.getOneMessage() can filter messages', async t => { const subprocess = execa('ipc-send-twice.js', {ipc: true}); const message = await subprocess.getOneMessage({filter: message => message === foobarArray[1]}); t.is(message, foobarArray[1]); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, foobarArray); }); test('exports.getOneMessage() can filter messages', async t => { const subprocess = execa('ipc-echo-filter.js', {ipc: true}); await subprocess.sendMessage(foobarArray[0]); await subprocess.sendMessage(foobarArray[1]); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarArray[1]]); }); test('Throwing from subprocess.getOneMessage() filter disconnects', async t => { const subprocess = execa('ipc-send-get.js', {ipc: true}); const error = new Error(foobarString); t.is(await t.throwsAsync(subprocess.getOneMessage({ filter() { throw error; }, })), error); const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(message.includes('Error: getOneMessage() could not complete')); t.deepEqual(ipcOutput, [foobarString]); }); test('Throwing from exports.getOneMessage() filter disconnects', async t => { const subprocess = execa('ipc-get-filter-throw.js', {ipcInput: 0}); await t.throwsAsync(subprocess.getOneMessage(), { message: /subprocess.getOneMessage\(\) could not complete/, }); const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(message.includes(`Error: ${foobarString}`)); t.deepEqual(ipcOutput, []); }); test.serial('Can retrieve initial IPC messages under heavy load', async t => { await Promise.all( Array.from({length: PARALLEL_COUNT}, async (_, index) => { const subprocess = execa('ipc-send-argv.js', [`${index}`], {ipc: true, buffer: false}); t.is(await subprocess.getOneMessage(), `${index}`); await subprocess; }), ); }); const testTwice = async (t, buffer, filter) => { const subprocess = execa('ipc-send.js', {ipc: true, buffer}); t.deepEqual( await Promise.all([subprocess.getOneMessage({filter}), subprocess.getOneMessage({filter})]), [foobarString, foobarString], ); await subprocess; }; test('subprocess.getOneMessage() can be called twice at the same time, buffer false', testTwice, false, undefined); test('subprocess.getOneMessage() can be called twice at the same time, buffer true', testTwice, true, undefined); test('subprocess.getOneMessage() can be called twice at the same time, buffer false, filter', testTwice, false, alwaysPass); test('subprocess.getOneMessage() can be called twice at the same time, buffer true, filter', testTwice, true, alwaysPass); const testCleanupListeners = async (t, buffer, filter) => { const subprocess = execa('ipc-send.js', {ipc: true, buffer}); t.is(subprocess.listenerCount('message'), 1); t.is(subprocess.listenerCount('disconnect'), 1); const promise = subprocess.getOneMessage({filter}); t.is(subprocess.listenerCount('message'), 1); t.is(subprocess.listenerCount('disconnect'), 1); t.is(await promise, foobarString); await subprocess; t.is(subprocess.listenerCount('message'), 0); t.is(subprocess.listenerCount('disconnect'), 0); }; test('Cleans up subprocess.getOneMessage() listeners, buffer false', testCleanupListeners, false, undefined); test('Cleans up subprocess.getOneMessage() listeners, buffer true', testCleanupListeners, true, undefined); test('Cleans up subprocess.getOneMessage() listeners, buffer false, filter', testCleanupListeners, false, alwaysPass); test('Cleans up subprocess.getOneMessage() listeners, buffer true, filter', testCleanupListeners, true, alwaysPass); const testParentDisconnect = async (t, buffer, filter) => { const subprocess = execa('ipc-get-send-get.js', [`${filter}`], {ipc: true, buffer}); await subprocess.sendMessage(foobarString); t.is(await subprocess.getOneMessage(), foobarString); subprocess.disconnect(); const {exitCode, isTerminated, message} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); if (buffer) { t.true(message.includes('Error: getOneMessage() could not complete')); } }; test('subprocess.disconnect() interrupts exports.getOneMessage(), buffer false', testParentDisconnect, false, false); test('subprocess.disconnect() interrupts exports.getOneMessage(), buffer true', testParentDisconnect, true, false); test('subprocess.disconnect() interrupts exports.getOneMessage(), buffer false, filter', testParentDisconnect, false, true); test('subprocess.disconnect() interrupts exports.getOneMessage(), buffer true, filter', testParentDisconnect, true, false); const testSubprocessDisconnect = async (t, buffer, filter) => { const subprocess = execa('empty.js', {ipc: true, buffer}); const {message} = await t.throwsAsync(subprocess.getOneMessage({filter})); t.true(message.includes('subprocess.getOneMessage() could not complete')); await subprocess; }; test('Subprocess exit interrupts subprocess.getOneMessage(), buffer false', testSubprocessDisconnect, false, undefined); test('Subprocess exit interrupts subprocess.getOneMessage(), buffer true', testSubprocessDisconnect, true, undefined); test('Subprocess exit interrupts subprocess.getOneMessage(), buffer false, filter', testSubprocessDisconnect, false, alwaysPass); test('Subprocess exit interrupts subprocess.getOneMessage(), buffer true, filter', testSubprocessDisconnect, true, alwaysPass); ================================================ FILE: test/ipc/graceful.js ================================================ import {getEventListeners} from 'node:events'; import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); test('Graceful cancelSignal can be already aborted', async t => { const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(execa('graceful-send.js', {cancelSignal: AbortSignal.abort(foobarString), gracefulCancel: true, forceKillAfterDelay: false})); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, [foobarString]); }); test('Graceful cancelSignal can be aborted', async t => { const controller = new AbortController(); const subprocess = execa('graceful-send-twice.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); t.false(await subprocess.getOneMessage()); controller.abort(foobarString); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, [false, foobarString]); }); test('Graceful cancelSignal can be never aborted', async t => { const controller = new AbortController(); const subprocess = execa('graceful-send-fast.js', {cancelSignal: controller.signal, gracefulCancel: true}); t.false(await subprocess.getOneMessage()); await subprocess; }); test('Graceful cancelSignal can be already aborted but not used', async t => { const subprocess = execa('ipc-send-get.js', {cancelSignal: AbortSignal.abort(foobarString), gracefulCancel: true, forceKillAfterDelay: false}); t.is(await subprocess.getOneMessage(), foobarString); await setTimeout(1e3); await subprocess.sendMessage('.'); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, [foobarString]); }); test('Graceful cancelSignal can be aborted but not used', async t => { const controller = new AbortController(); const subprocess = execa('ipc-send-get.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); t.is(await subprocess.getOneMessage(), foobarString); controller.abort(foobarString); await setTimeout(1e3); await subprocess.sendMessage(foobarString); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, [foobarString]); }); test('Graceful cancelSignal can be never aborted nor used', async t => { const controller = new AbortController(); const subprocess = execa('empty.js', {cancelSignal: controller.signal, gracefulCancel: true}); t.is(getEventListeners(controller.signal, 'abort').length, 1); await subprocess; t.is(getEventListeners(controller.signal, 'abort').length, 0); }); test('Graceful cancelSignal can be aborted twice', async t => { const controller = new AbortController(); const subprocess = execa('graceful-send-twice.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); t.false(await subprocess.getOneMessage()); controller.abort(foobarString); controller.abort('.'); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, [false, foobarString]); }); test('Graceful cancelSignal cannot be manually aborted after disconnection', async t => { const controller = new AbortController(); const subprocess = execa('empty.js', {cancelSignal: controller.signal, gracefulCancel: true}); subprocess.disconnect(); controller.abort(foobarString); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput, originalMessage} = await t.throwsAsync(subprocess); t.false(isCanceled); t.false(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, []); t.is(originalMessage, '`cancelSignal`\'s `controller.abort()` cannot be used: the subprocess has already exited or disconnected.'); }); test('Graceful cancelSignal can disconnect after being manually aborted', async t => { const controller = new AbortController(); const subprocess = execa('graceful-disconnect.js', {cancelSignal: controller.signal, gracefulCancel: true}); controller.abort(foobarString); t.is(await subprocess.getOneMessage(), foobarString); subprocess.disconnect(); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, [foobarString]); }); test('Graceful cancelSignal is automatically aborted on disconnection', async t => { const controller = new AbortController(); const subprocess = execa('graceful-send-print.js', {cancelSignal: controller.signal, gracefulCancel: true}); t.false(await subprocess.getOneMessage()); subprocess.disconnect(); const {isCanceled, isGracefullyCanceled, ipcOutput, stdout} = await subprocess; t.false(isCanceled); t.false(isGracefullyCanceled); t.deepEqual(ipcOutput, [false]); t.true(stdout.includes('Error: `cancelSignal` aborted: the parent process disconnected.')); }); test('getCancelSignal() aborts if already disconnected', async t => { const controller = new AbortController(); const subprocess = execa('graceful-print.js', {cancelSignal: controller.signal, gracefulCancel: true}); subprocess.disconnect(); const {isCanceled, isGracefullyCanceled, ipcOutput, stdout} = await subprocess; t.false(isCanceled); t.false(isGracefullyCanceled); t.deepEqual(ipcOutput, []); t.true(stdout.includes('Error: `cancelSignal` aborted: the parent process disconnected.')); }); test('getCancelSignal() fails if no IPC', async t => { const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput, stderr} = await t.throwsAsync(execa('graceful-none.js')); t.false(isCanceled); t.false(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 1); t.deepEqual(ipcOutput, []); t.true(stderr.includes('Error: `getCancelSignal()` cannot be used without setting the `cancelSignal` subprocess option.')); }); test.serial('getCancelSignal() hangs if cancelSignal without gracefulCancel', async t => { const controller = new AbortController(); const {timedOut, isCanceled, isGracefullyCanceled, signal, ipcOutput} = await t.throwsAsync(execa('graceful-wait.js', {ipc: true, cancelSignal: controller.signal, timeout: 1e3})); t.true(timedOut); t.false(isCanceled); t.false(isGracefullyCanceled); t.is(signal, 'SIGTERM'); t.deepEqual(ipcOutput, []); }); test('Subprocess cancelSignal does not keep subprocess alive', async t => { const controller = new AbortController(); const {ipcOutput} = await execa('graceful-ref.js', {cancelSignal: controller.signal, gracefulCancel: true}); t.deepEqual(ipcOutput, []); }); test('Subprocess can send a message right away', async t => { const controller = new AbortController(); const {ipcOutput} = await execa('graceful-send-string.js', {cancelSignal: controller.signal, gracefulCancel: true}); t.deepEqual(ipcOutput, [foobarString]); }); test('Subprocess can receive a message right away', async t => { const controller = new AbortController(); const {ipcOutput} = await execa('graceful-echo.js', {cancelSignal: controller.signal, gracefulCancel: true, ipcInput: foobarString}); t.deepEqual(ipcOutput, [foobarString]); }); test('getCancelSignal() can be called twice', async t => { const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(execa('graceful-twice.js', {cancelSignal: AbortSignal.abort(foobarString), gracefulCancel: true, forceKillAfterDelay: false})); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, [foobarString]); }); test('Graceful cancelSignal can use cancelSignal.onabort', async t => { const controller = new AbortController(); const subprocess = execa('graceful-listener.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); t.is(await subprocess.getOneMessage(), '.'); controller.abort(foobarString); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, ['.', foobarString]); }); test('Graceful cancelSignal abort reason cannot be directly received', async t => { const subprocess = execa('graceful-send-echo.js', {cancelSignal: AbortSignal.abort(foobarString), gracefulCancel: true, forceKillAfterDelay: false}); await setTimeout(0); await subprocess.sendMessage('.'); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, ['.', foobarString]); }); test('error.isGracefullyCanceled is always false with execaSync()', t => { const {isCanceled, isGracefullyCanceled} = execaSync('empty.js'); t.false(isCanceled); t.false(isGracefullyCanceled); }); ================================================ FILE: test/ipc/incoming.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {alwaysPass} from '../helpers/ipc.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); const testSeriesParent = async (t, buffer, filter) => { const subprocess = execa('ipc-send-many.js', [`${PARALLEL_COUNT}`], {ipc: true, buffer}); for (let index = 0; index < PARALLEL_COUNT; index += 1) { // eslint-disable-next-line no-await-in-loop t.is(await subprocess.getOneMessage({filter}), index); } const {ipcOutput} = await subprocess; if (buffer) { t.deepEqual(ipcOutput, Array.from({length: PARALLEL_COUNT}, (_, index) => index)); } }; test('subprocess.getOneMessage() can be called multiple times in a row, buffer false', testSeriesParent, false, undefined); test('subprocess.getOneMessage() can be called multiple times in a row, buffer true', testSeriesParent, true, undefined); test('subprocess.getOneMessage() can be called multiple times in a row, buffer false, filter', testSeriesParent, false, alwaysPass); test('subprocess.getOneMessage() can be called multiple times in a row, buffer true, filter', testSeriesParent, true, alwaysPass); const testSeriesSubprocess = async (t, filter) => { const subprocess = execa('ipc-print-many.js', [`${PARALLEL_COUNT}`, `${filter}`], {ipc: true}); const indexes = Array.from({length: PARALLEL_COUNT}, (_, index) => `${index}`); await Promise.all(indexes.map(index => subprocess.sendMessage(index))); const {stdout} = await subprocess; const expectedOutput = indexes.join('\n'); t.is(stdout, expectedOutput); }; test('exports.getOneMessage() can be called multiple times in a row', testSeriesSubprocess, false); test('exports.getOneMessage() can be called multiple times in a row, filter', testSeriesSubprocess, true); test('Can iterate multiple times over IPC messages in subprocess', async t => { const subprocess = execa('ipc-iterate-twice.js', {ipc: true}); t.is(await subprocess.getOneMessage(), foobarString); await subprocess.sendMessage('.'); t.is(await subprocess.getOneMessage(), '0.'); await subprocess.sendMessage(foobarString); t.is(await subprocess.getOneMessage(), foobarString); await subprocess.sendMessage('.'); t.is(await subprocess.getOneMessage(), '1.'); await subprocess.sendMessage(foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString, '0.', foobarString, '1.']); }); ================================================ FILE: test/ipc/ipc-input.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const testSuccess = async (t, options) => { const {ipcOutput} = await execa('ipc-echo.js', {ipcInput: foobarString, ...options}); t.deepEqual(ipcOutput, [foobarString]); }; test('Sends a message with the "ipcInput" option, ipc undefined', testSuccess, {}); test('Sends a message with the "ipcInput" option, ipc true', testSuccess, {ipc: true}); test('Cannot use the "ipcInput" option with "ipc" false', t => { t.throws(() => { execa('empty.js', {ipcInput: foobarString, ipc: false}); }, {message: /unless the `ipc` option is `true`/}); }); test('Cannot use the "ipcInput" option with execaSync()', t => { t.throws(() => { execaSync('empty.js', {ipcInput: foobarString}); }, {message: /The "ipcInput" option cannot be used with synchronous/}); }); test('Invalid "ipcInput" option v8 format', t => { const {message, cause} = t.throws(() => { execa('empty.js', {ipcInput() {}}); }); t.is(message, 'The `ipcInput` option is not serializable with a structured clone.'); t.is(cause.message, 'ipcInput() {} could not be cloned.'); }); test('Invalid "ipcInput" option JSON format', t => { const {message, cause} = t.throws(() => { execa('empty.js', {ipcInput: 0n, serialization: 'json'}); }); t.is(message, 'The `ipcInput` option is not serializable with JSON.'); t.is(cause.message, 'Do not know how to serialize a BigInt'); }); test('Handles "ipcInput" option during sending', async t => { const {message, cause} = await t.throwsAsync(execa('empty.js', {ipcInput: 0n})); t.true(message.includes('subprocess.sendMessage()\'s argument type is invalid: the message cannot be serialized: 0.')); t.true(cause.cause.message.includes('The "message" argument must be one of type string')); }); test.serial('Can use "ipcInput" option even if the subprocess is not listening to messages', async t => { const {ipcOutput} = await execa('empty.js', {ipcInput: foobarString}); t.deepEqual(ipcOutput, []); }); ================================================ FILE: test/ipc/outgoing.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {alwaysPass, subprocessGetOne, subprocessGetFirst} from '../helpers/ipc.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); const testSendHoldParent = async (t, getMessage, buffer, filter) => { const subprocess = execa('ipc-iterate.js', {ipc: true, buffer}); await subprocess.sendMessage(0); t.is(await subprocess.getOneMessage({filter}), 0); const messages = Array.from({length: PARALLEL_COUNT}, (_, index) => index + 1); await Promise.all([ ...messages.map(message => subprocess.sendMessage(message)), subprocess.sendMessage(foobarString), subprocess.emit('message', '.'), ]); t.is(await getMessage(subprocess, {filter}), '.'); const {ipcOutput} = await subprocess; if (buffer) { const expectedOutput = [0, '.', ...messages]; t.deepEqual(ipcOutput, expectedOutput); } }; test('Multiple parallel subprocess.sendMessage() + subprocess.getOneMessage(), buffer false', testSendHoldParent, subprocessGetOne, false, undefined); test('Multiple parallel subprocess.sendMessage() + subprocess.getOneMessage(), buffer true', testSendHoldParent, subprocessGetOne, true, undefined); test('Multiple parallel subprocess.sendMessage() + subprocess.getOneMessage(), buffer false, filter', testSendHoldParent, subprocessGetOne, false, alwaysPass); test('Multiple parallel subprocess.sendMessage() + subprocess.getOneMessage(), buffer true, filter', testSendHoldParent, subprocessGetOne, true, alwaysPass); test('Multiple parallel subprocess.sendMessage() + subprocess.getEachMessage(), buffer false', testSendHoldParent, subprocessGetFirst, false, undefined); test('Multiple parallel subprocess.sendMessage() + subprocess.getEachMessage(), buffer true', testSendHoldParent, subprocessGetFirst, true, undefined); const testSendHoldSubprocess = async (t, filter, isGetEach) => { const {ipcOutput} = await execa('ipc-iterate-back.js', [`${filter}`, `${isGetEach}`], {ipcInput: 0}); const expectedOutput = [...Array.from({length: PARALLEL_COUNT + 1}, (_, index) => index), '.']; t.deepEqual(ipcOutput, expectedOutput); }; test('Multiple parallel exports.sendMessage() + exports.getOneMessage()', testSendHoldSubprocess, false, false); test('Multiple parallel exports.sendMessage() + exports.getOneMessage(), filter', testSendHoldSubprocess, true, false); test('Multiple parallel exports.sendMessage() + exports.getEachMessage()', testSendHoldSubprocess, false, true); const testSendHoldParentSerial = async (t, getMessage, buffer, filter) => { const subprocess = execa('ipc-iterate.js', {ipc: true, buffer}); await subprocess.sendMessage(0); t.is(await subprocess.getOneMessage({filter}), 0); const promise = subprocess.sendMessage(1); subprocess.emit('message', '.'); await promise; const messages = Array.from({length: PARALLEL_COUNT}, (_, index) => index + 2); for (const message of messages) { // eslint-disable-next-line no-await-in-loop await subprocess.sendMessage(message); } await subprocess.sendMessage(foobarString); const {ipcOutput} = await subprocess; if (buffer) { const expectedOutput = [0, '.', 1, ...messages]; t.deepEqual(ipcOutput, expectedOutput); } }; test('Multiple serial subprocess.sendMessage() + subprocess.getOneMessage(), buffer false', testSendHoldParentSerial, subprocessGetOne, false, undefined); test('Multiple serial subprocess.sendMessage() + subprocess.getOneMessage(), buffer true', testSendHoldParentSerial, subprocessGetOne, true, undefined); test('Multiple serial subprocess.sendMessage() + subprocess.getOneMessage(), buffer false, filter', testSendHoldParentSerial, subprocessGetOne, false, alwaysPass); test('Multiple serial subprocess.sendMessage() + subprocess.getOneMessage(), buffer true, filter', testSendHoldParentSerial, subprocessGetOne, true, alwaysPass); test('Multiple serial subprocess.sendMessage() + subprocess.getEachMessage(), buffer false', testSendHoldParentSerial, subprocessGetFirst, false, undefined); test('Multiple serial subprocess.sendMessage() + subprocess.getEachMessage(), buffer true', testSendHoldParentSerial, subprocessGetFirst, true, undefined); const testSendHoldSubprocessSerial = async (t, filter, isGetEach) => { const {ipcOutput} = await execa('ipc-iterate-back-serial.js', [`${filter}`, `${isGetEach}`], {ipcInput: 0}); const expectedOutput = [...Array.from({length: PARALLEL_COUNT + 2}, (_, index) => index), '.']; t.deepEqual(ipcOutput, expectedOutput); }; test('Multiple serial exports.sendMessage() + exports.getOneMessage()', testSendHoldSubprocessSerial, false, false); test('Multiple serial exports.sendMessage() + exports.getOneMessage(), filter', testSendHoldSubprocessSerial, true, false); test('Multiple serial exports.sendMessage() + exports.getEachMessage()', testSendHoldSubprocessSerial, false, true); ================================================ FILE: test/ipc/pending.js ================================================ import {once} from 'node:events'; import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const testBufferInitial = async (t, buffer) => { const subprocess = execa('ipc-echo-wait.js', {buffer, ipcInput: foobarString}); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? [foobarString] : []); }; test('Buffers initial message to subprocess, buffer false', testBufferInitial, false); test('Buffers initial message to subprocess, buffer true', testBufferInitial, true); const testBufferInitialSend = async (t, buffer) => { const subprocess = execa('ipc-send-echo-wait.js', {buffer, ipcInput: foobarString}); t.is(await subprocess.getOneMessage(), '.'); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? ['.', foobarString] : []); }; test('sendMessage() does not empty the initial message buffering, buffer false', testBufferInitialSend, false); test('sendMessage() does not empty the initial message buffering, buffer true', testBufferInitialSend, true); const testBufferInitialStrict = async (t, buffer) => { const subprocess = execa('ipc-send-echo-strict.js', {buffer, ipcInput: foobarString}); t.is(await subprocess.getOneMessage(), '.'); await setTimeout(1e3); const promise = subprocess.getOneMessage(); await subprocess.sendMessage('..'); t.is(await promise, '..'); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? ['.', '..'] : []); }; test('sendMessage() with "strict" empties the initial message buffering, buffer false', testBufferInitialStrict, false); test('sendMessage() with "strict" empties the initial message buffering, buffer true', testBufferInitialStrict, true); const testNoBufferInitial = async (t, buffer) => { const subprocess = execa('ipc-send-print.js', {ipc: true, buffer}); const [chunk] = await once(subprocess.stdout, 'data'); t.is(chunk.toString(), '.'); await setTimeout(1e3); t.is(await Promise.race([setTimeout(0), subprocess.getOneMessage()]), undefined); await subprocess.sendMessage('.'); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? [foobarString] : []); }; test.serial('Does not buffer initial message to current process, buffer false', testNoBufferInitial, false); test.serial('Does not buffer initial message to current process, buffer true', testNoBufferInitial, true); const testReplay = async (t, buffer) => { const subprocess = execa('ipc-replay.js', {buffer, ipcInput: foobarString}); t.is(await subprocess.getOneMessage(), foobarString); await subprocess.sendMessage('.'); await setTimeout(2e3); await subprocess.sendMessage(foobarString); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? [foobarString, foobarString] : []); }; test.serial('Does not replay missed messages in subprocess, buffer false', testReplay, false); test.serial('Does not replay missed messages in subprocess, buffer true', testReplay, true); const testFastSend = async (t, buffer) => { const subprocess = execa('ipc-send-native.js', {ipc: true, buffer}); t.is(await subprocess.getOneMessage(), '.'); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? ['.'] : []); }; test('Subprocess can send messages right away, buffer false', testFastSend, false); test('Subprocess can send messages right away, buffer true', testFastSend, true); ================================================ FILE: test/ipc/reference.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); const testReference = async (t, fixtureName) => { const {timedOut} = await t.throwsAsync(execa(fixtureName, {ipc: true, timeout: 1e3})); t.true(timedOut); }; test('exports.getOneMessage() keeps the subprocess alive', testReference, 'ipc-get-ref.js'); test('exports.getEachMessage() keeps the subprocess alive', testReference, 'ipc-iterate-ref.js'); const testUnreference = async (t, fixtureName) => { const {ipcOutput} = await execa(fixtureName, {ipc: true}); t.deepEqual(ipcOutput, []); }; test('exports.getOneMessage() does not keep the subprocess alive, reference false', testUnreference, 'ipc-get-unref.js'); test('exports.getEachMessage() does not keep the subprocess alive, reference false', testUnreference, 'ipc-iterate-unref.js'); test('exports.sendMessage() keeps the subprocess alive', async t => { const {ipcOutput} = await execa('ipc-send-repeat.js', [`${PARALLEL_COUNT}`], {ipc: true}); const expectedOutput = Array.from({length: PARALLEL_COUNT}, (_, index) => index); t.deepEqual(ipcOutput, expectedOutput); }); test('process.send() keeps the subprocess alive', async t => { const {ipcOutput, stdout} = await execa('ipc-process-send.js', {ipc: true}); t.deepEqual(ipcOutput, [foobarString]); t.is(stdout, '.'); }); test('process.send() keeps the subprocess alive, after getOneMessage()', async t => { const {ipcOutput, stdout} = await execa('ipc-process-send-get.js', {ipcInput: 0}); t.deepEqual(ipcOutput, [foobarString]); t.is(stdout, '.'); }); test('process.send() keeps the subprocess alive, after sendMessage()', async t => { const {ipcOutput, stdout} = await execa('ipc-process-send-send.js', {ipc: true}); t.deepEqual(ipcOutput, ['.', foobarString]); t.is(stdout, '.'); }); test('process.once("message") keeps the subprocess alive', async t => { const subprocess = execa('ipc-once-message.js', {ipc: true}); t.is(await subprocess.getOneMessage(), '.'); await subprocess.sendMessage(foobarString); const {ipcOutput, stdout} = await subprocess; t.deepEqual(ipcOutput, ['.']); t.is(stdout, foobarString); }); test('process.once("message") keeps the subprocess alive, after sendMessage()', async t => { const subprocess = execa('ipc-once-message-send.js', {ipc: true}); t.is(await subprocess.getOneMessage(), '.'); await subprocess.sendMessage(foobarString); const {ipcOutput, stdout} = await subprocess; t.deepEqual(ipcOutput, ['.']); t.is(stdout, foobarString); }); test('process.once("message") keeps the subprocess alive, after getOneMessage()', async t => { const subprocess = execa('ipc-once-message-get.js', {ipc: true}); await subprocess.sendMessage('.'); t.is(await subprocess.getOneMessage(), '.'); await subprocess.sendMessage(foobarString); const {ipcOutput, stdout} = await subprocess; t.deepEqual(ipcOutput, ['.']); t.is(stdout, foobarString); }); test('process.once("disconnect") keeps the subprocess alive', async t => { const subprocess = execa('ipc-once-disconnect.js', {ipc: true}); t.is(await subprocess.getOneMessage(), '.'); subprocess.disconnect(); const {ipcOutput, stdout} = await subprocess; t.deepEqual(ipcOutput, ['.']); t.is(stdout, '.'); }); test('process.once("disconnect") keeps the subprocess alive, after sendMessage()', async t => { const subprocess = execa('ipc-once-disconnect-send.js', {ipc: true}); t.is(await subprocess.getOneMessage(), '.'); t.is(await subprocess.getOneMessage(), '.'); subprocess.disconnect(); const {ipcOutput, stdout} = await subprocess; t.deepEqual(ipcOutput, ['.', '.']); t.is(stdout, '.'); }); test('process.once("disconnect") does not keep the subprocess alive, after getOneMessage()', async t => { const subprocess = execa('ipc-once-disconnect-get.js', {ipc: true}); await subprocess.sendMessage('.'); t.is(await subprocess.getOneMessage(), '.'); subprocess.disconnect(); const {ipcOutput, stdout} = await subprocess; t.deepEqual(ipcOutput, ['.']); t.is(stdout, '.'); }); test('Can call subprocess.disconnect() right away', async t => { const subprocess = execa('ipc-send.js', {ipc: true}); subprocess.disconnect(); t.is(subprocess.channel, null); await t.throwsAsync(subprocess.getOneMessage(), { message: /subprocess.getOneMessage\(\) could not complete/, }); await t.throwsAsync(subprocess, { message: /Error: sendMessage\(\) cannot be used/, }); }); test('Can call process.disconnect() right away', async t => { const {stdout, stderr} = await t.throwsAsync(execa('ipc-disconnect-get.js', {ipc: true})); t.is(stdout, 'null'); t.true(stderr.includes('Error: getOneMessage() cannot be used')); }); ================================================ FILE: test/ipc/send.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; import {mockSendIoError} from '../helpers/ipc.js'; setFixtureDirectory(); test('Can exchange IPC messages', async t => { const subprocess = execa('ipc-echo.js', {ipc: true}); await subprocess.sendMessage(foobarString); t.is(await subprocess.getOneMessage(), foobarString); await subprocess; }); test.serial('Can exchange IPC messages under heavy load', async t => { await Promise.all( Array.from({length: PARALLEL_COUNT}, async (_, index) => { const subprocess = execa('ipc-echo.js', {ipc: true}); await subprocess.sendMessage(index); t.is(await subprocess.getOneMessage(), index); await subprocess; }), ); }); test('The "serialization" option defaults to "advanced"', async t => { const subprocess = execa('ipc-echo.js', {ipc: true}); await subprocess.sendMessage([0n]); const message = await subprocess.getOneMessage(); t.is(message[0], 0n); await subprocess; }); test('Can use "serialization: json" option', async t => { const subprocess = execa('ipc-echo.js', {ipc: true, serialization: 'json'}); const date = new Date(); await subprocess.sendMessage(date); t.is(await subprocess.getOneMessage(), date.toJSON()); await subprocess; }); test('Validates JSON payload with serialization: "json"', async t => { const subprocess = execa('ipc-echo.js', {ipc: true, serialization: 'json'}); await t.throwsAsync(subprocess.sendMessage([0n]), {message: /serialize a BigInt/}); await t.throwsAsync(subprocess); }); const BIG_PAYLOAD_SIZE = '.'.repeat(1e6); test('Handles backpressure', async t => { const subprocess = execa('ipc-iterate.js', {ipc: true}); await subprocess.sendMessage(BIG_PAYLOAD_SIZE); t.true(subprocess.send(foobarString)); t.is(await subprocess.getOneMessage(), BIG_PAYLOAD_SIZE); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [BIG_PAYLOAD_SIZE]); }); test('Disconnects IPC on exports.sendMessage() error', async t => { const subprocess = execa('ipc-get-send-get.js', ['false'], {ipc: true}); await subprocess.sendMessage(foobarString); t.is(await subprocess.getOneMessage(), foobarString); const {message, cause} = await t.throwsAsync(subprocess.sendMessage(0n)); t.is(message, 'subprocess.sendMessage()\'s argument type is invalid: the message cannot be serialized: 0.'); t.true(cause.message.includes('The "message" argument must be one of type string')); const {exitCode, isTerminated, stderr} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(stderr.includes('Error: getOneMessage() could not complete')); }); test('Disconnects IPC on subprocess.sendMessage() error', async t => { const subprocess = execa('ipc-send-error.js', {ipc: true}); const ipcError = await t.throwsAsync(subprocess.getOneMessage()); t.true(ipcError.message.includes('subprocess.getOneMessage() could not complete')); const {exitCode, isTerminated, stderr} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(stderr.includes('Error: sendMessage()\'s argument type is invalid: the message cannot be serialized: 0.')); t.true(stderr.includes('The "message" argument must be one of type string')); }); // EPIPE happens based on timing conditions, so we must repeat it until it happens const findEpipeError = async t => { // eslint-disable-next-line no-constant-condition while (true) { // eslint-disable-next-line no-await-in-loop const error = await t.throwsAsync(getEpipeError()); if (error.cause?.code === 'EPIPE') { return error; } } }; const getEpipeError = async () => { const subprocess = execa('delay.js', ['0'], {ipc: true}); // eslint-disable-next-line no-constant-condition while (true) { // eslint-disable-next-line no-await-in-loop await subprocess.sendMessage('.'); } }; test.serial('Can send messages while the subprocess is closing', async t => { const {message} = await findEpipeError(t); t.is(message, 'subprocess.sendMessage() cannot be used: the subprocess is disconnecting.'); }); test('subprocess.sendMessage() handles I/O errors', async t => { const subprocess = execa('ipc-echo.js', {ipc: true}); const error = mockSendIoError(subprocess); t.is(await t.throwsAsync(subprocess.sendMessage('.')), error); const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(message.includes('Error: getOneMessage()')); t.deepEqual(ipcOutput, []); }); test('Does not hold message events on I/O errors', async t => { const subprocess = execa('ipc-echo.js', {ipc: true}); const error = mockSendIoError(subprocess); const promise = subprocess.sendMessage('.'); subprocess.emit('message', '.'); t.is(await t.throwsAsync(promise), error); const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(message.includes('Error: getOneMessage()')); t.deepEqual(ipcOutput, ['.']); }); test('exports.sendMessage() handles I/O errors', async t => { const {exitCode, isTerminated, message, ipcOutput} = await t.throwsAsync(execa('ipc-send-io-error.js', {ipc: true})); t.is(exitCode, 1); t.false(isTerminated); t.true(message.includes(`Error: ${foobarString}`)); t.deepEqual(ipcOutput, []); }); ================================================ FILE: test/ipc/strict.js ================================================ import {once} from 'node:events'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {assertMaxListeners} from '../helpers/listeners.js'; import {subprocessGetOne, subprocessGetFirst, mockSendIoError} from '../helpers/ipc.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); const testStrictSuccessParentOne = async (t, buffer) => { const subprocess = execa('ipc-echo.js', {ipc: true, buffer}); await subprocess.sendMessage(foobarString, {strict: true}); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? [foobarString] : []); }; test('subprocess.sendMessage() "strict" succeeds if the subprocess uses exports.getOneMessage(), buffer false', testStrictSuccessParentOne, false); test('subprocess.sendMessage() "strict" succeeds if the subprocess uses exports.getOneMessage(), buffer true', testStrictSuccessParentOne, true); const testStrictSuccessParentEach = async (t, buffer) => { const subprocess = execa('ipc-iterate.js', {ipc: true, buffer}); await subprocess.sendMessage('.', {strict: true}); t.is(await subprocess.getOneMessage(), '.'); await subprocess.sendMessage(foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? ['.'] : []); }; test('subprocess.sendMessage() "strict" succeeds if the subprocess uses exports.getEachMessage(), buffer false', testStrictSuccessParentEach, false); test('subprocess.sendMessage() "strict" succeeds if the subprocess uses exports.getEachMessage(), buffer true', testStrictSuccessParentEach, true); const testStrictMissingParent = async (t, buffer) => { const subprocess = execa('ipc-echo-twice.js', {ipcInput: foobarString, buffer}); const promise = subprocess.getOneMessage(); const secondPromise = subprocess.sendMessage(foobarString, {strict: true}); const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); t.is(message, 'subprocess.sendMessage() failed: the subprocess is not listening to incoming messages.'); t.is(await promise, foobarString); await secondPromise; const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? [foobarString, foobarString] : []); }; test('subprocess.sendMessage() "strict" fails if the subprocess is not listening, buffer false', testStrictMissingParent, false); test('subprocess.sendMessage() "strict" fails if the subprocess is not listening, buffer true', testStrictMissingParent, true); const testStrictExit = async (t, buffer) => { const subprocess = execa('ipc-send.js', {ipc: true, buffer}); const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); t.is(message, 'subprocess.sendMessage() failed: the subprocess exited without listening to incoming messages.'); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? [foobarString] : []); }; test('subprocess.sendMessage() "strict" fails if the subprocess exits, buffer false', testStrictExit, false); test('subprocess.sendMessage() "strict" fails if the subprocess exits, buffer true', testStrictExit, true); const testStrictSuccessSubprocess = async (t, getMessage, buffer) => { const subprocess = execa('ipc-send-strict.js', {ipc: true, buffer}); t.is(await getMessage(subprocess), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, buffer ? [foobarString] : []); }; test('exports.sendMessage() "strict" succeeds if the current process uses subprocess.getOneMessage(), buffer false', testStrictSuccessSubprocess, subprocessGetOne, false); test('exports.sendMessage() "strict" succeeds if the current process uses subprocess.getOneMessage(), buffer true', testStrictSuccessSubprocess, subprocessGetOne, true); test('exports.sendMessage() "strict" succeeds if the current process uses subprocess.getEachMessage(), buffer false', testStrictSuccessSubprocess, subprocessGetFirst, false); test('exports.sendMessage() "strict" succeeds if the current process uses subprocess.getEachMessage(), buffer true', testStrictSuccessSubprocess, subprocessGetFirst, true); test('exports.sendMessage() "strict" succeeds if the current process uses result.ipcOutput', async t => { const {ipcOutput} = await execa('ipc-send-strict.js', {ipc: true}); t.deepEqual(ipcOutput, [foobarString]); }); test('exports.sendMessage() "strict" fails if the current process is not listening, buffer false', async t => { const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(execa('ipc-send-strict.js', {ipc: true, buffer: {ipc: false}})); t.is(exitCode, 1); t.false(isTerminated); t.true(stderr.includes('Error: sendMessage() failed: the parent process is not listening to incoming messages.')); t.deepEqual(ipcOutput, []); }); test.serial('Multiple subprocess.sendMessage() "strict" at once', async t => { const checkMaxListeners = assertMaxListeners(t); const subprocess = execa('ipc-iterate.js', {ipc: true}); const messages = Array.from({length: PARALLEL_COUNT}, (_, index) => index); await Promise.all(messages.map(message => subprocess.sendMessage(message, {strict: true}))); await subprocess.sendMessage(foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, messages); checkMaxListeners(); }); test('subprocess.sendMessage() "strict" fails if the subprocess uses once()', async t => { const subprocess = execa('ipc-once-message.js', {ipc: true}); const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); t.is(message, 'subprocess.sendMessage() failed: the subprocess exited without listening to incoming messages.'); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, ['.']); }); test('exports.sendMessage() "strict" fails if the current process uses once() and buffer false', async t => { const subprocess = execa('ipc-send-strict.js', {ipc: true, buffer: {ipc: false}}); const [message] = await once(subprocess, 'message'); t.deepEqual(message, { id: 0n, type: 'execa:ipc:request', message: foobarString, hasListeners: false, }); const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(stderr.includes('Error: sendMessage() failed: the parent process is not listening to incoming messages.')); t.deepEqual(ipcOutput, []); }); test('subprocess.sendMessage() "strict" failure disconnects', async t => { const subprocess = execa('ipc-echo-twice-wait.js', {ipcInput: foobarString}); const promise = subprocess.getOneMessage(); const secondPromise = subprocess.sendMessage(foobarString, {strict: true}); const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); t.is(message, 'subprocess.sendMessage() failed: the subprocess is not listening to incoming messages.'); t.is(await promise, foobarString); await secondPromise; const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(stderr.includes('Error: sendMessage() cannot be used: the parent process has already exited or disconnected.')); t.deepEqual(ipcOutput, [foobarString, foobarString]); }); test('exports.sendMessage() "strict" failure disconnects', async t => { const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(execa('ipc-send-strict-catch.js', {ipc: true, buffer: {ipc: false}})); t.is(exitCode, 1); t.false(isTerminated); t.true(stderr.includes('Error: sendMessage() cannot be used: the parent process has already exited or disconnected.')); t.deepEqual(ipcOutput, []); }); const testIoErrorParent = async (t, getMessage) => { const subprocess = execa('ipc-send-strict.js', {ipc: true}); const cause = mockSendIoError(subprocess); const error = await t.throwsAsync(getMessage(subprocess)); t.true(error.message.includes('subprocess.sendMessage() failed when sending an acknowledgment response to the subprocess.')); t.is(getMessage === subprocessGetOne ? error.cause : error.cause.cause, cause); const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(stderr.includes('Error: sendMessage() failed: the parent process exited without listening to incoming messages.')); t.deepEqual(ipcOutput, []); }; test('subprocess.getOneMessage() acknowledgment I/O error', testIoErrorParent, subprocessGetOne); test('subprocess.getEachMessage() acknowledgment I/O error', testIoErrorParent, subprocessGetFirst); const testIoErrorSubprocess = async (t, fixtureName) => { const subprocess = execa(fixtureName, {ipc: true}); const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); t.is(message, 'subprocess.sendMessage() failed: the subprocess exited without listening to incoming messages.'); const {exitCode, isTerminated, stdout, stderr, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.is(stdout, ''); t.true(stderr.includes('Error: sendMessage() failed when sending an acknowledgment response to the parent process.')); t.true(stderr.includes(`Error: ${foobarString}`)); t.deepEqual(ipcOutput, []); }; test('exports.getOneMessage() acknowledgment I/O error', testIoErrorSubprocess, 'ipc-get-io-error.js'); test('exports.getEachMessage() acknowledgment I/O error', testIoErrorSubprocess, 'ipc-iterate-io-error.js'); test('Opposite sendMessage() "strict", buffer true', async t => { const subprocess = execa('ipc-send-strict-get.js', {ipc: true}); await subprocess.sendMessage(foobarString, {strict: true}); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString, foobarString]); }); test('Opposite sendMessage() "strict", current process listening, buffer false', async t => { const subprocess = execa('ipc-send-strict-get.js', {ipc: true, buffer: {ipc: false}}); const [message] = await Promise.all([ subprocess.getOneMessage(), subprocess.sendMessage(foobarString, {strict: true}), ]); t.is(message, foobarString); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, []); }); test('Opposite sendMessage() "strict", subprocess listening, buffer false', async t => { const subprocess = execa('ipc-send-strict-listen.js', {ipc: true, buffer: {ipc: false}}); await subprocess.sendMessage(foobarString, {strict: true}); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, []); }); test('Opposite sendMessage() "strict", not listening, buffer false', async t => { const subprocess = execa('ipc-send-strict.js', {ipc: true, buffer: {ipc: false}}); const {message} = await t.throwsAsync(subprocess.sendMessage(foobarString, {strict: true})); t.true(message.startsWith('subprocess.sendMessage() failed: the subprocess is sending a message too, instead of listening to incoming messages.')); const {exitCode, isTerminated, stderr, ipcOutput} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.false(isTerminated); t.true(stderr.includes('Error: sendMessage() failed: the parent process is sending a message too, instead of listening to incoming messages.')); t.deepEqual(ipcOutput, []); }); ================================================ FILE: test/ipc/validation.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {getStdio} from '../helpers/stdio.js'; setFixtureDirectory(); const stdioIpc = getStdio(3, 'ipc'); const testRequiredIpcSubprocess = async (t, methodName, options) => { const subprocess = execa('empty.js', options); const {message} = await t.throws(() => subprocess[methodName]()); t.true(message.includes(`subprocess.${methodName}() can only be used`)); await subprocess; }; test('Cannot use subprocess.sendMessage() without ipc option', testRequiredIpcSubprocess, 'sendMessage', {}); test('Cannot use subprocess.sendMessage() with ipc: false', testRequiredIpcSubprocess, 'sendMessage', {ipc: false}); test('Cannot use subprocess.sendMessage() with stdio: [..., "ipc"]', testRequiredIpcSubprocess, 'sendMessage', stdioIpc); test('Cannot use subprocess.getOneMessage() without ipc option', testRequiredIpcSubprocess, 'getOneMessage', {}); test('Cannot use subprocess.getOneMessage() with ipc: false', testRequiredIpcSubprocess, 'getOneMessage', {ipc: false}); test('Cannot use subprocess.getOneMessage() with stdio: [..., "ipc"]', testRequiredIpcSubprocess, 'getOneMessage', stdioIpc); test('Cannot use subprocess.getEachMessage() without ipc option', testRequiredIpcSubprocess, 'getEachMessage', {}); test('Cannot use subprocess.getEachMessage() with ipc: false', testRequiredIpcSubprocess, 'getEachMessage', {ipc: false}); test('Cannot use subprocess.getEachMessage() with stdio: [..., "ipc"]', testRequiredIpcSubprocess, 'getEachMessage', stdioIpc); const testRequiredIpcExports = async (t, methodName, options) => { const {message} = await t.throwsAsync(execa('ipc-any.js', [methodName], options)); t.true(message.includes(`${methodName}() can only be used`)); }; test('Cannot use exports.sendMessage() without ipc option', testRequiredIpcExports, 'sendMessage', {}); test('Cannot use exports.sendMessage() with ipc: false', testRequiredIpcExports, 'sendMessage', {ipc: false}); test('Cannot use exports.getOneMessage() without ipc option', testRequiredIpcExports, 'getOneMessage', {}); test('Cannot use exports.getOneMessage() with ipc: false', testRequiredIpcExports, 'getOneMessage', {ipc: false}); test('Cannot use exports.getEachMessage() without ipc option', testRequiredIpcExports, 'getEachMessage', {}); test('Cannot use exports.getEachMessage() with ipc: false', testRequiredIpcExports, 'getEachMessage', {ipc: false}); const testPostDisconnection = async (t, methodName) => { const subprocess = execa('empty.js', {ipc: true}); await subprocess; const {message} = t.throws(() => subprocess[methodName](foobarString)); t.true(message.includes(`subprocess.${methodName}() cannot be used`)); }; test('subprocess.sendMessage() after disconnection', testPostDisconnection, 'sendMessage'); test('subprocess.getOneMessage() after disconnection', testPostDisconnection, 'getOneMessage'); test('subprocess.getEachMessage() after disconnection', testPostDisconnection, 'getEachMessage'); const testPostDisconnectionSubprocess = async (t, methodName) => { const subprocess = execa('ipc-disconnect.js', [methodName], {ipc: true}); subprocess.disconnect(); const {message} = await t.throwsAsync(subprocess); t.true(message.includes(`${methodName}() cannot be used`)); }; test('exports.sendMessage() after disconnection', testPostDisconnectionSubprocess, 'sendMessage'); test('exports.getOneMessage() after disconnection', testPostDisconnectionSubprocess, 'getOneMessage'); test('exports.getEachMessage() after disconnection', testPostDisconnectionSubprocess, 'getEachMessage'); const INVALID_TYPE_MESSAGE = 'The "message" argument must be one of type string'; const UNDEFINED_MESSAGE = 'The "message" argument must be specified'; const CLONE_MESSAGE = 'could not be cloned'; const CYCLE_MESSAGE = 'Converting circular structure to JSON'; const MAX_CALL_STACK_MESSAGE = 'Maximum call stack size exceeded'; const testInvalidPayload = async (t, serialization, message, expectedMessage) => { const subprocess = execa('empty.js', {ipc: true, serialization}); const error = await t.throwsAsync(subprocess.sendMessage(message)); t.true(error.message.includes('subprocess.sendMessage()\'s argument type is invalid: the message cannot be serialized')); t.true(error.cause.message.includes(expectedMessage)); await subprocess; }; const cycleObject = {}; cycleObject.self = cycleObject; const toJsonCycle = {toJSON: () => ({test: true, toJsonCycle})}; test('subprocess.sendMessage() cannot send undefined', testInvalidPayload, 'advanced', undefined, UNDEFINED_MESSAGE); test('subprocess.sendMessage() cannot send bigints', testInvalidPayload, 'advanced', 0n, INVALID_TYPE_MESSAGE); test('subprocess.sendMessage() cannot send symbols', testInvalidPayload, 'advanced', Symbol('test'), INVALID_TYPE_MESSAGE); test('subprocess.sendMessage() cannot send functions', testInvalidPayload, 'advanced', () => {}, INVALID_TYPE_MESSAGE); test('subprocess.sendMessage() cannot send promises', testInvalidPayload, 'advanced', Promise.resolve(), CLONE_MESSAGE); test('subprocess.sendMessage() cannot send proxies', testInvalidPayload, 'advanced', new Proxy({}, {}), CLONE_MESSAGE); test('subprocess.sendMessage() cannot send Intl', testInvalidPayload, 'advanced', new Intl.Collator(), CLONE_MESSAGE); test('subprocess.sendMessage() cannot send undefined, JSON', testInvalidPayload, 'json', undefined, UNDEFINED_MESSAGE); test('subprocess.sendMessage() cannot send bigints, JSON', testInvalidPayload, 'json', 0n, INVALID_TYPE_MESSAGE); test('subprocess.sendMessage() cannot send symbols, JSON', testInvalidPayload, 'json', Symbol('test'), INVALID_TYPE_MESSAGE); test('subprocess.sendMessage() cannot send functions, JSON', testInvalidPayload, 'json', () => {}, INVALID_TYPE_MESSAGE); test('subprocess.sendMessage() cannot send cycles, JSON', testInvalidPayload, 'json', cycleObject, CYCLE_MESSAGE); test('subprocess.sendMessage() cannot send cycles in toJSON(), JSON', testInvalidPayload, 'json', toJsonCycle, MAX_CALL_STACK_MESSAGE); test('exports.sendMessage() validates payload', async t => { const subprocess = execa('ipc-echo-item.js', {ipc: true}); await subprocess.sendMessage([undefined]); const {message} = await t.throwsAsync(subprocess); t.true(message.includes('Error: sendMessage()\'s argument type is invalid: the message cannot be serialized')); t.true(message.includes(UNDEFINED_MESSAGE)); }); ================================================ FILE: test/methods/bind.js ================================================ import path from 'node:path'; import test from 'ava'; import { execa, execaSync, execaNode, $, } from '../../index.js'; import {foobarString, foobarUppercase} from '../helpers/input.js'; import {uppercaseGenerator} from '../helpers/generator.js'; import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const NOOP_PATH = path.join(FIXTURES_DIRECTORY, 'noop.js'); const PRINT_ENV_PATH = path.join(FIXTURES_DIRECTORY, 'environment.js'); const testBindOptions = async (t, execaMethod) => { const {stdout} = await execaMethod({stripFinalNewline: false})(NOOP_PATH, [foobarString]); t.is(stdout, `${foobarString}\n`); }; test('execa() can bind options', testBindOptions, execa); test('execaNode() can bind options', testBindOptions, execaNode); test('$ can bind options', testBindOptions, $); const testBindOptionsSync = (t, execaMethod) => { const {stdout} = execaMethod({stripFinalNewline: false})(NOOP_PATH, [foobarString]); t.is(stdout, `${foobarString}\n`); }; test('execaSync() can bind options', testBindOptionsSync, execaSync); test('$.sync can bind options', testBindOptionsSync, $.sync); const testBindPriority = async (t, execaMethod) => { const {stdout} = await execaMethod({stripFinalNewline: false})(NOOP_PATH, [foobarString], {stripFinalNewline: true}); t.is(stdout, foobarString); }; test('execa() bound options have lower priority', testBindPriority, execa); test('execaSync() bound options have lower priority', testBindPriority, execaSync); test('execaNode() bound options have lower priority', testBindPriority, execaNode); test('$ bound options have lower priority', testBindPriority, $); test('$.sync bound options have lower priority', testBindPriority, $.sync); const testBindUndefined = async (t, execaMethod) => { const {stdout} = await execaMethod({stripFinalNewline: false})(NOOP_PATH, [foobarString], {stripFinalNewline: undefined}); t.is(stdout, foobarString); }; test('execa() undefined options use default value', testBindUndefined, execa); test('execaSync() undefined options use default value', testBindUndefined, execaSync); test('execaNode() undefined options use default value', testBindUndefined, execaNode); test('$ undefined options use default value', testBindUndefined, $); test('$.sync undefined options use default value', testBindUndefined, $.sync); const testMergeEnv = async (t, execaMethod) => { const {stdout} = await execaMethod({env: {FOO: 'foo'}})(PRINT_ENV_PATH, {env: {BAR: 'bar'}}); t.is(stdout, 'foo\nbar'); }; test('execa() bound options are merged', testMergeEnv, execa); test('execaSync() bound options are merged', testMergeEnv, execaSync); test('execaNode() bound options are merged', testMergeEnv, execaNode); test('$ bound options are merged', testMergeEnv, $); test('$.sync bound options are merged', testMergeEnv, $.sync); const testMergeMultiple = async (t, execaMethod) => { const {stdout} = await execaMethod({env: {FOO: 'baz'}})({env: {BAR: 'bar'}})(PRINT_ENV_PATH, {env: {FOO: 'foo'}}); t.is(stdout, 'foo\nbar'); }; test('execa() bound options are merged multiple times', testMergeMultiple, execa); test('execaSync() bound options are merged multiple times', testMergeMultiple, execaSync); test('execaNode() bound options are merged multiple times', testMergeMultiple, execaNode); test('$ bound options are merged multiple times', testMergeMultiple, $); test('$.sync bound options are merged multiple times', testMergeMultiple, $.sync); const testMergeFdSpecific = async (t, execaMethod) => { const {isMaxBuffer, shortMessage} = await t.throwsAsync(execaMethod({maxBuffer: {stdout: 1}})(NOOP_PATH, [foobarString], {maxBuffer: {stderr: 100}})); t.true(isMaxBuffer); t.true(shortMessage.includes('Command\'s stdout was larger than 1')); }; test('execa() bound options merge fd-specific ones', testMergeFdSpecific, execa); test('execaNode() bound options merge fd-specific ones', testMergeFdSpecific, execaNode); test('$ bound options merge fd-specific ones', testMergeFdSpecific, $); const testMergeEnvUndefined = async (t, execaMethod) => { const {stdout} = await execaMethod({env: {FOO: 'foo'}})(PRINT_ENV_PATH, {env: {BAR: undefined}}); t.is(stdout, 'foo\nundefined'); }; test('execa() bound options are merged even if undefined', testMergeEnvUndefined, execa); test('execaSync() bound options are merged even if undefined', testMergeEnvUndefined, execaSync); test('execaNode() bound options are merged even if undefined', testMergeEnvUndefined, execaNode); test('$ bound options are merged even if undefined', testMergeEnvUndefined, $); test('$.sync bound options are merged even if undefined', testMergeEnvUndefined, $.sync); const testMergeSpecific = async (t, execaMethod) => { const {stdout} = await execaMethod({stdout: {transform: uppercaseGenerator().transform, objectMode: true}})(NOOP_PATH, {stdout: {transform: uppercaseGenerator().transform}}); t.is(stdout, foobarUppercase); }; test('execa() bound options only merge specific ones', testMergeSpecific, execa); test('execaSync() bound options only merge specific ones', testMergeSpecific, execaSync); test('execaNode() bound options only merge specific ones', testMergeSpecific, execaNode); test('$ bound options only merge specific ones', testMergeSpecific, $); test('$.sync bound options only merge specific ones', testMergeSpecific, $.sync); ================================================ FILE: test/methods/command.js ================================================ import path from 'node:path'; import test from 'ava'; import { execa, execaSync, $, execaNode, execaCommand, execaCommandSync, parseCommandString, } from '../../index.js'; import { setFixtureDirectory, FIXTURES_DIRECTORY, FIXTURES_DIRECTORY_URL, } from '../helpers/fixtures-directory.js'; import {QUOTE} from '../helpers/verbose.js'; setFixtureDirectory(); const STDIN_FIXTURE = path.join(FIXTURES_DIRECTORY, 'stdin.js'); const ECHO_FIXTURE_URL = new URL('echo.js', FIXTURES_DIRECTORY_URL); const parseAndRunCommand = command => execa`${parseCommandString(command)}`; test('execaCommand()', async t => { const {stdout} = await execaCommand('echo.js foo bar'); t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execa()', async t => { const {stdout} = await execa('echo.js', parseCommandString('foo bar')); t.is(stdout, 'foo\nbar'); }); test('execaCommandSync()', t => { const {stdout} = execaCommandSync('echo.js foo bar'); t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execaSync()', t => { const {stdout} = execaSync('echo.js', parseCommandString('foo bar')); t.is(stdout, 'foo\nbar'); }); test('execaCommand`...`', async t => { const {stdout} = await execaCommand`${'echo.js foo bar'}`; t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execa`...`', async t => { const {stdout} = await execa`${parseCommandString('echo.js foo bar')}`; t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execa`...`, only arguments', async t => { const {stdout} = await execa`echo.js ${parseCommandString('foo bar')}`; t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execa`...`, only some arguments', async t => { const {stdout} = await execa`echo.js ${'foo bar'} ${parseCommandString('foo bar')}`; t.is(stdout, 'foo bar\nfoo\nbar'); }); test('execaCommandSync`...`', t => { const {stdout} = execaCommandSync`${'echo.js foo bar'}`; t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execaSync`...`', t => { const {stdout} = execaSync`${parseCommandString('echo.js foo bar')}`; t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execaSync`...`, only arguments', t => { const {stdout} = execaSync`echo.js ${parseCommandString('foo bar')}`; t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execaSync`...`, only some arguments', t => { const {stdout} = execaSync`echo.js ${'foo bar'} ${parseCommandString('foo bar')}`; t.is(stdout, 'foo bar\nfoo\nbar'); }); test('parseCommandString() + $', async t => { const {stdout} = await $`${parseCommandString('echo.js foo bar')}`; t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + $.sync', t => { const {stdout} = $.sync`${parseCommandString('echo.js foo bar')}`; t.is(stdout, 'foo\nbar'); }); test('parseCommandString() + execaNode', async t => { const {stdout} = await execaNode(ECHO_FIXTURE_URL, parseCommandString('foo bar')); t.is(stdout, 'foo\nbar'); }); test('execaCommand(options)`...`', async t => { const {stdout} = await execaCommand({stripFinalNewline: false})`${'echo.js foo bar'}`; t.is(stdout, 'foo\nbar\n'); }); test('execaCommandSync(options)`...`', t => { const {stdout} = execaCommandSync({stripFinalNewline: false})`${'echo.js foo bar'}`; t.is(stdout, 'foo\nbar\n'); }); test('execaCommand(options)()', async t => { const {stdout} = await execaCommand({stripFinalNewline: false})('echo.js foo bar'); t.is(stdout, 'foo\nbar\n'); }); test('execaCommandSync(options)()', t => { const {stdout} = execaCommandSync({stripFinalNewline: false})('echo.js foo bar'); t.is(stdout, 'foo\nbar\n'); }); test('execaCommand().pipe(execaCommand())', async t => { const {stdout} = await execaCommand('echo.js foo bar').pipe(execaCommand(`node ${STDIN_FIXTURE}`)); t.is(stdout, 'foo\nbar'); }); test('execaCommand().pipe(...) does not use execaCommand', async t => { const {escapedCommand} = await execaCommand('echo.js foo bar').pipe(`node ${STDIN_FIXTURE}`, {reject: false}); t.true(escapedCommand.startsWith(`${QUOTE}node `)); }); test('execaCommand() bound options have lower priority', async t => { const {stdout} = await execaCommand({stripFinalNewline: false})('echo.js foo bar', {stripFinalNewline: true}); t.is(stdout, 'foo\nbar'); }); test('execaCommandSync() bound options have lower priority', t => { const {stdout} = execaCommandSync({stripFinalNewline: false})('echo.js foo bar', {stripFinalNewline: true}); t.is(stdout, 'foo\nbar'); }); const testInvalidArgumentsArray = (t, execaMethod) => { t.throws(() => execaMethod('echo', ['foo']), { message: /The command and its arguments must be passed as a single string/, }); }; test('execaCommand() must not pass an array of arguments', testInvalidArgumentsArray, execaCommand); test('execaCommandSync() must not pass an array of arguments', testInvalidArgumentsArray, execaCommandSync); const testInvalidArgumentsTemplate = (t, execaMethod) => { t.throws(() => execaMethod`echo foo`, { message: /The command and its arguments must be passed as a single string/, }); }; test('execaCommand() must not pass an array of arguments with a template string', testInvalidArgumentsTemplate, execaCommand); test('execaCommandSync() must not pass an array of arguments with a template string', testInvalidArgumentsTemplate, execaCommandSync); const testInvalidArgumentsParse = (t, command) => { t.throws(() => parseCommandString(command), { message: /The command must be a string/, }); }; test('execaCommand() must not pass a number', testInvalidArgumentsParse, 0); test('execaCommand() must not pass undefined', testInvalidArgumentsParse, undefined); test('execaCommand() must not pass null', testInvalidArgumentsParse, null); test('execaCommand() must not pass a symbol', testInvalidArgumentsParse, Symbol('test')); test('execaCommand() must not pass an object', testInvalidArgumentsParse, {}); test('execaCommand() must not pass an array', testInvalidArgumentsParse, []); const testExecaCommandOutput = async (t, command, expectedOutput, execaMethod) => { const {stdout} = await execaMethod(command); t.is(stdout, expectedOutput); }; test('execaCommand() allows escaping spaces in commands', testExecaCommandOutput, 'command\\ with\\ space.js foo bar', 'foo\nbar', execaCommand); test('execaCommand() trims', testExecaCommandOutput, ' echo.js foo bar ', 'foo\nbar', execaCommand); test('execaCommand() ignores consecutive spaces', testExecaCommandOutput, 'echo.js foo bar', 'foo\nbar', execaCommand); test('execaCommand() escapes other whitespaces', testExecaCommandOutput, 'echo.js foo\tbar', 'foo\tbar', execaCommand); test('execaCommand() allows escaping spaces', testExecaCommandOutput, 'echo.js foo\\ bar', 'foo bar', execaCommand); test('execaCommand() allows escaping backslashes before spaces', testExecaCommandOutput, 'echo.js foo\\\\ bar', 'foo\\ bar', execaCommand); test('execaCommand() allows escaping multiple backslashes before spaces', testExecaCommandOutput, 'echo.js foo\\\\\\\\ bar', 'foo\\\\\\ bar', execaCommand); test('execaCommand() allows escaping backslashes not before spaces', testExecaCommandOutput, 'echo.js foo\\bar baz', 'foo\\bar\nbaz', execaCommand); test('parseCommandString() allows escaping spaces in commands', testExecaCommandOutput, 'command\\ with\\ space.js foo bar', 'foo\nbar', parseAndRunCommand); test('parseCommandString() trims', testExecaCommandOutput, ' echo.js foo bar ', 'foo\nbar', parseAndRunCommand); test('parseCommandString() ignores consecutive spaces', testExecaCommandOutput, 'echo.js foo bar', 'foo\nbar', parseAndRunCommand); test('parseCommandString() escapes other whitespaces', testExecaCommandOutput, 'echo.js foo\tbar', 'foo\tbar', parseAndRunCommand); test('parseCommandString() allows escaping spaces', testExecaCommandOutput, 'echo.js foo\\ bar', 'foo bar', parseAndRunCommand); test('parseCommandString() allows escaping backslashes before spaces', testExecaCommandOutput, 'echo.js foo\\\\ bar', 'foo\\ bar', parseAndRunCommand); test('parseCommandString() allows escaping multiple backslashes before spaces', testExecaCommandOutput, 'echo.js foo\\\\\\\\ bar', 'foo\\\\\\ bar', parseAndRunCommand); test('parseCommandString() allows escaping backslashes not before spaces', testExecaCommandOutput, 'echo.js foo\\bar baz', 'foo\\bar\nbaz', parseAndRunCommand); test('parseCommandString() can get empty strings', t => { t.deepEqual(parseCommandString(''), []); }); test('parseCommandString() can get only whitespaces', t => { t.deepEqual(parseCommandString(' '), []); }); ================================================ FILE: test/methods/create.js ================================================ import path from 'node:path'; import test from 'ava'; import { execa, execaSync, execaNode, $, } from '../../index.js'; import {foobarString, foobarArray} from '../helpers/input.js'; import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const NOOP_PATH = path.join(FIXTURES_DIRECTORY, 'noop.js'); const testTemplate = async (t, execaMethod) => { const {stdout} = await execaMethod`${NOOP_PATH} ${foobarString}`; t.is(stdout, foobarString); }; test('execa() can use template strings', testTemplate, execa); test('execaNode() can use template strings', testTemplate, execaNode); test('$ can use template strings', testTemplate, $); const testTemplateSync = (t, execaMethod) => { const {stdout} = execaMethod`${NOOP_PATH} ${foobarString}`; t.is(stdout, foobarString); }; test('execaSync() can use template strings', testTemplateSync, execaSync); test('$.sync can use template strings', testTemplateSync, $.sync); const testTemplateOptions = async (t, execaMethod) => { const {stdout} = await execaMethod({stripFinalNewline: false})`${NOOP_PATH} ${foobarString}`; t.is(stdout, `${foobarString}\n`); }; test('execa() can use template strings with options', testTemplateOptions, execa); test('execaNode() can use template strings with options', testTemplateOptions, execaNode); test('$ can use template strings with options', testTemplateOptions, $); const testTemplateOptionsSync = (t, execaMethod) => { const {stdout} = execaMethod({stripFinalNewline: false})`${NOOP_PATH} ${foobarString}`; t.is(stdout, `${foobarString}\n`); }; test('execaSync() can use template strings with options', testTemplateOptionsSync, execaSync); test('$.sync can use template strings with options', testTemplateOptionsSync, $.sync); const testSpacedCommand = async (t, commandArguments, execaMethod) => { const {stdout} = await execaMethod('command with space.js', commandArguments); const expectedStdout = commandArguments === undefined ? '' : commandArguments.join('\n'); t.is(stdout, expectedStdout); }; test('allow commands with spaces and no array arguments', testSpacedCommand, undefined, execa); test('allow commands with spaces and array arguments', testSpacedCommand, foobarArray, execa); test('allow commands with spaces and no array arguments, execaSync', testSpacedCommand, undefined, execaSync); test('allow commands with spaces and array arguments, execaSync', testSpacedCommand, foobarArray, execaSync); test('allow commands with spaces and no array arguments, $', testSpacedCommand, undefined, $); test('allow commands with spaces and array arguments, $', testSpacedCommand, foobarArray, $); test('allow commands with spaces and no array arguments, $.sync', testSpacedCommand, undefined, $.sync); test('allow commands with spaces and array arguments, $.sync', testSpacedCommand, foobarArray, $.sync); ================================================ FILE: test/methods/main-async.js ================================================ import process from 'node:process'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const isWindows = process.platform === 'win32'; if (isWindows) { test('execa() - cmd file', async t => { const {stdout} = await execa('hello.cmd'); t.is(stdout, 'Hello World'); }); test('execa() - run cmd command', async t => { const {stdout} = await execa('cmd', ['/c', 'hello.cmd']); t.is(stdout, 'Hello World'); }); } test('execa() returns a promise with pid', async t => { const subprocess = execa('noop.js', ['foo']); t.is(typeof subprocess.pid, 'number'); await subprocess; }); ================================================ FILE: test/methods/node.js ================================================ import path from 'node:path'; import process, {version} from 'node:process'; import {pathToFileURL} from 'node:url'; import test from 'ava'; import getNode from 'get-node'; import {execa, execaSync, execaNode} from '../../index.js'; import {FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; import {identity, fullStdio} from '../helpers/stdio.js'; import {foobarString} from '../helpers/input.js'; import {getDenoNodePath} from '../helpers/file-path.js'; process.chdir(FIXTURES_DIRECTORY); const runWithNodeOption = (file, commandArguments, options) => Array.isArray(commandArguments) ? execa(file, commandArguments, {...options, node: true}) : execa(file, {...options, node: true}); const runWithNodeOptionSync = (file, commandArguments, options) => Array.isArray(commandArguments) ? execaSync(file, commandArguments, {...options, node: true}) : execaSync(file, {...options, node: true}); const runWithIpc = (file, options) => execa('node', [file], {...options, ipc: true}); const testNodeSuccess = async (t, execaMethod) => { const {exitCode, stdout} = await execaMethod('noop.js', [foobarString]); t.is(exitCode, 0); t.is(stdout, foobarString); }; test('execaNode() succeeds', testNodeSuccess, execaNode); test('The "node" option succeeds', testNodeSuccess, runWithNodeOption); test('The "node" option succeeds - sync', testNodeSuccess, runWithNodeOptionSync); test('execaNode(options) succeeds', async t => { const {stdout} = await execaNode({stripFinalNewline: false})('noop.js', [foobarString]); t.is(stdout, `${foobarString}\n`); }); test('execaNode`...` succeeds', async t => { const {stdout} = await execaNode`noop.js ${foobarString}`; t.is(stdout, foobarString); }); test('execaNode().pipe(execaNode()) succeeds', async t => { const {stdout} = await execaNode('noop.js').pipe(execaNode('--version')); t.is(stdout, version); }); test('execaNode().pipe(execa()) requires using "node"', async t => { await t.throwsAsync(execaNode('noop.js').pipe(execa('--version'))); }); test('execaNode().pipe(...) requires using "node"', async t => { await t.throwsAsync(execaNode('noop.js').pipe('--version')); }); test('execaNode().pipe`...` requires using "node"', async t => { await t.throwsAsync(execaNode('noop.js').pipe`--version`); }); test('execaNode() cannot set the "node" option to false', t => { t.throws(() => { execaNode('empty.js', {node: false}); }, {message: /The "node" option cannot be false/}); }); const testDoubleNode = (t, nodePath, execaMethod) => { t.throws(() => { execaMethod(nodePath, ['noop.js']); }, {message: /does not need to be "node"/}); }; test('Cannot use "node" as binary - execaNode()', testDoubleNode, 'node', execaNode); test('Cannot use "node" as binary - "node" option', testDoubleNode, 'node', runWithNodeOption); test('Cannot use "node" as binary - "node" option sync', testDoubleNode, 'node', runWithNodeOptionSync); test('Cannot use path to "node" as binary - execaNode()', testDoubleNode, process.execPath, execaNode); test('Cannot use path to "node" as binary - "node" option', testDoubleNode, process.execPath, runWithNodeOption); test('Cannot use path to "node" as binary - "node" option sync', testDoubleNode, process.execPath, runWithNodeOptionSync); test('Cannot use deno style nodePath as binary - execaNode()', testDoubleNode, getDenoNodePath(), execaNode); test('Cannot use deno style nodePath as binary - "node" option', testDoubleNode, getDenoNodePath(), runWithNodeOption); test('Cannot use deno style nodePath as binary - "node" option sync', testDoubleNode, getDenoNodePath(), runWithNodeOptionSync); const getNodePath = async () => { const {path} = await getNode(TEST_NODE_VERSION); return path; }; const TEST_NODE_VERSION = '16.0.0'; const testNodePath = async (t, execaMethod, mapPath) => { const nodePath = mapPath(await getNodePath()); const {stdout} = await execaMethod('--version', [], {nodePath}); t.is(stdout, `v${TEST_NODE_VERSION}`); }; test.serial('The "nodePath" option can be used - execaNode()', testNodePath, execaNode, identity); test.serial('The "nodePath" option can be a file URL - execaNode()', testNodePath, execaNode, pathToFileURL); test.serial('The "nodePath" option can be used - "node" option', testNodePath, runWithNodeOption, identity); test.serial('The "nodePath" option can be a file URL - "node" option', testNodePath, runWithNodeOption, pathToFileURL); test.serial('The "nodePath" option can be used - "node" option sync', testNodePath, runWithNodeOptionSync, identity); test.serial('The "nodePath" option can be a file URL - "node" option sync', testNodePath, runWithNodeOptionSync, pathToFileURL); const testNodePathDefault = async (t, execaMethod) => { const {stdout} = await execaMethod('--version'); t.is(stdout, process.version); }; test('The "nodePath" option defaults to the current Node.js binary - execaNode()', testNodePathDefault, execaNode); test('The "nodePath" option defaults to the current Node.js binary - "node" option', testNodePathDefault, runWithNodeOption); test('The "nodePath" option defaults to the current Node.js binary - "node" option sync', testNodePathDefault, runWithNodeOptionSync); const testNodePathInvalid = (t, execaMethod) => { t.throws(() => { execaMethod('noop.js', [], {nodePath: true}); }, {message: /The "nodePath" option must be a string or a file URL/}); }; test('The "nodePath" option must be a string or URL - execaNode()', testNodePathInvalid, execaNode); test('The "nodePath" option must be a string or URL - "node" option', testNodePathInvalid, runWithNodeOption); test('The "nodePath" option must be a string or URL - "node" option sync', testNodePathInvalid, runWithNodeOptionSync); const testFormerNodePath = (t, execaMethod) => { t.throws(() => { execaMethod('noop.js', [], {execPath: process.execPath}); }, {message: /The "execPath" option has been removed/}); }; test('The "execPath" option cannot be used - execaNode()', testFormerNodePath, execaNode); test('The "execPath" option cannot be used - "node" option', testFormerNodePath, runWithNodeOption); test('The "execPath" option cannot be used - "node" option sync', testFormerNodePath, runWithNodeOptionSync); const nodePathArguments = ['-p', ['process.env.Path || process.env.PATH']]; const testSubprocessNodePath = async (t, execaMethod, mapPath) => { const nodePath = mapPath(await getNodePath()); const {stdout} = await execaMethod(...nodePathArguments, {nodePath}); t.true(stdout.includes(TEST_NODE_VERSION)); }; test.serial('The "nodePath" option impacts the subprocess - execaNode()', testSubprocessNodePath, execaNode, identity); test.serial('The "nodePath" option impacts the subprocess - "node" option', testSubprocessNodePath, runWithNodeOption, identity); test.serial('The "nodePath" option impacts the subprocess - "node" option sync', testSubprocessNodePath, runWithNodeOptionSync, identity); const testSubprocessNodePathDefault = async (t, execaMethod) => { const {stdout} = await execaMethod(...nodePathArguments); t.true(stdout.includes(path.dirname(process.execPath))); }; test('The "nodePath" option defaults to the current Node.js binary in the subprocess - execaNode()', testSubprocessNodePathDefault, execaNode); test('The "nodePath" option defaults to the current Node.js binary in the subprocess - "node" option', testSubprocessNodePathDefault, runWithNodeOption); test('The "nodePath" option defaults to the current Node.js binary in the subprocess - "node" option sync', testSubprocessNodePathDefault, runWithNodeOptionSync); test.serial('The "nodePath" option requires "node: true" to impact the subprocess', async t => { const nodePath = await getNodePath(); const {stdout} = await execa('node', nodePathArguments.flat(), {nodePath}); t.false(stdout.includes(TEST_NODE_VERSION)); }); const testSubprocessNodePathCwd = async (t, execaMethod) => { const nodePath = await getNodePath(); const cwd = path.dirname(path.dirname(nodePath)); const relativeExecPath = path.relative(cwd, nodePath); const {stdout} = await execaMethod(...nodePathArguments, {nodePath: relativeExecPath, cwd}); t.true(stdout.includes(TEST_NODE_VERSION)); }; test.serial('The "nodePath" option is relative to "cwd" when used in the subprocess - execaNode()', testSubprocessNodePathCwd, execaNode); test.serial('The "nodePath" option is relative to "cwd" when used in the subprocess - "node" option', testSubprocessNodePathCwd, runWithNodeOption); test.serial('The "nodePath" option is relative to "cwd" when used in the subprocess - "node" option sync', testSubprocessNodePathCwd, runWithNodeOptionSync); const testCwdNodePath = async (t, execaMethod) => { const nodePath = await getNodePath(); const cwd = path.dirname(path.dirname(nodePath)); const relativeExecPath = path.relative(cwd, nodePath); const {stdout} = await execaMethod('--version', [], {nodePath: relativeExecPath, cwd}); t.is(stdout, `v${TEST_NODE_VERSION}`); }; test.serial('The "nodePath" option is relative to "cwd" - execaNode()', testCwdNodePath, execaNode); test.serial('The "nodePath" option is relative to "cwd" - "node" option', testCwdNodePath, runWithNodeOption); test.serial('The "nodePath" option is relative to "cwd" - "node" option sync', testCwdNodePath, runWithNodeOptionSync); const testDenoExecPath = async (t, execaMethod) => { const {exitCode, stdout} = await execaMethod('noop.js', [], {nodePath: getDenoNodePath()}); t.is(exitCode, 0); t.is(stdout, foobarString); }; test('The deno style "nodePath" option can be used - execaNode()', testDenoExecPath, execaNode); test('The deno style "nodePath" option can be used - "node" option', testDenoExecPath, runWithNodeOption); test('The deno style "nodePath" option can be used - "node" option sync', testDenoExecPath, runWithNodeOptionSync); const testNodeOptions = async (t, execaMethod) => { const {stdout} = await execaMethod('empty.js', [], {nodeOptions: ['--version']}); t.is(stdout, process.version); }; test('The "nodeOptions" option can be used - execaNode()', testNodeOptions, execaNode); test('The "nodeOptions" option can be used - "node" option', testNodeOptions, runWithNodeOption); test('The "nodeOptions" option can be used - "node" option sync', testNodeOptions, runWithNodeOptionSync); const spawnNestedExecaNode = (realExecArgv, fakeExecArgv, execaMethod, nodeOptions) => execa( 'node', [...realExecArgv, 'nested-node.js', fakeExecArgv, execaMethod, nodeOptions, 'noop.js', foobarString], {...fullStdio, cwd: FIXTURES_DIRECTORY}, ); const testInspectRemoval = async (t, fakeExecArgv, execaMethod) => { const {stdout, stdio} = await spawnNestedExecaNode([], fakeExecArgv, execaMethod, ''); t.is(stdout, foobarString); t.is(stdio[3], ''); }; test('The "nodeOptions" option removes --inspect without a port when defined by current process - execaNode()', testInspectRemoval, '--inspect', 'execaNode'); test('The "nodeOptions" option removes --inspect without a port when defined by current process - "node" option', testInspectRemoval, '--inspect', 'nodeOption'); test('The "nodeOptions" option removes --inspect with a port when defined by current process - execaNode()', testInspectRemoval, '--inspect=9222', 'execaNode'); test('The "nodeOptions" option removes --inspect with a port when defined by current process - "node" option', testInspectRemoval, '--inspect=9222', 'nodeOption'); test('The "nodeOptions" option removes --inspect-brk without a port when defined by current process - execaNode()', testInspectRemoval, '--inspect-brk', 'execaNode'); test('The "nodeOptions" option removes --inspect-brk without a port when defined by current process - "node" option', testInspectRemoval, '--inspect-brk', 'nodeOption'); test('The "nodeOptions" option removes --inspect-brk with a port when defined by current process - execaNode()', testInspectRemoval, '--inspect-brk=9223', 'execaNode'); test('The "nodeOptions" option removes --inspect-brk with a port when defined by current process - "node" option', testInspectRemoval, '--inspect-brk=9223', 'nodeOption'); const testInspectDifferentPort = async (t, execaMethod) => { const {stdout, stdio} = await spawnNestedExecaNode(['--inspect=9225'], '', execaMethod, '--inspect=9224'); t.is(stdout, foobarString); t.true(stdio[3].includes('Debugger listening')); }; test.serial('The "nodeOptions" option allows --inspect with a different port even when defined by current process - execaNode()', testInspectDifferentPort, 'execaNode'); test.serial('The "nodeOptions" option allows --inspect with a different port even when defined by current process - "node" option', testInspectDifferentPort, 'nodeOption'); const testInspectSamePort = async (t, execaMethod) => { const {stdout, stdio} = await spawnNestedExecaNode(['--inspect=9226'], '', execaMethod, '--inspect=9226'); t.is(stdout, foobarString); t.true(stdio[3].includes('address already in use')); }; test.serial('The "nodeOptions" option forbids --inspect with the same port when defined by current process - execaNode()', testInspectSamePort, 'execaNode'); test.serial('The "nodeOptions" option forbids --inspect with the same port when defined by current process - "node" option', testInspectSamePort, 'nodeOption'); const testIpc = async (t, execaMethod, options) => { const subprocess = execaMethod('ipc-echo.js', [], options); await subprocess.sendMessage(foobarString); t.is(await subprocess.getOneMessage(), foobarString); const {stdio} = await subprocess; t.is(stdio.length, 4); t.is(stdio[3], undefined); }; test('execaNode() adds an ipc channel', testIpc, execaNode, {}); test('The "node" option adds an ipc channel', testIpc, runWithNodeOption, {}); test('The "ipc" option adds an ipc channel', testIpc, runWithIpc, {}); test('The "ipc" option works with "stdio: \'pipe\'"', testIpc, runWithIpc, {stdio: 'pipe'}); test('The "ipc" option works with "stdio: [\'pipe\', \'pipe\', \'pipe\']"', testIpc, runWithIpc, {stdio: ['pipe', 'pipe', 'pipe']}); test('The "ipc" option works with "stdio: [\'pipe\', \'pipe\', \'pipe\', \'ipc\']"', testIpc, runWithIpc, {stdio: ['pipe', 'pipe', 'pipe', 'ipc']}); test('The "ipc" option works with "stdout: \'pipe\'"', testIpc, runWithIpc, {stdout: 'pipe'}); const NO_SEND_MESSAGE = 'sendMessage() can only be used'; test('No ipc channel is added by default', async t => { const {message, stdio} = await t.throwsAsync(execa('node', ['ipc-send.js'])); t.true(message.includes(NO_SEND_MESSAGE)); t.is(stdio.length, 3); }); const testDisableIpc = async (t, execaMethod) => { const {failed, message, stdio} = await execaMethod('ipc-send.js', [], {ipc: false, reject: false}); t.true(failed); t.true(message.includes(NO_SEND_MESSAGE)); t.is(stdio.length, 3); }; test('Can disable "ipc" - execaNode()', testDisableIpc, execaNode); test('Can disable "ipc" - "node" option', testDisableIpc, runWithNodeOption); test('Can disable "ipc" - "node" option sync', testDisableIpc, runWithNodeOptionSync); const NO_IPC_MESSAGE = /The "ipc: true" option cannot be used/; const testNoIpcSync = (t, node) => { t.throws(() => { execaSync('node', ['ipc-send.js'], {ipc: true, node}); }, {message: NO_IPC_MESSAGE}); }; test('Cannot use "ipc: true" with execaSync()', testNoIpcSync, undefined); test('Cannot use "ipc: true" with execaSync() - "node: false"', testNoIpcSync, false); test('Cannot use "ipc: true" with execaSync() - "node: true"', t => { t.throws(() => { execaSync('ipc-send.js', {ipc: true, node: true}); }, {message: NO_IPC_MESSAGE}); }); const testNoShell = async (t, execaMethod) => { const {failed, message} = await execaMethod('node --version', [], {shell: true, reject: false}); t.true(failed); t.true(message.includes('MODULE_NOT_FOUND')); }; test('Cannot use "shell: true" - execaNode()', testNoShell, execaNode); test('Cannot use "shell: true" - "node" option', testNoShell, runWithNodeOption); test('Cannot use "shell: true" - "node" option sync', testNoShell, runWithNodeOptionSync); ================================================ FILE: test/methods/override-promise.js ================================================ import test from 'ava'; // The helper module overrides Promise on import so has to be imported before `execa`. import {restorePromise} from '../helpers/override-promise.js'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; restorePromise(); setFixtureDirectory(); test('should work with third-party Promise', async t => { const {stdout} = await execa('noop.js', ['foo']); t.is(stdout, 'foo'); }); ================================================ FILE: test/methods/parameters-args.js ================================================ import test from 'ava'; import { execa, execaSync, execaCommand, execaCommandSync, execaNode, $, } from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const testInvalidArguments = async (t, execaMethod) => { t.throws(() => { execaMethod('echo', true); }, {message: /Second argument must be either/}); }; test('execa()\'s second argument must be valid', testInvalidArguments, execa); test('execaSync()\'s second argument must be valid', testInvalidArguments, execaSync); test('execaCommand()\'s second argument must be valid', testInvalidArguments, execaCommand); test('execaCommandSync()\'s second argument must be valid', testInvalidArguments, execaCommandSync); test('execaNode()\'s second argument must be valid', testInvalidArguments, execaNode); test('$\'s second argument must be valid', testInvalidArguments, $); test('$.sync\'s second argument must be valid', testInvalidArguments, $.sync); const testInvalidArgumentsItems = async (t, execaMethod) => { t.throws(() => { execaMethod('echo', [{}]); }, {message: 'Second argument must be an array of strings: [object Object]'}); }; test('execa()\'s second argument must not be objects', testInvalidArgumentsItems, execa); test('execaSync()\'s second argument must not be objects', testInvalidArgumentsItems, execaSync); test('execaCommand()\'s second argument must not be objects', testInvalidArgumentsItems, execaCommand); test('execaCommandSync()\'s second argument must not be objects', testInvalidArgumentsItems, execaCommandSync); test('execaNode()\'s second argument must not be objects', testInvalidArgumentsItems, execaNode); test('$\'s second argument must not be objects', testInvalidArgumentsItems, $); test('$.sync\'s second argument must not be objects', testInvalidArgumentsItems, $.sync); const testNullByteArgument = async (t, execaMethod) => { t.throws(() => { execaMethod('echo', ['a\0b']); }, {message: /null bytes/}); }; test('execa()\'s second argument must not include \\0', testNullByteArgument, execa); test('execaSync()\'s second argument must not include \\0', testNullByteArgument, execaSync); test('execaCommand()\'s second argument must not include \\0', testNullByteArgument, execaCommand); test('execaCommandSync()\'s second argument must not include \\0', testNullByteArgument, execaCommandSync); test('execaNode()\'s second argument must not include \\0', testNullByteArgument, execaNode); test('$\'s second argument must not include \\0', testNullByteArgument, $); test('$.sync\'s second argument must not include \\0', testNullByteArgument, $.sync); ================================================ FILE: test/methods/parameters-command.js ================================================ import path from 'node:path'; import {fileURLToPath} from 'node:url'; import test from 'ava'; import { execa, execaSync, execaCommand, execaCommandSync, execaNode, $, } from '../../index.js'; import {setFixtureDirectory, FIXTURES_DIRECTORY_URL} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const testFileUrl = async (t, execaMethod) => { const command = new URL('noop.js', FIXTURES_DIRECTORY_URL); const {stdout} = await execaMethod(command); t.is(stdout, foobarString); }; test('execa()\'s command argument can be a file URL', testFileUrl, execa); test('execaSync()\'s command argument can be a file URL', testFileUrl, execaSync); test('execaCommand()\'s command argument can be a file URL', testFileUrl, execaCommand); test('execaCommandSync()\'s command argument can be a file URL', testFileUrl, execaCommandSync); test('execaNode()\'s command argument can be a file URL', testFileUrl, execaNode); test('$\'s command argument can be a file URL', testFileUrl, $); test('$.sync\'s command argument can be a file URL', testFileUrl, $.sync); const testInvalidFileUrl = async (t, execaMethod) => { const invalidUrl = new URL('https://invalid.com'); t.throws(() => { execaMethod(invalidUrl); }, {code: 'ERR_INVALID_URL_SCHEME'}); }; test('execa()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execa); test('execaSync()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execaSync); test('execaCommand()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execaCommand); test('execaCommandSync()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execaCommandSync); test('execaNode()\'s command argument cannot be a non-file URL', testInvalidFileUrl, execaNode); test('$\'s command argument cannot be a non-file URL', testInvalidFileUrl, $); test('$.sync\'s command argument cannot be a non-file URL', testInvalidFileUrl, $.sync); const testInvalidCommand = async (t, commandArgument, execaMethod) => { t.throws(() => { execaMethod(commandArgument); }, {message: /First argument must be a string or a file URL/}); }; test('execa()\'s first argument must be defined', testInvalidCommand, undefined, execa); test('execaSync()\'s first argument must be defined', testInvalidCommand, undefined, execaSync); test('execaCommand()\'s first argument must be defined', testInvalidCommand, undefined, execaCommand); test('execaCommandSync()\'s first argument must be defined', testInvalidCommand, undefined, execaCommandSync); test('execaNode()\'s first argument must be defined', testInvalidCommand, undefined, execaNode); test('$\'s first argument must be defined', testInvalidCommand, undefined, $); test('$.sync\'s first argument must be defined', testInvalidCommand, undefined, $.sync); test('execa()\'s first argument must be valid', testInvalidCommand, true, execa); test('execaSync()\'s first argument must be valid', testInvalidCommand, true, execaSync); test('execaCommand()\'s first argument must be valid', testInvalidCommand, true, execaCommand); test('execaCommandSync()\'s first argument must be valid', testInvalidCommand, true, execaCommandSync); test('execaNode()\'s first argument must be valid', testInvalidCommand, true, execaNode); test('$\'s first argument must be valid', testInvalidCommand, true, $); test('$.sync\'s first argument must be valid', testInvalidCommand, true, $.sync); test('execa()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execa); test('execaSync()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execaSync); test('execaCommand()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execaCommand); test('execaCommandSync()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execaCommandSync); test('execaNode()\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], execaNode); test('$\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], $); test('$.sync\'s command argument must be a string or file URL', testInvalidCommand, ['command', 'arg'], $.sync); const testRelativePath = async (t, execaMethod) => { // @todo: use import.meta.dirname after dropping support for Node <20.11.0 const rootDirectory = path.basename(fileURLToPath(new URL('../..', import.meta.url))); const pathViaParentDirectory = path.join('..', rootDirectory, 'test', 'fixtures', 'noop.js'); const {stdout} = await execaMethod(pathViaParentDirectory); t.is(stdout, foobarString); }; test('execa() use relative path with \'..\' chars', testRelativePath, execa); test('execaSync() use relative path with \'..\' chars', testRelativePath, execaSync); test('execaCommand() use relative path with \'..\' chars', testRelativePath, execaCommand); test('execaCommandSync() use relative path with \'..\' chars', testRelativePath, execaCommandSync); test('execaNode() use relative path with \'..\' chars', testRelativePath, execaNode); test('$ use relative path with \'..\' chars', testRelativePath, $); test('$.sync use relative path with \'..\' chars', testRelativePath, $.sync); ================================================ FILE: test/methods/parameters-options.js ================================================ import path from 'node:path'; import test from 'ava'; import { execa, execaSync, execaCommand, execaCommandSync, execaNode, $, } from '../../index.js'; import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const NOOP_PATH = path.join(FIXTURES_DIRECTORY, 'noop.js'); const testSerializeArgument = async (t, commandArgument, execaMethod) => { const {stdout} = await execaMethod(NOOP_PATH, [commandArgument]); t.is(stdout, String(commandArgument)); }; test('execa()\'s arguments can be numbers', testSerializeArgument, 1, execa); test('execa()\'s arguments can be booleans', testSerializeArgument, true, execa); test('execa()\'s arguments can be NaN', testSerializeArgument, Number.NaN, execa); test('execa()\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, execa); test('execa()\'s arguments can be null', testSerializeArgument, null, execa); test('execa()\'s arguments can be undefined', testSerializeArgument, undefined, execa); test('execa()\'s arguments can be bigints', testSerializeArgument, 1n, execa); test('execa()\'s arguments can be symbols', testSerializeArgument, Symbol('test'), execa); test('execaSync()\'s arguments can be numbers', testSerializeArgument, 1, execaSync); test('execaSync()\'s arguments can be booleans', testSerializeArgument, true, execaSync); test('execaSync()\'s arguments can be NaN', testSerializeArgument, Number.NaN, execaSync); test('execaSync()\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, execaSync); test('execaSync()\'s arguments can be null', testSerializeArgument, null, execaSync); test('execaSync()\'s arguments can be undefined', testSerializeArgument, undefined, execaSync); test('execaSync()\'s arguments can be bigints', testSerializeArgument, 1n, execaSync); test('execaSync()\'s arguments can be symbols', testSerializeArgument, Symbol('test'), execaSync); test('execaNode()\'s arguments can be numbers', testSerializeArgument, 1, execaNode); test('execaNode()\'s arguments can be booleans', testSerializeArgument, true, execaNode); test('execaNode()\'s arguments can be NaN', testSerializeArgument, Number.NaN, execaNode); test('execaNode()\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, execaNode); test('execaNode()\'s arguments can be null', testSerializeArgument, null, execaNode); test('execaNode()\'s arguments can be undefined', testSerializeArgument, undefined, execaNode); test('execaNode()\'s arguments can be bigints', testSerializeArgument, 1n, execaNode); test('execaNode()\'s arguments can be symbols', testSerializeArgument, Symbol('test'), execaNode); test('$\'s arguments can be numbers', testSerializeArgument, 1, $); test('$\'s arguments can be booleans', testSerializeArgument, true, $); test('$\'s arguments can be NaN', testSerializeArgument, Number.NaN, $); test('$\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, $); test('$\'s arguments can be null', testSerializeArgument, null, $); test('$\'s arguments can be undefined', testSerializeArgument, undefined, $); test('$\'s arguments can be bigints', testSerializeArgument, 1n, $); test('$\'s arguments can be symbols', testSerializeArgument, Symbol('test'), $); test('$.sync\'s arguments can be numbers', testSerializeArgument, 1, $.sync); test('$.sync\'s arguments can be booleans', testSerializeArgument, true, $.sync); test('$.sync\'s arguments can be NaN', testSerializeArgument, Number.NaN, $.sync); test('$.sync\'s arguments can be Infinity', testSerializeArgument, Number.POSITIVE_INFINITY, $.sync); test('$.sync\'s arguments can be null', testSerializeArgument, null, $.sync); test('$.sync\'s arguments can be undefined', testSerializeArgument, undefined, $.sync); test('$.sync\'s arguments can be bigints', testSerializeArgument, 1n, $.sync); test('$.sync\'s arguments can be symbols', testSerializeArgument, Symbol('test'), $.sync); const testInvalidOptions = async (t, execaMethod) => { t.throws(() => { execaMethod('echo', [], new Map()); }, {message: /Last argument must be an options object/}); }; test('execa()\'s third argument must be a plain object', testInvalidOptions, execa); test('execaSync()\'s third argument must be a plain object', testInvalidOptions, execaSync); test('execaCommand()\'s third argument must be a plain object', testInvalidOptions, execaCommand); test('execaCommandSync()\'s third argument must be a plain object', testInvalidOptions, execaCommandSync); test('execaNode()\'s third argument must be a plain object', testInvalidOptions, execaNode); test('$\'s third argument must be a plain object', testInvalidOptions, $); test('$.sync\'s third argument must be a plain object', testInvalidOptions, $.sync); ================================================ FILE: test/methods/promise.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); test('promise methods are not enumerable', t => { const descriptors = Object.getOwnPropertyDescriptors(execa('noop.js')); t.false(descriptors.then.enumerable); t.false(descriptors.catch.enumerable); t.false(descriptors.finally.enumerable); }); test('finally function is executed on success', async t => { let isCalled = false; const {stdout} = await execa('noop.js', ['foo']).finally(() => { isCalled = true; }); t.is(isCalled, true); t.is(stdout, 'foo'); }); test('finally function is executed on failure', async t => { let isError = false; const {stdout, stderr} = await t.throwsAsync(execa('exit.js', ['2']).finally(() => { isError = true; })); t.is(isError, true); t.is(typeof stdout, 'string'); t.is(typeof stderr, 'string'); }); test('throw in finally function bubbles up on success', async t => { const {message} = await t.throwsAsync(execa('noop.js', ['foo']).finally(() => { throw new Error('called'); })); t.is(message, 'called'); }); test('throw in finally bubbles up on error', async t => { const {message} = await t.throwsAsync(execa('exit.js', ['2']).finally(() => { throw new Error('called'); })); t.is(message, 'called'); }); const testNoAwait = async (t, fixtureName, options, message) => { const {stdout} = await execa('no-await.js', [JSON.stringify(options), fixtureName]); t.true(stdout.includes(message)); }; test('Throws if promise is not awaited and subprocess fails', testNoAwait, 'fail.js', {}, 'exit code 2'); test('Throws if promise is not awaited and subprocess times out', testNoAwait, 'forever.js', {timeout: 1}, 'timed out'); ================================================ FILE: test/methods/script.js ================================================ import test from 'ava'; import {isStream} from 'is-stream'; import {$} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const testScriptStdoutSync = (t, getSubprocess, expectedStdout) => { const {stdout} = getSubprocess(); t.is(stdout, expectedStdout); }; test('$.sync`...`', testScriptStdoutSync, () => $.sync`echo.js foo bar`, 'foo\nbar'); test('$.s`...`', testScriptStdoutSync, () => $.s`echo.js foo bar`, 'foo\nbar'); test('$(options).sync`...`', testScriptStdoutSync, () => $({stripFinalNewline: false}).sync`echo.js ${foobarString}`, `${foobarString}\n`); test('$.sync(options)`...`', testScriptStdoutSync, () => $.sync({stripFinalNewline: false})`echo.js ${foobarString}`, `${foobarString}\n`); test('Cannot call $.sync.sync', t => { t.false('sync' in $.sync); }); test('Cannot call $.sync(options).sync', t => { t.false('sync' in $.sync({})); }); test('$(options)() stdin defaults to "inherit"', async t => { const {stdout} = await $({input: foobarString})('stdin-script.js'); t.is(stdout, foobarString); }); test('$.sync(options)() stdin defaults to "inherit"', t => { const {stdout} = $.sync({input: foobarString})('stdin-script.js'); t.is(stdout, foobarString); }); test('$(options).sync() stdin defaults to "inherit"', t => { const {stdout} = $({input: foobarString}).sync('stdin-script.js'); t.is(stdout, foobarString); }); test('$(options)`...` stdin defaults to "inherit"', async t => { const {stdout} = await $({input: foobarString})`stdin-script.js`; t.is(stdout, foobarString); }); test('$.sync(options)`...` stdin defaults to "inherit"', t => { const {stdout} = $.sync({input: foobarString})`stdin-script.js`; t.is(stdout, foobarString); }); test('$(options).sync`...` stdin defaults to "inherit"', t => { const {stdout} = $({input: foobarString}).sync`stdin-script.js`; t.is(stdout, foobarString); }); test('$ stdin has no default value when stdio is set', t => { t.true(isStream($({stdio: 'pipe'})`noop.js`.stdin)); }); ================================================ FILE: test/methods/template.js ================================================ import test from 'ava'; import {$} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); // Workaround since some text editors or IDEs do not allow inputting \r directly const escapedCall = string => { const templates = [string]; templates.raw = [string]; return $(templates); }; const testScriptStdout = async (t, getSubprocess, expectedStdout) => { const {stdout} = await getSubprocess(); t.is(stdout, expectedStdout); }; test('$ allows number interpolation', testScriptStdout, () => $`echo.js 1 ${2}`, '1\n2'); test('$ can concatenate multiple tokens', testScriptStdout, () => $`echo.js ${'foo'}bar${'foo'}`, 'foobarfoo'); test('$ can use newlines and tab indentations', testScriptStdout, () => $`echo.js foo bar`, 'foo\nbar'); test('$ can use newlines and space indentations', testScriptStdout, () => $`echo.js foo bar`, 'foo\nbar'); test('$ can use escaped newlines and space indentations', testScriptStdout, () => $`echo.js foo\ bar`, 'foo\nbar'); test('$ can use escaped newlines and inline tab indentations', testScriptStdout, () => $`echo.js foo\ bar`, 'foo\nbar'); test('$ can use escaped newlines and character escaped tab indentations', testScriptStdout, () => $`echo.js foo\ \tbar`, 'foo\tbar'); test('$ can use escaped newlines and character escaped newlines', testScriptStdout, () => $`echo.js foo\ \n\nbar`, 'foo\n\nbar'); test('$ can use Windows newlines and tab indentations', testScriptStdout, () => escapedCall('echo.js foo\r\n\tbar'), 'foo\nbar'); test('$ can use Windows newlines and space indentations', testScriptStdout, () => escapedCall('echo.js foo\r\n bar'), 'foo\nbar'); test('$ does not ignore comments in expressions', testScriptStdout, () => $`echo.js foo ${/* This is a comment */''} bar ${/* This is another comment */''} baz `, 'foo\n\nbar\n\nbaz'); test('$ allows escaping spaces with interpolation', testScriptStdout, () => $`echo.js ${'foo bar'}`, 'foo bar'); test('$ allows escaping spaces in commands with interpolation', testScriptStdout, () => $`${'command with space.js'} foo bar`, 'foo\nbar'); test('$ trims', testScriptStdout, () => $` echo.js foo bar `, 'foo\nbar'); test('$ allows array interpolation', testScriptStdout, () => $`echo.js ${['foo', 'bar']}`, 'foo\nbar'); test('$ allows empty array interpolation', testScriptStdout, () => $`echo.js foo ${[]} bar`, 'foo\nbar'); test('$ allows space escaped values in array interpolation', testScriptStdout, () => $`echo.js ${['foo', 'bar baz']}`, 'foo\nbar baz'); test('$ can concatenate at the end of tokens followed by an array', testScriptStdout, () => $`echo.js foo${['bar', 'foo']}`, 'foobar\nfoo'); test('$ can concatenate at the start of tokens followed by an array', testScriptStdout, () => $`echo.js ${['foo', 'bar']}foo`, 'foo\nbarfoo'); test('$ can concatenate at the start and end of tokens followed by an array', testScriptStdout, () => $`echo.js foo${['bar', 'foo']}bar`, 'foobar\nfoobar'); test('$ handles escaped newlines', testScriptStdout, () => $`echo.js a\ b`, 'ab'); test('$ handles backslashes at end of lines', testScriptStdout, () => $`echo.js a\\ b`, 'a\\\nb'); test('$ handles double backslashes at end of lines', testScriptStdout, () => $`echo.js a\\\\ b`, 'a\\\\\nb'); test('$ handles tokens - a', testScriptStdout, () => $`echo.js a`, 'a'); test('$ handles expressions - a', testScriptStdout, () => $`echo.js ${'a'}`, 'a'); test('$ handles tokens - abc', testScriptStdout, () => $`echo.js abc`, 'abc'); test('$ handles expressions - abc', testScriptStdout, () => $`echo.js ${'abc'}`, 'abc'); test('$ handles tokens - ""', testScriptStdout, () => $`echo.js`, ''); test('$ handles expressions - ""', testScriptStdout, () => $`echo.js a ${''} b`, 'a\n\nb'); test('$ splits tokens - ""', testScriptStdout, () => $`echo.js ab`, 'ab'); test('$ splits expressions - ""', testScriptStdout, () => $`echo.js ${'a'}${'b'}`, 'ab'); test('$ concatenates expressions - ""', testScriptStdout, () => $`echo.js a${'b'}c`, 'abc'); test('$ handles tokens - " "', testScriptStdout, () => $`echo.js `, ''); test('$ handles expressions - " "', testScriptStdout, () => $`echo.js ${' '}`, ' '); test('$ splits tokens - " "', testScriptStdout, () => $`echo.js a b`, 'a\nb'); test('$ splits expressions - " "', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); test('$ concatenates tokens - " "', testScriptStdout, () => $`echo.js a `, 'a'); test('$ concatenates expressions - " "', testScriptStdout, () => $`echo.js ${'a'} `, 'a'); test('$ handles tokens - " " (2 spaces)', testScriptStdout, () => $`echo.js `, ''); test('$ handles expressions - " " (2 spaces)', testScriptStdout, () => $`echo.js ${' '}`, ' '); test('$ splits tokens - " " (2 spaces)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); test('$ splits expressions - " " (2 spaces)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); test('$ concatenates tokens - " " (2 spaces)', testScriptStdout, () => $`echo.js a `, 'a'); test('$ concatenates expressions - " " (2 spaces)', testScriptStdout, () => $`echo.js ${'a'} `, 'a'); test('$ handles tokens - " " (3 spaces)', testScriptStdout, () => $`echo.js `, ''); test('$ handles expressions - " " (3 spaces)', testScriptStdout, () => $`echo.js ${' '}`, ' '); test('$ splits tokens - " " (3 spaces)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); test('$ splits expressions - " " (3 spaces)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); test('$ concatenates tokens - " " (3 spaces)', testScriptStdout, () => $`echo.js a `, 'a'); test('$ concatenates expressions - " " (3 spaces)', testScriptStdout, () => $`echo.js ${'a'} `, 'a'); test('$ handles tokens - \\t (no escape)', testScriptStdout, () => $`echo.js `, ''); test('$ handles expressions - \\t (no escape)', testScriptStdout, () => $`echo.js ${' '}`, '\t'); test('$ splits tokens - \\t (no escape)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); test('$ splits expressions - \\t (no escape)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); test('$ concatenates tokens - \\t (no escape)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); test('$ concatenates expressions - \\t (no escape)', testScriptStdout, () => $`echo.js ${'a'} b`, 'a\nb'); test('$ handles tokens - \\t (escape)', testScriptStdout, () => $`echo.js \t`, '\t'); test('$ handles expressions - \\t (escape)', testScriptStdout, () => $`echo.js ${'\t'}`, '\t'); test('$ splits tokens - \\t (escape)', testScriptStdout, () => $`echo.js a\tb`, 'a\tb'); test('$ splits expressions - \\t (escape)', testScriptStdout, () => $`echo.js ${'a'}\t${'b'}`, 'a\tb'); test('$ concatenates tokens - \\t (escape)', testScriptStdout, () => $`echo.js \ta\t b`, '\ta\t\nb'); test('$ concatenates expressions - \\t (escape)', testScriptStdout, () => $`echo.js \t${'a'}\t b`, '\ta\t\nb'); test('$ handles tokens - \\n (no escape)', testScriptStdout, () => $`echo.js `, ''); test('$ handles expressions - \\n (no escape)', testScriptStdout, () => $`echo.js ${` `} `, '\n'); test('$ splits tokens - \\n (no escape)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); test('$ splits expressions - \\n (no escape)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\nb'); test('$ concatenates tokens - \\n (no escape)', testScriptStdout, () => $`echo.js a b`, 'a\nb'); test('$ concatenates expressions - \\n (no escape)', testScriptStdout, () => $`echo.js ${'a'} b`, 'a\nb'); test('$ handles tokens - \\n (escape)', testScriptStdout, () => $`echo.js \n `, '\n'); test('$ handles expressions - \\n (escape)', testScriptStdout, () => $`echo.js ${'\n'} `, '\n'); test('$ splits tokens - \\n (escape)', testScriptStdout, () => $`echo.js a\n b`, 'a\n\nb'); test('$ splits expressions - \\n (escape)', testScriptStdout, () => $`echo.js ${'a'}\n ${'b'}`, 'a\n\nb'); test('$ concatenates tokens - \\n (escape)', testScriptStdout, () => $`echo.js \na\n b`, '\na\n\nb'); test('$ concatenates expressions - \\n (escape)', testScriptStdout, () => $`echo.js \n${'a'}\n b`, '\na\n\nb'); test('$ handles tokens - \\r (no escape)', testScriptStdout, () => escapedCall('echo.js \r '), ''); test('$ splits tokens - \\r (no escape)', testScriptStdout, () => escapedCall('echo.js a\rb'), 'a\nb'); test('$ splits expressions - \\r (no escape)', testScriptStdout, () => escapedCall(`echo.js ${'a'}\r${'b'}`), 'a\nb'); test('$ concatenates tokens - \\r (no escape)', testScriptStdout, () => escapedCall('echo.js \ra\r b'), 'a\nb'); test('$ concatenates expressions - \\r (no escape)', testScriptStdout, () => escapedCall(`echo.js \r${'a'}\r b`), 'a\nb'); test('$ splits tokens - \\r (escape)', testScriptStdout, () => $`echo.js a\r b`, 'a\r\nb'); test('$ splits expressions - \\r (escape)', testScriptStdout, () => $`echo.js ${'a'}\r ${'b'}`, 'a\r\nb'); test('$ concatenates tokens - \\r (escape)', testScriptStdout, () => $`echo.js \ra\r b`, '\ra\r\nb'); test('$ concatenates expressions - \\r (escape)', testScriptStdout, () => $`echo.js \r${'a'}\r b`, '\ra\r\nb'); test('$ handles tokens - \\r\\n (no escape)', testScriptStdout, () => escapedCall('echo.js \r\n '), ''); test('$ splits tokens - \\r\\n (no escape)', testScriptStdout, () => escapedCall('echo.js a\r\nb'), 'a\nb'); test('$ splits expressions - \\r\\n (no escape)', testScriptStdout, () => escapedCall(`echo.js ${'a'}\r\n${'b'}`), 'a\nb'); test('$ concatenates tokens - \\r\\n (no escape)', testScriptStdout, () => escapedCall('echo.js \r\na\r\n b'), 'a\nb'); test('$ concatenates expressions - \\r\\n (no escape)', testScriptStdout, () => escapedCall(`echo.js \r\n${'a'}\r\n b`), 'a\nb'); test('$ handles tokens - \\r\\n (escape)', testScriptStdout, () => $`echo.js \r\n `, '\r\n'); test('$ handles expressions - \\r\\n (escape)', testScriptStdout, () => $`echo.js ${'\r\n'} `, '\r\n'); test('$ splits tokens - \\r\\n (escape)', testScriptStdout, () => $`echo.js a\r\n b`, 'a\r\n\nb'); test('$ splits expressions - \\r\\n (escape)', testScriptStdout, () => $`echo.js ${'a'}\r\n ${'b'}`, 'a\r\n\nb'); test('$ concatenates tokens - \\r\\n (escape)', testScriptStdout, () => $`echo.js \r\na\r\n b`, '\r\na\r\n\nb'); test('$ concatenates expressions - \\r\\n (escape)', testScriptStdout, () => $`echo.js \r\n${'a'}\r\n b`, '\r\na\r\n\nb'); /* eslint-disable no-irregular-whitespace */ test('$ handles expressions - \\f (no escape)', testScriptStdout, () => $`echo.js ${' '}`, '\f'); test('$ splits tokens - \\f (no escape)', testScriptStdout, () => $`echo.js a b`, 'a\fb'); test('$ splits expressions - \\f (no escape)', testScriptStdout, () => $`echo.js ${'a'} ${'b'}`, 'a\fb'); test('$ concatenates tokens - \\f (no escape)', testScriptStdout, () => $`echo.js a b`, '\fa\f\nb'); test('$ concatenates expressions - \\f (no escape)', testScriptStdout, () => $`echo.js ${'a'} b`, '\fa\f\nb'); /* eslint-enable no-irregular-whitespace */ test('$ handles tokens - \\f (escape)', testScriptStdout, () => $`echo.js \f`, '\f'); test('$ handles expressions - \\f (escape)', testScriptStdout, () => $`echo.js ${'\f'}`, '\f'); test('$ splits tokens - \\f (escape)', testScriptStdout, () => $`echo.js a\fb`, 'a\fb'); test('$ splits expressions - \\f (escape)', testScriptStdout, () => $`echo.js ${'a'}\f${'b'}`, 'a\fb'); test('$ concatenates tokens - \\f (escape)', testScriptStdout, () => $`echo.js \fa\f b`, '\fa\f\nb'); test('$ concatenates expressions - \\f (escape)', testScriptStdout, () => $`echo.js \f${'a'}\f b`, '\fa\f\nb'); test('$ handles tokens - \\', testScriptStdout, () => $`echo.js \\`, '\\'); test('$ handles expressions - \\', testScriptStdout, () => $`echo.js ${'\\'}`, '\\'); test('$ splits tokens - \\', testScriptStdout, () => $`echo.js a\\b`, 'a\\b'); test('$ splits expressions - \\', testScriptStdout, () => $`echo.js ${'a'}\\${'b'}`, 'a\\b'); test('$ concatenates tokens - \\', testScriptStdout, () => $`echo.js \\a\\ b`, '\\a\\\nb'); test('$ concatenates expressions - \\', testScriptStdout, () => $`echo.js \\${'a'}\\ b`, '\\a\\\nb'); test('$ handles tokens - \\\\', testScriptStdout, () => $`echo.js \\\\`, '\\\\'); test('$ handles expressions - \\\\', testScriptStdout, () => $`echo.js ${'\\\\'}`, '\\\\'); test('$ splits tokens - \\\\', testScriptStdout, () => $`echo.js a\\\\b`, 'a\\\\b'); test('$ splits expressions - \\\\', testScriptStdout, () => $`echo.js ${'a'}\\\\${'b'}`, 'a\\\\b'); test('$ concatenates tokens - \\\\', testScriptStdout, () => $`echo.js \\\\a\\\\ b`, '\\\\a\\\\\nb'); test('$ concatenates expressions - \\\\', testScriptStdout, () => $`echo.js \\\\${'a'}\\\\ b`, '\\\\a\\\\\nb'); test('$ handles tokens - `', testScriptStdout, () => $`echo.js \``, '`'); test('$ handles expressions - `', testScriptStdout, () => $`echo.js ${'`'}`, '`'); test('$ splits tokens - `', testScriptStdout, () => $`echo.js a\`b`, 'a`b'); test('$ splits expressions - `', testScriptStdout, () => $`echo.js ${'a'}\`${'b'}`, 'a`b'); test('$ concatenates tokens - `', testScriptStdout, () => $`echo.js \`a\` b`, '`a`\nb'); test('$ concatenates expressions - `', testScriptStdout, () => $`echo.js \`${'a'}\` b`, '`a`\nb'); test('$ handles tokens - \\v', testScriptStdout, () => $`echo.js \v`, '\v'); test('$ handles expressions - \\v', testScriptStdout, () => $`echo.js ${'\v'}`, '\v'); test('$ splits tokens - \\v', testScriptStdout, () => $`echo.js a\vb`, 'a\vb'); test('$ splits expressions - \\v', testScriptStdout, () => $`echo.js ${'a'}\v${'b'}`, 'a\vb'); test('$ concatenates tokens - \\v', testScriptStdout, () => $`echo.js \va\v b`, '\va\v\nb'); test('$ concatenates expressions - \\v', testScriptStdout, () => $`echo.js \v${'a'}\v b`, '\va\v\nb'); test('$ handles tokens - \\u2028', testScriptStdout, () => $`echo.js \u2028`, '\u2028'); test('$ handles expressions - \\u2028', testScriptStdout, () => $`echo.js ${'\u2028'}`, '\u2028'); test('$ splits tokens - \\u2028', testScriptStdout, () => $`echo.js a\u2028b`, 'a\u2028b'); test('$ splits expressions - \\u2028', testScriptStdout, () => $`echo.js ${'a'}\u2028${'b'}`, 'a\u2028b'); test('$ concatenates tokens - \\u2028', testScriptStdout, () => $`echo.js \u2028a\u2028 b`, '\u2028a\u2028\nb'); test('$ concatenates expressions - \\u2028', testScriptStdout, () => $`echo.js \u2028${'a'}\u2028 b`, '\u2028a\u2028\nb'); test('$ handles tokens - \\a', testScriptStdout, () => $`echo.js \a`, 'a'); test('$ splits tokens - \\a', testScriptStdout, () => $`echo.js a\ab`, 'aab'); test('$ splits expressions - \\a', testScriptStdout, () => $`echo.js ${'a'}\a${'b'}`, 'aab'); test('$ concatenates tokens - \\a', testScriptStdout, () => $`echo.js \aa\a b`, 'aaa\nb'); test('$ concatenates expressions - \\a', testScriptStdout, () => $`echo.js \a${'a'}\a b`, 'aaa\nb'); test('$ handles tokens - \\cJ', testScriptStdout, () => $`echo.js \cJ`, 'cJ'); test('$ splits tokens - \\cJ', testScriptStdout, () => $`echo.js a\cJb`, 'acJb'); test('$ splits expressions - \\cJ', testScriptStdout, () => $`echo.js ${'a'}\cJ${'b'}`, 'acJb'); test('$ concatenates tokens - \\cJ', testScriptStdout, () => $`echo.js \cJa\cJ b`, 'cJacJ\nb'); test('$ concatenates expressions - \\cJ', testScriptStdout, () => $`echo.js \cJ${'a'}\cJ b`, 'cJacJ\nb'); test('$ handles tokens - \\.', testScriptStdout, () => $`echo.js \.`, '.'); test('$ splits tokens - \\.', testScriptStdout, () => $`echo.js a\.b`, 'a.b'); test('$ splits expressions - \\.', testScriptStdout, () => $`echo.js ${'a'}\.${'b'}`, 'a.b'); test('$ concatenates tokens - \\.', testScriptStdout, () => $`echo.js \.a\. b`, '.a.\nb'); test('$ concatenates expressions - \\.', testScriptStdout, () => $`echo.js \.${'a'}\. b`, '.a.\nb'); /* eslint-disable unicorn/no-hex-escape */ test('$ handles tokens - \\x63', testScriptStdout, () => $`echo.js \x63`, 'c'); test('$ splits tokens - \\x63', testScriptStdout, () => $`echo.js a\x63b`, 'acb'); test('$ splits expressions - \\x63', testScriptStdout, () => $`echo.js ${'a'}\x63${'b'}`, 'acb'); test('$ concatenates tokens - \\x63', testScriptStdout, () => $`echo.js \x63a\x63 b`, 'cac\nb'); test('$ concatenates expressions - \\x63', testScriptStdout, () => $`echo.js \x63${'a'}\x63 b`, 'cac\nb'); /* eslint-enable unicorn/no-hex-escape */ test('$ handles tokens - \\u0063', testScriptStdout, () => $`echo.js \u0063`, 'c'); test('$ splits tokens - \\u0063', testScriptStdout, () => $`echo.js a\u0063b`, 'acb'); test('$ splits expressions - \\u0063', testScriptStdout, () => $`echo.js ${'a'}\u0063${'b'}`, 'acb'); test('$ concatenates tokens - \\u0063', testScriptStdout, () => $`echo.js \u0063a\u0063 b`, 'cac\nb'); test('$ concatenates expressions - \\u0063', testScriptStdout, () => $`echo.js \u0063${'a'}\u0063 b`, 'cac\nb'); test('$ handles tokens - \\u{1}', testScriptStdout, () => $`echo.js \u{1}`, '\u0001'); test('$ splits tokens - \\u{1}', testScriptStdout, () => $`echo.js a\u{1}b`, 'a\u0001b'); test('$ splits expressions - \\u{1}', testScriptStdout, () => $`echo.js ${'a'}\u{1}${'b'}`, 'a\u0001b'); test('$ concatenates tokens - \\u{1}', testScriptStdout, () => $`echo.js \u{1}a\u{1} b`, '\u0001a\u0001\nb'); test('$ concatenates expressions - \\u{1}', testScriptStdout, () => $`echo.js \u{1}${'a'}\u{1} b`, '\u0001a\u0001\nb'); test('$ handles tokens - \\u{63}', testScriptStdout, () => $`echo.js \u{63}`, 'c'); test('$ splits tokens - \\u{63}', testScriptStdout, () => $`echo.js a\u{63}b`, 'acb'); test('$ splits expressions - \\u{63}', testScriptStdout, () => $`echo.js ${'a'}\u{63}${'b'}`, 'acb'); test('$ concatenates tokens - \\u{63}', testScriptStdout, () => $`echo.js \u{63}a\u{63} b`, 'cac\nb'); test('$ concatenates expressions - \\u{63}', testScriptStdout, () => $`echo.js \u{63}${'a'}\u{63} b`, 'cac\nb'); test('$ handles tokens - \\u{063}', testScriptStdout, () => $`echo.js \u{063}`, 'c'); test('$ splits tokens - \\u{063}', testScriptStdout, () => $`echo.js a\u{063}b`, 'acb'); test('$ splits expressions - \\u{063}', testScriptStdout, () => $`echo.js ${'a'}\u{063}${'b'}`, 'acb'); test('$ concatenates tokens - \\u{063}', testScriptStdout, () => $`echo.js \u{063}a\u{063} b`, 'cac\nb'); test('$ concatenates expressions - \\u{063}', testScriptStdout, () => $`echo.js \u{063}${'a'}\u{063} b`, 'cac\nb'); test('$ handles tokens - \\u{0063}', testScriptStdout, () => $`echo.js \u{0063}`, 'c'); test('$ splits tokens - \\u{0063}', testScriptStdout, () => $`echo.js a\u{0063}b`, 'acb'); test('$ splits expressions - \\u{0063}', testScriptStdout, () => $`echo.js ${'a'}\u{0063}${'b'}`, 'acb'); test('$ concatenates tokens - \\u{0063}', testScriptStdout, () => $`echo.js \u{0063}a\u{0063} b`, 'cac\nb'); test('$ concatenates expressions - \\u{0063}', testScriptStdout, () => $`echo.js \u{0063}${'a'}\u{0063} b`, 'cac\nb'); test('$ handles tokens - \\u{00063}', testScriptStdout, () => $`echo.js \u{00063}`, 'c'); test('$ splits tokens - \\u{00063}', testScriptStdout, () => $`echo.js a\u{00063}b`, 'acb'); test('$ splits expressions - \\u{00063}', testScriptStdout, () => $`echo.js ${'a'}\u{00063}${'b'}`, 'acb'); test('$ concatenates tokens - \\u{00063}', testScriptStdout, () => $`echo.js \u{00063}a\u{00063} b`, 'cac\nb'); test('$ concatenates expressions - \\u{00063}', testScriptStdout, () => $`echo.js \u{00063}${'a'}\u{00063} b`, 'cac\nb'); test('$ handles tokens - \\u{000063}', testScriptStdout, () => $`echo.js \u{000063}`, 'c'); test('$ splits tokens - \\u{000063}', testScriptStdout, () => $`echo.js a\u{000063}b`, 'acb'); test('$ splits expressions - \\u{000063}', testScriptStdout, () => $`echo.js ${'a'}\u{000063}${'b'}`, 'acb'); test('$ concatenates tokens - \\u{000063}', testScriptStdout, () => $`echo.js \u{000063}a\u{000063} b`, 'cac\nb'); test('$ concatenates expressions - \\u{000063}', testScriptStdout, () => $`echo.js \u{000063}${'a'}\u{000063} b`, 'cac\nb'); test('$ handles tokens - \\u{0000063}', testScriptStdout, () => $`echo.js \u{0000063}`, 'c'); test('$ splits tokens - \\u{0000063}', testScriptStdout, () => $`echo.js a\u{0000063}b`, 'acb'); test('$ splits expressions - \\u{0000063}', testScriptStdout, () => $`echo.js ${'a'}\u{0000063}${'b'}`, 'acb'); test('$ concatenates tokens - \\u{0000063}', testScriptStdout, () => $`echo.js \u{0000063}a\u{0000063} b`, 'cac\nb'); test('$ concatenates expressions - \\u{0000063}', testScriptStdout, () => $`echo.js \u{0000063}${'a'}\u{0000063} b`, 'cac\nb'); test('$ handles tokens - \\u{0063}}', testScriptStdout, () => $`echo.js \u{0063}}`, 'c}'); test('$ splits tokens - \\u{0063}}', testScriptStdout, () => $`echo.js a\u{0063}}b`, 'ac}b'); test('$ splits expressions - \\u{0063}}', testScriptStdout, () => $`echo.js ${'a'}\u{0063}}${'b'}`, 'ac}b'); test('$ concatenates tokens - \\u{0063}}', testScriptStdout, () => $`echo.js \u{0063}}a\u{0063}} b`, 'c}ac}\nb'); test('$ concatenates expressions - \\u{0063}}', testScriptStdout, () => $`echo.js \u{0063}}${'a'}\u{0063}} b`, 'c}ac}\nb'); const testScriptErrorStdout = async (t, getSubprocess) => { t.throws(getSubprocess, {message: /null bytes/}); }; test('$ handles tokens - \\0', testScriptErrorStdout, () => $`echo.js \0`); test('$ splits tokens - \\0', testScriptErrorStdout, () => $`echo.js a\0b`); test('$ splits expressions - \\0', testScriptErrorStdout, () => $`echo.js ${'a'}\0${'b'}`); test('$ concatenates tokens - \\0', testScriptErrorStdout, () => $`echo.js \0a\0 b`); test('$ concatenates expressions - \\0', testScriptErrorStdout, () => $`echo.js \0${'a'}\0 b`); const testReturnInterpolate = async (t, getSubprocess, expectedStdout, options = {}) => { const foo = await $(options)`echo.js foo`; const {stdout} = await getSubprocess(foo); t.is(stdout, expectedStdout); }; test('$ allows execa return value interpolation', testReturnInterpolate, foo => $`echo.js ${foo} bar`, 'foo\nbar'); test('$ allows execa return value buffer interpolation', testReturnInterpolate, foo => $`echo.js ${foo} bar`, 'foo\nbar', {encoding: 'buffer'}); test('$ allows execa return value array interpolation', testReturnInterpolate, foo => $`echo.js ${[foo, 'bar']}`, 'foo\nbar'); test('$ allows execa return value buffer array interpolation', testReturnInterpolate, foo => $`echo.js ${[foo, 'bar']}`, 'foo\nbar', {encoding: 'buffer'}); const testReturnInterpolateSync = (t, getSubprocess, expectedStdout, options = {}) => { const foo = $(options).sync`echo.js foo`; const {stdout} = getSubprocess(foo); t.is(stdout, expectedStdout); }; test('$.sync allows execa return value interpolation', testReturnInterpolateSync, foo => $.sync`echo.js ${foo} bar`, 'foo\nbar'); test('$.sync allows execa return value buffer interpolation', testReturnInterpolateSync, foo => $.sync`echo.js ${foo} bar`, 'foo\nbar', {encoding: 'buffer'}); test('$.sync allows execa return value array interpolation', testReturnInterpolateSync, foo => $.sync`echo.js ${[foo, 'bar']}`, 'foo\nbar'); test('$.sync allows execa return value buffer array interpolation', testReturnInterpolateSync, foo => $.sync`echo.js ${[foo, 'bar']}`, 'foo\nbar', {encoding: 'buffer'}); const testInvalidSequence = (t, getSubprocess) => { t.throws(getSubprocess, {message: /Invalid backslash sequence/}); }; test('$ handles invalid escape sequence - \\1', testInvalidSequence, () => $`echo.js \1`); test('$ handles invalid escape sequence - \\u', testInvalidSequence, () => $`echo.js \u`); test('$ handles invalid escape sequence - \\u0', testInvalidSequence, () => $`echo.js \u0`); test('$ handles invalid escape sequence - \\u00', testInvalidSequence, () => $`echo.js \u00`); test('$ handles invalid escape sequence - \\u000', testInvalidSequence, () => $`echo.js \u000`); test('$ handles invalid escape sequence - \\ug', testInvalidSequence, () => $`echo.js \ug`); test('$ handles invalid escape sequence - \\u{', testInvalidSequence, () => $`echo.js \u{`); test('$ handles invalid escape sequence - \\u{0000', testInvalidSequence, () => $`echo.js \u{0000`); test('$ handles invalid escape sequence - \\u{g}', testInvalidSequence, () => $`echo.js \u{g}`); /* eslint-disable unicorn/no-hex-escape */ test('$ handles invalid escape sequence - \\x', testInvalidSequence, () => $`echo.js \x`); test('$ handles invalid escape sequence - \\x0', testInvalidSequence, () => $`echo.js \x0`); test('$ handles invalid escape sequence - \\xgg', testInvalidSequence, () => $`echo.js \xgg`); /* eslint-enable unicorn/no-hex-escape */ const testEmptyScript = (t, getSubprocess) => { t.throws(getSubprocess, {message: /Template script must not be empty/}); }; test('$``', testEmptyScript, () => $``); test('$` `', testEmptyScript, () => $` `); test('$` ` (2 spaces)', testEmptyScript, () => $` `); test('$`\\t`', testEmptyScript, () => $` `); test('$`\\n`', testEmptyScript, () => $` `); const testInvalidExpression = (t, invalidExpression) => { t.throws( () => $`echo.js ${invalidExpression}`, {message: /in template expression/}, ); }; test('$ throws on invalid expression - undefined', testInvalidExpression, undefined); test('$ throws on invalid expression - null', testInvalidExpression, null); test('$ throws on invalid expression - true', testInvalidExpression, true); test('$ throws on invalid expression - {}', testInvalidExpression, {}); test('$ throws on invalid expression - {foo: "bar"}', testInvalidExpression, {foo: 'bar'}); test('$ throws on invalid expression - {stdout: 1}', testInvalidExpression, {stdout: 1}); test('$ throws on invalid expression - [undefined]', testInvalidExpression, [undefined]); test('$ throws on invalid expression - [null]', testInvalidExpression, [null]); test('$ throws on invalid expression - [true]', testInvalidExpression, [true]); test('$ throws on invalid expression - [{}]', testInvalidExpression, [{}]); test('$ throws on invalid expression - [{foo: "bar"}]', testInvalidExpression, [{foo: 'bar'}]); test('$ throws on invalid expression - [{stdout: 1}]', testInvalidExpression, [{stdout: 1}]); const testMissingOutput = (t, invalidExpression) => { t.throws( () => $`echo.js ${invalidExpression()}`, {message: /Missing result.stdout/}, ); }; test('$ throws on invalid expression - {stdout: undefined}', testMissingOutput, () => ({stdout: undefined})); test('$ throws on invalid expression - [{stdout: undefined}]', testMissingOutput, () => [{stdout: undefined}]); test('$ throws on invalid expression - $(options).sync', testMissingOutput, () => $({stdio: 'ignore'}).sync`noop.js`); test('$ throws on invalid expression - [$(options).sync]', testMissingOutput, () => [$({stdio: 'ignore'}).sync`noop.js`]); const testInvalidPromise = (t, invalidExpression) => { t.throws( () => $`echo.js ${invalidExpression()}`, {message: /Please use \${await subprocess}/}, ); }; test('$ throws on invalid expression - Promise.resolve()', testInvalidPromise, async () => ({})); test('$ throws on invalid expression - Promise.resolve({stdout: "foo"})', testInvalidPromise, async () => ({foo: 'bar'})); test('$ throws on invalid expression - [Promise.resolve()]', testInvalidPromise, () => [Promise.resolve()]); test('$ throws on invalid expression - [Promise.resolve({stdout: "foo"})]', testInvalidPromise, () => [Promise.resolve({stdout: 'foo'})]); test('$ throws on invalid expression - $', testInvalidPromise, () => $`noop.js`); test('$ throws on invalid expression - [$]', testInvalidPromise, () => [$`noop.js`]); ================================================ FILE: test/pipe/abort.js ================================================ import {once} from 'node:events'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const assertUnPipeError = async (t, pipePromise) => { const error = await t.throwsAsync(pipePromise); t.is(error.command, 'source.pipe(destination)'); t.is(error.escapedCommand, error.command); t.is(typeof error.cwd, 'string'); t.true(error.failed); t.false(error.timedOut); t.false(error.isCanceled); t.false(error.isTerminated); t.is(error.exitCode, undefined); t.is(error.signal, undefined); t.is(error.signalDescription, undefined); t.is(error.stdout, undefined); t.is(error.stderr, undefined); t.is(error.all, undefined); t.deepEqual(error.stdio, Array.from({length: error.stdio.length})); t.deepEqual(error.pipedFrom, []); t.true(error.originalMessage.includes('Pipe canceled')); t.true(error.shortMessage.includes(`Command failed: ${error.command}`)); t.true(error.shortMessage.includes(error.originalMessage)); t.true(error.message.includes(error.shortMessage)); }; test('Can unpipe a single subprocess', async t => { const abortController = new AbortController(); const source = execa('stdin.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); abortController.abort(); await assertUnPipeError(t, pipePromise); source.stdin.end(foobarString); destination.stdin.end('.'); t.like(await destination, {stdout: '.'}); t.like(await source, {stdout: foobarString}); }); test('Can use an already aborted signal', async t => { const abortController = new AbortController(); abortController.abort(); const source = execa('empty.js'); const destination = execa('empty.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); await assertUnPipeError(t, pipePromise); }); test('Can unpipe a subprocess among other sources', async t => { const abortController = new AbortController(); const source = execa('stdin.js'); const secondSource = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); const secondPipePromise = secondSource.pipe(destination); abortController.abort(); await assertUnPipeError(t, pipePromise); source.stdin.end('.'); t.is(await secondPipePromise, await destination); t.like(await destination, {stdout: foobarString}); t.like(await source, {stdout: '.'}); t.like(await secondSource, {stdout: foobarString}); }); test('Can unpipe a subprocess among other sources on the same subprocess', async t => { const abortController = new AbortController(); const source = execa('stdin-both.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); const secondPipePromise = source.pipe(destination, {from: 'stderr'}); abortController.abort(); await assertUnPipeError(t, pipePromise); source.stdin.end(foobarString); t.is(await secondPipePromise, await destination); t.like(await destination, {stdout: foobarString}); t.like(await source, {stdout: foobarString, stderr: foobarString}); }); test('Can unpipe a subprocess among other destinations', async t => { const abortController = new AbortController(); const source = execa('stdin.js'); const destination = execa('stdin.js'); const secondDestination = execa('stdin.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); const secondPipePromise = source.pipe(secondDestination); abortController.abort(); await assertUnPipeError(t, pipePromise); source.stdin.end(foobarString); destination.stdin.end('.'); t.is(await secondPipePromise, await secondDestination); t.like(await destination, {stdout: '.'}); t.like(await source, {stdout: foobarString}); t.like(await secondDestination, {stdout: foobarString}); }); test('Can unpipe then re-pipe a subprocess', async t => { const abortController = new AbortController(); const source = execa('stdin.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); source.stdin.write('.'); const [firstWrite] = await once(source.stdout, 'data'); t.is(firstWrite.toString(), '.'); abortController.abort(); await assertUnPipeError(t, pipePromise); source.pipe(destination); source.stdin.end('.'); t.like(await destination, {stdout: '..'}); t.like(await source, {stdout: '..'}); }); test('Can unpipe to prevent termination to propagate to source', async t => { const abortController = new AbortController(); const source = execa('stdin.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); abortController.abort(); await assertUnPipeError(t, pipePromise); destination.kill(); t.like(await t.throwsAsync(destination), {signal: 'SIGTERM'}); source.stdin.end(foobarString); t.like(await source, {stdout: foobarString}); }); test('Can unpipe to prevent termination to propagate to destination', async t => { const abortController = new AbortController(); const source = execa('noop-forever.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); abortController.abort(); await assertUnPipeError(t, pipePromise); source.kill(); t.like(await t.throwsAsync(source), {signal: 'SIGTERM'}); destination.stdin.end(foobarString); t.like(await destination, {stdout: foobarString}); }); ================================================ FILE: test/pipe/pipe-arguments.js ================================================ import {spawn} from 'node:child_process'; import {pathToFileURL} from 'node:url'; import test from 'ava'; import {$, execa} from '../../index.js'; import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {getDenoNodePath} from '../helpers/file-path.js'; setFixtureDirectory(); test('$.pipe(subprocess)', async t => { const {stdout} = await $`noop.js ${foobarString}`.pipe($({stdin: 'pipe'})`stdin.js`); t.is(stdout, foobarString); }); test('execa.$.pipe(subprocess)', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe($({stdin: 'pipe'})`stdin.js`); t.is(stdout, foobarString); }); test('$.pipe.pipe(subprocess)', async t => { const {stdout} = await $`noop.js ${foobarString}` .pipe($({stdin: 'pipe'})`stdin.js`) .pipe($({stdin: 'pipe'})`stdin.js`); t.is(stdout, foobarString); }); test('$.pipe`command`', async t => { const {stdout} = await $`noop.js ${foobarString}`.pipe`stdin.js`; t.is(stdout, foobarString); }); test('execa.$.pipe`command`', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe`stdin.js`; t.is(stdout, foobarString); }); test('$.pipe.pipe`command`', async t => { const {stdout} = await $`noop.js ${foobarString}` .pipe`stdin.js` .pipe`stdin.js`; t.is(stdout, foobarString); }); test('$.pipe("file")', async t => { const {stdout} = await $`noop.js ${foobarString}`.pipe('stdin.js'); t.is(stdout, foobarString); }); test('execa.$.pipe("file")`', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe('stdin.js'); t.is(stdout, foobarString); }); test('$.pipe.pipe("file")', async t => { const {stdout} = await $`noop.js ${foobarString}` .pipe`stdin.js` .pipe('stdin.js'); t.is(stdout, foobarString); }); test('execa.$.pipe(fileUrl)`', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe(pathToFileURL(`${FIXTURES_DIRECTORY}/stdin.js`)); t.is(stdout, foobarString); }); test('$.pipe("file", commandArguments, options)', async t => { const {stdout} = await $`noop.js ${foobarString}`.pipe('node', ['stdin.js'], {cwd: FIXTURES_DIRECTORY}); t.is(stdout, foobarString); }); test('execa.$.pipe("file", commandArguments, options)`', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe('node', ['stdin.js'], {cwd: FIXTURES_DIRECTORY}); t.is(stdout, foobarString); }); test('execa.$.pipe("file", commandArguments, options with denoNodePath)`', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe('node', ['stdin.js'], {cwd: FIXTURES_DIRECTORY, nodePath: getDenoNodePath()}); t.is(stdout, foobarString); }); test('execa.$.pipe("file", commandArguments, denoNodePath)`', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe(getDenoNodePath(), ['stdin.js'], {cwd: FIXTURES_DIRECTORY}); t.is(stdout, foobarString); }); test('$.pipe.pipe("file", commandArguments, options)', async t => { const {stdout} = await $`noop.js ${foobarString}` .pipe`stdin.js` .pipe('node', ['stdin.js'], {cwd: FIXTURES_DIRECTORY}); t.is(stdout, foobarString); }); test('$.pipe(subprocess, pipeOptions)', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}`.pipe($({stdin: 'pipe'})`stdin.js`, {from: 'stderr'}); t.is(stdout, foobarString); }); test('execa.$.pipe(subprocess, pipeOptions)', async t => { const {stdout} = await execa('noop-fd.js', ['2', foobarString]).pipe($({stdin: 'pipe'})`stdin.js`, {from: 'stderr'}); t.is(stdout, foobarString); }); test('$.pipe.pipe(subprocess, pipeOptions)', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}` .pipe($({stdin: 'pipe'})`noop-stdin-fd.js 2`, {from: 'stderr'}) .pipe($({stdin: 'pipe'})`stdin.js`, {from: 'stderr'}); t.is(stdout, foobarString); }); test('$.pipe(pipeOptions)`command`', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}`.pipe({from: 'stderr'})`stdin.js`; t.is(stdout, foobarString); }); test('execa.$.pipe(pipeOptions)`command`', async t => { const {stdout} = await execa('noop-fd.js', ['2', foobarString]).pipe({from: 'stderr'})`stdin.js`; t.is(stdout, foobarString); }); test('$.pipe.pipe(pipeOptions)`command`', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}` .pipe({from: 'stderr'})`noop-stdin-fd.js 2` .pipe({from: 'stderr'})`stdin.js`; t.is(stdout, foobarString); }); test('$.pipe("file", pipeOptions)', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}`.pipe('stdin.js', {from: 'stderr'}); t.is(stdout, foobarString); }); test('execa.$.pipe("file", pipeOptions)', async t => { const {stdout} = await execa('noop-fd.js', ['2', foobarString]).pipe('stdin.js', {from: 'stderr'}); t.is(stdout, foobarString); }); test('$.pipe.pipe("file", pipeOptions)', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}` .pipe({from: 'stderr'})`noop-stdin-fd.js 2` .pipe('stdin.js', {from: 'stderr'}); t.is(stdout, foobarString); }); test('$.pipe(options)`command`', async t => { const {stdout} = await $`noop.js ${foobarString}`.pipe({stripFinalNewline: false})`stdin.js`; t.is(stdout, `${foobarString}\n`); }); test('execa.$.pipe(options)`command`', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe({stripFinalNewline: false})`stdin.js`; t.is(stdout, `${foobarString}\n`); }); test('$.pipe.pipe(options)`command`', async t => { const {stdout} = await $`noop.js ${foobarString}` .pipe({})`stdin.js` .pipe({stripFinalNewline: false})`stdin.js`; t.is(stdout, `${foobarString}\n`); }); test('$.pipe("file", options)', async t => { const {stdout} = await $`noop.js ${foobarString}`.pipe('stdin.js', {stripFinalNewline: false}); t.is(stdout, `${foobarString}\n`); }); test('execa.$.pipe("file", options)', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe('stdin.js', {stripFinalNewline: false}); t.is(stdout, `${foobarString}\n`); }); test('$.pipe.pipe("file", options)', async t => { const {stdout} = await $`noop.js ${foobarString}` .pipe({})`stdin.js` .pipe('stdin.js', {stripFinalNewline: false}); t.is(stdout, `${foobarString}\n`); }); test('$.pipe(pipeAndSubprocessOptions)`command`', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}\n`.pipe({from: 'stderr', stripFinalNewline: false})`stdin.js`; t.is(stdout, `${foobarString}\n`); }); test('execa.$.pipe(pipeAndSubprocessOptions)`command`', async t => { const {stdout} = await execa('noop-fd.js', ['2', `${foobarString}\n`]).pipe({from: 'stderr', stripFinalNewline: false})`stdin.js`; t.is(stdout, `${foobarString}\n`); }); test('$.pipe.pipe(pipeAndSubprocessOptions)`command`', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}\n` .pipe({from: 'stderr'})`noop-stdin-fd.js 2` .pipe({from: 'stderr', stripFinalNewline: false})`stdin.js`; t.is(stdout, `${foobarString}\n`); }); test('$.pipe("file", pipeAndSubprocessOptions)', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}\n`.pipe('stdin.js', {from: 'stderr', stripFinalNewline: false}); t.is(stdout, `${foobarString}\n`); }); test('execa.$.pipe("file", pipeAndSubprocessOptions)', async t => { const {stdout} = await execa('noop-fd.js', ['2', `${foobarString}\n`]).pipe('stdin.js', {from: 'stderr', stripFinalNewline: false}); t.is(stdout, `${foobarString}\n`); }); test('$.pipe.pipe("file", pipeAndSubprocessOptions)', async t => { const {stdout} = await $`noop-fd.js 2 ${foobarString}\n` .pipe({from: 'stderr'})`noop-stdin-fd.js 2` .pipe('stdin.js', {from: 'stderr', stripFinalNewline: false}); t.is(stdout, `${foobarString}\n`); }); test('$.pipe(options)(secondOptions)`command`', async t => { const {stdout} = await $`noop.js ${foobarString}`.pipe({stripFinalNewline: false})({stripFinalNewline: true})`stdin.js`; t.is(stdout, foobarString); }); test('execa.$.pipe(options)(secondOptions)`command`', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe({stripFinalNewline: false})({stripFinalNewline: true})`stdin.js`; t.is(stdout, foobarString); }); test('$.pipe.pipe(options)(secondOptions)`command`', async t => { const {stdout} = await $`noop.js ${foobarString}` .pipe({})({})`stdin.js` .pipe({stripFinalNewline: false})({stripFinalNewline: true})`stdin.js`; t.is(stdout, foobarString); }); test('$.pipe`command` forces "stdin: pipe"', async t => { const {stdout} = await $`noop.js ${foobarString}`.pipe({stdin: 'ignore'})`stdin.js`; t.is(stdout, foobarString); }); test('execa.pipe("file") forces "stdin: "pipe"', async t => { const {stdout} = await execa('noop.js', [foobarString]).pipe('stdin.js', {stdin: 'ignore'}); t.is(stdout, foobarString); }); test('execa.pipe(subprocess) does not force "stdin: pipe"', async t => { await t.throwsAsync( execa('noop.js', [foobarString]).pipe(execa('stdin.js', {stdin: 'ignore'})), {message: /"stdin: 'ignore'" option is incompatible/}, ); }); test('$.pipe(options)(subprocess) fails', async t => { await t.throwsAsync( $`empty.js`.pipe({stdout: 'pipe'})($`empty.js`), {message: /Please use \.pipe/}, ); }); test('execa.$.pipe(options)(subprocess) fails', async t => { await t.throwsAsync( execa('empty.js').pipe({stdout: 'pipe'})($`empty.js`), {message: /Please use \.pipe/}, ); }); test('$.pipe(options)("file") fails', async t => { await t.throwsAsync( $`empty.js`.pipe({stdout: 'pipe'})('empty.js'), {message: /Please use \.pipe/}, ); }); test('execa.$.pipe(options)("file") fails', async t => { await t.throwsAsync( execa('empty.js').pipe({stdout: 'pipe'})('empty.js'), {message: /Please use \.pipe/}, ); }); const testInvalidPipe = async (t, ...pipeArguments) => { await t.throwsAsync( $`empty.js`.pipe(...pipeArguments), {message: /must be a template string/}, ); }; test('$.pipe(nonExecaSubprocess) fails', testInvalidPipe, spawn('node', ['--version'])); test('$.pipe(false) fails', testInvalidPipe, false); ================================================ FILE: test/pipe/sequence.js ================================================ import {once} from 'node:events'; import process from 'node:process'; import {PassThrough} from 'node:stream'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {noopGenerator} from '../helpers/generator.js'; import {prematureClose} from '../helpers/stdio.js'; setFixtureDirectory(); const isLinux = process.platform === 'linux'; test('Source stream abort -> destination success', async t => { const source = execa('noop-repeat.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); source.stdout.destroy(); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); t.like(await t.throwsAsync(source), {exitCode: 1}); await destination; }); test('Source stream error -> destination success', async t => { const source = execa('noop-repeat.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const cause = new Error('test'); source.stdout.destroy(cause); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); t.like(await t.throwsAsync(source), {originalMessage: cause.message, exitCode: 1}); await destination; }); test('Destination stream abort -> source failure', async t => { const source = execa('noop-repeat.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); destination.stdin.destroy(); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.like(await t.throwsAsync(destination), prematureClose); t.like(await t.throwsAsync(source), {exitCode: 1}); }); test('Destination stream error -> source failure', async t => { const source = execa('noop-repeat.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const cause = new Error('test'); destination.stdin.destroy(cause); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.like(await t.throwsAsync(destination), {originalMessage: cause.message, exitCode: 0}); t.like(await t.throwsAsync(source), {exitCode: 1}); }); test('Source success -> destination success', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: foobarString}); t.is(await pipePromise, await destination); }); test('Destination stream end -> source failure', async t => { const source = execa('noop-repeat.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); destination.stdin.end(); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); await destination; t.like(await t.throwsAsync(source), {exitCode: 1}); }); test('Source normal failure -> destination success', async t => { const source = execa('noop-fail.js', ['1', foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); t.like(await t.throwsAsync(source), {stdout: foobarString, exitCode: 2}); await destination; }); test('Source normal failure -> deep destination success', async t => { const source = execa('noop-fail.js', ['1', foobarString]); const destination = execa('stdin.js'); const secondDestination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = pipePromise.pipe(secondDestination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); t.is(await t.throwsAsync(secondPipePromise), await t.throwsAsync(source)); t.like(await t.throwsAsync(source), {stdout: foobarString, exitCode: 2}); t.like(await destination, {stdout: foobarString}); t.like(await secondDestination, {stdout: foobarString}); }); const testSourceTerminated = async (t, signal) => { const source = execa('noop-repeat.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); source.kill(signal); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); t.like(await t.throwsAsync(source), {signal}); await destination; }; test('Source SIGTERM -> destination success', testSourceTerminated, 'SIGTERM'); test('Source SIGKILL -> destination success', testSourceTerminated, 'SIGKILL'); test('Destination success before source -> source success', async t => { const passThroughStream = new PassThrough(); const source = execa('stdin.js', {stdin: ['pipe', passThroughStream]}); const destination = execa('empty.js'); const pipePromise = source.pipe(destination); await destination; passThroughStream.end(); await source; t.is(await pipePromise, await destination); }); test('Destination normal failure -> source failure', async t => { const source = execa('noop-repeat.js'); const destination = execa('fail.js'); const pipePromise = source.pipe(destination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.like(await t.throwsAsync(destination), {exitCode: 2}); t.like(await t.throwsAsync(source), {exitCode: 1}); }); test('Destination normal failure -> deep source failure', async t => { const source = execa('noop-repeat.js'); const destination = execa('stdin.js'); const secondDestination = execa('fail.js'); const pipePromise = source.pipe(destination); const secondPipePromise = pipePromise.pipe(secondDestination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.is(await t.throwsAsync(secondPipePromise), await t.throwsAsync(secondDestination)); t.like(await t.throwsAsync(secondDestination), {exitCode: 2}); t.like(await t.throwsAsync(destination), {exitCode: 1}); t.like(await t.throwsAsync(source), {exitCode: 1}); }); const testDestinationTerminated = async (t, signal) => { const source = execa('noop-repeat.js'); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); destination.kill(signal); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.like(await t.throwsAsync(destination), {signal}); t.like(await t.throwsAsync(source), {exitCode: 1}); }; test('Destination SIGTERM -> source abort', testDestinationTerminated, 'SIGTERM'); test('Destination SIGKILL -> source abort', testDestinationTerminated, 'SIGKILL'); test('Source already ended -> ignore source', async t => { const source = execa('noop.js', [foobarString]); await source; const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); t.is(await pipePromise, await destination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: ''}); }); test('Source already aborted -> ignore source', async t => { const source = execa('noop.js', [foobarString]); source.stdout.destroy(); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); t.is(await pipePromise, await destination); t.like(await source, {stdout: ''}); t.like(await destination, {stdout: ''}); }); test('Source already errored -> failure', async t => { const source = execa('noop.js', [foobarString]); const cause = new Error('test'); source.stdout.destroy(cause); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); t.like(await t.throwsAsync(source), {cause}); t.like(await destination, {stdout: ''}); }); test('Destination already ended -> ignore source', async t => { const destination = execa('stdin.js'); destination.stdin.end('.'); await destination; const source = execa('noop.js', [foobarString]); const pipePromise = source.pipe(destination); t.is(await pipePromise, await destination); t.like(await destination, {stdout: '.'}); t.like(await source, {stdout: ''}); }); test('Destination already aborted -> failure', async t => { const destination = execa('stdin.js'); destination.stdin.destroy(); t.like(await t.throwsAsync(destination), prematureClose); const source = execa('noop.js', [foobarString]); const pipePromise = source.pipe(destination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.like(await source, {stdout: ''}); }); test('Destination already errored -> failure', async t => { const destination = execa('stdin.js'); const cause = new Error('test'); destination.stdin.destroy(cause); t.like(await t.throwsAsync(destination), {cause}); const source = execa('noop.js', [foobarString]); const pipePromise = source.pipe(destination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.like(await source, {stdout: ''}); }); test('Source normal failure + destination normal failure', async t => { const source = execa('noop-fail.js', ['1', foobarString]); const destination = execa('stdin-fail.js'); const pipePromise = source.pipe(destination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.like(await t.throwsAsync(source), {stdout: foobarString, exitCode: 2}); t.like(await t.throwsAsync(destination), {stdout: foobarString, exitCode: 2}); }); test('Simultaneous error on source and destination', async t => { const source = execa('noop.js', ['']); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const sourceCause = new Error(foobarString); source.emit('error', sourceCause); const destinationCause = new Error('other'); destination.emit('error', destinationCause); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(destination)); t.like(await t.throwsAsync(source), {cause: {originalMessage: sourceCause.originalMessage}}); t.like(await t.throwsAsync(destination), {cause: {originalMessage: destinationCause.originalMessage}}); }); test('Does not need to await individual promises', async t => { const source = execa('fail.js'); const destination = execa('fail.js'); await t.throwsAsync(source.pipe(destination)); }); test('Need to await .pipe() return value', async t => { const source = execa('fail.js'); const destination = execa('fail.js'); const pipePromise = source.pipe(destination); await Promise.all([ once(process, 'unhandledRejection'), t.throwsAsync(source), t.throwsAsync(destination), ]); await t.throwsAsync(pipePromise); }); if (isLinux) { const testYesHead = async (t, useStdoutTransform, useStdinTransform, all) => { const source = execa('yes', {stdout: useStdoutTransform ? noopGenerator(false) : 'pipe', all}); const destination = execa('head', ['-n', '1'], {stdin: useStdinTransform ? noopGenerator(false) : 'pipe'}); const pipePromise = source.pipe(destination); t.is(await t.throwsAsync(pipePromise), await t.throwsAsync(source)); t.like(await destination, {stdout: 'y'}); t.like(await t.throwsAsync(source), {exitCode: 1, stderr: 'yes: standard output: Connection reset by peer'}); t.false(source.stdout.readableEnded); t.is(source.stdout.errored, null); t.true(source.stdout.destroyed); t.true(source.stderr.readableEnded); t.is(source.stderr.errored, null); t.true(source.stderr.destroyed); if (all) { t.true(source.all.readableEnded); t.is(source.all.errored, null); t.true(source.all.destroyed); } }; test.serial('Works with yes | head', testYesHead, false, false, false); test.serial('Works with yes | head, input transform', testYesHead, false, true, false); test.serial('Works with yes | head, output transform', testYesHead, true, false, false); test.serial('Works with yes | head, input/output transform', testYesHead, true, true, false); test.serial('Works with yes | head, "all" option', testYesHead, false, false, true); test.serial('Works with yes | head, "all" option, input transform', testYesHead, false, true, true); test.serial('Works with yes | head, "all" option, output transform', testYesHead, true, false, true); test.serial('Works with yes | head, "all" option, input/output transform', testYesHead, true, true, true); } ================================================ FILE: test/pipe/setup.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio, fullReadableStdio} from '../helpers/stdio.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const pipeToSubprocess = async (t, readableFdNumber, writableFdNumber, from, to, readableOptions = {}, writableOptions = {}) => { const {stdout} = await execa('noop-fd.js', [`${readableFdNumber}`, foobarString], readableOptions) .pipe(execa('stdin-fd.js', [`${writableFdNumber}`], writableOptions), {from, to}); t.is(stdout, foobarString); }; test('pipe(...) can pipe', pipeToSubprocess, 1, 0); test('pipe(..., {from: "stdout"}) can pipe', pipeToSubprocess, 1, 0, 'stdout'); test('pipe(..., {from: "fd1"}) can pipe', pipeToSubprocess, 1, 0, 'fd1'); test('pipe(..., {from: "stderr"}) can pipe stderr', pipeToSubprocess, 2, 0, 'stderr'); test('pipe(..., {from: "fd2"}) can pipe', pipeToSubprocess, 2, 0, 'fd2'); test('pipe(..., {from: "fd3"}) can pipe', pipeToSubprocess, 3, 0, 'fd3', undefined, fullStdio); test('pipe(..., {from: "all"}) can pipe stdout', pipeToSubprocess, 1, 0, 'all', undefined, {all: true}); test('pipe(..., {from: "all"}) can pipe stderr', pipeToSubprocess, 2, 0, 'all', undefined, {all: true}); test('pipe(..., {from: "all"}) can pipe stdout even with "stderr: ignore"', pipeToSubprocess, 1, 0, 'all', undefined, {all: true, stderr: 'ignore'}); test('pipe(..., {from: "all"}) can pipe stderr even with "stdout: ignore"', pipeToSubprocess, 2, 0, 'all', undefined, {all: true, stdout: 'ignore'}); test('pipe(..., {to: "stdin"}) can pipe', pipeToSubprocess, 1, 0, undefined, 'stdin'); test('pipe(..., {to: "fd0"}) can pipe', pipeToSubprocess, 1, 0, undefined, 'fd0'); test('pipe(..., {to: "fd3"}) can pipe', pipeToSubprocess, 1, 3, undefined, 'fd3', {}, fullReadableStdio()); ================================================ FILE: test/pipe/streaming.js ================================================ import {once} from 'node:events'; import {PassThrough} from 'node:stream'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {assertMaxListeners} from '../helpers/listeners.js'; import {fullReadableStdio} from '../helpers/stdio.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); test('Can pipe two sources to same destination', async t => { const source = execa('noop.js', [foobarString]); const secondSource = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = secondSource.pipe(destination); t.like(await source, {stdout: foobarString}); t.like(await secondSource, {stdout: foobarString}); t.like(await destination, {stdout: `${foobarString}\n${foobarString}`}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await destination); }); test('Can pipe three sources to same destination', async t => { const source = execa('noop.js', [foobarString]); const secondSource = execa('noop.js', [foobarString]); const thirdSource = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = secondSource.pipe(destination); const thirdPromise = thirdSource.pipe(destination); t.like(await source, {stdout: foobarString}); t.like(await secondSource, {stdout: foobarString}); t.like(await thirdSource, {stdout: foobarString}); t.like(await destination, {stdout: `${foobarString}\n${foobarString}\n${foobarString}`}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await destination); t.is(await thirdPromise, await destination); }); test.serial('Can pipe many sources to same destination', async t => { const checkMaxListeners = assertMaxListeners(t); const expectedResults = Array.from({length: PARALLEL_COUNT}, (_, index) => `${index}`).sort(); const sources = expectedResults.map(expectedResult => execa('noop.js', [expectedResult])); const destination = execa('stdin.js'); const pipePromises = sources.map(source => source.pipe(destination)); const results = await Promise.all(sources); t.deepEqual(results.map(({stdout}) => stdout), expectedResults); const destinationResult = await destination; t.deepEqual(destinationResult.stdout.split('\n').sort(), expectedResults); t.deepEqual(await Promise.all(pipePromises), sources.map(() => destinationResult)); checkMaxListeners(); }); test.serial('Can pipe same source to many destinations', async t => { const checkMaxListeners = assertMaxListeners(t); const source = execa('noop-fd.js', ['1', foobarString]); const expectedResults = Array.from({length: PARALLEL_COUNT}, (_, index) => `${index}`); const destinations = expectedResults.map(expectedResult => execa('noop-stdin-double.js', [expectedResult])); const pipePromises = destinations.map(destination => source.pipe(destination)); t.like(await source, {stdout: foobarString}); const results = await Promise.all(destinations); t.deepEqual(results.map(({stdout}) => stdout), expectedResults.map(result => `${foobarString} ${result}`)); t.deepEqual(await Promise.all(pipePromises), results); checkMaxListeners(); }); test('Can pipe two streams from same subprocess to same destination', async t => { const source = execa('noop-both.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = source.pipe(destination, {from: 'stderr'}); t.like(await source, {stdout: foobarString, stderr: foobarString}); t.like(await destination, {stdout: `${foobarString}\n${foobarString}`}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await destination); }); test('Can pipe same source to two streams from same subprocess', async t => { const source = execa('noop-fd.js', ['1', foobarString]); const destination = execa('stdin-fd-both.js', ['3'], fullReadableStdio()); const pipePromise = source.pipe(destination); const secondPipePromise = source.pipe(destination, {to: 'fd3'}); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: `${foobarString}${foobarString}`}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await destination); }); test('Can pipe a new source to same destination after some source has already written', async t => { const passThroughStream = new PassThrough(); const source = execa('stdin.js', {stdin: ['pipe', passThroughStream]}); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); passThroughStream.write('foo'); const firstWrite = await once(destination.stdout, 'data'); t.is(firstWrite.toString(), 'foo'); const secondSource = execa('noop.js', ['bar']); const secondPipePromise = secondSource.pipe(destination); passThroughStream.end(); t.like(await source, {stdout: 'foo'}); t.like(await secondSource, {stdout: 'bar'}); t.like(await destination, {stdout: 'foobar'}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await destination); }); test('Can pipe a second source to same destination after destination has already ended', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: foobarString}); t.is(await pipePromise, await destination); const secondSource = execa('noop.js', [foobarString]); const secondPipePromise = secondSource.pipe(destination); t.like(await secondSource, {stdout: ''}); t.is(await secondPipePromise, await destination); }); test('Can pipe same source to a second destination after source has already ended', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: foobarString}); t.is(await pipePromise, await destination); const secondDestination = execa('stdin.js'); const secondPipePromise = source.pipe(secondDestination); t.like(await secondDestination, {stdout: ''}); t.is(await secondPipePromise, await secondDestination); }); test('Can pipe a new source to same destination after some but not all sources have ended', async t => { const source = execa('noop.js', [foobarString]); const passThroughStream = new PassThrough(); const secondSource = execa('stdin.js', {stdin: ['pipe', passThroughStream]}); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = secondSource.pipe(destination); t.like(await source, {stdout: foobarString}); const thirdSource = execa('noop.js', [foobarString]); const thirdPipePromise = thirdSource.pipe(destination); passThroughStream.end(`${foobarString}\n`); t.like(await secondSource, {stdout: foobarString}); t.like(await thirdSource, {stdout: foobarString}); t.like(await destination, {stdout: `${foobarString}\n${foobarString}\n${foobarString}`}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await destination); t.is(await thirdPipePromise, await destination); }); test('Can pipe two subprocesses already ended', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); destination.stdin.end('.'); await Promise.all([source, destination]); const pipePromise = source.pipe(destination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: '.'}); t.is(await pipePromise, await destination); }); test('Can pipe to same destination through multiple paths', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const secondDestination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = pipePromise.pipe(secondDestination); const thirdPipePromise = source.pipe(secondDestination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: foobarString}); t.like(await secondDestination, {stdout: `${foobarString}\n${foobarString}`}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await secondDestination); t.is(await thirdPipePromise, await secondDestination); }); test('Can pipe two sources to same destination in objectMode', async t => { const stdoutTransform = { * transform() { yield [foobarString]; }, objectMode: true, }; const source = execa('noop.js', [''], {stdout: stdoutTransform}); const secondSource = execa('noop.js', [''], {stdout: stdoutTransform}); t.true(source.stdout.readableObjectMode); t.true(secondSource.stdout.readableObjectMode); const stdinTransform = { * transform([chunk]) { yield chunk; }, objectMode: true, }; const destination = execa('stdin.js', {stdin: stdinTransform}); const pipePromise = source.pipe(destination); const secondPipePromise = secondSource.pipe(destination); t.like(await source, {stdout: [[foobarString]]}); t.like(await secondSource, {stdout: [[foobarString]]}); t.like(await destination, {stdout: `${foobarString}\n${foobarString}`}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await destination); }); test('Can pipe one source to two destinations', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const secondDestination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = source.pipe(secondDestination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: foobarString}); t.like(await secondDestination, {stdout: foobarString}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await secondDestination); }); test('Can pipe one source to three destinations', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const secondDestination = execa('stdin.js'); const thirdDestination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = source.pipe(secondDestination); const thirdPipePromise = source.pipe(thirdDestination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: foobarString}); t.like(await secondDestination, {stdout: foobarString}); t.like(await thirdDestination, {stdout: foobarString}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await secondDestination); t.is(await thirdPipePromise, await thirdDestination); }); test('Can create a series of pipes', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const secondDestination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = pipePromise.pipe(secondDestination); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: foobarString}); t.like(await secondDestination, {stdout: foobarString}); t.is(await pipePromise, await destination); t.is(await secondPipePromise, await secondDestination); }); test('Returns pipedFrom on success', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const destinationResult = await destination; t.deepEqual(destinationResult.pipedFrom, []); const sourceResult = await source; t.like(await pipePromise, {pipedFrom: [sourceResult]}); t.deepEqual(destinationResult.pipedFrom, [sourceResult]); t.deepEqual(sourceResult.pipedFrom, []); }); test('Returns pipedFrom on deep success', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const secondDestination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = pipePromise.pipe(secondDestination); const destinationResult = await destination; t.deepEqual(destinationResult.pipedFrom, []); const secondDestinationResult = await secondDestination; t.deepEqual(secondDestinationResult.pipedFrom, []); const sourceResult = await source; t.like(await secondPipePromise, {pipedFrom: [destinationResult]}); t.deepEqual(secondDestinationResult.pipedFrom, [destinationResult]); t.like(await pipePromise, {pipedFrom: [sourceResult]}); t.deepEqual(destinationResult.pipedFrom, [sourceResult]); t.deepEqual(sourceResult.pipedFrom, []); }); test('Returns pipedFrom on source failure', async t => { const source = execa('noop-fail.js', ['1', foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const destinationResult = await destination; t.deepEqual(destinationResult.pipedFrom, []); const sourceResult = await t.throwsAsync(source); t.like(await t.throwsAsync(pipePromise), {pipedFrom: []}); t.deepEqual(destinationResult.pipedFrom, [sourceResult]); t.deepEqual(sourceResult.pipedFrom, []); }); test('Returns pipedFrom on destination failure', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin-fail.js'); const pipePromise = source.pipe(destination); const destinationResult = await t.throwsAsync(destination); const sourceResult = await source; t.like(await t.throwsAsync(pipePromise), {pipedFrom: [sourceResult]}); t.deepEqual(destinationResult.pipedFrom, [sourceResult]); t.deepEqual(sourceResult.pipedFrom, []); }); test('Returns pipedFrom on source + destination failure', async t => { const source = execa('noop-fail.js', ['1', foobarString]); const destination = execa('stdin-fail.js'); const pipePromise = source.pipe(destination); const destinationResult = await t.throwsAsync(destination); const sourceResult = await t.throwsAsync(source); t.like(await t.throwsAsync(pipePromise), {pipedFrom: [sourceResult]}); t.deepEqual(destinationResult.pipedFrom, [sourceResult]); t.deepEqual(sourceResult.pipedFrom, []); }); test('Returns pipedFrom on deep failure', async t => { const source = execa('noop-fail.js', ['1', foobarString]); const destination = execa('stdin-fail.js'); const secondDestination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = pipePromise.pipe(secondDestination); const destinationResult = await t.throwsAsync(destination); const secondDestinationResult = await secondDestination; t.deepEqual(secondDestinationResult.pipedFrom, []); const sourceResult = await t.throwsAsync(source); t.like(await t.throwsAsync(secondPipePromise), {pipedFrom: [sourceResult]}); t.deepEqual(secondDestinationResult.pipedFrom, [destinationResult]); t.like(await t.throwsAsync(pipePromise), {pipedFrom: [sourceResult]}); t.deepEqual(destinationResult.pipedFrom, [sourceResult]); t.deepEqual(sourceResult.pipedFrom, []); }); test('Returns pipedFrom from multiple sources', async t => { const source = execa('noop.js', [foobarString]); const secondSource = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = secondSource.pipe(destination); const destinationResult = await destination; t.deepEqual(destinationResult.pipedFrom, []); const sourceResult = await source; const secondSourceResult = await secondSource; t.like(await pipePromise, {pipedFrom: [sourceResult, secondSourceResult]}); t.like(await secondPipePromise, {pipedFrom: [sourceResult, secondSourceResult]}); t.deepEqual(destinationResult.pipedFrom, [sourceResult, secondSourceResult]); t.deepEqual(sourceResult.pipedFrom, []); t.deepEqual(secondSourceResult.pipedFrom, []); }); test('Returns pipedFrom from already ended subprocesses', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); destination.stdin.end('.'); await Promise.all([source, destination]); const pipePromise = source.pipe(destination); const destinationResult = await destination; t.deepEqual(destinationResult.pipedFrom, []); const sourceResult = await source; t.deepEqual(sourceResult.pipedFrom, []); t.like(await pipePromise, {pipedFrom: [sourceResult]}); t.deepEqual(destinationResult.pipedFrom, [sourceResult]); t.deepEqual(sourceResult.pipedFrom, []); }); test('Does not return nor set pipedFrom on signal abort', async t => { const abortController = new AbortController(); const source = execa('empty.js'); const destination = execa('empty.js'); const pipePromise = source.pipe(destination, {unpipeSignal: abortController.signal}); abortController.abort(); t.like(await t.throwsAsync(pipePromise), {pipedFrom: []}); const destinationResult = await destination; t.deepEqual(destinationResult.pipedFrom, []); const sourceResult = await source; t.deepEqual(sourceResult.pipedFrom, []); }); test('Can pipe same source to same destination twice', async t => { const source = execa('noop.js', [foobarString]); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); const secondPipePromise = source.pipe(destination); const destinationResult = await destination; t.like(destinationResult, {pipedFrom: []}); const sourceResult = await source; t.like(sourceResult, {pipedFrom: []}); t.like(await source, {stdout: foobarString}); t.like(await destination, {stdout: foobarString}); t.is(await pipePromise, destinationResult); t.is(await secondPipePromise, destinationResult); t.deepEqual(destinationResult.pipedFrom, [sourceResult]); t.deepEqual(sourceResult.pipedFrom, []); }); ================================================ FILE: test/pipe/throw.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {foobarString} from '../helpers/input.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {assertPipeError} from '../helpers/pipe.js'; setFixtureDirectory(); test('Destination stream is ended when first argument is invalid', async t => { const source = execa('empty.js', {stdout: 'ignore'}); const destination = execa('stdin.js'); const pipePromise = source.pipe(destination); await assertPipeError(t, pipePromise, 'option is incompatible'); await source; t.like(await destination, {stdout: ''}); }); test('Destination stream is ended when first argument is invalid - $', async t => { const pipePromise = execa('empty.js', {stdout: 'ignore'}).pipe`stdin.js`; await assertPipeError(t, pipePromise, 'option is incompatible'); }); test('Source stream is aborted when second argument is invalid', async t => { const source = execa('noop.js', [foobarString]); const pipePromise = source.pipe(false); await assertPipeError(t, pipePromise, 'an Execa subprocess'); t.like(await source, {stdout: ''}); }); test('Both arguments might be invalid', async t => { const source = execa('empty.js', {stdout: 'ignore'}); const pipePromise = source.pipe(false); await assertPipeError(t, pipePromise, 'an Execa subprocess'); t.like(await source, {stdout: undefined}); }); ================================================ FILE: test/resolve/all.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {defaultHighWaterMark} from '../helpers/stream.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const textEncoder = new TextEncoder(); const foobarStringFull = `${foobarString}\n`; const doubleFoobarStringFull = `${foobarStringFull}${foobarStringFull}`; const doubleFoobarString = `${foobarStringFull}${foobarString}`; const doubleFoobarUint8ArrayFull = textEncoder.encode(doubleFoobarStringFull); const doubleFoobarUint8Array = textEncoder.encode(doubleFoobarString); const doubleFoobarArrayFull = [foobarStringFull, foobarStringFull]; const doubleFoobarArray = [foobarString, foobarString]; // eslint-disable-next-line max-params const testAllBoth = async (t, expectedOutput, encoding, lines, stripFinalNewline, isFailure, execaMethod) => { const fixtureName = isFailure ? 'noop-both-fail.js' : 'noop-both.js'; const {exitCode, all} = await execaMethod(fixtureName, [foobarString], { all: true, encoding, lines, stripFinalNewline, reject: !isFailure, }); t.is(exitCode, isFailure ? 1 : 0); t.deepEqual(all, expectedOutput); }; const fdOne = {stderr: true}; const fdBoth = {stdout: true, stderr: true}; test('result.all is defined', testAllBoth, doubleFoobarStringFull, 'utf8', false, false, false, execa); test('result.all is defined, encoding "buffer"', testAllBoth, doubleFoobarUint8ArrayFull, 'buffer', false, false, false, execa); test('result.all is defined, lines', testAllBoth, doubleFoobarArrayFull, 'utf8', true, false, false, execa); test('result.all is defined, lines, fd-specific one', testAllBoth, doubleFoobarArrayFull, 'utf8', fdOne, false, false, execa); test('result.all is defined, lines, fd-specific both', testAllBoth, doubleFoobarArrayFull, 'utf8', fdBoth, false, false, execa); test('result.all is defined, stripFinalNewline', testAllBoth, doubleFoobarString, 'utf8', false, true, false, execa); test('result.all is defined, stripFinalNewline, fd-specific one', testAllBoth, doubleFoobarString, 'utf8', false, fdOne, false, execa); test('result.all is defined, stripFinalNewline, fd-specific both', testAllBoth, doubleFoobarString, 'utf8', false, fdBoth, false, execa); test('result.all is defined, encoding "buffer", stripFinalNewline', testAllBoth, doubleFoobarUint8Array, 'buffer', false, true, false, execa); test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific one', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdOne, false, execa); test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific both', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdBoth, false, execa); test('result.all is defined, lines, stripFinalNewline', testAllBoth, doubleFoobarArray, 'utf8', true, true, false, execa); test('result.all is defined, lines, fd-specific one, stripFinalNewline', testAllBoth, doubleFoobarArray, 'utf8', fdOne, true, false, execa); test('result.all is defined, lines, fd-specific both, stripFinalNewline', testAllBoth, doubleFoobarArray, 'utf8', fdBoth, true, false, execa); test('result.all is defined, lines, stripFinalNewline, fd-specific one', testAllBoth, doubleFoobarArray, 'utf8', true, fdOne, false, execa); test('result.all is defined, lines, stripFinalNewline, fd-specific both', testAllBoth, doubleFoobarArray, 'utf8', true, fdBoth, false, execa); test('result.all is defined, failure', testAllBoth, doubleFoobarStringFull, 'utf8', false, false, true, execa); test('result.all is defined, encoding "buffer", failure', testAllBoth, doubleFoobarUint8ArrayFull, 'buffer', false, false, true, execa); test('result.all is defined, lines, failure', testAllBoth, doubleFoobarArrayFull, 'utf8', true, false, true, execa); test('result.all is defined, lines, fd-specific one, failure', testAllBoth, doubleFoobarArrayFull, 'utf8', fdOne, false, true, execa); test('result.all is defined, lines, fd-specific both, failure', testAllBoth, doubleFoobarArrayFull, 'utf8', fdBoth, false, true, execa); test('result.all is defined, stripFinalNewline, failure', testAllBoth, doubleFoobarString, 'utf8', false, true, true, execa); test('result.all is defined, stripFinalNewline, fd-specific one, failure', testAllBoth, doubleFoobarString, 'utf8', false, fdOne, true, execa); test('result.all is defined, stripFinalNewline, fd-specific both, failure', testAllBoth, doubleFoobarString, 'utf8', false, fdBoth, true, execa); test('result.all is defined, encoding "buffer", stripFinalNewline, failure', testAllBoth, doubleFoobarUint8Array, 'buffer', false, true, true, execa); test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific one, failure', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdOne, true, execa); test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific both, failure', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdBoth, true, execa); test('result.all is defined, lines, stripFinalNewline, failure', testAllBoth, doubleFoobarArray, 'utf8', true, true, true, execa); test('result.all is defined, lines, fd-specific one, stripFinalNewline, failure', testAllBoth, doubleFoobarArray, 'utf8', fdOne, true, true, execa); test('result.all is defined, lines, fd-specific both, stripFinalNewline, failure', testAllBoth, doubleFoobarArray, 'utf8', fdBoth, true, true, execa); test('result.all is defined, lines, stripFinalNewline, fd-specific one, failure', testAllBoth, doubleFoobarArray, 'utf8', true, fdOne, true, execa); test('result.all is defined, lines, stripFinalNewline, fd-specific both, failure', testAllBoth, doubleFoobarArray, 'utf8', true, fdBoth, true, execa); test('result.all is defined, sync', testAllBoth, doubleFoobarStringFull, 'utf8', false, false, false, execaSync); test('result.all is defined, encoding "buffer", sync', testAllBoth, doubleFoobarUint8ArrayFull, 'buffer', false, false, false, execaSync); test('result.all is defined, lines, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', true, false, false, execaSync); test('result.all is defined, lines, fd-specific one, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', fdOne, false, false, execaSync); test('result.all is defined, lines, fd-specific both, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', fdBoth, false, false, execaSync); test('result.all is defined, stripFinalNewline, sync', testAllBoth, doubleFoobarString, 'utf8', false, true, false, execaSync); test('result.all is defined, stripFinalNewline, fd-specific one, sync', testAllBoth, doubleFoobarString, 'utf8', false, fdOne, false, execaSync); test('result.all is defined, stripFinalNewline, fd-specific both, sync', testAllBoth, doubleFoobarString, 'utf8', false, fdBoth, false, execaSync); test('result.all is defined, encoding "buffer", stripFinalNewline, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, true, false, execaSync); test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific one, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdOne, false, execaSync); test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific both, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdBoth, false, execaSync); test('result.all is defined, lines, stripFinalNewline, sync', testAllBoth, doubleFoobarArray, 'utf8', true, true, false, execaSync); test('result.all is defined, lines, fd-specific one, stripFinalNewline, sync', testAllBoth, doubleFoobarArray, 'utf8', fdOne, true, false, execaSync); test('result.all is defined, lines, fd-specific both, stripFinalNewline, sync', testAllBoth, doubleFoobarArray, 'utf8', fdBoth, true, false, execaSync); test('result.all is defined, lines, stripFinalNewline, fd-specific one, sync', testAllBoth, doubleFoobarArray, 'utf8', true, fdOne, false, execaSync); test('result.all is defined, lines, stripFinalNewline, fd-specific both, sync', testAllBoth, doubleFoobarArray, 'utf8', true, fdBoth, false, execaSync); test('result.all is defined, failure, sync', testAllBoth, doubleFoobarStringFull, 'utf8', false, false, true, execaSync); test('result.all is defined, encoding "buffer", failure, sync', testAllBoth, doubleFoobarUint8ArrayFull, 'buffer', false, false, true, execaSync); test('result.all is defined, lines, failure, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', true, false, true, execaSync); test('result.all is defined, lines, fd-specific one, failure, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', fdOne, false, true, execaSync); test('result.all is defined, lines, fd-specific both, failure, sync', testAllBoth, doubleFoobarArrayFull, 'utf8', fdBoth, false, true, execaSync); test('result.all is defined, stripFinalNewline, failure, sync', testAllBoth, doubleFoobarString, 'utf8', false, true, true, execaSync); test('result.all is defined, stripFinalNewline, fd-specific one, failure, sync', testAllBoth, doubleFoobarString, 'utf8', false, fdOne, true, execaSync); test('result.all is defined, stripFinalNewline, fd-specific both, failure, sync', testAllBoth, doubleFoobarString, 'utf8', false, fdBoth, true, execaSync); test('result.all is defined, encoding "buffer", stripFinalNewline, failure, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, true, true, execaSync); test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific one, failure, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdOne, true, execaSync); test('result.all is defined, encoding "buffer", stripFinalNewline, fd-specific both, failure, sync', testAllBoth, doubleFoobarUint8Array, 'buffer', false, fdBoth, true, execaSync); test('result.all is defined, lines, stripFinalNewline, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', true, true, true, execaSync); test('result.all is defined, lines, fd-specific one, stripFinalNewline, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', fdOne, true, true, execaSync); test('result.all is defined, lines, fd-specific both, stripFinalNewline, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', fdBoth, true, true, execaSync); test('result.all is defined, lines, stripFinalNewline, fd-specific one, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', true, fdOne, true, execaSync); test('result.all is defined, lines, stripFinalNewline, fd-specific both, failure, sync', testAllBoth, doubleFoobarArray, 'utf8', true, fdBoth, true, execaSync); test.serial('result.all shows both `stdout` and `stderr` intermixed', async t => { const {all} = await execa('noop-132.js', {all: true}); t.is(all, '1\n2\n3'); }); test.serial('result.all shows both `stdout` and `stderr` not intermixed, sync', t => { const {all} = execaSync('noop-132.js', {all: true}); t.is(all, '1\n3\n2'); }); const testAllIgnored = async (t, options, execaMethod) => { const {all} = await execaMethod('noop.js'); t.is(all, undefined); }; test('result.all is undefined unless opts.all is true', testAllIgnored, {}, execa); test('result.all is undefined if opts.all is false', testAllIgnored, {all: false}, execa); test('result.all is undefined if ignored', testAllIgnored, {stdio: 'ignore', all: true}, execa); test('result.all is undefined unless opts.all is true, sync', testAllIgnored, {}, execaSync); test('result.all is undefined if opts.all is false, sync', testAllIgnored, {all: false}, execaSync); test('result.all is undefined if ignored, sync', testAllIgnored, {stdio: 'ignore', all: true}, execaSync); const testAllProperties = async (t, options) => { const subprocess = execa('empty.js', {...options, all: true}); t.is(subprocess.all.readableObjectMode, false); t.is(subprocess.all.readableHighWaterMark, defaultHighWaterMark); await subprocess; }; test('subprocess.all has the right objectMode and highWaterMark - stdout + stderr', testAllProperties, {}); test('subprocess.all has the right objectMode and highWaterMark - stdout only', testAllProperties, {stderr: 'ignore'}); test('subprocess.all has the right objectMode and highWaterMark - stderr only', testAllProperties, {stdout: 'ignore'}); const testAllIgnore = async (t, streamName, otherStreamName) => { const subprocess = execa('noop-both.js', {[otherStreamName]: 'ignore', all: true}); t.is(subprocess[otherStreamName], null); t.not(subprocess[streamName], null); t.not(subprocess.all, null); t.is(subprocess.all.readableObjectMode, subprocess[streamName].readableObjectMode); t.is(subprocess.all.readableHighWaterMark, subprocess[streamName].readableHighWaterMark); const result = await subprocess; t.is(result[otherStreamName], undefined); t.is(result[streamName], foobarString); t.is(result.all, foobarString); }; test('can use all: true with stdout: ignore', testAllIgnore, 'stderr', 'stdout'); test('can use all: true with stderr: ignore', testAllIgnore, 'stdout', 'stderr'); const testAllIgnoreSync = (t, streamName, otherStreamName) => { const result = execaSync('noop-both.js', {[otherStreamName]: 'ignore', all: true}); t.is(result[otherStreamName], undefined); t.is(result[streamName], foobarString); t.is(result.all, foobarString); }; test('can use all: true with stdout: ignore, sync', testAllIgnoreSync, 'stderr', 'stdout'); test('can use all: true with stderr: ignore, sync', testAllIgnoreSync, 'stdout', 'stderr'); test('can use all: true with stdout: ignore + stderr: ignore', async t => { const subprocess = execa('noop-both.js', {stdout: 'ignore', stderr: 'ignore', all: true}); t.is(subprocess.stdout, null); t.is(subprocess.stderr, null); t.is(subprocess.all, undefined); const {stdout, stderr, all} = await subprocess; t.is(stdout, undefined); t.is(stderr, undefined); t.is(all, undefined); }); test('can use all: true with stdout: ignore + stderr: ignore, sync', t => { const {stdout, stderr, all} = execaSync('noop-both.js', {stdout: 'ignore', stderr: 'ignore', all: true}); t.is(stdout, undefined); t.is(stderr, undefined); t.is(all, undefined); }); ================================================ FILE: test/resolve/buffer-end.js ================================================ import {once} from 'node:events'; import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio, getStdio} from '../helpers/stdio.js'; setFixtureDirectory(); const testBufferIgnore = async (t, fdNumber, all) => { await t.notThrowsAsync(execa('max-buffer.js', [`${fdNumber}`], {...getStdio(fdNumber, 'ignore'), buffer: false, all})); }; test('Subprocess buffers stdout, which does not prevent exit if ignored', testBufferIgnore, 1, false); test('Subprocess buffers stderr, which does not prevent exit if ignored', testBufferIgnore, 2, false); test('Subprocess buffers all, which does not prevent exit if ignored', testBufferIgnore, 1, true); const testBufferNotRead = async (t, fdNumber, all) => { const subprocess = execa('max-buffer.js', [`${fdNumber}`], {...fullStdio, buffer: false, all}); await t.notThrowsAsync(subprocess); }; test('Subprocess buffers stdout, which does not prevent exit if not read and buffer is false', testBufferNotRead, 1, false); test('Subprocess buffers stderr, which does not prevent exit if not read and buffer is false', testBufferNotRead, 2, false); test('Subprocess buffers stdio[*], which does not prevent exit if not read and buffer is false', testBufferNotRead, 3, false); test('Subprocess buffers all, which does not prevent exit if not read and buffer is false', testBufferNotRead, 1, true); const testBufferRead = async (t, fdNumber, all) => { const subprocess = execa('max-buffer.js', [`${fdNumber}`], {...fullStdio, buffer: false, all}); const stream = all ? subprocess.all : subprocess.stdio[fdNumber]; stream.resume(); await t.notThrowsAsync(subprocess); }; test('Subprocess buffers stdout, which does not prevent exit if read and buffer is false', testBufferRead, 1, false); test('Subprocess buffers stderr, which does not prevent exit if read and buffer is false', testBufferRead, 2, false); test('Subprocess buffers stdio[*], which does not prevent exit if read and buffer is false', testBufferRead, 3, false); test('Subprocess buffers all, which does not prevent exit if read and buffer is false', testBufferRead, 1, true); const testBufferExit = async (t, fdNumber, fixtureName, reject) => { const subprocess = execa(fixtureName, [`${fdNumber}`], {...fullStdio, reject}); await setTimeout(100); const {stdio} = await subprocess; t.is(stdio[fdNumber], 'foobar'); }; test('Subprocess buffers stdout before it is read', testBufferExit, 1, 'noop-delay.js', true); test('Subprocess buffers stderr before it is read', testBufferExit, 2, 'noop-delay.js', true); test('Subprocess buffers stdio[*] before it is read', testBufferExit, 3, 'noop-delay.js', true); test('Subprocess buffers stdout right away, on successfully exit', testBufferExit, 1, 'noop-fd.js', true); test('Subprocess buffers stderr right away, on successfully exit', testBufferExit, 2, 'noop-fd.js', true); test('Subprocess buffers stdio[*] right away, on successfully exit', testBufferExit, 3, 'noop-fd.js', true); test('Subprocess buffers stdout right away, on failure', testBufferExit, 1, 'noop-fail.js', false); test('Subprocess buffers stderr right away, on failure', testBufferExit, 2, 'noop-fail.js', false); test('Subprocess buffers stdio[*] right away, on failure', testBufferExit, 3, 'noop-fail.js', false); const testBufferDirect = async (t, fdNumber) => { const subprocess = execa('noop-fd.js', [`${fdNumber}`], fullStdio); const data = await once(subprocess.stdio[fdNumber], 'data'); t.is(data.toString().trim(), 'foobar'); const result = await subprocess; t.is(result.stdio[fdNumber], 'foobar'); }; test('Subprocess buffers stdout right away, even if directly read', testBufferDirect, 1); test('Subprocess buffers stderr right away, even if directly read', testBufferDirect, 2); test('Subprocess buffers stdio[*] right away, even if directly read', testBufferDirect, 3); const testBufferDestroyOnEnd = async (t, fdNumber) => { const subprocess = execa('noop-fd.js', [`${fdNumber}`], fullStdio); const result = await subprocess; t.is(result.stdio[fdNumber], 'foobar'); t.true(subprocess.stdio[fdNumber].destroyed); }; test('subprocess.stdout must be read right away', testBufferDestroyOnEnd, 1); test('subprocess.stderr must be read right away', testBufferDestroyOnEnd, 2); test('subprocess.stdio[*] must be read right away', testBufferDestroyOnEnd, 3); ================================================ FILE: test/resolve/exit.js ================================================ import process from 'node:process'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; const isWindows = process.platform === 'win32'; setFixtureDirectory(); test('exitCode is 0 on success', async t => { const {exitCode} = await execa('noop.js', ['foo']); t.is(exitCode, 0); }); const testExitCode = async (t, expectedExitCode) => { const {exitCode, originalMessage, shortMessage, message} = await t.throwsAsync( execa('exit.js', [`${expectedExitCode}`]), ); t.is(exitCode, expectedExitCode); t.is(originalMessage, undefined); t.is(shortMessage, `Command failed with exit code ${expectedExitCode}: exit.js ${expectedExitCode}`); t.is(message, shortMessage); }; test('exitCode is 2', testExitCode, 2); test('exitCode is 3', testExitCode, 3); test('exitCode is 4', testExitCode, 4); if (!isWindows) { test('error.signal is SIGINT', async t => { const subprocess = execa('forever.js'); process.kill(subprocess.pid, 'SIGINT'); const {signal} = await t.throwsAsync(subprocess, {message: /was killed with SIGINT/}); t.is(signal, 'SIGINT'); }); test('error.signalDescription is defined', async t => { const subprocess = execa('forever.js'); process.kill(subprocess.pid, 'SIGINT'); const {signalDescription} = await t.throwsAsync(subprocess, {message: /User interruption with CTRL-C/}); t.is(signalDescription, 'User interruption with CTRL-C'); }); test('error.signal is SIGTERM', async t => { const subprocess = execa('forever.js'); process.kill(subprocess.pid, 'SIGTERM'); const {signal} = await t.throwsAsync(subprocess, {message: /was killed with SIGTERM/}); t.is(signal, 'SIGTERM'); }); test('error.signal uses killSignal', async t => { const {signal} = await t.throwsAsync(execa('forever.js', {killSignal: 'SIGINT', timeout: 1, message: /timed out after/})); t.is(signal, 'SIGINT'); }); test('exitCode is undefined on signal termination', async t => { const subprocess = execa('forever.js'); process.kill(subprocess.pid); const {exitCode} = await t.throwsAsync(subprocess); t.is(exitCode, undefined); }); } test('result.signal is undefined for successful execution', async t => { const {signal} = await execa('noop.js'); t.is(signal, undefined); }); test('result.signal is undefined if subprocess failed, but was not killed', async t => { const {signal} = await t.throwsAsync(execa('fail.js')); t.is(signal, undefined); }); test('result.signalDescription is undefined for successful execution', async t => { const {signalDescription} = await execa('noop.js'); t.is(signalDescription, undefined); }); ================================================ FILE: test/resolve/no-buffer.js ================================================ import {once} from 'node:events'; import test from 'ava'; import getStream from 'get-stream'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import {foobarString, foobarUppercase, foobarUppercaseUint8Array} from '../helpers/input.js'; import {resultGenerator, uppercaseGenerator, uppercaseBufferGenerator} from '../helpers/generator.js'; setFixtureDirectory(); const testLateStream = async (t, fdNumber, all) => { const subprocess = execa('noop-fd-ipc.js', [`${fdNumber}`, foobarString], { ...fullStdio, ipc: true, buffer: false, all, }); await subprocess.getOneMessage(); const [output, allOutput] = await Promise.all([ getStream(subprocess.stdio[fdNumber]), all ? getStream(subprocess.all) : undefined, subprocess, ]); t.is(output, ''); if (all) { t.is(allOutput, ''); } }; test('Lacks some data when stdout is read too late `buffer` set to `false`', testLateStream, 1, false); test('Lacks some data when stderr is read too late `buffer` set to `false`', testLateStream, 2, false); test('Lacks some data when stdio[*] is read too late `buffer` set to `false`', testLateStream, 3, false); test('Lacks some data when all is read too late `buffer` set to `false`', testLateStream, 1, true); const getFirstDataEvent = async stream => { const [output] = await once(stream, 'data'); return output.toString(); }; // eslint-disable-next-line max-params const testIterationBuffer = async (t, fdNumber, buffer, useDataEvents, all) => { const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString], {...fullStdio, buffer, all}); const getOutput = useDataEvents ? getFirstDataEvent : getStream; const [result, output, allOutput] = await Promise.all([ subprocess, getOutput(subprocess.stdio[fdNumber]), all ? getOutput(subprocess.all) : undefined, ]); const expectedResult = buffer ? foobarString : undefined; t.is(result.stdio[fdNumber], expectedResult); t.is(output, foobarString); if (all) { t.is(result.all, expectedResult); t.is(allOutput, foobarString); } }; test('Can iterate stdout when `buffer` set to `false`', testIterationBuffer, 1, false, false, false); test('Can iterate stderr when `buffer` set to `false`', testIterationBuffer, 2, false, false, false); test('Can iterate stdio[*] when `buffer` set to `false`', testIterationBuffer, 3, false, false, false); test('Can iterate all when `buffer` set to `false`', testIterationBuffer, 1, false, false, true); test('Can iterate stdout when `buffer` set to `true`', testIterationBuffer, 1, true, false, false); test('Can iterate stderr when `buffer` set to `true`', testIterationBuffer, 2, true, false, false); test('Can iterate stdio[*] when `buffer` set to `true`', testIterationBuffer, 3, true, false, false); test('Can iterate all when `buffer` set to `true`', testIterationBuffer, 1, true, false, true); test('Can listen to `data` events on stdout when `buffer` set to `false`', testIterationBuffer, 1, false, true, false); test('Can listen to `data` events on stderr when `buffer` set to `false`', testIterationBuffer, 2, false, true, false); test('Can listen to `data` events on stdio[*] when `buffer` set to `false`', testIterationBuffer, 3, false, true, false); test('Can listen to `data` events on all when `buffer` set to `false`', testIterationBuffer, 1, false, true, true); test('Can listen to `data` events on stdout when `buffer` set to `true`', testIterationBuffer, 1, true, true, false); test('Can listen to `data` events on stderr when `buffer` set to `true`', testIterationBuffer, 2, true, true, false); test('Can listen to `data` events on stdio[*] when `buffer` set to `true`', testIterationBuffer, 3, true, true, false); test('Can listen to `data` events on all when `buffer` set to `true`', testIterationBuffer, 1, true, true, true); const testNoBufferStreamError = async (t, fdNumber, all) => { const subprocess = execa('noop-fd.js', [`${fdNumber}`], {...fullStdio, buffer: false, all}); const stream = all ? subprocess.all : subprocess.stdio[fdNumber]; const cause = new Error('test'); stream.destroy(cause); t.like(await t.throwsAsync(subprocess), {cause}); }; test('Listen to stdout errors even when `buffer` is `false`', testNoBufferStreamError, 1, false); test('Listen to stderr errors even when `buffer` is `false`', testNoBufferStreamError, 2, false); test('Listen to stdio[*] errors even when `buffer` is `false`', testNoBufferStreamError, 3, false); test('Listen to all errors even when `buffer` is `false`', testNoBufferStreamError, 1, true); const testOutput = async (t, buffer, execaMethod) => { const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], {buffer}); t.is(stdout, foobarString); }; test('buffer: true returns output', testOutput, true, execa); test('buffer: true returns output, fd-specific', testOutput, {stderr: false}, execa); test('buffer: default returns output', testOutput, undefined, execa); test('buffer: default returns output, fd-specific', testOutput, {}, execa); const testNoOutput = async (t, stdioOption, buffer, execaMethod) => { const {stdout} = await execaMethod('noop.js', {stdout: stdioOption, buffer}); t.is(stdout, undefined); }; test('buffer: false does not return output', testNoOutput, 'pipe', false, execa); test('buffer: false does not return output, fd-specific', testNoOutput, 'pipe', {stdout: false}, execa); test('buffer: false does not return output, stdout undefined', testNoOutput, undefined, false, execa); test('buffer: false does not return output, stdout null', testNoOutput, null, false, execa); test('buffer: false does not return output, stdout ["pipe"]', testNoOutput, ['pipe'], false, execa); test('buffer: false does not return output, stdout [undefined]', testNoOutput, [undefined], false, execa); test('buffer: false does not return output, stdout [null]', testNoOutput, [null], false, execa); test('buffer: false does not return output, stdout ["pipe", undefined]', testNoOutput, ['pipe', undefined], false, execa); test('buffer: false does not return output, sync', testNoOutput, 'pipe', false, execaSync); test('buffer: false does not return output, fd-specific, sync', testNoOutput, 'pipe', {stdout: false}, execaSync); test('buffer: false does not return output, stdout undefined, sync', testNoOutput, undefined, false, execaSync); test('buffer: false does not return output, stdout null, sync', testNoOutput, null, false, execaSync); test('buffer: false does not return output, stdout ["pipe"], sync', testNoOutput, ['pipe'], false, execaSync); test('buffer: false does not return output, stdout [undefined], sync', testNoOutput, [undefined], false, execaSync); test('buffer: false does not return output, stdout [null], sync', testNoOutput, [null], false, execaSync); test('buffer: false does not return output, stdout ["pipe", undefined], sync', testNoOutput, ['pipe', undefined], false, execaSync); const testNoOutputFail = async (t, execaMethod) => { const {exitCode, stdout} = await execaMethod('fail.js', {buffer: false, reject: false}); t.is(exitCode, 2); t.is(stdout, undefined); }; test('buffer: false does not return output, failure', testNoOutputFail, execa); test('buffer: false does not return output, failure, sync', testNoOutputFail, execaSync); // eslint-disable-next-line max-params const testNoOutputAll = async (t, buffer, bufferStdout, bufferStderr, execaMethod) => { const {stdout, stderr, all} = await execaMethod('noop-both.js', {all: true, buffer, stripFinalNewline: false}); t.is(stdout, bufferStdout ? `${foobarString}\n` : undefined); t.is(stderr, bufferStderr ? `${foobarString}\n` : undefined); const stdoutStderr = [stdout, stderr].filter(Boolean); t.is(all, stdoutStderr.length === 0 ? undefined : stdoutStderr.join('')); }; test('buffer: {}, all: true', testNoOutputAll, {}, true, true, execa); test('buffer: {stdout: false}, all: true', testNoOutputAll, {stdout: false}, false, true, execa); test('buffer: {stderr: false}, all: true', testNoOutputAll, {stderr: false}, true, false, execa); test('buffer: {all: false}, all: true', testNoOutputAll, {all: false}, false, false, execa); test('buffer: {}, all: true, sync', testNoOutputAll, {}, true, true, execaSync); test('buffer: {stdout: false}, all: true, sync', testNoOutputAll, {stdout: false}, false, true, execaSync); test('buffer: {stderr: false}, all: true, sync', testNoOutputAll, {stderr: false}, true, false, execaSync); test('buffer: {all: false}, all: true, sync', testNoOutputAll, {all: false}, false, false, execaSync); const testTransform = async (t, objectMode, execaMethod) => { const lines = []; const {stdout} = await execaMethod('noop.js', { buffer: false, stdout: [uppercaseGenerator(objectMode), resultGenerator(lines)(objectMode)], }); t.is(stdout, undefined); t.deepEqual(lines, [foobarUppercase]); }; test('buffer: false still runs transforms', testTransform, false, execa); test('buffer: false still runs transforms, objectMode', testTransform, true, execa); test('buffer: false still runs transforms, sync', testTransform, false, execaSync); test('buffer: false still runs transforms, objectMode, sync', testTransform, true, execaSync); const testTransformBinary = async (t, objectMode, execaMethod) => { const lines = []; const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], { buffer: false, stdout: [uppercaseBufferGenerator(objectMode, true), resultGenerator(lines)(objectMode)], encoding: 'buffer', }); t.is(stdout, undefined); t.deepEqual(lines, [foobarUppercaseUint8Array]); }; test('buffer: false still runs transforms, encoding "buffer"', testTransformBinary, false, execa); test('buffer: false still runs transforms, encoding "buffer", objectMode', testTransformBinary, true, execa); test('buffer: false still runs transforms, encoding "buffer", sync', testTransformBinary, false, execaSync); test('buffer: false still runs transforms, encoding "buffer", objectMode, sync', testTransformBinary, true, execaSync); const testStreamEnd = async (t, fdNumber, buffer) => { const subprocess = execa('wrong command', {...fullStdio, buffer}); await Promise.all([ t.throwsAsync(subprocess, {message: /wrong command/}), once(subprocess.stdio[fdNumber], 'end'), ]); }; test('buffer: false > emits end event on stdout when promise is rejected', testStreamEnd, 1, false); test('buffer: false > emits end event on stderr when promise is rejected', testStreamEnd, 2, false); test('buffer: false > emits end event on stdio[*] when promise is rejected', testStreamEnd, 3, false); test('buffer: true > emits end event on stdout when promise is rejected', testStreamEnd, 1, true); test('buffer: true > emits end event on stderr when promise is rejected', testStreamEnd, 2, true); test('buffer: true > emits end event on stdio[*] when promise is rejected', testStreamEnd, 3, true); ================================================ FILE: test/resolve/stdio.js ================================================ import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { fullStdio, getStdio, prematureClose, assertEpipe, } from '../helpers/stdio.js'; import {infiniteGenerator} from '../helpers/generator.js'; setFixtureDirectory(); const getStreamInputSubprocess = fdNumber => execa('stdin-fd.js', [`${fdNumber}`], fdNumber === 3 ? getStdio(3, [new Uint8Array(), infiniteGenerator()]) : {}); const getStreamOutputSubprocess = fdNumber => execa('noop-repeat.js', [`${fdNumber}`], fdNumber === 3 ? fullStdio : {}); const assertStreamInputError = (t, {exitCode, signal, isTerminated, failed}) => { t.is(exitCode, 0); t.is(signal, undefined); t.false(isTerminated); t.true(failed); }; const assertStreamOutputError = (t, fdNumber, {exitCode, signal, isTerminated, failed, stderr}) => { if (fdNumber !== 3) { t.is(exitCode, 1); } t.is(signal, undefined); t.false(isTerminated); t.true(failed); assertEpipe(t, stderr, fdNumber); }; const testStreamInputAbort = async (t, fdNumber) => { const subprocess = getStreamInputSubprocess(fdNumber); subprocess.stdio[fdNumber].destroy(); const error = await t.throwsAsync(subprocess, prematureClose); assertStreamInputError(t, error); }; test('Aborting stdin should not make the subprocess exit', testStreamInputAbort, 0); test('Aborting input stdio[*] should not make the subprocess exit', testStreamInputAbort, 3); const testStreamOutputAbort = async (t, fdNumber) => { const subprocess = getStreamOutputSubprocess(fdNumber); subprocess.stdio[fdNumber].destroy(); const error = await t.throwsAsync(subprocess); assertStreamOutputError(t, fdNumber, error); }; test('Aborting stdout should not make the subprocess exit', testStreamOutputAbort, 1); test('Aborting stderr should not make the subprocess exit', testStreamOutputAbort, 2); test('Aborting output stdio[*] should not make the subprocess exit', testStreamOutputAbort, 3); const testStreamInputDestroy = async (t, fdNumber) => { const subprocess = getStreamInputSubprocess(fdNumber); const cause = new Error('test'); subprocess.stdio[fdNumber].destroy(cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); assertStreamInputError(t, error); }; test('Destroying stdin should not make the subprocess exit', testStreamInputDestroy, 0); test('Destroying input stdio[*] should not make the subprocess exit', testStreamInputDestroy, 3); const testStreamOutputDestroy = async (t, fdNumber) => { const subprocess = getStreamOutputSubprocess(fdNumber); const cause = new Error('test'); subprocess.stdio[fdNumber].destroy(cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); assertStreamOutputError(t, fdNumber, error); }; test('Destroying stdout should not make the subprocess exit', testStreamOutputDestroy, 1); test('Destroying stderr should not make the subprocess exit', testStreamOutputDestroy, 2); test('Destroying output stdio[*] should not make the subprocess exit', testStreamOutputDestroy, 3); const testStreamInputError = async (t, fdNumber) => { const subprocess = getStreamInputSubprocess(fdNumber); const cause = new Error('test'); const stream = subprocess.stdio[fdNumber]; stream.emit('error', cause); stream.end(); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); assertStreamInputError(t, error); }; test('Errors on stdin should not make the subprocess exit', testStreamInputError, 0); test('Errors on input stdio[*] should not make the subprocess exit', testStreamInputError, 3); const testStreamOutputError = async (t, fdNumber) => { const subprocess = getStreamOutputSubprocess(fdNumber); const cause = new Error('test'); const stream = subprocess.stdio[fdNumber]; stream.emit('error', cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); assertStreamOutputError(t, fdNumber, error); }; test('Errors on stdout should make the subprocess exit', testStreamOutputError, 1); test('Errors on stderr should make the subprocess exit', testStreamOutputError, 2); test('Errors on output stdio[*] should make the subprocess exit', testStreamOutputError, 3); const testWaitOnStreamEnd = async (t, fdNumber) => { const subprocess = execa('stdin-fd.js', [`${fdNumber}`], fullStdio); await setTimeout(100); subprocess.stdio[fdNumber].end('foobar'); const {stdout} = await subprocess; t.is(stdout, 'foobar'); }; test('Subprocess waits on stdin before exiting', testWaitOnStreamEnd, 0); test('Subprocess waits on stdio[*] before exiting', testWaitOnStreamEnd, 3); ================================================ FILE: test/resolve/wait-abort.js ================================================ import {setImmediate} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {prematureClose} from '../helpers/stdio.js'; import {noopReadable, noopWritable, noopDuplex} from '../helpers/stream.js'; import { endOptionStream, destroyOptionStream, destroySubprocessStream, getStreamStdio, } from '../helpers/wait.js'; setFixtureDirectory(); const noop = () => {}; // eslint-disable-next-line max-params const testStreamAbortWait = async (t, streamMethod, stream, fdNumber, useTransform) => { const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], getStreamStdio(fdNumber, stream, useTransform)); streamMethod({stream, subprocess, fdNumber}); subprocess.stdin.end(); await setImmediate(); stream.destroy(); const {stdout} = await subprocess; t.is(stdout, ''); t.true(stream.destroyed); }; test('Keeps running when stdin option is used and subprocess.stdin ends', testStreamAbortWait, noop, noopReadable(), 0, false); test('Keeps running when stdin option is used and subprocess.stdin Duplex ends', testStreamAbortWait, noop, noopDuplex(), 0, false); test('Keeps running when input stdio[*] option is used and input subprocess.stdio[*] ends', testStreamAbortWait, noop, noopReadable(), 3, false); test('Keeps running when stdin option is used and subprocess.stdin ends, with a transform', testStreamAbortWait, noop, noopReadable(), 0, true); test('Keeps running when stdin option is used and subprocess.stdin Duplex ends, with a transform', testStreamAbortWait, noop, noopDuplex(), 0, true); test('Keeps running when input stdio[*] option is used and input subprocess.stdio[*] ends, with a transform', testStreamAbortWait, noop, noopReadable(), 3, true); // eslint-disable-next-line max-params const testStreamAbortSuccess = async (t, streamMethod, stream, fdNumber, useTransform) => { const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], getStreamStdio(fdNumber, stream, useTransform)); streamMethod({stream, subprocess, fdNumber}); subprocess.stdin.end(); const {stdout} = await subprocess; t.is(stdout, ''); t.true(stream.destroyed); }; test('Passes when stdin option aborts', testStreamAbortSuccess, destroyOptionStream, noopReadable(), 0, false); test('Passes when stdin option Duplex aborts', testStreamAbortSuccess, destroyOptionStream, noopDuplex(), 0, false); test('Passes when input stdio[*] option aborts', testStreamAbortSuccess, destroyOptionStream, noopReadable(), 3, false); test('Passes when stdout option ends with no more writes', testStreamAbortSuccess, endOptionStream, noopWritable(), 1, false); test('Passes when stderr option ends with no more writes', testStreamAbortSuccess, endOptionStream, noopWritable(), 2, false); test('Passes when output stdio[*] option ends with no more writes', testStreamAbortSuccess, endOptionStream, noopWritable(), 3, false); test('Passes when subprocess.stdout aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 1, false); test('Passes when subprocess.stdout Duplex aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 1, false); test('Passes when subprocess.stderr aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 2, false); test('Passes when subprocess.stderr Duplex aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 2, false); test('Passes when output subprocess.stdio[*] aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 3, false); test('Passes when output subprocess.stdio[*] Duplex aborts with no more writes', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 3, false); test('Passes when stdin option aborts, with a transform', testStreamAbortSuccess, destroyOptionStream, noopReadable(), 0, true); test('Passes when stdin option Duplex aborts, with a transform', testStreamAbortSuccess, destroyOptionStream, noopDuplex(), 0, true); test('Passes when input stdio[*] option aborts, with a transform', testStreamAbortSuccess, destroyOptionStream, noopReadable(), 3, true); test('Passes when stdout option ends with no more writes, with a transform', testStreamAbortSuccess, endOptionStream, noopWritable(), 1, true); test('Passes when stderr option ends with no more writes, with a transform', testStreamAbortSuccess, endOptionStream, noopWritable(), 2, true); test('Passes when output stdio[*] option ends with no more writes, with a transform', testStreamAbortSuccess, endOptionStream, noopWritable(), 3, true); test('Passes when subprocess.stdout aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 1, true); test('Passes when subprocess.stdout Duplex aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 1, true); test('Passes when subprocess.stderr aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 2, true); test('Passes when subprocess.stderr Duplex aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 2, true); test('Passes when output subprocess.stdio[*] aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopWritable(), 3, true); test('Passes when output subprocess.stdio[*] Duplex aborts with no more writes, with a transform', testStreamAbortSuccess, destroySubprocessStream, noopDuplex(), 3, true); // eslint-disable-next-line max-params const testStreamAbortFail = async (t, streamMethod, stream, fdNumber, useTransform) => { const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], getStreamStdio(fdNumber, stream, useTransform)); streamMethod({stream, subprocess, fdNumber}); if (fdNumber !== 0) { subprocess.stdin.end(); } const error = await t.throwsAsync(subprocess); t.like(error, {...prematureClose, exitCode: 0}); t.true(stream.destroyed); }; test('Throws abort error when subprocess.stdin aborts', testStreamAbortFail, destroySubprocessStream, noopReadable(), 0, false); test('Throws abort error when subprocess.stdin Duplex aborts', testStreamAbortFail, destroySubprocessStream, noopDuplex(), 0, false); test('Throws abort error when input subprocess.stdio[*] aborts', testStreamAbortFail, destroySubprocessStream, noopReadable(), 3, false); test('Throws abort error when stdout option aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopWritable(), 1, false); test('Throws abort error when stdout option Duplex aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopDuplex(), 1, false); test('Throws abort error when stderr option aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopWritable(), 2, false); test('Throws abort error when stderr option Duplex aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopDuplex(), 2, false); test('Throws abort error when output stdio[*] option aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopWritable(), 3, false); test('Throws abort error when output stdio[*] Duplex option aborts with no more writes', testStreamAbortFail, destroyOptionStream, noopDuplex(), 3, false); test('Throws abort error when subprocess.stdin aborts, with a transform', testStreamAbortFail, destroySubprocessStream, noopReadable(), 0, true); test('Throws abort error when subprocess.stdin Duplex aborts, with a transform', testStreamAbortFail, destroySubprocessStream, noopDuplex(), 0, true); test('Throws abort error when input subprocess.stdio[*] aborts, with a transform', testStreamAbortFail, destroySubprocessStream, noopReadable(), 3, true); test('Throws abort error when stdout option aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopWritable(), 1, true); test('Throws abort error when stdout option Duplex aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopDuplex(), 1, true); test('Throws abort error when stderr option aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopWritable(), 2, true); test('Throws abort error when stderr option Duplex aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopDuplex(), 2, true); test('Throws abort error when output stdio[*] option aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopWritable(), 3, true); test('Throws abort error when output stdio[*] Duplex option aborts with no more writes, with a transform', testStreamAbortFail, destroyOptionStream, noopDuplex(), 3, true); ================================================ FILE: test/resolve/wait-epipe.js ================================================ import {setImmediate} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {assertEpipe} from '../helpers/stdio.js'; import {foobarString} from '../helpers/input.js'; import {noopWritable, noopDuplex} from '../helpers/stream.js'; import { endOptionStream, destroyOptionStream, destroySubprocessStream, getStreamStdio, } from '../helpers/wait.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testStreamEpipeFail = async (t, streamMethod, stream, fdNumber, useTransform) => { const subprocess = execa('noop-stdin-fd.js', [`${fdNumber}`], getStreamStdio(fdNumber, stream, useTransform)); streamMethod({stream, subprocess, fdNumber}); await setImmediate(); subprocess.stdin.end(foobarString); const {exitCode, stdio, stderr} = await t.throwsAsync(subprocess); t.is(exitCode, 1); t.is(stdio[fdNumber], ''); t.true(stream.destroyed); assertEpipe(t, stderr, fdNumber); }; test('Throws EPIPE when stdout option ends with more writes', testStreamEpipeFail, endOptionStream, noopWritable(), 1, false); test('Throws EPIPE when stdout option aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopWritable(), 1, false); test('Throws EPIPE when stdout option Duplex aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 1, false); test('Throws EPIPE when stderr option ends with more writes', testStreamEpipeFail, endOptionStream, noopWritable(), 2, false); test('Throws EPIPE when stderr option aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopWritable(), 2, false); test('Throws EPIPE when stderr option Duplex aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 2, false); test('Throws EPIPE when output stdio[*] option ends with more writes', testStreamEpipeFail, endOptionStream, noopWritable(), 3, false); test('Throws EPIPE when output stdio[*] option aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopWritable(), 3, false); test('Throws EPIPE when output stdio[*] option Duplex aborts with more writes', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 3, false); test('Throws EPIPE when subprocess.stdout aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 1, false); test('Throws EPIPE when subprocess.stdout Duplex aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 1, false); test('Throws EPIPE when subprocess.stderr aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 2, false); test('Throws EPIPE when subprocess.stderr Duplex aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 2, false); test('Throws EPIPE when output subprocess.stdio[*] aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 3, false); test('Throws EPIPE when output subprocess.stdio[*] Duplex aborts with more writes', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 3, false); test('Throws EPIPE when stdout option ends with more writes, with a transform', testStreamEpipeFail, endOptionStream, noopWritable(), 1, true); test('Throws EPIPE when stdout option aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopWritable(), 1, true); test('Throws EPIPE when stdout option Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 1, true); test('Throws EPIPE when stderr option ends with more writes, with a transform', testStreamEpipeFail, endOptionStream, noopWritable(), 2, true); test('Throws EPIPE when stderr option aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopWritable(), 2, true); test('Throws EPIPE when stderr option Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 2, true); test('Throws EPIPE when output stdio[*] option ends with more writes, with a transform', testStreamEpipeFail, endOptionStream, noopWritable(), 3, true); test('Throws EPIPE when output stdio[*] option aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopWritable(), 3, true); test('Throws EPIPE when output stdio[*] option Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroyOptionStream, noopDuplex(), 3, true); test('Throws EPIPE when subprocess.stdout aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 1, true); test('Throws EPIPE when subprocess.stdout Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 1, true); test('Throws EPIPE when subprocess.stderr aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 2, true); test('Throws EPIPE when subprocess.stderr Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 2, true); test('Throws EPIPE when output subprocess.stdio[*] aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopWritable(), 3, true); test('Throws EPIPE when output subprocess.stdio[*] Duplex aborts with more writes, with a transform', testStreamEpipeFail, destroySubprocessStream, noopDuplex(), 3, true); ================================================ FILE: test/resolve/wait-error.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {noopReadable, noopWritable, noopDuplex} from '../helpers/stream.js'; import {destroyOptionStream, destroySubprocessStream, getStreamStdio} from '../helpers/wait.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testStreamError = async (t, streamMethod, stream, fdNumber, useTransform) => { const subprocess = execa('empty.js', getStreamStdio(fdNumber, stream, useTransform)); const cause = new Error('test'); streamMethod({ stream, subprocess, fdNumber, error: cause, }); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.is(error.exitCode, 0); t.is(error.signal, undefined); t.false(error.isTerminated); t.true(error.failed); t.true(stream.destroyed); }; test('Throws stream error when stdin option errors', testStreamError, destroyOptionStream, noopReadable(), 0, false); test('Throws stream error when stdin option Duplex errors', testStreamError, destroyOptionStream, noopDuplex(), 0, false); test('Throws stream error when stdout option errors', testStreamError, destroyOptionStream, noopWritable(), 1, false); test('Throws stream error when stdout option Duplex errors', testStreamError, destroyOptionStream, noopDuplex(), 1, false); test('Throws stream error when stderr option errors', testStreamError, destroyOptionStream, noopWritable(), 2, false); test('Throws stream error when stderr option Duplex errors', testStreamError, destroyOptionStream, noopDuplex(), 2, false); test('Throws stream error when output stdio[*] option errors', testStreamError, destroyOptionStream, noopWritable(), 3, false); test('Throws stream error when output stdio[*] Duplex option errors', testStreamError, destroyOptionStream, noopDuplex(), 3, false); test('Throws stream error when input stdio[*] option errors', testStreamError, destroyOptionStream, noopReadable(), 3, false); test('Throws stream error when subprocess.stdin errors', testStreamError, destroySubprocessStream, noopReadable(), 0, false); test('Throws stream error when subprocess.stdin Duplex errors', testStreamError, destroySubprocessStream, noopDuplex(), 0, false); test('Throws stream error when subprocess.stdout errors', testStreamError, destroySubprocessStream, noopWritable(), 1, false); test('Throws stream error when subprocess.stdout Duplex errors', testStreamError, destroySubprocessStream, noopDuplex(), 1, false); test('Throws stream error when subprocess.stderr errors', testStreamError, destroySubprocessStream, noopWritable(), 2, false); test('Throws stream error when subprocess.stderr Duplex errors', testStreamError, destroySubprocessStream, noopDuplex(), 2, false); test('Throws stream error when output subprocess.stdio[*] errors', testStreamError, destroySubprocessStream, noopWritable(), 3, false); test('Throws stream error when output subprocess.stdio[*] Duplex errors', testStreamError, destroySubprocessStream, noopDuplex(), 3, false); test('Throws stream error when input subprocess.stdio[*] errors', testStreamError, destroySubprocessStream, noopReadable(), 3, false); test('Throws stream error when stdin option errors, with a transform', testStreamError, destroyOptionStream, noopReadable(), 0, true); test('Throws stream error when stdin option Duplex errors, with a transform', testStreamError, destroyOptionStream, noopDuplex(), 0, true); test('Throws stream error when stdout option errors, with a transform', testStreamError, destroyOptionStream, noopWritable(), 1, true); test('Throws stream error when stdout option Duplex errors, with a transform', testStreamError, destroyOptionStream, noopDuplex(), 1, true); test('Throws stream error when stderr option errors, with a transform', testStreamError, destroyOptionStream, noopWritable(), 2, true); test('Throws stream error when stderr option Duplex errors, with a transform', testStreamError, destroyOptionStream, noopDuplex(), 2, true); test('Throws stream error when output stdio[*] option errors, with a transform', testStreamError, destroyOptionStream, noopWritable(), 3, true); test('Throws stream error when output stdio[*] Duplex option errors, with a transform', testStreamError, destroyOptionStream, noopDuplex(), 3, true); test('Throws stream error when input stdio[*] option errors, with a transform', testStreamError, destroyOptionStream, noopReadable(), 3, true); test('Throws stream error when subprocess.stdin errors, with a transform', testStreamError, destroySubprocessStream, noopReadable(), 0, true); test('Throws stream error when subprocess.stdin Duplex errors, with a transform', testStreamError, destroySubprocessStream, noopDuplex(), 0, true); test('Throws stream error when subprocess.stdout errors, with a transform', testStreamError, destroySubprocessStream, noopWritable(), 1, true); test('Throws stream error when subprocess.stdout Duplex errors, with a transform', testStreamError, destroySubprocessStream, noopDuplex(), 1, true); test('Throws stream error when subprocess.stderr errors, with a transform', testStreamError, destroySubprocessStream, noopWritable(), 2, true); test('Throws stream error when subprocess.stderr Duplex errors, with a transform', testStreamError, destroySubprocessStream, noopDuplex(), 2, true); test('Throws stream error when output subprocess.stdio[*] errors, with a transform', testStreamError, destroySubprocessStream, noopWritable(), 3, true); test('Throws stream error when output subprocess.stdio[*] Duplex errors, with a transform', testStreamError, destroySubprocessStream, noopDuplex(), 3, true); test('Throws stream error when input subprocess.stdio[*] errors, with a transform', testStreamError, destroySubprocessStream, noopReadable(), 3, true); ================================================ FILE: test/resolve/wait-subprocess.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; setFixtureDirectory(); const testIgnore = async (t, fdNumber, execaMethod) => { const result = await execaMethod('noop.js', getStdio(fdNumber, 'ignore')); t.is(result.stdio[fdNumber], undefined); }; test('stdout is undefined if ignored', testIgnore, 1, execa); test('stderr is undefined if ignored', testIgnore, 2, execa); test('stdio[*] is undefined if ignored', testIgnore, 3, execa); test('stdout is undefined if ignored - sync', testIgnore, 1, execaSync); test('stderr is undefined if ignored - sync', testIgnore, 2, execaSync); test('stdio[*] is undefined if ignored - sync', testIgnore, 3, execaSync); const testSubprocessEventsCleanup = async (t, fixtureName) => { const subprocess = execa(fixtureName, {reject: false}); t.deepEqual(subprocess.eventNames().map(String).sort(), ['error', 'exit', 'spawn']); await subprocess; t.deepEqual(subprocess.eventNames(), []); }; test('subprocess listeners are cleaned up on success', testSubprocessEventsCleanup, 'empty.js'); test('subprocess listeners are cleaned up on failure', testSubprocessEventsCleanup, 'fail.js'); test('Aborting stdout should not abort stderr nor all', async t => { const subprocess = execa('empty.js', {all: true}); subprocess.stdout.destroy(); t.false(subprocess.stdout.readable); t.true(subprocess.stderr.readable); t.true(subprocess.all.readable); await subprocess; t.false(subprocess.stdout.readableEnded); t.is(subprocess.stdout.errored, null); t.true(subprocess.stdout.destroyed); t.true(subprocess.stderr.readableEnded); t.is(subprocess.stderr.errored, null); t.true(subprocess.stderr.destroyed); t.true(subprocess.all.readableEnded); t.is(subprocess.all.errored, null); t.true(subprocess.all.destroyed); }); ================================================ FILE: test/return/duration.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getEarlyErrorSubprocess, getEarlyErrorSubprocessSync} from '../helpers/early-error.js'; setFixtureDirectory(); const assertDurationMs = (t, durationMs) => { t.is(typeof durationMs, 'number'); t.true(Number.isFinite(durationMs)); t.not(durationMs, 0); t.true(durationMs > 0); }; test('result.durationMs', async t => { const {durationMs} = await execa('empty.js'); assertDurationMs(t, durationMs); }); test('result.durationMs - sync', t => { const {durationMs} = execaSync('empty.js'); assertDurationMs(t, durationMs); }); test('error.durationMs', async t => { const {durationMs} = await t.throwsAsync(execa('fail.js')); assertDurationMs(t, durationMs); }); test('error.durationMs - sync', t => { const {durationMs} = t.throws(() => { execaSync('fail.js'); }); assertDurationMs(t, durationMs); }); test('error.durationMs - early validation', async t => { const {durationMs} = await t.throwsAsync(getEarlyErrorSubprocess()); assertDurationMs(t, durationMs); }); test('error.durationMs - early validation, sync', t => { const {durationMs} = t.throws(getEarlyErrorSubprocessSync); assertDurationMs(t, durationMs); }); test('error.durationMs - unpipeSignal', async t => { const {durationMs} = await t.throwsAsync(execa('noop.js').pipe('stdin.js', {signal: AbortSignal.abort()})); assertDurationMs(t, durationMs); }); test('error.durationMs - pipe validation', async t => { const {durationMs} = await t.throwsAsync(execa('noop.js').pipe(false)); assertDurationMs(t, durationMs); }); test.serial('result.durationMs is accurate', async t => { const minDurationMs = 1e3; const {durationMs} = await execa('delay.js', [minDurationMs]); t.true(durationMs >= minDurationMs); }); ================================================ FILE: test/return/early-error.js ================================================ import {arch} from 'node:os'; import process from 'node:process'; import test from 'ava'; import {execa, execaSync, $} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import { earlyErrorOptions, getEarlyErrorSubprocess, getEarlyErrorSubprocessSync, expectedEarlyError, expectedEarlyErrorSync, } from '../helpers/early-error.js'; setFixtureDirectory(); const isWindows = process.platform === 'win32'; const ENOENT_REGEXP = isWindows ? /failed with exit code 1/ : /spawn.* ENOENT/; test('execaSync() throws error if ENOENT', t => { t.throws(() => { execaSync('foo'); }, {message: ENOENT_REGEXP}); }); const testEarlyErrorShape = async (t, reject) => { const subprocess = getEarlyErrorSubprocess({reject}); t.notThrows(() => { // eslint-disable-next-line promise/prefer-await-to-then subprocess.catch(() => {}); subprocess.unref(); subprocess.on('error', () => {}); }); }; test('child_process.spawn() early errors have correct shape', testEarlyErrorShape, true); test('child_process.spawn() early errors have correct shape - reject false', testEarlyErrorShape, false); test('child_process.spawn() early errors are propagated', async t => { await t.throwsAsync(getEarlyErrorSubprocess(), expectedEarlyError); }); test('child_process.spawn() early errors are returned', async t => { const {failed} = await getEarlyErrorSubprocess({reject: false}); t.true(failed); }); test('child_process.spawnSync() early errors are propagated with a correct shape', t => { t.throws(getEarlyErrorSubprocessSync, expectedEarlyErrorSync); }); test('child_process.spawnSync() early errors are propagated with a correct shape - reject false', t => { const {failed} = getEarlyErrorSubprocessSync({reject: false}); t.true(failed); }); if (!isWindows) { test('execa() rejects if running non-executable', async t => { await t.throwsAsync(execa('non-executable.js')); }); test('execa() rejects with correct error and doesn\'t throw if running non-executable with input', async t => { await t.throwsAsync(execa('non-executable.js', {input: 'Hey!'}), {message: /EACCES/}); }); if (arch() === 'x64') { test('write to fast-exit subprocess', async t => { // Try-catch here is necessary, because this test is not 100% accurate // Sometimes subprocess can manage to accept input before exiting try { await execa(`fast-exit-${process.platform}`, [], {input: 'data'}); t.pass(); } catch (error) { t.is(error.code, 'EPIPE'); } }); } } const testEarlyErrorPipe = async (t, getSubprocess) => { await t.throwsAsync(getSubprocess(), expectedEarlyError); }; test('child_process.spawn() early errors on source can use .pipe()', testEarlyErrorPipe, () => getEarlyErrorSubprocess().pipe(execa('empty.js'))); test('child_process.spawn() early errors on destination can use .pipe()', testEarlyErrorPipe, () => execa('empty.js').pipe(getEarlyErrorSubprocess())); test('child_process.spawn() early errors on source and destination can use .pipe()', testEarlyErrorPipe, () => getEarlyErrorSubprocess().pipe(getEarlyErrorSubprocess())); test('child_process.spawn() early errors can use .pipe() multiple times', testEarlyErrorPipe, () => getEarlyErrorSubprocess().pipe(getEarlyErrorSubprocess()).pipe(getEarlyErrorSubprocess())); test('child_process.spawn() early errors can use .pipe``', testEarlyErrorPipe, () => $(earlyErrorOptions)`empty.js`.pipe(earlyErrorOptions)`empty.js`); test('child_process.spawn() early errors can use .pipe`` multiple times', testEarlyErrorPipe, () => $(earlyErrorOptions)`empty.js`.pipe(earlyErrorOptions)`empty.js`.pipe`empty.js`); const testEarlyErrorConvertor = async (t, streamMethod) => { const subprocess = getEarlyErrorSubprocess(); const stream = subprocess[streamMethod](); stream.on('close', () => {}); stream.read?.(); stream.write?.('.'); await t.throwsAsync(subprocess); }; test('child_process.spawn() early errors can use .readable()', testEarlyErrorConvertor, 'readable'); test('child_process.spawn() early errors can use .writable()', testEarlyErrorConvertor, 'writable'); test('child_process.spawn() early errors can use .duplex()', testEarlyErrorConvertor, 'duplex'); const testEarlyErrorStream = async (t, getStreamProperty, options) => { const subprocess = getEarlyErrorSubprocess(options); const stream = getStreamProperty(subprocess); stream.on('close', () => {}); stream.read?.(); stream.end?.(); await t.throwsAsync(subprocess); }; test('child_process.spawn() early errors can use .stdin', testEarlyErrorStream, ({stdin}) => stdin); test('child_process.spawn() early errors can use .stdout', testEarlyErrorStream, ({stdout}) => stdout); test('child_process.spawn() early errors can use .stderr', testEarlyErrorStream, ({stderr}) => stderr); test('child_process.spawn() early errors can use .stdio[1]', testEarlyErrorStream, ({stdio}) => stdio[1]); test('child_process.spawn() early errors can use .stdio[3]', testEarlyErrorStream, ({stdio}) => stdio[3], fullStdio); test('child_process.spawn() early errors can use .all', testEarlyErrorStream, ({all}) => all, {all: true}); ================================================ FILE: test/return/final-error.js ================================================ import test from 'ava'; import { execa, execaSync, ExecaError, ExecaSyncError, } from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {getEarlyErrorSubprocess, getEarlyErrorSubprocessSync} from '../helpers/early-error.js'; setFixtureDirectory(); const testUnusualError = async (t, error, expectedOriginalMessage = String(error)) => { const subprocess = execa('empty.js'); subprocess.emit('error', error); const {originalMessage, shortMessage, message} = await t.throwsAsync(subprocess); t.is(originalMessage, expectedOriginalMessage === '' ? undefined : expectedOriginalMessage); t.true(shortMessage.includes(expectedOriginalMessage)); t.is(message, shortMessage); }; test('error instance can be null', testUnusualError, null); test('error instance can be false', testUnusualError, false); test('error instance can be a string', testUnusualError, 'test'); test('error instance can be a number', testUnusualError, 0); test('error instance can be a BigInt', testUnusualError, 0n); test('error instance can be a symbol', testUnusualError, Symbol('test')); test('error instance can be a function', testUnusualError, () => {}); test('error instance can be an array', testUnusualError, ['test', 'test']); // eslint-disable-next-line unicorn/error-message test('error instance can be an error with an empty message', testUnusualError, new Error(''), ''); test('error instance can be undefined', testUnusualError, undefined, 'undefined'); test('error instance can be a plain object', async t => { const subprocess = execa('empty.js'); subprocess.emit('error', {message: foobarString}); await t.throwsAsync(subprocess, {message: new RegExp(foobarString)}); }); const runAndFail = (t, fixtureName, argument, error) => { const subprocess = execa(fixtureName, [argument]); subprocess.emit('error', error); return t.throwsAsync(subprocess); }; const testErrorCopy = async (t, getPreviousArgument, argument = 'two') => { const fixtureName = 'empty.js'; const firstArgument = 'foo'; const previousArgument = await getPreviousArgument(t, fixtureName); const previousError = await runAndFail(t, fixtureName, firstArgument, previousArgument); const error = await runAndFail(t, fixtureName, argument, previousError); const message = `Command failed: ${fixtureName} ${argument}\n${foobarString}`; t.not(error, previousError); t.is(error.cause, previousError); t.is(error.command, `${fixtureName} ${argument}`); t.is(error.message, message); t.true(error.stack.includes(message)); t.is(error.shortMessage, message); t.is(error.originalMessage, foobarString); }; test('error instance can be shared', testErrorCopy, () => new Error(foobarString)); test('error TypeError can be shared', testErrorCopy, () => new TypeError(foobarString)); test('error string can be shared', testErrorCopy, () => foobarString); test('error copy can be shared', testErrorCopy, (t, fixtureName) => runAndFail(t, fixtureName, 'bar', new Error(foobarString))); test('error with same message can be shared', testErrorCopy, () => new Error(foobarString), 'foo'); test('error.cause is not set if error.exitCode is not 0', async t => { const {exitCode, cause} = await t.throwsAsync(execa('fail.js')); t.is(exitCode, 2); t.is(cause, undefined); }); test('error.cause is not set if error.isTerminated', async t => { const subprocess = execa('forever.js'); subprocess.kill(); const {isTerminated, cause} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(cause, undefined); }); test('error.cause is not set if error.timedOut', async t => { const {timedOut, cause} = await t.throwsAsync(execa('forever.js', {timeout: 1})); t.true(timedOut); t.is(cause, undefined); }); test('error.cause is set on error event', async t => { const subprocess = execa('empty.js'); const error = new Error(foobarString); subprocess.emit('error', error); const {cause} = await t.throwsAsync(subprocess); t.is(cause, error); }); test('error.cause is set if error.isCanceled', async t => { const controller = new AbortController(); const subprocess = execa('forever.js', {cancelSignal: controller.signal}); const cause = new Error('test'); controller.abort(cause); const error = await t.throwsAsync(subprocess); t.true(error.isCanceled); t.true(error.isTerminated); t.is(error.signal, 'SIGTERM'); t.is(error.cause, cause); }); test('error.cause is not set if error.isTerminated with .kill(error)', async t => { const subprocess = execa('forever.js'); const error = new Error('test'); subprocess.kill(error); const {isTerminated, cause} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(cause, error); }); test('Error is instanceof ExecaError', async t => { await t.throwsAsync(execa('fail.js'), {instanceOf: ExecaError}); }); test('Early error is instanceof ExecaError', async t => { await t.throwsAsync(getEarlyErrorSubprocess(), {instanceOf: ExecaError}); }); test('Error is instanceof ExecaSyncError', t => { t.throws(() => { execaSync('fail.js'); }, {instanceOf: ExecaSyncError}); }); test('Early error is instanceof ExecaSyncError', t => { t.throws(() => { getEarlyErrorSubprocessSync(); }, {instanceOf: ExecaSyncError}); }); test('Pipe error is instanceof ExecaError', async t => { await t.throwsAsync(execa('empty.js').pipe(false), {instanceOf: ExecaError}); }); const assertNameShape = (t, error) => { t.false(Object.hasOwn(error, 'name')); t.true(Object.hasOwn(Object.getPrototypeOf(error), 'name')); t.false(propertyIsEnumerable.call(Object.getPrototypeOf(error), 'name')); }; const {propertyIsEnumerable} = Object.prototype; test('error.name is properly set', async t => { const error = await t.throwsAsync(execa('fail.js')); t.is(error.name, 'ExecaError'); assertNameShape(t, error); }); test('error.name is properly set - sync', async t => { const error = await t.throws(() => { execaSync('fail.js'); }); t.is(error.name, 'ExecaSyncError'); assertNameShape(t, error); }); ================================================ FILE: test/return/message.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio, getStdio} from '../helpers/stdio.js'; import {foobarString, foobarObject, foobarObjectInspect} from '../helpers/input.js'; import {QUOTE} from '../helpers/verbose.js'; import {noopGenerator, outputObjectGenerator} from '../helpers/generator.js'; setFixtureDirectory(); test('error.message contains the command', async t => { await t.throwsAsync(execa('exit.js', ['2', 'foo', 'bar']), {message: /exit.js 2 foo bar/}); }); // eslint-disable-next-line max-params const testStdioMessage = async (t, encoding, all, objectMode, execaMethod) => { const {exitCode, message} = await execaMethod('echo-fail.js', { ...getStdio(1, noopGenerator(objectMode, false, true), 4), encoding, all, reject: false, }); t.is(exitCode, 1); const output = all ? 'stdout\nstderr' : 'stderr\n\nstdout'; t.true(message.endsWith(`echo-fail.js\n\n${output}\n\nfd3`)); }; test('error.message contains stdout/stderr/stdio if available', testStdioMessage, 'utf8', false, false, execa); test('error.message contains stdout/stderr/stdio even with encoding "buffer"', testStdioMessage, 'buffer', false, false, execa); test('error.message contains all if available', testStdioMessage, 'utf8', true, false, execa); test('error.message contains all even with encoding "buffer"', testStdioMessage, 'buffer', true, false, execa); test('error.message contains stdout/stderr/stdio if available, objectMode', testStdioMessage, 'utf8', false, true, execa); test('error.message contains stdout/stderr/stdio even with encoding "buffer", objectMode', testStdioMessage, 'buffer', false, true, execa); test('error.message contains all if available, objectMode', testStdioMessage, 'utf8', true, true, execa); test('error.message contains all even with encoding "buffer", objectMode', testStdioMessage, 'buffer', true, true, execa); test('error.message contains stdout/stderr/stdio if available, sync', testStdioMessage, 'utf8', false, false, execaSync); test('error.message contains stdout/stderr/stdio even with encoding "buffer", sync', testStdioMessage, 'buffer', false, false, execaSync); test('error.message contains all if available, sync', testStdioMessage, 'utf8', true, false, execaSync); test('error.message contains all even with encoding "buffer", sync', testStdioMessage, 'buffer', true, false, execaSync); test('error.message contains stdout/stderr/stdio if available, objectMode, sync', testStdioMessage, 'utf8', false, true, execaSync); test('error.message contains stdout/stderr/stdio even with encoding "buffer", objectMode, sync', testStdioMessage, 'buffer', false, true, execaSync); test('error.message contains all if available, objectMode, sync', testStdioMessage, 'utf8', true, true, execaSync); test('error.message contains all even with encoding "buffer", objectMode, sync', testStdioMessage, 'buffer', true, true, execaSync); const testLinesMessage = async (t, encoding, stripFinalNewline, execaMethod) => { const {failed, message} = await execaMethod('noop-fail.js', ['1', `${foobarString}\n${foobarString}\n`], { lines: true, encoding, stripFinalNewline, reject: false, }); t.true(failed); t.true(message.endsWith(`noop-fail.js 1 ${QUOTE}${foobarString}\\n${foobarString}\\n${QUOTE}\n\n${foobarString}\n${foobarString}`)); }; test('error.message handles "lines: true"', testLinesMessage, 'utf8', false, execa); test('error.message handles "lines: true", stripFinalNewline', testLinesMessage, 'utf8', true, execa); test('error.message handles "lines: true", buffer', testLinesMessage, 'buffer', false, execa); test('error.message handles "lines: true", buffer, stripFinalNewline', testLinesMessage, 'buffer', true, execa); test('error.message handles "lines: true", sync', testLinesMessage, 'utf8', false, execaSync); test('error.message handles "lines: true", stripFinalNewline, sync', testLinesMessage, 'utf8', true, execaSync); test('error.message handles "lines: true", buffer, sync', testLinesMessage, 'buffer', false, execaSync); test('error.message handles "lines: true", buffer, stripFinalNewline, sync', testLinesMessage, 'buffer', true, execaSync); const testPartialIgnoreMessage = async (t, fdNumber, stdioOption, output) => { const {message} = await t.throwsAsync(execa('echo-fail.js', getStdio(fdNumber, stdioOption, 4))); t.true(message.endsWith(`echo-fail.js\n\n${output}\n\nfd3`)); }; test('error.message does not contain stdout if not available', testPartialIgnoreMessage, 1, 'ignore', 'stderr'); test('error.message does not contain stderr if not available', testPartialIgnoreMessage, 2, 'ignore', 'stdout'); test('error.message does not contain stdout if it is an object', testPartialIgnoreMessage, 1, outputObjectGenerator(), 'stderr'); test('error.message does not contain stderr if it is an object', testPartialIgnoreMessage, 2, outputObjectGenerator(), 'stdout'); const testFullIgnoreMessage = async (t, options, resultProperty) => { const {[resultProperty]: message} = await t.throwsAsync(execa('echo-fail.js', options)); t.false(message.includes('stderr')); t.false(message.includes('stdout')); t.false(message.includes('fd3')); }; test('error.message does not contain stdout/stderr/stdio if not available', testFullIgnoreMessage, {stdio: 'ignore'}, 'message'); test('error.shortMessage does not contain stdout/stderr/stdio', testFullIgnoreMessage, fullStdio, 'shortMessage'); const testErrorMessageConsistent = async (t, stdout) => { const {message} = await t.throwsAsync(execa('noop-both-fail-strict.js', [stdout, 'stderr'])); t.true(message.endsWith(' stderr\n\nstderr\n\nstdout')); }; test('error.message newlines are consistent - no newline', testErrorMessageConsistent, 'stdout'); test('error.message newlines are consistent - newline', testErrorMessageConsistent, 'stdout\n'); test('Original error.message is kept', async t => { const {originalMessage} = await t.throwsAsync(execa('noop.js', {uid: true})); t.is(originalMessage, 'The "options.uid" property must be int32. Received type boolean (true)'); }); const testIpcOutput = async (t, doubles, ipcInput, returnedMessage) => { const fixtureName = doubles ? 'ipc-echo-twice-fail.js' : 'ipc-echo-fail.js'; const {exitCode, message, ipcOutput} = await t.throwsAsync(execa(fixtureName, {ipcInput})); t.is(exitCode, 1); t.true(message.endsWith(`\n\n${doubles ? `${returnedMessage}\n${returnedMessage}` : returnedMessage}`)); t.deepEqual(ipcOutput, doubles ? [ipcInput, ipcInput] : [ipcInput]); }; test('error.message contains IPC messages, single string', testIpcOutput, false, foobarString, foobarString); test('error.message contains IPC messages, two strings', testIpcOutput, true, foobarString, foobarString); test('error.message contains IPC messages, single object', testIpcOutput, false, foobarObject, foobarObjectInspect); test('error.message contains IPC messages, two objects', testIpcOutput, true, foobarObject, foobarObjectInspect); test('error.message contains IPC messages, multiline string', testIpcOutput, false, `${foobarString}\n${foobarString}`, `${foobarString}\n${foobarString}`); test('error.message contains IPC messages, control characters', testIpcOutput, false, '\0', '\\u0000'); test('error.message does not contain IPC messages, buffer false', async t => { const {exitCode, message, ipcOutput} = await t.throwsAsync(execa('ipc-echo-fail.js', {ipcInput: foobarString, buffer: false})); t.is(exitCode, 1); t.true(message.endsWith('ipc-echo-fail.js')); t.deepEqual(ipcOutput, []); }); ================================================ FILE: test/return/output.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio, getStdio} from '../helpers/stdio.js'; import {foobarString} from '../helpers/input.js'; import { getEarlyErrorSubprocess, getEarlyErrorSubprocessSync, expectedEarlyError, expectedEarlyErrorSync, } from '../helpers/early-error.js'; setFixtureDirectory(); const testOutput = async (t, fdNumber, execaMethod) => { const {stdout, stderr, stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], fullStdio); t.is(stdio[fdNumber], foobarString); if (fdNumber === 1) { t.is(stdio[fdNumber], stdout); } else if (fdNumber === 2) { t.is(stdio[fdNumber], stderr); } }; test('can return stdout', testOutput, 1, execa); test('can return stderr', testOutput, 2, execa); test('can return output stdio[*]', testOutput, 3, execa); test('can return stdout, sync', testOutput, 1, execaSync); test('can return stderr, sync', testOutput, 2, execaSync); test('can return output stdio[*], sync', testOutput, 3, execaSync); const testNoStdin = async (t, execaMethod) => { const {stdio} = await execaMethod('noop.js', [foobarString]); t.is(stdio[0], undefined); }; test('cannot return stdin', testNoStdin, execa); test('cannot return stdin, sync', testNoStdin, execaSync); test('cannot return input stdio[*]', async t => { const {stdio} = await execa('stdin-fd.js', ['3'], getStdio(3, [[foobarString]])); t.is(stdio[3], undefined); }); test('do not try to consume streams twice', async t => { const subprocess = execa('noop.js', ['foo']); const {stdout} = await subprocess; const {stdout: stdout2} = await subprocess; t.is(stdout, 'foo'); t.is(stdout2, 'foo'); }); const testEmptyErrorStdio = async (t, execaMethod) => { const {failed, stdout, stderr, stdio} = await execaMethod('fail.js', {reject: false}); t.true(failed); t.is(stdout, ''); t.is(stderr, ''); t.deepEqual(stdio, [undefined, '', '']); }; test('empty error.stdout/stderr/stdio', testEmptyErrorStdio, execa); test('empty error.stdout/stderr/stdio, sync', testEmptyErrorStdio, execaSync); const testUndefinedErrorStdio = async (t, execaMethod) => { const {stdout, stderr, stdio} = await execaMethod('empty.js', {stdio: 'ignore'}); t.is(stdout, undefined); t.is(stderr, undefined); t.deepEqual(stdio, [undefined, undefined, undefined]); }; test('undefined error.stdout/stderr/stdio', testUndefinedErrorStdio, execa); test('undefined error.stdout/stderr/stdio, sync', testUndefinedErrorStdio, execaSync); const testEmptyAll = async (t, options, expectedValue, execaMethod) => { const {all} = await execaMethod('empty.js', options); t.is(all, expectedValue); }; test('empty error.all', testEmptyAll, {all: true}, '', execa); test('undefined error.all', testEmptyAll, {}, undefined, execa); test('ignored error.all', testEmptyAll, {all: true, stdio: 'ignore'}, undefined, execa); test('empty error.all, sync', testEmptyAll, {all: true}, '', execaSync); test('undefined error.all, sync', testEmptyAll, {}, undefined, execaSync); test('ignored error.all, sync', testEmptyAll, {all: true, stdio: 'ignore'}, undefined, execaSync); test('empty error.stdio[0] even with input', async t => { const {stdio} = await t.throwsAsync(execa('fail.js', {input: 'test'})); t.is(stdio[0], undefined); }); const validateSpawnErrorStdio = (t, {stdout, stderr, stdio, all}) => { t.is(stdout, undefined); t.is(stderr, undefined); t.is(all, undefined); t.deepEqual(stdio, [undefined, undefined, undefined]); }; test('stdout/stderr/all/stdio on subprocess spawning errors', async t => { const error = await t.throwsAsync(getEarlyErrorSubprocess({all: true})); t.like(error, expectedEarlyError); validateSpawnErrorStdio(t, error); }); test('stdout/stderr/all/stdio on subprocess spawning errors, sync', t => { const error = t.throws(() => getEarlyErrorSubprocessSync({all: true})); t.like(error, expectedEarlyErrorSync); validateSpawnErrorStdio(t, error); }); const testErrorOutput = async (t, execaMethod) => { const {failed, stdout, stderr, stdio} = await execaMethod('echo-fail.js', {...fullStdio, reject: false}); t.true(failed); t.is(stdout, 'stdout'); t.is(stderr, 'stderr'); t.deepEqual(stdio, [undefined, 'stdout', 'stderr', 'fd3']); }; test('error.stdout/stderr/stdio is defined', testErrorOutput, execa); test('error.stdout/stderr/stdio is defined, sync', testErrorOutput, execaSync); test('ipc on subprocess spawning errors', async t => { const error = await t.throwsAsync(getEarlyErrorSubprocess({ipc: true})); t.like(error, expectedEarlyError); t.deepEqual(error.ipcOutput, []); }); const testEarlyErrorNoIpc = async (t, options) => { const error = await t.throwsAsync(getEarlyErrorSubprocess(options)); t.like(error, expectedEarlyError); t.deepEqual(error.ipcOutput, []); }; test('ipc on subprocess spawning errors, ipc false', testEarlyErrorNoIpc, {ipc: false}); test('ipc on subprocess spawning errors, buffer false', testEarlyErrorNoIpc, {buffer: false}); ================================================ FILE: test/return/reject.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); test('skip throwing when using reject option', async t => { const {exitCode} = await execa('fail.js', {reject: false}); t.is(exitCode, 2); }); test('skip throwing when using reject option in sync mode', t => { const {exitCode} = execaSync('fail.js', {reject: false}); t.is(exitCode, 2); }); ================================================ FILE: test/return/result.js ================================================ import process from 'node:process'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; const isWindows = process.platform === 'win32'; setFixtureDirectory(); const testSuccessShape = async (t, execaMethod) => { const result = await execaMethod('empty.js', {...fullStdio, all: true}); t.deepEqual(Reflect.ownKeys(result), [ 'command', 'escapedCommand', 'cwd', 'durationMs', 'failed', 'timedOut', 'isCanceled', 'isGracefullyCanceled', 'isTerminated', 'isMaxBuffer', 'isForcefullyTerminated', 'exitCode', 'stdout', 'stderr', 'all', 'stdio', 'ipcOutput', 'pipedFrom', ]); }; test('Return value properties are not missing and are ordered', testSuccessShape, execa); test('Return value properties are not missing and are ordered, sync', testSuccessShape, execaSync); const testErrorShape = async (t, execaMethod) => { const error = await execaMethod('fail.js', {...fullStdio, all: true, reject: false}); t.is(error.exitCode, 2); t.deepEqual(Reflect.ownKeys(error), [ 'stack', 'message', 'shortMessage', 'command', 'escapedCommand', 'cwd', 'durationMs', 'failed', 'timedOut', 'isCanceled', 'isGracefullyCanceled', 'isTerminated', 'isMaxBuffer', 'isForcefullyTerminated', 'exitCode', 'stdout', 'stderr', 'all', 'stdio', 'ipcOutput', 'pipedFrom', ]); }; test('Error properties are not missing and are ordered', testErrorShape, execa); test('Error properties are not missing and are ordered, sync', testErrorShape, execaSync); test('failed is false on success', async t => { const {failed} = await execa('noop.js', ['foo']); t.false(failed); }); test('failed is true on failure', async t => { const {failed} = await t.throwsAsync(execa('fail.js')); t.true(failed); }); test('error.isTerminated is true if subprocess was killed directly', async t => { const subprocess = execa('forever.js', {killSignal: 'SIGINT'}); subprocess.kill(); const {isTerminated, signal, originalMessage, message, shortMessage} = await t.throwsAsync(subprocess, {message: /was killed with SIGINT/}); t.true(isTerminated); t.is(signal, 'SIGINT'); t.is(originalMessage, undefined); t.is(shortMessage, 'Command was killed with SIGINT (User interruption with CTRL-C): forever.js'); t.is(message, shortMessage); }); test('error.isTerminated is true if subprocess was killed indirectly', async t => { const subprocess = execa('forever.js', {killSignal: 'SIGHUP'}); process.kill(subprocess.pid, 'SIGINT'); // `subprocess.kill()` is emulated by Node.js on Windows if (isWindows) { const {isTerminated, signal} = await t.throwsAsync(subprocess, {message: /failed with exit code 1/}); t.is(isTerminated, false); t.is(signal, undefined); } else { const {isTerminated, signal} = await t.throwsAsync(subprocess, {message: /was killed with SIGINT/}); t.is(isTerminated, true); t.is(signal, 'SIGINT'); } }); test('result.isTerminated is false if not killed', async t => { const {isTerminated} = await execa('noop.js'); t.false(isTerminated); }); test('result.isTerminated is false if not killed and subprocess.kill() was called', async t => { const subprocess = execa('noop.js'); subprocess.kill(0); t.true(subprocess.killed); const {isTerminated} = await subprocess; t.false(isTerminated); }); test('result.isTerminated is false if not killed, in sync mode', t => { const {isTerminated} = execaSync('noop.js'); t.false(isTerminated); }); test('result.isTerminated is false on subprocess error', async t => { const {isTerminated} = await t.throwsAsync(execa('wrong command')); t.false(isTerminated); }); test('result.isTerminated is false on subprocess error, in sync mode', t => { const {isTerminated} = t.throws(() => { execaSync('wrong command'); }); t.false(isTerminated); }); test('error.code is undefined on success', async t => { const {code} = await execa('noop.js'); t.is(code, undefined); }); test('error.code is defined on failure if applicable', async t => { const {code} = await t.throwsAsync(execa('noop.js', {uid: true})); t.is(code, 'ERR_INVALID_ARG_TYPE'); }); ================================================ FILE: test/stdio/direction.js ================================================ import {readFile, writeFile, rm} from 'node:fs/promises'; import process from 'node:process'; import test from 'ava'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {getStdio} from '../helpers/stdio.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const testInputOutput = (t, stdioOption, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(3, [new ReadableStream(), stdioOption])); }, {message: /readable and writable/}); }; test('Cannot pass both readable and writable values to stdio[*] - WritableStream', testInputOutput, new WritableStream(), execa); test('Cannot pass both readable and writable values to stdio[*] - 1', testInputOutput, 1, execa); test('Cannot pass both readable and writable values to stdio[*] - 2', testInputOutput, 2, execa); test('Cannot pass both readable and writable values to stdio[*] - process.stdout', testInputOutput, process.stdout, execa); test('Cannot pass both readable and writable values to stdio[*] - process.stderr', testInputOutput, process.stderr, execa); test('Cannot pass both readable and writable values to stdio[*] - WritableStream - sync', testInputOutput, new WritableStream(), execaSync); test('Cannot pass both readable and writable values to stdio[*] - 1 - sync', testInputOutput, 1, execaSync); test('Cannot pass both readable and writable values to stdio[*] - 2 - sync', testInputOutput, 2, execaSync); test('Cannot pass both readable and writable values to stdio[*] - process.stdout - sync', testInputOutput, process.stdout, execaSync); test('Cannot pass both readable and writable values to stdio[*] - process.stderr - sync', testInputOutput, process.stderr, execaSync); const testAmbiguousDirection = async (t, execaMethod) => { const [filePathOne, filePathTwo] = [tempfile(), tempfile()]; await execaMethod('noop-fd.js', ['3', foobarString], getStdio(3, [{file: filePathOne}, {file: filePathTwo}])); t.deepEqual( await Promise.all([readFile(filePathOne, 'utf8'), readFile(filePathTwo, 'utf8')]), [foobarString, foobarString], ); await Promise.all([rm(filePathOne), rm(filePathTwo)]); }; test('stdio[*] default direction is output', testAmbiguousDirection, execa); test('stdio[*] default direction is output - sync', testAmbiguousDirection, execaSync); const testAmbiguousMultiple = async (t, fdNumber) => { const filePath = tempfile(); await writeFile(filePath, foobarString); const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [{file: filePath}, ['foo', 'bar']])); t.is(stdout, `${foobarString}${foobarString}`); await rm(filePath); }; test('stdin ambiguous direction is influenced by other values', testAmbiguousMultiple, 0); test('stdio[*] ambiguous direction is influenced by other values', testAmbiguousMultiple, 3); ================================================ FILE: test/stdio/duplex.js ================================================ import {createHash} from 'node:crypto'; import {promisify} from 'node:util'; import {createGzip, gunzip} from 'node:zlib'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { foobarString, foobarObject, foobarUppercase, foobarUppercaseHex, } from '../helpers/input.js'; import {uppercaseEncodingDuplex, getOutputDuplex} from '../helpers/duplex.js'; setFixtureDirectory(); test('Can use crypto.createHash()', async t => { const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: {transform: createHash('sha1')}, encoding: 'hex'}); const expectedStdout = createHash('sha1').update(foobarString).digest('hex'); t.is(stdout, expectedStdout); }); test('Can use zlib.createGzip()', async t => { const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: {transform: createGzip()}, encoding: 'buffer'}); const decompressedStdout = await promisify(gunzip)(stdout); t.is(decompressedStdout.toString(), foobarString); }); test('Can use encoding "hex"', async t => { const {transform} = uppercaseEncodingDuplex('hex')(); t.is(transform.readableEncoding, 'hex'); const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: {transform}}); t.is(stdout, foobarUppercaseHex); }); test('Cannot use objectMode: true with duplex.readableObjectMode: false', t => { t.throws(() => { execa('noop-fd.js', ['1', foobarString], {stdout: uppercaseEncodingDuplex(undefined, false)(true)}); }, {message: /cannot be `false` if `new Duplex\({objectMode: true}\)`/}); }); test('Cannot use objectMode: false with duplex.readableObjectMode: true', t => { t.throws(() => { execa('noop-fd.js', ['1', foobarString], {stdout: uppercaseEncodingDuplex(undefined, true)(false)}); }, {message: /can only be `true` if `new Duplex\({objectMode: true}\)`/}); }); const testObjectModeFalse = async (t, objectMode) => { const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: uppercaseEncodingDuplex(undefined, objectMode)(false)}); t.is(stdout, foobarUppercase); }; test('Can use objectMode: false with duplex.readableObjectMode: false', testObjectModeFalse, false); test('Can use objectMode: undefined with duplex.readableObjectMode: false', testObjectModeFalse, undefined); const testObjectModeTrue = async (t, objectMode) => { const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: getOutputDuplex(foobarObject, objectMode)(true)}); t.deepEqual(stdout, [foobarObject]); }; test('Can use objectMode: true with duplex.readableObjectMode: true', testObjectModeTrue, true); test('Can use objectMode: undefined with duplex.readableObjectMode: true', testObjectModeTrue, undefined); ================================================ FILE: test/stdio/duplicate.js ================================================ import {once} from 'node:events'; import {createReadStream, createWriteStream} from 'node:fs'; import {readFile, writeFile, rm} from 'node:fs/promises'; import {Readable, Writable} from 'node:stream'; import {pathToFileURL} from 'node:url'; import test from 'ava'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import { uppercaseGenerator, appendGenerator, appendAsyncGenerator, casedSuffix, } from '../helpers/generator.js'; import {appendDuplex} from '../helpers/duplex.js'; import {appendWebTransform} from '../helpers/web-transform.js'; import {foobarString, foobarUint8Array, foobarUppercase} from '../helpers/input.js'; import {fullStdio} from '../helpers/stdio.js'; import {nestedSubprocess} from '../helpers/nested.js'; import {getAbsolutePath} from '../helpers/file-path.js'; import {noopDuplex} from '../helpers/stream.js'; setFixtureDirectory(); const getNativeStream = stream => stream; const getNonNativeStream = stream => ['pipe', stream]; const getWebWritableStream = stream => Writable.toWeb(stream); const getDummyDuplex = () => ({transform: noopDuplex()}); const getDummyWebTransformStream = () => new TransformStream(); const getDummyPath = async () => { const filePath = tempfile(); await writeFile(filePath, ''); return filePath; }; const getDummyFilePath = async () => ({file: await getDummyPath()}); const getDummyFileURL = async () => pathToFileURL((await getDummyPath())); const duplexName = 'a Duplex stream'; const webTransformName = 'a web TransformStream'; const filePathName = 'a file path string'; const fileURLName = 'a file URL'; const getDifferentInputs = stdioOption => ({stdio: [stdioOption, 'pipe', 'pipe', stdioOption]}); const getDifferentOutputs = stdioOption => ({stdout: stdioOption, stderr: stdioOption}); const getDifferentInputsOutputs = stdioOption => ({stdin: stdioOption, stdout: stdioOption}); const differentInputsName = '`stdin` and `stdio[3]`'; const differentOutputsName = '`stdout` and `stderr`'; const differentInputsOutputsName = '`stdin` and `stdout`'; test('Can use multiple "pipe" on same input file descriptor', async t => { const subprocess = execa('stdin.js', {stdin: ['pipe', 'pipe']}); subprocess.stdin.end(foobarString); const {stdout} = await subprocess; t.is(stdout, foobarString); }); const testTwoPipeOutput = async (t, execaMethod) => { const {stdout} = await execaMethod('noop.js', [foobarString], {stdout: ['pipe', 'pipe']}); t.is(stdout, foobarString); }; test('Can use multiple "pipe" on same output file descriptor', testTwoPipeOutput, execa); test('Can use multiple "pipe" on same output file descriptor, sync', testTwoPipeOutput, execaSync); test('Can repeat same stream on same input file descriptor', async t => { const stream = Readable.from([foobarString]); const {stdout} = await execa('stdin.js', {stdin: ['pipe', stream, stream]}); t.is(stdout, foobarString); }); test('Can repeat same stream on same output file descriptor', async t => { let stdout = ''; const stream = new Writable({ write(chunk, encoding, done) { stdout += chunk.toString(); done(); }, }); await execa('noop-fd.js', ['1', foobarString], {stdout: ['pipe', stream, stream]}); t.is(stdout, foobarString); }); // eslint-disable-next-line max-params const testTwoGenerators = async (t, producesTwo, execaMethod, firstGenerator, secondGenerator = firstGenerator) => { const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], {stdout: [firstGenerator, secondGenerator]}); const expectedSuffix = producesTwo ? `${casedSuffix}${casedSuffix}` : casedSuffix; t.is(stdout, `${foobarString}${expectedSuffix}`); }; test('Can use multiple identical generators', testTwoGenerators, true, execa, appendGenerator().transform); test('Can use multiple identical generators, options object', testTwoGenerators, true, execa, appendGenerator()); test('Can use multiple identical generators, async', testTwoGenerators, true, execa, appendAsyncGenerator().transform); test('Can use multiple identical generators, options object, async', testTwoGenerators, true, execa, appendAsyncGenerator()); test('Can use multiple identical generators, sync', testTwoGenerators, true, execaSync, appendGenerator().transform); test('Can use multiple identical generators, options object, sync', testTwoGenerators, true, execaSync, appendGenerator()); test('Ignore duplicate identical duplexes', testTwoGenerators, false, execa, appendDuplex()); test('Ignore duplicate identical webTransforms', testTwoGenerators, false, execa, appendWebTransform()); test('Can use multiple generators with duplexes', testTwoGenerators, true, execa, appendGenerator(false, false, true), appendDuplex()); test('Can use multiple generators with webTransforms', testTwoGenerators, true, execa, appendGenerator(false, false, true), appendWebTransform()); test('Can use multiple duplexes with webTransforms', testTwoGenerators, true, execa, appendDuplex(), appendWebTransform()); const testMultiplePipeOutput = async (t, execaMethod) => { const {stdout, stderr} = await execaMethod('noop-both.js', [foobarString], fullStdio); t.is(stdout, foobarString); t.is(stderr, foobarString); }; test('Can use multiple "pipe" on different output file descriptors', testMultiplePipeOutput, execa); test('Can use multiple "pipe" on different output file descriptors, sync', testMultiplePipeOutput, execaSync); test('Can re-use same generator on different input file descriptors', async t => { const {stdout} = await execa('stdin-fd-both.js', ['3'], getDifferentInputs([foobarUint8Array, uppercaseGenerator(false, false, true)])); t.is(stdout, `${foobarUppercase}${foobarUppercase}`); }); test('Can re-use same generator on different output file descriptors', async t => { const {stdout, stderr} = await execa('noop-both.js', [foobarString], getDifferentOutputs(uppercaseGenerator(false, false, true))); t.is(stdout, foobarUppercase); t.is(stderr, foobarUppercase); }); test('Can re-use same non-native Readable stream on different input file descriptors', async t => { const filePath = tempfile(); await writeFile(filePath, foobarString); const stream = createReadStream(filePath); await once(stream, 'open'); const {stdout} = await execa('stdin-fd-both.js', ['3'], getDifferentInputs([new Uint8Array(0), stream])); t.is(stdout, `${foobarString}${foobarString}`); await rm(filePath); }); const testMultipleStreamOutput = async (t, getStreamOption) => { const filePath = tempfile(); const stream = createWriteStream(filePath); await once(stream, 'open'); await execa('noop-both.js', [foobarString], getDifferentOutputs(getStreamOption(stream))); t.is(await readFile(filePath, 'utf8'), `${foobarString}\n${foobarString}\n`); await rm(filePath); }; test('Can re-use same native Writable stream on different output file descriptors', testMultipleStreamOutput, getNativeStream); test('Can re-use same non-native Writable stream on different output file descriptors', testMultipleStreamOutput, getNonNativeStream); test('Can re-use same web Writable stream on different output file descriptors', testMultipleStreamOutput, getWebWritableStream); const testMultipleInheritOutput = async (t, isSync) => { const {stdout} = await nestedSubprocess('noop-both.js', [foobarString], {...getDifferentOutputs(1), isSync}); t.is(stdout, `${foobarString}\n${foobarString}`); }; test('Can re-use same parent file descriptor on different output file descriptors', testMultipleInheritOutput, false); test('Can re-use same parent file descriptor on different output file descriptors, sync', testMultipleInheritOutput, true); const testMultipleFileInput = async (t, mapFile) => { const filePath = tempfile(); await writeFile(filePath, foobarString); const {stdout} = await execa('stdin-fd-both.js', ['3'], getDifferentInputs([new Uint8Array(0), mapFile(filePath)])); t.is(stdout, `${foobarString}${foobarString}`); await rm(filePath); }; test('Can re-use same file path on different input file descriptors', testMultipleFileInput, getAbsolutePath); test('Can re-use same file URL on different input file descriptors', testMultipleFileInput, pathToFileURL); const testMultipleFileOutput = async (t, mapFile, execaMethod) => { const filePath = tempfile(); await execaMethod('noop-both.js', [foobarString], getDifferentOutputs(mapFile(filePath))); t.is(await readFile(filePath, 'utf8'), `${foobarString}\n${foobarString}\n`); await rm(filePath); }; test('Can re-use same file path on different output file descriptors', testMultipleFileOutput, getAbsolutePath, execa); test('Can re-use same file path on different output file descriptors, sync', testMultipleFileOutput, getAbsolutePath, execaSync); test('Can re-use same file URL on different output file descriptors', testMultipleFileOutput, pathToFileURL, execa); test('Can re-use same file URL on different output file descriptors, sync', testMultipleFileOutput, pathToFileURL, execaSync); const testMultipleFileInputOutput = async (t, mapFile, execaMethod) => { const inputFilePath = tempfile(); const outputFilePath = tempfile(); await writeFile(inputFilePath, foobarString); await execaMethod('stdin.js', {stdin: mapFile(inputFilePath), stdout: mapFile(outputFilePath)}); t.is(await readFile(outputFilePath, 'utf8'), foobarString); await Promise.all([rm(inputFilePath), rm(outputFilePath)]); }; test('Can use different file paths on different input/output file descriptors', testMultipleFileInputOutput, getAbsolutePath, execa); test('Can use different file paths on different input/output file descriptors, sync', testMultipleFileInputOutput, getAbsolutePath, execaSync); test('Can use different file URL on different input/output file descriptors', testMultipleFileInputOutput, pathToFileURL, execa); test('Can use different file URL on different input/output file descriptors, sync', testMultipleFileInputOutput, pathToFileURL, execaSync); // eslint-disable-next-line max-params const testMultipleInvalid = async (t, getDummyStream, typeName, getStdio, fdName, execaMethod) => { const stdioOption = await getDummyStream(); t.throws(() => { execaMethod('empty.js', getStdio(stdioOption)); }, {message: `The ${fdName} options must not target ${typeName} that is the same.`}); if (stdioOption.transform !== undefined) { t.true(stdioOption.transform.destroyed); } }; test('Cannot use same Duplex on different input file descriptors', testMultipleInvalid, getDummyDuplex, duplexName, getDifferentInputs, differentInputsName, execa); test('Cannot use same Duplex on different output file descriptors', testMultipleInvalid, getDummyDuplex, duplexName, getDifferentOutputs, differentOutputsName, execa); test('Cannot use same Duplex on both input and output file descriptors', testMultipleInvalid, getDummyDuplex, duplexName, getDifferentInputsOutputs, differentInputsOutputsName, execa); test('Cannot use same TransformStream on different input file descriptors', testMultipleInvalid, getDummyWebTransformStream, webTransformName, getDifferentInputs, differentInputsName, execa); test('Cannot use same TransformStream on different output file descriptors', testMultipleInvalid, getDummyWebTransformStream, webTransformName, getDifferentOutputs, differentOutputsName, execa); test('Cannot use same TransformStream on both input and output file descriptors', testMultipleInvalid, getDummyWebTransformStream, webTransformName, getDifferentInputsOutputs, differentInputsOutputsName, execa); test('Cannot use same file path on both input and output file descriptors', testMultipleInvalid, getDummyFilePath, filePathName, getDifferentInputsOutputs, differentInputsOutputsName, execa); test('Cannot use same file URL on both input and output file descriptors', testMultipleInvalid, getDummyFileURL, fileURLName, getDifferentInputsOutputs, differentInputsOutputsName, execa); test('Cannot use same file path on both input and output file descriptors, sync', testMultipleInvalid, getDummyFilePath, filePathName, getDifferentInputsOutputs, differentInputsOutputsName, execaSync); test('Cannot use same file URL on both input and output file descriptors, sync', testMultipleInvalid, getDummyFileURL, fileURLName, getDifferentInputsOutputs, differentInputsOutputsName, execaSync); ================================================ FILE: test/stdio/file-descriptor.js ================================================ import {readFile, open, rm} from 'node:fs/promises'; import test from 'ava'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; setFixtureDirectory(); const testFileDescriptorOption = async (t, fdNumber, execaMethod) => { const filePath = tempfile(); const fileDescriptor = await open(filePath, 'w'); await execaMethod('noop-fd.js', [`${fdNumber}`, 'foobar'], getStdio(fdNumber, fileDescriptor)); t.is(await readFile(filePath, 'utf8'), 'foobar'); await rm(filePath); await fileDescriptor.close(); }; test('pass `stdout` to a file descriptor', testFileDescriptorOption, 1, execa); test('pass `stderr` to a file descriptor', testFileDescriptorOption, 2, execa); test('pass `stdio[*]` to a file descriptor', testFileDescriptorOption, 3, execa); test('pass `stdout` to a file descriptor - sync', testFileDescriptorOption, 1, execaSync); test('pass `stderr` to a file descriptor - sync', testFileDescriptorOption, 2, execaSync); test('pass `stdio[*]` to a file descriptor - sync', testFileDescriptorOption, 3, execaSync); const testStdinWrite = async (t, fdNumber) => { const subprocess = execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, 'pipe')); subprocess.stdio[fdNumber].end('unicorns'); const {stdout} = await subprocess; t.is(stdout, 'unicorns'); }; test('you can write to subprocess.stdin', testStdinWrite, 0); test('you can write to subprocess.stdio[*]', testStdinWrite, 3); ================================================ FILE: test/stdio/file-path-error.js ================================================ import {readFile, writeFile, rm} from 'node:fs/promises'; import {pathToFileURL} from 'node:url'; import test from 'ava'; import {pathExists} from 'path-exists'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {identity, getStdio} from '../helpers/stdio.js'; import {foobarString, foobarUppercase} from '../helpers/input.js'; import { outputObjectGenerator, uppercaseGenerator, serializeGenerator, throwingGenerator, } from '../helpers/generator.js'; import {getAbsolutePath} from '../helpers/file-path.js'; setFixtureDirectory(); const nonFileUrl = new URL('https://example.com'); const testStdioNonFileUrl = (t, fdNumber, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(fdNumber, nonFileUrl)); }, {message: /pathToFileURL/}); }; test('inputFile cannot be a non-file URL', testStdioNonFileUrl, 'inputFile', execa); test('stdin cannot be a non-file URL', testStdioNonFileUrl, 0, execa); test('stdout cannot be a non-file URL', testStdioNonFileUrl, 1, execa); test('stderr cannot be a non-file URL', testStdioNonFileUrl, 2, execa); test('stdio[*] cannot be a non-file URL', testStdioNonFileUrl, 3, execa); test('inputFile cannot be a non-file URL - sync', testStdioNonFileUrl, 'inputFile', execaSync); test('stdin cannot be a non-file URL - sync', testStdioNonFileUrl, 0, execaSync); test('stdout cannot be a non-file URL - sync', testStdioNonFileUrl, 1, execaSync); test('stderr cannot be a non-file URL - sync', testStdioNonFileUrl, 2, execaSync); test('stdio[*] cannot be a non-file URL - sync', testStdioNonFileUrl, 3, execaSync); const testInvalidInputFile = (t, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio('inputFile', false)); }, {message: /a file path string or a file URL/}); }; test('inputFile must be a file URL or string', testInvalidInputFile, execa); test('inputFile must be a file URL or string - sync', testInvalidInputFile, execaSync); const testFilePathObject = (t, fdNumber, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(fdNumber, foobarString)); }, {message: /must be used/}); }; test('stdin must be an object when it is a file path string', testFilePathObject, 0, execa); test('stdout must be an object when it is a file path string', testFilePathObject, 1, execa); test('stderr must be an object when it is a file path string', testFilePathObject, 2, execa); test('stdio[*] must be an object when it is a file path string', testFilePathObject, 3, execa); test('stdin be an object when it is a file path string - sync', testFilePathObject, 0, execaSync); test('stdout be an object when it is a file path string - sync', testFilePathObject, 1, execaSync); test('stderr be an object when it is a file path string - sync', testFilePathObject, 2, execaSync); test('stdio[*] must be an object when it is a file path string - sync', testFilePathObject, 3, execaSync); const testFileError = async (t, fixtureName, mapFile, fdNumber) => { await t.throwsAsync( execa(fixtureName, [`${fdNumber}`], getStdio(fdNumber, mapFile('./unknown/file'))), {code: 'ENOENT'}, ); }; test.serial('inputFile file URL errors should be handled', testFileError, 'stdin-fd.js', pathToFileURL, 'inputFile'); test.serial('stdin file URL errors should be handled', testFileError, 'stdin-fd.js', pathToFileURL, 0); test.serial('stdout file URL errors should be handled', testFileError, 'noop-fd.js', pathToFileURL, 1); test.serial('stderr file URL errors should be handled', testFileError, 'noop-fd.js', pathToFileURL, 2); test.serial('stdio[*] file URL errors should be handled', testFileError, 'noop-fd.js', pathToFileURL, 3); test.serial('inputFile file path errors should be handled', testFileError, 'stdin-fd.js', identity, 'inputFile'); test.serial('stdin file path errors should be handled', testFileError, 'stdin-fd.js', getAbsolutePath, 0); test.serial('stdout file path errors should be handled', testFileError, 'noop-fd.js', getAbsolutePath, 1); test.serial('stderr file path errors should be handled', testFileError, 'noop-fd.js', getAbsolutePath, 2); test.serial('stdio[*] file path errors should be handled', testFileError, 'noop-fd.js', getAbsolutePath, 3); const testFileErrorSync = (t, mapFile, fdNumber) => { t.throws(() => { execaSync('empty.js', getStdio(fdNumber, mapFile('./unknown/file'))); }, {code: 'ENOENT'}); }; test('inputFile file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 'inputFile'); test('stdin file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 0); test('stdout file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 1); test('stderr file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 2); test('stdio[*] file URL errors should be handled - sync', testFileErrorSync, pathToFileURL, 3); test('inputFile file path errors should be handled - sync', testFileErrorSync, identity, 'inputFile'); test('stdin file path errors should be handled - sync', testFileErrorSync, getAbsolutePath, 0); test('stdout file path errors should be handled - sync', testFileErrorSync, getAbsolutePath, 1); test('stderr file path errors should be handled - sync', testFileErrorSync, getAbsolutePath, 2); test('stdio[*] file path errors should be handled - sync', testFileErrorSync, getAbsolutePath, 3); const testInputFileObject = async (t, fdNumber, mapFile, execaMethod) => { const filePath = tempfile(); await writeFile(filePath, foobarString); t.throws(() => { execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [ new Uint8Array(), mapFile(filePath), serializeGenerator(true), ])); }, {message: /cannot use both files and transforms in objectMode/}); await rm(filePath); }; test('stdin cannot use objectMode together with input file paths', testInputFileObject, 0, getAbsolutePath, execa); test('stdin cannot use objectMode together with input file URLs', testInputFileObject, 0, pathToFileURL, execa); test('stdio[*] cannot use objectMode together with input file paths', testInputFileObject, 3, getAbsolutePath, execa); test('stdio[*] cannot use objectMode together with input file URLs', testInputFileObject, 3, pathToFileURL, execa); test('stdin cannot use objectMode together with input file paths, sync', testInputFileObject, 0, getAbsolutePath, execaSync); test('stdin cannot use objectMode together with input file URLs, sync', testInputFileObject, 0, pathToFileURL, execaSync); const testOutputFileObject = async (t, fdNumber, mapFile, execaMethod) => { const filePath = tempfile(); t.throws(() => { execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, [ outputObjectGenerator(), mapFile(filePath), ])); }, {message: /cannot use both files and transforms in objectMode/}); t.false(await pathExists(filePath)); }; test('stdout cannot use objectMode together with output file paths', testOutputFileObject, 1, getAbsolutePath, execa); test('stdout cannot use objectMode together with output file URLs', testOutputFileObject, 1, pathToFileURL, execa); test('stderr cannot use objectMode together with output file paths', testOutputFileObject, 2, getAbsolutePath, execa); test('stderr cannot use objectMode together with output file URLs', testOutputFileObject, 2, pathToFileURL, execa); test('stdio[*] cannot use objectMode together with output file paths', testOutputFileObject, 3, getAbsolutePath, execa); test('stdio[*] cannot use objectMode together with output file URLs', testOutputFileObject, 3, pathToFileURL, execa); test('stdout cannot use objectMode together with output file paths, sync', testOutputFileObject, 1, getAbsolutePath, execaSync); test('stdout cannot use objectMode together with output file URLs, sync', testOutputFileObject, 1, pathToFileURL, execaSync); test('stderr cannot use objectMode together with output file paths, sync', testOutputFileObject, 2, getAbsolutePath, execaSync); test('stderr cannot use objectMode together with output file URLs, sync', testOutputFileObject, 2, pathToFileURL, execaSync); test('stdio[*] cannot use objectMode together with output file paths, sync', testOutputFileObject, 3, getAbsolutePath, execaSync); test('stdio[*] cannot use objectMode together with output file URLs, sync', testOutputFileObject, 3, pathToFileURL, execaSync); test('Generator error stops writing to output file', async t => { const filePath = tempfile(); const cause = new Error(foobarString); const error = await t.throwsAsync(execa('noop.js', { stdout: [throwingGenerator(cause)(), getAbsolutePath(filePath)], })); t.is(error.cause, cause); t.is(await readFile(filePath, 'utf8'), ''); }); test('Generator error does not create output file, sync', async t => { const filePath = tempfile(); const cause = new Error(foobarString); const error = t.throws(() => { execaSync('noop.js', { stdout: [throwingGenerator(cause)(), getAbsolutePath(filePath)], }); }); t.is(error.cause, cause); t.false(await pathExists(filePath)); }); test('Output file error still returns transformed output, sync', async t => { const filePath = tempfile(); const {stdout} = t.throws(() => { execaSync('noop-fd.js', ['1', foobarString], { stdout: [uppercaseGenerator(), getAbsolutePath('./unknown/file')], }); }, {code: 'ENOENT'}); t.false(await pathExists(filePath)); t.is(stdout, foobarUppercase); }); ================================================ FILE: test/stdio/file-path-main.js ================================================ import {readFile, writeFile, rm} from 'node:fs/promises'; import path from 'node:path'; import process from 'node:process'; import {pathToFileURL} from 'node:url'; import test from 'ava'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {identity, getStdio} from '../helpers/stdio.js'; import { runExeca, runExecaSync, runScript, runScriptSync, } from '../helpers/run.js'; import {foobarString, foobarUint8Array} from '../helpers/input.js'; import {getAbsolutePath, getRelativePath} from '../helpers/file-path.js'; setFixtureDirectory(); const getStdioInput = (fdNumberOrName, file) => { if (fdNumberOrName === 'string') { return {input: foobarString}; } if (fdNumberOrName === 'binary') { return {input: foobarUint8Array}; } return getStdioInputFile(fdNumberOrName, file); }; const getStdioInputFile = (fdNumberOrName, file) => getStdio(fdNumberOrName, typeof fdNumberOrName === 'string' ? file : {file}); const testStdinFile = async (t, mapFilePath, fdNumber, execaMethod) => { const filePath = tempfile(); await writeFile(filePath, foobarString); const {stdout} = await execaMethod('stdin.js', getStdio(fdNumber, mapFilePath(filePath))); t.is(stdout, foobarString); await rm(filePath); }; test('inputFile can be a file URL', testStdinFile, pathToFileURL, 'inputFile', execa); test('stdin can be a file URL', testStdinFile, pathToFileURL, 0, execa); test('inputFile can be an absolute file path', testStdinFile, identity, 'inputFile', execa); test('stdin can be an absolute file path', testStdinFile, getAbsolutePath, 0, execa); test('inputFile can be a relative file path', testStdinFile, identity, 'inputFile', execa); test('stdin can be a relative file path', testStdinFile, getRelativePath, 0, execa); test('inputFile can be a file URL - sync', testStdinFile, pathToFileURL, 'inputFile', execaSync); test('stdin can be a file URL - sync', testStdinFile, pathToFileURL, 0, execaSync); test('inputFile can be an absolute file path - sync', testStdinFile, identity, 'inputFile', execaSync); test('stdin can be an absolute file path - sync', testStdinFile, getAbsolutePath, 0, execaSync); test('inputFile can be a relative file path - sync', testStdinFile, identity, 'inputFile', execaSync); test('stdin can be a relative file path - sync', testStdinFile, getRelativePath, 0, execaSync); const testOutputFile = async (t, mapFile, fdNumber, execaMethod) => { const filePath = tempfile(); await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, mapFile(filePath))); t.is(await readFile(filePath, 'utf8'), foobarString); await rm(filePath); }; test('stdout can be a file URL', testOutputFile, pathToFileURL, 1, execa); test('stderr can be a file URL', testOutputFile, pathToFileURL, 2, execa); test('stdio[*] can be a file URL', testOutputFile, pathToFileURL, 3, execa); test('stdout can be an absolute file path', testOutputFile, getAbsolutePath, 1, execa); test('stderr can be an absolute file path', testOutputFile, getAbsolutePath, 2, execa); test('stdio[*] can be an absolute file path', testOutputFile, getAbsolutePath, 3, execa); test('stdout can be a relative file path', testOutputFile, getRelativePath, 1, execa); test('stderr can be a relative file path', testOutputFile, getRelativePath, 2, execa); test('stdio[*] can be a relative file path', testOutputFile, getRelativePath, 3, execa); test('stdout can be a file URL - sync', testOutputFile, pathToFileURL, 1, execaSync); test('stderr can be a file URL - sync', testOutputFile, pathToFileURL, 2, execaSync); test('stdio[*] can be a file URL - sync', testOutputFile, pathToFileURL, 3, execaSync); test('stdout can be an absolute file path - sync', testOutputFile, getAbsolutePath, 1, execaSync); test('stderr can be an absolute file path - sync', testOutputFile, getAbsolutePath, 2, execaSync); test('stdio[*] can be an absolute file path - sync', testOutputFile, getAbsolutePath, 3, execaSync); test('stdout can be a relative file path - sync', testOutputFile, getRelativePath, 1, execaSync); test('stderr can be a relative file path - sync', testOutputFile, getRelativePath, 2, execaSync); test('stdio[*] can be a relative file path - sync', testOutputFile, getRelativePath, 3, execaSync); const testInputFileValidUrl = async (t, fdNumber, execaMethod) => { const filePath = tempfile(); await writeFile(filePath, foobarString); const currentCwd = process.cwd(); process.chdir(path.dirname(filePath)); try { const {stdout} = await execaMethod('stdin.js', getStdioInputFile(fdNumber, path.basename(filePath))); t.is(stdout, foobarString); } finally { process.chdir(currentCwd); await rm(filePath); } }; test.serial('inputFile does not need to start with . when being a relative file path', testInputFileValidUrl, 'inputFile', execa); test.serial('stdin does not need to start with . when being a relative file path', testInputFileValidUrl, 0, execa); test.serial('inputFile does not need to start with . when being a relative file path - sync', testInputFileValidUrl, 'inputFile', execaSync); test.serial('stdin does not need to start with . when being a relative file path - sync', testInputFileValidUrl, 0, execaSync); const testMultipleInputs = async (t, indices, execaMethod) => { const filePath = tempfile(); await writeFile(filePath, foobarString); const options = Object.assign({}, ...indices.map(fdNumber => getStdioInput(fdNumber, filePath))); const {stdout} = await execaMethod('stdin.js', options); t.is(stdout, foobarString.repeat(indices.length)); await rm(filePath); }; test('inputFile can be set', testMultipleInputs, ['inputFile'], runExeca); test('inputFile can be set - sync', testMultipleInputs, ['inputFile'], runExecaSync); test('inputFile can be set with $', testMultipleInputs, ['inputFile'], runScript); test('inputFile can be set with $.sync', testMultipleInputs, ['inputFile'], runScriptSync); test('input String and inputFile can be both set', testMultipleInputs, ['inputFile', 'string'], execa); test('input String and stdin can be both set', testMultipleInputs, [0, 'string'], execa); test('input Uint8Array and inputFile can be both set', testMultipleInputs, ['inputFile', 'binary'], execa); test('input Uint8Array and stdin can be both set', testMultipleInputs, [0, 'binary'], execa); test('stdin and inputFile can be both set', testMultipleInputs, [0, 'inputFile'], execa); test('input String, stdin and inputFile can be all set', testMultipleInputs, ['inputFile', 0, 'string'], execa); test('input Uint8Array, stdin and inputFile can be all set', testMultipleInputs, ['inputFile', 0, 'binary'], execa); test('input String and inputFile can be both set - sync', testMultipleInputs, ['inputFile', 'string'], execaSync); test('input String and stdin can be both set - sync', testMultipleInputs, [0, 'string'], execaSync); test('input Uint8Array and inputFile can be both set - sync', testMultipleInputs, ['inputFile', 'binary'], execaSync); test('input Uint8Array and stdin can be both set - sync', testMultipleInputs, [0, 'binary'], execaSync); test('stdin and inputFile can be both set - sync', testMultipleInputs, [0, 'inputFile'], execaSync); test('input String, stdin and inputFile can be all set - sync', testMultipleInputs, ['inputFile', 0, 'string'], execaSync); test('input Uint8Array, stdin and inputFile can be all set - sync', testMultipleInputs, ['inputFile', 0, 'binary'], execaSync); const testMultipleOutputs = async (t, mapFile, fdNumber, execaMethod) => { const filePath = tempfile(); const filePathTwo = tempfile(); await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, [mapFile(filePath), mapFile(filePathTwo)])); t.is(await readFile(filePath, 'utf8'), foobarString); t.is(await readFile(filePathTwo, 'utf8'), foobarString); await Promise.all([rm(filePath), rm(filePathTwo)]); }; test('stdout can be two file URLs', testMultipleOutputs, pathToFileURL, 1, execa); test('stdout can be two file paths', testMultipleOutputs, getAbsolutePath, 1, execa); test('stdout can be two file URLs - sync', testMultipleOutputs, pathToFileURL, 1, execaSync); test('stdout can be two file paths - sync', testMultipleOutputs, getAbsolutePath, 1, execaSync); test('stderr can be two file URLs', testMultipleOutputs, pathToFileURL, 2, execa); test('stderr can be two file paths', testMultipleOutputs, getAbsolutePath, 2, execa); test('stderr can be two file URLs - sync', testMultipleOutputs, pathToFileURL, 2, execaSync); test('stderr can be two file paths - sync', testMultipleOutputs, getAbsolutePath, 2, execaSync); test('stdio[*] can be two file URLs', testMultipleOutputs, pathToFileURL, 3, execa); test('stdio[*] can be two file paths', testMultipleOutputs, getAbsolutePath, 3, execa); test('stdio[*] can be two file URLs - sync', testMultipleOutputs, pathToFileURL, 3, execaSync); test('stdio[*] can be two file paths - sync', testMultipleOutputs, getAbsolutePath, 3, execaSync); const testInputFileHanging = async (t, mapFilePath) => { const filePath = tempfile(); await writeFile(filePath, foobarString); await t.throwsAsync(execa('stdin.js', {stdin: mapFilePath(filePath), timeout: 1}), {message: /timed out/}); await rm(filePath); }; test('Passing an input file path when subprocess exits does not make promise hang', testInputFileHanging, getAbsolutePath); test('Passing an input file URL when subprocess exits does not make promise hang', testInputFileHanging, pathToFileURL); const testOverwriteFile = async (t, fdNumber, execaMethod, append) => { const filePath = tempfile(); await writeFile(filePath, '.'); await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, {file: filePath, append})); t.is(await readFile(filePath, 'utf8'), foobarString); await rm(filePath); }; test('Overwrite by default to stdout', testOverwriteFile, 1, execa, undefined); test('Overwrite by default to stderr', testOverwriteFile, 2, execa, undefined); test('Overwrite by default to stdio[*]', testOverwriteFile, 3, execa, undefined); test('Overwrite by default to stdout - sync', testOverwriteFile, 1, execaSync, undefined); test('Overwrite by default to stderr - sync', testOverwriteFile, 2, execaSync, undefined); test('Overwrite by default to stdio[*] - sync', testOverwriteFile, 3, execaSync, undefined); test('Overwrite with append false to stdout', testOverwriteFile, 1, execa, false); test('Overwrite with append false to stderr', testOverwriteFile, 2, execa, false); test('Overwrite with append false to stdio[*]', testOverwriteFile, 3, execa, false); test('Overwrite with append false to stdout - sync', testOverwriteFile, 1, execaSync, false); test('Overwrite with append false to stderr - sync', testOverwriteFile, 2, execaSync, false); test('Overwrite with append false to stdio[*] - sync', testOverwriteFile, 3, execaSync, false); const testAppendFile = async (t, fdNumber, execaMethod) => { const filePath = tempfile(); await writeFile(filePath, '.'); await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, {file: filePath, append: true})); t.is(await readFile(filePath, 'utf8'), `.${foobarString}`); await rm(filePath); }; test('Can append to stdout', testAppendFile, 1, execa); test('Can append to stderr', testAppendFile, 2, execa); test('Can append to stdio[*]', testAppendFile, 3, execa); test('Can append to stdout - sync', testAppendFile, 1, execaSync); test('Can append to stderr - sync', testAppendFile, 2, execaSync); test('Can append to stdio[*] - sync', testAppendFile, 3, execaSync); ================================================ FILE: test/stdio/file-path-mixed.js ================================================ import {readFile, writeFile, rm} from 'node:fs/promises'; import {pathToFileURL} from 'node:url'; import test from 'ava'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import {foobarString, foobarUppercase} from '../helpers/input.js'; import {uppercaseGenerator} from '../helpers/generator.js'; import {getAbsolutePath} from '../helpers/file-path.js'; setFixtureDirectory(); const testInputFileTransform = async (t, fdNumber, mapFile, execaMethod) => { const filePath = tempfile(); await writeFile(filePath, foobarString); const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [ new Uint8Array(), mapFile(filePath), uppercaseGenerator(), ])); t.is(stdout, foobarUppercase); await rm(filePath); }; test('stdin can use generators together with input file paths', testInputFileTransform, 0, getAbsolutePath, execa); test('stdin can use generators together with input file URLs', testInputFileTransform, 0, pathToFileURL, execa); test('stdio[*] can use generators together with input file paths', testInputFileTransform, 3, getAbsolutePath, execa); test('stdio[*] can use generators together with input file URLs', testInputFileTransform, 3, pathToFileURL, execa); test('stdin can use generators together with input file paths, sync', testInputFileTransform, 0, getAbsolutePath, execaSync); test('stdin can use generators together with input file URLs, sync', testInputFileTransform, 0, pathToFileURL, execaSync); const testOutputFileTransform = async (t, fdNumber, mapFile, execaMethod) => { const filePath = tempfile(); await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, [ uppercaseGenerator(), mapFile(filePath), ])); t.is(await readFile(filePath, 'utf8'), `${foobarUppercase}\n`); await rm(filePath); }; test('stdout can use generators together with output file paths', testOutputFileTransform, 1, getAbsolutePath, execa); test('stdout can use generators together with output file URLs', testOutputFileTransform, 1, pathToFileURL, execa); test('stderr can use generators together with output file paths', testOutputFileTransform, 2, getAbsolutePath, execa); test('stderr can use generators together with output file URLs', testOutputFileTransform, 2, pathToFileURL, execa); test('stdio[*] can use generators together with output file paths', testOutputFileTransform, 3, getAbsolutePath, execa); test('stdio[*] can use generators together with output file URLs', testOutputFileTransform, 3, pathToFileURL, execa); test('stdout can use generators together with output file paths, sync', testOutputFileTransform, 1, getAbsolutePath, execaSync); test('stdout can use generators together with output file URLs, sync', testOutputFileTransform, 1, pathToFileURL, execaSync); test('stderr can use generators together with output file paths, sync', testOutputFileTransform, 2, getAbsolutePath, execaSync); test('stderr can use generators together with output file URLs, sync', testOutputFileTransform, 2, pathToFileURL, execaSync); test('stdio[*] can use generators together with output file paths, sync', testOutputFileTransform, 3, getAbsolutePath, execaSync); test('stdio[*] can use generators together with output file URLs, sync', testOutputFileTransform, 3, pathToFileURL, execaSync); const testOutputFileLines = async (t, fdNumber, mapFile, execaMethod) => { const filePath = tempfile(); const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, foobarString], { ...getStdio(fdNumber, mapFile(filePath)), lines: true, }); t.deepEqual(stdio[fdNumber], [foobarString]); t.is(await readFile(filePath, 'utf8'), foobarString); await rm(filePath); }; test('stdout can use "lines: true" together with output file paths', testOutputFileLines, 1, getAbsolutePath, execa); test('stdout can use "lines: true" together with output file URLs', testOutputFileLines, 1, pathToFileURL, execa); test('stderr can use "lines: true" together with output file paths', testOutputFileLines, 2, getAbsolutePath, execa); test('stderr can use "lines: true" together with output file URLs', testOutputFileLines, 2, pathToFileURL, execa); test('stdio[*] can use "lines: true" together with output file paths', testOutputFileLines, 3, getAbsolutePath, execa); test('stdio[*] can use "lines: true" together with output file URLs', testOutputFileLines, 3, pathToFileURL, execa); test('stdout can use "lines: true" together with output file paths, sync', testOutputFileLines, 1, getAbsolutePath, execaSync); test('stdout can use "lines: true" together with output file URLs, sync', testOutputFileLines, 1, pathToFileURL, execaSync); test('stderr can use "lines: true" together with output file paths, sync', testOutputFileLines, 2, getAbsolutePath, execaSync); test('stderr can use "lines: true" together with output file URLs, sync', testOutputFileLines, 2, pathToFileURL, execaSync); test('stdio[*] can use "lines: true" together with output file paths, sync', testOutputFileLines, 3, getAbsolutePath, execaSync); test('stdio[*] can use "lines: true" together with output file URLs, sync', testOutputFileLines, 3, pathToFileURL, execaSync); const testOutputFileNoBuffer = async (t, buffer, execaMethod) => { const filePath = tempfile(); const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], { stdout: getAbsolutePath(filePath), buffer, }); t.is(stdout, undefined); t.is(await readFile(filePath, 'utf8'), foobarString); await rm(filePath); }; test('stdout can use "buffer: false" together with output file paths', testOutputFileNoBuffer, false, execa); test('stdout can use "buffer: false" together with output file paths, fd-specific', testOutputFileNoBuffer, {stdout: false}, execa); test('stdout can use "buffer: false" together with output file paths, sync', testOutputFileNoBuffer, false, execaSync); test('stdout can use "buffer: false" together with output file paths, fd-specific, sync', testOutputFileNoBuffer, {stdout: false}, execaSync); ================================================ FILE: test/stdio/handle-invalid.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {getStdio} from '../helpers/stdio.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const testEmptyArray = (t, fdNumber, optionName, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(fdNumber, [])); }, {message: `The \`${optionName}\` option must not be an empty array.`}); }; test('Cannot pass an empty array to stdin', testEmptyArray, 0, 'stdin', execa); test('Cannot pass an empty array to stdout', testEmptyArray, 1, 'stdout', execa); test('Cannot pass an empty array to stderr', testEmptyArray, 2, 'stderr', execa); test('Cannot pass an empty array to stdio[*]', testEmptyArray, 3, 'stdio[3]', execa); test('Cannot pass an empty array to stdin - sync', testEmptyArray, 0, 'stdin', execaSync); test('Cannot pass an empty array to stdout - sync', testEmptyArray, 1, 'stdout', execaSync); test('Cannot pass an empty array to stderr - sync', testEmptyArray, 2, 'stderr', execaSync); test('Cannot pass an empty array to stdio[*] - sync', testEmptyArray, 3, 'stdio[3]', execaSync); const testInvalidValueSync = (t, fdNumber, stdioOption) => { const {message} = t.throws(() => { execaSync('empty.js', getStdio(fdNumber, stdioOption)); }); t.true(message.includes(`cannot be "${stdioOption}" with synchronous methods`)); }; test('stdin cannot be "ipc", sync', testInvalidValueSync, 0, 'ipc'); test('stdout cannot be "ipc", sync', testInvalidValueSync, 1, 'ipc'); test('stderr cannot be "ipc", sync', testInvalidValueSync, 2, 'ipc'); test('stdio[*] cannot be "ipc", sync', testInvalidValueSync, 3, 'ipc'); test('stdin cannot be "overlapped", sync', testInvalidValueSync, 0, 'overlapped'); test('stdout cannot be "overlapped", sync', testInvalidValueSync, 1, 'overlapped'); test('stderr cannot be "overlapped", sync', testInvalidValueSync, 2, 'overlapped'); test('stdio[*] cannot be "overlapped", sync', testInvalidValueSync, 3, 'overlapped'); const testInvalidArrayValue = (t, invalidStdio, fdNumber, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(fdNumber, ['pipe', invalidStdio])); }, {message: /must not include/}); }; test('Cannot pass "ignore" and another value to stdin', testInvalidArrayValue, 'ignore', 0, execa); test('Cannot pass "ignore" and another value to stdout', testInvalidArrayValue, 'ignore', 1, execa); test('Cannot pass "ignore" and another value to stderr', testInvalidArrayValue, 'ignore', 2, execa); test('Cannot pass "ignore" and another value to stdio[*]', testInvalidArrayValue, 'ignore', 3, execa); test('Cannot pass "ignore" and another value to stdin - sync', testInvalidArrayValue, 'ignore', 0, execaSync); test('Cannot pass "ignore" and another value to stdout - sync', testInvalidArrayValue, 'ignore', 1, execaSync); test('Cannot pass "ignore" and another value to stderr - sync', testInvalidArrayValue, 'ignore', 2, execaSync); test('Cannot pass "ignore" and another value to stdio[*] - sync', testInvalidArrayValue, 'ignore', 3, execaSync); test('Cannot pass "ipc" and another value to stdin', testInvalidArrayValue, 'ipc', 0, execa); test('Cannot pass "ipc" and another value to stdout', testInvalidArrayValue, 'ipc', 1, execa); test('Cannot pass "ipc" and another value to stderr', testInvalidArrayValue, 'ipc', 2, execa); test('Cannot pass "ipc" and another value to stdio[*]', testInvalidArrayValue, 'ipc', 3, execa); test('Cannot pass "ipc" and another value to stdin - sync', testInvalidArrayValue, 'ipc', 0, execaSync); test('Cannot pass "ipc" and another value to stdout - sync', testInvalidArrayValue, 'ipc', 1, execaSync); test('Cannot pass "ipc" and another value to stderr - sync', testInvalidArrayValue, 'ipc', 2, execaSync); test('Cannot pass "ipc" and another value to stdio[*] - sync', testInvalidArrayValue, 'ipc', 3, execaSync); ================================================ FILE: test/stdio/handle-normal.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {getStdio} from '../helpers/stdio.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarUint8Array} from '../helpers/input.js'; setFixtureDirectory(); const testInputOverlapped = async (t, fdNumber) => { const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, [foobarUint8Array, 'overlapped', 'pipe'])); t.is(stdout, foobarString); }; test('stdin can be ["overlapped", "pipe"]', testInputOverlapped, 0); test('stdio[*] input can be ["overlapped", "pipe"]', testInputOverlapped, 3); const testOutputOverlapped = async (t, fdNumber) => { const {stdio} = await execa('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, ['overlapped', 'pipe'])); t.is(stdio[fdNumber], foobarString); }; test('stdout can be ["overlapped", "pipe"]', testOutputOverlapped, 1); test('stderr can be ["overlapped", "pipe"]', testOutputOverlapped, 2); test('stdio[*] output can be ["overlapped", "pipe"]', testOutputOverlapped, 3); const testFd3Undefined = async (t, stdioOption, options) => { const subprocess = execa('empty.js', {...getStdio(3, stdioOption), ...options}); t.is(subprocess.stdio.length, 4); t.is(subprocess.stdio[3], null); const {stdio} = await subprocess; t.is(stdio.length, 4); t.is(stdio[3], undefined); }; test('stdio[*] undefined means "ignore"', testFd3Undefined, undefined, {}); test('stdio[*] null means "ignore"', testFd3Undefined, null, {}); test('stdio[*] [undefined] means "ignore"', testFd3Undefined, [undefined], {}); test('stdio[*] [null] means "ignore"', testFd3Undefined, [null], {}); test('stdio[*] undefined means "ignore", "lines: true"', testFd3Undefined, undefined, {lines: true}); test('stdio[*] null means "ignore", "lines: true"', testFd3Undefined, null, {lines: true}); test('stdio[*] [undefined] means "ignore", "lines: true"', testFd3Undefined, [undefined], {lines: true}); test('stdio[*] [null] means "ignore", "lines: true"', testFd3Undefined, [null], {lines: true}); test('stdio[*] undefined means "ignore", "encoding: hex"', testFd3Undefined, undefined, {encoding: 'hex'}); test('stdio[*] null means "ignore", "encoding: hex"', testFd3Undefined, null, {encoding: 'hex'}); test('stdio[*] [undefined] means "ignore", "encoding: hex"', testFd3Undefined, [undefined], {encoding: 'hex'}); test('stdio[*] [null] means "ignore", "encoding: hex"', testFd3Undefined, [null], {encoding: 'hex'}); ================================================ FILE: test/stdio/handle-options.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {getStdio} from '../helpers/stdio.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const testNoPipeOption = async (t, stdioOption, fdNumber) => { const subprocess = execa('empty.js', getStdio(fdNumber, stdioOption)); t.is(subprocess.stdio[fdNumber], null); await subprocess; }; test('stdin can be "ignore"', testNoPipeOption, 'ignore', 0); test('stdin can be ["ignore"]', testNoPipeOption, ['ignore'], 0); test('stdin can be ["ignore", "ignore"]', testNoPipeOption, ['ignore', 'ignore'], 0); test('stdin can be "ipc"', testNoPipeOption, 'ipc', 0); test('stdin can be ["ipc"]', testNoPipeOption, ['ipc'], 0); test('stdin can be "inherit"', testNoPipeOption, 'inherit', 0); test('stdin can be ["inherit"]', testNoPipeOption, ['inherit'], 0); test('stdin can be 0', testNoPipeOption, 0, 0); test('stdin can be [0]', testNoPipeOption, [0], 0); test('stdout can be "ignore"', testNoPipeOption, 'ignore', 1); test('stdout can be ["ignore"]', testNoPipeOption, ['ignore'], 1); test('stdout can be ["ignore", "ignore"]', testNoPipeOption, ['ignore', 'ignore'], 1); test('stdout can be "ipc"', testNoPipeOption, 'ipc', 1); test('stdout can be ["ipc"]', testNoPipeOption, ['ipc'], 1); test('stdout can be "inherit"', testNoPipeOption, 'inherit', 1); test('stdout can be ["inherit"]', testNoPipeOption, ['inherit'], 1); test('stdout can be 1', testNoPipeOption, 1, 1); test('stdout can be [1]', testNoPipeOption, [1], 1); test('stderr can be "ignore"', testNoPipeOption, 'ignore', 2); test('stderr can be ["ignore"]', testNoPipeOption, ['ignore'], 2); test('stderr can be ["ignore", "ignore"]', testNoPipeOption, ['ignore', 'ignore'], 2); test('stderr can be "ipc"', testNoPipeOption, 'ipc', 2); test('stderr can be ["ipc"]', testNoPipeOption, ['ipc'], 2); test('stderr can be "inherit"', testNoPipeOption, 'inherit', 2); test('stderr can be ["inherit"]', testNoPipeOption, ['inherit'], 2); test('stderr can be 2', testNoPipeOption, 2, 2); test('stderr can be [2]', testNoPipeOption, [2], 2); test('stdio[*] can be "ignore"', testNoPipeOption, 'ignore', 3); test('stdio[*] can be ["ignore"]', testNoPipeOption, ['ignore'], 3); test('stdio[*] can be ["ignore", "ignore"]', testNoPipeOption, ['ignore', 'ignore'], 3); test('stdio[*] can be "ipc"', testNoPipeOption, 'ipc', 3); test('stdio[*] can be ["ipc"]', testNoPipeOption, ['ipc'], 3); test('stdio[*] can be "inherit"', testNoPipeOption, 'inherit', 3); test('stdio[*] can be ["inherit"]', testNoPipeOption, ['inherit'], 3); test('stdio[*] can be 3', testNoPipeOption, 3, 3); test('stdio[*] can be [3]', testNoPipeOption, [3], 3); ================================================ FILE: test/stdio/iterable.js ================================================ import {once} from 'node:events'; import {setImmediate} from 'node:timers/promises'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import { foobarString, foobarObject, foobarObjectString, foobarArray, } from '../helpers/input.js'; import { noopGenerator, serializeGenerator, infiniteGenerator, throwingGenerator, } from '../helpers/generator.js'; const stringGenerator = function * () { yield * foobarArray; }; const textEncoder = new TextEncoder(); const binaryFoo = textEncoder.encode('foo'); const binaryBar = textEncoder.encode('bar'); const binaryArray = [binaryFoo, binaryBar]; const binaryGenerator = function * () { yield * binaryArray; }; const mixedArray = [foobarArray[0], binaryArray[1]]; const mixedGenerator = function * () { yield * mixedArray; }; const asyncGenerator = async function * () { await setImmediate(); yield * foobarArray; }; setFixtureDirectory(); const testIterable = async (t, stdioOption, fdNumber, execaMethod) => { const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, stdioOption)); t.is(stdout, 'foobar'); }; test.serial('stdin option can be an array of strings', testIterable, [foobarArray], 0, execa); test.serial('stdin option can be an array of strings - sync', testIterable, [foobarArray], 0, execaSync); test.serial('stdio[*] option can be an array of strings', testIterable, [foobarArray], 3, execa); test.serial('stdin option can be an array of Uint8Arrays', testIterable, [binaryArray], 0, execa); test.serial('stdin option can be an array of Uint8Arrays - sync', testIterable, [binaryArray], 0, execaSync); test.serial('stdio[*] option can be an array of Uint8Arrays', testIterable, [binaryArray], 3, execa); test.serial('stdin option can be an iterable of strings', testIterable, stringGenerator(), 0, execa); test.serial('stdin option can be an iterable of strings - sync', testIterable, stringGenerator(), 0, execaSync); test.serial('stdio[*] option can be an iterable of strings', testIterable, stringGenerator(), 3, execa); test.serial('stdin option can be an iterable of Uint8Arrays', testIterable, binaryGenerator(), 0, execa); test.serial('stdin option can be an iterable of Uint8Arrays - sync', testIterable, binaryGenerator(), 0, execaSync); test.serial('stdio[*] option can be an iterable of Uint8Arrays', testIterable, binaryGenerator(), 3, execa); test.serial('stdin option can be an iterable of strings + Uint8Arrays', testIterable, mixedGenerator(), 0, execa); test.serial('stdin option can be an iterable of strings + Uint8Arrays - sync', testIterable, mixedGenerator(), 0, execaSync); test.serial('stdio[*] option can be an iterable of strings + Uint8Arrays', testIterable, mixedGenerator(), 3, execa); test.serial('stdin option can be an async iterable', testIterable, asyncGenerator(), 0, execa); test.serial('stdio[*] option can be an async iterable', testIterable, asyncGenerator(), 3, execa); const foobarObjectGenerator = function * () { yield foobarObject; }; const foobarAsyncObjectGenerator = async function * () { yield foobarObject; }; const testObjectIterable = async (t, stdioOption, fdNumber, execaMethod) => { const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stdioOption, serializeGenerator(true)])); t.is(stdout, foobarObjectString); }; test('stdin option can be an array of objects', testObjectIterable, [foobarObject], 0, execa); test('stdio[*] option can be an array of objects', testObjectIterable, [foobarObject], 3, execa); test('stdin option can be an iterable of objects', testObjectIterable, foobarObjectGenerator(), 0, execa); test('stdio[*] option can be an iterable of objects', testObjectIterable, foobarObjectGenerator(), 3, execa); test('stdin option can be an async iterable of objects', testObjectIterable, foobarAsyncObjectGenerator(), 0, execa); test('stdio[*] option can be an async iterable of objects', testObjectIterable, foobarAsyncObjectGenerator(), 3, execa); test('stdin option can be an array of objects - sync', testObjectIterable, [foobarObject], 0, execaSync); test('stdin option can be an iterable of objects - sync', testObjectIterable, foobarObjectGenerator(), 0, execaSync); const testIterableNoGeneratorsSync = (t, stdioOption, fdNumber) => { t.throws(() => { execaSync('empty.js', getStdio(fdNumber, stdioOption)); }, {message: /must be used to serialize/}); }; test('stdin option cannot be an array of objects without generators - sync', testIterableNoGeneratorsSync, [[foobarObject]], 0); test('stdin option cannot be an iterable of objects without generators - sync', testIterableNoGeneratorsSync, foobarObjectGenerator(), 0); const testIterableNoSerializeSync = (t, stdioOption, fdNumber) => { t.throws(() => { execaSync('empty.js', getStdio(fdNumber, [stdioOption, noopGenerator()])); }, {message: /The `stdin` option's transform must use "objectMode: true" to receive as input: object/}); }; test('stdin option cannot be an array of objects without serializing - sync', testIterableNoSerializeSync, [foobarObject], 0); test('stdin option cannot be an iterable of objects without serializing - sync', testIterableNoSerializeSync, foobarObjectGenerator(), 0); const testIterableSync = (t, stdioOption, fdNumber) => { t.throws(() => { execaSync('empty.js', getStdio(fdNumber, stdioOption)); }, {message: /an iterable with synchronous methods/}); }; test('stdio[*] option cannot be an array of strings - sync', testIterableSync, [foobarArray], 3); test('stdio[*] option cannot be an array of Uint8Arrays - sync', testIterableSync, [binaryArray], 3); test('stdio[*] option cannot be an array of objects - sync', testIterableSync, [[foobarObject]], 3); test('stdio[*] option cannot be an iterable of strings - sync', testIterableSync, stringGenerator(), 3); test('stdio[*] option cannot be an iterable of Uint8Arrays - sync', testIterableSync, binaryGenerator(), 3); test('stdio[*] option cannot be an iterable of objects - sync', testIterableSync, foobarObjectGenerator(), 3); test('stdio[*] option cannot be multiple iterables - sync', testIterableSync, [stringGenerator(), stringGenerator()], 3); const testAsyncIterableSync = (t, stdioOption, fdNumber) => { t.throws(() => { execaSync('empty.js', getStdio(fdNumber, stdioOption)); }, {message: /an async iterable with synchronous method/}); }; test('stdin option cannot be an async iterable - sync', testAsyncIterableSync, asyncGenerator(), 0); test('stdio[*] option cannot be an async iterable - sync', testAsyncIterableSync, asyncGenerator(), 3); test('stdin option cannot be an async iterable of objects - sync', testAsyncIterableSync, foobarAsyncObjectGenerator(), 0); test('stdio[*] option cannot be an async iterable of objects - sync', testAsyncIterableSync, foobarAsyncObjectGenerator(), 3); const testIterableError = async (t, fdNumber) => { const cause = new Error(foobarString); const error = await t.throwsAsync(execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, throwingGenerator(cause)().transform()))); t.is(error.cause, cause); }; test('stdin option handles errors in iterables', testIterableError, 0); test('stdio[*] option handles errors in iterables', testIterableError, 3); const testIterableErrorSync = (t, fdNumber) => { const cause = new Error(foobarString); const error = t.throws(() => { execaSync('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, throwingGenerator(cause)().transform())); }); t.is(error, cause); }; test('stdin option handles errors in iterables - sync', testIterableErrorSync, 0); test('stdio[*] option handles errors in iterables - sync', testIterableErrorSync, 3); const testNoIterableOutput = (t, stdioOption, fdNumber, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(fdNumber, stdioOption)); }, {message: /cannot be an iterable/}); }; test('stdout option cannot be an array of strings', testNoIterableOutput, [foobarArray], 1, execa); test('stderr option cannot be an array of strings', testNoIterableOutput, [foobarArray], 2, execa); test('stdout option cannot be an array of strings - sync', testNoIterableOutput, [foobarArray], 1, execaSync); test('stderr option cannot be an array of strings - sync', testNoIterableOutput, [foobarArray], 2, execaSync); test('stdout option cannot be an iterable', testNoIterableOutput, stringGenerator(), 1, execa); test('stderr option cannot be an iterable', testNoIterableOutput, stringGenerator(), 2, execa); test('stdout option cannot be an iterable - sync', testNoIterableOutput, stringGenerator(), 1, execaSync); test('stderr option cannot be an iterable - sync', testNoIterableOutput, stringGenerator(), 2, execaSync); test('stdin option can be an infinite iterable', async t => { const iterable = infiniteGenerator().transform(); const subprocess = execa('stdin.js', getStdio(0, iterable)); await once(subprocess.stdout, 'data'); subprocess.kill(); const {stdout} = await t.throwsAsync(subprocess); t.true(stdout.startsWith('foo')); t.deepEqual(await iterable.next(), {value: undefined, done: true}); }); const testMultipleIterable = async (t, stdioOption, fdNumber, execaMethod) => { const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, stdioOption)); const expectedOutputs = [ `${foobarArray[0]}${foobarArray[1]}${foobarArray[0]}${foobarArray[1]}`, `${foobarArray[0]}${foobarArray[0]}${foobarArray[1]}${foobarArray[1]}`, ]; t.true(expectedOutputs.includes(stdout)); }; test('stdin option can be multiple iterables', testMultipleIterable, [stringGenerator(), stringGenerator()], 0, execa); test('stdio[*] option can be multiple iterables', testMultipleIterable, [stringGenerator(), stringGenerator()], 3, execa); test('stdin option can be multiple iterables - sync', testMultipleIterable, [stringGenerator(), stringGenerator()], 0, execaSync); test('stdin option can be multiple mixed iterables', testMultipleIterable, [stringGenerator(), binaryGenerator()], 0, execa); test('stdio[*] option can be multiple mixed iterables', testMultipleIterable, [stringGenerator(), binaryGenerator()], 3, execa); test('stdin option can be multiple mixed iterables - sync', testMultipleIterable, [stringGenerator(), binaryGenerator()], 0, execaSync); test('stdin option can be sync/async mixed iterables', testMultipleIterable, [stringGenerator(), asyncGenerator()], 0, execa); test('stdio[*] option can be sync/async mixed iterables', testMultipleIterable, [stringGenerator(), asyncGenerator()], 3, execa); test('stdin option iterable is canceled on subprocess error', async t => { const iterable = infiniteGenerator().transform(); await t.throwsAsync(execa('stdin.js', {stdin: iterable, timeout: 1}), {message: /timed out/}); // eslint-disable-next-line no-empty for await (const _ of iterable) {} }); ================================================ FILE: test/stdio/lines-main.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import {getOutputsGenerator} from '../helpers/generator.js'; import {foobarString} from '../helpers/input.js'; import { simpleFull, simpleChunks, simpleFullEndChunks, simpleLines, simpleFullEndLines, noNewlinesChunks, getSimpleChunkSubprocessAsync, } from '../helpers/lines.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testStreamLines = async (t, fdNumber, input, expectedOutput, lines, stripFinalNewline, execaMethod) => { const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, input], {...fullStdio, lines, stripFinalNewline}); t.deepEqual(stdio[fdNumber], expectedOutput); }; test('"lines: true" splits lines, stdout', testStreamLines, 1, simpleFull, simpleLines, true, false, execa); test('"lines: true" splits lines, stdout, fd-specific', testStreamLines, 1, simpleFull, simpleLines, {stdout: true}, false, execa); test('"lines: true" splits lines, stderr', testStreamLines, 2, simpleFull, simpleLines, true, false, execa); test('"lines: true" splits lines, stderr, fd-specific', testStreamLines, 2, simpleFull, simpleLines, {stderr: true}, false, execa); test('"lines: true" splits lines, stdio[*]', testStreamLines, 3, simpleFull, simpleLines, true, false, execa); test('"lines: true" splits lines, stdio[*], fd-specific', testStreamLines, 3, simpleFull, simpleLines, {fd3: true}, false, execa); test('"lines: true" splits lines, stdout, stripFinalNewline', testStreamLines, 1, simpleFull, noNewlinesChunks, true, true, execa); test('"lines: true" splits lines, stdout, stripFinalNewline, fd-specific', testStreamLines, 1, simpleFull, noNewlinesChunks, true, {stdout: true}, execa); test('"lines: true" splits lines, stderr, stripFinalNewline', testStreamLines, 2, simpleFull, noNewlinesChunks, true, true, execa); test('"lines: true" splits lines, stderr, stripFinalNewline, fd-specific', testStreamLines, 2, simpleFull, noNewlinesChunks, true, {stderr: true}, execa); test('"lines: true" splits lines, stdio[*], stripFinalNewline', testStreamLines, 3, simpleFull, noNewlinesChunks, true, true, execa); test('"lines: true" splits lines, stdio[*], stripFinalNewline, fd-specific', testStreamLines, 3, simpleFull, noNewlinesChunks, true, {fd3: true}, execa); test('"lines: true" splits lines, stdout, sync', testStreamLines, 1, simpleFull, simpleLines, true, false, execaSync); test('"lines: true" splits lines, stdout, fd-specific, sync', testStreamLines, 1, simpleFull, simpleLines, {stdout: true}, false, execaSync); test('"lines: true" splits lines, stderr, sync', testStreamLines, 2, simpleFull, simpleLines, true, false, execaSync); test('"lines: true" splits lines, stderr, fd-specific, sync', testStreamLines, 2, simpleFull, simpleLines, {stderr: true}, false, execaSync); test('"lines: true" splits lines, stdio[*], sync', testStreamLines, 3, simpleFull, simpleLines, true, false, execaSync); test('"lines: true" splits lines, stdio[*], fd-specific, sync', testStreamLines, 3, simpleFull, simpleLines, {fd3: true}, false, execaSync); test('"lines: true" splits lines, stdout, stripFinalNewline, sync', testStreamLines, 1, simpleFull, noNewlinesChunks, true, true, execaSync); test('"lines: true" splits lines, stdout, stripFinalNewline, fd-specific, sync', testStreamLines, 1, simpleFull, noNewlinesChunks, true, {stdout: true}, execaSync); test('"lines: true" splits lines, stderr, stripFinalNewline, sync', testStreamLines, 2, simpleFull, noNewlinesChunks, true, true, execaSync); test('"lines: true" splits lines, stderr, stripFinalNewline, fd-specific, sync', testStreamLines, 2, simpleFull, noNewlinesChunks, true, {stderr: true}, execaSync); test('"lines: true" splits lines, stdio[*], stripFinalNewline, sync', testStreamLines, 3, simpleFull, noNewlinesChunks, true, true, execaSync); test('"lines: true" splits lines, stdio[*], stripFinalNewline, fd-specific, sync', testStreamLines, 3, simpleFull, noNewlinesChunks, true, {fd3: true}, execaSync); const bigArray = Array.from({length: 1e5}).fill('.\n'); const bigString = bigArray.join(''); const bigStringNoNewlines = '.'.repeat(1e6); const bigStringNoNewlinesEnd = `${bigStringNoNewlines}\n`; // eslint-disable-next-line max-params const testStreamLinesGenerator = async (t, input, expectedLines, objectMode, binary, stripFinalNewline, execaMethod) => { const {stdout} = await execaMethod('noop.js', { stdout: getOutputsGenerator(input)(objectMode, binary), lines: true, stripFinalNewline, }); t.deepEqual(stdout, expectedLines); }; test('"lines: true" works with strings generators', testStreamLinesGenerator, simpleChunks, simpleFullEndLines, false, false, false, execa); test('"lines: true" works with strings generators, binary', testStreamLinesGenerator, simpleChunks, simpleLines, false, true, false, execa); test('"lines: true" works with big strings generators', testStreamLinesGenerator, [bigString], bigArray, false, false, false, execa); test('"lines: true" works with big strings generators without newlines', testStreamLinesGenerator, [bigStringNoNewlines], [bigStringNoNewlinesEnd], false, false, false, execa); test('"lines: true" is a noop with strings generators, objectMode', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, false, execa); test('"lines: true" is a noop with strings generators, stripFinalNewline, objectMode', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, true, execa); test('"lines: true" is a noop with strings generators, stripFinalNewline, fd-specific, objectMode', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, {stdout: true}, execa); test('"lines: true" is a noop with strings generators, binary, objectMode', testStreamLinesGenerator, simpleChunks, simpleChunks, true, true, false, execa); test('"lines: true" is a noop big strings generators, objectMode', testStreamLinesGenerator, [bigString], [bigString], true, false, false, execa); test('"lines: true" is a noop big strings generators without newlines, objectMode', testStreamLinesGenerator, [bigStringNoNewlines], [bigStringNoNewlines], true, false, false, execa); test('"lines: true" works with strings generators, sync', testStreamLinesGenerator, simpleChunks, simpleFullEndLines, false, false, false, execaSync); test('"lines: true" works with strings generators, binary, sync', testStreamLinesGenerator, simpleChunks, simpleLines, false, true, false, execaSync); test('"lines: true" works with big strings generators, sync', testStreamLinesGenerator, [bigString], bigArray, false, false, false, execaSync); test('"lines: true" works with big strings generators without newlines, sync', testStreamLinesGenerator, [bigStringNoNewlines], [bigStringNoNewlinesEnd], false, false, false, execaSync); test('"lines: true" is a noop with strings generators, objectMode, sync', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, false, execaSync); test('"lines: true" is a noop with strings generators, stripFinalNewline, objectMode, sync', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, true, execaSync); test('"lines: true" is a noop with strings generators, stripFinalNewline, fd-specific, objectMode, sync', testStreamLinesGenerator, simpleFullEndChunks, simpleFullEndChunks, true, false, {stdout: true}, execaSync); test('"lines: true" is a noop with strings generators, binary, objectMode, sync', testStreamLinesGenerator, simpleChunks, simpleChunks, true, true, false, execaSync); test('"lines: true" is a noop big strings generators, objectMode, sync', testStreamLinesGenerator, [bigString], [bigString], true, false, false, execaSync); test('"lines: true" is a noop big strings generators without newlines, objectMode, sync', testStreamLinesGenerator, [bigStringNoNewlines], [bigStringNoNewlines], true, false, false, execaSync); test('"lines: true" stops on stream error', async t => { const cause = new Error(foobarString); const error = await t.throwsAsync(getSimpleChunkSubprocessAsync({ * stdout(line) { if (line === noNewlinesChunks[2]) { throw cause; } yield line; }, })); t.is(error.cause, cause); t.deepEqual(error.stdout, noNewlinesChunks.slice(0, 2)); }); test('"lines: true" stops on stream error event', async t => { const cause = new Error(foobarString); const subprocess = getSimpleChunkSubprocessAsync(); subprocess.stdout.emit('error', cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.deepEqual(error.stdout, []); }); ================================================ FILE: test/stdio/lines-max-buffer.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {simpleLines, noNewlinesChunks, getSimpleChunkSubprocessAsync} from '../helpers/lines.js'; import {assertErrorMessage} from '../helpers/max-buffer.js'; setFixtureDirectory(); const maxBuffer = simpleLines.length - 1; const testBelowMaxBuffer = async (t, lines) => { const {isMaxBuffer, stdout} = await getSimpleChunkSubprocessAsync({lines, maxBuffer: maxBuffer + 1}); t.false(isMaxBuffer); t.deepEqual(stdout, noNewlinesChunks); }; test('"lines: true" can be below "maxBuffer"', testBelowMaxBuffer, true); test('"lines: true" can be below "maxBuffer", fd-specific', testBelowMaxBuffer, {stdout: true}); const testAboveMaxBuffer = async (t, lines) => { const {isMaxBuffer, shortMessage, stdout} = await t.throwsAsync(getSimpleChunkSubprocessAsync({lines, maxBuffer})); t.true(isMaxBuffer); assertErrorMessage(t, shortMessage, {length: maxBuffer, unit: 'lines'}); t.deepEqual(stdout, noNewlinesChunks.slice(0, maxBuffer)); }; test('"lines: true" can be above "maxBuffer"', testAboveMaxBuffer, true); test('"lines: true" can be above "maxBuffer", fd-specific', testAboveMaxBuffer, {stdout: true}); const testMaxBufferUnit = async (t, lines) => { const {isMaxBuffer, shortMessage, stdout} = await t.throwsAsync(execa('noop-repeat.js', ['1', '...\n'], {lines, maxBuffer})); t.true(isMaxBuffer); assertErrorMessage(t, shortMessage, {length: maxBuffer, unit: 'lines'}); t.deepEqual(stdout, ['...', '...']); }; test('"maxBuffer" is measured in lines with "lines: true"', testMaxBufferUnit, true); test('"maxBuffer" is measured in lines with "lines: true", fd-specific', testMaxBufferUnit, {stdout: true}); const testMaxBufferUnitSync = (t, lines) => { const {isMaxBuffer, shortMessage, stdout} = t.throws(() => { execaSync('noop-repeat.js', ['1', '...\n'], {lines, maxBuffer}); }, {code: 'ENOBUFS'}); t.true(isMaxBuffer); assertErrorMessage(t, shortMessage, {execaMethod: execaSync, length: maxBuffer}); t.deepEqual(stdout, ['..']); }; test('"maxBuffer" is measured in bytes with "lines: true", sync', testMaxBufferUnitSync, true); test('"maxBuffer" is measured in bytes with "lines: true", fd-specific, sync', testMaxBufferUnitSync, {stdout: true}); ================================================ FILE: test/stdio/lines-mixed.js ================================================ import {Writable} from 'node:stream'; import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {assertStreamOutput, assertStreamDataEvents, assertIterableChunks} from '../helpers/convert.js'; import { simpleFull, simpleLines, noNewlinesChunks, getSimpleChunkSubprocessAsync, } from '../helpers/lines.js'; setFixtureDirectory(); const testAsyncIteration = async (t, expectedLines, stripFinalNewline) => { const subprocess = getSimpleChunkSubprocessAsync({stripFinalNewline}); t.false(subprocess.stdout.readableObjectMode); await assertStreamOutput(t, subprocess.stdout, simpleFull); const {stdout} = await subprocess; t.deepEqual(stdout, expectedLines); }; test('"lines: true" works with stream async iteration', testAsyncIteration, simpleLines, false); test('"lines: true" works with stream async iteration, stripFinalNewline', testAsyncIteration, noNewlinesChunks, true); const testDataEvents = async (t, expectedLines, stripFinalNewline) => { const subprocess = getSimpleChunkSubprocessAsync({stripFinalNewline}); await assertStreamDataEvents(t, subprocess.stdout, simpleFull); const {stdout} = await subprocess; t.deepEqual(stdout, expectedLines); }; test('"lines: true" works with stream "data" events', testDataEvents, simpleLines, false); test('"lines: true" works with stream "data" events, stripFinalNewline', testDataEvents, noNewlinesChunks, true); const testWritableStream = async (t, expectedLines, stripFinalNewline) => { let output = ''; const writable = new Writable({ write(line, encoding, done) { output += line.toString(); done(); }, decodeStrings: false, }); const {stdout} = await getSimpleChunkSubprocessAsync({stripFinalNewline, stdout: ['pipe', writable]}); t.deepEqual(output, simpleFull); t.deepEqual(stdout, expectedLines); }; test('"lines: true" works with writable streams targets', testWritableStream, simpleLines, false); test('"lines: true" works with writable streams targets, stripFinalNewline', testWritableStream, noNewlinesChunks, true); const testIterable = async (t, expectedLines, stripFinalNewline) => { const subprocess = getSimpleChunkSubprocessAsync({stripFinalNewline}); await assertIterableChunks(t, subprocess, noNewlinesChunks); const {stdout} = await subprocess; t.deepEqual(stdout, expectedLines); }; test('"lines: true" works with subprocess.iterable()', testIterable, simpleLines, false); test('"lines: true" works with subprocess.iterable(), stripFinalNewline', testIterable, noNewlinesChunks, true); ================================================ FILE: test/stdio/lines-noop.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getOutputsGenerator} from '../helpers/generator.js'; import {foobarObject} from '../helpers/input.js'; import { simpleFull, simpleFullUint8Array, simpleFullHex, simpleFullUtf16Uint8Array, simpleLines, noNewlinesChunks, getSimpleChunkSubprocess, } from '../helpers/lines.js'; setFixtureDirectory(); const testStreamLinesNoop = async (t, lines, execaMethod) => { const {stdout} = await execaMethod('noop-fd.js', ['1', simpleFull], {lines}); t.is(stdout, simpleFull); }; test('"lines: false" is a noop', testStreamLinesNoop, false, execa); test('"lines: false" is a noop, fd-specific', testStreamLinesNoop, {stderr: true}, execa); test('"lines: false" is a noop, fd-specific none', testStreamLinesNoop, {}, execa); test('"lines: false" is a noop, sync', testStreamLinesNoop, false, execaSync); test('"lines: false" is a noop, fd-specific, sync', testStreamLinesNoop, {stderr: true}, execaSync); test('"lines: false" is a noop, fd-specific none, sync', testStreamLinesNoop, {}, execaSync); const testLinesObjectMode = async (t, lines, execaMethod) => { const {stdout} = await execaMethod('noop.js', { stdout: getOutputsGenerator([foobarObject])(true), lines, }); t.deepEqual(stdout, [foobarObject]); }; test('"lines: true" is a noop with objects generators, objectMode', testLinesObjectMode, true, execa); test('"lines: true" is a noop with objects generators, fd-specific, objectMode', testLinesObjectMode, {stdout: true}, execa); test('"lines: true" is a noop with objects generators, objectMode, sync', testLinesObjectMode, true, execaSync); test('"lines: true" is a noop with objects generators, fd-specific, objectMode, sync', testLinesObjectMode, {stdout: true}, execaSync); // eslint-disable-next-line max-params const testEncoding = async (t, input, expectedOutput, encoding, lines, stripFinalNewline, execaMethod) => { const {stdout} = await execaMethod('stdin.js', { lines, stripFinalNewline, encoding, input, }); t.deepEqual(stdout, expectedOutput); }; test('"lines: true" is a noop with "encoding: utf16"', testEncoding, simpleFullUtf16Uint8Array, simpleLines, 'utf16le', true, false, execa); test('"lines: true" is a noop with "encoding: utf16", fd-specific', testEncoding, simpleFullUtf16Uint8Array, simpleLines, 'utf16le', {stdout: true}, false, execa); test('"lines: true" is a noop with "encoding: utf16", stripFinalNewline', testEncoding, simpleFullUtf16Uint8Array, noNewlinesChunks, 'utf16le', true, true, execa); test('"lines: true" is a noop with "encoding: utf16", stripFinalNewline, fd-specific', testEncoding, simpleFullUtf16Uint8Array, noNewlinesChunks, 'utf16le', true, {stdout: true}, execa); test('"lines: true" is a noop with "encoding: buffer"', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, false, execa); test('"lines: true" is a noop with "encoding: buffer", fd-specific', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', {stdout: true}, false, execa); test('"lines: true" is a noop with "encoding: buffer", stripFinalNewline', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, false, execa); test('"lines: true" is a noop with "encoding: buffer", stripFinalNewline, fd-specific', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, {stdout: false}, execa); test('"lines: true" is a noop with "encoding: hex"', testEncoding, simpleFull, simpleFullHex, 'hex', true, false, execa); test('"lines: true" is a noop with "encoding: hex", fd-specific', testEncoding, simpleFull, simpleFullHex, 'hex', {stdout: true}, false, execa); test('"lines: true" is a noop with "encoding: hex", stripFinalNewline', testEncoding, simpleFull, simpleFullHex, 'hex', true, true, execa); test('"lines: true" is a noop with "encoding: hex", stripFinalNewline, fd-specific', testEncoding, simpleFull, simpleFullHex, 'hex', true, {stdout: true}, execa); test('"lines: true" is a noop with "encoding: utf16", sync', testEncoding, simpleFullUtf16Uint8Array, simpleLines, 'utf16le', true, false, execaSync); test('"lines: true" is a noop with "encoding: utf16", fd-specific, sync', testEncoding, simpleFullUtf16Uint8Array, simpleLines, 'utf16le', {stdout: true}, false, execaSync); test('"lines: true" is a noop with "encoding: utf16", stripFinalNewline, sync', testEncoding, simpleFullUtf16Uint8Array, noNewlinesChunks, 'utf16le', true, true, execaSync); test('"lines: true" is a noop with "encoding: utf16", stripFinalNewline, fd-specific, sync', testEncoding, simpleFullUtf16Uint8Array, noNewlinesChunks, 'utf16le', true, {stdout: true}, execaSync); test('"lines: true" is a noop with "encoding: buffer", sync', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, false, execaSync); test('"lines: true" is a noop with "encoding: buffer", fd-specific, sync', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', {stdout: true}, false, execaSync); test('"lines: true" is a noop with "encoding: buffer", stripFinalNewline, sync', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, false, execaSync); test('"lines: true" is a noop with "encoding: buffer", stripFinalNewline, fd-specific, sync', testEncoding, simpleFull, simpleFullUint8Array, 'buffer', true, {stdout: false}, execaSync); test('"lines: true" is a noop with "encoding: hex", sync', testEncoding, simpleFull, simpleFullHex, 'hex', true, false, execaSync); test('"lines: true" is a noop with "encoding: hex", fd-specific, sync', testEncoding, simpleFull, simpleFullHex, 'hex', {stdout: true}, false, execaSync); test('"lines: true" is a noop with "encoding: hex", stripFinalNewline, sync', testEncoding, simpleFull, simpleFullHex, 'hex', true, true, execaSync); test('"lines: true" is a noop with "encoding: hex", stripFinalNewline, fd-specific, sync', testEncoding, simpleFull, simpleFullHex, 'hex', true, {stdout: true}, execaSync); const testLinesNoBuffer = async (t, lines, buffer, execaMethod) => { const {stdout} = await getSimpleChunkSubprocess(execaMethod, {lines, buffer}); t.is(stdout, undefined); }; test('"lines: true" is a noop with "buffer: false"', testLinesNoBuffer, true, false, execa); test('"lines: true" is a noop with "buffer: false", fd-specific buffer', testLinesNoBuffer, true, {stdout: false}, execa); test('"lines: true" is a noop with "buffer: false", fd-specific lines', testLinesNoBuffer, {stdout: true}, false, execa); test('"lines: true" is a noop with "buffer: false", sync', testLinesNoBuffer, true, false, execaSync); test('"lines: true" is a noop with "buffer: false", fd-specific buffer, sync', testLinesNoBuffer, true, {stdout: false}, execaSync); test('"lines: true" is a noop with "buffer: false", fd-specific lines, sync', testLinesNoBuffer, {stdout: true}, false, execaSync); ================================================ FILE: test/stdio/native-fd.js ================================================ import {platform} from 'node:process'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {getStdio, fullStdio} from '../helpers/stdio.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; setFixtureDirectory(); const isLinux = platform === 'linux'; const isWindows = platform === 'win32'; const testFd3InheritOutput = async (t, stdioOption, isSync) => { const {stdio} = await nestedSubprocess('noop-fd.js', ['3', foobarString], {...getStdio(3, stdioOption), isSync}, fullStdio); t.is(stdio[3], foobarString); }; test('stdio[*] output can use "inherit"', testFd3InheritOutput, 'inherit', false); test('stdio[*] output can use ["inherit"]', testFd3InheritOutput, ['inherit'], false); test('stdio[*] output can use "inherit", sync', testFd3InheritOutput, 'inherit', true); test('stdio[*] output can use ["inherit"], sync', testFd3InheritOutput, ['inherit'], true); if (isLinux) { const testOverflowStream = async (t, fdNumber, stdioOption, isSync) => { const {stdout} = await nestedSubprocess('empty.js', {...getStdio(fdNumber, stdioOption), isSync}, fullStdio); t.is(stdout, ''); }; test('stdin can use 4+', testOverflowStream, 0, 4, false); test('stdin can use [4+]', testOverflowStream, 0, [4], false); test('stdout can use 4+', testOverflowStream, 1, 4, false); test('stdout can use [4+]', testOverflowStream, 1, [4], false); test('stderr can use 4+', testOverflowStream, 2, 4, false); test('stderr can use [4+]', testOverflowStream, 2, [4], false); test('stdio[*] can use 4+', testOverflowStream, 3, 4, false); test('stdio[*] can use [4+]', testOverflowStream, 3, [4], false); test('stdin can use 4+, sync', testOverflowStream, 0, 4, true); test('stdin can use [4+], sync', testOverflowStream, 0, [4], true); test('stdout can use 4+, sync', testOverflowStream, 1, 4, true); test('stdout can use [4+], sync', testOverflowStream, 1, [4], true); test('stderr can use 4+, sync', testOverflowStream, 2, 4, true); test('stderr can use [4+], sync', testOverflowStream, 2, [4], true); test('stdio[*] can use 4+, sync', testOverflowStream, 3, 4, true); test('stdio[*] can use [4+], sync', testOverflowStream, 3, [4], true); } const testOverflowStreamArray = (t, fdNumber, stdioOption) => { t.throws(() => { execa('empty.js', getStdio(fdNumber, stdioOption)); }, {message: /no such standard stream/}); }; test('stdin cannot use 4+ and another value', testOverflowStreamArray, 0, [4, 'pipe']); test('stdout cannot use 4+ and another value', testOverflowStreamArray, 1, [4, 'pipe']); test('stderr cannot use 4+ and another value', testOverflowStreamArray, 2, [4, 'pipe']); test('stdio[*] cannot use 4+ and another value', testOverflowStreamArray, 3, [4, 'pipe']); test('stdio[*] cannot use "inherit" and another value', testOverflowStreamArray, 3, ['inherit', 'pipe']); const getInvalidFdCode = () => { if (isLinux) { return 'EINVAL'; } return isWindows ? 'EBADF' : 'ENXIO'; }; const testOverflowStreamArraySync = (t, fdNumber) => { t.throws(() => { execaSync('noop-fd.js', [fdNumber, foobarString], getStdio(fdNumber, [4, 'pipe'])); }, {code: getInvalidFdCode()}); }; test('stdout cannot use 4+ and another value, sync', testOverflowStreamArraySync, 1); test('stderr cannot use 4+ and another value, sync', testOverflowStreamArraySync, 2); test('stdio[*] cannot use 4+ and another value, sync', testOverflowStreamArraySync, 3); ================================================ FILE: test/stdio/native-inherit-pipe.js ================================================ import {readFile, rm} from 'node:fs/promises'; import test from 'ava'; import tempfile from 'tempfile'; import {execa} from '../../index.js'; import {getStdio, fullStdio} from '../helpers/stdio.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; setFixtureDirectory(); const testInheritStdin = async (t, stdioOption, isSync) => { const {stdout} = await execa('nested-multiple-stdin.js', [JSON.stringify(stdioOption), `${isSync}`], {input: foobarString}); t.is(stdout, `${foobarString}${foobarString}`); }; test('stdin can be ["inherit", "pipe"]', testInheritStdin, ['inherit', 'pipe'], false); test('stdin can be [0, "pipe"]', testInheritStdin, [0, 'pipe'], false); test('stdin can be [process.stdin, "pipe"]', testInheritStdin, ['stdin', 'pipe'], false); test.serial('stdin can be ["inherit", "pipe"], sync', testInheritStdin, ['inherit', 'pipe'], true); test.serial('stdin can be [0, "pipe"], sync', testInheritStdin, [0, 'pipe'], true); test.serial('stdin can be [process.stdin, "pipe"], sync', testInheritStdin, ['stdin', 'pipe'], true); // eslint-disable-next-line max-params const testInheritStdioOutput = async (t, fdNumber, outerFdNumber, stdioOption, isSync, encoding) => { const {stdio} = await execa('nested-multiple-stdio-output.js', [JSON.stringify(stdioOption), `${fdNumber}`, `${outerFdNumber}`, `${isSync}`, encoding], fullStdio); t.is(stdio[fdNumber], foobarString); t.is(stdio[outerFdNumber], `nested ${foobarString}`); }; test('stdout can be ["inherit", "pipe"]', testInheritStdioOutput, 1, 2, ['inherit', 'pipe'], false, 'utf8'); test('stdout can be [1, "pipe"]', testInheritStdioOutput, 1, 2, [1, 'pipe'], false, 'utf8'); test('stdout can be [process.stdout, "pipe"]', testInheritStdioOutput, 1, 2, ['stdout', 'pipe'], false, 'utf8'); test('stderr can be ["inherit", "pipe"]', testInheritStdioOutput, 2, 1, ['inherit', 'pipe'], false, 'utf8'); test('stderr can be [2, "pipe"]', testInheritStdioOutput, 2, 1, [2, 'pipe'], false, 'utf8'); test('stderr can be [process.stderr, "pipe"]', testInheritStdioOutput, 2, 1, ['stderr', 'pipe'], false, 'utf8'); test('stdout can be ["inherit", "pipe"], encoding "buffer"', testInheritStdioOutput, 1, 2, ['inherit', 'pipe'], false, 'buffer'); test('stdout can be [1, "pipe"], encoding "buffer"', testInheritStdioOutput, 1, 2, [1, 'pipe'], false, 'buffer'); test('stdout can be [process.stdout, "pipe"], encoding "buffer"', testInheritStdioOutput, 1, 2, ['stdout', 'pipe'], false, 'buffer'); test('stderr can be ["inherit", "pipe"], encoding "buffer"', testInheritStdioOutput, 2, 1, ['inherit', 'pipe'], false, 'buffer'); test('stderr can be [2, "pipe"], encoding "buffer"', testInheritStdioOutput, 2, 1, [2, 'pipe'], false, 'buffer'); test('stderr can be [process.stderr, "pipe"], encoding "buffer"', testInheritStdioOutput, 2, 1, ['stderr', 'pipe'], false, 'buffer'); test('stdout can be ["inherit", "pipe"], sync', testInheritStdioOutput, 1, 2, ['inherit', 'pipe'], true, 'utf8'); test('stdout can be [1, "pipe"], sync', testInheritStdioOutput, 1, 2, [1, 'pipe'], true, 'utf8'); test('stdout can be [process.stdout, "pipe"], sync', testInheritStdioOutput, 1, 2, ['stdout', 'pipe'], true, 'utf8'); test('stderr can be ["inherit", "pipe"], sync', testInheritStdioOutput, 2, 1, ['inherit', 'pipe'], true, 'utf8'); test('stderr can be [2, "pipe"], sync', testInheritStdioOutput, 2, 1, [2, 'pipe'], true, 'utf8'); test('stderr can be [process.stderr, "pipe"], sync', testInheritStdioOutput, 2, 1, ['stderr', 'pipe'], true, 'utf8'); test('stdio[*] output can be ["inherit", "pipe"], sync', testInheritStdioOutput, 3, 1, ['inherit', 'pipe'], true, 'utf8'); test('stdio[*] output can be [3, "pipe"], sync', testInheritStdioOutput, 3, 1, [3, 'pipe'], true, 'utf8'); test('stdout can be ["inherit", "pipe"], encoding "buffer", sync', testInheritStdioOutput, 1, 2, ['inherit', 'pipe'], true, 'buffer'); test('stdout can be [1, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 1, 2, [1, 'pipe'], true, 'buffer'); test('stdout can be [process.stdout, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 1, 2, ['stdout', 'pipe'], true, 'buffer'); test('stderr can be ["inherit", "pipe"], encoding "buffer", sync', testInheritStdioOutput, 2, 1, ['inherit', 'pipe'], true, 'buffer'); test('stderr can be [2, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 2, 1, [2, 'pipe'], true, 'buffer'); test('stderr can be [process.stderr, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 2, 1, ['stderr', 'pipe'], true, 'buffer'); test('stdio[*] output can be ["inherit", "pipe"], encoding "buffer", sync', testInheritStdioOutput, 3, 1, ['inherit', 'pipe'], true, 'buffer'); test('stdio[*] output can be [3, "pipe"], encoding "buffer", sync', testInheritStdioOutput, 3, 1, [3, 'pipe'], true, 'buffer'); const testInheritNoBuffer = async (t, stdioOption, isSync) => { const filePath = tempfile(); await nestedSubprocess('nested-write.js', [filePath, foobarString], {stdin: stdioOption, buffer: false, isSync}, {input: foobarString}); t.is(await readFile(filePath, 'utf8'), `${foobarString} ${foobarString}`); await rm(filePath); }; test('stdin can be ["inherit", "pipe"], buffer: false', testInheritNoBuffer, ['inherit', 'pipe'], false); test('stdin can be [0, "pipe"], buffer: false', testInheritNoBuffer, [0, 'pipe'], false); test.serial('stdin can be ["inherit", "pipe"], buffer: false, sync', testInheritNoBuffer, ['inherit', 'pipe'], true); test.serial('stdin can be [0, "pipe"], buffer: false, sync', testInheritNoBuffer, [0, 'pipe'], true); test('stdin can use ["inherit", "pipe"] in a TTY', async t => { const stdioOption = [['inherit', 'pipe'], 'inherit', 'pipe']; const {stdout} = await execa('nested-sync-tty.js', [JSON.stringify({stdio: stdioOption}), 'false', 'stdin-fd.js', '0'], {input: foobarString}); t.is(stdout, foobarString); }); const testNoTtyInput = async (t, fdNumber, optionName) => { const stdioOption = ['pipe', 'inherit', 'pipe']; stdioOption[fdNumber] = [[''], 'inherit', 'pipe']; const {message} = await t.throwsAsync(execa('nested-sync-tty.js', [JSON.stringify({stdio: stdioOption}), 'true', 'stdin-fd.js', `${fdNumber}`], fullStdio)); t.true(message.includes(`The \`${optionName}: 'inherit'\` option is invalid: it cannot be a TTY`)); }; test('stdin cannot use ["inherit", "pipe"] in a TTY, sync', testNoTtyInput, 0, 'stdin'); test('stdio[*] input cannot use ["inherit", "pipe"] in a TTY, sync', testNoTtyInput, 3, 'stdio[3]'); const testTtyOutput = async (t, fdNumber, isSync) => { const {stdio} = await execa('nested-sync-tty.js', [JSON.stringify(getStdio(fdNumber, ['inherit', 'pipe'])), `${isSync}`, 'noop-fd.js', `${fdNumber}`, foobarString], fullStdio); t.is(stdio[fdNumber], foobarString); }; test('stdout can use ["inherit", "pipe"] in a TTY', testTtyOutput, 1, false); test('stderr can use ["inherit", "pipe"] in a TTY', testTtyOutput, 2, false); test('stdout can use ["inherit", "pipe"] in a TTY, sync', testTtyOutput, 1, true); test('stderr can use ["inherit", "pipe"] in a TTY, sync', testTtyOutput, 2, true); test('stdio[*] output can use ["inherit", "pipe"] in a TTY, sync', testTtyOutput, 3, true); ================================================ FILE: test/stdio/native-redirect.js ================================================ import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testRedirect = async (t, stdioOption, fdNumber, isInput, isSync) => { const {fixtureName, ...options} = isInput ? {fixtureName: 'stdin-fd.js', input: foobarString} : {fixtureName: 'noop-fd.js'}; const {stdio} = await execa('nested-stdio.js', [JSON.stringify(stdioOption), `${fdNumber}`, `${isSync}`, fixtureName, foobarString], options); const resultFdNumber = isStderrDescriptor(stdioOption) ? 2 : 1; t.is(stdio[resultFdNumber], foobarString); }; const isStderrDescriptor = stdioOption => stdioOption === 2 || stdioOption === 'stderr' || (Array.isArray(stdioOption) && isStderrDescriptor(stdioOption[0])); test.serial('stdio[*] can be 0', testRedirect, 0, 3, true, false); test.serial('stdio[*] can be [0]', testRedirect, [0], 3, true, false); test.serial('stdio[*] can be [0, "pipe"]', testRedirect, [0, 'pipe'], 3, true, false); test.serial('stdio[*] can be process.stdin', testRedirect, 'stdin', 3, true, false); test.serial('stdio[*] can be [process.stdin]', testRedirect, ['stdin'], 3, true, false); test.serial('stdio[*] can be [process.stdin, "pipe"]', testRedirect, ['stdin', 'pipe'], 3, true, false); test('stdout can be 2', testRedirect, 2, 1, false, false); test('stdout can be [2]', testRedirect, [2], 1, false, false); test('stdout can be [2, "pipe"]', testRedirect, [2, 'pipe'], 1, false, false); test('stdout can be process.stderr', testRedirect, 'stderr', 1, false, false); test('stdout can be [process.stderr]', testRedirect, ['stderr'], 1, false, false); test('stdout can be [process.stderr, "pipe"]', testRedirect, ['stderr', 'pipe'], 1, false, false); test('stderr can be 1', testRedirect, 1, 2, false, false); test('stderr can be [1]', testRedirect, [1], 2, false, false); test('stderr can be [1, "pipe"]', testRedirect, [1, 'pipe'], 2, false, false); test('stderr can be process.stdout', testRedirect, 'stdout', 2, false, false); test('stderr can be [process.stdout]', testRedirect, ['stdout'], 2, false, false); test('stderr can be [process.stdout, "pipe"]', testRedirect, ['stdout', 'pipe'], 2, false, false); test('stdio[*] can be 1', testRedirect, 1, 3, false, false); test('stdio[*] can be [1]', testRedirect, [1], 3, false, false); test('stdio[*] can be [1, "pipe"]', testRedirect, [1, 'pipe'], 3, false, false); test('stdio[*] can be 2', testRedirect, 2, 3, false, false); test('stdio[*] can be [2]', testRedirect, [2], 3, false, false); test('stdio[*] can be [2, "pipe"]', testRedirect, [2, 'pipe'], 3, false, false); test('stdio[*] can be process.stdout', testRedirect, 'stdout', 3, false, false); test('stdio[*] can be [process.stdout]', testRedirect, ['stdout'], 3, false, false); test('stdio[*] can be [process.stdout, "pipe"]', testRedirect, ['stdout', 'pipe'], 3, false, false); test('stdio[*] can be process.stderr', testRedirect, 'stderr', 3, false, false); test('stdio[*] can be [process.stderr]', testRedirect, ['stderr'], 3, false, false); test('stdio[*] can be [process.stderr, "pipe"]', testRedirect, ['stderr', 'pipe'], 3, false, false); test('stdout can be 2, sync', testRedirect, 2, 1, false, true); test('stdout can be [2], sync', testRedirect, [2], 1, false, true); test('stdout can be [2, "pipe"], sync', testRedirect, [2, 'pipe'], 1, false, true); test('stdout can be process.stderr, sync', testRedirect, 'stderr', 1, false, true); test('stdout can be [process.stderr], sync', testRedirect, ['stderr'], 1, false, true); test('stdout can be [process.stderr, "pipe"], sync', testRedirect, ['stderr', 'pipe'], 1, false, true); test('stderr can be 1, sync', testRedirect, 1, 2, false, true); test('stderr can be [1], sync', testRedirect, [1], 2, false, true); test('stderr can be [1, "pipe"], sync', testRedirect, [1, 'pipe'], 2, false, true); test('stderr can be process.stdout, sync', testRedirect, 'stdout', 2, false, true); test('stderr can be [process.stdout], sync', testRedirect, ['stdout'], 2, false, true); test('stderr can be [process.stdout, "pipe"], sync', testRedirect, ['stdout', 'pipe'], 2, false, true); test('stdio[*] can be 1, sync', testRedirect, 1, 3, false, true); test('stdio[*] can be [1], sync', testRedirect, [1], 3, false, true); test('stdio[*] can be [1, "pipe"], sync', testRedirect, [1, 'pipe'], 3, false, true); test('stdio[*] can be 2, sync', testRedirect, 2, 3, false, true); test('stdio[*] can be [2], sync', testRedirect, [2], 3, false, true); test('stdio[*] can be [2, "pipe"], sync', testRedirect, [2, 'pipe'], 3, false, true); test('stdio[*] can be process.stdout, sync', testRedirect, 'stdout', 3, false, true); test('stdio[*] can be [process.stdout], sync', testRedirect, ['stdout'], 3, false, true); test('stdio[*] can be [process.stdout, "pipe"], sync', testRedirect, ['stdout', 'pipe'], 3, false, true); test('stdio[*] can be process.stderr, sync', testRedirect, 'stderr', 3, false, true); test('stdio[*] can be [process.stderr], sync', testRedirect, ['stderr'], 3, false, true); test('stdio[*] can be [process.stderr, "pipe"], sync', testRedirect, ['stderr', 'pipe'], 3, false, true); ================================================ FILE: test/stdio/node-stream-custom.js ================================================ import {createReadStream, createWriteStream} from 'node:fs'; import {readFile, writeFile, rm} from 'node:fs/promises'; import {Writable, PassThrough} from 'node:stream'; import {text} from 'node:stream/consumers'; import {setImmediate} from 'node:timers/promises'; import {callbackify} from 'node:util'; import test from 'ava'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import {foobarString} from '../helpers/input.js'; import {noopReadable, noopWritable} from '../helpers/stream.js'; import {getEarlyErrorSubprocess, expectedEarlyError} from '../helpers/early-error.js'; setFixtureDirectory(); const testLazyFileReadable = async (t, fdNumber) => { const filePath = tempfile(); await writeFile(filePath, 'foobar'); const stream = createReadStream(filePath); const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, 'pipe'])); t.is(stdout, 'foobar'); await rm(filePath); }; test('stdin can be [Readable, "pipe"] without a file descriptor', testLazyFileReadable, 0); test('stdio[*] can be [Readable, "pipe"] without a file descriptor', testLazyFileReadable, 3); const testLazyFileReadableSync = (t, fdNumber) => { t.throws(() => { execaSync('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [noopReadable(), 'pipe'])); }, {message: /cannot both be an array and include a stream/}); }; test('stdin cannot be [Readable, "pipe"] without a file descriptor, sync', testLazyFileReadableSync, 0); test('stdio[*] cannot be [Readable, "pipe"] without a file descriptor, sync', testLazyFileReadableSync, 3); const testLazyFileWritable = async (t, fdNumber) => { const filePath = tempfile(); const stream = createWriteStream(filePath); await execa('noop-fd.js', [`${fdNumber}`, 'foobar'], getStdio(fdNumber, [stream, 'pipe'])); t.is(await readFile(filePath, 'utf8'), 'foobar'); await rm(filePath); }; test('stdout can be [Writable, "pipe"] without a file descriptor', testLazyFileWritable, 1); test('stderr can be [Writable, "pipe"] without a file descriptor', testLazyFileWritable, 2); test('stdio[*] can be [Writable, "pipe"] without a file descriptor', testLazyFileWritable, 3); const testLazyFileWritableSync = (t, fdNumber) => { t.throws(() => { execaSync('noop-fd.js', [`${fdNumber}`], getStdio(fdNumber, [noopWritable(), 'pipe'])); }, {message: /cannot both be an array and include a stream/}); }; test('stdout cannot be [Writable, "pipe"] without a file descriptor, sync', testLazyFileWritableSync, 1); test('stderr cannot be [Writable, "pipe"] without a file descriptor, sync', testLazyFileWritableSync, 2); test('stdio[*] cannot be [Writable, "pipe"] without a file descriptor, sync', testLazyFileWritableSync, 3); test('Waits for custom streams destroy on subprocess errors', async t => { let waitedForDestroy = false; const stream = new Writable({ destroy: callbackify(async error => { await setImmediate(); waitedForDestroy = true; return error; }), }); const {timedOut} = await t.throwsAsync(execa('forever.js', {stdout: [stream, 'pipe'], timeout: 1})); t.true(timedOut); t.true(waitedForDestroy); }); test('Handles custom streams destroy errors on subprocess success', async t => { const cause = new Error('test'); const stream = new Writable({ destroy(destroyError, done) { done(destroyError ?? cause); }, }); const error = await t.throwsAsync(execa('empty.js', {stdout: [stream, 'pipe']})); t.is(error.cause, cause); }); const testStreamEarlyExit = async (t, stream, streamName) => { const error = await t.throwsAsync(getEarlyErrorSubprocess({[streamName]: [stream, 'pipe']})); t.like(error, expectedEarlyError); t.true(stream.destroyed); }; test('Input streams are canceled on early subprocess exit', testStreamEarlyExit, noopReadable(), 'stdin'); test('Output streams are canceled on early subprocess exit', testStreamEarlyExit, noopWritable(), 'stdout'); const testInputDuplexStream = async (t, fdNumber) => { const stream = new PassThrough(); stream.end(foobarString); const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, new Uint8Array()])); t.is(stdout, foobarString); }; test('Can pass Duplex streams to stdin', testInputDuplexStream, 0); test('Can pass Duplex streams to input stdio[*]', testInputDuplexStream, 3); const testOutputDuplexStream = async (t, fdNumber) => { const stream = new PassThrough(); const [output] = await Promise.all([ text(stream), execa('noop-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, 'pipe'])), ]); t.is(output, foobarString); }; test('Can pass Duplex streams to stdout', testOutputDuplexStream, 1); test('Can pass Duplex streams to stderr', testOutputDuplexStream, 2); test('Can pass Duplex streams to output stdio[*]', testOutputDuplexStream, 3); const testInputStreamAbort = async (t, fdNumber) => { const stream = new PassThrough(); stream.destroy(); const subprocess = execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, new Uint8Array()])); await subprocess; t.true(subprocess.stdio[fdNumber].writableEnded); }; test('subprocess.stdin is ended when an input stream aborts', testInputStreamAbort, 0); test('subprocess.stdio[*] is ended when an input stream aborts', testInputStreamAbort, 3); const testInputStreamError = async (t, fdNumber) => { const stream = new PassThrough(); const cause = new Error(foobarString); stream.destroy(cause); const subprocess = execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, new Uint8Array()])); t.like(await t.throwsAsync(subprocess), {cause}); t.true(subprocess.stdio[fdNumber].writableEnded); }; test('subprocess.stdin is ended when an input stream errors', testInputStreamError, 0); test('subprocess.stdio[*] is ended when an input stream errors', testInputStreamError, 3); const testOutputStreamError = async (t, fdNumber) => { const stream = new PassThrough(); const cause = new Error(foobarString); stream.destroy(cause); const subprocess = execa('noop-fd.js', [`${fdNumber}`], getStdio(fdNumber, [stream, 'pipe'])); t.like(await t.throwsAsync(subprocess), {cause}); t.true(subprocess.stdio[fdNumber].readableAborted); t.is(subprocess.stdio[fdNumber].errored, null); }; test('subprocess.stdout is aborted when an output stream errors', testOutputStreamError, 1); test('subprocess.stderr is aborted when an output stream errors', testOutputStreamError, 2); test('subprocess.stdio[*] is aborted when an output stream errors', testOutputStreamError, 3); ================================================ FILE: test/stdio/node-stream-native.js ================================================ import {once} from 'node:events'; import {createReadStream, createWriteStream} from 'node:fs'; import {readFile, writeFile, rm} from 'node:fs/promises'; import test from 'ava'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import {foobarString} from '../helpers/input.js'; import { noopReadable, noopWritable, noopDuplex, simpleReadable, } from '../helpers/stream.js'; setFixtureDirectory(); const testNoFileStreamSync = async (t, fdNumber, stream) => { t.throws(() => { execaSync('empty.js', getStdio(fdNumber, stream)); }, {code: 'ERR_INVALID_ARG_VALUE'}); }; test('stdin cannot be a Node.js Readable without a file descriptor - sync', testNoFileStreamSync, 0, noopReadable()); test('stdin cannot be a Node.js Duplex without a file descriptor - sync', testNoFileStreamSync, 0, noopDuplex()); test('stdout cannot be a Node.js Writable without a file descriptor - sync', testNoFileStreamSync, 1, noopWritable()); test('stdout cannot be a Node.js Duplex without a file descriptor - sync', testNoFileStreamSync, 1, noopDuplex()); test('stderr cannot be a Node.js Writable without a file descriptor - sync', testNoFileStreamSync, 2, noopWritable()); test('stderr cannot be a Node.js Duplex without a file descriptor - sync', testNoFileStreamSync, 2, noopDuplex()); test('stdio[*] cannot be a Node.js Readable without a file descriptor - sync', testNoFileStreamSync, 3, noopReadable()); test('stdio[*] cannot be a Node.js Writable without a file descriptor - sync', testNoFileStreamSync, 3, noopWritable()); test('stdio[*] cannot be a Node.js Duplex without a file descriptor - sync', testNoFileStreamSync, 3, noopDuplex()); test('input can be a Node.js Readable without a file descriptor', async t => { const {stdout} = await execa('stdin.js', {input: simpleReadable()}); t.is(stdout, foobarString); }); test('input cannot be a Node.js Readable without a file descriptor - sync', t => { t.throws(() => { execaSync('empty.js', {input: simpleReadable()}); }, {message: 'The `input` option cannot be a Node.js stream with synchronous methods.'}); }); const testNoFileStream = async (t, fdNumber, stream) => { await t.throwsAsync(execa('empty.js', getStdio(fdNumber, stream)), {code: 'ERR_INVALID_ARG_VALUE'}); }; test('stdin cannot be a Node.js Readable without a file descriptor', testNoFileStream, 0, noopReadable()); test('stdin cannot be a Node.js Duplex without a file descriptor', testNoFileStream, 0, noopDuplex()); test('stdout cannot be a Node.js Writable without a file descriptor', testNoFileStream, 1, noopWritable()); test('stdout cannot be a Node.js Duplex without a file descriptor', testNoFileStream, 1, noopDuplex()); test('stderr cannot be a Node.js Writable without a file descriptor', testNoFileStream, 2, noopWritable()); test('stderr cannot be a Node.js Duplex without a file descriptor', testNoFileStream, 2, noopDuplex()); test('stdio[*] cannot be a Node.js Readable without a file descriptor', testNoFileStream, 3, noopReadable()); test('stdio[*] cannot be a Node.js Writable without a file descriptor', testNoFileStream, 3, noopWritable()); test('stdio[*] cannot be a Node.js Duplex without a file descriptor', testNoFileStream, 3, noopDuplex()); const createFileReadStream = async () => { const filePath = tempfile(); await writeFile(filePath, 'foobar'); const stream = createReadStream(filePath); await once(stream, 'open'); return {stream, filePath}; }; const createFileWriteStream = async () => { const filePath = tempfile(); const stream = createWriteStream(filePath); await once(stream, 'open'); return {stream, filePath}; }; const assertFileStreamError = async (t, subprocess, stream, filePath) => { const cause = new Error('test'); stream.destroy(cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.is(error.exitCode, 0); t.is(error.signal, undefined); await rm(filePath); }; const testFileReadable = async (t, fdNumber, execaMethod) => { const {stream, filePath} = await createFileReadStream(); const fdNumberString = fdNumber === 'input' ? '0' : `${fdNumber}`; const {stdout} = await execaMethod('stdin-fd.js', [fdNumberString], getStdio(fdNumber, stream)); t.is(stdout, 'foobar'); await rm(filePath); }; test('input can be a Node.js Readable with a file descriptor', testFileReadable, 'input', execa); test('stdin can be a Node.js Readable with a file descriptor', testFileReadable, 0, execa); test('stdio[*] can be a Node.js Readable with a file descriptor', testFileReadable, 3, execa); test('stdin can be a Node.js Readable with a file descriptor - sync', testFileReadable, 0, execaSync); test('stdio[*] can be a Node.js Readable with a file descriptor - sync', testFileReadable, 3, execaSync); const testFileReadableError = async (t, fdNumber) => { const {stream, filePath} = await createFileReadStream(); const fdNumberString = fdNumber === 'input' ? '0' : `${fdNumber}`; const subprocess = execa('stdin-fd.js', [fdNumberString], getStdio(fdNumber, stream)); await assertFileStreamError(t, subprocess, stream, filePath); }; test.serial('input handles errors from a Node.js Readable with a file descriptor', testFileReadableError, 'input'); test.serial('stdin handles errors from a Node.js Readable with a file descriptor', testFileReadableError, 0); test.serial('stdio[*] handles errors from a Node.js Readable with a file descriptor', testFileReadableError, 3); const testFileReadableOpen = async (t, fdNumber, useSingle, execaMethod) => { const {stream, filePath} = await createFileReadStream(); t.deepEqual(stream.eventNames(), []); const stdioOption = useSingle ? stream : [stream, 'pipe']; await execaMethod('empty.js', getStdio(fdNumber, stdioOption)); t.is(stream.readable, useSingle && fdNumber !== 'input'); t.deepEqual(stream.eventNames(), []); await rm(filePath); }; test('input closes a Node.js Readable with a file descriptor', testFileReadableOpen, 'input', true, execa); test('stdin leaves open a single Node.js Readable with a file descriptor', testFileReadableOpen, 0, true, execa); test('stdin closes a combined Node.js Readable with a file descriptor', testFileReadableOpen, 0, false, execa); test('stdio[*] leaves open a single Node.js Readable with a file descriptor', testFileReadableOpen, 3, true, execa); test('stdin leaves open a single Node.js Readable with a file descriptor - sync', testFileReadableOpen, 0, true, execaSync); test('stdio[*] leaves open a single Node.js Readable with a file descriptor - sync', testFileReadableOpen, 3, true, execaSync); const testFileWritable = async (t, fdNumber, execaMethod) => { const {stream, filePath} = await createFileWriteStream(); await execaMethod('noop-fd.js', [`${fdNumber}`, 'foobar'], getStdio(fdNumber, stream)); t.is(await readFile(filePath, 'utf8'), 'foobar'); await rm(filePath); }; test('stdout can be a Node.js Writable with a file descriptor', testFileWritable, 1, execa); test('stderr can be a Node.js Writable with a file descriptor', testFileWritable, 2, execa); test('stdio[*] can be a Node.js Writable with a file descriptor', testFileWritable, 3, execa); test('stdout can be a Node.js Writable with a file descriptor - sync', testFileWritable, 1, execaSync); test('stderr can be a Node.js Writable with a file descriptor - sync', testFileWritable, 2, execaSync); test('stdio[*] can be a Node.js Writable with a file descriptor - sync', testFileWritable, 3, execaSync); const testFileWritableError = async (t, fdNumber) => { const {stream, filePath} = await createFileWriteStream(); const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, stream)); await assertFileStreamError(t, subprocess, stream, filePath); }; test.serial('stdout handles errors from a Node.js Writable with a file descriptor', testFileWritableError, 1); test.serial('stderr handles errors from a Node.js Writable with a file descriptor', testFileWritableError, 2); test.serial('stdio[*] handles errors from a Node.js Writable with a file descriptor', testFileWritableError, 3); const testFileWritableOpen = async (t, fdNumber, useSingle, execaMethod) => { const {stream, filePath} = await createFileWriteStream(); t.deepEqual(stream.eventNames(), []); const stdioOption = useSingle ? stream : [stream, 'pipe']; await execaMethod('empty.js', getStdio(fdNumber, stdioOption)); t.is(stream.writable, useSingle); t.deepEqual(stream.eventNames(), []); await rm(filePath); }; test('stdout leaves open a single Node.js Writable with a file descriptor', testFileWritableOpen, 1, true, execa); test('stdout closes a combined Node.js Writable with a file descriptor', testFileWritableOpen, 1, false, execa); test('stderr leaves open a single Node.js Writable with a file descriptor', testFileWritableOpen, 2, true, execa); test('stderr closes a combined Node.js Writable with a file descriptor', testFileWritableOpen, 2, false, execa); test('stdio[*] leaves open a single Node.js Writable with a file descriptor', testFileWritableOpen, 3, true, execa); test('stdio[*] closes a combined Node.js Writable with a file descriptor', testFileWritableOpen, 3, false, execa); test('stdout leaves open a single Node.js Writable with a file descriptor - sync', testFileWritableOpen, 1, true, execaSync); test('stderr leaves open a single Node.js Writable with a file descriptor - sync', testFileWritableOpen, 2, true, execaSync); test('stdio[*] leaves open a single Node.js Writable with a file descriptor - sync', testFileWritableOpen, 3, true, execaSync); ================================================ FILE: test/stdio/stdio-option.js ================================================ import {inspect} from 'node:util'; import test from 'ava'; import {normalizeStdioOption} from '../../lib/stdio/stdio-option.js'; const stdioMacro = (t, input, expected) => { if (expected instanceof Error) { t.throws(() => { normalizeStdioOption(input); }, {message: expected.message}); return; } t.deepEqual(normalizeStdioOption(input), expected); }; stdioMacro.title = (_, input) => `execa() ${(inspect(input))}`; test(stdioMacro, {stdio: 'inherit'}, ['inherit', 'inherit', 'inherit']); test(stdioMacro, {stdio: 'pipe'}, ['pipe', 'pipe', 'pipe']); test(stdioMacro, {stdio: 'ignore'}, ['ignore', 'ignore', 'ignore']); test(stdioMacro, {}, ['pipe', 'pipe', 'pipe']); test(stdioMacro, {stdio: []}, ['pipe', 'pipe', 'pipe']); test(stdioMacro, {stdio: [0]}, [0, 'pipe', 'pipe']); test(stdioMacro, {stdio: [0, 1]}, [0, 1, 'pipe']); test(stdioMacro, {stdio: [0, 1, 2]}, [0, 1, 2]); test(stdioMacro, {stdio: [0, 1, 2, 3]}, [0, 1, 2, 3]); test(stdioMacro, {stdio: [undefined, 1, 2]}, ['pipe', 1, 2]); test(stdioMacro, {stdio: [null, 1, 2]}, ['pipe', 1, 2]); test(stdioMacro, {stdio: [0, undefined, 2]}, [0, 'pipe', 2]); test(stdioMacro, {stdio: [0, null, 2]}, [0, 'pipe', 2]); test(stdioMacro, {stdio: [0, 1, undefined]}, [0, 1, 'pipe']); test(stdioMacro, {stdio: [0, 1, null]}, [0, 1, 'pipe']); test(stdioMacro, {stdio: [0, 1, 2, undefined]}, [0, 1, 2, 'ignore']); test(stdioMacro, {stdio: [0, 1, 2, null]}, [0, 1, 2, 'ignore']); test(stdioMacro, {stdin: 'pipe'}, ['pipe', 'pipe', 'pipe']); test(stdioMacro, {stdout: 'ignore'}, ['pipe', 'ignore', 'pipe']); test(stdioMacro, {stderr: 'inherit'}, ['pipe', 'pipe', 'inherit']); test(stdioMacro, {stdin: 'pipe', stdout: 'ignore', stderr: 'inherit'}, ['pipe', 'ignore', 'inherit']); test(stdioMacro, {stdin: 'pipe', stdout: 'ignore'}, ['pipe', 'ignore', 'pipe']); test(stdioMacro, {stdin: 'pipe', stderr: 'inherit'}, ['pipe', 'pipe', 'inherit']); test(stdioMacro, {stdout: 'ignore', stderr: 'inherit'}, ['pipe', 'ignore', 'inherit']); test(stdioMacro, {stdin: 0, stdout: 1, stderr: 2}, [0, 1, 2]); test(stdioMacro, {stdin: 0, stdout: 1}, [0, 1, 'pipe']); test(stdioMacro, {stdin: 0, stderr: 2}, [0, 'pipe', 2]); test(stdioMacro, {stdout: 1, stderr: 2}, ['pipe', 1, 2]); test(stdioMacro, {stdio: {foo: 'bar'}}, new TypeError('Expected `stdio` to be of type `string` or `Array`, got `object`')); test(stdioMacro, {stdin: 'inherit', stdio: 'pipe'}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); test(stdioMacro, {stdin: 'inherit', stdio: ['pipe']}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); test(stdioMacro, {stdin: 'inherit', stdio: [undefined, 'pipe']}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); test(stdioMacro, {stdin: 0, stdio: 'pipe'}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); ================================================ FILE: test/stdio/type-invalid.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {getStdio} from '../helpers/stdio.js'; import {noopGenerator} from '../helpers/generator.js'; import {generatorsMap} from '../helpers/map.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const testInvalidGenerator = (t, fdNumber, stdioOption, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(fdNumber, {...noopGenerator(), ...stdioOption})); }, {message: 'final' in stdioOption ? /must be a generator/ : /must be a generator, a Duplex stream or a web TransformStream/}); }; test('Cannot use invalid "transform" with stdin', testInvalidGenerator, 0, {transform: true}, execa); test('Cannot use invalid "transform" with stdout', testInvalidGenerator, 1, {transform: true}, execa); test('Cannot use invalid "transform" with stderr', testInvalidGenerator, 2, {transform: true}, execa); test('Cannot use invalid "transform" with stdio[*]', testInvalidGenerator, 3, {transform: true}, execa); test('Cannot use invalid "final" with stdin', testInvalidGenerator, 0, {final: true}, execa); test('Cannot use invalid "final" with stdout', testInvalidGenerator, 1, {final: true}, execa); test('Cannot use invalid "final" with stderr', testInvalidGenerator, 2, {final: true}, execa); test('Cannot use invalid "final" with stdio[*]', testInvalidGenerator, 3, {final: true}, execa); test('Cannot use invalid "transform" with stdin, sync', testInvalidGenerator, 0, {transform: true}, execaSync); test('Cannot use invalid "transform" with stdout, sync', testInvalidGenerator, 1, {transform: true}, execaSync); test('Cannot use invalid "transform" with stderr, sync', testInvalidGenerator, 2, {transform: true}, execaSync); test('Cannot use invalid "transform" with stdio[*], sync', testInvalidGenerator, 3, {transform: true}, execaSync); test('Cannot use invalid "final" with stdin, sync', testInvalidGenerator, 0, {final: true}, execaSync); test('Cannot use invalid "final" with stdout, sync', testInvalidGenerator, 1, {final: true}, execaSync); test('Cannot use invalid "final" with stderr, sync', testInvalidGenerator, 2, {final: true}, execaSync); test('Cannot use invalid "final" with stdio[*], sync', testInvalidGenerator, 3, {final: true}, execaSync); // eslint-disable-next-line max-params const testInvalidBinary = (t, fdNumber, optionName, type, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(fdNumber, {...generatorsMap[type].uppercase(), [optionName]: 'true'})); }, {message: /a boolean/}); }; test('Cannot use invalid "binary" with stdin', testInvalidBinary, 0, 'binary', 'generator', execa); test('Cannot use invalid "binary" with stdout', testInvalidBinary, 1, 'binary', 'generator', execa); test('Cannot use invalid "binary" with stderr', testInvalidBinary, 2, 'binary', 'generator', execa); test('Cannot use invalid "binary" with stdio[*]', testInvalidBinary, 3, 'binary', 'generator', execa); test('Cannot use invalid "objectMode" with stdin, generators', testInvalidBinary, 0, 'objectMode', 'generator', execa); test('Cannot use invalid "objectMode" with stdout, generators', testInvalidBinary, 1, 'objectMode', 'generator', execa); test('Cannot use invalid "objectMode" with stderr, generators', testInvalidBinary, 2, 'objectMode', 'generator', execa); test('Cannot use invalid "objectMode" with stdio[*], generators', testInvalidBinary, 3, 'objectMode', 'generator', execa); test('Cannot use invalid "binary" with stdin, sync', testInvalidBinary, 0, 'binary', 'generator', execaSync); test('Cannot use invalid "binary" with stdout, sync', testInvalidBinary, 1, 'binary', 'generator', execaSync); test('Cannot use invalid "binary" with stderr, sync', testInvalidBinary, 2, 'binary', 'generator', execaSync); test('Cannot use invalid "binary" with stdio[*], sync', testInvalidBinary, 3, 'binary', 'generator', execaSync); test('Cannot use invalid "objectMode" with stdin, generators, sync', testInvalidBinary, 0, 'objectMode', 'generator', execaSync); test('Cannot use invalid "objectMode" with stdout, generators, sync', testInvalidBinary, 1, 'objectMode', 'generator', execaSync); test('Cannot use invalid "objectMode" with stderr, generators, sync', testInvalidBinary, 2, 'objectMode', 'generator', execaSync); test('Cannot use invalid "objectMode" with stdio[*], generators, sync', testInvalidBinary, 3, 'objectMode', 'generator', execaSync); test('Cannot use invalid "objectMode" with stdin, duplexes', testInvalidBinary, 0, 'objectMode', 'duplex', execa); test('Cannot use invalid "objectMode" with stdout, duplexes', testInvalidBinary, 1, 'objectMode', 'duplex', execa); test('Cannot use invalid "objectMode" with stderr, duplexes', testInvalidBinary, 2, 'objectMode', 'duplex', execa); test('Cannot use invalid "objectMode" with stdio[*], duplexes', testInvalidBinary, 3, 'objectMode', 'duplex', execa); test('Cannot use invalid "objectMode" with stdin, webTransforms', testInvalidBinary, 0, 'objectMode', 'webTransform', execa); test('Cannot use invalid "objectMode" with stdout, webTransforms', testInvalidBinary, 1, 'objectMode', 'webTransform', execa); test('Cannot use invalid "objectMode" with stderr, webTransforms', testInvalidBinary, 2, 'objectMode', 'webTransform', execa); test('Cannot use invalid "objectMode" with stdio[*], webTransforms', testInvalidBinary, 3, 'objectMode', 'webTransform', execa); ================================================ FILE: test/stdio/type-undefined.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {getStdio} from '../helpers/stdio.js'; import {uppercaseGenerator} from '../helpers/generator.js'; import {uppercaseBufferDuplex} from '../helpers/duplex.js'; import {uppercaseBufferWebTransform} from '../helpers/web-transform.js'; import {generatorsMap} from '../helpers/map.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testUndefinedOption = (t, fdNumber, optionName, type, optionValue) => { t.throws(() => { execa('empty.js', getStdio(fdNumber, {...generatorsMap[type].uppercase(), [optionName]: optionValue})); }, {message: /can only be defined when using a generator/}); }; test('Cannot use "binary" with duplexes and stdin', testUndefinedOption, 0, 'binary', 'duplex', true); test('Cannot use "binary" with duplexes and stdout', testUndefinedOption, 1, 'binary', 'duplex', true); test('Cannot use "binary" with duplexes and stderr', testUndefinedOption, 2, 'binary', 'duplex', true); test('Cannot use "binary" with duplexes and stdio[*]', testUndefinedOption, 3, 'binary', 'duplex', true); test('Cannot use "final" with duplexes and stdin', testUndefinedOption, 0, 'final', 'duplex', uppercaseBufferDuplex().transform); test('Cannot use "final" with duplexes and stdout', testUndefinedOption, 1, 'final', 'duplex', uppercaseBufferDuplex().transform); test('Cannot use "final" with duplexes and stderr', testUndefinedOption, 2, 'final', 'duplex', uppercaseBufferDuplex().transform); test('Cannot use "final" with duplexes and stdio[*]', testUndefinedOption, 3, 'final', 'duplex', uppercaseBufferDuplex().transform); test('Cannot use "binary" with webTransforms and stdin', testUndefinedOption, 0, 'binary', 'webTransform', true); test('Cannot use "binary" with webTransforms and stdout', testUndefinedOption, 1, 'binary', 'webTransform', true); test('Cannot use "binary" with webTransforms and stderr', testUndefinedOption, 2, 'binary', 'webTransform', true); test('Cannot use "binary" with webTransforms and stdio[*]', testUndefinedOption, 3, 'binary', 'webTransform', true); test('Cannot use "final" with webTransforms and stdin', testUndefinedOption, 0, 'final', 'webTransform', uppercaseBufferWebTransform().transform); test('Cannot use "final" with webTransforms and stdout', testUndefinedOption, 1, 'final', 'webTransform', uppercaseBufferWebTransform().transform); test('Cannot use "final" with webTransforms and stderr', testUndefinedOption, 2, 'final', 'webTransform', uppercaseBufferWebTransform().transform); test('Cannot use "final" with webTransforms and stdio[*]', testUndefinedOption, 3, 'final', 'webTransform', uppercaseBufferWebTransform().transform); const testUndefinedFinal = (t, fdNumber, type, useTransform) => { t.throws(() => { execa('empty.js', getStdio(fdNumber, { transform: useTransform ? uppercaseGenerator().transform : undefined, final: generatorsMap[type].uppercase().transform, })); }, {message: type === 'duplex' ? /must not be a Duplex/ : /must not be a web TransformStream/}); }; test('Cannot use "final" with duplexes and stdin, without transform', testUndefinedFinal, 0, 'duplex', false); test('Cannot use "final" with duplexes and stdout, without transform', testUndefinedFinal, 1, 'duplex', false); test('Cannot use "final" with duplexes and stderr, without transform', testUndefinedFinal, 2, 'duplex', false); test('Cannot use "final" with duplexes and stdio[*], without transform', testUndefinedFinal, 3, 'duplex', false); test('Cannot use "final" with duplexes and stdin, with transform', testUndefinedFinal, 0, 'duplex', true); test('Cannot use "final" with duplexes and stdout, with transform', testUndefinedFinal, 1, 'duplex', true); test('Cannot use "final" with duplexes and stderr, with transform', testUndefinedFinal, 2, 'duplex', true); test('Cannot use "final" with duplexes and stdio[*], with transform', testUndefinedFinal, 3, 'duplex', true); test('Cannot use "final" with webTransforms and stdin, without transform', testUndefinedFinal, 0, 'webTransform', false); test('Cannot use "final" with webTransforms and stdout, without transform', testUndefinedFinal, 1, 'webTransform', false); test('Cannot use "final" with webTransforms and stderr, without transform', testUndefinedFinal, 2, 'webTransform', false); test('Cannot use "final" with webTransforms and stdio[*], without transform', testUndefinedFinal, 3, 'webTransform', false); test('Cannot use "final" with webTransforms and stdin, with transform', testUndefinedFinal, 0, 'webTransform', true); test('Cannot use "final" with webTransforms and stdout, with transform', testUndefinedFinal, 1, 'webTransform', true); test('Cannot use "final" with webTransforms and stderr, with transform', testUndefinedFinal, 2, 'webTransform', true); test('Cannot use "final" with webTransforms and stdio[*], with transform', testUndefinedFinal, 3, 'webTransform', true); const testSyncMethodsDuplex = (t, fdNumber, type) => { t.throws(() => { execaSync('empty.js', getStdio(fdNumber, generatorsMap[type].uppercase())); }, {message: type === 'duplex' ? /cannot be a Duplex stream/ : /cannot be a web TransformStream/}); }; test('Cannot use duplexes with sync methods and stdin', testSyncMethodsDuplex, 0, 'duplex'); test('Cannot use duplexes with sync methods and stdout', testSyncMethodsDuplex, 1, 'duplex'); test('Cannot use duplexes with sync methods and stderr', testSyncMethodsDuplex, 2, 'duplex'); test('Cannot use duplexes with sync methods and stdio[*]', testSyncMethodsDuplex, 3, 'duplex'); test('Cannot use webTransforms with sync methods and stdin', testSyncMethodsDuplex, 0, 'webTransform'); test('Cannot use webTransforms with sync methods and stdout', testSyncMethodsDuplex, 1, 'webTransform'); test('Cannot use webTransforms with sync methods and stderr', testSyncMethodsDuplex, 2, 'webTransform'); test('Cannot use webTransforms with sync methods and stdio[*]', testSyncMethodsDuplex, 3, 'webTransform'); ================================================ FILE: test/stdio/typed-array.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import {foobarUint8Array, foobarBuffer, foobarString} from '../helpers/input.js'; setFixtureDirectory(); const testUint8Array = async (t, fdNumber, stdioOption, execaMethod) => { const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, stdioOption)); t.is(stdout, foobarString); }; test('stdin option can be a Uint8Array', testUint8Array, 0, foobarUint8Array, execa); test('stdio[*] option can be a Uint8Array', testUint8Array, 3, foobarUint8Array, execa); test('stdin option can be a Uint8Array - sync', testUint8Array, 0, foobarUint8Array, execaSync); test('stdin option can be a Buffer', testUint8Array, 0, foobarBuffer, execa); test('stdio[*] option can be a Buffer', testUint8Array, 3, foobarBuffer, execa); test('stdin option can be a Buffer - sync', testUint8Array, 0, foobarBuffer, execaSync); const testNoUint8ArrayOutput = (t, fdNumber, stdioOption, execaMethod) => { t.throws(() => { execaMethod('empty.js', getStdio(fdNumber, stdioOption)); }, {message: /cannot be a Uint8Array/}); }; test('stdout option cannot be a Uint8Array', testNoUint8ArrayOutput, 1, foobarUint8Array, execa); test('stderr option cannot be a Uint8Array', testNoUint8ArrayOutput, 2, foobarUint8Array, execa); test('stdout option cannot be a Uint8Array - sync', testNoUint8ArrayOutput, 1, foobarUint8Array, execaSync); test('stderr option cannot be a Uint8Array - sync', testNoUint8ArrayOutput, 2, foobarUint8Array, execaSync); test('stdout option cannot be a Buffer', testNoUint8ArrayOutput, 1, foobarBuffer, execa); test('stderr option cannot be a Buffer', testNoUint8ArrayOutput, 2, foobarBuffer, execa); test('stdout option cannot be a Buffer - sync', testNoUint8ArrayOutput, 1, foobarBuffer, execaSync); test('stderr option cannot be a Buffer - sync', testNoUint8ArrayOutput, 2, foobarBuffer, execaSync); ================================================ FILE: test/stdio/web-stream.js ================================================ import {Readable} from 'node:stream'; import {setImmediate} from 'node:timers/promises'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; setFixtureDirectory(); const testReadableStream = async (t, fdNumber) => { const readableStream = Readable.toWeb(Readable.from('foobar')); const {stdout} = await execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, readableStream)); t.is(stdout, 'foobar'); }; test('stdin can be a ReadableStream', testReadableStream, 0); test('stdio[*] can be a ReadableStream', testReadableStream, 3); const testWritableStream = async (t, fdNumber) => { const result = []; const writableStream = new WritableStream({ write(chunk) { result.push(chunk); }, }); await execa('noop-fd.js', [`${fdNumber}`, 'foobar'], getStdio(fdNumber, writableStream)); t.is(result.join(''), 'foobar'); }; test('stdout can be a WritableStream', testWritableStream, 1); test('stderr can be a WritableStream', testWritableStream, 2); test('stdio[*] can be a WritableStream', testWritableStream, 3); const testWebStreamSync = (t, StreamClass, fdNumber, optionName) => { t.throws(() => { execaSync('empty.js', getStdio(fdNumber, new StreamClass())); }, {message: `The \`${optionName}\` option cannot be a web stream with synchronous methods.`}); }; test('stdin cannot be a ReadableStream - sync', testWebStreamSync, ReadableStream, 0, 'stdin'); test('stdio[*] cannot be a ReadableStream - sync', testWebStreamSync, ReadableStream, 3, 'stdio[3]'); test('stdout cannot be a WritableStream - sync', testWebStreamSync, WritableStream, 1, 'stdout'); test('stderr cannot be a WritableStream - sync', testWebStreamSync, WritableStream, 2, 'stderr'); test('stdio[*] cannot be a WritableStream - sync', testWebStreamSync, WritableStream, 3, 'stdio[3]'); const testLongWritableStream = async (t, fdNumber) => { let result = false; const writableStream = new WritableStream({ async close() { await setImmediate(); result = true; }, }); await execa('empty.js', getStdio(fdNumber, writableStream)); t.true(result); }; test('stdout waits for WritableStream completion', testLongWritableStream, 1); test('stderr waits for WritableStream completion', testLongWritableStream, 2); test('stdio[*] waits for WritableStream completion', testLongWritableStream, 3); const testWritableStreamError = async (t, fdNumber) => { const cause = new Error('foobar'); const writableStream = new WritableStream({ start(controller) { controller.error(cause); }, }); const error = await t.throwsAsync(execa('noop.js', getStdio(fdNumber, writableStream))); t.is(error.cause, cause); }; test('stdout option handles errors in WritableStream', testWritableStreamError, 1); test('stderr option handles errors in WritableStream', testWritableStreamError, 2); test('stdio[*] option handles errors in WritableStream', testWritableStreamError, 3); const testReadableStreamError = async (t, fdNumber) => { const cause = new Error('foobar'); const readableStream = new ReadableStream({ start(controller) { controller.error(cause); }, }); const error = await t.throwsAsync(execa('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, readableStream))); t.is(error.cause, cause); }; test('stdin option handles errors in ReadableStream', testReadableStreamError, 0); test('stdio[*] option handles errors in ReadableStream', testReadableStreamError, 3); test('ReadableStream with stdin is canceled on subprocess exit', async t => { let readableStream; const promise = new Promise(resolve => { readableStream = new ReadableStream({cancel: resolve}); }); await t.throwsAsync(execa('stdin.js', {stdin: readableStream, timeout: 1}), {message: /timed out/}); await promise; }); ================================================ FILE: test/stdio/web-transform.js ================================================ import {promisify} from 'node:util'; import {gunzip} from 'node:zlib'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarUtf16Uint8Array, foobarUint8Array} from '../helpers/input.js'; setFixtureDirectory(); test('Can use CompressionStream()', async t => { const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: new CompressionStream('gzip'), encoding: 'buffer'}); const decompressedStdout = await promisify(gunzip)(stdout); t.is(decompressedStdout.toString(), foobarString); }); test('Can use TextDecoderStream()', async t => { const {stdout} = await execa('stdin.js', { input: foobarUtf16Uint8Array, stdout: new TextDecoderStream('utf-16le'), encoding: 'buffer', }); t.deepEqual(stdout, foobarUint8Array); }); ================================================ FILE: test/terminate/cancel.js ================================================ import {once, getEventListeners} from 'node:events'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const testValidCancelSignal = (t, cancelSignal) => { t.throws(() => { execa('empty.js', {cancelSignal}); }, {message: /must be an AbortSignal/}); }; test('cancelSignal option cannot be AbortController', testValidCancelSignal, new AbortController()); test('cancelSignal option cannot be {}', testValidCancelSignal, {}); test('cancelSignal option cannot be null', testValidCancelSignal, null); test('cancelSignal option cannot be a symbol', testValidCancelSignal, Symbol('test')); test('result.isCanceled is false when abort isn\'t called (success)', async t => { const {isCanceled, isGracefullyCanceled} = await execa('noop.js'); t.false(isCanceled); t.false(isGracefullyCanceled); }); test('result.isCanceled is false when abort isn\'t called (failure)', async t => { const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(execa('fail.js')); t.false(isCanceled); t.false(isGracefullyCanceled); }); test('result.isCanceled is false when abort isn\'t called in sync mode (success)', t => { const {isCanceled, isGracefullyCanceled} = execaSync('noop.js'); t.false(isCanceled); t.false(isGracefullyCanceled); }); test('result.isCanceled is false when abort isn\'t called in sync mode (failure)', t => { const {isCanceled, isGracefullyCanceled} = t.throws(() => { execaSync('fail.js'); }); t.false(isCanceled); t.false(isGracefullyCanceled); }); const testCancelSuccess = async (t, options) => { const abortController = new AbortController(); const subprocess = execa('noop.js', {cancelSignal: abortController.signal, ...options}); abortController.abort(); const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); t.true(isCanceled); t.false(isGracefullyCanceled); }; test('error.isCanceled is true when abort is used', testCancelSuccess, {}); test('gracefulCancel can be false with cancelSignal', testCancelSuccess, {gracefulCancel: false}); test('ipc can be false with cancelSignal', testCancelSuccess, {ipc: false}); test('serialization can be "json" with cancelSignal', testCancelSuccess, {ipc: true, serialization: 'json'}); test('error.isCanceled is false when kill method is used', async t => { const abortController = new AbortController(); const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); subprocess.kill(); const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); t.false(isCanceled); t.false(isGracefullyCanceled); }); test('calling abort is considered a signal termination', async t => { const abortController = new AbortController(); const subprocess = execa('forever.js', {cancelSignal: abortController.signal}); await once(subprocess, 'spawn'); abortController.abort(); const {isCanceled, isGracefullyCanceled, isTerminated, signal} = await t.throwsAsync(subprocess); t.true(isCanceled); t.false(isGracefullyCanceled); t.true(isTerminated); t.is(signal, 'SIGTERM'); }); test('cancelSignal can already be aborted', async t => { const cancelSignal = AbortSignal.abort(); const {isCanceled, isGracefullyCanceled, isTerminated, signal} = await t.throwsAsync(execa('forever.js', {cancelSignal})); t.true(isCanceled); t.false(isGracefullyCanceled); t.true(isTerminated); t.is(signal, 'SIGTERM'); t.deepEqual(getEventListeners(cancelSignal, 'abort'), []); }); test('calling abort does not emit the "error" event', async t => { const abortController = new AbortController(); const subprocess = execa('forever.js', {cancelSignal: abortController.signal}); let error; subprocess.once('error', errorArgument => { error = errorArgument; }); abortController.abort(); const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); t.true(isCanceled); t.false(isGracefullyCanceled); t.is(error, undefined); }); test('calling abort cleans up listeners on cancelSignal, called', async t => { const abortController = new AbortController(); const subprocess = execa('forever.js', {cancelSignal: abortController.signal}); t.is(getEventListeners(abortController.signal, 'abort').length, 1); abortController.abort(); const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); t.true(isCanceled); t.false(isGracefullyCanceled); t.is(getEventListeners(abortController.signal, 'abort').length, 0); }); test('calling abort cleans up listeners on cancelSignal, not called', async t => { const abortController = new AbortController(); const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); t.is(getEventListeners(abortController.signal, 'abort').length, 1); await subprocess; t.is(getEventListeners(abortController.signal, 'abort').length, 0); }); test('calling abort cleans up listeners on cancelSignal, already aborted', async t => { const cancelSignal = AbortSignal.abort(); const subprocess = execa('noop.js', {cancelSignal}); t.is(getEventListeners(cancelSignal, 'abort').length, 0); const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); t.true(isCanceled); t.false(isGracefullyCanceled); t.is(getEventListeners(cancelSignal, 'abort').length, 0); }); test('calling abort throws an error with message "Command was canceled"', async t => { const abortController = new AbortController(); const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); abortController.abort(); await t.throwsAsync(subprocess, {message: /Command was canceled/}); }); test('calling abort with no argument keeps error properties', async t => { const abortController = new AbortController(); const subprocess = execa('empty.js', {cancelSignal: abortController.signal}); abortController.abort(); const {cause, originalMessage, shortMessage, message} = await t.throwsAsync(subprocess); t.is(cause.message, 'This operation was aborted'); t.is(cause.name, 'AbortError'); t.is(originalMessage, 'This operation was aborted'); t.is(shortMessage, 'Command was canceled: empty.js\nThis operation was aborted'); t.is(message, 'Command was canceled: empty.js\nThis operation was aborted'); }); test('calling abort with an error instance keeps error properties', async t => { const abortController = new AbortController(); const subprocess = execa('empty.js', {cancelSignal: abortController.signal}); const error = new Error(foobarString); error.code = foobarString; abortController.abort(error); const {cause, originalMessage, shortMessage, message, code} = await t.throwsAsync(subprocess); t.is(cause, error); t.is(originalMessage, foobarString); t.is(shortMessage, `Command was canceled: empty.js\n${foobarString}`); t.is(message, `Command was canceled: empty.js\n${foobarString}`); t.is(code, foobarString); }); test('calling abort with null keeps error properties', async t => { const abortController = new AbortController(); const subprocess = execa('empty.js', {cancelSignal: abortController.signal}); abortController.abort(null); const {cause, originalMessage, shortMessage, message} = await t.throwsAsync(subprocess); t.is(cause, null); t.is(originalMessage, 'null'); t.is(shortMessage, 'Command was canceled: empty.js\nnull'); t.is(message, 'Command was canceled: empty.js\nnull'); }); test('calling abort twice should show the same behaviour as calling it once', async t => { const abortController = new AbortController(); const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); abortController.abort(); abortController.abort(); const {isCanceled, isGracefullyCanceled} = await t.throwsAsync(subprocess); t.true(isCanceled); t.false(isGracefullyCanceled); }); test('calling abort on a successfully completed subprocess does not make result.isCanceled true', async t => { const abortController = new AbortController(); const subprocess = execa('noop.js', {cancelSignal: abortController.signal}); const {isCanceled, isGracefullyCanceled} = await subprocess; abortController.abort(); t.false(isCanceled); t.false(isGracefullyCanceled); }); test('Throws when using the former "signal" option name', t => { const abortController = new AbortController(); t.throws(() => { execa('empty.js', {signal: abortController.signal}); }, {message: /renamed to "cancelSignal"/}); }); test('Cannot use cancelSignal, sync', t => { const abortController = new AbortController(); t.throws(() => { execaSync('empty.js', {cancelSignal: abortController.signal}); }, {message: /The "cancelSignal" option cannot be used/}); }); ================================================ FILE: test/terminate/cleanup.js ================================================ import process from 'node:process'; import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import isRunning from 'is-running'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {nestedSubprocess} from '../helpers/nested.js'; import {foobarString} from '../helpers/input.js'; setFixtureDirectory(); const isWindows = process.platform === 'win32'; // When subprocess exits before current process const spawnAndExit = async (t, worker, cleanup, detached) => { const {nestedResult: {stdout}} = await nestedSubprocess('noop-fd.js', ['1', foobarString], {worker, cleanup, detached}); t.is(stdout, foobarString); }; test('spawnAndExit', spawnAndExit, false, false, false); test('spawnAndExit cleanup', spawnAndExit, false, true, false); test('spawnAndExit detached', spawnAndExit, false, false, true); test('spawnAndExit cleanup detached', spawnAndExit, false, true, true); test('spawnAndExit, worker', spawnAndExit, true, false, false); test('spawnAndExit cleanup, worker', spawnAndExit, true, true, false); test('spawnAndExit detached, worker', spawnAndExit, true, false, true); test('spawnAndExit cleanup detached, worker', spawnAndExit, true, true, true); // When current process exits before subprocess const spawnAndKill = async (t, [signal, cleanup, detached, isKilled]) => { const subprocess = execa('ipc-send-pid.js', [cleanup, detached], {stdio: 'ignore', ipc: true}); const pid = await subprocess.getOneMessage(); t.true(Number.isInteger(pid)); t.true(isRunning(pid)); process.kill(subprocess.pid, signal); await t.throwsAsync(subprocess); t.false(isRunning(subprocess.pid)); if (isKilled) { await Promise.race([ setTimeout(1e4, undefined, {ref: false}), pollForSubprocessExit(pid), ]); t.is(isRunning(pid), false); } else { t.is(isRunning(pid), true); process.kill(pid, 'SIGKILL'); } }; const pollForSubprocessExit = async pid => { while (isRunning(pid)) { // eslint-disable-next-line no-await-in-loop await setTimeout(100); } }; // Without `options.cleanup`: // - on Windows subprocesses are killed if `options.detached: false`, but not // if `options.detached: true` // - on Linux subprocesses are never killed regardless of `options.detached` // With `options.cleanup`, subprocesses are always killed // - `options.cleanup` with SIGKILL is a noop, since it cannot be handled test('spawnAndKill SIGTERM', spawnAndKill, ['SIGTERM', false, false, isWindows]); test('spawnAndKill SIGKILL', spawnAndKill, ['SIGKILL', false, false, isWindows]); test('spawnAndKill cleanup SIGTERM', spawnAndKill, ['SIGTERM', true, false, true]); test('spawnAndKill cleanup SIGKILL', spawnAndKill, ['SIGKILL', true, false, isWindows]); test('spawnAndKill detached SIGTERM', spawnAndKill, ['SIGTERM', false, true, false]); test('spawnAndKill detached SIGKILL', spawnAndKill, ['SIGKILL', false, true, false]); test('spawnAndKill cleanup detached SIGTERM', spawnAndKill, ['SIGTERM', true, true, false]); test('spawnAndKill cleanup detached SIGKILL', spawnAndKill, ['SIGKILL', true, true, false]); // See #128 test('removes exit handler on exit', async t => { // @todo this relies on `signal-exit` internals const exitListeners = globalThis[Symbol.for('signal-exit emitter')].listeners.exit; const subprocess = execa('noop.js'); const listener = exitListeners.at(-1); await subprocess; t.false(exitListeners.includes(listener)); }); test('detach subprocess', async t => { const {stdout} = await execa('detach.js'); const pid = Number(stdout); t.true(Number.isInteger(pid)); t.true(isRunning(pid)); process.kill(pid, 'SIGKILL'); }); test('Cannot use "detached" option, sync', t => { t.throws(() => { execaSync('empty.js', {detached: true}); }, {message: /The "detached: true" option cannot be used/}); }); ================================================ FILE: test/terminate/graceful.js ================================================ import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {mockSendIoError} from '../helpers/ipc.js'; setFixtureDirectory(); test('cancelSignal cannot be undefined with gracefulCancel', t => { t.throws(() => { execa('empty.js', {gracefulCancel: true}); }, {message: /The `cancelSignal` option must be defined/}); }); test('ipc cannot be false with gracefulCancel', t => { t.throws(() => { execa('empty.js', {gracefulCancel: true, cancelSignal: AbortSignal.abort(), ipc: false}); }, {message: /The `ipc` option cannot be false/}); }); test('serialization cannot be "json" with gracefulCancel', t => { t.throws(() => { execa('empty.js', {gracefulCancel: true, cancelSignal: AbortSignal.abort(), serialization: 'json'}); }, {message: /The `serialization` option cannot be 'json'/}); }); test('Current process can send a message right away', async t => { const controller = new AbortController(); const subprocess = execa('ipc-echo.js', {cancelSignal: controller.signal, gracefulCancel: true}); await subprocess.sendMessage(foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); }); test('Current process can receive a message right away', async t => { const controller = new AbortController(); const subprocess = execa('ipc-send.js', {cancelSignal: controller.signal, gracefulCancel: true}); t.is(await subprocess.getOneMessage(), foobarString); const {ipcOutput} = await subprocess; t.deepEqual(ipcOutput, [foobarString]); }); test('Does not disconnect during I/O errors when sending the abort reason', async t => { const controller = new AbortController(); const subprocess = execa('ipc-echo.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); const error = mockSendIoError(subprocess); controller.abort(foobarString); await setTimeout(0); t.true(subprocess.connected); subprocess.kill(); const {isCanceled, isGracefullyCanceled, signal, ipcOutput, cause} = await t.throwsAsync(subprocess); t.false(isCanceled); t.false(isGracefullyCanceled); t.is(signal, 'SIGTERM'); t.deepEqual(ipcOutput, []); t.is(cause, error); }); class AbortError extends Error { name = 'AbortError'; } test('Abort reason is sent to the subprocess', async t => { const controller = new AbortController(); const subprocess = execa('graceful-send.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); const error = new AbortError(foobarString); controller.abort(error); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, cause, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.is(cause, error); t.is(ipcOutput[0].message, error.message); t.is(ipcOutput[0].stack, error.stack); t.is(ipcOutput[0].name, 'Error'); }); test('Abort default reason is sent to the subprocess', async t => { const controller = new AbortController(); const subprocess = execa('graceful-send.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); controller.abort(); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, cause, ipcOutput} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); const {reason} = controller.signal; t.is(cause.stack, reason.stack); t.is(ipcOutput[0].message, reason.message); t.is(ipcOutput[0].stack, reason.stack); }); test('Fail when sending non-serializable abort reason', async t => { const controller = new AbortController(); const subprocess = execa('ipc-echo.js', {cancelSignal: controller.signal, gracefulCancel: true, forceKillAfterDelay: false}); controller.abort(() => {}); await setTimeout(0); t.true(subprocess.connected); await subprocess.sendMessage(foobarString); const {isCanceled, isGracefullyCanceled, isTerminated, exitCode, cause, ipcOutput} = await t.throwsAsync(subprocess); t.false(isCanceled); t.false(isGracefullyCanceled); t.false(isTerminated); t.is(exitCode, 0); t.deepEqual(ipcOutput, [foobarString]); t.is(cause.message, '`cancelSignal`\'s `controller.abort()`\'s argument type is invalid: the message cannot be serialized: () => {}.'); t.is(cause.cause.message, '() => {} could not be cloned.'); }); test('timeout does not use graceful cancelSignal', async t => { const controller = new AbortController(); const {timedOut, isCanceled, isGracefullyCanceled, isTerminated, signal, exitCode, shortMessage, ipcOutput} = await t.throwsAsync(execa('graceful-send.js', {cancelSignal: controller.signal, gracefulCancel: true, timeout: 1})); t.true(timedOut); t.false(isCanceled); t.false(isGracefullyCanceled); t.true(isTerminated); t.is(signal, 'SIGTERM'); t.is(exitCode, undefined); t.is(shortMessage, 'Command timed out after 1 milliseconds: graceful-send.js'); t.deepEqual(ipcOutput, []); }); test('error on graceful cancelSignal on non-0 exit code', async t => { const {isCanceled, isGracefullyCanceled, isTerminated, isForcefullyTerminated, exitCode, shortMessage} = await t.throwsAsync(execa('wait-fail.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true, forceKillAfterDelay: false})); t.true(isCanceled); t.true(isGracefullyCanceled); t.false(isTerminated); t.false(isForcefullyTerminated); t.is(exitCode, 2); t.is(shortMessage, 'Command was gracefully canceled with exit code 2: wait-fail.js'); }); test('error on graceful cancelSignal on forceful termination', async t => { const {isCanceled, isGracefullyCanceled, isTerminated, signal, isForcefullyTerminated, exitCode, shortMessage} = await t.throwsAsync(execa('forever.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true, forceKillAfterDelay: 1})); t.true(isCanceled); t.true(isGracefullyCanceled); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.true(isForcefullyTerminated); t.is(exitCode, undefined); t.is(shortMessage, 'Command was gracefully canceled and was forcefully terminated after 1 milliseconds: forever.js'); }); test('error on graceful cancelSignal on non-forceful termination', async t => { const subprocess = execa('ipc-send-get.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true, forceKillAfterDelay: 1e6}); t.is(await subprocess.getOneMessage(), foobarString); subprocess.kill(); const {isCanceled, isGracefullyCanceled, isTerminated, signal, isForcefullyTerminated, exitCode, shortMessage} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.true(isTerminated); t.is(signal, 'SIGTERM'); t.false(isForcefullyTerminated); t.is(exitCode, undefined); t.is(shortMessage, 'Command was gracefully canceled with SIGTERM (Termination): ipc-send-get.js'); }); test('`forceKillAfterDelay: false` with the "cancelSignal" option when graceful', async t => { const subprocess = execa('forever.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true, forceKillAfterDelay: false}); await setTimeout(6e3); subprocess.kill('SIGKILL'); const {isCanceled, isGracefullyCanceled, isTerminated, signal, isForcefullyTerminated, exitCode, shortMessage} = await t.throwsAsync(subprocess); t.true(isCanceled); t.true(isGracefullyCanceled); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.false(isForcefullyTerminated); t.is(exitCode, undefined); t.is(shortMessage, 'Command was gracefully canceled with SIGKILL (Forced termination): forever.js'); }); test('subprocess.getCancelSignal() is not defined', async t => { const subprocess = execa('empty.js', {cancelSignal: AbortSignal.abort(''), gracefulCancel: true}); t.is(subprocess.getCancelSignal, undefined); await t.throwsAsync(subprocess); }); ================================================ FILE: test/terminate/kill-error.js ================================================ import {once} from 'node:events'; import {setImmediate} from 'node:timers/promises'; import test from 'ava'; import isRunning from 'is-running'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); test('.kill(error) propagates error', async t => { const subprocess = execa('forever.js'); const originalMessage = 'test'; const cause = new Error(originalMessage); t.true(subprocess.kill(cause)); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.true(cause.stack.includes(import.meta.url)); t.is(error.exitCode, undefined); t.is(error.signal, 'SIGTERM'); t.true(error.isTerminated); t.is(error.originalMessage, originalMessage); t.true(error.message.includes(originalMessage)); t.true(error.message.includes('was killed with SIGTERM')); }); test('.kill(error) uses killSignal', async t => { const subprocess = execa('forever.js', {killSignal: 'SIGINT'}); const cause = new Error('test'); subprocess.kill(cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.is(error.signal, 'SIGINT'); }); test('.kill(signal, error) uses signal', async t => { const subprocess = execa('forever.js'); const cause = new Error('test'); subprocess.kill('SIGINT', cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.is(error.signal, 'SIGINT'); }); test('.kill(error) is a noop if subprocess already exited', async t => { const subprocess = execa('empty.js'); await subprocess; t.false(isRunning(subprocess.pid)); t.false(subprocess.kill(new Error('test'))); }); test('.kill(error) terminates but does not change the error if the subprocess already errored but did not exit yet', async t => { const subprocess = execa('forever.js'); const cause = new Error('first'); subprocess.stdout.destroy(cause); await setImmediate(); const secondError = new Error('second'); t.true(subprocess.kill(secondError)); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.is(error.exitCode, undefined); t.is(error.signal, 'SIGTERM'); t.true(error.isTerminated); t.false(error.message.includes(secondError.message)); }); test('.kill(error) twice in a row', async t => { const subprocess = execa('forever.js'); const cause = new Error('first'); subprocess.kill(cause); const secondCause = new Error('second'); subprocess.kill(secondCause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.false(error.message.includes(secondCause.message)); }); test('.kill(error) does not emit the "error" event', async t => { const subprocess = execa('forever.js'); const cause = new Error('test'); subprocess.kill(cause); const error = await Promise.race([t.throwsAsync(subprocess), once(subprocess, 'error')]); t.is(error.cause, cause); }); ================================================ FILE: test/terminate/kill-force.js ================================================ import process from 'node:process'; import {once, defaultMaxListeners} from 'node:events'; import {constants} from 'node:os'; import {setTimeout} from 'node:timers/promises'; import test from 'ava'; import isRunning from 'is-running'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {assertMaxListeners} from '../helpers/listeners.js'; import {foobarString} from '../helpers/input.js'; import {getEarlyErrorSubprocess} from '../helpers/early-error.js'; setFixtureDirectory(); const isWindows = process.platform === 'win32'; const spawnNoKillable = async (forceKillAfterDelay, options) => { const subprocess = execa('no-killable.js', { ipc: true, forceKillAfterDelay, ...options, }); await subprocess.getOneMessage(); return {subprocess}; }; const noKillableSimpleOptions = {killSignal: 'SIGWINCH', forceKillAfterDelay: 1}; const spawnNoKillableSimple = options => execa('forever.js', {...noKillableSimpleOptions, ...options}); test('kill("SIGKILL") should terminate cleanly', async t => { const {subprocess} = await spawnNoKillable(); subprocess.kill('SIGKILL'); const {isTerminated, signal, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.false(isForcefullyTerminated); t.is(shortMessage, 'Command was killed with SIGKILL (Forced termination): no-killable.js'); }); const testInvalidForceKill = async (t, forceKillAfterDelay) => { t.throws(() => { execa('empty.js', {forceKillAfterDelay}); }, {instanceOf: TypeError, message: /non-negative integer/}); }; test('`forceKillAfterDelay` should not be NaN', testInvalidForceKill, Number.NaN); test('`forceKillAfterDelay` should not be negative', testInvalidForceKill, -1); // `SIGTERM` cannot be caught on Windows, and it always aborts the subprocess (like `SIGKILL` on Unix). // Therefore, this feature and those tests must be different on Windows. if (isWindows) { test('Can call `.kill()` with `forceKillAfterDelay` on Windows', async t => { const {subprocess} = await spawnNoKillable(); subprocess.kill(); const {isTerminated, signal, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGTERM'); t.false(isForcefullyTerminated); t.is(shortMessage, 'Command was killed with SIGTERM (Termination): no-killable.js'); }); } else { const testNoForceKill = async (t, forceKillAfterDelay, killArgument, options) => { const {subprocess} = await spawnNoKillable(forceKillAfterDelay, options); subprocess.kill(killArgument); await setTimeout(6e3); t.true(isRunning(subprocess.pid)); subprocess.kill('SIGKILL'); const {isTerminated, signal, isForcefullyTerminated} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.false(isForcefullyTerminated); }; test('`forceKillAfterDelay: false` should not kill after a timeout', testNoForceKill, false); test('`forceKillAfterDelay` should not kill after a timeout with other signals', testNoForceKill, true, 'SIGINT'); test('`forceKillAfterDelay` should not kill after a timeout with wrong killSignal string', testNoForceKill, true, 'SIGTERM', {killSignal: 'SIGINT'}); test('`forceKillAfterDelay` should not kill after a timeout with wrong killSignal number', testNoForceKill, true, constants.signals.SIGTERM, {killSignal: constants.signals.SIGINT}); // eslint-disable-next-line max-params const testForceKill = async (t, forceKillAfterDelay, killSignal, expectedDelay, expectedKillSignal, options) => { const {subprocess} = await spawnNoKillable(forceKillAfterDelay, options); subprocess.kill(killSignal); const {isTerminated, signal, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.true(isForcefullyTerminated); const messageSuffix = killSignal instanceof Error ? `\n${killSignal.message}` : ''; const signalDescription = expectedKillSignal === 'SIGINT' ? 'User interruption with CTRL-C' : 'Termination'; t.is(shortMessage, `Command was killed with ${expectedKillSignal} (${signalDescription}) and was forcefully terminated after ${expectedDelay} milliseconds: no-killable.js${messageSuffix}`); }; test('`forceKillAfterDelay: number` should kill after a timeout', testForceKill, 50, undefined, 50, 'SIGTERM'); test('`forceKillAfterDelay: true` should kill after a timeout', testForceKill, true, undefined, 5e3, 'SIGTERM'); test('`forceKillAfterDelay: undefined` should kill after a timeout', testForceKill, undefined, undefined, 5e3, 'SIGTERM'); test('`forceKillAfterDelay` should kill after a timeout with SIGTERM', testForceKill, 50, 'SIGTERM', 50, 'SIGTERM'); test('`forceKillAfterDelay` should kill after a timeout with the killSignal string', testForceKill, 50, 'SIGINT', 50, 'SIGINT', {killSignal: 'SIGINT'}); test('`forceKillAfterDelay` should kill after a timeout with the killSignal string, mixed', testForceKill, 50, 'SIGINT', 50, 'SIGINT', {killSignal: constants.signals.SIGINT}); test('`forceKillAfterDelay` should kill after a timeout with the killSignal number', testForceKill, 50, constants.signals.SIGINT, 50, 'SIGINT', {killSignal: constants.signals.SIGINT}); test('`forceKillAfterDelay` should kill after a timeout with the killSignal number, mixed', testForceKill, 50, constants.signals.SIGINT, 50, 'SIGINT', {killSignal: 'SIGINT'}); test('`forceKillAfterDelay` should kill after a timeout with an error', testForceKill, 50, new Error('test'), 50, 'SIGTERM'); test('`forceKillAfterDelay` should kill after a timeout with an error and a killSignal', testForceKill, 50, new Error('test'), 50, 'SIGINT', {killSignal: 'SIGINT'}); test('`forceKillAfterDelay` works with the "cancelSignal" option', async t => { const abortController = new AbortController(); const subprocess = spawnNoKillableSimple({cancelSignal: abortController.signal}); await once(subprocess, 'spawn'); abortController.abort(''); const {isTerminated, signal, isCanceled, isGracefullyCanceled, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.true(isCanceled); t.false(isGracefullyCanceled); t.true(isForcefullyTerminated); t.is(shortMessage, 'Command was canceled and was forcefully terminated after 1 milliseconds: forever.js'); }); test('`forceKillAfterDelay` works with the "timeout" option', async t => { const {isTerminated, signal, timedOut, isForcefullyTerminated, shortMessage} = await t.throwsAsync(spawnNoKillableSimple({timeout: 1})); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.true(timedOut); t.true(isForcefullyTerminated); t.is(shortMessage, 'Command timed out after 1 milliseconds and was forcefully terminated after 1 milliseconds: forever.js'); }); test('`forceKillAfterDelay` works with the "maxBuffer" option', async t => { const subprocess = execa('noop-forever.js', ['.'], {...noKillableSimpleOptions, maxBuffer: 1}); const [chunk] = await once(subprocess.stdout, 'data'); t.is(chunk.toString(), '.\n'); subprocess.kill(); const {isTerminated, signal, isForcefullyTerminated, shortMessage} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.true(isForcefullyTerminated); t.is(shortMessage, 'Command\'s stdout was larger than 1 characters and was forcefully terminated after 1 milliseconds: noop-forever.js .\nmaxBuffer exceeded'); }); test('`forceKillAfterDelay` works with the "error" event', async t => { const subprocess = spawnNoKillableSimple(); await once(subprocess, 'spawn'); const error = new Error(foobarString); error.code = 'ECODE'; subprocess.emit('error', error); subprocess.kill(); const {isTerminated, signal, isForcefullyTerminated, shortMessage, originalMessage, cause} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.true(isForcefullyTerminated); t.is(cause, error); t.is(originalMessage, error.message); t.is(shortMessage, `Command failed with ${error.code} and was forcefully terminated after 1 milliseconds: forever.js\n${error.message}`); }); test.serial('Can call `.kill()` with `forceKillAfterDelay` many times without triggering the maxListeners warning', async t => { const checkMaxListeners = assertMaxListeners(t); const subprocess = spawnNoKillableSimple(); for (let index = 0; index < defaultMaxListeners + 1; index += 1) { subprocess.kill(); } const {isTerminated, signal, isForcefullyTerminated} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.true(isForcefullyTerminated); checkMaxListeners(); }); test('Can call `.kill()` with `forceKillAfterDelay` multiple times', async t => { const subprocess = spawnNoKillableSimple(); subprocess.kill(); subprocess.kill(); const {isTerminated, signal, isForcefullyTerminated} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGKILL'); t.true(isForcefullyTerminated); }); } test('result.isForcefullyTerminated is false on success', async t => { const {isForcefullyTerminated} = await execa('empty.js'); t.false(isForcefullyTerminated); }); test('error.isForcefullyTerminated is false on spawn errors', async t => { const {isForcefullyTerminated} = await t.throwsAsync(getEarlyErrorSubprocess()); t.false(isForcefullyTerminated); }); test('error.isForcefullyTerminated is false when already terminated', async t => { const abortController = new AbortController(); const final = async function * () { try { await setTimeout(1e6, undefined, {signal: abortController.signal}); } catch {} yield * []; }; const subprocess = execa('forever.js', {stdout: {final}}); subprocess.kill(); await setTimeout(6e3); abortController.abort(); const {isForcefullyTerminated, isTerminated, signal} = await t.throwsAsync(subprocess); t.false(isForcefullyTerminated); t.true(isTerminated); t.is(signal, 'SIGTERM'); }); ================================================ FILE: test/terminate/kill-signal.js ================================================ import {once} from 'node:events'; import {platform} from 'node:process'; import {constants} from 'node:os'; import {setImmediate} from 'node:timers/promises'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {majorNodeVersion} from '../helpers/node-version.js'; setFixtureDirectory(); const isWindows = platform === 'win32'; const testKillSignal = async (t, killSignal) => { const {isTerminated, signal} = await t.throwsAsync(execa('forever.js', {killSignal, timeout: 1})); t.true(isTerminated); t.is(signal, 'SIGINT'); }; test('Can use killSignal: "SIGINT"', testKillSignal, 'SIGINT'); test('Can use killSignal: 2', testKillSignal, constants.signals.SIGINT); const testKillSignalSync = (t, killSignal) => { const {isTerminated, signal} = t.throws(() => { execaSync('forever.js', {killSignal, timeout: 1}); }); t.true(isTerminated); t.is(signal, 'SIGINT'); }; test('Can use killSignal: "SIGINT", sync', testKillSignalSync, 'SIGINT'); test('Can use killSignal: 2, sync', testKillSignalSync, constants.signals.SIGINT); test('Can call .kill("SIGTERM")', async t => { const subprocess = execa('forever.js'); subprocess.kill('SIGTERM'); const {isTerminated, signal} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGTERM'); }); test('Can call .kill(15)', async t => { const subprocess = execa('forever.js'); subprocess.kill(constants.signals.SIGTERM); const {isTerminated, signal} = await t.throwsAsync(subprocess); t.true(isTerminated); t.is(signal, 'SIGTERM'); }); test('Can call .kill(0)', async t => { const subprocess = execa('forever.js'); t.true(subprocess.kill(0)); subprocess.kill(); await t.throwsAsync(subprocess); t.false(subprocess.kill(0)); }); test('Can call `.kill()` multiple times', async t => { const subprocess = execa('forever.js'); subprocess.kill(); subprocess.kill(); const {exitCode, isTerminated, signal, code} = await t.throwsAsync(subprocess); // On Windows, calling `subprocess.kill()` twice emits an `error` event on the subprocess. // This does not happen when passing an `error` argument, nor when passing a non-terminating signal. // There is no easy way to make this cross-platform, so we document the difference here. if (isWindows && majorNodeVersion === 22) { t.is(exitCode, undefined); t.false(isTerminated); t.is(signal, undefined); t.is(code, 'EPERM'); } else { t.is(exitCode, undefined); t.true(isTerminated); t.is(signal, 'SIGTERM'); t.is(code, undefined); } }); test('execa() returns a promise with kill()', async t => { const subprocess = execa('noop.js', ['foo']); t.is(typeof subprocess.kill, 'function'); await subprocess; }); const testInvalidKillArgument = async (t, killArgument, secondKillArgument) => { const subprocess = execa('empty.js'); const message = secondKillArgument instanceof Error || secondKillArgument === undefined ? /error instance or a signal name/ : /second argument is optional/; t.throws(() => { subprocess.kill(killArgument, secondKillArgument); }, {message}); await subprocess; }; test('Cannot call .kill(errorObject)', testInvalidKillArgument, {name: '', message: '', stack: ''}); test('Cannot call .kill(errorArray)', testInvalidKillArgument, [new Error('test')]); test('Cannot call .kill(undefined, true)', testInvalidKillArgument, undefined, true); test('Cannot call .kill("SIGTERM", true)', testInvalidKillArgument, 'SIGTERM', true); test('Cannot call .kill(true, error)', testInvalidKillArgument, true, new Error('test')); test('subprocess errors are handled before spawn', async t => { const subprocess = execa('forever.js'); const cause = new Error('test'); subprocess.emit('error', cause); subprocess.kill(); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.is(error.exitCode, undefined); t.is(error.signal, undefined); t.false(error.isTerminated); }); test('subprocess errors are handled after spawn', async t => { const subprocess = execa('forever.js'); await once(subprocess, 'spawn'); const cause = new Error('test'); subprocess.emit('error', cause); subprocess.kill(); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.is(error.exitCode, undefined); t.is(error.signal, 'SIGTERM'); t.true(error.isTerminated); }); test('subprocess double errors are handled after spawn', async t => { const abortController = new AbortController(); const subprocess = execa('forever.js', {cancelSignal: abortController.signal}); await once(subprocess, 'spawn'); const cause = new Error('test'); subprocess.emit('error', cause); await setImmediate(); abortController.abort(); subprocess.emit('error', cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.is(error.exitCode, undefined); t.is(error.signal, 'SIGTERM'); t.true(error.isTerminated); }); test('subprocess errors use killSignal', async t => { const subprocess = execa('forever.js', {killSignal: 'SIGINT'}); await once(subprocess, 'spawn'); const cause = new Error('test'); subprocess.emit('error', cause); subprocess.kill(); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.true(error.isTerminated); t.is(error.signal, 'SIGINT'); }); ================================================ FILE: test/terminate/signal.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const VALIDATION_MESSAGES = { string: 'this signal name does not exist', integer: 'this signal integer does not exist', other: 'it must be a string or an integer', rename: 'please rename it to', zero: '0 cannot be used', }; const validateMessage = (t, message, type) => { t.true(message.includes(VALIDATION_MESSAGES[type])); if (type !== 'rename' && type !== 'zero') { t.true(message.includes('Available signal names: \'SIGABRT\', ')); t.true(message.includes('Available signal numbers: 1, ')); } }; const testInvalidKillSignal = (t, killSignal, type, execaMethod) => { const {message} = t.throws(() => { execaMethod('empty.js', {killSignal}); }); t.true(message.includes('Invalid option `killSignal`')); validateMessage(t, message, type); }; test('Cannot use killSignal: "SIGOTHER"', testInvalidKillSignal, 'SIGOTHER', 'string', execa); test('Cannot use killSignal: "Sigterm"', testInvalidKillSignal, 'Sigterm', 'rename', execa); test('Cannot use killSignal: "sigterm"', testInvalidKillSignal, 'sigterm', 'rename', execa); test('Cannot use killSignal: -1', testInvalidKillSignal, -1, 'integer', execa); test('Cannot use killSignal: 200', testInvalidKillSignal, 200, 'integer', execa); test('Cannot use killSignal: 1n', testInvalidKillSignal, 1n, 'other', execa); test('Cannot use killSignal: 1.5', testInvalidKillSignal, 1.5, 'other', execa); test('Cannot use killSignal: Infinity', testInvalidKillSignal, Number.POSITIVE_INFINITY, 'other', execa); test('Cannot use killSignal: NaN', testInvalidKillSignal, Number.NaN, 'other', execa); test('Cannot use killSignal: false', testInvalidKillSignal, false, 'other', execa); test('Cannot use killSignal: null', testInvalidKillSignal, null, 'other', execa); test('Cannot use killSignal: symbol', testInvalidKillSignal, Symbol('test'), 'other', execa); test('Cannot use killSignal: {}', testInvalidKillSignal, {}, 'other', execa); test('Cannot use killSignal: 0', testInvalidKillSignal, 0, 'zero', execa); test('Cannot use killSignal: "SIGOTHER", sync', testInvalidKillSignal, 'SIGOTHER', 'string', execaSync); test('Cannot use killSignal: "Sigterm", sync', testInvalidKillSignal, 'Sigterm', 'rename', execaSync); test('Cannot use killSignal: "sigterm", sync', testInvalidKillSignal, 'sigterm', 'rename', execaSync); test('Cannot use killSignal: -1, sync', testInvalidKillSignal, -1, 'integer', execaSync); test('Cannot use killSignal: 200, sync', testInvalidKillSignal, 200, 'integer', execaSync); test('Cannot use killSignal: 1.5, sync', testInvalidKillSignal, 1.5, 'other', execaSync); test('Cannot use killSignal: Infinity, sync', testInvalidKillSignal, Number.POSITIVE_INFINITY, 'other', execaSync); test('Cannot use killSignal: NaN, sync', testInvalidKillSignal, Number.NaN, 'other', execaSync); test('Cannot use killSignal: null, sync', testInvalidKillSignal, null, 'other', execaSync); test('Cannot use killSignal: symbol, sync', testInvalidKillSignal, Symbol('test'), 'other', execaSync); test('Cannot use killSignal: {}, sync', testInvalidKillSignal, {}, 'other', execaSync); test('Cannot use killSignal: 0, sync', testInvalidKillSignal, 0, 'zero', execaSync); const testInvalidSignalArgument = async (t, signal, type) => { const subprocess = execa('empty.js'); const {message} = t.throws(() => { subprocess.kill(signal); }); if (type === 'other') { t.true(message.includes('must be an error instance or a signal name string/integer')); } else { t.true(message.includes('Invalid `subprocess.kill()`\'s argument')); validateMessage(t, message, type); } await subprocess; }; test('Cannot use subprocess.kill("SIGOTHER")', testInvalidSignalArgument, 'SIGOTHER', 'string'); test('Cannot use subprocess.kill("Sigterm")', testInvalidSignalArgument, 'Sigterm', 'rename'); test('Cannot use subprocess.kill("sigterm")', testInvalidSignalArgument, 'sigterm', 'rename'); test('Cannot use subprocess.kill(-1)', testInvalidSignalArgument, -1, 'integer'); test('Cannot use subprocess.kill(200)', testInvalidSignalArgument, 200, 'integer'); test('Cannot use subprocess.kill(1n)', testInvalidSignalArgument, 1n, 'other'); test('Cannot use subprocess.kill(1.5)', testInvalidSignalArgument, 1.5, 'other'); test('Cannot use subprocess.kill(Infinity)', testInvalidSignalArgument, Number.POSITIVE_INFINITY, 'other'); test('Cannot use subprocess.kill(NaN)', testInvalidSignalArgument, Number.NaN, 'other'); test('Cannot use subprocess.kill(false)', testInvalidSignalArgument, false, 'other'); test('Cannot use subprocess.kill(null)', testInvalidSignalArgument, null, 'other'); test('Cannot use subprocess.kill(symbol)', testInvalidSignalArgument, Symbol('test'), 'other'); test('Cannot use subprocess.kill({})', testInvalidSignalArgument, {}, 'other'); ================================================ FILE: test/terminate/timeout.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); test('timeout kills the subprocess if it times out', async t => { const {isTerminated, signal, timedOut, originalMessage, shortMessage, message} = await t.throwsAsync(execa('forever.js', {timeout: 1})); t.true(isTerminated); t.is(signal, 'SIGTERM'); t.true(timedOut); t.is(originalMessage, undefined); t.is(shortMessage, 'Command timed out after 1 milliseconds: forever.js'); t.is(message, shortMessage); }); test('timeout kills the subprocess if it times out, in sync mode', async t => { const {isTerminated, signal, timedOut, originalMessage, shortMessage, message} = await t.throws(() => { execaSync('node', ['forever.js'], {timeout: 1, cwd: FIXTURES_DIRECTORY}); }); t.true(isTerminated); t.is(signal, 'SIGTERM'); t.true(timedOut); t.is(originalMessage, 'spawnSync node ETIMEDOUT'); t.is(shortMessage, `Command timed out after 1 milliseconds: node forever.js\n${originalMessage}`); t.is(message, shortMessage); }); test('timeout does not kill the subprocess if it does not time out', async t => { const {timedOut} = await execa('delay.js', ['500'], {timeout: 1e8}); t.false(timedOut); }); test('timeout uses killSignal', async t => { const {isTerminated, signal, timedOut} = await t.throwsAsync(execa('forever.js', {timeout: 1, killSignal: 'SIGINT'})); t.true(isTerminated); t.is(signal, 'SIGINT'); t.true(timedOut); }); const INVALID_TIMEOUT_REGEXP = /`timeout` option to be a non-negative integer/; const testTimeoutValidation = (t, timeout, execaMethod) => { t.throws(() => { execaMethod('empty.js', {timeout}); }, {message: INVALID_TIMEOUT_REGEXP}); }; test('timeout must not be negative', testTimeoutValidation, -1, execa); test('timeout must be an integer', testTimeoutValidation, false, execa); test('timeout must not be negative - sync', testTimeoutValidation, -1, execaSync); test('timeout must be an integer - sync', testTimeoutValidation, false, execaSync); test('timedOut is false if timeout is undefined', async t => { const {timedOut} = await execa('noop.js'); t.false(timedOut); }); test('timedOut is false if timeout is 0', async t => { const {timedOut} = await execa('noop.js', {timeout: 0}); t.false(timedOut); }); test('timedOut is false if timeout is undefined and exit code is 0 in sync mode', t => { const {timedOut} = execaSync('noop.js'); t.false(timedOut); }); test('timedOut is false if the timeout happened after a different error occurred', async t => { const subprocess = execa('forever.js', {timeout: 1e3}); const cause = new Error('test'); subprocess.emit('error', cause); const error = await t.throwsAsync(subprocess); t.is(error.cause, cause); t.false(error.timedOut); }); ================================================ FILE: test/transform/encoding-final.js ================================================ import {Buffer} from 'node:buffer'; import {exec} from 'node:child_process'; import {promisify} from 'node:util'; import test from 'ava'; import getStream from 'get-stream'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory, FIXTURES_DIRECTORY} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import {foobarString, foobarUint8Array, foobarHex} from '../helpers/input.js'; const pExec = promisify(exec); setFixtureDirectory(); const checkEncoding = async (t, encoding, fdNumber, execaMethod) => { const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`, STRING_TO_ENCODE], {...fullStdio, encoding}); compareValues(t, stdio[fdNumber], encoding); if (execaMethod !== execaSync) { const subprocess = execaMethod('noop-fd.js', [`${fdNumber}`, STRING_TO_ENCODE], {...fullStdio, encoding}); const result = await getStream(subprocess.stdio[fdNumber]); compareValues(t, result, 'utf8'); await subprocess; } if (fdNumber === 3) { return; } const {stdout, stderr} = await pExec(`node noop-fd.js ${fdNumber} ${STRING_TO_ENCODE}`, {encoding, cwd: FIXTURES_DIRECTORY}); compareValues(t, fdNumber === 1 ? stdout : stderr, encoding); }; const compareValues = (t, value, encoding) => { if (encoding === 'buffer') { t.true(ArrayBuffer.isView(value)); t.true(BUFFER_TO_ENCODE.equals(value)); } else { t.is(value, BUFFER_TO_ENCODE.toString(encoding)); } }; // This string gives different outputs with each encoding type const STRING_TO_ENCODE = '\u1000.'; const BUFFER_TO_ENCODE = Buffer.from(STRING_TO_ENCODE); test('can pass encoding "buffer" to stdout', checkEncoding, 'buffer', 1, execa); test('can pass encoding "utf8" to stdout', checkEncoding, 'utf8', 1, execa); test('can pass encoding "utf16le" to stdout', checkEncoding, 'utf16le', 1, execa); test('can pass encoding "latin1" to stdout', checkEncoding, 'latin1', 1, execa); test('can pass encoding "ascii" to stdout', checkEncoding, 'ascii', 1, execa); test('can pass encoding "hex" to stdout', checkEncoding, 'hex', 1, execa); test('can pass encoding "base64" to stdout', checkEncoding, 'base64', 1, execa); test('can pass encoding "base64url" to stdout', checkEncoding, 'base64url', 1, execa); test('can pass encoding "buffer" to stderr', checkEncoding, 'buffer', 2, execa); test('can pass encoding "utf8" to stderr', checkEncoding, 'utf8', 2, execa); test('can pass encoding "utf16le" to stderr', checkEncoding, 'utf16le', 2, execa); test('can pass encoding "latin1" to stderr', checkEncoding, 'latin1', 2, execa); test('can pass encoding "ascii" to stderr', checkEncoding, 'ascii', 2, execa); test('can pass encoding "hex" to stderr', checkEncoding, 'hex', 2, execa); test('can pass encoding "base64" to stderr', checkEncoding, 'base64', 2, execa); test('can pass encoding "base64url" to stderr', checkEncoding, 'base64url', 2, execa); test('can pass encoding "buffer" to stdio[*]', checkEncoding, 'buffer', 3, execa); test('can pass encoding "utf8" to stdio[*]', checkEncoding, 'utf8', 3, execa); test('can pass encoding "utf16le" to stdio[*]', checkEncoding, 'utf16le', 3, execa); test('can pass encoding "latin1" to stdio[*]', checkEncoding, 'latin1', 3, execa); test('can pass encoding "ascii" to stdio[*]', checkEncoding, 'ascii', 3, execa); test('can pass encoding "hex" to stdio[*]', checkEncoding, 'hex', 3, execa); test('can pass encoding "base64" to stdio[*]', checkEncoding, 'base64', 3, execa); test('can pass encoding "base64url" to stdio[*]', checkEncoding, 'base64url', 3, execa); test('can pass encoding "buffer" to stdout - sync', checkEncoding, 'buffer', 1, execaSync); test('can pass encoding "utf8" to stdout - sync', checkEncoding, 'utf8', 1, execaSync); test('can pass encoding "utf16le" to stdout - sync', checkEncoding, 'utf16le', 1, execaSync); test('can pass encoding "latin1" to stdout - sync', checkEncoding, 'latin1', 1, execaSync); test('can pass encoding "ascii" to stdout - sync', checkEncoding, 'ascii', 1, execaSync); test('can pass encoding "hex" to stdout - sync', checkEncoding, 'hex', 1, execaSync); test('can pass encoding "base64" to stdout - sync', checkEncoding, 'base64', 1, execaSync); test('can pass encoding "base64url" to stdout - sync', checkEncoding, 'base64url', 1, execaSync); test('can pass encoding "buffer" to stderr - sync', checkEncoding, 'buffer', 2, execaSync); test('can pass encoding "utf8" to stderr - sync', checkEncoding, 'utf8', 2, execaSync); test('can pass encoding "utf16le" to stderr - sync', checkEncoding, 'utf16le', 2, execaSync); test('can pass encoding "latin1" to stderr - sync', checkEncoding, 'latin1', 2, execaSync); test('can pass encoding "ascii" to stderr - sync', checkEncoding, 'ascii', 2, execaSync); test('can pass encoding "hex" to stderr - sync', checkEncoding, 'hex', 2, execaSync); test('can pass encoding "base64" to stderr - sync', checkEncoding, 'base64', 2, execaSync); test('can pass encoding "base64url" to stderr - sync', checkEncoding, 'base64url', 2, execaSync); test('can pass encoding "buffer" to stdio[*] - sync', checkEncoding, 'buffer', 3, execaSync); test('can pass encoding "utf8" to stdio[*] - sync', checkEncoding, 'utf8', 3, execaSync); test('can pass encoding "utf16le" to stdio[*] - sync', checkEncoding, 'utf16le', 3, execaSync); test('can pass encoding "latin1" to stdio[*] - sync', checkEncoding, 'latin1', 3, execaSync); test('can pass encoding "ascii" to stdio[*] - sync', checkEncoding, 'ascii', 3, execaSync); test('can pass encoding "hex" to stdio[*] - sync', checkEncoding, 'hex', 3, execaSync); test('can pass encoding "base64" to stdio[*] - sync', checkEncoding, 'base64', 3, execaSync); test('can pass encoding "base64url" to stdio[*] - sync', checkEncoding, 'base64url', 3, execaSync); // eslint-disable-next-line max-params const testEncodingInput = async (t, input, expectedStdout, encoding, execaMethod) => { const {stdout} = await execaMethod('stdin.js', {input, encoding}); t.deepEqual(stdout, expectedStdout); }; test('Can use string input', testEncodingInput, foobarString, foobarString, 'utf8', execa); test('Can use Uint8Array input', testEncodingInput, foobarUint8Array, foobarString, 'utf8', execa); test('Can use string input, encoding "buffer"', testEncodingInput, foobarString, foobarUint8Array, 'buffer', execa); test('Can use Uint8Array input, encoding "buffer"', testEncodingInput, foobarUint8Array, foobarUint8Array, 'buffer', execa); test('Can use string input, encoding "hex"', testEncodingInput, foobarString, foobarHex, 'hex', execa); test('Can use Uint8Array input, encoding "hex"', testEncodingInput, foobarUint8Array, foobarHex, 'hex', execa); test('Can use string input, sync', testEncodingInput, foobarString, foobarString, 'utf8', execaSync); test('Can use Uint8Array input, sync', testEncodingInput, foobarUint8Array, foobarString, 'utf8', execaSync); test('Can use string input, encoding "buffer", sync', testEncodingInput, foobarString, foobarUint8Array, 'buffer', execaSync); test('Can use Uint8Array input, encoding "buffer", sync', testEncodingInput, foobarUint8Array, foobarUint8Array, 'buffer', execaSync); test('Can use string input, encoding "hex", sync', testEncodingInput, foobarString, foobarHex, 'hex', execaSync); test('Can use Uint8Array input, encoding "hex", sync', testEncodingInput, foobarUint8Array, foobarHex, 'hex', execaSync); const testSubprocessEncoding = (t, encoding) => { const subprocess = execa('empty.js', {...fullStdio, encoding}); t.is(subprocess.stdout.readableEncoding, null); t.is(subprocess.stderr.readableEncoding, null); t.is(subprocess.stdio[3].readableEncoding, null); }; test('Does not modify subprocess.std* encoding, "utf8"', testSubprocessEncoding, 'utf8'); test('Does not modify subprocess.std* encoding, "utf16le"', testSubprocessEncoding, 'utf16le'); test('Does not modify subprocess.std* encoding, "buffer"', testSubprocessEncoding, 'buffer'); test('Does not modify subprocess.std* encoding, "hex"', testSubprocessEncoding, 'hex'); test('Does not modify subprocess.std* encoding, "base64"', testSubprocessEncoding, 'base64'); test('Does not modify subprocess.std* encoding, "base64url"', testSubprocessEncoding, 'base64url'); test('Does not modify subprocess.std* encoding, "latin1"', testSubprocessEncoding, 'latin1'); test('Does not modify subprocess.std* encoding, "ascii"', testSubprocessEncoding, 'ascii'); ================================================ FILE: test/transform/encoding-ignored.js ================================================ import process from 'node:process'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {outputObjectGenerator, addNoopGenerator} from '../helpers/generator.js'; import {foobarObject} from '../helpers/input.js'; setFixtureDirectory(); const testObjectMode = async (t, addNoopTransform, execaMethod) => { const {stdout} = await execaMethod('noop.js', { stdout: addNoopGenerator(outputObjectGenerator(), addNoopTransform, true), encoding: 'base64', }); t.deepEqual(stdout, [foobarObject]); }; test('Other encodings work with transforms that return objects', testObjectMode, false, execa); test('Other encodings work with transforms that return objects, noop transform', testObjectMode, true, execa); test('Other encodings work with transforms that return objects, sync', testObjectMode, false, execaSync); test('Other encodings work with transforms that return objects, noop transform, sync', testObjectMode, true, execaSync); // eslint-disable-next-line max-params const testIgnoredEncoding = async (t, stdoutOption, isUndefined, options, execaMethod) => { const {stdout} = await execaMethod('empty.js', {stdout: stdoutOption, ...options}); t.is(stdout === undefined, isUndefined); }; const base64Options = {encoding: 'base64'}; const linesOptions = {lines: true}; test('Is ignored with other encodings and "ignore"', testIgnoredEncoding, 'ignore', true, base64Options, execa); test('Is ignored with other encodings and ["ignore"]', testIgnoredEncoding, ['ignore'], true, base64Options, execa); test('Is ignored with other encodings and "ipc"', testIgnoredEncoding, 'ipc', true, base64Options, execa); test('Is ignored with other encodings and ["ipc"]', testIgnoredEncoding, ['ipc'], true, base64Options, execa); test('Is ignored with other encodings and "inherit"', testIgnoredEncoding, 'inherit', true, base64Options, execa); test('Is ignored with other encodings and ["inherit"]', testIgnoredEncoding, ['inherit'], true, base64Options, execa); test('Is ignored with other encodings and 1', testIgnoredEncoding, 1, true, base64Options, execa); test('Is ignored with other encodings and [1]', testIgnoredEncoding, [1], true, base64Options, execa); test('Is ignored with other encodings and process.stdout', testIgnoredEncoding, process.stdout, true, base64Options, execa); test('Is ignored with other encodings and [process.stdout]', testIgnoredEncoding, [process.stdout], true, base64Options, execa); test('Is not ignored with other encodings and "pipe"', testIgnoredEncoding, 'pipe', false, base64Options, execa); test('Is not ignored with other encodings and ["pipe"]', testIgnoredEncoding, ['pipe'], false, base64Options, execa); test('Is not ignored with other encodings and "overlapped"', testIgnoredEncoding, 'overlapped', false, base64Options, execa); test('Is not ignored with other encodings and ["overlapped"]', testIgnoredEncoding, ['overlapped'], false, base64Options, execa); test('Is not ignored with other encodings and ["inherit", "pipe"]', testIgnoredEncoding, ['inherit', 'pipe'], false, base64Options, execa); test('Is not ignored with other encodings and undefined', testIgnoredEncoding, undefined, false, base64Options, execa); test('Is not ignored with other encodings and null', testIgnoredEncoding, null, false, base64Options, execa); test('Is ignored with "lines: true" and "ignore"', testIgnoredEncoding, 'ignore', true, linesOptions, execa); test('Is ignored with "lines: true" and ["ignore"]', testIgnoredEncoding, ['ignore'], true, linesOptions, execa); test('Is ignored with "lines: true" and "ipc"', testIgnoredEncoding, 'ipc', true, linesOptions, execa); test('Is ignored with "lines: true" and ["ipc"]', testIgnoredEncoding, ['ipc'], true, linesOptions, execa); test('Is ignored with "lines: true" and "inherit"', testIgnoredEncoding, 'inherit', true, linesOptions, execa); test('Is ignored with "lines: true" and ["inherit"]', testIgnoredEncoding, ['inherit'], true, linesOptions, execa); test('Is ignored with "lines: true" and 1', testIgnoredEncoding, 1, true, linesOptions, execa); test('Is ignored with "lines: true" and [1]', testIgnoredEncoding, [1], true, linesOptions, execa); test('Is ignored with "lines: true" and process.stdout', testIgnoredEncoding, process.stdout, true, linesOptions, execa); test('Is ignored with "lines: true" and [process.stdout]', testIgnoredEncoding, [process.stdout], true, linesOptions, execa); test('Is not ignored with "lines: true" and "pipe"', testIgnoredEncoding, 'pipe', false, linesOptions, execa); test('Is not ignored with "lines: true" and ["pipe"]', testIgnoredEncoding, ['pipe'], false, linesOptions, execa); test('Is not ignored with "lines: true" and "overlapped"', testIgnoredEncoding, 'overlapped', false, linesOptions, execa); test('Is not ignored with "lines: true" and ["overlapped"]', testIgnoredEncoding, ['overlapped'], false, linesOptions, execa); test('Is not ignored with "lines: true" and ["inherit", "pipe"]', testIgnoredEncoding, ['inherit', 'pipe'], false, linesOptions, execa); test('Is not ignored with "lines: true" and undefined', testIgnoredEncoding, undefined, false, linesOptions, execa); test('Is not ignored with "lines: true" and null', testIgnoredEncoding, null, false, linesOptions, execa); test('Is ignored with "lines: true", other encodings and "ignore"', testIgnoredEncoding, 'ignore', true, {...base64Options, ...linesOptions}, execa); test('Is not ignored with "lines: true", other encodings and "pipe"', testIgnoredEncoding, 'pipe', false, {...base64Options, ...linesOptions}, execa); test('Is ignored with other encodings and "ignore", sync', testIgnoredEncoding, 'ignore', true, base64Options, execaSync); test('Is ignored with other encodings and ["ignore"], sync', testIgnoredEncoding, ['ignore'], true, base64Options, execaSync); test('Is ignored with other encodings and "inherit", sync', testIgnoredEncoding, 'inherit', true, base64Options, execaSync); test('Is ignored with other encodings and ["inherit"], sync', testIgnoredEncoding, ['inherit'], true, base64Options, execaSync); test('Is ignored with other encodings and 1, sync', testIgnoredEncoding, 1, true, base64Options, execaSync); test('Is ignored with other encodings and [1], sync', testIgnoredEncoding, [1], true, base64Options, execaSync); test('Is ignored with other encodings and process.stdout, sync', testIgnoredEncoding, process.stdout, true, base64Options, execaSync); test('Is ignored with other encodings and [process.stdout], sync', testIgnoredEncoding, [process.stdout], true, base64Options, execaSync); test('Is not ignored with other encodings and "pipe", sync', testIgnoredEncoding, 'pipe', false, base64Options, execaSync); test('Is not ignored with other encodings and ["pipe"], sync', testIgnoredEncoding, ['pipe'], false, base64Options, execaSync); test('Is not ignored with other encodings and undefined, sync', testIgnoredEncoding, undefined, false, base64Options, execaSync); test('Is not ignored with other encodings and null, sync', testIgnoredEncoding, null, false, base64Options, execaSync); test('Is ignored with "lines: true" and "ignore", sync', testIgnoredEncoding, 'ignore', true, linesOptions, execaSync); test('Is ignored with "lines: true" and ["ignore"], sync', testIgnoredEncoding, ['ignore'], true, linesOptions, execaSync); test('Is ignored with "lines: true" and "inherit", sync', testIgnoredEncoding, 'inherit', true, linesOptions, execaSync); test('Is ignored with "lines: true" and ["inherit"], sync', testIgnoredEncoding, ['inherit'], true, linesOptions, execaSync); test('Is ignored with "lines: true" and 1, sync', testIgnoredEncoding, 1, true, linesOptions, execaSync); test('Is ignored with "lines: true" and [1], sync', testIgnoredEncoding, [1], true, linesOptions, execaSync); test('Is ignored with "lines: true" and process.stdout, sync', testIgnoredEncoding, process.stdout, true, linesOptions, execaSync); test('Is ignored with "lines: true" and [process.stdout], sync', testIgnoredEncoding, [process.stdout], true, linesOptions, execaSync); test('Is not ignored with "lines: true" and "pipe", sync', testIgnoredEncoding, 'pipe', false, linesOptions, execaSync); test('Is not ignored with "lines: true" and ["pipe"], sync', testIgnoredEncoding, ['pipe'], false, linesOptions, execaSync); test('Is not ignored with "lines: true" and undefined, sync', testIgnoredEncoding, undefined, false, linesOptions, execaSync); test('Is not ignored with "lines: true" and null, sync', testIgnoredEncoding, null, false, linesOptions, execaSync); test('Is ignored with "lines: true", other encodings and "ignore", sync', testIgnoredEncoding, 'ignore', true, {...base64Options, ...linesOptions}, execaSync); test('Is not ignored with "lines: true", other encodings and "pipe", sync', testIgnoredEncoding, 'pipe', false, {...base64Options, ...linesOptions}, execaSync); ================================================ FILE: test/transform/encoding-multibyte.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {noopGenerator, getOutputsGenerator, addNoopGenerator} from '../helpers/generator.js'; import { multibyteChar, multibyteString, multibyteUint8Array, breakingLength, brokenSymbol, } from '../helpers/encoding.js'; setFixtureDirectory(); const foobarArray = ['fo', 'ob', 'ar', '..']; const testMultibyteCharacters = async (t, objectMode, addNoopTransform, execaMethod) => { const {stdout} = await execaMethod('noop.js', { stdout: addNoopGenerator(getOutputsGenerator(foobarArray)(objectMode, true), addNoopTransform, objectMode), encoding: 'base64', }); if (objectMode) { t.deepEqual(stdout, foobarArray); } else { t.is(stdout, Buffer.from(foobarArray.join('')).toString('base64')); } }; test('Handle multibyte characters', testMultibyteCharacters, false, false, execa); test('Handle multibyte characters, noop transform', testMultibyteCharacters, false, true, execa); test('Handle multibyte characters, with objectMode', testMultibyteCharacters, true, false, execa); test('Handle multibyte characters, with objectMode, noop transform', testMultibyteCharacters, true, true, execa); test('Handle multibyte characters, sync', testMultibyteCharacters, false, false, execaSync); test('Handle multibyte characters, noop transform, sync', testMultibyteCharacters, false, true, execaSync); test('Handle multibyte characters, with objectMode, sync', testMultibyteCharacters, true, false, execaSync); test('Handle multibyte characters, with objectMode, noop transform, sync', testMultibyteCharacters, true, true, execaSync); const testMultibyte = async (t, objectMode, execaMethod) => { const {stdout} = await execaMethod('stdin.js', { stdin: [ [multibyteUint8Array.slice(0, breakingLength), multibyteUint8Array.slice(breakingLength)], noopGenerator(objectMode, true), ], }); t.is(stdout, multibyteString); }; test('Generator handles multibyte characters with Uint8Array', testMultibyte, false, execa); test('Generator handles multibyte characters with Uint8Array, objectMode', testMultibyte, true, execa); test('Generator handles multibyte characters with Uint8Array, sync', testMultibyte, false, execaSync); test('Generator handles multibyte characters with Uint8Array, objectMode, sync', testMultibyte, true, execaSync); const testMultibytePartial = async (t, objectMode, execaMethod) => { const {stdout} = await execaMethod('stdin.js', { stdin: [ [multibyteUint8Array.slice(0, breakingLength)], noopGenerator(objectMode, true), ], }); t.is(stdout, `${multibyteChar}${brokenSymbol}`); }; test('Generator handles partial multibyte characters with Uint8Array', testMultibytePartial, false, execa); test('Generator handles partial multibyte characters with Uint8Array, objectMode', testMultibytePartial, true, execa); test('Generator handles partial multibyte characters with Uint8Array, sync', testMultibytePartial, false, execaSync); test('Generator handles partial multibyte characters with Uint8Array, objectMode, sync', testMultibytePartial, true, execaSync); const testMultibytePartialOutput = async (t, execaMethod) => { const {stdout} = await execaMethod('noop.js', { stdout: getOutputsGenerator([ multibyteUint8Array.slice(0, breakingLength), multibyteUint8Array.slice(breakingLength), ])(false, true), }); t.is(stdout, multibyteString); }; test('Generator handles output multibyte characters with Uint8Array', testMultibytePartialOutput, execa); test('Generator handles output multibyte characters with Uint8Array, sync', testMultibytePartialOutput, execaSync); ================================================ FILE: test/transform/encoding-transform.js ================================================ import {Buffer} from 'node:buffer'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import { foobarString, foobarUint8Array, foobarBuffer, foobarObject, } from '../helpers/input.js'; import {noopGenerator, getOutputGenerator} from '../helpers/generator.js'; setFixtureDirectory(); const getTypeofGenerator = lines => (objectMode, binary) => ({ * transform(line) { lines.push(Object.prototype.toString.call(line)); yield ''; }, objectMode, binary, }); const assertTypeofChunk = (t, lines, expectedType) => { t.deepEqual(lines, [`[object ${expectedType}]`]); }; // eslint-disable-next-line max-params const testGeneratorFirstEncoding = async (t, input, encoding, expectedType, objectMode, binary) => { const lines = []; const subprocess = execa('stdin.js', {stdin: getTypeofGenerator(lines)(objectMode, binary), encoding}); subprocess.stdin.end(input); await subprocess; assertTypeofChunk(t, lines, expectedType); }; test('First generator argument is string with default encoding, with string writes', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', false, undefined); test('First generator argument is string with default encoding, with Buffer writes', testGeneratorFirstEncoding, foobarBuffer, 'utf8', 'String', false, undefined); test('First generator argument is string with default encoding, with Uint8Array writes', testGeneratorFirstEncoding, foobarUint8Array, 'utf8', 'String', false, undefined); test('First generator argument is string with default encoding, with string writes, "binary: false"', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', false, false); test('First generator argument is Uint8Array with default encoding, with string writes, "binary: true"', testGeneratorFirstEncoding, foobarString, 'utf8', 'Uint8Array', false, true); test('First generator argument is string with encoding "utf16le", with string writes', testGeneratorFirstEncoding, foobarString, 'utf16le', 'String', false, undefined); test('First generator argument is string with encoding "utf16le", with Buffer writes', testGeneratorFirstEncoding, foobarBuffer, 'utf16le', 'String', false, undefined); test('First generator argument is string with encoding "utf16le", with Uint8Array writes', testGeneratorFirstEncoding, foobarUint8Array, 'utf16le', 'String', false, undefined); test('First generator argument is string with encoding "utf16le", with string writes, "binary: false"', testGeneratorFirstEncoding, foobarString, 'utf16le', 'String', false, false); test('First generator argument is Uint8Array with encoding "utf16le", with string writes, "binary: true"', testGeneratorFirstEncoding, foobarString, 'utf16le', 'Uint8Array', false, true); test('First generator argument is Uint8Array with encoding "buffer", with string writes', testGeneratorFirstEncoding, foobarString, 'buffer', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "buffer", with Buffer writes', testGeneratorFirstEncoding, foobarBuffer, 'buffer', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "buffer", with Uint8Array writes', testGeneratorFirstEncoding, foobarUint8Array, 'buffer', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "buffer", with string writes, "binary: false"', testGeneratorFirstEncoding, foobarString, 'buffer', 'Uint8Array', false, false); test('First generator argument is Uint8Array with encoding "buffer", with string writes, "binary: true"', testGeneratorFirstEncoding, foobarString, 'buffer', 'Uint8Array', false, true); test('First generator argument is Uint8Array with encoding "hex", with string writes', testGeneratorFirstEncoding, foobarString, 'hex', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "hex", with Buffer writes', testGeneratorFirstEncoding, foobarBuffer, 'hex', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "hex", with Uint8Array writes', testGeneratorFirstEncoding, foobarUint8Array, 'hex', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "hex", with string writes, "binary: false"', testGeneratorFirstEncoding, foobarString, 'hex', 'Uint8Array', false, false); test('First generator argument is Uint8Array with encoding "hex", with string writes, "binary: true"', testGeneratorFirstEncoding, foobarString, 'hex', 'Uint8Array', false, true); test('First generator argument can be string with objectMode', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', true, undefined); test('First generator argument can be string with objectMode, "binary: false"', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', true, false); test('First generator argument can be string with objectMode, "binary: true"', testGeneratorFirstEncoding, foobarString, 'utf8', 'String', true, true); test('First generator argument can be objects with objectMode', testGeneratorFirstEncoding, foobarObject, 'utf8', 'Object', true, undefined); test('First generator argument can be objects with objectMode, "binary: false"', testGeneratorFirstEncoding, foobarObject, 'utf8', 'Object', true, false); test('First generator argument can be objects with objectMode, "binary: true"', testGeneratorFirstEncoding, foobarObject, 'utf8', 'Object', true, true); // eslint-disable-next-line max-params const testGeneratorFirstEncodingSync = (t, input, encoding, expectedType, objectMode, binary) => { const lines = []; execaSync('stdin.js', {stdin: [[input], getTypeofGenerator(lines)(objectMode, binary)], encoding}); assertTypeofChunk(t, lines, expectedType); }; test('First generator argument is string with default encoding, with string writes, sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', false, undefined); test('First generator argument is string with default encoding, with Uint8Array writes, sync', testGeneratorFirstEncodingSync, foobarUint8Array, 'utf8', 'String', false, undefined); test('First generator argument is string with default encoding, with string writes, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', false, false); test('First generator argument is Uint8Array with default encoding, with string writes, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'Uint8Array', false, true); test('First generator argument is string with encoding "utf16le", with string writes, sync', testGeneratorFirstEncodingSync, foobarString, 'utf16le', 'String', false, undefined); test('First generator argument is string with encoding "utf16le", with Uint8Array writes, sync', testGeneratorFirstEncodingSync, foobarUint8Array, 'utf16le', 'String', false, undefined); test('First generator argument is string with encoding "utf16le", with string writes, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'utf16le', 'String', false, false); test('First generator argument is Uint8Array with encoding "utf16le", with string writes, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'utf16le', 'Uint8Array', false, true); test('First generator argument is Uint8Array with encoding "buffer", with string writes, sync', testGeneratorFirstEncodingSync, foobarString, 'buffer', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, sync', testGeneratorFirstEncodingSync, foobarUint8Array, 'buffer', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "buffer", with string writes, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'buffer', 'Uint8Array', false, false); test('First generator argument is Uint8Array with encoding "buffer", with string writes, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'buffer', 'Uint8Array', false, true); test('First generator argument is Uint8Array with encoding "hex", with string writes, sync', testGeneratorFirstEncodingSync, foobarString, 'hex', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "hex", with Uint8Array writes, sync', testGeneratorFirstEncodingSync, foobarUint8Array, 'hex', 'Uint8Array', false, undefined); test('First generator argument is Uint8Array with encoding "hex", with string writes, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'hex', 'Uint8Array', false, false); test('First generator argument is Uint8Array with encoding "hex", with string writes, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'hex', 'Uint8Array', false, true); test('First generator argument can be string with objectMode, sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', true, undefined); test('First generator argument can be string with objectMode, "binary: false", sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', true, false); test('First generator argument can be string with objectMode, "binary: true", sync', testGeneratorFirstEncodingSync, foobarString, 'utf8', 'String', true, true); test('First generator argument can be objects with objectMode, sync', testGeneratorFirstEncodingSync, foobarObject, 'utf8', 'Object', true, undefined); test('First generator argument can be objects with objectMode, "binary: false", sync', testGeneratorFirstEncodingSync, foobarObject, 'utf8', 'Object', true, false); test('First generator argument can be objects with objectMode, "binary: true", sync', testGeneratorFirstEncodingSync, foobarObject, 'utf8', 'Object', true, true); const testEncodingIgnored = async (t, encoding) => { const input = Buffer.from(foobarString).toString(encoding); const subprocess = execa('stdin.js', {stdin: noopGenerator(true)}); subprocess.stdin.end(input, encoding); const {stdout} = await subprocess; t.is(stdout, input); }; test('Write call encoding "utf8" is ignored with objectMode', testEncodingIgnored, 'utf8'); test('Write call encoding "utf16le" is ignored with objectMode', testEncodingIgnored, 'utf16le'); test('Write call encoding "hex" is ignored with objectMode', testEncodingIgnored, 'hex'); test('Write call encoding "base64" is ignored with objectMode', testEncodingIgnored, 'base64'); // eslint-disable-next-line max-params const testGeneratorNextEncoding = async (t, input, encoding, firstObjectMode, secondObjectMode, expectedType, execaMethod) => { const lines = []; await execaMethod('noop.js', ['other'], { stdout: [ getOutputGenerator(input)(firstObjectMode), getTypeofGenerator(lines)(secondObjectMode), ], encoding, }); assertTypeofChunk(t, lines, expectedType); }; test('Next generator argument is string with default encoding, with string writes', testGeneratorNextEncoding, foobarString, 'utf8', false, false, 'String', execa); test('Next generator argument is string with default encoding, with string writes, objectMode first', testGeneratorNextEncoding, foobarString, 'utf8', true, false, 'String', execa); test('Next generator argument is string with default encoding, with string writes, objectMode both', testGeneratorNextEncoding, foobarString, 'utf8', true, true, 'String', execa); test('Next generator argument is string with default encoding, with Uint8Array writes', testGeneratorNextEncoding, foobarUint8Array, 'utf8', false, false, 'String', execa); test('Next generator argument is Uint8Array with default encoding, with Uint8Array writes, objectMode first', testGeneratorNextEncoding, foobarUint8Array, 'utf8', true, false, 'Uint8Array', execa); test('Next generator argument is string with default encoding, with Uint8Array writes, objectMode both', testGeneratorNextEncoding, foobarUint8Array, 'utf8', true, true, 'Uint8Array', execa); test('Next generator argument is string with encoding "utf16le", with string writes', testGeneratorNextEncoding, foobarString, 'utf16le', false, false, 'String', execa); test('Next generator argument is string with encoding "utf16le",, with string writes, objectMode first', testGeneratorNextEncoding, foobarString, 'utf16le', true, false, 'String', execa); test('Next generator argument is string with encoding "utf16le",, with string writes, objectMode both', testGeneratorNextEncoding, foobarString, 'utf16le', true, true, 'String', execa); test('Next generator argument is string with encoding "utf16le",, with Uint8Array writes', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', false, false, 'String', execa); test('Next generator argument is Uint8Array with encoding "utf16le",, with Uint8Array writes, objectMode first', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', true, false, 'Uint8Array', execa); test('Next generator argument is string with encoding "utf16le",, with Uint8Array writes, objectMode both', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', true, true, 'Uint8Array', execa); test('Next generator argument is Uint8Array with encoding "buffer", with string writes', testGeneratorNextEncoding, foobarString, 'buffer', false, false, 'Uint8Array', execa); test('Next generator argument is string with encoding "buffer", with string writes, objectMode first', testGeneratorNextEncoding, foobarString, 'buffer', true, false, 'String', execa); test('Next generator argument is string with encoding "buffer", with string writes, objectMode both', testGeneratorNextEncoding, foobarString, 'buffer', true, true, 'String', execa); test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes', testGeneratorNextEncoding, foobarUint8Array, 'buffer', false, false, 'Uint8Array', execa); test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, objectMode first', testGeneratorNextEncoding, foobarUint8Array, 'buffer', true, false, 'Uint8Array', execa); test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, objectMode both', testGeneratorNextEncoding, foobarUint8Array, 'buffer', true, true, 'Uint8Array', execa); test('Next generator argument is Uint8Array with encoding "hex", with string writes', testGeneratorNextEncoding, foobarString, 'hex', false, false, 'Uint8Array', execa); test('Next generator argument is Uint8Array with encoding "hex", with Uint8Array writes', testGeneratorNextEncoding, foobarUint8Array, 'hex', false, false, 'Uint8Array', execa); test('Next generator argument is object with default encoding, with object writes, objectMode first', testGeneratorNextEncoding, foobarObject, 'utf8', true, false, 'Object', execa); test('Next generator argument is object with default encoding, with object writes, objectMode both', testGeneratorNextEncoding, foobarObject, 'utf8', true, true, 'Object', execa); test('Next generator argument is string with default encoding, with string writes, sync', testGeneratorNextEncoding, foobarString, 'utf8', false, false, 'String', execaSync); test('Next generator argument is string with default encoding, with string writes, objectMode first, sync', testGeneratorNextEncoding, foobarString, 'utf8', true, false, 'String', execaSync); test('Next generator argument is string with default encoding, with string writes, objectMode both, sync', testGeneratorNextEncoding, foobarString, 'utf8', true, true, 'String', execaSync); test('Next generator argument is string with default encoding, with Uint8Array writes, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf8', false, false, 'String', execaSync); test('Next generator argument is Uint8Array with default encoding, with Uint8Array writes, objectMode first, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf8', true, false, 'Uint8Array', execaSync); test('Next generator argument is string with default encoding, with Uint8Array writes, objectMode both, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf8', true, true, 'Uint8Array', execaSync); test('Next generator argument is string with encoding "utf16le", with string writes, sync', testGeneratorNextEncoding, foobarString, 'utf16le', false, false, 'String', execaSync); test('Next generator argument is string with encoding "utf16le",, with string writes, objectMode first, sync', testGeneratorNextEncoding, foobarString, 'utf16le', true, false, 'String', execaSync); test('Next generator argument is string with encoding "utf16le",, with string writes, objectMode both, sync', testGeneratorNextEncoding, foobarString, 'utf16le', true, true, 'String', execaSync); test('Next generator argument is string with encoding "utf16le",, with Uint8Array writes, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', false, false, 'String', execaSync); test('Next generator argument is Uint8Array with encoding "utf16le",, with Uint8Array writes, objectMode first, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', true, false, 'Uint8Array', execaSync); test('Next generator argument is string with encoding "utf16le",, with Uint8Array writes, objectMode both, sync', testGeneratorNextEncoding, foobarUint8Array, 'utf16le', true, true, 'Uint8Array', execaSync); test('Next generator argument is Uint8Array with encoding "buffer", with string writes, sync', testGeneratorNextEncoding, foobarString, 'buffer', false, false, 'Uint8Array', execaSync); test('Next generator argument is string with encoding "buffer", with string writes, objectMode first, sync', testGeneratorNextEncoding, foobarString, 'buffer', true, false, 'String', execaSync); test('Next generator argument is string with encoding "buffer", with string writes, objectMode both, sync', testGeneratorNextEncoding, foobarString, 'buffer', true, true, 'String', execaSync); test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, sync', testGeneratorNextEncoding, foobarUint8Array, 'buffer', false, false, 'Uint8Array', execaSync); test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, objectMode first, sync', testGeneratorNextEncoding, foobarUint8Array, 'buffer', true, false, 'Uint8Array', execaSync); test('Next generator argument is Uint8Array with encoding "buffer", with Uint8Array writes, objectMode both, sync', testGeneratorNextEncoding, foobarUint8Array, 'buffer', true, true, 'Uint8Array', execaSync); test('Next generator argument is Uint8Array with encoding "hex", with string writes, sync', testGeneratorNextEncoding, foobarString, 'hex', false, false, 'Uint8Array', execaSync); test('Next generator argument is Uint8Array with encoding "hex", with Uint8Array writes, sync', testGeneratorNextEncoding, foobarUint8Array, 'hex', false, false, 'Uint8Array', execaSync); test('Next generator argument is object with default encoding, with object writes, objectMode first, sync', testGeneratorNextEncoding, foobarObject, 'utf8', true, false, 'Object', execaSync); test('Next generator argument is object with default encoding, with object writes, objectMode both, sync', testGeneratorNextEncoding, foobarObject, 'utf8', true, true, 'Object', execaSync); const testFirstOutputGeneratorArgument = async (t, fdNumber, execaMethod) => { const lines = []; await execaMethod('noop-fd.js', [`${fdNumber}`], getStdio(fdNumber, getTypeofGenerator(lines)(true))); assertTypeofChunk(t, lines, 'String'); }; test('The first generator with result.stdout does not receive an object argument even in objectMode', testFirstOutputGeneratorArgument, 1, execa); test('The first generator with result.stderr does not receive an object argument even in objectMode', testFirstOutputGeneratorArgument, 2, execa); test('The first generator with result.stdio[*] does not receive an object argument even in objectMode', testFirstOutputGeneratorArgument, 3, execa); test('The first generator with result.stdout does not receive an object argument even in objectMode, sync', testFirstOutputGeneratorArgument, 1, execaSync); test('The first generator with result.stderr does not receive an object argument even in objectMode, sync', testFirstOutputGeneratorArgument, 2, execaSync); test('The first generator with result.stdio[*] does not receive an object argument even in objectMode, sync', testFirstOutputGeneratorArgument, 3, execaSync); ================================================ FILE: test/transform/generator-all.js ================================================ import {Buffer} from 'node:buffer'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarObject} from '../helpers/input.js'; import { outputObjectGenerator, uppercaseGenerator, uppercaseBufferGenerator, } from '../helpers/generator.js'; setFixtureDirectory(); const textEncoder = new TextEncoder(); const getAllStdioOption = (stdioOption, encoding, objectMode) => { if (stdioOption) { return 'pipe'; } if (objectMode) { return outputObjectGenerator(); } return encoding === 'utf8' ? uppercaseGenerator() : uppercaseBufferGenerator(); }; const getStdoutStderrOutput = ({output, stdioOption, encoding, objectMode, lines}) => { if (objectMode && !stdioOption) { return encoding === 'utf8' ? [foobarObject, foobarObject] : [foobarObject]; } const stdioOutput = stdioOption ? output : output.toUpperCase(); if (encoding === 'hex') { return Buffer.from(stdioOutput).toString('hex'); } if (encoding === 'buffer') { return textEncoder.encode(stdioOutput); } return lines ? stdioOutput.trim().split('\n').map(string => `${string}\n`) : stdioOutput; }; const getAllOutput = ({stdoutOutput, stderrOutput, encoding, objectMode, lines}) => { if (objectMode || (lines && encoding === 'utf8')) { return [stdoutOutput, stderrOutput].flat(); } return encoding === 'buffer' ? new Uint8Array([...stdoutOutput, ...stderrOutput]) : `${stdoutOutput}${stderrOutput}`; }; // eslint-disable-next-line max-params const testGeneratorAll = async (t, reject, encoding, objectMode, stdoutOption, stderrOption, lines, execaMethod) => { const fixtureName = reject ? 'all.js' : 'all-fail.js'; const {stdout, stderr, all} = await execaMethod(fixtureName, { all: true, reject, stdout: getAllStdioOption(stdoutOption, encoding, objectMode), stderr: getAllStdioOption(stderrOption, encoding, objectMode), encoding, lines, stripFinalNewline: false, }); const stdoutOutput = getStdoutStderrOutput({ output: 'std\nout\n', stdioOption: stdoutOption, encoding, objectMode, lines, }); t.deepEqual(stdout, stdoutOutput); const stderrOutput = getStdoutStderrOutput({ output: 'std\nerr\n', stdioOption: stderrOption, encoding, objectMode, lines, }); t.deepEqual(stderr, stderrOutput); const allOutput = getAllOutput({ stdoutOutput, stderrOutput, encoding, objectMode, lines, }); if (Array.isArray(all) && Array.isArray(allOutput)) { t.deepEqual([...all].sort(), [...allOutput].sort()); } else { t.deepEqual(all, allOutput); } }; test('Can use generators with result.all = transform + transform', testGeneratorAll, true, 'utf8', false, false, false, false, execa); test('Can use generators with error.all = transform + transform', testGeneratorAll, false, 'utf8', false, false, false, false, execa); test('Can use generators with result.all = transform + transform, encoding "buffer"', testGeneratorAll, true, 'buffer', false, false, false, false, execa); test('Can use generators with error.all = transform + transform, encoding "buffer"', testGeneratorAll, false, 'buffer', false, false, false, false, execa); test('Can use generators with result.all = transform + transform, encoding "hex"', testGeneratorAll, true, 'hex', false, false, false, false, execa); test('Can use generators with error.all = transform + transform, encoding "hex"', testGeneratorAll, false, 'hex', false, false, false, false, execa); test('Can use generators with result.all = transform + pipe', testGeneratorAll, true, 'utf8', false, false, true, false, execa); test('Can use generators with error.all = transform + pipe', testGeneratorAll, false, 'utf8', false, false, true, false, execa); test('Can use generators with result.all = transform + pipe, encoding "buffer"', testGeneratorAll, true, 'buffer', false, false, true, false, execa); test('Can use generators with error.all = transform + pipe, encoding "buffer"', testGeneratorAll, false, 'buffer', false, false, true, false, execa); test('Can use generators with result.all = transform + pipe, encoding "hex"', testGeneratorAll, true, 'hex', false, false, true, false, execa); test('Can use generators with error.all = transform + pipe, encoding "hex"', testGeneratorAll, false, 'hex', false, false, true, false, execa); test('Can use generators with result.all = pipe + transform', testGeneratorAll, true, 'utf8', false, true, false, false, execa); test('Can use generators with error.all = pipe + transform', testGeneratorAll, false, 'utf8', false, true, false, false, execa); test('Can use generators with result.all = pipe + transform, encoding "buffer"', testGeneratorAll, true, 'buffer', false, true, false, false, execa); test('Can use generators with error.all = pipe + transform, encoding "buffer"', testGeneratorAll, false, 'buffer', false, true, false, false, execa); test('Can use generators with result.all = pipe + transform, encoding "hex"', testGeneratorAll, true, 'hex', false, true, false, false, execa); test('Can use generators with error.all = pipe + transform, encoding "hex"', testGeneratorAll, false, 'hex', false, true, false, false, execa); test('Can use generators with result.all = transform + transform, objectMode', testGeneratorAll, true, 'utf8', true, false, false, false, execa); test('Can use generators with error.all = transform + transform, objectMode', testGeneratorAll, false, 'utf8', true, false, false, false, execa); test('Can use generators with result.all = transform + transform, objectMode, encoding "buffer"', testGeneratorAll, true, 'buffer', true, false, false, false, execa); test('Can use generators with error.all = transform + transform, objectMode, encoding "buffer"', testGeneratorAll, false, 'buffer', true, false, false, false, execa); test('Can use generators with result.all = transform + transform, objectMode, encoding "hex"', testGeneratorAll, true, 'hex', true, false, false, false, execa); test('Can use generators with error.all = transform + transform, objectMode, encoding "hex"', testGeneratorAll, false, 'hex', true, false, false, false, execa); test('Can use generators with result.all = transform + pipe, objectMode', testGeneratorAll, true, 'utf8', true, false, true, false, execa); test('Can use generators with error.all = transform + pipe, objectMode', testGeneratorAll, false, 'utf8', true, false, true, false, execa); test('Can use generators with result.all = transform + pipe, objectMode, encoding "buffer"', testGeneratorAll, true, 'buffer', true, false, true, false, execa); test('Can use generators with error.all = transform + pipe, objectMode, encoding "buffer"', testGeneratorAll, false, 'buffer', true, false, true, false, execa); test('Can use generators with result.all = transform + pipe, objectMode, encoding "hex"', testGeneratorAll, true, 'hex', true, false, true, false, execa); test('Can use generators with error.all = transform + pipe, objectMode, encoding "hex"', testGeneratorAll, false, 'hex', true, false, true, false, execa); test('Can use generators with result.all = pipe + transform, objectMode', testGeneratorAll, true, 'utf8', true, true, false, false, execa); test('Can use generators with error.all = pipe + transform, objectMode', testGeneratorAll, false, 'utf8', true, true, false, false, execa); test('Can use generators with result.all = pipe + transform, objectMode, encoding "buffer"', testGeneratorAll, true, 'buffer', true, true, false, false, execa); test('Can use generators with error.all = pipe + transform, objectMode, encoding "buffer"', testGeneratorAll, false, 'buffer', true, true, false, false, execa); test('Can use generators with result.all = pipe + transform, objectMode, encoding "hex"', testGeneratorAll, true, 'hex', true, true, false, false, execa); test('Can use generators with error.all = pipe + transform, objectMode, encoding "hex"', testGeneratorAll, false, 'hex', true, true, false, false, execa); test('Can use generators with result.all = transform + transform, sync', testGeneratorAll, true, 'utf8', false, false, false, false, execaSync); test('Can use generators with error.all = transform + transform, sync', testGeneratorAll, false, 'utf8', false, false, false, false, execaSync); test('Can use generators with result.all = transform + transform, encoding "buffer", sync', testGeneratorAll, true, 'buffer', false, false, false, false, execaSync); test('Can use generators with error.all = transform + transform, encoding "buffer", sync', testGeneratorAll, false, 'buffer', false, false, false, false, execaSync); test('Can use generators with result.all = transform + transform, encoding "hex", sync', testGeneratorAll, true, 'hex', false, false, false, false, execaSync); test('Can use generators with error.all = transform + transform, encoding "hex", sync', testGeneratorAll, false, 'hex', false, false, false, false, execaSync); test('Can use generators with result.all = transform + pipe, sync', testGeneratorAll, true, 'utf8', false, false, true, false, execaSync); test('Can use generators with error.all = transform + pipe, sync', testGeneratorAll, false, 'utf8', false, false, true, false, execaSync); test('Can use generators with result.all = transform + pipe, encoding "buffer", sync', testGeneratorAll, true, 'buffer', false, false, true, false, execaSync); test('Can use generators with error.all = transform + pipe, encoding "buffer", sync', testGeneratorAll, false, 'buffer', false, false, true, false, execaSync); test('Can use generators with result.all = transform + pipe, encoding "hex", sync', testGeneratorAll, true, 'hex', false, false, true, false, execaSync); test('Can use generators with error.all = transform + pipe, encoding "hex", sync', testGeneratorAll, false, 'hex', false, false, true, false, execaSync); test('Can use generators with result.all = pipe + transform, sync', testGeneratorAll, true, 'utf8', false, true, false, false, execaSync); test('Can use generators with error.all = pipe + transform, sync', testGeneratorAll, false, 'utf8', false, true, false, false, execaSync); test('Can use generators with result.all = pipe + transform, encoding "buffer", sync', testGeneratorAll, true, 'buffer', false, true, false, false, execaSync); test('Can use generators with error.all = pipe + transform, encoding "buffer", sync', testGeneratorAll, false, 'buffer', false, true, false, false, execaSync); test('Can use generators with result.all = pipe + transform, encoding "hex", sync', testGeneratorAll, true, 'hex', false, true, false, false, execaSync); test('Can use generators with error.all = pipe + transform, encoding "hex", sync', testGeneratorAll, false, 'hex', false, true, false, false, execaSync); test('Can use generators with result.all = transform + transform, objectMode, sync', testGeneratorAll, true, 'utf8', true, false, false, false, execaSync); test('Can use generators with error.all = transform + transform, objectMode, sync', testGeneratorAll, false, 'utf8', true, false, false, false, execaSync); test('Can use generators with result.all = transform + transform, objectMode, encoding "buffer", sync', testGeneratorAll, true, 'buffer', true, false, false, false, execaSync); test('Can use generators with error.all = transform + transform, objectMode, encoding "buffer", sync', testGeneratorAll, false, 'buffer', true, false, false, false, execaSync); test('Can use generators with result.all = transform + transform, objectMode, encoding "hex", sync', testGeneratorAll, true, 'hex', true, false, false, false, execaSync); test('Can use generators with error.all = transform + transform, objectMode, encoding "hex", sync', testGeneratorAll, false, 'hex', true, false, false, false, execaSync); test('Can use generators with result.all = transform + pipe, objectMode, sync', testGeneratorAll, true, 'utf8', true, false, true, false, execaSync); test('Can use generators with error.all = transform + pipe, objectMode, sync', testGeneratorAll, false, 'utf8', true, false, true, false, execaSync); test('Can use generators with result.all = transform + pipe, objectMode, encoding "buffer", sync', testGeneratorAll, true, 'buffer', true, false, true, false, execaSync); test('Can use generators with error.all = transform + pipe, objectMode, encoding "buffer", sync', testGeneratorAll, false, 'buffer', true, false, true, false, execaSync); test('Can use generators with result.all = transform + pipe, objectMode, encoding "hex", sync', testGeneratorAll, true, 'hex', true, false, true, false, execaSync); test('Can use generators with error.all = transform + pipe, objectMode, encoding "hex", sync', testGeneratorAll, false, 'hex', true, false, true, false, execaSync); test('Can use generators with result.all = pipe + transform, objectMode, sync', testGeneratorAll, true, 'utf8', true, true, false, false, execaSync); test('Can use generators with error.all = pipe + transform, objectMode, sync', testGeneratorAll, false, 'utf8', true, true, false, false, execaSync); test('Can use generators with result.all = pipe + transform, objectMode, encoding "buffer", sync', testGeneratorAll, true, 'buffer', true, true, false, false, execaSync); test('Can use generators with error.all = pipe + transform, objectMode, encoding "buffer", sync', testGeneratorAll, false, 'buffer', true, true, false, false, execaSync); test('Can use generators with result.all = pipe + transform, objectMode, encoding "hex", sync', testGeneratorAll, true, 'hex', true, true, false, false, execaSync); test('Can use generators with error.all = pipe + transform, objectMode, encoding "hex", sync', testGeneratorAll, false, 'hex', true, true, false, false, execaSync); test('Can use generators with result.all = transform + transform, lines', testGeneratorAll, true, 'utf8', false, false, false, true, execa); test('Can use generators with error.all = transform + transform, lines', testGeneratorAll, false, 'utf8', false, false, false, true, execa); test('Can use generators with result.all = transform + transform, encoding "buffer", lines', testGeneratorAll, true, 'buffer', false, false, false, true, execa); test('Can use generators with error.all = transform + transform, encoding "buffer", lines', testGeneratorAll, false, 'buffer', false, false, false, true, execa); test('Can use generators with result.all = transform + transform, encoding "hex", lines', testGeneratorAll, true, 'hex', false, false, false, true, execa); test('Can use generators with error.all = transform + transform, encoding "hex", lines', testGeneratorAll, false, 'hex', false, false, false, true, execa); test('Can use generators with result.all = transform + pipe, lines', testGeneratorAll, true, 'utf8', false, false, true, true, execa); test('Can use generators with error.all = transform + pipe, lines', testGeneratorAll, false, 'utf8', false, false, true, true, execa); test('Can use generators with result.all = transform + pipe, encoding "buffer", lines', testGeneratorAll, true, 'buffer', false, false, true, true, execa); test('Can use generators with error.all = transform + pipe, encoding "buffer", lines', testGeneratorAll, false, 'buffer', false, false, true, true, execa); test('Can use generators with result.all = transform + pipe, encoding "hex", lines', testGeneratorAll, true, 'hex', false, false, true, true, execa); test('Can use generators with error.all = transform + pipe, encoding "hex", lines', testGeneratorAll, false, 'hex', false, false, true, true, execa); test('Can use generators with result.all = pipe + transform, lines', testGeneratorAll, true, 'utf8', false, true, false, true, execa); test('Can use generators with error.all = pipe + transform, lines', testGeneratorAll, false, 'utf8', false, true, false, true, execa); test('Can use generators with result.all = pipe + transform, encoding "buffer", lines', testGeneratorAll, true, 'buffer', false, true, false, true, execa); test('Can use generators with error.all = pipe + transform, encoding "buffer", lines', testGeneratorAll, false, 'buffer', false, true, false, true, execa); test('Can use generators with result.all = pipe + transform, encoding "hex", lines', testGeneratorAll, true, 'hex', false, true, false, true, execa); test('Can use generators with error.all = pipe + transform, encoding "hex", lines', testGeneratorAll, false, 'hex', false, true, false, true, execa); test('Can use generators with result.all = transform + transform, objectMode, lines', testGeneratorAll, true, 'utf8', true, false, false, true, execa); test('Can use generators with error.all = transform + transform, objectMode, lines', testGeneratorAll, false, 'utf8', true, false, false, true, execa); test('Can use generators with result.all = transform + transform, objectMode, encoding "buffer", lines', testGeneratorAll, true, 'buffer', true, false, false, true, execa); test('Can use generators with error.all = transform + transform, objectMode, encoding "buffer", lines', testGeneratorAll, false, 'buffer', true, false, false, true, execa); test('Can use generators with result.all = transform + transform, objectMode, encoding "hex", lines', testGeneratorAll, true, 'hex', true, false, false, true, execa); test('Can use generators with error.all = transform + transform, objectMode, encoding "hex", lines', testGeneratorAll, false, 'hex', true, false, false, true, execa); test('Can use generators with result.all = transform + pipe, objectMode, lines', testGeneratorAll, true, 'utf8', true, false, true, true, execa); test('Can use generators with error.all = transform + pipe, objectMode, lines', testGeneratorAll, false, 'utf8', true, false, true, true, execa); test('Can use generators with result.all = transform + pipe, objectMode, encoding "buffer", lines', testGeneratorAll, true, 'buffer', true, false, true, true, execa); test('Can use generators with error.all = transform + pipe, objectMode, encoding "buffer", lines', testGeneratorAll, false, 'buffer', true, false, true, true, execa); test('Can use generators with result.all = transform + pipe, objectMode, encoding "hex", lines', testGeneratorAll, true, 'hex', true, false, true, true, execa); test('Can use generators with error.all = transform + pipe, objectMode, encoding "hex", lines', testGeneratorAll, false, 'hex', true, false, true, true, execa); test('Can use generators with result.all = pipe + transform, objectMode, lines', testGeneratorAll, true, 'utf8', true, true, false, true, execa); test('Can use generators with error.all = pipe + transform, objectMode, lines', testGeneratorAll, false, 'utf8', true, true, false, true, execa); test('Can use generators with result.all = pipe + transform, objectMode, encoding "buffer", lines', testGeneratorAll, true, 'buffer', true, true, false, true, execa); test('Can use generators with error.all = pipe + transform, objectMode, encoding "buffer", lines', testGeneratorAll, false, 'buffer', true, true, false, true, execa); test('Can use generators with result.all = pipe + transform, objectMode, encoding "hex", lines', testGeneratorAll, true, 'hex', true, true, false, true, execa); test('Can use generators with error.all = pipe + transform, objectMode, encoding "hex", lines', testGeneratorAll, false, 'hex', true, true, false, true, execa); test('Can use generators with result.all = transform + transform, sync, lines', testGeneratorAll, true, 'utf8', false, false, false, true, execaSync); test('Can use generators with error.all = transform + transform, sync, lines', testGeneratorAll, false, 'utf8', false, false, false, true, execaSync); test('Can use generators with result.all = transform + transform, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', false, false, false, true, execaSync); test('Can use generators with error.all = transform + transform, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', false, false, false, true, execaSync); test('Can use generators with result.all = transform + transform, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', false, false, false, true, execaSync); test('Can use generators with error.all = transform + transform, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', false, false, false, true, execaSync); test('Can use generators with result.all = transform + pipe, sync, lines', testGeneratorAll, true, 'utf8', false, false, true, true, execaSync); test('Can use generators with error.all = transform + pipe, sync, lines', testGeneratorAll, false, 'utf8', false, false, true, true, execaSync); test('Can use generators with result.all = transform + pipe, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', false, false, true, true, execaSync); test('Can use generators with error.all = transform + pipe, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', false, false, true, true, execaSync); test('Can use generators with result.all = transform + pipe, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', false, false, true, true, execaSync); test('Can use generators with error.all = transform + pipe, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', false, false, true, true, execaSync); test('Can use generators with result.all = pipe + transform, sync, lines', testGeneratorAll, true, 'utf8', false, true, false, true, execaSync); test('Can use generators with error.all = pipe + transform, sync, lines', testGeneratorAll, false, 'utf8', false, true, false, true, execaSync); test('Can use generators with result.all = pipe + transform, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', false, true, false, true, execaSync); test('Can use generators with error.all = pipe + transform, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', false, true, false, true, execaSync); test('Can use generators with result.all = pipe + transform, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', false, true, false, true, execaSync); test('Can use generators with error.all = pipe + transform, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', false, true, false, true, execaSync); test('Can use generators with result.all = transform + transform, objectMode, sync, lines', testGeneratorAll, true, 'utf8', true, false, false, true, execaSync); test('Can use generators with error.all = transform + transform, objectMode, sync, lines', testGeneratorAll, false, 'utf8', true, false, false, true, execaSync); test('Can use generators with result.all = transform + transform, objectMode, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', true, false, false, true, execaSync); test('Can use generators with error.all = transform + transform, objectMode, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', true, false, false, true, execaSync); test('Can use generators with result.all = transform + transform, objectMode, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', true, false, false, true, execaSync); test('Can use generators with error.all = transform + transform, objectMode, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', true, false, false, true, execaSync); test('Can use generators with result.all = transform + pipe, objectMode, sync, lines', testGeneratorAll, true, 'utf8', true, false, true, true, execaSync); test('Can use generators with error.all = transform + pipe, objectMode, sync, lines', testGeneratorAll, false, 'utf8', true, false, true, true, execaSync); test('Can use generators with result.all = transform + pipe, objectMode, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', true, false, true, true, execaSync); test('Can use generators with error.all = transform + pipe, objectMode, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', true, false, true, true, execaSync); test('Can use generators with result.all = transform + pipe, objectMode, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', true, false, true, true, execaSync); test('Can use generators with error.all = transform + pipe, objectMode, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', true, false, true, true, execaSync); test('Can use generators with result.all = pipe + transform, objectMode, sync, lines', testGeneratorAll, true, 'utf8', true, true, false, true, execaSync); test('Can use generators with error.all = pipe + transform, objectMode, sync, lines', testGeneratorAll, false, 'utf8', true, true, false, true, execaSync); test('Can use generators with result.all = pipe + transform, objectMode, encoding "buffer", sync, lines', testGeneratorAll, true, 'buffer', true, true, false, true, execaSync); test('Can use generators with error.all = pipe + transform, objectMode, encoding "buffer", sync, lines', testGeneratorAll, false, 'buffer', true, true, false, true, execaSync); test('Can use generators with result.all = pipe + transform, objectMode, encoding "hex", sync, lines', testGeneratorAll, true, 'hex', true, true, false, true, execaSync); test('Can use generators with error.all = pipe + transform, objectMode, encoding "hex", sync, lines', testGeneratorAll, false, 'hex', true, true, false, true, execaSync); ================================================ FILE: test/transform/generator-error.js ================================================ import {once} from 'node:events'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {foobarString} from '../helpers/input.js'; import {noopGenerator, infiniteGenerator, convertTransformToFinal} from '../helpers/generator.js'; import {generatorsMap} from '../helpers/map.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getEarlyErrorSubprocess, expectedEarlyError} from '../helpers/early-error.js'; setFixtureDirectory(); const assertProcessError = async (t, type, execaMethod, getSubprocess) => { const cause = new Error(foobarString); const transform = generatorsMap[type].throwing(cause)(); const error = execaMethod === execa ? await t.throwsAsync(getSubprocess(transform)) : t.throws(() => { getSubprocess(transform); }); t.is(error.cause, cause); }; const testThrowingGenerator = async (t, type, final, execaMethod) => { await assertProcessError(t, type, execaMethod, transform => execaMethod('noop.js', { stdout: convertTransformToFinal(transform, final), })); }; test('Generators "transform" errors make subprocess fail', testThrowingGenerator, 'generator', false, execa); test('Generators "final" errors make subprocess fail', testThrowingGenerator, 'generator', true, execa); test('Generators "transform" errors make subprocess fail, sync', testThrowingGenerator, 'generator', false, execaSync); test('Generators "final" errors make subprocess fail, sync', testThrowingGenerator, 'generator', true, execaSync); test('Duplexes "transform" errors make subprocess fail', testThrowingGenerator, 'duplex', false, execa); test('WebTransform "transform" errors make subprocess fail', testThrowingGenerator, 'webTransform', false, execa); const testSingleErrorOutput = async (t, type, execaMethod) => { await assertProcessError(t, type, execaMethod, transform => execaMethod('noop.js', { stdout: [ generatorsMap[type].noop(false), transform, generatorsMap[type].noop(false), ], })); }; test('Generators errors make subprocess fail even when other output generators do not throw', testSingleErrorOutput, 'generator', execa); test('Generators errors make subprocess fail even when other output generators do not throw, sync', testSingleErrorOutput, 'generator', execaSync); test('Duplexes errors make subprocess fail even when other output generators do not throw', testSingleErrorOutput, 'duplex', execa); test('WebTransform errors make subprocess fail even when other output generators do not throw', testSingleErrorOutput, 'webTransform', execa); const testSingleErrorInput = async (t, type, execaMethod) => { await assertProcessError(t, type, execaMethod, transform => execaMethod('stdin.js', { stdin: [ ['foobar\n'], generatorsMap[type].noop(false), transform, generatorsMap[type].noop(false), ], })); }; test('Generators errors make subprocess fail even when other input generators do not throw', testSingleErrorInput, 'generator', execa); test('Generators errors make subprocess fail even when other input generators do not throw, sync', testSingleErrorInput, 'generator', execaSync); test('Duplexes errors make subprocess fail even when other input generators do not throw', testSingleErrorInput, 'duplex', execa); test('WebTransform errors make subprocess fail even when other input generators do not throw', testSingleErrorInput, 'webTransform', execa); const testGeneratorCancel = async (t, error) => { const subprocess = execa('noop.js', {stdout: infiniteGenerator()}); await once(subprocess.stdout, 'data'); subprocess.stdout.destroy(error); await (error === undefined ? t.notThrowsAsync(subprocess) : t.throwsAsync(subprocess)); }; test('Running generators are canceled on subprocess abort', testGeneratorCancel, undefined); test('Running generators are canceled on subprocess error', testGeneratorCancel, new Error('test')); const testGeneratorDestroy = async (t, transform) => { const subprocess = execa('forever.js', {stdout: transform}); const cause = new Error('test'); subprocess.stdout.destroy(cause); subprocess.kill(); t.like(await t.throwsAsync(subprocess), {cause}); }; test('Generators are destroyed on subprocess error, sync', testGeneratorDestroy, noopGenerator(false)); test('Generators are destroyed on subprocess error, async', testGeneratorDestroy, infiniteGenerator()); test('Generators are destroyed on early subprocess exit', async t => { const error = await t.throwsAsync(getEarlyErrorSubprocess({stdout: infiniteGenerator()})); t.like(error, expectedEarlyError); }); ================================================ FILE: test/transform/generator-final.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {foobarString} from '../helpers/input.js'; import {getOutputAsyncGenerator, getOutputGenerator, convertTransformToFinal} from '../helpers/generator.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); const testGeneratorFinal = async (t, fixtureName, execaMethod) => { const {stdout} = await execaMethod(fixtureName, {stdout: convertTransformToFinal(getOutputGenerator(foobarString)(), true)}); t.is(stdout, foobarString); }; test('Generators "final" can be used', testGeneratorFinal, 'noop.js', execa); test('Generators "final" is used even on empty streams', testGeneratorFinal, 'empty.js', execa); test('Generators "final" can be used, sync', testGeneratorFinal, 'noop.js', execaSync); test('Generators "final" is used even on empty streams, sync', testGeneratorFinal, 'empty.js', execaSync); const testFinalAlone = async (t, final, execaMethod) => { const {stdout} = await execaMethod('noop-fd.js', ['1', '.'], {stdout: {final: final(foobarString)().transform}}); t.is(stdout, `.\n${foobarString}`); }; test('Generators "final" can be used without "transform"', testFinalAlone, getOutputGenerator, execa); test('Generators "final" can be used without "transform", sync', testFinalAlone, getOutputGenerator, execaSync); test('Generators "final" can be used without "transform", async', testFinalAlone, getOutputAsyncGenerator, execa); const testFinalNoOutput = async (t, final, execaMethod) => { const {stdout} = await execaMethod('empty.js', {stdout: {final: final(foobarString)().transform}}); t.is(stdout, foobarString); }; test('Generators "final" can be used without "transform" nor output', testFinalNoOutput, getOutputGenerator, execa); test('Generators "final" can be used without "transform" nor output, sync', testFinalNoOutput, getOutputGenerator, execaSync); test('Generators "final" can be used without "transform" nor output, async', testFinalNoOutput, getOutputAsyncGenerator, execa); ================================================ FILE: test/transform/generator-input.js ================================================ import {Buffer} from 'node:buffer'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import { foobarString, foobarUppercase, foobarHex, foobarUint8Array, foobarBuffer, foobarObject, foobarObjectString, } from '../helpers/input.js'; import {generatorsMap} from '../helpers/map.js'; setFixtureDirectory(); const getInputObjectMode = (objectMode, addNoopTransform, type) => objectMode ? { input: [foobarObject], generators: generatorsMap[type].addNoop(generatorsMap[type].serialize(objectMode), addNoopTransform, objectMode), output: foobarObjectString, } : { input: foobarUint8Array, generators: generatorsMap[type].addNoop(generatorsMap[type].uppercase(objectMode), addNoopTransform, objectMode), output: foobarUppercase, }; // eslint-disable-next-line max-params const testGeneratorInput = async (t, fdNumber, objectMode, addNoopTransform, type, execaMethod) => { const {input, generators, output} = getInputObjectMode(objectMode, addNoopTransform, type); const {stdout} = await execaMethod('stdin-fd.js', [`${fdNumber}`], getStdio(fdNumber, [input, ...generators])); t.is(stdout, output); }; test('Can use generators with result.stdin', testGeneratorInput, 0, false, false, 'generator', execa); test('Can use generators with result.stdio[*] as input', testGeneratorInput, 3, false, false, 'generator', execa); test('Can use generators with result.stdin, objectMode', testGeneratorInput, 0, true, false, 'generator', execa); test('Can use generators with result.stdio[*] as input, objectMode', testGeneratorInput, 3, true, false, 'generator', execa); test('Can use generators with result.stdin, noop transform', testGeneratorInput, 0, false, true, 'generator', execa); test('Can use generators with result.stdio[*] as input, noop transform', testGeneratorInput, 3, false, true, 'generator', execa); test('Can use generators with result.stdin, objectMode, noop transform', testGeneratorInput, 0, true, true, 'generator', execa); test('Can use generators with result.stdio[*] as input, objectMode, noop transform', testGeneratorInput, 3, true, true, 'generator', execa); test('Can use generators with result.stdin, sync', testGeneratorInput, 0, false, false, 'generator', execaSync); test('Can use generators with result.stdin, objectMode, sync', testGeneratorInput, 0, true, false, 'generator', execaSync); test('Can use generators with result.stdin, noop transform, sync', testGeneratorInput, 0, false, true, 'generator', execaSync); test('Can use generators with result.stdin, objectMode, noop transform, sync', testGeneratorInput, 0, true, true, 'generator', execaSync); test('Can use duplexes with result.stdin', testGeneratorInput, 0, false, false, 'duplex', execa); test('Can use duplexes with result.stdio[*] as input', testGeneratorInput, 3, false, false, 'duplex', execa); test('Can use duplexes with result.stdin, objectMode', testGeneratorInput, 0, true, false, 'duplex', execa); test('Can use duplexes with result.stdio[*] as input, objectMode', testGeneratorInput, 3, true, false, 'duplex', execa); test('Can use duplexes with result.stdin, noop transform', testGeneratorInput, 0, false, true, 'duplex', execa); test('Can use duplexes with result.stdio[*] as input, noop transform', testGeneratorInput, 3, false, true, 'duplex', execa); test('Can use duplexes with result.stdin, objectMode, noop transform', testGeneratorInput, 0, true, true, 'duplex', execa); test('Can use duplexes with result.stdio[*] as input, objectMode, noop transform', testGeneratorInput, 3, true, true, 'duplex', execa); test('Can use webTransforms with result.stdin', testGeneratorInput, 0, false, false, 'webTransform', execa); test('Can use webTransforms with result.stdio[*] as input', testGeneratorInput, 3, false, false, 'webTransform', execa); test('Can use webTransforms with result.stdin, objectMode', testGeneratorInput, 0, true, false, 'webTransform', execa); test('Can use webTransforms with result.stdio[*] as input, objectMode', testGeneratorInput, 3, true, false, 'webTransform', execa); test('Can use webTransforms with result.stdin, noop transform', testGeneratorInput, 0, false, true, 'webTransform', execa); test('Can use webTransforms with result.stdio[*] as input, noop transform', testGeneratorInput, 3, false, true, 'webTransform', execa); test('Can use webTransforms with result.stdin, objectMode, noop transform', testGeneratorInput, 0, true, true, 'webTransform', execa); test('Can use webTransforms with result.stdio[*] as input, objectMode, noop transform', testGeneratorInput, 3, true, true, 'webTransform', execa); // eslint-disable-next-line max-params const testGeneratorInputPipe = async (t, useShortcutProperty, objectMode, addNoopTransform, type, input) => { const {generators, output} = getInputObjectMode(objectMode, addNoopTransform, type); const subprocess = execa('stdin-fd.js', ['0'], getStdio(0, generators)); const stream = useShortcutProperty ? subprocess.stdin : subprocess.stdio[0]; stream.end(...input); const {stdout} = await subprocess; const expectedOutput = input[1] === 'utf16le' ? Buffer.from(output, input[1]).toString() : output; t.is(stdout, expectedOutput); }; test('Can use generators with subprocess.stdio[0] and default encoding', testGeneratorInputPipe, false, false, false, 'generator', [foobarString, 'utf8']); test('Can use generators with subprocess.stdin and default encoding', testGeneratorInputPipe, true, false, false, 'generator', [foobarString, 'utf8']); test('Can use generators with subprocess.stdio[0] and encoding "utf16le"', testGeneratorInputPipe, false, false, false, 'generator', [foobarString, 'utf16le']); test('Can use generators with subprocess.stdin and encoding "utf16le"', testGeneratorInputPipe, true, false, false, 'generator', [foobarString, 'utf16le']); test('Can use generators with subprocess.stdio[0] and encoding "buffer"', testGeneratorInputPipe, false, false, false, 'generator', [foobarBuffer, 'buffer']); test('Can use generators with subprocess.stdin and encoding "buffer"', testGeneratorInputPipe, true, false, false, 'generator', [foobarBuffer, 'buffer']); test('Can use generators with subprocess.stdio[0] and encoding "hex"', testGeneratorInputPipe, false, false, false, 'generator', [foobarHex, 'hex']); test('Can use generators with subprocess.stdin and encoding "hex"', testGeneratorInputPipe, true, false, false, 'generator', [foobarHex, 'hex']); test('Can use generators with subprocess.stdio[0], objectMode', testGeneratorInputPipe, false, true, false, 'generator', [foobarObject]); test('Can use generators with subprocess.stdin, objectMode', testGeneratorInputPipe, true, true, false, 'generator', [foobarObject]); test('Can use generators with subprocess.stdio[0] and default encoding, noop transform', testGeneratorInputPipe, false, false, true, 'generator', [foobarString, 'utf8']); test('Can use generators with subprocess.stdin and default encoding, noop transform', testGeneratorInputPipe, true, false, true, 'generator', [foobarString, 'utf8']); test('Can use generators with subprocess.stdio[0] and encoding "utf16le", noop transform', testGeneratorInputPipe, false, false, true, 'generator', [foobarString, 'utf16le']); test('Can use generators with subprocess.stdin and encoding "utf16le", noop transform', testGeneratorInputPipe, true, false, true, 'generator', [foobarString, 'utf16le']); test('Can use generators with subprocess.stdio[0] and encoding "buffer", noop transform', testGeneratorInputPipe, false, false, true, 'generator', [foobarBuffer, 'buffer']); test('Can use generators with subprocess.stdin and encoding "buffer", noop transform', testGeneratorInputPipe, true, false, true, 'generator', [foobarBuffer, 'buffer']); test('Can use generators with subprocess.stdio[0] and encoding "hex", noop transform', testGeneratorInputPipe, false, false, true, 'generator', [foobarHex, 'hex']); test('Can use generators with subprocess.stdin and encoding "hex", noop transform', testGeneratorInputPipe, true, false, true, 'generator', [foobarHex, 'hex']); test('Can use generators with subprocess.stdio[0], objectMode, noop transform', testGeneratorInputPipe, false, true, true, 'generator', [foobarObject]); test('Can use generators with subprocess.stdin, objectMode, noop transform', testGeneratorInputPipe, true, true, true, 'generator', [foobarObject]); test('Can use duplexes with subprocess.stdio[0] and default encoding', testGeneratorInputPipe, false, false, false, 'duplex', [foobarString, 'utf8']); test('Can use duplexes with subprocess.stdin and default encoding', testGeneratorInputPipe, true, false, false, 'duplex', [foobarString, 'utf8']); test('Can use duplexes with subprocess.stdio[0] and encoding "utf16le"', testGeneratorInputPipe, false, false, false, 'duplex', [foobarString, 'utf16le']); test('Can use duplexes with subprocess.stdin and encoding "utf16le"', testGeneratorInputPipe, true, false, false, 'duplex', [foobarString, 'utf16le']); test('Can use duplexes with subprocess.stdio[0] and encoding "buffer"', testGeneratorInputPipe, false, false, false, 'duplex', [foobarBuffer, 'buffer']); test('Can use duplexes with subprocess.stdin and encoding "buffer"', testGeneratorInputPipe, true, false, false, 'duplex', [foobarBuffer, 'buffer']); test('Can use duplexes with subprocess.stdio[0] and encoding "hex"', testGeneratorInputPipe, false, false, false, 'duplex', [foobarHex, 'hex']); test('Can use duplexes with subprocess.stdin and encoding "hex"', testGeneratorInputPipe, true, false, false, 'duplex', [foobarHex, 'hex']); test('Can use duplexes with subprocess.stdio[0], objectMode', testGeneratorInputPipe, false, true, false, 'duplex', [foobarObject]); test('Can use duplexes with subprocess.stdin, objectMode', testGeneratorInputPipe, true, true, false, 'duplex', [foobarObject]); test('Can use duplexes with subprocess.stdio[0] and default encoding, noop transform', testGeneratorInputPipe, false, false, true, 'duplex', [foobarString, 'utf8']); test('Can use duplexes with subprocess.stdin and default encoding, noop transform', testGeneratorInputPipe, true, false, true, 'duplex', [foobarString, 'utf8']); test('Can use duplexes with subprocess.stdio[0] and encoding "utf16le", noop transform', testGeneratorInputPipe, false, false, true, 'duplex', [foobarString, 'utf16le']); test('Can use duplexes with subprocess.stdin and encoding "utf16le", noop transform', testGeneratorInputPipe, true, false, true, 'duplex', [foobarString, 'utf16le']); test('Can use duplexes with subprocess.stdio[0] and encoding "buffer", noop transform', testGeneratorInputPipe, false, false, true, 'duplex', [foobarBuffer, 'buffer']); test('Can use duplexes with subprocess.stdin and encoding "buffer", noop transform', testGeneratorInputPipe, true, false, true, 'duplex', [foobarBuffer, 'buffer']); test('Can use duplexes with subprocess.stdio[0] and encoding "hex", noop transform', testGeneratorInputPipe, false, false, true, 'duplex', [foobarHex, 'hex']); test('Can use duplexes with subprocess.stdin and encoding "hex", noop transform', testGeneratorInputPipe, true, false, true, 'duplex', [foobarHex, 'hex']); test('Can use duplexes with subprocess.stdio[0], objectMode, noop transform', testGeneratorInputPipe, false, true, true, 'duplex', [foobarObject]); test('Can use duplexes with subprocess.stdin, objectMode, noop transform', testGeneratorInputPipe, true, true, true, 'duplex', [foobarObject]); test('Can use webTransforms with subprocess.stdio[0] and default encoding', testGeneratorInputPipe, false, false, false, 'webTransform', [foobarString, 'utf8']); test('Can use webTransforms with subprocess.stdin and default encoding', testGeneratorInputPipe, true, false, false, 'webTransform', [foobarString, 'utf8']); test('Can use webTransforms with subprocess.stdio[0] and encoding "utf16le"', testGeneratorInputPipe, false, false, false, 'webTransform', [foobarString, 'utf16le']); test('Can use webTransforms with subprocess.stdin and encoding "utf16le"', testGeneratorInputPipe, true, false, false, 'webTransform', [foobarString, 'utf16le']); test('Can use webTransforms with subprocess.stdio[0] and encoding "buffer"', testGeneratorInputPipe, false, false, false, 'webTransform', [foobarBuffer, 'buffer']); test('Can use webTransforms with subprocess.stdin and encoding "buffer"', testGeneratorInputPipe, true, false, false, 'webTransform', [foobarBuffer, 'buffer']); test('Can use webTransforms with subprocess.stdio[0] and encoding "hex"', testGeneratorInputPipe, false, false, false, 'webTransform', [foobarHex, 'hex']); test('Can use webTransforms with subprocess.stdin and encoding "hex"', testGeneratorInputPipe, true, false, false, 'webTransform', [foobarHex, 'hex']); test('Can use webTransforms with subprocess.stdio[0], objectMode', testGeneratorInputPipe, false, true, false, 'webTransform', [foobarObject]); test('Can use webTransforms with subprocess.stdin, objectMode', testGeneratorInputPipe, true, true, false, 'webTransform', [foobarObject]); test('Can use webTransforms with subprocess.stdio[0] and default encoding, noop transform', testGeneratorInputPipe, false, false, true, 'webTransform', [foobarString, 'utf8']); test('Can use webTransforms with subprocess.stdin and default encoding, noop transform', testGeneratorInputPipe, true, false, true, 'webTransform', [foobarString, 'utf8']); test('Can use webTransforms with subprocess.stdio[0] and encoding "utf16le", noop transform', testGeneratorInputPipe, false, false, true, 'webTransform', [foobarString, 'utf16le']); test('Can use webTransforms with subprocess.stdin and encoding "utf16le", noop transform', testGeneratorInputPipe, true, false, true, 'webTransform', [foobarString, 'utf16le']); test('Can use webTransforms with subprocess.stdio[0] and encoding "buffer", noop transform', testGeneratorInputPipe, false, false, true, 'webTransform', [foobarBuffer, 'buffer']); test('Can use webTransforms with subprocess.stdin and encoding "buffer", noop transform', testGeneratorInputPipe, true, false, true, 'webTransform', [foobarBuffer, 'buffer']); test('Can use webTransforms with subprocess.stdio[0] and encoding "hex", noop transform', testGeneratorInputPipe, false, false, true, 'webTransform', [foobarHex, 'hex']); test('Can use webTransforms with subprocess.stdin and encoding "hex", noop transform', testGeneratorInputPipe, true, false, true, 'webTransform', [foobarHex, 'hex']); test('Can use webTransforms with subprocess.stdio[0], objectMode, noop transform', testGeneratorInputPipe, false, true, true, 'webTransform', [foobarObject]); test('Can use webTransforms with subprocess.stdin, objectMode, noop transform', testGeneratorInputPipe, true, true, true, 'webTransform', [foobarObject]); const testGeneratorStdioInputPipe = async (t, objectMode, addNoopTransform, type) => { const {input, generators, output} = getInputObjectMode(objectMode, addNoopTransform, type); const subprocess = execa('stdin-fd.js', ['3'], getStdio(3, [[], ...generators])); subprocess.stdio[3].write(Array.isArray(input) ? input[0] : input); const {stdout} = await subprocess; t.is(stdout, output); }; test('Can use generators with subprocess.stdio[*] as input', testGeneratorStdioInputPipe, false, false, 'generator'); test('Can use generators with subprocess.stdio[*] as input, objectMode', testGeneratorStdioInputPipe, true, false, 'generator'); test('Can use generators with subprocess.stdio[*] as input, noop transform', testGeneratorStdioInputPipe, false, true, 'generator'); test('Can use generators with subprocess.stdio[*] as input, objectMode, noop transform', testGeneratorStdioInputPipe, true, true, 'generator'); test('Can use duplexes with subprocess.stdio[*] as input', testGeneratorStdioInputPipe, false, false, 'duplex'); test('Can use duplexes with subprocess.stdio[*] as input, objectMode', testGeneratorStdioInputPipe, true, false, 'duplex'); test('Can use duplexes with subprocess.stdio[*] as input, noop transform', testGeneratorStdioInputPipe, false, true, 'duplex'); test('Can use duplexes with subprocess.stdio[*] as input, objectMode, noop transform', testGeneratorStdioInputPipe, true, true, 'duplex'); test('Can use webTransforms with subprocess.stdio[*] as input', testGeneratorStdioInputPipe, false, false, 'webTransform'); test('Can use webTransforms with subprocess.stdio[*] as input, objectMode', testGeneratorStdioInputPipe, true, false, 'webTransform'); test('Can use webTransforms with subprocess.stdio[*] as input, noop transform', testGeneratorStdioInputPipe, false, true, 'webTransform'); test('Can use webTransforms with subprocess.stdio[*] as input, objectMode, noop transform', testGeneratorStdioInputPipe, true, true, 'webTransform'); ================================================ FILE: test/transform/generator-main.js ================================================ import {Buffer} from 'node:buffer'; import {scheduler} from 'node:timers/promises'; import test from 'ava'; import {getStreamAsArray} from 'get-stream'; import {execa, execaSync} from '../../index.js'; import {foobarString} from '../helpers/input.js'; import { noopGenerator, outputObjectGenerator, convertTransformToFinal, prefix, suffix, } from '../helpers/generator.js'; import {generatorsMap} from '../helpers/map.js'; import {defaultHighWaterMark} from '../helpers/stream.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {maxBuffer, assertErrorMessage} from '../helpers/max-buffer.js'; setFixtureDirectory(); const repeatCount = defaultHighWaterMark * 3; const writerGenerator = function * () { for (let index = 0; index < repeatCount; index += 1) { yield '\n'; } }; const getLengthGenerator = function * (t, chunk) { t.is(chunk.length, 1); yield chunk; }; // eslint-disable-next-line max-params const testHighWaterMark = async (t, passThrough, binary, objectMode, execaMethod) => { const {stdout} = await execaMethod('noop.js', { stdout: [ ...(objectMode ? [outputObjectGenerator()] : []), writerGenerator, ...(passThrough ? [noopGenerator(false, binary)] : []), {transform: getLengthGenerator.bind(undefined, t), preserveNewlines: true, objectMode: true}, ], }); t.is(stdout.length, repeatCount); t.true(stdout.every(chunk => chunk === '\n')); }; test('Synchronous yields are not buffered, no passThrough', testHighWaterMark, false, false, false, execa); test('Synchronous yields are not buffered, line-wise passThrough', testHighWaterMark, true, false, false, execa); test('Synchronous yields are not buffered, binary passThrough', testHighWaterMark, true, true, false, execa); test('Synchronous yields are not buffered, objectMode as input but not output', testHighWaterMark, false, false, true, execa); test('Synchronous yields are not buffered, no passThrough, sync', testHighWaterMark, false, false, false, execaSync); test('Synchronous yields are not buffered, line-wise passThrough, sync', testHighWaterMark, true, false, false, execaSync); test('Synchronous yields are not buffered, binary passThrough, sync', testHighWaterMark, true, true, false, execaSync); test('Synchronous yields are not buffered, objectMode as input but not output, sync', testHighWaterMark, false, false, true, execaSync); // eslint-disable-next-line max-params const testNoYield = async (t, type, objectMode, final, output, execaMethod) => { const {stdout} = await execaMethod('noop.js', {stdout: convertTransformToFinal(generatorsMap[type].noYield(objectMode), final)}); t.deepEqual(stdout, output); }; test('Generator can filter "transform" by not calling yield', testNoYield, 'generator', false, false, '', execa); test('Generator can filter "transform" by not calling yield, objectMode', testNoYield, 'generator', true, false, [], execa); test('Generator can filter "final" by not calling yield', testNoYield, 'generator', false, true, '', execa); test('Generator can filter "final" by not calling yield, objectMode', testNoYield, 'generator', true, true, [], execa); test('Generator can filter "transform" by not calling yield, sync', testNoYield, 'generator', false, false, '', execaSync); test('Generator can filter "transform" by not calling yield, objectMode, sync', testNoYield, 'generator', true, false, [], execaSync); test('Generator can filter "final" by not calling yield, sync', testNoYield, 'generator', false, true, '', execaSync); test('Generator can filter "final" by not calling yield, objectMode, sync', testNoYield, 'generator', true, true, [], execaSync); test('Duplex can filter by not calling push', testNoYield, 'duplex', false, false, '', execa); test('Duplex can filter by not calling push, objectMode', testNoYield, 'duplex', true, false, [], execa); test('WebTransform can filter by not calling push', testNoYield, 'webTransform', false, false, '', execa); test('WebTransform can filter by not calling push, objectMode', testNoYield, 'webTransform', true, false, [], execa); const testMultipleYields = async (t, type, final, binary) => { const {stdout} = await execa('noop-fd.js', ['1', foobarString], {stdout: convertTransformToFinal(generatorsMap[type].multipleYield(), final)}); const newline = binary ? '' : '\n'; t.is(stdout, `${prefix}${newline}${foobarString}${newline}${suffix}`); }; test('Generator can yield "transform" multiple times at different moments', testMultipleYields, 'generator', false, false); test('Generator can yield "final" multiple times at different moments', testMultipleYields, 'generator', true, false); test('Duplex can push multiple times at different moments', testMultipleYields, 'duplex', false, true); test('WebTransform can push multiple times at different moments', testMultipleYields, 'webTransform', false, true); const partsPerChunk = 4; const chunksPerCall = 10; const callCount = 5; const fullString = '\n'.repeat(defaultHighWaterMark / partsPerChunk); const yieldFullStrings = function * () { yield * Array.from({length: partsPerChunk * chunksPerCall}).fill(fullString); }; const manyYieldGenerator = async function * () { for (let index = 0; index < callCount; index += 1) { yield * yieldFullStrings(); // eslint-disable-next-line no-await-in-loop await scheduler.yield(); } }; const testManyYields = async (t, final) => { const subprocess = execa('noop.js', {stdout: convertTransformToFinal(manyYieldGenerator, final), stripFinalNewline: false}); const [chunks, {stdout}] = await Promise.all([getStreamAsArray(subprocess.stdout), subprocess]); const expectedChunk = Buffer.from(fullString); t.deepEqual(chunks, Array.from({length: callCount * partsPerChunk * chunksPerCall}).fill(expectedChunk)); t.is(chunks.join(''), stdout); }; test('Generator "transform" yields are sent right away', testManyYields, false); test('Generator "final" yields are sent right away', testManyYields, true); const testMaxBuffer = async (t, type) => { const bigString = '.'.repeat(maxBuffer); const {stdout} = await execa('noop.js', { maxBuffer, stdout: generatorsMap[type].getOutput(bigString)(false, true), }); t.is(stdout, bigString); const {isMaxBuffer, shortMessage} = await t.throwsAsync(execa('noop.js', { maxBuffer, stdout: generatorsMap[type].getOutput(`${bigString}.`)(false, true), })); t.true(isMaxBuffer); assertErrorMessage(t, shortMessage); }; test('Generators take "maxBuffer" into account', testMaxBuffer, 'generator'); test('Duplexes take "maxBuffer" into account', testMaxBuffer, 'duplex'); test('WebTransforms take "maxBuffer" into account', testMaxBuffer, 'webTransform'); test('Generators does not take "maxBuffer" into account, sync', t => { const bigString = '.'.repeat(maxBuffer); const {isMaxBuffer, stdout} = execaSync('noop.js', { maxBuffer, stdout: generatorsMap.generator.getOutput(`${bigString}.`)(false, true), }); t.false(isMaxBuffer); t.is(stdout.length, maxBuffer + 1); }); const testMaxBufferObject = async (t, type) => { const bigArray = Array.from({length: maxBuffer}).fill('..'); const {stdout} = await execa('noop.js', { maxBuffer, stdout: generatorsMap[type].getOutputs(bigArray)(true, true), }); t.is(stdout.length, maxBuffer); const {isMaxBuffer, shortMessage} = await t.throwsAsync(execa('noop.js', { maxBuffer, stdout: generatorsMap[type].getOutputs([...bigArray, ''])(true, true), })); t.true(isMaxBuffer); assertErrorMessage(t, shortMessage, {unit: 'objects'}); }; test('Generators take "maxBuffer" into account, objectMode', testMaxBufferObject, 'generator'); test('Duplexes take "maxBuffer" into account, objectMode', testMaxBufferObject, 'duplex'); test('WebTransforms take "maxBuffer" into account, objectMode', testMaxBufferObject, 'webTransform'); test('Generators does not take "maxBuffer" into account, objectMode, sync', t => { const bigArray = Array.from({length: maxBuffer}).fill('..'); const {isMaxBuffer, stdout} = execaSync('noop.js', { maxBuffer, stdout: generatorsMap.generator.getOutputs([...bigArray, ''])(true, true), }); t.false(isMaxBuffer); t.is(stdout.length, maxBuffer + 1); }); const testAsyncGenerators = async (t, type, final) => { const {stdout} = await execa('noop.js', { stdout: convertTransformToFinal(generatorsMap[type].timeout(1e2)(), final), }); t.is(stdout, foobarString); }; test('Generators "transform" is awaited on success', testAsyncGenerators, 'generator', false); test('Generators "final" is awaited on success', testAsyncGenerators, 'generator', true); test('Duplex is awaited on success', testAsyncGenerators, 'duplex', false); test('WebTransform is awaited on success', testAsyncGenerators, 'webTransform', false); ================================================ FILE: test/transform/generator-mixed.js ================================================ import {readFile, writeFile, rm} from 'node:fs/promises'; import {PassThrough} from 'node:stream'; import test from 'ava'; import getStream from 'get-stream'; import tempfile from 'tempfile'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarUppercase, foobarUint8Array} from '../helpers/input.js'; import {uppercaseGenerator} from '../helpers/generator.js'; import {uppercaseBufferDuplex} from '../helpers/duplex.js'; import {uppercaseBufferWebTransform} from '../helpers/web-transform.js'; import {generatorsMap} from '../helpers/map.js'; setFixtureDirectory(); const testInputOption = async (t, type, execaMethod) => { const {stdout} = await execaMethod('stdin-fd.js', ['0'], {stdin: generatorsMap[type].uppercase(), input: foobarUint8Array}); t.is(stdout, foobarUppercase); }; test('Can use generators with input option', testInputOption, 'generator', execa); test('Can use generators with input option, sync', testInputOption, 'generator', execaSync); test('Can use duplexes with input option', testInputOption, 'duplex', execa); test('Can use webTransforms with input option', testInputOption, 'webTransform', execa); // eslint-disable-next-line max-params const testInputFile = async (t, stdinOption, useInputFile, reversed, execaMethod) => { const filePath = tempfile(); await writeFile(filePath, foobarString); const options = useInputFile ? {inputFile: filePath, stdin: stdinOption} : {stdin: [{file: filePath}, stdinOption]}; options.stdin = reversed ? options.stdin.reverse() : options.stdin; const {stdout} = await execaMethod('stdin-fd.js', ['0'], options); t.is(stdout, foobarUppercase); await rm(filePath); }; test('Can use generators with a file as input', testInputFile, uppercaseGenerator(), false, false, execa); test('Can use generators with a file as input, reversed', testInputFile, uppercaseGenerator(), false, true, execa); test('Can use generators with inputFile option', testInputFile, uppercaseGenerator(), true, false, execa); test('Can use generators with a file as input, sync', testInputFile, uppercaseGenerator(), false, false, execaSync); test('Can use generators with a file as input, reversed, sync', testInputFile, uppercaseGenerator(), false, true, execaSync); test('Can use generators with inputFile option, sync', testInputFile, uppercaseGenerator(), true, false, execaSync); test('Can use duplexes with a file as input', testInputFile, uppercaseBufferDuplex(), false, false, execa); test('Can use duplexes with a file as input, reversed', testInputFile, uppercaseBufferDuplex(), false, true, execa); test('Can use duplexes with inputFile option', testInputFile, uppercaseBufferDuplex(), true, false, execa); test('Can use webTransforms with a file as input', testInputFile, uppercaseBufferWebTransform(), false, false, execa); test('Can use webTransforms with a file as input, reversed', testInputFile, uppercaseBufferWebTransform(), false, true, execa); test('Can use webTransforms with inputFile option', testInputFile, uppercaseBufferWebTransform(), true, false, execa); const testOutputFile = async (t, reversed, type, execaMethod) => { const filePath = tempfile(); const stdoutOption = [generatorsMap[type].uppercaseBuffer(false, true), {file: filePath}]; const reversedStdoutOption = reversed ? stdoutOption.reverse() : stdoutOption; const {stdout} = await execaMethod('noop-fd.js', ['1'], {stdout: reversedStdoutOption}); t.is(stdout, foobarUppercase); t.is(await readFile(filePath, 'utf8'), foobarUppercase); await rm(filePath); }; test('Can use generators with a file as output', testOutputFile, false, 'generator', execa); test('Can use generators with a file as output, reversed', testOutputFile, true, 'generator', execa); test('Can use generators with a file as output, sync', testOutputFile, false, 'generator', execaSync); test('Can use generators with a file as output, reversed, sync', testOutputFile, true, 'generator', execaSync); test('Can use duplexes with a file as output', testOutputFile, false, 'duplex', execa); test('Can use duplexes with a file as output, reversed', testOutputFile, true, 'duplex', execa); test('Can use webTransforms with a file as output', testOutputFile, false, 'webTransform', execa); test('Can use webTransforms with a file as output, reversed', testOutputFile, true, 'webTransform', execa); const testWritableDestination = async (t, type) => { const passThrough = new PassThrough(); const [{stdout}, streamOutput] = await Promise.all([ execa('noop-fd.js', ['1', foobarString], {stdout: [generatorsMap[type].uppercaseBuffer(false, true), passThrough]}), getStream(passThrough), ]); t.is(stdout, foobarUppercase); t.is(streamOutput, foobarUppercase); }; test('Can use generators to a Writable stream', testWritableDestination, 'generator'); test('Can use duplexes to a Writable stream', testWritableDestination, 'duplex'); test('Can use webTransforms to a Writable stream', testWritableDestination, 'webTransform'); const testReadableSource = async (t, type) => { const passThrough = new PassThrough(); const subprocess = execa('stdin-fd.js', ['0'], {stdin: [passThrough, generatorsMap[type].uppercase()]}); passThrough.end(foobarString); const {stdout} = await subprocess; t.is(stdout, foobarUppercase); }; test('Can use generators from a Readable stream', testReadableSource, 'generator'); test('Can use duplexes from a Readable stream', testReadableSource, 'duplex'); test('Can use webTransforms from a Readable stream', testReadableSource, 'webTransform'); const testInherit = async (t, type) => { const {stdout} = await execa('nested-inherit.js', [type]); t.is(stdout, foobarUppercase); }; test('Can use generators with "inherit"', testInherit, 'generator'); test('Can use duplexes with "inherit"', testInherit, 'duplex'); test('Can use webTransforms with "inherit"', testInherit, 'webTransform'); ================================================ FILE: test/transform/generator-output.js ================================================ import test from 'ava'; import getStream, {getStreamAsArray} from 'get-stream'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import {foobarString, foobarUppercase, foobarObject} from '../helpers/input.js'; import {generatorsMap} from '../helpers/map.js'; setFixtureDirectory(); const getOutputObjectMode = (objectMode, addNoopTransform, type, binary) => objectMode ? { generators: generatorsMap[type].addNoop(generatorsMap[type].outputObject(), addNoopTransform, objectMode, binary), output: [foobarObject], getStreamMethod: getStreamAsArray, } : { generators: generatorsMap[type].addNoop(generatorsMap[type].uppercaseBuffer(objectMode, true), addNoopTransform, objectMode, binary), output: foobarUppercase, getStreamMethod: getStream, }; // eslint-disable-next-line max-params const testGeneratorOutput = async (t, fdNumber, reject, useShortcutProperty, objectMode, addNoopTransform, type, execaMethod) => { const {generators, output} = getOutputObjectMode(objectMode, addNoopTransform, type); const fixtureName = reject ? 'noop-fd.js' : 'noop-fail.js'; const {stdout, stderr, stdio} = await execaMethod(fixtureName, [`${fdNumber}`, foobarString], {...getStdio(fdNumber, generators), reject}); const result = useShortcutProperty ? [stdout, stderr][fdNumber - 1] : stdio[fdNumber]; t.deepEqual(result, output); }; test('Can use generators with result.stdio[1]', testGeneratorOutput, 1, true, false, false, false, 'generator', execa); test('Can use generators with result.stdout', testGeneratorOutput, 1, true, true, false, false, 'generator', execa); test('Can use generators with result.stdio[2]', testGeneratorOutput, 2, true, false, false, false, 'generator', execa); test('Can use generators with result.stderr', testGeneratorOutput, 2, true, true, false, false, 'generator', execa); test('Can use generators with result.stdio[*] as output', testGeneratorOutput, 3, true, false, false, false, 'generator', execa); test('Can use generators with error.stdio[1]', testGeneratorOutput, 1, false, false, false, false, 'generator', execa); test('Can use generators with error.stdout', testGeneratorOutput, 1, false, true, false, false, 'generator', execa); test('Can use generators with error.stdio[2]', testGeneratorOutput, 2, false, false, false, false, 'generator', execa); test('Can use generators with error.stderr', testGeneratorOutput, 2, false, true, false, false, 'generator', execa); test('Can use generators with error.stdio[*] as output', testGeneratorOutput, 3, false, false, false, false, 'generator', execa); test('Can use generators with result.stdio[1], objectMode', testGeneratorOutput, 1, true, false, true, false, 'generator', execa); test('Can use generators with result.stdout, objectMode', testGeneratorOutput, 1, true, true, true, false, 'generator', execa); test('Can use generators with result.stdio[2], objectMode', testGeneratorOutput, 2, true, false, true, false, 'generator', execa); test('Can use generators with result.stderr, objectMode', testGeneratorOutput, 2, true, true, true, false, 'generator', execa); test('Can use generators with result.stdio[*] as output, objectMode', testGeneratorOutput, 3, true, false, true, false, 'generator', execa); test('Can use generators with error.stdio[1], objectMode', testGeneratorOutput, 1, false, false, true, false, 'generator', execa); test('Can use generators with error.stdout, objectMode', testGeneratorOutput, 1, false, true, true, false, 'generator', execa); test('Can use generators with error.stdio[2], objectMode', testGeneratorOutput, 2, false, false, true, false, 'generator', execa); test('Can use generators with error.stderr, objectMode', testGeneratorOutput, 2, false, true, true, false, 'generator', execa); test('Can use generators with error.stdio[*] as output, objectMode', testGeneratorOutput, 3, false, false, true, false, 'generator', execa); test('Can use generators with result.stdio[1], noop transform', testGeneratorOutput, 1, true, false, false, true, 'generator', execa); test('Can use generators with result.stdout, noop transform', testGeneratorOutput, 1, true, true, false, true, 'generator', execa); test('Can use generators with result.stdio[2], noop transform', testGeneratorOutput, 2, true, false, false, true, 'generator', execa); test('Can use generators with result.stderr, noop transform', testGeneratorOutput, 2, true, true, false, true, 'generator', execa); test('Can use generators with result.stdio[*] as output, noop transform', testGeneratorOutput, 3, true, false, false, true, 'generator', execa); test('Can use generators with error.stdio[1], noop transform', testGeneratorOutput, 1, false, false, false, true, 'generator', execa); test('Can use generators with error.stdout, noop transform', testGeneratorOutput, 1, false, true, false, true, 'generator', execa); test('Can use generators with error.stdio[2], noop transform', testGeneratorOutput, 2, false, false, false, true, 'generator', execa); test('Can use generators with error.stderr, noop transform', testGeneratorOutput, 2, false, true, false, true, 'generator', execa); test('Can use generators with error.stdio[*] as output, noop transform', testGeneratorOutput, 3, false, false, false, true, 'generator', execa); test('Can use generators with result.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, true, false, true, true, 'generator', execa); test('Can use generators with result.stdout, objectMode, noop transform', testGeneratorOutput, 1, true, true, true, true, 'generator', execa); test('Can use generators with result.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, true, false, true, true, 'generator', execa); test('Can use generators with result.stderr, objectMode, noop transform', testGeneratorOutput, 2, true, true, true, true, 'generator', execa); test('Can use generators with result.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, true, false, true, true, 'generator', execa); test('Can use generators with error.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, false, false, true, true, 'generator', execa); test('Can use generators with error.stdout, objectMode, noop transform', testGeneratorOutput, 1, false, true, true, true, 'generator', execa); test('Can use generators with error.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, false, false, true, true, 'generator', execa); test('Can use generators with error.stderr, objectMode, noop transform', testGeneratorOutput, 2, false, true, true, true, 'generator', execa); test('Can use generators with error.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, false, false, true, true, 'generator', execa); test('Can use generators with result.stdio[1], sync', testGeneratorOutput, 1, true, false, false, false, 'generator', execaSync); test('Can use generators with result.stdout, sync', testGeneratorOutput, 1, true, true, false, false, 'generator', execaSync); test('Can use generators with result.stdio[2], sync', testGeneratorOutput, 2, true, false, false, false, 'generator', execaSync); test('Can use generators with result.stderr, sync', testGeneratorOutput, 2, true, true, false, false, 'generator', execaSync); test('Can use generators with result.stdio[*] as output, sync', testGeneratorOutput, 3, true, false, false, false, 'generator', execaSync); test('Can use generators with error.stdio[1], sync', testGeneratorOutput, 1, false, false, false, false, 'generator', execaSync); test('Can use generators with error.stdout, sync', testGeneratorOutput, 1, false, true, false, false, 'generator', execaSync); test('Can use generators with error.stdio[2], sync', testGeneratorOutput, 2, false, false, false, false, 'generator', execaSync); test('Can use generators with error.stderr, sync', testGeneratorOutput, 2, false, true, false, false, 'generator', execaSync); test('Can use generators with error.stdio[*] as output, sync', testGeneratorOutput, 3, false, false, false, false, 'generator', execaSync); test('Can use generators with result.stdio[1], objectMode, sync', testGeneratorOutput, 1, true, false, true, false, 'generator', execaSync); test('Can use generators with result.stdout, objectMode, sync', testGeneratorOutput, 1, true, true, true, false, 'generator', execaSync); test('Can use generators with result.stdio[2], objectMode, sync', testGeneratorOutput, 2, true, false, true, false, 'generator', execaSync); test('Can use generators with result.stderr, objectMode, sync', testGeneratorOutput, 2, true, true, true, false, 'generator', execaSync); test('Can use generators with result.stdio[*] as output, objectMode, sync', testGeneratorOutput, 3, true, false, true, false, 'generator', execaSync); test('Can use generators with error.stdio[1], objectMode, sync', testGeneratorOutput, 1, false, false, true, false, 'generator', execaSync); test('Can use generators with error.stdout, objectMode, sync', testGeneratorOutput, 1, false, true, true, false, 'generator', execaSync); test('Can use generators with error.stdio[2], objectMode, sync', testGeneratorOutput, 2, false, false, true, false, 'generator', execaSync); test('Can use generators with error.stderr, objectMode, sync', testGeneratorOutput, 2, false, true, true, false, 'generator', execaSync); test('Can use generators with error.stdio[*] as output, objectMode, sync', testGeneratorOutput, 3, false, false, true, false, 'generator', execaSync); test('Can use generators with result.stdio[1], noop transform, sync', testGeneratorOutput, 1, true, false, false, true, 'generator', execaSync); test('Can use generators with result.stdout, noop transform, sync', testGeneratorOutput, 1, true, true, false, true, 'generator', execaSync); test('Can use generators with result.stdio[2], noop transform, sync', testGeneratorOutput, 2, true, false, false, true, 'generator', execaSync); test('Can use generators with result.stderr, noop transform, sync', testGeneratorOutput, 2, true, true, false, true, 'generator', execaSync); test('Can use generators with result.stdio[*] as output, noop transform, sync', testGeneratorOutput, 3, true, false, false, true, 'generator', execaSync); test('Can use generators with error.stdio[1], noop transform, sync', testGeneratorOutput, 1, false, false, false, true, 'generator', execaSync); test('Can use generators with error.stdout, noop transform, sync', testGeneratorOutput, 1, false, true, false, true, 'generator', execaSync); test('Can use generators with error.stdio[2], noop transform, sync', testGeneratorOutput, 2, false, false, false, true, 'generator', execaSync); test('Can use generators with error.stderr, noop transform, sync', testGeneratorOutput, 2, false, true, false, true, 'generator', execaSync); test('Can use generators with error.stdio[*] as output, noop transform, sync', testGeneratorOutput, 3, false, false, false, true, 'generator', execaSync); test('Can use generators with result.stdio[1], objectMode, noop transform, sync', testGeneratorOutput, 1, true, false, true, true, 'generator', execaSync); test('Can use generators with result.stdout, objectMode, noop transform, sync', testGeneratorOutput, 1, true, true, true, true, 'generator', execaSync); test('Can use generators with result.stdio[2], objectMode, noop transform, sync', testGeneratorOutput, 2, true, false, true, true, 'generator', execaSync); test('Can use generators with result.stderr, objectMode, noop transform, sync', testGeneratorOutput, 2, true, true, true, true, 'generator', execaSync); test('Can use generators with result.stdio[*] as output, objectMode, noop transform, sync', testGeneratorOutput, 3, true, false, true, true, 'generator', execaSync); test('Can use generators with error.stdio[1], objectMode, noop transform, sync', testGeneratorOutput, 1, false, false, true, true, 'generator', execaSync); test('Can use generators with error.stdout, objectMode, noop transform, sync', testGeneratorOutput, 1, false, true, true, true, 'generator', execaSync); test('Can use generators with error.stdio[2], objectMode, noop transform, sync', testGeneratorOutput, 2, false, false, true, true, 'generator', execaSync); test('Can use generators with error.stderr, objectMode, noop transform, sync', testGeneratorOutput, 2, false, true, true, true, 'generator', execaSync); test('Can use generators with error.stdio[*] as output, objectMode, noop transform, sync', testGeneratorOutput, 3, false, false, true, true, 'generator', execaSync); test('Can use duplexes with result.stdio[1]', testGeneratorOutput, 1, true, false, false, false, 'duplex', execa); test('Can use duplexes with result.stdout', testGeneratorOutput, 1, true, true, false, false, 'duplex', execa); test('Can use duplexes with result.stdio[2]', testGeneratorOutput, 2, true, false, false, false, 'duplex', execa); test('Can use duplexes with result.stderr', testGeneratorOutput, 2, true, true, false, false, 'duplex', execa); test('Can use duplexes with result.stdio[*] as output', testGeneratorOutput, 3, true, false, false, false, 'duplex', execa); test('Can use duplexes with error.stdio[1]', testGeneratorOutput, 1, false, false, false, false, 'duplex', execa); test('Can use duplexes with error.stdout', testGeneratorOutput, 1, false, true, false, false, 'duplex', execa); test('Can use duplexes with error.stdio[2]', testGeneratorOutput, 2, false, false, false, false, 'duplex', execa); test('Can use duplexes with error.stderr', testGeneratorOutput, 2, false, true, false, false, 'duplex', execa); test('Can use duplexes with error.stdio[*] as output', testGeneratorOutput, 3, false, false, false, false, 'duplex', execa); test('Can use duplexes with result.stdio[1], objectMode', testGeneratorOutput, 1, true, false, true, false, 'duplex', execa); test('Can use duplexes with result.stdout, objectMode', testGeneratorOutput, 1, true, true, true, false, 'duplex', execa); test('Can use duplexes with result.stdio[2], objectMode', testGeneratorOutput, 2, true, false, true, false, 'duplex', execa); test('Can use duplexes with result.stderr, objectMode', testGeneratorOutput, 2, true, true, true, false, 'duplex', execa); test('Can use duplexes with result.stdio[*] as output, objectMode', testGeneratorOutput, 3, true, false, true, false, 'duplex', execa); test('Can use duplexes with error.stdio[1], objectMode', testGeneratorOutput, 1, false, false, true, false, 'duplex', execa); test('Can use duplexes with error.stdout, objectMode', testGeneratorOutput, 1, false, true, true, false, 'duplex', execa); test('Can use duplexes with error.stdio[2], objectMode', testGeneratorOutput, 2, false, false, true, false, 'duplex', execa); test('Can use duplexes with error.stderr, objectMode', testGeneratorOutput, 2, false, true, true, false, 'duplex', execa); test('Can use duplexes with error.stdio[*] as output, objectMode', testGeneratorOutput, 3, false, false, true, false, 'duplex', execa); test('Can use duplexes with result.stdio[1], noop transform', testGeneratorOutput, 1, true, false, false, true, 'duplex', execa); test('Can use duplexes with result.stdout, noop transform', testGeneratorOutput, 1, true, true, false, true, 'duplex', execa); test('Can use duplexes with result.stdio[2], noop transform', testGeneratorOutput, 2, true, false, false, true, 'duplex', execa); test('Can use duplexes with result.stderr, noop transform', testGeneratorOutput, 2, true, true, false, true, 'duplex', execa); test('Can use duplexes with result.stdio[*] as output, noop transform', testGeneratorOutput, 3, true, false, false, true, 'duplex', execa); test('Can use duplexes with error.stdio[1], noop transform', testGeneratorOutput, 1, false, false, false, true, 'duplex', execa); test('Can use duplexes with error.stdout, noop transform', testGeneratorOutput, 1, false, true, false, true, 'duplex', execa); test('Can use duplexes with error.stdio[2], noop transform', testGeneratorOutput, 2, false, false, false, true, 'duplex', execa); test('Can use duplexes with error.stderr, noop transform', testGeneratorOutput, 2, false, true, false, true, 'duplex', execa); test('Can use duplexes with error.stdio[*] as output, noop transform', testGeneratorOutput, 3, false, false, false, true, 'duplex', execa); test('Can use duplexes with result.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, true, false, true, true, 'duplex', execa); test('Can use duplexes with result.stdout, objectMode, noop transform', testGeneratorOutput, 1, true, true, true, true, 'duplex', execa); test('Can use duplexes with result.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, true, false, true, true, 'duplex', execa); test('Can use duplexes with result.stderr, objectMode, noop transform', testGeneratorOutput, 2, true, true, true, true, 'duplex', execa); test('Can use duplexes with result.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, true, false, true, true, 'duplex', execa); test('Can use duplexes with error.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, false, false, true, true, 'duplex', execa); test('Can use duplexes with error.stdout, objectMode, noop transform', testGeneratorOutput, 1, false, true, true, true, 'duplex', execa); test('Can use duplexes with error.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, false, false, true, true, 'duplex', execa); test('Can use duplexes with error.stderr, objectMode, noop transform', testGeneratorOutput, 2, false, true, true, true, 'duplex', execa); test('Can use duplexes with error.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, false, false, true, true, 'duplex', execa); test('Can use webTransforms with result.stdio[1]', testGeneratorOutput, 1, true, false, false, false, 'webTransform', execa); test('Can use webTransforms with result.stdout', testGeneratorOutput, 1, true, true, false, false, 'webTransform', execa); test('Can use webTransforms with result.stdio[2]', testGeneratorOutput, 2, true, false, false, false, 'webTransform', execa); test('Can use webTransforms with result.stderr', testGeneratorOutput, 2, true, true, false, false, 'webTransform', execa); test('Can use webTransforms with result.stdio[*] as output', testGeneratorOutput, 3, true, false, false, false, 'webTransform', execa); test('Can use webTransforms with error.stdio[1]', testGeneratorOutput, 1, false, false, false, false, 'webTransform', execa); test('Can use webTransforms with error.stdout', testGeneratorOutput, 1, false, true, false, false, 'webTransform', execa); test('Can use webTransforms with error.stdio[2]', testGeneratorOutput, 2, false, false, false, false, 'webTransform', execa); test('Can use webTransforms with error.stderr', testGeneratorOutput, 2, false, true, false, false, 'webTransform', execa); test('Can use webTransforms with error.stdio[*] as output', testGeneratorOutput, 3, false, false, false, false, 'webTransform', execa); test('Can use webTransforms with result.stdio[1], objectMode', testGeneratorOutput, 1, true, false, true, false, 'webTransform', execa); test('Can use webTransforms with result.stdout, objectMode', testGeneratorOutput, 1, true, true, true, false, 'webTransform', execa); test('Can use webTransforms with result.stdio[2], objectMode', testGeneratorOutput, 2, true, false, true, false, 'webTransform', execa); test('Can use webTransforms with result.stderr, objectMode', testGeneratorOutput, 2, true, true, true, false, 'webTransform', execa); test('Can use webTransforms with result.stdio[*] as output, objectMode', testGeneratorOutput, 3, true, false, true, false, 'webTransform', execa); test('Can use webTransforms with error.stdio[1], objectMode', testGeneratorOutput, 1, false, false, true, false, 'webTransform', execa); test('Can use webTransforms with error.stdout, objectMode', testGeneratorOutput, 1, false, true, true, false, 'webTransform', execa); test('Can use webTransforms with error.stdio[2], objectMode', testGeneratorOutput, 2, false, false, true, false, 'webTransform', execa); test('Can use webTransforms with error.stderr, objectMode', testGeneratorOutput, 2, false, true, true, false, 'webTransform', execa); test('Can use webTransforms with error.stdio[*] as output, objectMode', testGeneratorOutput, 3, false, false, true, false, 'webTransform', execa); test('Can use webTransforms with result.stdio[1], noop transform', testGeneratorOutput, 1, true, false, false, true, 'webTransform', execa); test('Can use webTransforms with result.stdout, noop transform', testGeneratorOutput, 1, true, true, false, true, 'webTransform', execa); test('Can use webTransforms with result.stdio[2], noop transform', testGeneratorOutput, 2, true, false, false, true, 'webTransform', execa); test('Can use webTransforms with result.stderr, noop transform', testGeneratorOutput, 2, true, true, false, true, 'webTransform', execa); test('Can use webTransforms with result.stdio[*] as output, noop transform', testGeneratorOutput, 3, true, false, false, true, 'webTransform', execa); test('Can use webTransforms with error.stdio[1], noop transform', testGeneratorOutput, 1, false, false, false, true, 'webTransform', execa); test('Can use webTransforms with error.stdout, noop transform', testGeneratorOutput, 1, false, true, false, true, 'webTransform', execa); test('Can use webTransforms with error.stdio[2], noop transform', testGeneratorOutput, 2, false, false, false, true, 'webTransform', execa); test('Can use webTransforms with error.stderr, noop transform', testGeneratorOutput, 2, false, true, false, true, 'webTransform', execa); test('Can use webTransforms with error.stdio[*] as output, noop transform', testGeneratorOutput, 3, false, false, false, true, 'webTransform', execa); test('Can use webTransforms with result.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, true, false, true, true, 'webTransform', execa); test('Can use webTransforms with result.stdout, objectMode, noop transform', testGeneratorOutput, 1, true, true, true, true, 'webTransform', execa); test('Can use webTransforms with result.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, true, false, true, true, 'webTransform', execa); test('Can use webTransforms with result.stderr, objectMode, noop transform', testGeneratorOutput, 2, true, true, true, true, 'webTransform', execa); test('Can use webTransforms with result.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, true, false, true, true, 'webTransform', execa); test('Can use webTransforms with error.stdio[1], objectMode, noop transform', testGeneratorOutput, 1, false, false, true, true, 'webTransform', execa); test('Can use webTransforms with error.stdout, objectMode, noop transform', testGeneratorOutput, 1, false, true, true, true, 'webTransform', execa); test('Can use webTransforms with error.stdio[2], objectMode, noop transform', testGeneratorOutput, 2, false, false, true, true, 'webTransform', execa); test('Can use webTransforms with error.stderr, objectMode, noop transform', testGeneratorOutput, 2, false, true, true, true, 'webTransform', execa); test('Can use webTransforms with error.stdio[*] as output, objectMode, noop transform', testGeneratorOutput, 3, false, false, true, true, 'webTransform', execa); // eslint-disable-next-line max-params const testGeneratorOutputPipe = async (t, fdNumber, useShortcutProperty, objectMode, addNoopTransform, type) => { const {generators, output, getStreamMethod} = getOutputObjectMode(objectMode, addNoopTransform, type, true); const subprocess = execa('noop-fd.js', [`${fdNumber}`, foobarString], getStdio(fdNumber, generators)); const stream = useShortcutProperty ? [subprocess.stdout, subprocess.stderr][fdNumber - 1] : subprocess.stdio[fdNumber]; const [result] = await Promise.all([getStreamMethod(stream), subprocess]); t.deepEqual(result, output); }; test('Can use generators with subprocess.stdio[1]', testGeneratorOutputPipe, 1, false, false, false, 'generator'); test('Can use generators with subprocess.stdout', testGeneratorOutputPipe, 1, true, false, false, 'generator'); test('Can use generators with subprocess.stdio[2]', testGeneratorOutputPipe, 2, false, false, false, 'generator'); test('Can use generators with subprocess.stderr', testGeneratorOutputPipe, 2, true, false, false, 'generator'); test('Can use generators with subprocess.stdio[*] as output', testGeneratorOutputPipe, 3, false, false, false, 'generator'); test('Can use generators with subprocess.stdio[1], objectMode', testGeneratorOutputPipe, 1, false, true, false, 'generator'); test('Can use generators with subprocess.stdout, objectMode', testGeneratorOutputPipe, 1, true, true, false, 'generator'); test('Can use generators with subprocess.stdio[2], objectMode', testGeneratorOutputPipe, 2, false, true, false, 'generator'); test('Can use generators with subprocess.stderr, objectMode', testGeneratorOutputPipe, 2, true, true, false, 'generator'); test('Can use generators with subprocess.stdio[*] as output, objectMode', testGeneratorOutputPipe, 3, false, true, false, 'generator'); test('Can use generators with subprocess.stdio[1], noop transform', testGeneratorOutputPipe, 1, false, false, true, 'generator'); test('Can use generators with subprocess.stdout, noop transform', testGeneratorOutputPipe, 1, true, false, true, 'generator'); test('Can use generators with subprocess.stdio[2], noop transform', testGeneratorOutputPipe, 2, false, false, true, 'generator'); test('Can use generators with subprocess.stderr, noop transform', testGeneratorOutputPipe, 2, true, false, true, 'generator'); test('Can use generators with subprocess.stdio[*] as output, noop transform', testGeneratorOutputPipe, 3, false, false, true, 'generator'); test('Can use generators with subprocess.stdio[1], objectMode, noop transform', testGeneratorOutputPipe, 1, false, true, true, 'generator'); test('Can use generators with subprocess.stdout, objectMode, noop transform', testGeneratorOutputPipe, 1, true, true, true, 'generator'); test('Can use generators with subprocess.stdio[2], objectMode, noop transform', testGeneratorOutputPipe, 2, false, true, true, 'generator'); test('Can use generators with subprocess.stderr, objectMode, noop transform', testGeneratorOutputPipe, 2, true, true, true, 'generator'); test('Can use generators with subprocess.stdio[*] as output, objectMode, noop transform', testGeneratorOutputPipe, 3, false, true, true, 'generator'); test('Can use duplexes with subprocess.stdio[1]', testGeneratorOutputPipe, 1, false, false, false, 'duplex'); test('Can use duplexes with subprocess.stdout', testGeneratorOutputPipe, 1, true, false, false, 'duplex'); test('Can use duplexes with subprocess.stdio[2]', testGeneratorOutputPipe, 2, false, false, false, 'duplex'); test('Can use duplexes with subprocess.stderr', testGeneratorOutputPipe, 2, true, false, false, 'duplex'); test('Can use duplexes with subprocess.stdio[*] as output', testGeneratorOutputPipe, 3, false, false, false, 'duplex'); test('Can use duplexes with subprocess.stdio[1], objectMode', testGeneratorOutputPipe, 1, false, true, false, 'duplex'); test('Can use duplexes with subprocess.stdout, objectMode', testGeneratorOutputPipe, 1, true, true, false, 'duplex'); test('Can use duplexes with subprocess.stdio[2], objectMode', testGeneratorOutputPipe, 2, false, true, false, 'duplex'); test('Can use duplexes with subprocess.stderr, objectMode', testGeneratorOutputPipe, 2, true, true, false, 'duplex'); test('Can use duplexes with subprocess.stdio[*] as output, objectMode', testGeneratorOutputPipe, 3, false, true, false, 'duplex'); test('Can use duplexes with subprocess.stdio[1], noop transform', testGeneratorOutputPipe, 1, false, false, true, 'duplex'); test('Can use duplexes with subprocess.stdout, noop transform', testGeneratorOutputPipe, 1, true, false, true, 'duplex'); test('Can use duplexes with subprocess.stdio[2], noop transform', testGeneratorOutputPipe, 2, false, false, true, 'duplex'); test('Can use duplexes with subprocess.stderr, noop transform', testGeneratorOutputPipe, 2, true, false, true, 'duplex'); test('Can use duplexes with subprocess.stdio[*] as output, noop transform', testGeneratorOutputPipe, 3, false, false, true, 'duplex'); test('Can use duplexes with subprocess.stdio[1], objectMode, noop transform', testGeneratorOutputPipe, 1, false, true, true, 'duplex'); test('Can use duplexes with subprocess.stdout, objectMode, noop transform', testGeneratorOutputPipe, 1, true, true, true, 'duplex'); test('Can use duplexes with subprocess.stdio[2], objectMode, noop transform', testGeneratorOutputPipe, 2, false, true, true, 'duplex'); test('Can use duplexes with subprocess.stderr, objectMode, noop transform', testGeneratorOutputPipe, 2, true, true, true, 'duplex'); test('Can use duplexes with subprocess.stdio[*] as output, objectMode, noop transform', testGeneratorOutputPipe, 3, false, true, true, 'duplex'); test('Can use webTransforms with subprocess.stdio[1]', testGeneratorOutputPipe, 1, false, false, false, 'webTransform'); test('Can use webTransforms with subprocess.stdout', testGeneratorOutputPipe, 1, true, false, false, 'webTransform'); test('Can use webTransforms with subprocess.stdio[2]', testGeneratorOutputPipe, 2, false, false, false, 'webTransform'); test('Can use webTransforms with subprocess.stderr', testGeneratorOutputPipe, 2, true, false, false, 'webTransform'); test('Can use webTransforms with subprocess.stdio[*] as output', testGeneratorOutputPipe, 3, false, false, false, 'webTransform'); test('Can use webTransforms with subprocess.stdio[1], objectMode', testGeneratorOutputPipe, 1, false, true, false, 'webTransform'); test('Can use webTransforms with subprocess.stdout, objectMode', testGeneratorOutputPipe, 1, true, true, false, 'webTransform'); test('Can use webTransforms with subprocess.stdio[2], objectMode', testGeneratorOutputPipe, 2, false, true, false, 'webTransform'); test('Can use webTransforms with subprocess.stderr, objectMode', testGeneratorOutputPipe, 2, true, true, false, 'webTransform'); test('Can use webTransforms with subprocess.stdio[*] as output, objectMode', testGeneratorOutputPipe, 3, false, true, false, 'webTransform'); test('Can use webTransforms with subprocess.stdio[1], noop transform', testGeneratorOutputPipe, 1, false, false, true, 'webTransform'); test('Can use webTransforms with subprocess.stdout, noop transform', testGeneratorOutputPipe, 1, true, false, true, 'webTransform'); test('Can use webTransforms with subprocess.stdio[2], noop transform', testGeneratorOutputPipe, 2, false, false, true, 'webTransform'); test('Can use webTransforms with subprocess.stderr, noop transform', testGeneratorOutputPipe, 2, true, false, true, 'webTransform'); test('Can use webTransforms with subprocess.stdio[*] as output, noop transform', testGeneratorOutputPipe, 3, false, false, true, 'webTransform'); test('Can use webTransforms with subprocess.stdio[1], objectMode, noop transform', testGeneratorOutputPipe, 1, false, true, true, 'webTransform'); test('Can use webTransforms with subprocess.stdout, objectMode, noop transform', testGeneratorOutputPipe, 1, true, true, true, 'webTransform'); test('Can use webTransforms with subprocess.stdio[2], objectMode, noop transform', testGeneratorOutputPipe, 2, false, true, true, 'webTransform'); test('Can use webTransforms with subprocess.stderr, objectMode, noop transform', testGeneratorOutputPipe, 2, true, true, true, 'webTransform'); test('Can use webTransforms with subprocess.stdio[*] as output, objectMode, noop transform', testGeneratorOutputPipe, 3, false, true, true, 'webTransform'); ================================================ FILE: test/transform/generator-return.js ================================================ import {Buffer} from 'node:buffer'; import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarUint8Array, foobarBuffer} from '../helpers/input.js'; import {getOutputGenerator, convertTransformToFinal} from '../helpers/generator.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testGeneratorReturnType = async (t, input, encoding, reject, objectMode, final, execaMethod) => { const fixtureName = reject ? 'noop-fd.js' : 'noop-fail.js'; const {stdout} = await execaMethod(fixtureName, ['1', foobarString], { stdout: convertTransformToFinal(getOutputGenerator(input)(objectMode, true), final), encoding, reject, }); const typeofChunk = Array.isArray(stdout) ? stdout[0] : stdout; const output = Buffer.from(typeofChunk, encoding === 'buffer' || objectMode ? undefined : encoding).toString(); t.is(output, foobarString); }; test('Generator can return string with default encoding', testGeneratorReturnType, foobarString, 'utf8', true, false, false, execa); test('Generator can return Uint8Array with default encoding', testGeneratorReturnType, foobarUint8Array, 'utf8', true, false, false, execa); test('Generator can return Buffer with default encoding', testGeneratorReturnType, foobarBuffer, 'utf8', true, false, false, execa); test('Generator can return string with encoding "utf16le"', testGeneratorReturnType, foobarString, 'utf16le', true, false, false, execa); test('Generator can return Uint8Array with encoding "utf16le"', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, false, false, execa); test('Generator can return Buffer with encoding "utf16le"', testGeneratorReturnType, foobarBuffer, 'utf16le', true, false, false, execa); test('Generator can return string with encoding "buffer"', testGeneratorReturnType, foobarString, 'buffer', true, false, false, execa); test('Generator can return Uint8Array with encoding "buffer"', testGeneratorReturnType, foobarUint8Array, 'buffer', true, false, false, execa); test('Generator can return Buffer with encoding "buffer"', testGeneratorReturnType, foobarBuffer, 'buffer', true, false, false, execa); test('Generator can return string with encoding "hex"', testGeneratorReturnType, foobarString, 'hex', true, false, false, execa); test('Generator can return Uint8Array with encoding "hex"', testGeneratorReturnType, foobarUint8Array, 'hex', true, false, false, execa); test('Generator can return Buffer with encoding "hex"', testGeneratorReturnType, foobarBuffer, 'hex', true, false, false, execa); test('Generator can return string with default encoding, failure', testGeneratorReturnType, foobarString, 'utf8', false, false, false, execa); test('Generator can return Uint8Array with default encoding, failure', testGeneratorReturnType, foobarUint8Array, 'utf8', false, false, false, execa); test('Generator can return string with encoding "utf16le", failure', testGeneratorReturnType, foobarString, 'utf16le', false, false, false, execa); test('Generator can return Uint8Array with encoding "utf16le", failure', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, false, false, execa); test('Generator can return string with encoding "buffer", failure', testGeneratorReturnType, foobarString, 'buffer', false, false, false, execa); test('Generator can return Uint8Array with encoding "buffer", failure', testGeneratorReturnType, foobarUint8Array, 'buffer', false, false, false, execa); test('Generator can return string with encoding "hex", failure', testGeneratorReturnType, foobarString, 'hex', false, false, false, execa); test('Generator can return Uint8Array with encoding "hex", failure', testGeneratorReturnType, foobarUint8Array, 'hex', false, false, false, execa); test('Generator can return string with default encoding, objectMode', testGeneratorReturnType, foobarString, 'utf8', true, true, false, execa); test('Generator can return Uint8Array with default encoding, objectMode', testGeneratorReturnType, foobarUint8Array, 'utf8', true, true, false, execa); test('Generator can return string with encoding "utf16le", objectMode', testGeneratorReturnType, foobarString, 'utf16le', true, true, false, execa); test('Generator can return Uint8Array with encoding "utf16le", objectMode', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, true, false, execa); test('Generator can return string with encoding "buffer", objectMode', testGeneratorReturnType, foobarString, 'buffer', true, true, false, execa); test('Generator can return Uint8Array with encoding "buffer", objectMode', testGeneratorReturnType, foobarUint8Array, 'buffer', true, true, false, execa); test('Generator can return string with encoding "hex", objectMode', testGeneratorReturnType, foobarString, 'hex', true, true, false, execa); test('Generator can return Uint8Array with encoding "hex", objectMode', testGeneratorReturnType, foobarUint8Array, 'hex', true, true, false, execa); test('Generator can return string with default encoding, objectMode, failure', testGeneratorReturnType, foobarString, 'utf8', false, true, false, execa); test('Generator can return Uint8Array with default encoding, objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'utf8', false, true, false, execa); test('Generator can return string with encoding "utf16le", objectMode, failure', testGeneratorReturnType, foobarString, 'utf16le', false, true, false, execa); test('Generator can return Uint8Array with encoding "utf16le", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, true, false, execa); test('Generator can return string with encoding "buffer", objectMode, failure', testGeneratorReturnType, foobarString, 'buffer', false, true, false, execa); test('Generator can return Uint8Array with encoding "buffer", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'buffer', false, true, false, execa); test('Generator can return string with encoding "hex", objectMode, failure', testGeneratorReturnType, foobarString, 'hex', false, true, false, execa); test('Generator can return Uint8Array with encoding "hex", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'hex', false, true, false, execa); test('Generator can return final string with default encoding', testGeneratorReturnType, foobarString, 'utf8', true, false, true, execa); test('Generator can return final Uint8Array with default encoding', testGeneratorReturnType, foobarUint8Array, 'utf8', true, false, true, execa); test('Generator can return final string with encoding "utf16le"', testGeneratorReturnType, foobarString, 'utf16le', true, false, true, execa); test('Generator can return final Uint8Array with encoding "utf16le"', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, false, true, execa); test('Generator can return final string with encoding "buffer"', testGeneratorReturnType, foobarString, 'buffer', true, false, true, execa); test('Generator can return final Uint8Array with encoding "buffer"', testGeneratorReturnType, foobarUint8Array, 'buffer', true, false, true, execa); test('Generator can return final string with encoding "hex"', testGeneratorReturnType, foobarString, 'hex', true, false, true, execa); test('Generator can return final Uint8Array with encoding "hex"', testGeneratorReturnType, foobarUint8Array, 'hex', true, false, true, execa); test('Generator can return final string with default encoding, failure', testGeneratorReturnType, foobarString, 'utf8', false, false, true, execa); test('Generator can return final Uint8Array with default encoding, failure', testGeneratorReturnType, foobarUint8Array, 'utf8', false, false, true, execa); test('Generator can return final string with encoding "utf16le", failure', testGeneratorReturnType, foobarString, 'utf16le', false, false, true, execa); test('Generator can return final Uint8Array with encoding "utf16le", failure', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, false, true, execa); test('Generator can return final string with encoding "buffer", failure', testGeneratorReturnType, foobarString, 'buffer', false, false, true, execa); test('Generator can return final Uint8Array with encoding "buffer", failure', testGeneratorReturnType, foobarUint8Array, 'buffer', false, false, true, execa); test('Generator can return final string with encoding "hex", failure', testGeneratorReturnType, foobarString, 'hex', false, false, true, execa); test('Generator can return final Uint8Array with encoding "hex", failure', testGeneratorReturnType, foobarUint8Array, 'hex', false, false, true, execa); test('Generator can return final string with default encoding, objectMode', testGeneratorReturnType, foobarString, 'utf8', true, true, true, execa); test('Generator can return final Uint8Array with default encoding, objectMode', testGeneratorReturnType, foobarUint8Array, 'utf8', true, true, true, execa); test('Generator can return final string with encoding "utf16le", objectMode', testGeneratorReturnType, foobarString, 'utf16le', true, true, true, execa); test('Generator can return final Uint8Array with encoding "utf16le", objectMode', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, true, true, execa); test('Generator can return final string with encoding "buffer", objectMode', testGeneratorReturnType, foobarString, 'buffer', true, true, true, execa); test('Generator can return final Uint8Array with encoding "buffer", objectMode', testGeneratorReturnType, foobarUint8Array, 'buffer', true, true, true, execa); test('Generator can return final string with encoding "hex", objectMode', testGeneratorReturnType, foobarString, 'hex', true, true, true, execa); test('Generator can return final Uint8Array with encoding "hex", objectMode', testGeneratorReturnType, foobarUint8Array, 'hex', true, true, true, execa); test('Generator can return final string with default encoding, objectMode, failure', testGeneratorReturnType, foobarString, 'utf8', false, true, true, execa); test('Generator can return final Uint8Array with default encoding, objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'utf8', false, true, true, execa); test('Generator can return final string with encoding "utf16le", objectMode, failure', testGeneratorReturnType, foobarString, 'utf16le', false, true, true, execa); test('Generator can return final Uint8Array with encoding "utf16le", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, true, true, execa); test('Generator can return final string with encoding "buffer", objectMode, failure', testGeneratorReturnType, foobarString, 'buffer', false, true, true, execa); test('Generator can return final Uint8Array with encoding "buffer", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'buffer', false, true, true, execa); test('Generator can return final string with encoding "hex", objectMode, failure', testGeneratorReturnType, foobarString, 'hex', false, true, true, execa); test('Generator can return final Uint8Array with encoding "hex", objectMode, failure', testGeneratorReturnType, foobarUint8Array, 'hex', false, true, true, execa); test('Generator can return string with default encoding, sync', testGeneratorReturnType, foobarString, 'utf8', true, false, false, execaSync); test('Generator can return Uint8Array with default encoding, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', true, false, false, execaSync); test('Generator can return Buffer with default encoding, sync', testGeneratorReturnType, foobarBuffer, 'utf8', true, false, false, execaSync); test('Generator can return string with encoding "utf16le", sync', testGeneratorReturnType, foobarString, 'utf16le', true, false, false, execaSync); test('Generator can return Uint8Array with encoding "utf16le", sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, false, false, execaSync); test('Generator can return Buffer with encoding "utf16le", sync', testGeneratorReturnType, foobarBuffer, 'utf16le', true, false, false, execaSync); test('Generator can return string with encoding "buffer", sync', testGeneratorReturnType, foobarString, 'buffer', true, false, false, execaSync); test('Generator can return Uint8Array with encoding "buffer", sync', testGeneratorReturnType, foobarUint8Array, 'buffer', true, false, false, execaSync); test('Generator can return Buffer with encoding "buffer", sync', testGeneratorReturnType, foobarBuffer, 'buffer', true, false, false, execaSync); test('Generator can return string with encoding "hex", sync', testGeneratorReturnType, foobarString, 'hex', true, false, false, execaSync); test('Generator can return Uint8Array with encoding "hex", sync', testGeneratorReturnType, foobarUint8Array, 'hex', true, false, false, execaSync); test('Generator can return Buffer with encoding "hex", sync', testGeneratorReturnType, foobarBuffer, 'hex', true, false, false, execaSync); test('Generator can return string with default encoding, failure, sync', testGeneratorReturnType, foobarString, 'utf8', false, false, false, execaSync); test('Generator can return Uint8Array with default encoding, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', false, false, false, execaSync); test('Generator can return string with encoding "utf16le", failure, sync', testGeneratorReturnType, foobarString, 'utf16le', false, false, false, execaSync); test('Generator can return Uint8Array with encoding "utf16le", failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, false, false, execaSync); test('Generator can return string with encoding "buffer", failure, sync', testGeneratorReturnType, foobarString, 'buffer', false, false, false, execaSync); test('Generator can return Uint8Array with encoding "buffer", failure, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', false, false, false, execaSync); test('Generator can return string with encoding "hex", failure, sync', testGeneratorReturnType, foobarString, 'hex', false, false, false, execaSync); test('Generator can return Uint8Array with encoding "hex", failure, sync', testGeneratorReturnType, foobarUint8Array, 'hex', false, false, false, execaSync); test('Generator can return string with default encoding, objectMode, sync', testGeneratorReturnType, foobarString, 'utf8', true, true, false, execaSync); test('Generator can return Uint8Array with default encoding, objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', true, true, false, execaSync); test('Generator can return string with encoding "utf16le", objectMode, sync', testGeneratorReturnType, foobarString, 'utf16le', true, true, false, execaSync); test('Generator can return Uint8Array with encoding "utf16le", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, true, false, execaSync); test('Generator can return string with encoding "buffer", objectMode, sync', testGeneratorReturnType, foobarString, 'buffer', true, true, false, execaSync); test('Generator can return Uint8Array with encoding "buffer", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', true, true, false, execaSync); test('Generator can return string with encoding "hex", objectMode, sync', testGeneratorReturnType, foobarString, 'hex', true, true, false, execaSync); test('Generator can return Uint8Array with encoding "hex", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'hex', true, true, false, execaSync); test('Generator can return string with default encoding, objectMode, failure, sync', testGeneratorReturnType, foobarString, 'utf8', false, true, false, execaSync); test('Generator can return Uint8Array with default encoding, objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', false, true, false, execaSync); test('Generator can return string with encoding "utf16le", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'utf16le', false, true, false, execaSync); test('Generator can return Uint8Array with encoding "utf16le", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, true, false, execaSync); test('Generator can return string with encoding "buffer", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'buffer', false, true, false, execaSync); test('Generator can return Uint8Array with encoding "buffer", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', false, true, false, execaSync); test('Generator can return string with encoding "hex", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'hex', false, true, false, execaSync); test('Generator can return Uint8Array with encoding "hex", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'hex', false, true, false, execaSync); test('Generator can return final string with default encoding, sync', testGeneratorReturnType, foobarString, 'utf8', true, false, true, execaSync); test('Generator can return final Uint8Array with default encoding, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', true, false, true, execaSync); test('Generator can return final string with encoding "utf16le", sync', testGeneratorReturnType, foobarString, 'utf16le', true, false, true, execaSync); test('Generator can return final Uint8Array with encoding "utf16le", sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, false, true, execaSync); test('Generator can return final string with encoding "buffer", sync', testGeneratorReturnType, foobarString, 'buffer', true, false, true, execaSync); test('Generator can return final Uint8Array with encoding "buffer", sync', testGeneratorReturnType, foobarUint8Array, 'buffer', true, false, true, execaSync); test('Generator can return final string with encoding "hex", sync', testGeneratorReturnType, foobarString, 'hex', true, false, true, execaSync); test('Generator can return final Uint8Array with encoding "hex", sync', testGeneratorReturnType, foobarUint8Array, 'hex', true, false, true, execaSync); test('Generator can return final string with default encoding, failure, sync', testGeneratorReturnType, foobarString, 'utf8', false, false, true, execaSync); test('Generator can return final Uint8Array with default encoding, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', false, false, true, execaSync); test('Generator can return final string with encoding "utf16le", failure, sync', testGeneratorReturnType, foobarString, 'utf16le', false, false, true, execaSync); test('Generator can return final Uint8Array with encoding "utf16le", failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, false, true, execaSync); test('Generator can return final string with encoding "buffer", failure, sync', testGeneratorReturnType, foobarString, 'buffer', false, false, true, execaSync); test('Generator can return final Uint8Array with encoding "buffer", failure, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', false, false, true, execaSync); test('Generator can return final string with encoding "hex", failure, sync', testGeneratorReturnType, foobarString, 'hex', false, false, true, execaSync); test('Generator can return final Uint8Array with encoding "hex", failure, sync', testGeneratorReturnType, foobarUint8Array, 'hex', false, false, true, execaSync); test('Generator can return final string with default encoding, objectMode, sync', testGeneratorReturnType, foobarString, 'utf8', true, true, true, execaSync); test('Generator can return final Uint8Array with default encoding, objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', true, true, true, execaSync); test('Generator can return final string with encoding "utf16le", objectMode, sync', testGeneratorReturnType, foobarString, 'utf16le', true, true, true, execaSync); test('Generator can return final Uint8Array with encoding "utf16le", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', true, true, true, execaSync); test('Generator can return final string with encoding "buffer", objectMode, sync', testGeneratorReturnType, foobarString, 'buffer', true, true, true, execaSync); test('Generator can return final Uint8Array with encoding "buffer", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', true, true, true, execaSync); test('Generator can return final string with encoding "hex", objectMode, sync', testGeneratorReturnType, foobarString, 'hex', true, true, true, execaSync); test('Generator can return final Uint8Array with encoding "hex", objectMode, sync', testGeneratorReturnType, foobarUint8Array, 'hex', true, true, true, execaSync); test('Generator can return final string with default encoding, objectMode, failure, sync', testGeneratorReturnType, foobarString, 'utf8', false, true, true, execaSync); test('Generator can return final Uint8Array with default encoding, objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf8', false, true, true, execaSync); test('Generator can return final string with encoding "utf16le", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'utf16le', false, true, true, execaSync); test('Generator can return final Uint8Array with encoding "utf16le", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'utf16le', false, true, true, execaSync); test('Generator can return final string with encoding "buffer", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'buffer', false, true, true, execaSync); test('Generator can return final Uint8Array with encoding "buffer", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'buffer', false, true, true, execaSync); test('Generator can return final string with encoding "hex", objectMode, failure, sync', testGeneratorReturnType, foobarString, 'hex', false, true, true, execaSync); test('Generator can return final Uint8Array with encoding "hex", objectMode, failure, sync', testGeneratorReturnType, foobarUint8Array, 'hex', false, true, true, execaSync); ================================================ FILE: test/transform/normalize-transform.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarUppercase, foobarUint8Array} from '../helpers/input.js'; import {casedSuffix} from '../helpers/generator.js'; import {generatorsMap} from '../helpers/map.js'; setFixtureDirectory(); const testAppendInput = async (t, reversed, type, execaMethod) => { const stdin = [foobarUint8Array, generatorsMap[type].uppercase(), generatorsMap[type].append()]; const reversedStdin = reversed ? stdin.reverse() : stdin; const {stdout} = await execaMethod('stdin-fd.js', ['0'], {stdin: reversedStdin}); const reversedSuffix = reversed ? casedSuffix.toUpperCase() : casedSuffix; t.is(stdout, `${foobarUppercase}${reversedSuffix}`); }; test('Can use multiple generators as input', testAppendInput, false, 'generator', execa); test('Can use multiple generators as input, reversed', testAppendInput, true, 'generator', execa); test('Can use multiple generators as input, sync', testAppendInput, false, 'generator', execaSync); test('Can use multiple generators as input, reversed, sync', testAppendInput, true, 'generator', execaSync); test('Can use multiple duplexes as input', testAppendInput, false, 'duplex', execa); test('Can use multiple duplexes as input, reversed', testAppendInput, true, 'duplex', execa); test('Can use multiple webTransforms as input', testAppendInput, false, 'webTransform', execa); test('Can use multiple webTransforms as input, reversed', testAppendInput, true, 'webTransform', execa); const testAppendOutput = async (t, reversed, type, execaMethod) => { const stdoutOption = [generatorsMap[type].uppercase(), generatorsMap[type].append()]; const reversedStdoutOption = reversed ? stdoutOption.reverse() : stdoutOption; const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], {stdout: reversedStdoutOption}); const reversedSuffix = reversed ? casedSuffix.toUpperCase() : casedSuffix; t.is(stdout, `${foobarUppercase}${reversedSuffix}`); }; test('Can use multiple generators as output', testAppendOutput, false, 'generator', execa); test('Can use multiple generators as output, reversed', testAppendOutput, true, 'generator', execa); test('Can use multiple generators as output, sync', testAppendOutput, false, 'generator', execaSync); test('Can use multiple generators as output, reversed, sync', testAppendOutput, true, 'generator', execaSync); test('Can use multiple duplexes as output', testAppendOutput, false, 'duplex', execa); test('Can use multiple duplexes as output, reversed', testAppendOutput, true, 'duplex', execa); test('Can use multiple webTransforms as output', testAppendOutput, false, 'webTransform', execa); test('Can use multiple webTransforms as output, reversed', testAppendOutput, true, 'webTransform', execa); const testGeneratorSyntax = async (t, type, usePlainObject, execaMethod) => { const transform = generatorsMap[type].uppercase(); const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], {stdout: usePlainObject ? transform : transform.transform}); t.is(stdout, foobarUppercase); }; test('Can pass generators with an options plain object', testGeneratorSyntax, 'generator', false, execa); test('Can pass generators without an options plain object', testGeneratorSyntax, 'generator', true, execa); test('Can pass generators with an options plain object, sync', testGeneratorSyntax, 'generator', false, execaSync); test('Can pass generators without an options plain object, sync', testGeneratorSyntax, 'generator', true, execaSync); test('Can pass webTransforms with an options plain object', testGeneratorSyntax, 'webTransform', true, execa); test('Can pass webTransforms without an options plain object', testGeneratorSyntax, 'webTransform', false, execa); ================================================ FILE: test/transform/split-binary.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getOutputsGenerator, resultGenerator} from '../helpers/generator.js'; import { simpleFull, simpleChunks, simpleFullUint8Array, simpleFullUtf16Inverted, simpleFullUtf16Uint8Array, simpleChunksUint8Array, simpleFullEnd, simpleFullEndUtf16Inverted, simpleFullHex, simpleLines, noNewlinesChunks, } from '../helpers/lines.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testBinaryOption = async (t, binary, input, expectedLines, expectedOutput, objectMode, preserveNewlines, encoding, execaMethod) => { const lines = []; const {stdout} = await execaMethod('noop.js', { stdout: [ getOutputsGenerator(input)(false, true), resultGenerator(lines)(objectMode, binary, preserveNewlines), ], stripFinalNewline: false, encoding, }); t.deepEqual(lines, expectedLines); t.deepEqual(stdout, expectedOutput); }; test('Does not split lines when "binary" is true', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFull, false, true, 'utf8', execa); test('Splits lines when "binary" is false', testBinaryOption, false, simpleChunks, simpleLines, simpleFull, false, true, 'utf8', execa); test('Splits lines when "binary" is undefined', testBinaryOption, undefined, simpleChunks, simpleLines, simpleFull, false, true, 'utf8', execa); test('Does not split lines when "binary" is true, encoding "utf16le"', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUtf16Inverted, false, true, 'utf16le', execa); test('Splits lines when "binary" is false, encoding "utf16le"', testBinaryOption, false, [simpleFullUtf16Uint8Array], simpleLines, simpleFullUtf16Inverted, false, true, 'utf16le', execa); test('Splits lines when "binary" is undefined, encoding "utf16le"', testBinaryOption, undefined, [simpleFullUtf16Uint8Array], simpleLines, simpleFullUtf16Inverted, false, true, 'utf16le', execa); test('Does not split lines when "binary" is true, encoding "buffer"', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execa); test('Does not split lines when "binary" is undefined, encoding "buffer"', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execa); test('Does not split lines when "binary" is false, encoding "buffer"', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execa); test('Does not split lines when "binary" is true, encoding "hex"', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execa); test('Does not split lines when "binary" is undefined, encoding "hex"', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execa); test('Does not split lines when "binary" is false, encoding "hex"', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execa); test('Does not split lines when "binary" is true, objectMode', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleChunksUint8Array, true, true, 'utf8', execa); test('Splits lines when "binary" is false, objectMode', testBinaryOption, false, simpleChunks, simpleLines, simpleLines, true, true, 'utf8', execa); test('Splits lines when "binary" is undefined, objectMode', testBinaryOption, undefined, simpleChunks, simpleLines, simpleLines, true, true, 'utf8', execa); test('Does not split lines when "binary" is true, preserveNewlines', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFull, false, false, 'utf8', execa); test('Splits lines when "binary" is false, preserveNewlines', testBinaryOption, false, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, 'utf8', execa); test('Splits lines when "binary" is undefined, preserveNewlines', testBinaryOption, undefined, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, 'utf8', execa); test('Does not split lines when "binary" is true, preserveNewlines, encoding "utf16le"', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUtf16Inverted, false, false, 'utf16le', execa); test('Splits lines when "binary" is false, preserveNewlines, encoding "utf16le"', testBinaryOption, false, [simpleFullUtf16Uint8Array], noNewlinesChunks, simpleFullEndUtf16Inverted, false, false, 'utf16le', execa); test('Splits lines when "binary" is undefined, preserveNewlines, encoding "utf16le"', testBinaryOption, undefined, [simpleFullUtf16Uint8Array], noNewlinesChunks, simpleFullEndUtf16Inverted, false, false, 'utf16le', execa); test('Does not split lines when "binary" is true, encoding "buffer", preserveNewlines', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execa); test('Does not split lines when "binary" is undefined, encoding "buffer", preserveNewlines', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execa); test('Does not split lines when "binary" is false, encoding "buffer", preserveNewlines', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execa); test('Does not split lines when "binary" is true, objectMode, preserveNewlines', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleChunksUint8Array, true, false, 'utf8', execa); test('Does not split lines when "binary" is true, encoding "hex", preserveNewlines', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execa); test('Does not split lines when "binary" is undefined, encoding "hex", preserveNewlines', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execa); test('Does not split lines when "binary" is false, encoding "hex", preserveNewlines', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execa); test('Splits lines when "binary" is false, objectMode, preserveNewlines', testBinaryOption, false, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, 'utf8', execa); test('Splits lines when "binary" is undefined, objectMode, preserveNewlines', testBinaryOption, undefined, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, 'utf8', execa); test('Does not split lines when "binary" is true, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFull, false, true, 'utf8', execaSync); test('Splits lines when "binary" is false, sync', testBinaryOption, false, simpleChunks, simpleLines, simpleFull, false, true, 'utf8', execaSync); test('Splits lines when "binary" is undefined, sync', testBinaryOption, undefined, simpleChunks, simpleLines, simpleFull, false, true, 'utf8', execaSync); test('Does not split lines when "binary" is true, encoding "utf16le", sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUtf16Inverted, false, true, 'utf16le', execaSync); test('Splits lines when "binary" is false, encoding "utf16le", sync', testBinaryOption, false, [simpleFullUtf16Uint8Array], simpleLines, simpleFullUtf16Inverted, false, true, 'utf16le', execaSync); test('Splits lines when "binary" is undefined, encoding "utf16le", sync', testBinaryOption, undefined, [simpleFullUtf16Uint8Array], simpleLines, simpleFullUtf16Inverted, false, true, 'utf16le', execaSync); test('Does not split lines when "binary" is true, encoding "buffer", sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execaSync); test('Does not split lines when "binary" is undefined, encoding "buffer", sync', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execaSync); test('Does not split lines when "binary" is false, encoding "buffer", sync', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, true, 'buffer', execaSync); test('Does not split lines when "binary" is true, encoding "hex", sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execaSync); test('Does not split lines when "binary" is undefined, encoding "hex", sync', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execaSync); test('Does not split lines when "binary" is false, encoding "hex", sync', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, true, 'hex', execaSync); test('Does not split lines when "binary" is true, objectMode, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleChunksUint8Array, true, true, 'utf8', execaSync); test('Splits lines when "binary" is false, objectMode, sync', testBinaryOption, false, simpleChunks, simpleLines, simpleLines, true, true, 'utf8', execaSync); test('Splits lines when "binary" is undefined, objectMode, sync', testBinaryOption, undefined, simpleChunks, simpleLines, simpleLines, true, true, 'utf8', execaSync); test('Does not split lines when "binary" is true, preserveNewlines, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFull, false, false, 'utf8', execaSync); test('Splits lines when "binary" is false, preserveNewlines, sync', testBinaryOption, false, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, 'utf8', execaSync); test('Splits lines when "binary" is undefined, preserveNewlines, sync', testBinaryOption, undefined, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, 'utf8', execaSync); test('Does not split lines when "binary" is true, preserveNewlines, encoding "utf16le", sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUtf16Inverted, false, false, 'utf16le', execaSync); test('Splits lines when "binary" is false, preserveNewlines, encoding "utf16le", sync', testBinaryOption, false, [simpleFullUtf16Uint8Array], noNewlinesChunks, simpleFullEndUtf16Inverted, false, false, 'utf16le', execaSync); test('Splits lines when "binary" is undefined, preserveNewlines, encoding "utf16le", sync', testBinaryOption, undefined, [simpleFullUtf16Uint8Array], noNewlinesChunks, simpleFullEndUtf16Inverted, false, false, 'utf16le', execaSync); test('Does not split lines when "binary" is true, encoding "buffer", preserveNewlines, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execaSync); test('Does not split lines when "binary" is undefined, encoding "buffer", preserveNewlines, sync', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execaSync); test('Does not split lines when "binary" is false, encoding "buffer", preserveNewlines, sync', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullUint8Array, false, false, 'buffer', execaSync); test('Does not split lines when "binary" is true, objectMode, preserveNewlines, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleChunksUint8Array, true, false, 'utf8', execaSync); test('Does not split lines when "binary" is true, encoding "hex", preserveNewlines, sync', testBinaryOption, true, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execaSync); test('Does not split lines when "binary" is undefined, encoding "hex", preserveNewlines, sync', testBinaryOption, undefined, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execaSync); test('Does not split lines when "binary" is false, encoding "hex", preserveNewlines, sync', testBinaryOption, false, simpleChunks, simpleChunksUint8Array, simpleFullHex, false, false, 'hex', execaSync); test('Splits lines when "binary" is false, objectMode, preserveNewlines, sync', testBinaryOption, false, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, 'utf8', execaSync); test('Splits lines when "binary" is undefined, objectMode, preserveNewlines, sync', testBinaryOption, undefined, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, 'utf8', execaSync); ================================================ FILE: test/transform/split-lines.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import {getOutputsGenerator, resultGenerator} from '../helpers/generator.js'; import { singleFull, singleFullEnd, simpleFull, simpleChunks, simpleFullEnd, simpleFullEndChunks, simpleLines, simpleFullEndLines, noNewlinesFull, noNewlinesChunks, } from '../helpers/lines.js'; setFixtureDirectory(); const windowsFull = 'aaa\r\nbbb\r\nccc'; const windowsFullEnd = `${windowsFull}\r\n`; const windowsChunks = [windowsFull]; const windowsLines = ['aaa\r\n', 'bbb\r\n', 'ccc']; const noNewlinesFullEnd = `${noNewlinesFull}\n`; const noNewlinesLines = ['aaabbbccc']; const singleChunks = [singleFull]; const noLines = []; const emptyFull = ''; const emptyChunks = [emptyFull]; const manyEmptyChunks = [emptyFull, emptyFull, emptyFull]; const newlineFull = '\n'; const newlineChunks = [newlineFull]; const newlinesFull = '\n\n\n'; const newlinesChunks = [newlinesFull]; const newlinesLines = ['\n', '\n', '\n']; const windowsNewlinesFull = '\r\n\r\n\r\n'; const windowsNewlinesChunks = [windowsNewlinesFull]; const windowsNewlinesLines = ['\r\n', '\r\n', '\r\n']; const runOverChunks = ['aaa\nb', 'b', 'b\nccc']; const bigFull = '.'.repeat(1e5); const bigFullEnd = `${bigFull}\n`; const bigChunks = [bigFull]; const manyChunks = Array.from({length: 1e3}).fill('.'); const manyFull = manyChunks.join(''); const manyFullEnd = `${manyFull}\n`; const manyLines = [manyFull]; // eslint-disable-next-line max-params const testLines = async (t, fdNumber, input, expectedLines, expectedOutput, objectMode, preserveNewlines, execaMethod) => { const lines = []; const {stdio} = await execaMethod('noop-fd.js', [`${fdNumber}`], { ...getStdio(fdNumber, [ getOutputsGenerator(input)(false, true), resultGenerator(lines)(objectMode, false, preserveNewlines), ]), stripFinalNewline: false, }); t.deepEqual(lines, expectedLines); t.deepEqual(stdio[fdNumber], expectedOutput); }; test('Split stdout - n newlines, 1 chunk', testLines, 1, simpleChunks, simpleLines, simpleFull, false, true, execa); test('Split stderr - n newlines, 1 chunk', testLines, 2, simpleChunks, simpleLines, simpleFull, false, true, execa); test('Split stdio[*] - n newlines, 1 chunk', testLines, 3, simpleChunks, simpleLines, simpleFull, false, true, execa); test('Split stdout - preserveNewlines, n chunks', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesFull, false, true, execa); test('Split stdout - 0 newlines, 1 chunk', testLines, 1, singleChunks, singleChunks, singleFull, false, true, execa); test('Split stdout - empty, 1 chunk', testLines, 1, emptyChunks, noLines, emptyFull, false, true, execa); test('Split stdout - Windows newlines', testLines, 1, windowsChunks, windowsLines, windowsFull, false, true, execa); test('Split stdout - chunk ends with newline', testLines, 1, simpleFullEndChunks, simpleFullEndLines, simpleFullEnd, false, true, execa); test('Split stdout - single newline', testLines, 1, newlineChunks, newlineChunks, newlineFull, false, true, execa); test('Split stdout - only newlines', testLines, 1, newlinesChunks, newlinesLines, newlinesFull, false, true, execa); test('Split stdout - only Windows newlines', testLines, 1, windowsNewlinesChunks, windowsNewlinesLines, windowsNewlinesFull, false, true, execa); test('Split stdout - line split over multiple chunks', testLines, 1, runOverChunks, simpleLines, simpleFull, false, true, execa); test('Split stdout - 0 newlines, big line', testLines, 1, bigChunks, bigChunks, bigFull, false, true, execa); test('Split stdout - 0 newlines, many chunks', testLines, 1, manyChunks, manyLines, manyFull, false, true, execa); test('Split stdout - n newlines, 1 chunk, objectMode', testLines, 1, simpleChunks, simpleLines, simpleLines, true, true, execa); test('Split stderr - n newlines, 1 chunk, objectMode', testLines, 2, simpleChunks, simpleLines, simpleLines, true, true, execa); test('Split stdio[*] - n newlines, 1 chunk, objectMode', testLines, 3, simpleChunks, simpleLines, simpleLines, true, true, execa); test('Split stdout - preserveNewlines, n chunks, objectMode', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesLines, true, true, execa); test('Split stdout - empty, 1 chunk, objectMode', testLines, 1, emptyChunks, noLines, noLines, true, true, execa); test('Split stdout - 0 newlines, 1 chunk, objectMode', testLines, 1, singleChunks, singleChunks, singleChunks, true, true, execa); test('Split stdout - Windows newlines, objectMode', testLines, 1, windowsChunks, windowsLines, windowsLines, true, true, execa); test('Split stdout - chunk ends with newline, objectMode', testLines, 1, simpleFullEndChunks, simpleFullEndLines, simpleFullEndLines, true, true, execa); test('Split stdout - single newline, objectMode', testLines, 1, newlineChunks, newlineChunks, newlineChunks, true, true, execa); test('Split stdout - only newlines, objectMode', testLines, 1, newlinesChunks, newlinesLines, newlinesLines, true, true, execa); test('Split stdout - only Windows newlines, objectMode', testLines, 1, windowsNewlinesChunks, windowsNewlinesLines, windowsNewlinesLines, true, true, execa); test('Split stdout - line split over multiple chunks, objectMode', testLines, 1, runOverChunks, simpleLines, simpleLines, true, true, execa); test('Split stdout - 0 newlines, big line, objectMode', testLines, 1, bigChunks, bigChunks, bigChunks, true, true, execa); test('Split stdout - 0 newlines, many chunks, objectMode', testLines, 1, manyChunks, manyLines, manyLines, true, true, execa); test('Split stdout - n newlines, 1 chunk, preserveNewlines', testLines, 1, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); test('Split stderr - n newlines, 1 chunk, preserveNewlines', testLines, 2, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); test('Split stdio[*] - n newlines, 1 chunk, preserveNewlines', testLines, 3, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); test('Split stdout - preserveNewlines, n chunks, preserveNewlines', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesFullEnd, false, false, execa); test('Split stdout - empty, 1 chunk, preserveNewlines', testLines, 1, emptyChunks, noLines, emptyFull, false, false, execa); test('Split stdout - 0 newlines, 1 chunk, preserveNewlines', testLines, 1, singleChunks, singleChunks, singleFullEnd, false, false, execa); test('Split stdout - Windows newlines, preserveNewlines', testLines, 1, windowsChunks, noNewlinesChunks, windowsFullEnd, false, false, execa); test('Split stdout - chunk ends with newline, preserveNewlines', testLines, 1, simpleFullEndChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); test('Split stdout - single newline, preserveNewlines', testLines, 1, newlineChunks, emptyChunks, newlineFull, false, false, execa); test('Split stdout - only newlines, preserveNewlines', testLines, 1, newlinesChunks, manyEmptyChunks, newlinesFull, false, false, execa); test('Split stdout - only Windows newlines, preserveNewlines', testLines, 1, windowsNewlinesChunks, manyEmptyChunks, windowsNewlinesFull, false, false, execa); test('Split stdout - line split over multiple chunks, preserveNewlines', testLines, 1, runOverChunks, noNewlinesChunks, simpleFullEnd, false, false, execa); test('Split stdout - 0 newlines, big line, preserveNewlines', testLines, 1, bigChunks, bigChunks, bigFullEnd, false, false, execa); test('Split stdout - 0 newlines, many chunks, preserveNewlines', testLines, 1, manyChunks, manyLines, manyFullEnd, false, false, execa); test('Split stdout - n newlines, 1 chunk, objectMode, preserveNewlines', testLines, 1, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); test('Split stderr - n newlines, 1 chunk, objectMode, preserveNewlines', testLines, 2, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); test('Split stdio[*] - n newlines, 1 chunk, objectMode, preserveNewlines', testLines, 3, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); test('Split stdout - preserveNewlines, n chunks, objectMode, preserveNewlines', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesLines, true, false, execa); test('Split stdout - empty, 1 chunk, objectMode, preserveNewlines', testLines, 1, emptyChunks, noLines, noLines, true, false, execa); test('Split stdout - 0 newlines, 1 chunk, objectMode, preserveNewlines', testLines, 1, singleChunks, singleChunks, singleChunks, true, false, execa); test('Split stdout - Windows newlines, objectMode, preserveNewlines', testLines, 1, windowsChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); test('Split stdout - chunk ends with newline, objectMode, preserveNewlines', testLines, 1, simpleFullEndChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); test('Split stdout - single newline, objectMode, preserveNewlines', testLines, 1, newlineChunks, emptyChunks, emptyChunks, true, false, execa); test('Split stdout - only newlines, objectMode, preserveNewlines', testLines, 1, newlinesChunks, manyEmptyChunks, manyEmptyChunks, true, false, execa); test('Split stdout - only Windows newlines, objectMode, preserveNewlines', testLines, 1, windowsNewlinesChunks, manyEmptyChunks, manyEmptyChunks, true, false, execa); test('Split stdout - line split over multiple chunks, objectMode, preserveNewlines', testLines, 1, runOverChunks, noNewlinesChunks, noNewlinesChunks, true, false, execa); test('Split stdout - 0 newlines, big line, objectMode, preserveNewlines', testLines, 1, bigChunks, bigChunks, bigChunks, true, false, execa); test('Split stdout - 0 newlines, many chunks, objectMode, preserveNewlines', testLines, 1, manyChunks, manyLines, manyLines, true, false, execa); test('Split stdout - n newlines, 1 chunk, sync', testLines, 1, simpleChunks, simpleLines, simpleFull, false, true, execaSync); test('Split stderr - n newlines, 1 chunk, sync', testLines, 2, simpleChunks, simpleLines, simpleFull, false, true, execaSync); test('Split stdio[*] - n newlines, 1 chunk, sync', testLines, 3, simpleChunks, simpleLines, simpleFull, false, true, execaSync); test('Split stdout - preserveNewlines, n chunks, sync', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesFull, false, true, execaSync); test('Split stdout - 0 newlines, 1 chunk, sync', testLines, 1, singleChunks, singleChunks, singleFull, false, true, execaSync); test('Split stdout - empty, 1 chunk, sync', testLines, 1, emptyChunks, noLines, emptyFull, false, true, execaSync); test('Split stdout - Windows newlines, sync', testLines, 1, windowsChunks, windowsLines, windowsFull, false, true, execaSync); test('Split stdout - chunk ends with newline, sync', testLines, 1, simpleFullEndChunks, simpleFullEndLines, simpleFullEnd, false, true, execaSync); test('Split stdout - single newline, sync', testLines, 1, newlineChunks, newlineChunks, newlineFull, false, true, execaSync); test('Split stdout - only newlines, sync', testLines, 1, newlinesChunks, newlinesLines, newlinesFull, false, true, execaSync); test('Split stdout - only Windows newlines, sync', testLines, 1, windowsNewlinesChunks, windowsNewlinesLines, windowsNewlinesFull, false, true, execaSync); test('Split stdout - line split over multiple chunks, sync', testLines, 1, runOverChunks, simpleLines, simpleFull, false, true, execaSync); test('Split stdout - 0 newlines, big line, sync', testLines, 1, bigChunks, bigChunks, bigFull, false, true, execaSync); test('Split stdout - 0 newlines, many chunks, sync', testLines, 1, manyChunks, manyLines, manyFull, false, true, execaSync); test('Split stdout - n newlines, 1 chunk, objectMode, sync', testLines, 1, simpleChunks, simpleLines, simpleLines, true, true, execaSync); test('Split stderr - n newlines, 1 chunk, objectMode, sync', testLines, 2, simpleChunks, simpleLines, simpleLines, true, true, execaSync); test('Split stdio[*] - n newlines, 1 chunk, objectMode, sync', testLines, 3, simpleChunks, simpleLines, simpleLines, true, true, execaSync); test('Split stdout - preserveNewlines, n chunks, objectMode, sync', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesLines, true, true, execaSync); test('Split stdout - empty, 1 chunk, objectMode, sync', testLines, 1, emptyChunks, noLines, noLines, true, true, execaSync); test('Split stdout - 0 newlines, 1 chunk, objectMode, sync', testLines, 1, singleChunks, singleChunks, singleChunks, true, true, execaSync); test('Split stdout - Windows newlines, objectMode, sync', testLines, 1, windowsChunks, windowsLines, windowsLines, true, true, execaSync); test('Split stdout - chunk ends with newline, objectMode, sync', testLines, 1, simpleFullEndChunks, simpleFullEndLines, simpleFullEndLines, true, true, execaSync); test('Split stdout - single newline, objectMode, sync', testLines, 1, newlineChunks, newlineChunks, newlineChunks, true, true, execaSync); test('Split stdout - only newlines, objectMode, sync', testLines, 1, newlinesChunks, newlinesLines, newlinesLines, true, true, execaSync); test('Split stdout - only Windows newlines, objectMode, sync', testLines, 1, windowsNewlinesChunks, windowsNewlinesLines, windowsNewlinesLines, true, true, execaSync); test('Split stdout - line split over multiple chunks, objectMode, sync', testLines, 1, runOverChunks, simpleLines, simpleLines, true, true, execaSync); test('Split stdout - 0 newlines, big line, objectMode, sync', testLines, 1, bigChunks, bigChunks, bigChunks, true, true, execaSync); test('Split stdout - 0 newlines, many chunks, objectMode, sync', testLines, 1, manyChunks, manyLines, manyLines, true, true, execaSync); test('Split stdout - n newlines, 1 chunk, preserveNewlines, sync', testLines, 1, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); test('Split stderr - n newlines, 1 chunk, preserveNewlines, sync', testLines, 2, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); test('Split stdio[*] - n newlines, 1 chunk, preserveNewlines, sync', testLines, 3, simpleChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); test('Split stdout - preserveNewlines, n chunks, preserveNewlines, sync', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesFullEnd, false, false, execaSync); test('Split stdout - empty, 1 chunk, preserveNewlines, sync', testLines, 1, emptyChunks, noLines, emptyFull, false, false, execaSync); test('Split stdout - 0 newlines, 1 chunk, preserveNewlines, sync', testLines, 1, singleChunks, singleChunks, singleFullEnd, false, false, execaSync); test('Split stdout - Windows newlines, preserveNewlines, sync', testLines, 1, windowsChunks, noNewlinesChunks, windowsFullEnd, false, false, execaSync); test('Split stdout - chunk ends with newline, preserveNewlines, sync', testLines, 1, simpleFullEndChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); test('Split stdout - single newline, preserveNewlines, sync', testLines, 1, newlineChunks, emptyChunks, newlineFull, false, false, execaSync); test('Split stdout - only newlines, preserveNewlines, sync', testLines, 1, newlinesChunks, manyEmptyChunks, newlinesFull, false, false, execaSync); test('Split stdout - only Windows newlines, preserveNewlines, sync', testLines, 1, windowsNewlinesChunks, manyEmptyChunks, windowsNewlinesFull, false, false, execaSync); test('Split stdout - line split over multiple chunks, preserveNewlines, sync', testLines, 1, runOverChunks, noNewlinesChunks, simpleFullEnd, false, false, execaSync); test('Split stdout - 0 newlines, big line, preserveNewlines, sync', testLines, 1, bigChunks, bigChunks, bigFullEnd, false, false, execaSync); test('Split stdout - 0 newlines, many chunks, preserveNewlines, sync', testLines, 1, manyChunks, manyLines, manyFullEnd, false, false, execaSync); test('Split stdout - n newlines, 1 chunk, objectMode, preserveNewlines, sync', testLines, 1, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); test('Split stderr - n newlines, 1 chunk, objectMode, preserveNewlines, sync', testLines, 2, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); test('Split stdio[*] - n newlines, 1 chunk, objectMode, preserveNewlines, sync', testLines, 3, simpleChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); test('Split stdout - preserveNewlines, n chunks, objectMode, preserveNewlines, sync', testLines, 1, noNewlinesChunks, noNewlinesLines, noNewlinesLines, true, false, execaSync); test('Split stdout - empty, 1 chunk, objectMode, preserveNewlines, sync', testLines, 1, emptyChunks, noLines, noLines, true, false, execaSync); test('Split stdout - 0 newlines, 1 chunk, objectMode, preserveNewlines, sync', testLines, 1, singleChunks, singleChunks, singleChunks, true, false, execaSync); test('Split stdout - Windows newlines, objectMode, preserveNewlines, sync', testLines, 1, windowsChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); test('Split stdout - chunk ends with newline, objectMode, preserveNewlines, sync', testLines, 1, simpleFullEndChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); test('Split stdout - single newline, objectMode, preserveNewlines, sync', testLines, 1, newlineChunks, emptyChunks, emptyChunks, true, false, execaSync); test('Split stdout - only newlines, objectMode, preserveNewlines, sync', testLines, 1, newlinesChunks, manyEmptyChunks, manyEmptyChunks, true, false, execaSync); test('Split stdout - only Windows newlines, objectMode, preserveNewlines, sync', testLines, 1, windowsNewlinesChunks, manyEmptyChunks, manyEmptyChunks, true, false, execaSync); test('Split stdout - line split over multiple chunks, objectMode, preserveNewlines, sync', testLines, 1, runOverChunks, noNewlinesChunks, noNewlinesChunks, true, false, execaSync); test('Split stdout - 0 newlines, big line, objectMode, preserveNewlines, sync', testLines, 1, bigChunks, bigChunks, bigChunks, true, false, execaSync); test('Split stdout - 0 newlines, many chunks, objectMode, preserveNewlines, sync', testLines, 1, manyChunks, manyLines, manyLines, true, false, execaSync); ================================================ FILE: test/transform/split-newline.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getOutputsGenerator, noopGenerator, noopAsyncGenerator} from '../helpers/generator.js'; import {singleFull, singleFullEnd} from '../helpers/lines.js'; setFixtureDirectory(); const singleFullEndWindows = `${singleFull}\r\n`; const mixedNewlines = '.\n.\r\n.\n.\r\n.\n'; const testStripNewline = async (t, input, expectedOutput, execaMethod) => { const {stdout} = await execaMethod('noop.js', { stdout: getOutputsGenerator([input])(), stripFinalNewline: false, }); t.is(stdout, expectedOutput); }; test('Strips newline when user do not mistakenly yield one at the end', testStripNewline, singleFull, singleFullEnd, execa); test('Strips newline when user mistakenly yielded one at the end', testStripNewline, singleFullEnd, singleFullEnd, execa); test('Strips newline when user mistakenly yielded one at the end, Windows newline', testStripNewline, singleFullEndWindows, singleFullEndWindows, execa); test('Strips newline when user do not mistakenly yield one at the end, sync', testStripNewline, singleFull, singleFullEnd, execaSync); test('Strips newline when user mistakenly yielded one at the end, sync', testStripNewline, singleFullEnd, singleFullEnd, execaSync); test('Strips newline when user mistakenly yielded one at the end, Windows newline, sync', testStripNewline, singleFullEndWindows, singleFullEndWindows, execaSync); const testMixNewlines = async (t, generator, execaMethod) => { const {stdout} = await execaMethod('noop-fd.js', ['1', mixedNewlines], { stdout: generator(), stripFinalNewline: false, }); t.is(stdout, mixedNewlines); }; test('Can mix Unix and Windows newlines', testMixNewlines, noopGenerator, execa); test('Can mix Unix and Windows newlines, sync', testMixNewlines, noopGenerator, execaSync); test('Can mix Unix and Windows newlines, async', testMixNewlines, noopAsyncGenerator, execa); ================================================ FILE: test/transform/split-transform.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getOutputsGenerator, resultGenerator} from '../helpers/generator.js'; import { foobarString, foobarUint8Array, foobarObject, foobarObjectString, } from '../helpers/input.js'; setFixtureDirectory(); const resultUint8ArrayGenerator = function * (lines, chunk) { lines.push(chunk); yield new TextEncoder().encode(chunk); }; // eslint-disable-next-line max-params const testStringToUint8Array = async (t, expectedOutput, objectMode, preserveNewlines, execaMethod) => { const lines = []; const {stdout} = await execaMethod('noop-fd.js', ['1', foobarString], { stdout: { transform: resultUint8ArrayGenerator.bind(undefined, lines), objectMode, preserveNewlines, }, lines: true, }); t.deepEqual(lines, [foobarString]); t.deepEqual(stdout, expectedOutput); }; test('Line splitting when converting from string to Uint8Array', testStringToUint8Array, [foobarString], false, true, execa); test('Line splitting when converting from string to Uint8Array, objectMode', testStringToUint8Array, [foobarUint8Array], true, true, execa); test('Line splitting when converting from string to Uint8Array, preserveNewlines', testStringToUint8Array, [foobarString], false, false, execa); test('Line splitting when converting from string to Uint8Array, objectMode, preserveNewlines', testStringToUint8Array, [foobarUint8Array], true, false, execa); test('Line splitting when converting from string to Uint8Array, sync', testStringToUint8Array, [foobarString], false, true, execaSync); test('Line splitting when converting from string to Uint8Array, objectMode, sync', testStringToUint8Array, [foobarUint8Array], true, true, execaSync); test('Line splitting when converting from string to Uint8Array, preserveNewlines, sync', testStringToUint8Array, [foobarString], false, false, execaSync); test('Line splitting when converting from string to Uint8Array, objectMode, preserveNewlines, sync', testStringToUint8Array, [foobarUint8Array], true, false, execaSync); const serializeResultGenerator = function * (lines, chunk) { lines.push(chunk); yield JSON.stringify(chunk); }; const testUnsetObjectMode = async (t, expectedOutput, preserveNewlines, execaMethod) => { const lines = []; const {stdout} = await execaMethod('noop.js', { stdout: [ getOutputsGenerator([foobarObject])(true), {transform: serializeResultGenerator.bind(undefined, lines), preserveNewlines, objectMode: false}, ], stripFinalNewline: false, }); t.deepEqual(lines, [foobarObject]); t.is(stdout, expectedOutput); }; test('Can switch from objectMode to non-objectMode', testUnsetObjectMode, `${foobarObjectString}\n`, false, execa); test('Can switch from objectMode to non-objectMode, preserveNewlines', testUnsetObjectMode, foobarObjectString, true, execa); test('Can switch from objectMode to non-objectMode, sync', testUnsetObjectMode, `${foobarObjectString}\n`, false, execaSync); test('Can switch from objectMode to non-objectMode, preserveNewlines, sync', testUnsetObjectMode, foobarObjectString, true, execaSync); // eslint-disable-next-line max-params const testYieldArray = async (t, input, expectedLines, expectedOutput, execaMethod) => { const lines = []; const {stdout} = await execaMethod('noop.js', { stdout: [ getOutputsGenerator(input)(), resultGenerator(lines)(), ], stripFinalNewline: false, }); t.deepEqual(lines, expectedLines); t.deepEqual(stdout, expectedOutput); }; test('Can use "yield* array" to produce multiple lines', testYieldArray, [foobarString, foobarString], [foobarString, foobarString], `${foobarString}\n${foobarString}\n`, execa); test('Can use "yield* array" to produce empty lines', testYieldArray, [foobarString, ''], [foobarString, ''], `${foobarString}\n\n`, execa); test('Can use "yield* array" to produce multiple lines, sync', testYieldArray, [foobarString, foobarString], [foobarString, foobarString], `${foobarString}\n${foobarString}\n`, execaSync); test('Can use "yield* array" to produce empty lines, sync', testYieldArray, [foobarString, ''], [foobarString, ''], `${foobarString}\n\n`, execaSync); ================================================ FILE: test/transform/validate.js ================================================ import test from 'ava'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {getStdio} from '../helpers/stdio.js'; import {foobarUint8Array, foobarObject} from '../helpers/input.js'; import {serializeGenerator, getOutputGenerator, convertTransformToFinal} from '../helpers/generator.js'; setFixtureDirectory(); const getMessage = input => input === null || input === undefined ? 'not be called at all' : 'a string or an Uint8Array'; const lastInputGenerator = input => objectMode => [foobarUint8Array, getOutputGenerator(input)(objectMode)]; const inputGenerator = input => objectMode => [...lastInputGenerator(input)(objectMode), serializeGenerator(true)]; // eslint-disable-next-line max-params const testGeneratorReturn = async (t, fdNumber, generator, input, objectMode, isInput) => { const fixtureName = isInput ? 'stdin-fd.js' : 'noop-fd.js'; const {message} = await t.throwsAsync(execa(fixtureName, [`${fdNumber}`], getStdio(fdNumber, generator(input)(objectMode)))); t.true(message.includes(getMessage(input))); }; test('Generators with result.stdin cannot return an object if not in objectMode', testGeneratorReturn, 0, inputGenerator, foobarObject, false, true); test('Generators with result.stdio[*] as input cannot return an object if not in objectMode', testGeneratorReturn, 3, inputGenerator, foobarObject, false, true); test('The last generator with result.stdin cannot return an object even in objectMode', testGeneratorReturn, 0, lastInputGenerator, foobarObject, true, true); test('The last generator with result.stdio[*] as input cannot return an object even in objectMode', testGeneratorReturn, 3, lastInputGenerator, foobarObject, true, true); test('Generators with result.stdout cannot return an object if not in objectMode', testGeneratorReturn, 1, getOutputGenerator, foobarObject, false, false); test('Generators with result.stderr cannot return an object if not in objectMode', testGeneratorReturn, 2, getOutputGenerator, foobarObject, false, false); test('Generators with result.stdio[*] as output cannot return an object if not in objectMode', testGeneratorReturn, 3, getOutputGenerator, foobarObject, false, false); test('Generators with result.stdin cannot return null if not in objectMode', testGeneratorReturn, 0, inputGenerator, null, false, true); test('Generators with result.stdin cannot return null if in objectMode', testGeneratorReturn, 0, inputGenerator, null, true, true); test('Generators with result.stdout cannot return null if not in objectMode', testGeneratorReturn, 1, getOutputGenerator, null, false, false); test('Generators with result.stdout cannot return null if in objectMode', testGeneratorReturn, 1, getOutputGenerator, null, true, false); test('Generators with result.stdin cannot return undefined if not in objectMode', testGeneratorReturn, 0, inputGenerator, undefined, false, true); test('Generators with result.stdin cannot return undefined if in objectMode', testGeneratorReturn, 0, inputGenerator, undefined, true, true); test('Generators with result.stdout cannot return undefined if not in objectMode', testGeneratorReturn, 1, getOutputGenerator, undefined, false, false); test('Generators with result.stdout cannot return undefined if in objectMode', testGeneratorReturn, 1, getOutputGenerator, undefined, true, false); // eslint-disable-next-line max-params const testGeneratorReturnSync = (t, fdNumber, generator, input, objectMode, isInput) => { const fixtureName = isInput ? 'stdin-fd.js' : 'noop-fd.js'; const {message} = t.throws(() => { execaSync(fixtureName, [`${fdNumber}`], getStdio(fdNumber, generator(input)(objectMode))); }); t.true(message.includes(getMessage(input))); }; test('Generators with result.stdin cannot return an object if not in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, foobarObject, false, true); test('The last generator with result.stdin cannot return an object even in objectMode, sync', testGeneratorReturnSync, 0, lastInputGenerator, foobarObject, true, true); test('Generators with result.stdout cannot return an object if not in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, foobarObject, false, false); test('Generators with result.stderr cannot return an object if not in objectMode, sync', testGeneratorReturnSync, 2, getOutputGenerator, foobarObject, false, false); test('Generators with result.stdio[*] as output cannot return an object if not in objectMode, sync', testGeneratorReturnSync, 3, getOutputGenerator, foobarObject, false, false); test('Generators with result.stdin cannot return null if not in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, null, false, true); test('Generators with result.stdin cannot return null if in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, null, true, true); test('Generators with result.stdout cannot return null if not in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, null, false, false); test('Generators with result.stdout cannot return null if in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, null, true, false); test('Generators with result.stdin cannot return undefined if not in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, undefined, false, true); test('Generators with result.stdin cannot return undefined if in objectMode, sync', testGeneratorReturnSync, 0, inputGenerator, undefined, true, true); test('Generators with result.stdout cannot return undefined if not in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, undefined, false, false); test('Generators with result.stdout cannot return undefined if in objectMode, sync', testGeneratorReturnSync, 1, getOutputGenerator, undefined, true, false); test('Generators "final" return value is validated', async t => { await t.throwsAsync( execa('noop.js', {stdout: convertTransformToFinal(getOutputGenerator(null)(true), true)}), {message: /not be called at all/}, ); }); test('Generators "final" return value is validated, sync', t => { t.throws(() => { execaSync('noop.js', {stdout: convertTransformToFinal(getOutputGenerator(null)(true), true)}); }, {message: /not be called at all/}); }); ================================================ FILE: test/verbose/complete.js ================================================ import {stripVTControlCharacters} from 'node:util'; import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { runErrorSubprocess, runWarningSubprocess, runEarlyErrorSubprocess, getCompletionLine, getCompletionLines, testTimestamp, getVerboseOption, stdoutNoneOption, stdoutShortOption, stdoutFullOption, stderrNoneOption, stderrShortOption, stderrFullOption, fd3NoneOption, fd3ShortOption, fd3FullOption, ipcNoneOption, ipcShortOption, ipcFullOption, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintCompletion = async (t, verbose, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, isSync}); t.is(getCompletionLine(stderr), `${testTimestamp} [0] √ (done in 0ms)`); }; test('Prints completion, verbose "short"', testPrintCompletion, 'short', false); test('Prints completion, verbose "full"', testPrintCompletion, 'full', false); test('Prints completion, verbose "short", fd-specific stdout', testPrintCompletion, stdoutShortOption, false); test('Prints completion, verbose "full", fd-specific stdout', testPrintCompletion, stdoutFullOption, false); test('Prints completion, verbose "short", fd-specific stderr', testPrintCompletion, stderrShortOption, false); test('Prints completion, verbose "full", fd-specific stderr', testPrintCompletion, stderrFullOption, false); test('Prints completion, verbose "short", fd-specific fd3', testPrintCompletion, fd3ShortOption, false); test('Prints completion, verbose "full", fd-specific fd3', testPrintCompletion, fd3FullOption, false); test('Prints completion, verbose "short", fd-specific ipc', testPrintCompletion, ipcShortOption, false); test('Prints completion, verbose "full", fd-specific ipc', testPrintCompletion, ipcFullOption, false); test('Prints completion, verbose "short", sync', testPrintCompletion, 'short', true); test('Prints completion, verbose "full", sync', testPrintCompletion, 'full', true); test('Prints completion, verbose "short", fd-specific stdout, sync', testPrintCompletion, stdoutShortOption, true); test('Prints completion, verbose "full", fd-specific stdout, sync', testPrintCompletion, stdoutFullOption, true); test('Prints completion, verbose "short", fd-specific stderr, sync', testPrintCompletion, stderrShortOption, true); test('Prints completion, verbose "full", fd-specific stderr, sync', testPrintCompletion, stderrFullOption, true); test('Prints completion, verbose "short", fd-specific fd3, sync', testPrintCompletion, fd3ShortOption, true); test('Prints completion, verbose "full", fd-specific fd3, sync', testPrintCompletion, fd3FullOption, true); test('Prints completion, verbose "short", fd-specific ipc, sync', testPrintCompletion, ipcShortOption, true); test('Prints completion, verbose "full", fd-specific ipc, sync', testPrintCompletion, ipcFullOption, true); const testNoPrintCompletion = async (t, verbose, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, isSync}); t.is(stderr, ''); }; test('Does not print completion, verbose "none"', testNoPrintCompletion, 'none', false); test('Does not print completion, verbose default"', testNoPrintCompletion, undefined, false); test('Does not print completion, verbose "none", fd-specific stdout', testNoPrintCompletion, stdoutNoneOption, false); test('Does not print completion, verbose "none", fd-specific stderr', testNoPrintCompletion, stderrNoneOption, false); test('Does not print completion, verbose "none", fd-specific fd3', testNoPrintCompletion, fd3NoneOption, false); test('Does not print completion, verbose "none", fd-specific ipc', testNoPrintCompletion, ipcNoneOption, false); test('Does not print completion, verbose default", fd-specific', testNoPrintCompletion, {}, false); test('Does not print completion, verbose "none", sync', testNoPrintCompletion, 'none', true); test('Does not print completion, verbose default", sync', testNoPrintCompletion, undefined, true); test('Does not print completion, verbose "none", fd-specific stdout, sync', testNoPrintCompletion, stdoutNoneOption, true); test('Does not print completion, verbose "none", fd-specific stderr, sync', testNoPrintCompletion, stderrNoneOption, true); test('Does not print completion, verbose "none", fd-specific fd3, sync', testNoPrintCompletion, fd3NoneOption, true); test('Does not print completion, verbose "none", fd-specific ipc, sync', testNoPrintCompletion, ipcNoneOption, true); test('Does not print completion, verbose default", fd-specific, sync', testNoPrintCompletion, {}, true); const testPrintCompletionError = async (t, isSync) => { const stderr = await runErrorSubprocess(t, 'short', isSync); t.is(getCompletionLine(stderr), `${testTimestamp} [0] × (done in 0ms)`); }; test('Prints completion after errors', testPrintCompletionError, false); test('Prints completion after errors, sync', testPrintCompletionError, true); const testPrintCompletionWarning = async (t, isSync) => { const stderr = await runWarningSubprocess(t, isSync); t.is(getCompletionLine(stderr), `${testTimestamp} [0] ‼ (done in 0ms)`); }; test('Prints completion after errors, "reject" false', testPrintCompletionWarning, false); test('Prints completion after errors, "reject" false, sync', testPrintCompletionWarning, true); const testPrintCompletionEarly = async (t, isSync) => { const stderr = await runEarlyErrorSubprocess(t, isSync); t.is(getCompletionLine(stderr), undefined); }; test('Prints completion after early validation errors', testPrintCompletionEarly, false); test('Prints completion after early validation errors, sync', testPrintCompletionEarly, true); test.serial('Prints duration', async t => { const {stderr} = await nestedSubprocess('delay.js', ['1000'], {verbose: 'short'}); t.regex(stripVTControlCharacters(stderr).split('\n').at(-1), /\(done in [\d.]+s\)/); }); const testPipeDuration = async (t, parentFixture, sourceVerbose, destinationVerbose) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], { parentFixture, sourceOptions: getVerboseOption(sourceVerbose), destinationFile: 'stdin.js', destinationOptions: getVerboseOption(destinationVerbose), }); const lines = getCompletionLines(stderr); t.is(lines.includes(`${testTimestamp} [0] √ (done in 0ms)`), sourceVerbose || destinationVerbose); t.is(lines.includes(`${testTimestamp} [1] √ (done in 0ms)`), sourceVerbose && destinationVerbose); }; test('Prints both durations piped with .pipe("file")', testPipeDuration, 'nested-pipe-file.js', true, true); test('Prints both durations piped with .pipe`command`', testPipeDuration, 'nested-pipe-script.js', true, true); test('Prints both durations piped with .pipe(subprocess)', testPipeDuration, 'nested-pipe-subprocesses.js', true, true); test('Prints first duration piped with .pipe("file")', testPipeDuration, 'nested-pipe-file.js', true, false); test('Prints first duration piped with .pipe`command`', testPipeDuration, 'nested-pipe-script.js', true, false); test('Prints first duration piped with .pipe(subprocess)', testPipeDuration, 'nested-pipe-subprocesses.js', true, false); test('Prints second duration piped with .pipe("file")', testPipeDuration, 'nested-pipe-file.js', false, true); test('Prints second duration piped with .pipe`command`', testPipeDuration, 'nested-pipe-script.js', false, true); test('Prints second duration piped with .pipe(subprocess)', testPipeDuration, 'nested-pipe-subprocesses.js', false, true); test('Prints neither durations piped with .pipe("file")', testPipeDuration, 'nested-pipe-file.js', false, false); test('Prints neither durations piped with .pipe`command`', testPipeDuration, 'nested-pipe-script.js', false, false); test('Prints neither durations piped with .pipe(subprocess)', testPipeDuration, 'nested-pipe-subprocesses.js', false, false); ================================================ FILE: test/verbose/custom-command.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import { QUOTE, getNormalizedLine, testTimestamp, runVerboseSubprocess, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintCommandCustom = async (t, fdNumber, isSync) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print.js', isSync, type: 'command', fdNumber, }); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('Prints command, verbose custom', testPrintCommandCustom, undefined, false); test('Prints command, verbose custom, fd-specific stdout', testPrintCommandCustom, 'stdout', false); test('Prints command, verbose custom, fd-specific stderr', testPrintCommandCustom, 'stderr', false); test('Prints command, verbose custom, fd-specific fd3', testPrintCommandCustom, 'fd3', false); test('Prints command, verbose custom, fd-specific ipc', testPrintCommandCustom, 'ipc', false); test('Prints command, verbose custom, sync', testPrintCommandCustom, undefined, true); test('Prints command, verbose custom, fd-specific stdout, sync', testPrintCommandCustom, 'stdout', true); test('Prints command, verbose custom, fd-specific stderr, sync', testPrintCommandCustom, 'stderr', true); test('Prints command, verbose custom, fd-specific fd3, sync', testPrintCommandCustom, 'fd3', true); test('Prints command, verbose custom, fd-specific ipc, sync', testPrintCommandCustom, 'ipc', true); const testPrintCommandOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-multiple.js', type: 'command', fdNumber, secondFdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); } else { t.is(stderr, ''); } }; test('Prints command, verbose custom, fd-specific stdout+stderr', testPrintCommandOrder, 'stdout', 'stderr', true); test('Prints command, verbose custom, fd-specific stderr+stdout', testPrintCommandOrder, 'stderr', 'stdout', false); test('Prints command, verbose custom, fd-specific stdout+fd3', testPrintCommandOrder, 'stdout', 'fd3', true); test('Prints command, verbose custom, fd-specific fd3+stdout', testPrintCommandOrder, 'fd3', 'stdout', false); test('Prints command, verbose custom, fd-specific stdout+ipc', testPrintCommandOrder, 'stdout', 'ipc', true); test('Prints command, verbose custom, fd-specific ipc+stdout', testPrintCommandOrder, 'ipc', 'stdout', false); test('Prints command, verbose custom, fd-specific stderr+fd3', testPrintCommandOrder, 'stderr', 'fd3', true); test('Prints command, verbose custom, fd-specific fd3+stderr', testPrintCommandOrder, 'fd3', 'stderr', false); test('Prints command, verbose custom, fd-specific stderr+ipc', testPrintCommandOrder, 'stderr', 'ipc', true); test('Prints command, verbose custom, fd-specific ipc+stderr', testPrintCommandOrder, 'ipc', 'stderr', false); test('Prints command, verbose custom, fd-specific fd3+ipc', testPrintCommandOrder, 'fd3', 'ipc', true); test('Prints command, verbose custom, fd-specific ipc+fd3', testPrintCommandOrder, 'ipc', 'fd3', false); const testPrintCommandFunction = async (t, fdNumber, secondFdNumber) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-function.js', type: 'command', fdNumber, secondFdNumber, ...fullStdio, }); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('Prints command, verbose custom, fd-specific stdout+stderr, single function', testPrintCommandFunction, 'stdout', 'stderr'); test('Prints command, verbose custom, fd-specific stderr+stdout, single function', testPrintCommandFunction, 'stderr', 'stdout'); test('Prints command, verbose custom, fd-specific stdout+fd3, single function', testPrintCommandFunction, 'stdout', 'fd3'); test('Prints command, verbose custom, fd-specific fd3+stdout, single function', testPrintCommandFunction, 'fd3', 'stdout'); test('Prints command, verbose custom, fd-specific stdout+ipc, single function', testPrintCommandFunction, 'stdout', 'ipc'); test('Prints command, verbose custom, fd-specific ipc+stdout, single function', testPrintCommandFunction, 'ipc', 'stdout'); test('Prints command, verbose custom, fd-specific stderr+fd3, single function', testPrintCommandFunction, 'stderr', 'fd3'); test('Prints command, verbose custom, fd-specific fd3+stderr, single function', testPrintCommandFunction, 'fd3', 'stderr'); test('Prints command, verbose custom, fd-specific stderr+ipc, single function', testPrintCommandFunction, 'stderr', 'ipc'); test('Prints command, verbose custom, fd-specific ipc+stderr, single function', testPrintCommandFunction, 'ipc', 'stderr'); test('Prints command, verbose custom, fd-specific fd3+ipc, single function', testPrintCommandFunction, 'fd3', 'ipc'); test('Prints command, verbose custom, fd-specific ipc+fd3, single function', testPrintCommandFunction, 'ipc', 'fd3'); const testVerboseMessage = async (t, isSync) => { const {stderr} = await runVerboseSubprocess({ isSync, type: 'command', eventProperty: 'message', }); t.is(getNormalizedLine(stderr), `noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('"verbose" function receives verboseObject.message', testVerboseMessage, false); test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); ================================================ FILE: test/verbose/custom-common.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import {QUOTE, getCommandLine, testTimestamp} from '../helpers/verbose.js'; setFixtureDirectory(); const testCustomReturn = async (t, verboseOutput, expectedOutput) => { const {stderr} = await nestedSubprocess( 'empty.js', {optionsFixture: 'custom-return.js', optionsInput: {verboseOutput}}, {stripFinalNewline: false}, ); t.is(stderr, expectedOutput); }; test('"verbose" returning a string prints it', testCustomReturn, `${foobarString}\n`, `${foobarString}\n`); test('"verbose" returning a string without a newline adds it', testCustomReturn, foobarString, `${foobarString}\n`); test('"verbose" returning a string with multiple newlines keeps them', testCustomReturn, `${foobarString}\n\n`, `${foobarString}\n\n`); test('"verbose" returning an empty string prints an empty line', testCustomReturn, '', '\n'); test('"verbose" returning undefined ignores it', testCustomReturn, undefined, ''); test('"verbose" returning a number ignores it', testCustomReturn, 0, ''); test('"verbose" returning a bigint ignores it', testCustomReturn, 0n, ''); test('"verbose" returning a boolean ignores it', testCustomReturn, true, ''); test('"verbose" returning an object ignores it', testCustomReturn, {}, ''); test('"verbose" returning an array ignores it', testCustomReturn, [], ''); test('"verbose" receives verboseLine string as first argument', async t => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {optionsFixture: 'custom-uppercase.js'}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ NOOP.js ${foobarString}`); }); test('"verbose" can print as JSON', async t => { const {stderr} = await nestedSubprocess('noop.js', ['. .'], {optionsFixture: 'custom-json.js', type: 'duration', reject: false}); const {type, message, escapedCommand, commandId, timestamp, piped, result, options} = JSON.parse(stderr); t.is(type, 'duration'); t.true(message.includes('done in')); t.is(escapedCommand, `noop.js ${QUOTE}. .${QUOTE}`); t.is(commandId, '0'); t.true(Number.isInteger(new Date(timestamp).getTime())); t.false(piped); t.false(result.failed); t.is(result.exitCode, 0); t.is(result.stdout, '. .'); t.is(result.stderr, ''); t.false(options.reject); }); ================================================ FILE: test/verbose/custom-complete.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import {getNormalizedLine, testTimestamp, runVerboseSubprocess} from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintCompletionCustom = async (t, fdNumber, isSync) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print.js', isSync, type: 'duration', fdNumber, }); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × (done in 0ms)`); }; test('Prints completion, verbose custom', testPrintCompletionCustom, undefined, false); test('Prints completion, verbose custom, fd-specific stdout', testPrintCompletionCustom, 'stdout', false); test('Prints completion, verbose custom, fd-specific stderr', testPrintCompletionCustom, 'stderr', false); test('Prints completion, verbose custom, fd-specific fd3', testPrintCompletionCustom, 'fd3', false); test('Prints completion, verbose custom, fd-specific ipc', testPrintCompletionCustom, 'ipc', false); test('Prints completion, verbose custom, sync', testPrintCompletionCustom, undefined, true); test('Prints completion, verbose custom, fd-specific stdout, sync', testPrintCompletionCustom, 'stdout', true); test('Prints completion, verbose custom, fd-specific stderr, sync', testPrintCompletionCustom, 'stderr', true); test('Prints completion, verbose custom, fd-specific fd3, sync', testPrintCompletionCustom, 'fd3', true); test('Prints completion, verbose custom, fd-specific ipc, sync', testPrintCompletionCustom, 'ipc', true); const testPrintCompletionOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-multiple.js', type: 'duration', fdNumber, secondFdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × (done in 0ms)`); } else { t.is(stderr, ''); } }; test('Prints completion, verbose custom, fd-specific stdout+stderr', testPrintCompletionOrder, 'stdout', 'stderr', true); test('Prints completion, verbose custom, fd-specific stderr+stdout', testPrintCompletionOrder, 'stderr', 'stdout', false); test('Prints completion, verbose custom, fd-specific stdout+fd3', testPrintCompletionOrder, 'stdout', 'fd3', true); test('Prints completion, verbose custom, fd-specific fd3+stdout', testPrintCompletionOrder, 'fd3', 'stdout', false); test('Prints completion, verbose custom, fd-specific stdout+ipc', testPrintCompletionOrder, 'stdout', 'ipc', true); test('Prints completion, verbose custom, fd-specific ipc+stdout', testPrintCompletionOrder, 'ipc', 'stdout', false); test('Prints completion, verbose custom, fd-specific stderr+fd3', testPrintCompletionOrder, 'stderr', 'fd3', true); test('Prints completion, verbose custom, fd-specific fd3+stderr', testPrintCompletionOrder, 'fd3', 'stderr', false); test('Prints completion, verbose custom, fd-specific stderr+ipc', testPrintCompletionOrder, 'stderr', 'ipc', true); test('Prints completion, verbose custom, fd-specific ipc+stderr', testPrintCompletionOrder, 'ipc', 'stderr', false); test('Prints completion, verbose custom, fd-specific fd3+ipc', testPrintCompletionOrder, 'fd3', 'ipc', true); test('Prints completion, verbose custom, fd-specific ipc+fd3', testPrintCompletionOrder, 'ipc', 'fd3', false); const testPrintCompletionFunction = async (t, fdNumber, secondFdNumber) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-function.js', type: 'duration', fdNumber, secondFdNumber, ...fullStdio, }); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × (done in 0ms)`); }; test('Prints completion, verbose custom, fd-specific stdout+stderr, single function', testPrintCompletionFunction, 'stdout', 'stderr'); test('Prints completion, verbose custom, fd-specific stderr+stdout, single function', testPrintCompletionFunction, 'stderr', 'stdout'); test('Prints completion, verbose custom, fd-specific stdout+fd3, single function', testPrintCompletionFunction, 'stdout', 'fd3'); test('Prints completion, verbose custom, fd-specific fd3+stdout, single function', testPrintCompletionFunction, 'fd3', 'stdout'); test('Prints completion, verbose custom, fd-specific stdout+ipc, single function', testPrintCompletionFunction, 'stdout', 'ipc'); test('Prints completion, verbose custom, fd-specific ipc+stdout, single function', testPrintCompletionFunction, 'ipc', 'stdout'); test('Prints completion, verbose custom, fd-specific stderr+fd3, single function', testPrintCompletionFunction, 'stderr', 'fd3'); test('Prints completion, verbose custom, fd-specific fd3+stderr, single function', testPrintCompletionFunction, 'fd3', 'stderr'); test('Prints completion, verbose custom, fd-specific stderr+ipc, single function', testPrintCompletionFunction, 'stderr', 'ipc'); test('Prints completion, verbose custom, fd-specific ipc+stderr, single function', testPrintCompletionFunction, 'ipc', 'stderr'); test('Prints completion, verbose custom, fd-specific fd3+ipc, single function', testPrintCompletionFunction, 'fd3', 'ipc'); test('Prints completion, verbose custom, fd-specific ipc+fd3, single function', testPrintCompletionFunction, 'ipc', 'fd3'); const testVerboseMessage = async (t, isSync) => { const {stderr} = await runVerboseSubprocess({ isSync, type: 'duration', eventProperty: 'message', }); t.is(getNormalizedLine(stderr), '(done in 0ms)'); }; test('"verbose" function receives verboseObject.message', testVerboseMessage, false); test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); ================================================ FILE: test/verbose/custom-error.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import { QUOTE, getNormalizedLine, testTimestamp, runVerboseSubprocess, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintErrorCustom = async (t, fdNumber, isSync) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print.js', isSync, type: 'error', fdNumber, }); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('Prints error, verbose custom', testPrintErrorCustom, undefined, false); test('Prints error, verbose custom, fd-specific stdout', testPrintErrorCustom, 'stdout', false); test('Prints error, verbose custom, fd-specific stderr', testPrintErrorCustom, 'stderr', false); test('Prints error, verbose custom, fd-specific fd3', testPrintErrorCustom, 'fd3', false); test('Prints error, verbose custom, fd-specific ipc', testPrintErrorCustom, 'ipc', false); test('Prints error, verbose custom, sync', testPrintErrorCustom, undefined, true); test('Prints error, verbose custom, fd-specific stdout, sync', testPrintErrorCustom, 'stdout', true); test('Prints error, verbose custom, fd-specific stderr, sync', testPrintErrorCustom, 'stderr', true); test('Prints error, verbose custom, fd-specific fd3, sync', testPrintErrorCustom, 'fd3', true); test('Prints error, verbose custom, fd-specific ipc, sync', testPrintErrorCustom, 'ipc', true); const testPrintErrorOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-multiple.js', type: 'error', fdNumber, secondFdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-verbose.js ${QUOTE}. .${QUOTE}`); } else { t.is(stderr, ''); } }; test('Prints error, verbose custom, fd-specific stdout+stderr', testPrintErrorOrder, 'stdout', 'stderr', true); test('Prints error, verbose custom, fd-specific stderr+stdout', testPrintErrorOrder, 'stderr', 'stdout', false); test('Prints error, verbose custom, fd-specific stdout+fd3', testPrintErrorOrder, 'stdout', 'fd3', true); test('Prints error, verbose custom, fd-specific fd3+stdout', testPrintErrorOrder, 'fd3', 'stdout', false); test('Prints error, verbose custom, fd-specific stdout+ipc', testPrintErrorOrder, 'stdout', 'ipc', true); test('Prints error, verbose custom, fd-specific ipc+stdout', testPrintErrorOrder, 'ipc', 'stdout', false); test('Prints error, verbose custom, fd-specific stderr+fd3', testPrintErrorOrder, 'stderr', 'fd3', true); test('Prints error, verbose custom, fd-specific fd3+stderr', testPrintErrorOrder, 'fd3', 'stderr', false); test('Prints error, verbose custom, fd-specific stderr+ipc', testPrintErrorOrder, 'stderr', 'ipc', true); test('Prints error, verbose custom, fd-specific ipc+stderr', testPrintErrorOrder, 'ipc', 'stderr', false); test('Prints error, verbose custom, fd-specific fd3+ipc', testPrintErrorOrder, 'fd3', 'ipc', true); test('Prints error, verbose custom, fd-specific ipc+fd3', testPrintErrorOrder, 'ipc', 'fd3', false); const testPrintErrorFunction = async (t, fdNumber, secondFdNumber) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-function.js', type: 'error', fdNumber, secondFdNumber, ...fullStdio, }); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('Prints error, verbose custom, fd-specific stdout+stderr, single function', testPrintErrorFunction, 'stdout', 'stderr'); test('Prints error, verbose custom, fd-specific stderr+stdout, single function', testPrintErrorFunction, 'stderr', 'stdout'); test('Prints error, verbose custom, fd-specific stdout+fd3, single function', testPrintErrorFunction, 'stdout', 'fd3'); test('Prints error, verbose custom, fd-specific fd3+stdout, single function', testPrintErrorFunction, 'fd3', 'stdout'); test('Prints error, verbose custom, fd-specific stdout+ipc, single function', testPrintErrorFunction, 'stdout', 'ipc'); test('Prints error, verbose custom, fd-specific ipc+stdout, single function', testPrintErrorFunction, 'ipc', 'stdout'); test('Prints error, verbose custom, fd-specific stderr+fd3, single function', testPrintErrorFunction, 'stderr', 'fd3'); test('Prints error, verbose custom, fd-specific fd3+stderr, single function', testPrintErrorFunction, 'fd3', 'stderr'); test('Prints error, verbose custom, fd-specific stderr+ipc, single function', testPrintErrorFunction, 'stderr', 'ipc'); test('Prints error, verbose custom, fd-specific ipc+stderr, single function', testPrintErrorFunction, 'ipc', 'stderr'); test('Prints error, verbose custom, fd-specific fd3+ipc, single function', testPrintErrorFunction, 'fd3', 'ipc'); test('Prints error, verbose custom, fd-specific ipc+fd3, single function', testPrintErrorFunction, 'ipc', 'fd3'); const testVerboseMessage = async (t, isSync) => { const {stderr} = await runVerboseSubprocess({ isSync, type: 'error', eventProperty: 'message', }); t.is(stderr, `Command failed with exit code 2: noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('"verbose" function receives verboseObject.message', testVerboseMessage, false); test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); ================================================ FILE: test/verbose/custom-event.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {runVerboseSubprocess} from '../helpers/verbose.js'; setFixtureDirectory(); const testVerboseType = async (t, type, isSync) => { const {stderr} = await runVerboseSubprocess({isSync, type, eventProperty: 'type'}); t.is(stderr, type); }; test('"verbose" function receives verboseObject.type "command"', testVerboseType, 'command', false); test('"verbose" function receives verboseObject.type "output"', testVerboseType, 'output', false); test('"verbose" function receives verboseObject.type "ipc"', testVerboseType, 'ipc', false); test('"verbose" function receives verboseObject.type "error"', testVerboseType, 'error', false); test('"verbose" function receives verboseObject.type "duration"', testVerboseType, 'duration', false); test('"verbose" function receives verboseObject.type "command", sync', testVerboseType, 'command', true); test('"verbose" function receives verboseObject.type "output", sync', testVerboseType, 'output', true); test('"verbose" function receives verboseObject.type "error", sync', testVerboseType, 'error', true); test('"verbose" function receives verboseObject.type "duration", sync', testVerboseType, 'duration', true); const testVerboseTimestamp = async (t, type, isSync) => { const {stderr} = await runVerboseSubprocess({isSync, type, eventProperty: 'timestamp'}); t.true(Number.isInteger(new Date(stderr).getTime())); }; test('"verbose" function receives verboseObject.timestamp, "command"', testVerboseTimestamp, 'command', false); test('"verbose" function receives verboseObject.timestamp, "output"', testVerboseTimestamp, 'output', false); test('"verbose" function receives verboseObject.timestamp, "ipc"', testVerboseTimestamp, 'ipc', false); test('"verbose" function receives verboseObject.timestamp, "error"', testVerboseTimestamp, 'error', false); test('"verbose" function receives verboseObject.timestamp, "duration"', testVerboseTimestamp, 'duration', false); test('"verbose" function receives verboseObject.timestamp, "command", sync', testVerboseTimestamp, 'command', true); test('"verbose" function receives verboseObject.timestamp, "output", sync', testVerboseTimestamp, 'output', true); test('"verbose" function receives verboseObject.timestamp, "error", sync', testVerboseTimestamp, 'error', true); test('"verbose" function receives verboseObject.timestamp, "duration", sync', testVerboseTimestamp, 'duration', true); const testVerbosePiped = async (t, type, isSync, expectedOutputs) => { const {stderr} = await runVerboseSubprocess({ isSync, type, parentFixture: 'nested-pipe-verbose.js', destinationFile: 'noop-verbose.js', destinationArguments: ['. . .'], eventProperty: 'piped', }); t.true(expectedOutputs.map(expectedOutput => expectedOutput.join('\n')).includes(stderr)); }; test('"verbose" function receives verboseObject.piped, "command"', testVerbosePiped, 'command', false, [[false, true]]); test('"verbose" function receives verboseObject.piped, "output"', testVerbosePiped, 'output', false, [[true]]); test('"verbose" function receives verboseObject.piped, "ipc"', testVerbosePiped, 'ipc', false, [[false, true], [true, false]]); test('"verbose" function receives verboseObject.piped, "error"', testVerbosePiped, 'error', false, [[false, true], [true, false]]); test('"verbose" function receives verboseObject.piped, "duration"', testVerbosePiped, 'duration', false, [[false, true], [true, false]]); test('"verbose" function receives verboseObject.piped, "command", sync', testVerbosePiped, 'command', true, [[false, true]]); test('"verbose" function receives verboseObject.piped, "output", sync', testVerbosePiped, 'output', true, [[true]]); test('"verbose" function receives verboseObject.piped, "error", sync', testVerbosePiped, 'error', true, [[false, true], [true, false]]); test('"verbose" function receives verboseObject.piped, "duration", sync', testVerbosePiped, 'duration', true, [[false, true], [true, false]]); ================================================ FILE: test/verbose/custom-id.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {QUOTE, runVerboseSubprocess} from '../helpers/verbose.js'; setFixtureDirectory(); const testVerboseCommandId = async (t, type, isSync) => { const {stderr} = await runVerboseSubprocess({isSync, type, eventProperty: 'commandId'}); t.is(stderr, '0'); }; test('"verbose" function receives verboseObject.commandId, "command"', testVerboseCommandId, 'command', false); test('"verbose" function receives verboseObject.commandId, "output"', testVerboseCommandId, 'output', false); test('"verbose" function receives verboseObject.commandId, "ipc"', testVerboseCommandId, 'ipc', false); test('"verbose" function receives verboseObject.commandId, "error"', testVerboseCommandId, 'error', false); test('"verbose" function receives verboseObject.commandId, "duration"', testVerboseCommandId, 'duration', false); test('"verbose" function receives verboseObject.commandId, "command", sync', testVerboseCommandId, 'command', true); test('"verbose" function receives verboseObject.commandId, "output", sync', testVerboseCommandId, 'output', true); test('"verbose" function receives verboseObject.commandId, "error", sync', testVerboseCommandId, 'error', true); test('"verbose" function receives verboseObject.commandId, "duration", sync', testVerboseCommandId, 'duration', true); const testVerboseEscapedCommand = async (t, type, isSync) => { const {stderr} = await runVerboseSubprocess({isSync, type, eventProperty: 'escapedCommand'}); t.is(stderr, `noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('"verbose" function receives verboseObject.escapedCommand, "command"', testVerboseEscapedCommand, 'command', false); test('"verbose" function receives verboseObject.escapedCommand, "output"', testVerboseEscapedCommand, 'output', false); test('"verbose" function receives verboseObject.escapedCommand, "ipc"', testVerboseEscapedCommand, 'ipc', false); test('"verbose" function receives verboseObject.escapedCommand, "error"', testVerboseEscapedCommand, 'error', false); test('"verbose" function receives verboseObject.escapedCommand, "duration"', testVerboseEscapedCommand, 'duration', false); test('"verbose" function receives verboseObject.escapedCommand, "command", sync', testVerboseEscapedCommand, 'command', true); test('"verbose" function receives verboseObject.escapedCommand, "output", sync', testVerboseEscapedCommand, 'output', true); test('"verbose" function receives verboseObject.escapedCommand, "error", sync', testVerboseEscapedCommand, 'error', true); test('"verbose" function receives verboseObject.escapedCommand, "duration", sync', testVerboseEscapedCommand, 'duration', true); ================================================ FILE: test/verbose/custom-ipc.js ================================================ import {inspect} from 'node:util'; import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import { getNormalizedLine, getNormalizedLines, testTimestamp, runVerboseSubprocess, } from '../helpers/verbose.js'; import {nestedSubprocess} from '../helpers/nested.js'; import {foobarObject} from '../helpers/input.js'; setFixtureDirectory(); const testPrintIpcCustom = async (t, fdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print.js', type: 'ipc', fdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] * . .`); } else { t.is(stderr, ''); } }; test('Prints IPC, verbose custom', testPrintIpcCustom, undefined, true); test('Prints IPC, verbose custom, fd-specific stdout', testPrintIpcCustom, 'stdout', false); test('Prints IPC, verbose custom, fd-specific stderr', testPrintIpcCustom, 'stderr', false); test('Prints IPC, verbose custom, fd-specific fd3', testPrintIpcCustom, 'fd3', false); test('Prints IPC, verbose custom, fd-specific ipc', testPrintIpcCustom, 'ipc', true); const testPrintIpcOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-multiple.js', type: 'ipc', fdNumber, secondFdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] * . .`); } else { t.is(stderr, ''); } }; test('Prints IPC, verbose custom, fd-specific stdout+stderr', testPrintIpcOrder, 'stdout', 'stderr', false); test('Prints IPC, verbose custom, fd-specific stderr+stdout', testPrintIpcOrder, 'stderr', 'stdout', false); test('Prints IPC, verbose custom, fd-specific stdout+fd3', testPrintIpcOrder, 'stdout', 'fd3', false); test('Prints IPC, verbose custom, fd-specific fd3+stdout', testPrintIpcOrder, 'fd3', 'stdout', false); test('Prints IPC, verbose custom, fd-specific stdout+ipc', testPrintIpcOrder, 'stdout', 'ipc', false); test('Prints IPC, verbose custom, fd-specific ipc+stdout', testPrintIpcOrder, 'ipc', 'stdout', true); test('Prints IPC, verbose custom, fd-specific stderr+fd3', testPrintIpcOrder, 'stderr', 'fd3', false); test('Prints IPC, verbose custom, fd-specific fd3+stderr', testPrintIpcOrder, 'fd3', 'stderr', false); test('Prints IPC, verbose custom, fd-specific stderr+ipc', testPrintIpcOrder, 'stderr', 'ipc', false); test('Prints IPC, verbose custom, fd-specific ipc+stderr', testPrintIpcOrder, 'ipc', 'stderr', true); test('Prints IPC, verbose custom, fd-specific fd3+ipc', testPrintIpcOrder, 'fd3', 'ipc', false); test('Prints IPC, verbose custom, fd-specific ipc+fd3', testPrintIpcOrder, 'ipc', 'fd3', true); const testPrintIpcFunction = async (t, fdNumber, secondFdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-function.js', type: 'ipc', fdNumber, secondFdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] * . .`); } else { t.is(stderr, ''); } }; test('Prints IPC, verbose custom, fd-specific stdout+stderr, single function', testPrintIpcFunction, 'stdout', 'stderr', false); test('Prints IPC, verbose custom, fd-specific stderr+stdout, single function', testPrintIpcFunction, 'stderr', 'stdout', false); test('Prints IPC, verbose custom, fd-specific stdout+fd3, single function', testPrintIpcFunction, 'stdout', 'fd3', false); test('Prints IPC, verbose custom, fd-specific fd3+stdout, single function', testPrintIpcFunction, 'fd3', 'stdout', false); test('Prints IPC, verbose custom, fd-specific stdout+ipc, single function', testPrintIpcFunction, 'stdout', 'ipc', false); test('Prints IPC, verbose custom, fd-specific ipc+stdout, single function', testPrintIpcFunction, 'ipc', 'stdout', true); test('Prints IPC, verbose custom, fd-specific stderr+fd3, single function', testPrintIpcFunction, 'stderr', 'fd3', false); test('Prints IPC, verbose custom, fd-specific fd3+stderr, single function', testPrintIpcFunction, 'fd3', 'stderr', false); test('Prints IPC, verbose custom, fd-specific stderr+ipc, single function', testPrintIpcFunction, 'stderr', 'ipc', false); test('Prints IPC, verbose custom, fd-specific ipc+stderr, single function', testPrintIpcFunction, 'ipc', 'stderr', true); test('Prints IPC, verbose custom, fd-specific fd3+ipc, single function', testPrintIpcFunction, 'fd3', 'ipc', false); test('Prints IPC, verbose custom, fd-specific ipc+fd3, single function', testPrintIpcFunction, 'ipc', 'fd3', true); test('"verbose" function receives verboseObject.message', async t => { const {stderr} = await runVerboseSubprocess({ type: 'ipc', eventProperty: 'message', }); t.is(stderr, '. .'); }); test('"verbose" function receives verboseObject.message line-wise', async t => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print.js', type: 'ipc', output: '.\n.', }); t.deepEqual(getNormalizedLines(stderr), [`${testTimestamp} [0] * .`, `${testTimestamp} [0] * .`]); }); test('"verbose" function receives verboseObject.message serialized', async t => { const {stderr} = await nestedSubprocess('ipc-echo.js', { ipcInput: foobarObject, optionsFixture: 'custom-print.js', optionsInput: {type: 'ipc'}, }); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] * ${inspect(foobarObject)}`); }); ================================================ FILE: test/verbose/custom-options.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {runVerboseSubprocess} from '../helpers/verbose.js'; setFixtureDirectory(); const testVerboseOptionsExplicit = async (t, type, isSync) => { const maxBuffer = 1000; const {stderr} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-option.js', optionName: 'maxBuffer', maxBuffer, }); t.is(stderr, `${maxBuffer}`); }; test('"verbose" function receives verboseObject.options explicitly set, "command"', testVerboseOptionsExplicit, 'command', false); test('"verbose" function receives verboseObject.options explicitly set, "output"', testVerboseOptionsExplicit, 'output', false); test('"verbose" function receives verboseObject.options explicitly set, "ipc"', testVerboseOptionsExplicit, 'ipc', false); test('"verbose" function receives verboseObject.options explicitly set, "error"', testVerboseOptionsExplicit, 'error', false); test('"verbose" function receives verboseObject.options explicitly set, "duration"', testVerboseOptionsExplicit, 'duration', false); test('"verbose" function receives verboseObject.options explicitly set, "command", sync', testVerboseOptionsExplicit, 'command', true); test('"verbose" function receives verboseObject.options explicitly set, "output", sync', testVerboseOptionsExplicit, 'output', true); test('"verbose" function receives verboseObject.options explicitly set, "error", sync', testVerboseOptionsExplicit, 'error', true); test('"verbose" function receives verboseObject.options explicitly set, "duration", sync', testVerboseOptionsExplicit, 'duration', true); const testVerboseOptionsDefault = async (t, type, isSync) => { const {stderr} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-option.js', optionName: 'maxBuffer', }); t.is(stderr, 'undefined'); }; test('"verbose" function receives verboseObject.options before default values and normalization, "command"', testVerboseOptionsDefault, 'command', false); test('"verbose" function receives verboseObject.options before default values and normalization, "output"', testVerboseOptionsDefault, 'output', false); test('"verbose" function receives verboseObject.options before default values and normalization, "ipc"', testVerboseOptionsDefault, 'ipc', false); test('"verbose" function receives verboseObject.options before default values and normalization, "error"', testVerboseOptionsDefault, 'error', false); test('"verbose" function receives verboseObject.options before default values and normalization, "duration"', testVerboseOptionsDefault, 'duration', false); test('"verbose" function receives verboseObject.options before default values and normalization, "command", sync', testVerboseOptionsDefault, 'command', true); test('"verbose" function receives verboseObject.options before default values and normalization, "output", sync', testVerboseOptionsDefault, 'output', true); test('"verbose" function receives verboseObject.options before default values and normalization, "error", sync', testVerboseOptionsDefault, 'error', true); test('"verbose" function receives verboseObject.options before default values and normalization, "duration", sync', testVerboseOptionsDefault, 'duration', true); ================================================ FILE: test/verbose/custom-output.js ================================================ import {inspect} from 'node:util'; import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import { getNormalizedLine, getNormalizedLines, testTimestamp, runVerboseSubprocess, } from '../helpers/verbose.js'; import {nestedSubprocess} from '../helpers/nested.js'; import {foobarObject} from '../helpers/input.js'; setFixtureDirectory(); const testPrintOutputCustom = async (t, fdNumber, isSync, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print.js', isSync, type: 'output', fdNumber, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] . .`); } else { t.is(stderr, ''); } }; test('Prints stdout, verbose custom', testPrintOutputCustom, undefined, false, true); test('Prints stdout, verbose custom, fd-specific stdout', testPrintOutputCustom, 'stdout', false, true); test('Prints stdout, verbose custom, fd-specific stderr', testPrintOutputCustom, 'stderr', false, false); test('Prints stdout, verbose custom, fd-specific fd3', testPrintOutputCustom, 'fd3', false, false); test('Prints stdout, verbose custom, fd-specific ipc', testPrintOutputCustom, 'ipc', false, false); test('Prints stdout, verbose custom, sync', testPrintOutputCustom, undefined, true, true); test('Prints stdout, verbose custom, fd-specific stdout, sync', testPrintOutputCustom, 'stdout', true, true); test('Prints stdout, verbose custom, fd-specific stderr, sync', testPrintOutputCustom, 'stderr', true, false); test('Prints stdout, verbose custom, fd-specific fd3, sync', testPrintOutputCustom, 'fd3', true, false); test('Prints stdout, verbose custom, fd-specific ipc, sync', testPrintOutputCustom, 'ipc', true, false); const testPrintOutputOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-multiple.js', type: 'output', fdNumber, secondFdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] . .`); } else { t.is(stderr, ''); } }; test('Prints stdout, verbose custom, fd-specific stdout+stderr', testPrintOutputOrder, 'stdout', 'stderr', true); test('Prints stdout, verbose custom, fd-specific stderr+stdout', testPrintOutputOrder, 'stderr', 'stdout', false); test('Prints stdout, verbose custom, fd-specific stdout+fd3', testPrintOutputOrder, 'stdout', 'fd3', true); test('Prints stdout, verbose custom, fd-specific fd3+stdout', testPrintOutputOrder, 'fd3', 'stdout', false); test('Prints stdout, verbose custom, fd-specific stdout+ipc', testPrintOutputOrder, 'stdout', 'ipc', true); test('Prints stdout, verbose custom, fd-specific ipc+stdout', testPrintOutputOrder, 'ipc', 'stdout', false); test('Prints stdout, verbose custom, fd-specific stderr+fd3', testPrintOutputOrder, 'stderr', 'fd3', false); test('Prints stdout, verbose custom, fd-specific fd3+stderr', testPrintOutputOrder, 'fd3', 'stderr', false); test('Prints stdout, verbose custom, fd-specific stderr+ipc', testPrintOutputOrder, 'stderr', 'ipc', false); test('Prints stdout, verbose custom, fd-specific ipc+stderr', testPrintOutputOrder, 'ipc', 'stderr', false); test('Prints stdout, verbose custom, fd-specific fd3+ipc', testPrintOutputOrder, 'fd3', 'ipc', false); test('Prints stdout, verbose custom, fd-specific ipc+fd3', testPrintOutputOrder, 'ipc', 'fd3', false); const testPrintOutputFunction = async (t, fdNumber, secondFdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-function.js', type: 'output', fdNumber, secondFdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] . .`); } else { t.is(stderr, ''); } }; test('Prints stdout, verbose custom, fd-specific stdout+stderr, single function', testPrintOutputFunction, 'stdout', 'stderr', true); test('Prints stdout, verbose custom, fd-specific stderr+stdout, single function', testPrintOutputFunction, 'stderr', 'stdout', false); test('Prints stdout, verbose custom, fd-specific stdout+fd3, single function', testPrintOutputFunction, 'stdout', 'fd3', true); test('Prints stdout, verbose custom, fd-specific fd3+stdout, single function', testPrintOutputFunction, 'fd3', 'stdout', false); test('Prints stdout, verbose custom, fd-specific stdout+ipc, single function', testPrintOutputFunction, 'stdout', 'ipc', true); test('Prints stdout, verbose custom, fd-specific ipc+stdout, single function', testPrintOutputFunction, 'ipc', 'stdout', false); test('Prints stdout, verbose custom, fd-specific stderr+fd3, single function', testPrintOutputFunction, 'stderr', 'fd3', false); test('Prints stdout, verbose custom, fd-specific fd3+stderr, single function', testPrintOutputFunction, 'fd3', 'stderr', false); test('Prints stdout, verbose custom, fd-specific stderr+ipc, single function', testPrintOutputFunction, 'stderr', 'ipc', false); test('Prints stdout, verbose custom, fd-specific ipc+stderr, single function', testPrintOutputFunction, 'ipc', 'stderr', false); test('Prints stdout, verbose custom, fd-specific fd3+ipc, single function', testPrintOutputFunction, 'fd3', 'ipc', false); test('Prints stdout, verbose custom, fd-specific ipc+fd3, single function', testPrintOutputFunction, 'ipc', 'fd3', false); const testVerboseMessage = async (t, isSync) => { const {stderr} = await runVerboseSubprocess({ isSync, type: 'output', eventProperty: 'message', }); t.is(stderr, '. .'); }; test('"verbose" function receives verboseObject.message', testVerboseMessage, false); test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); const testPrintOutputMultiline = async (t, isSync) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print.js', isSync, type: 'output', output: '.\n.', }); t.deepEqual(getNormalizedLines(stderr), [`${testTimestamp} [0] .`, `${testTimestamp} [0] .`]); }; test('"verbose" function receives verboseObject.message line-wise', testPrintOutputMultiline, false); test('"verbose" function receives verboseObject.message line-wise, sync', testPrintOutputMultiline, true); test('"verbose" function receives verboseObject.message serialized', async t => { const {stderr} = await nestedSubprocess('noop.js', {optionsFixture: 'custom-object-stdout.js'}); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] ${inspect(foobarObject)}`); }); ================================================ FILE: test/verbose/custom-reject.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {runVerboseSubprocess} from '../helpers/verbose.js'; import {earlyErrorOptions, earlyErrorOptionsSync} from '../helpers/early-error.js'; setFixtureDirectory(); // eslint-disable-next-line max-params const testVerboseReject = async (t, type, options, isSync, expectedOutput) => { const {stderr} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-option.js', optionName: 'reject', ...options, }); t.is(stderr, expectedOutput.map(String).join('\n')); }; test('"verbose" function receives verboseObject.options.reject, "command"', testVerboseReject, 'command', {}, false, [undefined]); test('"verbose" function receives verboseObject.options.reject, "output"', testVerboseReject, 'output', {}, false, [undefined]); test('"verbose" function receives verboseObject.options.reject, "ipc"', testVerboseReject, 'ipc', {}, false, [undefined]); test('"verbose" function receives verboseObject.options.reject, "error"', testVerboseReject, 'error', {}, false, [undefined]); test('"verbose" function receives verboseObject.options.reject, "duration"', testVerboseReject, 'duration', {}, false, [undefined]); test('"verbose" function receives verboseObject.options.reject, "command", spawn error', testVerboseReject, 'command', earlyErrorOptions, false, [undefined]); test('"verbose" function receives verboseObject.options.reject, "output", spawn error', testVerboseReject, 'output', earlyErrorOptions, false, []); test('"verbose" function receives verboseObject.options.reject, "ipc", spawn error', testVerboseReject, 'ipc', earlyErrorOptions, false, []); test('"verbose" function receives verboseObject.options.reject, "error", spawn error', testVerboseReject, 'error', earlyErrorOptions, false, [undefined, undefined]); test('"verbose" function receives verboseObject.options.reject, "duration", spawn error', testVerboseReject, 'duration', earlyErrorOptions, false, [undefined]); test('"verbose" function receives verboseObject.options.reject, "command", sync', testVerboseReject, 'command', {}, true, [undefined]); test('"verbose" function receives verboseObject.options.reject, "output", sync', testVerboseReject, 'output', {}, true, [undefined]); test('"verbose" function receives verboseObject.options.reject, "error", sync', testVerboseReject, 'error', {}, true, [undefined]); test('"verbose" function receives verboseObject.options.reject, "duration", sync', testVerboseReject, 'duration', {}, true, [undefined]); test('"verbose" function receives verboseObject.options.reject, "command", spawn error, sync', testVerboseReject, 'command', earlyErrorOptionsSync, true, [undefined]); test('"verbose" function receives verboseObject.options.reject, "output", spawn error, sync', testVerboseReject, 'output', earlyErrorOptionsSync, true, []); test('"verbose" function receives verboseObject.options.reject, "error", spawn error, sync', testVerboseReject, 'error', earlyErrorOptionsSync, true, [undefined, undefined]); test('"verbose" function receives verboseObject.options.reject, "duration", spawn error, sync', testVerboseReject, 'duration', earlyErrorOptionsSync, true, [undefined]); ================================================ FILE: test/verbose/custom-result.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {runVerboseSubprocess} from '../helpers/verbose.js'; import { earlyErrorOptions, earlyErrorOptionsSync, expectedEarlyError, expectedEarlyErrorSync, } from '../helpers/early-error.js'; setFixtureDirectory(); const testVerboseResultEnd = async (t, type, isSync) => { const {stderr: parentStderr} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-result.js', }); const {failed, exitCode, stdout, stderr, ipcOutput, durationMs} = JSON.parse(parentStderr); t.true(failed); t.is(exitCode, 2); t.is(stdout, '. .'); t.is(stderr, ''); t.is(typeof durationMs, 'number'); t.deepEqual(ipcOutput, isSync ? [] : ['. .']); }; test('"verbose" function receives verboseObject.result, "error"', testVerboseResultEnd, 'error', false); test('"verbose" function receives verboseObject.result, "duration"', testVerboseResultEnd, 'duration', false); test('"verbose" function receives verboseObject.result, "error", sync', testVerboseResultEnd, 'error', true); test('"verbose" function receives verboseObject.result, "duration", sync', testVerboseResultEnd, 'duration', true); // eslint-disable-next-line max-params const testVerboseResultEndSpawn = async (t, type, options, expectedOutput, isSync) => { const {stderr: parentStderr} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-result.js', ...options, }); const lastLine = parentStderr.split('\n').at(-1); const result = JSON.parse(lastLine); t.like(result, expectedOutput); t.true(result.failed); t.is(result.exitCode, undefined); t.is(result.stdout, undefined); t.is(result.stderr, undefined); t.is(typeof result.durationMs, 'number'); t.deepEqual(result.ipcOutput, []); }; test('"verbose" function receives verboseObject.result, "error", spawn error', testVerboseResultEndSpawn, 'error', earlyErrorOptions, expectedEarlyError, false); test('"verbose" function receives verboseObject.result, "duration", spawn error', testVerboseResultEndSpawn, 'duration', earlyErrorOptions, expectedEarlyError, false); test('"verbose" function receives verboseObject.result, "error", spawn error, sync', testVerboseResultEndSpawn, 'error', earlyErrorOptionsSync, expectedEarlyErrorSync, true); test('"verbose" function receives verboseObject.result, "duration", spawn error, sync', testVerboseResultEndSpawn, 'duration', earlyErrorOptionsSync, expectedEarlyErrorSync, true); const testVerboseResultStart = async (t, type, options, isSync) => { const {stderr: parentStderr} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-result.js', ...options, }); t.is(parentStderr, ''); }; test('"verbose" function does not receive verboseObject.result, "command"', testVerboseResultStart, 'command', {}, false); test('"verbose" function does not receive verboseObject.result, "output"', testVerboseResultStart, 'output', {}, false); test('"verbose" function does not receive verboseObject.result, "ipc"', testVerboseResultStart, 'ipc', {}, false); test('"verbose" function does not receive verboseObject.result, "command", spawn error', testVerboseResultStart, 'command', earlyErrorOptions, false); test('"verbose" function does not receive verboseObject.result, "output", spawn error', testVerboseResultStart, 'output', earlyErrorOptions, false); test('"verbose" function does not receive verboseObject.result, "ipc", spawn error', testVerboseResultStart, 'ipc', earlyErrorOptions, false); test('"verbose" function does not receive verboseObject.result, "command", sync', testVerboseResultStart, 'command', {}, true); test('"verbose" function does not receive verboseObject.result, "output", sync', testVerboseResultStart, 'output', {}, true); test('"verbose" function does not receive verboseObject.result, "command", spawn error, sync', testVerboseResultStart, 'command', earlyErrorOptionsSync, true); test('"verbose" function does not receive verboseObject.result, "output", spawn error, sync', testVerboseResultStart, 'output', earlyErrorOptionsSync, true); ================================================ FILE: test/verbose/custom-start.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; import { QUOTE, getNormalizedLine, testTimestamp, runVerboseSubprocess, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintCommandCustom = async (t, fdNumber, worker, isSync) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print.js', worker, isSync, type: 'command', fdNumber, }); t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('Prints command, verbose custom', testPrintCommandCustom, undefined, false, false); test('Prints command, verbose custom, fd-specific stdout', testPrintCommandCustom, 'stdout', false, false); test('Prints command, verbose custom, fd-specific stderr', testPrintCommandCustom, 'stderr', false, false); test('Prints command, verbose custom, fd-specific fd3', testPrintCommandCustom, 'fd3', false, false); test('Prints command, verbose custom, fd-specific ipc', testPrintCommandCustom, 'ipc', false, false); test('Prints command, verbose custom, sync', testPrintCommandCustom, undefined, false, true); test('Prints command, verbose custom, fd-specific stdout, sync', testPrintCommandCustom, 'stdout', false, true); test('Prints command, verbose custom, fd-specific stderr, sync', testPrintCommandCustom, 'stderr', false, true); test('Prints command, verbose custom, fd-specific fd3, sync', testPrintCommandCustom, 'fd3', false, true); test('Prints command, verbose custom, fd-specific ipc, sync', testPrintCommandCustom, 'ipc', false, true); test('Prints command, verbose custom, worker', testPrintCommandCustom, undefined, true, false); test('Prints command, verbose custom, fd-specific stdout, worker', testPrintCommandCustom, 'stdout', true, false); test('Prints command, verbose custom, fd-specific stderr, worker', testPrintCommandCustom, 'stderr', true, false); test('Prints command, verbose custom, fd-specific fd3, worker', testPrintCommandCustom, 'fd3', true, false); test('Prints command, verbose custom, fd-specific ipc, worker', testPrintCommandCustom, 'ipc', true, false); test('Prints command, verbose custom, worker, sync', testPrintCommandCustom, undefined, true, true); test('Prints command, verbose custom, fd-specific stdout, worker, sync', testPrintCommandCustom, 'stdout', true, true); test('Prints command, verbose custom, fd-specific stderr, worker, sync', testPrintCommandCustom, 'stderr', true, true); test('Prints command, verbose custom, fd-specific fd3, worker, sync', testPrintCommandCustom, 'fd3', true, true); test('Prints command, verbose custom, fd-specific ipc, worker, sync', testPrintCommandCustom, 'ipc', true, true); const testPrintCommandOrder = async (t, fdNumber, secondFdNumber, hasOutput) => { const {stderr} = await runVerboseSubprocess({ optionsFixture: 'custom-print-multiple.js', type: 'command', fdNumber, secondFdNumber, ...fullStdio, }); if (hasOutput) { t.is(getNormalizedLine(stderr), `${testTimestamp} [0] $ noop-verbose.js ${QUOTE}. .${QUOTE}`); } else { t.is(stderr, ''); } }; test('Prints command, verbose custom, fd-specific stdout+stderr', testPrintCommandOrder, 'stdout', 'stderr', true); test('Prints command, verbose custom, fd-specific stderr+stdout', testPrintCommandOrder, 'stderr', 'stdout', false); test('Prints command, verbose custom, fd-specific stdout+fd3', testPrintCommandOrder, 'stdout', 'fd3', true); test('Prints command, verbose custom, fd-specific fd3+stdout', testPrintCommandOrder, 'fd3', 'stdout', false); test('Prints command, verbose custom, fd-specific stdout+ipc', testPrintCommandOrder, 'stdout', 'ipc', true); test('Prints command, verbose custom, fd-specific ipc+stdout', testPrintCommandOrder, 'ipc', 'stdout', false); test('Prints command, verbose custom, fd-specific stderr+fd3', testPrintCommandOrder, 'stderr', 'fd3', true); test('Prints command, verbose custom, fd-specific fd3+stderr', testPrintCommandOrder, 'fd3', 'stderr', false); test('Prints command, verbose custom, fd-specific stderr+ipc', testPrintCommandOrder, 'stderr', 'ipc', true); test('Prints command, verbose custom, fd-specific ipc+stderr', testPrintCommandOrder, 'ipc', 'stderr', false); test('Prints command, verbose custom, fd-specific fd3+ipc', testPrintCommandOrder, 'fd3', 'ipc', true); test('Prints command, verbose custom, fd-specific ipc+fd3', testPrintCommandOrder, 'ipc', 'fd3', false); const testVerboseMessage = async (t, isSync) => { const {stderr} = await runVerboseSubprocess({ isSync, type: 'command', eventProperty: 'message', }); t.is(stderr, `noop-verbose.js ${QUOTE}. .${QUOTE}`); }; test('"verbose" function receives verboseObject.message', testVerboseMessage, false); test('"verbose" function receives verboseObject.message, sync', testVerboseMessage, true); ================================================ FILE: test/verbose/custom-throw.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {runVerboseSubprocess} from '../helpers/verbose.js'; import {earlyErrorOptions, earlyErrorOptionsSync} from '../helpers/early-error.js'; setFixtureDirectory(); const testCommandThrowPropagate = async (t, type, options, isSync) => { const {nestedResult} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-throw.js', errorMessage: foobarString, ...options, }); t.true(nestedResult instanceof Error); t.is(nestedResult.message, foobarString); }; test('Propagate verbose exception in "verbose" function, "command"', testCommandThrowPropagate, 'command', {}, false); test('Propagate verbose exception in "verbose" function, "error"', testCommandThrowPropagate, 'error', {}, false); test('Propagate verbose exception in "verbose" function, "duration"', testCommandThrowPropagate, 'duration', {}, false); test('Propagate verbose exception in "verbose" function, "command", spawn error', testCommandThrowPropagate, 'command', earlyErrorOptions, false); test('Propagate verbose exception in "verbose" function, "error", spawn error', testCommandThrowPropagate, 'error', earlyErrorOptions, false); test('Propagate verbose exception in "verbose" function, "duration", spawn error', testCommandThrowPropagate, 'duration', earlyErrorOptions, false); test('Propagate verbose exception in "verbose" function, "command", sync', testCommandThrowPropagate, 'command', {}, true); test('Propagate verbose exception in "verbose" function, "error", sync', testCommandThrowPropagate, 'error', {}, true); test('Propagate verbose exception in "verbose" function, "duration", sync', testCommandThrowPropagate, 'duration', {}, true); test('Propagate verbose exception in "verbose" function, "command", spawn error, sync', testCommandThrowPropagate, 'command', earlyErrorOptionsSync, true); test('Propagate verbose exception in "verbose" function, "error", spawn error, sync', testCommandThrowPropagate, 'error', earlyErrorOptionsSync, true); test('Propagate verbose exception in "verbose" function, "duration", spawn error, sync', testCommandThrowPropagate, 'duration', earlyErrorOptionsSync, true); const testCommandThrowHandle = async (t, type, isSync) => { const {nestedResult} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-throw.js', errorMessage: foobarString, }); t.true(nestedResult instanceof Error); t.true(nestedResult.stack.startsWith(isSync ? 'ExecaSyncError' : 'ExecaError')); t.true(nestedResult.cause instanceof Error); t.is(nestedResult.cause.message, foobarString); }; test('Handle exceptions in "verbose" function, "output"', testCommandThrowHandle, 'output', false); test('Handle exceptions in "verbose" function, "ipc"', testCommandThrowHandle, 'ipc', false); test('Handle exceptions in "verbose" function, "output", sync', testCommandThrowHandle, 'output', true); const testCommandThrowWrap = async (t, type, options, isSync) => { const {nestedResult} = await runVerboseSubprocess({ isSync, type, optionsFixture: 'custom-throw.js', errorMessage: foobarString, ...options, }); t.true(nestedResult instanceof Error); t.true(nestedResult.stack.startsWith(isSync ? 'ExecaSyncError' : 'ExecaError')); t.true(nestedResult.cause instanceof Error); t.not(nestedResult.cause.message, foobarString); }; test('Propagate wrapped exception in "verbose" function, "output", spawn error', testCommandThrowWrap, 'output', earlyErrorOptions, false); test('Propagate wrapped exception in "verbose" function, "ipc", spawn error', testCommandThrowWrap, 'ipc', earlyErrorOptions, false); test('Propagate wrapped exception in "verbose" function, "output", spawn error, sync', testCommandThrowWrap, 'output', earlyErrorOptionsSync, true); ================================================ FILE: test/verbose/error.js ================================================ import test from 'ava'; import {red} from 'yoctocolors'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { QUOTE, runErrorSubprocess, runEarlyErrorSubprocess, getErrorLine, getErrorLines, testTimestamp, getVerboseOption, stdoutNoneOption, stdoutShortOption, stdoutFullOption, stderrNoneOption, stderrShortOption, stderrFullOption, fd3NoneOption, fd3ShortOption, fd3FullOption, ipcNoneOption, ipcShortOption, ipcFullOption, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintError = async (t, verbose, isSync) => { const stderr = await runErrorSubprocess(t, verbose, isSync); t.is(getErrorLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-fail.js 1 ${foobarString}`); }; test('Prints error, verbose "short"', testPrintError, 'short', false); test('Prints error, verbose "full"', testPrintError, 'full', false); test('Prints error, verbose "short", fd-specific stdout', testPrintError, stdoutShortOption, false); test('Prints error, verbose "full", fd-specific stdout', testPrintError, stdoutFullOption, false); test('Prints error, verbose "short", fd-specific stderr', testPrintError, stderrShortOption, false); test('Prints error, verbose "full", fd-specific stderr', testPrintError, stderrFullOption, false); test('Prints error, verbose "short", fd-specific fd3', testPrintError, fd3ShortOption, false); test('Prints error, verbose "full", fd-specific fd3', testPrintError, fd3FullOption, false); test('Prints error, verbose "short", fd-specific ipc', testPrintError, ipcShortOption, false); test('Prints error, verbose "full", fd-specific ipc', testPrintError, ipcFullOption, false); test('Prints error, verbose "short", sync', testPrintError, 'short', true); test('Prints error, verbose "full", sync', testPrintError, 'full', true); test('Prints error, verbose "short", fd-specific stdout, sync', testPrintError, stdoutShortOption, true); test('Prints error, verbose "full", fd-specific stdout, sync', testPrintError, stdoutFullOption, true); test('Prints error, verbose "short", fd-specific stderr, sync', testPrintError, stderrShortOption, true); test('Prints error, verbose "full", fd-specific stderr, sync', testPrintError, stderrFullOption, true); test('Prints error, verbose "short", fd-specific fd3, sync', testPrintError, fd3ShortOption, true); test('Prints error, verbose "full", fd-specific fd3, sync', testPrintError, fd3FullOption, true); test('Prints error, verbose "short", fd-specific ipc, sync', testPrintError, ipcShortOption, true); test('Prints error, verbose "full", fd-specific ipc, sync', testPrintError, ipcFullOption, true); const testNoPrintError = async (t, verbose, isSync) => { const stderr = await runErrorSubprocess(t, verbose, isSync, false); t.is(getErrorLine(stderr), undefined); }; test('Does not print error, verbose "none"', testNoPrintError, 'none', false); test('Does not print error, verbose default', testNoPrintError, undefined, false); test('Does not print error, verbose "none", fd-specific stdout', testNoPrintError, stdoutNoneOption, false); test('Does not print error, verbose "none", fd-specific stderr', testNoPrintError, stderrNoneOption, false); test('Does not print error, verbose "none", fd-specific fd3', testNoPrintError, fd3NoneOption, false); test('Does not print error, verbose "none", fd-specific ipc', testNoPrintError, ipcNoneOption, false); test('Does not print error, verbose default, fd-specific', testNoPrintError, {}, false); test('Does not print error, verbose "none", sync', testNoPrintError, 'none', true); test('Does not print error, verbose default, sync', testNoPrintError, undefined, true); test('Does not print error, verbose "none", fd-specific stdout, sync', testNoPrintError, stdoutNoneOption, true); test('Does not print error, verbose "none", fd-specific stderr, sync', testNoPrintError, stderrNoneOption, true); test('Does not print error, verbose "none", fd-specific fd3, sync', testNoPrintError, fd3NoneOption, true); test('Does not print error, verbose "none", fd-specific ipc, sync', testNoPrintError, ipcNoneOption, true); test('Does not print error, verbose default, fd-specific, sync', testNoPrintError, {}, true); const testPrintNoError = async (t, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short', isSync}); t.is(getErrorLine(stderr), undefined); }; test('Does not print error if none', testPrintNoError, false); test('Does not print error if none, sync', testPrintNoError, true); const testPrintErrorEarly = async (t, isSync) => { const stderr = await runEarlyErrorSubprocess(t, isSync); t.is(getErrorLine(stderr), undefined); }; test('Prints early validation error', testPrintErrorEarly, false); test('Prints early validation error, sync', testPrintErrorEarly, true); test('Does not repeat stdout|stderr with error', async t => { const stderr = await runErrorSubprocess(t, 'short'); t.deepEqual(getErrorLines(stderr), [`${testTimestamp} [0] × Command failed with exit code 2: noop-fail.js 1 ${foobarString}`]); }); test('Prints error differently if "reject" is false', async t => { const {stderr} = await nestedSubprocess('noop-fail.js', ['1', foobarString], {verbose: 'short', reject: false}); t.deepEqual(getErrorLines(stderr), [`${testTimestamp} [0] ‼ Command failed with exit code 2: noop-fail.js 1 ${foobarString}`]); }); const testPipeError = async (t, parentFixture, sourceVerbose, destinationVerbose) => { const {stderr} = await t.throwsAsync(nestedSubprocess('noop-fail.js', ['1'], { parentFixture, sourceOptions: getVerboseOption(sourceVerbose), destinationFile: 'stdin-fail.js', destinationOptions: getVerboseOption(destinationVerbose), })); const lines = getErrorLines(stderr); t.is(lines.includes(`${testTimestamp} [0] × Command failed with exit code 2: noop-fail.js 1`), sourceVerbose); t.is(lines.includes(`${testTimestamp} [${sourceVerbose ? 1 : 0}] × Command failed with exit code 2: stdin-fail.js`), destinationVerbose); }; test('Prints both errors piped with .pipe("file")', testPipeError, 'nested-pipe-file.js', true, true); test('Prints both errors piped with .pipe`command`', testPipeError, 'nested-pipe-script.js', true, true); test('Prints both errors piped with .pipe(subprocess)', testPipeError, 'nested-pipe-subprocesses.js', true, true); test('Prints first error piped with .pipe("file")', testPipeError, 'nested-pipe-file.js', true, false); test('Prints first error piped with .pipe`command`', testPipeError, 'nested-pipe-script.js', true, false); test('Prints first error piped with .pipe(subprocess)', testPipeError, 'nested-pipe-subprocesses.js', true, false); test('Prints second error piped with .pipe("file")', testPipeError, 'nested-pipe-file.js', false, true); test('Prints second error piped with .pipe`command`', testPipeError, 'nested-pipe-script.js', false, true); test('Prints second error piped with .pipe(subprocess)', testPipeError, 'nested-pipe-subprocesses.js', false, true); test('Prints neither errors piped with .pipe("file")', testPipeError, 'nested-pipe-file.js', false, false); test('Prints neither errors piped with .pipe`command`', testPipeError, 'nested-pipe-script.js', false, false); test('Prints neither errors piped with .pipe(subprocess)', testPipeError, 'nested-pipe-subprocesses.js', false, false); test('Quotes spaces from error', async t => { const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['foo bar'], {parentFixture: 'nested-fail.js', verbose: 'short'})); t.deepEqual(getErrorLines(stderr), [ `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}foo bar${QUOTE}`, `${testTimestamp} [0] × foo bar`, ]); }); test('Quotes special punctuation from error', async t => { const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['%'], {parentFixture: 'nested-fail.js', verbose: 'short'})); t.deepEqual(getErrorLines(stderr), [ `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}%${QUOTE}`, `${testTimestamp} [0] × %`, ]); }); test('Does not escape internal characters from error', async t => { const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['ã'], {parentFixture: 'nested-fail.js', verbose: 'short'})); t.deepEqual(getErrorLines(stderr), [ `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}ã${QUOTE}`, `${testTimestamp} [0] × ã`, ]); }); test('Escapes and strips color sequences from error', async t => { const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', [red(foobarString)], {parentFixture: 'nested-fail.js', verbose: 'short'}, {env: {FORCE_COLOR: '1'}})); t.deepEqual(getErrorLines(stderr), [ `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}\\u001b[31m${foobarString}\\u001b[39m${QUOTE}`, `${testTimestamp} [0] × ${foobarString}`, ]); }); test('Escapes control characters from error', async t => { const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['\u0001'], {parentFixture: 'nested-fail.js', verbose: 'short'})); t.deepEqual(getErrorLines(stderr), [ `${testTimestamp} [0] × Command was killed with SIGTERM (Termination): noop-forever.js ${QUOTE}\\u0001${QUOTE}`, `${testTimestamp} [0] × \\u0001`, ]); }); ================================================ FILE: test/verbose/info.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {execa, execaSync} from '../../index.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { QUOTE, getCommandLine, getOutputLine, getNormalizedLines, testTimestamp, } from '../helpers/verbose.js'; import {earlyErrorOptions, earlyErrorOptionsSync} from '../helpers/early-error.js'; setFixtureDirectory(); const testVerboseGeneral = async (t, execaMethod) => { const {all} = await execaMethod('verbose-script.js', {env: {NODE_DEBUG: 'execa'}, all: true}); t.deepEqual(getNormalizedLines(all), [ `${testTimestamp} [0] $ node -e ${QUOTE}console.error(1)${QUOTE}`, '1', `${testTimestamp} [0] √ (done in 0ms)`, `${testTimestamp} [1] $ node -e ${QUOTE}process.exit(2)${QUOTE}`, `${testTimestamp} [1] ‼ Command failed with exit code 2: node -e ${QUOTE}process.exit(2)${QUOTE}`, `${testTimestamp} [1] ‼ (done in 0ms)`, ]); }; test('Prints command, NODE_DEBUG=execa + "inherit"', testVerboseGeneral, execa); test('Prints command, NODE_DEBUG=execa + "inherit", sync', testVerboseGeneral, execaSync); test('NODE_DEBUG=execa changes verbose default value to "full"', async t => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {}, {env: {NODE_DEBUG: 'execa'}}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${foobarString}`); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }); const testDebugEnvPriority = async (t, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short', isSync}, {env: {NODE_DEBUG: 'execa'}}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${foobarString}`); t.is(getOutputLine(stderr), undefined); }; test('NODE_DEBUG=execa has lower priority', testDebugEnvPriority, false); test('NODE_DEBUG=execa has lower priority, sync', testDebugEnvPriority, true); const invalidFalseMessage = 'renamed to "verbose: \'none\'"'; const invalidTrueMessage = 'renamed to "verbose: \'short\'"'; const invalidUnknownMessage = 'Allowed values are: \'none\', \'short\', \'full\''; const testInvalidVerbose = (t, verbose, expectedMessage, execaMethod) => { const {message} = t.throws(() => { execaMethod('empty.js', {verbose}); }); t.true(message.includes(expectedMessage)); }; test('Does not allow "verbose: false"', testInvalidVerbose, false, invalidFalseMessage, execa); test('Does not allow "verbose: false", sync', testInvalidVerbose, false, invalidFalseMessage, execaSync); test('Does not allow "verbose: true"', testInvalidVerbose, true, invalidTrueMessage, execa); test('Does not allow "verbose: true", sync', testInvalidVerbose, true, invalidTrueMessage, execaSync); test('Does not allow "verbose: \'unknown\'"', testInvalidVerbose, 'unknown', invalidUnknownMessage, execa); test('Does not allow "verbose: \'unknown\'", sync', testInvalidVerbose, 'unknown', invalidUnknownMessage, execaSync); const testValidationError = async (t, isSync) => { const {stderr, nestedResult} = await nestedSubprocess('empty.js', {verbose: 'full', isSync, timeout: []}); t.deepEqual(getNormalizedLines(stderr), [`${testTimestamp} [0] $ empty.js`]); t.true(nestedResult instanceof Error); }; test('Prints validation errors', testValidationError, false); test('Prints validation errors, sync', testValidationError, true); test('Prints early spawn errors', async t => { const {stderr} = await nestedSubprocess('empty.js', {...earlyErrorOptions, verbose: 'full'}); t.deepEqual(getNormalizedLines(stderr), [ `${testTimestamp} [0] $ empty.js`, `${testTimestamp} [0] × Command failed with ERR_INVALID_ARG_TYPE: empty.js`, `${testTimestamp} [0] × The "options.detached" property must be of type boolean. Received type string ('true')`, `${testTimestamp} [0] × (done in 0ms)`, ]); }); test('Prints early spawn errors, sync', async t => { const {stderr} = await nestedSubprocess('empty.js', {...earlyErrorOptionsSync, verbose: 'full', isSync: true}); t.deepEqual(getNormalizedLines(stderr), [ `${testTimestamp} [0] $ empty.js`, `${testTimestamp} [0] × Command failed with ERR_OUT_OF_RANGE: empty.js`, `${testTimestamp} [0] × The value of "options.maxBuffer" is out of range. It must be a positive number. Received false`, `${testTimestamp} [0] × (done in 0ms)`, ]); }); ================================================ FILE: test/verbose/ipc.js ================================================ import {on} from 'node:events'; import {inspect} from 'node:util'; import test from 'ava'; import {red} from 'yoctocolors'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarObject} from '../helpers/input.js'; import {nestedSubprocess, nestedInstance} from '../helpers/nested.js'; import { getIpcLine, getIpcLines, testTimestamp, ipcNoneOption, ipcShortOption, ipcFullOption, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintIpc = async (t, verbose) => { const {stderr} = await nestedSubprocess('ipc-send.js', {ipc: true, verbose}); t.is(getIpcLine(stderr), `${testTimestamp} [0] * ${foobarString}`); }; test('Prints IPC, verbose "full"', testPrintIpc, 'full'); test('Prints IPC, verbose "full", fd-specific', testPrintIpc, ipcFullOption); const testNoPrintIpc = async (t, verbose) => { const {stderr} = await nestedSubprocess('ipc-send.js', {ipc: true, verbose}); t.is(getIpcLine(stderr), undefined); }; test('Does not print IPC, verbose default', testNoPrintIpc, undefined); test('Does not print IPC, verbose "none"', testNoPrintIpc, 'none'); test('Does not print IPC, verbose "short"', testNoPrintIpc, 'short'); test('Does not print IPC, verbose default, fd-specific', testNoPrintIpc, {}); test('Does not print IPC, verbose "none", fd-specific', testNoPrintIpc, ipcNoneOption); test('Does not print IPC, verbose "short", fd-specific', testNoPrintIpc, ipcShortOption); const testNoIpc = async (t, ipc) => { const {nestedResult, stderr} = await nestedSubprocess('ipc-send.js', {ipc, verbose: 'full'}); t.true(nestedResult instanceof Error); t.true(nestedResult.message.includes('sendMessage() can only be used')); t.is(getIpcLine(stderr), undefined); }; test('Does not print IPC, ipc: false', testNoIpc, false); test('Does not print IPC, ipc: default', testNoIpc, undefined); test('Prints objects from IPC', async t => { const {stderr} = await nestedSubprocess('ipc-send-json.js', [JSON.stringify(foobarObject)], {ipc: true, verbose: 'full'}); t.is(getIpcLine(stderr), `${testTimestamp} [0] * ${inspect(foobarObject)}`); }); test('Prints multiline arrays from IPC', async t => { const bigArray = Array.from({length: 100}, (_, index) => index); const {stderr} = await nestedSubprocess('ipc-send-json.js', [JSON.stringify(bigArray)], {ipc: true, verbose: 'full'}); const ipcLines = getIpcLines(stderr); t.is(ipcLines[0], `${testTimestamp} [0] * [`); t.is(ipcLines.at(-2), `${testTimestamp} [0] * 96, 97, 98, 99`); t.is(ipcLines.at(-1), `${testTimestamp} [0] * ]`); }); test('Does not quote spaces from IPC', async t => { const {stderr} = await nestedSubprocess('ipc-send.js', ['foo bar'], {ipc: true, verbose: 'full'}); t.is(getIpcLine(stderr), `${testTimestamp} [0] * foo bar`); }); test('Does not quote newlines from IPC', async t => { const {stderr} = await nestedSubprocess('ipc-send.js', ['foo\nbar'], {ipc: true, verbose: 'full'}); t.deepEqual(getIpcLines(stderr), [ `${testTimestamp} [0] * foo`, `${testTimestamp} [0] * bar`, ]); }); test('Does not quote special punctuation from IPC', async t => { const {stderr} = await nestedSubprocess('ipc-send.js', ['%'], {ipc: true, verbose: 'full'}); t.is(getIpcLine(stderr), `${testTimestamp} [0] * %`); }); test('Does not escape internal characters from IPC', async t => { const {stderr} = await nestedSubprocess('ipc-send.js', ['ã'], {ipc: true, verbose: 'full'}); t.is(getIpcLine(stderr), `${testTimestamp} [0] * ã`); }); test('Strips color sequences from IPC', async t => { const {stderr} = await nestedSubprocess('ipc-send.js', [red(foobarString)], {ipc: true, verbose: 'full'}, {env: {FORCE_COLOR: '1'}}); t.is(getIpcLine(stderr), `${testTimestamp} [0] * ${foobarString}`); }); test('Escapes control characters from IPC', async t => { const {stderr} = await nestedSubprocess('ipc-send.js', ['\u0001'], {ipc: true, verbose: 'full'}); t.is(getIpcLine(stderr), `${testTimestamp} [0] * \\u0001`); }); test('Prints IPC progressively', async t => { const subprocess = nestedInstance('ipc-send-forever.js', {ipc: true, verbose: 'full'}); for await (const chunk of on(subprocess.stderr, 'data')) { const ipcLine = getIpcLine(chunk.toString()); if (ipcLine !== undefined) { t.is(ipcLine, `${testTimestamp} [0] * ${foobarString}`); break; } } subprocess.kill(); await t.throwsAsync(subprocess); }); ================================================ FILE: test/verbose/log.js ================================================ import {stripVTControlCharacters} from 'node:util'; import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import {getNormalizedLines, getCommandLine, getCompletionLine} from '../helpers/verbose.js'; import {PARALLEL_COUNT} from '../helpers/parallel.js'; setFixtureDirectory(); const testNoStdout = async (t, verbose, isSync) => { const {stdout} = await nestedSubprocess('noop.js', [foobarString], {verbose, stdio: 'inherit', isSync}); t.is(stdout, foobarString); }; test('Logs on stderr not stdout, verbose "none"', testNoStdout, 'none', false); test('Logs on stderr not stdout, verbose "short"', testNoStdout, 'short', false); test('Logs on stderr not stdout, verbose "full"', testNoStdout, 'full', false); test('Logs on stderr not stdout, verbose "none", sync', testNoStdout, 'none', true); test('Logs on stderr not stdout, verbose "short", sync', testNoStdout, 'short', true); test('Logs on stderr not stdout, verbose "full", sync', testNoStdout, 'full', true); const testColor = async (t, expectedResult, forceColor) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short'}, {env: {FORCE_COLOR: forceColor}}); t.is(stderr !== stripVTControlCharacters(stderr), expectedResult); }; test('Prints with colors if supported', testColor, true, '1'); test('Prints without colors if not supported', testColor, false, '0'); test.serial('Prints lines in order when interleaved with subprocess stderr', async t => { const results = await Promise.all(Array.from({length: PARALLEL_COUNT}, () => nestedSubprocess('noop-fd.js', ['2', `${foobarString}\n`], {verbose: 'full', stderr: 'inherit'}, {all: true}), )); for (const {all} of results) { t.deepEqual( getNormalizedLines(all), [getCommandLine(all), foobarString, getCompletionLine(all)], ); } }); ================================================ FILE: test/verbose/output-buffer.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarUppercase} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { getOutputLine, testTimestamp, stdoutNoneOption, stdoutFullOption, stderrFullOption, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintOutputNoBuffer = async (t, verbose, buffer, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, buffer, isSync}); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }; test('Prints stdout, buffer: false', testPrintOutputNoBuffer, 'full', false, false); test('Prints stdout, buffer: false, fd-specific buffer', testPrintOutputNoBuffer, 'full', {stdout: false}, false); test('Prints stdout, buffer: false, fd-specific verbose', testPrintOutputNoBuffer, stdoutFullOption, false, false); test('Prints stdout, buffer: false, sync', testPrintOutputNoBuffer, 'full', false, true); test('Prints stdout, buffer: false, fd-specific buffer, sync', testPrintOutputNoBuffer, 'full', {stdout: false}, true); test('Prints stdout, buffer: false, fd-specific verbose, sync', testPrintOutputNoBuffer, stdoutFullOption, false, true); const testPrintOutputNoBufferFalse = async (t, verbose, buffer, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, buffer, isSync}); t.is(getOutputLine(stderr), undefined); }; test('Does not print stdout, buffer: false, fd-specific none', testPrintOutputNoBufferFalse, stdoutNoneOption, false, false); test('Does not print stdout, buffer: false, different fd', testPrintOutputNoBufferFalse, stderrFullOption, false, false); test('Does not print stdout, buffer: false, different fd, fd-specific buffer', testPrintOutputNoBufferFalse, stderrFullOption, {stdout: false}, false); test('Does not print stdout, buffer: false, fd-specific none, sync', testPrintOutputNoBufferFalse, stdoutNoneOption, false, true); test('Does not print stdout, buffer: false, different fd, sync', testPrintOutputNoBufferFalse, stderrFullOption, false, true); test('Does not print stdout, buffer: false, different fd, fd-specific buffer, sync', testPrintOutputNoBufferFalse, stderrFullOption, {stdout: false}, true); const testPrintOutputNoBufferTransform = async (t, buffer, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], { optionsFixture: 'generator-uppercase.js', verbose: 'full', buffer, isSync, }); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarUppercase}`); }; test('Prints stdout, buffer: false, transform', testPrintOutputNoBufferTransform, false, false); test('Prints stdout, buffer: false, transform, fd-specific buffer', testPrintOutputNoBufferTransform, {stdout: false}, false); test('Prints stdout, buffer: false, transform, sync', testPrintOutputNoBufferTransform, false, true); test('Prints stdout, buffer: false, transform, fd-specific buffer, sync', testPrintOutputNoBufferTransform, {stdout: false}, true); ================================================ FILE: test/verbose/output-enable.js ================================================ import test from 'ava'; import {red} from 'yoctocolors'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarUtf16Uint8Array} from '../helpers/input.js'; import {fullStdio} from '../helpers/stdio.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { runErrorSubprocess, getOutputLine, getOutputLines, testTimestamp, stdoutNoneOption, stdoutShortOption, stdoutFullOption, stderrNoneOption, stderrShortOption, stderrFullOption, fd3NoneOption, fd3ShortOption, fd3FullOption, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintOutput = async (t, verbose, fdNumber, isSync) => { const {stderr} = await nestedSubprocess('noop-fd.js', [`${fdNumber}`, foobarString], {verbose, isSync}); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }; test('Prints stdout, verbose "full"', testPrintOutput, 'full', 1, false); test('Prints stderr, verbose "full"', testPrintOutput, 'full', 2, false); test('Prints stdout, verbose "full", fd-specific', testPrintOutput, stdoutFullOption, 1, false); test('Prints stderr, verbose "full", fd-specific', testPrintOutput, stderrFullOption, 2, false); test('Prints stdout, verbose "full", sync', testPrintOutput, 'full', 1, true); test('Prints stderr, verbose "full", sync', testPrintOutput, 'full', 2, true); test('Prints stdout, verbose "full", fd-specific, sync', testPrintOutput, stdoutFullOption, 1, true); test('Prints stderr, verbose "full", fd-specific, sync', testPrintOutput, stderrFullOption, 2, true); const testNoPrintOutput = async (t, verbose, fdNumber, isSync) => { const {stderr} = await nestedSubprocess('noop-fd.js', [`${fdNumber}`, foobarString], {verbose, ...fullStdio, isSync}); t.is(getOutputLine(stderr), undefined); }; test('Does not print stdout, verbose default', testNoPrintOutput, undefined, 1, false); test('Does not print stdout, verbose "none"', testNoPrintOutput, 'none', 1, false); test('Does not print stdout, verbose "short"', testNoPrintOutput, 'short', 1, false); test('Does not print stderr, verbose default', testNoPrintOutput, undefined, 2, false); test('Does not print stderr, verbose "none"', testNoPrintOutput, 'none', 2, false); test('Does not print stderr, verbose "short"', testNoPrintOutput, 'short', 2, false); test('Does not print stdio[*], verbose default', testNoPrintOutput, undefined, 3, false); test('Does not print stdio[*], verbose "none"', testNoPrintOutput, 'none', 3, false); test('Does not print stdio[*], verbose "short"', testNoPrintOutput, 'short', 3, false); test('Does not print stdio[*], verbose "full"', testNoPrintOutput, 'full', 3, false); test('Does not print stdout, verbose default, fd-specific', testNoPrintOutput, {}, 1, false); test('Does not print stdout, verbose "none", fd-specific', testNoPrintOutput, stdoutNoneOption, 1, false); test('Does not print stdout, verbose "short", fd-specific', testNoPrintOutput, stdoutShortOption, 1, false); test('Does not print stderr, verbose default, fd-specific', testNoPrintOutput, {}, 2, false); test('Does not print stderr, verbose "none", fd-specific', testNoPrintOutput, stderrNoneOption, 2, false); test('Does not print stderr, verbose "short", fd-specific', testNoPrintOutput, stderrShortOption, 2, false); test('Does not print stdio[*], verbose default, fd-specific', testNoPrintOutput, {}, 3, false); test('Does not print stdio[*], verbose "none", fd-specific', testNoPrintOutput, fd3NoneOption, 3, false); test('Does not print stdio[*], verbose "short", fd-specific', testNoPrintOutput, fd3ShortOption, 3, false); test('Does not print stdio[*], verbose "full", fd-specific', testNoPrintOutput, fd3FullOption, 3, false); test('Does not print stdout, verbose default, sync', testNoPrintOutput, undefined, 1, true); test('Does not print stdout, verbose "none", sync', testNoPrintOutput, 'none', 1, true); test('Does not print stdout, verbose "short", sync', testNoPrintOutput, 'short', 1, true); test('Does not print stderr, verbose default, sync', testNoPrintOutput, undefined, 2, true); test('Does not print stderr, verbose "none", sync', testNoPrintOutput, 'none', 2, true); test('Does not print stderr, verbose "short", sync', testNoPrintOutput, 'short', 2, true); test('Does not print stdio[*], verbose default, sync', testNoPrintOutput, undefined, 3, true); test('Does not print stdio[*], verbose "none", sync', testNoPrintOutput, 'none', 3, true); test('Does not print stdio[*], verbose "short", sync', testNoPrintOutput, 'short', 3, true); test('Does not print stdio[*], verbose "full", sync', testNoPrintOutput, 'full', 3, true); test('Does not print stdout, verbose default, fd-specific, sync', testNoPrintOutput, {}, 1, true); test('Does not print stdout, verbose "none", fd-specific, sync', testNoPrintOutput, stdoutNoneOption, 1, true); test('Does not print stdout, verbose "short", fd-specific, sync', testNoPrintOutput, stdoutShortOption, 1, true); test('Does not print stderr, verbose default, fd-specific, sync', testNoPrintOutput, {}, 2, true); test('Does not print stderr, verbose "none", fd-specific, sync', testNoPrintOutput, stderrNoneOption, 2, true); test('Does not print stderr, verbose "short", fd-specific, sync', testNoPrintOutput, stderrShortOption, 2, true); test('Does not print stdio[*], verbose default, fd-specific, sync', testNoPrintOutput, {}, 3, true); test('Does not print stdio[*], verbose "none", fd-specific, sync', testNoPrintOutput, fd3NoneOption, 3, true); test('Does not print stdio[*], verbose "short", fd-specific, sync', testNoPrintOutput, fd3ShortOption, 3, true); test('Does not print stdio[*], verbose "full", fd-specific, sync', testNoPrintOutput, fd3FullOption, 3, true); const testPrintError = async (t, isSync) => { const stderr = await runErrorSubprocess(t, 'full', isSync); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }; test('Prints stdout after errors', testPrintError, false); test('Prints stdout after errors, sync', testPrintError, true); test('Does not quote spaces from stdout', async t => { const {stderr} = await nestedSubprocess('noop.js', ['foo bar'], {verbose: 'full'}); t.is(getOutputLine(stderr), `${testTimestamp} [0] foo bar`); }); test('Does not quote special punctuation from stdout', async t => { const {stderr} = await nestedSubprocess('noop.js', ['%'], {verbose: 'full'}); t.is(getOutputLine(stderr), `${testTimestamp} [0] %`); }); test('Does not escape internal characters from stdout', async t => { const {stderr} = await nestedSubprocess('noop.js', ['ã'], {verbose: 'full'}); t.is(getOutputLine(stderr), `${testTimestamp} [0] ã`); }); test('Strips color sequences from stdout', async t => { const {stderr} = await nestedSubprocess('noop.js', [red(foobarString)], {verbose: 'full'}, {env: {FORCE_COLOR: '1'}}); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }); test('Escapes control characters from stdout', async t => { const {stderr} = await nestedSubprocess('noop.js', ['\u0001'], {verbose: 'full'}); t.is(getOutputLine(stderr), `${testTimestamp} [0] \\u0001`); }); const testStdioSame = async (t, fdNumber) => { const {nestedResult: {stdio}} = await nestedSubprocess('noop-fd.js', [`${fdNumber}`, foobarString], {verbose: 'full'}); t.is(stdio[fdNumber], foobarString); }; test('Does not change subprocess.stdout', testStdioSame, 1); test('Does not change subprocess.stderr', testStdioSame, 2); const testSingleNewline = async (t, isSync) => { const {stderr} = await nestedSubprocess('noop-fd.js', ['1', '\n'], {verbose: 'full', isSync}); t.deepEqual(getOutputLines(stderr), [`${testTimestamp} [0] `]); }; test('Prints stdout, single newline', testSingleNewline, false); test('Prints stdout, single newline, sync', testSingleNewline, true); const testUtf16 = async (t, isSync) => { const {stderr} = await nestedSubprocess('stdin.js', { verbose: 'full', input: foobarUtf16Uint8Array, encoding: 'utf16le', isSync, }); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }; test('Can use encoding UTF16, verbose "full"', testUtf16, false); test('Can use encoding UTF16, verbose "full", sync', testUtf16, true); ================================================ FILE: test/verbose/output-mixed.js ================================================ import {inspect} from 'node:util'; import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString, foobarObject} from '../helpers/input.js'; import {simpleFull, noNewlinesChunks} from '../helpers/lines.js'; import {nestedSubprocess} from '../helpers/nested.js'; import {getOutputLine, getOutputLines, testTimestamp} from '../helpers/verbose.js'; setFixtureDirectory(); const testLines = async (t, lines, stripFinalNewline, isSync) => { const {stderr} = await nestedSubprocess('noop-fd.js', ['1', simpleFull], { verbose: 'full', lines, stripFinalNewline, isSync, }); t.deepEqual(getOutputLines(stderr), noNewlinesChunks.map(line => `${testTimestamp} [0] ${line}`)); }; test('Prints stdout, "lines: true"', testLines, true, false, false); test('Prints stdout, "lines: true", fd-specific', testLines, {stdout: true}, false, false); test('Prints stdout, "lines: true", stripFinalNewline', testLines, true, true, false); test('Prints stdout, "lines: true", sync', testLines, true, false, true); test('Prints stdout, "lines: true", fd-specific, sync', testLines, {stdout: true}, false, true); test('Prints stdout, "lines: true", stripFinalNewline, sync', testLines, true, true, true); const testOnlyTransforms = async (t, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], { optionsFixture: 'generator-uppercase.js', verbose: 'full', isSync, }); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString.toUpperCase()}`); }; test('Prints stdout with only transforms', testOnlyTransforms, false); test('Prints stdout with only transforms, sync', testOnlyTransforms, true); test('Prints stdout with only duplexes', async t => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], { optionsFixture: 'generator-duplex.js', verbose: 'full', }); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString.toUpperCase()}`); }); const testObjectMode = async (t, isSync) => { const {stderr} = await nestedSubprocess('noop.js', { optionsFixture: 'generator-object.js', verbose: 'full', isSync, }); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${inspect(foobarObject)}`); }; test('Prints stdout with object transforms', testObjectMode, false); test('Prints stdout with object transforms, sync', testObjectMode, true); const testBigArray = async (t, isSync) => { const {stderr} = await nestedSubprocess('noop.js', { optionsFixture: 'generator-big-array.js', verbose: 'full', isSync, }); const lines = getOutputLines(stderr); t.is(lines[0], `${testTimestamp} [0] [`); t.true(lines[1].startsWith(`${testTimestamp} [0] 0, 1,`)); t.is(lines.at(-1), `${testTimestamp} [0] ]`); }; test('Prints stdout with big object transforms', testBigArray, false); test('Prints stdout with big object transforms, sync', testBigArray, true); const testObjectModeString = async (t, isSync) => { const {stderr} = await nestedSubprocess('noop.js', { optionsFixture: 'generator-string-object.js', verbose: 'full', isSync, }); t.deepEqual(getOutputLines(stderr), noNewlinesChunks.map(line => `${testTimestamp} [0] ${line}`)); }; test('Prints stdout with string transforms in objectMode', testObjectModeString, false); test('Prints stdout with string transforms in objectMode, sync', testObjectModeString, true); ================================================ FILE: test/verbose/output-noop.js ================================================ import {rm, readFile} from 'node:fs/promises'; import test from 'ava'; import tempfile from 'tempfile'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import {getOutputLine, testTimestamp} from '../helpers/verbose.js'; setFixtureDirectory(); const testNoOutputOptions = async (t, isSync, options) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'full', isSync, ...options}); t.is(getOutputLine(stderr), undefined); }; test('Does not print stdout, encoding "buffer"', testNoOutputOptions, false, {encoding: 'buffer'}); test('Does not print stdout, encoding "hex"', testNoOutputOptions, false, {encoding: 'hex'}); test('Does not print stdout, encoding "base64"', testNoOutputOptions, false, {encoding: 'base64'}); test('Does not print stdout, stdout "ignore"', testNoOutputOptions, false, {stdout: 'ignore'}); test('Does not print stdout, stdout "inherit"', testNoOutputOptions, false, {stdout: 'inherit'}); test('Does not print stdout, stdout 1', testNoOutputOptions, false, {stdout: 1}); test('Does not print stdout, encoding "buffer", sync', testNoOutputOptions, true, {encoding: 'buffer'}); test('Does not print stdout, encoding "hex", sync', testNoOutputOptions, true, {encoding: 'hex'}); test('Does not print stdout, encoding "base64", sync', testNoOutputOptions, true, {encoding: 'base64'}); test('Does not print stdout, stdout "ignore", sync', testNoOutputOptions, true, {stdout: 'ignore'}); test('Does not print stdout, stdout "inherit", sync', testNoOutputOptions, true, {stdout: 'inherit'}); test('Does not print stdout, stdout 1, sync', testNoOutputOptions, true, {stdout: 1}); const testNoOutputDynamic = async (t, isSync, optionsFixture) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'full', isSync, optionsFixture}); t.is(getOutputLine(stderr), undefined); }; test('Does not print stdout, stdout Writable', testNoOutputDynamic, false, 'writable.js'); test('Does not print stdout, stdout WritableStream', testNoOutputDynamic, false, 'writable-web.js'); test('Does not print stdout, stdout Writable, sync', testNoOutputDynamic, true, 'writable.js'); test('Does not print stdout, stdout WritableStream, sync', testNoOutputDynamic, true, 'writable-web.js'); const testNoOutputStream = async (t, parentFixture) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'full', parentFixture}); t.is(getOutputLine(stderr), undefined); }; test('Does not print stdout, .pipe(stream)', testNoOutputStream, 'nested-pipe-stream.js'); test('Does not print stdout, .pipe(subprocess)', testNoOutputStream, 'nested-pipe-subprocess.js'); const testStdoutFile = async (t, isSync, optionsFixture) => { const file = tempfile(); const {stderr} = await nestedSubprocess('noop.js', [foobarString], { verbose: 'full', stdout: {file}, isSync, optionsFixture, }); t.is(getOutputLine(stderr), undefined); const contents = await readFile(file, 'utf8'); t.is(contents.trim(), foobarString); await rm(file); }; test('Does not print stdout, stdout { file }', testStdoutFile, false); test('Does not print stdout, stdout fileUrl', testStdoutFile, false, 'file-url.js'); test('Does not print stdout, stdout { file }, sync', testStdoutFile, true); test('Does not print stdout, stdout fileUrl, sync', testStdoutFile, true, 'file-url.js'); const testPrintOutputOptions = async (t, options, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'full', isSync, ...options}); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }; test('Prints stdout, stdout "pipe"', testPrintOutputOptions, {stdout: 'pipe'}, false); test('Prints stdout, stdout "overlapped"', testPrintOutputOptions, {stdout: 'overlapped'}, false); test('Prints stdout, stdout null', testPrintOutputOptions, {stdout: null}, false); test('Prints stdout, stdout ["pipe"]', testPrintOutputOptions, {stdout: ['pipe']}, false); test('Prints stdout, stdout "pipe", sync', testPrintOutputOptions, {stdout: 'pipe'}, true); test('Prints stdout, stdout null, sync', testPrintOutputOptions, {stdout: null}, true); test('Prints stdout, stdout ["pipe"], sync', testPrintOutputOptions, {stdout: ['pipe']}, true); ================================================ FILE: test/verbose/output-pipe.js ================================================ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { getOutputLine, getOutputLines, testTimestamp, getVerboseOption, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPipeOutput = async (t, parentFixture, sourceVerbose, destinationVerbose) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], { parentFixture, sourceOptions: getVerboseOption(sourceVerbose, 'full'), destinationFile: 'stdin.js', destinationOptions: getVerboseOption(destinationVerbose, 'full'), }); const lines = getOutputLines(stderr); const id = sourceVerbose && destinationVerbose ? 1 : 0; t.deepEqual(lines, destinationVerbose ? [`${testTimestamp} [${id}] ${foobarString}`] : []); }; test('Prints stdout if both verbose with .pipe("file")', testPipeOutput, 'nested-pipe-file.js', true, true); test('Prints stdout if both verbose with .pipe`command`', testPipeOutput, 'nested-pipe-script.js', true, true); test('Prints stdout if both verbose with .pipe(subprocess)', testPipeOutput, 'nested-pipe-subprocesses.js', true, true); test('Prints stdout if only second verbose with .pipe("file")', testPipeOutput, 'nested-pipe-file.js', false, true); test('Prints stdout if only second verbose with .pipe`command`', testPipeOutput, 'nested-pipe-script.js', false, true); test('Prints stdout if only second verbose with .pipe(subprocess)', testPipeOutput, 'nested-pipe-subprocesses.js', false, true); test('Does not print stdout if only first verbose with .pipe("file")', testPipeOutput, 'nested-pipe-file.js', true, false); test('Does not print stdout if only first verbose with .pipe`command`', testPipeOutput, 'nested-pipe-script.js', true, false); test('Does not print stdout if only first verbose with .pipe(subprocess)', testPipeOutput, 'nested-pipe-subprocesses.js', true, false); test('Does not print stdout if neither verbose with .pipe("file")', testPipeOutput, 'nested-pipe-file.js', false, false); test('Does not print stdout if neither verbose with .pipe`command`', testPipeOutput, 'nested-pipe-script.js', false, false); test('Does not print stdout if neither verbose with .pipe(subprocess)', testPipeOutput, 'nested-pipe-subprocesses.js', false, false); const testPrintOutputFixture = async (t, parentFixture) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {parentFixture, verbose: 'full', unpipe: true}); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }; test('Prints stdout, .pipe(stream) + .unpipe()', testPrintOutputFixture, 'nested-pipe-stream.js'); test('Prints stdout, .pipe(subprocess) + .unpipe()', testPrintOutputFixture, 'nested-pipe-subprocess.js'); ================================================ FILE: test/verbose/output-progressive.js ================================================ import {on} from 'node:events'; import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess, nestedInstance} from '../helpers/nested.js'; import {getOutputLine, getOutputLines, testTimestamp} from '../helpers/verbose.js'; setFixtureDirectory(); test('Prints stdout one line at a time', async t => { const subprocess = nestedInstance('noop-progressive.js', [foobarString], {verbose: 'full'}); for await (const chunk of on(subprocess.stderr, 'data')) { const outputLine = getOutputLine(chunk.toString().trim()); if (outputLine !== undefined) { t.is(outputLine, `${testTimestamp} [0] ${foobarString}`); break; } } await subprocess; }); test.serial('Prints stdout progressively, interleaved', async t => { const subprocess = nestedInstance('noop-repeat.js', ['1', `${foobarString}\n`], {parentFixture: 'nested-double.js', verbose: 'full'}); let firstSubprocessPrinted = false; let secondSubprocessPrinted = false; for await (const chunk of on(subprocess.stderr, 'data')) { const outputLine = getOutputLine(chunk.toString().trim()); if (outputLine === undefined) { continue; } if (outputLine.includes(foobarString)) { t.is(outputLine, `${testTimestamp} [0] ${foobarString}`); firstSubprocessPrinted ||= true; } else { t.is(outputLine, `${testTimestamp} [1] ${foobarString.toUpperCase()}`); secondSubprocessPrinted ||= true; } if (firstSubprocessPrinted && secondSubprocessPrinted) { break; } } subprocess.kill(); await t.throwsAsync(subprocess); }); const testInterleaved = async (t, expectedLines, isSync) => { const {stderr} = await nestedSubprocess('noop-132.js', {verbose: 'full', isSync}); t.deepEqual(getOutputLines(stderr), expectedLines.map(line => `${testTimestamp} [0] ${line}`)); }; test('Prints stdout + stderr interleaved', testInterleaved, [1, 2, 3], false); test('Prints stdout + stderr not interleaved, sync', testInterleaved, [1, 3, 2], true); ================================================ FILE: test/verbose/start.js ================================================ import test from 'ava'; import {red} from 'yoctocolors'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {foobarString} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { QUOTE, runErrorSubprocess, runEarlyErrorSubprocess, getCommandLine, getCommandLines, testTimestamp, getVerboseOption, stdoutNoneOption, stdoutShortOption, stdoutFullOption, stderrNoneOption, stderrShortOption, stderrFullOption, fd3NoneOption, fd3ShortOption, fd3FullOption, ipcNoneOption, ipcShortOption, ipcFullOption, } from '../helpers/verbose.js'; setFixtureDirectory(); const testPrintCommand = async (t, verbose, worker, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, worker, isSync}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${foobarString}`); }; test('Prints command, verbose "short"', testPrintCommand, 'short', false, false); test('Prints command, verbose "full"', testPrintCommand, 'full', false, false); test('Prints command, verbose "short", fd-specific stdout', testPrintCommand, stdoutShortOption, false, false); test('Prints command, verbose "full", fd-specific stdout', testPrintCommand, stdoutFullOption, false, false); test('Prints command, verbose "short", fd-specific stderr', testPrintCommand, stderrShortOption, false, false); test('Prints command, verbose "full", fd-specific stderr', testPrintCommand, stderrFullOption, false, false); test('Prints command, verbose "short", fd-specific fd3', testPrintCommand, fd3ShortOption, false, false); test('Prints command, verbose "full", fd-specific fd3', testPrintCommand, fd3FullOption, false, false); test('Prints command, verbose "short", fd-specific ipc', testPrintCommand, ipcShortOption, false, false); test('Prints command, verbose "full", fd-specific ipc', testPrintCommand, ipcFullOption, false, false); test('Prints command, verbose "short", sync', testPrintCommand, 'short', false, true); test('Prints command, verbose "full", sync', testPrintCommand, 'full', false, true); test('Prints command, verbose "short", fd-specific stdout, sync', testPrintCommand, stdoutShortOption, false, true); test('Prints command, verbose "full", fd-specific stdout, sync', testPrintCommand, stdoutFullOption, false, true); test('Prints command, verbose "short", fd-specific stderr, sync', testPrintCommand, stderrShortOption, false, true); test('Prints command, verbose "full", fd-specific stderr, sync', testPrintCommand, stderrFullOption, false, true); test('Prints command, verbose "short", fd-specific fd3, sync', testPrintCommand, fd3ShortOption, false, true); test('Prints command, verbose "full", fd-specific fd3, sync', testPrintCommand, fd3FullOption, false, true); test('Prints command, verbose "short", fd-specific ipc, sync', testPrintCommand, ipcShortOption, false, true); test('Prints command, verbose "full", fd-specific ipc, sync', testPrintCommand, ipcFullOption, false, true); test('Prints command, verbose "short", worker', testPrintCommand, 'short', true, false); test('Prints command, verbose "full", worker', testPrintCommand, 'full', true, false); test('Prints command, verbose "short", fd-specific stdout, worker', testPrintCommand, stdoutShortOption, true, false); test('Prints command, verbose "full", fd-specific stdout, worker', testPrintCommand, stdoutFullOption, true, false); test('Prints command, verbose "short", fd-specific stderr, worker', testPrintCommand, stderrShortOption, true, false); test('Prints command, verbose "full", fd-specific stderr, worker', testPrintCommand, stderrFullOption, true, false); test('Prints command, verbose "short", fd-specific fd3, worker', testPrintCommand, fd3ShortOption, true, false); test('Prints command, verbose "full", fd-specific fd3, worker', testPrintCommand, fd3FullOption, true, false); test('Prints command, verbose "short", fd-specific ipc, worker', testPrintCommand, ipcShortOption, true, false); test('Prints command, verbose "full", fd-specific ipc, worker', testPrintCommand, ipcFullOption, true, false); test('Prints command, verbose "short", worker, sync', testPrintCommand, 'short', true, true); test('Prints command, verbose "full", worker, sync', testPrintCommand, 'full', true, true); test('Prints command, verbose "short", fd-specific stdout, worker, sync', testPrintCommand, stdoutShortOption, true, true); test('Prints command, verbose "full", fd-specific stdout, worker, sync', testPrintCommand, stdoutFullOption, true, true); test('Prints command, verbose "short", fd-specific stderr, worker, sync', testPrintCommand, stderrShortOption, true, true); test('Prints command, verbose "full", fd-specific stderr, worker, sync', testPrintCommand, stderrFullOption, true, true); test('Prints command, verbose "short", fd-specific fd3, worker, sync', testPrintCommand, fd3ShortOption, true, true); test('Prints command, verbose "full", fd-specific fd3, worker, sync', testPrintCommand, fd3FullOption, true, true); test('Prints command, verbose "short", fd-specific ipc, worker, sync', testPrintCommand, ipcShortOption, true, true); test('Prints command, verbose "full", fd-specific ipc, worker, sync', testPrintCommand, ipcFullOption, true, true); const testNoPrintCommand = async (t, verbose, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose, isSync}); t.is(stderr, ''); }; test('Does not print command, verbose "none"', testNoPrintCommand, 'none', false); test('Does not print command, verbose default', testNoPrintCommand, undefined, false); test('Does not print command, verbose "none", fd-specific stdout', testNoPrintCommand, stdoutNoneOption, false); test('Does not print command, verbose "none", fd-specific stderr', testNoPrintCommand, stderrNoneOption, false); test('Does not print command, verbose "none", fd-specific fd3', testNoPrintCommand, fd3NoneOption, false); test('Does not print command, verbose "none", fd-specific ipc', testNoPrintCommand, ipcNoneOption, false); test('Does not print command, verbose default, fd-specific', testNoPrintCommand, {}, false); test('Does not print command, verbose "none", sync', testNoPrintCommand, 'none', true); test('Does not print command, verbose default, sync', testNoPrintCommand, undefined, true); test('Does not print command, verbose "none", fd-specific stdout, sync', testNoPrintCommand, stdoutNoneOption, true); test('Does not print command, verbose "none", fd-specific stderr, sync', testNoPrintCommand, stderrNoneOption, true); test('Does not print command, verbose "none", fd-specific fd3, sync', testNoPrintCommand, fd3NoneOption, true); test('Does not print command, verbose "none", fd-specific ipc, sync', testNoPrintCommand, ipcNoneOption, true); test('Does not print command, verbose default, fd-specific, sync', testNoPrintCommand, {}, true); const testPrintCommandError = async (t, isSync) => { const stderr = await runErrorSubprocess(t, 'short', isSync); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop-fail.js 1 ${foobarString}`); }; test('Prints command after errors', testPrintCommandError, false); test('Prints command after errors, sync', testPrintCommandError, true); const testPrintCommandEarly = async (t, isSync) => { const stderr = await runEarlyErrorSubprocess(t, isSync); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${foobarString}`); }; test('Prints command before early validation errors', testPrintCommandEarly, false); test('Prints command before early validation errors, sync', testPrintCommandEarly, true); const testPipeCommand = async (t, parentFixture, sourceVerbose, destinationVerbose) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], { parentFixture, sourceOptions: getVerboseOption(sourceVerbose), destinationFile: 'stdin.js', destinationOptions: getVerboseOption(destinationVerbose), }); const pipeSymbol = parentFixture === 'nested-pipe-subprocesses.js' ? '$' : '|'; const lines = getCommandLines(stderr); t.is(lines.includes(`${testTimestamp} [0] $ noop.js ${foobarString}`), sourceVerbose); t.is(lines.includes(`${testTimestamp} [${sourceVerbose ? 1 : 0}] ${pipeSymbol} stdin.js`), destinationVerbose); }; test('Prints both commands piped with .pipe("file")', testPipeCommand, 'nested-pipe-file.js', true, true); test('Prints both commands piped with .pipe`command`', testPipeCommand, 'nested-pipe-script.js', true, true); test('Prints both commands piped with .pipe(subprocess)', testPipeCommand, 'nested-pipe-subprocesses.js', true, true); test('Prints first command piped with .pipe("file")', testPipeCommand, 'nested-pipe-file.js', true, false); test('Prints first command piped with .pipe`command`', testPipeCommand, 'nested-pipe-script.js', true, false); test('Prints first command piped with .pipe(subprocess)', testPipeCommand, 'nested-pipe-subprocesses.js', true, false); test('Prints second command piped with .pipe("file")', testPipeCommand, 'nested-pipe-file.js', false, true); test('Prints second command piped with .pipe`command`', testPipeCommand, 'nested-pipe-script.js', false, true); test('Prints second command piped with .pipe(subprocess)', testPipeCommand, 'nested-pipe-subprocesses.js', false, true); test('Prints neither commands piped with .pipe("file")', testPipeCommand, 'nested-pipe-file.js', false, false); test('Prints neither commands piped with .pipe`command`', testPipeCommand, 'nested-pipe-script.js', false, false); test('Prints neither commands piped with .pipe(subprocess)', testPipeCommand, 'nested-pipe-subprocesses.js', false, false); test('Quotes spaces from command', async t => { const {stderr} = await nestedSubprocess('noop.js', ['foo bar'], {verbose: 'short'}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}foo bar${QUOTE}`); }); test('Quotes special punctuation from command', async t => { const {stderr} = await nestedSubprocess('noop.js', ['%'], {verbose: 'short'}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}%${QUOTE}`); }); test('Does not escape internal characters from command', async t => { const {stderr} = await nestedSubprocess('noop.js', ['ã'], {verbose: 'short'}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}ã${QUOTE}`); }); test('Escapes color sequences from command', async t => { const {stderr} = await nestedSubprocess('noop.js', [red(foobarString)], {verbose: 'short'}, {env: {FORCE_COLOR: '1'}}); t.true(getCommandLine(stderr).includes(`${QUOTE}\\u001b[31m${foobarString}\\u001b[39m${QUOTE}`)); }); test('Escapes control characters from command', async t => { const {stderr} = await nestedSubprocess('noop.js', ['\u0001'], {verbose: 'short'}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}\\u0001${QUOTE}`); }); ================================================ FILE: test-d/arguments/encoding-option.test-d.ts ================================================ import {expectError} from 'tsd'; import {execa, execaSync} from '../../index.js'; await execa('unicorns', {encoding: 'utf8'}); execaSync('unicorns', {encoding: 'utf8'}); /* eslint-disable unicorn/text-encoding-identifier-case */ expectError(await execa('unicorns', {encoding: 'utf-8'})); expectError(execaSync('unicorns', {encoding: 'utf-8'})); expectError(await execa('unicorns', {encoding: 'UTF8'})); expectError(execaSync('unicorns', {encoding: 'UTF8'})); /* eslint-enable unicorn/text-encoding-identifier-case */ await execa('unicorns', {encoding: 'utf16le'}); execaSync('unicorns', {encoding: 'utf16le'}); expectError(await execa('unicorns', {encoding: 'utf-16le'})); expectError(execaSync('unicorns', {encoding: 'utf-16le'})); expectError(await execa('unicorns', {encoding: 'ucs2'})); expectError(execaSync('unicorns', {encoding: 'ucs2'})); expectError(await execa('unicorns', {encoding: 'ucs-2'})); expectError(execaSync('unicorns', {encoding: 'ucs-2'})); await execa('unicorns', {encoding: 'buffer'}); execaSync('unicorns', {encoding: 'buffer'}); expectError(await execa('unicorns', {encoding: null})); expectError(execaSync('unicorns', {encoding: null})); await execa('unicorns', {encoding: 'hex'}); execaSync('unicorns', {encoding: 'hex'}); await execa('unicorns', {encoding: 'base64'}); execaSync('unicorns', {encoding: 'base64'}); await execa('unicorns', {encoding: 'base64url'}); execaSync('unicorns', {encoding: 'base64url'}); await execa('unicorns', {encoding: 'latin1'}); execaSync('unicorns', {encoding: 'latin1'}); expectError(await execa('unicorns', {encoding: 'binary'})); expectError(execaSync('unicorns', {encoding: 'binary'})); await execa('unicorns', {encoding: 'ascii'}); execaSync('unicorns', {encoding: 'ascii'}); expectError(await execa('unicorns', {encoding: 'utf8' as string})); expectError(execaSync('unicorns', {encoding: 'utf8' as string})); expectError(await execa('unicorns', {encoding: 'unknownEncoding'})); expectError(execaSync('unicorns', {encoding: 'unknownEncoding'})); ================================================ FILE: test-d/arguments/env.test-d.ts ================================================ import process, {type env} from 'node:process'; import {expectType, expectAssignable} from 'tsd'; import {execa, type Options, type Result} from '../../index.js'; type NodeEnv = 'production' | 'development' | 'test'; // Libraries like Next.js or Remix do the following type augmentation. // The following type tests ensure this works with Execa. // See https://github.com/sindresorhus/execa/pull/1141 and https://github.com/sindresorhus/execa/issues/1132 declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace NodeJS { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface ProcessEnv { readonly NODE_ENV: NodeEnv; } } } // The global types are impacted expectType(process.env.NODE_ENV); expectType('' as (typeof env)['NODE_ENV']); expectType('' as NodeJS.ProcessEnv['NODE_ENV']); expectType('' as globalThis.NodeJS.ProcessEnv['NODE_ENV']); // But Execa's types are not impacted expectType('' as Exclude['NODE_ENV']); expectAssignable(await execa({env: {test: 'example'}})`unicorns`); expectAssignable(await execa({env: {test: 'example'} as const})`unicorns`); expectAssignable(await execa({env: {test: undefined}})`unicorns`); expectAssignable(await execa({env: {test: undefined} as const})`unicorns`); ================================================ FILE: test-d/arguments/options.test-d.ts ================================================ import * as process from 'node:process'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type Options, type SyncOptions, } from '../../index.js'; const fileUrl = new URL('file:///test'); expectAssignable({preferLocal: false}); expectAssignable({cleanup: false}); expectNotAssignable({other: false}); expectAssignable({preferLocal: false}); expectNotAssignable({cleanup: false}); expectNotAssignable({other: false}); await execa('unicorns', {preferLocal: false}); execaSync('unicorns', {preferLocal: false}); await execa('unicorns', {preferLocal: false as boolean}); execaSync('unicorns', {preferLocal: false as boolean}); expectError(await execa('unicorns', {preferLocal: 'false'})); expectError(execaSync('unicorns', {preferLocal: 'false'})); await execa('unicorns', {localDir: '.'}); execaSync('unicorns', {localDir: '.'}); await execa('unicorns', {localDir: '.' as string}); execaSync('unicorns', {localDir: '.' as string}); await execa('unicorns', {localDir: fileUrl}); execaSync('unicorns', {localDir: fileUrl}); expectError(await execa('unicorns', {localDir: false})); expectError(execaSync('unicorns', {localDir: false})); await execa('unicorns', {node: true}); execaSync('unicorns', {node: true}); await execa('unicorns', {node: true as boolean}); execaSync('unicorns', {node: true as boolean}); expectError(await execa('unicorns', {node: 'true'})); expectError(execaSync('unicorns', {node: 'true'})); await execa('unicorns', {nodePath: './node'}); execaSync('unicorns', {nodePath: './node'}); await execa('unicorns', {nodePath: './node' as string}); execaSync('unicorns', {nodePath: './node' as string}); await execa('unicorns', {nodePath: fileUrl}); execaSync('unicorns', {nodePath: fileUrl}); expectError(await execa('unicorns', {nodePath: false})); expectError(execaSync('unicorns', {nodePath: false})); await execa('unicorns', {nodeOptions: ['--async-stack-traces'] as const}); execaSync('unicorns', {nodeOptions: ['--async-stack-traces'] as const}); await execa('unicorns', {nodeOptions: ['--async-stack-traces'] as string[]}); execaSync('unicorns', {nodeOptions: ['--async-stack-traces'] as string[]}); expectError(await execa('unicorns', {nodeOptions: [false] as const})); expectError(execaSync('unicorns', {nodeOptions: [false] as const})); await execa('unicorns', {input: ''}); execaSync('unicorns', {input: ''}); await execa('unicorns', {input: '' as string}); execaSync('unicorns', {input: '' as string}); await execa('unicorns', {input: new Uint8Array()}); execaSync('unicorns', {input: new Uint8Array()}); await execa('unicorns', {input: process.stdin}); execaSync('unicorns', {input: process.stdin}); expectError(await execa('unicorns', {input: false})); expectError(execaSync('unicorns', {input: false})); await execa('unicorns', {inputFile: ''}); execaSync('unicorns', {inputFile: ''}); await execa('unicorns', {inputFile: '' as string}); execaSync('unicorns', {inputFile: '' as string}); await execa('unicorns', {inputFile: fileUrl}); execaSync('unicorns', {inputFile: fileUrl}); expectError(await execa('unicorns', {inputFile: false})); expectError(execaSync('unicorns', {inputFile: false})); await execa('unicorns', {lines: false}); execaSync('unicorns', {lines: false}); await execa('unicorns', {lines: false as boolean}); execaSync('unicorns', {lines: false as boolean}); expectError(await execa('unicorns', {lines: 'false'})); expectError(execaSync('unicorns', {lines: 'false'})); await execa('unicorns', {reject: false}); execaSync('unicorns', {reject: false}); await execa('unicorns', {reject: false as boolean}); execaSync('unicorns', {reject: false as boolean}); expectError(await execa('unicorns', {reject: 'false'})); expectError(execaSync('unicorns', {reject: 'false'})); await execa('unicorns', {stripFinalNewline: false}); execaSync('unicorns', {stripFinalNewline: false}); await execa('unicorns', {stripFinalNewline: false as boolean}); execaSync('unicorns', {stripFinalNewline: false as boolean}); expectError(await execa('unicorns', {stripFinalNewline: 'false'})); expectError(execaSync('unicorns', {stripFinalNewline: 'false'})); await execa('unicorns', {extendEnv: false}); execaSync('unicorns', {extendEnv: false}); await execa('unicorns', {extendEnv: false as boolean}); execaSync('unicorns', {extendEnv: false as boolean}); expectError(await execa('unicorns', {extendEnv: 'false'})); expectError(execaSync('unicorns', {extendEnv: 'false'})); await execa('unicorns', {cwd: '.'}); execaSync('unicorns', {cwd: '.'}); await execa('unicorns', {cwd: '.' as string}); execaSync('unicorns', {cwd: '.' as string}); await execa('unicorns', {cwd: fileUrl}); execaSync('unicorns', {cwd: fileUrl}); expectError(await execa('unicorns', {cwd: false})); expectError(execaSync('unicorns', {cwd: false})); /* eslint-disable @typescript-eslint/naming-convention */ await execa('unicorns', {env: {PATH: ''}}); execaSync('unicorns', {env: {PATH: ''}}); const env: Record = {PATH: ''}; await execa('unicorns', {env}); execaSync('unicorns', {env}); /* eslint-enable @typescript-eslint/naming-convention */ expectError(await execa('unicorns', {env: false})); expectError(execaSync('unicorns', {env: false})); await execa('unicorns', {argv0: ''}); execaSync('unicorns', {argv0: ''}); await execa('unicorns', {argv0: '' as string}); execaSync('unicorns', {argv0: '' as string}); expectError(await execa('unicorns', {argv0: false})); expectError(execaSync('unicorns', {argv0: false})); await execa('unicorns', {uid: 0}); execaSync('unicorns', {uid: 0}); await execa('unicorns', {uid: 0 as number}); execaSync('unicorns', {uid: 0 as number}); expectError(await execa('unicorns', {uid: '0'})); expectError(execaSync('unicorns', {uid: '0'})); await execa('unicorns', {gid: 0}); execaSync('unicorns', {gid: 0}); await execa('unicorns', {gid: 0 as number}); execaSync('unicorns', {gid: 0 as number}); expectError(await execa('unicorns', {gid: '0'})); expectError(execaSync('unicorns', {gid: '0'})); await execa('unicorns', {shell: true}); execaSync('unicorns', {shell: true}); await execa('unicorns', {shell: true as boolean}); execaSync('unicorns', {shell: true as boolean}); await execa('unicorns', {shell: '/bin/sh'}); execaSync('unicorns', {shell: '/bin/sh'}); await execa('unicorns', {shell: '/bin/sh' as string}); execaSync('unicorns', {shell: '/bin/sh' as string}); await execa('unicorns', {shell: fileUrl}); execaSync('unicorns', {shell: fileUrl}); expectError(await execa('unicorns', {shell: {}})); expectError(execaSync('unicorns', {shell: {}})); await execa('unicorns', {timeout: 1000}); execaSync('unicorns', {timeout: 1000}); await execa('unicorns', {timeout: 1000 as number}); execaSync('unicorns', {timeout: 1000 as number}); expectError(await execa('unicorns', {timeout: '1000'})); expectError(execaSync('unicorns', {timeout: '1000'})); await execa('unicorns', {maxBuffer: 1000}); execaSync('unicorns', {maxBuffer: 1000}); await execa('unicorns', {maxBuffer: 1000 as number}); execaSync('unicorns', {maxBuffer: 1000 as number}); expectError(await execa('unicorns', {maxBuffer: '1000'})); expectError(execaSync('unicorns', {maxBuffer: '1000'})); await execa('unicorns', {killSignal: 'SIGTERM'}); execaSync('unicorns', {killSignal: 'SIGTERM'}); expectError(await execa('unicorns', {killSignal: 'SIGTERM' as string})); expectError(execaSync('unicorns', {killSignal: 'SIGTERM' as string})); await execa('unicorns', {killSignal: 9}); execaSync('unicorns', {killSignal: 9}); await execa('unicorns', {killSignal: 9 as number}); execaSync('unicorns', {killSignal: 9 as number}); expectError(await execa('unicorns', {killSignal: false})); expectError(execaSync('unicorns', {killSignal: false})); expectError(await execa('unicorns', {killSignal: 'Sigterm'})); expectError(execaSync('unicorns', {killSignal: 'Sigterm'})); expectError(await execa('unicorns', {killSignal: 'sigterm'})); expectError(execaSync('unicorns', {killSignal: 'sigterm'})); expectError(await execa('unicorns', {killSignal: 'SIGOTHER'})); expectError(execaSync('unicorns', {killSignal: 'SIGOTHER'})); expectError(await execa('unicorns', {killSignal: 'SIGEMT'})); expectError(execaSync('unicorns', {killSignal: 'SIGEMT'})); expectError(await execa('unicorns', {killSignal: 'SIGCLD'})); expectError(execaSync('unicorns', {killSignal: 'SIGCLD'})); expectError(await execa('unicorns', {killSignal: 'SIGRT1'})); expectError(execaSync('unicorns', {killSignal: 'SIGRT1'})); await execa('unicorns', {forceKillAfterDelay: false}); expectError(execaSync('unicorns', {forceKillAfterDelay: false})); await execa('unicorns', {forceKillAfterDelay: true}); expectError(execaSync('unicorns', {forceKillAfterDelay: true})); await execa('unicorns', {forceKillAfterDelay: false as boolean}); expectError(execaSync('unicorns', {forceKillAfterDelay: false as boolean})); await execa('unicorns', {forceKillAfterDelay: 42}); expectError(execaSync('unicorns', {forceKillAfterDelay: 42})); await execa('unicorns', {forceKillAfterDelay: 42 as number}); expectError(execaSync('unicorns', {forceKillAfterDelay: 42 as number})); expectError(await execa('unicorns', {forceKillAfterDelay: 'true'})); expectError(execaSync('unicorns', {forceKillAfterDelay: 'true'})); await execa('unicorns', {windowsVerbatimArguments: true}); execaSync('unicorns', {windowsVerbatimArguments: true}); await execa('unicorns', {windowsVerbatimArguments: true as boolean}); execaSync('unicorns', {windowsVerbatimArguments: true as boolean}); expectError(await execa('unicorns', {windowsVerbatimArguments: 'true'})); expectError(execaSync('unicorns', {windowsVerbatimArguments: 'true'})); await execa('unicorns', {windowsHide: false}); execaSync('unicorns', {windowsHide: false}); await execa('unicorns', {windowsHide: false as boolean}); execaSync('unicorns', {windowsHide: false as boolean}); expectError(await execa('unicorns', {windowsHide: 'false'})); expectError(execaSync('unicorns', {windowsHide: 'false'})); await execa('unicorns', {cleanup: false}); expectError(execaSync('unicorns', {cleanup: false})); await execa('unicorns', {cleanup: false as boolean}); expectError(execaSync('unicorns', {cleanup: false as boolean})); expectError(await execa('unicorns', {cleanup: 'false'})); expectError(execaSync('unicorns', {cleanup: 'false'})); await execa('unicorns', {buffer: false}); execaSync('unicorns', {buffer: false}); await execa('unicorns', {buffer: false as boolean}); execaSync('unicorns', {buffer: false as boolean}); expectError(await execa('unicorns', {buffer: 'false'})); expectError(execaSync('unicorns', {buffer: 'false'})); await execa('unicorns', {all: true}); execaSync('unicorns', {all: true}); await execa('unicorns', {all: true as boolean}); execaSync('unicorns', {all: true as boolean}); expectError(await execa('unicorns', {all: 'true'})); expectError(execaSync('unicorns', {all: 'true'})); await execa('unicorns', {ipc: true}); expectError(execaSync('unicorns', {ipc: true})); await execa('unicorns', {ipc: true as boolean}); expectError(execaSync('unicorns', {ipc: true as boolean})); expectError(await execa('unicorns', {ipc: 'true'})); expectError(execaSync('unicorns', {ipc: 'true'})); await execa('unicorns', {serialization: 'json'}); expectError(execaSync('unicorns', {serialization: 'json'})); await execa('unicorns', {serialization: 'advanced'}); expectError(execaSync('unicorns', {serialization: 'advanced'})); expectError(await execa('unicorns', {serialization: 'advanced' as string})); expectError(execaSync('unicorns', {serialization: 'advanced' as string})); expectError(await execa('unicorns', {serialization: 'other'})); expectError(execaSync('unicorns', {serialization: 'other'})); await execa('unicorns', {ipcInput: ''}); expectError(execaSync('unicorns', {ipcInput: ''})); await execa('unicorns', {ipcInput: '' as string}); expectError(execaSync('unicorns', {ipcInput: '' as string})); await execa('unicorns', {ipcInput: {}}); expectError(execaSync('unicorns', {ipcInput: {}})); await execa('unicorns', {ipcInput: undefined}); execaSync('unicorns', {ipcInput: undefined}); expectError(await execa('unicorns', {ipcInput: 0n})); expectError(execaSync('unicorns', {ipcInput: 0n})); await execa('unicorns', {detached: true}); expectError(execaSync('unicorns', {detached: true})); await execa('unicorns', {detached: true as boolean}); expectError(execaSync('unicorns', {detached: true as boolean})); expectError(await execa('unicorns', {detached: 'true'})); expectError(execaSync('unicorns', {detached: 'true'})); await execa('unicorns', {cancelSignal: AbortSignal.abort()}); expectError(execaSync('unicorns', {cancelSignal: AbortSignal.abort()})); expectError(await execa('unicorns', {cancelSignal: false})); expectError(execaSync('unicorns', {cancelSignal: false})); await execa('unicorns', {gracefulCancel: true, cancelSignal: AbortSignal.abort()}); expectError(execaSync('unicorns', {gracefulCancel: true, cancelSignal: AbortSignal.abort()})); await execa('unicorns', {gracefulCancel: true as boolean, cancelSignal: AbortSignal.abort()}); expectError(execaSync('unicorns', {gracefulCancel: true as boolean, cancelSignal: AbortSignal.abort()})); expectError(await execa('unicorns', {gracefulCancel: 'true', cancelSignal: AbortSignal.abort()})); expectError(execaSync('unicorns', {gracefulCancel: 'true', cancelSignal: AbortSignal.abort()})); ================================================ FILE: test-d/arguments/specific.test-d.ts ================================================ import {expectType, expectError} from 'tsd'; import {execa, execaSync} from '../../index.js'; await execa('unicorns', {maxBuffer: {}}); expectError(await execa('unicorns', {maxBuffer: []})); await execa('unicorns', {maxBuffer: {stdout: 0}}); await execa('unicorns', {maxBuffer: {stderr: 0}}); await execa('unicorns', {maxBuffer: {stdout: 0, stderr: 0} as const}); await execa('unicorns', {maxBuffer: {all: 0}}); await execa('unicorns', {maxBuffer: {fd1: 0}}); await execa('unicorns', {maxBuffer: {fd2: 0}}); await execa('unicorns', {maxBuffer: {fd3: 0}}); await execa('unicorns', {maxBuffer: {ipc: 0}}); expectError(await execa('unicorns', {maxBuffer: {stdout: '0'}})); execaSync('unicorns', {maxBuffer: {}}); expectError(execaSync('unicorns', {maxBuffer: []})); execaSync('unicorns', {maxBuffer: {stdout: 0}}); execaSync('unicorns', {maxBuffer: {stderr: 0}}); execaSync('unicorns', {maxBuffer: {stdout: 0, stderr: 0} as const}); execaSync('unicorns', {maxBuffer: {all: 0}}); execaSync('unicorns', {maxBuffer: {fd1: 0}}); execaSync('unicorns', {maxBuffer: {fd2: 0}}); execaSync('unicorns', {maxBuffer: {fd3: 0}}); execaSync('unicorns', {maxBuffer: {ipc: 0}}); expectError(execaSync('unicorns', {maxBuffer: {stdout: '0'}})); await execa('unicorns', {verbose: {}}); expectError(await execa('unicorns', {verbose: []})); await execa('unicorns', {verbose: {stdout: 'none'}}); await execa('unicorns', {verbose: {stderr: 'none'}}); await execa('unicorns', {verbose: {stdout: 'none', stderr: 'none'} as const}); await execa('unicorns', {verbose: {all: 'none'}}); await execa('unicorns', {verbose: {fd1: 'none'}}); await execa('unicorns', {verbose: {fd2: 'none'}}); await execa('unicorns', {verbose: {fd3: 'none'}}); await execa('unicorns', {verbose: {ipc: 'none'}}); expectError(await execa('unicorns', {verbose: {stdout: 'other'}})); execaSync('unicorns', {verbose: {}}); expectError(execaSync('unicorns', {verbose: []})); execaSync('unicorns', {verbose: {stdout: 'none'}}); execaSync('unicorns', {verbose: {stderr: 'none'}}); execaSync('unicorns', {verbose: {stdout: 'none', stderr: 'none'} as const}); execaSync('unicorns', {verbose: {all: 'none'}}); execaSync('unicorns', {verbose: {fd1: 'none'}}); execaSync('unicorns', {verbose: {fd2: 'none'}}); execaSync('unicorns', {verbose: {fd3: 'none'}}); execaSync('unicorns', {verbose: {ipc: 'none'}}); expectError(execaSync('unicorns', {verbose: {stdout: 'other'}})); await execa('unicorns', {stripFinalNewline: {}}); expectError(await execa('unicorns', {stripFinalNewline: []})); await execa('unicorns', {stripFinalNewline: {stdout: true}}); await execa('unicorns', {stripFinalNewline: {stderr: true}}); await execa('unicorns', {stripFinalNewline: {stdout: true, stderr: true} as const}); await execa('unicorns', {stripFinalNewline: {all: true}}); await execa('unicorns', {stripFinalNewline: {fd1: true}}); await execa('unicorns', {stripFinalNewline: {fd2: true}}); await execa('unicorns', {stripFinalNewline: {fd3: true}}); await execa('unicorns', {stripFinalNewline: {ipc: true}}); expectError(await execa('unicorns', {stripFinalNewline: {stdout: 'true'}})); execaSync('unicorns', {stripFinalNewline: {}}); expectError(execaSync('unicorns', {stripFinalNewline: []})); execaSync('unicorns', {stripFinalNewline: {stdout: true}}); execaSync('unicorns', {stripFinalNewline: {stderr: true}}); execaSync('unicorns', {stripFinalNewline: {stdout: true, stderr: true} as const}); execaSync('unicorns', {stripFinalNewline: {all: true}}); execaSync('unicorns', {stripFinalNewline: {fd1: true}}); execaSync('unicorns', {stripFinalNewline: {fd2: true}}); execaSync('unicorns', {stripFinalNewline: {fd3: true}}); execaSync('unicorns', {stripFinalNewline: {ipc: true}}); expectError(execaSync('unicorns', {stripFinalNewline: {stdout: 'true'}})); await execa('unicorns', {lines: {}}); expectError(await execa('unicorns', {lines: []})); await execa('unicorns', {lines: {stdout: true}}); await execa('unicorns', {lines: {stderr: true}}); await execa('unicorns', {lines: {stdout: true, stderr: true} as const}); await execa('unicorns', {lines: {all: true}}); await execa('unicorns', {lines: {fd1: true}}); await execa('unicorns', {lines: {fd2: true}}); await execa('unicorns', {lines: {fd3: true}}); await execa('unicorns', {lines: {ipc: true}}); expectError(await execa('unicorns', {lines: {stdout: 'true'}})); execaSync('unicorns', {lines: {}}); expectError(execaSync('unicorns', {lines: []})); execaSync('unicorns', {lines: {stdout: true}}); execaSync('unicorns', {lines: {stderr: true}}); execaSync('unicorns', {lines: {stdout: true, stderr: true} as const}); execaSync('unicorns', {lines: {all: true}}); execaSync('unicorns', {lines: {fd1: true}}); execaSync('unicorns', {lines: {fd2: true}}); execaSync('unicorns', {lines: {fd3: true}}); execaSync('unicorns', {lines: {ipc: true}}); expectError(execaSync('unicorns', {lines: {stdout: 'true'}})); await execa('unicorns', {buffer: {}}); expectError(await execa('unicorns', {buffer: []})); await execa('unicorns', {buffer: {stdout: true}}); await execa('unicorns', {buffer: {stderr: true}}); await execa('unicorns', {buffer: {stdout: true, stderr: true} as const}); await execa('unicorns', {buffer: {all: true}}); await execa('unicorns', {buffer: {fd1: true}}); await execa('unicorns', {buffer: {fd2: true}}); await execa('unicorns', {buffer: {fd3: true}}); await execa('unicorns', {buffer: {ipc: true}}); expectError(await execa('unicorns', {buffer: {stdout: 'true'}})); execaSync('unicorns', {buffer: {}}); expectError(execaSync('unicorns', {buffer: []})); execaSync('unicorns', {buffer: {stdout: true}}); execaSync('unicorns', {buffer: {stderr: true}}); execaSync('unicorns', {buffer: {stdout: true, stderr: true} as const}); execaSync('unicorns', {buffer: {all: true}}); execaSync('unicorns', {buffer: {fd1: true}}); execaSync('unicorns', {buffer: {fd2: true}}); execaSync('unicorns', {buffer: {fd3: true}}); execaSync('unicorns', {buffer: {ipc: true}}); expectError(execaSync('unicorns', {buffer: {stdout: 'true'}})); expectError(await execa('unicorns', {preferLocal: {}})); expectError(await execa('unicorns', {preferLocal: []})); expectError(await execa('unicorns', {preferLocal: {stdout: 0}})); expectError(await execa('unicorns', {preferLocal: {stderr: 0}})); expectError(await execa('unicorns', {preferLocal: {stdout: 0, stderr: 0} as const})); expectError(await execa('unicorns', {preferLocal: {all: 0}})); expectError(await execa('unicorns', {preferLocal: {fd1: 0}})); expectError(await execa('unicorns', {preferLocal: {fd2: 0}})); expectError(await execa('unicorns', {preferLocal: {fd3: 0}})); expectError(await execa('unicorns', {preferLocal: {ipc: 0}})); expectError(await execa('unicorns', {preferLocal: {stdout: '0'}})); expectError(execaSync('unicorns', {preferLocal: {}})); expectError(execaSync('unicorns', {preferLocal: []})); expectError(execaSync('unicorns', {preferLocal: {stdout: 0}})); expectError(execaSync('unicorns', {preferLocal: {stderr: 0}})); expectError(execaSync('unicorns', {preferLocal: {stdout: 0, stderr: 0} as const})); expectError(execaSync('unicorns', {preferLocal: {all: 0}})); expectError(execaSync('unicorns', {preferLocal: {fd1: 0}})); expectError(execaSync('unicorns', {preferLocal: {fd2: 0}})); expectError(execaSync('unicorns', {preferLocal: {fd3: 0}})); expectError(execaSync('unicorns', {preferLocal: {ipc: 0}})); expectError(execaSync('unicorns', {preferLocal: {stdout: '0'}})); expectType(execaSync('unicorns', {lines: {stdout: true, fd1: false}}).stdout); expectType(execaSync('unicorns', {lines: {stdout: true, all: false}}).stdout); expectType(execaSync('unicorns', {lines: {fd1: true, all: false}}).stdout); expectType(execaSync('unicorns', {lines: {stderr: true, fd2: false}}).stderr); expectType(execaSync('unicorns', {lines: {stderr: true, all: false}}).stderr); expectType(execaSync('unicorns', {lines: {fd2: true, all: false}}).stderr); expectType(execaSync('unicorns', {lines: {fd1: false, stdout: true}}).stdout); expectType(execaSync('unicorns', {lines: {all: false, stdout: true}}).stdout); expectType(execaSync('unicorns', {lines: {all: false, fd1: true}}).stdout); expectType(execaSync('unicorns', {lines: {fd2: false, stderr: true}}).stderr); expectType(execaSync('unicorns', {lines: {all: false, stderr: true}}).stderr); expectType(execaSync('unicorns', {lines: {all: false, fd2: true}}).stderr); ================================================ FILE: test-d/convert/duplex.test-d.ts ================================================ import type {Duplex} from 'node:stream'; import {expectType, expectError} from 'tsd'; import {execa} from '../../index.js'; const subprocess = execa('unicorns'); expectType(subprocess.duplex()); subprocess.duplex({from: 'stdout'}); subprocess.duplex({from: 'stderr'}); subprocess.duplex({from: 'all'}); subprocess.duplex({from: 'fd3'}); subprocess.duplex({to: 'fd3'}); subprocess.duplex({from: 'stdout', to: 'stdin'}); subprocess.duplex({from: 'stdout', to: 'fd3'}); expectError(subprocess.duplex({from: 'stdout' as string})); expectError(subprocess.duplex({to: 'fd3' as string})); expectError(subprocess.duplex({from: 'stdin'})); expectError(subprocess.duplex({from: 'stderr', to: 'stdout'})); expectError(subprocess.duplex({from: 'fd'})); expectError(subprocess.duplex({from: 'fdNotANumber'})); expectError(subprocess.duplex({to: 'fd'})); expectError(subprocess.duplex({to: 'fdNotANumber'})); subprocess.duplex({binary: false}); expectError(subprocess.duplex({binary: 'false'})); subprocess.duplex({preserveNewlines: false}); expectError(subprocess.duplex({preserveNewlines: 'false'})); expectError(subprocess.duplex('stdout')); expectError(subprocess.duplex({other: 'stdout'})); ================================================ FILE: test-d/convert/iterable.test-d.ts ================================================ import {expectType, expectError} from 'tsd'; import {execa} from '../../index.js'; const subprocess = execa('unicorns'); const bufferSubprocess = execa('unicorns', {encoding: 'buffer', all: true}); const hexSubprocess = execa('unicorns', {encoding: 'hex', all: true}); const asyncIteration = async () => { for await (const line of subprocess) { expectType(line); } for await (const line of subprocess.iterable()) { expectType(line); } for await (const line of subprocess.iterable({binary: false})) { expectType(line); } for await (const line of subprocess.iterable({binary: true})) { expectType(line); } for await (const line of subprocess.iterable({} as {binary: boolean})) { expectType(line); } for await (const line of bufferSubprocess) { expectType(line); } for await (const line of bufferSubprocess.iterable()) { expectType(line); } for await (const line of bufferSubprocess.iterable({binary: false})) { expectType(line); } for await (const line of bufferSubprocess.iterable({binary: true})) { expectType(line); } for await (const line of bufferSubprocess.iterable({} as {binary: boolean})) { expectType(line); } }; await asyncIteration(); expectType>(subprocess.iterable()); expectType>(subprocess.iterable({binary: false})); expectType>(subprocess.iterable({binary: true})); expectType>(subprocess.iterable({} as {binary: boolean})); expectType>(bufferSubprocess.iterable()); expectType>(bufferSubprocess.iterable({binary: false})); expectType>(bufferSubprocess.iterable({binary: true})); expectType>(bufferSubprocess.iterable({} as {binary: boolean})); expectType>(hexSubprocess.iterable()); expectType>(hexSubprocess.iterable({binary: false})); expectType>(hexSubprocess.iterable({binary: true})); expectType>(hexSubprocess.iterable({} as {binary: boolean})); subprocess.iterable({}); subprocess.iterable({from: 'stdout'}); subprocess.iterable({from: 'stderr'}); subprocess.iterable({from: 'all'}); subprocess.iterable({from: 'fd3'}); expectError(subprocess.iterable({from: 'fd3' as string})); expectError(subprocess.iterable({from: 'stdin'})); expectError(subprocess.iterable({from: 'fd'})); expectError(subprocess.iterable({from: 'fdNotANumber'})); expectError(subprocess.iterable({to: 'stdin'})); subprocess.iterable({binary: false}); expectError(subprocess.iterable({binary: 'false'})); subprocess.iterable({preserveNewlines: false}); expectError(subprocess.iterable({preserveNewlines: 'false'})); expectError(subprocess.iterable('stdout')); expectError(subprocess.iterable({other: 'stdout'})); ================================================ FILE: test-d/convert/readable.test-d.ts ================================================ import type {Readable} from 'node:stream'; import {expectType, expectError} from 'tsd'; import {execa} from '../../index.js'; const subprocess = execa('unicorns'); expectType(subprocess.readable()); subprocess.readable({from: 'stdout'}); subprocess.readable({from: 'stderr'}); subprocess.readable({from: 'all'}); subprocess.readable({from: 'fd3'}); expectError(subprocess.readable({from: 'fd3' as string})); expectError(subprocess.readable({from: 'stdin'})); expectError(subprocess.readable({from: 'fd'})); expectError(subprocess.readable({from: 'fdNotANumber'})); expectError(subprocess.readable({to: 'stdin'})); subprocess.readable({binary: false}); expectError(subprocess.readable({binary: 'false'})); subprocess.readable({preserveNewlines: false}); expectError(subprocess.readable({preserveNewlines: 'false'})); expectError(subprocess.readable('stdout')); expectError(subprocess.readable({other: 'stdout'})); ================================================ FILE: test-d/convert/writable.test-d.ts ================================================ import type {Writable} from 'node:stream'; import {expectType, expectError} from 'tsd'; import {execa} from '../../index.js'; const subprocess = execa('unicorns'); expectType(subprocess.writable()); subprocess.writable({to: 'stdin'}); subprocess.writable({to: 'fd3'}); expectError(subprocess.writable({to: 'fd3' as string})); expectError(subprocess.writable({to: 'stdout'})); expectError(subprocess.writable({to: 'fd'})); expectError(subprocess.writable({to: 'fdNotANumber'})); expectError(subprocess.writable({from: 'stdout'})); expectError(subprocess.writable({binary: false})); expectError(subprocess.writable({preserveNewlines: false})); expectError(subprocess.writable('stdin')); expectError(subprocess.writable({other: 'stdin'})); ================================================ FILE: test-d/ipc/get-each.test-d.ts ================================================ import {expectType, expectError} from 'tsd'; import { getEachMessage, execa, type Message, type Options, } from '../../index.js'; const subprocess = execa('test', {ipc: true}); for await (const message of subprocess.getEachMessage()) { expectType>(message); } for await (const message of execa('test', {ipc: true, serialization: 'json'}).getEachMessage()) { expectType>(message); } for await (const message of getEachMessage()) { expectType(message); } expectError(subprocess.getEachMessage('')); expectError(getEachMessage('')); execa('test', {ipcInput: ''}).getEachMessage(); execa('test', {ipcInput: '' as Message}).getEachMessage(); execa('test', {gracefulCancel: true, cancelSignal: AbortSignal.abort()}).getEachMessage(); execa('test', {} as Options).getEachMessage?.(); execa('test', {ipc: true as boolean}).getEachMessage?.(); execa('test', {ipcInput: '' as '' | undefined}).getEachMessage?.(); execa('test', {gracefulCancel: true as boolean | undefined, cancelSignal: AbortSignal.abort()}).getEachMessage?.(); expectType(execa('test').getEachMessage); expectType(execa('test', {}).getEachMessage); expectType(execa('test', {ipc: false}).getEachMessage); expectType(execa('test', {ipcInput: undefined}).getEachMessage); expectType(execa('test', {gracefulCancel: undefined}).getEachMessage); expectType(execa('test', {gracefulCancel: false}).getEachMessage); expectType(execa('test', {ipc: false, ipcInput: ''}).getEachMessage); expectType(execa('test', {ipc: false, gracefulCancel: true, cancelSignal: AbortSignal.abort()}).getEachMessage); subprocess.getEachMessage({reference: true} as const); getEachMessage({reference: true} as const); subprocess.getEachMessage({reference: true as boolean}); getEachMessage({reference: true as boolean}); expectError(subprocess.getEachMessage({reference: 'true'} as const)); expectError(getEachMessage({reference: 'true'} as const)); ================================================ FILE: test-d/ipc/get-one.test-d.ts ================================================ import {expectType, expectError} from 'tsd'; import { getOneMessage, execa, type Message, type Options, } from '../../index.js'; const subprocess = execa('test', {ipc: true}); expectType>>(subprocess.getOneMessage()); const jsonSubprocess = execa('test', {ipc: true, serialization: 'json'}); expectType>>(jsonSubprocess.getOneMessage()); expectType>(getOneMessage()); expectError(await subprocess.getOneMessage('')); expectError(await getOneMessage('')); expectError(await subprocess.getOneMessage({}, '')); expectError(await getOneMessage({}, '')); await execa('test', {ipcInput: ''}).getOneMessage(); await execa('test', {ipcInput: '' as Message}).getOneMessage(); await execa('test', {gracefulCancel: true, cancelSignal: AbortSignal.abort()}).getOneMessage(); await execa('test', {} as Options).getOneMessage?.(); await execa('test', {ipc: true as boolean}).getOneMessage?.(); await execa('test', {ipcInput: '' as '' | undefined}).getOneMessage?.(); await execa('test', {gracefulCancel: true as boolean | undefined, cancelSignal: AbortSignal.abort()}).getOneMessage?.(); expectType(execa('test').getOneMessage); expectType(execa('test', {}).getOneMessage); expectType(execa('test', {ipc: false}).getOneMessage); expectType(execa('test', {ipcInput: undefined}).getOneMessage); expectType(execa('test', {gracefulCancel: undefined}).getOneMessage); expectType(execa('test', {gracefulCancel: false}).getOneMessage); expectType(execa('test', {ipc: false, ipcInput: ''}).getOneMessage); expectType(execa('test', {ipc: false, gracefulCancel: true, cancelSignal: AbortSignal.abort()}).getOneMessage); await subprocess.getOneMessage({filter: undefined} as const); await subprocess.getOneMessage({filter: (message: Message<'advanced'>) => true} as const); await jsonSubprocess.getOneMessage({filter: (message: Message<'json'>) => true} as const); await jsonSubprocess.getOneMessage({filter: (message: Message<'advanced'>) => true} as const); await subprocess.getOneMessage({filter: (message: Message<'advanced'> | bigint) => true} as const); await subprocess.getOneMessage({filter: () => true} as const); expectError(await subprocess.getOneMessage({filter: (message: Message<'advanced'>) => ''} as const)); // eslint-disable-next-line @typescript-eslint/no-empty-function expectError(await subprocess.getOneMessage({filter(message: Message<'advanced'>) {}} as const)); expectError(await subprocess.getOneMessage({filter: (message: Message<'json'>) => true} as const)); expectError(await subprocess.getOneMessage({filter: (message: '') => true} as const)); expectError(await subprocess.getOneMessage({filter: true} as const)); await getOneMessage({filter: undefined} as const); await getOneMessage({filter: (message: Message) => true} as const); await getOneMessage({filter: (message: Message<'advanced'>) => true} as const); await getOneMessage({filter: (message: Message | bigint) => true} as const); await getOneMessage({filter: () => true} as const); expectError(await getOneMessage({filter: (message: Message) => ''} as const)); // eslint-disable-next-line @typescript-eslint/no-empty-function expectError(await getOneMessage({filter(message: Message) {}} as const)); expectError(await getOneMessage({filter: (message: Message<'json'>) => true} as const)); expectError(await getOneMessage({filter: (message: '') => true} as const)); expectError(await getOneMessage({filter: true} as const)); expectError(await subprocess.getOneMessage({unknownOption: true} as const)); expectError(await getOneMessage({unknownOption: true} as const)); await subprocess.getOneMessage({reference: true} as const); await getOneMessage({reference: true} as const); await subprocess.getOneMessage({reference: true as boolean}); await getOneMessage({reference: true as boolean}); expectError(await subprocess.getOneMessage({reference: 'true'} as const)); expectError(await getOneMessage({reference: 'true'} as const)); ================================================ FILE: test-d/ipc/graceful.ts ================================================ import {expectType, expectError} from 'tsd'; import {getCancelSignal, execa} from '../../index.js'; expectType>(getCancelSignal()); expectError(await getCancelSignal('')); expectError(execa('test').getCancelSignal); ================================================ FILE: test-d/ipc/message.test-d.ts ================================================ import {File} from 'node:buffer'; import {expectAssignable, expectNotAssignable} from 'tsd'; import {sendMessage, type Message} from '../../index.js'; await sendMessage(''); expectAssignable(''); expectAssignable>(''); expectAssignable>(''); await sendMessage(0); expectAssignable(0); expectAssignable>(0); expectAssignable>(0); await sendMessage(true); expectAssignable(true); expectAssignable>(true); expectAssignable>(true); await sendMessage([] as const); expectAssignable([] as const); expectAssignable>([] as const); expectAssignable>([] as const); await sendMessage([true] as const); expectAssignable([true] as const); expectAssignable>([true] as const); expectAssignable>([true] as const); await sendMessage([undefined] as const); expectAssignable([undefined] as const); expectAssignable>([undefined] as const); expectNotAssignable>([undefined] as const); await sendMessage([0n] as const); expectAssignable([0n] as const); expectAssignable>([0n] as const); expectNotAssignable>([0n] as const); await sendMessage({} as const); expectAssignable({} as const); expectAssignable>({} as const); expectAssignable>({} as const); await sendMessage({test: true} as const); expectAssignable({test: true} as const); expectAssignable>({test: true} as const); expectAssignable>({test: true} as const); await sendMessage({test: undefined} as const); expectAssignable({test: undefined} as const); expectAssignable>({test: undefined} as const); expectNotAssignable>({test: undefined} as const); await sendMessage({test: 0n} as const); expectAssignable({test: 0n} as const); expectAssignable>({test: 0n} as const); expectNotAssignable>({test: 0n} as const); await sendMessage(null); expectAssignable(null); expectAssignable>(null); expectAssignable>(null); await sendMessage(Number.NaN); expectAssignable(Number.NaN); expectAssignable>(Number.NaN); expectAssignable>(Number.NaN); await sendMessage(Number.POSITIVE_INFINITY); expectAssignable(Number.POSITIVE_INFINITY); expectAssignable>(Number.POSITIVE_INFINITY); expectAssignable>(Number.POSITIVE_INFINITY); await sendMessage(new Map()); expectAssignable(new Map()); expectAssignable>(new Map()); expectNotAssignable>(new Map()); await sendMessage(new Set()); expectAssignable(new Set()); expectAssignable>(new Set()); expectNotAssignable>(new Set()); await sendMessage(new Date()); expectAssignable(new Date()); expectAssignable>(new Date()); expectNotAssignable>(new Date()); await sendMessage(/regexp/); expectAssignable(/regexp/); expectAssignable>(/regexp/); expectNotAssignable>(/regexp/); await sendMessage(new Blob()); expectAssignable(new Blob()); expectAssignable>(new Blob()); expectNotAssignable>(new Blob()); await sendMessage(new File([], '')); expectAssignable(new File([], '')); expectAssignable>(new File([], '')); expectNotAssignable>(new File([], '')); await sendMessage(new DataView(new ArrayBuffer(0))); expectAssignable(new DataView(new ArrayBuffer(0))); expectAssignable>(new DataView(new ArrayBuffer(0))); expectNotAssignable>(new DataView(new ArrayBuffer(0))); await sendMessage(new ArrayBuffer(0)); expectAssignable(new ArrayBuffer(0)); expectAssignable>(new ArrayBuffer(0)); expectNotAssignable>(new ArrayBuffer(0)); await sendMessage(new SharedArrayBuffer(0)); expectAssignable(new SharedArrayBuffer(0)); expectAssignable>(new SharedArrayBuffer(0)); expectNotAssignable>(new SharedArrayBuffer(0)); await sendMessage(new Uint8Array()); expectAssignable(new Uint8Array()); expectAssignable>(new Uint8Array()); expectNotAssignable>(new Uint8Array()); await sendMessage(AbortSignal.abort()); expectAssignable(AbortSignal.abort()); expectAssignable>(AbortSignal.abort()); expectNotAssignable>(AbortSignal.abort()); await sendMessage(new Error('test')); expectAssignable(new Error('test')); expectAssignable>(new Error('test')); expectNotAssignable>(new Error('test')); ================================================ FILE: test-d/ipc/send.test-d.ts ================================================ import {expectType, expectError} from 'tsd'; import { sendMessage, execa, type Message, type Options, } from '../../index.js'; const subprocess = execa('test', {ipc: true}); expectType(await subprocess.sendMessage('')); expectType>(sendMessage('')); expectError(await subprocess.sendMessage()); expectError(await sendMessage()); expectError(await subprocess.sendMessage(undefined)); expectError(await sendMessage(undefined)); expectError(await subprocess.sendMessage(0n)); expectError(await sendMessage(0n)); expectError(await subprocess.sendMessage(Symbol('test'))); expectError(await sendMessage(Symbol('test'))); await execa('test', {ipcInput: ''}).sendMessage(''); await execa('test', {ipcInput: '' as Message}).sendMessage(''); await execa('test', {gracefulCancel: true, cancelSignal: AbortSignal.abort()}).sendMessage(''); await execa('test', {} as Options).sendMessage?.(''); await execa('test', {ipc: true as boolean}).sendMessage?.(''); await execa('test', {ipcInput: '' as '' | undefined}).sendMessage?.(''); await execa('test', {gracefulCancel: true as boolean | undefined, cancelSignal: AbortSignal.abort()}).sendMessage?.(''); expectType(execa('test').sendMessage); expectType(execa('test', {}).sendMessage); expectType(execa('test', {ipc: false}).sendMessage); expectType(execa('test', {ipcInput: undefined}).sendMessage); expectType(execa('test', {gracefulCancel: undefined}).sendMessage); expectType(execa('test', {gracefulCancel: false}).sendMessage); expectType(execa('test', {ipc: false, ipcInput: ''}).sendMessage); expectType(execa('test', {ipc: false, gracefulCancel: true, cancelSignal: AbortSignal.abort()}).sendMessage); await subprocess.sendMessage('', {} as const); await sendMessage('', {} as const); await subprocess.sendMessage('', {strict: true} as const); await sendMessage('', {strict: true} as const); await subprocess.sendMessage('', {strict: true as boolean}); await sendMessage('', {strict: true as boolean}); expectError(await subprocess.sendMessage('', true)); expectError(await sendMessage('', true)); expectError(await subprocess.sendMessage('', {strict: 'true'})); expectError(await sendMessage('', {strict: 'true'})); expectError(await subprocess.sendMessage('', {unknown: true})); expectError(await sendMessage('', {unknown: true})); expectError(await subprocess.sendMessage('', {strict: true}, {})); expectError(await sendMessage('', {strict: true}, {})); ================================================ FILE: test-d/methods/command.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import { execa, execaSync, $, execaNode, execaCommand, execaCommandSync, parseCommandString, type Result, type ResultPromise, type SyncResult, } from '../../index.js'; const fileUrl = new URL('file:///test'); const stringArray = ['foo', 'bar'] as const; expectError(parseCommandString()); expectError(parseCommandString(true)); expectError(parseCommandString(['unicorns', 'arg'])); expectType(parseCommandString('')); expectType(parseCommandString('unicorns foo bar')); expectType>(await execa`${parseCommandString('unicorns foo bar')}`); expectType>(execaSync`${parseCommandString('unicorns foo bar')}`); expectType>(await $`${parseCommandString('unicorns foo bar')}`); expectType>($.sync`${parseCommandString('unicorns foo bar')}`); expectType>(await execaNode`${parseCommandString('foo bar')}`); expectType>(await execa`unicorns ${parseCommandString('foo bar')}`); expectType>(await execa('unicorns', parseCommandString('foo bar'))); expectType>(await execa('unicorns', ['foo', ...parseCommandString('bar')])); expectError(execaCommand()); expectError(execaCommand(true)); expectError(execaCommand(['unicorns', 'arg'])); expectAssignable(execaCommand('unicorns')); expectError(execaCommand(fileUrl)); expectError(execaCommand('unicorns', [])); expectError(execaCommand('unicorns', ['foo'])); expectError(execaCommand('unicorns', 'foo')); expectError(execaCommand('unicorns', [true])); expectAssignable(execaCommand('unicorns', {})); expectError(execaCommand('unicorns', [], {})); expectError(execaCommand('unicorns', [], [])); expectError(execaCommand('unicorns', {other: true})); expectAssignable(execaCommand`unicorns`); expectType>(await execaCommand('unicorns')); expectType>(await execaCommand`unicorns`); expectAssignable(execaCommand({})); expectAssignable(execaCommand({})('unicorns')); expectAssignable(execaCommand({})`unicorns`); expectAssignable<{stdout: string}>(await execaCommand('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execaCommand('unicorns', {encoding: 'buffer'})); expectAssignable<{stdout: string}>(await execaCommand({})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execaCommand({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execaCommand({})({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execaCommand({encoding: 'buffer'})({})('unicorns')); expectAssignable<{stdout: string}>(await execaCommand({})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execaCommand({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execaCommand({})({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execaCommand({encoding: 'buffer'})({})`unicorns`); expectType>(await execaCommand`${'unicorns'}`); expectType>(await execaCommand`unicorns ${'foo'}`); expectError(await execaCommand`unicorns ${'foo'} ${'bar'}`); expectError(await execaCommand`unicorns ${1}`); expectError(await execaCommand`unicorns ${stringArray}`); expectError(await execaCommand`unicorns ${[1, 2]}`); expectType>(await execaCommand`unicorns ${false.toString()}`); expectError(await execaCommand`unicorns ${false}`); expectError(await execaCommand`unicorns ${await execaCommand`echo foo`}`); expectError(await execaCommand`unicorns ${await execaCommand({reject: false})`echo foo`}`); expectError(await execaCommand`unicorns ${execaCommand`echo foo`}`); expectError(await execaCommand`unicorns ${[await execaCommand`echo foo`, 'bar']}`); expectError(await execaCommand`unicorns ${[execaCommand`echo foo`, 'bar']}`); expectError(execaCommandSync()); expectError(execaCommandSync(true)); expectError(execaCommandSync(['unicorns', 'arg'])); expectType>(execaCommandSync('unicorns')); expectError(execaCommandSync(fileUrl)); expectError(execaCommandSync('unicorns', [])); expectError(execaCommandSync('unicorns', ['foo'])); expectType>(execaCommandSync('unicorns', {})); expectError(execaCommandSync('unicorns', [], {})); expectError(execaCommandSync('unicorns', 'foo')); expectError(execaCommandSync('unicorns', [true])); expectError(execaCommandSync('unicorns', [], [])); expectError(execaCommandSync('unicorns', {other: true})); expectType>(execaCommandSync`unicorns`); expectAssignable(execaCommandSync({})); expectType>(execaCommandSync({})('unicorns')); expectType>(execaCommandSync({})`unicorns`); expectType>(execaCommandSync('unicorns')); expectType>(execaCommandSync`unicorns`); expectAssignable<{stdout: string}>(execaCommandSync('unicorns')); expectAssignable<{stdout: Uint8Array}>(execaCommandSync('unicorns', {encoding: 'buffer'})); expectAssignable<{stdout: string}>(execaCommandSync({})('unicorns')); expectAssignable<{stdout: Uint8Array}>(execaCommandSync({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(execaCommandSync({})({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(execaCommandSync({encoding: 'buffer'})({})('unicorns')); expectAssignable<{stdout: string}>(execaCommandSync({})`unicorns`); expectAssignable<{stdout: Uint8Array}>(execaCommandSync({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(execaCommandSync({})({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(execaCommandSync({encoding: 'buffer'})({})`unicorns`); expectType>(execaCommandSync`${'unicorns'}`); expectType>(execaCommandSync`unicorns ${'foo'}`); expectError(execaCommandSync`unicorns ${'foo'} ${'bar'}`); expectError(execaCommandSync`unicorns ${1}`); expectError(execaCommandSync`unicorns ${stringArray}`); expectError(execaCommandSync`unicorns ${[1, 2]}`); expectError(execaCommandSync`unicorns ${execaCommandSync`echo foo`}`); expectError(execaCommandSync`unicorns ${[execaCommandSync`echo foo`, 'bar']}`); expectType>(execaCommandSync`unicorns ${false.toString()}`); expectError(execaCommandSync`unicorns ${false}`); ================================================ FILE: test-d/methods/list.test-d.ts ================================================ import {expectAssignable} from 'tsd'; import { type ExecaMethod, type ExecaSyncMethod, type ExecaNodeMethod, type ExecaScriptMethod, type ExecaScriptSyncMethod, execa, execaSync, execaNode, $, } from '../../index.js'; const options = {preferLocal: true} as const; const secondOptions = {node: true} as const; expectAssignable(execa); expectAssignable(execa({})); expectAssignable(execa({})({})); expectAssignable(execa(options)); expectAssignable(execa(options)(secondOptions)); expectAssignable(execa(options)({})); expectAssignable(execa({})(options)); expectAssignable(execaSync); expectAssignable(execaSync({})); expectAssignable(execaSync({})({})); expectAssignable(execaSync(options)); expectAssignable(execaSync(options)(secondOptions)); expectAssignable(execaSync(options)({})); expectAssignable(execaSync({})(options)); expectAssignable(execaNode); expectAssignable(execaNode({})); expectAssignable(execaNode({})({})); expectAssignable(execaNode(options)); expectAssignable(execaNode(options)(secondOptions)); expectAssignable(execaNode(options)({})); expectAssignable(execaNode({})(options)); expectAssignable($); expectAssignable($({})); expectAssignable($({})({})); expectAssignable($(options)); expectAssignable($(options)(secondOptions)); expectAssignable($(options)({})); expectAssignable($({})(options)); expectAssignable($.sync); expectAssignable($.sync({})); expectAssignable($.sync({})({})); expectAssignable($.sync(options)); expectAssignable($.sync(options)(secondOptions)); expectAssignable($.sync(options)({})); expectAssignable($.sync({})(options)); expectAssignable($.s); expectAssignable($.s({})); expectAssignable($.s({})({})); expectAssignable($.s(options)); expectAssignable($.s(options)(secondOptions)); expectAssignable($.s(options)({})); expectAssignable($.s({})(options)); ================================================ FILE: test-d/methods/main-async.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import {execa, type Result, type ResultPromise} from '../../index.js'; const fileUrl = new URL('file:///test'); const stringArray = ['foo', 'bar'] as const; expectError(execa()); expectError(execa(true)); expectError(execa(['unicorns', 'arg'])); expectAssignable(execa('unicorns')); expectAssignable(execa(fileUrl)); expectAssignable(execa('unicorns', [])); expectAssignable(execa('unicorns', ['foo'])); expectError(execa('unicorns', 'foo')); expectError(execa('unicorns', [true])); expectAssignable(execa('unicorns', {})); expectAssignable(execa('unicorns', [], {})); expectError(execa('unicorns', [], [])); expectError(execa('unicorns', {other: true})); expectAssignable(execa`unicorns`); expectType>(await execa('unicorns')); expectType>(await execa`unicorns`); expectAssignable(execa({})); expectAssignable(execa({})('unicorns')); expectAssignable(execa({})`unicorns`); expectAssignable<{stdout: string}>(await execa('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execa('unicorns', {encoding: 'buffer'})); expectAssignable<{stdout: string}>(await execa('unicorns', ['foo'])); expectAssignable<{stdout: Uint8Array}>(await execa('unicorns', ['foo'], {encoding: 'buffer'})); expectAssignable<{stdout: string}>(await execa({})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execa({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execa({})({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execa({encoding: 'buffer'})({})('unicorns')); expectAssignable<{stdout: string}>(await execa({})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execa({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execa({})({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execa({encoding: 'buffer'})({})`unicorns`); expectType>(await execa`${'unicorns'}`); expectType>(await execa`unicorns ${'foo'}`); expectType>(await execa`unicorns ${'foo'} ${'bar'}`); expectType>(await execa`unicorns ${1}`); expectType>(await execa`unicorns ${stringArray}`); expectType>(await execa`unicorns ${[1, 2]}`); expectType>(await execa`unicorns ${false.toString()}`); expectError(await execa`unicorns ${false}`); expectType>(await execa`unicorns ${await execa`echo foo`}`); expectType>(await execa`unicorns ${await execa({reject: false})`echo foo`}`); expectError(await execa`unicorns ${execa`echo foo`}`); expectType>(await execa`unicorns ${[await execa`echo foo`, 'bar']}`); expectError(await execa`unicorns ${[execa`echo foo`, 'bar']}`); ================================================ FILE: test-d/methods/main-sync.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import {execaSync, type SyncResult} from '../../index.js'; const fileUrl = new URL('file:///test'); const stringArray = ['foo', 'bar'] as const; expectError(execaSync()); expectError(execaSync(true)); expectError(execaSync(['unicorns', 'arg'])); expectType>(execaSync('unicorns')); expectType>(execaSync(fileUrl)); expectType>(execaSync('unicorns', [])); expectType>(execaSync('unicorns', ['foo'])); expectError(execaSync('unicorns', 'foo')); expectError(execaSync('unicorns', [true])); expectType>(execaSync('unicorns', {})); expectType>(execaSync('unicorns', [], {})); expectError(execaSync('unicorns', [], [])); expectError(execaSync('unicorns', {other: true})); expectType>(execaSync`unicorns`); expectType>(execaSync('unicorns')); expectType>(execaSync`unicorns`); expectAssignable(execaSync({})); expectType>(execaSync({})('unicorns')); expectType>(execaSync({})`unicorns`); expectAssignable<{stdout: string}>(execaSync('unicorns')); expectAssignable<{stdout: Uint8Array}>(execaSync('unicorns', {encoding: 'buffer'})); expectAssignable<{stdout: string}>(execaSync('unicorns', ['foo'])); expectAssignable<{stdout: Uint8Array}>(execaSync('unicorns', ['foo'], {encoding: 'buffer'})); expectAssignable<{stdout: string}>(execaSync({})('unicorns')); expectAssignable<{stdout: Uint8Array}>(execaSync({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(execaSync({})({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(execaSync({encoding: 'buffer'})({})('unicorns')); expectAssignable<{stdout: string}>(execaSync({})`unicorns`); expectAssignable<{stdout: Uint8Array}>(execaSync({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(execaSync({})({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(execaSync({encoding: 'buffer'})({})`unicorns`); expectType>(execaSync`${'unicorns'}`); expectType>(execaSync`unicorns ${'foo'}`); expectType>(execaSync`unicorns ${'foo'} ${'bar'}`); expectType>(execaSync`unicorns ${1}`); expectType>(execaSync`unicorns ${stringArray}`); expectType>(execaSync`unicorns ${[1, 2]}`); expectType>(execaSync`unicorns ${false.toString()}`); expectError(execaSync`unicorns ${false}`); expectType>(execaSync`unicorns ${execaSync`echo foo`}`); expectType>(execaSync`unicorns ${execaSync({reject: false})`echo foo`}`); expectType>(execaSync`unicorns ${[execaSync`echo foo`, 'bar']}`); ================================================ FILE: test-d/methods/node.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import {execaNode, type Result, type ResultPromise} from '../../index.js'; const fileUrl = new URL('file:///test'); const stringArray = ['foo', 'bar'] as const; expectError(execaNode()); expectError(execaNode(true)); expectError(execaNode(['unicorns', 'arg'])); expectAssignable(execaNode('unicorns')); expectAssignable(execaNode(fileUrl)); expectAssignable(execaNode('unicorns', [])); expectAssignable(execaNode('unicorns', ['foo'])); expectError(execaNode('unicorns', 'foo')); expectError(execaNode('unicorns', [true])); expectAssignable(execaNode('unicorns', {})); expectAssignable(execaNode('unicorns', [], {})); expectError(execaNode('unicorns', [], [])); expectError(execaNode('unicorns', {other: true})); expectAssignable(execaNode`unicorns`); expectType>(await execaNode('unicorns')); expectType>(await execaNode`unicorns`); expectAssignable(execaNode({})); expectAssignable(execaNode({})('unicorns')); expectAssignable(execaNode({})`unicorns`); expectAssignable<{stdout: string}>(await execaNode('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execaNode('unicorns', {encoding: 'buffer'})); expectAssignable<{stdout: string}>(await execaNode('unicorns', ['foo'])); expectAssignable<{stdout: Uint8Array}>(await execaNode('unicorns', ['foo'], {encoding: 'buffer'})); expectAssignable<{stdout: string}>(await execaNode({})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execaNode({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execaNode({})({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await execaNode({encoding: 'buffer'})({})('unicorns')); expectAssignable<{stdout: string}>(await execaNode({})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execaNode({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execaNode({})({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await execaNode({encoding: 'buffer'})({})`unicorns`); expectType>(await execaNode`${'unicorns'}`); expectType>(await execaNode`unicorns ${'foo'}`); expectType>(await execaNode`unicorns ${'foo'} ${'bar'}`); expectType>(await execaNode`unicorns ${1}`); expectType>(await execaNode`unicorns ${stringArray}`); expectType>(await execaNode`unicorns ${[1, 2]}`); expectType>(await execaNode`unicorns ${false.toString()}`); expectError(await execaNode`unicorns ${false}`); expectType>(await execaNode`unicorns ${await execaNode`echo foo`}`); expectType>(await execaNode`unicorns ${await execaNode({reject: false})`echo foo`}`); expectError(await execaNode`unicorns ${execaNode`echo foo`}`); expectType>(await execaNode`unicorns ${[await execaNode`echo foo`, 'bar']}`); expectError(await execaNode`unicorns ${[execaNode`echo foo`, 'bar']}`); expectAssignable(execaNode('unicorns', {nodePath: './node'})); expectAssignable(execaNode('unicorns', {nodePath: fileUrl})); expectAssignable<{stdout: string}>(await execaNode('unicorns', {nodeOptions: ['--async-stack-traces']})); expectAssignable<{stdout: Uint8Array}>(await execaNode('unicorns', {nodeOptions: ['--async-stack-traces'], encoding: 'buffer'})); expectAssignable<{stdout: string}>(await execaNode('unicorns', ['foo'], {nodeOptions: ['--async-stack-traces']})); expectAssignable<{stdout: Uint8Array}>(await execaNode('unicorns', ['foo'], {nodeOptions: ['--async-stack-traces'], encoding: 'buffer'})); ================================================ FILE: test-d/methods/script-s.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import {$, type SyncResult} from '../../index.js'; const fileUrl = new URL('file:///test'); const stringArray = ['foo', 'bar'] as const; expectError($.s()); expectError($.s(true)); expectError($.s(['unicorns', 'arg'])); expectType>($.s('unicorns')); expectType>($.s(fileUrl)); expectType>($.s('unicorns', [])); expectType>($.s('unicorns', ['foo'])); expectError($.s('unicorns', 'foo')); expectError($.s('unicorns', [true])); expectType>($.s('unicorns', {})); expectType>($.s('unicorns', [], {})); expectError($.s('unicorns', [], [])); expectError($.s('unicorns', {other: true})); expectType>($.s`unicorns`); expectType>($.s('unicorns')); expectType>($.s`unicorns`); expectAssignable($.s({})); expectType>($.s({})('unicorns')); expectType>($({}).s('unicorns')); expectType>($.s({})`unicorns`); expectType>($({}).s`unicorns`); expectAssignable<{stdout: string}>($.s('unicorns')); expectAssignable<{stdout: Uint8Array}>($.s('unicorns', {encoding: 'buffer'})); expectAssignable<{stdout: string}>($.s('unicorns', ['foo'])); expectAssignable<{stdout: Uint8Array}>($.s('unicorns', ['foo'], {encoding: 'buffer'})); expectAssignable<{stdout: string}>($.s({})('unicorns')); expectAssignable<{stdout: string}>($({}).s('unicorns')); expectAssignable<{stdout: Uint8Array}>($.s({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>($({encoding: 'buffer'}).s('unicorns')); expectAssignable<{stdout: Uint8Array}>($.s({})({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>($({})({encoding: 'buffer'}).s('unicorns')); expectAssignable<{stdout: Uint8Array}>($.s({encoding: 'buffer'})({})('unicorns')); expectAssignable<{stdout: Uint8Array}>($({encoding: 'buffer'}).s({})('unicorns')); expectAssignable<{stdout: string}>($.s({})`unicorns`); expectAssignable<{stdout: string}>($({}).s`unicorns`); expectAssignable<{stdout: Uint8Array}>($.s({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>($({encoding: 'buffer'}).s`unicorns`); expectAssignable<{stdout: Uint8Array}>($.s({})({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>($({})({encoding: 'buffer'}).s`unicorns`); expectAssignable<{stdout: Uint8Array}>($.s({encoding: 'buffer'})({})`unicorns`); expectAssignable<{stdout: Uint8Array}>($({encoding: 'buffer'}).s({})`unicorns`); expectType>($.s`${'unicorns'}`); expectType>($.s`unicorns ${'foo'}`); expectType>($.s`unicorns ${'foo'} ${'bar'}`); expectType>($.s`unicorns ${1}`); expectType>($.s`unicorns ${stringArray}`); expectType>($.s`unicorns ${[1, 2]}`); expectType>($.s`unicorns ${false.toString()}`); expectError($.s`unicorns ${false}`); expectType>($.s`unicorns ${$.s`echo foo`}`); expectType>($.s`unicorns ${$.s({reject: false})`echo foo`}`); expectType>($.s`unicorns ${[$.s`echo foo`, 'bar']}`); ================================================ FILE: test-d/methods/script-sync.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import {$, type SyncResult} from '../../index.js'; const fileUrl = new URL('file:///test'); const stringArray = ['foo', 'bar'] as const; expectError($.sync()); expectError($.sync(true)); expectError($.sync(['unicorns', 'arg'])); expectType>($.sync('unicorns')); expectType>($.sync(fileUrl)); expectType>($.sync('unicorns', [])); expectType>($.sync('unicorns', ['foo'])); expectError($.sync('unicorns', 'foo')); expectError($.sync('unicorns', [true])); expectType>($.sync('unicorns', {})); expectType>($.sync('unicorns', [], {})); expectError($.sync('unicorns', [], [])); expectError($.sync('unicorns', {other: true})); expectType>($.sync`unicorns`); expectType>($.sync('unicorns')); expectType>($.sync`unicorns`); expectAssignable($.sync({})); expectType>($.sync({})('unicorns')); expectType>($({}).sync('unicorns')); expectType>($.sync({})`unicorns`); expectType>($({}).sync`unicorns`); expectAssignable<{stdout: string}>($.sync('unicorns')); expectAssignable<{stdout: Uint8Array}>($.sync('unicorns', {encoding: 'buffer'})); expectAssignable<{stdout: string}>($.sync('unicorns', ['foo'])); expectAssignable<{stdout: Uint8Array}>($.sync('unicorns', ['foo'], {encoding: 'buffer'})); expectAssignable<{stdout: string}>($.sync({})('unicorns')); expectAssignable<{stdout: string}>($({}).sync('unicorns')); expectAssignable<{stdout: Uint8Array}>($.sync({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>($({encoding: 'buffer'}).sync('unicorns')); expectAssignable<{stdout: Uint8Array}>($.sync({})({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>($({})({encoding: 'buffer'}).sync('unicorns')); expectAssignable<{stdout: Uint8Array}>($.sync({encoding: 'buffer'})({})('unicorns')); expectAssignable<{stdout: Uint8Array}>($({encoding: 'buffer'}).sync({})('unicorns')); expectAssignable<{stdout: string}>($.sync({})`unicorns`); expectAssignable<{stdout: string}>($({}).sync`unicorns`); expectAssignable<{stdout: Uint8Array}>($.sync({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>($({encoding: 'buffer'}).sync`unicorns`); expectAssignable<{stdout: Uint8Array}>($.sync({})({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>($({})({encoding: 'buffer'}).sync`unicorns`); expectAssignable<{stdout: Uint8Array}>($.sync({encoding: 'buffer'})({})`unicorns`); expectAssignable<{stdout: Uint8Array}>($({encoding: 'buffer'}).sync({})`unicorns`); expectType>($.sync`${'unicorns'}`); expectType>($.sync`unicorns ${'foo'}`); expectType>($.sync`unicorns ${'foo'} ${'bar'}`); expectType>($.sync`unicorns ${1}`); expectType>($.sync`unicorns ${stringArray}`); expectType>($.sync`unicorns ${[1, 2]}`); expectType>($.sync`unicorns ${false.toString()}`); expectError($.sync`unicorns ${false}`); expectType>($.sync`unicorns ${$.sync`echo foo`}`); expectType>($.sync`unicorns ${$.sync({reject: false})`echo foo`}`); expectType>($.sync`unicorns ${[$.sync`echo foo`, 'bar']}`); ================================================ FILE: test-d/methods/script.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import {$, type Result, type ResultPromise} from '../../index.js'; const fileUrl = new URL('file:///test'); const stringArray = ['foo', 'bar'] as const; expectError($()); expectError($(true)); expectError($(['unicorns', 'arg'])); expectAssignable($('unicorns')); expectAssignable($(fileUrl)); expectAssignable($('unicorns', [])); expectAssignable($('unicorns', ['foo'])); expectError($('unicorns', 'foo')); expectError($('unicorns', [true])); expectAssignable($('unicorns', {})); expectAssignable($('unicorns', [], {})); expectError($('unicorns', [], [])); expectError($('unicorns', {other: true})); expectAssignable($`unicorns`); expectType>(await $('unicorns')); expectType>(await $`unicorns`); expectAssignable($({})); expectAssignable($({})('unicorns')); expectAssignable($({})`unicorns`); expectAssignable<{stdout: string}>(await $('unicorns')); expectAssignable<{stdout: Uint8Array}>(await $('unicorns', {encoding: 'buffer'})); expectAssignable<{stdout: string}>(await $('unicorns', ['foo'])); expectAssignable<{stdout: Uint8Array}>(await $('unicorns', ['foo'], {encoding: 'buffer'})); expectAssignable<{stdout: string}>(await $({})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await $({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await $({})({encoding: 'buffer'})('unicorns')); expectAssignable<{stdout: Uint8Array}>(await $({encoding: 'buffer'})({})('unicorns')); expectAssignable<{stdout: string}>(await $({})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await $({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await $({})({encoding: 'buffer'})`unicorns`); expectAssignable<{stdout: Uint8Array}>(await $({encoding: 'buffer'})({})`unicorns`); expectType>(await $`${'unicorns'}`); expectType>(await $`unicorns ${'foo'}`); expectType>(await $`unicorns ${'foo'} ${'bar'}`); expectType>(await $`unicorns ${1}`); expectType>(await $`unicorns ${stringArray}`); expectType>(await $`unicorns ${[1, 2]}`); expectType>(await $`unicorns ${false.toString()}`); expectError(await $`unicorns ${false}`); expectType>(await $`unicorns ${await $`echo foo`}`); expectType>(await $`unicorns ${await $({reject: false})`echo foo`}`); expectError(await $`unicorns ${$`echo foo`}`); expectType>(await $`unicorns ${[await $`echo foo`, 'bar']}`); expectError(await $`unicorns ${[$`echo foo`, 'bar']}`); ================================================ FILE: test-d/methods/template.test-d.ts ================================================ import {expectAssignable, expectNotAssignable} from 'tsd'; import {execa, type TemplateExpression} from '../../index.js'; const stringArray = ['foo', 'bar'] as const; const numberArray = [1, 2] as const; expectAssignable('unicorns'); expectAssignable(1); expectAssignable(stringArray); expectAssignable(numberArray); expectAssignable(false.toString()); expectNotAssignable(false); expectAssignable(await execa`echo foo`); expectAssignable(await execa({reject: false})`echo foo`); expectNotAssignable(execa`echo foo`); expectAssignable([await execa`echo foo`, 'bar']); expectNotAssignable([execa`echo foo`, 'bar']); ================================================ FILE: test-d/pipe.test-d.ts ================================================ import {createWriteStream} from 'node:fs'; import {expectType, expectNotType, expectError} from 'tsd'; import { execa, execaSync, $, type Result, } from '../index.js'; const fileUrl = new URL('file:///test'); const stringArray = ['foo', 'bar'] as const; const pipeOptions = {from: 'stderr', to: 'fd3', all: true} as const; const subprocess = execa('unicorns', {all: true}); const bufferSubprocess = execa('unicorns', {encoding: 'buffer', all: true}); const scriptSubprocess = $`unicorns`; const bufferResult = await bufferSubprocess; type BufferExecaReturnValue = typeof bufferResult; type EmptyExecaReturnValue = Result<{}>; type ShortcutExecaReturnValue = Result; expectNotType(await subprocess.pipe(subprocess)); expectNotType(await scriptSubprocess.pipe(subprocess)); expectType(await subprocess.pipe(bufferSubprocess)); expectType(await scriptSubprocess.pipe(bufferSubprocess)); expectType(await subprocess.pipe(bufferSubprocess, pipeOptions)); expectType(await scriptSubprocess.pipe(bufferSubprocess, pipeOptions)); expectType(await subprocess.pipe`stdin`); expectType(await scriptSubprocess.pipe`stdin`); expectType(await subprocess.pipe(pipeOptions)`stdin`); expectType(await scriptSubprocess.pipe(pipeOptions)`stdin`); expectType(await subprocess.pipe('stdin')); expectType(await scriptSubprocess.pipe('stdin')); expectType(await subprocess.pipe('stdin', pipeOptions)); expectType(await scriptSubprocess.pipe('stdin', pipeOptions)); expectType(await subprocess.pipe(subprocess).pipe(bufferSubprocess)); expectType(await scriptSubprocess.pipe(subprocess).pipe(bufferSubprocess)); expectType(await subprocess.pipe(subprocess, pipeOptions).pipe(bufferSubprocess)); expectType(await scriptSubprocess.pipe(subprocess, pipeOptions).pipe(bufferSubprocess)); expectType(await subprocess.pipe(subprocess).pipe(bufferSubprocess, pipeOptions)); expectType(await scriptSubprocess.pipe(subprocess).pipe(bufferSubprocess, pipeOptions)); expectType(await subprocess.pipe(subprocess).pipe`stdin`); expectType(await scriptSubprocess.pipe(subprocess).pipe`stdin`); expectType(await subprocess.pipe(subprocess, pipeOptions).pipe`stdin`); expectType(await scriptSubprocess.pipe(subprocess, pipeOptions).pipe`stdin`); expectType(await subprocess.pipe(subprocess).pipe(pipeOptions)`stdin`); expectType(await scriptSubprocess.pipe(subprocess).pipe(pipeOptions)`stdin`); expectType(await subprocess.pipe(subprocess).pipe('stdin')); expectType(await scriptSubprocess.pipe(subprocess).pipe('stdin')); expectType(await subprocess.pipe(subprocess, pipeOptions).pipe('stdin')); expectType(await scriptSubprocess.pipe(subprocess, pipeOptions).pipe('stdin')); expectType(await subprocess.pipe(subprocess).pipe('stdin', pipeOptions)); expectType(await scriptSubprocess.pipe(subprocess).pipe('stdin', pipeOptions)); expectType(await subprocess.pipe`stdin`.pipe(bufferSubprocess)); expectType(await scriptSubprocess.pipe`stdin`.pipe(bufferSubprocess)); expectType(await subprocess.pipe(pipeOptions)`stdin`.pipe(bufferSubprocess)); expectType(await scriptSubprocess.pipe(pipeOptions)`stdin`.pipe(bufferSubprocess)); expectType(await subprocess.pipe`stdin`.pipe(bufferSubprocess, pipeOptions)); expectType(await scriptSubprocess.pipe`stdin`.pipe(bufferSubprocess, pipeOptions)); expectType(await subprocess.pipe`stdin`.pipe`stdin`); expectType(await scriptSubprocess.pipe`stdin`.pipe`stdin`); expectType(await subprocess.pipe(pipeOptions)`stdin`.pipe`stdin`); expectType(await scriptSubprocess.pipe(pipeOptions)`stdin`.pipe`stdin`); expectType(await subprocess.pipe`stdin`.pipe(pipeOptions)`stdin`); expectType(await scriptSubprocess.pipe`stdin`.pipe(pipeOptions)`stdin`); expectType(await subprocess.pipe`stdin`.pipe('stdin')); expectType(await scriptSubprocess.pipe`stdin`.pipe('stdin')); expectType(await subprocess.pipe(pipeOptions)`stdin`.pipe('stdin')); expectType(await scriptSubprocess.pipe(pipeOptions)`stdin`.pipe('stdin')); expectType(await subprocess.pipe`stdin`.pipe('stdin', pipeOptions)); expectType(await scriptSubprocess.pipe`stdin`.pipe('stdin', pipeOptions)); expectType(await subprocess.pipe('pipe').pipe(bufferSubprocess)); expectType(await scriptSubprocess.pipe('pipe').pipe(bufferSubprocess)); expectType(await subprocess.pipe('pipe', pipeOptions).pipe(bufferSubprocess)); expectType(await scriptSubprocess.pipe('pipe', pipeOptions).pipe(bufferSubprocess)); expectType(await subprocess.pipe('pipe').pipe(bufferSubprocess, pipeOptions)); expectType(await scriptSubprocess.pipe('pipe').pipe(bufferSubprocess, pipeOptions)); expectType(await subprocess.pipe('pipe').pipe`stdin`); expectType(await scriptSubprocess.pipe('pipe').pipe`stdin`); expectType(await subprocess.pipe('pipe', pipeOptions).pipe`stdin`); expectType(await scriptSubprocess.pipe('pipe', pipeOptions).pipe`stdin`); expectType(await subprocess.pipe('pipe').pipe(pipeOptions)`stdin`); expectType(await scriptSubprocess.pipe('pipe').pipe(pipeOptions)`stdin`); expectType(await subprocess.pipe('pipe').pipe('stdin')); expectType(await scriptSubprocess.pipe('pipe').pipe('stdin')); expectType(await subprocess.pipe('pipe', pipeOptions).pipe('stdin')); expectType(await scriptSubprocess.pipe('pipe', pipeOptions).pipe('stdin')); expectType(await subprocess.pipe('pipe').pipe('stdin', pipeOptions)); expectType(await scriptSubprocess.pipe('pipe').pipe('stdin', pipeOptions)); await subprocess.pipe(bufferSubprocess, {}); await scriptSubprocess.pipe(bufferSubprocess, {}); await subprocess.pipe({})`stdin`; await scriptSubprocess.pipe({})`stdin`; await subprocess.pipe('stdin', {}); await scriptSubprocess.pipe('stdin', {}); expectError(subprocess.pipe(bufferSubprocess).stdout); expectError(scriptSubprocess.pipe(bufferSubprocess).stdout); expectError(subprocess.pipe`stdin`.stdout); expectError(scriptSubprocess.pipe`stdin`.stdout); expectError(subprocess.pipe('stdin').stdout); expectError(scriptSubprocess.pipe('stdin').stdout); expectError(await subprocess.pipe({})({})); expectError(await scriptSubprocess.pipe({})({})); expectError(await subprocess.pipe({})(subprocess)); expectError(await scriptSubprocess.pipe({})(subprocess)); expectError(await subprocess.pipe({})('stdin')); expectError(await scriptSubprocess.pipe({})('stdin')); expectError(subprocess.pipe(createWriteStream('output.txt'))); expectError(scriptSubprocess.pipe(createWriteStream('output.txt'))); expectError(subprocess.pipe(false)); expectError(scriptSubprocess.pipe(false)); expectError(subprocess.pipe(bufferSubprocess, 'stdout')); expectError(scriptSubprocess.pipe(bufferSubprocess, 'stdout')); expectError(subprocess.pipe('stdout')`stdin`); expectError(scriptSubprocess.pipe('stdout')`stdin`); await subprocess.pipe(bufferSubprocess, {from: 'stdout'}); await scriptSubprocess.pipe(bufferSubprocess, {from: 'stdout'}); await subprocess.pipe({from: 'stdout'})`stdin`; await scriptSubprocess.pipe({from: 'stdout'})`stdin`; await subprocess.pipe('stdin', {from: 'stdout'}); await scriptSubprocess.pipe('stdin', {from: 'stdout'}); await subprocess.pipe(bufferSubprocess, {from: 'stderr'}); await scriptSubprocess.pipe(bufferSubprocess, {from: 'stderr'}); await subprocess.pipe({from: 'stderr'})`stdin`; await scriptSubprocess.pipe({from: 'stderr'})`stdin`; await subprocess.pipe('stdin', {from: 'stderr'}); await scriptSubprocess.pipe('stdin', {from: 'stderr'}); await subprocess.pipe(bufferSubprocess, {from: 'all'}); await scriptSubprocess.pipe(bufferSubprocess, {from: 'all'}); await subprocess.pipe({from: 'all'})`stdin`; await scriptSubprocess.pipe({from: 'all'})`stdin`; await subprocess.pipe('stdin', {from: 'all'}); await scriptSubprocess.pipe('stdin', {from: 'all'}); await subprocess.pipe(bufferSubprocess, {from: 'fd3'}); await scriptSubprocess.pipe(bufferSubprocess, {from: 'fd3'}); await subprocess.pipe({from: 'fd3'})`stdin`; await scriptSubprocess.pipe({from: 'fd3'})`stdin`; await subprocess.pipe('stdin', {from: 'fd3'}); await scriptSubprocess.pipe('stdin', {from: 'fd3'}); expectError(subprocess.pipe(bufferSubprocess, {from: 'stdin'})); expectError(scriptSubprocess.pipe(bufferSubprocess, {from: 'stdin'})); expectError(subprocess.pipe({from: 'stdin'})`stdin`); expectError(scriptSubprocess.pipe({from: 'stdin'})`stdin`); expectError(subprocess.pipe('stdin', {from: 'stdin'})); expectError(scriptSubprocess.pipe('stdin', {from: 'stdin'})); await subprocess.pipe(bufferSubprocess, {to: 'stdin'}); await scriptSubprocess.pipe(bufferSubprocess, {to: 'stdin'}); await subprocess.pipe({to: 'stdin'})`stdin`; await scriptSubprocess.pipe({to: 'stdin'})`stdin`; await subprocess.pipe('stdin', {to: 'stdin'}); await scriptSubprocess.pipe('stdin', {to: 'stdin'}); await subprocess.pipe(bufferSubprocess, {to: 'fd3'}); await scriptSubprocess.pipe(bufferSubprocess, {to: 'fd3'}); await subprocess.pipe({to: 'fd3'})`stdin`; await scriptSubprocess.pipe({to: 'fd3'})`stdin`; await subprocess.pipe('stdin', {to: 'fd3'}); await scriptSubprocess.pipe('stdin', {to: 'fd3'}); expectError(subprocess.pipe(bufferSubprocess, {to: 'stdout'})); expectError(scriptSubprocess.pipe(bufferSubprocess, {to: 'stdout'})); expectError(subprocess.pipe({to: 'stdout'})`stdin`); expectError(scriptSubprocess.pipe({to: 'stdout'})`stdin`); expectError(subprocess.pipe('stdin', {to: 'stdout'})); expectError(scriptSubprocess.pipe('stdin', {to: 'stdout'})); await subprocess.pipe(bufferSubprocess, {unpipeSignal: new AbortController().signal}); await scriptSubprocess.pipe(bufferSubprocess, {unpipeSignal: new AbortController().signal}); await subprocess.pipe({unpipeSignal: new AbortController().signal})`stdin`; await scriptSubprocess.pipe({unpipeSignal: new AbortController().signal})`stdin`; await subprocess.pipe('stdin', {unpipeSignal: new AbortController().signal}); await scriptSubprocess.pipe('stdin', {unpipeSignal: new AbortController().signal}); expectError(await subprocess.pipe(bufferSubprocess, {unpipeSignal: true})); expectError(await scriptSubprocess.pipe(bufferSubprocess, {unpipeSignal: true})); expectError(await subprocess.pipe({unpipeSignal: true})`stdin`); expectError(await scriptSubprocess.pipe({unpipeSignal: true})`stdin`); expectError(await subprocess.pipe('stdin', {unpipeSignal: true})); expectError(await scriptSubprocess.pipe('stdin', {unpipeSignal: true})); expectType(await subprocess.pipe('stdin')); await subprocess.pipe('stdin'); await subprocess.pipe(fileUrl); await subprocess.pipe('stdin', []); await subprocess.pipe('stdin', stringArray); await subprocess.pipe('stdin', stringArray, {}); await subprocess.pipe('stdin', stringArray, {from: 'stderr', to: 'stdin', all: true}); await subprocess.pipe('stdin', {from: 'stderr'}); await subprocess.pipe('stdin', {to: 'stdin'}); await subprocess.pipe('stdin', {all: true}); expectError(await subprocess.pipe(stringArray)); expectError(await subprocess.pipe('stdin', 'foo')); expectError(await subprocess.pipe('stdin', [false])); expectError(await subprocess.pipe('stdin', [], false)); expectError(await subprocess.pipe('stdin', {other: true})); expectError(await subprocess.pipe('stdin', [], {other: true})); expectError(await subprocess.pipe('stdin', {from: 'fd'})); expectError(await subprocess.pipe('stdin', [], {from: 'fd'})); expectError(await subprocess.pipe('stdin', {from: 'fdNotANumber'})); expectError(await subprocess.pipe('stdin', [], {from: 'fdNotANumber'})); expectError(await subprocess.pipe('stdin', {from: 'other'})); expectError(await subprocess.pipe('stdin', [], {from: 'other'})); expectError(await subprocess.pipe('stdin', {to: 'fd'})); expectError(await subprocess.pipe('stdin', [], {to: 'fd'})); expectError(await subprocess.pipe('stdin', {to: 'fdNotANumber'})); expectError(await subprocess.pipe('stdin', [], {to: 'fdNotANumber'})); expectError(await subprocess.pipe('stdin', {to: 'other'})); expectError(await subprocess.pipe('stdin', [], {to: 'other'})); const pipeResult = await subprocess.pipe`stdin`; expectType(pipeResult.stdout); const ignorePipeResult = await subprocess.pipe({stdout: 'ignore'})`stdin`; expectType(ignorePipeResult.stdout); const scriptPipeResult = await scriptSubprocess.pipe`stdin`; expectType(scriptPipeResult.stdout); const ignoreScriptPipeResult = await scriptSubprocess.pipe({stdout: 'ignore'})`stdin`; expectType(ignoreScriptPipeResult.stdout); const shortcutPipeResult = await subprocess.pipe('stdin'); expectType(shortcutPipeResult.stdout); const ignoreShortcutPipeResult = await subprocess.pipe('stdin', {stdout: 'ignore'}); expectType(ignoreShortcutPipeResult.stdout); const scriptShortcutPipeResult = await scriptSubprocess.pipe('stdin'); expectType(scriptShortcutPipeResult.stdout); const ignoreShortcutScriptPipeResult = await scriptSubprocess.pipe('stdin', {stdout: 'ignore'}); expectType(ignoreShortcutScriptPipeResult.stdout); const unicornsResult = execaSync('unicorns'); expectError(unicornsResult.pipe); ================================================ FILE: test-d/return/ignore-option.test-d.ts ================================================ import {type Readable, type Writable} from 'node:stream'; import {expectType, expectError} from 'tsd'; import { execa, execaSync, type ExecaError, type ExecaSyncError, } from '../../index.js'; const ignoreAnySubprocess = execa('unicorns', { stdin: 'ignore', stdout: 'ignore', stderr: 'ignore', all: true, }); expectType(ignoreAnySubprocess.stdin); expectType(ignoreAnySubprocess.stdio[0]); expectType(ignoreAnySubprocess.stdout); expectType(ignoreAnySubprocess.stdio[1]); expectType(ignoreAnySubprocess.stderr); expectType(ignoreAnySubprocess.stdio[2]); expectType(ignoreAnySubprocess.all); expectError(ignoreAnySubprocess.stdio[3].destroy()); const ignoreAnyResult = await ignoreAnySubprocess; expectType(ignoreAnyResult.stdout); expectType(ignoreAnyResult.stdio[1]); expectType(ignoreAnyResult.stderr); expectType(ignoreAnyResult.stdio[2]); expectType(ignoreAnyResult.all); const ignoreAllSubprocess = execa('unicorns', {stdio: 'ignore', all: true}); expectType(ignoreAllSubprocess.stdin); expectType(ignoreAllSubprocess.stdio[0]); expectType(ignoreAllSubprocess.stdout); expectType(ignoreAllSubprocess.stdio[1]); expectType(ignoreAllSubprocess.stderr); expectType(ignoreAllSubprocess.stdio[2]); expectType(ignoreAllSubprocess.all); expectError(ignoreAllSubprocess.stdio[3].destroy()); const ignoreAllResult = await ignoreAllSubprocess; expectType(ignoreAllResult.stdout); expectType(ignoreAllResult.stdio[1]); expectType(ignoreAllResult.stderr); expectType(ignoreAllResult.stdio[2]); expectType(ignoreAllResult.all); const ignoreStdioArraySubprocess = execa('unicorns', {stdio: ['ignore', 'ignore', 'pipe', 'pipe'], all: true}); expectType(ignoreStdioArraySubprocess.stdin); expectType(ignoreStdioArraySubprocess.stdio[0]); expectType(ignoreStdioArraySubprocess.stdout); expectType(ignoreStdioArraySubprocess.stdio[1]); expectType(ignoreStdioArraySubprocess.stderr); expectType(ignoreStdioArraySubprocess.stdio[2]); expectType(ignoreStdioArraySubprocess.all); expectType(ignoreStdioArraySubprocess.stdio[3]); const ignoreStdioArrayResult = await ignoreStdioArraySubprocess; expectType(ignoreStdioArrayResult.stdout); expectType(ignoreStdioArrayResult.stdio[1]); expectType(ignoreStdioArrayResult.stderr); expectType(ignoreStdioArrayResult.stdio[2]); expectType(ignoreStdioArrayResult.all); const ignoreStdioArrayReadSubprocess = execa('unicorns', {stdio: ['ignore', 'ignore', 'pipe', new Uint8Array()], all: true}); expectType(ignoreStdioArrayReadSubprocess.stdio[3]); const ignoreStdinSubprocess = execa('unicorns', {stdin: 'ignore'}); expectType(ignoreStdinSubprocess.stdin); const ignoreStdoutSubprocess = execa('unicorns', {stdout: 'ignore', all: true}); expectType(ignoreStdoutSubprocess.stdin); expectType(ignoreStdoutSubprocess.stdio[0]); expectType(ignoreStdoutSubprocess.stdout); expectType(ignoreStdoutSubprocess.stdio[1]); expectType(ignoreStdoutSubprocess.stderr); expectType(ignoreStdoutSubprocess.stdio[2]); expectType(ignoreStdoutSubprocess.all); expectError(ignoreStdoutSubprocess.stdio[3].destroy()); const ignoreStdoutResult = await ignoreStdoutSubprocess; expectType(ignoreStdoutResult.stdout); expectType(ignoreStdoutResult.stderr); expectType(ignoreStdoutResult.all); const ignoreStderrSubprocess = execa('unicorns', {stderr: 'ignore', all: true}); expectType(ignoreStderrSubprocess.stdin); expectType(ignoreStderrSubprocess.stdio[0]); expectType(ignoreStderrSubprocess.stdout); expectType(ignoreStderrSubprocess.stdio[1]); expectType(ignoreStderrSubprocess.stderr); expectType(ignoreStderrSubprocess.stdio[2]); expectType(ignoreStderrSubprocess.all); expectError(ignoreStderrSubprocess.stdio[3].destroy()); const ignoreStderrResult = await ignoreStderrSubprocess; expectType(ignoreStderrResult.stdout); expectType(ignoreStderrResult.stderr); expectType(ignoreStderrResult.all); const ignoreStdioSubprocess = execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'ignore'], all: true}); expectType(ignoreStdioSubprocess.stdin); expectType(ignoreStdioSubprocess.stdio[0]); expectType(ignoreStdioSubprocess.stdout); expectType(ignoreStdioSubprocess.stdio[1]); expectType(ignoreStdioSubprocess.stderr); expectType(ignoreStdioSubprocess.stdio[2]); expectType(ignoreStdioSubprocess.all); expectType(ignoreStdioSubprocess.stdio[3]); const ignoreStdioResult = await ignoreStdioSubprocess; expectType(ignoreStdioResult.stdout); expectType(ignoreStdioResult.stderr); expectType(ignoreStdioResult.all); const ignoreStdoutResultSync = execaSync('unicorns', {stdout: 'ignore', all: true}); expectType(ignoreStdoutResultSync.stdout); expectType(ignoreStdoutResultSync.stdio[1]); expectType(ignoreStdoutResultSync.stderr); expectType(ignoreStdoutResultSync.stdio[2]); expectType(ignoreStdoutResultSync.all); const ignoreStderrResultSync = execaSync('unicorns', {stderr: 'ignore', all: true}); expectType(ignoreStderrResultSync.stdout); expectType(ignoreStderrResultSync.stderr); expectType(ignoreStderrResultSync.all); const ignoreFd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'ignore']}); expectType(ignoreFd3Result.stdio[3]); const ignoreStdoutError = new Error('.') as ExecaError<{stdout: 'ignore'; all: true}>; expectType(ignoreStdoutError.stdout); expectType(ignoreStdoutError.stdio[1]); expectType(ignoreStdoutError.stderr); expectType(ignoreStdoutError.stdio[2]); expectType(ignoreStdoutError.all); const ignoreStderrError = new Error('.') as ExecaError<{stderr: 'ignore'; all: true}>; expectType(ignoreStderrError.stdout); expectType(ignoreStderrError.stderr); expectType(ignoreStderrError.all); const ignoreStdoutErrorSync = new Error('.') as ExecaSyncError<{stdout: 'ignore'; all: true}>; expectType(ignoreStdoutErrorSync.stdout); expectType(ignoreStdoutErrorSync.stdio[1]); expectType(ignoreStdoutErrorSync.stderr); expectType(ignoreStdoutErrorSync.stdio[2]); expectType(ignoreStdoutErrorSync.all); const ignoreStderrErrorSync = new Error('.') as ExecaSyncError<{stderr: 'ignore'; all: true}>; expectType(ignoreStderrErrorSync.stdout); expectType(ignoreStderrErrorSync.stderr); expectType(ignoreStderrErrorSync.all); ================================================ FILE: test-d/return/ignore-other.test-d.ts ================================================ import * as process from 'node:process'; import {expectType} from 'tsd'; import { execa, execaSync, type ExecaError, type ExecaSyncError, } from '../../index.js'; const inheritStdoutResult = await execa('unicorns', {stdout: 'inherit', all: true}); expectType(inheritStdoutResult.stdout); expectType(inheritStdoutResult.stderr); expectType(inheritStdoutResult.all); const inheritStderrResult = await execa('unicorns', {stderr: 'inherit', all: true}); expectType(inheritStderrResult.stdout); expectType(inheritStderrResult.stderr); expectType(inheritStderrResult.all); const inheritArrayStdoutResult = await execa('unicorns', {stdout: ['inherit'] as ['inherit'], all: true}); expectType(inheritArrayStdoutResult.stdout); expectType(inheritArrayStdoutResult.stderr); expectType(inheritArrayStdoutResult.all); const inheritArrayStderrResult = await execa('unicorns', {stderr: ['inherit'] as ['inherit'], all: true}); expectType(inheritArrayStderrResult.stdout); expectType(inheritArrayStderrResult.stderr); expectType(inheritArrayStderrResult.all); const inheritStdoutResultSync = execaSync('unicorns', {stdout: 'inherit', all: true}); expectType(inheritStdoutResultSync.stdout); expectType(inheritStdoutResultSync.stderr); expectType(inheritStdoutResultSync.all); const inheritStderrResultSync = execaSync('unicorns', {stderr: 'inherit', all: true}); expectType(inheritStderrResultSync.stdout); expectType(inheritStderrResultSync.stderr); expectType(inheritStderrResultSync.all); const inheritStdoutError = new Error('.') as ExecaError<{stdout: 'inherit'; all: true}>; expectType(inheritStdoutError.stdout); expectType(inheritStdoutError.stderr); expectType(inheritStdoutError.all); const inheritStderrError = new Error('.') as ExecaError<{stderr: 'inherit'; all: true}>; expectType(inheritStderrError.stdout); expectType(inheritStderrError.stderr); expectType(inheritStderrError.all); const inheritStdoutErrorSync = new Error('.') as ExecaSyncError<{stdout: 'inherit'; all: true}>; expectType(inheritStdoutErrorSync.stdout); expectType(inheritStdoutErrorSync.stderr); expectType(inheritStdoutErrorSync.all); const inheritStderrErrorSync = new Error('.') as ExecaSyncError<{stderr: 'inherit'; all: true}>; expectType(inheritStderrErrorSync.stdout); expectType(inheritStderrErrorSync.stderr); expectType(inheritStderrErrorSync.all); const ipcStdoutResult = await execa('unicorns', {stdout: 'ipc', all: true}); expectType(ipcStdoutResult.stdout); expectType(ipcStdoutResult.stderr); expectType(ipcStdoutResult.all); const ipcStderrResult = await execa('unicorns', {stderr: 'ipc', all: true}); expectType(ipcStderrResult.stdout); expectType(ipcStderrResult.stderr); expectType(ipcStderrResult.all); const ipcStdoutError = new Error('.') as ExecaError<{stdout: 'ipc'; all: true}>; expectType(ipcStdoutError.stdout); expectType(ipcStdoutError.stderr); expectType(ipcStdoutError.all); const ipcStderrError = new Error('.') as ExecaError<{stderr: 'ipc'; all: true}>; expectType(ipcStderrError.stdout); expectType(ipcStderrError.stderr); expectType(ipcStderrError.all); const streamStdoutResult = await execa('unicorns', {stdout: process.stdout, all: true}); expectType(streamStdoutResult.stdout); expectType(streamStdoutResult.stderr); expectType(streamStdoutResult.all); const streamArrayStdoutResult = await execa('unicorns', {stdout: [process.stdout] as [typeof process.stdout], all: true}); expectType(streamArrayStdoutResult.stdout); expectType(streamArrayStdoutResult.stderr); expectType(streamArrayStdoutResult.all); const streamStderrResult = await execa('unicorns', {stderr: process.stdout, all: true}); expectType(streamStderrResult.stdout); expectType(streamStderrResult.stderr); expectType(streamStderrResult.all); const streamArrayStderrResult = await execa('unicorns', {stderr: [process.stdout] as [typeof process.stdout], all: true}); expectType(streamArrayStderrResult.stdout); expectType(streamArrayStderrResult.stderr); expectType(streamArrayStderrResult.all); const streamStdoutError = new Error('.') as ExecaError<{stdout: typeof process.stdout; all: true}>; expectType(streamStdoutError.stdout); expectType(streamStdoutError.stderr); expectType(streamStdoutError.all); const streamStderrError = new Error('.') as ExecaError<{stderr: typeof process.stdout; all: true}>; expectType(streamStderrError.stdout); expectType(streamStderrError.stderr); expectType(streamStderrError.all); const numberStdoutResult = await execa('unicorns', {stdout: 1, all: true}); expectType(numberStdoutResult.stdout); expectType(numberStdoutResult.stderr); expectType(numberStdoutResult.all); const numberStderrResult = await execa('unicorns', {stderr: 1, all: true}); expectType(numberStderrResult.stdout); expectType(numberStderrResult.stderr); expectType(numberStderrResult.all); const numberArrayStdoutResult = await execa('unicorns', {stdout: [1] as [1], all: true}); expectType(numberArrayStdoutResult.stdout); expectType(numberArrayStdoutResult.stderr); expectType(numberArrayStdoutResult.all); const numberArrayStderrResult = await execa('unicorns', {stderr: [1] as [1], all: true}); expectType(numberArrayStderrResult.stdout); expectType(numberArrayStderrResult.stderr); expectType(numberArrayStderrResult.all); const numberStdoutResultSync = execaSync('unicorns', {stdout: 1, all: true}); expectType(numberStdoutResultSync.stdout); expectType(numberStdoutResultSync.stderr); expectType(numberStdoutResultSync.all); const numberStderrResultSync = execaSync('unicorns', {stderr: 1, all: true}); expectType(numberStderrResultSync.stdout); expectType(numberStderrResultSync.stderr); expectType(numberStderrResultSync.all); const numberStdoutError = new Error('.') as ExecaError<{stdout: 1; all: true}>; expectType(numberStdoutError.stdout); expectType(numberStdoutError.stderr); expectType(numberStdoutError.all); const numberStderrError = new Error('.') as ExecaError<{stderr: 1; all: true}>; expectType(numberStderrError.stdout); expectType(numberStderrError.stderr); expectType(numberStderrError.all); const numberStdoutErrorSync = new Error('.') as ExecaSyncError<{stdout: 1; all: true}>; expectType(numberStdoutErrorSync.stdout); expectType(numberStdoutErrorSync.stderr); expectType(numberStdoutErrorSync.all); const numberStderrErrorSync = new Error('.') as ExecaSyncError<{stderr: 1; all: true}>; expectType(numberStderrErrorSync.stdout); expectType(numberStderrErrorSync.stderr); expectType(numberStderrErrorSync.all); ================================================ FILE: test-d/return/lines-main.test-d.ts ================================================ import {expectType} from 'tsd'; import { execa, execaSync, type ExecaError, type ExecaSyncError, } from '../../index.js'; const linesResult = await execa('unicorns', {lines: true, all: true}); expectType(linesResult.stdout); expectType(linesResult.stdio[1]); expectType(linesResult.stderr); expectType(linesResult.stdio[2]); expectType(linesResult.all); const linesBufferResult = await execa('unicorns', {lines: true, encoding: 'buffer', all: true}); expectType(linesBufferResult.stdout); expectType(linesBufferResult.stdio[1]); expectType(linesBufferResult.stderr); expectType(linesBufferResult.stdio[2]); expectType(linesBufferResult.all); const linesHexResult = await execa('unicorns', {lines: true, encoding: 'hex', all: true}); expectType(linesHexResult.stdout); expectType(linesHexResult.stdio[1]); expectType(linesHexResult.stderr); expectType(linesHexResult.stdio[2]); expectType(linesHexResult.all); const linesFd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe'], lines: true}); expectType(linesFd3Result.stdio[3]); const linesBufferFd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe'], lines: true, encoding: 'buffer'}); expectType(linesBufferFd3Result.stdio[3]); const execaLinesError = new Error('.') as ExecaError<{lines: true; all: true}>; expectType(execaLinesError.stdout); expectType(execaLinesError.stdio[1]); expectType(execaLinesError.stderr); expectType(execaLinesError.stdio[2]); expectType(execaLinesError.all); const execaLinesBufferError = new Error('.') as ExecaError<{lines: true; encoding: 'buffer'; all: true}>; expectType(execaLinesBufferError.stdout); expectType(execaLinesBufferError.stdio[1]); expectType(execaLinesBufferError.stderr); expectType(execaLinesBufferError.stdio[2]); expectType(execaLinesBufferError.all); const linesResultSync = execaSync('unicorns', {lines: true, all: true}); expectType(linesResultSync.stdout); expectType(linesResultSync.stderr); expectType(linesResultSync.all); const linesBufferResultSync = execaSync('unicorns', {lines: true, encoding: 'buffer', all: true}); expectType(linesBufferResultSync.stdout); expectType(linesBufferResultSync.stderr); expectType(linesBufferResultSync.all); const linesHexResultSync = execaSync('unicorns', {lines: true, encoding: 'hex', all: true}); expectType(linesHexResultSync.stdout); expectType(linesHexResultSync.stderr); expectType(linesHexResultSync.all); const execaLinesErrorSync = new Error('.') as ExecaSyncError<{lines: true; all: true}>; expectType(execaLinesErrorSync.stdout); expectType(execaLinesErrorSync.stderr); expectType(execaLinesErrorSync.all); const execaLinesBufferErrorSync = new Error('.') as ExecaSyncError<{lines: true; encoding: 'buffer'; all: true}>; expectType(execaLinesBufferErrorSync.stdout); expectType(execaLinesBufferErrorSync.stderr); expectType(execaLinesBufferErrorSync.all); ================================================ FILE: test-d/return/lines-specific.test-d.ts ================================================ import {expectType} from 'tsd'; import {execa, execaSync} from '../../index.js'; const linesStdoutResult = await execa('unicorns', {all: true, lines: {stdout: true}}); expectType(linesStdoutResult.stdout); expectType(linesStdoutResult.stdio[1]); expectType(linesStdoutResult.stderr); expectType(linesStdoutResult.stdio[2]); expectType(linesStdoutResult.all); const linesStderrResult = await execa('unicorns', {all: true, lines: {stderr: true}}); expectType(linesStderrResult.stdout); expectType(linesStderrResult.stdio[1]); expectType(linesStderrResult.stderr); expectType(linesStderrResult.stdio[2]); expectType(linesStderrResult.all); const linesFd1Result = await execa('unicorns', {all: true, lines: {fd1: true}}); expectType(linesFd1Result.stdout); expectType(linesFd1Result.stdio[1]); expectType(linesFd1Result.stderr); expectType(linesFd1Result.stdio[2]); expectType(linesFd1Result.all); const linesFd2Result = await execa('unicorns', {all: true, lines: {fd2: true}}); expectType(linesFd2Result.stdout); expectType(linesFd2Result.stdio[1]); expectType(linesFd2Result.stderr); expectType(linesFd2Result.stdio[2]); expectType(linesFd2Result.all); const linesAllResult = await execa('unicorns', {all: true, lines: {all: true}}); expectType(linesAllResult.stdout); expectType(linesAllResult.stdio[1]); expectType(linesAllResult.stderr); expectType(linesAllResult.stdio[2]); expectType(linesAllResult.all); const linesFd3Result = await execa('unicorns', {all: true, lines: {fd3: true}, stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe']}); expectType(linesFd3Result.stdout); expectType(linesFd3Result.stdio[1]); expectType(linesFd3Result.stderr); expectType(linesFd3Result.stdio[2]); expectType(linesFd3Result.all); expectType(linesFd3Result.stdio[3]); expectType(linesFd3Result.stdio[4]); const linesStdoutResultSync = execaSync('unicorns', {all: true, lines: {stdout: true}}); expectType(linesStdoutResultSync.stdout); expectType(linesStdoutResultSync.stdio[1]); expectType(linesStdoutResultSync.stderr); expectType(linesStdoutResultSync.stdio[2]); expectType(linesStdoutResultSync.all); const linesStderrResultSync = execaSync('unicorns', {all: true, lines: {stderr: true}}); expectType(linesStderrResultSync.stdout); expectType(linesStderrResultSync.stdio[1]); expectType(linesStderrResultSync.stderr); expectType(linesStderrResultSync.stdio[2]); expectType(linesStderrResultSync.all); const linesFd1ResultSync = execaSync('unicorns', {all: true, lines: {fd1: true}}); expectType(linesFd1ResultSync.stdout); expectType(linesFd1ResultSync.stdio[1]); expectType(linesFd1ResultSync.stderr); expectType(linesFd1ResultSync.stdio[2]); expectType(linesFd1ResultSync.all); const linesFd2ResultSync = execaSync('unicorns', {all: true, lines: {fd2: true}}); expectType(linesFd2ResultSync.stdout); expectType(linesFd2ResultSync.stdio[1]); expectType(linesFd2ResultSync.stderr); expectType(linesFd2ResultSync.stdio[2]); expectType(linesFd2ResultSync.all); const linesAllResultSync = execaSync('unicorns', {all: true, lines: {all: true}}); expectType(linesAllResultSync.stdout); expectType(linesAllResultSync.stdio[1]); expectType(linesAllResultSync.stderr); expectType(linesAllResultSync.stdio[2]); expectType(linesAllResultSync.all); const linesFd3ResultSync = execaSync('unicorns', {all: true, lines: {fd3: true}, stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe']}); expectType(linesFd3ResultSync.stdout); expectType(linesFd3ResultSync.stdio[1]); expectType(linesFd3ResultSync.stderr); expectType(linesFd3ResultSync.stdio[2]); expectType(linesFd3ResultSync.all); expectType(linesFd3ResultSync.stdio[3]); expectType(linesFd3ResultSync.stdio[4]); ================================================ FILE: test-d/return/no-buffer-main.test-d.ts ================================================ import type {Readable, Writable} from 'node:stream'; import {expectType, expectError} from 'tsd'; import { execa, execaSync, type ExecaError, type ExecaSyncError, } from '../../index.js'; const noBufferSubprocess = execa('unicorns', {buffer: false, all: true}); expectType(noBufferSubprocess.stdin); expectType(noBufferSubprocess.stdio[0]); expectType(noBufferSubprocess.stdout); expectType(noBufferSubprocess.stdio[1]); expectType(noBufferSubprocess.stderr); expectType(noBufferSubprocess.stdio[2]); expectType(noBufferSubprocess.all); expectError(noBufferSubprocess.stdio[3].destroy()); const noBufferResult = await noBufferSubprocess; expectType(noBufferResult.stdout); expectType(noBufferResult.stdio[1]); expectType(noBufferResult.stderr); expectType(noBufferResult.stdio[2]); expectType(noBufferResult.all); const noBufferResultSync = execaSync('unicorns', {buffer: false, all: true}); expectType(noBufferResultSync.stdout); expectType(noBufferResultSync.stderr); expectType(noBufferResultSync.all); const noBuffer3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe'], buffer: false}); expectType(noBuffer3Result.stdio[3]); const noBufferError = new Error('.') as ExecaError<{buffer: false; all: true}>; expectType(noBufferError.stdout); expectType(noBufferError.stdio[1]); expectType(noBufferError.stderr); expectType(noBufferError.stdio[2]); expectType(noBufferError.all); const noBufferErrorSync = new Error('.') as ExecaSyncError<{buffer: false; all: true}>; expectType(noBufferErrorSync.stdout); expectType(noBufferErrorSync.stderr); expectType(noBufferErrorSync.all); ================================================ FILE: test-d/return/no-buffer-specific.test-d.ts ================================================ import {expectType} from 'tsd'; import {execa, execaSync} from '../../index.js'; const noBufferStdoutResult = await execa('unicorns', {all: true, buffer: {stdout: false}}); expectType(noBufferStdoutResult.stdout); expectType(noBufferStdoutResult.stdio[1]); expectType(noBufferStdoutResult.stderr); expectType(noBufferStdoutResult.stdio[2]); expectType(noBufferStdoutResult.all); const noBufferStderrResult = await execa('unicorns', {all: true, buffer: {stderr: false}}); expectType(noBufferStderrResult.stdout); expectType(noBufferStderrResult.stdio[1]); expectType(noBufferStderrResult.stderr); expectType(noBufferStderrResult.stdio[2]); expectType(noBufferStderrResult.all); const noBufferFd1Result = await execa('unicorns', {all: true, buffer: {fd1: false}}); expectType(noBufferFd1Result.stdout); expectType(noBufferFd1Result.stdio[1]); expectType(noBufferFd1Result.stderr); expectType(noBufferFd1Result.stdio[2]); expectType(noBufferFd1Result.all); const noBufferFd2Result = await execa('unicorns', {all: true, buffer: {fd2: false}}); expectType(noBufferFd2Result.stdout); expectType(noBufferFd2Result.stdio[1]); expectType(noBufferFd2Result.stderr); expectType(noBufferFd2Result.stdio[2]); expectType(noBufferFd2Result.all); const noBufferAllResult = await execa('unicorns', {all: true, buffer: {all: false}}); expectType(noBufferAllResult.stdout); expectType(noBufferAllResult.stdio[1]); expectType(noBufferAllResult.stderr); expectType(noBufferAllResult.stdio[2]); expectType(noBufferAllResult.all); const noBufferFd3Result = await execa('unicorns', {all: true, buffer: {fd3: false}, stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe']}); expectType(noBufferFd3Result.stdout); expectType(noBufferFd3Result.stdio[1]); expectType(noBufferFd3Result.stderr); expectType(noBufferFd3Result.stdio[2]); expectType(noBufferFd3Result.all); expectType(noBufferFd3Result.stdio[3]); expectType(noBufferFd3Result.stdio[4]); const noBufferStdoutResultSync = execaSync('unicorns', {all: true, buffer: {stdout: false}}); expectType(noBufferStdoutResultSync.stdout); expectType(noBufferStdoutResultSync.stdio[1]); expectType(noBufferStdoutResultSync.stderr); expectType(noBufferStdoutResultSync.stdio[2]); expectType(noBufferStdoutResultSync.all); const noBufferStderrResultSync = execaSync('unicorns', {all: true, buffer: {stderr: false}}); expectType(noBufferStderrResultSync.stdout); expectType(noBufferStderrResultSync.stdio[1]); expectType(noBufferStderrResultSync.stderr); expectType(noBufferStderrResultSync.stdio[2]); expectType(noBufferStderrResultSync.all); const noBufferFd1ResultSync = execaSync('unicorns', {all: true, buffer: {fd1: false}}); expectType(noBufferFd1ResultSync.stdout); expectType(noBufferFd1ResultSync.stdio[1]); expectType(noBufferFd1ResultSync.stderr); expectType(noBufferFd1ResultSync.stdio[2]); expectType(noBufferFd1ResultSync.all); const noBufferFd2ResultSync = execaSync('unicorns', {all: true, buffer: {fd2: false}}); expectType(noBufferFd2ResultSync.stdout); expectType(noBufferFd2ResultSync.stdio[1]); expectType(noBufferFd2ResultSync.stderr); expectType(noBufferFd2ResultSync.stdio[2]); expectType(noBufferFd2ResultSync.all); const noBufferAllResultSync = execaSync('unicorns', {all: true, buffer: {all: false}}); expectType(noBufferAllResultSync.stdout); expectType(noBufferAllResultSync.stdio[1]); expectType(noBufferAllResultSync.stderr); expectType(noBufferAllResultSync.stdio[2]); expectType(noBufferAllResultSync.all); const noBufferFd3ResultSync = execaSync('unicorns', {all: true, buffer: {fd3: false}, stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe']}); expectType(noBufferFd3ResultSync.stdout); expectType(noBufferFd3ResultSync.stdio[1]); expectType(noBufferFd3ResultSync.stderr); expectType(noBufferFd3ResultSync.stdio[2]); expectType(noBufferFd3ResultSync.all); expectType(noBufferFd3ResultSync.stdio[3]); expectType(noBufferFd3ResultSync.stdio[4]); ================================================ FILE: test-d/return/result-all.test-d.ts ================================================ import {expectType} from 'tsd'; import { execa, execaSync, type ExecaError, type ExecaSyncError, } from '../../index.js'; const allResult = await execa('unicorns', {all: true}); expectType(allResult.all); const noAllResult = await execa('unicorns'); expectType(noAllResult.all); const allResultSync = execaSync('unicorns', {all: true}); expectType(allResultSync.all); const noAllResultSync = execaSync('unicorns'); expectType(noAllResultSync.all); const allError = new Error('.') as ExecaError<{all: true}>; expectType(allError.all); const noAllError = new Error('.') as ExecaError<{}>; expectType(noAllError.all); const allErrorSync = new Error('.') as ExecaError<{all: true}>; expectType(allErrorSync.all); const noAllErrorSync = new Error('.') as ExecaSyncError<{}>; expectType(noAllErrorSync.all); ================================================ FILE: test-d/return/result-ipc.ts ================================================ import {expectAssignable, expectType} from 'tsd'; import { execa, execaSync, type Result, type SyncResult, type ExecaError, type ExecaSyncError, type Message, type Options, } from '../../index.js'; const ipcResult = await execa('unicorns', {ipc: true}); expectType>>(ipcResult.ipcOutput); const ipcFdResult = await execa('unicorns', {ipc: true, buffer: {stdout: false}}); expectType>>(ipcFdResult.ipcOutput); const advancedResult = await execa('unicorns', {ipc: true, serialization: 'advanced'}); expectType>>(advancedResult.ipcOutput); const jsonResult = await execa('unicorns', {ipc: true, serialization: 'json'}); expectType>>(jsonResult.ipcOutput); const inputResult = await execa('unicorns', {ipcInput: ''}); expectType>>(inputResult.ipcOutput); const genericInputResult = await execa('unicorns', {ipcInput: '' as Message}); expectType>>(genericInputResult.ipcOutput); const gracefulResult = await execa('unicorns', {gracefulCancel: true, cancelSignal: AbortSignal.abort()}); expectType>>(gracefulResult.ipcOutput); const genericResult = await execa('unicorns', {} as Options); expectType(genericResult.ipcOutput); const genericIpc = await execa('unicorns', {ipc: true as boolean}); expectType> | []>(genericIpc.ipcOutput); const maybeInputResult = await execa('unicorns', {ipcInput: '' as '' | undefined}); expectType> | []>(maybeInputResult.ipcOutput); const maybeGracefulResult = await execa('unicorns', {gracefulCancel: true as boolean | undefined, cancelSignal: AbortSignal.abort()}); expectType> | []>(maybeGracefulResult.ipcOutput); const falseIpcResult = await execa('unicorns', {ipc: false}); expectType<[]>(falseIpcResult.ipcOutput); const noIpcResult = await execa('unicorns'); expectType<[]>(noIpcResult.ipcOutput); const emptyIpcResult = await execa('unicorns', {}); expectType<[]>(emptyIpcResult.ipcOutput); const undefinedInputResult = await execa('unicorns', {ipcInput: undefined}); expectType<[]>(undefinedInputResult.ipcOutput); const inputNoIpcResult = await execa('unicorns', {ipc: false, ipcInput: ''}); expectType<[]>(inputNoIpcResult.ipcOutput); const undefinedGracefulResult = await execa('unicorns', {gracefulCancel: undefined}); expectType<[]>(undefinedGracefulResult.ipcOutput); const falseGracefulResult = await execa('unicorns', {gracefulCancel: false}); expectType<[]>(falseGracefulResult.ipcOutput); const gracefulNoIpcResult = await execa('unicorns', {ipc: false, gracefulCancel: true, cancelSignal: AbortSignal.abort()}); expectType<[]>(gracefulNoIpcResult.ipcOutput); const noBufferResult = await execa('unicorns', {ipc: true, buffer: false}); expectType<[]>(noBufferResult.ipcOutput); const noBufferFdResult = await execa('unicorns', {ipc: true, buffer: {ipc: false}}); expectType<[]>(noBufferFdResult.ipcOutput); const syncResult = execaSync('unicorns'); expectType<[]>(syncResult.ipcOutput); expectType({} as Result['ipcOutput']); expectAssignable({} as Result['ipcOutput']); expectType<[]>({} as unknown as SyncResult['ipcOutput']); const ipcError = new Error('.') as ExecaError<{ipc: true}>; expectType>>(ipcError.ipcOutput); const ipcFalseError = new Error('.') as ExecaError<{ipc: false}>; expectType<[]>(ipcFalseError.ipcOutput); const asyncError = new Error('.') as ExecaError; expectType(asyncError.ipcOutput); expectAssignable(asyncError.ipcOutput); const syncError = new Error('.') as ExecaSyncError; expectType<[]>(syncError.ipcOutput); ================================================ FILE: test-d/return/result-main.test-d.ts ================================================ import type {SignalConstants} from 'node:os'; import {expectType, expectAssignable} from 'tsd'; import { execa, execaSync, ExecaError, ExecaSyncError, type Result, type SyncResult, } from '../../index.js'; type AnyChunk = string | Uint8Array | string[] | unknown[] | undefined; expectType({} as Result['stdout']); expectType({} as Result['stderr']); expectType({} as Result['all']); expectAssignable<[undefined, AnyChunk, AnyChunk, ...AnyChunk[]]>({} as Result['stdio']); expectType({} as SyncResult['stdout']); expectType({} as SyncResult['stderr']); expectType({} as SyncResult['all']); expectAssignable<[undefined, AnyChunk, AnyChunk, ...AnyChunk[]]>({} as SyncResult['stdio']); const unicornsResult = await execa('unicorns', {all: true}); expectAssignable(unicornsResult); expectType(unicornsResult.command); expectType(unicornsResult.escapedCommand); expectType(unicornsResult.exitCode); expectType(unicornsResult.failed); expectType(unicornsResult.timedOut); expectType(unicornsResult.isCanceled); expectType(unicornsResult.isGracefullyCanceled); expectType(unicornsResult.isTerminated); expectType(unicornsResult.isMaxBuffer); expectType(unicornsResult.isForcefullyTerminated); expectType(unicornsResult.signal); expectType(unicornsResult.signalDescription); expectType(unicornsResult.cwd); expectType(unicornsResult.durationMs); expectType(unicornsResult.pipedFrom); const unicornsResultSync = execaSync('unicorns', {all: true}); expectAssignable(unicornsResultSync); expectType(unicornsResultSync.command); expectType(unicornsResultSync.escapedCommand); expectType(unicornsResultSync.exitCode); expectType(unicornsResultSync.failed); expectType(unicornsResultSync.timedOut); expectType(unicornsResultSync.isCanceled); expectType(unicornsResultSync.isGracefullyCanceled); expectType(unicornsResultSync.isTerminated); expectType(unicornsResultSync.isMaxBuffer); expectType(unicornsResultSync.isForcefullyTerminated); expectType(unicornsResultSync.signal); expectType(unicornsResultSync.signalDescription); expectType(unicornsResultSync.cwd); expectType(unicornsResultSync.durationMs); expectType<[]>(unicornsResultSync.pipedFrom); const error = new Error('.'); if (error instanceof ExecaError) { expectType>(error); expectType<'ExecaError'>(error.name); expectType(error.message); expectType(error.exitCode); expectType(error.failed); expectType(error.timedOut); expectType(error.isCanceled); expectType(error.isGracefullyCanceled); expectType(error.isTerminated); expectType(error.isMaxBuffer); expectType(error.isForcefullyTerminated); expectType(error.signal); expectType(error.signalDescription); expectType(error.cwd); expectType(error.durationMs); expectType(error.shortMessage); expectType(error.originalMessage); expectType(error.code); expectType(error.cause); expectType(error.pipedFrom); } const errorSync = new Error('.'); if (errorSync instanceof ExecaSyncError) { expectType>(errorSync); expectType<'ExecaSyncError'>(errorSync.name); expectType(errorSync.message); expectType(errorSync.exitCode); expectType(errorSync.failed); expectType(errorSync.timedOut); expectType(errorSync.isCanceled); expectType(errorSync.isGracefullyCanceled); expectType(errorSync.isTerminated); expectType(errorSync.isMaxBuffer); expectType(errorSync.isForcefullyTerminated); expectType(errorSync.signal); expectType(errorSync.signalDescription); expectType(errorSync.cwd); expectType(errorSync.durationMs); expectType(errorSync.shortMessage); expectType(errorSync.originalMessage); expectType(errorSync.code); expectType(errorSync.cause); expectType<[]>(errorSync.pipedFrom); } ================================================ FILE: test-d/return/result-reject.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import { type Result, type SyncResult, execa, execaSync, } from '../../index.js'; const rejectsResult = await execa('unicorns'); expectAssignable(rejectsResult); expectError(rejectsResult.stack?.toString()); expectError(rejectsResult.message?.toString()); expectError(rejectsResult.shortMessage?.toString()); expectError(rejectsResult.originalMessage?.toString()); expectError(rejectsResult.code?.toString()); expectError(rejectsResult.cause?.valueOf()); const noRejectsResult = await execa('unicorns', {reject: false}); expectAssignable(noRejectsResult); expectType(noRejectsResult.stack); expectType(noRejectsResult.message); expectType(noRejectsResult.shortMessage); expectType(noRejectsResult.originalMessage); expectType(noRejectsResult.code); expectType(noRejectsResult.cause); const rejectsSyncResult = execaSync('unicorns'); expectAssignable(rejectsSyncResult); expectError(rejectsSyncResult.stack?.toString()); expectError(rejectsSyncResult.message?.toString()); expectError(rejectsSyncResult.shortMessage?.toString()); expectError(rejectsSyncResult.originalMessage?.toString()); expectError(rejectsSyncResult.code?.toString()); expectError(rejectsSyncResult.cause?.valueOf()); const noRejectsSyncResult = execaSync('unicorns', {reject: false}); expectAssignable(noRejectsSyncResult); expectType(noRejectsSyncResult.stack); expectType(noRejectsSyncResult.message); expectType(noRejectsSyncResult.shortMessage); expectType(noRejectsSyncResult.originalMessage); ================================================ FILE: test-d/return/result-stdio.test-d.ts ================================================ import {Readable, Writable} from 'node:stream'; import {expectType} from 'tsd'; import { execa, execaSync, type ExecaError, type ExecaSyncError, } from '../../index.js'; const unicornsResult = await execa('unicorns', {all: true}); expectType(unicornsResult.stdio[0]); expectType(unicornsResult.stdout); expectType(unicornsResult.stdio[1]); expectType(unicornsResult.stderr); expectType(unicornsResult.stdio[2]); expectType(unicornsResult.all); expectType(unicornsResult.stdio[3 as number]); const bufferResult = await execa('unicorns', {encoding: 'buffer', all: true}); expectType(bufferResult.stdout); expectType(bufferResult.stdio[1]); expectType(bufferResult.stderr); expectType(bufferResult.stdio[2]); expectType(bufferResult.all); const hexResult = await execa('unicorns', {encoding: 'hex', all: true}); expectType(hexResult.stdout); expectType(hexResult.stdio[1]); expectType(hexResult.stderr); expectType(hexResult.stdio[2]); expectType(hexResult.all); const unicornsResultSync = execaSync('unicorns', {all: true}); expectType(unicornsResultSync.stdio[0]); expectType(unicornsResultSync.stdout); expectType(unicornsResultSync.stdio[1]); expectType(unicornsResultSync.stderr); expectType(unicornsResultSync.stdio[2]); expectType(unicornsResultSync.all); const bufferResultSync = execaSync('unicorns', {encoding: 'buffer', all: true}); expectType(bufferResultSync.stdio[0]); expectType(bufferResultSync.stdout); expectType(bufferResultSync.stdio[1]); expectType(bufferResultSync.stderr); expectType(bufferResultSync.stdio[2]); expectType(bufferResultSync.all); const execaStringError = new Error('.') as ExecaError<{all: true}>; expectType(execaStringError.stdio[0]); expectType(execaStringError.stdout); expectType(execaStringError.stdio[1]); expectType(execaStringError.stderr); expectType(execaStringError.stdio[2]); expectType(execaStringError.all); const execaBufferError = new Error('.') as ExecaError<{encoding: 'buffer'; all: true}>; expectType(execaBufferError.stdio[0]); expectType(execaBufferError.stdout); expectType(execaBufferError.stdio[1]); expectType(execaBufferError.stderr); expectType(execaBufferError.stdio[2]); expectType(execaBufferError.all); const execaStringErrorSync = new Error('.') as ExecaSyncError<{all: true}>; expectType(execaStringErrorSync.stdio[0]); expectType(execaStringErrorSync.stdout); expectType(execaStringErrorSync.stdio[1]); expectType(execaStringErrorSync.stderr); expectType(execaStringErrorSync.stdio[2]); expectType(execaStringErrorSync.all); const execaBufferErrorSync = new Error('.') as ExecaSyncError<{encoding: 'buffer'; all: true}>; expectType(execaBufferErrorSync.stdio[0]); expectType(execaBufferErrorSync.stdout); expectType(execaBufferErrorSync.stdio[1]); expectType(execaBufferErrorSync.stderr); expectType(execaBufferErrorSync.stdio[2]); expectType(execaBufferErrorSync.all); const multipleStdoutResult = await execa('unicorns', {stdout: ['inherit', 'pipe'] as ['inherit', 'pipe'], all: true}); expectType(multipleStdoutResult.stdout); expectType(multipleStdoutResult.stdio[1]); expectType(multipleStdoutResult.stderr); expectType(multipleStdoutResult.stdio[2]); expectType(multipleStdoutResult.all); const undefinedStdoutResult = await execa('unicorns', {stdout: undefined, all: true}); expectType(undefinedStdoutResult.stdout); expectType(undefinedStdoutResult.stderr); expectType(undefinedStdoutResult.all); const undefinedArrayStdoutResult = await execa('unicorns', {stdout: [undefined] as const, all: true}); expectType(undefinedArrayStdoutResult.stdout); expectType(undefinedArrayStdoutResult.stderr); expectType(undefinedArrayStdoutResult.all); const undefinedStderrResult = await execa('unicorns', {stderr: undefined, all: true}); expectType(undefinedStderrResult.stdout); expectType(undefinedStderrResult.stderr); expectType(undefinedStderrResult.all); const undefinedArrayStderrResult = await execa('unicorns', {stderr: [undefined] as const, all: true}); expectType(undefinedArrayStderrResult.stdout); expectType(undefinedArrayStderrResult.stderr); expectType(undefinedArrayStderrResult.all); const fd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); expectType(fd3Result.stdio[3]); const inputFd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['pipe', new Readable()]]}); expectType(inputFd3Result.stdio[3]); const outputFd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['pipe', new Writable()]]}); expectType(outputFd3Result.stdio[3]); const bufferFd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe'], encoding: 'buffer'}); expectType(bufferFd3Result.stdio[3]); const undefinedFd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', undefined]}); expectType(undefinedFd3Result.stdio[3]); const undefinedArrayFd3Result = await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [undefined] as const]}); expectType(undefinedArrayFd3Result.stdio[3]); ================================================ FILE: test-d/stdio/array.test-d.ts ================================================ import {expectError} from 'tsd'; import {execa, execaSync} from '../../index.js'; expectError(await execa('unicorns', {stdio: []})); expectError(execaSync('unicorns', {stdio: []})); expectError(await execa('unicorns', {stdio: ['pipe']})); expectError(execaSync('unicorns', {stdio: ['pipe']})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe']})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe']})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe', 'pipe']}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe', 'pipe']}); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe', 'unknown']})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe', 'unknown']})); ================================================ FILE: test-d/stdio/direction.test-d.ts ================================================ import {Readable, Writable} from 'node:stream'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../index.js'; await execa('unicorns', {stdio: [new Readable(), 'pipe', 'pipe']}); execaSync('unicorns', {stdio: [new Readable(), 'pipe', 'pipe']}); await execa('unicorns', {stdio: [[new Readable()], ['pipe'], ['pipe']]}); expectError(execaSync('unicorns', {stdio: [[new Readable()], ['pipe'], ['pipe']]})); await execa('unicorns', {stdio: ['pipe', new Writable(), 'pipe']}); execaSync('unicorns', {stdio: ['pipe', new Writable(), 'pipe']}); await execa('unicorns', {stdio: [['pipe'], [new Writable()], ['pipe']]}); expectError(execaSync('unicorns', {stdio: [['pipe'], [new Writable()], ['pipe']]})); await execa('unicorns', {stdio: ['pipe', 'pipe', new Writable()]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', new Writable()]}); await execa('unicorns', {stdio: [['pipe'], ['pipe'], [new Writable()]]}); expectError(execaSync('unicorns', {stdio: [['pipe'], ['pipe'], [new Writable()]]})); expectError(await execa('unicorns', {stdio: [new Writable(), 'pipe', 'pipe']})); expectError(execaSync('unicorns', {stdio: [new Writable(), 'pipe', 'pipe']})); expectError(await execa('unicorns', {stdio: [[new Writable()], ['pipe'], ['pipe']]})); expectError(execaSync('unicorns', {stdio: [[new Writable()], ['pipe'], ['pipe']]})); expectError(await execa('unicorns', {stdio: ['pipe', new Readable(), 'pipe']})); expectError(execaSync('unicorns', {stdio: ['pipe', new Readable(), 'pipe']})); expectError(await execa('unicorns', {stdio: [['pipe'], [new Readable()], ['pipe']]})); expectError(execaSync('unicorns', {stdio: [['pipe'], [new Readable()], ['pipe']]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', new Readable()]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', new Readable()]})); expectError(await execa('unicorns', {stdio: [['pipe'], ['pipe'], [new Readable()]]})); expectError(execaSync('unicorns', {stdio: [['pipe'], ['pipe'], [new Readable()]]})); expectAssignable([new Uint8Array(), new Uint8Array()]); expectAssignable([new Uint8Array(), new Uint8Array()]); expectNotAssignable([new Writable(), new Uint8Array()]); expectNotAssignable([new Writable(), new Uint8Array()]); ================================================ FILE: test-d/stdio/option/array-binary.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const binaryArray = [new Uint8Array(), new Uint8Array()] as const; await execa('unicorns', {stdin: [binaryArray]}); execaSync('unicorns', {stdin: [binaryArray]}); expectError(await execa('unicorns', {stdout: [binaryArray]})); expectError(execaSync('unicorns', {stdout: [binaryArray]})); expectError(await execa('unicorns', {stderr: [binaryArray]})); expectError(execaSync('unicorns', {stderr: [binaryArray]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [binaryArray]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [binaryArray]]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [[binaryArray]]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [[binaryArray]]]})); expectAssignable([binaryArray]); expectAssignable([binaryArray]); expectNotAssignable([binaryArray]); expectNotAssignable([binaryArray]); ================================================ FILE: test-d/stdio/option/array-object.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const objectArray = [{}, {}] as const; await execa('unicorns', {stdin: [objectArray]}); execaSync('unicorns', {stdin: [objectArray]}); expectError(await execa('unicorns', {stdout: [objectArray]})); expectError(execaSync('unicorns', {stdout: [objectArray]})); expectError(await execa('unicorns', {stderr: [objectArray]})); expectError(execaSync('unicorns', {stderr: [objectArray]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectArray]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectArray]]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [[objectArray]]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [[objectArray]]]})); expectAssignable([objectArray]); expectAssignable([objectArray]); expectNotAssignable([objectArray]); expectNotAssignable([objectArray]); ================================================ FILE: test-d/stdio/option/array-string.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const stringArray = ['foo', 'bar'] as const; await execa('unicorns', {stdin: [stringArray]}); execaSync('unicorns', {stdin: [stringArray]}); expectError(await execa('unicorns', {stdout: [stringArray]})); expectError(execaSync('unicorns', {stdout: [stringArray]})); expectError(await execa('unicorns', {stderr: [stringArray]})); expectError(execaSync('unicorns', {stderr: [stringArray]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [stringArray]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [stringArray]]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [[stringArray]]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [[stringArray]]]})); expectAssignable([stringArray]); expectAssignable([stringArray]); expectNotAssignable([stringArray]); expectNotAssignable([stringArray]); ================================================ FILE: test-d/stdio/option/duplex-invalid.test-d.ts ================================================ import {Duplex} from 'node:stream'; import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const duplexWithInvalidObjectMode = { transform: new Duplex(), objectMode: 'true', } as const; expectError(await execa('unicorns', {stdin: duplexWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdin: duplexWithInvalidObjectMode})); expectError(await execa('unicorns', {stdin: [duplexWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdin: [duplexWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdout: duplexWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdout: duplexWithInvalidObjectMode})); expectError(await execa('unicorns', {stdout: [duplexWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdout: [duplexWithInvalidObjectMode]})); expectError(await execa('unicorns', {stderr: duplexWithInvalidObjectMode})); expectError(execaSync('unicorns', {stderr: duplexWithInvalidObjectMode})); expectError(await execa('unicorns', {stderr: [duplexWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stderr: [duplexWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdio: duplexWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdio: duplexWithInvalidObjectMode})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', duplexWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', duplexWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [duplexWithInvalidObjectMode]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [duplexWithInvalidObjectMode]]})); expectNotAssignable(duplexWithInvalidObjectMode); expectNotAssignable(duplexWithInvalidObjectMode); expectNotAssignable([duplexWithInvalidObjectMode]); expectNotAssignable([duplexWithInvalidObjectMode]); expectNotAssignable(duplexWithInvalidObjectMode); expectNotAssignable(duplexWithInvalidObjectMode); expectNotAssignable([duplexWithInvalidObjectMode]); expectNotAssignable([duplexWithInvalidObjectMode]); ================================================ FILE: test-d/stdio/option/duplex-object.test-d.ts ================================================ import {Duplex} from 'node:stream'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const duplexObjectProperty = { transform: new Duplex(), objectMode: true as const, } as const; await execa('unicorns', {stdin: duplexObjectProperty}); expectError(execaSync('unicorns', {stdin: duplexObjectProperty})); await execa('unicorns', {stdin: [duplexObjectProperty]}); expectError(execaSync('unicorns', {stdin: [duplexObjectProperty]})); await execa('unicorns', {stdout: duplexObjectProperty}); expectError(execaSync('unicorns', {stdout: duplexObjectProperty})); await execa('unicorns', {stdout: [duplexObjectProperty]}); expectError(execaSync('unicorns', {stdout: [duplexObjectProperty]})); await execa('unicorns', {stderr: duplexObjectProperty}); expectError(execaSync('unicorns', {stderr: duplexObjectProperty})); await execa('unicorns', {stderr: [duplexObjectProperty]}); expectError(execaSync('unicorns', {stderr: [duplexObjectProperty]})); expectError(await execa('unicorns', {stdio: duplexObjectProperty})); expectError(execaSync('unicorns', {stdio: duplexObjectProperty})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', duplexObjectProperty]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', duplexObjectProperty]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [duplexObjectProperty]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [duplexObjectProperty]]})); expectAssignable(duplexObjectProperty); expectNotAssignable(duplexObjectProperty); expectAssignable([duplexObjectProperty]); expectNotAssignable([duplexObjectProperty]); expectAssignable(duplexObjectProperty); expectNotAssignable(duplexObjectProperty); expectAssignable([duplexObjectProperty]); expectNotAssignable([duplexObjectProperty]); ================================================ FILE: test-d/stdio/option/duplex-transform.test-d.ts ================================================ import {Transform} from 'node:stream'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const duplexTransform = {transform: new Transform()} as const; await execa('unicorns', {stdin: duplexTransform}); expectError(execaSync('unicorns', {stdin: duplexTransform})); await execa('unicorns', {stdin: [duplexTransform]}); expectError(execaSync('unicorns', {stdin: [duplexTransform]})); await execa('unicorns', {stdout: duplexTransform}); expectError(execaSync('unicorns', {stdout: duplexTransform})); await execa('unicorns', {stdout: [duplexTransform]}); expectError(execaSync('unicorns', {stdout: [duplexTransform]})); await execa('unicorns', {stderr: duplexTransform}); expectError(execaSync('unicorns', {stderr: duplexTransform})); await execa('unicorns', {stderr: [duplexTransform]}); expectError(execaSync('unicorns', {stderr: [duplexTransform]})); expectError(await execa('unicorns', {stdio: duplexTransform})); expectError(execaSync('unicorns', {stdio: duplexTransform})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', duplexTransform]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', duplexTransform]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [duplexTransform]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [duplexTransform]]})); expectAssignable(duplexTransform); expectNotAssignable(duplexTransform); expectAssignable([duplexTransform]); expectNotAssignable([duplexTransform]); expectAssignable(duplexTransform); expectNotAssignable(duplexTransform); expectAssignable([duplexTransform]); expectNotAssignable([duplexTransform]); ================================================ FILE: test-d/stdio/option/duplex.test-d.ts ================================================ import {Duplex} from 'node:stream'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const duplex = {transform: new Duplex()} as const; await execa('unicorns', {stdin: duplex}); expectError(execaSync('unicorns', {stdin: duplex})); await execa('unicorns', {stdin: [duplex]}); expectError(execaSync('unicorns', {stdin: [duplex]})); await execa('unicorns', {stdout: duplex}); expectError(execaSync('unicorns', {stdout: duplex})); await execa('unicorns', {stdout: [duplex]}); expectError(execaSync('unicorns', {stdout: [duplex]})); await execa('unicorns', {stderr: duplex}); expectError(execaSync('unicorns', {stderr: duplex})); await execa('unicorns', {stderr: [duplex]}); expectError(execaSync('unicorns', {stderr: [duplex]})); expectError(await execa('unicorns', {stdio: duplex})); expectError(execaSync('unicorns', {stdio: duplex})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', duplex]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', duplex]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [duplex]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [duplex]]})); expectAssignable(duplex); expectNotAssignable(duplex); expectAssignable([duplex]); expectNotAssignable([duplex]); expectAssignable(duplex); expectNotAssignable(duplex); expectAssignable([duplex]); expectNotAssignable([duplex]); ================================================ FILE: test-d/stdio/option/fd-integer-0.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: 0}); execaSync('unicorns', {stdin: 0}); await execa('unicorns', {stdin: [0]}); execaSync('unicorns', {stdin: [0]}); expectError(await execa('unicorns', {stdout: 0})); expectError(execaSync('unicorns', {stdout: 0})); expectError(await execa('unicorns', {stdout: [0]})); expectError(execaSync('unicorns', {stdout: [0]})); expectError(await execa('unicorns', {stderr: 0})); expectError(execaSync('unicorns', {stderr: 0})); expectError(await execa('unicorns', {stderr: [0]})); expectError(execaSync('unicorns', {stderr: [0]})); expectError(await execa('unicorns', {stdio: 0})); expectError(execaSync('unicorns', {stdio: 0})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 0]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 0]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [0]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [0]]}); expectAssignable(0); expectAssignable(0); expectAssignable([0]); expectAssignable([0]); expectNotAssignable(0.5); expectNotAssignable(-1); expectNotAssignable(Number.POSITIVE_INFINITY); expectNotAssignable(Number.NaN); expectNotAssignable(0); expectNotAssignable(0); expectNotAssignable([0]); expectNotAssignable([0]); expectNotAssignable(0.5); expectNotAssignable(-1); expectNotAssignable(Number.POSITIVE_INFINITY); expectNotAssignable(Number.NaN); ================================================ FILE: test-d/stdio/option/fd-integer-1.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: 1})); expectError(execaSync('unicorns', {stdin: 1})); expectError(await execa('unicorns', {stdin: [1]})); expectError(execaSync('unicorns', {stdin: [1]})); await execa('unicorns', {stdout: 1}); execaSync('unicorns', {stdout: 1}); await execa('unicorns', {stdout: [1]}); execaSync('unicorns', {stdout: [1]}); await execa('unicorns', {stderr: 1}); execaSync('unicorns', {stderr: 1}); await execa('unicorns', {stderr: [1]}); execaSync('unicorns', {stderr: [1]}); expectError(await execa('unicorns', {stdio: 1})); expectError(execaSync('unicorns', {stdio: 1})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 1]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 1]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [1]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [1]]}); expectNotAssignable(1); expectNotAssignable(1); expectNotAssignable([1]); expectNotAssignable([1]); expectAssignable(1); expectAssignable(1); expectAssignable([1]); expectAssignable([1]); ================================================ FILE: test-d/stdio/option/fd-integer-2.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: 2})); expectError(execaSync('unicorns', {stdin: 2})); expectError(await execa('unicorns', {stdin: [2]})); expectError(execaSync('unicorns', {stdin: [2]})); await execa('unicorns', {stdout: 2}); execaSync('unicorns', {stdout: 2}); await execa('unicorns', {stdout: [2]}); execaSync('unicorns', {stdout: [2]}); await execa('unicorns', {stderr: 2}); execaSync('unicorns', {stderr: 2}); await execa('unicorns', {stderr: [2]}); execaSync('unicorns', {stderr: [2]}); expectError(await execa('unicorns', {stdio: 2})); expectError(execaSync('unicorns', {stdio: 2})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 2]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 2]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [2]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [2]]}); expectNotAssignable(2); expectNotAssignable(2); expectNotAssignable([2]); expectNotAssignable([2]); expectAssignable(2); expectAssignable(2); expectAssignable([2]); expectAssignable([2]); ================================================ FILE: test-d/stdio/option/fd-integer-3.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: 3}); execaSync('unicorns', {stdin: 3}); expectError(await execa('unicorns', {stdin: [3]})); execaSync('unicorns', {stdin: [3]}); await execa('unicorns', {stdout: 3}); execaSync('unicorns', {stdout: 3}); expectError(await execa('unicorns', {stdout: [3]})); execaSync('unicorns', {stdout: [3]}); await execa('unicorns', {stderr: 3}); execaSync('unicorns', {stderr: 3}); expectError(await execa('unicorns', {stderr: [3]})); execaSync('unicorns', {stderr: [3]}); expectError(await execa('unicorns', {stdio: 3})); expectError(execaSync('unicorns', {stdio: 3})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 3]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 3]}); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [3]]})); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [3]]}); expectAssignable(3); expectAssignable(3); expectNotAssignable([3]); expectAssignable([3]); expectAssignable(3); expectAssignable(3); expectNotAssignable([3]); expectAssignable([3]); ================================================ FILE: test-d/stdio/option/file-append-invalid.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const invalidFileAppend = {file: './test', append: 'true'} as const; expectError(await execa('unicorns', {stdin: invalidFileAppend})); expectError(execaSync('unicorns', {stdin: invalidFileAppend})); expectError(await execa('unicorns', {stdin: [invalidFileAppend]})); expectError(execaSync('unicorns', {stdin: [invalidFileAppend]})); expectError(await execa('unicorns', {stdout: invalidFileAppend})); expectError(execaSync('unicorns', {stdout: invalidFileAppend})); expectError(await execa('unicorns', {stdout: [invalidFileAppend]})); expectError(execaSync('unicorns', {stdout: [invalidFileAppend]})); expectError(await execa('unicorns', {stderr: invalidFileAppend})); expectError(execaSync('unicorns', {stderr: invalidFileAppend})); expectError(await execa('unicorns', {stderr: [invalidFileAppend]})); expectError(execaSync('unicorns', {stderr: [invalidFileAppend]})); expectError(await execa('unicorns', {stdio: invalidFileAppend})); expectError(execaSync('unicorns', {stdio: invalidFileAppend})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidFileAppend]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidFileAppend]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidFileAppend]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidFileAppend]]})); expectNotAssignable(invalidFileAppend); expectNotAssignable(invalidFileAppend); expectNotAssignable([invalidFileAppend]); expectNotAssignable([invalidFileAppend]); expectNotAssignable(invalidFileAppend); expectNotAssignable(invalidFileAppend); expectNotAssignable([invalidFileAppend]); expectNotAssignable([invalidFileAppend]); ================================================ FILE: test-d/stdio/option/file-append.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const fileAppend = {file: './test', append: true} as const; await execa('unicorns', {stdin: fileAppend}); execaSync('unicorns', {stdin: fileAppend}); await execa('unicorns', {stdin: [fileAppend]}); execaSync('unicorns', {stdin: [fileAppend]}); await execa('unicorns', {stdout: fileAppend}); execaSync('unicorns', {stdout: fileAppend}); await execa('unicorns', {stdout: [fileAppend]}); execaSync('unicorns', {stdout: [fileAppend]}); await execa('unicorns', {stderr: fileAppend}); execaSync('unicorns', {stderr: fileAppend}); await execa('unicorns', {stderr: [fileAppend]}); execaSync('unicorns', {stderr: [fileAppend]}); expectError(await execa('unicorns', {stdio: fileAppend})); expectError(execaSync('unicorns', {stdio: fileAppend})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', fileAppend]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', fileAppend]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [fileAppend]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [fileAppend]]}); expectAssignable(fileAppend); expectAssignable(fileAppend); expectAssignable([fileAppend]); expectAssignable([fileAppend]); expectAssignable(fileAppend); expectAssignable(fileAppend); expectAssignable([fileAppend]); expectAssignable([fileAppend]); ================================================ FILE: test-d/stdio/option/file-object-invalid.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const invalidFileObject = {file: new URL('file:///test')} as const; expectError(await execa('unicorns', {stdin: invalidFileObject})); expectError(execaSync('unicorns', {stdin: invalidFileObject})); expectError(await execa('unicorns', {stdin: [invalidFileObject]})); expectError(execaSync('unicorns', {stdin: [invalidFileObject]})); expectError(await execa('unicorns', {stdout: invalidFileObject})); expectError(execaSync('unicorns', {stdout: invalidFileObject})); expectError(await execa('unicorns', {stdout: [invalidFileObject]})); expectError(execaSync('unicorns', {stdout: [invalidFileObject]})); expectError(await execa('unicorns', {stderr: invalidFileObject})); expectError(execaSync('unicorns', {stderr: invalidFileObject})); expectError(await execa('unicorns', {stderr: [invalidFileObject]})); expectError(execaSync('unicorns', {stderr: [invalidFileObject]})); expectError(await execa('unicorns', {stdio: invalidFileObject})); expectError(execaSync('unicorns', {stdio: invalidFileObject})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidFileObject]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidFileObject]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidFileObject]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidFileObject]]})); expectNotAssignable(invalidFileObject); expectNotAssignable(invalidFileObject); expectNotAssignable([invalidFileObject]); expectNotAssignable([invalidFileObject]); expectNotAssignable(invalidFileObject); expectNotAssignable(invalidFileObject); expectNotAssignable([invalidFileObject]); expectNotAssignable([invalidFileObject]); ================================================ FILE: test-d/stdio/option/file-object.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const fileObject = {file: './test'} as const; await execa('unicorns', {stdin: fileObject}); execaSync('unicorns', {stdin: fileObject}); await execa('unicorns', {stdin: [fileObject]}); execaSync('unicorns', {stdin: [fileObject]}); await execa('unicorns', {stdout: fileObject}); execaSync('unicorns', {stdout: fileObject}); await execa('unicorns', {stdout: [fileObject]}); execaSync('unicorns', {stdout: [fileObject]}); await execa('unicorns', {stderr: fileObject}); execaSync('unicorns', {stderr: fileObject}); await execa('unicorns', {stderr: [fileObject]}); execaSync('unicorns', {stderr: [fileObject]}); expectError(await execa('unicorns', {stdio: fileObject})); expectError(execaSync('unicorns', {stdio: fileObject})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', fileObject]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', fileObject]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [fileObject]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [fileObject]]}); expectAssignable(fileObject); expectAssignable(fileObject); expectAssignable([fileObject]); expectAssignable([fileObject]); expectAssignable(fileObject); expectAssignable(fileObject); expectAssignable([fileObject]); expectAssignable([fileObject]); ================================================ FILE: test-d/stdio/option/file-url.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const fileUrl = new URL('file:///test'); await execa('unicorns', {stdin: fileUrl}); execaSync('unicorns', {stdin: fileUrl}); await execa('unicorns', {stdin: [fileUrl]}); execaSync('unicorns', {stdin: [fileUrl]}); await execa('unicorns', {stdout: fileUrl}); execaSync('unicorns', {stdout: fileUrl}); await execa('unicorns', {stdout: [fileUrl]}); execaSync('unicorns', {stdout: [fileUrl]}); await execa('unicorns', {stderr: fileUrl}); execaSync('unicorns', {stderr: fileUrl}); await execa('unicorns', {stderr: [fileUrl]}); execaSync('unicorns', {stderr: [fileUrl]}); expectError(await execa('unicorns', {stdio: fileUrl})); expectError(execaSync('unicorns', {stdio: fileUrl})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', fileUrl]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', fileUrl]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [fileUrl]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [fileUrl]]}); expectAssignable(fileUrl); expectAssignable(fileUrl); expectAssignable([fileUrl]); expectAssignable([fileUrl]); expectAssignable(fileUrl); expectAssignable(fileUrl); expectAssignable([fileUrl]); expectAssignable([fileUrl]); ================================================ FILE: test-d/stdio/option/final-async-full.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const asyncFinalFull = { async * transform(line: unknown) { yield ''; }, async * final() { yield ''; }, } as const; await execa('unicorns', {stdin: asyncFinalFull}); expectError(execaSync('unicorns', {stdin: asyncFinalFull})); await execa('unicorns', {stdin: [asyncFinalFull]}); expectError(execaSync('unicorns', {stdin: [asyncFinalFull]})); await execa('unicorns', {stdout: asyncFinalFull}); expectError(execaSync('unicorns', {stdout: asyncFinalFull})); await execa('unicorns', {stdout: [asyncFinalFull]}); expectError(execaSync('unicorns', {stdout: [asyncFinalFull]})); await execa('unicorns', {stderr: asyncFinalFull}); expectError(execaSync('unicorns', {stderr: asyncFinalFull})); await execa('unicorns', {stderr: [asyncFinalFull]}); expectError(execaSync('unicorns', {stderr: [asyncFinalFull]})); expectError(await execa('unicorns', {stdio: asyncFinalFull})); expectError(execaSync('unicorns', {stdio: asyncFinalFull})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncFinalFull]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncFinalFull]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncFinalFull]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncFinalFull]]})); expectAssignable(asyncFinalFull); expectNotAssignable(asyncFinalFull); expectAssignable([asyncFinalFull]); expectNotAssignable([asyncFinalFull]); expectAssignable(asyncFinalFull); expectNotAssignable(asyncFinalFull); expectAssignable([asyncFinalFull]); expectNotAssignable([asyncFinalFull]); ================================================ FILE: test-d/stdio/option/final-invalid-full.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const invalidReturnFinalFull = { * transform(line: string) { yield line; }, * final() { yield {} as unknown; return false; }, } as const; expectError(await execa('unicorns', {stdin: invalidReturnFinalFull})); expectError(execaSync('unicorns', {stdin: invalidReturnFinalFull})); expectError(await execa('unicorns', {stdin: [invalidReturnFinalFull]})); expectError(execaSync('unicorns', {stdin: [invalidReturnFinalFull]})); expectError(await execa('unicorns', {stdout: invalidReturnFinalFull})); expectError(execaSync('unicorns', {stdout: invalidReturnFinalFull})); expectError(await execa('unicorns', {stdout: [invalidReturnFinalFull]})); expectError(execaSync('unicorns', {stdout: [invalidReturnFinalFull]})); expectError(await execa('unicorns', {stderr: invalidReturnFinalFull})); expectError(execaSync('unicorns', {stderr: invalidReturnFinalFull})); expectError(await execa('unicorns', {stderr: [invalidReturnFinalFull]})); expectError(execaSync('unicorns', {stderr: [invalidReturnFinalFull]})); expectError(await execa('unicorns', {stdio: invalidReturnFinalFull})); expectError(execaSync('unicorns', {stdio: invalidReturnFinalFull})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidReturnFinalFull]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidReturnFinalFull]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidReturnFinalFull]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidReturnFinalFull]]})); expectNotAssignable(invalidReturnFinalFull); expectNotAssignable(invalidReturnFinalFull); expectNotAssignable([invalidReturnFinalFull]); expectNotAssignable([invalidReturnFinalFull]); expectNotAssignable(invalidReturnFinalFull); expectNotAssignable(invalidReturnFinalFull); expectNotAssignable([invalidReturnFinalFull]); expectNotAssignable([invalidReturnFinalFull]); ================================================ FILE: test-d/stdio/option/final-object-full.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const objectFinalFull = { * transform(line: unknown) { yield JSON.parse(line as string) as object; }, * final() { yield {}; }, objectMode: true, } as const; await execa('unicorns', {stdin: objectFinalFull}); execaSync('unicorns', {stdin: objectFinalFull}); await execa('unicorns', {stdin: [objectFinalFull]}); execaSync('unicorns', {stdin: [objectFinalFull]}); await execa('unicorns', {stdout: objectFinalFull}); execaSync('unicorns', {stdout: objectFinalFull}); await execa('unicorns', {stdout: [objectFinalFull]}); execaSync('unicorns', {stdout: [objectFinalFull]}); await execa('unicorns', {stderr: objectFinalFull}); execaSync('unicorns', {stderr: objectFinalFull}); await execa('unicorns', {stderr: [objectFinalFull]}); execaSync('unicorns', {stderr: [objectFinalFull]}); expectError(await execa('unicorns', {stdio: objectFinalFull})); expectError(execaSync('unicorns', {stdio: objectFinalFull})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectFinalFull]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectFinalFull]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectFinalFull]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectFinalFull]]}); expectAssignable(objectFinalFull); expectAssignable(objectFinalFull); expectAssignable([objectFinalFull]); expectAssignable([objectFinalFull]); expectAssignable(objectFinalFull); expectAssignable(objectFinalFull); expectAssignable([objectFinalFull]); expectAssignable([objectFinalFull]); ================================================ FILE: test-d/stdio/option/final-unknown-full.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const unknownFinalFull = { * transform(line: unknown) { yield line; }, * final() { yield {} as unknown; }, objectMode: true, } as const; await execa('unicorns', {stdin: unknownFinalFull}); execaSync('unicorns', {stdin: unknownFinalFull}); await execa('unicorns', {stdin: [unknownFinalFull]}); execaSync('unicorns', {stdin: [unknownFinalFull]}); await execa('unicorns', {stdout: unknownFinalFull}); execaSync('unicorns', {stdout: unknownFinalFull}); await execa('unicorns', {stdout: [unknownFinalFull]}); execaSync('unicorns', {stdout: [unknownFinalFull]}); await execa('unicorns', {stderr: unknownFinalFull}); execaSync('unicorns', {stderr: unknownFinalFull}); await execa('unicorns', {stderr: [unknownFinalFull]}); execaSync('unicorns', {stderr: [unknownFinalFull]}); expectError(await execa('unicorns', {stdio: unknownFinalFull})); expectError(execaSync('unicorns', {stdio: unknownFinalFull})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', unknownFinalFull]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', unknownFinalFull]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [unknownFinalFull]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [unknownFinalFull]]}); expectAssignable(unknownFinalFull); expectAssignable(unknownFinalFull); expectAssignable([unknownFinalFull]); expectAssignable([unknownFinalFull]); expectAssignable(unknownFinalFull); expectAssignable(unknownFinalFull); expectAssignable([unknownFinalFull]); expectAssignable([unknownFinalFull]); ================================================ FILE: test-d/stdio/option/generator-async-full.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const asyncGeneratorFull = { async * transform(line: unknown) { yield ''; }, } as const; await execa('unicorns', {stdin: asyncGeneratorFull}); expectError(execaSync('unicorns', {stdin: asyncGeneratorFull})); await execa('unicorns', {stdin: [asyncGeneratorFull]}); expectError(execaSync('unicorns', {stdin: [asyncGeneratorFull]})); await execa('unicorns', {stdout: asyncGeneratorFull}); expectError(execaSync('unicorns', {stdout: asyncGeneratorFull})); await execa('unicorns', {stdout: [asyncGeneratorFull]}); expectError(execaSync('unicorns', {stdout: [asyncGeneratorFull]})); await execa('unicorns', {stderr: asyncGeneratorFull}); expectError(execaSync('unicorns', {stderr: asyncGeneratorFull})); await execa('unicorns', {stderr: [asyncGeneratorFull]}); expectError(execaSync('unicorns', {stderr: [asyncGeneratorFull]})); expectError(await execa('unicorns', {stdio: asyncGeneratorFull})); expectError(execaSync('unicorns', {stdio: asyncGeneratorFull})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncGeneratorFull]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncGeneratorFull]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncGeneratorFull]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncGeneratorFull]]})); expectAssignable(asyncGeneratorFull); expectNotAssignable(asyncGeneratorFull); expectAssignable([asyncGeneratorFull]); expectNotAssignable([asyncGeneratorFull]); expectAssignable(asyncGeneratorFull); expectNotAssignable(asyncGeneratorFull); expectAssignable([asyncGeneratorFull]); expectNotAssignable([asyncGeneratorFull]); ================================================ FILE: test-d/stdio/option/generator-async.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const asyncGenerator = async function * (line: unknown) { yield ''; }; await execa('unicorns', {stdin: asyncGenerator}); expectError(execaSync('unicorns', {stdin: asyncGenerator})); await execa('unicorns', {stdin: [asyncGenerator]}); expectError(execaSync('unicorns', {stdin: [asyncGenerator]})); await execa('unicorns', {stdout: asyncGenerator}); expectError(execaSync('unicorns', {stdout: asyncGenerator})); await execa('unicorns', {stdout: [asyncGenerator]}); expectError(execaSync('unicorns', {stdout: [asyncGenerator]})); await execa('unicorns', {stderr: asyncGenerator}); expectError(execaSync('unicorns', {stderr: asyncGenerator})); await execa('unicorns', {stderr: [asyncGenerator]}); expectError(execaSync('unicorns', {stderr: [asyncGenerator]})); expectError(await execa('unicorns', {stdio: asyncGenerator})); expectError(execaSync('unicorns', {stdio: asyncGenerator})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncGenerator]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncGenerator]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncGenerator]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncGenerator]]})); expectAssignable(asyncGenerator); expectNotAssignable(asyncGenerator); expectAssignable([asyncGenerator]); expectNotAssignable([asyncGenerator]); expectAssignable(asyncGenerator); expectNotAssignable(asyncGenerator); expectAssignable([asyncGenerator]); expectNotAssignable([asyncGenerator]); ================================================ FILE: test-d/stdio/option/generator-binary-invalid.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const transformWithInvalidBinary = { * transform(line: unknown) { yield line; }, binary: 'true', } as const; expectError(await execa('unicorns', {stdin: transformWithInvalidBinary})); expectError(execaSync('unicorns', {stdin: transformWithInvalidBinary})); expectError(await execa('unicorns', {stdin: [transformWithInvalidBinary]})); expectError(execaSync('unicorns', {stdin: [transformWithInvalidBinary]})); expectError(await execa('unicorns', {stdout: transformWithInvalidBinary})); expectError(execaSync('unicorns', {stdout: transformWithInvalidBinary})); expectError(await execa('unicorns', {stdout: [transformWithInvalidBinary]})); expectError(execaSync('unicorns', {stdout: [transformWithInvalidBinary]})); expectError(await execa('unicorns', {stderr: transformWithInvalidBinary})); expectError(execaSync('unicorns', {stderr: transformWithInvalidBinary})); expectError(await execa('unicorns', {stderr: [transformWithInvalidBinary]})); expectError(execaSync('unicorns', {stderr: [transformWithInvalidBinary]})); expectError(await execa('unicorns', {stdio: transformWithInvalidBinary})); expectError(execaSync('unicorns', {stdio: transformWithInvalidBinary})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithInvalidBinary]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithInvalidBinary]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithInvalidBinary]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithInvalidBinary]]})); expectNotAssignable(transformWithInvalidBinary); expectNotAssignable(transformWithInvalidBinary); expectNotAssignable([transformWithInvalidBinary]); expectNotAssignable([transformWithInvalidBinary]); expectNotAssignable(transformWithInvalidBinary); expectNotAssignable(transformWithInvalidBinary); expectNotAssignable([transformWithInvalidBinary]); expectNotAssignable([transformWithInvalidBinary]); ================================================ FILE: test-d/stdio/option/generator-binary.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const transformWithBinary = { * transform(line: unknown) { yield line; }, binary: true, } as const; await execa('unicorns', {stdin: transformWithBinary}); execaSync('unicorns', {stdin: transformWithBinary}); await execa('unicorns', {stdin: [transformWithBinary]}); execaSync('unicorns', {stdin: [transformWithBinary]}); await execa('unicorns', {stdout: transformWithBinary}); execaSync('unicorns', {stdout: transformWithBinary}); await execa('unicorns', {stdout: [transformWithBinary]}); execaSync('unicorns', {stdout: [transformWithBinary]}); await execa('unicorns', {stderr: transformWithBinary}); execaSync('unicorns', {stderr: transformWithBinary}); await execa('unicorns', {stderr: [transformWithBinary]}); execaSync('unicorns', {stderr: [transformWithBinary]}); expectError(await execa('unicorns', {stdio: transformWithBinary})); expectError(execaSync('unicorns', {stdio: transformWithBinary})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithBinary]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithBinary]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithBinary]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithBinary]]}); expectAssignable(transformWithBinary); expectAssignable(transformWithBinary); expectAssignable([transformWithBinary]); expectAssignable([transformWithBinary]); expectAssignable(transformWithBinary); expectAssignable(transformWithBinary); expectAssignable([transformWithBinary]); expectAssignable([transformWithBinary]); ================================================ FILE: test-d/stdio/option/generator-boolean-full.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const booleanGeneratorFull = { * transform(line: boolean) { yield line; }, } as const; expectError(await execa('unicorns', {stdin: booleanGeneratorFull})); expectError(execaSync('unicorns', {stdin: booleanGeneratorFull})); expectError(await execa('unicorns', {stdin: [booleanGeneratorFull]})); expectError(execaSync('unicorns', {stdin: [booleanGeneratorFull]})); expectError(await execa('unicorns', {stdout: booleanGeneratorFull})); expectError(execaSync('unicorns', {stdout: booleanGeneratorFull})); expectError(await execa('unicorns', {stdout: [booleanGeneratorFull]})); expectError(execaSync('unicorns', {stdout: [booleanGeneratorFull]})); expectError(await execa('unicorns', {stderr: booleanGeneratorFull})); expectError(execaSync('unicorns', {stderr: booleanGeneratorFull})); expectError(await execa('unicorns', {stderr: [booleanGeneratorFull]})); expectError(execaSync('unicorns', {stderr: [booleanGeneratorFull]})); expectError(await execa('unicorns', {stdio: booleanGeneratorFull})); expectError(execaSync('unicorns', {stdio: booleanGeneratorFull})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', booleanGeneratorFull]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', booleanGeneratorFull]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [booleanGeneratorFull]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [booleanGeneratorFull]]})); expectNotAssignable(booleanGeneratorFull); expectNotAssignable(booleanGeneratorFull); expectNotAssignable([booleanGeneratorFull]); expectNotAssignable([booleanGeneratorFull]); expectNotAssignable(booleanGeneratorFull); expectNotAssignable(booleanGeneratorFull); expectNotAssignable([booleanGeneratorFull]); expectNotAssignable([booleanGeneratorFull]); ================================================ FILE: test-d/stdio/option/generator-boolean.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const booleanGenerator = function * (line: boolean) { yield line; }; expectError(await execa('unicorns', {stdin: booleanGenerator})); expectError(execaSync('unicorns', {stdin: booleanGenerator})); expectError(await execa('unicorns', {stdin: [booleanGenerator]})); expectError(execaSync('unicorns', {stdin: [booleanGenerator]})); expectError(await execa('unicorns', {stdout: booleanGenerator})); expectError(execaSync('unicorns', {stdout: booleanGenerator})); expectError(await execa('unicorns', {stdout: [booleanGenerator]})); expectError(execaSync('unicorns', {stdout: [booleanGenerator]})); expectError(await execa('unicorns', {stderr: booleanGenerator})); expectError(execaSync('unicorns', {stderr: booleanGenerator})); expectError(await execa('unicorns', {stderr: [booleanGenerator]})); expectError(execaSync('unicorns', {stderr: [booleanGenerator]})); expectError(await execa('unicorns', {stdio: booleanGenerator})); expectError(execaSync('unicorns', {stdio: booleanGenerator})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', booleanGenerator]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', booleanGenerator]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [booleanGenerator]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [booleanGenerator]]})); expectNotAssignable(booleanGenerator); expectNotAssignable(booleanGenerator); expectNotAssignable([booleanGenerator]); expectNotAssignable([booleanGenerator]); expectNotAssignable(booleanGenerator); expectNotAssignable(booleanGenerator); expectNotAssignable([booleanGenerator]); expectNotAssignable([booleanGenerator]); ================================================ FILE: test-d/stdio/option/generator-empty.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: {}})); expectError(execaSync('unicorns', {stdin: {}})); expectError(await execa('unicorns', {stdin: [{}]})); expectError(execaSync('unicorns', {stdin: [{}]})); expectError(await execa('unicorns', {stdout: {}})); expectError(execaSync('unicorns', {stdout: {}})); expectError(await execa('unicorns', {stdout: [{}]})); expectError(execaSync('unicorns', {stdout: [{}]})); expectError(await execa('unicorns', {stderr: {}})); expectError(execaSync('unicorns', {stderr: {}})); expectError(await execa('unicorns', {stderr: [{}]})); expectError(execaSync('unicorns', {stderr: [{}]})); expectError(await execa('unicorns', {stdio: {}})); expectError(execaSync('unicorns', {stdio: {}})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', {}]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', {}]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [{}]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [{}]]})); expectNotAssignable({}); expectNotAssignable({}); expectNotAssignable([{}]); expectNotAssignable([{}]); expectNotAssignable({}); expectNotAssignable({}); expectNotAssignable([{}]); expectNotAssignable([{}]); ================================================ FILE: test-d/stdio/option/generator-invalid-full.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const invalidReturnGeneratorFull = { * transform(line: unknown) { yield line; return false; }, } as const; expectError(await execa('unicorns', {stdin: invalidReturnGeneratorFull})); expectError(execaSync('unicorns', {stdin: invalidReturnGeneratorFull})); expectError(await execa('unicorns', {stdin: [invalidReturnGeneratorFull]})); expectError(execaSync('unicorns', {stdin: [invalidReturnGeneratorFull]})); expectError(await execa('unicorns', {stdout: invalidReturnGeneratorFull})); expectError(execaSync('unicorns', {stdout: invalidReturnGeneratorFull})); expectError(await execa('unicorns', {stdout: [invalidReturnGeneratorFull]})); expectError(execaSync('unicorns', {stdout: [invalidReturnGeneratorFull]})); expectError(await execa('unicorns', {stderr: invalidReturnGeneratorFull})); expectError(execaSync('unicorns', {stderr: invalidReturnGeneratorFull})); expectError(await execa('unicorns', {stderr: [invalidReturnGeneratorFull]})); expectError(execaSync('unicorns', {stderr: [invalidReturnGeneratorFull]})); expectError(await execa('unicorns', {stdio: invalidReturnGeneratorFull})); expectError(execaSync('unicorns', {stdio: invalidReturnGeneratorFull})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidReturnGeneratorFull]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidReturnGeneratorFull]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidReturnGeneratorFull]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidReturnGeneratorFull]]})); expectNotAssignable(invalidReturnGeneratorFull); expectNotAssignable(invalidReturnGeneratorFull); expectNotAssignable([invalidReturnGeneratorFull]); expectNotAssignable([invalidReturnGeneratorFull]); expectNotAssignable(invalidReturnGeneratorFull); expectNotAssignable(invalidReturnGeneratorFull); expectNotAssignable([invalidReturnGeneratorFull]); expectNotAssignable([invalidReturnGeneratorFull]); ================================================ FILE: test-d/stdio/option/generator-invalid.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const invalidReturnGenerator = function * (line: unknown) { yield line; return false; }; expectError(await execa('unicorns', {stdin: invalidReturnGenerator})); expectError(execaSync('unicorns', {stdin: invalidReturnGenerator})); expectError(await execa('unicorns', {stdin: [invalidReturnGenerator]})); expectError(execaSync('unicorns', {stdin: [invalidReturnGenerator]})); expectError(await execa('unicorns', {stdout: invalidReturnGenerator})); expectError(execaSync('unicorns', {stdout: invalidReturnGenerator})); expectError(await execa('unicorns', {stdout: [invalidReturnGenerator]})); expectError(execaSync('unicorns', {stdout: [invalidReturnGenerator]})); expectError(await execa('unicorns', {stderr: invalidReturnGenerator})); expectError(execaSync('unicorns', {stderr: invalidReturnGenerator})); expectError(await execa('unicorns', {stderr: [invalidReturnGenerator]})); expectError(execaSync('unicorns', {stderr: [invalidReturnGenerator]})); expectError(await execa('unicorns', {stdio: invalidReturnGenerator})); expectError(execaSync('unicorns', {stdio: invalidReturnGenerator})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidReturnGenerator]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', invalidReturnGenerator]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidReturnGenerator]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [invalidReturnGenerator]]})); expectNotAssignable(invalidReturnGenerator); expectNotAssignable(invalidReturnGenerator); expectNotAssignable([invalidReturnGenerator]); expectNotAssignable([invalidReturnGenerator]); expectNotAssignable(invalidReturnGenerator); expectNotAssignable(invalidReturnGenerator); expectNotAssignable([invalidReturnGenerator]); expectNotAssignable([invalidReturnGenerator]); ================================================ FILE: test-d/stdio/option/generator-object-full.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const objectGeneratorFull = { * transform(line: unknown) { yield JSON.parse(line as string) as object; }, objectMode: true, } as const; await execa('unicorns', {stdin: objectGeneratorFull}); execaSync('unicorns', {stdin: objectGeneratorFull}); await execa('unicorns', {stdin: [objectGeneratorFull]}); execaSync('unicorns', {stdin: [objectGeneratorFull]}); await execa('unicorns', {stdout: objectGeneratorFull}); execaSync('unicorns', {stdout: objectGeneratorFull}); await execa('unicorns', {stdout: [objectGeneratorFull]}); execaSync('unicorns', {stdout: [objectGeneratorFull]}); await execa('unicorns', {stderr: objectGeneratorFull}); execaSync('unicorns', {stderr: objectGeneratorFull}); await execa('unicorns', {stderr: [objectGeneratorFull]}); execaSync('unicorns', {stderr: [objectGeneratorFull]}); expectError(await execa('unicorns', {stdio: objectGeneratorFull})); expectError(execaSync('unicorns', {stdio: objectGeneratorFull})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectGeneratorFull]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectGeneratorFull]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectGeneratorFull]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectGeneratorFull]]}); expectAssignable(objectGeneratorFull); expectAssignable(objectGeneratorFull); expectAssignable([objectGeneratorFull]); expectAssignable([objectGeneratorFull]); expectAssignable(objectGeneratorFull); expectAssignable(objectGeneratorFull); expectAssignable([objectGeneratorFull]); expectAssignable([objectGeneratorFull]); ================================================ FILE: test-d/stdio/option/generator-object-mode-invalid.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const transformWithInvalidObjectMode = { * transform(line: unknown) { yield line; }, objectMode: 'true', } as const; expectError(await execa('unicorns', {stdin: transformWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdin: transformWithInvalidObjectMode})); expectError(await execa('unicorns', {stdin: [transformWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdin: [transformWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdout: transformWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdout: transformWithInvalidObjectMode})); expectError(await execa('unicorns', {stdout: [transformWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdout: [transformWithInvalidObjectMode]})); expectError(await execa('unicorns', {stderr: transformWithInvalidObjectMode})); expectError(execaSync('unicorns', {stderr: transformWithInvalidObjectMode})); expectError(await execa('unicorns', {stderr: [transformWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stderr: [transformWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdio: transformWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdio: transformWithInvalidObjectMode})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithInvalidObjectMode]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithInvalidObjectMode]]})); expectNotAssignable(transformWithInvalidObjectMode); expectNotAssignable(transformWithInvalidObjectMode); expectNotAssignable([transformWithInvalidObjectMode]); expectNotAssignable([transformWithInvalidObjectMode]); expectNotAssignable(transformWithInvalidObjectMode); expectNotAssignable(transformWithInvalidObjectMode); expectNotAssignable([transformWithInvalidObjectMode]); expectNotAssignable([transformWithInvalidObjectMode]); ================================================ FILE: test-d/stdio/option/generator-object-mode.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const transformWithObjectMode = { * transform(line: unknown) { yield line; }, objectMode: true, } as const; await execa('unicorns', {stdin: transformWithObjectMode}); execaSync('unicorns', {stdin: transformWithObjectMode}); await execa('unicorns', {stdin: [transformWithObjectMode]}); execaSync('unicorns', {stdin: [transformWithObjectMode]}); await execa('unicorns', {stdout: transformWithObjectMode}); execaSync('unicorns', {stdout: transformWithObjectMode}); await execa('unicorns', {stdout: [transformWithObjectMode]}); execaSync('unicorns', {stdout: [transformWithObjectMode]}); await execa('unicorns', {stderr: transformWithObjectMode}); execaSync('unicorns', {stderr: transformWithObjectMode}); await execa('unicorns', {stderr: [transformWithObjectMode]}); execaSync('unicorns', {stderr: [transformWithObjectMode]}); expectError(await execa('unicorns', {stdio: transformWithObjectMode})); expectError(execaSync('unicorns', {stdio: transformWithObjectMode})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithObjectMode]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithObjectMode]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithObjectMode]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithObjectMode]]}); expectAssignable(transformWithObjectMode); expectAssignable(transformWithObjectMode); expectAssignable([transformWithObjectMode]); expectAssignable([transformWithObjectMode]); expectAssignable(transformWithObjectMode); expectAssignable(transformWithObjectMode); expectAssignable([transformWithObjectMode]); expectAssignable([transformWithObjectMode]); ================================================ FILE: test-d/stdio/option/generator-object.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const objectGenerator = function * (line: unknown) { yield JSON.parse(line as string) as object; }; await execa('unicorns', {stdin: objectGenerator}); execaSync('unicorns', {stdin: objectGenerator}); await execa('unicorns', {stdin: [objectGenerator]}); execaSync('unicorns', {stdin: [objectGenerator]}); await execa('unicorns', {stdout: objectGenerator}); execaSync('unicorns', {stdout: objectGenerator}); await execa('unicorns', {stdout: [objectGenerator]}); execaSync('unicorns', {stdout: [objectGenerator]}); await execa('unicorns', {stderr: objectGenerator}); execaSync('unicorns', {stderr: objectGenerator}); await execa('unicorns', {stderr: [objectGenerator]}); execaSync('unicorns', {stderr: [objectGenerator]}); expectError(await execa('unicorns', {stdio: objectGenerator})); expectError(execaSync('unicorns', {stdio: objectGenerator})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectGenerator]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectGenerator]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectGenerator]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectGenerator]]}); expectAssignable(objectGenerator); expectAssignable(objectGenerator); expectAssignable([objectGenerator]); expectAssignable([objectGenerator]); expectAssignable(objectGenerator); expectAssignable(objectGenerator); expectAssignable([objectGenerator]); expectAssignable([objectGenerator]); ================================================ FILE: test-d/stdio/option/generator-only-binary.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const binaryOnly = {binary: true} as const; expectError(await execa('unicorns', {stdin: binaryOnly})); expectError(execaSync('unicorns', {stdin: binaryOnly})); expectError(await execa('unicorns', {stdin: [binaryOnly]})); expectError(execaSync('unicorns', {stdin: [binaryOnly]})); expectError(await execa('unicorns', {stdout: binaryOnly})); expectError(execaSync('unicorns', {stdout: binaryOnly})); expectError(await execa('unicorns', {stdout: [binaryOnly]})); expectError(execaSync('unicorns', {stdout: [binaryOnly]})); expectError(await execa('unicorns', {stderr: binaryOnly})); expectError(execaSync('unicorns', {stderr: binaryOnly})); expectError(await execa('unicorns', {stderr: [binaryOnly]})); expectError(execaSync('unicorns', {stderr: [binaryOnly]})); expectError(await execa('unicorns', {stdio: binaryOnly})); expectError(execaSync('unicorns', {stdio: binaryOnly})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', binaryOnly]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', binaryOnly]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [binaryOnly]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [binaryOnly]]})); expectNotAssignable(binaryOnly); expectNotAssignable(binaryOnly); expectNotAssignable([binaryOnly]); expectNotAssignable([binaryOnly]); expectNotAssignable(binaryOnly); expectNotAssignable(binaryOnly); expectNotAssignable([binaryOnly]); expectNotAssignable([binaryOnly]); ================================================ FILE: test-d/stdio/option/generator-only-final.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const finalOnly = { * final() { yield {} as unknown; }, } as const; expectError(await execa('unicorns', {stdin: finalOnly})); expectError(execaSync('unicorns', {stdin: finalOnly})); expectError(await execa('unicorns', {stdin: [finalOnly]})); expectError(execaSync('unicorns', {stdin: [finalOnly]})); expectError(await execa('unicorns', {stdout: finalOnly})); expectError(execaSync('unicorns', {stdout: finalOnly})); expectError(await execa('unicorns', {stdout: [finalOnly]})); expectError(execaSync('unicorns', {stdout: [finalOnly]})); expectError(await execa('unicorns', {stderr: finalOnly})); expectError(execaSync('unicorns', {stderr: finalOnly})); expectError(await execa('unicorns', {stderr: [finalOnly]})); expectError(execaSync('unicorns', {stderr: [finalOnly]})); expectError(await execa('unicorns', {stdio: finalOnly})); expectError(execaSync('unicorns', {stdio: finalOnly})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', finalOnly]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', finalOnly]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [finalOnly]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [finalOnly]]})); expectNotAssignable(finalOnly); expectNotAssignable(finalOnly); expectNotAssignable([finalOnly]); expectNotAssignable([finalOnly]); expectNotAssignable(finalOnly); expectNotAssignable(finalOnly); expectNotAssignable([finalOnly]); expectNotAssignable([finalOnly]); ================================================ FILE: test-d/stdio/option/generator-only-object-mode.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const objectModeOnly = {objectMode: true} as const; expectError(await execa('unicorns', {stdin: objectModeOnly})); expectError(execaSync('unicorns', {stdin: objectModeOnly})); expectError(await execa('unicorns', {stdin: [objectModeOnly]})); expectError(execaSync('unicorns', {stdin: [objectModeOnly]})); expectError(await execa('unicorns', {stdout: objectModeOnly})); expectError(execaSync('unicorns', {stdout: objectModeOnly})); expectError(await execa('unicorns', {stdout: [objectModeOnly]})); expectError(execaSync('unicorns', {stdout: [objectModeOnly]})); expectError(await execa('unicorns', {stderr: objectModeOnly})); expectError(execaSync('unicorns', {stderr: objectModeOnly})); expectError(await execa('unicorns', {stderr: [objectModeOnly]})); expectError(execaSync('unicorns', {stderr: [objectModeOnly]})); expectError(await execa('unicorns', {stdio: objectModeOnly})); expectError(execaSync('unicorns', {stdio: objectModeOnly})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectModeOnly]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectModeOnly]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectModeOnly]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectModeOnly]]})); expectNotAssignable(objectModeOnly); expectNotAssignable(objectModeOnly); expectNotAssignable([objectModeOnly]); expectNotAssignable([objectModeOnly]); expectNotAssignable(objectModeOnly); expectNotAssignable(objectModeOnly); expectNotAssignable([objectModeOnly]); expectNotAssignable([objectModeOnly]); ================================================ FILE: test-d/stdio/option/generator-only-preserve.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const preserveNewlinesOnly = {preserveNewlines: true} as const; expectError(await execa('unicorns', {stdin: preserveNewlinesOnly})); expectError(execaSync('unicorns', {stdin: preserveNewlinesOnly})); expectError(await execa('unicorns', {stdin: [preserveNewlinesOnly]})); expectError(execaSync('unicorns', {stdin: [preserveNewlinesOnly]})); expectError(await execa('unicorns', {stdout: preserveNewlinesOnly})); expectError(execaSync('unicorns', {stdout: preserveNewlinesOnly})); expectError(await execa('unicorns', {stdout: [preserveNewlinesOnly]})); expectError(execaSync('unicorns', {stdout: [preserveNewlinesOnly]})); expectError(await execa('unicorns', {stderr: preserveNewlinesOnly})); expectError(execaSync('unicorns', {stderr: preserveNewlinesOnly})); expectError(await execa('unicorns', {stderr: [preserveNewlinesOnly]})); expectError(execaSync('unicorns', {stderr: [preserveNewlinesOnly]})); expectError(await execa('unicorns', {stdio: preserveNewlinesOnly})); expectError(execaSync('unicorns', {stdio: preserveNewlinesOnly})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', preserveNewlinesOnly]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', preserveNewlinesOnly]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [preserveNewlinesOnly]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [preserveNewlinesOnly]]})); expectNotAssignable(preserveNewlinesOnly); expectNotAssignable(preserveNewlinesOnly); expectNotAssignable([preserveNewlinesOnly]); expectNotAssignable([preserveNewlinesOnly]); expectNotAssignable(preserveNewlinesOnly); expectNotAssignable(preserveNewlinesOnly); expectNotAssignable([preserveNewlinesOnly]); expectNotAssignable([preserveNewlinesOnly]); ================================================ FILE: test-d/stdio/option/generator-preserve-invalid.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const transformWithInvalidPreserveNewlines = { * transform(line: unknown) { yield line; }, preserveNewlines: 'true', } as const; expectError(await execa('unicorns', {stdin: transformWithInvalidPreserveNewlines})); expectError(execaSync('unicorns', {stdin: transformWithInvalidPreserveNewlines})); expectError(await execa('unicorns', {stdin: [transformWithInvalidPreserveNewlines]})); expectError(execaSync('unicorns', {stdin: [transformWithInvalidPreserveNewlines]})); expectError(await execa('unicorns', {stdout: transformWithInvalidPreserveNewlines})); expectError(execaSync('unicorns', {stdout: transformWithInvalidPreserveNewlines})); expectError(await execa('unicorns', {stdout: [transformWithInvalidPreserveNewlines]})); expectError(execaSync('unicorns', {stdout: [transformWithInvalidPreserveNewlines]})); expectError(await execa('unicorns', {stderr: transformWithInvalidPreserveNewlines})); expectError(execaSync('unicorns', {stderr: transformWithInvalidPreserveNewlines})); expectError(await execa('unicorns', {stderr: [transformWithInvalidPreserveNewlines]})); expectError(execaSync('unicorns', {stderr: [transformWithInvalidPreserveNewlines]})); expectError(await execa('unicorns', {stdio: transformWithInvalidPreserveNewlines})); expectError(execaSync('unicorns', {stdio: transformWithInvalidPreserveNewlines})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithInvalidPreserveNewlines]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithInvalidPreserveNewlines]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithInvalidPreserveNewlines]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithInvalidPreserveNewlines]]})); expectNotAssignable(transformWithInvalidPreserveNewlines); expectNotAssignable(transformWithInvalidPreserveNewlines); expectNotAssignable([transformWithInvalidPreserveNewlines]); expectNotAssignable([transformWithInvalidPreserveNewlines]); expectNotAssignable(transformWithInvalidPreserveNewlines); expectNotAssignable(transformWithInvalidPreserveNewlines); expectNotAssignable([transformWithInvalidPreserveNewlines]); expectNotAssignable([transformWithInvalidPreserveNewlines]); ================================================ FILE: test-d/stdio/option/generator-preserve.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const transformWithPreserveNewlines = { * transform(line: unknown) { yield line; }, preserveNewlines: true, } as const; await execa('unicorns', {stdin: transformWithPreserveNewlines}); execaSync('unicorns', {stdin: transformWithPreserveNewlines}); await execa('unicorns', {stdin: [transformWithPreserveNewlines]}); execaSync('unicorns', {stdin: [transformWithPreserveNewlines]}); await execa('unicorns', {stdout: transformWithPreserveNewlines}); execaSync('unicorns', {stdout: transformWithPreserveNewlines}); await execa('unicorns', {stdout: [transformWithPreserveNewlines]}); execaSync('unicorns', {stdout: [transformWithPreserveNewlines]}); await execa('unicorns', {stderr: transformWithPreserveNewlines}); execaSync('unicorns', {stderr: transformWithPreserveNewlines}); await execa('unicorns', {stderr: [transformWithPreserveNewlines]}); execaSync('unicorns', {stderr: [transformWithPreserveNewlines]}); expectError(await execa('unicorns', {stdio: transformWithPreserveNewlines})); expectError(execaSync('unicorns', {stdio: transformWithPreserveNewlines})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithPreserveNewlines]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', transformWithPreserveNewlines]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithPreserveNewlines]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [transformWithPreserveNewlines]]}); expectAssignable(transformWithPreserveNewlines); expectAssignable(transformWithPreserveNewlines); expectAssignable([transformWithPreserveNewlines]); expectAssignable([transformWithPreserveNewlines]); expectAssignable(transformWithPreserveNewlines); expectAssignable(transformWithPreserveNewlines); expectAssignable([transformWithPreserveNewlines]); expectAssignable([transformWithPreserveNewlines]); ================================================ FILE: test-d/stdio/option/generator-string-full.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const stringGeneratorFull = { * transform(line: string) { yield line; }, } as const; expectError(await execa('unicorns', {stdin: stringGeneratorFull})); expectError(execaSync('unicorns', {stdin: stringGeneratorFull})); expectError(await execa('unicorns', {stdin: [stringGeneratorFull]})); expectError(execaSync('unicorns', {stdin: [stringGeneratorFull]})); expectError(await execa('unicorns', {stdout: stringGeneratorFull})); expectError(execaSync('unicorns', {stdout: stringGeneratorFull})); expectError(await execa('unicorns', {stdout: [stringGeneratorFull]})); expectError(execaSync('unicorns', {stdout: [stringGeneratorFull]})); expectError(await execa('unicorns', {stderr: stringGeneratorFull})); expectError(execaSync('unicorns', {stderr: stringGeneratorFull})); expectError(await execa('unicorns', {stderr: [stringGeneratorFull]})); expectError(execaSync('unicorns', {stderr: [stringGeneratorFull]})); expectError(await execa('unicorns', {stdio: stringGeneratorFull})); expectError(execaSync('unicorns', {stdio: stringGeneratorFull})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', stringGeneratorFull]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', stringGeneratorFull]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [stringGeneratorFull]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [stringGeneratorFull]]})); expectNotAssignable(stringGeneratorFull); expectNotAssignable(stringGeneratorFull); expectNotAssignable([stringGeneratorFull]); expectNotAssignable([stringGeneratorFull]); expectNotAssignable(stringGeneratorFull); expectNotAssignable(stringGeneratorFull); expectNotAssignable([stringGeneratorFull]); expectNotAssignable([stringGeneratorFull]); ================================================ FILE: test-d/stdio/option/generator-string.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const stringGenerator = function * (line: string) { yield line; }; expectError(await execa('unicorns', {stdin: stringGenerator})); expectError(execaSync('unicorns', {stdin: stringGenerator})); expectError(await execa('unicorns', {stdin: [stringGenerator]})); expectError(execaSync('unicorns', {stdin: [stringGenerator]})); expectError(await execa('unicorns', {stdout: stringGenerator})); expectError(execaSync('unicorns', {stdout: stringGenerator})); expectError(await execa('unicorns', {stdout: [stringGenerator]})); expectError(execaSync('unicorns', {stdout: [stringGenerator]})); expectError(await execa('unicorns', {stderr: stringGenerator})); expectError(execaSync('unicorns', {stderr: stringGenerator})); expectError(await execa('unicorns', {stderr: [stringGenerator]})); expectError(execaSync('unicorns', {stderr: [stringGenerator]})); expectError(await execa('unicorns', {stdio: stringGenerator})); expectError(execaSync('unicorns', {stdio: stringGenerator})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', stringGenerator]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', stringGenerator]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [stringGenerator]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [stringGenerator]]})); expectNotAssignable(stringGenerator); expectNotAssignable(stringGenerator); expectNotAssignable([stringGenerator]); expectNotAssignable([stringGenerator]); expectNotAssignable(stringGenerator); expectNotAssignable(stringGenerator); expectNotAssignable([stringGenerator]); expectNotAssignable([stringGenerator]); ================================================ FILE: test-d/stdio/option/generator-unknown-full.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const unknownGeneratorFull = { * transform(line: unknown) { yield line; }, objectMode: true, } as const; await execa('unicorns', {stdin: unknownGeneratorFull}); execaSync('unicorns', {stdin: unknownGeneratorFull}); await execa('unicorns', {stdin: [unknownGeneratorFull]}); execaSync('unicorns', {stdin: [unknownGeneratorFull]}); await execa('unicorns', {stdout: unknownGeneratorFull}); execaSync('unicorns', {stdout: unknownGeneratorFull}); await execa('unicorns', {stdout: [unknownGeneratorFull]}); execaSync('unicorns', {stdout: [unknownGeneratorFull]}); await execa('unicorns', {stderr: unknownGeneratorFull}); execaSync('unicorns', {stderr: unknownGeneratorFull}); await execa('unicorns', {stderr: [unknownGeneratorFull]}); execaSync('unicorns', {stderr: [unknownGeneratorFull]}); expectError(await execa('unicorns', {stdio: unknownGeneratorFull})); expectError(execaSync('unicorns', {stdio: unknownGeneratorFull})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', unknownGeneratorFull]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', unknownGeneratorFull]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [unknownGeneratorFull]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [unknownGeneratorFull]]}); expectAssignable(unknownGeneratorFull); expectAssignable(unknownGeneratorFull); expectAssignable([unknownGeneratorFull]); expectAssignable([unknownGeneratorFull]); expectAssignable(unknownGeneratorFull); expectAssignable(unknownGeneratorFull); expectAssignable([unknownGeneratorFull]); expectAssignable([unknownGeneratorFull]); ================================================ FILE: test-d/stdio/option/generator-unknown.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const unknownGenerator = function * (line: unknown) { yield line; }; await execa('unicorns', {stdin: unknownGenerator}); execaSync('unicorns', {stdin: unknownGenerator}); await execa('unicorns', {stdin: [unknownGenerator]}); execaSync('unicorns', {stdin: [unknownGenerator]}); await execa('unicorns', {stdout: unknownGenerator}); execaSync('unicorns', {stdout: unknownGenerator}); await execa('unicorns', {stdout: [unknownGenerator]}); execaSync('unicorns', {stdout: [unknownGenerator]}); await execa('unicorns', {stderr: unknownGenerator}); execaSync('unicorns', {stderr: unknownGenerator}); await execa('unicorns', {stderr: [unknownGenerator]}); execaSync('unicorns', {stderr: [unknownGenerator]}); expectError(await execa('unicorns', {stdio: unknownGenerator})); expectError(execaSync('unicorns', {stdio: unknownGenerator})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', unknownGenerator]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', unknownGenerator]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [unknownGenerator]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [unknownGenerator]]}); expectAssignable(unknownGenerator); expectAssignable(unknownGenerator); expectAssignable([unknownGenerator]); expectAssignable([unknownGenerator]); expectAssignable(unknownGenerator); expectAssignable(unknownGenerator); expectAssignable([unknownGenerator]); expectAssignable([unknownGenerator]); ================================================ FILE: test-d/stdio/option/ignore.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: 'ignore'}); execaSync('unicorns', {stdin: 'ignore'}); expectError(await execa('unicorns', {stdin: ['ignore']})); expectError(execaSync('unicorns', {stdin: ['ignore']})); await execa('unicorns', {stdout: 'ignore'}); execaSync('unicorns', {stdout: 'ignore'}); expectError(await execa('unicorns', {stdout: ['ignore']})); expectError(execaSync('unicorns', {stdout: ['ignore']})); await execa('unicorns', {stderr: 'ignore'}); execaSync('unicorns', {stderr: 'ignore'}); expectError(await execa('unicorns', {stderr: ['ignore']})); expectError(execaSync('unicorns', {stderr: ['ignore']})); await execa('unicorns', {stdio: 'ignore'}); execaSync('unicorns', {stdio: 'ignore'}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'ignore']}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'ignore']}); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['ignore']]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['ignore']]})); expectAssignable('ignore'); expectAssignable('ignore'); expectNotAssignable(['ignore']); expectNotAssignable(['ignore']); expectAssignable('ignore'); expectAssignable('ignore'); expectNotAssignable(['ignore']); expectNotAssignable(['ignore']); ================================================ FILE: test-d/stdio/option/inherit.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: 'inherit'}); execaSync('unicorns', {stdin: 'inherit'}); await execa('unicorns', {stdin: ['inherit']}); execaSync('unicorns', {stdin: ['inherit']}); await execa('unicorns', {stdout: 'inherit'}); execaSync('unicorns', {stdout: 'inherit'}); await execa('unicorns', {stdout: ['inherit']}); execaSync('unicorns', {stdout: ['inherit']}); await execa('unicorns', {stderr: 'inherit'}); execaSync('unicorns', {stderr: 'inherit'}); await execa('unicorns', {stderr: ['inherit']}); execaSync('unicorns', {stderr: ['inherit']}); await execa('unicorns', {stdio: 'inherit'}); execaSync('unicorns', {stdio: 'inherit'}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'inherit']}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'inherit']}); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['inherit']]})); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['inherit']]}); expectAssignable('inherit'); expectAssignable('inherit'); expectAssignable(['inherit']); expectAssignable(['inherit']); expectAssignable('inherit'); expectAssignable('inherit'); expectAssignable(['inherit']); expectAssignable(['inherit']); ================================================ FILE: test-d/stdio/option/ipc.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: 'ipc'}); expectError(execaSync('unicorns', {stdin: 'ipc'})); expectError(await execa('unicorns', {stdin: ['ipc']})); expectError(execaSync('unicorns', {stdin: ['ipc']})); await execa('unicorns', {stdout: 'ipc'}); expectError(execaSync('unicorns', {stdout: 'ipc'})); expectError(await execa('unicorns', {stdout: ['ipc']})); expectError(execaSync('unicorns', {stdout: ['ipc']})); await execa('unicorns', {stderr: 'ipc'}); expectError(execaSync('unicorns', {stderr: 'ipc'})); expectError(await execa('unicorns', {stderr: ['ipc']})); expectError(execaSync('unicorns', {stderr: ['ipc']})); expectError(await execa('unicorns', {stdio: 'ipc'})); expectError(execaSync('unicorns', {stdio: 'ipc'})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'ipc']}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'ipc']})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['ipc']]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['ipc']]})); expectAssignable('ipc'); expectNotAssignable('ipc'); expectNotAssignable(['ipc']); expectNotAssignable(['ipc']); expectAssignable('ipc'); expectNotAssignable('ipc'); expectNotAssignable(['ipc']); expectNotAssignable(['ipc']); ================================================ FILE: test-d/stdio/option/iterable-async-binary.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const asyncBinaryIterableFunction = async function * () { yield new Uint8Array(0); }; const asyncBinaryIterable = asyncBinaryIterableFunction(); await execa('unicorns', {stdin: asyncBinaryIterable}); expectError(execaSync('unicorns', {stdin: asyncBinaryIterable})); await execa('unicorns', {stdin: [asyncBinaryIterable]}); expectError(execaSync('unicorns', {stdin: [asyncBinaryIterable]})); expectError(await execa('unicorns', {stdout: asyncBinaryIterable})); expectError(execaSync('unicorns', {stdout: asyncBinaryIterable})); expectError(await execa('unicorns', {stdout: [asyncBinaryIterable]})); expectError(execaSync('unicorns', {stdout: [asyncBinaryIterable]})); expectError(await execa('unicorns', {stderr: asyncBinaryIterable})); expectError(execaSync('unicorns', {stderr: asyncBinaryIterable})); expectError(await execa('unicorns', {stderr: [asyncBinaryIterable]})); expectError(execaSync('unicorns', {stderr: [asyncBinaryIterable]})); expectError(await execa('unicorns', {stdio: asyncBinaryIterable})); expectError(execaSync('unicorns', {stdio: asyncBinaryIterable})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncBinaryIterable]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncBinaryIterable]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncBinaryIterable]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncBinaryIterable]]})); expectAssignable(asyncBinaryIterable); expectNotAssignable(asyncBinaryIterable); expectAssignable([asyncBinaryIterable]); expectNotAssignable([asyncBinaryIterable]); expectNotAssignable(asyncBinaryIterable); expectNotAssignable(asyncBinaryIterable); expectNotAssignable([asyncBinaryIterable]); expectNotAssignable([asyncBinaryIterable]); ================================================ FILE: test-d/stdio/option/iterable-async-object.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const asyncObjectIterableFunction = async function * () { yield {}; }; const asyncObjectIterable = asyncObjectIterableFunction(); await execa('unicorns', {stdin: asyncObjectIterable}); expectError(execaSync('unicorns', {stdin: asyncObjectIterable})); await execa('unicorns', {stdin: [asyncObjectIterable]}); expectError(execaSync('unicorns', {stdin: [asyncObjectIterable]})); expectError(await execa('unicorns', {stdout: asyncObjectIterable})); expectError(execaSync('unicorns', {stdout: asyncObjectIterable})); expectError(await execa('unicorns', {stdout: [asyncObjectIterable]})); expectError(execaSync('unicorns', {stdout: [asyncObjectIterable]})); expectError(await execa('unicorns', {stderr: asyncObjectIterable})); expectError(execaSync('unicorns', {stderr: asyncObjectIterable})); expectError(await execa('unicorns', {stderr: [asyncObjectIterable]})); expectError(execaSync('unicorns', {stderr: [asyncObjectIterable]})); expectError(await execa('unicorns', {stdio: asyncObjectIterable})); expectError(execaSync('unicorns', {stdio: asyncObjectIterable})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncObjectIterable]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncObjectIterable]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncObjectIterable]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncObjectIterable]]})); expectAssignable(asyncObjectIterable); expectNotAssignable(asyncObjectIterable); expectAssignable([asyncObjectIterable]); expectNotAssignable([asyncObjectIterable]); expectNotAssignable(asyncObjectIterable); expectNotAssignable(asyncObjectIterable); expectNotAssignable([asyncObjectIterable]); expectNotAssignable([asyncObjectIterable]); ================================================ FILE: test-d/stdio/option/iterable-async-string.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const asyncStringIterableFunction = async function * () { yield ''; }; const asyncStringIterable = asyncStringIterableFunction(); await execa('unicorns', {stdin: asyncStringIterable}); expectError(execaSync('unicorns', {stdin: asyncStringIterable})); await execa('unicorns', {stdin: [asyncStringIterable]}); expectError(execaSync('unicorns', {stdin: [asyncStringIterable]})); expectError(await execa('unicorns', {stdout: asyncStringIterable})); expectError(execaSync('unicorns', {stdout: asyncStringIterable})); expectError(await execa('unicorns', {stdout: [asyncStringIterable]})); expectError(execaSync('unicorns', {stdout: [asyncStringIterable]})); expectError(await execa('unicorns', {stderr: asyncStringIterable})); expectError(execaSync('unicorns', {stderr: asyncStringIterable})); expectError(await execa('unicorns', {stderr: [asyncStringIterable]})); expectError(execaSync('unicorns', {stderr: [asyncStringIterable]})); expectError(await execa('unicorns', {stdio: asyncStringIterable})); expectError(execaSync('unicorns', {stdio: asyncStringIterable})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncStringIterable]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', asyncStringIterable]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncStringIterable]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [asyncStringIterable]]})); expectAssignable(asyncStringIterable); expectNotAssignable(asyncStringIterable); expectAssignable([asyncStringIterable]); expectNotAssignable([asyncStringIterable]); expectNotAssignable(asyncStringIterable); expectNotAssignable(asyncStringIterable); expectNotAssignable([asyncStringIterable]); expectNotAssignable([asyncStringIterable]); ================================================ FILE: test-d/stdio/option/iterable-binary.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const binaryIterableFunction = function * () { yield new Uint8Array(0); }; const binaryIterable = binaryIterableFunction(); await execa('unicorns', {stdin: binaryIterable}); execaSync('unicorns', {stdin: binaryIterable}); await execa('unicorns', {stdin: [binaryIterable]}); execaSync('unicorns', {stdin: [binaryIterable]}); expectError(await execa('unicorns', {stdout: binaryIterable})); expectError(execaSync('unicorns', {stdout: binaryIterable})); expectError(await execa('unicorns', {stdout: [binaryIterable]})); expectError(execaSync('unicorns', {stdout: [binaryIterable]})); expectError(await execa('unicorns', {stderr: binaryIterable})); expectError(execaSync('unicorns', {stderr: binaryIterable})); expectError(await execa('unicorns', {stderr: [binaryIterable]})); expectError(execaSync('unicorns', {stderr: [binaryIterable]})); expectError(await execa('unicorns', {stdio: binaryIterable})); expectError(execaSync('unicorns', {stdio: binaryIterable})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', binaryIterable]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', binaryIterable]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [binaryIterable]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [binaryIterable]]})); expectAssignable(binaryIterable); expectAssignable(binaryIterable); expectAssignable([binaryIterable]); expectAssignable([binaryIterable]); expectNotAssignable(binaryIterable); expectNotAssignable(binaryIterable); expectNotAssignable([binaryIterable]); expectNotAssignable([binaryIterable]); ================================================ FILE: test-d/stdio/option/iterable-object.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const objectIterableFunction = function * () { yield {}; }; const objectIterable = objectIterableFunction(); await execa('unicorns', {stdin: objectIterable}); execaSync('unicorns', {stdin: objectIterable}); await execa('unicorns', {stdin: [objectIterable]}); execaSync('unicorns', {stdin: [objectIterable]}); expectError(await execa('unicorns', {stdout: objectIterable})); expectError(execaSync('unicorns', {stdout: objectIterable})); expectError(await execa('unicorns', {stdout: [objectIterable]})); expectError(execaSync('unicorns', {stdout: [objectIterable]})); expectError(await execa('unicorns', {stderr: objectIterable})); expectError(execaSync('unicorns', {stderr: objectIterable})); expectError(await execa('unicorns', {stderr: [objectIterable]})); expectError(execaSync('unicorns', {stderr: [objectIterable]})); expectError(await execa('unicorns', {stdio: objectIterable})); expectError(execaSync('unicorns', {stdio: objectIterable})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectIterable]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', objectIterable]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectIterable]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [objectIterable]]})); expectAssignable(objectIterable); expectAssignable(objectIterable); expectAssignable([objectIterable]); expectAssignable([objectIterable]); expectNotAssignable(objectIterable); expectNotAssignable(objectIterable); expectNotAssignable([objectIterable]); expectNotAssignable([objectIterable]); ================================================ FILE: test-d/stdio/option/iterable-string.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const stringIterableFunction = function * () { yield ''; }; const stringIterable = stringIterableFunction(); await execa('unicorns', {stdin: stringIterable}); execaSync('unicorns', {stdin: stringIterable}); await execa('unicorns', {stdin: [stringIterable]}); execaSync('unicorns', {stdin: [stringIterable]}); expectError(await execa('unicorns', {stdout: stringIterable})); expectError(execaSync('unicorns', {stdout: stringIterable})); expectError(await execa('unicorns', {stdout: [stringIterable]})); expectError(execaSync('unicorns', {stdout: [stringIterable]})); expectError(await execa('unicorns', {stderr: stringIterable})); expectError(execaSync('unicorns', {stderr: stringIterable})); expectError(await execa('unicorns', {stderr: [stringIterable]})); expectError(execaSync('unicorns', {stderr: [stringIterable]})); expectError(await execa('unicorns', {stdio: stringIterable})); expectError(execaSync('unicorns', {stdio: stringIterable})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', stringIterable]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', stringIterable]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [stringIterable]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [stringIterable]]})); expectAssignable(stringIterable); expectAssignable(stringIterable); expectAssignable([stringIterable]); expectAssignable([stringIterable]); expectNotAssignable(stringIterable); expectNotAssignable(stringIterable); expectNotAssignable([stringIterable]); expectNotAssignable([stringIterable]); ================================================ FILE: test-d/stdio/option/null.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: null})); expectError(execaSync('unicorns', {stdin: null})); expectError(await execa('unicorns', {stdin: [null]})); expectError(execaSync('unicorns', {stdin: [null]})); expectError(await execa('unicorns', {stdout: null})); expectError(execaSync('unicorns', {stdout: null})); expectError(await execa('unicorns', {stdout: [null]})); expectError(execaSync('unicorns', {stdout: [null]})); expectError(await execa('unicorns', {stderr: null})); expectError(execaSync('unicorns', {stderr: null})); expectError(await execa('unicorns', {stderr: [null]})); expectError(execaSync('unicorns', {stderr: [null]})); expectError(await execa('unicorns', {stdio: null})); expectError(execaSync('unicorns', {stdio: null})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', null]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', null]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [null]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [null]]})); expectNotAssignable(null); expectNotAssignable(null); expectNotAssignable([null]); expectNotAssignable([null]); expectNotAssignable(null); expectNotAssignable(null); expectNotAssignable([null]); expectNotAssignable([null]); ================================================ FILE: test-d/stdio/option/overlapped.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: 'overlapped'}); expectError(execaSync('unicorns', {stdin: 'overlapped'})); await execa('unicorns', {stdin: ['overlapped']}); expectError(execaSync('unicorns', {stdin: ['overlapped']})); await execa('unicorns', {stdout: 'overlapped'}); expectError(execaSync('unicorns', {stdout: 'overlapped'})); await execa('unicorns', {stdout: ['overlapped']}); expectError(execaSync('unicorns', {stdout: ['overlapped']})); await execa('unicorns', {stderr: 'overlapped'}); expectError(execaSync('unicorns', {stderr: 'overlapped'})); await execa('unicorns', {stderr: ['overlapped']}); expectError(execaSync('unicorns', {stderr: ['overlapped']})); await execa('unicorns', {stdio: 'overlapped'}); expectError(execaSync('unicorns', {stdio: 'overlapped'})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'overlapped']}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'overlapped']})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['overlapped']]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['overlapped']]})); expectAssignable('overlapped'); expectNotAssignable('overlapped'); expectAssignable(['overlapped']); expectNotAssignable(['overlapped']); expectAssignable('overlapped'); expectNotAssignable('overlapped'); expectAssignable(['overlapped']); expectNotAssignable(['overlapped']); ================================================ FILE: test-d/stdio/option/pipe-inherit.test-d.ts ================================================ import {expectError, expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const pipeInherit = ['pipe', 'inherit'] as const; await execa('unicorns', {stdin: pipeInherit}); execaSync('unicorns', {stdin: pipeInherit}); await execa('unicorns', {stdout: pipeInherit}); execaSync('unicorns', {stdout: pipeInherit}); await execa('unicorns', {stderr: pipeInherit}); execaSync('unicorns', {stderr: pipeInherit}); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', pipeInherit]})); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', pipeInherit]}); expectAssignable(pipeInherit); expectAssignable(pipeInherit); expectAssignable(pipeInherit); expectAssignable(pipeInherit); ================================================ FILE: test-d/stdio/option/pipe-undefined.test-d.ts ================================================ import {expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const pipeUndefined = ['pipe', undefined] as const; await execa('unicorns', {stdin: pipeUndefined}); execaSync('unicorns', {stdin: pipeUndefined}); await execa('unicorns', {stdout: pipeUndefined}); execaSync('unicorns', {stdout: pipeUndefined}); await execa('unicorns', {stderr: pipeUndefined}); execaSync('unicorns', {stderr: pipeUndefined}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', pipeUndefined]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', pipeUndefined]}); expectAssignable(pipeUndefined); expectAssignable(pipeUndefined); expectAssignable(pipeUndefined); expectAssignable(pipeUndefined); ================================================ FILE: test-d/stdio/option/pipe-wide.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const pipe = 'pipe' as string; const pipes = ['pipe'] as string[]; const pipesOfPipes = [['pipe']] as string[][]; expectError(await execa('unicorns', {stdin: pipe})); expectError(execaSync('unicorns', {stdin: pipe})); expectError(await execa('unicorns', {stdin: pipes})); expectError(execaSync('unicorns', {stdin: pipes})); expectError(await execa('unicorns', {stdout: pipe})); expectError(execaSync('unicorns', {stdout: pipe})); expectError(await execa('unicorns', {stdout: pipes})); expectError(execaSync('unicorns', {stdout: pipes})); expectError(await execa('unicorns', {stderr: pipe})); expectError(execaSync('unicorns', {stderr: pipe})); expectError(await execa('unicorns', {stderr: pipes})); expectError(execaSync('unicorns', {stderr: pipes})); expectError(await execa('unicorns', {stdio: pipe})); expectError(execaSync('unicorns', {stdio: pipe})); expectError(await execa('unicorns', {stdio: pipes})); expectError(execaSync('unicorns', {stdio: pipes})); expectError(await execa('unicorns', {stdio: pipesOfPipes})); expectError(execaSync('unicorns', {stdio: pipesOfPipes})); expectNotAssignable(pipe); expectNotAssignable(pipe); expectNotAssignable(pipes); expectNotAssignable(pipes); expectNotAssignable(pipe); expectNotAssignable(pipe); expectNotAssignable(pipes); expectNotAssignable(pipes); ================================================ FILE: test-d/stdio/option/pipe.test-d.ts ================================================ import {expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: 'pipe'}); execaSync('unicorns', {stdin: 'pipe'}); await execa('unicorns', {stdin: ['pipe']}); execaSync('unicorns', {stdin: ['pipe']}); await execa('unicorns', {stdout: 'pipe'}); execaSync('unicorns', {stdout: 'pipe'}); await execa('unicorns', {stdout: ['pipe']}); execaSync('unicorns', {stdout: ['pipe']}); await execa('unicorns', {stderr: 'pipe'}); execaSync('unicorns', {stderr: 'pipe'}); await execa('unicorns', {stderr: ['pipe']}); execaSync('unicorns', {stderr: ['pipe']}); await execa('unicorns', {stdio: 'pipe'}); execaSync('unicorns', {stdio: 'pipe'}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['pipe']]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['pipe']]}); expectAssignable('pipe'); expectAssignable('pipe'); expectAssignable(['pipe']); expectAssignable(['pipe']); expectAssignable('pipe'); expectAssignable('pipe'); expectAssignable(['pipe']); expectAssignable(['pipe']); ================================================ FILE: test-d/stdio/option/process-stderr.test-d.ts ================================================ import * as process from 'node:process'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: process.stderr})); expectError(execaSync('unicorns', {stdin: process.stderr})); expectError(await execa('unicorns', {stdin: [process.stderr]})); expectError(execaSync('unicorns', {stdin: [process.stderr]})); await execa('unicorns', {stdout: process.stderr}); execaSync('unicorns', {stdout: process.stderr}); await execa('unicorns', {stdout: [process.stderr]}); expectError(execaSync('unicorns', {stdout: [process.stderr]})); await execa('unicorns', {stderr: process.stderr}); execaSync('unicorns', {stderr: process.stderr}); await execa('unicorns', {stderr: [process.stderr]}); expectError(execaSync('unicorns', {stderr: [process.stderr]})); expectError(await execa('unicorns', {stdio: process.stderr})); expectError(execaSync('unicorns', {stdio: process.stderr})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', process.stderr]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', process.stderr]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [process.stderr]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [process.stderr]]})); expectNotAssignable(process.stderr); expectNotAssignable(process.stderr); expectNotAssignable([process.stderr]); expectNotAssignable([process.stderr]); expectAssignable(process.stderr); expectAssignable(process.stderr); expectAssignable([process.stderr]); expectNotAssignable([process.stderr]); ================================================ FILE: test-d/stdio/option/process-stdin.test-d.ts ================================================ import * as process from 'node:process'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: process.stdin}); execaSync('unicorns', {stdin: process.stdin}); await execa('unicorns', {stdin: [process.stdin]}); expectError(execaSync('unicorns', {stdin: [process.stdin]})); expectError(await execa('unicorns', {stdout: process.stdin})); expectError(execaSync('unicorns', {stdout: process.stdin})); expectError(await execa('unicorns', {stdout: [process.stdin]})); expectError(execaSync('unicorns', {stdout: [process.stdin]})); expectError(await execa('unicorns', {stderr: process.stdin})); expectError(execaSync('unicorns', {stderr: process.stdin})); expectError(await execa('unicorns', {stderr: [process.stdin]})); expectError(execaSync('unicorns', {stderr: [process.stdin]})); expectError(await execa('unicorns', {stdio: process.stdin})); expectError(execaSync('unicorns', {stdio: process.stdin})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', process.stdin]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', process.stdin]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [process.stdin]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [process.stdin]]})); expectAssignable(process.stdin); expectAssignable(process.stdin); expectAssignable([process.stdin]); expectNotAssignable([process.stdin]); expectNotAssignable(process.stdin); expectNotAssignable(process.stdin); expectNotAssignable([process.stdin]); expectNotAssignable([process.stdin]); ================================================ FILE: test-d/stdio/option/process-stdout.test-d.ts ================================================ import * as process from 'node:process'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: process.stdout})); expectError(execaSync('unicorns', {stdin: process.stdout})); expectError(await execa('unicorns', {stdin: [process.stdout]})); expectError(execaSync('unicorns', {stdin: [process.stdout]})); await execa('unicorns', {stdout: process.stdout}); execaSync('unicorns', {stdout: process.stdout}); await execa('unicorns', {stdout: [process.stdout]}); expectError(execaSync('unicorns', {stdout: [process.stdout]})); await execa('unicorns', {stderr: process.stdout}); execaSync('unicorns', {stderr: process.stdout}); await execa('unicorns', {stderr: [process.stdout]}); expectError(execaSync('unicorns', {stderr: [process.stdout]})); expectError(await execa('unicorns', {stdio: process.stdout})); expectError(execaSync('unicorns', {stdio: process.stdout})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', process.stdout]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', process.stdout]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [process.stdout]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [process.stdout]]})); expectNotAssignable(process.stdout); expectNotAssignable(process.stdout); expectNotAssignable([process.stdout]); expectNotAssignable([process.stdout]); expectAssignable(process.stdout); expectAssignable(process.stdout); expectAssignable([process.stdout]); expectNotAssignable([process.stdout]); ================================================ FILE: test-d/stdio/option/readable-stream.test-d.ts ================================================ import {ReadableStream} from 'node:stream/web'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: new ReadableStream()}); expectError(execaSync('unicorns', {stdin: new ReadableStream()})); await execa('unicorns', {stdin: [new ReadableStream()]}); expectError(execaSync('unicorns', {stdin: [new ReadableStream()]})); expectError(await execa('unicorns', {stdout: new ReadableStream()})); expectError(execaSync('unicorns', {stdout: new ReadableStream()})); expectError(await execa('unicorns', {stdout: [new ReadableStream()]})); expectError(execaSync('unicorns', {stdout: [new ReadableStream()]})); expectError(await execa('unicorns', {stderr: new ReadableStream()})); expectError(execaSync('unicorns', {stderr: new ReadableStream()})); expectError(await execa('unicorns', {stderr: [new ReadableStream()]})); expectError(execaSync('unicorns', {stderr: [new ReadableStream()]})); expectError(await execa('unicorns', {stdio: new ReadableStream()})); expectError(execaSync('unicorns', {stdio: new ReadableStream()})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new ReadableStream()]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new ReadableStream()]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new ReadableStream()]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new ReadableStream()]]})); expectAssignable(new ReadableStream()); expectNotAssignable(new ReadableStream()); expectAssignable([new ReadableStream()]); expectNotAssignable([new ReadableStream()]); expectNotAssignable(new ReadableStream()); expectNotAssignable(new ReadableStream()); expectNotAssignable([new ReadableStream()]); expectNotAssignable([new ReadableStream()]); ================================================ FILE: test-d/stdio/option/readable.test-d.ts ================================================ import {Readable} from 'node:stream'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: new Readable()}); execaSync('unicorns', {stdin: new Readable()}); await execa('unicorns', {stdin: [new Readable()]}); expectError(execaSync('unicorns', {stdin: [new Readable()]})); expectError(await execa('unicorns', {stdout: new Readable()})); expectError(execaSync('unicorns', {stdout: new Readable()})); expectError(await execa('unicorns', {stdout: [new Readable()]})); expectError(execaSync('unicorns', {stdout: [new Readable()]})); expectError(await execa('unicorns', {stderr: new Readable()})); expectError(execaSync('unicorns', {stderr: new Readable()})); expectError(await execa('unicorns', {stderr: [new Readable()]})); expectError(execaSync('unicorns', {stderr: [new Readable()]})); expectError(await execa('unicorns', {stdio: new Readable()})); expectError(execaSync('unicorns', {stdio: new Readable()})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new Readable()]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new Readable()]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new Readable()]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new Readable()]]})); expectAssignable(new Readable()); expectAssignable(new Readable()); expectAssignable([new Readable()]); expectNotAssignable([new Readable()]); expectNotAssignable(new Readable()); expectNotAssignable(new Readable()); expectNotAssignable([new Readable()]); expectNotAssignable([new Readable()]); ================================================ FILE: test-d/stdio/option/uint-array.test-d.ts ================================================ import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: new Uint8Array()}); execaSync('unicorns', {stdin: new Uint8Array()}); await execa('unicorns', {stdin: [new Uint8Array()]}); execaSync('unicorns', {stdin: [new Uint8Array()]}); expectError(await execa('unicorns', {stdout: new Uint8Array()})); expectError(execaSync('unicorns', {stdout: new Uint8Array()})); expectError(await execa('unicorns', {stdout: [new Uint8Array()]})); expectError(execaSync('unicorns', {stdout: [new Uint8Array()]})); expectError(await execa('unicorns', {stderr: new Uint8Array()})); expectError(execaSync('unicorns', {stderr: new Uint8Array()})); expectError(await execa('unicorns', {stderr: [new Uint8Array()]})); expectError(execaSync('unicorns', {stderr: [new Uint8Array()]})); expectError(await execa('unicorns', {stdio: new Uint8Array()})); expectError(execaSync('unicorns', {stdio: new Uint8Array()})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new Uint8Array()]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new Uint8Array()]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new Uint8Array()]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new Uint8Array()]]})); expectAssignable(new Uint8Array()); expectAssignable(new Uint8Array()); expectAssignable([new Uint8Array()]); expectAssignable([new Uint8Array()]); expectNotAssignable(new Uint8Array()); expectNotAssignable(new Uint8Array()); expectNotAssignable([new Uint8Array()]); expectNotAssignable([new Uint8Array()]); ================================================ FILE: test-d/stdio/option/undefined.test-d.ts ================================================ import {expectAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; await execa('unicorns', {stdin: undefined}); execaSync('unicorns', {stdin: undefined}); await execa('unicorns', {stdin: [undefined]}); execaSync('unicorns', {stdin: [undefined]}); await execa('unicorns', {stdout: undefined}); execaSync('unicorns', {stdout: undefined}); await execa('unicorns', {stdout: [undefined]}); execaSync('unicorns', {stdout: [undefined]}); await execa('unicorns', {stderr: undefined}); execaSync('unicorns', {stderr: undefined}); await execa('unicorns', {stderr: [undefined]}); execaSync('unicorns', {stderr: [undefined]}); await execa('unicorns', {stdio: undefined}); execaSync('unicorns', {stdio: undefined}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', undefined]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', undefined]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [undefined]]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [undefined]]}); expectAssignable(undefined); expectAssignable(undefined); expectAssignable([undefined]); expectAssignable([undefined]); expectAssignable(undefined); expectAssignable(undefined); expectAssignable([undefined]); expectAssignable([undefined]); ================================================ FILE: test-d/stdio/option/unknown.test-d.ts ================================================ import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: 'unknown'})); expectError(execaSync('unicorns', {stdin: 'unknown'})); expectError(await execa('unicorns', {stdin: ['unknown']})); expectError(execaSync('unicorns', {stdin: ['unknown']})); expectError(await execa('unicorns', {stdout: 'unknown'})); expectError(execaSync('unicorns', {stdout: 'unknown'})); expectError(await execa('unicorns', {stdout: ['unknown']})); expectError(execaSync('unicorns', {stdout: ['unknown']})); expectError(await execa('unicorns', {stderr: 'unknown'})); expectError(execaSync('unicorns', {stderr: 'unknown'})); expectError(await execa('unicorns', {stderr: ['unknown']})); expectError(execaSync('unicorns', {stderr: ['unknown']})); expectError(await execa('unicorns', {stdio: 'unknown'})); expectError(execaSync('unicorns', {stdio: 'unknown'})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'unknown']})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', 'unknown']})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['unknown']]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', ['unknown']]})); expectNotAssignable('unknown'); expectNotAssignable('unknown'); expectNotAssignable(['unknown']); expectNotAssignable(['unknown']); expectNotAssignable('unknown'); expectNotAssignable('unknown'); expectNotAssignable(['unknown']); expectNotAssignable(['unknown']); ================================================ FILE: test-d/stdio/option/web-transform-instance.test-d.ts ================================================ import {TransformStream} from 'node:stream/web'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const webTransformInstance = new TransformStream(); await execa('unicorns', {stdin: webTransformInstance}); expectError(execaSync('unicorns', {stdin: webTransformInstance})); await execa('unicorns', {stdin: [webTransformInstance]}); expectError(execaSync('unicorns', {stdin: [webTransformInstance]})); await execa('unicorns', {stdout: webTransformInstance}); expectError(execaSync('unicorns', {stdout: webTransformInstance})); await execa('unicorns', {stdout: [webTransformInstance]}); expectError(execaSync('unicorns', {stdout: [webTransformInstance]})); await execa('unicorns', {stderr: webTransformInstance}); expectError(execaSync('unicorns', {stderr: webTransformInstance})); await execa('unicorns', {stderr: [webTransformInstance]}); expectError(execaSync('unicorns', {stderr: [webTransformInstance]})); expectError(await execa('unicorns', {stdio: webTransformInstance})); expectError(execaSync('unicorns', {stdio: webTransformInstance})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', webTransformInstance]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', webTransformInstance]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [webTransformInstance]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [webTransformInstance]]})); expectAssignable(webTransformInstance); expectNotAssignable(webTransformInstance); expectAssignable([webTransformInstance]); expectNotAssignable([webTransformInstance]); expectAssignable(webTransformInstance); expectNotAssignable(webTransformInstance); expectAssignable([webTransformInstance]); expectNotAssignable([webTransformInstance]); ================================================ FILE: test-d/stdio/option/web-transform-invalid.test-d.ts ================================================ import {TransformStream} from 'node:stream/web'; import {expectError, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const webTransformWithInvalidObjectMode = { transform: new TransformStream(), objectMode: 'true', } as const; expectError(await execa('unicorns', {stdin: webTransformWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdin: webTransformWithInvalidObjectMode})); expectError(await execa('unicorns', {stdin: [webTransformWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdin: [webTransformWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdout: webTransformWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdout: webTransformWithInvalidObjectMode})); expectError(await execa('unicorns', {stdout: [webTransformWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdout: [webTransformWithInvalidObjectMode]})); expectError(await execa('unicorns', {stderr: webTransformWithInvalidObjectMode})); expectError(execaSync('unicorns', {stderr: webTransformWithInvalidObjectMode})); expectError(await execa('unicorns', {stderr: [webTransformWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stderr: [webTransformWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdio: webTransformWithInvalidObjectMode})); expectError(execaSync('unicorns', {stdio: webTransformWithInvalidObjectMode})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', webTransformWithInvalidObjectMode]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', webTransformWithInvalidObjectMode]})); expectError(await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [webTransformWithInvalidObjectMode]]})); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [webTransformWithInvalidObjectMode]]})); expectNotAssignable(webTransformWithInvalidObjectMode); expectNotAssignable(webTransformWithInvalidObjectMode); expectNotAssignable([webTransformWithInvalidObjectMode]); expectNotAssignable([webTransformWithInvalidObjectMode]); expectNotAssignable(webTransformWithInvalidObjectMode); expectNotAssignable(webTransformWithInvalidObjectMode); expectNotAssignable([webTransformWithInvalidObjectMode]); expectNotAssignable([webTransformWithInvalidObjectMode]); ================================================ FILE: test-d/stdio/option/web-transform-object.test-d.ts ================================================ import {TransformStream} from 'node:stream/web'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const webTransformObject = { transform: new TransformStream(), objectMode: true as const, } as const; await execa('unicorns', {stdin: webTransformObject}); expectError(execaSync('unicorns', {stdin: webTransformObject})); await execa('unicorns', {stdin: [webTransformObject]}); expectError(execaSync('unicorns', {stdin: [webTransformObject]})); await execa('unicorns', {stdout: webTransformObject}); expectError(execaSync('unicorns', {stdout: webTransformObject})); await execa('unicorns', {stdout: [webTransformObject]}); expectError(execaSync('unicorns', {stdout: [webTransformObject]})); await execa('unicorns', {stderr: webTransformObject}); expectError(execaSync('unicorns', {stderr: webTransformObject})); await execa('unicorns', {stderr: [webTransformObject]}); expectError(execaSync('unicorns', {stderr: [webTransformObject]})); expectError(await execa('unicorns', {stdio: webTransformObject})); expectError(execaSync('unicorns', {stdio: webTransformObject})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', webTransformObject]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', webTransformObject]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [webTransformObject]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [webTransformObject]]})); expectAssignable(webTransformObject); expectNotAssignable(webTransformObject); expectAssignable([webTransformObject]); expectNotAssignable([webTransformObject]); expectAssignable(webTransformObject); expectNotAssignable(webTransformObject); expectAssignable([webTransformObject]); expectNotAssignable([webTransformObject]); ================================================ FILE: test-d/stdio/option/web-transform.test-d.ts ================================================ import {TransformStream} from 'node:stream/web'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; const webTransform = {transform: new TransformStream()} as const; await execa('unicorns', {stdin: webTransform}); expectError(execaSync('unicorns', {stdin: webTransform})); await execa('unicorns', {stdin: [webTransform]}); expectError(execaSync('unicorns', {stdin: [webTransform]})); await execa('unicorns', {stdout: webTransform}); expectError(execaSync('unicorns', {stdout: webTransform})); await execa('unicorns', {stdout: [webTransform]}); expectError(execaSync('unicorns', {stdout: [webTransform]})); await execa('unicorns', {stderr: webTransform}); expectError(execaSync('unicorns', {stderr: webTransform})); await execa('unicorns', {stderr: [webTransform]}); expectError(execaSync('unicorns', {stderr: [webTransform]})); expectError(await execa('unicorns', {stdio: webTransform})); expectError(execaSync('unicorns', {stdio: webTransform})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', webTransform]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', webTransform]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [webTransform]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [webTransform]]})); expectAssignable(webTransform); expectNotAssignable(webTransform); expectAssignable([webTransform]); expectNotAssignable([webTransform]); expectAssignable(webTransform); expectNotAssignable(webTransform); expectAssignable([webTransform]); expectNotAssignable([webTransform]); ================================================ FILE: test-d/stdio/option/writable-stream.test-d.ts ================================================ import {WritableStream} from 'node:stream/web'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: new WritableStream()})); expectError(execaSync('unicorns', {stdin: new WritableStream()})); expectError(await execa('unicorns', {stdin: [new WritableStream()]})); expectError(execaSync('unicorns', {stdin: [new WritableStream()]})); await execa('unicorns', {stdout: new WritableStream()}); expectError(execaSync('unicorns', {stdout: new WritableStream()})); await execa('unicorns', {stdout: [new WritableStream()]}); expectError(execaSync('unicorns', {stdout: [new WritableStream()]})); await execa('unicorns', {stderr: new WritableStream()}); expectError(execaSync('unicorns', {stderr: new WritableStream()})); await execa('unicorns', {stderr: [new WritableStream()]}); expectError(execaSync('unicorns', {stderr: [new WritableStream()]})); expectError(await execa('unicorns', {stdio: new WritableStream()})); expectError(execaSync('unicorns', {stdio: new WritableStream()})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new WritableStream()]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new WritableStream()]})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new WritableStream()]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new WritableStream()]]})); expectNotAssignable(new WritableStream()); expectNotAssignable(new WritableStream()); expectNotAssignable([new WritableStream()]); expectNotAssignable([new WritableStream()]); expectAssignable(new WritableStream()); expectNotAssignable(new WritableStream()); expectAssignable([new WritableStream()]); expectNotAssignable([new WritableStream()]); ================================================ FILE: test-d/stdio/option/writable.test-d.ts ================================================ import {Writable} from 'node:stream'; import {expectError, expectAssignable, expectNotAssignable} from 'tsd'; import { execa, execaSync, type StdinOption, type StdinSyncOption, type StdoutStderrOption, type StdoutStderrSyncOption, } from '../../../index.js'; expectError(await execa('unicorns', {stdin: new Writable()})); expectError(execaSync('unicorns', {stdin: new Writable()})); expectError(await execa('unicorns', {stdin: [new Writable()]})); expectError(execaSync('unicorns', {stdin: [new Writable()]})); await execa('unicorns', {stdout: new Writable()}); execaSync('unicorns', {stdout: new Writable()}); await execa('unicorns', {stdout: [new Writable()]}); expectError(execaSync('unicorns', {stdout: [new Writable()]})); await execa('unicorns', {stderr: new Writable()}); execaSync('unicorns', {stderr: new Writable()}); await execa('unicorns', {stderr: [new Writable()]}); expectError(execaSync('unicorns', {stderr: [new Writable()]})); expectError(await execa('unicorns', {stdio: new Writable()})); expectError(execaSync('unicorns', {stdio: new Writable()})); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new Writable()]}); execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', new Writable()]}); await execa('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new Writable()]]}); expectError(execaSync('unicorns', {stdio: ['pipe', 'pipe', 'pipe', [new Writable()]]})); expectNotAssignable(new Writable()); expectNotAssignable(new Writable()); expectNotAssignable([new Writable()]); expectNotAssignable([new Writable()]); expectAssignable(new Writable()); expectAssignable(new Writable()); expectAssignable([new Writable()]); expectNotAssignable([new Writable()]); ================================================ FILE: test-d/subprocess/all.test-d.ts ================================================ import type {Readable} from 'node:stream'; import {expectType} from 'tsd'; import {execa} from '../../index.js'; const allSubprocess = execa('unicorns', {all: true}); expectType(allSubprocess.all); const noAllSubprocess = execa('unicorns'); expectType(noAllSubprocess.all); ================================================ FILE: test-d/subprocess/stdio.test-d.ts ================================================ import type {Readable, Writable} from 'node:stream'; import {expectType, expectError} from 'tsd'; import {execa, type Subprocess} from '../../index.js'; expectType({} as Subprocess['stdin']); expectType({} as Subprocess['stdout']); expectType({} as Subprocess['stderr']); expectType({} as Subprocess['all']); const bufferSubprocess = execa('unicorns', {encoding: 'buffer', all: true}); expectType(bufferSubprocess.stdin); expectType(bufferSubprocess.stdio[0]); expectType(bufferSubprocess.stdout); expectType(bufferSubprocess.stdio[1]); expectType(bufferSubprocess.stderr); expectType(bufferSubprocess.stdio[2]); expectType(bufferSubprocess.all); expectError(bufferSubprocess.stdio[3].destroy()); const hexSubprocess = execa('unicorns', {encoding: 'hex', all: true}); expectType(hexSubprocess.stdin); expectType(hexSubprocess.stdio[0]); expectType(hexSubprocess.stdout); expectType(hexSubprocess.stdio[1]); expectType(hexSubprocess.stderr); expectType(hexSubprocess.stdio[2]); expectType(hexSubprocess.all); expectError(hexSubprocess.stdio[3].destroy()); const multipleStdinSubprocess = execa('unicorns', {stdin: ['inherit', 'pipe']}); expectType(multipleStdinSubprocess.stdin); const multipleStdoutSubprocess = execa('unicorns', {stdout: ['inherit', 'pipe'] as ['inherit', 'pipe'], all: true}); expectType(multipleStdoutSubprocess.stdin); expectType(multipleStdoutSubprocess.stdio[0]); expectType(multipleStdoutSubprocess.stdout); expectType(multipleStdoutSubprocess.stdio[1]); expectType(multipleStdoutSubprocess.stderr); expectType(multipleStdoutSubprocess.stdio[2]); expectType(multipleStdoutSubprocess.all); expectError(multipleStdoutSubprocess.stdio[3].destroy()); ================================================ FILE: test-d/subprocess/subprocess.test-d.ts ================================================ import {expectType, expectError, expectAssignable} from 'tsd'; import {execa, type Subprocess} from '../../index.js'; const subprocess = execa('unicorns'); expectAssignable(subprocess); expectType(subprocess.pid); expectType(subprocess.kill()); subprocess.kill('SIGKILL'); subprocess.kill(undefined); subprocess.kill(new Error('test')); subprocess.kill('SIGKILL', new Error('test')); subprocess.kill(undefined, new Error('test')); expectError(subprocess.kill(null)); expectError(subprocess.kill(0n)); expectError(subprocess.kill('Sigkill')); expectError(subprocess.kill('sigkill')); expectError(subprocess.kill('SIGOTHER')); expectError(subprocess.kill('SIGEMT')); expectError(subprocess.kill('SIGCLD')); expectError(subprocess.kill('SIGRT1')); expectError(subprocess.kill([new Error('test')])); expectError(subprocess.kill({message: 'test'})); expectError(subprocess.kill(undefined, {})); expectError(subprocess.kill('SIGKILL', {})); expectError(subprocess.kill(null, new Error('test'))); const ipcSubprocess = execa('unicorns', {ipc: true}); expectAssignable(subprocess); ================================================ FILE: test-d/transform/object-mode.test-d.ts ================================================ import {Duplex} from 'node:stream'; import {TransformStream} from 'node:stream/web'; import {expectType} from 'tsd'; import {execa, type ExecaError} from '../../index.js'; const duplexStream = new Duplex(); const duplex = {transform: duplexStream} as const; const duplexObject = {transform: duplexStream as Duplex & {readonly readableObjectMode: true}} as const; const duplexNotObject = {transform: duplexStream as Duplex & {readonly readableObjectMode: false}} as const; const duplexObjectProperty = {transform: duplexStream, objectMode: true as const} as const; const duplexNotObjectProperty = {transform: duplexStream, objectMode: false as const} as const; const webTransformInstance = new TransformStream(); const webTransform = {transform: webTransformInstance} as const; const webTransformObject = {transform: webTransformInstance, objectMode: true as const} as const; const webTransformNotObject = {transform: webTransformInstance, objectMode: false as const} as const; const objectGenerator = function * (line: unknown) { yield JSON.parse(line as string) as object; }; const objectFinal = function * () { yield {}; }; const objectTransformLinesStdoutResult = await execa('unicorns', {lines: true, stdout: {transform: objectGenerator, final: objectFinal, objectMode: true} as const}); expectType(objectTransformLinesStdoutResult.stdout); expectType<[undefined, unknown[], string[]]>(objectTransformLinesStdoutResult.stdio); const objectWebTransformStdoutResult = await execa('unicorns', {stdout: webTransformObject}); expectType(objectWebTransformStdoutResult.stdout); expectType<[undefined, unknown[], string]>(objectWebTransformStdoutResult.stdio); const objectDuplexStdoutResult = await execa('unicorns', {stdout: duplexObject}); expectType(objectDuplexStdoutResult.stdout); expectType<[undefined, unknown[], string]>(objectDuplexStdoutResult.stdio); const objectDuplexPropertyStdoutResult = await execa('unicorns', {stdout: duplexObjectProperty}); expectType(objectDuplexPropertyStdoutResult.stdout); expectType<[undefined, unknown[], string]>(objectDuplexPropertyStdoutResult.stdio); const objectTransformStdoutResult = await execa('unicorns', {stdout: {transform: objectGenerator, final: objectFinal, objectMode: true} as const}); expectType(objectTransformStdoutResult.stdout); expectType<[undefined, unknown[], string]>(objectTransformStdoutResult.stdio); const objectWebTransformStderrResult = await execa('unicorns', {stderr: webTransformObject}); expectType(objectWebTransformStderrResult.stderr); expectType<[undefined, string, unknown[]]>(objectWebTransformStderrResult.stdio); const objectDuplexStderrResult = await execa('unicorns', {stderr: duplexObject}); expectType(objectDuplexStderrResult.stderr); expectType<[undefined, string, unknown[]]>(objectDuplexStderrResult.stdio); const objectDuplexPropertyStderrResult = await execa('unicorns', {stderr: duplexObjectProperty}); expectType(objectDuplexPropertyStderrResult.stderr); expectType<[undefined, string, unknown[]]>(objectDuplexPropertyStderrResult.stdio); const objectTransformStderrResult = await execa('unicorns', {stderr: {transform: objectGenerator, final: objectFinal, objectMode: true} as const}); expectType(objectTransformStderrResult.stderr); expectType<[undefined, string, unknown[]]>(objectTransformStderrResult.stdio); const objectWebTransformStdioResult = await execa('unicorns', {stdio: ['pipe', 'pipe', webTransformObject]}); expectType(objectWebTransformStdioResult.stderr); expectType<[undefined, string, unknown[]]>(objectWebTransformStdioResult.stdio); const objectDuplexStdioResult = await execa('unicorns', {stdio: ['pipe', 'pipe', duplexObject]}); expectType(objectDuplexStdioResult.stderr); expectType<[undefined, string, unknown[]]>(objectDuplexStdioResult.stdio); const objectDuplexPropertyStdioResult = await execa('unicorns', {stdio: ['pipe', 'pipe', duplexObjectProperty]}); expectType(objectDuplexPropertyStdioResult.stderr); expectType<[undefined, string, unknown[]]>(objectDuplexPropertyStdioResult.stdio); const objectTransformStdioResult = await execa('unicorns', {stdio: ['pipe', 'pipe', {transform: objectGenerator, final: objectFinal, objectMode: true} as const]}); expectType(objectTransformStdioResult.stderr); expectType<[undefined, string, unknown[]]>(objectTransformStdioResult.stdio); const singleObjectWebTransformStdoutResult = await execa('unicorns', {stdout: [webTransformObject]}); expectType(singleObjectWebTransformStdoutResult.stdout); expectType<[undefined, unknown[], string]>(singleObjectWebTransformStdoutResult.stdio); const singleObjectDuplexStdoutResult = await execa('unicorns', {stdout: [duplexObject]}); expectType(singleObjectDuplexStdoutResult.stdout); expectType<[undefined, unknown[], string]>(singleObjectDuplexStdoutResult.stdio); const singleObjectDuplexPropertyStdoutResult = await execa('unicorns', {stdout: [duplexObjectProperty]}); expectType(singleObjectDuplexPropertyStdoutResult.stdout); expectType<[undefined, unknown[], string]>(singleObjectDuplexPropertyStdoutResult.stdio); const singleObjectTransformStdoutResult = await execa('unicorns', {stdout: [{transform: objectGenerator, final: objectFinal, objectMode: true} as const]}); expectType(singleObjectTransformStdoutResult.stdout); expectType<[undefined, unknown[], string]>(singleObjectTransformStdoutResult.stdio); const manyObjectWebTransformStdoutResult = await execa('unicorns', {stdout: [webTransformObject, webTransformObject]}); expectType(manyObjectWebTransformStdoutResult.stdout); expectType<[undefined, unknown[], string]>(manyObjectWebTransformStdoutResult.stdio); const manyObjectDuplexStdoutResult = await execa('unicorns', {stdout: [duplexObject, duplexObject]}); expectType(manyObjectDuplexStdoutResult.stdout); expectType<[undefined, unknown[], string]>(manyObjectDuplexStdoutResult.stdio); const manyObjectDuplexPropertyStdoutResult = await execa('unicorns', {stdout: [duplexObjectProperty, duplexObjectProperty]}); expectType(manyObjectDuplexPropertyStdoutResult.stdout); expectType<[undefined, unknown[], string]>(manyObjectDuplexPropertyStdoutResult.stdio); const manyObjectTransformStdoutResult = await execa('unicorns', {stdout: [{transform: objectGenerator, final: objectFinal, objectMode: true} as const, {transform: objectGenerator, final: objectFinal, objectMode: true} as const]}); expectType(manyObjectTransformStdoutResult.stdout); expectType<[undefined, unknown[], string]>(manyObjectTransformStdoutResult.stdio); const falseObjectWebTransformStdoutResult = await execa('unicorns', {stdout: webTransformNotObject}); expectType(falseObjectWebTransformStdoutResult.stdout); expectType<[undefined, string, string]>(falseObjectWebTransformStdoutResult.stdio); const falseObjectDuplexStdoutResult = await execa('unicorns', {stdout: duplexNotObject}); expectType(falseObjectDuplexStdoutResult.stdout); expectType<[undefined, string, string]>(falseObjectDuplexStdoutResult.stdio); const falseObjectDuplexPropertyStdoutResult = await execa('unicorns', {stdout: duplexNotObjectProperty}); expectType(falseObjectDuplexPropertyStdoutResult.stdout); expectType<[undefined, string, string]>(falseObjectDuplexPropertyStdoutResult.stdio); const falseObjectTransformStdoutResult = await execa('unicorns', {stdout: {transform: objectGenerator, final: objectFinal, objectMode: false} as const}); expectType(falseObjectTransformStdoutResult.stdout); expectType<[undefined, string, string]>(falseObjectTransformStdoutResult.stdio); const falseObjectWebTransformStderrResult = await execa('unicorns', {stderr: webTransformNotObject}); expectType(falseObjectWebTransformStderrResult.stderr); expectType<[undefined, string, string]>(falseObjectWebTransformStderrResult.stdio); const falseObjectDuplexStderrResult = await execa('unicorns', {stderr: duplexNotObject}); expectType(falseObjectDuplexStderrResult.stderr); expectType<[undefined, string, string]>(falseObjectDuplexStderrResult.stdio); const falseObjectDuplexPropertyStderrResult = await execa('unicorns', {stderr: duplexNotObjectProperty}); expectType(falseObjectDuplexPropertyStderrResult.stderr); expectType<[undefined, string, string]>(falseObjectDuplexPropertyStderrResult.stdio); const falseObjectTransformStderrResult = await execa('unicorns', {stderr: {transform: objectGenerator, final: objectFinal, objectMode: false} as const}); expectType(falseObjectTransformStderrResult.stderr); expectType<[undefined, string, string]>(falseObjectTransformStderrResult.stdio); const falseObjectWebTransformStdioResult = await execa('unicorns', {stdio: ['pipe', 'pipe', webTransformNotObject]}); expectType(falseObjectWebTransformStdioResult.stderr); expectType<[undefined, string, string]>(falseObjectWebTransformStdioResult.stdio); const falseObjectDuplexStdioResult = await execa('unicorns', {stdio: ['pipe', 'pipe', duplexNotObject]}); expectType(falseObjectDuplexStdioResult.stderr); expectType<[undefined, string, string]>(falseObjectDuplexStdioResult.stdio); const falseObjectDuplexPropertyStdioResult = await execa('unicorns', {stdio: ['pipe', 'pipe', duplexNotObjectProperty]}); expectType(falseObjectDuplexPropertyStdioResult.stderr); expectType<[undefined, string, string]>(falseObjectDuplexPropertyStdioResult.stdio); const falseObjectTransformStdioResult = await execa('unicorns', {stdio: ['pipe', 'pipe', {transform: objectGenerator, final: objectFinal, objectMode: false} as const]}); expectType(falseObjectTransformStdioResult.stderr); expectType<[undefined, string, string]>(falseObjectTransformStdioResult.stdio); const topObjectWebTransformStdoutResult = await execa('unicorns', {stdout: webTransformInstance}); expectType(topObjectWebTransformStdoutResult.stdout); expectType<[undefined, string, string]>(topObjectWebTransformStdoutResult.stdio); const undefinedObjectWebTransformStdoutResult = await execa('unicorns', {stdout: webTransform}); expectType(undefinedObjectWebTransformStdoutResult.stdout); expectType<[undefined, string, string]>(undefinedObjectWebTransformStdoutResult.stdio); const undefinedObjectDuplexStdoutResult = await execa('unicorns', {stdout: duplex}); expectType(undefinedObjectDuplexStdoutResult.stdout); expectType<[undefined, string | unknown[], string]>(undefinedObjectDuplexStdoutResult.stdio); const undefinedObjectTransformStdoutResult = await execa('unicorns', {stdout: {transform: objectGenerator, final: objectFinal}}); expectType(undefinedObjectTransformStdoutResult.stdout); expectType<[undefined, string, string]>(undefinedObjectTransformStdoutResult.stdio); const noObjectTransformStdoutResult = await execa('unicorns', {stdout: objectGenerator, final: objectFinal}); expectType(noObjectTransformStdoutResult.stdout); expectType<[undefined, string, string]>(noObjectTransformStdoutResult.stdio); const trueTrueObjectTransformResult = await execa('unicorns', {stdout: {transform: objectGenerator, final: objectFinal, objectMode: true} as const, stderr: {transform: objectGenerator, final: objectFinal, objectMode: true} as const, all: true}); expectType(trueTrueObjectTransformResult.stdout); expectType(trueTrueObjectTransformResult.stderr); expectType(trueTrueObjectTransformResult.all); expectType<[undefined, unknown[], unknown[]]>(trueTrueObjectTransformResult.stdio); const trueFalseObjectTransformResult = await execa('unicorns', {stdout: {transform: objectGenerator, final: objectFinal, objectMode: true} as const, stderr: {transform: objectGenerator, final: objectFinal, objectMode: false} as const, all: true}); expectType(trueFalseObjectTransformResult.stdout); expectType(trueFalseObjectTransformResult.stderr); expectType(trueFalseObjectTransformResult.all); expectType<[undefined, unknown[], string]>(trueFalseObjectTransformResult.stdio); const falseTrueObjectTransformResult = await execa('unicorns', {stdout: {transform: objectGenerator, final: objectFinal, objectMode: false} as const, stderr: {transform: objectGenerator, final: objectFinal, objectMode: true} as const, all: true}); expectType(falseTrueObjectTransformResult.stdout); expectType(falseTrueObjectTransformResult.stderr); expectType(falseTrueObjectTransformResult.all); expectType<[undefined, string, unknown[]]>(falseTrueObjectTransformResult.stdio); const falseFalseObjectTransformResult = await execa('unicorns', {stdout: {transform: objectGenerator, final: objectFinal, objectMode: false} as const, stderr: {transform: objectGenerator, final: objectFinal, objectMode: false} as const, all: true}); expectType(falseFalseObjectTransformResult.stdout); expectType(falseFalseObjectTransformResult.stderr); expectType(falseFalseObjectTransformResult.all); expectType<[undefined, string, string]>(falseFalseObjectTransformResult.stdio); const objectTransformStdoutError = new Error('.') as ExecaError<{stdout: {transform: typeof objectGenerator; final: typeof objectFinal; readonly objectMode: true}}>; expectType(objectTransformStdoutError.stdout); expectType<[undefined, unknown[], string]>(objectTransformStdoutError.stdio); const objectTransformStderrError = new Error('.') as ExecaError<{stderr: {transform: typeof objectGenerator; final: typeof objectFinal; readonly objectMode: true}}>; expectType(objectTransformStderrError.stderr); expectType<[undefined, string, unknown[]]>(objectTransformStderrError.stdio); const objectTransformStdioError = new Error('.') as ExecaError<{stdio: ['pipe', 'pipe', {transform: typeof objectGenerator; final: typeof objectFinal; readonly objectMode: true}]}>; expectType(objectTransformStdioError.stderr); expectType<[undefined, string, unknown[]]>(objectTransformStdioError.stdio); const falseObjectTransformStdoutError = new Error('.') as ExecaError<{stdout: {transform: typeof objectGenerator; final: typeof objectFinal; readonly objectMode: false}}>; expectType(falseObjectTransformStdoutError.stdout); expectType<[undefined, string, string]>(falseObjectTransformStdoutError.stdio); const falseObjectTransformStderrError = new Error('.') as ExecaError<{stderr: {transform: typeof objectGenerator; final: typeof objectFinal; readonly objectMode: false}}>; expectType(falseObjectTransformStderrError.stderr); expectType<[undefined, string, string]>(falseObjectTransformStderrError.stdio); const falseObjectTransformStdioError = new Error('.') as ExecaError<{stdio: ['pipe', 'pipe', {transform: typeof objectGenerator; final: typeof objectFinal; readonly objectMode: false}]}>; expectType(falseObjectTransformStdioError.stderr); expectType<[undefined, string, string]>(falseObjectTransformStdioError.stdio); ================================================ FILE: test-d/verbose.test-d.ts ================================================ import { expectType, expectNotType, expectAssignable, expectNotAssignable, expectError, } from 'tsd'; import { execa, execaSync, type VerboseObject, type SyncVerboseObject, type Options, type SyncOptions, type Result, type SyncResult, } from '../index.js'; await execa('unicorns', {verbose: 'none'}); execaSync('unicorns', {verbose: 'none'}); await execa('unicorns', {verbose: 'short'}); execaSync('unicorns', {verbose: 'short'}); await execa('unicorns', {verbose: 'full'}); execaSync('unicorns', {verbose: 'full'}); expectError(await execa('unicorns', {verbose: 'full' as string})); expectError(execaSync('unicorns', {verbose: 'full' as string})); expectError(await execa('unicorns', {verbose: 'other'})); expectError(execaSync('unicorns', {verbose: 'other'})); const voidVerbose = () => { console.log(''); }; await execa('unicorns', {verbose: voidVerbose}); execaSync('unicorns', {verbose: voidVerbose}); await execa('unicorns', {verbose: () => ''}); execaSync('unicorns', {verbose: () => ''}); await execa('unicorns', {verbose: voidVerbose as () => never}); execaSync('unicorns', {verbose: voidVerbose as () => never}); expectError(await execa('unicorns', {verbose: () => true})); expectError(execaSync('unicorns', {verbose: () => true})); expectError(await execa('unicorns', {verbose: () => '' as unknown})); expectError(execaSync('unicorns', {verbose: () => '' as unknown})); await execa('unicorns', {verbose: (verboseLine: string) => ''}); execaSync('unicorns', {verbose: (verboseLine: string) => ''}); await execa('unicorns', {verbose: (verboseLine: unknown) => ''}); execaSync('unicorns', {verbose: (verboseLine: unknown) => ''}); expectError(await execa('unicorns', {verbose: (verboseLine: boolean) => ''})); expectError(execaSync('unicorns', {verbose: (verboseLine: boolean) => ''})); expectError(await execa('unicorns', {verbose: (verboseLine: never) => ''})); expectError(execaSync('unicorns', {verbose: (verboseLine: never) => ''})); await execa('unicorns', {verbose: (verboseLine: string, verboseObject: object) => ''}); execaSync('unicorns', {verbose: (verboseLine: string, verboseObject: object) => ''}); await execa('unicorns', {verbose: (verboseLine: string, verboseObject: VerboseObject) => ''}); execaSync('unicorns', {verbose: (verboseLine: string, verboseObject: VerboseObject) => ''}); await execa('unicorns', {verbose: (verboseLine: string, verboseObject: unknown) => ''}); execaSync('unicorns', {verbose: (verboseLine: string, verboseObject: unknown) => ''}); expectError(await execa('unicorns', {verbose: (verboseLine: string, verboseObject: string) => ''})); expectError(execaSync('unicorns', {verbose: (verboseLine: string, verboseObject: string) => ''})); expectError(await execa('unicorns', {verbose: (verboseLine: string, verboseObject: never) => ''})); expectError(execaSync('unicorns', {verbose: (verboseLine: string, verboseObject: never) => ''})); expectError(await execa('unicorns', {verbose: (verboseLine: string, verboseObject: object, other: string) => ''})); expectError(execaSync('unicorns', {verbose: (verboseLine: string, verboseObject: object, other: string) => ''})); await execa('unicorns', { verbose(verboseLine: string, verboseObject: VerboseObject) { expectNotType(verboseObject.type); expectAssignable(verboseObject.type); expectNotAssignable(verboseObject.type); expectType(verboseObject.message); expectType(verboseObject.escapedCommand); expectType(verboseObject.commandId); expectType(verboseObject.timestamp); expectType(verboseObject.result); expectType(verboseObject.piped); expectType(verboseObject.options); expectError(verboseObject.other); }, }); execaSync('unicorns', { verbose(verboseLine: string, verboseObject: SyncVerboseObject) { expectNotType(verboseObject.type); expectAssignable(verboseObject.type); expectNotAssignable(verboseObject.type); expectType(verboseObject.message); expectType(verboseObject.escapedCommand); expectType(verboseObject.commandId); expectType(verboseObject.timestamp); expectType(verboseObject.result); expectType(verboseObject.piped); expectType(verboseObject.options); expectError(verboseObject.other); }, }); ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "module": "nodenext", "moduleResolution": "nodenext", "target": "ES2022", "strict": true }, "files": [ "index.d.ts" ] } ================================================ FILE: types/arguments/encoding-option.d.ts ================================================ type DefaultEncodingOption = 'utf8'; type TextEncodingOption = | DefaultEncodingOption | 'utf16le'; export type BufferEncodingOption = 'buffer'; export type BinaryEncodingOption = | BufferEncodingOption | 'hex' | 'base64' | 'base64url' | 'latin1' | 'ascii'; // `options.encoding` export type EncodingOption = | TextEncodingOption | BinaryEncodingOption | undefined; export {}; ================================================ FILE: types/arguments/fd-options.d.ts ================================================ type FileDescriptorOption = `fd${number}`; // `from` option of `subprocess.readable|duplex|iterable|pipe()` // Also used by fd-specific options export type FromOption = 'stdout' | 'stderr' | 'all' | FileDescriptorOption; // `to` option of `subprocess.writable|duplex|pipe()` export type ToOption = 'stdin' | FileDescriptorOption; export {}; ================================================ FILE: types/arguments/options.d.ts ================================================ import type {SignalConstants} from 'node:os'; import type {Readable} from 'node:stream'; import type {Unless} from '../utils.js'; import type {Message} from '../ipc.js'; import type {StdinOptionCommon, StdoutStderrOptionCommon, StdioOptionsProperty} from '../stdio/type.js'; import type {VerboseOption} from '../verbose.js'; import type {FdGenericOption} from './specific.js'; import type {EncodingOption} from './encoding-option.js'; export type CommonOptions = { /** Prefer locally installed binaries when looking for a binary to execute. @default `true` with `$`, `false` otherwise */ readonly preferLocal?: boolean; /** Preferred path to find locally installed binaries, when using the `preferLocal` option. @default `cwd` option */ readonly localDir?: string | URL; /** If `true`, runs with Node.js. The first argument must be a Node.js file. The subprocess inherits the current Node.js [CLI flags](https://nodejs.org/api/cli.html#options) and version. This can be overridden using the `nodeOptions` and `nodePath` options. @default `true` with `execaNode()`, `false` otherwise */ readonly node?: boolean; /** List of [CLI flags](https://nodejs.org/api/cli.html#cli_options) passed to the Node.js executable. Requires the `node` option to be `true`. @default [`process.execArgv`](https://nodejs.org/api/process.html#process_process_execargv) (current Node.js CLI flags) */ readonly nodeOptions?: readonly string[]; /** Path to the Node.js executable. Requires the `node` option to be `true`. @default [`process.execPath`](https://nodejs.org/api/process.html#process_process_execpath) (current Node.js executable) */ readonly nodePath?: string | URL; /** If `true`, runs the command inside of a [shell](https://en.wikipedia.org/wiki/Shell_(computing)). Uses [`/bin/sh`](https://en.wikipedia.org/wiki/Unix_shell) on UNIX and [`cmd.exe`](https://en.wikipedia.org/wiki/Cmd.exe) on Windows. A different shell can be specified as a string. The shell should understand the `-c` switch on UNIX or `/d /s /c` on Windows. We recommend against using this option. @default false */ readonly shell?: boolean | string | URL; /** Current [working directory](https://en.wikipedia.org/wiki/Working_directory) of the subprocess. This is also used to resolve the `nodePath` option when it is a relative path. @default process.cwd() */ readonly cwd?: string | URL; /** [Environment variables](https://en.wikipedia.org/wiki/Environment_variable). Unless the `extendEnv` option is `false`, the subprocess also uses the current process' environment variables ([`process.env`](https://nodejs.org/api/process.html#processenv)). @default [process.env](https://nodejs.org/api/process.html#processenv) */ readonly env?: Readonly>>; /** If `true`, the subprocess uses both the `env` option and the current process' environment variables ([`process.env`](https://nodejs.org/api/process.html#processenv)). If `false`, only the `env` option is used, not `process.env`. @default true */ readonly extendEnv?: boolean; /** Write some input to the subprocess' [`stdin`](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). See also the `inputFile` and `stdin` options. */ readonly input?: string | Uint8Array | Readable; /** Use a file as input to the subprocess' [`stdin`](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). See also the `input` and `stdin` options. */ readonly inputFile?: string | URL; /** How to setup the subprocess' [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). This can be `'pipe'`, `'overlapped'`, `'ignore`, `'inherit'`, a file descriptor integer, a Node.js `Readable` stream, a web `ReadableStream`, a `{ file: 'path' }` object, a file URL, an `Iterable`, an `AsyncIterable`, an `Uint8Array`, a generator function, a `Duplex` or a web `TransformStream`. This can be an array of values such as `['inherit', 'pipe']` or `[fileUrl, 'pipe']`. @default `'inherit'` with `$`, `'pipe'` otherwise */ readonly stdin?: StdinOptionCommon; /** How to setup the subprocess' [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). This can be `'pipe'`, `'overlapped'`, `'ignore`, `'inherit'`, a file descriptor integer, a Node.js `Writable` stream, a web `WritableStream`, a `{ file: 'path' }` object, a file URL, a generator function, a `Duplex` or a web `TransformStream`. This can be an array of values such as `['inherit', 'pipe']` or `[fileUrl, 'pipe']`. @default 'pipe' */ readonly stdout?: StdoutStderrOptionCommon; /** How to setup the subprocess' [standard error](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). This can be `'pipe'`, `'overlapped'`, `'ignore`, `'inherit'`, a file descriptor integer, a Node.js `Writable` stream, a web `WritableStream`, a `{ file: 'path' }` object, a file URL, a generator function, a `Duplex` or a web `TransformStream`. This can be an array of values such as `['inherit', 'pipe']` or `[fileUrl, 'pipe']`. @default 'pipe' */ readonly stderr?: StdoutStderrOptionCommon; /** Like the `stdin`, `stdout` and `stderr` options but for all [file descriptors](https://en.wikipedia.org/wiki/File_descriptor) at once. For example, `{stdio: ['ignore', 'pipe', 'pipe']}` is the same as `{stdin: 'ignore', stdout: 'pipe', stderr: 'pipe'}`. A single string can be used as a shortcut. The array can have more than 3 items, to create additional file descriptors beyond `stdin`/`stdout`/`stderr`. @default 'pipe' */ readonly stdio?: StdioOptionsProperty; /** Add a `subprocess.all` stream and a `result.all` property. They contain the combined/interleaved output of the subprocess' `stdout` and `stderr`. @default false */ readonly all?: boolean; /** If the subprocess outputs text, specifies its character encoding, either [`'utf8'`](https://en.wikipedia.org/wiki/UTF-8) or [`'utf16le'`](https://en.wikipedia.org/wiki/UTF-16). If it outputs binary data instead, this should be either: - `'buffer'`: returns the binary output as an `Uint8Array`. - [`'hex'`](https://en.wikipedia.org/wiki/Hexadecimal), [`'base64'`](https://en.wikipedia.org/wiki/Base64), [`'base64url'`](https://en.wikipedia.org/wiki/Base64#URL_applications), [`'latin1'`](https://nodejs.org/api/buffer.html#buffers-and-character-encodings) or [`'ascii'`](https://nodejs.org/api/buffer.html#buffers-and-character-encodings): encodes the binary output as a string. The output is available with `result.stdout`, `result.stderr` and `result.stdio`. @default 'utf8' */ readonly encoding?: EncodingOption; /** Set `result.stdout`, `result.stderr`, `result.all` and `result.stdio` as arrays of strings, splitting the subprocess' output into lines. This cannot be used if the `encoding` option is binary. By default, this applies to both `stdout` and `stderr`, but different values can also be passed. @default false */ readonly lines?: FdGenericOption; /** Strip the final [newline character](https://en.wikipedia.org/wiki/Newline) from the output. If the `lines` option is true, this applies to each output line instead. By default, this applies to both `stdout` and `stderr`, but different values can also be passed. @default true */ readonly stripFinalNewline?: FdGenericOption; /** Largest amount of data allowed on `stdout`, `stderr` and `stdio`. By default, this applies to both `stdout` and `stderr`, but different values can also be passed. When reached, `error.isMaxBuffer` becomes `true`. @default 100_000_000 */ readonly maxBuffer?: FdGenericOption; /** When `buffer` is `false`, the `result.stdout`, `result.stderr`, `result.all` and `result.stdio` properties are not set. By default, this applies to both `stdout` and `stderr`, but different values can also be passed. @default true */ readonly buffer?: FdGenericOption; /** Enables exchanging messages with the subprocess using `subprocess.sendMessage(message)`, `subprocess.getOneMessage()` and `subprocess.getEachMessage()`. The subprocess must be a Node.js file. @default `true` if the `node`, `ipcInput` or `gracefulCancel` option is set, `false` otherwise */ readonly ipc?: Unless; /** Specify the kind of serialization used for sending messages between subprocesses when using the `ipc` option. @default 'advanced' */ readonly serialization?: Unless; /** Sends an IPC message when the subprocess starts. The subprocess must be a Node.js file. The value's type depends on the `serialization` option. */ readonly ipcInput?: Unless; /** If `verbose` is `'short'`, prints the command on [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)): its file, arguments, duration and (if it failed) error message. If `verbose` is `'full'` or a function, the command's [`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)), `stderr` and IPC messages are also printed. A function can be passed to customize logging. By default, this applies to both `stdout` and `stderr`, but different values can also be passed. @default 'none' */ readonly verbose?: VerboseOption; /** Setting this to `false` resolves the result's promise with the error instead of rejecting it. @default true */ readonly reject?: boolean; /** If `timeout` is greater than `0`, the subprocess will be terminated if it runs for longer than that amount of milliseconds. On timeout, `error.timedOut` becomes `true`. @default 0 */ readonly timeout?: number; /** When the `cancelSignal` is [aborted](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort), terminate the subprocess using a `SIGTERM` signal. When aborted, `error.isCanceled` becomes `true`. @example ``` import {execaNode} from 'execa'; const controller = new AbortController(); const cancelSignal = controller.signal; setTimeout(() => { controller.abort(); }, 5000); try { await execaNode({cancelSignal})`build.js`; } catch (error) { if (error.isCanceled) { console.error('Canceled by cancelSignal.'); } throw error; } ``` */ readonly cancelSignal?: Unless; /** When the `cancelSignal` option is [aborted](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort), do not send any `SIGTERM`. Instead, abort the [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) returned by `getCancelSignal()`. The subprocess should use it to terminate gracefully. The subprocess must be a Node.js file. When aborted, `error.isGracefullyCanceled` becomes `true`. @default false */ readonly gracefulCancel?: Unless; /** If the subprocess is terminated but does not exit, forcefully exit it by sending [`SIGKILL`](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGKILL). When this happens, `error.isForcefullyTerminated` becomes `true`. @default 5000 */ readonly forceKillAfterDelay?: Unless; /** Default [signal](https://en.wikipedia.org/wiki/Signal_(IPC)) used to terminate the subprocess. This can be either a name (like `'SIGTERM'`) or a number (like `9`). @default 'SIGTERM' */ readonly killSignal?: keyof SignalConstants | number; /** Run the subprocess independently from the current process. @default false */ readonly detached?: Unless; /** Kill the subprocess when the current process exits. @default true */ readonly cleanup?: Unless; /** Sets the [user identifier](https://en.wikipedia.org/wiki/User_identifier) of the subprocess. @default current user identifier */ readonly uid?: number; /** Sets the [group identifier](https://en.wikipedia.org/wiki/Group_identifier) of the subprocess. @default current group identifier */ readonly gid?: number; /** Value of [`argv[0]`](https://nodejs.org/api/process.html#processargv0) sent to the subprocess. @default file being executed */ readonly argv0?: string; /** On Windows, do not create a new console window. @default true */ readonly windowsHide?: boolean; /** If `false`, escapes the command arguments on Windows. @default `true` if the `shell` option is `true`, `false` otherwise */ readonly windowsVerbatimArguments?: boolean; }; /** Subprocess options. Some options are related to the subprocess output: `verbose`, `lines`, `stripFinalNewline`, `buffer`, `maxBuffer`. By default, those options apply to all file descriptors (`stdout`, `stderr`, etc.). A plain object can be passed instead to apply them to only `stdout`, `stderr`, `all` (both stdout and stderr), `ipc`, `fd3`, etc. @example ``` // Same value for stdout and stderr await execa({verbose: 'full'})`npm run build`; // Different values for stdout and stderr await execa({verbose: {stdout: 'none', stderr: 'full'}})`npm run build`; ``` */ export type Options = CommonOptions; /** Subprocess options, with synchronous methods. Some options are related to the subprocess output: `verbose`, `lines`, `stripFinalNewline`, `buffer`, `maxBuffer`. By default, those options apply to all file descriptors (`stdout`, `stderr`, etc.). A plain object can be passed instead to apply them to only `stdout`, `stderr`, `all` (both stdout and stderr), `ipc`, `fd3`, etc. @example ``` // Same value for stdout and stderr execaSync({verbose: 'full'})`npm run build`; // Different values for stdout and stderr execaSync({verbose: {stdout: 'none', stderr: 'full'}})`npm run build`; ``` */ export type SyncOptions = CommonOptions; export type StricterOptions< WideOptions extends CommonOptions, StrictOptions extends CommonOptions, > = WideOptions extends StrictOptions ? WideOptions : StrictOptions; export {}; ================================================ FILE: types/arguments/specific.d.ts ================================================ import type {FromOption} from './fd-options.js'; // Options which can be fd-specific like `{verbose: {stdout: 'none', stderr: 'full'}}` export type FdGenericOption = OptionType | GenericOptionObject; type GenericOptionObject = { readonly [FdName in GenericFromOption]?: OptionType }; type GenericFromOption = FromOption | 'ipc'; // Retrieve fd-specific option's value export type FdSpecificOption< GenericOption extends FdGenericOption, FdNumber extends string, > = GenericOption extends GenericOptionObject ? FdSpecificObjectOption : GenericOption; type FdSpecificObjectOption< GenericOption extends GenericOptionObject, FdNumber extends string, > = keyof GenericOption extends GenericFromOption ? FdNumberToFromOption extends never ? undefined : GenericOption[FdNumberToFromOption] : GenericOption; type FdNumberToFromOption< FdNumber extends string, GenericOptionKeys extends GenericFromOption, > = FdNumber extends '1' ? 'stdout' extends GenericOptionKeys ? 'stdout' : 'fd1' extends GenericOptionKeys ? 'fd1' : 'all' extends GenericOptionKeys ? 'all' : never : FdNumber extends '2' ? 'stderr' extends GenericOptionKeys ? 'stderr' : 'fd2' extends GenericOptionKeys ? 'fd2' : 'all' extends GenericOptionKeys ? 'all' : never : `fd${FdNumber}` extends GenericOptionKeys ? `fd${FdNumber}` : 'ipc' extends GenericOptionKeys ? 'ipc' : never; export {}; ================================================ FILE: types/convert.d.ts ================================================ import type {BinaryEncodingOption} from './arguments/encoding-option.js'; import type {Options} from './arguments/options.js'; import type {FromOption, ToOption} from './arguments/fd-options.js'; // `subprocess.readable|duplex|iterable()` options export type ReadableOptions = { /** Which stream to read from the subprocess. A [file descriptor](https://en.wikipedia.org/wiki/File_descriptor) like `"fd3"` can also be passed. `"all"` reads both `stdout` and `stderr`. This requires the `all` option to be `true`. @default 'stdout' */ readonly from?: FromOption; /** If `false`, iterates over lines. Each line is a string. If `true`, iterates over arbitrary chunks of data. Each line is an [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) (with `subprocess.iterable()`) or a [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer) (with `subprocess.readable()`/`subprocess.duplex()`). This is always `true` when the `encoding` option is binary. @default `false` with `subprocess.iterable()`, `true` otherwise */ readonly binary?: boolean; /** If both this option and the `binary` option is `false`, [newlines](https://en.wikipedia.org/wiki/Newline) are stripped from each line. @default `false` with `subprocess.iterable()`, `true` otherwise */ readonly preserveNewlines?: boolean; }; // `subprocess.writable|duplex()` options export type WritableOptions = { /** Which stream to write to the subprocess. A [file descriptor](https://en.wikipedia.org/wiki/File_descriptor) like `"fd3"` can also be passed. @default 'stdin' */ readonly to?: ToOption; }; // `subprocess.duplex()` options export type DuplexOptions = ReadableOptions & WritableOptions; // `subprocess.iterable()` return value export type SubprocessAsyncIterable< BinaryOption extends boolean | undefined, EncodingOption extends Options['encoding'], > = AsyncIterableIterator< EncodingOption extends BinaryEncodingOption ? Uint8Array : BinaryOption extends true ? Uint8Array : string >; export {}; ================================================ FILE: types/ipc.d.ts ================================================ import type {Options} from './arguments/options.js'; // Message when the `serialization` option is `'advanced'` type AdvancedMessage = | string | number | boolean | null | object; // Message when the `serialization` option is `'json'` type JsonMessage = | string | number | boolean | null | readonly JsonMessage[] | {readonly [key: string | number]: JsonMessage}; /** Type of messages exchanged between a process and its subprocess using `sendMessage()`, `getOneMessage()` and `getEachMessage()`. This requires the `ipc` option to be `true`. The type of `message` depends on the `serialization` option. */ export type Message< Serialization extends Options['serialization'] = Options['serialization'], > = Serialization extends 'json' ? JsonMessage : AdvancedMessage; /** Options to `sendMessage()` and `subprocess.sendMessage()` */ type SendMessageOptions = { /** Throw when the other process is not receiving or listening to messages. @default false */ readonly strict?: boolean; }; // IPC methods in subprocess /** Send a `message` to the parent process. This requires the `ipc` option to be `true`. The type of `message` depends on the `serialization` option. */ export function sendMessage(message: Message, sendMessageOptions?: SendMessageOptions): Promise; /** Options to `getOneMessage()` and `subprocess.getOneMessage()` */ type GetOneMessageOptions< Serialization extends Options['serialization'], > = { /** Ignore any `message` that returns `false`. */ readonly filter?: (message: Message) => boolean; /** Keep the subprocess alive while `getOneMessage()` is waiting. @default true */ readonly reference?: boolean; }; /** Receive a single `message` from the parent process. This requires the `ipc` option to be `true`. The type of `message` depends on the `serialization` option. */ export function getOneMessage(getOneMessageOptions?: GetOneMessageOptions): Promise; /** Options to `getEachMessage()` and `subprocess.getEachMessage()` */ type GetEachMessageOptions = { /** Keep the subprocess alive while `getEachMessage()` is waiting. @default true */ readonly reference?: boolean; }; /** Iterate over each `message` from the parent process. This requires the `ipc` option to be `true`. The type of `message` depends on the `serialization` option. */ export function getEachMessage(getEachMessageOptions?: GetEachMessageOptions): AsyncIterableIterator; /** Retrieves the [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) shared by the `cancelSignal` option. This can only be called inside a subprocess. This requires the `gracefulCancel` option to be `true`. */ export function getCancelSignal(): Promise; // IPC methods in the subprocess export type IpcMethods< IpcEnabled extends boolean, Serialization extends Options['serialization'], > = IpcEnabled extends true ? { /** Send a `message` to the subprocess. This requires the `ipc` option to be `true`. The type of `message` depends on the `serialization` option. */ sendMessage(message: Message, sendMessageOptions?: SendMessageOptions): Promise; /** Receive a single `message` from the subprocess. This requires the `ipc` option to be `true`. The type of `message` depends on the `serialization` option. */ getOneMessage(getOneMessageOptions?: GetOneMessageOptions): Promise>; /** Iterate over each `message` from the subprocess. This requires the `ipc` option to be `true`. The type of `message` depends on the `serialization` option. */ getEachMessage(getEachMessageOptions?: GetEachMessageOptions): AsyncIterableIterator>; } // Those methods only work if the `ipc` option is `true`. // At runtime, they are actually defined, in order to provide with a nice error message. // At type check time, they are typed as `undefined` to prevent calling them. : { sendMessage: undefined; getOneMessage: undefined; getEachMessage: undefined; }; // Whether IPC is enabled, based on the `ipc`, `ipcInput` and `gracefulCancel` options export type HasIpc = HasIpcOption< OptionsType['ipc'], 'ipcInput' extends keyof OptionsType ? OptionsType['ipcInput'] : undefined, 'gracefulCancel' extends keyof OptionsType ? OptionsType['gracefulCancel'] : undefined >; type HasIpcOption< IpcOption extends Options['ipc'], IpcInputOption extends Options['ipcInput'], GracefulCancelOption extends Options['gracefulCancel'], > = IpcOption extends true ? true : IpcOption extends false ? false : IpcInputOption extends undefined ? GracefulCancelOption extends true ? true : false : true; export {}; ================================================ FILE: types/methods/command.d.ts ================================================ import type {Options, SyncOptions} from '../arguments/options.js'; import type {SyncResult} from '../return/result.js'; import type {ResultPromise} from '../subprocess/subprocess.js'; import type {SimpleTemplateString} from './template.js'; /** Executes a command. `command` is a string that includes both the `file` and its `arguments`. When `command` is a template string, it includes both the `file` and its `arguments`. `execaCommand(options)` can be used to return a new instance of this method but with different default `options`. Consecutive calls are merged to previous ones. This is only intended for very specific cases, such as a REPL. This should be avoided otherwise. @param command - The program/script to execute and its arguments. @returns A `ResultPromise` that is both: - the subprocess. - a `Promise` either resolving with its successful `result`, or rejecting with its `error`. @throws `ExecaError` @example ``` import {execaCommand} from 'execa'; for await (const commandAndArguments of getReplLine()) { await execaCommand(commandAndArguments); } ``` */ export declare const execaCommand: ExecaCommandMethod<{}>; type ExecaCommandMethod = & ExecaCommandBind & ExecaCommandTemplate & ExecaCommandArray; // `execaCommand(options)` binding type ExecaCommandBind = (options: NewOptionsType) => ExecaCommandMethod; // `execaCommand`command`` template syntax type ExecaCommandTemplate = (...templateString: SimpleTemplateString) => ResultPromise; // `execaCommand('command', {})` array syntax type ExecaCommandArray = (command: string, options?: NewOptionsType) => ResultPromise; /** Same as `execaCommand()` but synchronous. When `command` is a template string, it includes both the `file` and its `arguments`. `execaCommandSync(options)` can be used to return a new instance of this method but with different default `options`. Consecutive calls are merged to previous ones. Returns a subprocess `result` or throws an `error`. The `subprocess` is not returned: its methods and properties are not available. @param command - The program/script to execute and its arguments. @returns `SyncResult` @throws `ExecaSyncError` @example ``` import {execaCommandSync} from 'execa'; for (const commandAndArguments of getReplLine()) { execaCommandSync(commandAndArguments); } ``` */ export declare const execaCommandSync: ExecaCommandSyncMethod<{}>; type ExecaCommandSyncMethod = & ExecaCommandSyncBind & ExecaCommandSyncTemplate & ExecaCommandSyncArray; // `execaCommandSync(options)` binding type ExecaCommandSyncBind = (options: NewOptionsType) => ExecaCommandSyncMethod; // `execaCommandSync`command`` template syntax type ExecaCommandSyncTemplate = (...templateString: SimpleTemplateString) => SyncResult; // `execaCommandSync('command', {})` array syntax type ExecaCommandSyncArray = (command: string, options?: NewOptionsType) => SyncResult; /** Split a `command` string into an array. For example, `'npm run build'` returns `['npm', 'run', 'build']` and `'argument otherArgument'` returns `['argument', 'otherArgument']`. @param command - The file to execute and/or its arguments. @returns fileOrArgument[] @example ``` import {execa, parseCommandString} from 'execa'; const commandString = 'npm run task'; const commandArray = parseCommandString(commandString); await execa`${commandArray}`; const [file, ...commandArguments] = commandArray; await execa(file, commandArguments); ``` */ export function parseCommandString(command: string): string[]; export {}; ================================================ FILE: types/methods/main-async.d.ts ================================================ import type {Options} from '../arguments/options.js'; import type {ResultPromise} from '../subprocess/subprocess.js'; import type {TemplateString} from './template.js'; /** Executes a command using `file ...arguments`. When `command` is a template string, it includes both the `file` and its `arguments`. `execa(options)` can be used to return a new instance of this method but with different default `options`. Consecutive calls are merged to previous ones. @param file - The program/script to execute, as a string or file URL @param arguments - Arguments to pass to `file` on execution. @returns A `ResultPromise` that is both: - the subprocess. - a `Promise` either resolving with its successful `result`, or rejecting with its `error`. @throws `ExecaError` @example Simple syntax ``` import {execa} from 'execa'; const {stdout} = await execa`npm run build`; // Print command's output console.log(stdout); ``` @example Script ``` import {$} from 'execa'; const {stdout: name} = await $`cat package.json`.pipe`grep name`; console.log(name); const branch = await $`git branch --show-current`; await $`dep deploy --branch=${branch}`; await Promise.all([ $`sleep 1`, $`sleep 2`, $`sleep 3`, ]); const directoryName = 'foo bar'; await $`mkdir /tmp/${directoryName}`; ``` @example Local binaries ``` $ npm install -D eslint ``` ``` await execa({preferLocal: true})`eslint`; ``` @example Pipe multiple subprocesses ``` const {stdout, pipedFrom} = await execa`npm run build` .pipe`sort` .pipe`head -n 2`; // Output of `npm run build | sort | head -n 2` console.log(stdout); // Output of `npm run build | sort` console.log(pipedFrom[0].stdout); // Output of `npm run build` console.log(pipedFrom[0].pipedFrom[0].stdout); ``` @example Interleaved output ``` const {all} = await execa({all: true})`npm run build`; // stdout + stderr, interleaved console.log(all); ``` @example Programmatic + terminal output ``` const {stdout} = await execa({stdout: ['pipe', 'inherit']})`npm run build`; // stdout is also printed to the terminal console.log(stdout); ``` @example Simple input ``` const getInputString = () => { /* ... *\/ }; const {stdout} = await execa({input: getInputString()})`sort`; console.log(stdout); ``` @example File input ``` // Similar to: npm run build < input.txt await execa({stdin: {file: 'input.txt'}})`npm run build`; ``` @example File output ``` // Similar to: npm run build > output.txt await execa({stdout: {file: 'output.txt'}})`npm run build`; ``` @example Split into text lines ``` const {stdout} = await execa({lines: true})`npm run build`; // Print first 10 lines console.log(stdout.slice(0, 10).join('\n')); ``` @example Iterate over text lines ``` for await (const line of execa`npm run build`) { if (line.includes('WARN')) { console.warn(line); } } ``` @example Transform/filter output ``` let count = 0; // Filter out secret lines, then prepend the line number const transform = function * (line) { if (!line.includes('secret')) { yield `[${count++}] ${line}`; } }; await execa({stdout: transform})`npm run build`; ``` @example Web streams ``` const response = await fetch('https://example.com'); await execa({stdin: response.body})`sort`; ``` @example Convert to Duplex stream ``` import {execa} from 'execa'; import {pipeline} from 'node:stream/promises'; import {createReadStream, createWriteStream} from 'node:fs'; await pipeline( createReadStream('./input.txt'), execa`node ./transform.js`.duplex(), createWriteStream('./output.txt'), ); ``` @example Exchange messages ``` // parent.js import {execaNode} from 'execa'; const subprocess = execaNode`child.js`; await subprocess.sendMessage('Hello from parent'); const message = await subprocess.getOneMessage(); console.log(message); // 'Hello from child' ``` ``` // child.js import {getOneMessage, sendMessage} from 'execa'; const message = await getOneMessage(); // 'Hello from parent' const newMessage = message.replace('parent', 'child'); // 'Hello from child' await sendMessage(newMessage); ``` @example Any input type ``` // main.js import {execaNode} from 'execa'; const ipcInput = [ {task: 'lint', ignore: /test\.js/}, {task: 'copy', files: new Set(['main.js', 'index.js']), }]; await execaNode({ipcInput})`build.js`; ``` ``` // build.js import {getOneMessage} from 'execa'; const ipcInput = await getOneMessage(); ``` @example Any output type ``` // main.js import {execaNode} from 'execa'; const {ipcOutput} = await execaNode`build.js`; console.log(ipcOutput[0]); // {kind: 'start', timestamp: date} console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date} ``` ``` // build.js import {sendMessage} from 'execa'; const runBuild = () => { /* ... *\/ }; await sendMessage({kind: 'start', timestamp: new Date()}); await runBuild(); await sendMessage({kind: 'stop', timestamp: new Date()}); ``` @example Graceful termination ``` // main.js import {execaNode} from 'execa'; const controller = new AbortController(); setTimeout(() => { controller.abort(); }, 5000); await execaNode({ cancelSignal: controller.signal, gracefulCancel: true, })`build.js`; ``` ``` // build.js import {getCancelSignal} from 'execa'; const cancelSignal = await getCancelSignal(); const url = 'https://example.com/build/info'; const response = await fetch(url, {signal: cancelSignal}); ``` @example Detailed error ``` import {execa, ExecaError} from 'execa'; try { await execa`unknown command`; } catch (error) { if (error instanceof ExecaError) { console.log(error); } /* ExecaError: Command failed with ENOENT: unknown command spawn unknown ENOENT at ... at ... { shortMessage: 'Command failed with ENOENT: unknown command\nspawn unknown ENOENT', originalMessage: 'spawn unknown ENOENT', command: 'unknown command', escapedCommand: 'unknown command', cwd: '/path/to/cwd', durationMs: 28.217566, failed: true, timedOut: false, isCanceled: false, isTerminated: false, isMaxBuffer: false, code: 'ENOENT', stdout: '', stderr: '', stdio: [undefined, '', ''], pipedFrom: [] [cause]: Error: spawn unknown ENOENT at ... at ... { errno: -2, code: 'ENOENT', syscall: 'spawn unknown', path: 'unknown', spawnargs: [ 'command' ] } } *\/ } ``` @example Verbose mode ``` await execa`npm run build`; await execa`npm run test`; ``` ``` $ NODE_DEBUG=execa node build.js [00:57:44.581] [0] $ npm run build [00:57:44.653] [0] Building application... [00:57:44.653] [0] Done building. [00:57:44.658] [0] ✔ (done in 78ms) [00:57:44.658] [1] $ npm run test [00:57:44.740] [1] Running tests... [00:57:44.740] [1] Error: the entrypoint is invalid. [00:57:44.747] [1] ✘ Command failed with exit code 1: npm run test [00:57:44.747] [1] ✘ (done in 89ms) ``` @example Custom logging ``` import {execa as execa_} from 'execa'; import {createLogger, transports} from 'winston'; // Log to a file using Winston const transport = new transports.File({filename: 'logs.txt'}); const logger = createLogger({transports: [transport]}); const LOG_LEVELS = { command: 'info', output: 'verbose', ipc: 'verbose', error: 'error', duration: 'info', }; const execa = execa_({ verbose(verboseLine, {message, ...verboseObject}) { const level = LOG_LEVELS[verboseObject.type]; logger[level](message, verboseObject); }, }); await execa`npm run build`; await execa`npm run test`; ``` */ export declare const execa: ExecaMethod<{}>; /** `execa()` method either exported by Execa, or bound using `execa(options)`. */ export type ExecaMethod = & ExecaBind & ExecaTemplate & ExecaArrayLong & ExecaArrayShort; // `execa(options)` binding type ExecaBind = (options: NewOptionsType) => ExecaMethod; // `execa`command`` template syntax type ExecaTemplate = (...templateString: TemplateString) => ResultPromise; // `execa('file', ['argument'], {})` array syntax type ExecaArrayLong = (file: string | URL, arguments?: readonly string[], options?: NewOptionsType) => ResultPromise; // `execa('file', {})` array syntax type ExecaArrayShort = (file: string | URL, options?: NewOptionsType) => ResultPromise; export {}; ================================================ FILE: types/methods/main-sync.d.ts ================================================ import type {SyncOptions} from '../arguments/options.js'; import type {SyncResult} from '../return/result.js'; import type {TemplateString} from './template.js'; /** Same as `execa()` but synchronous. Returns a subprocess `result` or throws an `error`. The `subprocess` is not returned: its methods and properties are not available. When `command` is a template string, it includes both the `file` and its `arguments`. `execaSync(options)` can be used to return a new instance of this method but with different default `options`. Consecutive calls are merged to previous ones. This method is discouraged as it holds the CPU and lacks multiple features. @param file - The program/script to execute, as a string or file URL @param arguments - Arguments to pass to `file` on execution. @returns `SyncResult` @throws `ExecaSyncError` @example ``` import {execaSync} from 'execa'; const {stdout} = execaSync`npm run build`; // Print command's output console.log(stdout); ``` */ export declare const execaSync: ExecaSyncMethod<{}>; // For the moment, we purposely do not export `ExecaSyncMethod` and `ExecaScriptSyncMethod`. // This is because synchronous invocation is discouraged. export type ExecaSyncMethod = & ExecaSyncBind & ExecaSyncTemplate & ExecaSyncArrayLong & ExecaSyncArrayShort; // `execaSync(options)` binding type ExecaSyncBind = (options: NewOptionsType) => ExecaSyncMethod; // `execaSync`command`` template syntax type ExecaSyncTemplate = (...templateString: TemplateString) => SyncResult; // `execaSync('file', ['argument'], {})` array syntax type ExecaSyncArrayLong = (file: string | URL, arguments?: readonly string[], options?: NewOptionsType) => SyncResult; // `execaSync('file', {})` array syntax type ExecaSyncArrayShort = (file: string | URL, options?: NewOptionsType) => SyncResult; export {}; ================================================ FILE: types/methods/node.d.ts ================================================ import type {Options} from '../arguments/options.js'; import type {ResultPromise} from '../subprocess/subprocess.js'; import type {TemplateString} from './template.js'; /** Same as `execa()` but using the `node: true` option. Executes a Node.js file using `node scriptPath ...arguments`. When `command` is a template string, it includes both the `file` and its `arguments`. `execaNode(options)` can be used to return a new instance of this method but with different default `options`. Consecutive calls are merged to previous ones. This is the preferred method when executing Node.js files. @param scriptPath - Node.js script to execute, as a string or file URL @param arguments - Arguments to pass to `scriptPath` on execution. @returns A `ResultPromise` that is both: - the subprocess. - a `Promise` either resolving with its successful `result`, or rejecting with its `error`. @throws `ExecaError` @example ``` import {execaNode, execa} from 'execa'; await execaNode`file.js argument`; // Is the same as: await execa({node: true})`file.js argument`; // Or: await execa`node file.js argument`; ``` */ export declare const execaNode: ExecaNodeMethod<{}>; /** `execaNode()` method either exported by Execa, or bound using `execaNode(options)`. */ export type ExecaNodeMethod = & ExecaNodeBind & ExecaNodeTemplate & ExecaNodeArrayLong & ExecaNodeArrayShort; // `execaNode(options)` binding type ExecaNodeBind = (options: NewOptionsType) => ExecaNodeMethod; // `execaNode`command`` template syntax type ExecaNodeTemplate = (...templateString: TemplateString) => ResultPromise; // `execaNode('script', ['argument'], {})` array syntax type ExecaNodeArrayLong = (scriptPath: string | URL, arguments?: readonly string[], options?: NewOptionsType) => ResultPromise; // `execaNode('script', {})` array syntax type ExecaNodeArrayShort = (scriptPath: string | URL, options?: NewOptionsType) => ResultPromise; export {}; ================================================ FILE: types/methods/script.d.ts ================================================ import type { CommonOptions, Options, SyncOptions, StricterOptions, } from '../arguments/options.js'; import type {SyncResult} from '../return/result.js'; import type {ResultPromise} from '../subprocess/subprocess.js'; import type {TemplateString} from './template.js'; /** Same as `execa()` but using script-friendly default options. When `command` is a template string, it includes both the `file` and its `arguments`. `$(options)` can be used to return a new instance of this method but with different default `options`. Consecutive calls are merged to previous ones. This is the preferred method when executing multiple commands in a script file. @returns A `ResultPromise` that is both: - the subprocess. - a `Promise` either resolving with its successful `result`, or rejecting with its `error`. @throws `ExecaError` @example Basic ``` import {$} from 'execa'; const branch = await $`git branch --show-current`; await $`dep deploy --branch=${branch}`; ``` @example Verbose mode ``` $ node build.js Building application... Done building. Running tests... Error: the entrypoint is invalid. $ NODE_DEBUG=execa node build.js [00:57:44.581] [0] $ npm run build [00:57:44.653] [0] Building application... [00:57:44.653] [0] Done building. [00:57:44.658] [0] ✔ (done in 78ms) [00:57:44.658] [1] $ npm run test [00:57:44.740] [1] Running tests... [00:57:44.740] [1] Error: the entrypoint is invalid. [00:57:44.747] [1] ✘ Command failed with exit code 1: npm run test [00:57:44.747] [1] ✘ (done in 89ms) ``` */ export const $: ExecaScriptMethod<{}>; /** `$()` method either exported by Execa, or bound using `$(options)`. */ export type ExecaScriptMethod = & ExecaScriptBind & ExecaScriptTemplate & ExecaScriptArrayLong & ExecaScriptArrayShort & {sync: ExecaScriptSyncMethod} & {s: ExecaScriptSyncMethod}; // `$(options)` binding type ExecaScriptBind = (options: NewOptionsType) => ExecaScriptMethod; // `$`command`` template syntax type ExecaScriptTemplate = (...templateString: TemplateString) => ResultPromise>; // `$('file', ['arg'], {})` array syntax type ExecaScriptArrayLong = (file: string | URL, arguments?: readonly string[], options?: NewOptionsType) => ResultPromise>; // `$('file', {})` array syntax type ExecaScriptArrayShort = (file: string | URL, options?: NewOptionsType) => ResultPromise>; // We must intersect the overloaded methods with & instead of using a simple object as a workaround for a TypeScript bug // See https://github.com/microsoft/TypeScript/issues/58765 /** `$.sync()` method either exported by Execa, or bound using `$.sync(options)`. */ export type ExecaScriptSyncMethod = & ExecaScriptSyncBind & ExecaScriptSyncTemplate & ExecaScriptSyncArrayLong & ExecaScriptSyncArrayShort; // `$.sync(options)` binding type ExecaScriptSyncBind = (options: NewOptionsType) => ExecaScriptSyncMethod; // $.sync`command` template syntax type ExecaScriptSyncTemplate = (...templateString: TemplateString) => SyncResult>; // `$.sync('file', ['arg'], {})` array syntax type ExecaScriptSyncArrayLong = (file: string | URL, arguments?: readonly string[], options?: NewOptionsType) => SyncResult>; // `$.sync('file', {})` array syntax type ExecaScriptSyncArrayShort = (file: string | URL, options?: NewOptionsType) => SyncResult>; export {}; ================================================ FILE: types/methods/template.d.ts ================================================ import type {Result, SyncResult} from '../return/result.js'; type TemplateExpressionItem = | string | number | Result | SyncResult; /** Value allowed inside `${...}` when using the template string syntax. */ export type TemplateExpression = TemplateExpressionItem | readonly TemplateExpressionItem[]; // `...${...}...` template syntax export type TemplateString = readonly [TemplateStringsArray, ...readonly TemplateExpression[]]; // `...${...}...` template syntax, but only allowing a single argument, for `execaCommand()` export type SimpleTemplateString = readonly [TemplateStringsArray, string?]; export {}; ================================================ FILE: types/pipe.d.ts ================================================ import type {Options} from './arguments/options.js'; import type {Result} from './return/result.js'; import type {FromOption, ToOption} from './arguments/fd-options.js'; import type {ResultPromise} from './subprocess/subprocess.js'; import type {TemplateExpression} from './methods/template.js'; // `subprocess.pipe()` options type PipeOptions = { /** Which stream to pipe from the source subprocess. A [file descriptor](https://en.wikipedia.org/wiki/File_descriptor) like `"fd3"` can also be passed. `"all"` pipes both `stdout` and `stderr`. This requires the `all` option to be `true`. */ readonly from?: FromOption; /** Which stream to pipe to the destination subprocess. A [file descriptor](https://en.wikipedia.org/wiki/File_descriptor) like `"fd3"` can also be passed. */ readonly to?: ToOption; /** Unpipe the subprocess when the signal aborts. */ readonly unpipeSignal?: AbortSignal; }; // `subprocess.pipe()` export type PipableSubprocess = { /** [Pipe](https://nodejs.org/api/stream.html#readablepipedestination-options) the subprocess' `stdout` to a second Execa subprocess' `stdin`. This resolves with that second subprocess' result. If either subprocess is rejected, this is rejected with that subprocess' error instead. This follows the same syntax as `execa(file, arguments?, options?)` except both regular options and pipe-specific options can be specified. */ pipe( file: string | URL, arguments?: readonly string[], options?: OptionsType, ): Promise> & PipableSubprocess; pipe( file: string | URL, options?: OptionsType, ): Promise> & PipableSubprocess; /** Like `subprocess.pipe(file, arguments?, options?)` but using a `command` template string instead. This follows the same syntax as `$`. */ pipe(templates: TemplateStringsArray, ...expressions: readonly TemplateExpression[]): Promise> & PipableSubprocess; pipe(options: OptionsType): (templates: TemplateStringsArray, ...expressions: readonly TemplateExpression[]) => Promise> & PipableSubprocess; /** Like `subprocess.pipe(file, arguments?, options?)` but using the return value of another `execa()` call instead. */ pipe(destination: Destination, options?: PipeOptions): Promise> & PipableSubprocess; }; export {}; ================================================ FILE: types/return/final-error.d.ts ================================================ import type {CommonOptions, Options, SyncOptions} from '../arguments/options.js'; import {CommonResult} from './result.js'; declare abstract class CommonError< IsSync extends boolean, OptionsType extends CommonOptions, > extends CommonResult { message: CommonErrorProperty; shortMessage: CommonErrorProperty; originalMessage: CommonErrorProperty; readonly name: CommonErrorProperty; stack: CommonErrorProperty; } type CommonErrorProperty< IsSync extends boolean, OptionsType extends CommonOptions, PropertyName extends keyof CommonResult, > = NonNullable[PropertyName]>; // `result.*` defined only on failure, i.e. on `error.*` export type ErrorProperties = | 'name' | 'message' | 'stack' | 'cause' | 'shortMessage' | 'originalMessage' | 'code'; /** Result of a subprocess failed execution. This error is thrown as an exception. If the `reject` option is false, it is returned instead. This has the same shape as successful results, with a few additional properties. */ export class ExecaError extends CommonError { readonly name: 'ExecaError'; } /** Result of a subprocess failed execution. This error is thrown as an exception. If the `reject` option is false, it is returned instead. This has the same shape as successful results, with a few additional properties. */ export class ExecaSyncError extends CommonError { readonly name: 'ExecaSyncError'; } export {}; ================================================ FILE: types/return/ignore.d.ts ================================================ import type {NoStreamStdioOption} from '../stdio/type.js'; import type {IsInputFd} from '../stdio/direction.js'; import type {FdStdioOption} from '../stdio/option.js'; import type {FdSpecificOption} from '../arguments/specific.js'; import type {CommonOptions} from '../arguments/options.js'; // Whether `result.stdin|stdout|stderr|all|stdio[*]` is `undefined` export type IgnoresResultOutput< FdNumber extends string, OptionsType extends CommonOptions, > = FdSpecificOption extends false ? true : IsInputFd extends true ? true : IgnoresSubprocessOutput; // Whether `subprocess.stdout|stderr|all` is `undefined|null` export type IgnoresSubprocessOutput< FdNumber extends string, OptionsType extends CommonOptions, > = IgnoresOutput>; type IgnoresOutput< FdNumber extends string, StdioOptionType, > = StdioOptionType extends NoStreamStdioOption ? true : false; export {}; ================================================ FILE: types/return/result-all.d.ts ================================================ import type {IsObjectFd} from '../transform/object-mode.js'; import type {CommonOptions} from '../arguments/options.js'; import type {FdSpecificOption} from '../arguments/specific.js'; import type {IgnoresResultOutput} from './ignore.js'; import type {ResultStdio} from './result-stdout.js'; // `result.all` export type ResultAll = ResultAllProperty; type ResultAllProperty< AllOption extends CommonOptions['all'], OptionsType extends CommonOptions, > = AllOption extends true ? ResultStdio< AllMainFd, AllObjectFd, AllLinesFd, OptionsType > : undefined; type AllMainFd = IgnoresResultOutput<'1', OptionsType> extends true ? '2' : '1'; type AllObjectFd = IsObjectFd<'1', OptionsType> extends true ? '1' : '2'; type AllLinesFd = FdSpecificOption extends true ? '1' : '2'; export {}; ================================================ FILE: types/return/result-ipc.d.ts ================================================ import type {FdSpecificOption} from '../arguments/specific.js'; import type {CommonOptions, Options, StricterOptions} from '../arguments/options.js'; import type {Message, HasIpc} from '../ipc.js'; // `result.ipcOutput` // This is empty unless the `ipc` option is `true`. // Also, this is empty if the `buffer` option is `false`. export type ResultIpcOutput< IsSync, OptionsType extends CommonOptions, > = IsSync extends true ? [] : ResultIpcAsync< FdSpecificOption, HasIpc>, OptionsType['serialization'] >; type ResultIpcAsync< BufferOption extends boolean | undefined, IpcEnabled extends boolean, SerializationOption extends CommonOptions['serialization'], > = BufferOption extends false ? [] : IpcEnabled extends true ? Array> : []; export {}; ================================================ FILE: types/return/result-stdio.d.ts ================================================ import type {StdioOptionNormalizedArray} from '../stdio/array.js'; import type {CommonOptions} from '../arguments/options.js'; import type {ResultStdioNotAll} from './result-stdout.js'; // `result.stdio` export type ResultStdioArray = MapResultStdio, OptionsType>; type MapResultStdio< StdioOptionsArrayType, OptionsType extends CommonOptions, > = { -readonly [FdNumber in keyof StdioOptionsArrayType]: ResultStdioNotAll< FdNumber extends string ? FdNumber : string, OptionsType > }; export {}; ================================================ FILE: types/return/result-stdout.d.ts ================================================ import type {BufferEncodingOption, BinaryEncodingOption} from '../arguments/encoding-option.js'; import type {IsObjectFd} from '../transform/object-mode.js'; import type {FdSpecificOption} from '../arguments/specific.js'; import type {CommonOptions} from '../arguments/options.js'; import type {IgnoresResultOutput} from './ignore.js'; // `result.stdout|stderr|stdio` export type ResultStdioNotAll< FdNumber extends string, OptionsType extends CommonOptions, > = ResultStdio; // `result.stdout|stderr|stdio|all` export type ResultStdio< MainFdNumber extends string, ObjectFdNumber extends string, LinesFdNumber extends string, OptionsType extends CommonOptions, > = ResultStdioProperty< ObjectFdNumber, LinesFdNumber, IgnoresResultOutput, OptionsType >; type ResultStdioProperty< ObjectFdNumber extends string, LinesFdNumber extends string, StreamOutputIgnored, OptionsType extends CommonOptions, > = StreamOutputIgnored extends true ? undefined : ResultStdioItem< IsObjectFd, FdSpecificOption, OptionsType['encoding'] >; type ResultStdioItem< IsObjectResult, LinesOption extends boolean | undefined, Encoding extends CommonOptions['encoding'], > = IsObjectResult extends true ? unknown[] : Encoding extends BufferEncodingOption ? Uint8Array : LinesOption extends true ? Encoding extends BinaryEncodingOption ? string : string[] : string; export {}; ================================================ FILE: types/return/result.d.ts ================================================ import type {SignalConstants} from 'node:os'; import type {Unless} from '../utils.js'; import type {CommonOptions, Options, SyncOptions} from '../arguments/options.js'; import type {ErrorProperties} from './final-error.js'; import type {ResultAll} from './result-all.js'; import type {ResultStdioArray} from './result-stdio.js'; import type {ResultStdioNotAll} from './result-stdout.js'; import type {ResultIpcOutput} from './result-ipc.js'; export declare abstract class CommonResult< IsSync extends boolean, OptionsType extends CommonOptions, > { /** The output of the subprocess on [`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)). This is `undefined` if the `stdout` option is set to only `'inherit'`, `'ignore'`, `Writable` or `integer`, or if the `buffer` option is `false`. This is an array if the `lines` option is `true`, or if the `stdout` option is a transform in object mode. */ stdout: ResultStdioNotAll<'1', OptionsType>; /** The output of the subprocess on [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). This is `undefined` if the `stderr` option is set to only `'inherit'`, `'ignore'`, `Writable` or `integer`, or if the `buffer` option is `false`. This is an array if the `lines` option is `true`, or if the `stderr` option is a transform in object mode. */ stderr: ResultStdioNotAll<'2', OptionsType>; /** The output of the subprocess with `result.stdout` and `result.stderr` interleaved. This requires the `all` option to be `true`. This is `undefined` if both `stdout` and `stderr` options are set to only `'inherit'`, `'ignore'`, `Writable` or `integer`, or if the `buffer` option is `false`. This is an array if the `lines` option is `true`, or if either the `stdout` or `stderr` option is a transform in object mode. */ all: ResultAll; /** The output of the subprocess on `stdin`, `stdout`, `stderr` and other file descriptors. Items are `undefined` when their corresponding `stdio` option is set to only `'inherit'`, `'ignore'`, `Writable` or `integer`, or if the `buffer` option is `false`. Items are arrays when their corresponding `stdio` option is a transform in object mode. */ stdio: ResultStdioArray; /** All the messages sent by the subprocess to the current process. This is empty unless the `ipc` option is `true`. Also, this is empty if the `buffer` option is `false`. */ ipcOutput: ResultIpcOutput; /** Results of the other subprocesses that were piped into this subprocess. This array is initially empty and is populated each time the `subprocess.pipe()` method resolves. */ pipedFrom: Unless; /** The file and arguments that were run. */ command: string; /** Same as `command` but escaped. */ escapedCommand: string; /** The current directory in which the command was run. */ cwd: string; /** Duration of the subprocess, in milliseconds. */ durationMs: number; /** Whether the subprocess failed to run. When this is `true`, the result is an `ExecaError` instance with additional error-related properties. */ failed: boolean; /** Whether the subprocess timed out due to the `timeout` option. */ timedOut: boolean; /** Whether the subprocess was canceled using the `cancelSignal` option. */ isCanceled: boolean; /** Whether the subprocess was canceled using both the `cancelSignal` and the `gracefulCancel` options. */ isGracefullyCanceled: boolean; /** Whether the subprocess failed because its output was larger than the `maxBuffer` option. */ isMaxBuffer: boolean; /** Whether the subprocess was terminated by a signal (like `SIGTERM`) sent by either: - The current process. - Another process. This case is [not supported on Windows](https://nodejs.org/api/process.html#signal-events). */ isTerminated: boolean; /** Whether the subprocess was terminated by the `SIGKILL` signal sent by the `forceKillAfterDelay` option. */ isForcefullyTerminated: boolean; /** The numeric [exit code](https://en.wikipedia.org/wiki/Exit_status) of the subprocess that was run. This is `undefined` when the subprocess could not be spawned or was terminated by a signal. */ exitCode?: number; /** The name of the signal (like `SIGTERM`) that terminated the subprocess, sent by either: - The current process. - Another process. This case is [not supported on Windows](https://nodejs.org/api/process.html#signal-events). If a signal terminated the subprocess, this property is defined and included in the error message. Otherwise it is `undefined`. */ signal?: keyof SignalConstants; /** A human-friendly description of the signal that was used to terminate the subprocess. If a signal terminated the subprocess, this property is defined and included in the error message. Otherwise it is `undefined`. It is also `undefined` when the signal is very uncommon which should seldomly happen. */ signalDescription?: string; /** Error message when the subprocess failed to run. */ message?: string; /** This is the same as `error.message` except it does not include the subprocess output. */ shortMessage?: string; /** Original error message. This is the same as `error.message` excluding the subprocess output and some additional information added by Execa. This exists only in specific instances, such as during a timeout. */ originalMessage?: string; /** Underlying error, if there is one. For example, this is set by `subprocess.kill(error)`. This is usually an `Error` instance. */ cause?: unknown; /** Node.js-specific [error code](https://nodejs.org/api/errors.html#errorcode), when available. */ code?: string; // We cannot `extend Error` because `message` must be optional. So we copy its types here. readonly name?: Error['name']; stack?: Error['stack']; } export type SuccessResult< IsSync extends boolean = boolean, OptionsType extends CommonOptions = CommonOptions, > = InstanceType> & OmitErrorIfReject; type OmitErrorIfReject = { [ErrorProperty in ErrorProperties]: RejectOption extends false ? unknown : never }; /** Result of a subprocess successful execution. When the subprocess fails, it is rejected with an `ExecaError` instead. */ export type Result = SuccessResult; /** Result of a subprocess successful execution. When the subprocess fails, it is rejected with an `ExecaError` instead. */ export type SyncResult = SuccessResult; export {}; ================================================ FILE: types/stdio/array.d.ts ================================================ import type {CommonOptions} from '../arguments/options.js'; import type {StdinOptionCommon, StdoutStderrOptionCommon, StdioOptionsArray} from './type.js'; // `options.stdio`, normalized as an array export type StdioOptionNormalizedArray = StdioOptionNormalized; type StdioOptionNormalized = StdioOption extends StdioOptionsArray ? StdioOption : StdioOption extends StdinOptionCommon ? StdioOption extends StdoutStderrOptionCommon ? readonly [StdioOption, StdioOption, StdioOption] : DefaultStdioOption : DefaultStdioOption; // `options.stdio` default value type DefaultStdioOption = readonly ['pipe', 'pipe', 'pipe']; export {}; ================================================ FILE: types/stdio/direction.d.ts ================================================ import type {CommonOptions} from '../arguments/options.js'; import type {Intersects} from '../utils.js'; import type {StdioSingleOptionItems, InputStdioOption} from './type.js'; import type {FdStdioArrayOption} from './option.js'; // Whether `result.stdio[FdNumber]` is an input stream export type IsInputFd< FdNumber extends string, OptionsType extends CommonOptions, > = FdNumber extends '0' ? true : Intersects>, InputStdioOption>; export {}; ================================================ FILE: types/stdio/option.d.ts ================================================ import type {CommonOptions} from '../arguments/options.js'; import type {StdioOptionNormalizedArray} from './array.js'; import type {StandardStreams, StdioOptionCommon, StdioOptionsArray} from './type.js'; // `options.stdin|stdout|stderr|stdio` for a given file descriptor export type FdStdioOption< FdNumber extends string, OptionsType extends CommonOptions, > = FdStdioOptionProperty; type FdStdioOptionProperty< FdNumber extends string, OptionsType extends CommonOptions, > = string extends FdNumber ? StdioOptionCommon : FdNumber extends keyof StandardStreams ? StandardStreams[FdNumber] extends keyof OptionsType ? OptionsType[StandardStreams[FdNumber]] extends undefined ? FdStdioArrayOption : OptionsType[StandardStreams[FdNumber]] : FdStdioArrayOption : FdStdioArrayOption; // `options.stdio[FdNumber]`, excluding `options.stdin|stdout|stderr` export type FdStdioArrayOption< FdNumber extends string, OptionsType extends CommonOptions, > = FdStdioArrayOptionProperty>; type FdStdioArrayOptionProperty< FdNumber extends string, StdioOptionsType, > = string extends FdNumber ? StdioOptionCommon | undefined : StdioOptionsType extends StdioOptionsArray ? FdNumber extends keyof StdioOptionsType ? StdioOptionsType[FdNumber] : StdioOptionNormalizedArray extends StdioOptionsType ? StdioOptionsType[number] : undefined : undefined; export {}; ================================================ FILE: types/stdio/type.d.ts ================================================ import type {Readable, Writable} from 'node:stream'; import type {ReadableStream, WritableStream, TransformStream} from 'node:stream/web'; import type { Not, And, Or, Unless, AndUnless, } from '../utils.js'; import type { GeneratorTransform, GeneratorTransformFull, DuplexTransform, WebTransform, } from '../transform/normalize.js'; type IsStandardStream = FdNumber extends keyof StandardStreams ? true : false; export type StandardStreams = readonly ['stdin', 'stdout', 'stderr']; // When `options.stdin|stdout|stderr|stdio` is set to one of those values, no stream is created export type NoStreamStdioOption = | 'ignore' | 'inherit' | 'ipc' | number | Readable | Writable | Unless, undefined> | readonly [NoStreamStdioOption]; // `options.stdio` when it is not an array type SimpleStdioOption< IsSync extends boolean, IsExtra extends boolean, IsArray extends boolean, > = | undefined | 'pipe' | Unless, IsArray>, IsExtra>, 'inherit'> | Unless | Unless; // Values available in both `options.stdin|stdio` and `options.stdout|stderr|stdio` type CommonStdioOption< IsSync extends boolean, IsExtra extends boolean, IsArray extends boolean, > = | SimpleStdioOption | URL | {readonly file: string; readonly append?: boolean} | GeneratorTransform | GeneratorTransformFull | Unless, IsArray>, 3 | 4 | 5 | 6 | 7 | 8 | 9> | Unless, 'ipc'> | Unless; // Synchronous iterables excluding strings, Uint8Arrays and Arrays type IterableObject = Iterable & object & {readonly BYTES_PER_ELEMENT?: never} & AndUnless; // `process.stdin|stdout|stderr` are `Duplex` with a `fd` property. // This ensures they can only be passed to `stdin`/`stdout`/`stderr`, based on their direction. type ProcessStdinFd = {readonly fd?: 0}; type ProcessStdoutStderrFd = {readonly fd?: 1 | 2}; // Values available only in `options.stdin|stdio` export type InputStdioOption< IsSync extends boolean = boolean, IsExtra extends boolean = boolean, IsArray extends boolean = boolean, > = | 0 | Unless, Uint8Array | IterableObject> | Unless, Readable & ProcessStdinFd> | Unless & ProcessStdinFd) | ReadableStream>; // Values available only in `options.stdout|stderr|stdio` type OutputStdioOption< IsSync extends boolean, IsArray extends boolean, > = | 1 | 2 | Unless, Writable & ProcessStdoutStderrFd> | Unless; // `options.stdin` array items type StdinSingleOption< IsSync extends boolean, IsExtra extends boolean, IsArray extends boolean, > = | CommonStdioOption | InputStdioOption; // `options.stdin` export type StdinOptionCommon< IsSync extends boolean = boolean, IsExtra extends boolean = boolean, > = | StdinSingleOption | ReadonlyArray>; // `options.stdin`, async export type StdinOption = StdinOptionCommon; // `options.stdin`, sync export type StdinSyncOption = StdinOptionCommon; // `options.stdout|stderr` array items type StdoutStderrSingleOption< IsSync extends boolean, IsExtra extends boolean, IsArray extends boolean, > = | CommonStdioOption | OutputStdioOption; // `options.stdout|stderr` export type StdoutStderrOptionCommon< IsSync extends boolean = boolean, IsExtra extends boolean = boolean, > = | StdoutStderrSingleOption | ReadonlyArray>; // `options.stdout|stderr`, async export type StdoutStderrOption = StdoutStderrOptionCommon; // `options.stdout|stderr`, sync export type StdoutStderrSyncOption = StdoutStderrOptionCommon; // `options.stdio[3+]` type StdioExtraOptionCommon = | StdinOptionCommon | StdoutStderrOptionCommon; // `options.stdin|stdout|stderr|stdio` array items type StdioSingleOption< IsSync extends boolean = boolean, IsExtra extends boolean = boolean, IsArray extends boolean = boolean, > = | StdinSingleOption | StdoutStderrSingleOption; // Get `options.stdin|stdout|stderr|stdio` items if it is an array, else keep as is export type StdioSingleOptionItems = StdioOptionType extends readonly StdioSingleOption[] ? StdioOptionType[number] : StdioOptionType; // `options.stdin|stdout|stderr|stdio` export type StdioOptionCommon = | StdinOptionCommon | StdoutStderrOptionCommon; // `options.stdio` when it is an array export type StdioOptionsArray = readonly [ StdinOptionCommon, StdoutStderrOptionCommon, StdoutStderrOptionCommon, ...ReadonlyArray>, ]; // `options.stdio` export type StdioOptionsProperty = | SimpleStdioOption | StdioOptionsArray; export {}; ================================================ FILE: types/subprocess/all.d.ts ================================================ import type {Readable} from 'node:stream'; import type {IgnoresSubprocessOutput} from '../return/ignore.js'; import type {Options} from '../arguments/options.js'; // `subprocess.all` export type SubprocessAll = AllStream>; type AllStream = IsIgnored extends true ? undefined : Readable; type AllIgnored< AllOption, OptionsType extends Options, > = AllOption extends true ? IgnoresSubprocessOutput<'1', OptionsType> extends true ? IgnoresSubprocessOutput<'2', OptionsType> : false : true; export {}; ================================================ FILE: types/subprocess/stdio.d.ts ================================================ import type {StdioOptionNormalizedArray} from '../stdio/array.js'; import type {Options} from '../arguments/options.js'; import type {SubprocessStdioStream} from './stdout.js'; // `subprocess.stdio` export type SubprocessStdioArray = MapStdioStreams, OptionsType>; // We cannot use mapped types because it must be compatible with Node.js `ChildProcess["stdio"]` which uses a tuple with exactly 5 items type MapStdioStreams< StdioOptionsArrayType, OptionsType extends Options, > = [ SubprocessStdioStream<'0', OptionsType>, SubprocessStdioStream<'1', OptionsType>, SubprocessStdioStream<'2', OptionsType>, '3' extends keyof StdioOptionsArrayType ? SubprocessStdioStream<'3', OptionsType> : never, '4' extends keyof StdioOptionsArrayType ? SubprocessStdioStream<'4', OptionsType> : never, ]; export {}; ================================================ FILE: types/subprocess/stdout.d.ts ================================================ import type {Readable, Writable} from 'node:stream'; import type {IsInputFd} from '../stdio/direction.js'; import type {IgnoresSubprocessOutput} from '../return/ignore.js'; import type {Options} from '../arguments/options.js'; // `subprocess.stdin|stdout|stderr|stdio` export type SubprocessStdioStream< FdNumber extends string, OptionsType extends Options, > = SubprocessStream, OptionsType>; type SubprocessStream< FdNumber extends string, StreamResultIgnored, OptionsType extends Options, > = StreamResultIgnored extends true ? null : InputOutputStream>; type InputOutputStream = IsInput extends true ? Writable : Readable; export {}; ================================================ FILE: types/subprocess/subprocess.d.ts ================================================ import type {ChildProcess} from 'node:child_process'; import type {SignalConstants} from 'node:os'; import type {Readable, Writable, Duplex} from 'node:stream'; import type {Options} from '../arguments/options.js'; import type {Result} from '../return/result.js'; import type {PipableSubprocess} from '../pipe.js'; import type { ReadableOptions, WritableOptions, DuplexOptions, SubprocessAsyncIterable, } from '../convert.js'; import type {IpcMethods, HasIpc} from '../ipc.js'; import type {SubprocessStdioStream} from './stdout.js'; import type {SubprocessStdioArray} from './stdio.js'; import type {SubprocessAll} from './all.js'; type ExecaCustomSubprocess = { /** Process identifier ([PID](https://en.wikipedia.org/wiki/Process_identifier)). This is `undefined` if the subprocess failed to spawn. */ pid?: number; /** The subprocess [`stdin`](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)) as a stream. This is `null` if the `stdin` option is set to `'inherit'`, `'ignore'`, `Readable` or `integer`. */ stdin: SubprocessStdioStream<'0', OptionsType>; /** The subprocess [`stdout`](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)) as a stream. This is `null` if the `stdout` option is set to `'inherit'`, `'ignore'`, `Writable` or `integer`, or if the `buffer` option is `false`. */ stdout: SubprocessStdioStream<'1', OptionsType>; /** The subprocess [`stderr`](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)) as a stream. This is `null` if the `stderr` option is set to `'inherit'`, `'ignore'`, `Writable` or `integer`, or if the `buffer` option is `false`. */ stderr: SubprocessStdioStream<'2', OptionsType>; /** Stream combining/interleaving `subprocess.stdout` and `subprocess.stderr`. This requires the `all` option to be `true`. This is `undefined` if `stdout` and `stderr` options are set to `'inherit'`, `'ignore'`, `Writable` or `integer`, or if the `buffer` option is `false`. */ all: SubprocessAll; /** The subprocess `stdin`, `stdout`, `stderr` and other files descriptors as an array of streams. Each array item is `null` if the corresponding `stdin`, `stdout`, `stderr` or `stdio` option is set to `'inherit'`, `'ignore'`, `Stream` or `integer`, or if the `buffer` option is `false`. */ stdio: SubprocessStdioArray; /** Sends a [signal](https://nodejs.org/api/os.html#signal-constants) to the subprocess. The default signal is the `killSignal` option. `killSignal` defaults to `SIGTERM`, which terminates the subprocess. This returns `false` when the signal could not be sent, for example when the subprocess has already exited. When an error is passed as argument, it is set to the subprocess' `error.cause`. The subprocess is then terminated with the default signal. This does not emit the [`error` event](https://nodejs.org/api/child_process.html#event-error). [More info.](https://nodejs.org/api/child_process.html#subprocesskillsignal) */ kill(signal?: keyof SignalConstants | number, error?: Error): boolean; kill(error?: Error): boolean; /** Subprocesses are [async iterables](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator). They iterate over each output line. */ [Symbol.asyncIterator](): SubprocessAsyncIterable; /** Same as `subprocess[Symbol.asyncIterator]` except options can be provided. */ iterable(readableOptions?: IterableOptions): SubprocessAsyncIterable; /** Converts the subprocess to a readable stream. */ readable(readableOptions?: ReadableOptions): Readable; /** Converts the subprocess to a writable stream. */ writable(writableOptions?: WritableOptions): Writable; /** Converts the subprocess to a duplex stream. */ duplex(duplexOptions?: DuplexOptions): Duplex; } & IpcMethods, OptionsType['serialization']> & PipableSubprocess; /** [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with additional methods and properties. */ export type Subprocess = & Omit> & ExecaCustomSubprocess; /** The return value of all asynchronous methods is both: - the subprocess. - a `Promise` either resolving with its successful `result`, or rejecting with its `error`. */ export type ResultPromise = & Subprocess & Promise>; export {}; ================================================ FILE: types/transform/normalize.d.ts ================================================ import type {TransformStream} from 'node:stream/web'; import type {Duplex} from 'node:stream'; import type {Unless} from '../utils.js'; // `options.std*: Generator` // @todo Use `string`, `Uint8Array` or `unknown` for both the argument and the return type, based on whether `encoding: 'buffer'` and `objectMode: true` are used. // See https://github.com/sindresorhus/execa/issues/694 export type GeneratorTransform = (chunk: unknown) => | Unless> | Generator; type GeneratorFinal = () => | Unless> | Generator; export type TransformCommon = { /** If `true`, allow `transformOptions.transform` and `transformOptions.final` to return any type, not just `string` or `Uint8Array`. */ readonly objectMode?: boolean; }; /** A transform or an array of transforms can be passed to the `stdin`, `stdout`, `stderr` or `stdio` option. A transform is either a generator function or a plain object with the following members. */ export type GeneratorTransformFull = { /** Map or filter the input or output of the subprocess. */ readonly transform: GeneratorTransform; /** Create additional lines after the last one. */ readonly final?: GeneratorFinal; /** If `true`, iterate over arbitrary chunks of `Uint8Array`s instead of line `string`s. */ readonly binary?: boolean; /** If `true`, keep newlines in each `line` argument. Also, this allows multiple `yield`s to produces a single line. */ readonly preserveNewlines?: boolean; } & TransformCommon; // `options.std*: Duplex` export type DuplexTransform = { readonly transform: Duplex; } & TransformCommon; // `options.std*: TransformStream` export type WebTransform = { readonly transform: TransformStream; } & TransformCommon; export {}; ================================================ FILE: types/transform/object-mode.d.ts ================================================ import type {StdioSingleOptionItems} from '../stdio/type.js'; import type {FdStdioOption} from '../stdio/option.js'; import type {CommonOptions} from '../arguments/options.js'; import type {DuplexTransform, TransformCommon} from './normalize.js'; // Whether a file descriptor is in object mode // I.e. whether `result.stdout|stderr|stdio|all` is an array of `unknown` due to `objectMode: true` export type IsObjectFd< FdNumber extends string, OptionsType extends CommonOptions, > = IsObjectStdioOption>; type IsObjectStdioOption = IsObjectStdioSingleOption>; type IsObjectStdioSingleOption = StdioSingleOptionType extends TransformCommon ? BooleanObjectMode : StdioSingleOptionType extends DuplexTransform ? StdioSingleOptionType['transform']['readableObjectMode'] : false; type BooleanObjectMode = ObjectModeOption extends true ? true : false; export {}; ================================================ FILE: types/utils.d.ts ================================================ export type Not = Value extends true ? false : true; export type And = First extends true ? Second : false; export type Or = First extends true ? true : Second; export type Unless = Condition extends true ? ElseValue : ThenValue; export type AndUnless = Condition extends true ? ElseValue : ThenValue; // Whether any of T's union element is the same as one of U's union element. // `&` does not work here. export type Intersects = true extends (T extends U ? true : false) ? true : false; export {}; ================================================ FILE: types/verbose.d.ts ================================================ import type {FdGenericOption} from './arguments/specific.js'; import type {Options, SyncOptions} from './arguments/options.js'; import type {Result, SyncResult} from './return/result.js'; export type VerboseOption = FdGenericOption< | 'none' | 'short' | 'full' | VerboseFunction >; type VerboseFunction = (verboseLine: string, verboseObject: MinimalVerboseObject) => string | void; type GenericVerboseObject = { /** Event type. This can be: - `'command'`: subprocess start - `'output'`: `stdout`/`stderr` output - `'ipc'`: IPC output - `'error'`: subprocess failure - `'duration'`: subprocess success or failure */ type: 'command' | 'output' | 'ipc' | 'error' | 'duration'; /** Depending on `verboseObject.type`, this is: - `'command'`: the `result.escapedCommand` - `'output'`: one line from `result.stdout` or `result.stderr` - `'ipc'`: one IPC message from `result.ipcOutput` - `'error'`: the `error.shortMessage` - `'duration'`: the `result.durationMs` */ message: string; /** The file and arguments that were run. This is the same as `result.escapedCommand`. */ escapedCommand: string; /** Serial number identifying the subprocess within the current process. It is incremented from `'0'`. This is helpful when multiple subprocesses are running at the same time. This is similar to a [PID](https://en.wikipedia.org/wiki/Process_identifier) except it has no maximum limit, which means it never repeats. Also, it is usually shorter. */ commandId: string; /** Event date/time. */ timestamp: Date; /** Whether another subprocess is piped into this subprocess. This is `false` when `result.pipedFrom` is empty. */ piped: boolean; }; type MinimalVerboseObject = GenericVerboseObject & { // We cannot use the `CommonOptions` type because it would make this type recursive options: object; result?: never; }; /** Subprocess event object, for logging purpose, using the `verbose` option and `execa()`. */ export type VerboseObject = GenericVerboseObject & { /** The options passed to the subprocess. */ options: Options; /** Subprocess result. This is `undefined` if `verboseObject.type` is `'command'`, `'output'` or `'ipc'`. */ result?: Result; }; /** Subprocess event object, for logging purpose, using the `verbose` option and `execaSync()`. */ export type SyncVerboseObject = GenericVerboseObject & { /** The options passed to the subprocess. */ options: SyncOptions; /** Subprocess result. This is `undefined` if `verboseObject.type` is `'command'`, `'output'` or `'ipc'`. */ result?: SyncResult; }; export {};