Repository: sindresorhus/execa
Pull request #373 (2019-10-06 14:07:19)

Add `error.originalMessage` property
When an `error` event is thrown on the child process or when a timeout happens, we re-use the original `Error` instance instead of creating a new one. However we override the `error.message`.

This PR keep the original `error.message` as an additional property `error.originalMessage`.

I need this for one my projects using Execa. That project should proxy a user's command in such a way that users should not be aware of that proxy. In particular, the error messages produced by Execa should be not printed to the console. However the error messages coming from `error` events on the child process (such as `command not found`) should be printed. This PR allows for this to happen.

ehmicky: Fixed :)

Patch:
diff --git a/index.d.ts b/index.d.ts
index f32ac41..2050cfa 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -302,6 +302,13 @@ declare namespace execa {
 		The error message.
 		*/
 		message: string;
+
+		/**
+		Original error message. This is `undefined` unless the child process exited due to an `error` event or a timeout.
+
+		The `message` property contains both the `originalMessage` and some additional information added by Execa.
+		*/
+		originalMessage?: string;
 	}
 
 	interface ExecaError<StdoutErrorType = string>
diff --git a/lib/error.js b/lib/error.js
index c746fbf..f071c5f 100644
--- a/lib/error.js
+++ b/lib/error.js
@@ -53,6 +53,7 @@ const makeError = ({
 	const message = `Command ${prefix}: ${command}`;
 
 	if (error instanceof Error) {
+		error.originalMessage = error.message;
 		error.message = `${message}\n${error.message}`;
 	} else {
 		error = new Error(message);
diff --git a/readme.md b/readme.md
index 6b42383..44b9ee2 100644
--- a/readme.md
+++ b/readme.md
@@ -277,6 +277,14 @@ Type: `string | undefined`
 
 The signal that was used to terminate the process.
 
+#### originalMessage
+
+Type: `string | undefined`
+
+Original error message. This is `undefined` unless the child process exited due to an `error` event or a timeout.
+
+The `message` property contains both the `originalMessage` and some additional information added by Execa.
+
 ### options
 
 Type: `object`


Test patch:
diff --git a/index.test-d.ts b/index.test-d.ts
index f1261b3..74ff581 100644
--- a/index.test-d.ts
+++ b/index.test-d.ts
@@ -40,6 +40,7 @@ try {
 	expectType<boolean>(execaError.isCanceled);
 	expectType<boolean>(execaError.killed);
 	expectType<string | undefined>(execaError.signal);
+	expectType<string | undefined>(execaError.originalMessage);
 }
 
 try {
@@ -69,6 +70,7 @@ try {
 	expectError(execaError.isCanceled);
 	expectType<boolean>(execaError.killed);
 	expectType<string | undefined>(execaError.signal);
+	expectType<string | undefined>(execaError.originalMessage);
 }
 
 execa('unicorns', {cleanup: false});
diff --git a/test/error.js b/test/error.js
index a009b04..4a8288b 100644
--- a/test/error.js
+++ b/test/error.js
@@ -163,3 +163,8 @@ errorMessage.title = (message, expected) => `error.message matches: ${expected}`
 
 test(errorMessage, /Command failed with exit code 2.*: exit 2 foo bar/, 2, 'foo', 'bar');
 test(errorMessage, /Command failed with exit code 3.*: exit 3 baz quz/, 3, 'baz', 'quz');
+
+test('Original error message is kept', async t => {
+	const {originalMessage} = await t.throwsAsync(execa('wrong command'));
+	t.is(originalMessage, 'spawn wrong command ENOENT');
+});