From 5b6debcac44507a8616ec2d07823c8f883163a6a Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 1 Jul 2026 21:56:58 +0200 Subject: [PATCH] Require Node.js 22 --- .github/workflows/main.yml | 13 ++- .gitignore | 1 + docs/api.md | 2 +- docs/binary.md | 2 +- docs/termination.md | 2 +- lib/arguments/escape.js | 4 +- lib/arguments/specific.js | 4 +- lib/convert/readable.js | 8 +- lib/convert/writable.js | 12 +- lib/io/output-async.js | 8 +- lib/io/output-sync.js | 5 +- lib/ipc/reference.js | 16 ++- lib/methods/bind.js | 10 +- lib/methods/main-async.js | 2 +- lib/resolve/wait-stream.js | 3 +- lib/return/final-error.js | 26 +++-- lib/stdio/input-option.js | 22 ++-- lib/transform/normalize.js | 17 +-- lib/utils/max-listeners.js | 2 +- lib/verbose/log.js | 28 ++--- lib/verbose/output.js | 2 +- package.json | 58 +++++++--- test-d/arguments/encoding-option.test-d.ts | 2 +- test-d/arguments/options.test-d.ts | 50 ++++---- test-d/arguments/specific.test-d.ts | 10 ++ test-d/ipc/message.test-d.ts | 18 +-- test-d/stdio/option/fd-integer-0.test-d.ts | 8 +- test/arguments/encoding-option.js | 2 +- test/arguments/escape-no-icu.js | 5 +- test/arguments/escape.js | 25 ++-- test/fixtures/graceful-disconnect.js | 2 +- test/fixtures/graceful-print.js | 2 +- test/fixtures/graceful-send-echo.js | 2 +- test/fixtures/graceful-send-print.js | 2 +- test/fixtures/graceful-send-string.js | 2 +- test/fixtures/graceful-send-twice.js | 2 +- test/fixtures/graceful-send.js | 2 +- test/fixtures/graceful-twice.js | 2 +- test/fixtures/graceful-wait.js | 2 +- test/fixtures/ipc-iterate-error.js | 2 +- test/fixtures/ipc-iterate-twice.js | 1 + test/fixtures/ipc-print-many-each.js | 1 + test/helpers/convert.js | 2 +- test/helpers/encoding.js | 2 +- test/helpers/fixtures-directory.js | 1 + test/helpers/input.js | 2 +- test/helpers/node-version.js | 2 +- test/helpers/override-promise.js | 2 + test/helpers/verbose.js | 2 +- test/helpers/wait.js | 4 +- test/io/output-async.js | 12 +- test/ipc/buffer-messages.js | 10 +- test/ipc/get-one.js | 12 +- test/ipc/send.js | 17 ++- test/methods/command.js | 1 + test/methods/parameters-options.js | 20 ++-- test/methods/promise.js | 4 +- test/methods/template.js | 40 +++---- test/resolve/exit.js | 4 +- test/return/early-error.js | 8 +- test/return/message.js | 3 +- test/return/result.js | 4 +- test/terminate/kill-force.js | 2 +- test/terminate/kill-signal.js | 22 +--- test/terminate/signal.js | 12 +- test/transform/encoding-final.js | 2 +- test/transform/encoding-multibyte.js | 1 + test/transform/split-lines.js | 2 +- test/verbose/error.js | 8 +- test/verbose/ipc.js | 10 +- test/verbose/log.js | 3 +- test/verbose/output-enable.js | 7 +- test/verbose/output-progressive.js | 3 + test/verbose/start.js | 8 +- tsconfig.json | 3 +- types/arguments/encoding-option.d.ts | 16 +-- types/arguments/options.d.ts | 2 +- types/arguments/specific.d.ts | 4 +- types/convert.d.ts | 8 +- types/ipc.d.ts | 6 +- types/return/final-error.d.ts | 14 +-- types/return/result-all.d.ts | 8 +- types/return/result-ipc.d.ts | 6 +- types/return/result-stdio.d.ts | 4 +- types/return/result-stdout.d.ts | 14 +-- types/return/result.d.ts | 4 +- types/stdio/type.d.ts | 19 +-- types/subprocess/subprocess.d.ts | 127 +++++++++++---------- types/transform/normalize.d.ts | 20 ++-- types/verbose.d.ts | 8 +- 90 files changed, 455 insertions(+), 429 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 452b039aa6..1495f96fa5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,28 +12,29 @@ jobs: node-version: - 26 - 24 - - 18 + - 22 os: - ubuntu - macos - windows steps: - - uses: actions/cache@v4 + - uses: actions/cache@v6 with: path: .lycheecache key: cache-lychee-${{ github.sha }} restore-keys: cache-lychee- - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} - run: npm install - - uses: lycheeverse/lychee-action@v1 + - uses: lycheeverse/lychee-action@v2 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' + args: --cache --verbose --no-progress --include-fragments --exclude packagephobia --exclude /pull/ --exclude linkedin --exclude stackoverflow --exclude stackexchange --exclude github.com/nodejs/node --exclude github.com/denoland/deno --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 == 26 }} - run: npm run lint + if: ${{ matrix.os == 'ubuntu' && matrix.node-version == 26 }} - run: npm run type - run: npm run unit - uses: codecov/codecov-action@v4 diff --git a/.gitignore b/.gitignore index a042fbb0c8..fc3357ecf3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules yarn.lock .nyc_output coverage +lychee diff --git a/docs/api.md b/docs/api.md index a5de03ec23..0410c1831d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -945,7 +945,7 @@ If the subprocess outputs text, specifies its character encoding, either [`'utf8 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. +- [`'hex'`](https://en.wikipedia.org/wiki/Hexadecimal), [`'base64'`](https://en.wikipedia.org/wiki/Base64), [`'base64url'`](https://en.wikipedia.org/wiki/Base64#RFC_4648), [`'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). diff --git a/docs/binary.md b/docs/binary.md index 558989d7cc..bcb274584d 100644 --- a/docs/binary.md +++ b/docs/binary.md @@ -30,7 +30,7 @@ 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. +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#RFC_4648). The output will be a string then. ```js const {stdout} = await execa({encoding: 'hex'})`zip -r - input.txt`; diff --git a/docs/termination.md b/docs/termination.md index 99ed31da03..e134c27c61 100644 --- a/docs/termination.md +++ b/docs/termination.md @@ -118,7 +118,7 @@ addAbortListener(cancelSignal, async () => { }); ``` -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). +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. diff --git a/lib/arguments/escape.js b/lib/arguments/escape.js index 48ae3c244f..7ac59b280e 100644 --- a/lib/arguments/escape.js +++ b/lib/arguments/escape.js @@ -49,7 +49,7 @@ const getSpecialCharRegExp = () => { // 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 + // eslint-disable-next-line no-control-regex, regexp/no-control-character, unicorn/prefer-unicode-code-point-escapes return /[\s\u0000-\u001F\u007F-\u009F\u00AD]/g; } }; @@ -85,4 +85,4 @@ const quoteString = escapedArgument => { : `'${escapedArgument.replaceAll('\'', '\'\\\'\'')}'`; }; -const NO_ESCAPE_REGEXP = /^[\w./-]+$/; +const NO_ESCAPE_REGEXP = /^[\w\-./]+$/; diff --git a/lib/arguments/specific.js b/lib/arguments/specific.js index fe8e1e32a3..76c9b74fa7 100644 --- a/lib/arguments/specific.js +++ b/lib/arguments/specific.js @@ -82,11 +82,11 @@ export const parseFd = fdName => { const regexpResult = FD_REGEXP.exec(fdName); if (regexpResult !== null) { - return Number(regexpResult[1]); + return Number(regexpResult.groups.fdNumber); } }; -const FD_REGEXP = /^fd(\d+)$/; +const FD_REGEXP = /^fd(?\d+)$/; const addDefaultValue = (optionArray, optionName) => optionArray.map(optionValue => optionValue === undefined ? DEFAULT_OPTIONS[optionName] diff --git a/lib/convert/readable.js b/lib/convert/readable.js index a63b0c0098..cefd663cb9 100644 --- a/lib/convert/readable.js +++ b/lib/convert/readable.js @@ -102,10 +102,12 @@ export const onStdoutFinished = async ({subprocessStdout, onStdoutDataDone, read // 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); + if (!await waitForConcurrentStreams(waitReadableDestroy, subprocess)) { + return; } + + destroyOtherReadable(subprocessStdout, error); + await waitForSubprocess(subprocess, error); }; const destroyOtherReadable = (stream, error) => { diff --git a/lib/convert/writable.js b/lib/convert/writable.js index fd727e3ee3..2dc1c5402c 100644 --- a/lib/convert/writable.js +++ b/lib/convert/writable.js @@ -54,13 +54,15 @@ const onWrite = (subprocessStdin, chunk, encoding, done) => { // 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(); - } + if (!await waitForConcurrentStreams(waitWritableFinal, subprocess)) { + return; + } - await subprocess; + if (subprocessStdin.writable) { + subprocessStdin.end(); } + + await subprocess; }; // When `subprocess.stdin` ends/aborts/errors, do the same on `writable`. diff --git a/lib/io/output-async.js b/lib/io/output-async.js index ededfa9b23..ee94b171b4 100644 --- a/lib/io/output-async.js +++ b/lib/io/output-async.js @@ -10,11 +10,13 @@ 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))) { + const transformItems = stdioItems.filter(({type}) => TRANSFORM_TYPES.has(type)); + for (const {stream} of transformItems) { pipeTransform(subprocess, stream, direction, fdNumber); } - for (const {stream} of stdioItems.filter(({type}) => !TRANSFORM_TYPES.has(type))) { + const nonTransformItems = stdioItems.filter(({type}) => !TRANSFORM_TYPES.has(type)); + for (const {stream} of nonTransformItems) { pipeStdioItem({ subprocess, stream, @@ -26,7 +28,7 @@ export const pipeOutputAsync = (subprocess, fileDescriptors, controller) => { } } - for (const [outputStream, inputStreams] of pipeGroups.entries()) { + for (const [outputStream, inputStreams] of pipeGroups) { const inputStream = inputStreams.length === 1 ? inputStreams[0] : mergeStreams(inputStreams); pipeStreams(inputStream, outputStream); } diff --git a/lib/io/output-sync.js b/lib/io/output-sync.js index 36c9a8af9f..e096ddd4d8 100644 --- a/lib/io/output-sync.js +++ b/lib/io/output-sync.js @@ -13,7 +13,7 @@ export const transformOutputSync = ({fileDescriptors, syncResult: {output}, opti } const state = {}; - const outputFiles = new Set([]); + const outputFiles = new Set(); const transformedOutput = output.map((result, fdNumber) => transformOutputResultSync({ result, @@ -123,7 +123,8 @@ const logOutputSync = ({serializedResult, fdNumber, state, verboseInfo, encoding // 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 fileItems = stdioItems.filter(({type}) => FILE_TYPES.has(type)); + for (const {path, append} of fileItems) { const pathString = typeof path === 'string' ? path : path.toString(); if (append || outputFiles.has(pathString)) { appendFileSync(path, serializedResult); diff --git a/lib/ipc/reference.js b/lib/ipc/reference.js index 25eec52768..659fd194b8 100644 --- a/lib/ipc/reference.js +++ b/lib/ipc/reference.js @@ -29,16 +29,20 @@ const removeReferenceCount = channel => { // 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); + if (!isSubprocess) { + return; } + + removeReferenceCount(channel); + removeReferenceCount(channel); }; // Reverse it during `disconnect` export const redoAddedReferences = (channel, isSubprocess) => { - if (isSubprocess) { - addReferenceCount(channel); - addReferenceCount(channel); + if (!isSubprocess) { + return; } + + addReferenceCount(channel); + addReferenceCount(channel); }; diff --git a/lib/methods/bind.js b/lib/methods/bind.js index dd1b285eb2..94182e5668 100644 --- a/lib/methods/bind.js +++ b/lib/methods/bind.js @@ -5,12 +5,10 @@ import {FD_SPECIFIC_OPTIONS} from '../arguments/specific.js'; // 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), - ]), - ); + const mergedOptions = Object.fromEntries(Object.entries(options).map(([optionName, optionValue]) => [ + optionName, + mergeOption(optionName, safeBoundOptions[optionName], optionValue), + ])); return {...safeBoundOptions, ...mergedOptions}; }; diff --git a/lib/methods/main-async.js b/lib/methods/main-async.js index 47d092ce55..bcfa83ac80 100644 --- a/lib/methods/main-async.js +++ b/lib/methods/main-async.js @@ -90,7 +90,7 @@ const spawnSubprocessAsync = ({file, commandArguments, options, startTime, verbo } const controller = new AbortController(); - setMaxListeners(Number.POSITIVE_INFINITY, controller.signal); + setMaxListeners(Infinity, controller.signal); const originalStreams = [...subprocess.stdio]; pipeOutputAsync(subprocess, fileDescriptors, controller); diff --git a/lib/resolve/wait-stream.js b/lib/resolve/wait-stream.js index 8090888cfb..d92a45ff0f 100644 --- a/lib/resolve/wait-stream.js +++ b/lib/resolve/wait-stream.js @@ -31,7 +31,8 @@ export const waitForStream = async (stream, fdNumber, streamInfo, {isSameDirecti // 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 handleStdinDestroy = (stream, {originalStreams, subprocess}) => { + const [originalStdin] = originalStreams; const state = {stdinCleanedUp: false}; if (stream === originalStdin) { spyOnStdinDestroy(stream, subprocess, state); diff --git a/lib/return/final-error.js b/lib/return/final-error.js index 045bb6e3ba..3477214d82 100644 --- a/lib/return/final-error.js +++ b/lib/return/final-error.js @@ -11,17 +11,19 @@ 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, + Object.defineProperties(ErrorClass.prototype, { + name: { + value, + writable: true, + enumerable: false, + configurable: true, + }, + [execaErrorSymbol]: { + value: true, + writable: false, + enumerable: false, + configurable: false, + }, }); }; @@ -34,7 +36,9 @@ export const isErrorInstance = value => Object.prototype.toString.call(value) == // We use two different Error classes for async/sync methods since they have slightly different shape and types export class ExecaError extends Error {} +// eslint-disable-next-line unicorn/no-top-level-side-effects -- `error.name` must be set as soon as the class is defined setErrorName(ExecaError, ExecaError.name); export class ExecaSyncError extends Error {} +// eslint-disable-next-line unicorn/no-top-level-side-effects -- `error.name` must be set as soon as the class is defined setErrorName(ExecaSyncError, ExecaSyncError.name); diff --git a/lib/stdio/input-option.js b/lib/stdio/input-option.js index 361538bf39..539c64212b 100644 --- a/lib/stdio/input-option.js +++ b/lib/stdio/input-option.js @@ -10,11 +10,13 @@ export const handleInputOptions = ({input, inputFile}, fdNumber) => fdNumber === ] : []; -const handleInputOption = input => input === undefined ? [] : [{ - type: getInputType(input), - value: input, - optionName: 'input', -}]; +const handleInputOption = input => input === undefined + ? [] + : [{ + type: getInputType(input), + value: input, + optionName: 'input', + }]; const getInputType = input => { if (isReadableStream(input, {checkOpen: false})) { @@ -32,10 +34,12 @@ const getInputType = input => { 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 handleInputFileOption = inputFile => inputFile === undefined + ? [] + : [{ + ...getInputFileType(inputFile), + optionName: 'inputFile', + }]; const getInputFileType = inputFile => { if (isUrl(inputFile)) { diff --git a/lib/transform/normalize.js b/lib/transform/normalize.js index 06d8e43215..ad1d194a02 100644 --- a/lib/transform/normalize.js +++ b/lib/transform/normalize.js @@ -51,17 +51,12 @@ const normalizeTransform = ({stdioItem, stdioItem: {type}, index, newTransforms, }); }; -const normalizeDuplex = ({ - stdioItem, - stdioItem: { - value: { - transform, - transform: {writableObjectMode, readableObjectMode}, - objectMode = readableObjectMode, - }, - }, - optionName, -}) => { +const normalizeDuplex = ({stdioItem, optionName}) => { + const {value} = stdioItem; + const {transform} = value; + const {writableObjectMode, readableObjectMode} = transform; + const {objectMode = readableObjectMode} = value; + if (objectMode && !readableObjectMode) { throw new TypeError(`The \`${optionName}.objectMode\` option can only be \`true\` if \`new Duplex({objectMode: true})\` is used.`); } diff --git a/lib/utils/max-listeners.js b/lib/utils/max-listeners.js index 16856936ec..6aa2be1f58 100644 --- a/lib/utils/max-listeners.js +++ b/lib/utils/max-listeners.js @@ -3,7 +3,7 @@ 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) { + if (maxListeners === 0 || maxListeners === Infinity) { return; } diff --git a/lib/verbose/log.js b/lib/verbose/log.js index 52f46b7a7d..1f233b7997 100644 --- a/lib/verbose/log.js +++ b/lib/verbose/log.js @@ -20,19 +20,19 @@ export const verboseLog = ({type, verboseMessage, fdNumber, verboseInfo, result} } }; -const getVerboseObject = ({ - type, - result, - verboseInfo: {escapedCommand, commandId, rawOptions: {piped = false, ...options}}, -}) => ({ - type, - escapedCommand, - commandId: `${commandId}`, - timestamp: new Date(), - piped, - result, - options, -}); +const getVerboseObject = ({type, result, verboseInfo}) => { + const {escapedCommand, commandId, rawOptions} = verboseInfo; + const {piped = false, ...options} = rawOptions; + return { + type, + escapedCommand, + commandId: `${commandId}`, + timestamp: new Date(), + piped, + result, + options, + }; +}; const getPrintedLines = (verboseMessage, verboseObject) => verboseMessage .split('\n') @@ -47,7 +47,7 @@ const getPrintedLine = verboseObject => { export const serializeVerboseMessage = message => { const messageString = typeof message === 'string' ? message : inspect(message); const escapedMessage = escapeLines(messageString); - return escapedMessage.replaceAll('\t', ' '.repeat(TAB_SIZE)); + return escapedMessage.replaceAll('\t', () => ' '.repeat(TAB_SIZE)); }; // Same as `util.inspect()` diff --git a/lib/verbose/output.js b/lib/verbose/output.js index c95b6274d9..31cd900b63 100644 --- a/lib/verbose/output.js +++ b/lib/verbose/output.js @@ -13,7 +13,7 @@ export const shouldLogOutput = ({stdioItems, encoding, 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))); + || 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. diff --git a/package.json b/package.json index a339358f99..fa28c47b42 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,13 @@ }, "sideEffects": false, "engines": { - "node": "^18.19.0 || >=20.5.0" + "node": ">=22" }, "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" + "type": "tsd && tsc" }, "files": [ "index.js", @@ -54,31 +54,31 @@ "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", - "get-stream": "^9.0.0", + "get-stream": "^9.0.1", "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", + "pretty-ms": "^9.3.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", - "yoctocolors": "^2.1.1" + "yoctocolors": "^2.1.2" }, "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", + "@types/node": "^26.1.0", + "ava": "^8.0.1", + "c8": "^11.0.0", + "get-node": "^15.0.4", + "is-in-ci": "^2.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" + "tempfile": "^6.0.1", + "tsd": "^0.33.0", + "typescript": "^6.0.3", + "which": "^7.0.0", + "xo": "^3.0.2" }, "c8": { "reporter": [ @@ -99,7 +99,33 @@ "xo": { "rules": { "unicorn/no-empty-file": "off", - "@typescript-eslint/ban-types": "off" + "unicorn/prefer-string-raw": "off", + "ava/test-title": "off", + "@typescript-eslint/no-empty-object-type": "off", + "unicorn/no-useless-template-literals": "off", + "require-unicode-regexp": "off", + "ava/no-nested-assertions": "off", + "@stylistic/max-len": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/await-thenable": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/no-restricted-types": "off", + "no-shadow": "off", + "unicorn/no-computed-property-existence-check": "off", + "unicorn/max-nested-calls": "off", + "markdown/fenced-code-language": "off", + "unicorn/no-array-sort": "off", + "unicorn/prefer-minimal-ternary": "off", + "unicorn/no-array-reverse": "off", + "unicorn/no-array-from-fill": "off", + "unicorn/prefer-number-is-safe-integer": "off", + "unicorn/require-array-sort-compare": "off", + "@typescript-eslint/ban-types": "off", + "regexp/strict": "off", + "unicorn/require-module-specifiers": "off", + "jsdoc/require-asterisk-prefix": "off" } } } diff --git a/test-d/arguments/encoding-option.test-d.ts b/test-d/arguments/encoding-option.test-d.ts index 60df785c4e..ff240ab465 100644 --- a/test-d/arguments/encoding-option.test-d.ts +++ b/test-d/arguments/encoding-option.test-d.ts @@ -3,7 +3,7 @@ import {execa, execaSync} from '../../index.js'; await execa('unicorns', {encoding: 'utf8'}); execaSync('unicorns', {encoding: 'utf8'}); -/* eslint-disable unicorn/text-encoding-identifier-case */ +/* eslint-disable unicorn/text-encoding-identifier-case -- intentionally testing invalid encoding casing */ expectError(await execa('unicorns', {encoding: 'utf-8'})); expectError(execaSync('unicorns', {encoding: 'utf-8'})); expectError(await execa('unicorns', {encoding: 'UTF8'})); diff --git a/test-d/arguments/options.test-d.ts b/test-d/arguments/options.test-d.ts index 7ef0b997ff..cd4902ac43 100644 --- a/test-d/arguments/options.test-d.ts +++ b/test-d/arguments/options.test-d.ts @@ -25,8 +25,8 @@ 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: '.'}); +execaSync('unicorns', {localDir: '.'}); await execa('unicorns', {localDir: fileUrl}); execaSync('unicorns', {localDir: fileUrl}); expectError(await execa('unicorns', {localDir: false})); @@ -41,8 +41,8 @@ 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: './node'}); +execaSync('unicorns', {nodePath: './node'}); await execa('unicorns', {nodePath: fileUrl}); execaSync('unicorns', {nodePath: fileUrl}); expectError(await execa('unicorns', {nodePath: false})); @@ -57,8 +57,8 @@ 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: ''}); +execaSync('unicorns', {input: ''}); await execa('unicorns', {input: new Uint8Array()}); execaSync('unicorns', {input: new Uint8Array()}); await execa('unicorns', {input: process.stdin}); @@ -68,8 +68,8 @@ 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: ''}); +execaSync('unicorns', {inputFile: ''}); await execa('unicorns', {inputFile: fileUrl}); execaSync('unicorns', {inputFile: fileUrl}); expectError(await execa('unicorns', {inputFile: false})); @@ -105,14 +105,14 @@ 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: '.'}); +execaSync('unicorns', {cwd: '.'}); 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 */ +/* eslint-disable @typescript-eslint/naming-convention -- `PATH` is a real environment variable name */ await execa('unicorns', {env: {PATH: ''}}); execaSync('unicorns', {env: {PATH: ''}}); const env: Record = {PATH: ''}; @@ -124,22 +124,22 @@ expectError(execaSync('unicorns', {env: false})); await execa('unicorns', {argv0: ''}); execaSync('unicorns', {argv0: ''}); -await execa('unicorns', {argv0: '' as string}); -execaSync('unicorns', {argv0: '' as string}); +await execa('unicorns', {argv0: ''}); +execaSync('unicorns', {argv0: ''}); 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}); +await execa('unicorns', {uid: 0}); +execaSync('unicorns', {uid: 0}); 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}); +await execa('unicorns', {gid: 0}); +execaSync('unicorns', {gid: 0}); expectError(await execa('unicorns', {gid: '0'})); expectError(execaSync('unicorns', {gid: '0'})); @@ -149,8 +149,8 @@ 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: '/bin/sh'}); +execaSync('unicorns', {shell: '/bin/sh'}); await execa('unicorns', {shell: fileUrl}); execaSync('unicorns', {shell: fileUrl}); expectError(await execa('unicorns', {shell: {}})); @@ -158,15 +158,15 @@ 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}); +await execa('unicorns', {timeout: 1000}); +execaSync('unicorns', {timeout: 1000}); 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}); +await execa('unicorns', {maxBuffer: 1000}); +execaSync('unicorns', {maxBuffer: 1000}); expectError(await execa('unicorns', {maxBuffer: '1000'})); expectError(execaSync('unicorns', {maxBuffer: '1000'})); @@ -176,8 +176,8 @@ 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}); +await execa('unicorns', {killSignal: 9}); +execaSync('unicorns', {killSignal: 9}); expectError(await execa('unicorns', {killSignal: false})); expectError(execaSync('unicorns', {killSignal: false})); expectError(await execa('unicorns', {killSignal: 'Sigterm'})); diff --git a/test-d/arguments/specific.test-d.ts b/test-d/arguments/specific.test-d.ts index aafff607b4..acc33d0245 100644 --- a/test-d/arguments/specific.test-d.ts +++ b/test-d/arguments/specific.test-d.ts @@ -9,6 +9,7 @@ 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: {fd3: 0}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); await execa('unicorns', {maxBuffer: {ipc: 0}}); expectError(await execa('unicorns', {maxBuffer: {stdout: '0'}})); @@ -21,6 +22,7 @@ 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: {fd3: 0}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); execaSync('unicorns', {maxBuffer: {ipc: 0}}); expectError(execaSync('unicorns', {maxBuffer: {stdout: '0'}})); @@ -33,6 +35,7 @@ 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: {fd3: 'none'}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); await execa('unicorns', {verbose: {ipc: 'none'}}); expectError(await execa('unicorns', {verbose: {stdout: 'other'}})); @@ -45,6 +48,7 @@ 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: {fd3: 'none'}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); execaSync('unicorns', {verbose: {ipc: 'none'}}); expectError(execaSync('unicorns', {verbose: {stdout: 'other'}})); @@ -57,6 +61,7 @@ await execa('unicorns', {stripFinalNewline: {stdout: true, stderr: true} as cons 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: {fd3: true}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); await execa('unicorns', {stripFinalNewline: {ipc: true}}); expectError(await execa('unicorns', {stripFinalNewline: {stdout: 'true'}})); @@ -69,6 +74,7 @@ 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: {fd3: true}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); execaSync('unicorns', {stripFinalNewline: {ipc: true}}); expectError(execaSync('unicorns', {stripFinalNewline: {stdout: 'true'}})); @@ -81,6 +87,7 @@ 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: {fd3: true}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); await execa('unicorns', {lines: {ipc: true}}); expectError(await execa('unicorns', {lines: {stdout: 'true'}})); @@ -93,6 +100,7 @@ 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: {fd3: true}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); execaSync('unicorns', {lines: {ipc: true}}); expectError(execaSync('unicorns', {lines: {stdout: 'true'}})); @@ -105,6 +113,7 @@ 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: {fd3: true}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); await execa('unicorns', {buffer: {ipc: true}}); expectError(await execa('unicorns', {buffer: {stdout: 'true'}})); @@ -117,6 +126,7 @@ 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: {fd3: true}, stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); execaSync('unicorns', {buffer: {ipc: true}}); expectError(execaSync('unicorns', {buffer: {stdout: 'true'}})); diff --git a/test-d/ipc/message.test-d.ts b/test-d/ipc/message.test-d.ts index 4671dc1e7e..b451076854 100644 --- a/test-d/ipc/message.test-d.ts +++ b/test-d/ipc/message.test-d.ts @@ -62,15 +62,15 @@ 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(NaN); +expectAssignable(NaN); +expectAssignable>(NaN); +expectAssignable>(NaN); + +await sendMessage(Infinity); +expectAssignable(Infinity); +expectAssignable>(Infinity); +expectAssignable>(Infinity); await sendMessage(new Map()); expectAssignable(new Map()); diff --git a/test-d/stdio/option/fd-integer-0.test-d.ts b/test-d/stdio/option/fd-integer-0.test-d.ts index caba702ef6..987c48006f 100644 --- a/test-d/stdio/option/fd-integer-0.test-d.ts +++ b/test-d/stdio/option/fd-integer-0.test-d.ts @@ -38,8 +38,8 @@ expectAssignable([0]); expectNotAssignable(0.5); expectNotAssignable(-1); -expectNotAssignable(Number.POSITIVE_INFINITY); -expectNotAssignable(Number.NaN); +expectNotAssignable(Infinity); +expectNotAssignable(NaN); expectNotAssignable(0); expectNotAssignable(0); @@ -48,5 +48,5 @@ expectNotAssignable([0]); expectNotAssignable(0.5); expectNotAssignable(-1); -expectNotAssignable(Number.POSITIVE_INFINITY); -expectNotAssignable(Number.NaN); +expectNotAssignable(Infinity); +expectNotAssignable(NaN); diff --git a/test/arguments/encoding-option.js b/test/arguments/encoding-option.js index e1f4073e49..320dc88792 100644 --- a/test/arguments/encoding-option.js +++ b/test/arguments/encoding-option.js @@ -21,7 +21,7 @@ test('cannot pass encoding: false', testInvalidEncoding, false, UNKNOWN_ENCODING 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 */ +/* eslint-disable unicorn/text-encoding-identifier-case -- intentionally testing invalid encoding casing */ 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); diff --git a/test/arguments/escape-no-icu.js b/test/arguments/escape-no-icu.js index 424abb5d38..cc3e5c70b4 100644 --- a/test/arguments/escape-no-icu.js +++ b/test/arguments/escape-no-icu.js @@ -1,6 +1,9 @@ // Mimics Node.js when built without ICU support // See https://github.com/sindresorhus/execa/issues/1143 +// eslint-disable-next-line unicorn/no-global-object-property-assignment -- intentionally mocking the global `RegExp` globalThis.RegExp = class extends RegExp { + static isMocked = true; + constructor(regExpString, flags) { if (flags?.includes('u') && regExpString.includes('\\p{')) { throw new Error('Invalid property name'); @@ -8,8 +11,6 @@ globalThis.RegExp = class extends RegExp { super(regExpString, flags); } - - static isMocked = true; }; // Execa computes the RegExp when first loaded, so we must delay this import diff --git a/test/arguments/escape.js b/test/arguments/escape.js index f2e2d7560a..62614a300e 100644 --- a/test/arguments/escape.js +++ b/test/arguments/escape.js @@ -23,6 +23,7 @@ test(testResultCommand, ''); // eslint-disable-next-line max-params const testEscapedCommand = async (t, commandArguments, expectedUnix, expectedWindows, expectedUnixNoIcu = expectedUnix, expectedWindowsNoIcu = expectedWindows) => { + // eslint-disable-next-line no-use-extend-native/no-use-extend-native -- `RegExp.isMocked` is a static property added by `escape-no-icu.js`, not a native extension const expected = RegExp.isMocked ? (isWindows ? expectedWindowsNoIcu : expectedUnixNoIcu) : (isWindows ? expectedWindows : expectedUnix); @@ -80,24 +81,24 @@ 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 - \\a', testEscapedCommand, ['\u{7}'], '\'\\u0007\'', '"\\u0007"'); test('result.escapedCommand - \\b', testEscapedCommand, ['\b'], '\'\\b\'', '"\\b"'); -test('result.escapedCommand - \\e', testEscapedCommand, ['\u001B'], '\'\\u001b\'', '"\\u001b"'); +test('result.escapedCommand - \\e', testEscapedCommand, ['\u{1B}'], '\'\\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 - \\x01', testEscapedCommand, ['\u{1}'], '\'\\u0001\'', '"\\u0001"'); +test('result.escapedCommand - \\x7f', testEscapedCommand, ['\u{7F}'], '\'\\u007f\'', '"\\u007f"'); +test('result.escapedCommand - \\u0085', testEscapedCommand, ['\u{85}'], '\'\\u0085\'', '"\\u0085"'); +test('result.escapedCommand - \\u2000', testEscapedCommand, ['\u{2000}'], '\'\\u2000\'', '"\\u2000"'); +test('result.escapedCommand - \\u200E', testEscapedCommand, ['\u{200E}'], '\'\\u200e\'', '"\\u200e"', '\'\u{200E}\'', '"\u{200E}"'); +test('result.escapedCommand - \\u2028', testEscapedCommand, ['\u{2028}'], '\'\\u2028\'', '"\\u2028"'); +test('result.escapedCommand - \\u2029', testEscapedCommand, ['\u{2029}'], '\'\\u2029\'', '"\\u2029"'); +test('result.escapedCommand - \\u5555', testEscapedCommand, ['\u{5555}'], '\'\u{5555}\'', '"\u{5555}"'); +test('result.escapedCommand - \\uD800', testEscapedCommand, ['\u{D800}'], '\'\\ud800\'', '"\\ud800"', '\'\u{D800}\'', '"\u{D800}"'); +test('result.escapedCommand - \\uE000', testEscapedCommand, ['\u{E000}'], '\'\\ue000\'', '"\\ue000"', '\'\u{E000}\'', '"\u{E000}"'); 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}"'); diff --git a/test/fixtures/graceful-disconnect.js b/test/fixtures/graceful-disconnect.js index f31aebcf49..b98df3faab 100755 --- a/test/fixtures/graceful-disconnect.js +++ b/test/fixtures/graceful-disconnect.js @@ -1,8 +1,8 @@ #!/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'; +import {getCancelSignal, sendMessage} from 'execa'; const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); diff --git a/test/fixtures/graceful-print.js b/test/fixtures/graceful-print.js index a86e98e811..ba8a9ef54a 100755 --- a/test/fixtures/graceful-print.js +++ b/test/fixtures/graceful-print.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import {getCancelSignal} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; +import {getCancelSignal} from 'execa'; const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); diff --git a/test/fixtures/graceful-send-echo.js b/test/fixtures/graceful-send-echo.js index d79e2c52db..743e41fa88 100755 --- a/test/fixtures/graceful-send-echo.js +++ b/test/fixtures/graceful-send-echo.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import {getCancelSignal, getOneMessage, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; +import {getCancelSignal, getOneMessage, sendMessage} from 'execa'; const message = await getOneMessage(); const cancelSignal = await getCancelSignal(); diff --git a/test/fixtures/graceful-send-print.js b/test/fixtures/graceful-send-print.js index 77b32c1194..c444437cee 100755 --- a/test/fixtures/graceful-send-print.js +++ b/test/fixtures/graceful-send-print.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; +import {getCancelSignal, sendMessage} from 'execa'; const cancelSignal = await getCancelSignal(); await sendMessage(cancelSignal.aborted); diff --git a/test/fixtures/graceful-send-string.js b/test/fixtures/graceful-send-string.js index 54458fcf22..da470c99cc 100755 --- a/test/fixtures/graceful-send-string.js +++ b/test/fixtures/graceful-send-string.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import {getCancelSignal, sendMessage} from 'execa'; import {foobarString} from '../helpers/input.js'; +import {getCancelSignal, sendMessage} from 'execa'; await getCancelSignal(); await sendMessage(foobarString); diff --git a/test/fixtures/graceful-send-twice.js b/test/fixtures/graceful-send-twice.js index 075ae83d7c..b9c1f27887 100755 --- a/test/fixtures/graceful-send-twice.js +++ b/test/fixtures/graceful-send-twice.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; +import {getCancelSignal, sendMessage} from 'execa'; const cancelSignal = await getCancelSignal(); await sendMessage(cancelSignal.aborted); diff --git a/test/fixtures/graceful-send.js b/test/fixtures/graceful-send.js index adebca9aae..8434cdcfb9 100755 --- a/test/fixtures/graceful-send.js +++ b/test/fixtures/graceful-send.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; +import {getCancelSignal, sendMessage} from 'execa'; const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); diff --git a/test/fixtures/graceful-twice.js b/test/fixtures/graceful-twice.js index 8cf5ceb0fc..dfcfc26bad 100755 --- a/test/fixtures/graceful-twice.js +++ b/test/fixtures/graceful-twice.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import {getCancelSignal, sendMessage} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; +import {getCancelSignal, sendMessage} from 'execa'; await getCancelSignal(); const cancelSignal = await getCancelSignal(); diff --git a/test/fixtures/graceful-wait.js b/test/fixtures/graceful-wait.js index f21c0a8e22..4d74caec00 100755 --- a/test/fixtures/graceful-wait.js +++ b/test/fixtures/graceful-wait.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import {getCancelSignal} from 'execa'; import {onAbortedSignal} from '../helpers/graceful.js'; +import {getCancelSignal} from 'execa'; const cancelSignal = await getCancelSignal(); await onAbortedSignal(cancelSignal); diff --git a/test/fixtures/ipc-iterate-error.js b/test/fixtures/ipc-iterate-error.js index 9f10cc9c52..43f0df51c0 100755 --- a/test/fixtures/ipc-iterate-error.js +++ b/test/fixtures/ipc-iterate-error.js @@ -14,7 +14,7 @@ const echoMessages = async () => { }; process.on('error', () => {}); -// eslint-disable-next-line unicorn/prefer-top-level-await + const promise = echoMessages(); process.emit('error', new Error(foobarString)); await promise; diff --git a/test/fixtures/ipc-iterate-twice.js b/test/fixtures/ipc-iterate-twice.js index 9e9a086100..c862a515ba 100755 --- a/test/fixtures/ipc-iterate-twice.js +++ b/test/fixtures/ipc-iterate-twice.js @@ -8,6 +8,7 @@ for (let index = 0; index < 2; index += 1) { // eslint-disable-next-line no-await-in-loop for await (const message of getEachMessage()) { if (message === foobarString) { + // eslint-disable-next-line unicorn/no-break-in-nested-loop -- breaks only the inner loop, the outer loop must continue break; } diff --git a/test/fixtures/ipc-print-many-each.js b/test/fixtures/ipc-print-many-each.js index 0712f802b7..49abc01502 100755 --- a/test/fixtures/ipc-print-many-each.js +++ b/test/fixtures/ipc-print-many-each.js @@ -8,6 +8,7 @@ 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); + // eslint-disable-next-line unicorn/no-break-in-nested-loop -- breaks only the inner loop, the outer loop must continue break; } } diff --git a/test/helpers/convert.js b/test/helpers/convert.js index f4e3e9dcb1..f7c63357b5 100644 --- a/test/helpers/convert.js +++ b/test/helpers/convert.js @@ -3,7 +3,7 @@ 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'; +import {foobarString} from './input.js'; export const arrayFromAsync = async (asyncIterable, lines = []) => { for await (const line of asyncIterable) { diff --git a/test/helpers/encoding.js b/test/helpers/encoding.js index 89d7c01a01..9bdd175057 100644 --- a/test/helpers/encoding.js +++ b/test/helpers/encoding.js @@ -4,4 +4,4 @@ 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'; +export const brokenSymbol = '\u{FFFD}'; diff --git a/test/helpers/fixtures-directory.js b/test/helpers/fixtures-directory.js index e55cae7e25..cbe29ed7d6 100644 --- a/test/helpers/fixtures-directory.js +++ b/test/helpers/fixtures-directory.js @@ -5,6 +5,7 @@ import logProcessErrors from 'log-process-errors'; import pathKey from 'path-key'; // Make tests fail if any warning (such as a deprecation warning) is emitted +// eslint-disable-next-line unicorn/no-top-level-side-effects -- must run as soon as this helper is imported logProcessErrors({ onError(error, event) { if (event === 'warning') { diff --git a/test/helpers/input.js b/test/helpers/input.js index 509fcc019a..c30e8b8ba4 100644 --- a/test/helpers/input.js +++ b/test/helpers/input.js @@ -4,7 +4,7 @@ import {inspect} from 'node:util'; const textEncoder = new TextEncoder(); export const foobarString = 'foobar'; -export const foobarRed = `\u001B[31m${foobarString}\u001B[39m`; +export const foobarRed = `\u{1B}[31m${foobarString}\u{1B}[39m`; export const foobarArray = ['foo', 'bar']; export const foobarUint8Array = textEncoder.encode(foobarString); export const foobarArrayBuffer = foobarUint8Array.buffer; diff --git a/test/helpers/node-version.js b/test/helpers/node-version.js index bbe49ab3c9..b3b56ac724 100644 --- a/test/helpers/node-version.js +++ b/test/helpers/node-version.js @@ -1,3 +1,3 @@ import {version} from 'node:process'; -export const majorNodeVersion = Number(version.split('.')[0].slice(1)); +export const majorNodeVersion = Number(version.split('.', 1)[0].slice(1)); diff --git a/test/helpers/override-promise.js b/test/helpers/override-promise.js index 89540675c5..7542627296 100644 --- a/test/helpers/override-promise.js +++ b/test/helpers/override-promise.js @@ -1,5 +1,6 @@ // Can't use `test.before`, because `ava` needs `Promise`. const nativePromise = Promise; +// eslint-disable-next-line unicorn/no-global-object-property-assignment -- intentionally mocking the global `Promise` globalThis.Promise = class BrokenPromise { then() { // eslint-disable-line unicorn/no-thenable throw new Error('error'); @@ -7,5 +8,6 @@ globalThis.Promise = class BrokenPromise { }; export function restorePromise() { + // eslint-disable-next-line unicorn/no-global-object-property-assignment -- restoring the mocked global `Promise` globalThis.Promise = nativePromise; } diff --git a/test/helpers/verbose.js b/test/helpers/verbose.js index d334dcc8ee..51897b47f0 100644 --- a/test/helpers/verbose.js +++ b/test/helpers/verbose.js @@ -85,7 +85,7 @@ 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 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'}); diff --git a/test/helpers/wait.js b/test/helpers/wait.js index 6c3e8b849c..644b8bf31f 100644 --- a/test/helpers/wait.js +++ b/test/helpers/wait.js @@ -1,5 +1,5 @@ -import {getStdio} from '../helpers/stdio.js'; -import {noopGenerator} from '../helpers/generator.js'; +import {getStdio} from './stdio.js'; +import {noopGenerator} from './generator.js'; export const endOptionStream = ({stream}) => { stream.end(); diff --git a/test/io/output-async.js b/test/io/output-async.js index 081948418b..1af51f0612 100644 --- a/test/io/output-async.js +++ b/test/io/output-async.js @@ -41,9 +41,7 @@ test.serial('process.std* listeners are cleaned up on success with a single inpu 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])), - ); + const results = await Promise.all(Array.from({length: PARALLEL_COUNT}, () => execa('noop.js', [foobarString]))); t.true(results.every(({stdout}) => stdout === foobarString)); }); @@ -55,9 +53,7 @@ const testMaxListeners = async (t, isMultiple, maxListenersCount) => { } try { - const results = await Promise.all( - Array.from({length: PARALLEL_COUNT}, () => execa('empty.js', getComplexStdio(isMultiple))), - ); + 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(); @@ -74,10 +70,10 @@ const testMaxListeners = async (t, isMultiple, maxListenersCount) => { 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 Infinity and ["pipe", "inherit"]', testMaxListeners, false, 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 Infinity and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, Infinity); test.serial('No warning with maxListeners 0 and ["pipe", "inherit"], multiple inputs', testMaxListeners, true, 0); diff --git a/test/ipc/buffer-messages.js b/test/ipc/buffer-messages.js index 28b87525b4..a573980854 100644 --- a/test/ipc/buffer-messages.js +++ b/test/ipc/buffer-messages.js @@ -79,10 +79,8 @@ test('Sets empty error.ipcOutput, sync', t => { }); 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}`]); - }), - ); + 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}`]); + })); }); diff --git a/test/ipc/get-one.js b/test/ipc/get-one.js index 4552151e31..dfad7e7ccd 100644 --- a/test/ipc/get-one.js +++ b/test/ipc/get-one.js @@ -55,13 +55,11 @@ test('Throwing from exports.getOneMessage() filter disconnects', async t => { }); 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; - }), - ); + 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) => { diff --git a/test/ipc/send.js b/test/ipc/send.js index 0375769b76..f1bc212761 100644 --- a/test/ipc/send.js +++ b/test/ipc/send.js @@ -15,14 +15,12 @@ test('Can exchange IPC messages', async t => { }); 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; - }), - ); + 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 => { @@ -87,7 +85,6 @@ test('Disconnects IPC on subprocess.sendMessage() error', async t => { // 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()); @@ -99,7 +96,7 @@ const findEpipeError = async t => { 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('.'); diff --git a/test/methods/command.js b/test/methods/command.js index e68417a1ea..1e65a7b68e 100644 --- a/test/methods/command.js +++ b/test/methods/command.js @@ -195,5 +195,6 @@ test('parseCommandString() can get empty strings', t => { }); test('parseCommandString() can get only whitespaces', t => { + // eslint-disable-next-line unicorn/prefer-string-repeat t.deepEqual(parseCommandString(' '), []); }); diff --git a/test/methods/parameters-options.js b/test/methods/parameters-options.js index 2c7119d003..8c8535b52d 100644 --- a/test/methods/parameters-options.js +++ b/test/methods/parameters-options.js @@ -21,40 +21,40 @@ const testSerializeArgument = async (t, commandArgument, execaMethod) => { 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 NaN', testSerializeArgument, NaN, execa); +test('execa()\'s arguments can be Infinity', testSerializeArgument, 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 NaN', testSerializeArgument, NaN, execaSync); +test('execaSync()\'s arguments can be Infinity', testSerializeArgument, 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 NaN', testSerializeArgument, NaN, execaNode); +test('execaNode()\'s arguments can be Infinity', testSerializeArgument, 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 NaN', testSerializeArgument, NaN, $); +test('$\'s arguments can be Infinity', testSerializeArgument, 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 NaN', testSerializeArgument, NaN, $.sync); +test('$.sync\'s arguments can be Infinity', testSerializeArgument, 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); diff --git a/test/methods/promise.js b/test/methods/promise.js index ded4b2bea8..389eb20015 100644 --- a/test/methods/promise.js +++ b/test/methods/promise.js @@ -16,7 +16,7 @@ test('finally function is executed on success', async t => { const {stdout} = await execa('noop.js', ['foo']).finally(() => { isCalled = true; }); - t.is(isCalled, true); + t.true(isCalled); t.is(stdout, 'foo'); }); @@ -25,7 +25,7 @@ test('finally function is executed on failure', async t => { const {stdout, stderr} = await t.throwsAsync(execa('exit.js', ['2']).finally(() => { isError = true; })); - t.is(isError, true); + t.true(isError); t.is(typeof stdout, 'string'); t.is(typeof stderr, 'string'); }); diff --git a/test/methods/template.js b/test/methods/template.js index c67f536759..0f4f4fee49 100644 --- a/test/methods/template.js +++ b/test/methods/template.js @@ -75,6 +75,7 @@ test('$ splits expressions - " " (2 spaces)', testScriptStdout, () => $`echo.js 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 `, ''); +// eslint-disable-next-line unicorn/prefer-string-repeat 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'); @@ -114,25 +115,25 @@ test('$ concatenates tokens - \\n (escape)', testScriptStdout, () => $`echo.js \ 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('$ splits expressions - \\r (no escape)', testScriptStdout, () => escapedCall('echo.js a\rb'), '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('$ concatenates expressions - \\r (no escape)', testScriptStdout, () => escapedCall('echo.js \ra\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('$ splits expressions - \\r\\n (no escape)', testScriptStdout, () => escapedCall('echo.js a\r\nb'), '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('$ concatenates expressions - \\r\\n (no escape)', testScriptStdout, () => escapedCall('echo.js \r\na\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 */ +/* eslint-disable no-irregular-whitespace -- intentionally testing a literal form feed character */ 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'); @@ -169,12 +170,12 @@ 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 - \\u2028', testScriptStdout, () => $`echo.js \u2028`, '\u{2028}'); +test('$ handles expressions - \\u2028', testScriptStdout, () => $`echo.js ${'\u{2028}'}`, '\u{2028}'); +test('$ splits tokens - \\u2028', testScriptStdout, () => $`echo.js a\u2028b`, 'a\u{2028}b'); +test('$ splits expressions - \\u2028', testScriptStdout, () => $`echo.js ${'a'}\u2028${'b'}`, 'a\u{2028}b'); +test('$ concatenates tokens - \\u2028', testScriptStdout, () => $`echo.js \u2028a\u2028 b`, '\u{2028}a\u{2028}\nb'); +test('$ concatenates expressions - \\u2028', testScriptStdout, () => $`echo.js \u2028${'a'}\u2028 b`, '\u{2028}a\u{2028}\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'); @@ -190,23 +191,23 @@ 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{1}', testScriptStdout, () => $`echo.js \u{1}`, '\u{1}'); +test('$ splits tokens - \\u{1}', testScriptStdout, () => $`echo.js a\u{1}b`, 'a\u{1}b'); +test('$ splits expressions - \\u{1}', testScriptStdout, () => $`echo.js ${'a'}\u{1}${'b'}`, 'a\u{1}b'); +test('$ concatenates tokens - \\u{1}', testScriptStdout, () => $`echo.js \u{1}a\u{1} b`, '\u{1}a\u{1}\nb'); +test('$ concatenates expressions - \\u{1}', testScriptStdout, () => $`echo.js \u{1}${'a'}\u{1} b`, '\u{1}a\u{1}\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'); @@ -288,11 +289,10 @@ test('$ handles invalid escape sequence - \\ug', testInvalidSequence, () => $`ec 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/}); diff --git a/test/resolve/exit.js b/test/resolve/exit.js index 84c0a00ed1..56fb824c8e 100644 --- a/test/resolve/exit.js +++ b/test/resolve/exit.js @@ -13,9 +13,7 @@ test('exitCode is 0 on success', async t => { }); const testExitCode = async (t, expectedExitCode) => { - const {exitCode, originalMessage, shortMessage, message} = await t.throwsAsync( - execa('exit.js', [`${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}`); diff --git a/test/return/early-error.js b/test/return/early-error.js index fd545b1911..bf6513b9b7 100644 --- a/test/return/early-error.js +++ b/test/return/early-error.js @@ -26,7 +26,6 @@ test('execaSync() throws error if ENOENT', t => { 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', () => {}); @@ -65,12 +64,15 @@ if (!isWindows) { 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 + t.plan(1); + + // 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) { + // eslint-disable-next-line ava/no-conditional-assertion -- either outcome is acceptable, see comment above t.is(error.code, 'EPIPE'); } }); diff --git a/test/return/message.js b/test/return/message.js index ddbcde0976..596fb036c5 100644 --- a/test/return/message.js +++ b/test/return/message.js @@ -73,7 +73,8 @@ test('error.message does not contain stdout if it is an object', testPartialIgno 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)); + const result = await t.throwsAsync(execa('echo-fail.js', options)); + const message = result[resultProperty]; t.false(message.includes('stderr')); t.false(message.includes('stdout')); t.false(message.includes('fd3')); diff --git a/test/return/result.js b/test/return/result.js index 30d8d4cfcb..78b338a8a9 100644 --- a/test/return/result.js +++ b/test/return/result.js @@ -97,11 +97,11 @@ test('error.isTerminated is true if subprocess was killed indirectly', async t = // `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.false(isTerminated); t.is(signal, undefined); } else { const {isTerminated, signal} = await t.throwsAsync(subprocess, {message: /was killed with SIGINT/}); - t.is(isTerminated, true); + t.true(isTerminated); t.is(signal, 'SIGINT'); } }); diff --git a/test/terminate/kill-force.js b/test/terminate/kill-force.js index 8b4e36748f..961e048e4e 100644 --- a/test/terminate/kill-force.js +++ b/test/terminate/kill-force.js @@ -45,7 +45,7 @@ const testInvalidForceKill = async (t, forceKillAfterDelay) => { }, {instanceOf: TypeError, message: /non-negative integer/}); }; -test('`forceKillAfterDelay` should not be NaN', testInvalidForceKill, Number.NaN); +test('`forceKillAfterDelay` should not be NaN', testInvalidForceKill, 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). diff --git a/test/terminate/kill-signal.js b/test/terminate/kill-signal.js index 99937c80c1..26c500d2e5 100644 --- a/test/terminate/kill-signal.js +++ b/test/terminate/kill-signal.js @@ -1,16 +1,12 @@ 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); @@ -61,20 +57,10 @@ test('Can call `.kill()` multiple times', async t => { 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); - } + t.is(exitCode, undefined); + t.true(isTerminated); + t.is(signal, 'SIGTERM'); + t.is(code, undefined); }); test('execa() returns a promise with kill()', async t => { diff --git a/test/terminate/signal.js b/test/terminate/signal.js index 96d5114b7a..bc24752ed4 100644 --- a/test/terminate/signal.js +++ b/test/terminate/signal.js @@ -36,8 +36,8 @@ 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: Infinity', testInvalidKillSignal, Infinity, 'other', execa); +test('Cannot use killSignal: NaN', testInvalidKillSignal, 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); @@ -49,8 +49,8 @@ test('Cannot use killSignal: "sigterm", sync', testInvalidKillSignal, 'sigterm', 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: Infinity, sync', testInvalidKillSignal, Infinity, 'other', execaSync); +test('Cannot use killSignal: NaN, sync', testInvalidKillSignal, 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); @@ -79,8 +79,8 @@ 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(Infinity)', testInvalidSignalArgument, Infinity, 'other'); +test('Cannot use subprocess.kill(NaN)', testInvalidSignalArgument, 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'); diff --git a/test/transform/encoding-final.js b/test/transform/encoding-final.js index ff66b223aa..4a28afb693 100644 --- a/test/transform/encoding-final.js +++ b/test/transform/encoding-final.js @@ -41,7 +41,7 @@ const compareValues = (t, value, encoding) => { }; // This string gives different outputs with each encoding type -const STRING_TO_ENCODE = '\u1000.'; +const STRING_TO_ENCODE = '\u{1000}.'; const BUFFER_TO_ENCODE = Buffer.from(STRING_TO_ENCODE); test('can pass encoding "buffer" to stdout', checkEncoding, 'buffer', 1, execa); diff --git a/test/transform/encoding-multibyte.js b/test/transform/encoding-multibyte.js index 62d2efa845..02be2cff05 100644 --- a/test/transform/encoding-multibyte.js +++ b/test/transform/encoding-multibyte.js @@ -22,6 +22,7 @@ const testMultibyteCharacters = async (t, objectMode, addNoopTransform, execaMet if (objectMode) { t.deepEqual(stdout, foobarArray); } else { + // eslint-disable-next-line n/prefer-global/buffer, unicorn/prefer-uint8array-base64 -- `Uint8Array#toBase64()` is not available on the minimum supported Node.js version t.is(stdout, Buffer.from(foobarArray.join('')).toString('base64')); } }; diff --git a/test/transform/split-lines.js b/test/transform/split-lines.js index 05f633369e..603e3c6b51 100644 --- a/test/transform/split-lines.js +++ b/test/transform/split-lines.js @@ -31,7 +31,7 @@ const emptyChunks = [emptyFull]; const manyEmptyChunks = [emptyFull, emptyFull, emptyFull]; const newlineFull = '\n'; const newlineChunks = [newlineFull]; -const newlinesFull = '\n\n\n'; +const newlinesFull = '\n'.repeat(3); const newlinesChunks = [newlinesFull]; const newlinesLines = ['\n', '\n', '\n']; const windowsNewlinesFull = '\r\n\r\n\r\n'; diff --git a/test/verbose/error.js b/test/verbose/error.js index 65bad87371..047b1e5f1e 100644 --- a/test/verbose/error.js +++ b/test/verbose/error.js @@ -1,6 +1,6 @@ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; +import {foobarString, foobarRed} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { QUOTE, @@ -27,8 +27,6 @@ import { setFixtureDirectory(); -const redFoobarString = `\u001B[31m${foobarString}\u001B[39m`; - const testPrintError = async (t, verbose, isSync) => { const stderr = await runErrorSubprocess(t, verbose, isSync, getStdioForFd3Option(verbose)); t.is(getErrorLine(stderr), `${testTimestamp} [0] × Command failed with exit code 2: noop-fail.js 1 ${foobarString}`); @@ -152,7 +150,7 @@ test('Does not escape internal characters from error', async t => { }); test('Escapes and strips color sequences from error', async t => { - const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', [redFoobarString], {parentFixture: 'nested-fail.js', verbose: 'short'})); + const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', [foobarRed], {parentFixture: 'nested-fail.js', verbose: 'short'})); 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}`, @@ -160,7 +158,7 @@ test('Escapes and strips color sequences from error', async t => { }); test('Escapes control characters from error', async t => { - const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['\u0001'], {parentFixture: 'nested-fail.js', verbose: 'short'})); + const {stderr} = await t.throwsAsync(nestedSubprocess('noop-forever.js', ['\u{1}'], {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`, diff --git a/test/verbose/ipc.js b/test/verbose/ipc.js index 41986e8e67..61d9e1a502 100644 --- a/test/verbose/ipc.js +++ b/test/verbose/ipc.js @@ -1,10 +1,9 @@ import {on} from 'node:events'; import {inspect} from 'node:util'; import test from 'ava'; -import {red} from 'yoctocolors'; import {execa} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarObject} from '../helpers/input.js'; +import {foobarString, foobarRed, foobarObject} from '../helpers/input.js'; import {nestedSubprocess, nestedInstance} from '../helpers/nested.js'; import {fullStdio} from '../helpers/stdio.js'; import { @@ -99,20 +98,23 @@ test('Does not escape internal characters from IPC', async t => { }); 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', NO_COLOR: undefined}}); + const {stderr} = await nestedSubprocess('ipc-send.js', [foobarRed], {ipc: true, verbose: 'full'}); 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'}); + const {stderr} = await nestedSubprocess('ipc-send.js', ['\u{1}'], {ipc: true, verbose: 'full'}); t.is(getIpcLine(stderr), `${testTimestamp} [0] * \\u0001`); }); test('Prints IPC progressively', async t => { + t.plan(2); + 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) { + // eslint-disable-next-line ava/no-conditional-assertion -- `t.plan()` ensures this always executes t.is(ipcLine, `${testTimestamp} [0] * ${foobarString}`); break; } diff --git a/test/verbose/log.js b/test/verbose/log.js index 778a01603e..5f1b5eee87 100644 --- a/test/verbose/log.js +++ b/test/verbose/log.js @@ -30,8 +30,7 @@ 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}), - )); + nestedSubprocess('noop-fd.js', ['2', `${foobarString}\n`], {verbose: 'full', stderr: 'inherit'}, {all: true}))); for (const {all} of results) { t.deepEqual( getNormalizedLines(all), diff --git a/test/verbose/output-enable.js b/test/verbose/output-enable.js index a0e6bc489f..79a01f40d7 100644 --- a/test/verbose/output-enable.js +++ b/test/verbose/output-enable.js @@ -1,7 +1,6 @@ import test from 'ava'; -import {red} from 'yoctocolors'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString, foobarUtf16Uint8Array} from '../helpers/input.js'; +import {foobarString, foobarRed, foobarUtf16Uint8Array} from '../helpers/input.js'; import {fullStdio} from '../helpers/stdio.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { @@ -106,12 +105,12 @@ test('Does not escape internal characters from stdout', async t => { }); test('Strips color sequences from stdout', async t => { - const {stderr} = await nestedSubprocess('noop.js', [red(foobarString)], {verbose: 'full'}, {env: {FORCE_COLOR: '1', NO_COLOR: undefined}}); + const {stderr} = await nestedSubprocess('noop.js', [foobarRed], {verbose: 'full'}); t.is(getOutputLine(stderr), `${testTimestamp} [0] ${foobarString}`); }); test('Escapes control characters from stdout', async t => { - const {stderr} = await nestedSubprocess('noop.js', ['\u0001'], {verbose: 'full'}); + const {stderr} = await nestedSubprocess('noop.js', ['\u{1}'], {verbose: 'full'}); t.is(getOutputLine(stderr), `${testTimestamp} [0] \\u0001`); }); diff --git a/test/verbose/output-progressive.js b/test/verbose/output-progressive.js index ee7b4694ac..03a585c238 100644 --- a/test/verbose/output-progressive.js +++ b/test/verbose/output-progressive.js @@ -8,11 +8,14 @@ import {getOutputLine, getOutputLines, testTimestamp} from '../helpers/verbose.j setFixtureDirectory(); test('Prints stdout one line at a time', async t => { + t.plan(1); + 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) { + // eslint-disable-next-line ava/no-conditional-assertion -- `t.plan()` ensures this always executes t.is(outputLine, `${testTimestamp} [0] ${foobarString}`); break; } diff --git a/test/verbose/start.js b/test/verbose/start.js index ec12438ea0..2ecba41932 100644 --- a/test/verbose/start.js +++ b/test/verbose/start.js @@ -1,6 +1,6 @@ import test from 'ava'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; -import {foobarString} from '../helpers/input.js'; +import {foobarString, foobarRed} from '../helpers/input.js'; import {nestedSubprocess} from '../helpers/nested.js'; import { QUOTE, @@ -27,8 +27,6 @@ import { setFixtureDirectory(); -const redFoobarString = `\u001B[31m${foobarString}\u001B[39m`; - const testPrintCommand = async (t, verbose, worker, isSync) => { const {stderr} = await nestedSubprocess('noop.js', [foobarString], { verbose, @@ -159,11 +157,11 @@ test('Does not escape internal characters from command', async t => { }); test('Escapes color sequences from command', async t => { - const {stderr} = await nestedSubprocess('noop.js', [redFoobarString], {verbose: 'short'}); + const {stderr} = await nestedSubprocess('noop.js', [foobarRed], {verbose: 'short'}); 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'}); + const {stderr} = await nestedSubprocess('noop.js', ['\u{1}'], {verbose: 'short'}); t.is(getCommandLine(stderr), `${testTimestamp} [0] $ noop.js ${QUOTE}\\u0001${QUOTE}`); }); diff --git a/tsconfig.json b/tsconfig.json index 5f02c88274..467d498f8e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,8 @@ "module": "nodenext", "moduleResolution": "nodenext", "target": "ES2022", - "strict": true + "strict": true, + "types": ["node"] }, "files": [ "index.d.ts" diff --git a/types/arguments/encoding-option.d.ts b/types/arguments/encoding-option.d.ts index 04712534c5..1a457ef6b5 100644 --- a/types/arguments/encoding-option.d.ts +++ b/types/arguments/encoding-option.d.ts @@ -1,16 +1,16 @@ type DefaultEncodingOption = 'utf8'; type TextEncodingOption = - | DefaultEncodingOption - | 'utf16le'; + | DefaultEncodingOption + | 'utf16le'; export type BufferEncodingOption = 'buffer'; export type BinaryEncodingOption = - | BufferEncodingOption - | 'hex' - | 'base64' - | 'base64url' - | 'latin1' - | 'ascii'; + | BufferEncodingOption + | 'hex' + | 'base64' + | 'base64url' + | 'latin1' + | 'ascii'; // `options.encoding` export type EncodingOption = diff --git a/types/arguments/options.d.ts b/types/arguments/options.d.ts index 2719cc9b4f..5185ac49f1 100644 --- a/types/arguments/options.d.ts +++ b/types/arguments/options.d.ts @@ -150,7 +150,7 @@ export type CommonOptions = { 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. + - [`'hex'`](https://en.wikipedia.org/wiki/Hexadecimal), [`'base64'`](https://en.wikipedia.org/wiki/Base64), [`'base64url'`](https://en.wikipedia.org/wiki/Base64#RFC_4648), [`'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`. diff --git a/types/arguments/specific.d.ts b/types/arguments/specific.d.ts index e3293dd538..482caf9338 100644 --- a/types/arguments/specific.d.ts +++ b/types/arguments/specific.d.ts @@ -3,9 +3,7 @@ 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 GenericOptionObject = Readonly>>; type GenericFromOption = FromOption | 'ipc'; diff --git a/types/convert.d.ts b/types/convert.d.ts index 89162a4ecd..8876c96ead 100644 --- a/types/convert.d.ts +++ b/types/convert.d.ts @@ -50,11 +50,11 @@ export type SubprocessAsyncIterable< BinaryOption extends boolean | undefined, EncodingOption extends Options['encoding'], > = AsyncIterableIterator< -EncodingOption extends BinaryEncodingOption - ? Uint8Array - : BinaryOption extends true + EncodingOption extends BinaryEncodingOption ? Uint8Array - : string + : BinaryOption extends true + ? Uint8Array + : string >; export {}; diff --git a/types/ipc.d.ts b/types/ipc.d.ts index 72cfee28b8..2a9cef6901 100644 --- a/types/ipc.d.ts +++ b/types/ipc.d.ts @@ -136,9 +136,9 @@ export type IpcMethods< // 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 + OptionsType['ipc'], + 'ipcInput' extends keyof OptionsType ? OptionsType['ipcInput'] : undefined, + 'gracefulCancel' extends keyof OptionsType ? OptionsType['gracefulCancel'] : undefined >; type HasIpcOption< diff --git a/types/return/final-error.d.ts b/types/return/final-error.d.ts index 451986cc28..379a0d6dae 100644 --- a/types/return/final-error.d.ts +++ b/types/return/final-error.d.ts @@ -20,13 +20,13 @@ type CommonErrorProperty< // `result.*` defined only on failure, i.e. on `error.*` export type ErrorProperties = - | 'name' - | 'message' - | 'stack' - | 'cause' - | 'shortMessage' - | 'originalMessage' - | 'code'; + | 'name' + | 'message' + | 'stack' + | 'cause' + | 'shortMessage' + | 'originalMessage' + | 'code'; /** Result of a subprocess failed execution. diff --git a/types/return/result-all.d.ts b/types/return/result-all.d.ts index f5c9b868a2..01cda55124 100644 --- a/types/return/result-all.d.ts +++ b/types/return/result-all.d.ts @@ -13,10 +13,10 @@ type ResultAllProperty< OptionsType extends CommonOptions, > = AllOption extends true ? ResultStdio< - AllMainFd, - AllObjectFd, - AllLinesFd, - OptionsType + AllMainFd, + AllObjectFd, + AllLinesFd, + OptionsType > : undefined; diff --git a/types/return/result-ipc.d.ts b/types/return/result-ipc.d.ts index 7ed3363f55..5188e66a6d 100644 --- a/types/return/result-ipc.d.ts +++ b/types/return/result-ipc.d.ts @@ -11,9 +11,9 @@ export type ResultIpcOutput< > = IsSync extends true ? [] : ResultIpcAsync< - FdSpecificOption, - HasIpc>, - OptionsType['serialization'] + FdSpecificOption, + HasIpc>, + OptionsType['serialization'] >; type ResultIpcAsync< diff --git a/types/return/result-stdio.d.ts b/types/return/result-stdio.d.ts index 2365c5379d..438e8c8af1 100644 --- a/types/return/result-stdio.d.ts +++ b/types/return/result-stdio.d.ts @@ -11,8 +11,8 @@ type MapResultStdio< OptionsType extends CommonOptions, > = { -readonly [FdNumber in keyof StdioOptionsArrayType]: ResultStdioNotAll< - FdNumber extends string ? FdNumber : string, - OptionsType + FdNumber extends string ? FdNumber : string, + OptionsType > }; diff --git a/types/return/result-stdout.d.ts b/types/return/result-stdout.d.ts index 03e41c9dcc..a36e095f3e 100644 --- a/types/return/result-stdout.d.ts +++ b/types/return/result-stdout.d.ts @@ -17,10 +17,10 @@ export type ResultStdio< LinesFdNumber extends string, OptionsType extends CommonOptions, > = ResultStdioProperty< -ObjectFdNumber, -LinesFdNumber, -IgnoresResultOutput, -OptionsType + ObjectFdNumber, + LinesFdNumber, + IgnoresResultOutput, + OptionsType >; type ResultStdioProperty< @@ -31,9 +31,9 @@ type ResultStdioProperty< > = StreamOutputIgnored extends true ? undefined : ResultStdioItem< - IsObjectFd, - FdSpecificOption, - OptionsType['encoding'] + IsObjectFd, + FdSpecificOption, + OptionsType['encoding'] >; type ResultStdioItem< diff --git a/types/return/result.d.ts b/types/return/result.d.ts index efbe8dc679..a7f69713f9 100644 --- a/types/return/result.d.ts +++ b/types/return/result.d.ts @@ -184,9 +184,7 @@ export type SuccessResult< OptionsType extends CommonOptions = CommonOptions, > = InstanceType> & OmitErrorIfReject; -type OmitErrorIfReject = { - [ErrorProperty in ErrorProperties]: RejectOption extends false ? unknown : never -}; +type OmitErrorIfReject = Record; /** Result of a subprocess successful execution. diff --git a/types/stdio/type.d.ts b/types/stdio/type.d.ts index 63da0847ff..52c9d4c973 100644 --- a/types/stdio/type.d.ts +++ b/types/stdio/type.d.ts @@ -47,20 +47,13 @@ type CommonStdioOption< 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; + SimpleStdioOption | URL | GeneratorTransform | GeneratorTransformFull | Unless, IsArray>, 3 | 4 | 5 | 6 | 7 | 8 | 9> | Unless, 'ipc'> | Unless | {readonly file: string; readonly append?: boolean}; // Synchronous iterables excluding strings, Uint8Arrays and Arrays type IterableObject = Iterable -& object -& {readonly BYTES_PER_ELEMENT?: never} -& AndUnless; + & object + & AndUnless + & {readonly BYTES_PER_ELEMENT?: never}; // `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. @@ -116,8 +109,8 @@ type StdoutStderrSingleOption< IsExtra extends boolean, IsArray extends boolean, > = - | CommonStdioOption - | OutputStdioOption; + | CommonStdioOption + | OutputStdioOption; // `options.stdout|stderr` export type StdoutStderrOptionCommon< diff --git a/types/subprocess/subprocess.d.ts b/types/subprocess/subprocess.d.ts index d590fc369e..ec054ef89b 100644 --- a/types/subprocess/subprocess.d.ts +++ b/types/subprocess/subprocess.d.ts @@ -15,90 +15,91 @@ 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)). +type ExecaCustomSubprocess = + & IpcMethods, OptionsType['serialization']> + & PipableSubprocess + & { + /** + Process identifier ([PID](https://en.wikipedia.org/wiki/Process_identifier)). - This is `undefined` if the subprocess failed to spawn. - */ - pid?: number; + 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. + /** + 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>; + 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. + /** + 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>; + 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. + /** + 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>; + 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`. + /** + Stream combining/interleaving `subprocess.stdout` and `subprocess.stderr`. - This requires the `all` option to be `true`. + 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; + 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. + /** + 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; + 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. + /** + 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. + 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). + 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; + [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; + /** + 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; + /** + 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 readable stream. + */ + readable(readableOptions?: ReadableOptions): Readable; - /** - Converts the subprocess to a writable stream. - */ - writable(writableOptions?: WritableOptions): Writable; + /** + 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; + /** + Converts the subprocess to a duplex stream. + */ + duplex(duplexOptions?: DuplexOptions): Duplex; + }; /** [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with additional methods and properties. diff --git a/types/transform/normalize.d.ts b/types/transform/normalize.d.ts index 04ffa2e8ba..96841a03b6 100644 --- a/types/transform/normalize.d.ts +++ b/types/transform/normalize.d.ts @@ -6,11 +6,11 @@ import type {Unless} from '../utils.js'; // @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; + | Unless> + | Generator; type GeneratorFinal = () => -| Unless> -| Generator; + | Unless> + | Generator; export type TransformCommon = { /** @@ -24,7 +24,7 @@ A transform or an array of transforms can be passed to the `stdin`, `stdout`, `s A transform is either a generator function or a plain object with the following members. */ -export type GeneratorTransformFull = { +export type GeneratorTransformFull = TransformCommon & { /** Map or filter the input or output of the subprocess. */ @@ -44,16 +44,16 @@ export type GeneratorTransformFull = { 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 = { +export type DuplexTransform = TransformCommon & { readonly transform: Duplex; -} & TransformCommon; +}; // `options.std*: TransformStream` -export type WebTransform = { +export type WebTransform = TransformCommon & { readonly transform: TransformStream; -} & TransformCommon; +}; export {}; diff --git a/types/verbose.d.ts b/types/verbose.d.ts index 314f7baf34..762fba677e 100644 --- a/types/verbose.d.ts +++ b/types/verbose.d.ts @@ -3,10 +3,10 @@ import type {Options, SyncOptions} from './arguments/options.js'; import type {Result, SyncResult} from './return/result.js'; export type VerboseOption = FdGenericOption< -| 'none' -| 'short' -| 'full' -| VerboseFunction + | 'none' + | 'short' + | 'full' + | VerboseFunction >; type VerboseFunction = (verboseLine: string, verboseObject: MinimalVerboseObject) => string | void;