Skip to content

Commit 5bd188a

Browse files
crisbetodylhunn
authored andcommitted
feat(compiler-cli): add partial compilation support for deferred blocks (#54908)
Builds on top of the previous changes to add support for deferred blocks during partial compilation. To do this, the following changes had to be made: * The metadata passed into `ɵɵngDeclareComponent` has an additional field called `deferBlockDependencies` which has an array of the dependency loading functions for each defer block in the template. During linking, the dependency functions are loaded by matching their template index to the index in the `deferBlockDependencies` array. * There's a new `ɵɵngDeclareClassMetadataAsync` function that is created for components that have deferred dependencies. It gets transpiled to `setClassMetadataAsync` and works in the same way by capturing a dependency loading function and setting the metadata after the dependencies are resolved. It also has some extra fields for capturing the version which are standard in linker-generated code. * Deferred import statements are now stripped in partial compilation mode, similar to full compilation. PR Close #54908
1 parent 84410f5 commit 5bd188a

25 files changed

+448
-144
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {compileOpaqueAsyncClassMetadata, ConstantPool, R3ClassMetadata, R3DeclareClassMetadataAsync} from '@angular/compiler';
9+
10+
import {AstObject, AstValue} from '../../ast/ast_value';
11+
import {FatalLinkerError} from '../../fatal_linker_error';
12+
13+
import {LinkedDefinition, PartialLinker} from './partial_linker';
14+
15+
/**
16+
* A `PartialLinker` that is designed to process `ɵɵngDeclareClassMetadataAsync()` call expressions.
17+
*/
18+
export class PartialClassMetadataAsyncLinkerVersion1<TExpression> implements
19+
PartialLinker<TExpression> {
20+
linkPartialDeclaration(
21+
constantPool: ConstantPool,
22+
metaObj: AstObject<R3DeclareClassMetadataAsync, TExpression>): LinkedDefinition {
23+
const resolveMetadataKey = 'resolveMetadata';
24+
const resolveMetadata =
25+
metaObj.getValue(resolveMetadataKey) as unknown as AstValue<Function, TExpression>;
26+
27+
if (!resolveMetadata.isFunction()) {
28+
throw new FatalLinkerError(
29+
resolveMetadata, `Unsupported \`${resolveMetadataKey}\` value. Expected a function.`);
30+
}
31+
32+
const dependencyResolverFunction = metaObj.getOpaque('resolveDeferredDeps');
33+
const deferredSymbolNames =
34+
resolveMetadata.getFunctionParameters().map(p => p.getSymbolName()!);
35+
const returnValue = resolveMetadata.getFunctionReturnValue<R3ClassMetadata>().getObject();
36+
const metadata: R3ClassMetadata = {
37+
type: metaObj.getOpaque('type'),
38+
decorators: returnValue.getOpaque('decorators'),
39+
ctorParameters: returnValue.getOpaque('ctorParameters'),
40+
propDecorators: returnValue.getOpaque('propDecorators'),
41+
};
42+
43+
return {
44+
expression: compileOpaqueAsyncClassMetadata(
45+
metadata, dependencyResolverFunction, deferredSymbolNames),
46+
statements: [],
47+
};
48+
}
49+
}

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
178178
declarationListEmitMode,
179179
styles: metaObj.has('styles') ? metaObj.getArray('styles').map(entry => entry.getString()) :
180180
[],
181-
defer: this.createR3ComponentDeferMetadata(boundTarget),
181+
defer: this.createR3ComponentDeferMetadata(metaObj, boundTarget),
182182
encapsulation: metaObj.has('encapsulation') ?
183183
parseEncapsulation(metaObj.getValue('encapsulation')) :
184184
ViewEncapsulation.Emulated,
@@ -257,13 +257,24 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
257257
};
258258
}
259259

260-
private createR3ComponentDeferMetadata(boundTarget: BoundTarget<any>): R3ComponentDeferMetadata {
260+
private createR3ComponentDeferMetadata(
261+
metaObj: AstObject<R3DeclareComponentMetadata, TExpression>,
262+
boundTarget: BoundTarget<any>): R3ComponentDeferMetadata {
261263
const deferredBlocks = boundTarget.getDeferBlocks();
262264
const blocks = new Map<TmplAstDeferredBlock, o.Expression|null>();
263-
264-
for (const block of deferredBlocks) {
265-
// TODO: leaving `deps` empty for now, to be implemented as one of the next steps.
266-
blocks.set(block, null);
265+
const dependencies =
266+
metaObj.has('deferBlockDependencies') ? metaObj.getArray('deferBlockDependencies') : null;
267+
268+
for (let i = 0; i < deferredBlocks.length; i++) {
269+
const matchingDependencyFn = dependencies?.[i];
270+
271+
if (matchingDependencyFn == null) {
272+
blocks.set(deferredBlocks[i], null);
273+
} else {
274+
blocks.set(
275+
deferredBlocks[i],
276+
matchingDependencyFn.isNull() ? null : matchingDependencyFn.getOpaque());
277+
}
267278
}
268279

269280
return {mode: DeferBlockDepsEmitMode.PerBlock, blocks};

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {Logger} from '../../../../src/ngtsc/logging';
1212
import {createGetSourceFile} from '../get_source_file';
1313
import {LinkerEnvironment} from '../linker_environment';
1414

15+
import {PartialClassMetadataAsyncLinkerVersion1} from './partial_class_metadata_async_linker_1';
1516
import {PartialClassMetadataLinkerVersion1} from './partial_class_metadata_linker_1';
1617
import {PartialComponentLinkerVersion1} from './partial_component_linker_1';
1718
import {PartialDirectiveLinkerVersion1} from './partial_directive_linker_1';
@@ -31,9 +32,11 @@ export const ɵɵngDeclareInjectable = 'ɵɵngDeclareInjectable';
3132
export const ɵɵngDeclareInjector = 'ɵɵngDeclareInjector';
3233
export const ɵɵngDeclareNgModule = 'ɵɵngDeclareNgModule';
3334
export const ɵɵngDeclarePipe = 'ɵɵngDeclarePipe';
35+
export const ɵɵngDeclareClassMetadataAsync = 'ɵɵngDeclareClassMetadataAsync';
3436
export const declarationFunctions = [
3537
ɵɵngDeclareDirective, ɵɵngDeclareClassMetadata, ɵɵngDeclareComponent, ɵɵngDeclareFactory,
36-
ɵɵngDeclareInjectable, ɵɵngDeclareInjector, ɵɵngDeclareNgModule, ɵɵngDeclarePipe
38+
ɵɵngDeclareInjectable, ɵɵngDeclareInjector, ɵɵngDeclareNgModule, ɵɵngDeclarePipe,
39+
ɵɵngDeclareClassMetadataAsync
3740
];
3841

3942
export interface LinkerRange<TExpression> {
@@ -74,6 +77,9 @@ export function createLinkerMap<TStatement, TExpression>(
7477
linkers.set(ɵɵngDeclareDirective, [
7578
{range: LATEST_VERSION_RANGE, linker: new PartialDirectiveLinkerVersion1(sourceUrl, code)},
7679
]);
80+
linkers.set(ɵɵngDeclareClassMetadataAsync, [
81+
{range: LATEST_VERSION_RANGE, linker: new PartialClassMetadataAsyncLinkerVersion1()},
82+
]);
7783
linkers.set(ɵɵngDeclareClassMetadata, [
7884
{range: LATEST_VERSION_RANGE, linker: new PartialClassMetadataLinkerVersion1()},
7985
]);

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

Lines changed: 12 additions & 11 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, BoundTarget, compileClassDebugInfo, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeferResolverFunction, ConstantPool, CssSelector, DeclarationListEmitMode, DeclareComponentTemplateInfo, DEFAULT_INTERPOLATION_CONFIG, DeferBlockDepsEmitMode, DomElementSchemaRegistry, ExternalExpr, FactoryTarget, makeBindingParser, outputAst as o, R3ComponentDeferMetadata, R3ComponentMetadata, R3DeferPerComponentDependency, R3DirectiveDependencyMetadata, R3NgModuleDependencyMetadata, R3PipeDependencyMetadata, R3TargetBinder, R3TemplateDependency, R3TemplateDependencyKind, R3TemplateDependencyMetadata, SchemaMetadata, SelectorMatcher, TmplAstDeferredBlock, ViewEncapsulation} from '@angular/compiler';
9+
import {AnimationTriggerNames, BoundTarget, compileClassDebugInfo, compileComponentClassMetadata, compileComponentDeclareClassMetadata, compileComponentFromMetadata, compileDeclareComponentFromMetadata, compileDeferResolverFunction, ConstantPool, CssSelector, DeclarationListEmitMode, DeclareComponentTemplateInfo, DEFAULT_INTERPOLATION_CONFIG, DeferBlockDepsEmitMode, DomElementSchemaRegistry, ExternalExpr, FactoryTarget, makeBindingParser, outputAst as o, R3ComponentDeferMetadata, R3ComponentMetadata, R3DeferPerComponentDependency, R3DirectiveDependencyMetadata, R3NgModuleDependencyMetadata, R3PipeDependencyMetadata, R3TargetBinder, R3TemplateDependency, R3TemplateDependencyKind, R3TemplateDependencyMetadata, SchemaMetadata, SelectorMatcher, TmplAstDeferredBlock, ViewEncapsulation} from '@angular/compiler';
1010
import ts from 'typescript';
1111

1212
import {Cycle, CycleAnalyzer, CycleHandlingStrategy} from '../../../cycles';
@@ -1143,21 +1143,20 @@ export class ComponentDecoratorHandler implements
11431143
return [];
11441144
}
11451145

1146-
const deferrableTypes = this.collectDeferredSymbols(resolution);
1147-
1146+
const perComponentDeferredDeps = this.resolveAllDeferredDependencies(resolution);
11481147
const meta: R3ComponentMetadata<R3TemplateDependency> = {
11491148
...analysis.meta,
11501149
...resolution,
11511150
defer: this.compileDeferBlocks(resolution),
11521151
};
11531152
const fac = compileNgFactoryDefField(toFactoryMetadata(meta, FactoryTarget.Component));
11541153

1155-
removeDeferrableTypesFromComponentDecorator(analysis, deferrableTypes);
1154+
removeDeferrableTypesFromComponentDecorator(analysis, perComponentDeferredDeps);
11561155

11571156
const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
11581157
const inputTransformFields = compileInputTransformFields(analysis.inputs);
11591158
const classMetadata = analysis.classMetadata !== null ?
1160-
compileComponentClassMetadata(analysis.classMetadata, deferrableTypes).toStmt() :
1159+
compileComponentClassMetadata(analysis.classMetadata, perComponentDeferredDeps).toStmt() :
11611160
null;
11621161
const debugInfo = analysis.classDebugInfo !== null ?
11631162
compileClassDebugInfo(analysis.classDebugInfo).toStmt() :
@@ -1182,6 +1181,7 @@ export class ComponentDecoratorHandler implements
11821181
null,
11831182
};
11841183

1184+
const perComponentDeferredDeps = this.resolveAllDeferredDependencies(resolution);
11851185
const meta: R3ComponentMetadata<R3TemplateDependencyMetadata> = {
11861186
...analysis.meta,
11871187
...resolution,
@@ -1191,11 +1191,11 @@ export class ComponentDecoratorHandler implements
11911191
const inputTransformFields = compileInputTransformFields(analysis.inputs);
11921192
const def = compileDeclareComponentFromMetadata(meta, analysis.template, templateInfo);
11931193
const classMetadata = analysis.classMetadata !== null ?
1194-
compileDeclareClassMetadata(analysis.classMetadata).toStmt() :
1194+
compileComponentDeclareClassMetadata(analysis.classMetadata, perComponentDeferredDeps)
1195+
.toStmt() :
11951196
null;
1196-
1197-
return compileResults(
1198-
fac, def, classMetadata, 'ɵcmp', inputTransformFields, null /* deferrableImports */);
1197+
const deferrableImports = this.deferredSymbolTracker.getDeferrableImportDecls();
1198+
return compileResults(fac, def, classMetadata, 'ɵcmp', inputTransformFields, deferrableImports);
11991199
}
12001200

12011201
compileLocal(
@@ -1256,7 +1256,8 @@ export class ComponentDecoratorHandler implements
12561256
* Computes a list of deferrable symbols based on dependencies from
12571257
* the `@Component.imports` field and their usage in `@defer` blocks.
12581258
*/
1259-
private collectDeferredSymbols(resolution: Readonly<ComponentResolutionData>) {
1259+
private resolveAllDeferredDependencies(resolution: Readonly<ComponentResolutionData>):
1260+
R3DeferPerComponentDependency[] {
12601261
const deferrableTypes: R3DeferPerComponentDependency[] = [];
12611262
// Go over all dependencies of all defer blocks and update the value of
12621263
// the `isDeferrable` flag and the `importPath` to reflect the current
@@ -1505,7 +1506,7 @@ export class ComponentDecoratorHandler implements
15051506
'Internal error: deferPerComponentDependencies must be present in PerComponent mode');
15061507
}
15071508
return {
1508-
mode: DeferBlockDepsEmitMode.PerComponent,
1509+
mode,
15091510
dependenciesFn: perComponentDeps.length === 0 ?
15101511
null :
15111512
compileDeferResolverFunction({mode, dependencies: perComponentDeps})

0 commit comments

Comments
 (0)