Skip to content

Commit eee620a

Browse files
crisbetoatscott
authored andcommitted
refactor(compiler): rework defer block analysis (#54700)
Currently we have the `deferrableDeclToImportDecl`, `deferBlocks`, `deferrableTypes` and `deferBlockDepsEmitMode` fields on the `R3ComponentMetadata` which is incorrect, because the interface is used both for JIT and AOT mode even though the information for those fields is AOT-specific. It will be problematic for partial compilation since the runtime will have a reference to the dependency loading function, but will not be able to provide any of the other information. These changes make the following refactors: 1. It changes the defer-related information in `R3ComponentMetadata` to include only references to dependency functions which can be provided both in JIT and AOT. 2. Moves the AOT-specific defer analysis into the `ComponentResolutionData`. 3. Moves the construction the defer dependency function into the compilation phase of the `ComponentDecoratorHandler`. 4. Drops support for defer blocks from the `TemplateDefinitionBuilder`. This allows us to clean up some TDB-specific code and shouldn't have an effect on users since the TDB isn't used anymore. PR Close #54700
1 parent d888da4 commit eee620a

File tree

13 files changed

+270
-496
lines changed

13 files changed

+270
-496
lines changed

packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {BoundTarget, ChangeDetectionStrategy, compileComponentFromMetadata, ConstantPool, DeclarationListEmitMode, DEFAULT_INTERPOLATION_CONFIG, DeferBlockDepsEmitMode, ForwardRefHandling, InterpolationConfig, makeBindingParser, outputAst as o, parseTemplate, R3ComponentMetadata, R3DeclareComponentMetadata, R3DeclareDirectiveDependencyMetadata, R3DeclarePipeDependencyMetadata, R3DeferBlockMetadata, R3DirectiveDependencyMetadata, R3PartialDeclaration, R3TargetBinder, R3TemplateDependencyKind, R3TemplateDependencyMetadata, SelectorMatcher, TmplAstDeferredBlock, TmplAstDeferredBlockTriggers, TmplAstDeferredTrigger, TmplAstElement, ViewEncapsulation} from '@angular/compiler';
8+
import {BoundTarget, ChangeDetectionStrategy, compileComponentFromMetadata, ConstantPool, DeclarationListEmitMode, DEFAULT_INTERPOLATION_CONFIG, DeferBlockDepsEmitMode, ForwardRefHandling, InterpolationConfig, makeBindingParser, outputAst as o, parseTemplate, R3ComponentMetadata, R3DeclareComponentMetadata, R3DeclareDirectiveDependencyMetadata, R3DeclarePipeDependencyMetadata, R3DeferMetadata, R3DirectiveDependencyMetadata, R3PartialDeclaration, R3TargetBinder, R3TemplateDependencyKind, R3TemplateDependencyMetadata, SelectorMatcher, TmplAstDeferredBlock, ViewEncapsulation} from '@angular/compiler';
99
import semver from 'semver';
1010

1111
import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system';
@@ -178,13 +178,7 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
178178
declarationListEmitMode,
179179
styles: metaObj.has('styles') ? metaObj.getArray('styles').map(entry => entry.getString()) :
180180
[],
181-
deferBlocks: this.createR3DeferredMetadata(boundTarget),
182-
183-
// Defer blocks are not yet fully supported in partial compilation.
184-
deferrableDeclToImportDecl: new Map(),
185-
deferrableTypes: new Map(),
186-
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerBlock,
187-
181+
defer: this.createR3DeferMetadata(boundTarget),
188182
encapsulation: metaObj.has('encapsulation') ?
189183
parseEncapsulation(metaObj.getValue('encapsulation')) :
190184
ViewEncapsulation.Emulated,
@@ -264,32 +258,16 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
264258
};
265259
}
266260

267-
private createR3DeferredMetadata(boundTarget: BoundTarget<any>):
268-
Map<TmplAstDeferredBlock, R3DeferBlockMetadata> {
261+
private createR3DeferMetadata(boundTarget: BoundTarget<any>): R3DeferMetadata {
269262
const deferredBlocks = boundTarget.getDeferBlocks();
270-
const meta = new Map<TmplAstDeferredBlock, R3DeferBlockMetadata>();
263+
const blocks = new Map<TmplAstDeferredBlock, o.ArrowFunctionExpr|null>();
271264

272265
for (const block of deferredBlocks) {
273-
const triggerElements = new Map<TmplAstDeferredTrigger, TmplAstElement>();
274-
275-
this.resolveDeferTriggers(block, block.triggers, boundTarget, triggerElements);
276-
this.resolveDeferTriggers(block, block.prefetchTriggers, boundTarget, triggerElements);
277-
278266
// TODO: leaving `deps` empty for now, to be implemented as one of the next steps.
279-
meta.set(block, {deps: [], triggerElements});
267+
blocks.set(block, null);
280268
}
281269

282-
return meta;
283-
}
284-
285-
private resolveDeferTriggers(
286-
block: TmplAstDeferredBlock, triggers: TmplAstDeferredBlockTriggers,
287-
boundTarget: BoundTarget<any>,
288-
triggerElements: Map<TmplAstDeferredTrigger, TmplAstElement|null>): void {
289-
Object.keys(triggers).forEach(key => {
290-
const trigger = triggers[key as keyof TmplAstDeferredBlockTriggers]!;
291-
triggerElements.set(trigger, boundTarget.getDeferredTriggerTarget(block, trigger));
292-
});
270+
return {mode: DeferBlockDepsEmitMode.PerBlock, blocks};
293271
}
294272
}
295273

packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts

Lines changed: 107 additions & 64 deletions
Large diffs are not rendered by default.

packages/compiler-cli/src/ngtsc/annotations/component/src/metadata.ts

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {AnimationTriggerNames, R3ClassDebugInfo, R3ClassMetadata, R3ComponentMetadata, R3TemplateDependencyMetadata, SchemaMetadata} from '@angular/compiler';
9+
import {AnimationTriggerNames, DeclarationListEmitMode, DeferBlockDepsEmitMode, R3ClassDebugInfo, R3ClassMetadata, R3ComponentMetadata, R3TemplateDependencyMetadata, SchemaMetadata, TmplAstDeferredBlock} from '@angular/compiler';
1010
import ts from 'typescript';
1111

1212
import {Reference} from '../../../imports';
@@ -24,8 +24,7 @@ import {ParsedTemplateWithSource, StyleUrlMeta} from './resources';
2424
*/
2525
export type ComponentMetadataResolvedFields = SubsetOfKeys<
2626
R3ComponentMetadata<R3TemplateDependencyMetadata>,
27-
'declarations'|'declarationListEmitMode'|'deferBlocks'|'deferrableDeclToImportDecl'|
28-
'deferrableTypes'|'deferBlockDepsEmitMode'>;
27+
'declarations'|'declarationListEmitMode'|'defer'>;
2928

3029
export interface ComponentAnalysisData {
3130
/**
@@ -90,5 +89,61 @@ export interface ComponentAnalysisData {
9089
rawHostDirectives: ts.Expression|null;
9190
}
9291

93-
export type ComponentResolutionData =
94-
Pick<R3ComponentMetadata<R3TemplateDependencyMetadata>, ComponentMetadataResolvedFields>;
92+
export interface ComponentResolutionData {
93+
declarations: R3TemplateDependencyMetadata[];
94+
declarationListEmitMode: DeclarationListEmitMode;
95+
96+
/**
97+
* Map of all types that can be defer loaded (ts.ClassDeclaration) ->
98+
* corresponding import declaration (ts.ImportDeclaration) within
99+
* the current source file.
100+
*/
101+
deferrableDeclToImportDecl: Map<ClassDeclaration, ts.ImportDeclaration>;
102+
103+
/**
104+
* Map of `@defer` blocks -> their corresponding metadata.
105+
*/
106+
deferBlockDependencies: Map<TmplAstDeferredBlock, DeferredComponentDependency[]>;
107+
108+
/**
109+
* Defines how dynamic imports for deferred dependencies should be grouped:
110+
* - either in a function on per-component basis (in case of local compilation)
111+
* - or in a function on per-block basis (in full compilation mode)
112+
*/
113+
deferBlockDepsEmitMode: DeferBlockDepsEmitMode;
114+
115+
/**
116+
* Map of deferrable symbol names -> corresponding import paths.
117+
*/
118+
deferrableTypes: Map<string, {importPath: string, isDefaultImport: boolean}>;
119+
}
120+
121+
/**
122+
* Describes a dependency used within a `@defer` block.
123+
*/
124+
export interface DeferredComponentDependency {
125+
/**
126+
* Reference to a dependency.
127+
*/
128+
type: Reference<ClassDeclaration>;
129+
130+
/**
131+
* Dependency class name.
132+
*/
133+
symbolName: string;
134+
135+
/**
136+
* Whether this dependency can be defer-loaded.
137+
*/
138+
isDeferrable: boolean;
139+
140+
/**
141+
* Import path where this dependency is located.
142+
*/
143+
importPath: string|null;
144+
145+
/**
146+
* Whether the symbol is the default export.
147+
*/
148+
isDefaultImport: boolean;
149+
}

packages/compiler/src/jit_compiler_facade.ts

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ import {ConstantPool} from './constant_pool';
1111
import {ChangeDetectionStrategy, HostBinding, HostListener, Input, Output, ViewEncapsulation} from './core';
1212
import {compileInjectable} from './injectable_compiler_2';
1313
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/defaults';
14-
import {DeclareVarStmt, Expression, literal, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast';
14+
import {ArrowFunctionExpr, DeclareVarStmt, Expression, literal, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast';
1515
import {JitEvaluator} from './output/output_jit';
1616
import {ParseError, ParseSourceSpan, r3JitTypeSourceSpan} from './parse_util';
17-
import {DeferredBlock, DeferredBlockTriggers, DeferredTrigger, Element} from './render3/r3_ast';
17+
import {DeferredBlock} from './render3/r3_ast';
1818
import {compileFactoryFunction, FactoryTarget, R3DependencyMetadata} from './render3/r3_factory';
1919
import {compileInjector, R3InjectorMetadata} from './render3/r3_injector_compiler';
2020
import {R3JitReflector} from './render3/r3_jit';
2121
import {compileNgModule, compileNgModuleDeclarationExpression, R3NgModuleMetadata, R3NgModuleMetadataKind, R3SelectorScopeMode} from './render3/r3_module_compiler';
2222
import {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
2323
import {createMayBeForwardRefExpression, ForwardRefHandling, getSafePropertyAccessString, MaybeForwardRefExpression, wrapReference} from './render3/util';
24-
import {DeclarationListEmitMode, DeferBlockDepsEmitMode, R3ComponentMetadata, R3DeferBlockMetadata, R3DirectiveDependencyMetadata, R3DirectiveMetadata, R3HostDirectiveMetadata, R3HostMetadata, R3InputMetadata, R3PipeDependencyMetadata, R3QueryMetadata, R3TemplateDependency, R3TemplateDependencyKind, R3TemplateDependencyMetadata} from './render3/view/api';
24+
import {DeclarationListEmitMode, DeferBlockDepsEmitMode, R3ComponentMetadata, R3DeferMetadata, R3DirectiveDependencyMetadata, R3DirectiveMetadata, R3HostDirectiveMetadata, R3HostMetadata, R3InputMetadata, R3PipeDependencyMetadata, R3QueryMetadata, R3TemplateDependency, R3TemplateDependencyKind, R3TemplateDependencyMetadata} from './render3/view/api';
2525
import {compileComponentFromMetadata, compileDirectiveFromMetadata, ParsedHostBindings, parseHostBindings, verifyHostBindings} from './render3/view/compiler';
2626
import type {BoundTarget} from './render3/view/t2_api';
2727
import {R3TargetBinder} from './render3/view/t2_binder';
@@ -182,7 +182,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
182182
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
183183
facade: R3ComponentMetadataFacade): any {
184184
// Parse the template and check for errors.
185-
const {template, interpolation, deferBlocks} = parseJitTemplate(
185+
const {template, interpolation, defer} = parseJitTemplate(
186186
facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces,
187187
facade.interpolation);
188188

@@ -194,10 +194,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
194194
template,
195195
declarations: facade.declarations.map(convertDeclarationFacadeToMetadata),
196196
declarationListEmitMode: DeclarationListEmitMode.Direct,
197-
deferBlocks,
198-
deferrableTypes: new Map(),
199-
deferrableDeclToImportDecl: new Map(),
200-
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerBlock,
197+
defer,
201198

202199
styles: [...facade.styles, ...template.styles],
203200
encapsulation: facade.encapsulation,
@@ -447,7 +444,7 @@ function convertOpaqueValuesToExpressions(obj: {[key: string]: OpaqueValue}):
447444
function convertDeclareComponentFacadeToMetadata(
448445
decl: R3DeclareComponentFacade, typeSourceSpan: ParseSourceSpan,
449446
sourceMapUrl: string): R3ComponentMetadata<R3TemplateDependencyMetadata> {
450-
const {template, interpolation, deferBlocks} = parseJitTemplate(
447+
const {template, interpolation, defer} = parseJitTemplate(
451448
decl.template, decl.type.name, sourceMapUrl, decl.preserveWhitespaces ?? false,
452449
decl.interpolation);
453450

@@ -484,10 +481,7 @@ function convertDeclareComponentFacadeToMetadata(
484481
viewProviders: decl.viewProviders !== undefined ? new WrappedNodeExpr(decl.viewProviders) :
485482
null,
486483
animations: decl.animations !== undefined ? new WrappedNodeExpr(decl.animations) : null,
487-
deferBlocks,
488-
deferrableTypes: new Map(),
489-
deferrableDeclToImportDecl: new Map(),
490-
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerBlock,
484+
defer,
491485

492486
changeDetection: decl.changeDetection ?? ChangeDetectionStrategy.Default,
493487
encapsulation: decl.encapsulation ?? ViewEncapsulation.Emulated,
@@ -562,7 +556,7 @@ function parseJitTemplate(
562556
return {
563557
template: parsed,
564558
interpolation: interpolationConfig,
565-
deferBlocks: createR3DeferredMetadata(boundTarget)
559+
defer: createR3DeferMetadata(boundTarget)
566560
};
567561
}
568562

@@ -633,31 +627,17 @@ function createR3DependencyMetadata(
633627
return {token, attributeNameType, host, optional, self, skipSelf};
634628
}
635629

636-
function createR3DeferredMetadata(boundTarget: BoundTarget<any>):
637-
Map<DeferredBlock, R3DeferBlockMetadata> {
630+
function createR3DeferMetadata(boundTarget: BoundTarget<any>): R3DeferMetadata {
638631
const deferredBlocks = boundTarget.getDeferBlocks();
639-
const meta = new Map<DeferredBlock, R3DeferBlockMetadata>();
632+
const blocks = new Map<DeferredBlock, ArrowFunctionExpr|null>();
640633

641634
for (const block of deferredBlocks) {
642-
const triggerElements = new Map<DeferredTrigger, Element>();
643-
644-
resolveDeferTriggers(block, block.triggers, boundTarget, triggerElements);
645-
resolveDeferTriggers(block, block.prefetchTriggers, boundTarget, triggerElements);
646-
647-
// TODO: leaving `deps` empty in JIT mode for now, to be implemented as one of the next steps.
648-
meta.set(block, {deps: [], triggerElements});
635+
// TODO: leaving dependency function empty in JIT mode for now,
636+
// to be implemented as one of the next steps.
637+
blocks.set(block, null);
649638
}
650639

651-
return meta;
652-
}
653-
654-
function resolveDeferTriggers(
655-
block: DeferredBlock, triggers: DeferredBlockTriggers, boundTarget: BoundTarget<any>,
656-
triggerElements: Map<DeferredTrigger, Element|null>): void {
657-
Object.keys(triggers).forEach(key => {
658-
const trigger = triggers[key as keyof DeferredBlockTriggers]!;
659-
triggerElements.set(trigger, boundTarget.getDeferredTriggerTarget(block, trigger));
660-
});
640+
return {mode: DeferBlockDepsEmitMode.PerBlock, blocks};
661641
}
662642

663643
function extractHostBindings(

packages/compiler/src/render3/view/api.ts

Lines changed: 13 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -196,47 +196,6 @@ export const enum DeclarationListEmitMode {
196196
RuntimeResolved,
197197
}
198198

199-
/**
200-
* Describes a dependency used within a `@defer` block.
201-
*/
202-
export interface R3DeferBlockTemplateDependency {
203-
/**
204-
* Reference to a dependency.
205-
*/
206-
type: o.WrappedNodeExpr<unknown>;
207-
208-
/**
209-
* Dependency class name.
210-
*/
211-
symbolName: string;
212-
213-
/**
214-
* Whether this dependency can be defer-loaded.
215-
*/
216-
isDeferrable: boolean;
217-
218-
/**
219-
* Import path where this dependency is located.
220-
*/
221-
importPath: string|null;
222-
223-
/**
224-
* Whether the symbol is the default export.
225-
*/
226-
isDefaultImport: boolean;
227-
}
228-
229-
/**
230-
* Information necessary to compile a `defer` block.
231-
*/
232-
export interface R3DeferBlockMetadata {
233-
/** Dependencies used within the block. */
234-
deps: R3DeferBlockTemplateDependency[];
235-
236-
/** Mapping between triggers and the DOM nodes they refer to. */
237-
triggerElements: Map<t.DeferredTrigger, t.Element|null>;
238-
}
239-
240199
/**
241200
* Information needed to compile a component for the render3 runtime.
242201
*/
@@ -265,29 +224,8 @@ export interface R3ComponentMetadata<DeclarationT extends R3TemplateDependency>
265224

266225
declarations: DeclarationT[];
267226

268-
/**
269-
* Map of all types that can be defer loaded (ts.ClassDeclaration) ->
270-
* corresponding import declaration (ts.ImportDeclaration) within
271-
* the current source file.
272-
*/
273-
deferrableDeclToImportDecl: Map<o.Expression, o.Expression>;
274-
275-
/**
276-
* Map of `@defer` blocks -> their corresponding metadata.
277-
*/
278-
deferBlocks: Map<t.DeferredBlock, R3DeferBlockMetadata>;
279-
280-
/**
281-
* Defines how dynamic imports for deferred dependencies should be grouped:
282-
* - either in a function on per-component basis (in case of local compilation)
283-
* - or in a function on per-block basis (in full compilation mode)
284-
*/
285-
deferBlockDepsEmitMode: DeferBlockDepsEmitMode;
286-
287-
/**
288-
* Map of deferrable symbol names -> corresponding import paths.
289-
*/
290-
deferrableTypes: Map<string, {importPath: string, isDefaultImport: boolean}>;
227+
/** Metadata related to the deferred blocks in the component's template. */
228+
defer: R3DeferMetadata;
291229

292230
/**
293231
* Specifies how the 'directives' and/or `pipes` array, if generated, need to be emitted.
@@ -356,6 +294,17 @@ export interface R3ComponentMetadata<DeclarationT extends R3TemplateDependency>
356294
useTemplatePipeline: boolean;
357295
}
358296

297+
/**
298+
* Information about the deferred blocks in a component's template.
299+
*/
300+
export type R3DeferMetadata = {
301+
mode: DeferBlockDepsEmitMode.PerBlock,
302+
blocks: Map<t.DeferredBlock, o.ArrowFunctionExpr|null>,
303+
}|{
304+
mode: DeferBlockDepsEmitMode.PerComponent,
305+
dependenciesFn: o.ArrowFunctionExpr | null,
306+
};
307+
359308
/**
360309
* Metadata for an individual input on a directive.
361310
*/

0 commit comments

Comments
 (0)