From 4212c7b9e0b1500378a1e4e88efc2d59f39f3d29 Mon Sep 17 00:00:00 2001 From: undergroundwires Date: Sat, 25 May 2024 13:55:30 +0200 Subject: [PATCH] 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. --- src/application/Common/CustomError.ts | 6 +- src/application/Parser/CategoryParser.ts | 158 +++-- src/application/Parser/ContextualError.ts | 42 ++ src/application/Parser/DocumentationParser.ts | 8 +- .../Parser/NodeValidation/NodeDataError.ts | 34 - .../NodeValidation/NodeDataErrorContext.ts | 25 + .../NodeDataErrorContextMessage.ts | 35 ++ .../Parser/NodeValidation/NodeDataType.ts | 4 + .../NodeValidation/NodeDataValidator.ts | 69 ++ .../Parser/NodeValidation/NodeType.ts | 4 - .../Parser/NodeValidation/NodeValidator.ts | 39 -- .../Expressions/Expression/Expression.ts | 21 +- .../Expression/ExpressionPositionFactory.ts | 13 +- .../Parser/CompositeExpressionParser.ts | 4 +- .../Expressions/Parser/Regex/RegexParser.ts | 108 +++- .../ParameterSubstitutionParser.ts | 4 +- .../Call/Argument/FunctionCallArgument.ts | 2 +- .../AdaptiveFunctionCallCompiler.ts | 4 +- .../NestedFunctionArgumentCompiler.ts | 25 +- .../Strategies/NestedFunctionCallCompiler.ts | 18 +- .../FunctionParameterCollectionFactory.ts | 12 + .../Function/SharedFunctionCollection.ts | 2 +- .../Function/SharedFunctionsParser.ts | 76 ++- .../Parser/Script/Compiler/ScriptCompiler.ts | 9 +- src/application/Parser/Script/ScriptParser.ts | 121 ++-- .../ScriptingDefinition/CodeSubstituter.ts | 5 +- src/domain/Category.ts | 38 +- src/domain/Script.ts | 30 +- src/domain/ScriptCodeFactory.ts | 10 + .../application/Parser/CategoryParser.spec.ts | 587 ++++++++++++------ .../Parser/ContextualError.spec.ts | 121 ++++ .../Parser/ContextualErrorTester.ts | 53 ++ .../DataValidationTestScenarioGenerator.ts | 36 ++ .../Parser/NodeDataValidationTester.ts | 213 +++++++ .../NodeValidation/NodeDataError.spec.ts | 60 -- .../NodeValidation/NodeDataValidator.spec.ts | 242 ++++++++ .../NodeValidation/NodeValidator.spec.ts | 99 --- .../NodeValidation/NodeValidatorTestRunner.ts | 87 --- .../Expressions/Expression/Expression.spec.ts | 6 +- ...c.ts => ExpressionPositionFactory.spec.ts} | 27 +- .../Parser/Regex/RegexParser.spec.ts | 484 +++++++++++---- .../Argument/FunctionCallArgument.spec.ts | 2 +- .../FunctionCallSequenceCompiler.spec.ts | 4 +- .../NestedFunctionCallCompiler.spec.ts | 175 ++++-- .../AdaptiveFunctionCallCompiler.spec.ts | 24 +- .../NestedFunctionArgumentCompiler.spec.ts | 42 +- .../FunctionParameterCollection.spec.ts | 4 +- ...FunctionParameterCollectionFactory.spec.ts | 23 + .../Function/SharedFunctionCollection.spec.ts | 2 +- .../Function/SharedFunctionsParser.spec.ts | 102 ++- .../Script/Compiler/ScriptCompiler.spec.ts | 183 ++++-- .../Parser/Script/ScriptParser.spec.ts | 466 ++++++++++---- .../Script/Validation/CodeValidator.spec.ts | 4 +- tests/unit/domain/Category.spec.ts | 147 ++++- tests/unit/domain/Script.spec.ts | 22 +- tests/unit/domain/ScriptCodeFactory.spec.ts | 52 ++ .../EnvironmentVariablesFactory.ts | 4 +- .../bootstrapping/DependencyProvider.spec.ts | 24 +- .../Reverter/ReverterFactory.spec.ts | 3 +- .../CategoryNodeMetadataConverter.spec.ts | 6 +- .../Hooks/Log/ClientLoggerFactory.spec.ts | 4 +- tests/unit/shared/ExceptionCollector.ts | 16 +- .../unit/shared/Stubs/CategoryFactoryStub.ts | 19 + tests/unit/shared/Stubs/CodeValidatorStub.ts | 8 +- .../Stubs/ErrorWithContextWrapperStub.ts | 4 + tests/unit/shared/Stubs/ErrorWrapperStub.ts | 67 ++ .../shared/Stubs/FunctionCallCompilerStub.ts | 28 +- .../Stubs/FunctionParameterCollectionStub.ts | 2 +- .../shared/Stubs/FunctionParameterStub.ts | 2 +- .../shared/Stubs/NodeDataErrorContextStub.ts | 17 +- .../shared/Stubs/NodeDataValidatorStub.ts | 57 ++ .../shared/Stubs/ScriptCodeFactoryStub.ts | 22 + tests/unit/shared/Stubs/ScriptCodeStub.ts | 4 +- tests/unit/shared/Stubs/ScriptFactoryStub.ts | 19 + tests/unit/shared/Stubs/ScriptParserStub.ts | 37 ++ .../Stubs/StubWithObservableMethodCalls.ts | 4 +- ...letonTests.ts => SingletonFactoryTests.ts} | 4 +- .../shared/TestCases/TransientFactoryTests.ts | 24 + 78 files changed, 3323 insertions(+), 1245 deletions(-) create mode 100644 src/application/Parser/ContextualError.ts delete mode 100644 src/application/Parser/NodeValidation/NodeDataError.ts create mode 100644 src/application/Parser/NodeValidation/NodeDataErrorContext.ts create mode 100644 src/application/Parser/NodeValidation/NodeDataErrorContextMessage.ts create mode 100644 src/application/Parser/NodeValidation/NodeDataType.ts create mode 100644 src/application/Parser/NodeValidation/NodeDataValidator.ts delete mode 100644 src/application/Parser/NodeValidation/NodeType.ts delete mode 100644 src/application/Parser/NodeValidation/NodeValidator.ts create mode 100644 src/application/Parser/Script/Compiler/Function/Parameter/FunctionParameterCollectionFactory.ts create mode 100644 src/domain/ScriptCodeFactory.ts create mode 100644 tests/unit/application/Parser/ContextualError.spec.ts create mode 100644 tests/unit/application/Parser/ContextualErrorTester.ts create mode 100644 tests/unit/application/Parser/DataValidationTestScenarioGenerator.ts create mode 100644 tests/unit/application/Parser/NodeDataValidationTester.ts delete mode 100644 tests/unit/application/Parser/NodeValidation/NodeDataError.spec.ts create mode 100644 tests/unit/application/Parser/NodeValidation/NodeDataValidator.spec.ts delete mode 100644 tests/unit/application/Parser/NodeValidation/NodeValidator.spec.ts delete mode 100644 tests/unit/application/Parser/NodeValidation/NodeValidatorTestRunner.ts rename tests/unit/application/Parser/Script/Compiler/Expressions/Expression/{ExpressiontPositionFactory.spec.ts => ExpressionPositionFactory.spec.ts} (84%) create mode 100644 tests/unit/application/Parser/Script/Compiler/Function/Parameter/FunctionParameterCollectionFactory.spec.ts create mode 100644 tests/unit/domain/ScriptCodeFactory.spec.ts create mode 100644 tests/unit/shared/Stubs/CategoryFactoryStub.ts create mode 100644 tests/unit/shared/Stubs/ErrorWithContextWrapperStub.ts create mode 100644 tests/unit/shared/Stubs/ErrorWrapperStub.ts create mode 100644 tests/unit/shared/Stubs/NodeDataValidatorStub.ts create mode 100644 tests/unit/shared/Stubs/ScriptCodeFactoryStub.ts create mode 100644 tests/unit/shared/Stubs/ScriptFactoryStub.ts create mode 100644 tests/unit/shared/Stubs/ScriptParserStub.ts rename tests/unit/shared/TestCases/{SingletonTests.ts => SingletonFactoryTests.ts} (84%) create mode 100644 tests/unit/shared/TestCases/TransientFactoryTests.ts diff --git a/src/application/Common/CustomError.ts b/src/application/Common/CustomError.ts index 8a31884d..eff52bf6 100644 --- a/src/application/Common/CustomError.ts +++ b/src/application/Common/CustomError.ts @@ -1,4 +1,4 @@ -import { isFunction } from '@/TypeHelpers'; +import { isFunction, type ConstructorArguments } from '@/TypeHelpers'; /* Provides a unified and resilient way to extend errors across platforms. @@ -12,8 +12,8 @@ import { isFunction } from '@/TypeHelpers'; > 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(message?: string, options?: ErrorOptions) { - super(message, options); + constructor(...args: ConstructorArguments) { + super(...args); fixPrototype(this, new.target.prototype); ensureStackTrace(this); diff --git a/src/application/Parser/CategoryParser.ts b/src/application/Parser/CategoryParser.ts index ccaff8e8..1eaa3fbc 100644 --- a/src/application/Parser/CategoryParser.ts +++ b/src/application/Parser/CategoryParser.ts @@ -3,10 +3,12 @@ import type { } from '@/application/collections/'; import { Script } from '@/domain/Script'; import { Category } from '@/domain/Category'; -import { NodeValidator } from '@/application/Parser/NodeValidation/NodeValidator'; -import { NodeType } from '@/application/Parser/NodeValidation/NodeType'; -import { parseDocs } from './DocumentationParser'; -import { parseScript } from './Script/ScriptParser'; +import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError'; +import type { ICategory } from '@/domain/ICategory'; +import { parseDocs, type DocsParser } from './DocumentationParser'; +import { parseScript, type ScriptParser } from './Script/ScriptParser'; +import { createNodeDataValidator, type NodeDataValidator, type NodeDataValidatorFactory } from './NodeValidation/NodeDataValidator'; +import { NodeDataType } from './NodeValidation/NodeDataType'; import type { ICategoryCollectionParseContext } from './Script/ICategoryCollectionParseContext'; let categoryIdCounter = 0; @@ -14,96 +16,108 @@ let categoryIdCounter = 0; export function parseCategory( category: CategoryData, context: ICategoryCollectionParseContext, - factory: CategoryFactoryType = CategoryFactory, + utilities: CategoryParserUtilities = DefaultCategoryParserUtilities, ): Category { return parseCategoryRecursively({ categoryData: category, context, - factory, + utilities, }); } -interface ICategoryParseContext { - readonly categoryData: CategoryData, - readonly context: ICategoryCollectionParseContext, - readonly factory: CategoryFactoryType, - readonly parentCategory?: CategoryData, +interface CategoryParseContext { + readonly categoryData: CategoryData; + readonly context: ICategoryCollectionParseContext; + readonly parentCategory?: CategoryData; + readonly utilities: CategoryParserUtilities; } -function parseCategoryRecursively(context: ICategoryParseContext): Category | never { - ensureValidCategory(context.categoryData, context.parentCategory); - const children: ICategoryChildren = { - subCategories: new Array(), - subScripts: new Array