Skip to content

Commit c6b9a3e

Browse files
pmvaldpkozlowski-opensource
authored andcommitted
refactor(core): forbid rendering orphan components in local compilation mode (angular#51726)
Certain code patterns and tools in Google (and possibly 3P world) lead to the situation that a component is bootstrapped/rendered without its ng-module being loaded in the browser. Technically speaking this should be an anti-pattern since the ng-module could contain some runtime logic (e.g., providing something, calling some services, etc) and its not being loaded leads to unexpected behaviour. However, in many cases ng-module is an empty class and its only usage is for providing scope, and since in AoT full compilation mode we already hard-code dependencies into components so we can get away with not loading the ng-module. But in AoT local compilation mode it is not possible to get away since the component's dependencies are computed in runtime and the presence of the corresponding ng-module in the browser is needed. For this reason in this change it is forbidden to attempt to render a component without first loading its ng-module in local compilation mode and an explicit error message is created to make this situation clear. This error message can help with catching such cases when running TGP in Google. It would be an interesting question as to whether to ban this situation in full compilation mode as well, as it is error prone and these errors are sometimes very hard to debug. PR Close angular#51726
1 parent 5a20a44 commit c6b9a3e

File tree

4 files changed

+10
-5
lines changed

4 files changed

+10
-5
lines changed

goldens/public-api/core/errors.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ export const enum RuntimeErrorCode {
117117
// (undocumented)
118118
RUNTIME_DEPS_INVALID_IMPORTED_TYPE = 1000,
119119
// (undocumented)
120+
RUNTIME_DEPS_ORPHAN_COMPONENT = 1001,
121+
// (undocumented)
120122
SIGNAL_WRITE_FROM_ILLEGAL_CONTEXT = 600,
121123
// (undocumented)
122124
TEMPLATE_STRUCTURE_ERROR = 305,

packages/core/src/errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export const enum RuntimeErrorCode {
113113

114114
// Runtime dependency tracker errors
115115
RUNTIME_DEPS_INVALID_IMPORTED_TYPE = 1000,
116+
RUNTIME_DEPS_ORPHAN_COMPONENT = 1001,
116117
}
117118

118119

packages/core/src/render3/deps_tracker/deps_tracker.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ class DepsTracker implements DepsTrackerApi {
8585
};
8686
} else {
8787
if (!this.ownerNgModule.has(type)) {
88-
return {dependencies: []};
88+
throw new RuntimeError(
89+
RuntimeErrorCode.RUNTIME_DEPS_ORPHAN_COMPONENT,
90+
`Orphan component found! Trying to render the component ${
91+
type.name} without first loading the NgModule that declares it. Make sure that you import the component's NgModule in the NgModule or the standalone component in which you are trying to render this component. Also make sure the way the app is bundled and served always includes the component's NgModule before the component.`);
8992
}
9093

9194
const scope = this.getNgModuleScope(this.ownerNgModule.get(type)!);

packages/core/test/render3/deps_tracker_spec.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -948,14 +948,13 @@ describe('runtime dependency tracker', () => {
948948
]));
949949
});
950950

951-
it('should return empty deps if component has no registered module', () => {
951+
it('should throw orphan component error if component has no registered module', () => {
952952
@Component({})
953953
class MainComponent {
954954
}
955955

956-
const ans = depsTracker.getComponentDependencies(MainComponent as ComponentType<any>);
957-
958-
expect(ans.dependencies).toEqual([]);
956+
expect(() => depsTracker.getComponentDependencies(MainComponent as ComponentType<any>))
957+
.toThrowError(/Orphan component found! Trying to render the component MainComponent/);
959958
});
960959

961960
it('should return empty deps if the compilation scope of the declaring module is corrupted',

0 commit comments

Comments
 (0)