Skip to content

Commit

Permalink
Add error.signalDescription (#378)
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky authored and sindresorhus committed Oct 17, 2019
1 parent 1ac56ea commit d8cb08f
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 5 deletions.
11 changes: 10 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,18 @@ declare namespace execa {
killed: boolean;

/**
The signal that was used to terminate the process.
The name of the signal that was used to terminate the process. For example, `SIGFPE`.
If a signal terminated the process, this property is defined and included in the error message. Otherwise it is `undefined`.
*/
signal?: string;

/**
A human-friendly description of the signal that was used to terminate the process. For example, `Floating point arithmetic error`.
If a signal terminated the process, this property is defined and included in the error message. Otherwise it is `undefined`. It is also `undefined` when the signal is very uncommon which should seldomly happen.
*/
signalDescription?: string;
}

interface ExecaSyncReturnValue<StdoutErrorType = string>
Expand Down
4 changes: 4 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ try {
expectType<boolean>(unicornsResult.isCanceled);
expectType<boolean>(unicornsResult.killed);
expectType<string | undefined>(unicornsResult.signal);
expectType<string | undefined>(unicornsResult.signalDescription);
} catch (error) {
const execaError: ExecaError = error;

Expand All @@ -38,6 +39,7 @@ try {
expectType<boolean>(execaError.isCanceled);
expectType<boolean>(execaError.killed);
expectType<string | undefined>(execaError.signal);
expectType<string | undefined>(execaError.signalDescription);
expectType<string | undefined>(execaError.originalMessage);
}

Expand All @@ -53,6 +55,7 @@ try {
expectError(unicornsResult.isCanceled);
expectType<boolean>(unicornsResult.killed);
expectType<string | undefined>(unicornsResult.signal);
expectType<string | undefined>(unicornsResult.signalDescription);
} catch (error) {
const execaError: ExecaSyncError = error;

Expand All @@ -66,6 +69,7 @@ try {
expectError(execaError.isCanceled);
expectType<boolean>(execaError.killed);
expectType<string | undefined>(execaError.signal);
expectType<string | undefined>(execaError.signalDescription);
expectType<string | undefined>(execaError.originalMessage);
}

Expand Down
10 changes: 7 additions & 3 deletions lib/error.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';
const getErrorPrefix = ({timedOut, timeout, errorCode, signal, exitCode, isCanceled}) => {
const {signalsByName} = require('human-signals');

const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => {
if (timedOut) {
return `timed out after ${timeout} milliseconds`;
}
Expand All @@ -13,7 +15,7 @@ const getErrorPrefix = ({timedOut, timeout, errorCode, signal, exitCode, isCance
}

if (signal !== undefined) {
return `was killed with ${signal}`;
return `was killed with ${signal} (${signalDescription})`;
}

if (exitCode !== undefined) {
Expand All @@ -40,10 +42,11 @@ const makeError = ({
// We normalize them to `undefined`
exitCode = exitCode === null ? undefined : exitCode;
signal = signal === null ? undefined : signal;
const signalDescription = signal === undefined ? undefined : signalsByName[signal].description;

const errorCode = error && error.code;

const prefix = getErrorPrefix({timedOut, timeout, errorCode, signal, exitCode, isCanceled});
const prefix = getErrorPrefix({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled});
const message = `Command ${prefix}: ${command}`;

if (error instanceof Error) {
Expand All @@ -56,6 +59,7 @@ const makeError = ({
error.command = command;
error.exitCode = exitCode;
error.signal = signal;
error.signalDescription = signalDescription;
error.stdout = stdout;
error.stderr = stderr;

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"dependencies": {
"cross-spawn": "^7.0.0",
"get-stream": "^5.0.0",
"human-signals": "^1.1.1",
"is-stream": "^2.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^4.0.0",
Expand Down
12 changes: 11 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,17 @@ Whether the process was killed.

Type: `string | undefined`

The signal that was used to terminate the process.
The name of the signal that was used to terminate the process. For example, `SIGFPE`.

If a signal terminated the process, this property is defined and included in the error message. Otherwise it is `undefined`.

#### signalDescription

Type: `string | undefined`

A human-friendly description of the signal that was used to terminate the process. For example, `Floating point arithmetic error`.

If a signal terminated the process, this property is defined and included in the error message. Otherwise it is `undefined`. It is also `undefined` when the signal is very uncommon which should seldomly happen.

#### originalMessage

Expand Down
14 changes: 14 additions & 0 deletions test/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,15 @@ if (process.platform !== 'win32') {
t.is(signal, 'SIGINT');
});

test('error.signalDescription is defined', async t => {
const subprocess = execa('noop');

process.kill(subprocess.pid, 'SIGINT');

const {signalDescription} = await t.throwsAsync(subprocess, {message: /User interruption with CTRL-C/});
t.is(signalDescription, 'User interruption with CTRL-C');
});

test('error.signal is SIGTERM', async t => {
const subprocess = execa('noop');

Expand Down Expand Up @@ -167,6 +176,11 @@ test('result.signal is undefined if process failed, but was not killed', async t
t.is(signal, undefined);
});

test('result.signalDescription is undefined for successful execution', async t => {
const {signalDescription} = await execa('noop');
t.is(signalDescription, undefined);
});

test('error.code is undefined on success', async t => {
const {code} = await execa('noop');
t.is(code, undefined);
Expand Down

0 comments on commit d8cb08f

Please sign in to comment.