Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions modules/benchmarks/src/largetable/render3/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
*/

import {ɵC as C, ɵE as E, ɵRenderFlags as RenderFlags, ɵT as T, ɵV as V, ɵb as b, ɵcR as cR, ɵcr as cr, ɵdefineComponent as defineComponent, ɵdetectChanges as detectChanges, ɵe as e, ɵsn as sn, ɵt as t, ɵv as v} from '@angular/core';
import {ComponentDef} from '@angular/core/src/render3/interfaces/definition';
import {ComponentDefInternal} from '@angular/core/src/render3/interfaces/definition';

import {TableCell, buildTable, emptyTable} from '../util';

export class LargeTableComponent {
data: TableCell[][] = emptyTable;

/** @nocollapse */
static ngComponentDef: ComponentDef<LargeTableComponent> = defineComponent({
static ngComponentDef: ComponentDefInternal<LargeTableComponent> = defineComponent({
type: LargeTableComponent,
selectors: [['largetable']],
template: function(rf: RenderFlags, ctx: LargeTableComponent) {
Expand Down
6 changes: 3 additions & 3 deletions modules/benchmarks/src/tree/render3/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {ɵC as C, ɵE as E, ɵRenderFlags as RenderFlags, ɵT as T, ɵV as V, ɵb as b, ɵcR as cR, ɵcr as cr, ɵdefineComponent as defineComponent, ɵdetectChanges as _detectChanges, ɵe as e, ɵi1 as i1, ɵp as p, ɵsn as sn, ɵt as t, ɵv as v} from '@angular/core';
import {ComponentDef} from '@angular/core/src/render3/interfaces/definition';
import {ComponentDefInternal} from '@angular/core/src/render3/interfaces/definition';

import {TreeNode, buildTree, emptyTree} from '../util';

Expand Down Expand Up @@ -35,7 +35,7 @@ export class TreeComponent {
data: TreeNode = emptyTree;

/** @nocollapse */
static ngComponentDef: ComponentDef<TreeComponent> = defineComponent({
static ngComponentDef: ComponentDefInternal<TreeComponent> = defineComponent({
type: TreeComponent,
selectors: [['tree']],
template: function(rf: RenderFlags, ctx: TreeComponent) {
Expand Down Expand Up @@ -95,7 +95,7 @@ export class TreeFunction {
data: TreeNode = emptyTree;

/** @nocollapse */
static ngComponentDef: ComponentDef<TreeFunction> = defineComponent({
static ngComponentDef: ComponentDefInternal<TreeFunction> = defineComponent({
type: TreeFunction,
selectors: [['tree']],
template: function(rf: RenderFlags, ctx: TreeFunction) {
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-cli/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ts_library(
tsconfig = ":tsconfig",
deps = [
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/annotations",
"//packages/compiler-cli/src/ngtsc/transform",
],
)
Expand Down
17 changes: 17 additions & 0 deletions packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package(default_visibility = ["//visibility:public"])

load("//tools:defaults.bzl", "ts_library")

ts_library(
name = "annotations",
srcs = glob([
"index.ts",
"src/**/*.ts",
]),
module_name = "@angular/compiler-cli/src/ngtsc/annotations",
deps = [
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/transform",
],
)
13 changes: 13 additions & 0 deletions packages/compiler-cli/src/ngtsc/annotations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @license
* Copyright Google Inc. 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
*/

export {ComponentDecoratorHandler} from './src/component';
export {DirectiveDecoratorHandler} from './src/directive';
export {InjectableDecoratorHandler} from './src/injectable';
export {NgModuleDecoratorHandler} from './src/ng_module';
export {CompilationScope, SelectorScopeRegistry} from './src/selector_scope';
117 changes: 117 additions & 0 deletions packages/compiler-cli/src/ngtsc/annotations/src/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* @license
* Copyright Google Inc. 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 {ConstantPool, Expression, R3ComponentMetadata, R3DirectiveMetadata, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
import * as ts from 'typescript';

import {Decorator, reflectNonStaticField, reflectObjectLiteral, staticallyResolve} from '../../metadata';
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';

import {extractDirectiveMetadata} from './directive';
import {SelectorScopeRegistry} from './selector_scope';

const EMPTY_MAP = new Map<string, Expression>();

/**
* `DecoratorHandler` which handles the `@Component` annotation.
*/
export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMetadata> {
constructor(private checker: ts.TypeChecker, private scopeRegistry: SelectorScopeRegistry) {}

detect(decorators: Decorator[]): Decorator|undefined {
return decorators.find(
decorator => decorator.name === 'Component' && decorator.from === '@angular/core');
}

analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3ComponentMetadata> {
const meta = decorator.args[0];
if (!ts.isObjectLiteralExpression(meta)) {
throw new Error(`Decorator argument must be literal.`);
}

// @Component inherits @Directive, so begin by extracting the @Directive metadata and building
// on it.
const directiveMetadata = extractDirectiveMetadata(node, decorator, this.checker);
if (directiveMetadata === undefined) {
// `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
// case, compilation of the decorator is skipped. Returning an empty object signifies
// that no analysis was produced.
return {};
}

// Next, read the `@Component`-specific fields.
const component = reflectObjectLiteral(meta);

// Resolve and parse the template.
if (!component.has('template')) {
throw new Error(`For now, components must directly have a template.`);
}
const templateExpr = component.get('template') !;
const templateStr = staticallyResolve(templateExpr, this.checker);
if (typeof templateStr !== 'string') {
throw new Error(`Template must statically resolve to a string: ${node.name!.text}`);
}

let preserveWhitespaces: boolean = false;
if (component.has('preserveWhitespaces')) {
const value = staticallyResolve(component.get('preserveWhitespaces') !, this.checker);
if (typeof value !== 'boolean') {
throw new Error(`preserveWhitespaces must resolve to a boolean if present`);
}
preserveWhitespaces = value;
}

const template = parseTemplate(
templateStr, `${node.getSourceFile().fileName}#${node.name!.text}/template.html`,
{preserveWhitespaces});
if (template.errors !== undefined) {
throw new Error(
`Errors parsing template: ${template.errors.map(e => e.toString()).join(', ')}`);
}

// If the component has a selector, it should be registered with the `SelectorScopeRegistry` so
// when this component appears in an `@NgModule` scope, its selector can be determined.
if (directiveMetadata.selector !== null) {
this.scopeRegistry.registerSelector(node, directiveMetadata.selector);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In current implementation, a component would be given ng-component as its selector when not specified, is this being a breaking change?

if (!selector) {
selector = this._schemaRegistry.getDefaultComponentElementName();
}

(Even I don't think this feature should be depend upon)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is selector scope registration, which determines what selectors we look for when parsing a template of another component. If a component/directive doesn't have a selector, then it cannot be in the selector scope because it can never match an element in a template.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alxhub A selector-less component would indeed match <ng-component> in template:

https://stackblitz.com/edit/angular-hxgxhz?file=src%2Fapp%2Fhello.component.ts

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

....

I have no words.

}

return {
analysis: {
...directiveMetadata,
template,
viewQueries: [],

// These will be replaced during the compilation step, after all `NgModule`s have been
// analyzed and the full compilation scope for the component can be realized.
pipes: EMPTY_MAP,
directives: EMPTY_MAP,
}
};
}
compile(node: ts.ClassDeclaration, analysis: R3ComponentMetadata): CompileResult {
const pool = new ConstantPool();

// Check whether this component was registered with an NgModule. If so, it should be compiled
// under that module's compilation scope.
const scope = this.scopeRegistry.lookupCompilationScope(node);
if (scope !== null) {
// Replace the empty components and directives from the analyze() step with a fully expanded
// scope. This is possible now because during compile() the whole compilation unit has been
// fully analyzed.
analysis = {...analysis, ...scope};
}

const res = compileComponentFromMetadata(analysis, pool, makeBindingParser());
return {
field: 'ngComponentDef',
initializer: res.expression,
statements: pool.statements,
type: res.type,
};
}
}
Loading