refactor(compiler): don’t require `id` in metadata and use `appId`

The output of the compiler has to be the same
given the same input. Requiring a unique id for
every type already during compilation makes it
hard to parallelize compilation.

Part of #3605
Closes #4397
This commit is contained in:
Tobias Bosch 2015-09-28 10:30:33 -07:00
parent cd0e9c9cd4
commit 8ff65a30c7
13 changed files with 327 additions and 186 deletions

View File

@ -8,7 +8,8 @@ import {
endElement, endElement,
beginComponent, beginComponent,
endComponent, endComponent,
embeddedTemplate embeddedTemplate,
CompiledTemplate
} from 'angular2/src/core/compiler/template_commands'; } from 'angular2/src/core/compiler/template_commands';
import { import {
TemplateAst, TemplateAst,
@ -30,7 +31,12 @@ import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadat
import {SourceExpressions, SourceExpression, moduleRef} from './source_module'; import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
import {ViewEncapsulation} from 'angular2/src/core/render/api'; import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {shimHostAttribute, shimContentAttribute} from './style_compiler'; import {
shimHostAttribute,
shimContentAttribute,
shimContentAttributeExpr,
shimHostAttributeExpr
} from './style_compiler';
import {escapeSingleQuoteString} from './util'; import {escapeSingleQuoteString} from './util';
import {Injectable} from 'angular2/src/core/di'; import {Injectable} from 'angular2/src/core/di';
@ -40,21 +46,25 @@ const IMPLICIT_TEMPLATE_VAR = '\$implicit';
@Injectable() @Injectable()
export class CommandCompiler { export class CommandCompiler {
compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[], compileComponentRuntime(component: CompileDirectiveMetadata, appId: string, templateId: number,
changeDetectorFactories: Function[], template: TemplateAst[], changeDetectorFactories: Function[],
componentTemplateFactory: Function): TemplateCmd[] { componentTemplateFactory: Function): TemplateCmd[] {
var visitor = new CommandBuilderVisitor( var visitor = new CommandBuilderVisitor(
new RuntimeCommandFactory(componentTemplateFactory, changeDetectorFactories), component, 0); new RuntimeCommandFactory(component, appId, templateId, componentTemplateFactory,
changeDetectorFactories),
0);
templateVisitAll(visitor, template); templateVisitAll(visitor, template);
return visitor.result; return visitor.result;
} }
compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[], compileComponentCodeGen(component: CompileDirectiveMetadata, appIdExpr: string,
templateIdExpr: string, template: TemplateAst[],
changeDetectorFactoryExpressions: string[], changeDetectorFactoryExpressions: string[],
componentTemplateFactory: Function): SourceExpression { componentTemplateFactory: Function): SourceExpression {
var visitor = new CommandBuilderVisitor( var visitor = new CommandBuilderVisitor(
new CodegenCommandFactory(componentTemplateFactory, changeDetectorFactoryExpressions), new CodegenCommandFactory(component, appIdExpr, templateIdExpr, componentTemplateFactory,
component, 0); changeDetectorFactoryExpressions),
0);
templateVisitAll(visitor, template); templateVisitAll(visitor, template);
var source = `[${visitor.result.join(',')}]`; var source = `[${visitor.result.join(',')}]`;
return new SourceExpression([], source); return new SourceExpression([], source);
@ -78,11 +88,27 @@ interface CommandFactory<R> {
} }
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> { class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
constructor(public componentTemplateFactory: Function, constructor(private component: CompileDirectiveMetadata, private appId: string,
public changeDetectorFactories: Function[]) {} private templateId: number, private componentTemplateFactory: Function,
private changeDetectorFactories: Function[]) {}
private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] { private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] {
return directives.map(directive => directive.type.runtime); return directives.map(directive => directive.type.runtime);
} }
private _addStyleShimAttributes(attrNameAndValues: string[],
localComponent: CompileDirectiveMetadata,
localTemplateId: number): string[] {
var additionalStyles = [];
if (isPresent(localComponent) &&
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
additionalStyles.push(shimHostAttribute(this.appId, localTemplateId));
additionalStyles.push('');
}
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
additionalStyles.push(shimContentAttribute(this.appId, this.templateId));
additionalStyles.push('');
}
return additionalStyles.concat(attrNameAndValues);
}
createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd { createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd {
return text(value, isBound, ngContentIndex); return text(value, isBound, ngContentIndex);
@ -91,16 +117,19 @@ class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isBound: boolean, ngContentIndex: number): TemplateCmd { isBound: boolean, ngContentIndex: number): TemplateCmd {
return beginElement(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, return beginElement(name, this._addStyleShimAttributes(attrNameAndValues, null, null),
this._mapDirectives(directives), isBound, ngContentIndex); eventTargetAndNames, variableNameAndValues, this._mapDirectives(directives),
isBound, ngContentIndex);
} }
createEndElement(): TemplateCmd { return endElement(); } createEndElement(): TemplateCmd { return endElement(); }
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
nativeShadow: boolean, ngContentIndex: number): TemplateCmd { nativeShadow: boolean, ngContentIndex: number): TemplateCmd {
return beginComponent(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, var nestedTemplate = this.componentTemplateFactory(directives[0]);
this._mapDirectives(directives), nativeShadow, ngContentIndex, return beginComponent(
this.componentTemplateFactory(directives[0])); name, this._addStyleShimAttributes(attrNameAndValues, directives[0], nestedTemplate.id),
eventTargetAndNames, variableNameAndValues, this._mapDirectives(directives), nativeShadow,
ngContentIndex, nestedTemplate);
} }
createEndComponent(): TemplateCmd { return endComponent(); } createEndComponent(): TemplateCmd { return endComponent(); }
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[], createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
@ -113,21 +142,28 @@ class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
} }
} }
function escapePrimitiveArray(data: any[]): string {
return `[${data.map( (value) => {
if (isString(value)) {
return escapeSingleQuoteString(value);
} else if (isBlank(value)) {
return 'null';
} else {
return value;
}
}).join(',')}]`;
}
class CodegenCommandFactory implements CommandFactory<string> { class CodegenCommandFactory implements CommandFactory<string> {
constructor(public componentTemplateFactory: Function, constructor(private component: CompileDirectiveMetadata, private appIdExpr: string,
public changeDetectorFactoryExpressions: string[]) {} private templateIdExpr: string, private componentTemplateFactory: Function,
private changeDetectorFactoryExpressions: string[]) {}
private _addStyleShimAttributes(attrNameAndValues: string[],
localComponent: CompileDirectiveMetadata,
localTemplateIdExpr: string): any[] {
var additionalStlyes = [];
if (isPresent(localComponent) &&
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
additionalStlyes.push(
new Expression(shimHostAttributeExpr(this.appIdExpr, localTemplateIdExpr)));
additionalStlyes.push('');
}
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
additionalStlyes.push(
new Expression(shimContentAttributeExpr(this.appIdExpr, this.templateIdExpr)));
additionalStlyes.push('');
}
return additionalStlyes.concat(attrNameAndValues);
}
createText(value: string, isBound: boolean, ngContentIndex: number): string { createText(value: string, isBound: boolean, ngContentIndex: number): string {
return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`; return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
@ -138,28 +174,27 @@ class CodegenCommandFactory implements CommandFactory<string> {
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isBound: boolean, ngContentIndex: number): string { isBound: boolean, ngContentIndex: number): string {
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(eventTargetAndNames)}, ${escapePrimitiveArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`; var attrsExpression = codeGenArray(this._addStyleShimAttributes(attrNameAndValues, null, null));
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${attrsExpression}, ${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${isBound}, ${ngContentIndex})`;
} }
createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; } createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; }
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
nativeShadow: boolean, ngContentIndex: number): string { nativeShadow: boolean, ngContentIndex: number): string {
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(eventTargetAndNames)}, ${escapePrimitiveArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`; var nestedCompExpr = this.componentTemplateFactory(directives[0]);
var attrsExpression = codeGenArray(
this._addStyleShimAttributes(attrNameAndValues, directives[0], `${nestedCompExpr}.id`));
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${attrsExpression}, ${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${nativeShadow}, ${ngContentIndex}, ${nestedCompExpr})`;
} }
createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; } createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; }
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[], createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isMerged: boolean, ngContentIndex: number, children: string[]): string { isMerged: boolean, ngContentIndex: number, children: string[]): string {
return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(variableNameAndValues)}, ` + return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${codeGenArray(attrNameAndValues)}, ${codeGenArray(variableNameAndValues)}, ` +
`[${_escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, [${children.join(',')}])`; `${codeGenDirectivesArray(directives)}, ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, [${children.join(',')}])`;
} }
} }
function _escapeDirectives(directives: CompileDirectiveMetadata[]): string[] {
return directives.map(directiveType =>
`${moduleRef(directiveType.type.moduleId)}${directiveType.type.name}`);
}
function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[], context: any): function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[], context: any):
any { any {
templateVisitAll(visitor, asts, context); templateVisitAll(visitor, asts, context);
@ -169,22 +204,11 @@ function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
class CommandBuilderVisitor<R> implements TemplateAstVisitor { class CommandBuilderVisitor<R> implements TemplateAstVisitor {
result: R[] = []; result: R[] = [];
transitiveNgContentCount: number = 0; transitiveNgContentCount: number = 0;
constructor(public commandFactory: CommandFactory<R>, public component: CompileDirectiveMetadata, constructor(public commandFactory: CommandFactory<R>, public embeddedTemplateIndex: number) {}
public embeddedTemplateIndex: number) {}
private _readAttrNameAndValues(localComponent: CompileDirectiveMetadata, private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
directives: CompileDirectiveMetadata[],
attrAsts: TemplateAst[]): string[] { attrAsts: TemplateAst[]): string[] {
var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []); var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []);
if (isPresent(localComponent) &&
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
attrNameAndValues.push(shimHostAttribute(localComponent.type.id));
attrNameAndValues.push('');
}
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
attrNameAndValues.push(shimContentAttribute(this.component.type.id));
attrNameAndValues.push('');
}
directives.forEach(directiveMeta => { directives.forEach(directiveMeta => {
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => { StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
attrNameAndValues.push(name); attrNameAndValues.push(name);
@ -201,8 +225,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
} }
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
this.embeddedTemplateIndex++; this.embeddedTemplateIndex++;
var childVisitor = var childVisitor = new CommandBuilderVisitor(this.commandFactory, this.embeddedTemplateIndex);
new CommandBuilderVisitor(this.commandFactory, this.component, this.embeddedTemplateIndex);
templateVisitAll(childVisitor, ast.children); templateVisitAll(childVisitor, ast.children);
var isMerged = childVisitor.transitiveNgContentCount > 0; var isMerged = childVisitor.transitiveNgContentCount > 0;
var variableNameAndValues = []; var variableNameAndValues = [];
@ -215,7 +238,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
directiveAst.visit(this, new DirectiveContext(index, [], [], directives)); directiveAst.visit(this, new DirectiveContext(index, [], [], directives));
}); });
this.result.push(this.commandFactory.createEmbeddedTemplate( this.result.push(this.commandFactory.createEmbeddedTemplate(
this.embeddedTemplateIndex, this._readAttrNameAndValues(null, directives, ast.attrs), this.embeddedTemplateIndex, this._readAttrNameAndValues(directives, ast.attrs),
variableNameAndValues, directives, isMerged, ast.ngContentIndex, childVisitor.result)); variableNameAndValues, directives, isMerged, ast.ngContentIndex, childVisitor.result));
this.transitiveNgContentCount += childVisitor.transitiveNgContentCount; this.transitiveNgContentCount += childVisitor.transitiveNgContentCount;
this.embeddedTemplateIndex = childVisitor.embeddedTemplateIndex; this.embeddedTemplateIndex = childVisitor.embeddedTemplateIndex;
@ -238,7 +261,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
}); });
eventTargetAndNames = removeKeyValueArrayDuplicates(eventTargetAndNames); eventTargetAndNames = removeKeyValueArrayDuplicates(eventTargetAndNames);
var attrNameAndValues = this._readAttrNameAndValues(component, directives, ast.attrs); var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
if (isPresent(component)) { if (isPresent(component)) {
this.result.push(this.commandFactory.createBeginComponent( this.result.push(this.commandFactory.createBeginComponent(
ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives, ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
@ -306,4 +329,30 @@ class DirectiveContext {
constructor(public index: number, public eventTargetAndNames: string[], constructor(public index: number, public eventTargetAndNames: string[],
public targetVariableNameAndValues: any[], public targetVariableNameAndValues: any[],
public targetDirectives: CompileDirectiveMetadata[]) {} public targetDirectives: CompileDirectiveMetadata[]) {}
} }
class Expression {
constructor(public value: string) {}
}
function escapeValue(value: any): string {
if (value instanceof Expression) {
return value.value;
} else if (isString(value)) {
return escapeSingleQuoteString(value);
} else if (isBlank(value)) {
return 'null';
} else {
return `${value}`;
}
}
function codeGenArray(data: any[]): string {
return `[${data.map(escapeValue).join(',')}]`;
}
function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
var expressions = directives.map(
directiveType => `${moduleRef(directiveType.type.moduleId)}${directiveType.type.name}`);
return `[${expressions.join(',')}]`;
}

View File

@ -22,27 +22,22 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g; var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
export class CompileTypeMetadata { export class CompileTypeMetadata {
id: number;
runtime: Type; runtime: Type;
name: string; name: string;
moduleId: string; moduleId: string;
constructor({id, runtime, name, moduleId}: constructor({runtime, name, moduleId}: {runtime?: Type, name?: string, moduleId?: string} = {}) {
{id?: number, runtime?: Type, name?: string, moduleId?: string} = {}) {
this.id = id;
this.runtime = runtime; this.runtime = runtime;
this.name = name; this.name = name;
this.moduleId = moduleId; this.moduleId = moduleId;
} }
static fromJson(data: StringMap<string, any>): CompileTypeMetadata { static fromJson(data: StringMap<string, any>): CompileTypeMetadata {
return new CompileTypeMetadata( return new CompileTypeMetadata({name: data['name'], moduleId: data['moduleId']});
{id: data['id'], name: data['name'], moduleId: data['moduleId']});
} }
toJson(): StringMap<string, any> { toJson(): StringMap<string, any> {
return { return {
// Note: Runtime type can't be serialized... // Note: Runtime type can't be serialized...
'id': this.id,
'name': this.name, 'name': this.name,
'moduleId': this.moduleId 'moduleId': this.moduleId
}; };
@ -253,12 +248,8 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
componentSelector: string): CompileDirectiveMetadata { componentSelector: string): CompileDirectiveMetadata {
var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate(); var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate();
return CompileDirectiveMetadata.create({ return CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({ type: new CompileTypeMetadata(
runtime: Object, {runtime: Object, name: `Host${componentType.name}`, moduleId: componentType.moduleId}),
id: (componentType.id * -1) - 1,
name: `Host${componentType.name}`,
moduleId: componentType.moduleId
}),
template: new CompileTemplateMetadata( template: new CompileTemplateMetadata(
{template: template, templateUrl: '', styles: [], styleUrls: [], ngContentSelectors: []}), {template: template, templateUrl: '', styles: [], styleUrls: [], ngContentSelectors: []}),
changeDetection: ChangeDetectionStrategy.Default, changeDetection: ChangeDetectionStrategy.Default,

View File

@ -25,7 +25,6 @@ var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
@Injectable() @Injectable()
export class RuntimeMetadataResolver { export class RuntimeMetadataResolver {
private _directiveCounter = 0;
private _cache: Map<Type, cpl.CompileDirectiveMetadata> = new Map(); private _cache: Map<Type, cpl.CompileDirectiveMetadata> = new Map();
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {} constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {}
@ -55,12 +54,8 @@ export class RuntimeMetadataResolver {
exportAs: directiveAnnotation.exportAs, exportAs: directiveAnnotation.exportAs,
isComponent: isPresent(templateMeta), isComponent: isPresent(templateMeta),
dynamicLoadable: true, dynamicLoadable: true,
type: new cpl.CompileTypeMetadata({ type: new cpl.CompileTypeMetadata(
id: this._directiveCounter++, {name: stringify(directiveType), moduleId: moduleId, runtime: directiveType}),
name: stringify(directiveType),
moduleId: moduleId,
runtime: directiveType
}),
template: templateMeta, template: templateMeta,
changeDetection: changeDetectionStrategy, changeDetection: changeDetectionStrategy,
properties: directiveAnnotation.properties, properties: directiveAnnotation.properties,
@ -89,8 +84,8 @@ export class RuntimeMetadataResolver {
function removeDuplicatedDirectives(directives: cpl.CompileDirectiveMetadata[]): function removeDuplicatedDirectives(directives: cpl.CompileDirectiveMetadata[]):
cpl.CompileDirectiveMetadata[] { cpl.CompileDirectiveMetadata[] {
var directivesMap: Map<number, cpl.CompileDirectiveMetadata> = new Map(); var directivesMap: Map<Type, cpl.CompileDirectiveMetadata> = new Map();
directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); }); directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.runtime, dirMeta); });
return MapWrapper.values(directivesMap); return MapWrapper.values(directivesMap);
} }

View File

@ -13,14 +13,17 @@ import {
codeGenConcatArray, codeGenConcatArray,
codeGenMapArray, codeGenMapArray,
codeGenReplaceAll, codeGenReplaceAll,
codeGenExportVariable codeGenExportVariable,
codeGenToString
} from './util'; } from './util';
import {Injectable} from 'angular2/src/core/di'; import {Injectable} from 'angular2/src/core/di';
const COMPONENT_VARIABLE = '%COMP%'; const COMPONENT_VARIABLE = '%COMP%';
var COMPONENT_REGEX = /%COMP%/g; var COMPONENT_REGEX = /%COMP%/g;
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`; const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
const HOST_ATTR_EXPR = `'_nghost-'+${COMPONENT_VARIABLE}`;
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`; const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
const CONTENT_ATTR_EXPR = `'_ngcontent-'+${COMPONENT_VARIABLE}`;
@Injectable() @Injectable()
export class StyleCompiler { export class StyleCompiler {
@ -29,24 +32,24 @@ export class StyleCompiler {
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {} constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
compileComponentRuntime(type: CompileTypeMetadata, compileComponentRuntime(appId: string, templateId: number,
template: CompileTemplateMetadata): Promise<string[]> { template: CompileTemplateMetadata): Promise<string[]> {
var styles = template.styles; var styles = template.styles;
var styleAbsUrls = template.styleUrls; var styleAbsUrls = template.styleUrls;
return this._loadStyles(styles, styleAbsUrls, return this._loadStyles(styles, styleAbsUrls,
template.encapsulation === ViewEncapsulation.Emulated) template.encapsulation === ViewEncapsulation.Emulated)
.then(styles => styles.map( .then(styles => styles.map(style => StringWrapper.replaceAll(
style => StringWrapper.replaceAll(style, COMPONENT_REGEX, `${type.id}`))); style, COMPONENT_REGEX, componentId(appId, templateId))));
} }
compileComponentCodeGen(type: CompileTypeMetadata, compileComponentCodeGen(appIdExpression: string, templateIdExpression: string,
template: CompileTemplateMetadata): SourceExpression { template: CompileTemplateMetadata): SourceExpression {
var shim = template.encapsulation === ViewEncapsulation.Emulated; var shim = template.encapsulation === ViewEncapsulation.Emulated;
var suffix; var suffix;
if (shim) { if (shim) {
var componentId = `${ type.id}`; suffix = codeGenMapArray(
suffix = ['style'],
codeGenMapArray(['style'], `style${codeGenReplaceAll(COMPONENT_VARIABLE, componentId)}`); `style${codeGenReplaceAll(COMPONENT_VARIABLE, componentIdExpression(appIdExpression, templateIdExpression))}`);
} else { } else {
suffix = ''; suffix = '';
} }
@ -118,10 +121,28 @@ export class StyleCompiler {
} }
} }
export function shimContentAttribute(componentId: number): string { export function shimContentAttribute(appId: string, templateId: number): string {
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, `${componentId}`); return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentId(appId, templateId));
} }
export function shimHostAttribute(componentId: number): string { export function shimContentAttributeExpr(appIdExpr: string, templateIdExpr: string): string {
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, `${componentId}`); return StringWrapper.replaceAll(CONTENT_ATTR_EXPR, COMPONENT_REGEX,
componentIdExpression(appIdExpr, templateIdExpr));
} }
export function shimHostAttribute(appId: string, templateId: number): string {
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentId(appId, templateId));
}
export function shimHostAttributeExpr(appIdExpr: string, templateIdExpr: string): string {
return StringWrapper.replaceAll(HOST_ATTR_EXPR, COMPONENT_REGEX,
componentIdExpression(appIdExpr, templateIdExpr));
}
function componentId(appId: string, templateId: number): string {
return `${appId}-${templateId}`;
}
function componentIdExpression(appIdExpression: string, templateIdExpression: string): string {
return `${appIdExpression}+'-'+${codeGenToString(templateIdExpression)}`;
}

View File

@ -2,7 +2,12 @@ import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang';
import {BaseException} from 'angular2/src/core/facade/exceptions'; import {BaseException} from 'angular2/src/core/facade/exceptions';
import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection'; import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async'; import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands'; import {
CompiledTemplate,
TemplateCmd,
nextTemplateId,
CompiledHostTemplate
} from 'angular2/src/core/compiler/template_commands';
import { import {
createHostComponentMeta, createHostComponentMeta,
CompileDirectiveMetadata, CompileDirectiveMetadata,
@ -18,20 +23,26 @@ import {CommandCompiler} from './command_compiler';
import {TemplateParser} from './template_parser'; import {TemplateParser} from './template_parser';
import {TemplateNormalizer} from './template_normalizer'; import {TemplateNormalizer} from './template_normalizer';
import {RuntimeMetadataResolver} from './runtime_metadata'; import {RuntimeMetadataResolver} from './runtime_metadata';
import {APP_ID} from 'angular2/src/core/render/dom/dom_tokens';
import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler'; import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
import {IS_DART, codeGenExportVariable, escapeSingleQuoteString, codeGenValueFn} from './util'; import {IS_DART, codeGenExportVariable, escapeSingleQuoteString, codeGenValueFn} from './util';
import {Inject} from 'angular2/src/core/di';
@Injectable() @Injectable()
export class TemplateCompiler { export class TemplateCompiler {
private _compiledTemplateCache: Map<number, CompiledTemplate> = new Map(); private _hostCacheKeys: Map<Type, any> = new Map();
private _compiledTemplateDone: Map<number, Promise<CompiledTemplate>> = new Map(); private _compiledTemplateCache: Map<Type, CompiledTemplate> = new Map();
private _compiledTemplateDone: Map<Type, Promise<CompiledTemplate>> = new Map();
private _appId: string;
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver, constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
private _templateNormalizer: TemplateNormalizer, private _templateNormalizer: TemplateNormalizer,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _commandCompiler: CommandCompiler, private _commandCompiler: CommandCompiler,
private _cdCompiler: ChangeDetectionCompiler) {} private _cdCompiler: ChangeDetectionCompiler, @Inject(APP_ID) appId: string) {
this._appId = appId;
}
normalizeDirectiveMetadata(directive: normalizeDirectiveMetadata(directive:
CompileDirectiveMetadata): Promise<CompileDirectiveMetadata> { CompileDirectiveMetadata): Promise<CompileDirectiveMetadata> {
@ -63,40 +74,49 @@ export class TemplateCompiler {
})); }));
} }
compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> { compileHostComponentRuntime(type: Type): Promise<CompiledHostTemplate> {
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type); var hostCacheKey = this._hostCacheKeys.get(type);
assertComponent(compMeta); if (isBlank(hostCacheKey)) {
var hostMeta: CompileDirectiveMetadata = hostCacheKey = new Object();
createHostComponentMeta(compMeta.type, compMeta.selector); this._hostCacheKeys.set(type, hostCacheKey);
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
assertComponent(compMeta);
var hostMeta: CompileDirectiveMetadata =
createHostComponentMeta(compMeta.type, compMeta.selector);
this._compileComponentRuntime(hostMeta, [compMeta], new Set()); this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set());
return this._compiledTemplateDone.get(hostMeta.type.id); }
return this._compiledTemplateDone.get(hostCacheKey)
.then(compiledTemplate => new CompiledHostTemplate(() => compiledTemplate));
} }
clearCache() { clearCache() {
this._hostCacheKeys.clear();
this._styleCompiler.clearCache(); this._styleCompiler.clearCache();
this._compiledTemplateCache.clear(); this._compiledTemplateCache.clear();
this._compiledTemplateDone.clear(); this._compiledTemplateDone.clear();
} }
private _compileComponentRuntime(compMeta: CompileDirectiveMetadata, private _compileComponentRuntime(cacheKey: any, compMeta: CompileDirectiveMetadata,
viewDirectives: CompileDirectiveMetadata[], viewDirectives: CompileDirectiveMetadata[],
compilingComponentIds: Set<number>): CompiledTemplate { compilingComponentCacheKeys: Set<any>): CompiledTemplate {
var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.id); var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
var done = this._compiledTemplateDone.get(compMeta.type.id); var done = this._compiledTemplateDone.get(cacheKey);
if (isBlank(compiledTemplate)) { if (isBlank(compiledTemplate)) {
var styles; var styles;
var changeDetectorFactory; var changeDetectorFactory;
var commands; var commands;
var templateId = nextTemplateId();
compiledTemplate = compiledTemplate =
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactory, commands, styles]); new CompiledTemplate(templateId, (_a, _b) => [changeDetectorFactory, commands, styles]);
this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate); this._compiledTemplateCache.set(cacheKey, compiledTemplate);
compilingComponentIds.add(compMeta.type.id); compilingComponentCacheKeys.add(cacheKey);
done = done =
PromiseWrapper PromiseWrapper.all([
.all([ <any>this._styleCompiler.compileComponentRuntime(this._appId, templateId,
<any>this._styleCompiler.compileComponentRuntime(compMeta.type, compMeta.template) compMeta.template)
].concat(viewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta)))) ].concat(viewDirectives.map(dirMeta =>
this.normalizeDirectiveMetadata(dirMeta))))
.then((stylesAndNormalizedViewDirMetas: any[]) => { .then((stylesAndNormalizedViewDirMetas: any[]) => {
var childPromises = []; var childPromises = [];
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1); var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
@ -107,36 +127,38 @@ export class TemplateCompiler {
compMeta.type, compMeta.changeDetection, parsedTemplate); compMeta.type, compMeta.changeDetection, parsedTemplate);
changeDetectorFactory = changeDetectorFactories[0]; changeDetectorFactory = changeDetectorFactories[0];
styles = stylesAndNormalizedViewDirMetas[0]; styles = stylesAndNormalizedViewDirMetas[0];
commands = commands = this._compileCommandsRuntime(compMeta, templateId, parsedTemplate,
this._compileCommandsRuntime(compMeta, parsedTemplate, changeDetectorFactories, changeDetectorFactories,
compilingComponentIds, childPromises); compilingComponentCacheKeys, childPromises);
return PromiseWrapper.all(childPromises); return PromiseWrapper.all(childPromises);
}) })
.then((_) => { .then((_) => {
SetWrapper.delete(compilingComponentIds, compMeta.type.id); SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
return compiledTemplate; return compiledTemplate;
}); });
this._compiledTemplateDone.set(compMeta.type.id, done); this._compiledTemplateDone.set(cacheKey, done);
} }
return compiledTemplate; return compiledTemplate;
} }
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[], private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, templateId: number,
parsedTemplate: TemplateAst[],
changeDetectorFactories: Function[], changeDetectorFactories: Function[],
compilingComponentIds: Set<number>, compilingComponentCacheKeys: Set<Type>,
childPromises: Promise<any>[]): TemplateCmd[] { childPromises: Promise<any>[]): TemplateCmd[] {
return this._commandCompiler.compileComponentRuntime( return this._commandCompiler.compileComponentRuntime(
compMeta, parsedTemplate, changeDetectorFactories, compMeta, this._appId, templateId, parsedTemplate, changeDetectorFactories,
(childComponentDir: CompileDirectiveMetadata) => { (childComponentDir: CompileDirectiveMetadata) => {
var childCacheKey = childComponentDir.type.runtime;
var childViewDirectives: CompileDirectiveMetadata[] = var childViewDirectives: CompileDirectiveMetadata[] =
this._runtimeMetadataResolver.getViewDirectivesMetadata( this._runtimeMetadataResolver.getViewDirectivesMetadata(
childComponentDir.type.runtime); childComponentDir.type.runtime);
var childIsRecursive = SetWrapper.has(compilingComponentIds, childComponentDir.type.id); var childIsRecursive = SetWrapper.has(compilingComponentCacheKeys, childCacheKey);
var childTemplate = this._compileComponentRuntime(childComponentDir, childViewDirectives, var childTemplate = this._compileComponentRuntime(
compilingComponentIds); childCacheKey, childComponentDir, childViewDirectives, compilingComponentCacheKeys);
if (!childIsRecursive) { if (!childIsRecursive) {
// Only wait for a child if it is not a cycle // Only wait for a child if it is not a cycle
childPromises.push(this._compiledTemplateDone.get(childComponentDir.type.id)); childPromises.push(this._compiledTemplateDone.get(childCacheKey));
} }
return childTemplate; return childTemplate;
}); });
@ -147,24 +169,40 @@ export class TemplateCompiler {
var declarations = []; var declarations = [];
var templateArguments = []; var templateArguments = [];
var componentMetas: CompileDirectiveMetadata[] = []; var componentMetas: CompileDirectiveMetadata[] = [];
var isHost: boolean[] = [];
var templateIdVariable = 'templateId';
var appIdVariable = 'appId';
components.forEach(componentWithDirs => { components.forEach(componentWithDirs => {
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component; var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
assertComponent(compMeta); assertComponent(compMeta);
componentMetas.push(compMeta); componentMetas.push(compMeta);
this._processTemplateCodeGen(compMeta, isHost.push(false);
this._processTemplateCodeGen(compMeta, appIdVariable, templateIdVariable,
<CompileDirectiveMetadata[]>componentWithDirs.directives, <CompileDirectiveMetadata[]>componentWithDirs.directives,
declarations, templateArguments); declarations, templateArguments);
if (compMeta.dynamicLoadable) { if (compMeta.dynamicLoadable) {
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
componentMetas.push(hostMeta); componentMetas.push(hostMeta);
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments); isHost.push(true);
this._processTemplateCodeGen(hostMeta, appIdVariable, templateIdVariable, [compMeta],
declarations, templateArguments);
} }
}); });
ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata, ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
index: number) => { index: number) => {
var templateDataFn = codeGenValueFn([], `[${(<any[]>templateArguments[index]).join(',')}]`); var templateDataFn = codeGenValueFn([templateIdVariable, appIdVariable],
`[${(<any[]>templateArguments[index]).join(',')}]`);
var compiledTemplateExpr =
`new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${TEMPLATE_COMMANDS_MODULE_REF}nextTemplateId(),${templateDataFn})`;
var variableValueExpr;
if (isHost[index]) {
variableValueExpr =
`new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${codeGenValueFn([], compiledTemplateExpr)})`;
} else {
variableValueExpr = compiledTemplateExpr;
}
declarations.push( declarations.push(
`${codeGenExportVariable(templateVariableName(compMeta.type))}new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${compMeta.type.id},${templateDataFn});`); `${codeGenExportVariable(templateVariableName(compMeta.type))}${variableValueExpr};`);
}); });
return new SourceModule(`${templateModuleName(moduleId)}`, declarations.join('\n')); return new SourceModule(`${templateModuleName(moduleId)}`, declarations.join('\n'));
} }
@ -173,16 +211,17 @@ export class TemplateCompiler {
return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText); return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText);
} }
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata, private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata, appIdExpr: string,
directives: CompileDirectiveMetadata[], templateIdExpr: string, directives: CompileDirectiveMetadata[],
targetDeclarations: string[], targetTemplateArguments: any[][]) { targetDeclarations: string[], targetTemplateArguments: any[][]) {
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.type, compMeta.template); var styleExpr =
this._styleCompiler.compileComponentCodeGen(appIdExpr, templateIdExpr, compMeta.template);
var parsedTemplate = var parsedTemplate =
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name); this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen( var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
compMeta.type, compMeta.changeDetection, parsedTemplate); compMeta.type, compMeta.changeDetection, parsedTemplate);
var commandsExpr = this._commandCompiler.compileComponentCodeGen( var commandsExpr = this._commandCompiler.compileComponentCodeGen(
compMeta, parsedTemplate, changeDetectorsExprs.expressions, compMeta, appIdExpr, templateIdExpr, parsedTemplate, changeDetectorsExprs.expressions,
codeGenComponentTemplateFactory); codeGenComponentTemplateFactory);
addAll(styleExpr.declarations, targetDeclarations); addAll(styleExpr.declarations, targetDeclarations);

View File

@ -95,7 +95,7 @@ export class TemplateParser {
class TemplateParseVisitor implements HtmlAstVisitor { class TemplateParseVisitor implements HtmlAstVisitor {
selectorMatcher: SelectorMatcher; selectorMatcher: SelectorMatcher;
errors: string[] = []; errors: string[] = [];
directivesIndexByTypeId: Map<number, number> = new Map(); directivesIndex: Map<CompileDirectiveMetadata, number> = new Map();
constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser, constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser,
private _schemaRegistry: ElementSchemaRegistry) { private _schemaRegistry: ElementSchemaRegistry) {
this.selectorMatcher = new SelectorMatcher(); this.selectorMatcher = new SelectorMatcher();
@ -103,7 +103,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
(directive: CompileDirectiveMetadata, index: number) => { (directive: CompileDirectiveMetadata, index: number) => {
var selector = CssSelector.parse(directive.selector); var selector = CssSelector.parse(directive.selector);
this.selectorMatcher.addSelectables(selector, directive); this.selectorMatcher.addSelectables(selector, directive);
this.directivesIndexByTypeId.set(directive.type.id, index); this.directivesIndex.set(directive, index);
}); });
} }
@ -397,8 +397,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} else if (!dir1Comp && dir2Comp) { } else if (!dir1Comp && dir2Comp) {
return 1; return 1;
} else { } else {
return this.directivesIndexByTypeId.get(dir1.type.id) - return this.directivesIndex.get(dir1) - this.directivesIndex.get(dir2);
this.directivesIndexByTypeId.get(dir2.type.id);
} }
}); });
return directives; return directives;

View File

@ -59,11 +59,11 @@ export function codeGenMapArray(argNames: string[], callback: string): string {
} }
} }
export function codeGenReplaceAll(pattern: string, value: string): string { export function codeGenReplaceAll(pattern: string, expression: string): string {
if (IS_DART) { if (IS_DART) {
return `.replaceAll('${pattern}', '${value}')`; return `.replaceAll('${pattern}', ${expression})`;
} else { } else {
return `.replace(/${pattern}/g, '${value}')`; return `.replace(/${pattern}/g, ${expression})`;
} }
} }
@ -75,6 +75,14 @@ export function codeGenValueFn(params: string[], value: string): string {
} }
} }
export function codeGenToString(expr: string): string {
if (IS_DART) {
return `'\${${expr}}'`;
} else {
// JS automatically convets to string...
return expr;
}
}
export function splitAtColon(input: string, defaultValues: string[]): string[] { export function splitAtColon(input: string, defaultValues: string[]): string[] {
var parts = StringWrapper.split(input.trim(), /\s*:\s*/g); var parts = StringWrapper.split(input.trim(), /\s*:\s*/g);

View File

@ -9,23 +9,48 @@ import {
RenderEmbeddedTemplateCmd RenderEmbeddedTemplateCmd
} from 'angular2/src/core/render/render'; } from 'angular2/src/core/render/render';
var _nextTemplateId: number = 0;
export function nextTemplateId(): number {
return _nextTemplateId++;
}
/** /**
* A compiled template. This is const as we are storing it as annotation * A compiled host template.
*
* This is const as we are storing it as annotation
* for the compiled component type. * for the compiled component type.
*/ */
@CONST() @CONST()
export class CompiledTemplate { export class CompiledHostTemplate {
static getChangeDetectorFromData(data: any[]): Function { return data[0]; } // Note: _templateGetter is a function so that CompiledHostTemplate can be
static getCommandsFromData(data: any[]): TemplateCmd[] { return data[1]; } // a const!
static getSylesFromData(data: any[]): string[] { return data[2]; } constructor(private _templateGetter: Function) {}
getTemplate(): CompiledTemplate { return this._templateGetter(); }
}
/**
* A compiled template.
*/
export class CompiledTemplate {
// Note: paramGetter is a function so that we can have cycles between templates! // Note: paramGetter is a function so that we can have cycles between templates!
// paramGetter returns a tuple with: // paramGetter returns a tuple with:
// - ChangeDetector factory function // - ChangeDetector factory function
// - TemplateCmd[] // - TemplateCmd[]
// - styles // - styles
constructor(public id: number, constructor(public id: number,
public dataGetter: /*()=>[Function, TemplateCmd[], string[]]*/ Function) {} private _dataGetter: /*()=>Array<Function, TemplateCmd[], string[]>*/ Function) {}
getData(appId: string): CompiledTemplateData {
var data = this._dataGetter(appId, this.id);
return new CompiledTemplateData(data[0], data[1], data[2]);
}
}
export class CompiledTemplateData {
constructor(public changeDetectorFactory: Function, public commands: TemplateCmd[],
public styles: string[]) {}
} }
const EMPTY_ARR = CONST_EXPR([]); const EMPTY_ARR = CONST_EXPR([]);

View File

@ -14,6 +14,7 @@ import {
} from 'angular2/test_lib'; } from 'angular2/test_lib';
import {CONST_EXPR, stringify, isType, Type, isBlank} from 'angular2/src/core/facade/lang'; import {CONST_EXPR, stringify, isType, Type, isBlank} from 'angular2/src/core/facade/lang';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async'; import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {TemplateParser} from 'angular2/src/compiler/template_parser'; import {TemplateParser} from 'angular2/src/compiler/template_parser';
import { import {
@ -61,12 +62,15 @@ export class RootComp {}
export class SomeDir {} export class SomeDir {}
export class AComp {} export class AComp {}
var RootCompTypeMeta = new CompileTypeMetadata( var RootCompTypeMeta =
{id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME}); new CompileTypeMetadata({name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME});
var SomeDirTypeMeta = var SomeDirTypeMeta =
new CompileTypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME}); new CompileTypeMetadata({name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME});
var ACompTypeMeta = var ACompTypeMeta =
new CompileTypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME}); new CompileTypeMetadata({name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME});
var compTypeTemplateId: Map<CompileTypeMetadata, number> =
MapWrapper.createFromPairs([[RootCompTypeMeta, 1], [SomeDirTypeMeta, 2], [ACompTypeMeta, 3]]);
const APP_ID = 'app1';
var NESTED_COMPONENT = new CompiledTemplate(45, () => []); var NESTED_COMPONENT = new CompiledTemplate(45, () => []);
@ -221,7 +225,7 @@ export function main() {
run(rootComp, []) run(rootComp, [])
.then((data) => { .then((data) => {
expect(data).toEqual([ expect(data).toEqual([
[BEGIN_ELEMENT, 'div', ['_ngcontent-1', ''], [], [], [], false, null], [BEGIN_ELEMENT, 'div', ['_ngcontent-app1-1', ''], [], [], [], false, null],
[END_ELEMENT] [END_ELEMENT]
]); ]);
async.done(); async.done();
@ -283,7 +287,7 @@ export function main() {
[ [
BEGIN_COMPONENT, BEGIN_COMPONENT,
'a', 'a',
['_nghost-3', '', '_ngcontent-1', ''], ['_nghost-app1-3', '', '_ngcontent-app1-1', ''],
[], [],
[], [],
['ACompType'], ['ACompType'],
@ -424,7 +428,7 @@ export function main() {
describe('compileComponentRuntime', () => { describe('compileComponentRuntime', () => {
beforeEach(() => { beforeEach(() => {
componentTemplateFactory = (directive: CompileDirectiveMetadata) => { componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
return new CompiledTemplate(directive.type.id, () => []); return new CompiledTemplate(compTypeTemplateId.get(directive.type), () => []);
}; };
}); });
@ -437,7 +441,8 @@ export function main() {
var parsedTemplate = var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name); parser.parse(component.template.template, directives, component.type.name);
var commands = commandCompiler.compileComponentRuntime( var commands = commandCompiler.compileComponentRuntime(
component, parsedTemplate, changeDetectorFactories, componentTemplateFactory); component, APP_ID, compTypeTemplateId.get(component.type), parsedTemplate,
changeDetectorFactories, componentTemplateFactory);
return PromiseWrapper.resolve(humanize(commands)); return PromiseWrapper.resolve(humanize(commands));
} }
@ -448,7 +453,7 @@ export function main() {
describe('compileComponentCodeGen', () => { describe('compileComponentCodeGen', () => {
beforeEach(() => { beforeEach(() => {
componentTemplateFactory = (directive: CompileDirectiveMetadata) => { componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${directive.type.id}, ${codeGenValueFn([], '{}')})`; return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${compTypeTemplateId.get(directive.type)}, ${codeGenValueFn([], '{}')})`;
}; };
}); });
@ -461,7 +466,8 @@ export function main() {
var parsedTemplate = var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name); parser.parse(component.template.template, directives, component.type.name);
var sourceModule = commandCompiler.compileComponentCodeGen( var sourceModule = commandCompiler.compileComponentCodeGen(
component, parsedTemplate, changeDetectorFactoryExpressions, componentTemplateFactory); component, `'${APP_ID}'`, `${compTypeTemplateId.get(component.type)}`, parsedTemplate,
changeDetectorFactoryExpressions, componentTemplateFactory);
var testableModule = createTestableModule(sourceModule).getSourceWithImports(); var testableModule = createTestableModule(sourceModule).getSourceWithImports();
return evalModule(testableModule.source, testableModule.imports, null); return evalModule(testableModule.source, testableModule.imports, null);
} }

View File

@ -28,7 +28,7 @@ export function main() {
var fullDirectiveMeta: CompileDirectiveMetadata; var fullDirectiveMeta: CompileDirectiveMetadata;
beforeEach(() => { beforeEach(() => {
fullTypeMeta = new CompileTypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'}); fullTypeMeta = new CompileTypeMetadata({name: 'SomeType', moduleId: 'someUrl'});
fullTemplateMeta = new CompileTemplateMetadata({ fullTemplateMeta = new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.Emulated, encapsulation: ViewEncapsulation.Emulated,
template: '<a></a>', template: '<a></a>',

View File

@ -42,7 +42,8 @@ const IMPORT_ABS_MODULE_NAME_WITH_IMPORT =
export function main() { export function main() {
describe('StyleCompiler', () => { describe('StyleCompiler', () => {
var xhr: SpyXHR; var xhr: SpyXHR;
var typeMeta; var templateId;
var appId;
beforeEachBindings(() => { beforeEachBindings(() => {
xhr = <any>new SpyXHR(); xhr = <any>new SpyXHR();
@ -52,7 +53,8 @@ export function main() {
var compiler: StyleCompiler; var compiler: StyleCompiler;
beforeEach(inject([StyleCompiler], (_compiler) => { beforeEach(inject([StyleCompiler], (_compiler) => {
typeMeta = new CompileTypeMetadata({id: 23, moduleId: 'someUrl'}); templateId = 23;
appId = 'app1';
compiler = _compiler; compiler = _compiler;
})); }));
@ -81,8 +83,9 @@ export function main() {
return PromiseWrapper.resolve(response); return PromiseWrapper.resolve(response);
}); });
return compiler.compileComponentRuntime( return compiler.compileComponentRuntime(
typeMeta, new CompileTemplateMetadata( appId, templateId,
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation})); new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
} }
describe('no shim', () => { describe('no shim', () => {
@ -121,8 +124,8 @@ export function main() {
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation) compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-23] {\ncolor: red;\n}', 'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'span[_ngcontent-23] {\ncolor: blue;\n}' 'span[_ngcontent-app1-23] {\ncolor: blue;\n}'
]); ]);
async.done(); async.done();
}); });
@ -132,8 +135,8 @@ export function main() {
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME], encapsulation) compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-23] {\ncolor: red;\n}', 'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'span[_ngcontent-23] {\ncolor: blue;\n}' 'span[_ngcontent-app1-23] {\ncolor: blue;\n}'
]); ]);
async.done(); async.done();
}); });
@ -143,9 +146,9 @@ export function main() {
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation) compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-23] {\ncolor: red;\n}', 'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'a[_ngcontent-23] {\ncolor: green;\n}', 'a[_ngcontent-app1-23] {\ncolor: green;\n}',
'span[_ngcontent-23] {\ncolor: blue;\n}' 'span[_ngcontent-app1-23] {\ncolor: blue;\n}'
]); ]);
async.done(); async.done();
}); });
@ -198,8 +201,9 @@ export function main() {
function compile(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation): function compile(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
Promise<string[]> { Promise<string[]> {
var sourceExpression = compiler.compileComponentCodeGen( var sourceExpression = compiler.compileComponentCodeGen(
typeMeta, new CompileTemplateMetadata( `'${appId}'`, `${templateId}`,
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation})); new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports(); var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports, null); return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
}; };
@ -207,7 +211,7 @@ export function main() {
describe('no shim', () => { describe('no shim', () => {
var encapsulation = ViewEncapsulation.None; var encapsulation = ViewEncapsulation.None;
it('should compile plain css ruless', inject([AsyncTestCompleter], (async) => { it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}', 'span {color: blue}'], [], encapsulation) compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
.then(styles => { .then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']); expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
@ -240,8 +244,8 @@ export function main() {
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation) compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-23] {\ncolor: red;\n}', 'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'span[_ngcontent-23] {\ncolor: blue;\n}' 'span[_ngcontent-app1-23] {\ncolor: blue;\n}'
]); ]);
async.done(); async.done();
}); });
@ -251,8 +255,8 @@ export function main() {
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation) compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-23] {\ncolor: red;\n}', 'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'span[_ngcontent-23] {\ncolor: blue;\n}' 'span[_ngcontent-app1-23] {\ncolor: blue;\n}'
]); ]);
async.done(); async.done();
}); });

View File

@ -41,22 +41,25 @@ import {
CompiledTemplate CompiledTemplate
} from 'angular2/src/core/compiler/template_commands'; } from 'angular2/src/core/compiler/template_commands';
import {Component, View, Directive} from 'angular2/core'; import {Component, View, Directive, bind} from 'angular2/core';
import {TEST_BINDINGS} from './test_bindings'; import {TEST_BINDINGS} from './test_bindings';
import {TestContext, TestDispatcher, TestPipes} from './change_detector_mocks'; import {TestContext, TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util'; import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
import {APP_ID} from 'angular2/src/core/render/dom/dom_tokens';
// Attention: This path has to point to this test file! // Attention: This path has to point to this test file!
const THIS_MODULE = 'angular2/test/compiler/template_compiler_spec'; const THIS_MODULE = 'angular2/test/compiler/template_compiler_spec';
var THIS_MODULE_REF = moduleRef(THIS_MODULE); var THIS_MODULE_REF = moduleRef(THIS_MODULE);
const APP_ID_VALUE = 'app1';
export function main() { export function main() {
describe('TemplateCompiler', () => { describe('TemplateCompiler', () => {
var compiler: TemplateCompiler; var compiler: TemplateCompiler;
var runtimeMetadataResolver: RuntimeMetadataResolver; var runtimeMetadataResolver: RuntimeMetadataResolver;
beforeEachBindings(() => TEST_BINDINGS); beforeEachBindings(() => [bind(APP_ID).toValue(APP_ID_VALUE), TEST_BINDINGS]);
beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver], beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver],
(_compiler, _runtimeMetadataResolver) => { (_compiler, _runtimeMetadataResolver) => {
compiler = _compiler; compiler = _compiler;
@ -125,7 +128,8 @@ export function main() {
describe('compileHostComponentRuntime', () => { describe('compileHostComponentRuntime', () => {
function compile(components: Type[]): Promise<any[]> { function compile(components: Type[]): Promise<any[]> {
return compiler.compileHostComponentRuntime(components[0]).then(humanizeTemplate); return compiler.compileHostComponentRuntime(components[0])
.then((compiledHostTemplate) => humanizeTemplate(compiledHostTemplate.getTemplate()));
} }
runTests(compile); runTests(compile);
@ -307,7 +311,8 @@ class CompWithoutHost {
function testableTemplateModule(sourceModule: SourceModule, normComp: CompileDirectiveMetadata): function testableTemplateModule(sourceModule: SourceModule, normComp: CompileDirectiveMetadata):
SourceModule { SourceModule {
var resultExpression = `${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template)`; var resultExpression =
`${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template.getTemplate())`;
var testableSource = `${sourceModule.sourceWithModuleRefs} var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`; ${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
return new SourceModule(sourceModule.moduleId, testableSource); return new SourceModule(sourceModule.moduleId, testableSource);
@ -330,16 +335,15 @@ export function humanizeTemplate(template: CompiledTemplate,
if (isPresent(result)) { if (isPresent(result)) {
return result; return result;
} }
var templateData = template.getData(APP_ID_VALUE);
var commands = []; var commands = [];
var templateData = template.dataGetter();
result = { result = {
'styles': CompiledTemplate.getSylesFromData(templateData), 'styles': templateData.styles,
'commands': commands, 'commands': commands,
'cd': testChangeDetector(CompiledTemplate.getChangeDetectorFromData(templateData)) 'cd': testChangeDetector(templateData.changeDetectorFactory)
}; };
humanizedTemplates.set(template.id, result); humanizedTemplates.set(template.id, result);
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), templateData.commands);
CompiledTemplate.getCommandsFromData(templateData));
return result; return result;
} }

View File

@ -322,11 +322,11 @@ export function main() {
it('should locate directives components first and ordered by the directives array in the View', it('should locate directives components first and ordered by the directives array in the View',
() => { () => {
var dirA = CompileDirectiveMetadata.create( var dirA = CompileDirectiveMetadata.create(
{selector: '[a]', type: new CompileTypeMetadata({name: 'DirA', id: 3})}); {selector: '[a]', type: new CompileTypeMetadata({name: 'DirA'})});
var dirB = CompileDirectiveMetadata.create( var dirB = CompileDirectiveMetadata.create(
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB', id: 2})}); {selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
var dirC = CompileDirectiveMetadata.create( var dirC = CompileDirectiveMetadata.create(
{selector: '[c]', type: new CompileTypeMetadata({name: 'DirC', id: 1})}); {selector: '[c]', type: new CompileTypeMetadata({name: 'DirC'})});
var comp = CompileDirectiveMetadata.create({ var comp = CompileDirectiveMetadata.create({
selector: 'div', selector: 'div',
isComponent: true, isComponent: true,