This commit introduces a custom error object to provide additional context for errors throwing during parsing and compiling operations, improving troubleshooting. By integrating error context handling, the error messages become more informative and user-friendly, providing sequence of trace with context to aid in troubleshooting. Changes include: - Introduce custom error object that extends errors with contextual information. This replaces previous usages of `AggregateError` which is not displayed well by browsers when logged. - Improve parsing functions to encapsulate error context with more details. - Increase unit test coverage and refactor the related code to be more testable.
55 lines
2.2 KiB
TypeScript
55 lines
2.2 KiB
TypeScript
import { isFunction, type ConstructorArguments } from '@/TypeHelpers';
|
|
|
|
/*
|
|
Provides a unified and resilient way to extend errors across platforms.
|
|
|
|
Rationale:
|
|
- Babel:
|
|
> "Built-in classes cannot be properly subclassed due to limitations in ES5"
|
|
> https://web.archive.org/web/20230810014108/https://babeljs.io/docs/caveats#classes
|
|
- TypeScript:
|
|
> "Extending built-ins like Error, Array, and Map may no longer work"
|
|
> https://web.archive.org/web/20230810014143/https://github.com/Microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
*/
|
|
export abstract class CustomError extends Error {
|
|
constructor(...args: ConstructorArguments<typeof Error>) {
|
|
super(...args);
|
|
|
|
fixPrototype(this, new.target.prototype);
|
|
ensureStackTrace(this);
|
|
|
|
this.name = this.constructor.name;
|
|
}
|
|
}
|
|
|
|
interface ErrorPrototypeManipulation {
|
|
getSetPrototypeOf: () => (typeof Object.setPrototypeOf | undefined);
|
|
getCaptureStackTrace: () => (typeof Error.captureStackTrace | undefined);
|
|
}
|
|
|
|
export const PlatformErrorPrototypeManipulation: ErrorPrototypeManipulation = {
|
|
getSetPrototypeOf: () => Object.setPrototypeOf,
|
|
getCaptureStackTrace: () => Error.captureStackTrace,
|
|
};
|
|
|
|
function fixPrototype(target: Error, prototype: CustomError) {
|
|
// This is recommended by TypeScript guidelines.
|
|
// Source: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
|
|
// Snapshots: https://web.archive.org/web/20231111234849/https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget, https://archive.ph/tr7cX#support-for-newtarget
|
|
const setPrototypeOf = PlatformErrorPrototypeManipulation.getSetPrototypeOf();
|
|
if (!isFunction(setPrototypeOf)) {
|
|
return;
|
|
}
|
|
setPrototypeOf(target, prototype);
|
|
}
|
|
|
|
function ensureStackTrace(target: Error) {
|
|
const captureStackTrace = PlatformErrorPrototypeManipulation.getCaptureStackTrace();
|
|
if (!isFunction(captureStackTrace)) {
|
|
// captureStackTrace is only available on V8, if it's not available
|
|
// modern JS engines will usually generate a stack trace on error objects when they're thrown.
|
|
return;
|
|
}
|
|
captureStackTrace(target, target.constructor);
|
|
}
|