Skip to content

Commit

Permalink
fix: use native ES2022 error cause (#5010)
Browse files Browse the repository at this point in the history
This improves error messages and stack traces. For example:

Before:
```
Error

at new SubprocessError (.../dist/index.js:41:23)
at ChildProcess.<anonymous> (.../dist/index.js:125:27)
at ChildProcess.emit (node:events:513:28)
at maybeClose (node:internal/child_process:1100:16)
at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5)
```

After:
```
SubprocessError: Signal exit from subprocess.
    at ChildProcess.<anonymous> (.../dist/index.js:122:27)
    at ChildProcess.emit (node:events:513:28)
    at maybeClose (node:internal/child_process:1100:16)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5) {
  code: 'ERR_SUBPROCESS_SIGNAL_EXIT',
  signal: 'SIGTERM'
}
```
  • Loading branch information
ltm authored May 17, 2023
1 parent 9326871 commit 0c4cd0f
Show file tree
Hide file tree
Showing 5 changed files with 10 additions and 37 deletions.
17 changes: 1 addition & 16 deletions packages/@ionic/cli-framework/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as lodash from 'lodash';
import * as util from 'util';

import { ValidationError } from './definitions';
Expand All @@ -10,25 +9,11 @@ export const ERROR_IPC_UNKNOWN_PROCEDURE = 'ERR_ICF_IPC_UNKNOWN_PROCEDURE';

export abstract class BaseError extends Error {
abstract readonly name: string;
message: string;
stack: string;
code?: string;
error?: Error;
exitCode?: number;

constructor(message: string) {
super(message);
this.message = message;
this.stack = (new Error()).stack || '';
}

toString(): string {
const repr = lodash.pick(this, lodash.pull(lodash.keys(this), 'error'));

return (
`${this.name}: ${this.message} ${util.inspect(repr, { breakLength: Infinity })} ${this.stack} ` +
`${this.error ? `\nWrapped: ${this.error.stack ? this.error.stack : this.error}` : ''}`
);
return util.inspect(this);
}

inspect(): string {
Expand Down
7 changes: 2 additions & 5 deletions packages/@ionic/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { readPackageJsonFile } from '@ionic/cli-framework/utils/node';
import { processExit } from '@ionic/utils-process';
import * as Debug from 'debug';
import * as path from 'path';
import * as util from 'util';

import { IonicNamespace } from './commands';
import { IPCMessage, IonicContext, IonicEnvironment } from './definitions';
Expand Down Expand Up @@ -171,11 +172,7 @@ export async function run(pargv: string[]): Promise<void> {
} else if (err instanceof BaseError) {
ienv.log.error(err.message);
} else {
ienv.log.msg(failure(String(err.stack ? err.stack : err)));

if (err.stack) {
debug(failure(String(err.stack)));
}
ienv.log.rawmsg(failure(util.inspect(err)));
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/@ionic/cli/src/lib/project/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export class ProjectDetailsError extends BaseException {
/**
* The underlying error that caused this error.
*/
readonly error?: Error
cause?: Error
) {
super(msg);
super(msg, { cause });
}
}

Expand Down Expand Up @@ -193,7 +193,7 @@ export class ProjectDetails {
if (e1) {
log.error(
`Error while loading config (project config: ${strong(prettyPath(result.configPath))})\n` +
`${e1.error ? `${e1.message}: ${failure(e1.error.toString())}` : failure(e1.message)}. ` +
`${e1.cause ? `${e1.message}: ${failure(e1.cause.toString())}` : failure(e1.message)}. ` +
`Run ${input('ionic init')} to re-initialize your Ionic project. Without a valid project config, the CLI will not have project context.`
);

Expand Down
14 changes: 2 additions & 12 deletions packages/@ionic/utils-subprocess/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,11 @@ export function convertPATH(path = process.env.PATH || ''): string {

export class SubprocessError extends Error {
readonly name = 'SubprocessError';
message: string;
stack: string;

code?: typeof ERROR_COMMAND_NOT_FOUND | typeof ERROR_NON_ZERO_EXIT | typeof ERROR_SIGNAL_EXIT;
error?: Error;
output?: string;
signal?: string;
exitCode?: number;

constructor(message: string) {
super(message);
this.message = message;
this.stack = (new Error()).stack || '';
}
}

export interface SubprocessOptions extends SpawnOptions {}
Expand Down Expand Up @@ -172,13 +163,12 @@ export class Subprocess {
let err: SubprocessError;

if (error.code === 'ENOENT') {
err = new SubprocessError('Command not found.');
err = new SubprocessError('Command not found.', { cause: error });
err.code = ERROR_COMMAND_NOT_FOUND;
} else {
err = new SubprocessError('Command error.');
err = new SubprocessError('Command error.', { cause: error });
}

err.error = error;
reject(err);
});

Expand Down
3 changes: 2 additions & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"target": "ES2021",
"types": [],
"lib": [
"ES2021"
"ES2021",
"ES2022.Error"
]
}
}

0 comments on commit 0c4cd0f

Please sign in to comment.