mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(compiler-cli): prevent recursive scope checks for invalid NgModule imports
Avoid recursive local scope lookups when invalid NgModule imports create import cycles.
This commit is contained in:
parent
12b3a1a755
commit
fcd0bb0db8
2 changed files with 46 additions and 13 deletions
|
|
@ -151,10 +151,12 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
|||
registerPipeMetadata(pipe: PipeMeta): void {}
|
||||
|
||||
getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope | null {
|
||||
const scope = !this.declarationToModule.has(clazz)
|
||||
? null
|
||||
: this.getScopeOfModule(this.declarationToModule.get(clazz)!.ngModule);
|
||||
return scope;
|
||||
if (!this.declarationToModule.has(clazz)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const module = this.declarationToModule.get(clazz)!.ngModule;
|
||||
return this.getScopeOfModule(module);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -181,9 +183,12 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
|||
* defined, or the string `'error'` if the scope contained errors.
|
||||
*/
|
||||
getScopeOfModule(clazz: ClassDeclaration): LocalModuleScope | null {
|
||||
return this.moduleToRef.has(clazz)
|
||||
? this.getScopeOfModuleReference(this.moduleToRef.get(clazz)!)
|
||||
: null;
|
||||
if (!this.moduleToRef.has(clazz)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scope = this.getScopeOfModuleReference(this.moduleToRef.get(clazz)!);
|
||||
return scope === 'cycle' ? null : scope;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -249,13 +254,17 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
|||
/**
|
||||
* Implementation of `getScopeOfModule` which accepts a reference to a class.
|
||||
*/
|
||||
private getScopeOfModuleReference(ref: Reference<ClassDeclaration>): LocalModuleScope | null {
|
||||
private getScopeOfModuleReference(
|
||||
ref: Reference<ClassDeclaration>,
|
||||
): LocalModuleScope | null | 'cycle' {
|
||||
if (this.cache.has(ref.node)) {
|
||||
const cachedValue = this.cache.get(ref.node);
|
||||
|
||||
if (cachedValue !== IN_PROGRESS_RESOLUTION) {
|
||||
return cachedValue as LocalModuleScope | null;
|
||||
if (cachedValue === IN_PROGRESS_RESOLUTION) {
|
||||
return 'cycle';
|
||||
}
|
||||
|
||||
return cachedValue as LocalModuleScope | null;
|
||||
}
|
||||
|
||||
this.cache.set(ref.node, IN_PROGRESS_RESOLUTION);
|
||||
|
|
@ -593,7 +602,8 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
|||
}
|
||||
return this.dependencyScopeReader.resolve(ref);
|
||||
} else {
|
||||
if (this.cache.get(ref.node) === IN_PROGRESS_RESOLUTION) {
|
||||
const scope = this.getScopeOfModuleReference(ref);
|
||||
if (scope === 'cycle') {
|
||||
diagnostics.push(
|
||||
makeDiagnostic(
|
||||
type === 'import'
|
||||
|
|
@ -603,11 +613,10 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
|||
`NgModule "${type}" field contains a cycle`,
|
||||
),
|
||||
);
|
||||
return 'cycle';
|
||||
}
|
||||
|
||||
// The NgModule is declared locally in the current program. Resolve it from the registry.
|
||||
return this.getScopeOfModuleReference(ref);
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11492,6 +11492,30 @@ runInEachFileSystem((os: string) => {
|
|||
`The pipe 'TestPipe' appears in 'imports', but is not standalone`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not recurse when a non-standalone component is both declared and imported', () => {
|
||||
env.write(
|
||||
'/test.ts',
|
||||
`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({standalone: false, template: ''})
|
||||
export class TestComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [TestComp],
|
||||
imports: [TestComp],
|
||||
})
|
||||
export class TestModule {}
|
||||
`,
|
||||
);
|
||||
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].messageText).toContain(
|
||||
`The component 'TestComp' appears in 'imports', but is not standalone`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue