Skip to content

Commit 6ea208e

Browse files
crisbetoatscott
authored andcommitted
refactor(compiler-cli): move defer resolver compilation into compiler package (#54759)
Moves the logic that creates the defer resolver function into `@angular/compiler` for consistency with the rest of the compilation APIs. Also renames some of the symbols to make it clearer what they're used for. PR Close #54759
1 parent 9b424d7 commit 6ea208e

File tree

10 files changed

+172
-121
lines changed

10 files changed

+172
-121
lines changed

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

Lines changed: 3 additions & 3 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, R3DeferMetadata, R3DirectiveDependencyMetadata, R3PartialDeclaration, R3TargetBinder, R3TemplateDependencyKind, R3TemplateDependencyMetadata, SelectorMatcher, TmplAstDeferredBlock, ViewEncapsulation} from '@angular/compiler';
8+
import {BoundTarget, ChangeDetectionStrategy, compileComponentFromMetadata, ConstantPool, DeclarationListEmitMode, DEFAULT_INTERPOLATION_CONFIG, DeferBlockDepsEmitMode, ForwardRefHandling, InterpolationConfig, makeBindingParser, outputAst as o, parseTemplate, R3ComponentDeferMetadata, R3ComponentMetadata, R3DeclareComponentMetadata, R3DeclareDirectiveDependencyMetadata, R3DeclarePipeDependencyMetadata, 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,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.createR3DeferMetadata(boundTarget),
181+
defer: this.createR3ComponentDeferMetadata(boundTarget),
182182
encapsulation: metaObj.has('encapsulation') ?
183183
parseEncapsulation(metaObj.getValue('encapsulation')) :
184184
ViewEncapsulation.Emulated,
@@ -257,7 +257,7 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
257257
};
258258
}
259259

260-
private createR3DeferMetadata(boundTarget: BoundTarget<any>): R3DeferMetadata {
260+
private createR3ComponentDeferMetadata(boundTarget: BoundTarget<any>): R3ComponentDeferMetadata {
261261
const deferredBlocks = boundTarget.getDeferBlocks();
262262
const blocks = new Map<TmplAstDeferredBlock, o.ArrowFunctionExpr|null>();
263263

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

Lines changed: 37 additions & 70 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, ConstantPool, CssSelector, DeclarationListEmitMode, DeclareComponentTemplateInfo, DEFAULT_INTERPOLATION_CONFIG, DeferBlockDepsEmitMode, DomElementSchemaRegistry, ExternalExpr, FactoryTarget, makeBindingParser, outputAst as o, R3ComponentMetadata, R3DeferMetadata, R3DirectiveDependencyMetadata, R3NgModuleDependencyMetadata, R3PipeDependencyMetadata, R3TargetBinder, R3TemplateDependency, R3TemplateDependencyKind, R3TemplateDependencyMetadata, SchemaMetadata, SelectorMatcher, TmplAstDeferredBlock, ViewEncapsulation} from '@angular/compiler';
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';
1010
import ts from 'typescript';
1111

1212
import {Cycle, CycleAnalyzer, CycleHandlingStrategy} from '../../../cycles';
@@ -484,13 +484,13 @@ export class ComponentDecoratorHandler implements
484484
// (if it exists) and populate the `DeferredSymbolTracker` state. These operations are safe
485485
// for the local compilation mode, since they don't require accessing/resolving symbols
486486
// outside of the current source file.
487-
let explicitlyDeferredTypes: Map<string, {importPath: string, isDefaultImport: boolean}>|null =
488-
null;
487+
let explicitlyDeferredTypes: R3DeferPerComponentDependency[]|null = null;
489488
if (metadata.isStandalone && rawDeferredImports !== null) {
490489
const deferredTypes = this.collectExplicitlyDeferredSymbols(rawDeferredImports);
491490
for (const [deferredType, importDetails] of deferredTypes) {
492-
explicitlyDeferredTypes ??= new Map();
493-
explicitlyDeferredTypes.set(importDetails.name, {
491+
explicitlyDeferredTypes ??= [];
492+
explicitlyDeferredTypes.push({
493+
symbolName: importDetails.name,
494494
importPath: importDetails.from,
495495
isDefaultImport: isDefaultImport(importDetails.node),
496496
});
@@ -715,10 +715,10 @@ export class ComponentDecoratorHandler implements
715715
declarationListEmitMode: (!analysis.meta.isStandalone || analysis.rawImports !== null) ?
716716
DeclarationListEmitMode.RuntimeResolved :
717717
DeclarationListEmitMode.Direct,
718-
deferBlockDependencies: this.locateDeferBlocksWithoutScope(analysis.template),
718+
deferPerBlockDependencies: this.locateDeferBlocksWithoutScope(analysis.template),
719719
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerComponent,
720720
deferrableDeclToImportDecl: new Map(),
721-
deferrableTypes: analysis.explicitlyDeferredTypes ?? new Map(),
721+
deferPerComponentDependencies: analysis.explicitlyDeferredTypes ?? [],
722722
};
723723

724724
if (this.localCompilationExtraImportsTracker === null) {
@@ -731,10 +731,10 @@ export class ComponentDecoratorHandler implements
731731
data = {
732732
declarations: EMPTY_ARRAY,
733733
declarationListEmitMode: DeclarationListEmitMode.Direct,
734-
deferBlockDependencies: new Map(),
734+
deferPerBlockDependencies: new Map(),
735735
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerBlock,
736736
deferrableDeclToImportDecl: new Map(),
737-
deferrableTypes: new Map(),
737+
deferPerComponentDependencies: [],
738738
};
739739
}
740740

@@ -1032,7 +1032,7 @@ export class ComponentDecoratorHandler implements
10321032
} else {
10331033
// If there is no scope, we can still use the binder to retrieve *some* information about the
10341034
// deferred blocks.
1035-
data.deferBlockDependencies = this.locateDeferBlocksWithoutScope(metadata.template);
1035+
data.deferPerBlockDependencies = this.locateDeferBlocksWithoutScope(metadata.template);
10361036
}
10371037

10381038
// Run diagnostics only in global mode.
@@ -1257,22 +1257,19 @@ export class ComponentDecoratorHandler implements
12571257
* the `@Component.imports` field and their usage in `@defer` blocks.
12581258
*/
12591259
private collectDeferredSymbols(resolution: Readonly<ComponentResolutionData>) {
1260-
const deferrableTypes = new Map<string, {importPath: string, isDefaultImport: boolean}>();
1260+
const deferrableTypes: R3DeferPerComponentDependency[] = [];
12611261
// Go over all dependencies of all defer blocks and update the value of
12621262
// the `isDeferrable` flag and the `importPath` to reflect the current
12631263
// state after visiting all components during the `resolve` phase.
1264-
for (const [_, deps] of resolution.deferBlockDependencies) {
1264+
for (const [_, deps] of resolution.deferPerBlockDependencies) {
12651265
for (const deferBlockDep of deps) {
12661266
const importDecl =
12671267
resolution.deferrableDeclToImportDecl.get(deferBlockDep.declaration.node) ?? null;
12681268
if (importDecl !== null && this.deferredSymbolTracker.canDefer(importDecl)) {
12691269
deferBlockDep.isDeferrable = true;
12701270
deferBlockDep.importPath = (importDecl.moduleSpecifier as ts.StringLiteral).text;
12711271
deferBlockDep.isDefaultImport = isDefaultImport(importDecl);
1272-
deferrableTypes.set(deferBlockDep.symbolName, {
1273-
importPath: deferBlockDep.importPath,
1274-
isDefaultImport: deferBlockDep.isDefaultImport,
1275-
});
1272+
deferrableTypes.push(deferBlockDep as R3DeferPerComponentDependency);
12761273
}
12771274
}
12781275
}
@@ -1353,11 +1350,11 @@ export class ComponentDecoratorHandler implements
13531350
const usedPipes = new Set(bound.getEagerlyUsedPipes());
13541351
let deps: DeferredComponentDependency[];
13551352

1356-
if (resolutionData.deferBlockDependencies.has(deferBlock)) {
1357-
deps = resolutionData.deferBlockDependencies.get(deferBlock)!;
1353+
if (resolutionData.deferPerBlockDependencies.has(deferBlock)) {
1354+
deps = resolutionData.deferPerBlockDependencies.get(deferBlock)!;
13581355
} else {
13591356
deps = [];
1360-
resolutionData.deferBlockDependencies.set(deferBlock, deps);
1357+
resolutionData.deferPerBlockDependencies.set(deferBlock, deps);
13611358
}
13621359

13631360
for (const decl of Array.from(deferrableDecls.values())) {
@@ -1479,68 +1476,39 @@ export class ComponentDecoratorHandler implements
14791476
}
14801477

14811478
private compileDeferBlocks(resolution: Readonly<Partial<ComponentResolutionData>>):
1482-
R3DeferMetadata {
1483-
if (resolution.deferBlockDepsEmitMode === DeferBlockDepsEmitMode.PerBlock) {
1484-
if (!resolution.deferBlockDependencies) {
1479+
R3ComponentDeferMetadata {
1480+
const {
1481+
deferBlockDepsEmitMode: mode,
1482+
deferPerBlockDependencies: perBlockDeps,
1483+
deferPerComponentDependencies: perComponentDeps
1484+
} = resolution;
1485+
1486+
if (mode === DeferBlockDepsEmitMode.PerBlock) {
1487+
if (!perBlockDeps) {
14851488
throw new Error(
1486-
'Internal error: deferBlockDependencies must be present when compiling in PerBlock mode');
1489+
'Internal error: deferPerBlockDependencies must be present when compiling in PerBlock mode');
14871490
}
14881491

14891492
const blocks = new Map<TmplAstDeferredBlock, o.ArrowFunctionExpr|null>();
1490-
1491-
for (const [block, dependencies] of resolution.deferBlockDependencies) {
1492-
const depExpressions: o.Expression[] = [];
1493-
for (const dep of dependencies) {
1494-
if (dep.isDeferrable) {
1495-
// Callback function, e.g. `m () => m.MyCmp;`.
1496-
const innerFn = o.arrowFn(
1497-
// Default imports are always accessed through the `default` property.
1498-
[new o.FnParam('m', o.DYNAMIC_TYPE)],
1499-
o.variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName));
1500-
1501-
// Dynamic import, e.g. `import('./a').then(...)`.
1502-
const importExpr =
1503-
(new o.DynamicImportExpr(dep.importPath!)).prop('then').callFn([innerFn]);
1504-
depExpressions.push(importExpr);
1505-
} else {
1506-
// Non-deferrable symbol, just use a reference to the type. Note that it's important to
1507-
// go through `typeReference`, rather than `symbolName` in order to preserve the
1508-
// original reference within the source file.
1509-
depExpressions.push(dep.typeReference);
1510-
}
1511-
}
1493+
for (const [block, dependencies] of perBlockDeps) {
15121494
blocks.set(
15131495
block,
1514-
depExpressions.length === 0 ? null : o.arrowFn([], o.literalArr(depExpressions)));
1496+
dependencies.length === 0 ? null : compileDeferResolverFunction({mode, dependencies}));
15151497
}
15161498

1517-
return {mode: DeferBlockDepsEmitMode.PerBlock, blocks};
1499+
return {mode, blocks};
15181500
}
15191501

1520-
if (resolution.deferBlockDepsEmitMode === DeferBlockDepsEmitMode.PerComponent) {
1521-
if (!resolution.deferBlockDependencies || !resolution.deferrableTypes) {
1502+
if (mode === DeferBlockDepsEmitMode.PerComponent) {
1503+
if (!perComponentDeps) {
15221504
throw new Error(
1523-
'Internal error: deferBlockDependencies and deferrableTypes must be present in PerComponent mode');
1505+
'Internal error: deferPerComponentDependencies must be present in PerComponent mode');
15241506
}
1525-
1526-
// This defer block has deps for which we need to generate dynamic imports.
1527-
const depExpressions: o.Expression[] = [];
1528-
1529-
for (const [symbolName, {importPath, isDefaultImport}] of resolution.deferrableTypes) {
1530-
// Callback function, e.g. `m () => m.MyCmp;`.
1531-
const innerFn = o.arrowFn(
1532-
[new o.FnParam('m', o.DYNAMIC_TYPE)],
1533-
o.variable('m').prop(isDefaultImport ? 'default' : symbolName));
1534-
1535-
// Dynamic import, e.g. `import('./a').then(...)`.
1536-
const importExpr = (new o.DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
1537-
depExpressions.push(importExpr);
1538-
}
1539-
15401507
return {
15411508
mode: DeferBlockDepsEmitMode.PerComponent,
1542-
dependenciesFn: depExpressions.length === 0 ? null :
1543-
o.arrowFn([], o.literalArr(depExpressions))
1509+
dependenciesFn: perComponentDeps.length === 0 ?
1510+
null :
1511+
compileDeferResolverFunction({mode, dependencies: perComponentDeps})
15441512
};
15451513
}
15461514

@@ -1586,10 +1554,9 @@ function extractPipes(dependencies: Array<PipeMeta|DirectiveMeta|NgModuleMeta>):
15861554
* in the `setClassMetadataAsync` call. Otherwise, an import declaration gets retained.
15871555
*/
15881556
function removeDeferrableTypesFromComponentDecorator(
1589-
analysis: Readonly<ComponentAnalysisData>,
1590-
deferrableTypes: Map<string, {importPath: string, isDefaultImport: boolean}>) {
1557+
analysis: Readonly<ComponentAnalysisData>, deferrableTypes: R3DeferPerComponentDependency[]) {
15911558
if (analysis.classMetadata) {
1592-
const deferrableSymbols = new Set(deferrableTypes.keys());
1559+
const deferrableSymbols = new Set(deferrableTypes.map(t => t.symbolName));
15931560
const rewrittenDecoratorsNode = removeIdentifierReferences(
15941561
(analysis.classMetadata.decorators as o.WrappedNodeExpr<ts.Node>).node, deferrableSymbols);
15951562
analysis.classMetadata.decorators = new o.WrappedNodeExpr(rewrittenDecoratorsNode);

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

Lines changed: 10 additions & 33 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, DeclarationListEmitMode, DeferBlockDepsEmitMode, Expression, R3ClassDebugInfo, R3ClassMetadata, R3ComponentMetadata, R3TemplateDependencyMetadata, SchemaMetadata, TmplAstDeferredBlock} from '@angular/compiler';
9+
import {AnimationTriggerNames, DeclarationListEmitMode, DeferBlockDepsEmitMode, R3ClassDebugInfo, R3ClassMetadata, R3ComponentMetadata, R3DeferPerBlockDependency, R3DeferPerComponentDependency, R3TemplateDependencyMetadata, SchemaMetadata, TmplAstDeferredBlock} from '@angular/compiler';
1010
import ts from 'typescript';
1111

1212
import {Reference} from '../../../imports';
@@ -76,7 +76,7 @@ export interface ComponentAnalysisData {
7676
/**
7777
* Map of symbol name -> import path for types from `@Component.deferredImports` field.
7878
*/
79-
explicitlyDeferredTypes: Map<string, {importPath: string, isDefaultImport: boolean}>|null;
79+
explicitlyDeferredTypes: R3DeferPerComponentDependency[]|null;
8080

8181
schemas: SchemaMetadata[]|null;
8282

@@ -101,9 +101,10 @@ export interface ComponentResolutionData {
101101
deferrableDeclToImportDecl: Map<ClassDeclaration, ts.ImportDeclaration>;
102102

103103
/**
104-
* Map of `@defer` blocks -> their corresponding metadata.
104+
* Map of `@defer` blocks -> their corresponding dependencies.
105+
* Required to compile the defer resolver function in `PerBlock` mode.
105106
*/
106-
deferBlockDependencies: Map<TmplAstDeferredBlock, DeferredComponentDependency[]>;
107+
deferPerBlockDependencies: Map<TmplAstDeferredBlock, DeferredComponentDependency[]>;
107108

108109
/**
109110
* Defines how dynamic imports for deferred dependencies should be grouped:
@@ -113,40 +114,16 @@ export interface ComponentResolutionData {
113114
deferBlockDepsEmitMode: DeferBlockDepsEmitMode;
114115

115116
/**
116-
* Map of deferrable symbol names -> corresponding import paths.
117+
* List of deferrable dependencies in the entire component. Used to compile the
118+
* defer resolver function in `PerComponent` mode.
117119
*/
118-
deferrableTypes: Map<string, {importPath: string, isDefaultImport: boolean}>;
120+
deferPerComponentDependencies: R3DeferPerComponentDependency[];
119121
}
120122

121123
/**
122124
* Describes a dependency used within a `@defer` block.
123125
*/
124-
export interface DeferredComponentDependency {
125-
/**
126-
* Reference to a dependency.
127-
*/
128-
typeReference: Expression;
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-
126+
export type DeferredComponentDependency = R3DeferPerBlockDependency&{
150127
/** Reference to the declaration that defines the dependency. */
151128
declaration: Reference<ClassDeclaration>;
152-
}
129+
};

packages/compiler/src/compiler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export {compileInjector, R3InjectorMetadata} from './render3/r3_injector_compile
7676
export {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
7777
export {makeBindingParser, ParsedTemplate, parseTemplate, ParseTemplateOptions} from './render3/view/template';
7878
export {ForwardRefHandling, MaybeForwardRefExpression, R3CompiledExpression, R3Reference, createMayBeForwardRefExpression, devOnlyGuardedExpression, getSafePropertyAccessString} from './render3/util';
79-
export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings, ParsedHostBindings, verifyHostBindings, encapsulateStyle} from './render3/view/compiler';
79+
export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings, ParsedHostBindings, verifyHostBindings, encapsulateStyle, compileDeferResolverFunction} from './render3/view/compiler';
8080
export {compileDeclareClassMetadata} from './render3/partial/class_metadata';
8181
export {compileDeclareComponentFromMetadata, DeclareComponentTemplateInfo} from './render3/partial/component';
8282
export {compileDeclareDirectiveFromMetadata} from './render3/partial/directive';

0 commit comments

Comments
 (0)