diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 709e96ba4b..dc2cede0d2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,21 +10,21 @@ jobs: fail-fast: false matrix: node-version: + - 18 - 16 - 14 - - 12 os: - ubuntu-latest - macos-latest # - windows-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm test - uses: codecov/codecov-action@v2 - if: matrix.os == 'ubuntu-latest' && matrix.node-version == 16 + if: matrix.os == 'ubuntu-latest' && matrix.node-version == 18 with: fail_ci_if_error: true diff --git a/index.d.ts b/index.d.ts index f9cf418782..d06e08f63b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -4,6 +4,7 @@ import {Stream, Readable as ReadableStream} from 'node:stream'; export type StdioOption = | 'pipe' + | 'overlapped' | 'ipc' | 'ignore' | 'inherit' @@ -33,8 +34,6 @@ export interface CommonOptions { /** Preferred path to find locally installed binaries in (use with `preferLocal`). - Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. - @default process.cwd() */ readonly localDir?: string | URL; @@ -113,8 +112,6 @@ export interface CommonOptions { /** Current working directory of the child process. - Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. - @default process.cwd() */ readonly cwd?: string | URL; @@ -136,15 +133,13 @@ export interface CommonOptions { @default 'pipe' */ - readonly stdio?: 'pipe' | 'ignore' | 'inherit' | readonly StdioOption[]; + readonly stdio?: 'pipe' | 'overlapped' | 'ignore' | 'inherit' | readonly StdioOption[]; /** Specify the kind of serialization used for sending messages between processes when using the `stdio: 'ipc'` option or `execaNode()`: - `json`: Uses `JSON.stringify()` and `JSON.parse()`. - `advanced`: Uses [`v8.serialize()`](https://nodejs.org/api/v8.html#v8_v8_serialize_value) - Requires Node.js `13.2.0` or later. - [More info.](https://nodejs.org/api/child_process.html#child_process_advanced_serialization) @default 'json' diff --git a/index.test-d.ts b/index.test-d.ts index b73cfa0369..fc00f2c9eb 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -97,6 +97,7 @@ execa('unicorns', {input: ''}); execa('unicorns', {input: Buffer.from('')}); execa('unicorns', {input: process.stdin}); execa('unicorns', {stdin: 'pipe'}); +execa('unicorns', {stdin: 'overlapped'}); execa('unicorns', {stdin: 'ipc'}); execa('unicorns', {stdin: 'ignore'}); execa('unicorns', {stdin: 'inherit'}); @@ -104,6 +105,7 @@ execa('unicorns', {stdin: process.stdin}); execa('unicorns', {stdin: 1}); execa('unicorns', {stdin: undefined}); execa('unicorns', {stdout: 'pipe'}); +execa('unicorns', {stdout: 'overlapped'}); execa('unicorns', {stdout: 'ipc'}); execa('unicorns', {stdout: 'ignore'}); execa('unicorns', {stdout: 'inherit'}); @@ -111,6 +113,7 @@ execa('unicorns', {stdout: process.stdout}); execa('unicorns', {stdout: 1}); execa('unicorns', {stdout: undefined}); execa('unicorns', {stderr: 'pipe'}); +execa('unicorns', {stderr: 'overlapped'}); execa('unicorns', {stderr: 'ipc'}); execa('unicorns', {stderr: 'ignore'}); execa('unicorns', {stderr: 'inherit'}); @@ -127,10 +130,11 @@ execa('unicorns', {cwd: new URL('file:///test')}); execa('unicorns', {env: {PATH: ''}}); execa('unicorns', {argv0: ''}); execa('unicorns', {stdio: 'pipe'}); +execa('unicorns', {stdio: 'overlapped'}); execa('unicorns', {stdio: 'ignore'}); execa('unicorns', {stdio: 'inherit'}); execa('unicorns', { - stdio: ['pipe', 'ipc', 'ignore', 'inherit', process.stdin, 1, undefined], + stdio: ['pipe', 'overlapped', 'ipc', 'ignore', 'inherit', process.stdin, 1, undefined], }); execa('unicorns', {serialization: 'advanced'}); execa('unicorns', {detached: true}); diff --git a/lib/stream.js b/lib/stream.js index b140bf14f9..3dcc81d07d 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -4,9 +4,7 @@ import mergeStream from 'merge-stream'; // `input` option export const handleInput = (spawned, input) => { - // Checking for stdin is workaround for https://github.com/nodejs/node/issues/26852 - // @todo remove `|| spawned.stdin === undefined` once we drop support for Node.js <=12.2.0 - if (input === undefined || spawned.stdin === undefined) { + if (input === undefined) { return; } diff --git a/package.json b/package.json index 90bd07b659..9c420b204b 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "type": "module", "exports": "./index.js", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" }, "scripts": { "test": "xo && c8 ava && tsd" @@ -58,7 +58,6 @@ "get-node": "^12.0.0", "is-running": "^2.1.0", "p-event": "^5.0.1", - "semver": "^7.3.5", "tempfile": "^4.0.0", "tsd": "^0.19.1", "xo": "^0.48.0" diff --git a/readme.md b/readme.md index a529f39033..d94df1383b 100644 --- a/readme.md +++ b/readme.md @@ -361,8 +361,6 @@ Default: `process.cwd()` Preferred path to find locally installed binaries in (use with `preferLocal`). -Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. - #### execPath Type: `string`\ @@ -452,8 +450,6 @@ Default: `process.cwd()` Current working directory of the child process. -Using a `URL` is only supported in Node.js `14.18.0`, `16.14.0` or above. - #### env Type: `object`\ @@ -483,8 +479,6 @@ Specify the kind of serialization used for sending messages between processes wh - `json`: Uses `JSON.stringify()` and `JSON.parse()`. - `advanced`: Uses [`v8.serialize()`](https://nodejs.org/api/v8.html#v8_v8_serialize_value) -Requires Node.js `13.2.0` or later. - [More info.](https://nodejs.org/api/child_process.html#child_process_advanced_serialization) #### detached diff --git a/test/error.js b/test/error.js index ae7f34b71f..378ef34014 100644 --- a/test/error.js +++ b/test/error.js @@ -79,10 +79,7 @@ test('error.shortMessage does not contain stdout/stderr', async t => { test('Original error.message is kept', async t => { const {originalMessage} = await t.throwsAsync(execa('noop.js', {cwd: 1})); - // On Node >=14.18.0, the error message is - // `The "options.cwd" property must be of type string or an instance of Buffer or URL. Received type number (1)` - t.true(originalMessage.startsWith('The "options.cwd" property must be of type string')); - t.true(originalMessage.includes('. Received type number')); + t.true(originalMessage.startsWith('The "options.cwd" property must be of type string or an instance of Buffer or URL. Received type number')); }); test('failed is false on success', async t => { diff --git a/test/override-promise.js b/test/override-promise.js index 986316e3a4..660ffe45ed 100644 --- a/test/override-promise.js +++ b/test/override-promise.js @@ -3,7 +3,6 @@ import process from 'node:process'; import {fileURLToPath} from 'node:url'; import test from 'ava'; // The helper module overrides Promise on import so has to be imported before `execa`. -// Can't use top-level await (TLA) + `import(…)` since Node.js 12 doesn't support TLA. import {restorePromise} from './helpers/override-promise.js'; // eslint-disable-next-line import/order import {execa} from '../index.js'; diff --git a/test/test.js b/test/test.js index e49e4e846b..69cced782d 100644 --- a/test/test.js +++ b/test/test.js @@ -4,7 +4,6 @@ import {fileURLToPath, pathToFileURL} from 'node:url'; import test from 'ava'; import isRunning from 'is-running'; import getNode from 'get-node'; -import semver from 'semver'; import {execa, execaSync} from '../index.js'; process.env.PATH = fileURLToPath(new URL('fixtures', import.meta.url)) + path.delimiter + process.env.PATH; @@ -167,9 +166,7 @@ if (process.platform !== 'win32') { }); test('execa() rejects with correct error and doesn\'t throw if running non-executable with input', async t => { - // On Node <12.6.0, `EACCESS` is emitted on `childProcess`. - // On Node >=12.6.0, `EPIPE` is emitted on `childProcess.stdin`. - await t.throwsAsync(execa('non-executable.js', {input: 'Hey!'}), {message: /EACCES|EPIPE/}); + await t.throwsAsync(execa('non-executable.js', {input: 'Hey!'}), {message: /EACCES/}); }); } @@ -207,21 +204,19 @@ test('can use `options.cwd` as a string', async t => { t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); }); -if (semver.satisfies(process.version, '^14.18.0 || >=16.4.0')) { - test('localDir option can be a URL', async t => { - const command = process.platform === 'win32' ? 'echo %PATH%' : 'echo $PATH'; - const {stdout} = await execa(command, {shell: true, preferLocal: true, localDir: pathToFileURL('/test')}); - const envPaths = stdout.split(path.delimiter); - t.true(envPaths.some(envPath => envPath.endsWith('.bin'))); - }); +test('localDir option can be a URL', async t => { + const command = process.platform === 'win32' ? 'echo %PATH%' : 'echo $PATH'; + const {stdout} = await execa(command, {shell: true, preferLocal: true, localDir: pathToFileURL('/test')}); + const envPaths = stdout.split(path.delimiter); + t.true(envPaths.some(envPath => envPath.endsWith('.bin'))); +}); - test('can use `options.cwd` as a URL', async t => { - const cwd = '/'; - const cwdUrl = pathToFileURL(cwd); - const {stdout} = await execa('node', ['-p', 'process.cwd()'], {cwd: cwdUrl}); - t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); - }); -} +test('can use `options.cwd` as a URL', async t => { + const cwd = '/'; + const cwdUrl = pathToFileURL(cwd); + const {stdout} = await execa('node', ['-p', 'process.cwd()'], {cwd: cwdUrl}); + t.is(path.toNamespacedPath(stdout), path.toNamespacedPath(cwd)); +}); test('can use `options.shell: true`', async t => { const {stdout} = await execa('node test/fixtures/noop.js foo', {shell: true});