From 8f26a52aa8f23ff335df004dfb091d59fdceacaf Mon Sep 17 00:00:00 2001 From: SkyZeroZx <73321943+SkyZeroZx@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:43:58 -0500 Subject: [PATCH] refactor(core): introduce tree-shakeable runtime error codes for `NgModule` handling and `ViewContainerRef` errors Adds new tree-shakeable runtime error codes to improve error reporting for NgModule resolution issues (duplicate or missing IDs) and invalid ViewContainerRef operations involving destroyed views. (cherry picked from commit 1532be9d00fddc2bfe89cac2200e0d2a0a9fd4df) --- goldens/public-api/core/errors.api.md | 10 +++++++++- packages/core/src/errors.ts | 5 ++++- packages/core/src/linker/ng_module_factory_loader.ts | 6 +++++- packages/core/src/linker/ng_module_registration.ts | 7 +++++-- packages/core/src/linker/view_container_ref.ts | 11 +++++++++-- packages/core/test/linker/integration_spec.ts | 4 ++-- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/goldens/public-api/core/errors.api.md b/goldens/public-api/core/errors.api.md index 56bb883d919..2471ef65509 100644 --- a/goldens/public-api/core/errors.api.md +++ b/goldens/public-api/core/errors.api.md @@ -42,6 +42,8 @@ export const enum RuntimeErrorCode { // (undocumented) DUPLICATE_DIRECTIVE = 309, // (undocumented) + DUPLICATE_NG_MODULE_ID = 921, + // (undocumented) EXPORT_NOT_FOUND = -301, // (undocumented) EXPRESSION_CHANGED_AFTER_CHECKED = -100, @@ -134,6 +136,8 @@ export const enum RuntimeErrorCode { // (undocumented) MUST_PROVIDE_STREAM_OPTION = 990, // (undocumented) + NG_MODULE_ID_NOT_FOUND = 920, + // (undocumented) NO_BINDING_TARGET = 315, // (undocumented) NO_COMPONENT_FACTORY_FOUND = 917, @@ -200,7 +204,11 @@ export const enum RuntimeErrorCode { // (undocumented) VIEW_ALREADY_ATTACHED = 902, // (undocumented) - VIEW_ALREADY_DESTROYED = 911 + VIEW_ALREADY_DESTROYED = 911, + // (undocumented) + VIEW_DESTROYED_INSERT_ERROR = 922, + // (undocumented) + VIEW_DESTROYED_MOVE_ERROR = 923 } // (No @packageDocumentation comment for this package) diff --git a/packages/core/src/errors.ts b/packages/core/src/errors.ts index 3cba1178a71..f44afebf30c 100644 --- a/packages/core/src/errors.ts +++ b/packages/core/src/errors.ts @@ -135,7 +135,10 @@ export const enum RuntimeErrorCode { NO_COMPONENT_FACTORY_FOUND = 917, EXTERNAL_RESOURCE_LOADING_FAILED = 918, DEF_TYPE_UNDEFINED = -919, - + NG_MODULE_ID_NOT_FOUND = 920, + DUPLICATE_NG_MODULE_ID = 921, + VIEW_DESTROYED_INSERT_ERROR = 922, + VIEW_DESTROYED_MOVE_ERROR = 923, // Signal integration errors REQUIRED_INPUT_NO_VALUE = -950, REQUIRED_QUERY_NO_VALUE = -951, diff --git a/packages/core/src/linker/ng_module_factory_loader.ts b/packages/core/src/linker/ng_module_factory_loader.ts index 0a465caab8e..4c3478436f1 100644 --- a/packages/core/src/linker/ng_module_factory_loader.ts +++ b/packages/core/src/linker/ng_module_factory_loader.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ +import {RuntimeError, RuntimeErrorCode} from '../errors'; import {Type} from '../interface/type'; import {NgModuleFactory as R3NgModuleFactory} from '../render3/ng_module_ref'; @@ -38,5 +39,8 @@ export function getNgModuleById(id: string): Type { } function noModuleError(id: string): Error { - return new Error(`No module with ID ${id} loaded`); + return new RuntimeError( + RuntimeErrorCode.NG_MODULE_ID_NOT_FOUND, + ngDevMode && `No module with ID ${id} loaded`, + ); } diff --git a/packages/core/src/linker/ng_module_registration.ts b/packages/core/src/linker/ng_module_registration.ts index 6c5e7875edb..4bce4a2ffd7 100644 --- a/packages/core/src/linker/ng_module_registration.ts +++ b/packages/core/src/linker/ng_module_registration.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ +import {RuntimeError, RuntimeErrorCode} from '../errors'; import {Type} from '../interface/type'; import {NgModuleType} from '../metadata/ng_module_def'; import {stringify} from '../util/stringify'; @@ -24,8 +25,10 @@ let checkForDuplicateNgModules = true; function assertSameOrNotExisting(id: string, type: Type | null, incoming: Type): void { if (type && type !== incoming && checkForDuplicateNgModules) { - throw new Error( - `Duplicate module registered for ${id} - ${stringify(type)} vs ${stringify(type.name)}`, + throw new RuntimeError( + RuntimeErrorCode.DUPLICATE_NG_MODULE_ID, + ngDevMode && + `Duplicate module registered for ${id} - ${stringify(type)} vs ${stringify(type.name)}`, ); } } diff --git a/packages/core/src/linker/view_container_ref.ts b/packages/core/src/linker/view_container_ref.ts index e6132e1033b..48ff86ca24f 100644 --- a/packages/core/src/linker/view_container_ref.ts +++ b/packages/core/src/linker/view_container_ref.ts @@ -78,6 +78,7 @@ import {EmbeddedViewRef, ViewRef} from './view_ref'; import {addLViewToLContainer, createLContainer, detachView} from '../render3/view/container'; import {addToEndOfViewTree} from '../render3/view/construction'; import {Binding, DirectiveWithBindings} from '../render3/dynamic_bindings'; +import {RuntimeError, RuntimeErrorCode} from '../errors'; /** * Represents a container where one or more views can be attached to a component. @@ -592,7 +593,10 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef { const lView = (viewRef as R3ViewRef)._lView!; if (ngDevMode && viewRef.destroyed) { - throw new Error('Cannot insert a destroyed View in a ViewContainer!'); + throw new RuntimeError( + RuntimeErrorCode.VIEW_DESTROYED_INSERT_ERROR, + ngDevMode && 'Cannot insert a destroyed View in a ViewContainer!', + ); } if (viewAttachedToContainer(lView)) { @@ -641,7 +645,10 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef { override move(viewRef: ViewRef, newIndex: number): ViewRef { if (ngDevMode && viewRef.destroyed) { - throw new Error('Cannot move a destroyed View in a ViewContainer!'); + throw new RuntimeError( + RuntimeErrorCode.VIEW_DESTROYED_MOVE_ERROR, + ngDevMode && 'Cannot move a destroyed View in a ViewContainer!', + ); } return this.insert(viewRef, newIndex); } diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index 6efc1f70eb1..81877fc51ee 100644 --- a/packages/core/test/linker/integration_spec.ts +++ b/packages/core/test/linker/integration_spec.ts @@ -1240,7 +1240,7 @@ describe('integration tests', function () { ref.destroy(); expect(() => { dynamicVp.insert(ref.hostView); - }).toThrowError('Cannot insert a destroyed View in a ViewContainer!'); + }).toThrowError(/Cannot insert a destroyed View in a ViewContainer!/); })); }); @@ -1257,7 +1257,7 @@ describe('integration tests', function () { ref.destroy(); expect(() => { dynamicVp.move(ref.hostView, 1); - }).toThrowError('Cannot move a destroyed View in a ViewContainer!'); + }).toThrowError(/Cannot move a destroyed View in a ViewContainer!/); })); }); });