From 0a888a805a84cf1a8a22e44bac2ba80052ee3169 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Fri, 4 Feb 2022 23:29:51 +0900 Subject: [PATCH 01/21] Add `abortSignal` --- index.d.ts | 18 ++++++++++++++++++ index.js | 6 ++++++ readme.md | 15 +++++++++++++++ test/kill.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) diff --git a/index.d.ts b/index.d.ts index fdf9de1c39..fff61e9fb2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -204,6 +204,24 @@ export interface CommonOptions { */ readonly killSignal?: string | number; + /** + You can abort the spawned process by the [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). + It works on Node 16 and higher versions. + + @example + ``` + import {execa} from 'execa'; + + const abortController = new AbortController(); + const subprocess = execa('node', [], { abortSignal: abortController.signal }); + + abortController.abort(); + + console.log(subprocess.killed); // true + ``` + */ + readonly abortSignal?: AbortSignal; + /** If `true`, no quoting or escaping of arguments is done on Windows. Ignored on other platforms. This is set to `true` automatically when the `shell` option is `true`. diff --git a/index.js b/index.js index 9df491ded1..076359ab75 100644 --- a/index.js +++ b/index.js @@ -109,6 +109,12 @@ export function execa(file, args, options) { spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); spawned.cancel = spawnedCancel.bind(null, spawned, context); + if (parsed.options.abortSignal) { + parsed.options.abortSignal.addEventListener('abort', () => { + spawned.cancel(); + }, {once: true}); + } + const handlePromise = async () => { const [{error, exitCode, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone); const stdout = handleOutput(parsed.options, stdoutResult); diff --git a/readme.md b/readme.md index a9db621b9f..4f81b46ad9 100644 --- a/readme.md +++ b/readme.md @@ -80,6 +80,8 @@ try { ### Cancelling a spawned process +#### Use `cancel` method + ```js import {execa} from 'execa'; @@ -97,6 +99,19 @@ try { } ``` +#### Use `AbortController` + +```js +import {execa} from 'execa'; + +const abortController = new AbortController(); +const subprocess = execa('node', [], { abortSignal: abortController.signal }); + +abortController.abort(); + +console.log(subprocess.killed); // true +``` + ### Catching an error with the sync method ```js diff --git a/test/kill.js b/test/kill.js index 08ae393bf2..6ee737cd9b 100644 --- a/test/kill.js +++ b/test/kill.js @@ -262,3 +262,48 @@ test('calling cancel method on a process which has been killed does not make err const {isCanceled} = await t.throwsAsync(subprocess); t.false(isCanceled); }); + +test('calling abort throws an error with message "Command was canceled"', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + abortController.abort(); + await t.throwsAsync(subprocess, {message: /Command was canceled/}); +}); + +test('calling abort twice should show the same behaviour as calling it once', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + abortController.abort(); + abortController.abort(); + const {isCanceled} = await t.throwsAsync(subprocess); + t.true(isCanceled); + t.true(subprocess.killed); +}); + +test('calling abort on a successfully completed process does not make result.isCanceled true', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + const {isCanceled} = await subprocess; + abortController.abort(); + t.false(isCanceled); +}); + +test('calling cancel after abort should show the same behaviour as only calling cancel', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + abortController.abort(); + subprocess.cancel(); + const {isCanceled} = await t.throwsAsync(subprocess); + t.true(isCanceled); + t.true(subprocess.killed); +}); + +test('calling abort after cancel should show the same behaviour as only calling cancel', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + subprocess.cancel(); + abortController.abort(); + const {isCanceled} = await t.throwsAsync(subprocess); + t.true(isCanceled); + t.true(subprocess.killed); +}); From d921f780f002e1805e176acc3dcf38de2ec9047d Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Fri, 4 Feb 2022 23:31:10 +0900 Subject: [PATCH 02/21] Update kill.js --- test/kill.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/kill.js b/test/kill.js index 6ee737cd9b..67b47ae21a 100644 --- a/test/kill.js +++ b/test/kill.js @@ -265,14 +265,14 @@ test('calling cancel method on a process which has been killed does not make err test('calling abort throws an error with message "Command was canceled"', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); abortController.abort(); await t.throwsAsync(subprocess, {message: /Command was canceled/}); }); test('calling abort twice should show the same behaviour as calling it once', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); abortController.abort(); abortController.abort(); const {isCanceled} = await t.throwsAsync(subprocess); @@ -282,7 +282,7 @@ test('calling abort twice should show the same behaviour as calling it once', as test('calling abort on a successfully completed process does not make result.isCanceled true', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); const {isCanceled} = await subprocess; abortController.abort(); t.false(isCanceled); @@ -290,7 +290,7 @@ test('calling abort on a successfully completed process does not make result.isC test('calling cancel after abort should show the same behaviour as only calling cancel', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); abortController.abort(); subprocess.cancel(); const {isCanceled} = await t.throwsAsync(subprocess); @@ -300,7 +300,7 @@ test('calling cancel after abort should show the same behaviour as only calling test('calling abort after cancel should show the same behaviour as only calling cancel', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], { abortSignal: abortController.signal }); + const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); subprocess.cancel(); abortController.abort(); const {isCanceled} = await t.throwsAsync(subprocess); From 7b791a40a235b8fc89be7c07bc72799e57ea2e5a Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Sat, 5 Feb 2022 10:50:57 +0900 Subject: [PATCH 03/21] abortSignal -> signal --- index.d.ts | 4 ++-- index.js | 4 ++-- readme.md | 2 +- test/kill.js | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/index.d.ts b/index.d.ts index fff61e9fb2..89c8004788 100644 --- a/index.d.ts +++ b/index.d.ts @@ -213,14 +213,14 @@ export interface CommonOptions { import {execa} from 'execa'; const abortController = new AbortController(); - const subprocess = execa('node', [], { abortSignal: abortController.signal }); + const subprocess = execa('node', [], { signal: abortController.signal }); abortController.abort(); console.log(subprocess.killed); // true ``` */ - readonly abortSignal?: AbortSignal; + readonly signal?: AbortSignal; /** If `true`, no quoting or escaping of arguments is done on Windows. Ignored on other platforms. This is set to `true` automatically when the `shell` option is `true`. diff --git a/index.js b/index.js index 076359ab75..bf73d7f7dd 100644 --- a/index.js +++ b/index.js @@ -109,8 +109,8 @@ export function execa(file, args, options) { spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); spawned.cancel = spawnedCancel.bind(null, spawned, context); - if (parsed.options.abortSignal) { - parsed.options.abortSignal.addEventListener('abort', () => { + if (parsed.options.signal) { + parsed.options.signal.addEventListener('abort', () => { spawned.cancel(); }, {once: true}); } diff --git a/readme.md b/readme.md index 4f81b46ad9..41b1ef8fe0 100644 --- a/readme.md +++ b/readme.md @@ -105,7 +105,7 @@ try { import {execa} from 'execa'; const abortController = new AbortController(); -const subprocess = execa('node', [], { abortSignal: abortController.signal }); +const subprocess = execa('node', [], { signal: abortController.signal }); abortController.abort(); diff --git a/test/kill.js b/test/kill.js index 67b47ae21a..69f72ea382 100644 --- a/test/kill.js +++ b/test/kill.js @@ -265,14 +265,14 @@ test('calling cancel method on a process which has been killed does not make err test('calling abort throws an error with message "Command was canceled"', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); abortController.abort(); await t.throwsAsync(subprocess, {message: /Command was canceled/}); }); test('calling abort twice should show the same behaviour as calling it once', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); abortController.abort(); abortController.abort(); const {isCanceled} = await t.throwsAsync(subprocess); @@ -282,7 +282,7 @@ test('calling abort twice should show the same behaviour as calling it once', as test('calling abort on a successfully completed process does not make result.isCanceled true', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); const {isCanceled} = await subprocess; abortController.abort(); t.false(isCanceled); @@ -290,7 +290,7 @@ test('calling abort on a successfully completed process does not make result.isC test('calling cancel after abort should show the same behaviour as only calling cancel', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); abortController.abort(); subprocess.cancel(); const {isCanceled} = await t.throwsAsync(subprocess); @@ -300,7 +300,7 @@ test('calling cancel after abort should show the same behaviour as only calling test('calling abort after cancel should show the same behaviour as only calling cancel', async t => { const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {abortSignal: abortController.signal}); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); subprocess.cancel(); abortController.abort(); const {isCanceled} = await t.throwsAsync(subprocess); From bda9c62001a9b7bb896e0e69fd005ea06255bb0f Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 5 Feb 2022 11:56:49 +0700 Subject: [PATCH 04/21] Update index.d.ts --- index.d.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index 89c8004788..3bd800d212 100644 --- a/index.d.ts +++ b/index.d.ts @@ -205,19 +205,21 @@ export interface CommonOptions { readonly killSignal?: string | number; /** - You can abort the spawned process by the [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). - It works on Node 16 and higher versions. + You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). + + *Requires Node.js 16 or later.* @example ``` import {execa} from 'execa'; const abortController = new AbortController(); - const subprocess = execa('node', [], { signal: abortController.signal }); + const subprocess = execa('node', [], {signal: abortController.signal}); abortController.abort(); - console.log(subprocess.killed); // true + console.log(subprocess.killed); + //=> true ``` */ readonly signal?: AbortSignal; From 3f2301825407d2df51f48bd052c1fc3e47bcdcea Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 5 Feb 2022 11:57:47 +0700 Subject: [PATCH 05/21] Update readme.md --- readme.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 41b1ef8fe0..11b3643d60 100644 --- a/readme.md +++ b/readme.md @@ -105,11 +105,12 @@ try { import {execa} from 'execa'; const abortController = new AbortController(); -const subprocess = execa('node', [], { signal: abortController.signal }); +const subprocess = execa('node', [], {signal: abortController.signal}); abortController.abort(); -console.log(subprocess.killed); // true +console.log(subprocess.killed); +//=> true ``` ### Catching an error with the sync method From 7ed6580e7ed657be1d7e16349ec85f17777ba2e0 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Sat, 5 Feb 2022 21:19:10 +0900 Subject: [PATCH 06/21] Update readme.md --- readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/readme.md b/readme.md index 11b3643d60..9880574dbf 100644 --- a/readme.md +++ b/readme.md @@ -558,6 +558,16 @@ Default: `SIGTERM` Signal value to be used when the spawned process will be killed. +#### signal + +Type: `object (AbortSignal)` + +You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). + +See [this Example](#use-abortcontroller). + +*Requires Node.js 16 or later.* + #### windowsVerbatimArguments Type: `boolean`\ From 29bc38e6ed2ce8add74baa5a06555f97edd43a14 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Sat, 5 Feb 2022 21:59:54 +0900 Subject: [PATCH 07/21] Update readme.md --- readme.md | 47 +++++++++++------------------------------------ 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/readme.md b/readme.md index 9880574dbf..689571cd6f 100644 --- a/readme.md +++ b/readme.md @@ -78,41 +78,6 @@ try { } ``` -### Cancelling a spawned process - -#### Use `cancel` method - -```js -import {execa} from 'execa'; - -const subprocess = execa('node'); - -setTimeout(() => { - subprocess.cancel(); -}, 1000); - -try { - await subprocess; -} catch (error) { - console.log(subprocess.killed); // true - console.log(error.isCanceled); // true -} -``` - -#### Use `AbortController` - -```js -import {execa} from 'execa'; - -const abortController = new AbortController(); -const subprocess = execa('node', [], {signal: abortController.signal}); - -abortController.abort(); - -console.log(subprocess.killed); -//=> true -``` - ### Catching an error with the sync method ```js @@ -564,7 +529,17 @@ Type: `object (AbortSignal)` You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). -See [this Example](#use-abortcontroller). +```js +import {execa} from 'execa'; + +const abortController = new AbortController(); +const subprocess = execa('node', [], {signal: abortController.signal}); + +abortController.abort(); + +console.log(subprocess.killed); +//=> true +``` *Requires Node.js 16 or later.* From a1d7cd29d906b1a8e672c1ecb131735ef7bd1d91 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Mon, 7 Feb 2022 14:44:28 +0900 Subject: [PATCH 08/21] Move example https://github.com/sindresorhus/execa/pull/490#discussion_r800084971 --- readme.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/readme.md b/readme.md index 689571cd6f..c7e76a9317 100644 --- a/readme.md +++ b/readme.md @@ -78,6 +78,20 @@ try { } ``` +### Cancelling a spawned process + +```js +import {execa} from 'execa'; + +const abortController = new AbortController(); +const subprocess = execa('node', [], {signal: abortController.signal}); + +abortController.abort(); + +console.log(subprocess.killed); +//=> true +``` + ### Catching an error with the sync method ```js @@ -529,18 +543,6 @@ Type: `object (AbortSignal)` You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). -```js -import {execa} from 'execa'; - -const abortController = new AbortController(); -const subprocess = execa('node', [], {signal: abortController.signal}); - -abortController.abort(); - -console.log(subprocess.killed); -//=> true -``` - *Requires Node.js 16 or later.* #### windowsVerbatimArguments From 1deecd102fdad4828af9ec5f805e01f09a69f442 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Mon, 7 Feb 2022 14:45:15 +0900 Subject: [PATCH 09/21] Remove `cancel` doc https://github.com/sindresorhus/execa/pull/490#discussion_r800085898 --- readme.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/readme.md b/readme.md index c7e76a9317..be862f0a5d 100644 --- a/readme.md +++ b/readme.md @@ -166,10 +166,6 @@ Milliseconds to wait for the child process to terminate before sending `SIGKILL` Can be disabled with `false`. -#### cancel() - -Similar to [`childProcess.kill()`](https://nodejs.org/api/child_process.html#child_process_subprocess_kill_signal). This is preferred when cancelling the child process execution as the error is more descriptive and [`childProcessResult.isCanceled`](#iscanceled) is set to `true`. - #### all Type: `ReadableStream | undefined` From 192ace8a084e1abda8af2c89f1fd1af25cf7759b Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Mon, 7 Feb 2022 15:00:05 +0900 Subject: [PATCH 10/21] `signal` and `isCanceled` should mention each other https://github.com/sindresorhus/execa/pull/490#discussion_r800085898 --- readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.md b/readme.md index be862f0a5d..e69e2119d9 100644 --- a/readme.md +++ b/readme.md @@ -281,6 +281,8 @@ Type: `boolean` Whether the process was canceled. +You can cancel the spawned process using the [signal](#signal-1) option. + #### killed Type: `boolean` @@ -539,6 +541,8 @@ Type: `object (AbortSignal)` You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). +When the `abort` is called, [isCancel](#iscanceled) becomes false. + *Requires Node.js 16 or later.* #### windowsVerbatimArguments From 9db69e290761984c13a30212bf9fe4967360cf0d Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Mon, 7 Feb 2022 15:19:11 +0900 Subject: [PATCH 11/21] Replace example with more practical one https://github.com/sindresorhus/execa/pull/490#discussion_r800084971 --- index.d.ts | 14 ++++++++++---- readme.md | 12 +++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index 3bd800d212..1bdf149709 100644 --- a/index.d.ts +++ b/index.d.ts @@ -210,16 +210,22 @@ export interface CommonOptions { *Requires Node.js 16 or later.* @example - ``` + ```js import {execa} from 'execa'; const abortController = new AbortController(); const subprocess = execa('node', [], {signal: abortController.signal}); - abortController.abort(); + setTimeout(() => { + abortController.abort(); + }, 1000); - console.log(subprocess.killed); - //=> true + try { + await subprocess; + } catch (error) { + console.log(subprocess.killed); // true + console.log(error.isCanceled); // true + } ``` */ readonly signal?: AbortSignal; diff --git a/readme.md b/readme.md index e69e2119d9..9ff65ba8f2 100644 --- a/readme.md +++ b/readme.md @@ -86,10 +86,16 @@ import {execa} from 'execa'; const abortController = new AbortController(); const subprocess = execa('node', [], {signal: abortController.signal}); -abortController.abort(); +setTimeout(() => { + abortController.abort(); +}, 1000); -console.log(subprocess.killed); -//=> true +try { + await subprocess; +} catch (error) { + console.log(subprocess.killed); // true + console.log(error.isCanceled); // true +} ``` ### Catching an error with the sync method From b8e9581a1cf99182b782dbb28fe4a3330b8d6a4d Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Mon, 7 Feb 2022 15:40:56 +0900 Subject: [PATCH 12/21] Remove useless event listener https://github.com/sindresorhus/execa/pull/490/files/29bc38e6ed2ce8add74baa5a06555f97edd43a14#r800083964 --- index.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index bf73d7f7dd..33e692529b 100644 --- a/index.js +++ b/index.js @@ -109,11 +109,7 @@ export function execa(file, args, options) { spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); spawned.cancel = spawnedCancel.bind(null, spawned, context); - if (parsed.options.signal) { - parsed.options.signal.addEventListener('abort', () => { - spawned.cancel(); - }, {once: true}); - } + const isCanceled = () => context.isCanceled || (parsed.options.signal ? parsed.options.signal.aborted : false); const handlePromise = async () => { const [{error, exitCode, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone); @@ -133,7 +129,7 @@ export function execa(file, args, options) { escapedCommand, parsed, timedOut, - isCanceled: context.isCanceled, + isCanceled: isCanceled(), killed: spawned.killed, }); @@ -153,7 +149,7 @@ export function execa(file, args, options) { all, failed: false, timedOut: false, - isCanceled: false, + isCanceled: isCanceled(), killed: false, }; }; From 59c40c2b5fc823ff0093570ce10ffa238294c4b6 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Mon, 7 Feb 2022 15:46:13 +0900 Subject: [PATCH 13/21] Add type test https://github.com/sindresorhus/execa/pull/490/files/29bc38e6ed2ce8add74baa5a06555f97edd43a14#r800085126 --- index.test-d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.test-d.ts b/index.test-d.ts index 5e06366a57..c0b3bc5875 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -140,6 +140,7 @@ execa('unicorns', {timeout: 1000}); execa('unicorns', {maxBuffer: 1000}); execa('unicorns', {killSignal: 'SIGTERM'}); execa('unicorns', {killSignal: 9}); +execa('unicorns', {signal: new AbortController().signal}); execa('unicorns', {windowsVerbatimArguments: true}); execa('unicorns', {windowsHide: false}); /* eslint-enable @typescript-eslint/no-floating-promises */ From c29d44744d4e06c31f7e09ceff3929aa95dcfabb Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Tue, 8 Feb 2022 13:11:10 +0900 Subject: [PATCH 14/21] Resolve test failing on Node 12, 14 https://github.com/sindresorhus/execa/pull/490#discussion_r800667720 --- test/kill.js | 82 +++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/test/kill.js b/test/kill.js index 69f72ea382..c26c58d494 100644 --- a/test/kill.js +++ b/test/kill.js @@ -263,47 +263,49 @@ test('calling cancel method on a process which has been killed does not make err t.false(isCanceled); }); -test('calling abort throws an error with message "Command was canceled"', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {signal: abortController.signal}); - abortController.abort(); - await t.throwsAsync(subprocess, {message: /Command was canceled/}); -}); +if (globalThis.AbortController !== undefined) { + test('calling abort throws an error with message "Command was canceled"', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); + abortController.abort(); + await t.throwsAsync(subprocess, {message: /Command was canceled/}); + }); -test('calling abort twice should show the same behaviour as calling it once', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {signal: abortController.signal}); - abortController.abort(); - abortController.abort(); - const {isCanceled} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(subprocess.killed); -}); + test('calling abort twice should show the same behaviour as calling it once', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); + abortController.abort(); + abortController.abort(); + const {isCanceled} = await t.throwsAsync(subprocess); + t.true(isCanceled); + t.true(subprocess.killed); + }); -test('calling abort on a successfully completed process does not make result.isCanceled true', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {signal: abortController.signal}); - const {isCanceled} = await subprocess; - abortController.abort(); - t.false(isCanceled); -}); + test('calling abort on a successfully completed process does not make result.isCanceled true', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); + const {isCanceled} = await subprocess; + abortController.abort(); + t.false(isCanceled); + }); -test('calling cancel after abort should show the same behaviour as only calling cancel', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {signal: abortController.signal}); - abortController.abort(); - subprocess.cancel(); - const {isCanceled} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(subprocess.killed); -}); + test('calling cancel after abort should show the same behaviour as only calling cancel', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); + abortController.abort(); + subprocess.cancel(); + const {isCanceled} = await t.throwsAsync(subprocess); + t.true(isCanceled); + t.true(subprocess.killed); + }); -test('calling abort after cancel should show the same behaviour as only calling cancel', async t => { - const abortController = new AbortController(); - const subprocess = execa('noop.js', [], {signal: abortController.signal}); - subprocess.cancel(); - abortController.abort(); - const {isCanceled} = await t.throwsAsync(subprocess); - t.true(isCanceled); - t.true(subprocess.killed); -}); + test('calling abort after cancel should show the same behaviour as only calling cancel', async t => { + const abortController = new AbortController(); + const subprocess = execa('noop.js', [], {signal: abortController.signal}); + subprocess.cancel(); + abortController.abort(); + const {isCanceled} = await t.throwsAsync(subprocess); + t.true(isCanceled); + t.true(subprocess.killed); + }); +} From 2908af40b1a98b4659e150fc5730d185467c66bb Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Tue, 8 Feb 2022 13:11:42 +0900 Subject: [PATCH 15/21] Update readme.md https://github.com/sindresorhus/execa/pull/490#discussion_r800661589 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 9ff65ba8f2..565616f688 100644 --- a/readme.md +++ b/readme.md @@ -547,7 +547,7 @@ Type: `object (AbortSignal)` You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). -When the `abort` is called, [isCancel](#iscanceled) becomes false. +When `AbortController.abort()` is called, [isCanceled](#iscanceled) becomes `false`. *Requires Node.js 16 or later.* From 3bfe3157ec174106d0acc98a601c1ae31abce9ae Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Tue, 8 Feb 2022 13:12:06 +0900 Subject: [PATCH 16/21] `isCanceled` is always false when the process is successful. https://github.com/sindresorhus/execa/pull/490#discussion_r800659707 --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 33e692529b..573e4636d4 100644 --- a/index.js +++ b/index.js @@ -149,7 +149,7 @@ export function execa(file, args, options) { all, failed: false, timedOut: false, - isCanceled: isCanceled(), + isCanceled: false, killed: false, }; }; From 3d44b69edbafe200801a2249f0b6765c48b10202 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Wed, 9 Feb 2022 20:27:50 +0900 Subject: [PATCH 17/21] Update index.js --- index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/index.js b/index.js index 573e4636d4..f060590c8c 100644 --- a/index.js +++ b/index.js @@ -109,8 +109,6 @@ export function execa(file, args, options) { spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); spawned.cancel = spawnedCancel.bind(null, spawned, context); - const isCanceled = () => context.isCanceled || (parsed.options.signal ? parsed.options.signal.aborted : false); - const handlePromise = async () => { const [{error, exitCode, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone); const stdout = handleOutput(parsed.options, stdoutResult); @@ -129,7 +127,7 @@ export function execa(file, args, options) { escapedCommand, parsed, timedOut, - isCanceled: isCanceled(), + isCanceled: context.isCanceled || (parsed.options.signal ? parsed.options.signal.aborted : false), killed: spawned.killed, }); From ad9d29c125cef5cbdecff6d73cf9277e29178b10 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Sun, 13 Feb 2022 17:31:10 +0900 Subject: [PATCH 18/21] Update readme.md --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 565616f688..03b2d9b282 100644 --- a/readme.md +++ b/readme.md @@ -287,7 +287,7 @@ Type: `boolean` Whether the process was canceled. -You can cancel the spawned process using the [signal](#signal-1) option. +You can cancel the spawned process using the [`signal`](#signal-1) option. #### killed @@ -543,7 +543,7 @@ Signal value to be used when the spawned process will be killed. #### signal -Type: `object (AbortSignal)` +Type: [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). From 7bfc357d2ad995b7d3ae139fdb285d015fe63b65 Mon Sep 17 00:00:00 2001 From: Gyubong Lee Date: Sun, 13 Feb 2022 17:31:13 +0900 Subject: [PATCH 19/21] Update index.d.ts --- index.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.d.ts b/index.d.ts index 1bdf149709..3c84136b2e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -207,6 +207,8 @@ export interface CommonOptions { /** You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). + When `AbortController.abort()` is called, [isCanceled](https://github.com/sindresorhus/execa#iscanceled) becomes `false`. + *Requires Node.js 16 or later.* @example @@ -363,6 +365,8 @@ export interface ExecaReturnValue /** Whether the process was canceled. + + You can cancel the spawned process using the [`signal`](https://github.com/sindresorhus/execa#signal-1) option. */ isCanceled: boolean; } From e598fb0d040eb70e3bf35bba846abbf56d3af9d1 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 13 Feb 2022 16:07:39 +0700 Subject: [PATCH 20/21] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 03b2d9b282..25a1602093 100644 --- a/readme.md +++ b/readme.md @@ -547,7 +547,7 @@ Type: [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSign You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). -When `AbortController.abort()` is called, [isCanceled](#iscanceled) becomes `false`. +When `AbortController.abort()` is called, [`.isCanceled`](#iscanceled) becomes `false`. *Requires Node.js 16 or later.* From 8b49e3a52637b0e08d7c16605941156cddb67ab9 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 13 Feb 2022 16:07:53 +0700 Subject: [PATCH 21/21] Update index.d.ts --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 3c84136b2e..e5cc79d762 100644 --- a/index.d.ts +++ b/index.d.ts @@ -207,7 +207,7 @@ export interface CommonOptions { /** You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). - When `AbortController.abort()` is called, [isCanceled](https://github.com/sindresorhus/execa#iscanceled) becomes `false`. + When `AbortController.abort()` is called, [`.isCanceled`](https://github.com/sindresorhus/execa#iscanceled) becomes `false`. *Requires Node.js 16 or later.*