Improve context for errors thrown by compiler
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.
This commit is contained in:
@@ -5,15 +5,21 @@ import type { IScript } from './IScript';
|
||||
export class Category extends BaseEntity<number> implements ICategory {
|
||||
private allSubScripts?: ReadonlyArray<IScript> = undefined;
|
||||
|
||||
constructor(
|
||||
id: number,
|
||||
public readonly name: string,
|
||||
public readonly docs: ReadonlyArray<string>,
|
||||
public readonly subCategories: ReadonlyArray<ICategory>,
|
||||
public readonly scripts: ReadonlyArray<IScript>,
|
||||
) {
|
||||
super(id);
|
||||
validateCategory(this);
|
||||
public readonly name: string;
|
||||
|
||||
public readonly docs: ReadonlyArray<string>;
|
||||
|
||||
public readonly subCategories: ReadonlyArray<ICategory>;
|
||||
|
||||
public readonly scripts: ReadonlyArray<IScript>;
|
||||
|
||||
constructor(parameters: CategoryInitParameters) {
|
||||
super(parameters.id);
|
||||
validateParameters(parameters);
|
||||
this.name = parameters.name;
|
||||
this.docs = parameters.docs;
|
||||
this.subCategories = parameters.subcategories;
|
||||
this.scripts = parameters.scripts;
|
||||
}
|
||||
|
||||
public includes(script: IScript): boolean {
|
||||
@@ -28,6 +34,14 @@ export class Category extends BaseEntity<number> implements ICategory {
|
||||
}
|
||||
}
|
||||
|
||||
export interface CategoryInitParameters {
|
||||
readonly id: number;
|
||||
readonly name: string;
|
||||
readonly docs: ReadonlyArray<string>;
|
||||
readonly subcategories: ReadonlyArray<ICategory>;
|
||||
readonly scripts: ReadonlyArray<IScript>;
|
||||
}
|
||||
|
||||
function parseScriptsRecursively(category: ICategory): ReadonlyArray<IScript> {
|
||||
return [
|
||||
...category.scripts,
|
||||
@@ -35,11 +49,11 @@ function parseScriptsRecursively(category: ICategory): ReadonlyArray<IScript> {
|
||||
];
|
||||
}
|
||||
|
||||
function validateCategory(category: ICategory) {
|
||||
if (!category.name) {
|
||||
function validateParameters(parameters: CategoryInitParameters) {
|
||||
if (!parameters.name) {
|
||||
throw new Error('missing name');
|
||||
}
|
||||
if (category.subCategories.length === 0 && category.scripts.length === 0) {
|
||||
if (parameters.subcategories.length === 0 && parameters.scripts.length === 0) {
|
||||
throw new Error('A category must have at least one sub-category or script');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,21 @@ import type { IScript } from './IScript';
|
||||
import type { IScriptCode } from './IScriptCode';
|
||||
|
||||
export class Script extends BaseEntity<string> implements IScript {
|
||||
constructor(
|
||||
public readonly name: string,
|
||||
public readonly code: IScriptCode,
|
||||
public readonly docs: ReadonlyArray<string>,
|
||||
public readonly level?: RecommendationLevel,
|
||||
) {
|
||||
super(name);
|
||||
validateLevel(level);
|
||||
public readonly name: string;
|
||||
|
||||
public readonly code: IScriptCode;
|
||||
|
||||
public readonly docs: ReadonlyArray<string>;
|
||||
|
||||
public readonly level?: RecommendationLevel;
|
||||
|
||||
constructor(parameters: ScriptInitParameters) {
|
||||
super(parameters.name);
|
||||
this.name = parameters.name;
|
||||
this.code = parameters.code;
|
||||
this.docs = parameters.docs;
|
||||
this.level = parameters.level;
|
||||
validateLevel(parameters.level);
|
||||
}
|
||||
|
||||
public canRevert(): boolean {
|
||||
@@ -19,6 +26,13 @@ export class Script extends BaseEntity<string> implements IScript {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ScriptInitParameters {
|
||||
readonly name: string;
|
||||
readonly code: IScriptCode;
|
||||
readonly docs: ReadonlyArray<string>;
|
||||
readonly level?: RecommendationLevel;
|
||||
}
|
||||
|
||||
function validateLevel(level?: RecommendationLevel) {
|
||||
if (level !== undefined && !(level in RecommendationLevel)) {
|
||||
throw new Error(`invalid level: ${level}`);
|
||||
|
||||
10
src/domain/ScriptCodeFactory.ts
Normal file
10
src/domain/ScriptCodeFactory.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ScriptCode } from './ScriptCode';
|
||||
import type { IScriptCode } from './IScriptCode';
|
||||
|
||||
export interface ScriptCodeFactory {
|
||||
(
|
||||
...args: ConstructorParameters<typeof ScriptCode>
|
||||
): IScriptCode;
|
||||
}
|
||||
|
||||
export const createScriptCode: ScriptCodeFactory = (...args) => new ScriptCode(...args);
|
||||
Reference in New Issue
Block a user