refactor(compiler-cli): separate used components from used directives in partial declaration (#41104)

The partial declaration of a component includes the list of directives
that are used in its template, including some metadata of the directive
which can be used during actual compilation of the component. Used
components are currently part of this list, as components are also
directives. This commit splits the used components into a dedicate
property in the partial declaration, which allows for template
compilation to optimize the generated code for components.

PR Close #41104
This commit is contained in:
JoostK 2021-03-06 15:50:37 +01:00 committed by Jessica Janiuk
parent 98de4cb1bf
commit eb74a96935
13 changed files with 103 additions and 77 deletions

View File

@ -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 {compileComponentFromMetadata, ConstantPool, DeclarationListEmitMode, DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig, makeBindingParser, parseTemplate, R3ComponentMetadata, R3DeclareComponentMetadata, R3PartialDeclaration, R3UsedDirectiveMetadata} from '@angular/compiler';
import {compileComponentFromMetadata, ConstantPool, DeclarationListEmitMode, DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig, makeBindingParser, parseTemplate, R3ComponentMetadata, R3DeclareComponentMetadata, R3DeclareUsedDirectiveMetadata, R3PartialDeclaration, R3UsedDirectiveMetadata} from '@angular/compiler';
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/compiler/src/core';
import * as o from '@angular/compiler/src/output/output_ast';
@ -74,34 +74,42 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
let declarationListEmitMode = DeclarationListEmitMode.Direct;
let directives: R3UsedDirectiveMetadata[] = [];
if (metaObj.has('directives')) {
directives = metaObj.getArray('directives').map(directive => {
const directiveExpr = directive.getObject();
const type = directiveExpr.getValue('type');
const selector = directiveExpr.getString('selector');
const collectUsedDirectives =
(directives: AstValue<R3DeclareUsedDirectiveMetadata, TExpression>[]) => {
return directives.map(directive => {
const directiveExpr = directive.getObject();
const type = directiveExpr.getValue('type');
const selector = directiveExpr.getString('selector');
let typeExpr = type.getOpaque();
const forwardRefType = extractForwardRef(type);
if (forwardRefType !== null) {
typeExpr = forwardRefType;
declarationListEmitMode = DeclarationListEmitMode.Closure;
}
let typeExpr = type.getOpaque();
const forwardRefType = extractForwardRef(type);
if (forwardRefType !== null) {
typeExpr = forwardRefType;
declarationListEmitMode = DeclarationListEmitMode.Closure;
}
return {
type: typeExpr,
selector: selector,
inputs: directiveExpr.has('inputs') ?
directiveExpr.getArray('inputs').map(input => input.getString()) :
[],
outputs: directiveExpr.has('outputs') ?
directiveExpr.getArray('outputs').map(input => input.getString()) :
[],
exportAs: directiveExpr.has('exportAs') ?
directiveExpr.getArray('exportAs').map(exportAs => exportAs.getString()) :
null,
return {
type: typeExpr,
selector: selector,
inputs: directiveExpr.has('inputs') ?
directiveExpr.getArray('inputs').map(input => input.getString()) :
[],
outputs: directiveExpr.has('outputs') ?
directiveExpr.getArray('outputs').map(input => input.getString()) :
[],
exportAs: directiveExpr.has('exportAs') ?
directiveExpr.getArray('exportAs').map(exportAs => exportAs.getString()) :
null,
};
});
};
});
let directives: R3UsedDirectiveMetadata[] = [];
if (metaObj.has('components')) {
directives.push(...collectUsedDirectives(metaObj.getArray('components')));
}
if (metaObj.has('directives')) {
directives.push(...collectUsedDirectives(metaObj.getArray('directives')));
}
let pipes = new Map<string, o.Expression>();

View File

@ -181,7 +181,7 @@ SomeComp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type:
export class MyApp {
}
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: '<some-comp [prop]="{}" [otherProp]="{a: 1, b: 2}"></some-comp>', isInline: true, directives: [{ type: SomeComp, selector: "some-comp", inputs: ["prop", "otherProp"] }] });
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: '<some-comp [prop]="{}" [otherProp]="{a: 1, b: 2}"></some-comp>', isInline: true, components: [{ type: SomeComp, selector: "some-comp", inputs: ["prop", "otherProp"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{ template: '<some-comp [prop]="{}" [otherProp]="{a: 1, b: 2}"></some-comp>' }]
@ -236,7 +236,7 @@ SomeComp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type:
export class MyApp {
}
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: '<some-comp [prop]="[]" [otherProp]="[0, 1, 2]"></some-comp>', isInline: true, directives: [{ type: SomeComp, selector: "some-comp", inputs: ["prop", "otherProp"] }] });
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: '<some-comp [prop]="[]" [otherProp]="[0, 1, 2]"></some-comp>', isInline: true, components: [{ type: SomeComp, selector: "some-comp", inputs: ["prop", "otherProp"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{ template: '<some-comp [prop]="[]" [otherProp]="[0, 1, 2]"></some-comp>' }]

View File

@ -29,7 +29,7 @@ ComplexComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER
export class MyApp {
}
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: '<simple>content</simple> <complex></complex>', isInline: true, directives: [{ type: SimpleComponent, selector: "simple" }, { type: ComplexComponent, selector: "complex" }] });
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: '<simple>content</simple> <complex></complex>', isInline: true, components: [{ type: SimpleComponent, selector: "simple" }, { type: ComplexComponent, selector: "complex" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{ selector: 'my-app', template: '<simple>content</simple> <complex></complex>' }]
@ -224,7 +224,7 @@ SimpleComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER"
export class MyApp {
}
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: '<simple><h1 ngProjectAs="[title]"></h1></simple>', isInline: true, directives: [{ type: SimpleComponent, selector: "simple" }] });
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: '<simple><h1 ngProjectAs="[title]"></h1></simple>', isInline: true, components: [{ type: SimpleComponent, selector: "simple" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{ selector: 'my-app', template: '<simple><h1 ngProjectAs="[title]"></h1></simple>' }]
@ -273,7 +273,7 @@ SimpleComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER"
export class MyApp {
}
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: '<simple><h1 ngProjectAs="[title],[header]"></h1></simple>', isInline: true, directives: [{ type: SimpleComponent, selector: "simple" }] });
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: '<simple><h1 ngProjectAs="[title],[header]"></h1></simple>', isInline: true, components: [{ type: SimpleComponent, selector: "simple" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{ selector: 'my-app', template: '<simple><h1 ngProjectAs="[title],[header]"></h1></simple>' }]

View File

@ -211,7 +211,7 @@ SimpleLayout.ɵfac = function SimpleLayout_Factory(t) { return new (t || SimpleL
SimpleLayout.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: SimpleLayout, selector: "simple-layout", ngImport: i0, template: `
<lifecycle-comp [name]="name1"></lifecycle-comp>
<lifecycle-comp [name]="name2"></lifecycle-comp>
`, isInline: true, directives: [{ type: LifecycleComp, selector: "lifecycle-comp", inputs: ["name"] }] });
`, isInline: true, components: [{ type: LifecycleComp, selector: "lifecycle-comp", inputs: ["name"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SimpleLayout, [{
type: Component,
args: [{

View File

@ -365,7 +365,7 @@ MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: My
<content-query-component>
<div someDir></div>
</content-query-component>
`, isInline: true, directives: [{ type: i0.forwardRef(function () { return ContentQueryComponent; }), selector: "content-query-component" }, { type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] });
`, isInline: true, components: [{ type: i0.forwardRef(function () { return ContentQueryComponent; }), selector: "content-query-component" }], directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{
@ -524,7 +524,7 @@ MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: My
<content-query-component>
<div someDir></div>
</content-query-component>
`, isInline: true, directives: [{ type: i0.forwardRef(function () { return ContentQueryComponent; }), selector: "content-query-component" }, { type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] });
`, isInline: true, components: [{ type: i0.forwardRef(function () { return ContentQueryComponent; }), selector: "content-query-component" }], directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{

View File

@ -22,7 +22,7 @@ SomeDirective.ɵdir = i0.ɵɵngDeclareDirective({ version: "0.0.0-PLACEHOLDER",
export class MyComponent {
}
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '<child some-directive></child>!', isInline: true, directives: [{ type: ChildComponent, selector: "child" }, { type: SomeDirective, selector: "[some-directive]" }] });
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '<child some-directive></child>!', isInline: true, components: [{ type: ChildComponent, selector: "child" }], directives: [{ type: SomeDirective, selector: "[some-directive]" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{
type: Component,
args: [{ selector: 'my-component', template: '<child some-directive></child>!' }]
@ -329,7 +329,7 @@ export class MyApp {
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: `
<my-comp [names]="['Nancy', customName]"></my-comp>
`, isInline: true, directives: [{ type: MyComp, selector: "my-comp", inputs: ["names"] }] });
`, isInline: true, components: [{ type: MyComp, selector: "my-comp", inputs: ["names"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{
@ -428,7 +428,7 @@ MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: `
<my-comp [names]="['start-', n0, n1, n2, n3, n4, '-middle-', n5, n6, n7, n8, '-end']">
</my-comp>
`, isInline: true, directives: [{ type: MyComp, selector: "my-comp", inputs: ["names"] }] });
`, isInline: true, components: [{ type: MyComp, selector: "my-comp", inputs: ["names"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{
@ -509,7 +509,7 @@ export class MyApp {
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: `
<object-comp [config]="{'duration': 500, animation: name}"></object-comp>
`, isInline: true, directives: [{ type: ObjectComp, selector: "object-comp", inputs: ["config"] }] });
`, isInline: true, components: [{ type: ObjectComp, selector: "object-comp", inputs: ["config"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{
@ -587,7 +587,7 @@ MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: `
<nested-comp [config]="{animation: name, actions: [{ opacity: 0, duration: 0}, {opacity: 1, duration: duration }]}">
</nested-comp>
`, isInline: true, directives: [{ type: NestedComp, selector: "nested-comp", inputs: ["config"] }] });
`, isInline: true, components: [{ type: NestedComp, selector: "nested-comp", inputs: ["config"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{

View File

@ -62,7 +62,7 @@ InfinityCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", ty
export class MyComponent {
}
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '<div class="my-app" title="Hello"><math><infinity/></math><p>test</p></div>', isInline: true, directives: [{ type: MathCmp, selector: "math" }, { type: InfinityCmp, selector: "infinity" }] });
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: '<div class="my-app" title="Hello"><math><infinity/></math><p>test</p></div>', isInline: true, components: [{ type: MathCmp, selector: "math" }, { type: InfinityCmp, selector: "infinity" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{
type: Component,
args: [{

View File

@ -9,7 +9,7 @@ export class MyApp {
}
}
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: '<todo [data]="list"></todo>', isInline: true, directives: [{ type: i0.forwardRef(function () { return TodoComponent; }), selector: "todo", inputs: ["data"] }] });
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: '<todo [data]="list"></todo>', isInline: true, components: [{ type: i0.forwardRef(function () { return TodoComponent; }), selector: "todo", inputs: ["data"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{ selector: 'my-app', template: '<todo [data]="list"></todo>' }]

View File

@ -158,7 +158,7 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", ty
<button [attr.title]="myTitle" [attr.id]="buttonId" [attr.tabindex]="1"></button>
<span [attr.id]="1" [attr.title]="'hello'" [attr.some-attr]="1 + 2"></span>
<custom-element [attr.some-attr]="'one'" [attr.some-other-attr]="2"></custom-element>
`, isInline: true, directives: [{ type: CustomEl, selector: "custom-element" }] });
`, isInline: true, components: [{ type: CustomEl, selector: "custom-element" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{
type: Component,
args: [{

View File

@ -519,7 +519,7 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", ty
<button [title]="myTitle" [id]="buttonId" [tabindex]="1"></button>
<span [id]="1" [title]="'hello'" [someProp]="1 + 2"></span>
<custom-element [prop]="'one'" [otherProp]="2"></custom-element>
`, isInline: true, directives: [{ type: SpanDir, selector: "span", inputs: ["someProp"] }, { type: CustomEl, selector: "custom-element", inputs: ["prop", "otherProp"] }] });
`, isInline: true, components: [{ type: CustomEl, selector: "custom-element", inputs: ["prop", "otherProp"] }], directives: [{ type: SpanDir, selector: "span", inputs: ["someProp"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{
type: Component,
args: [{

View File

@ -54,7 +54,7 @@ export class MyComponent {
onClick(event) { }
}
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `<my-app (click)="onClick($event);"></my-app>`, isInline: true, directives: [{ type: MyApp, selector: "my-app" }] });
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `<my-app (click)="onClick($event);"></my-app>`, isInline: true, components: [{ type: MyApp, selector: "my-app" }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{
type: Component,
args: [{ selector: 'my-component', template: `<my-app (click)="onClick($event);"></my-app>` }]
@ -268,7 +268,7 @@ MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyCompone
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: `
<div (click)="click()" (change)="change()"></div>
<some-comp (update)="update()" (delete)="delete()"></some-comp>
`, isInline: true, directives: [{ type: SomeComp, selector: "some-comp", outputs: ["update", "delete"] }] });
`, isInline: true, components: [{ type: SomeComp, selector: "some-comp", outputs: ["update", "delete"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{
type: Component,
args: [{

View File

@ -133,39 +133,21 @@ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata {
*/
styles?: string[];
/**
* List of components which matched in the template, including sufficient
* metadata for each directive to attribute bindings and references within
* the template to each directive specifically, if the runtime instructions
* support this.
*/
components?: R3DeclareUsedDirectiveMetadata[];
/**
* List of directives which matched in the template, including sufficient
* metadata for each directive to attribute bindings and references within
* the template to each directive specifically, if the runtime instructions
* support this.
*/
directives?: {
/**
* Selector of the directive.
*/
selector: string;
/**
* Reference to the directive class (possibly a forward reference wrapped in a `forwardRef`
* invocation).
*/
type: o.Expression | (() => o.Expression);
/**
* Property names of the directive's inputs.
*/
inputs?: string[];
/**
* Event names of the directive's outputs.
*/
outputs?: string[];
/**
* Names by which this directive exports itself for references.
*/
exportAs?: string[];
}[];
directives?: R3DeclareUsedDirectiveMetadata[];
/**
* A map of pipe names to an expression referencing the pipe type (possibly a forward reference
@ -206,6 +188,34 @@ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata {
preserveWhitespaces?: boolean;
}
export interface R3DeclareUsedDirectiveMetadata {
/**
* Selector of the directive.
*/
selector: string;
/**
* Reference to the directive class (possibly a forward reference wrapped in a `forwardRef`
* invocation).
*/
type: o.Expression|(() => o.Expression);
/**
* Property names of the directive's inputs.
*/
inputs?: string[];
/**
* Event names of the directive's outputs.
*/
outputs?: string[];
/**
* Names by which this directive exports itself for references.
*/
exportAs?: string[];
}
export interface R3DeclareQueryMetadata {
/**
* Name of the property on the class to update with query results.

View File

@ -16,7 +16,7 @@ import {createComponentType} from '../view/compiler';
import {ParsedTemplate} from '../view/template';
import {DefinitionMap} from '../view/util';
import {R3DeclareComponentMetadata} from './api';
import {R3DeclareComponentMetadata, R3DeclareUsedDirectiveMetadata} from './api';
import {createDirectiveDefinitionMap} from './directive';
import {toOptionalLiteralArray} from './util';
@ -48,7 +48,12 @@ export function createComponentDefinitionMap(meta: R3ComponentMetadata, template
}
definitionMap.set('styles', toOptionalLiteralArray(meta.styles, o.literal));
definitionMap.set('directives', compileUsedDirectiveMetadata(meta));
definitionMap.set(
'components',
compileUsedDirectiveMetadata(meta, directive => directive.isComponent === true));
definitionMap.set(
'directives',
compileUsedDirectiveMetadata(meta, directive => directive.isComponent !== true));
definitionMap.set('pipes', compileUsedPipeMetadata(meta));
definitionMap.set('viewProviders', meta.viewProviders);
definitionMap.set('animations', meta.animations);
@ -119,13 +124,16 @@ function computeEndLocation(file: ParseSourceFile, contents: string): ParseLocat
* Compiles the directives as registered in the component metadata into an array literal of the
* individual directives. If the component does not use any directives, then null is returned.
*/
function compileUsedDirectiveMetadata(meta: R3ComponentMetadata): o.LiteralArrayExpr|null {
function compileUsedDirectiveMetadata(
meta: R3ComponentMetadata,
predicate: (directive: R3UsedDirectiveMetadata) => boolean): o.LiteralArrayExpr|null {
const wrapType = meta.declarationListEmitMode !== DeclarationListEmitMode.Direct ?
generateForwardRef :
(expr: o.Expression) => expr;
return toOptionalLiteralArray(meta.directives, directive => {
const dirMeta = new DefinitionMap<R3UsedDirectiveMetadata>();
const directives = meta.directives.filter(predicate);
return toOptionalLiteralArray(directives, directive => {
const dirMeta = new DefinitionMap<R3DeclareUsedDirectiveMetadata>();
dirMeta.set('type', wrapType(directive.type));
dirMeta.set('selector', o.literal(directive.selector));
dirMeta.set('inputs', toOptionalLiteralArray(directive.inputs, o.literal));