diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1cc851db09..4fd774a7dd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,9 +10,9 @@ jobs: fail-fast: false matrix: node-version: + - 20 - 18 - 16 - - 14 os: - ubuntu-latest - macos-latest @@ -25,6 +25,6 @@ jobs: - run: npm install - run: npm test - uses: codecov/codecov-action@v2 - if: matrix.os == 'ubuntu-latest' && matrix.node-version == 18 + if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20 with: fail_ci_if_error: true diff --git a/index.d.ts b/index.d.ts index c9cb28f11b..5540af4bac 100644 --- a/index.d.ts +++ b/index.d.ts @@ -208,8 +208,6 @@ export type CommonOptions = { When `AbortController.abort()` is called, [`.isCanceled`](https://github.com/sindresorhus/execa#iscanceled) becomes `false`. - *Requires Node.js 16 or later.* - @example ``` import {execa} from 'execa'; diff --git a/lib/command.js b/lib/command.js index 7ae9c2bc73..727ce5f589 100644 --- a/lib/command.js +++ b/lib/command.js @@ -10,14 +10,13 @@ const normalizeArgs = (file, args = []) => { }; const NO_ESCAPE_REGEXP = /^[\w.-]+$/; -const DOUBLE_QUOTES_REGEXP = /"/g; const escapeArg = arg => { if (typeof arg !== 'string' || NO_ESCAPE_REGEXP.test(arg)) { return arg; } - return `"${arg.replace(DOUBLE_QUOTES_REGEXP, '\\"')}"`; + return `"${arg.replaceAll('"', '\\"')}"`; }; export const joinCommand = (file, args) => normalizeArgs(file, args).join(' '); @@ -31,7 +30,7 @@ export const parseCommand = command => { const tokens = []; for (const token of command.trim().split(SPACES_REGEXP)) { // Allow spaces to be escaped by a backslash if not meant as a delimiter - const previousToken = tokens[tokens.length - 1]; + const previousToken = tokens.at(-1); if (previousToken && previousToken.endsWith('\\')) { // Merge previous token with current one tokens[tokens.length - 1] = `${previousToken.slice(0, -1)} ${token}`; @@ -80,7 +79,7 @@ const concatTokens = (tokens, nextTokens, isNew) => isNew || tokens.length === 0 ? [...tokens, ...nextTokens] : [ ...tokens.slice(0, -1), - `${tokens[tokens.length - 1]}${nextTokens[0]}`, + `${tokens.at(-1)}${nextTokens[0]}`, ...nextTokens.slice(1), ]; diff --git a/package.json b/package.json index 8044e81fee..d863812e9f 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,12 @@ "url": "https://sindresorhus.com" }, "type": "module", - "exports": "./index.js", + "exports": { + "types": "./index.d.ts", + "default": "./index.js" + }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=16.17" }, "scripts": { "test": "xo && c8 ava && tsd" @@ -44,7 +47,7 @@ "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.1", - "human-signals": "^4.3.0", + "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", @@ -53,16 +56,16 @@ "strip-final-newline": "^3.0.0" }, "devDependencies": { - "@types/node": "^18.13.0", + "@types/node": "^20.4.0", "ava": "^5.2.0", - "c8": "^7.12.0", - "get-node": "^13.5.0", + "c8": "^8.0.1", + "get-node": "^14.2.0", "is-running": "^2.1.0", - "p-event": "^5.0.1", + "p-event": "^6.0.0", "path-key": "^4.0.0", - "tempfile": "^4.0.0", - "tsd": "^0.25.0", - "xo": "^0.54.2" + "tempfile": "^5.0.0", + "tsd": "^0.28.1", + "xo": "^0.55.0" }, "c8": { "reporter": [ diff --git a/readme.md b/readme.md index 067168a45e..48f5701ecc 100644 --- a/readme.md +++ b/readme.md @@ -713,8 +713,6 @@ You can abort the spawned process using [`AbortController`](https://developer.mo When `AbortController.abort()` is called, [`.isCanceled`](#iscanceled) becomes `false`. -*Requires Node.js 16 or later.* - #### windowsVerbatimArguments Type: `boolean`\ diff --git a/test/pipe.js b/test/pipe.js index b465440e10..264f41f7b6 100644 --- a/test/pipe.js +++ b/test/pipe.js @@ -32,7 +32,7 @@ test('pipeAll() can pipe stdout to streams', pipeToStream, 'noop.js', 'pipeAll', test('pipeAll() can pipe stderr to streams', pipeToStream, 'noop-err.js', 'pipeAll', 'stderr'); const pipeToFile = async (t, fixtureName, funcName, streamName) => { - const file = tempfile('.txt'); + const file = tempfile({extension: '.txt'}); const result = await execa(fixtureName, ['test'], {all: true})[funcName](file); t.is(result[streamName], 'test'); t.is(await readFile(file, 'utf8'), 'test\n'); diff --git a/test/stream.js b/test/stream.js index 44ad0919e6..481d9c8110 100644 --- a/test/stream.js +++ b/test/stream.js @@ -18,13 +18,13 @@ test('buffer', async t => { }); test('pass `stdout` to a file descriptor', async t => { - const file = tempfile('.txt'); + const file = tempfile({extension: '.txt'}); await execa('noop.js', ['foo bar'], {stdout: fs.openSync(file, 'w')}); t.is(fs.readFileSync(file, 'utf8'), 'foo bar\n'); }); test('pass `stderr` to a file descriptor', async t => { - const file = tempfile('.txt'); + const file = tempfile({extension: '.txt'}); await execa('noop-err.js', ['foo bar'], {stderr: fs.openSync(file, 'w')}); t.is(fs.readFileSync(file, 'utf8'), 'foo bar\n'); }); diff --git a/test/verbose.js b/test/verbose.js index fa0a605463..34c3b849a9 100644 --- a/test/verbose.js +++ b/test/verbose.js @@ -4,7 +4,7 @@ import {setFixtureDir} from './helpers/fixtures-dir.js'; setFixtureDir(); -const normalizeTimestamp = output => output.replace(/\d/g, '0'); +const normalizeTimestamp = output => output.replaceAll(/\d/g, '0'); const testTimestamp = '[00:00:00.000]'; test('Prints command when "verbose" is true', async t => {