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,7 +7,7 @@
*/

import {ɵRenderFlags, ɵbind, ɵcontainer, ɵcontainerRefreshEnd, ɵcontainerRefreshStart, ɵdefineComponent, ɵdetectChanges, ɵelementEnd, ɵelementStart, ɵelementStyleProp, ɵelementStyling, ɵembeddedViewEnd, ɵembeddedViewStart, ɵtext, ɵtextBinding as ɵtextBinding} from '@angular/core';
import {ComponentDefInternal} from '@angular/core/src/render3/interfaces/definition';
import {ComponentDef} from '@angular/core/src/render3/interfaces/definition';

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

Expand All @@ -16,7 +16,7 @@ export class LargeTableComponent {
data: TableCell[][] = emptyTable;

/** @nocollapse */
static ngComponentDef: ComponentDefInternal<LargeTableComponent> = ɵdefineComponent({
static ngComponentDef: ComponentDef<LargeTableComponent> = ɵdefineComponent({
type: LargeTableComponent,
selectors: [['largetable']],
consts: 3,
Expand Down
10 changes: 10 additions & 0 deletions packages/common/src/directives/ng_for_of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ export class NgForOf<T> implements DoCheck {
view: EmbeddedViewRef<NgForOfContext<T>>, record: IterableChangeRecord<any>) {
view.context.$implicit = record.item;
}

/**
* Assert the correct type of the context for the template that `NgForOf` will render.
*
* The presence of this method is a signal to the Ivy template type check compiler that the
* `NgForOf` structural directive renders its template with a specific context type.
*/
static ngTemplateContextGuard<T>(dir: NgForOf<T>, ctx: any): ctx is NgForOfContext<T> {
return true;
}
}

class RecordViewTuple<T> {
Expand Down
10 changes: 10 additions & 0 deletions packages/common/src/directives/ng_if.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,16 @@ export class NgIf {

/** @internal */
public static ngIfUseIfTypeGuard: void;

/**
* Assert the correct type of the expression bound to the `ngIf` input within the template.
*
* The presence of this method is a signal to the Ivy template type check compiler that when the
* `NgIf` structural directive renders its template, the type of the expression bound to `ngIf`
* should be narrowed in some way. For `NgIf`, it is narrowed to be non-null, which allows the
* strictNullChecks feature of TypeScript to work with `NgIf`.
*/
static ngTemplateGuard_ngIf<E>(dir: NgIf, expr: E): expr is NonNullable<E> { return true; }
}

export class NgIfContext {
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 @@ -30,6 +30,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/factories",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/transform",
"//packages/compiler-cli/src/ngtsc/typecheck",
],
)

Expand Down
11 changes: 11 additions & 0 deletions packages/compiler-cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ export function main(
return reportErrorsAndExit(compileDiags, options, consoleError);
}

export function mainDiagnosticsForTest(
args: string[], config?: NgcParsedConfiguration): ReadonlyArray<ts.Diagnostic|api.Diagnostic> {
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
config || readNgcCommandLineAndConfiguration(args);
if (configErrors.length) {
return configErrors;
}
const {diagnostics: compileDiags} = performCompilation(
{rootNames, options, emitFlags, emitCallback: createEmitCallback(options)});
return compileDiags;
}

function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
const transformDecorators = options.enableIvy !== 'ngtsc' && options.enableIvy !== 'tsc' &&
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-cli/src/ngcc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/host",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/transform",
"//packages/compiler-cli/src/ngtsc/translator",
],
)
2 changes: 1 addition & 1 deletion packages/compiler-cli/src/ngcc/src/rendering/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {SourceMapConsumer, SourceMapGenerator, RawSourceMap} from 'source-map';
import * as ts from 'typescript';

import {Decorator} from '../../../ngtsc/host';
import {ImportManager, translateStatement} from '../../../ngtsc/transform';
import {ImportManager, translateStatement} from '../../../ngtsc/translator';
import {AnalyzedClass, AnalyzedFile} from '../analyzer';
import {IMPORT_PREFIX} from '../constants';
import {NgccReflectionHost} from '../host/ngcc_host';
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/host",
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/transform",
"//packages/compiler-cli/src/ngtsc/typecheck",
],
)
81 changes: 58 additions & 23 deletions packages/compiler-cli/src/ngtsc/annotations/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,33 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ConstantPool, Expression, R3ComponentMetadata, R3DirectiveMetadata, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
import {ConstantPool, CssSelector, Expression, R3ComponentMetadata, R3DirectiveMetadata, SelectorMatcher, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
import * as path from 'path';
import * as ts from 'typescript';

import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {Decorator, ReflectionHost} from '../../host';
import {filterToMembersWithDecorator, reflectObjectLiteral, staticallyResolve} from '../../metadata';
import {AbsoluteReference, Reference, ResolvedReference, filterToMembersWithDecorator, reflectObjectLiteral, staticallyResolve} from '../../metadata';
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
import {TypeCheckContext, TypeCheckableDirectiveMeta} from '../../typecheck';

import {ResourceLoader} from './api';
import {extractDirectiveMetadata, extractQueriesFromDecorator, parseFieldArrayValue, queriesFromFields} from './directive';
import {SelectorScopeRegistry} from './selector_scope';
import {isAngularCore, unwrapExpression} from './util';
import {ScopeDirective, SelectorScopeRegistry} from './selector_scope';
import {extractDirectiveGuards, isAngularCore, unwrapExpression} from './util';

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

export interface ComponentHandlerData {
meta: R3ComponentMetadata;
parsedTemplate: TmplAstNode[];
}

/**
* `DecoratorHandler` which handles the `@Component` annotation.
*/
export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMetadata, Decorator> {
export class ComponentDecoratorHandler implements
DecoratorHandler<ComponentHandlerData, Decorator> {
constructor(
private checker: ts.TypeChecker, private reflector: ReflectionHost,
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean,
Expand Down Expand Up @@ -59,7 +66,7 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
return undefined;
}

analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3ComponentMetadata> {
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<ComponentHandlerData> {
const meta = this._resolveLiteral(decorator);
this.literalCache.delete(decorator);

Expand Down Expand Up @@ -134,7 +141,18 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
// 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 (metadata.selector !== null) {
this.scopeRegistry.registerSelector(node, metadata.selector);
const ref = new ResolvedReference(node, node.name !);
this.scopeRegistry.registerDirective(node, {
ref,
name: node.name !.text,
directive: ref,
selector: metadata.selector,
exportAs: metadata.exportAs,
inputs: metadata.inputs,
outputs: metadata.outputs,
queries: metadata.queries.map(query => query.propertyName),
isComponent: true, ...extractDirectiveGuards(node, this.reflector),
});
}

// Construct the list of view queries.
Expand Down Expand Up @@ -174,36 +192,53 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe

return {
analysis: {
...metadata,
template,
viewQueries,
encapsulation,
styles: styles || [],

// 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,
wrapDirectivesInClosure: false, animations,
}
meta: {
...metadata,
template,
viewQueries,
encapsulation,
styles: styles || [],

// 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,
wrapDirectivesInClosure: false, animations,
},
parsedTemplate: template.nodes,
},
typeCheck: true,
};
}

compile(node: ts.ClassDeclaration, analysis: R3ComponentMetadata, pool: ConstantPool):
typeCheck(ctx: TypeCheckContext, node: ts.Declaration, meta: ComponentHandlerData): void {
const scope = this.scopeRegistry.lookupCompilationScopeAsRefs(node);
const matcher = new SelectorMatcher<ScopeDirective<any>>();
if (scope !== null) {
scope.directives.forEach(
(meta, selector) => { matcher.addSelectables(CssSelector.parse(selector), meta); });
ctx.addTemplate(node as ts.ClassDeclaration, meta.parsedTemplate, matcher);
}
}

compile(node: ts.ClassDeclaration, analysis: ComponentHandlerData, pool: ConstantPool):
CompileResult {
// 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);
let metadata = analysis.meta;
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.
const {directives, pipes, containsForwardDecls} = scope;
const {pipes, containsForwardDecls} = scope;
const directives = new Map<string, Expression>();
scope.directives.forEach((meta, selector) => directives.set(selector, meta.directive));
const wrapDirectivesInClosure: boolean = !!containsForwardDecls;
analysis = {...analysis, directives, pipes, wrapDirectivesInClosure};
metadata = {...metadata, directives, pipes, wrapDirectivesInClosure};
}

const res = compileComponentFromMetadata(analysis, pool, makeBindingParser());
const res = compileComponentFromMetadata(metadata, pool, makeBindingParser());
return {
name: 'ngComponentDef',
initializer: res.expression,
Expand Down
17 changes: 14 additions & 3 deletions packages/compiler-cli/src/ngtsc/annotations/src/directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import * as ts from 'typescript';

import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {ClassMember, ClassMemberKind, Decorator, Import, ReflectionHost} from '../../host';
import {Reference, filterToMembersWithDecorator, reflectObjectLiteral, staticallyResolve} from '../../metadata';
import {Reference, ResolvedReference, filterToMembersWithDecorator, reflectObjectLiteral, staticallyResolve} from '../../metadata';
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';

import {SelectorScopeRegistry} from './selector_scope';
import {getConstructorDependencies, isAngularCore, unwrapExpression, unwrapForwardRef} from './util';
import {extractDirectiveGuards, getConstructorDependencies, isAngularCore, unwrapExpression, unwrapForwardRef} from './util';

const EMPTY_OBJECT: {[key: string]: string} = {};

Expand All @@ -40,7 +40,18 @@ export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMe
// If the directive has a selector, it should be registered with the `SelectorScopeRegistry` so
// when this directive appears in an `@NgModule` scope, its selector can be determined.
if (analysis && analysis.selector !== null) {
this.scopeRegistry.registerSelector(node, analysis.selector);
let ref = new ResolvedReference(node, node.name !);
this.scopeRegistry.registerDirective(node, {
ref,
directive: ref,
name: node.name !.text,
selector: analysis.selector,
exportAs: analysis.exportAs,
inputs: analysis.inputs,
outputs: analysis.outputs,
queries: analysis.queries.map(query => query.propertyName),
isComponent: false, ...extractDirectiveGuards(node, this.reflector),
});
}

return {analysis};
Expand Down
21 changes: 14 additions & 7 deletions packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,29 +65,29 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
}

// Extract the module declarations, imports, and exports.
let declarations: Reference[] = [];
let declarations: Reference<ts.Declaration>[] = [];
if (ngModule.has('declarations')) {
const expr = ngModule.get('declarations') !;
const declarationMeta = staticallyResolve(expr, this.reflector, this.checker);
declarations = this.resolveTypeList(expr, declarationMeta, 'declarations');
}
let imports: Reference[] = [];
let imports: Reference<ts.Declaration>[] = [];
if (ngModule.has('imports')) {
const expr = ngModule.get('imports') !;
const importsMeta = staticallyResolve(
expr, this.reflector, this.checker,
ref => this._extractModuleFromModuleWithProvidersFn(ref.node));
imports = this.resolveTypeList(expr, importsMeta, 'imports');
}
let exports: Reference[] = [];
let exports: Reference<ts.Declaration>[] = [];
if (ngModule.has('exports')) {
const expr = ngModule.get('exports') !;
const exportsMeta = staticallyResolve(
expr, this.reflector, this.checker,
ref => this._extractModuleFromModuleWithProvidersFn(ref.node));
exports = this.resolveTypeList(expr, exportsMeta, 'exports');
}
let bootstrap: Reference[] = [];
let bootstrap: Reference<ts.Declaration>[] = [];
if (ngModule.has('bootstrap')) {
const expr = ngModule.get('bootstrap') !;
const bootstrapMeta = staticallyResolve(expr, this.reflector, this.checker);
Expand Down Expand Up @@ -198,8 +198,9 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
/**
* Compute a list of `Reference`s from a resolved metadata value.
*/
private resolveTypeList(expr: ts.Node, resolvedList: ResolvedValue, name: string): Reference[] {
const refList: Reference[] = [];
private resolveTypeList(expr: ts.Node, resolvedList: ResolvedValue, name: string):
Reference<ts.Declaration>[] {
const refList: Reference<ts.Declaration>[] = [];
if (!Array.isArray(resolvedList)) {
throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, expr, `Expected array when reading property ${name}`);
Expand All @@ -215,7 +216,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
if (Array.isArray(entry)) {
// Recurse into nested arrays.
refList.push(...this.resolveTypeList(expr, entry, name));
} else if (entry instanceof Reference) {
} else if (isDeclarationReference(entry)) {
if (!entry.expressable) {
throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, expr, `One entry in ${name} is not a type`);
Expand All @@ -234,3 +235,9 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
return refList;
}
}

function isDeclarationReference(ref: any): ref is Reference<ts.Declaration> {
return ref instanceof Reference &&
(ts.isClassDeclaration(ref.node) || ts.isFunctionDeclaration(ref.node) ||
ts.isVariableDeclaration(ref.node));
}
Loading