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 1532be9d00)
This commit is contained in:
SkyZeroZx
2026-01-02 14:43:58 -05:00
committed by kirjs
parent 1748c8984c
commit 8f26a52aa8
6 changed files with 34 additions and 9 deletions
+9 -1
View File
@@ -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)
+4 -1
View File
@@ -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,
@@ -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<T>(id: string): Type<T> {
}
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`,
);
}
@@ -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<any> | null, incoming: Type<any>): 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)}`,
);
}
}
@@ -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<any>)._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);
}
@@ -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!/);
}));
});
});