Skip to content

Commit

Permalink
refactor(core): introduce API for declaring asynchronous partial meta…
Browse files Browse the repository at this point in the history
…data (#54908)

Adds the `ɵɵngDeclareClassMetadataAsync` function that will be produced during partial compilation for component classes that have deferred dependencies. At runtime the dependencies will be resolved before setting the metadata.

PR Close #54908
  • Loading branch information
crisbeto authored and dylhunn committed Mar 22, 2024
1 parent ffa217e commit 84410f5
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/compiler/src/compiler_facade_interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ export interface R3DeclareComponentFacade extends R3DeclareDirectiveFacade {
pipes?: {[pipeName: string]: OpaqueValue|(() => OpaqueValue)};


deferBlockDependencies?: (() => Promise<Type>)[];
viewProviders?: OpaqueValue;
animations?: OpaqueValue;
changeDetection?: ChangeDetectionStrategy;
Expand Down
20 changes: 11 additions & 9 deletions packages/compiler/src/jit_compiler_facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
// Parse the template and check for errors.
const {template, interpolation, defer} = parseJitTemplate(
facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces,
facade.interpolation);
facade.interpolation, undefined);

// Compile the component metadata, including template, into an expression.
const meta: R3ComponentMetadata<R3TemplateDependency> = {
Expand Down Expand Up @@ -442,7 +442,7 @@ function convertDeclareComponentFacadeToMetadata(
sourceMapUrl: string): R3ComponentMetadata<R3TemplateDependencyMetadata> {
const {template, interpolation, defer} = parseJitTemplate(
decl.template, decl.type.name, sourceMapUrl, decl.preserveWhitespaces ?? false,
decl.interpolation);
decl.interpolation, decl.deferBlockDependencies);

const declarations: R3TemplateDependencyMetadata[] = [];
if (decl.dependencies) {
Expand Down Expand Up @@ -536,7 +536,8 @@ function convertPipeDeclarationToMetadata(pipe: R3DeclarePipeDependencyFacade):

function parseJitTemplate(
template: string, typeName: string, sourceMapUrl: string, preserveWhitespaces: boolean,
interpolation: [string, string]|undefined) {
interpolation: [string, string]|undefined,
deferBlockDependencies: (() => Promise<unknown>)[]|undefined) {
const interpolationConfig =
interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG;
// Parse the template and check for errors.
Expand All @@ -551,7 +552,7 @@ function parseJitTemplate(
return {
template: parsed,
interpolation: interpolationConfig,
defer: createR3ComponentDeferMetadata(boundTarget)
defer: createR3ComponentDeferMetadata(boundTarget, deferBlockDependencies)
};
}

Expand Down Expand Up @@ -622,14 +623,15 @@ function createR3DependencyMetadata(
return {token, attributeNameType, host, optional, self, skipSelf};
}

function createR3ComponentDeferMetadata(boundTarget: BoundTarget<any>): R3ComponentDeferMetadata {
function createR3ComponentDeferMetadata(
boundTarget: BoundTarget<any>,
deferBlockDependencies: (() => Promise<unknown>)[]|undefined): R3ComponentDeferMetadata {
const deferredBlocks = boundTarget.getDeferBlocks();
const blocks = new Map<DeferredBlock, Expression|null>();

for (const block of deferredBlocks) {
// TODO: leaving dependency function empty in JIT mode for now,
// to be implemented as one of the next steps.
blocks.set(block, null);
for (let i = 0; i < deferredBlocks.length; i++) {
const dependencyFn = deferBlockDependencies?.[i];
blocks.set(deferredBlocks[i], dependencyFn ? new WrappedNodeExpr(dependencyFn) : null);
}

return {mode: DeferBlockDepsEmitMode.PerBlock, blocks};
Expand Down
23 changes: 23 additions & 0 deletions packages/compiler/src/render3/partial/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata {
*/
dependencies?: R3DeclareTemplateDependencyMetadata[];

/**
* List of defer block dependency functions, ordered by the appearance
* of the corresponding deferred block in the template.
*/
deferBlockDependencies?: o.Expression[];

/**
* A map of pipe names to an expression referencing the pipe type (possibly a forward reference
* wrapped in a `forwardRef` invocation) which are used in the template.
Expand Down Expand Up @@ -557,6 +563,23 @@ export interface R3DeclareClassMetadata extends R3PartialDeclaration {
propDecorators?: o.Expression;
}

/**
* Describes the shape of the object that the `ɵɵngDeclareClassMetadataAsync()` function accepts.
*
* This interface serves primarily as documentation, as conformance to this interface is not
* enforced during linking.
*/
export interface R3DeclareClassMetadataAsync extends R3PartialDeclaration {
/** Function that loads the deferred dependencies associated with the component. */
resolveDeferredDeps: o.Expression;

/**
* Function that, when invoked with the resolved deferred
* dependencies, will return the class metadata.
*/
resolveMetadata: o.Expression;
}

/**
* Describes the shape of the object literal that can be
* passed in as a part of the `hostDirectives` array.
Expand Down
2 changes: 2 additions & 0 deletions packages/compiler/src/render3/r3_identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ export class Identifiers {

static declareClassMetadata:
o.ExternalReference = {name: 'ɵɵngDeclareClassMetadata', moduleName: CORE};
static declareClassMetadataAsync:
o.ExternalReference = {name: 'ɵɵngDeclareClassMetadataAsync', moduleName: CORE};
static setClassMetadata: o.ExternalReference = {name: 'ɵsetClassMetadata', moduleName: CORE};
static setClassMetadataAsync:
o.ExternalReference = {name: 'ɵsetClassMetadataAsync', moduleName: CORE};
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/compiler/compiler_facade_interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ export interface R3DeclareComponentFacade extends R3DeclareDirectiveFacade {
pipes?: {[pipeName: string]: OpaqueValue|(() => OpaqueValue)};


deferBlockDependencies?: (() => Promise<Type>)[];
viewProviders?: OpaqueValue;
animations?: OpaqueValue;
changeDetection?: ChangeDetectionStrategy;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/core_render3_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ export {
export {
FactoryTarget as ɵɵFactoryTarget,
ɵɵngDeclareClassMetadata,
ɵɵngDeclareClassMetadataAsync,
ɵɵngDeclareComponent,
ɵɵngDeclareDirective,
ɵɵngDeclareFactory,
Expand Down
23 changes: 22 additions & 1 deletion packages/core/src/render3/jit/partial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

import {FactoryTarget, getCompilerFacade, JitCompilerUsage, R3DeclareComponentFacade, R3DeclareDirectiveFacade, R3DeclareFactoryFacade, R3DeclareInjectableFacade, R3DeclareInjectorFacade, R3DeclareNgModuleFacade, R3DeclarePipeFacade} from '../../compiler/compiler_facade';
import {Type} from '../../interface/type';
import {setClassMetadata} from '../metadata';
import {setClassMetadata, setClassMetadataAsync} from '../metadata';

import {angularCoreEnv} from './environment';

/**
Expand Down Expand Up @@ -37,6 +38,26 @@ export function ɵɵngDeclareClassMetadata(decl: {
decl.type, decl.decorators, decl.ctorParameters ?? null, decl.propDecorators ?? null);
}

/**
* Evaluates the class metadata of a component that contains deferred blocks.
*
* @codeGenApi
*/
export function ɵɵngDeclareClassMetadataAsync(decl: {
type: Type<any>,
resolveDeferredDeps: () => Promise<Type<unknown>>[],
resolveMetadata: (...types: Type<unknown>[]) => {
decorators: any[];
ctorParameters: (() => any[])|null;
propDecorators: ({[field: string]: any})|null;
},
}): void {
setClassMetadataAsync(decl.type, decl.resolveDeferredDeps, (...types: Type<unknown>[]) => {
const meta = decl.resolveMetadata(...types);
setClassMetadata(decl.type, meta.decorators, meta.ctorParameters, meta.propDecorators);
});
}

/**
* Compiles a partial component declaration object into a full component definition object.
*
Expand Down

0 comments on commit 84410f5

Please sign in to comment.