fix(compiler-cli): fix bug tracking indirect NgModule dependencies (#36211)
The compiler needs to track the dependencies of a component, including any NgModules which happen to be present in a component's scope. If an upstream NgModule changes, any downstream components need to have their templates re-compiled and re-typechecked. Previously, the compiler handled this well for the A -> B -> C case where module A imports module B which re-exports module C. However, it fell apart in the A -> B -> C -> D case, because previously tracking focused on changes to components/directives in the scope, and not NgModules specifically. This commit introduces logic to track which NgModules contributed to a given scope, and treat them as dependencies of any components within. This logic also contains a bug, which is intentional for now. It purposefully does not track transitive dependencies of the NgModules which contribute to a scope. If it did, using the current dependency system, this would treat all components and directives (even those not exported into the scope) as dependencies, causing a major performance bottleneck. Only those dependencies which contributed to the module's export scope should be considered, but the current system is incapable of making this distinction. This will be fixed at a later date. PR Close #36211
This commit is contained in:
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {DirectiveMeta, PipeMeta} from '../../metadata';
|
||||
import {ClassDeclaration} from '../../reflection';
|
||||
|
||||
|
||||
/**
|
||||
@ -22,6 +23,11 @@ export interface ScopeData {
|
||||
* Pipes in the exported scope of the module.
|
||||
*/
|
||||
pipes: PipeMeta[];
|
||||
|
||||
/**
|
||||
* NgModules which contributed to the scope of the module.
|
||||
*/
|
||||
ngModules: ClassDeclaration[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,6 +58,7 @@ export class MetadataDtsModuleScopeResolver implements DtsModuleScopeResolver {
|
||||
// Build up the export scope - those directives and pipes made visible by this module.
|
||||
const directives: DirectiveMeta[] = [];
|
||||
const pipes: PipeMeta[] = [];
|
||||
const ngModules = new Set<ClassDeclaration>([clazz]);
|
||||
|
||||
const meta = this.dtsMetaReader.getNgModuleMetadata(ref);
|
||||
if (meta === null) {
|
||||
@ -114,6 +115,9 @@ export class MetadataDtsModuleScopeResolver implements DtsModuleScopeResolver {
|
||||
for (const pipe of exportScope.exported.pipes) {
|
||||
pipes.push(this.maybeAlias(pipe, sourceFile, /* isReExport */ true));
|
||||
}
|
||||
for (const ngModule of exportScope.exported.ngModules) {
|
||||
ngModules.add(ngModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
@ -124,7 +128,11 @@ export class MetadataDtsModuleScopeResolver implements DtsModuleScopeResolver {
|
||||
}
|
||||
|
||||
const exportScope: ExportScope = {
|
||||
exported: {directives, pipes},
|
||||
exported: {
|
||||
directives,
|
||||
pipes,
|
||||
ngModules: Array.from(ngModules),
|
||||
},
|
||||
};
|
||||
this.cache.set(clazz, exportScope);
|
||||
return exportScope;
|
||||
|
@ -278,6 +278,11 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
return null;
|
||||
}
|
||||
|
||||
// Modules which contributed to the compilation scope of this module.
|
||||
const compilationModules = new Set<ClassDeclaration>([ngModule.ref.node]);
|
||||
// Modules which contributed to the export scope of this module.
|
||||
const exportedModules = new Set<ClassDeclaration>([ngModule.ref.node]);
|
||||
|
||||
// Errors produced during computation of the scope are recorded here. At the end, if this array
|
||||
// isn't empty then `undefined` will be cached and returned to indicate this scope is invalid.
|
||||
const diagnostics: ts.Diagnostic[] = [];
|
||||
@ -329,6 +334,9 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
for (const pipe of importScope.exported.pipes) {
|
||||
compilationPipes.set(pipe.ref.node, pipe);
|
||||
}
|
||||
for (const importedModule of importScope.exported.ngModules) {
|
||||
compilationModules.add(importedModule);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) add declarations.
|
||||
@ -379,6 +387,9 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
for (const pipe of importScope.exported.pipes) {
|
||||
exportPipes.set(pipe.ref.node, pipe);
|
||||
}
|
||||
for (const exportedModule of importScope.exported.ngModules) {
|
||||
exportedModules.add(exportedModule);
|
||||
}
|
||||
} else if (compilationDirectives.has(decl.node)) {
|
||||
// decl is a directive or component in the compilation scope of this NgModule.
|
||||
const directive = compilationDirectives.get(decl.node)!;
|
||||
@ -402,6 +413,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
const exported = {
|
||||
directives: Array.from(exportDirectives.values()),
|
||||
pipes: Array.from(exportPipes.values()),
|
||||
ngModules: Array.from(exportedModules),
|
||||
};
|
||||
|
||||
const reexports = this.getReexports(ngModule, ref, declared, exported, diagnostics);
|
||||
@ -424,6 +436,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
compilation: {
|
||||
directives: Array.from(compilationDirectives.values()),
|
||||
pipes: Array.from(compilationPipes.values()),
|
||||
ngModules: Array.from(compilationModules),
|
||||
},
|
||||
exported,
|
||||
reexports,
|
||||
|
Reference in New Issue
Block a user