mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
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
This commit is contained in:
parent
d888da4606
commit
eee620aa00
13 changed files with 270 additions and 496 deletions
|
|
@ -5,7 +5,7 @@
|
|||
* 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 {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';
|
||||
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';
|
||||
import semver from 'semver';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system';
|
||||
|
|
@ -178,13 +178,7 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
|
|||
declarationListEmitMode,
|
||||
styles: metaObj.has('styles') ? metaObj.getArray('styles').map(entry => entry.getString()) :
|
||||
[],
|
||||
deferBlocks: this.createR3DeferredMetadata(boundTarget),
|
||||
|
||||
// Defer blocks are not yet fully supported in partial compilation.
|
||||
deferrableDeclToImportDecl: new Map(),
|
||||
deferrableTypes: new Map(),
|
||||
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerBlock,
|
||||
|
||||
defer: this.createR3DeferMetadata(boundTarget),
|
||||
encapsulation: metaObj.has('encapsulation') ?
|
||||
parseEncapsulation(metaObj.getValue('encapsulation')) :
|
||||
ViewEncapsulation.Emulated,
|
||||
|
|
@ -264,32 +258,16 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
|
|||
};
|
||||
}
|
||||
|
||||
private createR3DeferredMetadata(boundTarget: BoundTarget<any>):
|
||||
Map<TmplAstDeferredBlock, R3DeferBlockMetadata> {
|
||||
private createR3DeferMetadata(boundTarget: BoundTarget<any>): R3DeferMetadata {
|
||||
const deferredBlocks = boundTarget.getDeferBlocks();
|
||||
const meta = new Map<TmplAstDeferredBlock, R3DeferBlockMetadata>();
|
||||
const blocks = new Map<TmplAstDeferredBlock, o.ArrowFunctionExpr|null>();
|
||||
|
||||
for (const block of deferredBlocks) {
|
||||
const triggerElements = new Map<TmplAstDeferredTrigger, TmplAstElement>();
|
||||
|
||||
this.resolveDeferTriggers(block, block.triggers, boundTarget, triggerElements);
|
||||
this.resolveDeferTriggers(block, block.prefetchTriggers, boundTarget, triggerElements);
|
||||
|
||||
// TODO: leaving `deps` empty for now, to be implemented as one of the next steps.
|
||||
meta.set(block, {deps: [], triggerElements});
|
||||
blocks.set(block, null);
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
private resolveDeferTriggers(
|
||||
block: TmplAstDeferredBlock, triggers: TmplAstDeferredBlockTriggers,
|
||||
boundTarget: BoundTarget<any>,
|
||||
triggerElements: Map<TmplAstDeferredTrigger, TmplAstElement|null>): void {
|
||||
Object.keys(triggers).forEach(key => {
|
||||
const trigger = triggers[key as keyof TmplAstDeferredBlockTriggers]!;
|
||||
triggerElements.set(trigger, boundTarget.getDeferredTriggerTarget(block, trigger));
|
||||
});
|
||||
return {mode: DeferBlockDepsEmitMode.PerBlock, blocks};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AnimationTriggerNames, BoundTarget, compileClassDebugInfo, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, ConstantPool, CssSelector, DeclarationListEmitMode, DeclareComponentTemplateInfo, DEFAULT_INTERPOLATION_CONFIG, DeferBlockDepsEmitMode, DomElementSchemaRegistry, Expression, ExternalExpr, FactoryTarget, makeBindingParser, R3ComponentMetadata, R3DeferBlockMetadata, R3DeferBlockTemplateDependency, R3DirectiveDependencyMetadata, R3NgModuleDependencyMetadata, R3PipeDependencyMetadata, R3TargetBinder, R3TemplateDependency, R3TemplateDependencyKind, R3TemplateDependencyMetadata, SchemaMetadata, SelectorMatcher, TmplAstDeferredBlock, TmplAstDeferredBlockTriggers, TmplAstDeferredTrigger, TmplAstElement, ViewEncapsulation, WrappedNodeExpr} from '@angular/compiler';
|
||||
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';
|
||||
import ts from 'typescript';
|
||||
|
||||
import {Cycle, CycleAnalyzer, CycleHandlingStrategy} from '../../../cycles';
|
||||
|
|
@ -32,7 +32,7 @@ import {extractDirectiveMetadata, parseDirectiveStyles} from '../../directive';
|
|||
import {createModuleWithProvidersResolver, NgModuleSymbol} from '../../ng_module';
|
||||
|
||||
import {checkCustomElementSelectorForErrors, makeCyclicImportInfo} from './diagnostics';
|
||||
import {ComponentAnalysisData, ComponentResolutionData} from './metadata';
|
||||
import {ComponentAnalysisData, ComponentResolutionData, DeferredComponentDependency} from './metadata';
|
||||
import {_extractTemplateStyleUrls, extractComponentStyleUrls, extractStyleResources, extractTemplate, makeResourceNotFoundError, ParsedTemplateWithSource, parseTemplateDeclaration, preloadAndParseTemplate, ResourceTypeForDiagnostics, StyleUrlMeta, transformDecoratorResources} from './resources';
|
||||
import {ComponentSymbol} from './symbol';
|
||||
import {animationTriggerResolver, collectAnimationNames, validateAndFlattenComponentImports} from './util';
|
||||
|
|
@ -245,19 +245,19 @@ export class ComponentDecoratorHandler implements
|
|||
resolveEncapsulationEnumValueLocally(component.get('encapsulation'))) ??
|
||||
ViewEncapsulation.Emulated;
|
||||
|
||||
let changeDetection: number|Expression|null = null;
|
||||
let changeDetection: number|o.Expression|null = null;
|
||||
if (this.compilationMode !== CompilationMode.LOCAL) {
|
||||
changeDetection =
|
||||
resolveEnumValue(this.evaluator, component, 'changeDetection', 'ChangeDetectionStrategy');
|
||||
} else if (component.has('changeDetection')) {
|
||||
changeDetection = new WrappedNodeExpr(component.get('changeDetection')!);
|
||||
changeDetection = new o.WrappedNodeExpr(component.get('changeDetection')!);
|
||||
}
|
||||
|
||||
let animations: Expression|null = null;
|
||||
let animations: o.Expression|null = null;
|
||||
let animationTriggerNames: AnimationTriggerNames|null = null;
|
||||
if (component.has('animations')) {
|
||||
const animationExpression = component.get('animations')!;
|
||||
animations = new WrappedNodeExpr(animationExpression);
|
||||
animations = new o.WrappedNodeExpr(animationExpression);
|
||||
const animationsValue =
|
||||
this.evaluator.evaluate(animationExpression, animationTriggerResolver);
|
||||
animationTriggerNames = {includesDynamicAnimations: false, staticTriggerNames: []};
|
||||
|
|
@ -281,13 +281,13 @@ export class ComponentDecoratorHandler implements
|
|||
// we can distinguish where an error is coming from when logging the diagnostics in `resolve`.
|
||||
let viewProvidersRequiringFactory: Set<Reference<ClassDeclaration>>|null = null;
|
||||
let providersRequiringFactory: Set<Reference<ClassDeclaration>>|null = null;
|
||||
let wrappedViewProviders: Expression|null = null;
|
||||
let wrappedViewProviders: o.Expression|null = null;
|
||||
|
||||
if (component.has('viewProviders')) {
|
||||
const viewProviders = component.get('viewProviders')!;
|
||||
viewProvidersRequiringFactory =
|
||||
resolveProvidersRequiringFactory(viewProviders, this.reflector, this.evaluator);
|
||||
wrappedViewProviders = new WrappedNodeExpr(
|
||||
wrappedViewProviders = new o.WrappedNodeExpr(
|
||||
this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(viewProviders) :
|
||||
viewProviders);
|
||||
}
|
||||
|
|
@ -520,7 +520,7 @@ export class ComponentDecoratorHandler implements
|
|||
viewProviders: wrappedViewProviders,
|
||||
i18nUseExternalIds: this.i18nUseExternalIds,
|
||||
relativeContextFilePath,
|
||||
rawImports: rawImports !== null ? new WrappedNodeExpr(rawImports) : undefined,
|
||||
rawImports: rawImports !== null ? new o.WrappedNodeExpr(rawImports) : undefined,
|
||||
useTemplatePipeline: this.useTemplatePipeline,
|
||||
},
|
||||
typeCheckMeta: extractDirectiveTypeCheckMeta(node, inputs, this.reflector),
|
||||
|
|
@ -710,10 +710,10 @@ export class ComponentDecoratorHandler implements
|
|||
declarationListEmitMode: (!analysis.meta.isStandalone || analysis.rawImports !== null) ?
|
||||
DeclarationListEmitMode.RuntimeResolved :
|
||||
DeclarationListEmitMode.Direct,
|
||||
deferBlocks: this.locateDeferBlocksWithoutScope(analysis.template),
|
||||
deferBlockDependencies: this.locateDeferBlocksWithoutScope(analysis.template),
|
||||
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerComponent,
|
||||
deferrableDeclToImportDecl: new Map(),
|
||||
deferrableTypes: new Map(),
|
||||
deferrableTypes: analysis.explicitlyDeferredTypes ?? new Map(),
|
||||
};
|
||||
|
||||
if (this.localCompilationExtraImportsTracker === null) {
|
||||
|
|
@ -726,7 +726,7 @@ export class ComponentDecoratorHandler implements
|
|||
data = {
|
||||
declarations: EMPTY_ARRAY,
|
||||
declarationListEmitMode: DeclarationListEmitMode.Direct,
|
||||
deferBlocks: new Map(),
|
||||
deferBlockDependencies: new Map(),
|
||||
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerBlock,
|
||||
deferrableDeclToImportDecl: new Map(),
|
||||
deferrableTypes: new Map(),
|
||||
|
|
@ -910,8 +910,7 @@ export class ComponentDecoratorHandler implements
|
|||
|
||||
// Process information related to defer blocks
|
||||
if (this.compilationMode !== CompilationMode.LOCAL) {
|
||||
this.resolveDeferBlocks(
|
||||
node, deferBlocks, declarations, data, analysis, eagerlyUsed, bound);
|
||||
this.resolveDeferBlocks(node, deferBlocks, declarations, data, analysis, eagerlyUsed);
|
||||
}
|
||||
|
||||
const cyclesFromDirectives = new Map<UsedDirective, Cycle>();
|
||||
|
|
@ -1028,7 +1027,7 @@ export class ComponentDecoratorHandler implements
|
|||
} else {
|
||||
// If there is no scope, we can still use the binder to retrieve *some* information about the
|
||||
// deferred blocks.
|
||||
data.deferBlocks = this.locateDeferBlocksWithoutScope(metadata.template);
|
||||
data.deferBlockDependencies = this.locateDeferBlocksWithoutScope(metadata.template);
|
||||
}
|
||||
|
||||
// Run diagnostics only in global mode.
|
||||
|
|
@ -1048,7 +1047,7 @@ export class ComponentDecoratorHandler implements
|
|||
}
|
||||
|
||||
if (analysis.providersRequiringFactory !== null &&
|
||||
analysis.meta.providers instanceof WrappedNodeExpr) {
|
||||
analysis.meta.providers instanceof o.WrappedNodeExpr) {
|
||||
const providerDiagnostics = getProviderDiagnostics(
|
||||
analysis.providersRequiringFactory, analysis.meta.providers!.node,
|
||||
this.injectableRegistry);
|
||||
|
|
@ -1056,7 +1055,7 @@ export class ComponentDecoratorHandler implements
|
|||
}
|
||||
|
||||
if (analysis.viewProvidersRequiringFactory !== null &&
|
||||
analysis.meta.viewProviders instanceof WrappedNodeExpr) {
|
||||
analysis.meta.viewProviders instanceof o.WrappedNodeExpr) {
|
||||
const viewProviderDiagnostics = getProviderDiagnostics(
|
||||
analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders!.node,
|
||||
this.injectableRegistry);
|
||||
|
|
@ -1145,7 +1144,7 @@ export class ComponentDecoratorHandler implements
|
|||
const meta: R3ComponentMetadata<R3TemplateDependency> = {
|
||||
...analysis.meta,
|
||||
...resolution,
|
||||
deferrableTypes,
|
||||
defer: this.compileDeferBlocks(resolution),
|
||||
useTemplatePipeline,
|
||||
};
|
||||
const fac = compileNgFactoryDefField(toFactoryMetadata(meta, FactoryTarget.Component));
|
||||
|
|
@ -1176,7 +1175,7 @@ export class ComponentDecoratorHandler implements
|
|||
sourceUrl: analysis.template.declaration.resolvedTemplateUrl,
|
||||
isInline: analysis.template.declaration.isInline,
|
||||
inlineTemplateLiteralExpression: analysis.template.sourceMapping.type === 'direct' ?
|
||||
new WrappedNodeExpr(analysis.template.sourceMapping.node) :
|
||||
new o.WrappedNodeExpr(analysis.template.sourceMapping.node) :
|
||||
null,
|
||||
};
|
||||
|
||||
|
|
@ -1184,6 +1183,7 @@ export class ComponentDecoratorHandler implements
|
|||
const meta: R3ComponentMetadata<R3TemplateDependencyMetadata> = {
|
||||
...analysis.meta,
|
||||
...resolution,
|
||||
defer: this.compileDeferBlocks(resolution),
|
||||
useTemplatePipeline
|
||||
};
|
||||
const fac = compileDeclareFactory(toFactoryMetadata(meta, FactoryTarget.Component));
|
||||
|
|
@ -1213,7 +1213,7 @@ export class ComponentDecoratorHandler implements
|
|||
const meta = {
|
||||
...analysis.meta,
|
||||
...resolution,
|
||||
deferrableTypes: deferrableTypes ?? new Map(),
|
||||
defer: this.compileDeferBlocks(resolution),
|
||||
useTemplatePipeline,
|
||||
} as R3ComponentMetadata<R3TemplateDependency>;
|
||||
|
||||
|
|
@ -1240,18 +1240,15 @@ export class ComponentDecoratorHandler implements
|
|||
* For example, this happens in the local compilation mode.
|
||||
*/
|
||||
private locateDeferBlocksWithoutScope(template: ComponentTemplate):
|
||||
Map<TmplAstDeferredBlock, R3DeferBlockMetadata> {
|
||||
const deferBlocks = new Map<TmplAstDeferredBlock, R3DeferBlockMetadata>();
|
||||
Map<TmplAstDeferredBlock, DeferredComponentDependency[]> {
|
||||
const deferBlocks = new Map<TmplAstDeferredBlock, DeferredComponentDependency[]>();
|
||||
const directivelessBinder = new R3TargetBinder<DirectiveMeta>(new SelectorMatcher());
|
||||
const bound = directivelessBinder.bind({template: template.nodes});
|
||||
const deferredBlocks = bound.getDeferBlocks();
|
||||
const triggerElements = new Map<TmplAstDeferredTrigger, TmplAstElement|null>();
|
||||
|
||||
for (const block of deferredBlocks) {
|
||||
this.resolveDeferTriggers(block, block.triggers, bound, triggerElements);
|
||||
this.resolveDeferTriggers(block, block.prefetchTriggers, bound, triggerElements);
|
||||
// We can't determine the dependencies without a scope so we leave them empty.
|
||||
deferBlocks.set(block, {deps: [], triggerElements});
|
||||
deferBlocks.set(block, []);
|
||||
}
|
||||
return deferBlocks;
|
||||
}
|
||||
|
|
@ -1265,14 +1262,10 @@ export class ComponentDecoratorHandler implements
|
|||
// Go over all dependencies of all defer blocks and update the value of
|
||||
// the `isDeferrable` flag and the `importPath` to reflect the current
|
||||
// state after visiting all components during the `resolve` phase.
|
||||
for (const [_, metadata] of resolution.deferBlocks) {
|
||||
for (const deferBlockDep of metadata.deps) {
|
||||
const dep = deferBlockDep as unknown as {
|
||||
classDeclaration: ts.ClassDeclaration;
|
||||
};
|
||||
const classDecl = dep.classDeclaration as unknown as Expression;
|
||||
const importDecl = (resolution.deferrableDeclToImportDecl.get(classDecl) ??
|
||||
null) as (ts.ImportDeclaration | null);
|
||||
for (const [_, deps] of resolution.deferBlockDependencies) {
|
||||
for (const deferBlockDep of deps) {
|
||||
const importDecl =
|
||||
resolution.deferrableDeclToImportDecl.get(deferBlockDep.type.node) ?? null;
|
||||
if (importDecl !== null && this.deferredSymbolTracker.canDefer(importDecl)) {
|
||||
deferBlockDep.isDeferrable = true;
|
||||
deferBlockDep.importPath = (importDecl.moduleSpecifier as ts.StringLiteral).text;
|
||||
|
|
@ -1320,7 +1313,7 @@ export class ComponentDecoratorHandler implements
|
|||
* @returns a `Cycle` object if a cycle would be created, otherwise `null`.
|
||||
*/
|
||||
private _checkForCyclicImport(
|
||||
importedFile: ImportedFile, expr: Expression, origin: ts.SourceFile): Cycle|null {
|
||||
importedFile: ImportedFile, expr: o.Expression, origin: ts.SourceFile): Cycle|null {
|
||||
const imported = resolveImportedFile(this.moduleResolver, importedFile, expr, origin);
|
||||
if (imported === null) {
|
||||
return null;
|
||||
|
|
@ -1330,7 +1323,7 @@ export class ComponentDecoratorHandler implements
|
|||
}
|
||||
|
||||
private maybeRecordSyntheticImport(
|
||||
importedFile: ImportedFile, expr: Expression, origin: ts.SourceFile): void {
|
||||
importedFile: ImportedFile, expr: o.Expression, origin: ts.SourceFile): void {
|
||||
const imported = resolveImportedFile(this.moduleResolver, importedFile, expr, origin);
|
||||
if (imported === null) {
|
||||
return;
|
||||
|
|
@ -1350,7 +1343,6 @@ export class ComponentDecoratorHandler implements
|
|||
resolutionData: ComponentResolutionData,
|
||||
analysisData: Readonly<ComponentAnalysisData>,
|
||||
eagerlyUsedDecls: Set<ClassDeclaration>,
|
||||
componentBoundTarget: BoundTarget<DirectiveMeta>,
|
||||
) {
|
||||
// Collect all deferred decls from all defer blocks from the entire template
|
||||
// to intersect with the information from the `imports` field of a particular
|
||||
|
|
@ -1360,9 +1352,14 @@ export class ComponentDecoratorHandler implements
|
|||
for (const [deferBlock, bound] of deferBlocks) {
|
||||
const usedDirectives = new Set(bound.getEagerlyUsedDirectives().map(d => d.ref.node));
|
||||
const usedPipes = new Set(bound.getEagerlyUsedPipes());
|
||||
const deps: Array<R3DeferBlockTemplateDependency&{classDeclaration: ts.ClassDeclaration}> =
|
||||
[];
|
||||
const triggerElements = new Map<TmplAstDeferredTrigger, TmplAstElement|null>();
|
||||
let deps: DeferredComponentDependency[];
|
||||
|
||||
if (resolutionData.deferBlockDependencies.has(deferBlock)) {
|
||||
deps = resolutionData.deferBlockDependencies.get(deferBlock)!;
|
||||
} else {
|
||||
deps = [];
|
||||
resolutionData.deferBlockDependencies.set(deferBlock, deps);
|
||||
}
|
||||
|
||||
for (const decl of Array.from(deferrableDecls.values())) {
|
||||
if (decl.kind === R3TemplateDependencyKind.NgModule) {
|
||||
|
|
@ -1379,23 +1376,14 @@ export class ComponentDecoratorHandler implements
|
|||
// `isDeferrable`, `importPath` and `isDefaultImport` will be
|
||||
// added later during the `compile` step.
|
||||
deps.push({
|
||||
type: decl.type as WrappedNodeExpr<ts.Identifier>,
|
||||
symbolName: decl.ref.node.name.escapedText as string,
|
||||
type: decl.ref,
|
||||
symbolName: decl.ref.node.name.text,
|
||||
isDeferrable: false,
|
||||
importPath: null,
|
||||
isDefaultImport: false,
|
||||
// Extra info to match corresponding import during the `compile` phase.
|
||||
classDeclaration: decl.ref.node as ts.ClassDeclaration,
|
||||
});
|
||||
allDeferredDecls.add(decl.ref.node);
|
||||
}
|
||||
|
||||
this.resolveDeferTriggers(
|
||||
deferBlock, deferBlock.triggers, componentBoundTarget, triggerElements);
|
||||
this.resolveDeferTriggers(
|
||||
deferBlock, deferBlock.prefetchTriggers, componentBoundTarget, triggerElements);
|
||||
|
||||
resolutionData.deferBlocks.set(deferBlock, {deps, triggerElements});
|
||||
}
|
||||
|
||||
// For standalone components with the `imports` and `deferredImports` fields -
|
||||
|
|
@ -1483,23 +1471,78 @@ export class ComponentDecoratorHandler implements
|
|||
|
||||
// Keep track of how this class made it into the current source file
|
||||
// (which ts.ImportDeclaration was used for this symbol).
|
||||
resolutionData.deferrableDeclToImportDecl.set(
|
||||
decl.node as unknown as Expression, imp.node as unknown as Expression);
|
||||
resolutionData.deferrableDeclToImportDecl.set(decl.node, imp.node);
|
||||
|
||||
this.deferredSymbolTracker.markAsDeferrableCandidate(
|
||||
node, imp.node, componentClassDecl, isDeferredImport);
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolves the triggers of the defer block to the elements that they're pointing to. */
|
||||
private resolveDeferTriggers(
|
||||
block: TmplAstDeferredBlock, triggers: TmplAstDeferredBlockTriggers,
|
||||
componentBoundTarget: BoundTarget<DirectiveMeta>,
|
||||
triggerElements: Map<TmplAstDeferredTrigger, TmplAstElement|null>): void {
|
||||
Object.keys(triggers).forEach(key => {
|
||||
const trigger = triggers[key as keyof TmplAstDeferredBlockTriggers]!;
|
||||
triggerElements.set(trigger, componentBoundTarget.getDeferredTriggerTarget(block, trigger));
|
||||
});
|
||||
private compileDeferBlocks(resolution: Readonly<Partial<ComponentResolutionData>>):
|
||||
R3DeferMetadata {
|
||||
if (resolution.deferBlockDepsEmitMode === DeferBlockDepsEmitMode.PerBlock) {
|
||||
if (!resolution.deferBlockDependencies) {
|
||||
throw new Error(
|
||||
'Internal error: deferBlockDependencies must be present when compiling in PerBlock mode');
|
||||
}
|
||||
|
||||
const blocks = new Map<TmplAstDeferredBlock, o.ArrowFunctionExpr|null>();
|
||||
|
||||
for (const [block, dependencies] of resolution.deferBlockDependencies) {
|
||||
const depExpressions: o.Expression[] = [];
|
||||
for (const dep of dependencies) {
|
||||
if (dep.isDeferrable) {
|
||||
// Callback function, e.g. `m () => m.MyCmp;`.
|
||||
const innerFn = o.arrowFn(
|
||||
// Default imports are always accessed through the `default` property.
|
||||
[new o.FnParam('m', o.DYNAMIC_TYPE)],
|
||||
o.variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName));
|
||||
|
||||
// Dynamic import, e.g. `import('./a').then(...)`.
|
||||
const importExpr =
|
||||
(new o.DynamicImportExpr(dep.importPath!)).prop('then').callFn([innerFn]);
|
||||
depExpressions.push(importExpr);
|
||||
} else {
|
||||
// Non-deferrable symbol, just use a reference to the type.
|
||||
depExpressions.push(o.variable(dep.symbolName));
|
||||
}
|
||||
}
|
||||
blocks.set(
|
||||
block,
|
||||
depExpressions.length === 0 ? null : o.arrowFn([], o.literalArr(depExpressions)));
|
||||
}
|
||||
|
||||
return {mode: DeferBlockDepsEmitMode.PerBlock, blocks};
|
||||
}
|
||||
|
||||
if (resolution.deferBlockDepsEmitMode === DeferBlockDepsEmitMode.PerComponent) {
|
||||
if (!resolution.deferBlockDependencies || !resolution.deferrableTypes) {
|
||||
throw new Error(
|
||||
'Internal error: deferBlockDependencies and deferrableTypes must be present in PerComponent mode');
|
||||
}
|
||||
|
||||
// This defer block has deps for which we need to generate dynamic imports.
|
||||
const depExpressions: o.Expression[] = [];
|
||||
|
||||
for (const [symbolName, {importPath, isDefaultImport}] of resolution.deferrableTypes) {
|
||||
// Callback function, e.g. `m () => m.MyCmp;`.
|
||||
const innerFn = o.arrowFn(
|
||||
[new o.FnParam('m', o.DYNAMIC_TYPE)],
|
||||
o.variable('m').prop(isDefaultImport ? 'default' : symbolName));
|
||||
|
||||
// Dynamic import, e.g. `import('./a').then(...)`.
|
||||
const importExpr = (new o.DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
|
||||
depExpressions.push(importExpr);
|
||||
}
|
||||
|
||||
return {
|
||||
mode: DeferBlockDepsEmitMode.PerComponent,
|
||||
dependenciesFn: depExpressions.length === 0 ? null :
|
||||
o.arrowFn([], o.literalArr(depExpressions))
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Invalid deferBlockDepsEmitMode. Cannot compile deferred block metadata.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1546,8 +1589,8 @@ function removeDeferrableTypesFromComponentDecorator(
|
|||
if (analysis.classMetadata) {
|
||||
const deferrableSymbols = new Set(deferrableTypes.keys());
|
||||
const rewrittenDecoratorsNode = removeIdentifierReferences(
|
||||
(analysis.classMetadata.decorators as WrappedNodeExpr<ts.Node>).node, deferrableSymbols);
|
||||
analysis.classMetadata.decorators = new WrappedNodeExpr(rewrittenDecoratorsNode);
|
||||
(analysis.classMetadata.decorators as o.WrappedNodeExpr<ts.Node>).node, deferrableSymbols);
|
||||
analysis.classMetadata.decorators = new o.WrappedNodeExpr(rewrittenDecoratorsNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AnimationTriggerNames, R3ClassDebugInfo, R3ClassMetadata, R3ComponentMetadata, R3TemplateDependencyMetadata, SchemaMetadata} from '@angular/compiler';
|
||||
import {AnimationTriggerNames, DeclarationListEmitMode, DeferBlockDepsEmitMode, R3ClassDebugInfo, R3ClassMetadata, R3ComponentMetadata, R3TemplateDependencyMetadata, SchemaMetadata, TmplAstDeferredBlock} from '@angular/compiler';
|
||||
import ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../../imports';
|
||||
|
|
@ -24,8 +24,7 @@ import {ParsedTemplateWithSource, StyleUrlMeta} from './resources';
|
|||
*/
|
||||
export type ComponentMetadataResolvedFields = SubsetOfKeys<
|
||||
R3ComponentMetadata<R3TemplateDependencyMetadata>,
|
||||
'declarations'|'declarationListEmitMode'|'deferBlocks'|'deferrableDeclToImportDecl'|
|
||||
'deferrableTypes'|'deferBlockDepsEmitMode'>;
|
||||
'declarations'|'declarationListEmitMode'|'defer'>;
|
||||
|
||||
export interface ComponentAnalysisData {
|
||||
/**
|
||||
|
|
@ -90,5 +89,61 @@ export interface ComponentAnalysisData {
|
|||
rawHostDirectives: ts.Expression|null;
|
||||
}
|
||||
|
||||
export type ComponentResolutionData =
|
||||
Pick<R3ComponentMetadata<R3TemplateDependencyMetadata>, ComponentMetadataResolvedFields>;
|
||||
export interface ComponentResolutionData {
|
||||
declarations: R3TemplateDependencyMetadata[];
|
||||
declarationListEmitMode: DeclarationListEmitMode;
|
||||
|
||||
/**
|
||||
* Map of all types that can be defer loaded (ts.ClassDeclaration) ->
|
||||
* corresponding import declaration (ts.ImportDeclaration) within
|
||||
* the current source file.
|
||||
*/
|
||||
deferrableDeclToImportDecl: Map<ClassDeclaration, ts.ImportDeclaration>;
|
||||
|
||||
/**
|
||||
* Map of `@defer` blocks -> their corresponding metadata.
|
||||
*/
|
||||
deferBlockDependencies: Map<TmplAstDeferredBlock, DeferredComponentDependency[]>;
|
||||
|
||||
/**
|
||||
* Defines how dynamic imports for deferred dependencies should be grouped:
|
||||
* - either in a function on per-component basis (in case of local compilation)
|
||||
* - or in a function on per-block basis (in full compilation mode)
|
||||
*/
|
||||
deferBlockDepsEmitMode: DeferBlockDepsEmitMode;
|
||||
|
||||
/**
|
||||
* Map of deferrable symbol names -> corresponding import paths.
|
||||
*/
|
||||
deferrableTypes: Map<string, {importPath: string, isDefaultImport: boolean}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a dependency used within a `@defer` block.
|
||||
*/
|
||||
export interface DeferredComponentDependency {
|
||||
/**
|
||||
* Reference to a dependency.
|
||||
*/
|
||||
type: Reference<ClassDeclaration>;
|
||||
|
||||
/**
|
||||
* Dependency class name.
|
||||
*/
|
||||
symbolName: string;
|
||||
|
||||
/**
|
||||
* Whether this dependency can be defer-loaded.
|
||||
*/
|
||||
isDeferrable: boolean;
|
||||
|
||||
/**
|
||||
* Import path where this dependency is located.
|
||||
*/
|
||||
importPath: string|null;
|
||||
|
||||
/**
|
||||
* Whether the symbol is the default export.
|
||||
*/
|
||||
isDefaultImport: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ import {ConstantPool} from './constant_pool';
|
|||
import {ChangeDetectionStrategy, HostBinding, HostListener, Input, Output, ViewEncapsulation} from './core';
|
||||
import {compileInjectable} from './injectable_compiler_2';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/defaults';
|
||||
import {DeclareVarStmt, Expression, literal, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast';
|
||||
import {ArrowFunctionExpr, DeclareVarStmt, Expression, literal, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast';
|
||||
import {JitEvaluator} from './output/output_jit';
|
||||
import {ParseError, ParseSourceSpan, r3JitTypeSourceSpan} from './parse_util';
|
||||
import {DeferredBlock, DeferredBlockTriggers, DeferredTrigger, Element} from './render3/r3_ast';
|
||||
import {DeferredBlock} from './render3/r3_ast';
|
||||
import {compileFactoryFunction, FactoryTarget, R3DependencyMetadata} from './render3/r3_factory';
|
||||
import {compileInjector, R3InjectorMetadata} from './render3/r3_injector_compiler';
|
||||
import {R3JitReflector} from './render3/r3_jit';
|
||||
import {compileNgModule, compileNgModuleDeclarationExpression, R3NgModuleMetadata, R3NgModuleMetadataKind, R3SelectorScopeMode} from './render3/r3_module_compiler';
|
||||
import {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
|
||||
import {createMayBeForwardRefExpression, ForwardRefHandling, getSafePropertyAccessString, MaybeForwardRefExpression, wrapReference} from './render3/util';
|
||||
import {DeclarationListEmitMode, DeferBlockDepsEmitMode, R3ComponentMetadata, R3DeferBlockMetadata, R3DirectiveDependencyMetadata, R3DirectiveMetadata, R3HostDirectiveMetadata, R3HostMetadata, R3InputMetadata, R3PipeDependencyMetadata, R3QueryMetadata, R3TemplateDependency, R3TemplateDependencyKind, R3TemplateDependencyMetadata} from './render3/view/api';
|
||||
import {DeclarationListEmitMode, DeferBlockDepsEmitMode, R3ComponentMetadata, R3DeferMetadata, R3DirectiveDependencyMetadata, R3DirectiveMetadata, R3HostDirectiveMetadata, R3HostMetadata, R3InputMetadata, R3PipeDependencyMetadata, R3QueryMetadata, R3TemplateDependency, R3TemplateDependencyKind, R3TemplateDependencyMetadata} from './render3/view/api';
|
||||
import {compileComponentFromMetadata, compileDirectiveFromMetadata, ParsedHostBindings, parseHostBindings, verifyHostBindings} from './render3/view/compiler';
|
||||
import type {BoundTarget} from './render3/view/t2_api';
|
||||
import {R3TargetBinder} from './render3/view/t2_binder';
|
||||
|
|
@ -182,7 +182,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
|
|||
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
|
||||
facade: R3ComponentMetadataFacade): any {
|
||||
// Parse the template and check for errors.
|
||||
const {template, interpolation, deferBlocks} = parseJitTemplate(
|
||||
const {template, interpolation, defer} = parseJitTemplate(
|
||||
facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces,
|
||||
facade.interpolation);
|
||||
|
||||
|
|
@ -194,10 +194,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
|
|||
template,
|
||||
declarations: facade.declarations.map(convertDeclarationFacadeToMetadata),
|
||||
declarationListEmitMode: DeclarationListEmitMode.Direct,
|
||||
deferBlocks,
|
||||
deferrableTypes: new Map(),
|
||||
deferrableDeclToImportDecl: new Map(),
|
||||
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerBlock,
|
||||
defer,
|
||||
|
||||
styles: [...facade.styles, ...template.styles],
|
||||
encapsulation: facade.encapsulation,
|
||||
|
|
@ -447,7 +444,7 @@ function convertOpaqueValuesToExpressions(obj: {[key: string]: OpaqueValue}):
|
|||
function convertDeclareComponentFacadeToMetadata(
|
||||
decl: R3DeclareComponentFacade, typeSourceSpan: ParseSourceSpan,
|
||||
sourceMapUrl: string): R3ComponentMetadata<R3TemplateDependencyMetadata> {
|
||||
const {template, interpolation, deferBlocks} = parseJitTemplate(
|
||||
const {template, interpolation, defer} = parseJitTemplate(
|
||||
decl.template, decl.type.name, sourceMapUrl, decl.preserveWhitespaces ?? false,
|
||||
decl.interpolation);
|
||||
|
||||
|
|
@ -484,10 +481,7 @@ function convertDeclareComponentFacadeToMetadata(
|
|||
viewProviders: decl.viewProviders !== undefined ? new WrappedNodeExpr(decl.viewProviders) :
|
||||
null,
|
||||
animations: decl.animations !== undefined ? new WrappedNodeExpr(decl.animations) : null,
|
||||
deferBlocks,
|
||||
deferrableTypes: new Map(),
|
||||
deferrableDeclToImportDecl: new Map(),
|
||||
deferBlockDepsEmitMode: DeferBlockDepsEmitMode.PerBlock,
|
||||
defer,
|
||||
|
||||
changeDetection: decl.changeDetection ?? ChangeDetectionStrategy.Default,
|
||||
encapsulation: decl.encapsulation ?? ViewEncapsulation.Emulated,
|
||||
|
|
@ -562,7 +556,7 @@ function parseJitTemplate(
|
|||
return {
|
||||
template: parsed,
|
||||
interpolation: interpolationConfig,
|
||||
deferBlocks: createR3DeferredMetadata(boundTarget)
|
||||
defer: createR3DeferMetadata(boundTarget)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -633,31 +627,17 @@ function createR3DependencyMetadata(
|
|||
return {token, attributeNameType, host, optional, self, skipSelf};
|
||||
}
|
||||
|
||||
function createR3DeferredMetadata(boundTarget: BoundTarget<any>):
|
||||
Map<DeferredBlock, R3DeferBlockMetadata> {
|
||||
function createR3DeferMetadata(boundTarget: BoundTarget<any>): R3DeferMetadata {
|
||||
const deferredBlocks = boundTarget.getDeferBlocks();
|
||||
const meta = new Map<DeferredBlock, R3DeferBlockMetadata>();
|
||||
const blocks = new Map<DeferredBlock, ArrowFunctionExpr|null>();
|
||||
|
||||
for (const block of deferredBlocks) {
|
||||
const triggerElements = new Map<DeferredTrigger, Element>();
|
||||
|
||||
resolveDeferTriggers(block, block.triggers, boundTarget, triggerElements);
|
||||
resolveDeferTriggers(block, block.prefetchTriggers, boundTarget, triggerElements);
|
||||
|
||||
// TODO: leaving `deps` empty in JIT mode for now, to be implemented as one of the next steps.
|
||||
meta.set(block, {deps: [], triggerElements});
|
||||
// TODO: leaving dependency function empty in JIT mode for now,
|
||||
// to be implemented as one of the next steps.
|
||||
blocks.set(block, null);
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
function resolveDeferTriggers(
|
||||
block: DeferredBlock, triggers: DeferredBlockTriggers, boundTarget: BoundTarget<any>,
|
||||
triggerElements: Map<DeferredTrigger, Element|null>): void {
|
||||
Object.keys(triggers).forEach(key => {
|
||||
const trigger = triggers[key as keyof DeferredBlockTriggers]!;
|
||||
triggerElements.set(trigger, boundTarget.getDeferredTriggerTarget(block, trigger));
|
||||
});
|
||||
return {mode: DeferBlockDepsEmitMode.PerBlock, blocks};
|
||||
}
|
||||
|
||||
function extractHostBindings(
|
||||
|
|
|
|||
|
|
@ -196,47 +196,6 @@ export const enum DeclarationListEmitMode {
|
|||
RuntimeResolved,
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a dependency used within a `@defer` block.
|
||||
*/
|
||||
export interface R3DeferBlockTemplateDependency {
|
||||
/**
|
||||
* Reference to a dependency.
|
||||
*/
|
||||
type: o.WrappedNodeExpr<unknown>;
|
||||
|
||||
/**
|
||||
* Dependency class name.
|
||||
*/
|
||||
symbolName: string;
|
||||
|
||||
/**
|
||||
* Whether this dependency can be defer-loaded.
|
||||
*/
|
||||
isDeferrable: boolean;
|
||||
|
||||
/**
|
||||
* Import path where this dependency is located.
|
||||
*/
|
||||
importPath: string|null;
|
||||
|
||||
/**
|
||||
* Whether the symbol is the default export.
|
||||
*/
|
||||
isDefaultImport: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information necessary to compile a `defer` block.
|
||||
*/
|
||||
export interface R3DeferBlockMetadata {
|
||||
/** Dependencies used within the block. */
|
||||
deps: R3DeferBlockTemplateDependency[];
|
||||
|
||||
/** Mapping between triggers and the DOM nodes they refer to. */
|
||||
triggerElements: Map<t.DeferredTrigger, t.Element|null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information needed to compile a component for the render3 runtime.
|
||||
*/
|
||||
|
|
@ -265,29 +224,8 @@ export interface R3ComponentMetadata<DeclarationT extends R3TemplateDependency>
|
|||
|
||||
declarations: DeclarationT[];
|
||||
|
||||
/**
|
||||
* Map of all types that can be defer loaded (ts.ClassDeclaration) ->
|
||||
* corresponding import declaration (ts.ImportDeclaration) within
|
||||
* the current source file.
|
||||
*/
|
||||
deferrableDeclToImportDecl: Map<o.Expression, o.Expression>;
|
||||
|
||||
/**
|
||||
* Map of `@defer` blocks -> their corresponding metadata.
|
||||
*/
|
||||
deferBlocks: Map<t.DeferredBlock, R3DeferBlockMetadata>;
|
||||
|
||||
/**
|
||||
* Defines how dynamic imports for deferred dependencies should be grouped:
|
||||
* - either in a function on per-component basis (in case of local compilation)
|
||||
* - or in a function on per-block basis (in full compilation mode)
|
||||
*/
|
||||
deferBlockDepsEmitMode: DeferBlockDepsEmitMode;
|
||||
|
||||
/**
|
||||
* Map of deferrable symbol names -> corresponding import paths.
|
||||
*/
|
||||
deferrableTypes: Map<string, {importPath: string, isDefaultImport: boolean}>;
|
||||
/** Metadata related to the deferred blocks in the component's template. */
|
||||
defer: R3DeferMetadata;
|
||||
|
||||
/**
|
||||
* Specifies how the 'directives' and/or `pipes` array, if generated, need to be emitted.
|
||||
|
|
@ -356,6 +294,17 @@ export interface R3ComponentMetadata<DeclarationT extends R3TemplateDependency>
|
|||
useTemplatePipeline: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about the deferred blocks in a component's template.
|
||||
*/
|
||||
export type R3DeferMetadata = {
|
||||
mode: DeferBlockDepsEmitMode.PerBlock,
|
||||
blocks: Map<t.DeferredBlock, o.ArrowFunctionExpr|null>,
|
||||
}|{
|
||||
mode: DeferBlockDepsEmitMode.PerComponent,
|
||||
dependenciesFn: o.ArrowFunctionExpr | null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata for an individual input on a directive.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -159,34 +159,6 @@ export function compileDirectiveFromMetadata(
|
|||
return {expression, type, statements: []};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AST for a function that contains dynamic imports representing
|
||||
* deferrable dependencies.
|
||||
*/
|
||||
function createDeferredDepsFunction(
|
||||
constantPool: ConstantPool, name: string,
|
||||
deps: Map<string, {importPath: string, isDefaultImport: boolean}>) {
|
||||
// This defer block has deps for which we need to generate dynamic imports.
|
||||
const dependencyExp: o.Expression[] = [];
|
||||
|
||||
for (const [symbolName, {importPath, isDefaultImport}] of deps) {
|
||||
// Callback function, e.g. `m () => m.MyCmp;`.
|
||||
const innerFn = o.arrowFn(
|
||||
[new o.FnParam('m', o.DYNAMIC_TYPE)],
|
||||
o.variable('m').prop(isDefaultImport ? 'default' : symbolName));
|
||||
|
||||
// Dynamic import, e.g. `import('./a').then(...)`.
|
||||
const importExpr = (new o.DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
|
||||
dependencyExp.push(importExpr);
|
||||
}
|
||||
|
||||
const depsFnExpr = o.arrowFn([], o.literalArr(dependencyExp));
|
||||
|
||||
constantPool.statements.push(depsFnExpr.toDeclStmt(name, o.StmtModifier.Final));
|
||||
|
||||
return o.variable(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
|
||||
*/
|
||||
|
|
@ -219,10 +191,12 @@ export function compileComponentFromMetadata(
|
|||
|
||||
|
||||
let allDeferrableDepsFn: o.ReadVarExpr|null = null;
|
||||
if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
|
||||
meta.deferBlockDepsEmitMode === DeferBlockDepsEmitMode.PerComponent) {
|
||||
if (meta.defer.mode === DeferBlockDepsEmitMode.PerComponent &&
|
||||
meta.defer.dependenciesFn !== null) {
|
||||
const fnName = `${templateTypeName}_DeferFn`;
|
||||
allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
|
||||
constantPool.statements.push(
|
||||
meta.defer.dependenciesFn.toDeclStmt(fnName, o.StmtModifier.Final));
|
||||
allDeferrableDepsFn = o.variable(fnName);
|
||||
}
|
||||
|
||||
// Template compilation is currently conditional as we're in the process of rewriting it.
|
||||
|
|
@ -233,8 +207,7 @@ export function compileComponentFromMetadata(
|
|||
const template = meta.template;
|
||||
const templateBuilder = new TemplateDefinitionBuilder(
|
||||
constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName,
|
||||
R3.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks,
|
||||
new Map(), allDeferrableDepsFn);
|
||||
R3.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds, new Map());
|
||||
|
||||
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
||||
|
||||
|
|
@ -276,7 +249,7 @@ export function compileComponentFromMetadata(
|
|||
// ingested into IR:
|
||||
const tpl = ingestComponent(
|
||||
meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath,
|
||||
meta.i18nUseExternalIds, meta.deferBlocks, allDeferrableDepsFn);
|
||||
meta.i18nUseExternalIds, meta.defer, allDeferrableDepsFn);
|
||||
|
||||
// Then the IR is transformed to prepare it for cod egeneration.
|
||||
transform(tpl, CompilationJobKind.Tmpl);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import {ParseError, ParseSourceSpan, sanitizeIdentifier} from '../../parse_util'
|
|||
import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry';
|
||||
import {isIframeSecuritySensitiveAttr} from '../../schema/dom_security_schema';
|
||||
import {isTrustedTypesSink} from '../../schema/trusted_types_sinks';
|
||||
import {CssSelector} from '../../selector';
|
||||
import {BindingParser} from '../../template_parser/binding_parser';
|
||||
import {error, partitionArray} from '../../util';
|
||||
import * as t from '../r3_ast';
|
||||
|
|
@ -33,7 +32,6 @@ import {Identifiers as R3} from '../r3_identifiers';
|
|||
import {htmlAstToRender3Ast} from '../r3_template_transform';
|
||||
import {prepareSyntheticListenerFunctionName, prepareSyntheticListenerName, prepareSyntheticPropertyName} from '../util';
|
||||
|
||||
import {R3DeferBlockMetadata} from './api';
|
||||
import {I18nContext} from './i18n/context';
|
||||
import {createGoogleGetMsgStatements} from './i18n/get_msg_utils';
|
||||
import {createLocalizeStatements} from './i18n/localize_utils';
|
||||
|
|
@ -250,9 +248,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
private templateIndex: number|null, private templateName: string|null,
|
||||
private _namespace: o.ExternalReference, relativeContextFilePath: string,
|
||||
private i18nUseExternalIds: boolean,
|
||||
private deferBlocks: Map<t.DeferredBlock, R3DeferBlockMetadata>,
|
||||
private elementLocations: Map<t.Element, {index: number, level: number}>,
|
||||
private allDeferrableDepsFn: o.ReadVarExpr|null,
|
||||
private _constants: ComponentDefConsts = createComponentDefConsts()) {
|
||||
this._bindingScope = parentBindingScope.nestedScope(level);
|
||||
|
||||
|
|
@ -972,8 +968,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
// Create the template function
|
||||
const visitor = new TemplateDefinitionBuilder(
|
||||
this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n, index, name,
|
||||
this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this.deferBlocks,
|
||||
this.elementLocations, this.allDeferrableDepsFn, this._constants);
|
||||
this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this.elementLocations,
|
||||
this._constants);
|
||||
|
||||
// Nested templates must not be visited until after their parent templates have completed
|
||||
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
|
||||
|
|
@ -1327,192 +1323,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
}
|
||||
|
||||
visitDeferredBlock(deferred: t.DeferredBlock): void {
|
||||
const {loading, placeholder, error, triggers, prefetchTriggers} = deferred;
|
||||
const metadata = this.deferBlocks.get(deferred);
|
||||
|
||||
if (!metadata) {
|
||||
throw new Error('Could not resolve `defer` block metadata. Block may need to be analyzed.');
|
||||
}
|
||||
|
||||
const primaryTemplateIndex = this.createEmbeddedTemplateFn(
|
||||
null, deferred.children, '_Defer', deferred.sourceSpan, undefined, undefined, undefined,
|
||||
deferred.i18n);
|
||||
const loadingIndex = loading ? this.createEmbeddedTemplateFn(
|
||||
null, loading.children, '_DeferLoading', loading.sourceSpan,
|
||||
undefined, undefined, undefined, loading.i18n) :
|
||||
null;
|
||||
const loadingConsts = loading ?
|
||||
trimTrailingNulls([o.literal(loading.minimumTime), o.literal(loading.afterTime)]) :
|
||||
null;
|
||||
|
||||
const placeholderIndex = placeholder ?
|
||||
this.createEmbeddedTemplateFn(
|
||||
null, placeholder.children, '_DeferPlaceholder', placeholder.sourceSpan, undefined,
|
||||
undefined, undefined, placeholder.i18n) :
|
||||
null;
|
||||
const placeholderConsts = placeholder && placeholder.minimumTime !== null ?
|
||||
// TODO(crisbeto): potentially pass the time directly instead of storing it in the `consts`
|
||||
// since the placeholder block can only have one parameter?
|
||||
o.literalArr([o.literal(placeholder.minimumTime)]) :
|
||||
null;
|
||||
|
||||
const errorIndex = error ? this.createEmbeddedTemplateFn(
|
||||
null, error.children, '_DeferError', error.sourceSpan, undefined,
|
||||
undefined, undefined, error.i18n) :
|
||||
null;
|
||||
|
||||
// Note: we generate this last so the index matches the instruction order.
|
||||
const deferredIndex = this.allocateDataSlot();
|
||||
const depsFnName = `${this.contextName}_Defer_${deferredIndex}_DepsFn`;
|
||||
|
||||
// e.g. `defer(1, 0, MyComp_Defer_1_DepsFn, ...)`
|
||||
this.creationInstruction(
|
||||
deferred.sourceSpan, R3.defer, trimTrailingNulls([
|
||||
o.literal(deferredIndex),
|
||||
o.literal(primaryTemplateIndex),
|
||||
this.allDeferrableDepsFn ?? this.createDeferredDepsFunction(depsFnName, metadata),
|
||||
o.literal(loadingIndex),
|
||||
o.literal(placeholderIndex),
|
||||
o.literal(errorIndex),
|
||||
loadingConsts?.length ? this.addToConsts(o.literalArr(loadingConsts)) : o.TYPED_NULL_EXPR,
|
||||
placeholderConsts ? this.addToConsts(placeholderConsts) : o.TYPED_NULL_EXPR,
|
||||
(loadingConsts?.length || placeholderConsts) ?
|
||||
o.importExpr(R3.deferEnableTimerScheduling) :
|
||||
o.TYPED_NULL_EXPR,
|
||||
]));
|
||||
|
||||
// Allocate an extra data slot right after a defer block slot to store
|
||||
// instance-specific state of that defer block at runtime.
|
||||
this.allocateDataSlot();
|
||||
|
||||
// Note: the triggers need to be processed *after* the various templates,
|
||||
// otherwise pipes injecting some symbols won't work (see #52102).
|
||||
this.createDeferTriggerInstructions(deferredIndex, triggers, metadata, false);
|
||||
this.createDeferTriggerInstructions(deferredIndex, prefetchTriggers, metadata, true);
|
||||
}
|
||||
|
||||
private createDeferredDepsFunction(name: string, metadata: R3DeferBlockMetadata) {
|
||||
if (metadata.deps.length === 0) {
|
||||
return o.TYPED_NULL_EXPR;
|
||||
}
|
||||
|
||||
// This defer block has deps for which we need to generate dynamic imports.
|
||||
const dependencyExp: o.Expression[] = [];
|
||||
|
||||
for (const deferredDep of metadata.deps) {
|
||||
if (deferredDep.isDeferrable) {
|
||||
// Callback function, e.g. `m () => m.MyCmp;`.
|
||||
const innerFn = o.arrowFn(
|
||||
[new o.FnParam('m', o.DYNAMIC_TYPE)],
|
||||
// Default imports are always accessed through the `default` property.
|
||||
o.variable('m').prop(deferredDep.isDefaultImport ? 'default' : deferredDep.symbolName));
|
||||
|
||||
// Dynamic import, e.g. `import('./a').then(...)`.
|
||||
const importExpr =
|
||||
(new o.DynamicImportExpr(deferredDep.importPath!)).prop('then').callFn([innerFn]);
|
||||
dependencyExp.push(importExpr);
|
||||
} else {
|
||||
// Non-deferrable symbol, just use a reference to the type.
|
||||
dependencyExp.push(deferredDep.type);
|
||||
}
|
||||
}
|
||||
|
||||
const depsFnExpr = o.arrowFn([], o.literalArr(dependencyExp));
|
||||
|
||||
this.constantPool.statements.push(depsFnExpr.toDeclStmt(name, o.StmtModifier.Final));
|
||||
|
||||
return o.variable(name);
|
||||
}
|
||||
|
||||
private createDeferTriggerInstructions(
|
||||
deferredIndex: number, triggers: t.DeferredBlockTriggers, metadata: R3DeferBlockMetadata,
|
||||
prefetch: boolean) {
|
||||
const {when, idle, immediate, timer, hover, interaction, viewport} = triggers;
|
||||
|
||||
// `deferWhen(ctx.someValue)`
|
||||
if (when) {
|
||||
const value = when.value.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstructionWithAdvance(
|
||||
deferredIndex, when.sourceSpan, prefetch ? R3.deferPrefetchWhen : R3.deferWhen,
|
||||
() => this.convertPropertyBinding(value));
|
||||
}
|
||||
|
||||
// Note that we generate an implicit `on idle` if the `deferred` block has no triggers.
|
||||
// `deferOnIdle()`
|
||||
if (idle || (!prefetch && Object.keys(triggers).length === 0)) {
|
||||
this.creationInstruction(
|
||||
idle?.sourceSpan || null, prefetch ? R3.deferPrefetchOnIdle : R3.deferOnIdle);
|
||||
}
|
||||
|
||||
// `deferOnImmediate()`
|
||||
if (immediate) {
|
||||
this.creationInstruction(
|
||||
immediate.sourceSpan, prefetch ? R3.deferPrefetchOnImmediate : R3.deferOnImmediate);
|
||||
}
|
||||
|
||||
// `deferOnTimer(1337)`
|
||||
if (timer) {
|
||||
this.creationInstruction(
|
||||
timer.sourceSpan, prefetch ? R3.deferPrefetchOnTimer : R3.deferOnTimer,
|
||||
[o.literal(timer.delay)]);
|
||||
}
|
||||
|
||||
// `deferOnHover(index, walkUpTimes)`
|
||||
if (hover) {
|
||||
this.domNodeBasedTrigger(
|
||||
'hover', hover, metadata, prefetch ? R3.deferPrefetchOnHover : R3.deferOnHover);
|
||||
}
|
||||
|
||||
// `deferOnInteraction(index, walkUpTimes)`
|
||||
if (interaction) {
|
||||
this.domNodeBasedTrigger(
|
||||
'interaction', interaction, metadata,
|
||||
prefetch ? R3.deferPrefetchOnInteraction : R3.deferOnInteraction);
|
||||
}
|
||||
|
||||
// `deferOnViewport(index, walkUpTimes)`
|
||||
if (viewport) {
|
||||
this.domNodeBasedTrigger(
|
||||
'viewport', viewport, metadata,
|
||||
prefetch ? R3.deferPrefetchOnViewport : R3.deferOnViewport);
|
||||
}
|
||||
}
|
||||
|
||||
private domNodeBasedTrigger(
|
||||
name: string,
|
||||
trigger: t.InteractionDeferredTrigger|t.HoverDeferredTrigger|t.ViewportDeferredTrigger,
|
||||
metadata: R3DeferBlockMetadata, instructionRef: o.ExternalReference) {
|
||||
const triggerEl = metadata.triggerElements.get(trigger);
|
||||
|
||||
// Don't generate anything if a trigger cannot be resolved.
|
||||
// We'll have template diagnostics to surface these to users.
|
||||
if (!triggerEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.creationInstruction(trigger.sourceSpan, instructionRef, () => {
|
||||
const location = this.elementLocations.get(triggerEl);
|
||||
|
||||
if (!location) {
|
||||
throw new Error(
|
||||
`Could not determine location of reference passed into ` +
|
||||
`'${name}' trigger. Template may not have been fully analyzed.`);
|
||||
}
|
||||
|
||||
// A negative depth means that the trigger is inside the placeholder.
|
||||
// Cap it at -1 since we only care whether or not it's negative.
|
||||
const depth = Math.max(this.level - location.level, -1);
|
||||
const params = [o.literal(location.index)];
|
||||
|
||||
// The most common case should be a trigger within the view so we can omit a depth of
|
||||
// zero. For triggers in parent views and in the placeholder we need to pass it in.
|
||||
if (depth !== 0) {
|
||||
params.push(o.literal(depth));
|
||||
}
|
||||
|
||||
return params;
|
||||
});
|
||||
throw new Error(
|
||||
'Deferred blocks are no longer supported by the obsolete TemplateDefinitionBuilder, ' +
|
||||
'Please enable the new template compilation pipeline for your application.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {SecurityContext} from '../../../../../core';
|
|||
import * as i18n from '../../../../../i18n/i18n_ast';
|
||||
import * as o from '../../../../../output/output_ast';
|
||||
import {ParseSourceSpan} from '../../../../../parse_util';
|
||||
import {R3DeferBlockMetadata} from '../../../../../render3/view/api';
|
||||
import {BindingKind, DeferTriggerKind, I18nContextKind, I18nParamValueFlags, Namespace, OpKind, TemplateKind} from '../enums';
|
||||
import {SlotHandle} from '../handle';
|
||||
import {Op, OpList, XrefId} from '../operations';
|
||||
|
|
@ -838,9 +837,11 @@ export interface DeferOp extends Op<CreateOp>, ConsumesSlotOpTrait {
|
|||
loadingConfig: o.Expression|null;
|
||||
|
||||
/**
|
||||
* Metadata about this defer block, provided by the parser.
|
||||
* Depending on the compilation mode, there can be either one dependency resolution function
|
||||
* per deferred block or one for the entire template. This field contains the function that
|
||||
* belongs specifically to the current deferred block.
|
||||
*/
|
||||
metadata: R3DeferBlockMetadata;
|
||||
ownResolverFn: o.ArrowFunctionExpr|null;
|
||||
|
||||
/**
|
||||
* After processing, the resolver function for the defer deps will be extracted to the constant
|
||||
|
|
@ -852,7 +853,7 @@ export interface DeferOp extends Op<CreateOp>, ConsumesSlotOpTrait {
|
|||
}
|
||||
|
||||
export function createDeferOp(
|
||||
xref: XrefId, main: XrefId, mainSlot: SlotHandle, metadata: R3DeferBlockMetadata,
|
||||
xref: XrefId, main: XrefId, mainSlot: SlotHandle, ownResolverFn: o.ArrowFunctionExpr|null,
|
||||
resolverFn: o.Expression|null, sourceSpan: ParseSourceSpan): DeferOp {
|
||||
return {
|
||||
kind: OpKind.Defer,
|
||||
|
|
@ -871,7 +872,7 @@ export function createDeferOp(
|
|||
placeholderMinimumTime: null,
|
||||
errorView: null,
|
||||
errorSlot: null,
|
||||
metadata,
|
||||
ownResolverFn,
|
||||
resolverFn,
|
||||
sourceSpan,
|
||||
...NEW_OP,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
import {ConstantPool} from '../../../constant_pool';
|
||||
import * as o from '../../../output/output_ast';
|
||||
import * as t from '../../../render3/r3_ast';
|
||||
import {R3DeferBlockMetadata} from '../../../render3/view/api';
|
||||
import {R3DeferMetadata} from '../../../render3/view/api';
|
||||
import * as ir from '../ir';
|
||||
|
||||
export enum CompilationJobKind {
|
||||
|
|
@ -67,8 +66,7 @@ export class ComponentCompilationJob extends CompilationJob {
|
|||
constructor(
|
||||
componentName: string, pool: ConstantPool, compatibility: ir.CompatibilityMode,
|
||||
readonly relativeContextFilePath: string, readonly i18nUseExternalIds: boolean,
|
||||
readonly deferBlocksMeta: Map<t.DeferredBlock, R3DeferBlockMetadata>,
|
||||
readonly allDeferrableDepsFn: o.ReadVarExpr|null) {
|
||||
readonly deferMeta: R3DeferMetadata, readonly allDeferrableDepsFn: o.ReadVarExpr|null) {
|
||||
super(componentName, pool, compatibility);
|
||||
this.root = new ViewCompilationUnit(this, this.allocateXrefId(), null);
|
||||
this.views.set(this.root.xref, this.root);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import {collapseSingletonInterpolations} from './phases/collapse_singleton_inter
|
|||
import {generateConditionalExpressions} from './phases/conditionals';
|
||||
import {collectElementConsts} from './phases/const_collection';
|
||||
import {convertI18nBindings} from './phases/convert_i18n_bindings';
|
||||
import {createDeferDepsFns} from './phases/create_defer_deps_fns';
|
||||
import {resolveDeferDepsFns} from './phases/resolve_defer_deps_fns';
|
||||
import {createI18nContexts} from './phases/create_i18n_contexts';
|
||||
import {deduplicateTextBindings} from './phases/deduplicate_text_bindings';
|
||||
import {configureDeferInstructions} from './phases/defer_configs';
|
||||
|
|
@ -140,7 +140,7 @@ const phases: Phase[] = [
|
|||
{kind: Kind.Tmpl, fn: generateAdvance},
|
||||
{kind: Kind.Both, fn: optimizeVariables},
|
||||
{kind: Kind.Both, fn: nameFunctionsAndVariables},
|
||||
{kind: Kind.Tmpl, fn: createDeferDepsFns},
|
||||
{kind: Kind.Tmpl, fn: resolveDeferDepsFns},
|
||||
{kind: Kind.Tmpl, fn: mergeNextContextExpressions},
|
||||
{kind: Kind.Tmpl, fn: generateNgContainerOps},
|
||||
{kind: Kind.Tmpl, fn: collapseEmptyInstructions},
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {splitNsName} from '../../../ml_parser/tags';
|
|||
import * as o from '../../../output/output_ast';
|
||||
import {ParseSourceSpan} from '../../../parse_util';
|
||||
import * as t from '../../../render3/r3_ast';
|
||||
import {R3DeferBlockMetadata} from '../../../render3/view/api';
|
||||
import {DeferBlockDepsEmitMode, R3DeferMetadata} from '../../../render3/view/api';
|
||||
import {icuFromI18nMessage, isSingleI18nIcu} from '../../../render3/view/i18n/util';
|
||||
import {DomElementSchemaRegistry} from '../../../schema/dom_element_schema_registry';
|
||||
import {BindingParser} from '../../../template_parser/binding_parser';
|
||||
|
|
@ -38,12 +38,11 @@ const NG_TEMPLATE_TAG_NAME = 'ng-template';
|
|||
*/
|
||||
export function ingestComponent(
|
||||
componentName: string, template: t.Node[], constantPool: ConstantPool,
|
||||
relativeContextFilePath: string, i18nUseExternalIds: boolean,
|
||||
deferBlocksMeta: Map<t.DeferredBlock, R3DeferBlockMetadata>,
|
||||
relativeContextFilePath: string, i18nUseExternalIds: boolean, deferMeta: R3DeferMetadata,
|
||||
allDeferrableDepsFn: o.ReadVarExpr|null): ComponentCompilationJob {
|
||||
const job = new ComponentCompilationJob(
|
||||
componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds,
|
||||
deferBlocksMeta, allDeferrableDepsFn);
|
||||
deferMeta, allDeferrableDepsFn);
|
||||
ingestNodes(job.root, template);
|
||||
return job;
|
||||
}
|
||||
|
|
@ -443,9 +442,14 @@ function ingestDeferView(
|
|||
}
|
||||
|
||||
function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock): void {
|
||||
const blockMeta = unit.job.deferBlocksMeta.get(deferBlock);
|
||||
if (blockMeta === undefined) {
|
||||
throw new Error(`AssertionError: unable to find metadata for deferred block`);
|
||||
let ownResolverFn: o.ArrowFunctionExpr|null = null;
|
||||
|
||||
if (unit.job.deferMeta.mode === DeferBlockDepsEmitMode.PerBlock) {
|
||||
if (!unit.job.deferMeta.blocks.has(deferBlock)) {
|
||||
throw new Error(
|
||||
`AssertionError: unable to find a dependency function for this deferred block`);
|
||||
}
|
||||
ownResolverFn = unit.job.deferMeta.blocks.get(deferBlock) ?? null;
|
||||
}
|
||||
|
||||
// Generate the defer main view and all secondary views.
|
||||
|
|
@ -464,7 +468,7 @@ function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock
|
|||
// Create the main defer op, and ops for all secondary views.
|
||||
const deferXref = unit.job.allocateXrefId();
|
||||
const deferOp = ir.createDeferOp(
|
||||
deferXref, main.xref, main.handle, blockMeta, unit.job.allDeferrableDepsFn,
|
||||
deferXref, main.xref, main.handle, ownResolverFn, unit.job.allDeferrableDepsFn,
|
||||
deferBlock.sourceSpan);
|
||||
deferOp.placeholderView = placeholder?.xref ?? null;
|
||||
deferOp.placeholderSlot = placeholder?.handle ?? null;
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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 * as o from '../../../../output/output_ast';
|
||||
import * as ir from '../../ir';
|
||||
import {ComponentCompilationJob} from '../compilation';
|
||||
|
||||
/**
|
||||
* Create extracted deps functions for defer ops.
|
||||
*/
|
||||
export function createDeferDepsFns(job: ComponentCompilationJob): void {
|
||||
for (const unit of job.units) {
|
||||
for (const op of unit.create) {
|
||||
if (op.kind === ir.OpKind.Defer) {
|
||||
if (op.metadata.deps.length === 0) {
|
||||
continue;
|
||||
}
|
||||
if (op.resolverFn !== null) {
|
||||
continue;
|
||||
}
|
||||
const dependencies: o.Expression[] = [];
|
||||
for (const dep of op.metadata.deps) {
|
||||
if (dep.isDeferrable) {
|
||||
// Callback function, e.g. `m () => m.MyCmp;`.
|
||||
const innerFn = o.arrowFn(
|
||||
// Default imports are always accessed through the `default` property.
|
||||
[new o.FnParam('m', o.DYNAMIC_TYPE)],
|
||||
o.variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName));
|
||||
|
||||
// Dynamic import, e.g. `import('./a').then(...)`.
|
||||
const importExpr =
|
||||
(new o.DynamicImportExpr(dep.importPath!)).prop('then').callFn([innerFn]);
|
||||
dependencies.push(importExpr);
|
||||
} else {
|
||||
// Non-deferrable symbol, just use a reference to the type.
|
||||
dependencies.push(dep.type);
|
||||
}
|
||||
}
|
||||
const depsFnExpr = o.arrowFn([], o.literalArr(dependencies));
|
||||
if (op.handle.slot === null) {
|
||||
throw new Error(
|
||||
'AssertionError: slot must be assigned bfore extracting defer deps functions');
|
||||
}
|
||||
const fullPathName = unit.fnName?.replace(`_Template`, ``);
|
||||
op.resolverFn = job.pool.getSharedFunctionReference(
|
||||
depsFnExpr, `${fullPathName}_Defer_${op.handle.slot}_DepsFn`,
|
||||
/* Don't use unique names for TDB compatibility */ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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 * as ir from '../../ir';
|
||||
import {ComponentCompilationJob} from '../compilation';
|
||||
|
||||
/**
|
||||
* Resolve the dependency function of a deferred block.
|
||||
*/
|
||||
export function resolveDeferDepsFns(job: ComponentCompilationJob): void {
|
||||
for (const unit of job.units) {
|
||||
for (const op of unit.create) {
|
||||
if (op.kind === ir.OpKind.Defer) {
|
||||
if (op.resolverFn !== null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op.ownResolverFn !== null) {
|
||||
if (op.handle.slot === null) {
|
||||
throw new Error(
|
||||
'AssertionError: slot must be assigned before extracting defer deps functions');
|
||||
}
|
||||
const fullPathName = unit.fnName?.replace('_Template', '');
|
||||
op.resolverFn = job.pool.getSharedFunctionReference(
|
||||
op.ownResolverFn, `${fullPathName}_Defer_${op.handle.slot}_DepsFn`,
|
||||
/* Don't use unique names for TDB compatibility */ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue