fix(ivy): reuse compilation scope for incremental template changes. (#31932)
Previously if only a component template changed then we would know to rebuild its component source file. But the compilation was incorrect if the component was part of an NgModule, since we were not capturing the compilation scope information that had a been acquired from the NgModule and was not being regenerated since we were not needing to recompile the NgModule. Now we register compilation scope information for each component, via the `ComponentScopeRegistry` interface, so that it is available for incremental compilation. The `ComponentDecoratorHandler` now reads the compilation scope from a `ComponentScopeReader` interface which is implemented as a compound reader composed of the original `LocalModuleScopeRegistry` and the `IncrementalState`. Fixes #31654 PR Close #31932
This commit is contained in:

committed by
Kara Erickson

parent
7533338362
commit
eb5412d76f
@ -7,5 +7,6 @@
|
||||
*/
|
||||
|
||||
export {ExportScope, ScopeData} from './src/api';
|
||||
export {ComponentScopeReader, ComponentScopeRegistry, CompoundComponentScopeReader} from './src/component_scope';
|
||||
export {DtsModuleScopeResolver, MetadataDtsModuleScopeResolver} from './src/dependency';
|
||||
export {LocalModuleScope, LocalModuleScopeRegistry, LocalNgModuleData} from './src/local';
|
||||
|
67
packages/compiler-cli/src/ngtsc/scope/src/component_scope.ts
Normal file
67
packages/compiler-cli/src/ngtsc/scope/src/component_scope.ts
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {ClassDeclaration} from '../../reflection';
|
||||
import {LocalModuleScope} from './local';
|
||||
|
||||
/**
|
||||
* Register information about the compilation scope of components.
|
||||
*/
|
||||
export interface ComponentScopeRegistry {
|
||||
registerComponentScope(clazz: ClassDeclaration, scope: LocalModuleScope): void;
|
||||
setComponentAsRequiringRemoteScoping(clazz: ClassDeclaration): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read information about the compilation scope of components.
|
||||
*/
|
||||
export interface ComponentScopeReader {
|
||||
getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null;
|
||||
getRequiresRemoteScope(clazz: ClassDeclaration): boolean|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A noop registry that doesn't do anything.
|
||||
*
|
||||
* This can be used in tests and cases where we don't care about the compilation scopes
|
||||
* being registered.
|
||||
*/
|
||||
export class NoopComponentScopeRegistry implements ComponentScopeRegistry {
|
||||
registerComponentScope(clazz: ClassDeclaration, scope: LocalModuleScope): void {}
|
||||
setComponentAsRequiringRemoteScoping(clazz: ClassDeclaration): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ComponentScopeReader` that reads from an ordered set of child readers until it obtains the
|
||||
* requested scope.
|
||||
*
|
||||
* This is used to combine `ComponentScopeReader`s that read from different sources (e.g. from a
|
||||
* registry and from the incremental state).
|
||||
*/
|
||||
export class CompoundComponentScopeReader implements ComponentScopeReader {
|
||||
constructor(private readers: ComponentScopeReader[]) {}
|
||||
|
||||
getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null {
|
||||
for (const reader of this.readers) {
|
||||
const meta = reader.getScopeForComponent(clazz);
|
||||
if (meta !== null) {
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getRequiresRemoteScope(clazz: ClassDeclaration): boolean|null {
|
||||
for (const reader of this.readers) {
|
||||
const requiredScoping = reader.getRequiresRemoteScope(clazz);
|
||||
if (requiredScoping !== null) {
|
||||
return requiredScoping;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -11,11 +11,12 @@ import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode, makeDiagnostic} from '../../diagnostics';
|
||||
import {AliasGenerator, Reexport, Reference, ReferenceEmitter} from '../../imports';
|
||||
import {DirectiveMeta, LocalMetadataRegistry, MetadataReader, MetadataRegistry, NgModuleMeta, PipeMeta} from '../../metadata';
|
||||
import {DirectiveMeta, MetadataReader, MetadataRegistry, NgModuleMeta, PipeMeta} from '../../metadata';
|
||||
import {ClassDeclaration} from '../../reflection';
|
||||
import {identifierOfNode, nodeNameForError} from '../../util/src/typescript';
|
||||
|
||||
import {ExportScope, ScopeData} from './api';
|
||||
import {ComponentScopeReader, ComponentScopeRegistry, NoopComponentScopeRegistry} from './component_scope';
|
||||
import {DtsModuleScopeResolver} from './dependency';
|
||||
|
||||
export interface LocalNgModuleData {
|
||||
@ -58,7 +59,7 @@ export interface CompilationScope extends ScopeData {
|
||||
* The `LocalModuleScopeRegistry` is also capable of producing `ts.Diagnostic` errors when Angular
|
||||
* semantics are violated.
|
||||
*/
|
||||
export class LocalModuleScopeRegistry implements MetadataRegistry {
|
||||
export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScopeReader {
|
||||
/**
|
||||
* Tracks whether the registry has been asked to produce scopes for a module or component. Once
|
||||
* this is true, the registry cannot accept registrations of new directives/pipes/modules as it
|
||||
@ -102,7 +103,8 @@ export class LocalModuleScopeRegistry implements MetadataRegistry {
|
||||
|
||||
constructor(
|
||||
private localReader: MetadataReader, private dependencyScopeReader: DtsModuleScopeResolver,
|
||||
private refEmitter: ReferenceEmitter, private aliasGenerator: AliasGenerator|null) {}
|
||||
private refEmitter: ReferenceEmitter, private aliasGenerator: AliasGenerator|null,
|
||||
private componentScopeRegistry: ComponentScopeRegistry = new NoopComponentScopeRegistry()) {}
|
||||
|
||||
/**
|
||||
* Add an NgModule's data to the registry.
|
||||
@ -120,10 +122,13 @@ export class LocalModuleScopeRegistry implements MetadataRegistry {
|
||||
registerPipeMetadata(pipe: PipeMeta): void {}
|
||||
|
||||
getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null {
|
||||
if (!this.declarationToModule.has(clazz)) {
|
||||
return null;
|
||||
const scope = !this.declarationToModule.has(clazz) ?
|
||||
null :
|
||||
this.getScopeOfModule(this.declarationToModule.get(clazz) !);
|
||||
if (scope !== null) {
|
||||
this.componentScopeRegistry.registerComponentScope(clazz, scope);
|
||||
}
|
||||
return this.getScopeOfModule(this.declarationToModule.get(clazz) !);
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -383,6 +388,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry {
|
||||
*/
|
||||
setComponentAsRequiringRemoteScoping(node: ClassDeclaration): void {
|
||||
this.remoteScoping.add(node);
|
||||
this.componentScopeRegistry.setComponentAsRequiringRemoteScoping(node);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user