fix(core): throw better errors for potential circular references

Currently circular references in user code manifest themselves with an error like `Cannot read properties of undefined (reading 'ɵcmp')`. This is a bit cryptic so these changes add an assertion mentioning circular references.

Relates to #65917.

(cherry picked from commit 7be4ddef1c)
This commit is contained in:
Kristiyan Kostadinov 2025-12-10 21:48:29 +01:00 committed by Andrew Kushnir
parent b8cb6cda30
commit 4c8fb3631d
11 changed files with 34 additions and 8 deletions

View file

@ -34,6 +34,8 @@ export const enum RuntimeErrorCode {
// (undocumented)
CYCLIC_DI_DEPENDENCY = -200,
// (undocumented)
DEF_TYPE_UNDEFINED = 919,
// (undocumented)
DEFER_IN_HMR_MODE = -751,
// (undocumented)
DEFER_LOADING_FAILED = -750,

View file

@ -134,6 +134,7 @@ export const enum RuntimeErrorCode {
MISSING_DIRECTIVE_DEFINITION = 916,
NO_COMPONENT_FACTORY_FOUND = 917,
EXTERNAL_RESOURCE_LOADING_FAILED = 918,
DEF_TYPE_UNDEFINED = 919,
// Signal integration errors
REQUIRED_INPUT_NO_VALUE = -950,

View file

@ -14,6 +14,7 @@ import {NG_COMP_DEF, NG_DIR_DEF, NG_MOD_DEF, NG_PIPE_DEF} from './fields';
import type {ComponentDef, DirectiveDef, PipeDef} from './interfaces/definition';
export function getNgModuleDef<T>(type: any): NgModuleDef<T> | null {
assertTypeDefined(type, '@NgModule');
return type[NG_MOD_DEF] || null;
}
@ -36,6 +37,7 @@ export function getNgModuleDefOrThrow<T>(type: any): NgModuleDef<T> | never {
*/
export function getComponentDef<T>(type: any): ComponentDef<T> | null {
assertTypeDefined(type, '@Component');
return type[NG_COMP_DEF] || null;
}
@ -52,13 +54,26 @@ export function getDirectiveDefOrThrow<T>(type: any): DirectiveDef<T> | never {
}
export function getDirectiveDef<T>(type: any): DirectiveDef<T> | null {
assertTypeDefined(type, '@Directive');
return type[NG_DIR_DEF] || null;
}
export function getPipeDef<T>(type: any): PipeDef<T> | null {
assertTypeDefined(type, '@Pipe');
return type[NG_PIPE_DEF] || null;
}
function assertTypeDefined(type: any, symbolType: string): void {
if (type == null) {
throw new RuntimeError(
RuntimeErrorCode.DEF_TYPE_UNDEFINED,
(typeof ngDevMode === 'undefined' || ngDevMode) &&
`Cannot read ${symbolType} metadata. This can indicate a runtime ` +
`circular dependency in your app that needs to be resolved.`,
);
}
}
/**
* Checks whether a given Component, Directive or Pipe is marked as standalone.
* This will return false if passed anything other than a Component, Directive, or Pipe class

View file

@ -339,6 +339,7 @@
"areAnimationSupported",
"arrRemove",
"assertNotDestroyed",
"assertTypeDefined",
"attachPatchData",
"balancePreviousStylesIntoKeyframes",
"balanceProperties",
@ -858,4 +859,4 @@
],
"lazy": []
}
}
}

View file

@ -265,6 +265,7 @@
"areAnimationSupported",
"arrRemove",
"assertNotDestroyed",
"assertTypeDefined",
"attachPatchData",
"baseElement",
"bind",
@ -689,4 +690,4 @@
],
"lazy": []
}
}
}

View file

@ -310,6 +310,7 @@
"areAnimationSupported",
"arrRemove",
"assertNotDestroyed",
"assertTypeDefined",
"attachPatchData",
"bind",
"bindingUpdated",
@ -744,4 +745,4 @@
"writeToDirectiveInput"
]
}
}
}

View file

@ -371,6 +371,7 @@
"assertControlPresent",
"assertNotDestroyed",
"assertPlatform",
"assertTypeDefined",
"attachPatchData",
"baseElement",
"bind",
@ -1018,4 +1019,4 @@
],
"lazy": []
}
}
}

View file

@ -375,6 +375,7 @@
"assertControlPresent",
"assertNotDestroyed",
"assertPlatform",
"assertTypeDefined",
"attachPatchData",
"baseElement",
"bind",
@ -1018,4 +1019,4 @@
],
"lazy": []
}
}
}

View file

@ -305,6 +305,7 @@
"areAnimationSupported",
"arrRemove",
"assertNotDestroyed",
"assertTypeDefined",
"attachPatchData",
"baseElement",
"bind",
@ -797,4 +798,4 @@
],
"lazy": []
}
}
}

View file

@ -415,6 +415,7 @@
"arrRemove",
"arrayEquals",
"assertNotDestroyed",
"assertTypeDefined",
"attachPatchData",
"baseElement",
"bind",
@ -1148,4 +1149,4 @@
],
"lazy": []
}
}
}

View file

@ -245,6 +245,7 @@
"areAnimationSupported",
"arrRemove",
"assertNotDestroyed",
"assertTypeDefined",
"attachPatchData",
"baseElement",
"bind",
@ -629,4 +630,4 @@
],
"lazy": []
}
}
}