feat(ivy): update compiler to specification (#21657)

PR Close #21657
This commit is contained in:
Chuck Jazdzewski 2018-01-11 15:37:56 -08:00 committed by Miško Hevery
parent 8c51c276c7
commit 86d9612230
9 changed files with 565 additions and 90 deletions

View File

@ -19,7 +19,7 @@ import {NgModuleCompiler} from '../ng_module_compiler';
import {OutputEmitter} from '../output/abstract_emitter'; import {OutputEmitter} from '../output/abstract_emitter';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ParseError} from '../parse_util'; import {ParseError} from '../parse_util';
import {compileComponent as compileIvyComponent} from '../render3/r3_view_compiler'; import {compileComponent as compileIvyComponent, compileDirective as compileIvyDirective} from '../render3/r3_view_compiler';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler'; import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver'; import {SummaryResolver} from '../summary_resolver';
import {TemplateAst} from '../template_parser/template_ast'; import {TemplateAst} from '../template_parser/template_ast';
@ -325,7 +325,7 @@ export class AotCompiler {
const context = this._createOutputContext(fileName); const context = this._createOutputContext(fileName);
// Process all components // Process all components and directives
directives.forEach(directiveType => { directives.forEach(directiveType => {
const directiveMetadata = this._metadataResolver.getDirectiveMetadata(directiveType); const directiveMetadata = this._metadataResolver.getDirectiveMetadata(directiveType);
if (directiveMetadata.isComponent) { if (directiveMetadata.isComponent) {
@ -337,6 +337,8 @@ export class AotCompiler {
const {template: parsedTemplate} = const {template: parsedTemplate} =
this._parseTemplate(directiveMetadata, module, module.transitiveModule.directives); this._parseTemplate(directiveMetadata, module, module.transitiveModule.directives);
compileIvyComponent(context, directiveMetadata, parsedTemplate, this._reflector); compileIvyComponent(context, directiveMetadata, parsedTemplate, this._reflector);
} else {
compileIvyDirective(context, directiveMetadata, this._reflector);
} }
}); });

View File

@ -9,6 +9,8 @@
import * as o from './output/output_ast'; import * as o from './output/output_ast';
import {OutputContext, error} from './util'; import {OutputContext, error} from './util';
const CONSTANT_PREFIX = '_c';
export const enum DefinitionKind {Injector, Directive, Component} export const enum DefinitionKind {Injector, Directive, Component}
/** /**
@ -48,29 +50,34 @@ export class ConstantPool {
private literals = new Map<string, FixupExpression>(); private literals = new Map<string, FixupExpression>();
private injectorDefinitions = new Map<any, FixupExpression>(); private injectorDefinitions = new Map<any, FixupExpression>();
private directiveDefinitions = new Map<any, FixupExpression>(); private directiveDefinitions = new Map<any, FixupExpression>();
private componentDefintions = new Map<any, FixupExpression>(); private componentDefinitions = new Map<any, FixupExpression>();
private nextNameIndex = 0; private nextNameIndex = 0;
getConstLiteral(literal: o.Expression): o.Expression { getConstLiteral(literal: o.Expression, forceShared?: boolean): o.Expression {
const key = this.keyOf(literal); const key = this.keyOf(literal);
let fixup = this.literals.get(key); let fixup = this.literals.get(key);
let newValue = false;
if (!fixup) { if (!fixup) {
fixup = new FixupExpression(literal); fixup = new FixupExpression(literal);
this.literals.set(key, fixup); this.literals.set(key, fixup);
} else if (!fixup.shared) { newValue = true;
}
if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
// Replace the expression with a variable // Replace the expression with a variable
const name = this.freshName(); const name = this.freshName();
this.statements.push( this.statements.push(
o.variable(name).set(literal).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final])); o.variable(name).set(literal).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
fixup.fixup(o.variable(name)); fixup.fixup(o.variable(name));
} }
return fixup; return fixup;
} }
getDefinition(type: any, kind: DefinitionKind, ctx: OutputContext): o.Expression { getDefinition(type: any, kind: DefinitionKind, ctx: OutputContext): o.Expression {
const declarations = kind == DefinitionKind.Component ? const declarations = kind == DefinitionKind.Component ?
this.componentDefintions : this.componentDefinitions :
kind == DefinitionKind.Directive ? this.directiveDefinitions : this.injectorDefinitions; kind == DefinitionKind.Directive ? this.directiveDefinitions : this.injectorDefinitions;
let fixup = declarations.get(type); let fixup = declarations.get(type);
if (!fixup) { if (!fixup) {
@ -97,7 +104,7 @@ export class ConstantPool {
*/ */
uniqueName(prefix: string): string { return `${prefix}${this.nextNameIndex++}`; } uniqueName(prefix: string): string { return `${prefix}${this.nextNameIndex++}`; }
private freshName(): string { return this.uniqueName(`_$`); } private freshName(): string { return this.uniqueName(CONSTANT_PREFIX); }
private keyOf(expression: o.Expression) { private keyOf(expression: o.Expression) {
return expression.visitExpression(new KeyVisitor(), null); return expression.visitExpression(new KeyVisitor(), null);
@ -105,15 +112,22 @@ export class ConstantPool {
} }
class KeyVisitor implements o.ExpressionVisitor { class KeyVisitor implements o.ExpressionVisitor {
visitLiteralExpr(ast: o.LiteralExpr): string { return `${ast.value}`; } visitLiteralExpr(ast: o.LiteralExpr): string {
return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
}
visitLiteralArrayExpr(ast: o.LiteralArrayExpr): string { visitLiteralArrayExpr(ast: o.LiteralArrayExpr): string {
return ast.entries.map(entry => entry.visitExpression(this, null)).join(','); return `[${ast.entries.map(entry => entry.visitExpression(this, null)).join(',')}]`;
} }
visitLiteralMapExpr(ast: o.LiteralMapExpr): string { visitLiteralMapExpr(ast: o.LiteralMapExpr): string {
const entries = const mapEntry = (entry: o.LiteralMapEntry) =>
ast.entries.map(entry => `${entry.key}:${entry.value.visitExpression(this, null)}`); `${entry.key}:${entry.value.visitExpression(this, null)}`;
return `{${entries.join(',')}`; return `{${ast.entries.map(mapEntry).join(',')}`;
}
visitExternalExpr(ast: o.ExternalExpr): string {
return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
`EX:${ast.value.runtime.name}`;
} }
visitReadVarExpr = invalid; visitReadVarExpr = invalid;
@ -123,7 +137,6 @@ class KeyVisitor implements o.ExpressionVisitor {
visitInvokeMethodExpr = invalid; visitInvokeMethodExpr = invalid;
visitInvokeFunctionExpr = invalid; visitInvokeFunctionExpr = invalid;
visitInstantiateExpr = invalid; visitInstantiateExpr = invalid;
visitExternalExpr = invalid;
visitConditionalExpr = invalid; visitConditionalExpr = invalid;
visitNotExpr = invalid; visitNotExpr = invalid;
visitAssertNotNullExpr = invalid; visitAssertNotNullExpr = invalid;

View File

@ -40,9 +40,9 @@ export type ReferenceFilter = (reference: o.ExternalReference) => boolean;
export class TypeScriptEmitter implements OutputEmitter { export class TypeScriptEmitter implements OutputEmitter {
emitStatementsAndContext( emitStatementsAndContext(
genFilePath: string, stmts: o.Statement[], preamble: string = '', genFilePath: string, stmts: o.Statement[], preamble: string = '',
emitSourceMaps: boolean = true, emitSourceMaps: boolean = true, referenceFilter?: ReferenceFilter,
referenceFilter?: ReferenceFilter): {sourceText: string, context: EmitterVisitorContext} { importFilter?: ReferenceFilter): {sourceText: string, context: EmitterVisitorContext} {
const converter = new _TsEmitterVisitor(referenceFilter); const converter = new _TsEmitterVisitor(referenceFilter, importFilter);
const ctx = EmitterVisitorContext.createRoot(); const ctx = EmitterVisitorContext.createRoot();
@ -83,7 +83,9 @@ export class TypeScriptEmitter implements OutputEmitter {
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor { class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
private typeExpression = 0; private typeExpression = 0;
constructor(private referenceFilter?: ReferenceFilter) { super(false); } constructor(private referenceFilter?: ReferenceFilter, private importFilter?: ReferenceFilter) {
super(false);
}
importsWithPrefixes = new Map<string, string>(); importsWithPrefixes = new Map<string, string>();
reexports = new Map<string, {name: string, as: string}[]>(); reexports = new Map<string, {name: string, as: string}[]>();
@ -390,7 +392,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
ctx.print(null, '(null as any)'); ctx.print(null, '(null as any)');
return; return;
} }
if (moduleName) { if (moduleName && (!this.importFilter || !this.importFilter(value))) {
let prefix = this.importsWithPrefixes.get(moduleName); let prefix = this.importsWithPrefixes.get(moduleName);
if (prefix == null) { if (prefix == null) {
prefix = `i${this.importsWithPrefixes.size}`; prefix = `i${this.importsWithPrefixes.size}`;

View File

@ -40,6 +40,10 @@ export class Identifiers {
static containerEnd: o.ExternalReference = {name: 'ɵc', moduleName: CORE}; static containerEnd: o.ExternalReference = {name: 'ɵc', moduleName: CORE};
static containerRefreshStart: o.ExternalReference = {name: 'ɵcR', moduleName: CORE};
static containerRefreshEnd: o.ExternalReference = {name: 'ɵcr', moduleName: CORE};
static directiveCreate: o.ExternalReference = {name: 'ɵD', moduleName: CORE}; static directiveCreate: o.ExternalReference = {name: 'ɵD', moduleName: CORE};
static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE}; static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};
@ -61,6 +65,8 @@ export class Identifiers {
static bind9: o.ExternalReference = {name: 'ɵb9', moduleName: CORE}; static bind9: o.ExternalReference = {name: 'ɵb9', moduleName: CORE};
static bindV: o.ExternalReference = {name: 'ɵbV', moduleName: CORE}; static bindV: o.ExternalReference = {name: 'ɵbV', moduleName: CORE};
static memory: o.ExternalReference = {name: 'ɵm', moduleName: CORE};
static refreshComponent: o.ExternalReference = {name: 'ɵr', moduleName: CORE}; static refreshComponent: o.ExternalReference = {name: 'ɵr', moduleName: CORE};
static directiveLifeCycle: o.ExternalReference = {name: 'ɵl', moduleName: CORE}; static directiveLifeCycle: o.ExternalReference = {name: 'ɵl', moduleName: CORE};

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompileDirectiveMetadata, CompilePipeSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata'; import {CompileDirectiveMetadata, CompilePipeSummary, CompileTokenMetadata, CompileTypeMetadata, flatten, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
import {CompileReflector} from '../compile_reflector'; import {CompileReflector} from '../compile_reflector';
import {BindingForm, BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter'; import {BindingForm, BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
import {ConstantPool, DefinitionKind} from '../constant_pool'; import {ConstantPool, DefinitionKind} from '../constant_pool';
@ -21,6 +21,7 @@ import {OutputContext, error} from '../util';
import {Identifiers as R3} from './r3_identifiers'; import {Identifiers as R3} from './r3_identifiers';
/** Name of the context parameter passed into a template function */ /** Name of the context parameter passed into a template function */
const CONTEXT_NAME = 'ctx'; const CONTEXT_NAME = 'ctx';
@ -30,15 +31,40 @@ const CREATION_MODE_FLAG = 'cm';
/** Name of the temporary to use during data binding */ /** Name of the temporary to use during data binding */
const TEMPORARY_NAME = '_t'; const TEMPORARY_NAME = '_t';
/** The prefix reference variables */
const REFERENCE_PREFIX = '_r';
export function compileDirective(
outputCtx: OutputContext, directive: CompileDirectiveMetadata, reflector: CompileReflector) {
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
// e.g. `factory: () => new MyApp(injectElementRef())`
const templateFactory = createFactory(directive.type, outputCtx, reflector);
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
const className = identifierName(directive.type) !;
className || error(`Cannot resolver the name of ${directive.type}`);
// Create the partial class to be merged with the actual class.
outputCtx.statements.push(new o.ClassStmt(
/* name */ className,
/* parent */ null,
/* fields */[new o.ClassField(
/* name */ 'ngDirectiveDef',
/* type */ o.INFERRED_TYPE,
/* modifiers */[o.StmtModifier.Static],
/* initializer */ o.importExpr(R3.defineDirective).callFn([o.literalMap(
definitionMapValues)]))],
/* getters */[],
/* constructorMethod */ new o.ClassMethod(null, [], []),
/* methods */[]));
}
export function compileComponent( export function compileComponent(
outputCtx: OutputContext, component: CompileDirectiveMetadata, template: TemplateAst[], outputCtx: OutputContext, component: CompileDirectiveMetadata, template: TemplateAst[],
reflector: CompileReflector) { reflector: CompileReflector) {
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = []; const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
// e.g. `type: MyApp`
definitionMapValues.push(
{key: 'type', value: outputCtx.importExpr(component.type.reference), quoted: false});
// e.g. `tag: 'my-app' // e.g. `tag: 'my-app'
// This is optional and only included if the first selector of a component has element. // This is optional and only included if the first selector of a component has element.
const selector = component.selector && CssSelector.parse(component.selector); const selector = component.selector && CssSelector.parse(component.selector);
@ -54,24 +80,27 @@ export function compileComponent(
if (selectorAttributes.length) { if (selectorAttributes.length) {
definitionMapValues.push({ definitionMapValues.push({
key: 'attrs', key: 'attrs',
value: outputCtx.constantPool.getConstLiteral(o.literalArr(selectorAttributes.map( value: outputCtx.constantPool.getConstLiteral(
value => value != null ? o.literal(value) : o.literal(undefined)))), o.literalArr(selectorAttributes.map(
value => value != null ? o.literal(value) : o.literal(undefined))),
/* forceShared */ true),
quoted: false quoted: false
}); });
} }
} }
// e.g. `template: function(_ctx, _cm) {...}`
const templateFunctionExpression =
new TemplateDefinitionBuilder(outputCtx, outputCtx.constantPool, CONTEXT_NAME)
.buildTemplateFunction(template);
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
// e.g. `factory: () => new MyApp(injectElementRef())` // e.g. `factory: () => new MyApp(injectElementRef())`
const templateFactory = createFactory(component.type, outputCtx, reflector); const templateFactory = createFactory(component.type, outputCtx, reflector);
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false}); definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
// e.g. `template: function(_ctx, _cm) {...}`
const templateFunctionExpression =
new TemplateDefinitionBuilder(
outputCtx, outputCtx.constantPool, CONTEXT_NAME, ROOT_SCOPE.nestedScope())
.buildTemplateFunction(template);
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
const className = identifierName(component.type) !; const className = identifierName(component.type) !;
className || error(`Cannot resolver the name of ${component.type}`); className || error(`Cannot resolver the name of ${component.type}`);
@ -136,9 +165,49 @@ function interpolate(args: o.Expression[]): o.Expression {
return o.importExpr(R3.bindV).callFn(args); return o.importExpr(R3.bindV).callFn(args);
} }
class TemplateDefinitionBuilder implements TemplateAstVisitor { class BindingScope {
private map = new Map<string, o.Expression>();
private referenceNameIndex = 0;
constructor(private parent: BindingScope|null) {}
get(name: string): o.Expression|null {
let current: BindingScope|null = this;
while (current) {
const value = current.map.get(name);
if (value != null) {
// Cache the value locally.
this.map.set(name, value);
return value;
}
current = current.parent;
}
return null;
}
set(name: string, variableName: string): BindingScope {
!this.map.has(name) ||
error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
this.map.set(name, o.variable(variableName));
return this;
}
nestedScope(): BindingScope { return new BindingScope(this); }
freshReferenceName(): string {
let current: BindingScope|null = this;
// Find the top scope as it maintains the global reference count
while (current.parent) current = current.parent;
return `${REFERENCE_PREFIX}${current.referenceNameIndex++}`;
}
}
const ROOT_SCOPE = new BindingScope(null).set('$event', '$event');
class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
private _dataIndex = 0; private _dataIndex = 0;
private _bindingContext = 0; private _bindingContext = 0;
private _referenceIndex = 0;
private _temporaryAllocated = false; private _temporaryAllocated = false;
private _prefix: o.Statement[] = []; private _prefix: o.Statement[] = [];
private _creationMode: o.Statement[] = []; private _creationMode: o.Statement[] = [];
@ -151,7 +220,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
constructor( constructor(
private outputCtx: OutputContext, private constantPool: ConstantPool, private outputCtx: OutputContext, private constantPool: ConstantPool,
private contextParameter: string, private level = 0) {} private contextParameter: string, private bindingScope: BindingScope, private level = 0) {}
buildTemplateFunction(asts: TemplateAst[]): o.FunctionExpr { buildTemplateFunction(asts: TemplateAst[]): o.FunctionExpr {
templateVisitAll(this, asts); templateVisitAll(this, asts);
@ -173,7 +242,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
// Host mode (i.e. Comp.h(...)) // Host mode (i.e. Comp.h(...))
...this._hostMode, ...this._hostMode,
// Refesh mode (i.e. Comp.r(...)) // Refresh mode (i.e. Comp.r(...))
...this._refreshMode, ...this._refreshMode,
// Nested templates (i.e. function CompTemplate() {}) // Nested templates (i.e. function CompTemplate() {})
@ -182,31 +251,91 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
o.INFERRED_TYPE); o.INFERRED_TYPE);
} }
getLocal(name: string): o.Expression|null { return this.bindingScope.get(name); }
// TODO(chuckj): Implement ng-content // TODO(chuckj): Implement ng-content
visitNgContent = unknown; visitNgContent = unknown;
private _computeDirectivesArray(directives: DirectiveAst[]) {
const directiveIndexMap = new Map<any, number>();
const directiveExpressions: o.Expression[] =
directives.filter(directive => !directive.directive.isComponent).map(directive => {
directiveIndexMap.set(directive.directive.type.reference, this.allocateDataSlot());
return this.typeReference(directive.directive.type.reference);
});
return {
directivesArray: directiveExpressions.length ?
this.constantPool.getConstLiteral(
o.literalArr(directiveExpressions), /* forceShared */ true) :
o.literal(null),
directiveIndexMap
};
}
visitElement(ast: ElementAst) { visitElement(ast: ElementAst) {
let bindingCount = 0; let bindingCount = 0;
const elementIndex = this.allocateNode(); const elementIndex = this.allocateDataSlot();
let componentIndex: number|undefined = undefined;
const referenceDataSlots = new Map<string, number>();
// Element creation mode // Element creation mode
const component = findComponent(ast.directives); const component = findComponent(ast.directives);
const nullNode = o.literal(null, o.INFERRED_TYPE);
const parameters: o.Expression[] = [o.literal(elementIndex)]; const parameters: o.Expression[] = [o.literal(elementIndex)];
// Add component type or element tag
if (component) { if (component) {
parameters.push(this.typeReference(component.directive.type.reference)); parameters.push(this.typeReference(component.directive.type.reference));
componentIndex = this.allocateDataSlot();
} else { } else {
parameters.push(o.literal(ast.name)); parameters.push(o.literal(ast.name));
} }
// Add attributes array
const attributes: o.Expression[] = []; const attributes: o.Expression[] = [];
for (let attr of ast.attrs) { for (let attr of ast.attrs) {
attributes.push(o.literal(attr.name), o.literal(attr.value)); attributes.push(o.literal(attr.name), o.literal(attr.value));
} }
parameters.push(
attributes.length > 0 ?
this.constantPool.getConstLiteral(o.literalArr(attributes), /* forceShared */ true) :
nullNode);
if (attributes.length !== 0) { // Add directives array
parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributes))); const {directivesArray, directiveIndexMap} = this._computeDirectivesArray(ast.directives);
parameters.push(directiveIndexMap.size > 0 ? directivesArray : nullNode);
if (component && componentIndex != null) {
// Record the data slot for the component
directiveIndexMap.set(component.directive.type.reference, componentIndex);
} }
// Add references array
if (ast.references && ast.references.length > 0) {
const references =
flatten(ast.references.map(reference => {
const slot = this.allocateDataSlot();
referenceDataSlots.set(reference.name, slot);
// Generate the update temporary.
const variableName = this.bindingScope.freshReferenceName();
this._bindingMode.push(o.variable(variableName, o.INFERRED_TYPE)
.set(o.importExpr(R3.memory).callFn([o.literal(slot)]))
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
this.bindingScope.set(reference.name, variableName);
return [reference.name, reference.originalValue];
})).map(value => o.literal(value));
parameters.push(
this.constantPool.getConstLiteral(o.literalArr(references), /* forceShared */ true));
} else {
parameters.push(nullNode);
}
// Remove trailing null nodes as they are implied.
while (parameters[parameters.length - 1] === nullNode) {
parameters.pop();
}
// Generate the instruction create element instruction
this.instruction(this._creationMode, ast.sourceSpan, R3.createElement, ...parameters); this.instruction(this._creationMode, ast.sourceSpan, R3.createElement, ...parameters);
const implicit = o.variable(this.contextParameter); const implicit = o.variable(this.contextParameter);
@ -216,9 +345,9 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
if (input.isAnimation) { if (input.isAnimation) {
this.unsupported('animations'); this.unsupported('animations');
} }
// TODO(chuckj): Builtins transform? // TODO(chuckj): Built-in transform?
const convertedBinding = convertPropertyBinding( const convertedBinding = convertPropertyBinding(
null, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate); this, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
this._bindingMode.push(...convertedBinding.stmts); this._bindingMode.push(...convertedBinding.stmts);
const parameters = const parameters =
[o.literal(elementIndex), o.literal(input.name), convertedBinding.currValExpr]; [o.literal(elementIndex), o.literal(input.name), convertedBinding.currValExpr];
@ -234,19 +363,20 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
} }
// Generate directives input bindings // Generate directives input bindings
this._visitDirectives(ast.directives, implicit, elementIndex); this._visitDirectives(ast.directives, implicit, elementIndex, directiveIndexMap);
// Traverse element child nodes // Traverse element child nodes
templateVisitAll(this, ast.children); templateVisitAll(this, ast.children);
// Finish element construction mode. // Finish element construction mode.
this.instruction(this._creationMode, ast.endSourceSpan || ast.sourceSpan, R3.elementEnd); this.instruction(this._creationMode, ast.endSourceSpan || ast.sourceSpan, R3.elementEnd);
} }
private _visitDirectives(directives: DirectiveAst[], implicit: o.Expression, nodeIndex: number) { private _visitDirectives(
directives: DirectiveAst[], implicit: o.Expression, nodeIndex: number,
directiveIndexMap: Map<any, number>) {
for (let directive of directives) { for (let directive of directives) {
const directiveIndex = this.allocateDirective(); const directiveIndex = directiveIndexMap.get(directive.directive.type.reference);
// Creation mode // Creation mode
// e.g. D(0, TodoComponentDef.n(), TodoComponentDef); // e.g. D(0, TodoComponentDef.n(), TodoComponentDef);
@ -258,28 +388,16 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
// node is referenced multiple times to know that it must generate the reference into a // node is referenced multiple times to know that it must generate the reference into a
// temporary. // temporary.
this.instruction(
this._creationMode, directive.sourceSpan, R3.directiveCreate, o.literal(directiveIndex),
this.definitionOf(directiveType, kind)
.callMethod(R3.NEW_METHOD, [], directive.sourceSpan),
this.definitionOf(directiveType, kind));
// Bindings // Bindings
for (const input of directive.inputs) { for (const input of directive.inputs) {
const convertedBinding = convertPropertyBinding( const convertedBinding = convertPropertyBinding(
null, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate); this, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
this._bindingMode.push(...convertedBinding.stmts); this._bindingMode.push(...convertedBinding.stmts);
this.instruction( this.instruction(
this._bindingMode, directive.sourceSpan, R3.elementProperty, this._bindingMode, directive.sourceSpan, R3.elementProperty,
o.literal(input.templateName), o.literal(nodeIndex), convertedBinding.currValExpr); o.literal(input.templateName), o.literal(nodeIndex), convertedBinding.currValExpr);
} }
// e.g. TodoComponentDef.h(0, 0);
this._hostMode.push(
this.definitionOf(directiveType, kind)
.callMethod(R3.HOST_BINDING_METHOD, [o.literal(directiveIndex), o.literal(nodeIndex)])
.toStmt());
// e.g. TodoComponentDef.r(0, 0); // e.g. TodoComponentDef.r(0, 0);
this._refreshMode.push( this._refreshMode.push(
this.definitionOf(directiveType, kind) this.definitionOf(directiveType, kind)
@ -289,32 +407,35 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
} }
visitEmbeddedTemplate(ast: EmbeddedTemplateAst) { visitEmbeddedTemplate(ast: EmbeddedTemplateAst) {
const templateIndex = this.allocateNode(); const templateIndex = this.allocateDataSlot();
const templateName = `C${templateIndex}Template`; const templateName = `C${templateIndex}Template`;
const templateContext = `ctx${this.level}`; const templateContext = `ctx${this.level}`;
// TODO(chuckj): attrs? const {directivesArray, directiveIndexMap} = this._computeDirectivesArray(ast.directives);
// e.g. C(1, C1Template) // e.g. C(1, C1Template)
this.instruction( this.instruction(
this._creationMode, ast.sourceSpan, R3.containerCreate, o.literal(templateIndex), this._creationMode, ast.sourceSpan, R3.containerCreate, o.literal(templateIndex),
o.variable(templateName)); directivesArray, o.variable(templateName));
// Generate directies // e.g. Cr(1)
this.instruction(
this._refreshMode, ast.sourceSpan, R3.containerRefreshStart, o.literal(templateIndex));
// Generate directives
this._visitDirectives( this._visitDirectives(
ast.directives, o.variable(this.contextParameter), ast.directives, o.variable(this.contextParameter), templateIndex, directiveIndexMap);
// TODO(chuckj): This should be the element index of the element that contained the template
templateIndex); // e.g. cr();
this.instruction(this._refreshMode, ast.sourceSpan, R3.containerRefreshEnd);
// Create the template function // Create the template function
const templateVisitor = new TemplateDefinitionBuilder( const templateVisitor = new TemplateDefinitionBuilder(
this.outputCtx, this.constantPool, templateContext, this.level + 1); this.outputCtx, this.constantPool, templateContext, this.bindingScope.nestedScope(),
this.level + 1);
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children); const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children);
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null)); this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
// Terminate the definition
this.instruction(this._creationMode, ast.sourceSpan, R3.containerEnd);
} }
// These should be handled in the template or element directly. // These should be handled in the template or element directly.
@ -325,7 +446,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
readonly visitAttr = invalid; readonly visitAttr = invalid;
visitBoundText(ast: BoundTextAst) { visitBoundText(ast: BoundTextAst) {
const nodeIndex = this.allocateNode(); const nodeIndex = this.allocateDataSlot();
// Creation mode // Creation mode
this.instruction(this._creationMode, ast.sourceSpan, R3.text, o.literal(nodeIndex)); this.instruction(this._creationMode, ast.sourceSpan, R3.text, o.literal(nodeIndex));
@ -333,20 +454,21 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
// Refresh mode // Refresh mode
this.instruction( this.instruction(
this._refreshMode, ast.sourceSpan, R3.textCreateBound, o.literal(nodeIndex), this._refreshMode, ast.sourceSpan, R3.textCreateBound, o.literal(nodeIndex),
this.bind(o.variable(this.contextParameter), ast.value, ast.sourceSpan)); this.bind(o.variable(CONTEXT_NAME), ast.value, ast.sourceSpan));
} }
visitText(ast: TextAst) { visitText(ast: TextAst) {
// Text is defined in creation mode only. // Text is defined in creation mode only.
this.instruction(this._creationMode, ast.sourceSpan, R3.text, o.literal(ast.value)); this.instruction(
this._creationMode, ast.sourceSpan, R3.text, o.literal(this.allocateDataSlot()),
o.literal(ast.value));
} }
// These should be handled in the template or element directly // These should be handled in the template or element directly
readonly visitDirective = invalid; readonly visitDirective = invalid;
readonly visitDirectiveProperty = invalid; readonly visitDirectiveProperty = invalid;
private allocateDirective() { return this._dataIndex++; } private allocateDataSlot() { return this._dataIndex++; }
private allocateNode() { return this._dataIndex++; }
private bindingContext() { return `${this._bindingContext++}`; } private bindingContext() { return `${this._bindingContext++}`; }
private instruction( private instruction(
@ -373,13 +495,13 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
private convertPropertyBinding(implicit: o.Expression, value: AST): o.Expression { private convertPropertyBinding(implicit: o.Expression, value: AST): o.Expression {
const convertedPropertyBinding = convertPropertyBinding( const convertedPropertyBinding = convertPropertyBinding(
null, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolate); this, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolate);
this._refreshMode.push(...convertedPropertyBinding.stmts); this._refreshMode.push(...convertedPropertyBinding.stmts);
return convertedPropertyBinding.currValExpr; return convertedPropertyBinding.currValExpr;
} }
private bind(implicit: o.Expression, value: AST, sourceSpan: ParseSourceSpan): o.Expression { private bind(implicit: o.Expression, value: AST, sourceSpan: ParseSourceSpan): o.Expression {
return o.importExpr(R3.bind).callFn([this.convertPropertyBinding(implicit, value)]); return this.convertPropertyBinding(implicit, value);
} }
} }

View File

@ -110,8 +110,8 @@ export class BoundEventAst implements TemplateAst {
*/ */
export class ReferenceAst implements TemplateAst { export class ReferenceAst implements TemplateAst {
constructor( constructor(
public name: string, public value: CompileTokenMetadata, public sourceSpan: ParseSourceSpan) { public name: string, public value: CompileTokenMetadata, public originalValue: string,
} public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any { visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitReference(this, context); return visitor.visitReference(this, context);
} }

View File

@ -574,7 +574,7 @@ class TemplateParseVisitor implements html.Visitor {
if ((elOrDirRef.value.length === 0 && directive.isComponent) || if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
(elOrDirRef.isReferenceToDirective(directive))) { (elOrDirRef.isReferenceToDirective(directive))) {
targetReferences.push(new ReferenceAst( targetReferences.push(new ReferenceAst(
elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.value,
elOrDirRef.sourceSpan)); elOrDirRef.sourceSpan));
matchedReferences.add(elOrDirRef.name); matchedReferences.add(elOrDirRef.name);
} }
@ -598,7 +598,8 @@ class TemplateParseVisitor implements html.Visitor {
if (isTemplateElement) { if (isTemplateElement) {
refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef); refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef);
} }
targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan)); targetReferences.push(
new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan));
} }
}); });
return directiveAsts; return directiveAsts;

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AotCompilerHost, AotCompilerOptions, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, Lexer, NgModuleResolver, Parser, PipeResolver, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, TypeScriptEmitter, analyzeNgModules, createAotUrlResolver} from '@angular/compiler'; import {AotCompilerHost, AotCompilerOptions, AotSummaryResolver, CompileDirectiveMetadata, CompileMetadataResolver, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, Lexer, NgModuleResolver, Parser, PipeResolver, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, TypeScriptEmitter, analyzeNgModules, createAotUrlResolver} from '@angular/compiler';
import {ViewEncapsulation} from '@angular/core'; import {ViewEncapsulation} from '@angular/core';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ConstantPool} from '../../src/constant_pool'; import {ConstantPool} from '../../src/constant_pool';
import * as o from '../../src/output/output_ast'; import * as o from '../../src/output/output_ast';
import {compileComponent} from '../../src/render3/r3_view_compiler'; import {compileComponent, compileDirective} from '../../src/render3/r3_view_compiler';
import {OutputContext} from '../../src/util'; import {OutputContext} from '../../src/util';
import {MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, arrayToMockDir, settings, setup, toMockFileArray} from '../aot/test_util'; import {MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, arrayToMockDir, settings, setup, toMockFileArray} from '../aot/test_util';
@ -99,8 +99,327 @@ describe('r3_view_compiler', () => {
const result = compile(files, angularFiles); const result = compile(files, angularFiles);
expect(result.source).toContain('@angular/core'); expect(result.source).toContain('@angular/core');
}); });
/* These tests are codified version of the tests in compiler_canonical_spec.ts. Every
* test in compiler_canonical_spec.ts should have a corresponding test here.
*/
describe('compiler conformance', () => {
describe('elements', () => {
it('should translate DOM structure', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`<div class="my-app" title="Hello">Hello <b>World</b>!</div>\`
})
export class MyComponent {}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
// The factory should look like this:
const factory = 'factory: () => { return new MyComponent(); }';
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
template: (ctx: IDENT, cm: IDENT) => {
if (cm) {
IDENT.ɵE(0, 'div', IDENT);
IDENT.ɵT(1, 'Hello ');
IDENT.ɵE(2, 'b');
IDENT.ɵT(3, 'World');
IDENT.ɵe();
IDENT.ɵT(4, '!');
IDENT.ɵe();
}
}
`;
// The compiler should also emit a const array like this:
const constants = `const IDENT = ['class', 'my-app', 'title', 'Hello'];`;
const result = compile(files, angularFiles);
expectEmit(result.source, factory, 'Incorrect factory');
expectEmit(result.source, template, 'Incorrect template');
expectEmit(result.source, constants, 'Incorrect shared constants');
});
});
});
describe('components & directives', () => {
it('should instantiate directives', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule} from '@angular/core';
@Component({selector: 'child', template: 'child-view'})
export class ChildComponent {}
@Directive({selector: '[some-directive]'})
export class SomeDirective {}
@Component({selector: 'my-component', template: '<child some-directive></child>!'})
export class MyComponent {}
@NgModule({declarations: [ChildComponent, SomeDirective, MyComponent]})
export class MyModule{}
`
}
};
// ChildComponent definition should be:
const ChildComponentDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({
tag: 'child',
factory: () => { return new ChildComponent(); },
template: (ctx: IDENT, cm: IDENT) => {
if (cm) {
IDENT.ɵT(0, 'child-view');
}
}
});`;
// SomeDirective definition should be:
const SomeDirectiveDefinition = `
static ngDirectiveDef = IDENT.ɵdefineDirective({
factory: () => {return new SomeDirective(); }
});
`;
// MyComponent definition should be:
const MyComponentDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({
tag: 'my-component',
factory: () => { return new MyComponent(); },
template: (ctx: IDENT, cm: IDENT) => {
if (cm) {
IDENT.ɵE(0, ChildComponent, IDENT, IDENT);
IDENT.ɵe();
IDENT.ɵT(3, '!');
}
ChildComponent.ngComponentDef.r(1, 0);
SomeDirective.ngDirectiveDef.r(2, 0);
}
});
`;
// The following constants should be emitted as well.
const AttributesConstant = `const IDENT = ['some-directive', ''];`;
const DirectivesConstant = `const IDENT = [SomeDirective];`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, ChildComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
expectEmit(source, SomeDirectiveDefinition, 'Incorrect SomeDirective.ngDirectiveDef');
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponentDefinition.ngComponentDef');
expectEmit(source, AttributesConstant, 'Incorrect shared attributes constant');
expectEmit(source, DirectivesConstant, 'Incorrect share directives constant');
});
it('should support structural directives', () => {
const files = {
app: {
'spec.ts': `
import {Component, Directive, NgModule, TemplateRef} from '@angular/core';
@Directive({selector: '[if]'})
export class IfDirective {
constructor(template: TemplateRef<any>) { }
}
@Component({
selector: 'my-component',
template: '<ul #foo><li *if>{{salutation}} {{foo}}</li></ul>'
})
export class MyComponent {
salutation = 'Hello';
}
@NgModule({declarations: [IfDirective, MyComponent]})
export class MyModule {}
`
}
};
const IfDirectiveDefinition = `
static ngDirectiveDef = IDENT.ɵdefineDirective({
factory: () => { return new IfDirective(IDENT.ɵinjectTemplateRef()); }
});`;
const MyComponentDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({
tag: 'my-component',
factory: () => { return new MyComponent(); },
template: (ctx: IDENT, cm: IDENT) => {
if (cm) {
IDENT.ɵE(0, 'ul', null, null, IDENT);
IDENT.ɵC(2, IDENT, C2Template);
IDENT.ɵe();
}
const IDENT = IDENT.ɵm(1);
IDENT.ɵcR(2);
IfDirective.ngDirectiveDef.r(3,2);
IDENT.ɵcr();
function C2Template(ctx0: IDENT, cm: IDENT) {
if (cm) {
IDENT.ɵE(0, 'li');
IDENT.ɵT(1);
IDENT.ɵe();
}
IDENT.ɵt(1, IDENT.ɵb2('', ctx.salutation, ' ', IDENT, ''));
}
}
});`;
const locals = `const IDENT = ['foo', ''];`;
const directives = `const IDENT = [IfDirective];`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, IfDirectiveDefinition, 'Incorrect IfDirective.ngDirectiveDef');
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponent.ngComponentDef');
expectEmit(source, locals, 'Incorrect share locals constant');
expectEmit(source, directives, 'Incorrect shared directive constant');
});
it('local reference', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({selector: 'my-component', template: '<input #user>Hello {{user.value}}!'})
export class MyComponent {}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const MyComponentDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({
tag: 'my-component',
factory: () => { return new MyComponent(); },
template: (ctx: IDENT, cm: IDENT) => {
if (cm) {
IDENT.ɵE(0, 'input', null, null, IDENT);
IDENT.ɵe();
IDENT.ɵT(2);
}
const IDENT = IDENT.ɵm(1);
IDENT.ɵt(2, IDENT.ɵb1('Hello ', IDENT.value, '!'));
}
});
`;
const locals = `
const IDENT = ['user', ''];
`;
const result = compile(files, angularFiles);
const source = result.source;
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponent.ngComponentDef');
expectEmit(source, locals, 'Incorrect locals constant definition');
});
});
}); });
const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/;
const OPERATOR =
/!|%|\*|\/|\^|\&|\&\&\|\||\|\||\(|\)|\{|\}|\[|\]|:|;|\.|<|<=|>|>=|=|==|===|!=|!==|=>|\+|\+\+|-|--|@|,|\.|\.\.\./;
const STRING = /\'[^'\n]*\'|"[^'\n]*"|`[^`]*`/;
const NUMBER = /[0-9]+/;
const TOKEN = new RegExp(
`^((${IDENTIFIER.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source})`);
const WHITESPACE = /^\s+/;
type Piece = string | RegExp;
const IDENT = /[A-Za-z$_][A-Za-z0-9$_]*/;
function tokenize(text: string): Piece[] {
function matches(exp: RegExp): string|false {
const m = text.match(exp);
if (!m) return false;
text = text.substr(m[0].length);
return m[0];
}
function next(): string {
const result = matches(TOKEN);
if (!result) {
throw Error(`Invalid test, no token found for '${text.substr(0, 30)}...'`);
}
matches(WHITESPACE);
return result;
}
const pieces: Piece[] = [];
matches(WHITESPACE);
while (text) {
const token = next();
if (token === 'IDENT') {
pieces.push(IDENT);
} else {
pieces.push(token);
}
}
return pieces;
}
const contextWidth = 100;
function expectEmit(source: string, emitted: string, description: string) {
const pieces = tokenize(emitted);
const expr = r(...pieces);
if (!expr.test(source)) {
let last: number = 0;
for (let i = 1; i < pieces.length; i++) {
let t = r(...pieces.slice(0, i));
let m = source.match(t);
let expected = pieces[i - 1] == IDENT ? '<IDENT>' : pieces[i - 1];
if (!m) {
const contextPieceWidth = contextWidth / 2;
fail(
`${description}: Expected to find ${expected} '${source.substr(0,last)}[<---HERE]${source.substr(last)}'`);
return;
} else {
last = (m.index || 0) + m[0].length;
}
}
fail(
'Test helper failure: Expected expression failed but the reporting logic could not find where it failed');
}
}
const IDENT_LIKE = /^[a-z][A-Z]/;
const SPECIAL_RE_CHAR = /\/|\(|\)|\||\*|\+|\[|\]|\{|\}/g;
function r(...pieces: (string | RegExp)[]): RegExp {
let results: string[] = [];
let first = true;
for (const piece of pieces) {
if (!first)
results.push(`\\s${typeof piece === 'string' && IDENT_LIKE.test(piece) ? '+' : '*'}`);
first = false;
if (typeof piece === 'string') {
results.push(piece.replace(SPECIAL_RE_CHAR, s => '\\' + s));
} else {
results.push('(' + piece.source + ')');
}
}
return new RegExp(results.join(''));
}
function compile( function compile(
data: MockDirectory, angularFiles: MockData, options: AotCompilerOptions = {}, data: MockDirectory, angularFiles: MockData, options: AotCompilerOptions = {},
errorCollector: (error: any, fileName?: string) => void = error => { throw error; }) { errorCollector: (error: any, fileName?: string) => void = error => { throw error; }) {
@ -156,7 +475,7 @@ function compile(
const directives = Array.from(analyzedModules.ngModuleByPipeOrDirective.keys()); const directives = Array.from(analyzedModules.ngModuleByPipeOrDirective.keys());
const fakeOuputContext: OutputContext = { const fakeOutputContext: OutputContext = {
genFilePath: 'fakeFactory.ts', genFilePath: 'fakeFactory.ts',
statements: [], statements: [],
importExpr(symbol: StaticSymbol, typeParams: o.Type[]) { importExpr(symbol: StaticSymbol, typeParams: o.Type[]) {
@ -182,7 +501,10 @@ function compile(
// Compile the directives. // Compile the directives.
for (const directive of directives) { for (const directive of directives) {
const module = analyzedModules.ngModuleByPipeOrDirective.get(directive) !; const module = analyzedModules.ngModuleByPipeOrDirective.get(directive);
if (!module || !module.type.reference.filePath.startsWith('/app')) {
continue;
}
if (resolver.isDirective(directive)) { if (resolver.isDirective(directive)) {
const metadata = resolver.getDirectiveMetadata(directive); const metadata = resolver.getDirectiveMetadata(directive);
if (metadata.isComponent) { if (metadata.isComponent) {
@ -196,17 +518,24 @@ function compile(
const parsedTemplate = templateParser.parse( const parsedTemplate = templateParser.parse(
metadata, htmlAst, directives, pipes, module.schemas, fakeUrl, false); metadata, htmlAst, directives, pipes, module.schemas, fakeUrl, false);
compileComponent(fakeOuputContext, metadata, parsedTemplate.template, staticReflector); compileComponent(fakeOutputContext, metadata, parsedTemplate.template, staticReflector);
} else {
compileDirective(fakeOutputContext, metadata, staticReflector);
} }
} }
} }
fakeOuputContext.statements.unshift(...fakeOuputContext.constantPool.statements); fakeOutputContext.statements.unshift(...fakeOutputContext.constantPool.statements);
const emitter = new TypeScriptEmitter(); const emitter = new TypeScriptEmitter();
const result = emitter.emitStatementsAndContext( const moduleName = compilerHost.fileNameToModuleName(
fakeOuputContext.genFilePath, fakeOuputContext.statements, '', false); fakeOutputContext.genFilePath, fakeOutputContext.genFilePath);
return {source: result.sourceText, outputContext: fakeOuputContext}; const result = emitter.emitStatementsAndContext(
fakeOutputContext.genFilePath, fakeOutputContext.statements, '', false,
/* referenceFilter */ undefined,
/* importFilter */ e => e.moduleName != null && e.moduleName.startsWith('/app'));
return {source: result.sourceText, outputContext: fakeOutputContext};
} }

View File

@ -407,7 +407,7 @@ class ArrayConsole implements Console {
expectVisitedNode( expectVisitedNode(
new class extends new class extends
NullVisitor{visitReference(ast: ReferenceAst, context: any): any{return ast;}}, NullVisitor{visitReference(ast: ReferenceAst, context: any): any{return ast;}},
new ReferenceAst('foo', null !, null !)); new ReferenceAst('foo', null !, null !, null !));
}); });
it('should visit VariableAst', () => { it('should visit VariableAst', () => {
@ -474,7 +474,7 @@ class ArrayConsole implements Console {
new NgContentAst(0, 0, null !), new NgContentAst(0, 0, null !),
new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null !), new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null !),
new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null !, null !), new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null !, null !),
new ReferenceAst('foo', null !, null !), new VariableAst('foo', 'bar', null !), new ReferenceAst('foo', null !, 'bar', null !), new VariableAst('foo', 'bar', null !),
new BoundEventAst('foo', 'bar', 'goo', null !, null !), new BoundEventAst('foo', 'bar', 'goo', null !, null !),
new BoundElementPropertyAst('foo', null !, null !, null !, 'bar', null !), new BoundElementPropertyAst('foo', null !, null !, null !, 'bar', null !),
new AttrAst('foo', 'bar', null !), new BoundTextAst(null !, 0, null !), new AttrAst('foo', 'bar', null !), new BoundTextAst(null !, 0, null !),