feat(compiler): add TemplateCompiler
TemplateCompiler is the entry point to the new compiler Related to #3605 Closes #4220
This commit is contained in:
parent
eaa20f661a
commit
457b689bf0
|
@ -14,7 +14,7 @@ import {
|
||||||
ASTWithSource
|
ASTWithSource
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
|
||||||
import {DirectiveMetadata, TypeMetadata} from './api';
|
import {NormalizedDirectiveMetadata, TypeMetadata} from './directive_metadata';
|
||||||
import {
|
import {
|
||||||
TemplateAst,
|
TemplateAst,
|
||||||
ElementAst,
|
ElementAst,
|
||||||
|
@ -203,5 +203,5 @@ function _collectNestedProtoViewsVariableNames(pvVisitors: ProtoViewVisitor[]):
|
||||||
|
|
||||||
|
|
||||||
function _protoViewId(hostComponentType: TypeMetadata, pvIndex: number, viewType: string): string {
|
function _protoViewId(hostComponentType: TypeMetadata, pvIndex: number, viewType: string): string {
|
||||||
return `${hostComponentType.typeName}_${viewType}_${pvIndex}`;
|
return `${hostComponentType.name}_${viewType}_${pvIndex}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {TypeMetadata, SourceModule} from './api';
|
import {TypeMetadata} from './directive_metadata';
|
||||||
|
import {SourceExpression, moduleRef} from './source_module';
|
||||||
import {
|
import {
|
||||||
ChangeDetectorJITGenerator
|
ChangeDetectorJITGenerator
|
||||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
||||||
|
@ -15,20 +16,19 @@ import {
|
||||||
|
|
||||||
import {TemplateAst} from './template_ast';
|
import {TemplateAst} from './template_ast';
|
||||||
import {Codegen} from 'angular2/src/transform/template_compiler/change_detector_codegen';
|
import {Codegen} from 'angular2/src/transform/template_compiler/change_detector_codegen';
|
||||||
|
import {IS_DART} from './util';
|
||||||
var IS_DART = !isJsObject({});
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||||
const UTIL = "ChangeDetectionUtil";
|
const UTIL = "ChangeDetectionUtil";
|
||||||
|
|
||||||
const JS_CHANGE_DETECTOR_IMPORTS = CONST_EXPR([
|
var ABSTRACT_CHANGE_DETECTOR_MODULE =
|
||||||
['angular2/src/core/change_detection/abstract_change_detector', 'acd'],
|
moduleRef('angular2/src/core/change_detection/abstract_change_detector');
|
||||||
['angular2/src/core/change_detection/change_detection_util', 'cdu']
|
var UTIL_MODULE = moduleRef('angular2/src/core/change_detection/change_detection_util');
|
||||||
]);
|
var PREGEN_PROTO_CHANGE_DETECTOR_MODULE =
|
||||||
|
moduleRef('angular2/src/core/change_detection/pregen_proto_change_detector');
|
||||||
const DART_CHANGE_DETECTOR_IMPORTS =
|
|
||||||
CONST_EXPR([['angular2/src/core/change_detection/pregen_proto_change_detector', '_gen']]);
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class ChangeDetectionCompiler {
|
export class ChangeDetectionCompiler {
|
||||||
constructor(private _genConfig: ChangeDetectorGenConfig) {}
|
constructor(private _genConfig: ChangeDetectorGenConfig) {}
|
||||||
|
|
||||||
|
@ -52,10 +52,9 @@ export class ChangeDetectionCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
compileComponentCodeGen(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
||||||
parsedTemplate: TemplateAst[]): SourceModule {
|
parsedTemplate: TemplateAst[]): SourceExpression {
|
||||||
var changeDetectorDefinitions =
|
var changeDetectorDefinitions =
|
||||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
||||||
var imports = IS_DART ? DART_CHANGE_DETECTOR_IMPORTS : JS_CHANGE_DETECTOR_IMPORTS;
|
|
||||||
var factories = [];
|
var factories = [];
|
||||||
var sourceParts = changeDetectorDefinitions.map(definition => {
|
var sourceParts = changeDetectorDefinitions.map(definition => {
|
||||||
var codegen: any;
|
var codegen: any;
|
||||||
|
@ -63,19 +62,20 @@ export class ChangeDetectionCompiler {
|
||||||
// suffix
|
// suffix
|
||||||
// and have the same API for calling them!
|
// and have the same API for calling them!
|
||||||
if (IS_DART) {
|
if (IS_DART) {
|
||||||
codegen = new Codegen();
|
codegen = new Codegen(PREGEN_PROTO_CHANGE_DETECTOR_MODULE);
|
||||||
var className = definition.id;
|
var className = definition.id;
|
||||||
codegen.generate(componentType.typeName, className, definition);
|
codegen.generate(componentType.name, className, definition);
|
||||||
factories.push(`(dispatcher) => new ${className}(dispatcher)`);
|
factories.push(`(dispatcher) => new ${className}(dispatcher)`);
|
||||||
return codegen.toString();
|
return codegen.toString();
|
||||||
} else {
|
} else {
|
||||||
codegen = new ChangeDetectorJITGenerator(definition, `cdu.${UTIL}`,
|
codegen = new ChangeDetectorJITGenerator(
|
||||||
`acd.${ABSTRACT_CHANGE_DETECTOR}`);
|
definition, `${UTIL_MODULE}${UTIL}`,
|
||||||
|
`${ABSTRACT_CHANGE_DETECTOR_MODULE}${ABSTRACT_CHANGE_DETECTOR}`);
|
||||||
factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
|
factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
|
||||||
return codegen.generateSource();
|
return codegen.generateSource();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sourceParts.push(`var CHANGE_DETECTORS = [ ${factories.join(',')} ];`);
|
var expression = `[ ${factories.join(',')} ]`;
|
||||||
return new SourceModule(componentType.typeUrl, sourceParts.join('\n'), imports);
|
return new SourceExpression(sourceParts, expression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,16 +25,19 @@ import {
|
||||||
BoundDirectivePropertyAst,
|
BoundDirectivePropertyAst,
|
||||||
templateVisitAll
|
templateVisitAll
|
||||||
} from './template_ast';
|
} from './template_ast';
|
||||||
import {SourceModule, DirectiveMetadata, TypeMetadata} from './api';
|
import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata';
|
||||||
|
import {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} from './style_compiler';
|
||||||
import {escapeSingleQuoteString} from './util';
|
import {escapeSingleQuoteString} from './util';
|
||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
const TEMPLATE_COMMANDS_MODULE = 'angular2/src/core/compiler/template_commands';
|
export var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
|
||||||
const TEMPLATE_COMMANDS_MODULE_ALIAS = 'tc';
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class CommandCompiler {
|
export class CommandCompiler {
|
||||||
compileComponentRuntime(component: DirectiveMetadata, template: TemplateAst[],
|
compileComponentRuntime(component: NormalizedDirectiveMetadata, template: TemplateAst[],
|
||||||
componentTemplateFactory: Function): TemplateCmd[] {
|
componentTemplateFactory: Function): TemplateCmd[] {
|
||||||
var visitor =
|
var visitor =
|
||||||
new CommandBuilderVisitor(new RuntimeCommandFactory(componentTemplateFactory), component);
|
new CommandBuilderVisitor(new RuntimeCommandFactory(componentTemplateFactory), component);
|
||||||
|
@ -42,16 +45,13 @@ export class CommandCompiler {
|
||||||
return visitor.result;
|
return visitor.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(component: DirectiveMetadata, template: TemplateAst[],
|
compileComponentCodeGen(component: NormalizedDirectiveMetadata, template: TemplateAst[],
|
||||||
componentTemplateFactory: Function): SourceModule {
|
componentTemplateFactory: Function): SourceExpression {
|
||||||
var imports: string[][] = [[TEMPLATE_COMMANDS_MODULE, TEMPLATE_COMMANDS_MODULE_ALIAS]];
|
var visitor =
|
||||||
var visitor = new CommandBuilderVisitor(
|
new CommandBuilderVisitor(new CodegenCommandFactory(componentTemplateFactory), component);
|
||||||
new CodegenCommandFactory(componentTemplateFactory, TEMPLATE_COMMANDS_MODULE_ALIAS,
|
|
||||||
imports),
|
|
||||||
component);
|
|
||||||
templateVisitAll(visitor, template);
|
templateVisitAll(visitor, template);
|
||||||
var source = `var COMMANDS = [${visitor.result.join(',')}];`;
|
var source = `[${visitor.result.join(',')}]`;
|
||||||
return new SourceModule(null, source, imports);
|
return new SourceExpression([], source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,22 +59,22 @@ interface CommandFactory<R> {
|
||||||
createText(value: string, isBound: boolean, ngContentIndex: number): R;
|
createText(value: string, isBound: boolean, ngContentIndex: number): R;
|
||||||
createNgContent(ngContentIndex: number): R;
|
createNgContent(ngContentIndex: number): R;
|
||||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||||
variableNameAndValues: string[], directives: TypeMetadata[], isBound: boolean,
|
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||||
ngContentIndex: number): R;
|
isBound: boolean, ngContentIndex: number): R;
|
||||||
createEndElement(): R;
|
createEndElement(): R;
|
||||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||||
variableNameAndValues: string[], directives: TypeMetadata[],
|
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||||
nativeShadow: boolean, ngContentIndex: number): R;
|
nativeShadow: boolean, ngContentIndex: number): R;
|
||||||
createEndComponent(): R;
|
createEndComponent(): R;
|
||||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||||
directives: TypeMetadata[], isMerged: boolean, ngContentIndex: number,
|
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
||||||
children: R[]): R;
|
ngContentIndex: number, children: R[]): R;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||||
constructor(public componentTemplateFactory: Function) {}
|
constructor(public componentTemplateFactory: Function) {}
|
||||||
private _mapDirectives(directives: TypeMetadata[]): Type[] {
|
private _mapDirectives(directives: NormalizedDirectiveMetadata[]): Type[] {
|
||||||
return directives.map(directive => directive.type);
|
return directives.map(directive => directive.type.runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd {
|
createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||||
|
@ -82,14 +82,14 @@ class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||||
}
|
}
|
||||||
createNgContent(ngContentIndex: number): TemplateCmd { return ngContent(ngContentIndex); }
|
createNgContent(ngContentIndex: number): TemplateCmd { return ngContent(ngContentIndex); }
|
||||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||||
variableNameAndValues: string[], directives: TypeMetadata[], isBound: boolean,
|
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||||
ngContentIndex: number): TemplateCmd {
|
isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||||
return beginElement(name, attrNameAndValues, eventNames, variableNameAndValues,
|
return beginElement(name, attrNameAndValues, eventNames, variableNameAndValues,
|
||||||
this._mapDirectives(directives), isBound, ngContentIndex);
|
this._mapDirectives(directives), isBound, ngContentIndex);
|
||||||
}
|
}
|
||||||
createEndElement(): TemplateCmd { return endElement(); }
|
createEndElement(): TemplateCmd { return endElement(); }
|
||||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||||
variableNameAndValues: string[], directives: TypeMetadata[],
|
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||||
nativeShadow: boolean, ngContentIndex: number): TemplateCmd {
|
nativeShadow: boolean, ngContentIndex: number): TemplateCmd {
|
||||||
return beginComponent(name, attrNameAndValues, eventNames, variableNameAndValues,
|
return beginComponent(name, attrNameAndValues, eventNames, variableNameAndValues,
|
||||||
this._mapDirectives(directives), nativeShadow, ngContentIndex,
|
this._mapDirectives(directives), nativeShadow, ngContentIndex,
|
||||||
|
@ -97,8 +97,8 @@ class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||||
}
|
}
|
||||||
createEndComponent(): TemplateCmd { return endComponent(); }
|
createEndComponent(): TemplateCmd { return endComponent(); }
|
||||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||||
directives: TypeMetadata[], isMerged: boolean, ngContentIndex: number,
|
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
||||||
children: TemplateCmd[]): TemplateCmd {
|
ngContentIndex: number, children: TemplateCmd[]): TemplateCmd {
|
||||||
return embeddedTemplate(attrNameAndValues, variableNameAndValues,
|
return embeddedTemplate(attrNameAndValues, variableNameAndValues,
|
||||||
this._mapDirectives(directives), isMerged, ngContentIndex, children);
|
this._mapDirectives(directives), isMerged, ngContentIndex, children);
|
||||||
}
|
}
|
||||||
|
@ -109,42 +109,38 @@ function escapeStringArray(data: string[]): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
class CodegenCommandFactory implements CommandFactory<string> {
|
class CodegenCommandFactory implements CommandFactory<string> {
|
||||||
constructor(public componentTemplateFactory: Function, public templateCommandsModuleAlias,
|
constructor(public componentTemplateFactory: Function) {}
|
||||||
public imports: string[][]) {}
|
|
||||||
|
|
||||||
private _escapeDirectives(directives: TypeMetadata[]): string[] {
|
|
||||||
return directives.map(directiveType => {
|
|
||||||
var importAlias = `dir${this.imports.length}`;
|
|
||||||
this.imports.push([directiveType.typeUrl, importAlias]);
|
|
||||||
return `${importAlias}.${directiveType.typeName}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createText(value: string, isBound: boolean, ngContentIndex: number): string {
|
createText(value: string, isBound: boolean, ngContentIndex: number): string {
|
||||||
return `${this.templateCommandsModuleAlias}.text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
|
||||||
}
|
}
|
||||||
createNgContent(ngContentIndex: number): string {
|
createNgContent(ngContentIndex: number): string {
|
||||||
return `${this.templateCommandsModuleAlias}.ngContent(${ngContentIndex})`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${ngContentIndex})`;
|
||||||
}
|
}
|
||||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||||
variableNameAndValues: string[], directives: TypeMetadata[], isBound: boolean,
|
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||||
ngContentIndex: number): string {
|
isBound: boolean, ngContentIndex: number): string {
|
||||||
return `${this.templateCommandsModuleAlias}.beginElement(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${this._escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`;
|
||||||
}
|
}
|
||||||
createEndElement(): string { return `${this.templateCommandsModuleAlias}.endElement()`; }
|
createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; }
|
||||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||||
variableNameAndValues: string[], directives: TypeMetadata[],
|
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
||||||
nativeShadow: boolean, ngContentIndex: number): string {
|
nativeShadow: boolean, ngContentIndex: number): string {
|
||||||
return `${this.templateCommandsModuleAlias}.beginComponent(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${this._escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0], this.imports)})`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`;
|
||||||
}
|
}
|
||||||
createEndComponent(): string { return `${this.templateCommandsModuleAlias}.endComponent()`; }
|
createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; }
|
||||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||||
directives: TypeMetadata[], isMerged: boolean, ngContentIndex: number,
|
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
||||||
children: string[]): string {
|
ngContentIndex: number, children: string[]): string {
|
||||||
return `${this.templateCommandsModuleAlias}.embeddedTemplate(${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(variableNameAndValues)}, [${this._escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, [${children.join(',')}])`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, [${children.join(',')}])`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _escapeDirectives(directives: NormalizedDirectiveMetadata[]): 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);
|
||||||
|
@ -154,18 +150,19 @@ 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: DirectiveMetadata) {}
|
constructor(public commandFactory: CommandFactory<R>,
|
||||||
|
public component: NormalizedDirectiveMetadata) {}
|
||||||
|
|
||||||
private _readAttrNameAndValues(localComponent: DirectiveMetadata,
|
private _readAttrNameAndValues(localComponent: NormalizedDirectiveMetadata,
|
||||||
attrAsts: TemplateAst[]): string[] {
|
attrAsts: TemplateAst[]): string[] {
|
||||||
var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []);
|
var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []);
|
||||||
if (isPresent(localComponent) &&
|
if (isPresent(localComponent) &&
|
||||||
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
|
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||||
attrNameAndValues.push(shimHostAttribute(localComponent.type));
|
attrNameAndValues.push(shimHostAttribute(localComponent.type.id));
|
||||||
attrNameAndValues.push('');
|
attrNameAndValues.push('');
|
||||||
}
|
}
|
||||||
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
|
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||||
attrNameAndValues.push(shimContentAttribute(this.component.type));
|
attrNameAndValues.push(shimContentAttribute(this.component.type.id));
|
||||||
attrNameAndValues.push('');
|
attrNameAndValues.push('');
|
||||||
}
|
}
|
||||||
return attrNameAndValues;
|
return attrNameAndValues;
|
||||||
|
@ -228,7 +225,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitDirective(ast: DirectiveAst, directivesAndEventNames: any[][]): any {
|
visitDirective(ast: DirectiveAst, directivesAndEventNames: any[][]): any {
|
||||||
directivesAndEventNames[0].push(ast.directive.type);
|
directivesAndEventNames[0].push(ast.directive);
|
||||||
templateVisitAll(this, ast.hostEvents, directivesAndEventNames[1]);
|
templateVisitAll(this, ast.hostEvents, directivesAndEventNames[1]);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
export {TemplateCompiler} from './template_compiler';
|
||||||
|
export {
|
||||||
|
DirectiveMetadata,
|
||||||
|
TypeMetadata,
|
||||||
|
TemplateMetadata,
|
||||||
|
ChangeDetectionMetadata,
|
||||||
|
INormalizedDirectiveMetadata
|
||||||
|
} from './directive_metadata';
|
||||||
|
export {SourceModule, SourceWithImports} from './source_module';
|
|
@ -4,31 +4,31 @@ import {
|
||||||
changeDetectionStrategyFromJson
|
changeDetectionStrategyFromJson
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {ViewEncapsulation, viewEncapsulationFromJson} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation, viewEncapsulationFromJson} from 'angular2/src/core/render/api';
|
||||||
|
import {CssSelector} from 'angular2/src/core/render/dom/compiler/selector';
|
||||||
|
|
||||||
export class TypeMetadata {
|
export class TypeMetadata {
|
||||||
id: number;
|
id: number;
|
||||||
type: Type;
|
runtime: Type;
|
||||||
typeName: string;
|
name: string;
|
||||||
typeUrl: string;
|
moduleId: string;
|
||||||
constructor({id, type, typeName, typeUrl}:
|
constructor({id, runtime, name, moduleId}:
|
||||||
{id?: number, type?: Type, typeName?: string, typeUrl?: string} = {}) {
|
{id?: number, runtime?: Type, name?: string, moduleId?: string} = {}) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
this.runtime = runtime;
|
||||||
this.typeName = typeName;
|
this.name = name;
|
||||||
this.typeUrl = typeUrl;
|
this.moduleId = moduleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: StringMap<string, any>): TypeMetadata {
|
static fromJson(data: StringMap<string, any>): TypeMetadata {
|
||||||
return new TypeMetadata(
|
return new TypeMetadata({id: data['id'], name: data['name'], moduleId: data['moduleId']});
|
||||||
{id: data['id'], type: data['type'], typeName: data['typeName'], typeUrl: data['typeUrl']});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
'id': this.id,
|
||||||
'typeName': this.typeName,
|
'name': this.name,
|
||||||
'typeUrl': this.typeUrl
|
'moduleId': this.moduleId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,17 +63,17 @@ export class ChangeDetectionMetadata {
|
||||||
callOnInit?: boolean
|
callOnInit?: boolean
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.changeDetection = changeDetection;
|
this.changeDetection = changeDetection;
|
||||||
this.properties = properties;
|
this.properties = isPresent(properties) ? properties : [];
|
||||||
this.events = events;
|
this.events = isPresent(events) ? events : [];
|
||||||
this.hostListeners = hostListeners;
|
this.hostListeners = isPresent(hostListeners) ? hostListeners : {};
|
||||||
this.hostProperties = hostProperties;
|
this.hostProperties = isPresent(hostProperties) ? hostProperties : {};
|
||||||
this.callAfterContentInit = callAfterContentInit;
|
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
||||||
this.callAfterContentChecked = callAfterContentChecked;
|
this.callAfterContentChecked = normalizeBool(callAfterContentChecked);
|
||||||
this.callAfterViewInit = callAfterViewInit;
|
this.callAfterViewInit = normalizeBool(callAfterViewInit);
|
||||||
this.callAfterViewChecked = callAfterViewChecked;
|
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
||||||
this.callOnChanges = callOnChanges;
|
this.callOnChanges = normalizeBool(callOnChanges);
|
||||||
this.callDoCheck = callDoCheck;
|
this.callDoCheck = normalizeBool(callDoCheck);
|
||||||
this.callOnInit = callOnInit;
|
this.callOnInit = normalizeBool(callOnInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: StringMap<string, any>): ChangeDetectionMetadata {
|
static fromJson(data: StringMap<string, any>): ChangeDetectionMetadata {
|
||||||
|
@ -115,27 +115,79 @@ export class ChangeDetectionMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TemplateMetadata {
|
export class TemplateMetadata {
|
||||||
|
encapsulation: ViewEncapsulation;
|
||||||
|
template: string;
|
||||||
|
templateUrl: string;
|
||||||
|
styles: string[];
|
||||||
|
styleUrls: string[];
|
||||||
|
hostAttributes: StringMap<string, string>;
|
||||||
|
constructor({encapsulation, template, templateUrl, styles, styleUrls, hostAttributes}: {
|
||||||
|
encapsulation?: ViewEncapsulation,
|
||||||
|
template?: string,
|
||||||
|
templateUrl?: string,
|
||||||
|
styles?: string[],
|
||||||
|
styleUrls?: string[],
|
||||||
|
hostAttributes?: StringMap<string, string>
|
||||||
|
} = {}) {
|
||||||
|
this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.None;
|
||||||
|
this.template = template;
|
||||||
|
this.templateUrl = templateUrl;
|
||||||
|
this.styles = isPresent(styles) ? styles : [];
|
||||||
|
this.styleUrls = isPresent(styleUrls) ? styleUrls : [];
|
||||||
|
this.hostAttributes = isPresent(hostAttributes) ? hostAttributes : {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class DirectiveMetadata {
|
||||||
|
type: TypeMetadata;
|
||||||
|
isComponent: boolean;
|
||||||
|
dynamicLoadable: boolean;
|
||||||
|
selector: string;
|
||||||
|
changeDetection: ChangeDetectionMetadata;
|
||||||
|
template: TemplateMetadata;
|
||||||
|
constructor({type, isComponent, dynamicLoadable, selector, changeDetection, template}: {
|
||||||
|
type?: TypeMetadata,
|
||||||
|
isComponent?: boolean,
|
||||||
|
dynamicLoadable?: boolean,
|
||||||
|
selector?: string,
|
||||||
|
changeDetection?: ChangeDetectionMetadata,
|
||||||
|
template?: TemplateMetadata
|
||||||
|
} = {}) {
|
||||||
|
this.type = type;
|
||||||
|
this.isComponent = normalizeBool(isComponent);
|
||||||
|
this.dynamicLoadable = normalizeBool(dynamicLoadable);
|
||||||
|
this.selector = selector;
|
||||||
|
this.changeDetection = changeDetection;
|
||||||
|
this.template = template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NormalizedTemplateMetadata {
|
||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
template: string;
|
template: string;
|
||||||
styles: string[];
|
styles: string[];
|
||||||
styleAbsUrls: string[];
|
styleAbsUrls: string[];
|
||||||
ngContentSelectors: string[];
|
ngContentSelectors: string[];
|
||||||
constructor({encapsulation, template, styles, styleAbsUrls, ngContentSelectors}: {
|
hostAttributes: StringMap<string, string>;
|
||||||
|
constructor({encapsulation, template, styles, styleAbsUrls, ngContentSelectors, hostAttributes}: {
|
||||||
encapsulation?: ViewEncapsulation,
|
encapsulation?: ViewEncapsulation,
|
||||||
template?: string,
|
template?: string,
|
||||||
styles?: string[],
|
styles?: string[],
|
||||||
styleAbsUrls?: string[],
|
styleAbsUrls?: string[],
|
||||||
ngContentSelectors?: string[]
|
ngContentSelectors?: string[],
|
||||||
|
hostAttributes?: StringMap<string, string>
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.encapsulation = encapsulation;
|
this.encapsulation = encapsulation;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
this.styles = styles;
|
this.styles = styles;
|
||||||
this.styleAbsUrls = styleAbsUrls;
|
this.styleAbsUrls = styleAbsUrls;
|
||||||
this.ngContentSelectors = ngContentSelectors;
|
this.ngContentSelectors = ngContentSelectors;
|
||||||
|
this.hostAttributes = hostAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: StringMap<string, any>): TemplateMetadata {
|
static fromJson(data: StringMap<string, any>): NormalizedTemplateMetadata {
|
||||||
return new TemplateMetadata({
|
return new NormalizedTemplateMetadata({
|
||||||
encapsulation: isPresent(data['encapsulation']) ?
|
encapsulation: isPresent(data['encapsulation']) ?
|
||||||
viewEncapsulationFromJson(data['encapsulation']) :
|
viewEncapsulationFromJson(data['encapsulation']) :
|
||||||
data['encapsulation'],
|
data['encapsulation'],
|
||||||
|
@ -143,6 +195,7 @@ export class TemplateMetadata {
|
||||||
styles: data['styles'],
|
styles: data['styles'],
|
||||||
styleAbsUrls: data['styleAbsUrls'],
|
styleAbsUrls: data['styleAbsUrls'],
|
||||||
ngContentSelectors: data['ngContentSelectors'],
|
ngContentSelectors: data['ngContentSelectors'],
|
||||||
|
hostAttributes: data['hostAttributes']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,54 +207,58 @@ export class TemplateMetadata {
|
||||||
'styles': this.styles,
|
'styles': this.styles,
|
||||||
'styleAbsUrls': this.styleAbsUrls,
|
'styleAbsUrls': this.styleAbsUrls,
|
||||||
'ngContentSelectors': this.ngContentSelectors,
|
'ngContentSelectors': this.ngContentSelectors,
|
||||||
|
'hostAttributes': this.hostAttributes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface INormalizedDirectiveMetadata {}
|
||||||
|
|
||||||
export class DirectiveMetadata {
|
export class NormalizedDirectiveMetadata implements INormalizedDirectiveMetadata {
|
||||||
type: TypeMetadata;
|
type: TypeMetadata;
|
||||||
isComponent: boolean;
|
isComponent: boolean;
|
||||||
|
dynamicLoadable: boolean;
|
||||||
selector: string;
|
selector: string;
|
||||||
hostAttributes: StringMap<string, string>;
|
|
||||||
changeDetection: ChangeDetectionMetadata;
|
changeDetection: ChangeDetectionMetadata;
|
||||||
template: TemplateMetadata;
|
template: NormalizedTemplateMetadata;
|
||||||
constructor({type, isComponent, selector, hostAttributes, changeDetection, template}: {
|
constructor({type, isComponent, dynamicLoadable, selector, changeDetection, template}: {
|
||||||
|
id?: number,
|
||||||
type?: TypeMetadata,
|
type?: TypeMetadata,
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
|
dynamicLoadable?: boolean,
|
||||||
selector?: string,
|
selector?: string,
|
||||||
hostAttributes?: StringMap<string, string>,
|
|
||||||
changeDetection?: ChangeDetectionMetadata,
|
changeDetection?: ChangeDetectionMetadata,
|
||||||
template?: TemplateMetadata
|
template?: NormalizedTemplateMetadata
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.isComponent = normalizeBool(isComponent);
|
this.isComponent = normalizeBool(isComponent);
|
||||||
|
this.dynamicLoadable = normalizeBool(dynamicLoadable);
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.hostAttributes = hostAttributes;
|
|
||||||
this.changeDetection = changeDetection;
|
this.changeDetection = changeDetection;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: StringMap<string, any>): DirectiveMetadata {
|
static fromJson(data: StringMap<string, any>): NormalizedDirectiveMetadata {
|
||||||
return new DirectiveMetadata({
|
return new NormalizedDirectiveMetadata({
|
||||||
type: isPresent(data['type']) ? TypeMetadata.fromJson(data['type']) : data['type'],
|
|
||||||
isComponent: data['isComponent'],
|
isComponent: data['isComponent'],
|
||||||
|
dynamicLoadable: data['dynamicLoadable'],
|
||||||
selector: data['selector'],
|
selector: data['selector'],
|
||||||
hostAttributes: data['hostAttributes'],
|
type: isPresent(data['type']) ? TypeMetadata.fromJson(data['type']) : data['type'],
|
||||||
changeDetection: isPresent(data['changeDetection']) ?
|
changeDetection: isPresent(data['changeDetection']) ?
|
||||||
ChangeDetectionMetadata.fromJson(data['changeDetection']) :
|
ChangeDetectionMetadata.fromJson(data['changeDetection']) :
|
||||||
data['changeDetection'],
|
data['changeDetection'],
|
||||||
template: isPresent(data['template']) ? TemplateMetadata.fromJson(data['template']) :
|
template:
|
||||||
data['template']
|
isPresent(data['template']) ? NormalizedTemplateMetadata.fromJson(data['template']) :
|
||||||
|
data['template']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): StringMap<string, any> {
|
toJson(): StringMap<string, any> {
|
||||||
return {
|
return {
|
||||||
'type': isPresent(this.type) ? this.type.toJson() : this.type,
|
|
||||||
'isComponent': this.isComponent,
|
'isComponent': this.isComponent,
|
||||||
|
'dynamicLoadable': this.dynamicLoadable,
|
||||||
'selector': this.selector,
|
'selector': this.selector,
|
||||||
'hostAttributes': this.hostAttributes,
|
'type': isPresent(this.type) ? this.type.toJson() : this.type,
|
||||||
'changeDetection':
|
'changeDetection':
|
||||||
isPresent(this.changeDetection) ? this.changeDetection.toJson() : this.changeDetection,
|
isPresent(this.changeDetection) ? this.changeDetection.toJson() : this.changeDetection,
|
||||||
'template': isPresent(this.template) ? this.template.toJson() : this.template
|
'template': isPresent(this.template) ? this.template.toJson() : this.template
|
||||||
|
@ -209,6 +266,39 @@ export class DirectiveMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SourceModule {
|
export function createHostComponentMeta(componentType: TypeMetadata, componentSelector: string):
|
||||||
constructor(public moduleName: string, public source: string, public imports: string[][]) {}
|
NormalizedDirectiveMetadata {
|
||||||
|
var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate();
|
||||||
|
return new NormalizedDirectiveMetadata({
|
||||||
|
type: new TypeMetadata({
|
||||||
|
runtime: Object,
|
||||||
|
id: (componentType.id * -1) - 1,
|
||||||
|
name: `Host${componentType.name}`,
|
||||||
|
moduleId: componentType.moduleId
|
||||||
|
}),
|
||||||
|
template: new NormalizedTemplateMetadata({
|
||||||
|
template: template,
|
||||||
|
styles: [],
|
||||||
|
styleAbsUrls: [],
|
||||||
|
hostAttributes: {},
|
||||||
|
ngContentSelectors: []
|
||||||
|
}),
|
||||||
|
changeDetection: new ChangeDetectionMetadata({
|
||||||
|
changeDetection: ChangeDetectionStrategy.Default,
|
||||||
|
properties: [],
|
||||||
|
events: [],
|
||||||
|
hostListeners: {},
|
||||||
|
hostProperties: {},
|
||||||
|
callAfterContentInit: false,
|
||||||
|
callAfterContentChecked: false,
|
||||||
|
callAfterViewInit: false,
|
||||||
|
callAfterViewChecked: false,
|
||||||
|
callOnChanges: false,
|
||||||
|
callDoCheck: false,
|
||||||
|
callOnInit: false
|
||||||
|
}),
|
||||||
|
isComponent: true,
|
||||||
|
dynamicLoadable: false,
|
||||||
|
selector: '*'
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -17,9 +17,11 @@ import {
|
||||||
} from './html_ast';
|
} from './html_ast';
|
||||||
|
|
||||||
import {escapeDoubleQuoteString} from './util';
|
import {escapeDoubleQuoteString} from './util';
|
||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
const NG_NON_BINDABLE = 'ng-non-bindable';
|
const NG_NON_BINDABLE = 'ng-non-bindable';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class HtmlParser {
|
export class HtmlParser {
|
||||||
parse(template: string, sourceInfo: string): HtmlAst[] {
|
parse(template: string, sourceInfo: string): HtmlAst[] {
|
||||||
var root = DOM.createTemplate(template);
|
var root = DOM.createTemplate(template);
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
import {resolveForwardRef} from 'angular2/src/core/di';
|
||||||
|
import {
|
||||||
|
Type,
|
||||||
|
isBlank,
|
||||||
|
isPresent,
|
||||||
|
isArray,
|
||||||
|
stringify,
|
||||||
|
RegExpWrapper
|
||||||
|
} from 'angular2/src/core/facade/lang';
|
||||||
|
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||||
|
import {MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
|
import * as cpl from './directive_metadata';
|
||||||
|
import * as dirAnn from 'angular2/src/core/metadata/directives';
|
||||||
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||||
|
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||||
|
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
|
||||||
|
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||||
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
|
// group 1: "property" from "[property]"
|
||||||
|
// group 2: "event" from "(event)"
|
||||||
|
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RuntimeMetadataResolver {
|
||||||
|
private _directiveCounter = 0;
|
||||||
|
private _cache: Map<Type, cpl.DirectiveMetadata> = new Map();
|
||||||
|
|
||||||
|
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {}
|
||||||
|
|
||||||
|
getMetadata(directiveType: Type): cpl.DirectiveMetadata {
|
||||||
|
var meta = this._cache.get(directiveType);
|
||||||
|
if (isBlank(meta)) {
|
||||||
|
var directiveAnnotation = this._directiveResolver.resolve(directiveType);
|
||||||
|
var moduleId = calcModuleId(directiveType, directiveAnnotation);
|
||||||
|
var templateMeta = null;
|
||||||
|
var hostListeners = {};
|
||||||
|
var hostProperties = {};
|
||||||
|
var hostAttributes = {};
|
||||||
|
var changeDetectionStrategy = null;
|
||||||
|
var dynamicLoadable: boolean = false;
|
||||||
|
|
||||||
|
if (isPresent(directiveAnnotation.host)) {
|
||||||
|
StringMapWrapper.forEach(directiveAnnotation.host, (value: string, key: string) => {
|
||||||
|
var matches = RegExpWrapper.firstMatch(HOST_REG_EXP, key);
|
||||||
|
if (isBlank(matches)) {
|
||||||
|
hostAttributes[key] = value;
|
||||||
|
} else if (isPresent(matches[1])) {
|
||||||
|
hostProperties[matches[1]] = value;
|
||||||
|
} else if (isPresent(matches[2])) {
|
||||||
|
hostListeners[matches[2]] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (directiveAnnotation instanceof dirAnn.ComponentMetadata) {
|
||||||
|
var compAnnotation = <dirAnn.ComponentMetadata>directiveAnnotation;
|
||||||
|
var viewAnnotation = this._viewResolver.resolve(directiveType);
|
||||||
|
templateMeta = new cpl.TemplateMetadata({
|
||||||
|
encapsulation: viewAnnotation.encapsulation,
|
||||||
|
template: viewAnnotation.template,
|
||||||
|
templateUrl: viewAnnotation.templateUrl,
|
||||||
|
styles: viewAnnotation.styles,
|
||||||
|
styleUrls: viewAnnotation.styleUrls,
|
||||||
|
hostAttributes: hostAttributes
|
||||||
|
});
|
||||||
|
changeDetectionStrategy = compAnnotation.changeDetection;
|
||||||
|
dynamicLoadable = compAnnotation.dynamicLoadable;
|
||||||
|
}
|
||||||
|
meta = new cpl.DirectiveMetadata({
|
||||||
|
selector: directiveAnnotation.selector,
|
||||||
|
isComponent: isPresent(templateMeta),
|
||||||
|
dynamicLoadable: dynamicLoadable,
|
||||||
|
type: new cpl.TypeMetadata({
|
||||||
|
id: this._directiveCounter++,
|
||||||
|
name: stringify(directiveType),
|
||||||
|
moduleId: moduleId,
|
||||||
|
runtime: directiveType
|
||||||
|
}),
|
||||||
|
template: templateMeta,
|
||||||
|
changeDetection: new cpl.ChangeDetectionMetadata({
|
||||||
|
changeDetection: changeDetectionStrategy,
|
||||||
|
properties: directiveAnnotation.properties,
|
||||||
|
events: directiveAnnotation.events,
|
||||||
|
hostListeners: hostListeners,
|
||||||
|
hostProperties: hostProperties,
|
||||||
|
callAfterContentInit: hasLifecycleHook(LifecycleHooks.AfterContentInit, directiveType),
|
||||||
|
callAfterContentChecked:
|
||||||
|
hasLifecycleHook(LifecycleHooks.AfterContentChecked, directiveType),
|
||||||
|
callAfterViewInit: hasLifecycleHook(LifecycleHooks.AfterViewInit, directiveType),
|
||||||
|
callAfterViewChecked: hasLifecycleHook(LifecycleHooks.AfterViewChecked, directiveType),
|
||||||
|
callOnChanges: hasLifecycleHook(LifecycleHooks.OnChanges, directiveType),
|
||||||
|
callDoCheck: hasLifecycleHook(LifecycleHooks.DoCheck, directiveType),
|
||||||
|
callOnInit: hasLifecycleHook(LifecycleHooks.OnInit, directiveType),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
this._cache.set(directiveType, meta);
|
||||||
|
}
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewDirectivesMetadata(component: Type): cpl.DirectiveMetadata[] {
|
||||||
|
var view = this._viewResolver.resolve(component);
|
||||||
|
var directives = flattenDirectives(view);
|
||||||
|
for (var i = 0; i < directives.length; i++) {
|
||||||
|
if (!isValidDirective(directives[i])) {
|
||||||
|
throw new BaseException(
|
||||||
|
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removeDuplicatedDirectives(directives.map(type => this.getMetadata(type)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeDuplicatedDirectives(directives: cpl.DirectiveMetadata[]): cpl.DirectiveMetadata[] {
|
||||||
|
var directivesMap: Map<number, cpl.DirectiveMetadata> = new Map();
|
||||||
|
directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); });
|
||||||
|
return MapWrapper.values(directivesMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenDirectives(view: ViewMetadata): Type[] {
|
||||||
|
if (isBlank(view.directives)) return [];
|
||||||
|
var directives = [];
|
||||||
|
flattenList(view.directives, directives);
|
||||||
|
return directives;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenList(tree: any[], out: Array<Type | any[]>): void {
|
||||||
|
for (var i = 0; i < tree.length; i++) {
|
||||||
|
var item = resolveForwardRef(tree[i]);
|
||||||
|
if (isArray(item)) {
|
||||||
|
flattenList(item, out);
|
||||||
|
} else {
|
||||||
|
out.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidDirective(value: Type): boolean {
|
||||||
|
return isPresent(value) && (value instanceof Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcModuleId(type: Type, directiveAnnotation: dirAnn.DirectiveMetadata): string {
|
||||||
|
if (isPresent(directiveAnnotation.moduleId)) {
|
||||||
|
return directiveAnnotation.moduleId;
|
||||||
|
} else {
|
||||||
|
return reflector.moduleId(type);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import {StringWrapper, isBlank} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
|
var MODULE_REGEXP = /#MODULE\[([^\]]*)\]/g;
|
||||||
|
|
||||||
|
export function moduleRef(moduleId): string {
|
||||||
|
return `#MODULE[${moduleId}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SourceModule {
|
||||||
|
constructor(public moduleId: string, public source: string) {}
|
||||||
|
|
||||||
|
getSourceWithImports(): SourceWithImports {
|
||||||
|
var moduleAliases = {};
|
||||||
|
var imports: string[][] = [];
|
||||||
|
var newSource = StringWrapper.replaceAllMapped(this.source, MODULE_REGEXP, (match) => {
|
||||||
|
var moduleId = match[1];
|
||||||
|
var alias = moduleAliases[moduleId];
|
||||||
|
if (isBlank(alias)) {
|
||||||
|
if (moduleId == this.moduleId) {
|
||||||
|
alias = '';
|
||||||
|
} else {
|
||||||
|
alias = `import${imports.length}`;
|
||||||
|
imports.push([moduleId, alias]);
|
||||||
|
}
|
||||||
|
moduleAliases[moduleId] = alias;
|
||||||
|
}
|
||||||
|
return alias.length > 0 ? `${alias}.` : '';
|
||||||
|
});
|
||||||
|
return new SourceWithImports(newSource, imports);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SourceExpression {
|
||||||
|
constructor(public declarations: string[], public expression: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SourceWithImports {
|
||||||
|
constructor(public source: string, public imports: string[][]) {}
|
||||||
|
}
|
|
@ -1,26 +1,35 @@
|
||||||
import {DirectiveMetadata, SourceModule, TypeMetadata} from './api';
|
import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata';
|
||||||
|
import {SourceModule, SourceExpression, moduleRef} from './source_module';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
import {StringWrapper, isJsObject, isBlank} from 'angular2/src/core/facade/lang';
|
import {StringWrapper, isBlank} from 'angular2/src/core/facade/lang';
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||||
import {ShadowCss} from 'angular2/src/core/render/dom/compiler/shadow_css';
|
import {ShadowCss} from 'angular2/src/core/render/dom/compiler/shadow_css';
|
||||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||||
import {resolveStyleUrls} from './style_url_resolver';
|
import {resolveStyleUrls} from './style_url_resolver';
|
||||||
import {escapeSingleQuoteString} from './util';
|
import {
|
||||||
|
escapeSingleQuoteString,
|
||||||
|
IS_DART,
|
||||||
|
codeGenConcatArray,
|
||||||
|
codeGenMapArray,
|
||||||
|
codeGenReplaceAll,
|
||||||
|
codeGenExportVariable
|
||||||
|
} from './util';
|
||||||
|
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 CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||||
var IS_DART = !isJsObject({});
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class StyleCompiler {
|
export class StyleCompiler {
|
||||||
private _styleCache: Map<string, Promise<string[]>> = new Map<string, Promise<string[]>>();
|
private _styleCache: Map<string, Promise<string[]>> = new Map<string, Promise<string[]>>();
|
||||||
private _shadowCss: ShadowCss = new ShadowCss();
|
private _shadowCss: ShadowCss = new ShadowCss();
|
||||||
|
|
||||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
||||||
|
|
||||||
compileComponentRuntime(component: DirectiveMetadata): Promise<string[]> {
|
compileComponentRuntime(component: NormalizedDirectiveMetadata): Promise<string[]> {
|
||||||
var styles = component.template.styles;
|
var styles = component.template.styles;
|
||||||
var styleAbsUrls = component.template.styleAbsUrls;
|
var styleAbsUrls = component.template.styleAbsUrls;
|
||||||
return this._loadStyles(styles, styleAbsUrls,
|
return this._loadStyles(styles, styleAbsUrls,
|
||||||
|
@ -29,7 +38,7 @@ export class StyleCompiler {
|
||||||
`${component.type.id}`)));
|
`${component.type.id}`)));
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(component: DirectiveMetadata): SourceModule {
|
compileComponentCodeGen(component: NormalizedDirectiveMetadata): SourceExpression {
|
||||||
var shim = component.template.encapsulation === ViewEncapsulation.Emulated;
|
var shim = component.template.encapsulation === ViewEncapsulation.Emulated;
|
||||||
var suffix;
|
var suffix;
|
||||||
if (shim) {
|
if (shim) {
|
||||||
|
@ -39,16 +48,17 @@ export class StyleCompiler {
|
||||||
} else {
|
} else {
|
||||||
suffix = '';
|
suffix = '';
|
||||||
}
|
}
|
||||||
return this._styleCodeGen(`$component.type.typeUrl}.styles`, component.template.styles,
|
return this._styleCodeGen(component.template.styles, component.template.styleAbsUrls, shim,
|
||||||
component.template.styleAbsUrls, shim, suffix);
|
suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
compileStylesheetCodeGen(moduleName: string, cssText: string): SourceModule[] {
|
compileStylesheetCodeGen(moduleId: string, cssText: string): SourceModule[] {
|
||||||
var styleWithImports = resolveStyleUrls(this._urlResolver, moduleName, cssText);
|
var styleWithImports = resolveStyleUrls(this._urlResolver, moduleId, cssText);
|
||||||
return [
|
return [
|
||||||
this._styleCodeGen(moduleName, [styleWithImports.style], styleWithImports.styleUrls, false,
|
this._styleModule(moduleId, false, this._styleCodeGen([styleWithImports.style],
|
||||||
''),
|
styleWithImports.styleUrls, false, '')),
|
||||||
this._styleCodeGen(moduleName, [styleWithImports.style], styleWithImports.styleUrls, true, '')
|
this._styleModule(moduleId, true, this._styleCodeGen([styleWithImports.style],
|
||||||
|
styleWithImports.styleUrls, true, ''))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,55 +84,41 @@ export class StyleCompiler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _styleCodeGen(moduleName: string, plainStyles: string[], absUrls: string[], shim: boolean,
|
private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean,
|
||||||
suffix: string): SourceModule {
|
suffix: string): SourceExpression {
|
||||||
var imports: string[][] = [];
|
var expressionSource = `(`;
|
||||||
var moduleSource = `var STYLES = (`;
|
expressionSource +=
|
||||||
moduleSource +=
|
|
||||||
`[${plainStyles.map( plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)) ).join(',')}]`;
|
`[${plainStyles.map( plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)) ).join(',')}]`;
|
||||||
for (var i = 0; i < absUrls.length; i++) {
|
for (var i = 0; i < absUrls.length; i++) {
|
||||||
var url = absUrls[i];
|
var moduleId = this._shimModuleIdIfNeeded(absUrls[i], shim);
|
||||||
var moduleAlias = `import${i}`;
|
expressionSource += codeGenConcatArray(`${moduleRef(moduleId)}STYLES`);
|
||||||
imports.push([this._shimModuleName(url, shim), moduleAlias]);
|
|
||||||
moduleSource += `${codeGenConcatArray(moduleAlias+'.STYLES')}`;
|
|
||||||
}
|
}
|
||||||
moduleSource += `)${suffix};`;
|
expressionSource += `)${suffix}`;
|
||||||
return new SourceModule(this._shimModuleName(moduleName, shim), moduleSource, imports);
|
return new SourceExpression([], expressionSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _styleModule(moduleId: string, shim: boolean,
|
||||||
|
expression: SourceExpression): SourceModule {
|
||||||
|
var moduleSource = `
|
||||||
|
${expression.declarations.join('\n')}
|
||||||
|
${codeGenExportVariable('STYLES')}${expression.expression};
|
||||||
|
`;
|
||||||
|
return new SourceModule(this._shimModuleIdIfNeeded(moduleId, shim), moduleSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _shimIfNeeded(style: string, shim: boolean): string {
|
private _shimIfNeeded(style: string, shim: boolean): string {
|
||||||
return shim ? this._shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR) : style;
|
return shim ? this._shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR) : style;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _shimModuleName(originalUrl: string, shim: boolean): string {
|
private _shimModuleIdIfNeeded(moduleId: string, shim: boolean): string {
|
||||||
return shim ? `${originalUrl}.shim` : originalUrl;
|
return shim ? `${moduleId}.shim` : moduleId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shimContentAttribute(component: TypeMetadata): string {
|
export function shimContentAttribute(componentId: number): string {
|
||||||
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, `${component.id}`);
|
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, `${componentId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shimHostAttribute(component: TypeMetadata): string {
|
export function shimHostAttribute(componentId: number): string {
|
||||||
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, `${component.id}`);
|
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, `${componentId}`);
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenConcatArray(expression: string): string {
|
|
||||||
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenMapArray(argNames: string[], callback: string): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `.map( (${argNames.join(',')}) => ${callback} ).toList()`;
|
|
||||||
} else {
|
|
||||||
return `.map(function(${argNames.join(',')}) { return ${callback}; })`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenReplaceAll(pattern: string, value: string): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `.replaceAll('${pattern}', '${value}')`;
|
|
||||||
} else {
|
|
||||||
return `.replace(/${pattern}/g, '${value}')`;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import {AST} from 'angular2/src/core/change_detection/change_detection';
|
import {AST} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
import {DirectiveMetadata} from './api';
|
import {NormalizedDirectiveMetadata} from './directive_metadata';
|
||||||
|
|
||||||
export interface TemplateAst {
|
export interface TemplateAst {
|
||||||
sourceInfo: string;
|
sourceInfo: string;
|
||||||
|
@ -69,7 +69,7 @@ export class ElementAst implements TemplateAst {
|
||||||
this.directives.length > 0);
|
this.directives.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponent(): DirectiveMetadata {
|
getComponent(): NormalizedDirectiveMetadata {
|
||||||
return this.directives.length > 0 && this.directives[0].directive.isComponent ?
|
return this.directives.length > 0 && this.directives[0].directive.isComponent ?
|
||||||
this.directives[0].directive :
|
this.directives[0].directive :
|
||||||
null;
|
null;
|
||||||
|
@ -94,7 +94,8 @@ export class BoundDirectivePropertyAst implements TemplateAst {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DirectiveAst implements TemplateAst {
|
export class DirectiveAst implements TemplateAst {
|
||||||
constructor(public directive: DirectiveMetadata, public properties: BoundDirectivePropertyAst[],
|
constructor(public directive: NormalizedDirectiveMetadata,
|
||||||
|
public properties: BoundDirectivePropertyAst[],
|
||||||
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
|
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
|
||||||
public sourceInfo: string) {}
|
public sourceInfo: string) {}
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang';
|
||||||
|
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||||
|
import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection';
|
||||||
|
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||||
|
import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands';
|
||||||
|
import {
|
||||||
|
createHostComponentMeta,
|
||||||
|
DirectiveMetadata,
|
||||||
|
INormalizedDirectiveMetadata,
|
||||||
|
NormalizedDirectiveMetadata,
|
||||||
|
TypeMetadata,
|
||||||
|
ChangeDetectionMetadata,
|
||||||
|
NormalizedTemplateMetadata
|
||||||
|
} from './directive_metadata';
|
||||||
|
import {TemplateAst} from './template_ast';
|
||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
import {SourceModule, moduleRef} from './source_module';
|
||||||
|
import {ChangeDetectionCompiler} from './change_detector_compiler';
|
||||||
|
import {StyleCompiler} from './style_compiler';
|
||||||
|
import {CommandCompiler} from './command_compiler';
|
||||||
|
import {TemplateParser} from './template_parser';
|
||||||
|
import {TemplateNormalizer} from './template_normalizer';
|
||||||
|
import {RuntimeMetadataResolver} from './runtime_metadata';
|
||||||
|
|
||||||
|
import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
|
||||||
|
import {IS_DART, codeGenExportVariable, escapeSingleQuoteString, codeGenValueFn} from './util';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TemplateCompiler {
|
||||||
|
private _compiledTemplateCache: Map<number, CompiledTemplate> = new Map();
|
||||||
|
private _compiledTemplateDone: Map<number, Promise<CompiledTemplate>> = new Map();
|
||||||
|
|
||||||
|
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
||||||
|
private _templateNormalizer: TemplateNormalizer,
|
||||||
|
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||||
|
private _commandCompiler: CommandCompiler,
|
||||||
|
private _cdCompiler: ChangeDetectionCompiler) {}
|
||||||
|
|
||||||
|
normalizeDirective(directive: DirectiveMetadata): Promise<INormalizedDirectiveMetadata> {
|
||||||
|
var normalizedTemplatePromise;
|
||||||
|
if (directive.isComponent) {
|
||||||
|
normalizedTemplatePromise =
|
||||||
|
this._templateNormalizer.normalizeTemplate(directive.type, directive.template);
|
||||||
|
} else {
|
||||||
|
normalizedTemplatePromise = PromiseWrapper.resolve(null);
|
||||||
|
}
|
||||||
|
return normalizedTemplatePromise.then(
|
||||||
|
(normalizedTemplate) => new NormalizedDirectiveMetadata({
|
||||||
|
selector: directive.selector,
|
||||||
|
dynamicLoadable: directive.dynamicLoadable,
|
||||||
|
isComponent: directive.isComponent,
|
||||||
|
type: directive.type,
|
||||||
|
changeDetection: directive.changeDetection, template: normalizedTemplate
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeTemplateMetadata(metadata: INormalizedDirectiveMetadata): string {
|
||||||
|
return Json.stringify((<NormalizedDirectiveMetadata>metadata).toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializeTemplateMetadata(data: string): INormalizedDirectiveMetadata {
|
||||||
|
return NormalizedDirectiveMetadata.fromJson(Json.parse(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> {
|
||||||
|
var compMeta: DirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
||||||
|
if (isBlank(compMeta) || !compMeta.isComponent || !compMeta.dynamicLoadable) {
|
||||||
|
throw new BaseException(
|
||||||
|
`Could not compile '${stringify(type)}' because it is not dynamically loadable.`);
|
||||||
|
}
|
||||||
|
var hostMeta: NormalizedDirectiveMetadata =
|
||||||
|
createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
|
this._compileComponentRuntime(hostMeta, [compMeta], new Set());
|
||||||
|
return this._compiledTemplateDone.get(hostMeta.type.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileComponentRuntime(compMeta: NormalizedDirectiveMetadata,
|
||||||
|
viewDirectives: DirectiveMetadata[],
|
||||||
|
compilingComponentIds: Set<number>): CompiledTemplate {
|
||||||
|
var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.id);
|
||||||
|
var done = this._compiledTemplateDone.get(compMeta.type.id);
|
||||||
|
if (isBlank(compiledTemplate)) {
|
||||||
|
var styles;
|
||||||
|
var changeDetectorFactories;
|
||||||
|
var commands;
|
||||||
|
compiledTemplate =
|
||||||
|
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactories, commands, styles]);
|
||||||
|
this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate);
|
||||||
|
compilingComponentIds.add(compMeta.type.id);
|
||||||
|
done =
|
||||||
|
PromiseWrapper.all([this._styleCompiler.compileComponentRuntime(compMeta)].concat(
|
||||||
|
viewDirectives.map(dirMeta => this.normalizeDirective(dirMeta))))
|
||||||
|
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||||
|
var childPromises = [];
|
||||||
|
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
||||||
|
var parsedTemplate = this._templateParser.parse(
|
||||||
|
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
||||||
|
|
||||||
|
changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||||
|
compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate);
|
||||||
|
styles = stylesAndNormalizedViewDirMetas[0];
|
||||||
|
commands = this._compileCommandsRuntime(compMeta, parsedTemplate,
|
||||||
|
compilingComponentIds, childPromises);
|
||||||
|
return PromiseWrapper.all(childPromises);
|
||||||
|
})
|
||||||
|
.then((_) => {
|
||||||
|
SetWrapper.delete(compilingComponentIds, compMeta.type.id);
|
||||||
|
return compiledTemplate;
|
||||||
|
});
|
||||||
|
this._compiledTemplateDone.set(compMeta.type.id, done);
|
||||||
|
}
|
||||||
|
return compiledTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileCommandsRuntime(compMeta: NormalizedDirectiveMetadata,
|
||||||
|
parsedTemplate: TemplateAst[], compilingComponentIds: Set<number>,
|
||||||
|
childPromises: Promise<any>[]): TemplateCmd[] {
|
||||||
|
return this._commandCompiler.compileComponentRuntime(
|
||||||
|
compMeta, parsedTemplate, (childComponentDir: NormalizedDirectiveMetadata) => {
|
||||||
|
var childViewDirectives: DirectiveMetadata[] =
|
||||||
|
this._runtimeMetadataResolver.getViewDirectivesMetadata(
|
||||||
|
childComponentDir.type.runtime);
|
||||||
|
var childIsRecursive = SetWrapper.has(compilingComponentIds, childComponentDir.type.id);
|
||||||
|
var childTemplate = this._compileComponentRuntime(childComponentDir, childViewDirectives,
|
||||||
|
compilingComponentIds);
|
||||||
|
if (!childIsRecursive) {
|
||||||
|
// Only wait for a child if it is not a cycle
|
||||||
|
childPromises.push(this._compiledTemplateDone.get(childComponentDir.type.id));
|
||||||
|
}
|
||||||
|
return childTemplate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTemplatesCodeGen(moduleId: string,
|
||||||
|
components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
||||||
|
var declarations = [];
|
||||||
|
var templateArguments = [];
|
||||||
|
var componentMetas: NormalizedDirectiveMetadata[] = [];
|
||||||
|
components.forEach(componentWithDirs => {
|
||||||
|
var compMeta = <NormalizedDirectiveMetadata>componentWithDirs.component;
|
||||||
|
componentMetas.push(compMeta);
|
||||||
|
this._processTemplateCodeGen(compMeta,
|
||||||
|
<NormalizedDirectiveMetadata[]>componentWithDirs.directives,
|
||||||
|
declarations, templateArguments);
|
||||||
|
if (compMeta.dynamicLoadable) {
|
||||||
|
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
|
componentMetas.push(hostMeta);
|
||||||
|
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ListWrapper.forEachWithIndex(componentMetas, (compMeta: NormalizedDirectiveMetadata,
|
||||||
|
index: number) => {
|
||||||
|
var templateDataFn = codeGenValueFn([], `[${templateArguments[index].join(',')}]`);
|
||||||
|
declarations.push(
|
||||||
|
`${codeGenExportVariable(templateVariableName(compMeta.type))}new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${compMeta.type.id},${templateDataFn});`);
|
||||||
|
});
|
||||||
|
return new SourceModule(`${templateModuleName(moduleId)}`, declarations.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
compileStylesheetCodeGen(moduleId: string, cssText: string): SourceModule[] {
|
||||||
|
return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _processTemplateCodeGen(compMeta: NormalizedDirectiveMetadata,
|
||||||
|
directives: NormalizedDirectiveMetadata[],
|
||||||
|
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
||||||
|
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta);
|
||||||
|
var parsedTemplate =
|
||||||
|
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
||||||
|
var changeDetectorsExpr = this._cdCompiler.compileComponentCodeGen(
|
||||||
|
compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate);
|
||||||
|
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
||||||
|
compMeta, parsedTemplate, codeGenComponentTemplateFactory);
|
||||||
|
|
||||||
|
addAll(styleExpr.declarations, targetDeclarations);
|
||||||
|
addAll(changeDetectorsExpr.declarations, targetDeclarations);
|
||||||
|
addAll(commandsExpr.declarations, targetDeclarations);
|
||||||
|
|
||||||
|
targetTemplateArguments.push(
|
||||||
|
[changeDetectorsExpr.expression, commandsExpr.expression, styleExpr.expression]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NormalizedComponentWithViewDirectives {
|
||||||
|
constructor(public component: INormalizedDirectiveMetadata,
|
||||||
|
public directives: INormalizedDirectiveMetadata[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateVariableName(type: TypeMetadata): string {
|
||||||
|
return `${type.name}Template`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateModuleName(moduleId: string): string {
|
||||||
|
return `${moduleId}.template`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAll(source: any[], target: any[]) {
|
||||||
|
for (var i = 0; i < source.length; i++) {
|
||||||
|
target.push(source[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function codeGenComponentTemplateFactory(nestedCompType: NormalizedDirectiveMetadata): string {
|
||||||
|
return `${moduleRef(templateModuleName(nestedCompType.type.moduleId))}${templateVariableName(nestedCompType.type)}`;
|
||||||
|
}
|
|
@ -1,11 +1,16 @@
|
||||||
import {TypeMetadata, TemplateMetadata} from './api';
|
import {
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
TypeMetadata,
|
||||||
|
TemplateMetadata,
|
||||||
|
NormalizedDirectiveMetadata,
|
||||||
|
NormalizedTemplateMetadata
|
||||||
|
} from './directive_metadata';
|
||||||
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||||
|
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||||
import {resolveStyleUrls} from './style_url_resolver';
|
import {resolveStyleUrls} from './style_url_resolver';
|
||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HtmlAstVisitor,
|
HtmlAstVisitor,
|
||||||
|
@ -25,43 +30,43 @@ const LINK_STYLE_HREF_ATTR = 'href';
|
||||||
const LINK_STYLE_REL_VALUE = 'stylesheet';
|
const LINK_STYLE_REL_VALUE = 'stylesheet';
|
||||||
const STYLE_ELEMENT = 'style';
|
const STYLE_ELEMENT = 'style';
|
||||||
|
|
||||||
export class TemplateLoader {
|
@Injectable()
|
||||||
|
export class TemplateNormalizer {
|
||||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver,
|
constructor(private _xhr: XHR, private _urlResolver: UrlResolver,
|
||||||
private _domParser: HtmlParser) {}
|
private _domParser: HtmlParser) {}
|
||||||
|
|
||||||
loadTemplate(directiveType: TypeMetadata, encapsulation: ViewEncapsulation, template: string,
|
normalizeTemplate(directiveType: TypeMetadata,
|
||||||
templateUrl: string, styles: string[],
|
template: TemplateMetadata): Promise<NormalizedTemplateMetadata> {
|
||||||
styleUrls: string[]): Promise<TemplateMetadata> {
|
if (isPresent(template.template)) {
|
||||||
if (isPresent(template)) {
|
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
|
||||||
return PromiseWrapper.resolve(this.createTemplateFromString(
|
directiveType, template, template.template, directiveType.moduleId));
|
||||||
directiveType, encapsulation, template, directiveType.typeUrl, styles, styleUrls));
|
|
||||||
} else {
|
} else {
|
||||||
var sourceAbsUrl = this._urlResolver.resolve(directiveType.typeUrl, templateUrl);
|
var sourceAbsUrl = this._urlResolver.resolve(directiveType.moduleId, template.templateUrl);
|
||||||
return this._xhr.get(sourceAbsUrl)
|
return this._xhr.get(sourceAbsUrl)
|
||||||
.then(templateContent =>
|
.then(templateContent => this.normalizeLoadedTemplate(directiveType, template,
|
||||||
this.createTemplateFromString(directiveType, encapsulation, templateContent,
|
templateContent, sourceAbsUrl));
|
||||||
sourceAbsUrl, styles, styleUrls));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createTemplateFromString(directiveType: TypeMetadata, encapsulation: ViewEncapsulation,
|
normalizeLoadedTemplate(directiveType: TypeMetadata, templateMeta: TemplateMetadata,
|
||||||
template: string, templateSourceUrl: string, styles: string[],
|
template: string, templateAbsUrl: string): NormalizedTemplateMetadata {
|
||||||
styleUrls: string[]): TemplateMetadata {
|
var domNodes = this._domParser.parse(template, directiveType.name);
|
||||||
var domNodes = this._domParser.parse(template, directiveType.typeName);
|
|
||||||
var visitor = new TemplatePreparseVisitor();
|
var visitor = new TemplatePreparseVisitor();
|
||||||
var remainingNodes = htmlVisitAll(visitor, domNodes);
|
var remainingNodes = htmlVisitAll(visitor, domNodes);
|
||||||
var allStyles = styles.concat(visitor.styles);
|
var allStyles = templateMeta.styles.concat(visitor.styles);
|
||||||
var allStyleUrls = styleUrls.concat(visitor.styleUrls);
|
|
||||||
var allResolvedStyles = allStyles.map(style => {
|
|
||||||
var styleWithImports = resolveStyleUrls(this._urlResolver, templateSourceUrl, style);
|
|
||||||
styleWithImports.styleUrls.forEach(styleUrl => allStyleUrls.push(styleUrl));
|
|
||||||
return styleWithImports.style;
|
|
||||||
});
|
|
||||||
|
|
||||||
var allStyleAbsUrls =
|
var allStyleAbsUrls =
|
||||||
allStyleUrls.map(styleUrl => this._urlResolver.resolve(templateSourceUrl, styleUrl));
|
visitor.styleUrls.map(url => this._urlResolver.resolve(templateAbsUrl, url))
|
||||||
return new TemplateMetadata({
|
.concat(templateMeta.styleUrls.map(
|
||||||
encapsulation: encapsulation,
|
url => this._urlResolver.resolve(directiveType.moduleId, url)));
|
||||||
|
|
||||||
|
var allResolvedStyles = allStyles.map(style => {
|
||||||
|
var styleWithImports = resolveStyleUrls(this._urlResolver, templateAbsUrl, style);
|
||||||
|
styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl));
|
||||||
|
return styleWithImports.style;
|
||||||
|
});
|
||||||
|
return new NormalizedTemplateMetadata({
|
||||||
|
encapsulation: templateMeta.encapsulation,
|
||||||
template: this._domParser.unparse(remainingNodes),
|
template: this._domParser.unparse(remainingNodes),
|
||||||
styles: allResolvedStyles,
|
styles: allResolvedStyles,
|
||||||
styleAbsUrls: allStyleAbsUrls,
|
styleAbsUrls: allStyleAbsUrls,
|
|
@ -8,11 +8,13 @@ import {
|
||||||
assertionsEnabled,
|
assertionsEnabled,
|
||||||
isBlank
|
isBlank
|
||||||
} from 'angular2/src/core/facade/lang';
|
} from 'angular2/src/core/facade/lang';
|
||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||||
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
||||||
|
import {NormalizedDirectiveMetadata} from './directive_metadata';
|
||||||
|
import {HtmlParser} from './html_parser';
|
||||||
|
|
||||||
import {DirectiveMetadata, TemplateMetadata} from './api';
|
|
||||||
import {
|
import {
|
||||||
ElementAst,
|
ElementAst,
|
||||||
BoundElementPropertyAst,
|
BoundElementPropertyAst,
|
||||||
|
@ -68,12 +70,16 @@ const STYLE_PREFIX = 'style';
|
||||||
|
|
||||||
var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class TemplateParser {
|
export class TemplateParser {
|
||||||
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) {}
|
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
||||||
|
private _htmlParser: HtmlParser) {}
|
||||||
|
|
||||||
parse(domNodes: HtmlAst[], directives: DirectiveMetadata[]): TemplateAst[] {
|
parse(template: string, directives: NormalizedDirectiveMetadata[],
|
||||||
|
sourceInfo: string): TemplateAst[] {
|
||||||
var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
|
var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
|
||||||
var result = htmlVisitAll(parseVisitor, domNodes, EMPTY_COMPONENT);
|
var result =
|
||||||
|
htmlVisitAll(parseVisitor, this._htmlParser.parse(template, sourceInfo), EMPTY_COMPONENT);
|
||||||
if (parseVisitor.errors.length > 0) {
|
if (parseVisitor.errors.length > 0) {
|
||||||
var errorString = parseVisitor.errors.join('\n');
|
var errorString = parseVisitor.errors.join('\n');
|
||||||
throw new BaseException(`Template parse errors:\n${errorString}`);
|
throw new BaseException(`Template parse errors:\n${errorString}`);
|
||||||
|
@ -85,7 +91,7 @@ export class TemplateParser {
|
||||||
class TemplateParseVisitor implements HtmlAstVisitor {
|
class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
selectorMatcher: SelectorMatcher;
|
selectorMatcher: SelectorMatcher;
|
||||||
errors: string[] = [];
|
errors: string[] = [];
|
||||||
constructor(directives: DirectiveMetadata[], private _exprParser: Parser,
|
constructor(directives: NormalizedDirectiveMetadata[], private _exprParser: Parser,
|
||||||
private _schemaRegistry: ElementSchemaRegistry) {
|
private _schemaRegistry: ElementSchemaRegistry) {
|
||||||
this.selectorMatcher = new SelectorMatcher();
|
this.selectorMatcher = new SelectorMatcher();
|
||||||
directives.forEach(directive => {
|
directives.forEach(directive => {
|
||||||
|
@ -371,31 +377,32 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
||||||
elementCssSelector: CssSelector): DirectiveMetadata[] {
|
elementCssSelector: CssSelector): NormalizedDirectiveMetadata[] {
|
||||||
var directives = [];
|
var directives = [];
|
||||||
selectorMatcher.match(elementCssSelector,
|
selectorMatcher.match(elementCssSelector,
|
||||||
(selector, directive) => { directives.push(directive); });
|
(selector, directive) => { directives.push(directive); });
|
||||||
// Need to sort the directives so that we get consistent results throughout,
|
// Need to sort the directives so that we get consistent results throughout,
|
||||||
// as selectorMatcher uses Maps inside.
|
// as selectorMatcher uses Maps inside.
|
||||||
// Also need to make components the first directive in the array
|
// Also need to make components the first directive in the array
|
||||||
ListWrapper.sort(directives, (dir1: DirectiveMetadata, dir2: DirectiveMetadata) => {
|
ListWrapper.sort(directives,
|
||||||
var dir1Comp = dir1.isComponent;
|
(dir1: NormalizedDirectiveMetadata, dir2: NormalizedDirectiveMetadata) => {
|
||||||
var dir2Comp = dir2.isComponent;
|
var dir1Comp = dir1.isComponent;
|
||||||
if (dir1Comp && !dir2Comp) {
|
var dir2Comp = dir2.isComponent;
|
||||||
return -1;
|
if (dir1Comp && !dir2Comp) {
|
||||||
} else if (!dir1Comp && dir2Comp) {
|
return -1;
|
||||||
return 1;
|
} else if (!dir1Comp && dir2Comp) {
|
||||||
} else {
|
return 1;
|
||||||
return StringWrapper.compare(dir1.type.typeName, dir2.type.typeName);
|
} else {
|
||||||
}
|
return StringWrapper.compare(dir1.type.name, dir2.type.name);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
return directives;
|
return directives;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createDirectiveAsts(elementName: string, directives: DirectiveMetadata[],
|
private _createDirectiveAsts(elementName: string, directives: NormalizedDirectiveMetadata[],
|
||||||
props: BoundElementOrDirectiveProperty[],
|
props: BoundElementOrDirectiveProperty[],
|
||||||
sourceInfo: string): DirectiveAst[] {
|
sourceInfo: string): DirectiveAst[] {
|
||||||
return directives.map((directive: DirectiveMetadata) => {
|
return directives.map((directive: NormalizedDirectiveMetadata) => {
|
||||||
var hostProperties: BoundElementPropertyAst[] = [];
|
var hostProperties: BoundElementPropertyAst[] = [];
|
||||||
var hostEvents: BoundEventAst[] = [];
|
var hostEvents: BoundEventAst[] = [];
|
||||||
var directiveProperties: BoundDirectivePropertyAst[] = [];
|
var directiveProperties: BoundDirectivePropertyAst[] = [];
|
||||||
|
@ -510,7 +517,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
|
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
|
||||||
var componentTypeNames: string[] = [];
|
var componentTypeNames: string[] = [];
|
||||||
directives.forEach(directive => {
|
directives.forEach(directive => {
|
||||||
var typeName = directive.directive.type.typeName;
|
var typeName = directive.directive.type.name;
|
||||||
if (directive.directive.isComponent) {
|
if (directive.directive.isComponent) {
|
||||||
componentTypeNames.push(typeName);
|
componentTypeNames.push(typeName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import {StringWrapper, isBlank} from 'angular2/src/core/facade/lang';
|
import {StringWrapper, isBlank, isJsObject} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||||
var DASH_CASE_REGEXP = /-([a-z])/g;
|
var DASH_CASE_REGEXP = /-([a-z])/g;
|
||||||
var SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n/g;
|
var SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n/g;
|
||||||
var DOUBLE_QUOTE_ESCAPE_STRING_RE = /"|\\|\n/g;
|
var DOUBLE_QUOTE_ESCAPE_STRING_RE = /"|\\|\n/g;
|
||||||
|
|
||||||
|
export var IS_DART = !isJsObject({});
|
||||||
|
|
||||||
export function camelCaseToDashCase(input: string): string {
|
export function camelCaseToDashCase(input: string): string {
|
||||||
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
||||||
(m) => { return '-' + m[1].toLowerCase(); });
|
(m) => { return '-' + m[1].toLowerCase(); });
|
||||||
|
@ -38,3 +40,35 @@ function escapeString(input: string, re: RegExp): string {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function codeGenExportVariable(name: string): string {
|
||||||
|
return IS_DART ? `var ${name} = ` : `var ${name} = exports['${name}'] = `;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function codeGenConcatArray(expression: string): string {
|
||||||
|
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function codeGenMapArray(argNames: string[], callback: string): string {
|
||||||
|
if (IS_DART) {
|
||||||
|
return `.map( (${argNames.join(',')}) => ${callback} ).toList()`;
|
||||||
|
} else {
|
||||||
|
return `.map(function(${argNames.join(',')}) { return ${callback}; })`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function codeGenReplaceAll(pattern: string, value: string): string {
|
||||||
|
if (IS_DART) {
|
||||||
|
return `.replaceAll('${pattern}', '${value}')`;
|
||||||
|
} else {
|
||||||
|
return `.replace(/${pattern}/g, '${value}')`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function codeGenValueFn(params: string[], value: string): string {
|
||||||
|
if (IS_DART) {
|
||||||
|
return `(${params.join(',')}) => ${value}`;
|
||||||
|
} else {
|
||||||
|
return `function(${params.join(',')}) { return ${value}; }`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -93,8 +93,11 @@ export class DirectiveResolver {
|
||||||
properties: mergedProperties,
|
properties: mergedProperties,
|
||||||
events: mergedEvents,
|
events: mergedEvents,
|
||||||
host: mergedHost,
|
host: mergedHost,
|
||||||
|
dynamicLoadable: dm.dynamicLoadable,
|
||||||
|
compiledHostTemplate: dm.compiledHostTemplate,
|
||||||
bindings: dm.bindings,
|
bindings: dm.bindings,
|
||||||
exportAs: dm.exportAs,
|
exportAs: dm.exportAs,
|
||||||
|
moduleId: dm.moduleId,
|
||||||
compileChildren: dm.compileChildren,
|
compileChildren: dm.compileChildren,
|
||||||
changeDetection: dm.changeDetection,
|
changeDetection: dm.changeDetection,
|
||||||
viewBindings: dm.viewBindings
|
viewBindings: dm.viewBindings
|
||||||
|
@ -108,6 +111,7 @@ export class DirectiveResolver {
|
||||||
host: mergedHost,
|
host: mergedHost,
|
||||||
bindings: dm.bindings,
|
bindings: dm.bindings,
|
||||||
exportAs: dm.exportAs,
|
exportAs: dm.exportAs,
|
||||||
|
moduleId: dm.moduleId,
|
||||||
compileChildren: dm.compileChildren
|
compileChildren: dm.compileChildren
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Type, CONST_EXPR, isPresent} from 'angular2/src/core/facade/lang';
|
import {Type, CONST_EXPR, isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
||||||
import {
|
import {
|
||||||
RenderTemplateCmd,
|
RenderTemplateCmd,
|
||||||
RenderCommandVisitor,
|
RenderCommandVisitor,
|
||||||
|
@ -10,7 +10,35 @@ import {
|
||||||
} from 'angular2/src/core/render/render';
|
} from 'angular2/src/core/render/render';
|
||||||
|
|
||||||
export class CompiledTemplate {
|
export class CompiledTemplate {
|
||||||
constructor(public id: string, public commands: TemplateCmd[]) {}
|
private _changeDetectorFactories: Function[] = null;
|
||||||
|
private _styles: string[] = null;
|
||||||
|
private _commands: TemplateCmd[] = null;
|
||||||
|
// Note: paramGetter is a function so that we can have cycles between templates!
|
||||||
|
constructor(public id: number, private _paramGetter: Function) {}
|
||||||
|
|
||||||
|
private _init() {
|
||||||
|
if (isBlank(this._commands)) {
|
||||||
|
var params = this._paramGetter();
|
||||||
|
this._changeDetectorFactories = params[0];
|
||||||
|
this._commands = params[1];
|
||||||
|
this._styles = params[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get changeDetectorFactories(): Function[] {
|
||||||
|
this._init();
|
||||||
|
return this._changeDetectorFactories;
|
||||||
|
}
|
||||||
|
|
||||||
|
get styles(): string[] {
|
||||||
|
this._init();
|
||||||
|
return this._styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
get commands(): TemplateCmd[] {
|
||||||
|
this._init();
|
||||||
|
return this._commands;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY_ARR = CONST_EXPR([]);
|
const EMPTY_ARR = CONST_EXPR([]);
|
||||||
|
@ -73,14 +101,14 @@ export function endElement(): TemplateCmd {
|
||||||
|
|
||||||
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
|
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
|
||||||
isBound: boolean = true;
|
isBound: boolean = true;
|
||||||
templateId: string;
|
templateId: number;
|
||||||
component: Type;
|
component: Type;
|
||||||
constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[],
|
constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[],
|
||||||
public variableNameAndValues: string[], public directives: Type[],
|
public variableNameAndValues: string[], public directives: Type[],
|
||||||
public nativeShadow: boolean, public ngContentIndex: number,
|
public nativeShadow: boolean, public ngContentIndex: number,
|
||||||
public template: CompiledTemplate) {
|
public template: CompiledTemplate) {
|
||||||
this.component = directives[0];
|
this.component = directives[0];
|
||||||
this.templateId = isPresent(template) ? template.id : null;
|
this.templateId = template.id;
|
||||||
}
|
}
|
||||||
visit(visitor: CommandVisitor, context: any): any {
|
visit(visitor: CommandVisitor, context: any): any {
|
||||||
return visitor.visitBeginComponent(this, context);
|
return visitor.visitBeginComponent(this, context);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ViewResolver {
|
export class ViewResolver {
|
||||||
_cache: Map<Type, /*node*/ any> = new Map();
|
_cache: Map<Type, ViewMetadata> = new Map();
|
||||||
|
|
||||||
resolve(component: Type): ViewMetadata {
|
resolve(component: Type): ViewMetadata {
|
||||||
var view = this._cache.get(component);
|
var view = this._cache.get(component);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
library angular2.src.core.metadata;
|
library angular2.src.core.metadata;
|
||||||
|
|
||||||
import "package:angular2/src/core/facade/collection.dart" show List;
|
import 'package:angular2/src/core/facade/collection.dart' show List;
|
||||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||||
import "./metadata/di.dart";
|
import './metadata/di.dart';
|
||||||
import "./metadata/directives.dart";
|
import './metadata/directives.dart';
|
||||||
import "./metadata/view.dart";
|
import './metadata/view.dart';
|
||||||
|
|
||||||
export "./metadata/di.dart";
|
export './metadata/di.dart';
|
||||||
export "./metadata/directives.dart";
|
export './metadata/directives.dart';
|
||||||
export "./metadata/view.dart";
|
export './metadata/view.dart';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See: [DirectiveMetadata] for docs.
|
* See: [DirectiveMetadata] for docs.
|
||||||
|
@ -16,7 +16,7 @@ export "./metadata/view.dart";
|
||||||
class Directive extends DirectiveMetadata {
|
class Directive extends DirectiveMetadata {
|
||||||
const Directive({String selector, List<String> properties,
|
const Directive({String selector, List<String> properties,
|
||||||
List<String> events, Map<String, String> host,
|
List<String> events, Map<String, String> host,
|
||||||
List bindings, String exportAs,
|
List bindings, String exportAs, String moduleId,
|
||||||
bool compileChildren: true})
|
bool compileChildren: true})
|
||||||
: super(
|
: super(
|
||||||
selector: selector,
|
selector: selector,
|
||||||
|
@ -25,6 +25,7 @@ class Directive extends DirectiveMetadata {
|
||||||
host: host,
|
host: host,
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
exportAs: exportAs,
|
exportAs: exportAs,
|
||||||
|
moduleId: moduleId,
|
||||||
compileChildren: compileChildren);
|
compileChildren: compileChildren);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,16 +34,18 @@ class Directive extends DirectiveMetadata {
|
||||||
*/
|
*/
|
||||||
class Component extends ComponentMetadata {
|
class Component extends ComponentMetadata {
|
||||||
const Component({String selector, List<String> properties,
|
const Component({String selector, List<String> properties,
|
||||||
List<String> events, Map<String, String> host,
|
List<String> events, Map<String, String> host, bool dynamicLoadable,
|
||||||
List bindings, String exportAs,
|
List bindings, String exportAs, String moduleId,
|
||||||
bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection})
|
bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection})
|
||||||
: super(
|
: super(
|
||||||
selector: selector,
|
selector: selector,
|
||||||
properties: properties,
|
properties: properties,
|
||||||
events: events,
|
events: events,
|
||||||
host: host,
|
host: host,
|
||||||
|
dynamicLoadable: dynamicLoadable,
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
exportAs: exportAs,
|
exportAs: exportAs,
|
||||||
|
moduleId: moduleId,
|
||||||
compileChildren: compileChildren,
|
compileChildren: compileChildren,
|
||||||
viewBindings: viewBindings,
|
viewBindings: viewBindings,
|
||||||
changeDetection: changeDetection);
|
changeDetection: changeDetection);
|
||||||
|
|
|
@ -139,11 +139,11 @@ export interface ViewDecorator extends TypeDecorator {
|
||||||
export interface DirectiveFactory {
|
export interface DirectiveFactory {
|
||||||
(obj: {
|
(obj: {
|
||||||
selector?: string, properties?: string[], events?: string[], host?: StringMap<string, string>,
|
selector?: string, properties?: string[], events?: string[], host?: StringMap<string, string>,
|
||||||
bindings?: any[], exportAs?: string, compileChildren?: boolean;
|
bindings?: any[], exportAs?: string, moduleId?: string, compileChildren?: boolean;
|
||||||
}): DirectiveDecorator;
|
}): DirectiveDecorator;
|
||||||
new (obj: {
|
new (obj: {
|
||||||
selector?: string, properties?: string[], events?: string[], host?: StringMap<string, string>,
|
selector?: string, properties?: string[], events?: string[], host?: StringMap<string, string>,
|
||||||
bindings?: any[], exportAs?: string, compileChildren?: boolean;
|
bindings?: any[], exportAs?: string, moduleId?: string, compileChildren?: boolean;
|
||||||
}): DirectiveMetadata;
|
}): DirectiveMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,8 +196,10 @@ export interface ComponentFactory {
|
||||||
properties?: string[],
|
properties?: string[],
|
||||||
events?: string[],
|
events?: string[],
|
||||||
host?: StringMap<string, string>,
|
host?: StringMap<string, string>,
|
||||||
|
dynamicLoadable?: boolean,
|
||||||
bindings?: any[],
|
bindings?: any[],
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
|
moduleId?: string,
|
||||||
compileChildren?: boolean,
|
compileChildren?: boolean,
|
||||||
viewBindings?: any[],
|
viewBindings?: any[],
|
||||||
changeDetection?: ChangeDetectionStrategy,
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
|
@ -207,8 +209,10 @@ export interface ComponentFactory {
|
||||||
properties?: string[],
|
properties?: string[],
|
||||||
events?: string[],
|
events?: string[],
|
||||||
host?: StringMap<string, string>,
|
host?: StringMap<string, string>,
|
||||||
|
dynamicLoadable?: boolean,
|
||||||
bindings?: any[],
|
bindings?: any[],
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
|
moduleId?: string,
|
||||||
compileChildren?: boolean,
|
compileChildren?: boolean,
|
||||||
viewBindings?: any[],
|
viewBindings?: any[],
|
||||||
changeDetection?: ChangeDetectionStrategy,
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {isPresent, CONST, CONST_EXPR} from 'angular2/src/core/facade/lang';
|
import {isPresent, CONST, CONST_EXPR, Type} from 'angular2/src/core/facade/lang';
|
||||||
import {InjectableMetadata} from 'angular2/src/core/di/metadata';
|
import {InjectableMetadata} from 'angular2/src/core/di/metadata';
|
||||||
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
||||||
|
|
||||||
|
@ -699,8 +699,29 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||||
*/
|
*/
|
||||||
exportAs: string;
|
exportAs: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module id of the module that contains the directive.
|
||||||
|
* Needed to be able to resolve relative urls for templates and styles.
|
||||||
|
* In Dart, this can be determined automatically and does not need to be set.
|
||||||
|
* In CommonJS, this can always be set to `module.id`.
|
||||||
|
*
|
||||||
|
* ## Simple Example
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Directive({
|
||||||
|
* selector: 'someDir',
|
||||||
|
* moduleId: module.id
|
||||||
|
* })
|
||||||
|
* class SomeDir {
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
moduleId: string;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
selector, properties, events, host, bindings, exportAs, compileChildren = true,
|
selector, properties, events, host, bindings, exportAs, moduleId,
|
||||||
|
compileChildren = true,
|
||||||
}: {
|
}: {
|
||||||
selector?: string,
|
selector?: string,
|
||||||
properties?: string[],
|
properties?: string[],
|
||||||
|
@ -708,6 +729,7 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||||
host?: StringMap<string, string>,
|
host?: StringMap<string, string>,
|
||||||
bindings?: any[],
|
bindings?: any[],
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
|
moduleId?: string,
|
||||||
compileChildren?: boolean,
|
compileChildren?: boolean,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
super();
|
super();
|
||||||
|
@ -716,6 +738,7 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.exportAs = exportAs;
|
this.exportAs = exportAs;
|
||||||
|
this.moduleId = moduleId;
|
||||||
this.compileChildren = compileChildren;
|
this.compileChildren = compileChildren;
|
||||||
this.bindings = bindings;
|
this.bindings = bindings;
|
||||||
}
|
}
|
||||||
|
@ -764,6 +787,34 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||||
*/
|
*/
|
||||||
@CONST()
|
@CONST()
|
||||||
export class ComponentMetadata extends DirectiveMetadata {
|
export class ComponentMetadata extends DirectiveMetadata {
|
||||||
|
/**
|
||||||
|
* Declare that this component can be programatically loaded.
|
||||||
|
* Every component that is used in bootstrap, routing, ... has to be
|
||||||
|
* annotated with this.
|
||||||
|
*
|
||||||
|
* ## Example
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Component({
|
||||||
|
* selector: 'root',
|
||||||
|
* dynamicLoadable: true
|
||||||
|
* })
|
||||||
|
* @View({
|
||||||
|
* template: 'hello world!'
|
||||||
|
* })
|
||||||
|
* class RootComponent {
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
dynamicLoadable: boolean;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by build tools to store the compiled template.
|
||||||
|
* Not intended to be used by a user.
|
||||||
|
*/
|
||||||
|
compiledHostTemplate: /* CompiledTemplate */ any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the used change detection strategy.
|
* Defines the used change detection strategy.
|
||||||
*
|
*
|
||||||
|
@ -817,14 +868,18 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||||
*/
|
*/
|
||||||
viewBindings: any[];
|
viewBindings: any[];
|
||||||
|
|
||||||
constructor({selector, properties, events, host, exportAs, bindings, viewBindings,
|
constructor({selector, properties, events, host, dynamicLoadable, compiledHostTemplate, exportAs,
|
||||||
changeDetection = ChangeDetectionStrategy.Default, compileChildren = true}: {
|
moduleId, bindings, viewBindings, changeDetection = ChangeDetectionStrategy.Default,
|
||||||
|
compileChildren = true}: {
|
||||||
selector?: string,
|
selector?: string,
|
||||||
properties?: string[],
|
properties?: string[],
|
||||||
events?: string[],
|
events?: string[],
|
||||||
host?: StringMap<string, string>,
|
host?: StringMap<string, string>,
|
||||||
|
dynamicLoadable?: boolean,
|
||||||
|
compiledHostTemplate?: any,
|
||||||
bindings?: any[],
|
bindings?: any[],
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
|
moduleId?: string,
|
||||||
compileChildren?: boolean,
|
compileChildren?: boolean,
|
||||||
viewBindings?: any[],
|
viewBindings?: any[],
|
||||||
changeDetection?: ChangeDetectionStrategy,
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
|
@ -835,12 +890,15 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||||
events: events,
|
events: events,
|
||||||
host: host,
|
host: host,
|
||||||
exportAs: exportAs,
|
exportAs: exportAs,
|
||||||
|
moduleId: moduleId,
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
compileChildren: compileChildren
|
compileChildren: compileChildren
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeDetection = changeDetection;
|
this.changeDetection = changeDetection;
|
||||||
this.viewBindings = viewBindings;
|
this.viewBindings = viewBindings;
|
||||||
|
this.dynamicLoadable = dynamicLoadable;
|
||||||
|
this.compiledHostTemplate = compiledHostTemplate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,5 +11,8 @@ export interface PlatformReflectionCapabilities {
|
||||||
getter(name: string): GetterFn;
|
getter(name: string): GetterFn;
|
||||||
setter(name: string): SetterFn;
|
setter(name: string): SetterFn;
|
||||||
method(name: string): MethodFn;
|
method(name: string): MethodFn;
|
||||||
|
// TODO(tbosch): remove this method after the new compiler is done
|
||||||
|
// (and ComponentUrlMapper as well).
|
||||||
importUri(type: Type): string;
|
importUri(type: Type): string;
|
||||||
|
moduleId(type: Type): string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ class NoReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
String importUri(Type type) => './';
|
String importUri(Type type) => './';
|
||||||
|
|
||||||
|
String moduleId(Type type) => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Reflector reflector = new Reflector(new NoReflectionCapabilities());
|
final Reflector reflector = new Reflector(new NoReflectionCapabilities());
|
||||||
|
|
|
@ -5,6 +5,8 @@ import 'types.dart';
|
||||||
import 'dart:mirrors';
|
import 'dart:mirrors';
|
||||||
import 'platform_reflection_capabilities.dart';
|
import 'platform_reflection_capabilities.dart';
|
||||||
|
|
||||||
|
var DOT_REGEX = new RegExp('\\.');
|
||||||
|
|
||||||
class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
ReflectionCapabilities([metadataReader]) {}
|
ReflectionCapabilities([metadataReader]) {}
|
||||||
|
|
||||||
|
@ -315,4 +317,8 @@ class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
String importUri(Type type) {
|
String importUri(Type type) {
|
||||||
return '${(reflectClass(type).owner as LibraryMirror).uri}';
|
return '${(reflectClass(type).owner as LibraryMirror).uri}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String moduleId(Type type) {
|
||||||
|
return '${MirrorSystem.getName((reflectClass(type).owner as LibraryMirror).qualifiedName).replaceAll(DOT_REGEX, "/")}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,4 +168,6 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
|
|
||||||
// There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
// There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
||||||
importUri(type: Type): string { return './'; }
|
importUri(type: Type): string { return './'; }
|
||||||
|
|
||||||
|
moduleId(type: Type): string { return null; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,8 @@ export class Reflector {
|
||||||
_containsReflectionInfo(typeOrFunc) { return this._injectableInfo.has(typeOrFunc); }
|
_containsReflectionInfo(typeOrFunc) { return this._injectableInfo.has(typeOrFunc); }
|
||||||
|
|
||||||
importUri(type: Type): string { return this.reflectionCapabilities.importUri(type); }
|
importUri(type: Type): string { return this.reflectionCapabilities.importUri(type); }
|
||||||
|
|
||||||
|
moduleId(type: Type): string { return this.reflectionCapabilities.moduleId(type); }
|
||||||
}
|
}
|
||||||
|
|
||||||
function _mergeMaps(target: Map<any, any>, config: StringMap<string, Function>): void {
|
function _mergeMaps(target: Map<any, any>, config: StringMap<string, Function>): void {
|
||||||
|
|
|
@ -409,7 +409,7 @@ export interface RenderBeginElementCmd extends RenderBeginCmd {
|
||||||
|
|
||||||
export interface RenderBeginComponentCmd extends RenderBeginElementCmd {
|
export interface RenderBeginComponentCmd extends RenderBeginElementCmd {
|
||||||
nativeShadow: boolean;
|
nativeShadow: boolean;
|
||||||
templateId: string;
|
templateId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
||||||
|
|
|
@ -29,8 +29,11 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
||||||
properties: dm.properties,
|
properties: dm.properties,
|
||||||
events: dm.events,
|
events: dm.events,
|
||||||
host: dm.host,
|
host: dm.host,
|
||||||
|
dynamicLoadable: dm.dynamicLoadable,
|
||||||
|
compiledHostTemplate: dm.compiledHostTemplate,
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
exportAs: dm.exportAs,
|
exportAs: dm.exportAs,
|
||||||
|
moduleId: dm.moduleId,
|
||||||
compileChildren: dm.compileChildren,
|
compileChildren: dm.compileChildren,
|
||||||
changeDetection: dm.changeDetection,
|
changeDetection: dm.changeDetection,
|
||||||
viewBindings: viewBindings
|
viewBindings: viewBindings
|
||||||
|
@ -44,6 +47,7 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
||||||
host: dm.host,
|
host: dm.host,
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
exportAs: dm.exportAs,
|
exportAs: dm.exportAs,
|
||||||
|
moduleId: dm.moduleId,
|
||||||
compileChildren: dm.compileChildren
|
compileChildren: dm.compileChildren
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
// TODO(tbosch): Move the corresponding code into angular2/src/compiler once
|
// TODO(tbosch): Move the corresponding code into angular2/src/compiler once
|
||||||
// the new compiler is done.
|
// the new compiler is done.
|
||||||
export class Codegen {
|
export class Codegen {
|
||||||
|
constructor(moduleAlias: string) {}
|
||||||
generate(typeName: string, changeDetectorTypeName: string, def: ChangeDetectorDefinition): void {
|
generate(typeName: string, changeDetectorTypeName: string, def: ChangeDetectorDefinition): void {
|
||||||
throw "Not implemented in JS";
|
throw "Not implemented in JS";
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,15 @@ import {
|
||||||
inject,
|
inject,
|
||||||
it,
|
it,
|
||||||
xit,
|
xit,
|
||||||
TestComponentBuilder
|
TestComponentBuilder,
|
||||||
|
beforeEachBindings
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
import {
|
||||||
import {DirectiveMetadata, TypeMetadata, ChangeDetectionMetadata} from 'angular2/src/compiler/api';
|
NormalizedDirectiveMetadata,
|
||||||
import {MockSchemaRegistry} from './template_parser_spec';
|
TypeMetadata,
|
||||||
|
ChangeDetectionMetadata
|
||||||
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||||
import {
|
import {
|
||||||
Parser,
|
Parser,
|
||||||
|
@ -33,9 +36,12 @@ import {Pipes} from 'angular2/src/core/change_detection/pipes';
|
||||||
import {createChangeDetectorDefinitions} from 'angular2/src/compiler/change_definition_factory';
|
import {createChangeDetectorDefinitions} from 'angular2/src/compiler/change_definition_factory';
|
||||||
import {TestContext, TestDirective, TestDispatcher, TestPipes} from './change_detector_mocks';
|
import {TestContext, TestDirective, TestDispatcher, TestPipes} from './change_detector_mocks';
|
||||||
|
|
||||||
|
import {TEST_BINDINGS} from './test_bindings';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ChangeDefinitionFactory', () => {
|
describe('ChangeDefinitionFactory', () => {
|
||||||
var domParser: HtmlParser;
|
beforeEachBindings(() => TEST_BINDINGS);
|
||||||
|
|
||||||
var parser: TemplateParser;
|
var parser: TemplateParser;
|
||||||
var dispatcher: TestDispatcher;
|
var dispatcher: TestDispatcher;
|
||||||
var context: TestContext;
|
var context: TestContext;
|
||||||
|
@ -44,26 +50,23 @@ export function main() {
|
||||||
var pipes: Pipes;
|
var pipes: Pipes;
|
||||||
var eventLocals: Locals;
|
var eventLocals: Locals;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(inject([TemplateParser], (_templateParser) => {
|
||||||
domParser = new HtmlParser();
|
parser = _templateParser;
|
||||||
parser = new TemplateParser(
|
|
||||||
new Parser(new Lexer()),
|
|
||||||
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
|
|
||||||
context = new TestContext();
|
context = new TestContext();
|
||||||
directive = new TestDirective();
|
directive = new TestDirective();
|
||||||
dispatcher = new TestDispatcher([directive], []);
|
dispatcher = new TestDispatcher([directive], []);
|
||||||
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
||||||
eventLocals = new Locals(null, MapWrapper.createFromStringMap({'$event': null}));
|
eventLocals = new Locals(null, MapWrapper.createFromStringMap({'$event': null}));
|
||||||
pipes = new TestPipes();
|
pipes = new TestPipes();
|
||||||
});
|
}));
|
||||||
|
|
||||||
function createChangeDetector(template: string, directives: DirectiveMetadata[],
|
function createChangeDetector(template: string, directives: NormalizedDirectiveMetadata[],
|
||||||
protoViewIndex: number = 0): ChangeDetector {
|
protoViewIndex: number = 0): ChangeDetector {
|
||||||
var protoChangeDetectors =
|
var protoChangeDetectors =
|
||||||
createChangeDetectorDefinitions(
|
createChangeDetectorDefinitions(new TypeMetadata({name: 'SomeComp'}),
|
||||||
new TypeMetadata({typeName: 'SomeComp'}), ChangeDetectionStrategy.Default,
|
ChangeDetectionStrategy.Default,
|
||||||
new ChangeDetectorGenConfig(true, true, false, false),
|
new ChangeDetectorGenConfig(true, true, false, false),
|
||||||
parser.parse(domParser.parse(template, 'TestComp'), directives))
|
parser.parse(template, directives, 'TestComp'))
|
||||||
.map(definition => new DynamicProtoChangeDetector(definition));
|
.map(definition => new DynamicProtoChangeDetector(definition));
|
||||||
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate(dispatcher);
|
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate(dispatcher);
|
||||||
changeDetector.hydrate(context, locals, dispatcher, pipes);
|
changeDetector.hydrate(context, locals, dispatcher, pipes);
|
||||||
|
@ -103,8 +106,8 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should write directive properties', () => {
|
it('should write directive properties', () => {
|
||||||
var dirMeta = new DirectiveMetadata({
|
var dirMeta = new NormalizedDirectiveMetadata({
|
||||||
type: new TypeMetadata({typeName: 'SomeDir'}),
|
type: new TypeMetadata({name: 'SomeDir'}),
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['dirProp']})
|
changeDetection: new ChangeDetectionMetadata({properties: ['dirProp']})
|
||||||
});
|
});
|
||||||
|
@ -117,8 +120,8 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should watch directive host properties', () => {
|
it('should watch directive host properties', () => {
|
||||||
var dirMeta = new DirectiveMetadata({
|
var dirMeta = new NormalizedDirectiveMetadata({
|
||||||
type: new TypeMetadata({typeName: 'SomeDir'}),
|
type: new TypeMetadata({name: 'SomeDir'}),
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
changeDetection: new ChangeDetectionMetadata({hostProperties: {'elProp': 'dirProp'}})
|
changeDetection: new ChangeDetectionMetadata({hostProperties: {'elProp': 'dirProp'}})
|
||||||
});
|
});
|
||||||
|
@ -131,8 +134,8 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle directive events', () => {
|
it('should handle directive events', () => {
|
||||||
var dirMeta = new DirectiveMetadata({
|
var dirMeta = new NormalizedDirectiveMetadata({
|
||||||
type: new TypeMetadata({typeName: 'SomeDir'}),
|
type: new TypeMetadata({name: 'SomeDir'}),
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
changeDetection:
|
changeDetection:
|
||||||
new ChangeDetectionMetadata({hostListeners: {'click': 'onEvent($event)'}})
|
new ChangeDetectionMetadata({hostListeners: {'click': 'onEvent($event)'}})
|
||||||
|
|
|
@ -9,9 +9,10 @@ import {
|
||||||
beforeEach,
|
beforeEach,
|
||||||
afterEach,
|
afterEach,
|
||||||
AsyncTestCompleter,
|
AsyncTestCompleter,
|
||||||
inject
|
inject,
|
||||||
|
beforeEachBindings
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {IS_DART} from '../platform';
|
import {bind} from 'angular2/src/core/di';
|
||||||
|
|
||||||
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
|
@ -19,21 +20,16 @@ import {Promise} from 'angular2/src/core/facade/async';
|
||||||
|
|
||||||
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
||||||
|
|
||||||
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
|
||||||
import {
|
import {
|
||||||
DirectiveMetadata,
|
NormalizedDirectiveMetadata,
|
||||||
TypeMetadata,
|
TypeMetadata,
|
||||||
ChangeDetectionMetadata,
|
ChangeDetectionMetadata
|
||||||
SourceModule
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
} from 'angular2/src/compiler/api';
|
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
|
||||||
|
|
||||||
import {MockSchemaRegistry} from './template_parser_spec';
|
|
||||||
|
|
||||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Parser,
|
|
||||||
Lexer,
|
|
||||||
ChangeDetectorGenConfig,
|
ChangeDetectorGenConfig,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDispatcher,
|
ChangeDispatcher,
|
||||||
|
@ -45,61 +41,76 @@ import {
|
||||||
|
|
||||||
import {evalModule} from './eval_module';
|
import {evalModule} from './eval_module';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
// Attention: These module names have to correspond to real modules!
|
// Attention: These module names have to correspond to real modules!
|
||||||
const MODULE_NAME = 'angular2/test/compiler/change_detector_compiler_spec';
|
const THIS_MODULE = 'angular2/test/compiler/change_detector_compiler_spec';
|
||||||
|
var THIS_MODULE_REF = moduleRef(THIS_MODULE);
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ChangeDetectorCompiler', () => {
|
describe('ChangeDetectorCompiler', () => {
|
||||||
var domParser: HtmlParser;
|
beforeEachBindings(() => TEST_BINDINGS);
|
||||||
|
|
||||||
var parser: TemplateParser;
|
var parser: TemplateParser;
|
||||||
|
var compiler: ChangeDetectionCompiler;
|
||||||
|
|
||||||
function createCompiler(useJit: boolean): ChangeDetectionCompiler {
|
beforeEach(inject([TemplateParser, ChangeDetectionCompiler], (_parser, _compiler) => {
|
||||||
return new ChangeDetectionCompiler(new ChangeDetectorGenConfig(true, true, false, useJit));
|
parser = _parser;
|
||||||
}
|
compiler = _compiler;
|
||||||
|
}));
|
||||||
beforeEach(() => {
|
|
||||||
domParser = new HtmlParser();
|
|
||||||
parser = new TemplateParser(
|
|
||||||
new Parser(new Lexer()),
|
|
||||||
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('compileComponentRuntime', () => {
|
describe('compileComponentRuntime', () => {
|
||||||
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
||||||
directives: DirectiveMetadata[] = CONST_EXPR([])): string[] {
|
directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])): string[] {
|
||||||
var type = new TypeMetadata({typeName: 'SomeComp'});
|
var type = new TypeMetadata({name: 'SomeComp'});
|
||||||
var parsedTemplate = parser.parse(domParser.parse(template, 'TestComp'), directives);
|
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
||||||
var factories =
|
var factories =
|
||||||
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||||
return testChangeDetector(factories[0]);
|
return testChangeDetector(factories[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should watch element properties (no jit)', () => {
|
describe('no jit', () => {
|
||||||
expect(detectChanges(createCompiler(false), '<div [el-prop]="someProp">'))
|
beforeEachBindings(() => [
|
||||||
.toEqual(['elementProperty(elProp)=someValue']);
|
bind(ChangeDetectorGenConfig)
|
||||||
|
.toValue(new ChangeDetectorGenConfig(true, true, false, false))
|
||||||
|
]);
|
||||||
|
it('should watch element properties', () => {
|
||||||
|
expect(detectChanges(compiler, '<div [el-prop]="someProp">'))
|
||||||
|
.toEqual(['elementProperty(elProp)=someValue']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should watch element properties (jit)', () => {
|
describe('jit', () => {
|
||||||
expect(detectChanges(createCompiler(true), '<div [el-prop]="someProp">'))
|
beforeEachBindings(() => [
|
||||||
.toEqual(['elementProperty(elProp)=someValue']);
|
bind(ChangeDetectorGenConfig)
|
||||||
|
.toValue(new ChangeDetectorGenConfig(true, true, false, true))
|
||||||
|
]);
|
||||||
|
it('should watch element properties', () => {
|
||||||
|
expect(detectChanges(compiler, '<div [el-prop]="someProp">'))
|
||||||
|
.toEqual(['elementProperty(elProp)=someValue']);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('compileComponentCodeGen', () => {
|
describe('compileComponentCodeGen', () => {
|
||||||
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
||||||
directives: DirectiveMetadata[] = CONST_EXPR([])): Promise<string[]> {
|
directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])):
|
||||||
var type = new TypeMetadata({typeName: 'SomeComp'});
|
Promise<string[]> {
|
||||||
var parsedTemplate = parser.parse(domParser.parse(template, 'TestComp'), directives);
|
var type = new TypeMetadata({name: 'SomeComp'});
|
||||||
var sourceModule =
|
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
||||||
|
var sourceExpression =
|
||||||
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||||
var testableModule = createTestableModule(sourceModule, 0);
|
var testableModule = createTestableModule(sourceExpression, 0).getSourceWithImports();
|
||||||
return evalModule(testableModule.source, testableModule.imports, null);
|
return evalModule(testableModule.source, testableModule.imports, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should watch element properties', inject([AsyncTestCompleter], (async) => {
|
it('should watch element properties', inject([AsyncTestCompleter], (async) => {
|
||||||
detectChanges(createCompiler(true), '<div [el-prop]="someProp">')
|
detectChanges(compiler, '<div [el-prop]="someProp">')
|
||||||
.then((value) => {
|
.then((value) => {
|
||||||
expect(value).toEqual(['elementProperty(elProp)=someValue']);
|
expect(value).toEqual(['elementProperty(elProp)=someValue']);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -111,19 +122,12 @@ export function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTestableModule(source: SourceExpression, changeDetectorIndex: number): SourceModule {
|
||||||
function createTestableModule(sourceModule: SourceModule, changeDetectorIndex: number):
|
var resultExpression =
|
||||||
SourceModule {
|
`${THIS_MODULE_REF}testChangeDetector((${source.expression})[${changeDetectorIndex}])`;
|
||||||
var testableSource;
|
var testableSource = `${source.declarations.join('\n')}
|
||||||
var testableImports = [[MODULE_NAME, 'mocks']].concat(sourceModule.imports);
|
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
|
||||||
if (IS_DART) {
|
return new SourceModule(null, testableSource);
|
||||||
testableSource = `${sourceModule.source}
|
|
||||||
run(_) { return mocks.testChangeDetector(CHANGE_DETECTORS[${changeDetectorIndex}]); }`;
|
|
||||||
} else {
|
|
||||||
testableSource = `${sourceModule.source}
|
|
||||||
exports.run = function(_) { return mocks.testChangeDetector(CHANGE_DETECTORS[${changeDetectorIndex}]); }`;
|
|
||||||
}
|
|
||||||
return new SourceModule(null, testableSource, testableImports);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function testChangeDetector(changeDetectorFactory: Function): string[] {
|
export function testChangeDetector(changeDetectorFactory: Function): string[] {
|
||||||
|
|
|
@ -9,16 +9,13 @@ import {
|
||||||
beforeEach,
|
beforeEach,
|
||||||
afterEach,
|
afterEach,
|
||||||
AsyncTestCompleter,
|
AsyncTestCompleter,
|
||||||
inject
|
inject,
|
||||||
|
beforeEachBindings
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {IS_DART} from '../platform';
|
|
||||||
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 {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||||
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
|
||||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||||
import {MockSchemaRegistry} from './template_parser_spec';
|
|
||||||
import {Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {
|
import {
|
||||||
CommandVisitor,
|
CommandVisitor,
|
||||||
TextCmd,
|
TextCmd,
|
||||||
|
@ -32,14 +29,19 @@ import {
|
||||||
} from 'angular2/src/core/compiler/template_commands';
|
} from 'angular2/src/core/compiler/template_commands';
|
||||||
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
||||||
import {
|
import {
|
||||||
DirectiveMetadata,
|
NormalizedDirectiveMetadata,
|
||||||
TypeMetadata,
|
TypeMetadata,
|
||||||
TemplateMetadata,
|
NormalizedTemplateMetadata
|
||||||
SourceModule
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
} from 'angular2/src/compiler/api';
|
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
import {evalModule} from './eval_module';
|
import {evalModule} from './eval_module';
|
||||||
import {escapeSingleQuoteString} from 'angular2/src/compiler/util';
|
import {
|
||||||
|
escapeSingleQuoteString,
|
||||||
|
codeGenValueFn,
|
||||||
|
codeGenExportVariable
|
||||||
|
} from 'angular2/src/compiler/util';
|
||||||
|
import {TEST_BINDINGS} from './test_bindings';
|
||||||
|
|
||||||
const BEGIN_ELEMENT = 'BEGIN_ELEMENT';
|
const BEGIN_ELEMENT = 'BEGIN_ELEMENT';
|
||||||
const END_ELEMENT = 'END_ELEMENT';
|
const END_ELEMENT = 'END_ELEMENT';
|
||||||
|
@ -50,8 +52,9 @@ const NG_CONTENT = 'NG_CONTENT';
|
||||||
const EMBEDDED_TEMPLATE = 'EMBEDDED_TEMPLATE';
|
const EMBEDDED_TEMPLATE = 'EMBEDDED_TEMPLATE';
|
||||||
|
|
||||||
// Attention: These module names have to correspond to real modules!
|
// Attention: These module names have to correspond to real modules!
|
||||||
const MODULE_NAME = 'angular2/test/compiler/command_compiler_spec';
|
const THIS_MODULE_NAME = 'angular2/test/compiler/command_compiler_spec';
|
||||||
const TEMPLATE_COMMANDS_MODULE_NAME = 'angular2/src/core/compiler/template_commands';
|
var THIS_MODULE_REF = moduleRef(THIS_MODULE_NAME);
|
||||||
|
var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
|
||||||
|
|
||||||
// Attention: read by eval!
|
// Attention: read by eval!
|
||||||
export class RootComp {}
|
export class RootComp {}
|
||||||
|
@ -59,27 +62,26 @@ export class SomeDir {}
|
||||||
export class AComp {}
|
export class AComp {}
|
||||||
|
|
||||||
var RootCompTypeMeta =
|
var RootCompTypeMeta =
|
||||||
new TypeMetadata({typeName: 'RootComp', id: 1, type: RootComp, typeUrl: MODULE_NAME});
|
new TypeMetadata({id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME});
|
||||||
var SomeDirTypeMeta =
|
var SomeDirTypeMeta =
|
||||||
new TypeMetadata({typeName: 'SomeDir', id: 2, type: SomeDir, typeUrl: MODULE_NAME});
|
new TypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME});
|
||||||
var ACompTypeMeta = new TypeMetadata({typeName: 'AComp', id: 3, type: AComp, typeUrl: MODULE_NAME});
|
var ACompTypeMeta =
|
||||||
|
new TypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME});
|
||||||
|
|
||||||
var NESTED_COMPONENT = new CompiledTemplate('someNestedComponentId', []);
|
var NESTED_COMPONENT = new CompiledTemplate(45, () => []);
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('CommandCompiler', () => {
|
describe('CommandCompiler', () => {
|
||||||
var domParser: HtmlParser;
|
beforeEachBindings(() => TEST_BINDINGS);
|
||||||
|
|
||||||
var parser: TemplateParser;
|
var parser: TemplateParser;
|
||||||
var commandCompiler: CommandCompiler;
|
var commandCompiler: CommandCompiler;
|
||||||
var componentTemplateFactory: Function;
|
var componentTemplateFactory: Function;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(inject([TemplateParser, CommandCompiler], (_templateParser, _commandCompiler) => {
|
||||||
domParser = new HtmlParser();
|
parser = _templateParser;
|
||||||
parser = new TemplateParser(
|
commandCompiler = _commandCompiler;
|
||||||
new Parser(new Lexer()),
|
}));
|
||||||
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
|
|
||||||
commandCompiler = new CommandCompiler();
|
|
||||||
});
|
|
||||||
|
|
||||||
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
|
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
|
||||||
type?: TypeMetadata,
|
type?: TypeMetadata,
|
||||||
|
@ -87,7 +89,7 @@ export function main() {
|
||||||
template?: string,
|
template?: string,
|
||||||
encapsulation?: ViewEncapsulation,
|
encapsulation?: ViewEncapsulation,
|
||||||
ngContentSelectors?: string[]
|
ngContentSelectors?: string[]
|
||||||
}): DirectiveMetadata {
|
}): NormalizedDirectiveMetadata {
|
||||||
if (isBlank(encapsulation)) {
|
if (isBlank(encapsulation)) {
|
||||||
encapsulation = ViewEncapsulation.None;
|
encapsulation = ViewEncapsulation.None;
|
||||||
}
|
}
|
||||||
|
@ -100,11 +102,11 @@ export function main() {
|
||||||
if (isBlank(template)) {
|
if (isBlank(template)) {
|
||||||
template = '';
|
template = '';
|
||||||
}
|
}
|
||||||
return new DirectiveMetadata({
|
return new NormalizedDirectiveMetadata({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: type,
|
type: type,
|
||||||
template: new TemplateMetadata({
|
template: new NormalizedTemplateMetadata({
|
||||||
template: template,
|
template: template,
|
||||||
ngContentSelectors: ngContentSelectors,
|
ngContentSelectors: ngContentSelectors,
|
||||||
encapsulation: encapsulation
|
encapsulation: encapsulation
|
||||||
|
@ -112,8 +114,8 @@ export function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDirective(type: TypeMetadata, selector: string): DirectiveMetadata {
|
function createDirective(type: TypeMetadata, selector: string): NormalizedDirectiveMetadata {
|
||||||
return new DirectiveMetadata({selector: selector, isComponent: false, type: type});
|
return new NormalizedDirectiveMetadata({selector: selector, isComponent: false, type: type});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -229,7 +231,7 @@ export function main() {
|
||||||
['ACompType'],
|
['ACompType'],
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
'AComp'
|
3
|
||||||
],
|
],
|
||||||
[END_COMPONENT]
|
[END_COMPONENT]
|
||||||
]);
|
]);
|
||||||
|
@ -258,7 +260,7 @@ export function main() {
|
||||||
['ACompType'],
|
['ACompType'],
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
'AComp'
|
3
|
||||||
],
|
],
|
||||||
[END_COMPONENT]
|
[END_COMPONENT]
|
||||||
]);
|
]);
|
||||||
|
@ -273,7 +275,7 @@ export function main() {
|
||||||
run(rootComp, [comp])
|
run(rootComp, [comp])
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], true, null, 'AComp'],
|
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], true, null, 3],
|
||||||
[END_COMPONENT]
|
[END_COMPONENT]
|
||||||
]);
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -287,7 +289,7 @@ export function main() {
|
||||||
run(rootComp, [comp])
|
run(rootComp, [comp])
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], false, null, 'AComp'],
|
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], false, null, 3],
|
||||||
[TEXT, 't', false, 0],
|
[TEXT, 't', false, 0],
|
||||||
[END_COMPONENT]
|
[END_COMPONENT]
|
||||||
]);
|
]);
|
||||||
|
@ -362,15 +364,15 @@ export function main() {
|
||||||
|
|
||||||
describe('compileComponentRuntime', () => {
|
describe('compileComponentRuntime', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
componentTemplateFactory = (directiveType: TypeMetadata) => {
|
componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => {
|
||||||
return new CompiledTemplate(directiveType.typeName, []);
|
return new CompiledTemplate(directive.type.id, () => []);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function run(component: DirectiveMetadata, directives: DirectiveMetadata[]):
|
function run(component: NormalizedDirectiveMetadata,
|
||||||
Promise<any[][]> {
|
directives: NormalizedDirectiveMetadata[]): Promise<any[][]> {
|
||||||
var parsedTemplate = parser.parse(
|
var parsedTemplate =
|
||||||
domParser.parse(component.template.template, component.type.typeName), directives);
|
parser.parse(component.template.template, directives, component.type.name);
|
||||||
var commands = commandCompiler.compileComponentRuntime(component, parsedTemplate,
|
var commands = commandCompiler.compileComponentRuntime(component, parsedTemplate,
|
||||||
componentTemplateFactory);
|
componentTemplateFactory);
|
||||||
return PromiseWrapper.resolve(humanize(commands));
|
return PromiseWrapper.resolve(humanize(commands));
|
||||||
|
@ -382,19 +384,18 @@ export function main() {
|
||||||
|
|
||||||
describe('compileComponentCodeGen', () => {
|
describe('compileComponentCodeGen', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
componentTemplateFactory = (directiveType: TypeMetadata, imports: string[][]) => {
|
componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => {
|
||||||
imports.push([TEMPLATE_COMMANDS_MODULE_NAME, 'tcm']);
|
return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${directive.type.id}, ${codeGenValueFn([], '{}')})`;
|
||||||
return `new tcm.CompiledTemplate(${escapeSingleQuoteString(directiveType.typeName)}, [])`;
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function run(component: DirectiveMetadata, directives: DirectiveMetadata[]):
|
function run(component: NormalizedDirectiveMetadata,
|
||||||
Promise<any[][]> {
|
directives: NormalizedDirectiveMetadata[]): Promise<any[][]> {
|
||||||
var parsedTemplate = parser.parse(
|
var parsedTemplate =
|
||||||
domParser.parse(component.template.template, component.type.typeName), directives);
|
parser.parse(component.template.template, directives, component.type.name);
|
||||||
var sourceModule = commandCompiler.compileComponentCodeGen(component, parsedTemplate,
|
var sourceModule = commandCompiler.compileComponentCodeGen(component, parsedTemplate,
|
||||||
componentTemplateFactory);
|
componentTemplateFactory);
|
||||||
var testableModule = createTestableModule(sourceModule);
|
var testableModule = createTestableModule(sourceModule).getSourceWithImports();
|
||||||
return evalModule(testableModule.source, testableModule.imports, null);
|
return evalModule(testableModule.source, testableModule.imports, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,6 +454,7 @@ class CommandHumanizer implements CommandVisitor {
|
||||||
cmd.directives.map(checkAndStringifyType),
|
cmd.directives.map(checkAndStringifyType),
|
||||||
cmd.nativeShadow,
|
cmd.nativeShadow,
|
||||||
cmd.ngContentIndex,
|
cmd.ngContentIndex,
|
||||||
|
// TODO humanizeTemplate(cmd.template)
|
||||||
cmd.template.id
|
cmd.template.id
|
||||||
]);
|
]);
|
||||||
return null;
|
return null;
|
||||||
|
@ -475,15 +477,9 @@ class CommandHumanizer implements CommandVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTestableModule(sourceModule: SourceModule): SourceModule {
|
function createTestableModule(source: SourceExpression): SourceModule {
|
||||||
var testableSource;
|
var resultExpression = `${THIS_MODULE_REF}humanize(${source.expression})`;
|
||||||
var testableImports = [[MODULE_NAME, 'mocks']].concat(sourceModule.imports);
|
var testableSource = `${source.declarations.join('\n')}
|
||||||
if (IS_DART) {
|
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
|
||||||
testableSource = `${sourceModule.source}
|
return new SourceModule(null, testableSource);
|
||||||
run(_) { return mocks.humanize(COMMANDS); }`;
|
|
||||||
} else {
|
|
||||||
testableSource = `${sourceModule.source}
|
|
||||||
exports.run = function(_) { return mocks.humanize(COMMANDS); }`;
|
|
||||||
}
|
|
||||||
return new SourceModule(null, testableSource, testableImports);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,28 +13,29 @@ import {
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DirectiveMetadata,
|
NormalizedDirectiveMetadata,
|
||||||
TypeMetadata,
|
TypeMetadata,
|
||||||
TemplateMetadata,
|
NormalizedTemplateMetadata,
|
||||||
ChangeDetectionMetadata
|
ChangeDetectionMetadata
|
||||||
} from 'angular2/src/compiler/api';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('Compiler api', () => {
|
describe('DirectiveMetadata', () => {
|
||||||
var fullTypeMeta: TypeMetadata;
|
var fullTypeMeta: TypeMetadata;
|
||||||
var fullTemplateMeta: TemplateMetadata;
|
var fullTemplateMeta: NormalizedTemplateMetadata;
|
||||||
var fullChangeDetectionMeta: ChangeDetectionMetadata;
|
var fullChangeDetectionMeta: ChangeDetectionMetadata;
|
||||||
var fullDirectiveMeta: DirectiveMetadata;
|
var fullDirectiveMeta: NormalizedDirectiveMetadata;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fullTypeMeta = new TypeMetadata({id: 23, typeName: 'SomeType', typeUrl: 'someUrl'});
|
fullTypeMeta = new TypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'});
|
||||||
fullTemplateMeta = new TemplateMetadata({
|
fullTemplateMeta = new NormalizedTemplateMetadata({
|
||||||
encapsulation: ViewEncapsulation.Emulated,
|
encapsulation: ViewEncapsulation.Emulated,
|
||||||
template: '<a></a>',
|
template: '<a></a>',
|
||||||
styles: ['someStyle'],
|
styles: ['someStyle'],
|
||||||
styleAbsUrls: ['someStyleUrl'],
|
styleAbsUrls: ['someStyleUrl'],
|
||||||
|
hostAttributes: {'attr1': 'attrValue2'},
|
||||||
ngContentSelectors: ['*']
|
ngContentSelectors: ['*']
|
||||||
});
|
});
|
||||||
fullChangeDetectionMeta = new ChangeDetectionMetadata({
|
fullChangeDetectionMeta = new ChangeDetectionMetadata({
|
||||||
|
@ -51,10 +52,10 @@ export function main() {
|
||||||
callDoCheck: true,
|
callDoCheck: true,
|
||||||
callOnInit: true
|
callOnInit: true
|
||||||
});
|
});
|
||||||
fullDirectiveMeta = new DirectiveMetadata({
|
fullDirectiveMeta = new NormalizedDirectiveMetadata({
|
||||||
selector: 'someSelector',
|
selector: 'someSelector',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
hostAttributes: {'attr1': 'attrValue2'},
|
dynamicLoadable: true,
|
||||||
type: fullTypeMeta, template: fullTemplateMeta,
|
type: fullTypeMeta, template: fullTemplateMeta,
|
||||||
changeDetection: fullChangeDetectionMeta,
|
changeDetection: fullChangeDetectionMeta,
|
||||||
});
|
});
|
||||||
|
@ -63,12 +64,13 @@ export function main() {
|
||||||
|
|
||||||
describe('DirectiveMetadata', () => {
|
describe('DirectiveMetadata', () => {
|
||||||
it('should serialize with full data', () => {
|
it('should serialize with full data', () => {
|
||||||
expect(DirectiveMetadata.fromJson(fullDirectiveMeta.toJson())).toEqual(fullDirectiveMeta);
|
expect(NormalizedDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
|
||||||
|
.toEqual(fullDirectiveMeta);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should serialize with no data', () => {
|
it('should serialize with no data', () => {
|
||||||
var empty = new DirectiveMetadata();
|
var empty = new NormalizedDirectiveMetadata();
|
||||||
expect(DirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
|
expect(NormalizedDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -84,12 +86,13 @@ export function main() {
|
||||||
|
|
||||||
describe('TemplateMetadata', () => {
|
describe('TemplateMetadata', () => {
|
||||||
it('should serialize with full data', () => {
|
it('should serialize with full data', () => {
|
||||||
expect(TemplateMetadata.fromJson(fullTemplateMeta.toJson())).toEqual(fullTemplateMeta);
|
expect(NormalizedTemplateMetadata.fromJson(fullTemplateMeta.toJson()))
|
||||||
|
.toEqual(fullTemplateMeta);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should serialize with no data', () => {
|
it('should serialize with no data', () => {
|
||||||
var empty = new TemplateMetadata();
|
var empty = new NormalizedTemplateMetadata();
|
||||||
expect(TemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
|
expect(NormalizedTemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,31 +7,27 @@ Uri toDartDataUri(String source) {
|
||||||
}
|
}
|
||||||
|
|
||||||
createIsolateSource(String moduleSource, List<List<String>> moduleImports) {
|
createIsolateSource(String moduleSource, List<List<String>> moduleImports) {
|
||||||
var moduleSourceParts = [];
|
var moduleSourceParts = ['import "dart:isolate";'];
|
||||||
moduleImports.forEach((sourceImport) {
|
moduleImports.forEach((sourceImport) {
|
||||||
String modName = sourceImport[0];
|
String modName = sourceImport[0];
|
||||||
String modAlias = sourceImport[1];
|
String modAlias = sourceImport[1];
|
||||||
moduleSourceParts.add("import 'package:${modName}.dart' as ${modAlias};");
|
moduleSourceParts.add("import 'package:${modName}.dart' as ${modAlias};");
|
||||||
});
|
});
|
||||||
moduleSourceParts.add(moduleSource);
|
moduleSourceParts.add(moduleSource);
|
||||||
|
moduleSourceParts.add("""
|
||||||
return """
|
|
||||||
import "dart:isolate";
|
|
||||||
import "${toDartDataUri(moduleSourceParts.join('\n'))}" as mut;
|
|
||||||
|
|
||||||
main(List args, SendPort replyPort) {
|
main(List args, SendPort replyPort) {
|
||||||
replyPort.send(mut.run(args));
|
replyPort.send(run(args));
|
||||||
}
|
}
|
||||||
""";
|
""");
|
||||||
|
return moduleSourceParts.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
var timeStamp = new DateTime.now().millisecondsSinceEpoch;
|
var timeStamp = new DateTime.now().millisecondsSinceEpoch;
|
||||||
|
|
||||||
dynamic callModule(dynamic data) { return data.map( (a) => a+1); }
|
dynamic callModule(dynamic data) { return data.map( (a) => a+1); }
|
||||||
|
|
||||||
evalModule(String moduleSource, List<List<String>> moduleImports, List args) {
|
evalModule(String moduleSource, List<List<String>> imports, List args) {
|
||||||
String source = createIsolateSource(moduleSource, moduleImports);
|
String source = createIsolateSource(moduleSource, imports);
|
||||||
Completer completer = new Completer();
|
Completer completer = new Completer();
|
||||||
RawReceivePort receivePort;
|
RawReceivePort receivePort;
|
||||||
receivePort = new RawReceivePort( (message) {
|
receivePort = new RawReceivePort( (message) {
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||||
import {isPresent, global} from 'angular2/src/core/facade/lang';
|
import {isPresent, global, StringWrapper} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
var evalCounter = 0;
|
var evalCounter = 0;
|
||||||
|
|
||||||
function nextModuleName() {
|
function nextModuleId() {
|
||||||
return `evalScript${evalCounter++}`;
|
return `evalScript${evalCounter++}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function evalModule(moduleSource: string, moduleImports: string[][], args: any[]):
|
export function evalModule(moduleSource: string, imports: string[][], args: any[]): Promise<any> {
|
||||||
Promise<any> {
|
var moduleId = nextModuleId();
|
||||||
var moduleName = nextModuleName();
|
|
||||||
var moduleSourceWithImports = [];
|
var moduleSourceWithImports = [];
|
||||||
var importModuleNames = [];
|
var importModuleNames = [];
|
||||||
moduleImports.forEach(sourceImport => {
|
imports.forEach(sourceImport => {
|
||||||
var modName = sourceImport[0];
|
var modName = sourceImport[0];
|
||||||
var modAlias = sourceImport[1];
|
var modAlias = sourceImport[1];
|
||||||
importModuleNames.push(modName);
|
importModuleNames.push(modName);
|
||||||
|
@ -28,8 +27,8 @@ export function evalModule(moduleSource: string, moduleImports: string[][], args
|
||||||
var moduleBody = new Function('require', 'exports', 'module', moduleSourceWithImports.join('\n'));
|
var moduleBody = new Function('require', 'exports', 'module', moduleSourceWithImports.join('\n'));
|
||||||
var System = global['System'];
|
var System = global['System'];
|
||||||
if (isPresent(System) && isPresent(System.registerDynamic)) {
|
if (isPresent(System) && isPresent(System.registerDynamic)) {
|
||||||
System.registerDynamic(moduleName, importModuleNames, false, moduleBody);
|
System.registerDynamic(moduleId, importModuleNames, false, moduleBody);
|
||||||
return <Promise<any>>System.import(moduleName).then((module) => module.run(args));
|
return <Promise<any>>System.import(moduleId).then((module) => module.run(args));
|
||||||
} else {
|
} else {
|
||||||
var exports = {};
|
var exports = {};
|
||||||
moduleBody(require, exports, {});
|
moduleBody(require, exports, {});
|
||||||
|
|
|
@ -19,14 +19,14 @@ import {evalModule} from './eval_module';
|
||||||
// when evaling the test module!
|
// when evaling the test module!
|
||||||
export var TEST_VALUE = 23;
|
export var TEST_VALUE = 23;
|
||||||
|
|
||||||
|
const THIS_MODULE = 'angular2/test/compiler/eval_module_spec';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('evalModule', () => {
|
describe('evalModule', () => {
|
||||||
it('should call the "run" function and allow to use imports',
|
it('should call the "run" function and allow to use imports',
|
||||||
inject([AsyncTestCompleter], (async) => {
|
inject([AsyncTestCompleter], (async) => {
|
||||||
var moduleSource = IS_DART ? testDartModule : testJsModule;
|
var moduleSource = IS_DART ? testDartModule : testJsModule;
|
||||||
var imports = [['angular2/test/compiler/eval_module_spec', 'testMod']];
|
evalModule(moduleSource, [[THIS_MODULE, 'tst']], [1])
|
||||||
|
|
||||||
evalModule(moduleSource, imports, [1])
|
|
||||||
.then((value) => {
|
.then((value) => {
|
||||||
expect(value).toEqual([1, 23]);
|
expect(value).toEqual([1, 23]);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -37,14 +37,14 @@ export function main() {
|
||||||
|
|
||||||
var testDartModule = `
|
var testDartModule = `
|
||||||
run(data) {
|
run(data) {
|
||||||
data.add(testMod.TEST_VALUE);
|
data.add(tst.TEST_VALUE);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
var testJsModule = `
|
var testJsModule = `
|
||||||
exports.run = function(data) {
|
exports.run = function(data) {
|
||||||
data.push(testMod.TEST_VALUE);
|
data.push(tst.TEST_VALUE);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
|
@ -0,0 +1,131 @@
|
||||||
|
import {
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
xdescribe,
|
||||||
|
it,
|
||||||
|
iit,
|
||||||
|
xit,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
AsyncTestCompleter,
|
||||||
|
inject,
|
||||||
|
beforeEachBindings
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {stringify} from 'angular2/src/core/facade/lang';
|
||||||
|
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
View,
|
||||||
|
Directive,
|
||||||
|
ViewEncapsulation,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
OnChanges,
|
||||||
|
OnInit,
|
||||||
|
DoCheck,
|
||||||
|
OnDestroy,
|
||||||
|
AfterContentInit,
|
||||||
|
AfterContentChecked,
|
||||||
|
AfterViewInit,
|
||||||
|
AfterViewChecked
|
||||||
|
} from 'angular2/core';
|
||||||
|
|
||||||
|
import {TEST_BINDINGS} from './test_bindings';
|
||||||
|
import {IS_DART} from '../platform';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('RuntimeMetadataResolver', () => {
|
||||||
|
beforeEachBindings(() => TEST_BINDINGS);
|
||||||
|
|
||||||
|
describe('getMetadata', () => {
|
||||||
|
it('should read metadata',
|
||||||
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
|
var meta = resolver.getMetadata(ComponentWithEverything);
|
||||||
|
expect(meta.selector).toEqual('someSelector');
|
||||||
|
expect(meta.isComponent).toBe(true);
|
||||||
|
expect(meta.dynamicLoadable).toBe(true);
|
||||||
|
expect(meta.type.runtime).toBe(ComponentWithEverything);
|
||||||
|
expect(meta.type.name).toEqual(stringify(ComponentWithEverything));
|
||||||
|
expect(meta.type.moduleId).toEqual('someModuleId');
|
||||||
|
expect(meta.changeDetection.callAfterContentChecked).toBe(true);
|
||||||
|
expect(meta.changeDetection.callAfterContentInit).toBe(true);
|
||||||
|
expect(meta.changeDetection.callAfterViewChecked).toBe(true);
|
||||||
|
expect(meta.changeDetection.callAfterViewInit).toBe(true);
|
||||||
|
expect(meta.changeDetection.callDoCheck).toBe(true);
|
||||||
|
expect(meta.changeDetection.callOnChanges).toBe(true);
|
||||||
|
expect(meta.changeDetection.callOnInit).toBe(true);
|
||||||
|
expect(meta.changeDetection.changeDetection).toBe(ChangeDetectionStrategy.CheckAlways);
|
||||||
|
expect(meta.changeDetection.properties).toEqual(['someProp']);
|
||||||
|
expect(meta.changeDetection.events).toEqual(['someEvent']);
|
||||||
|
expect(meta.changeDetection.hostListeners)
|
||||||
|
.toEqual({'someHostListener': 'someHostListenerExpr'});
|
||||||
|
expect(meta.changeDetection.hostProperties)
|
||||||
|
.toEqual({'someHostProp': 'someHostPropExpr'});
|
||||||
|
expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated);
|
||||||
|
expect(meta.template.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'});
|
||||||
|
expect(meta.template.styles).toEqual(['someStyle']);
|
||||||
|
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
|
||||||
|
expect(meta.template.template).toEqual('someTemplate');
|
||||||
|
expect(meta.template.templateUrl).toEqual('someTemplateUrl');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should use the moduleId from the reflector if none is given',
|
||||||
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
|
var expectedValue = IS_DART ? 'angular2/test/compiler/runtime_metadata_spec' : null;
|
||||||
|
expect(resolver.getMetadata(DirectiveWithoutModuleId).type.moduleId)
|
||||||
|
.toEqual(expectedValue);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getViewDirectivesMetadata', () => {
|
||||||
|
|
||||||
|
it('should return the directive metadatas',
|
||||||
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
|
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
|
||||||
|
.toEqual([resolver.getMetadata(DirectiveWithoutModuleId)]);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Directive({selector: 'someSelector'})
|
||||||
|
class DirectiveWithoutModuleId {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'someSelector',
|
||||||
|
properties: ['someProp'],
|
||||||
|
events: ['someEvent'],
|
||||||
|
host: {
|
||||||
|
'[someHostProp]': 'someHostPropExpr',
|
||||||
|
'(someHostListener)': 'someHostListenerExpr',
|
||||||
|
'someHostAttr': 'someHostAttrValue'
|
||||||
|
},
|
||||||
|
dynamicLoadable: true,
|
||||||
|
moduleId: 'someModuleId',
|
||||||
|
changeDetection: ChangeDetectionStrategy.CheckAlways
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
template: 'someTemplate',
|
||||||
|
templateUrl: 'someTemplateUrl',
|
||||||
|
encapsulation: ViewEncapsulation.Emulated,
|
||||||
|
styles: ['someStyle'],
|
||||||
|
styleUrls: ['someStyleUrl'],
|
||||||
|
directives: [DirectiveWithoutModuleId]
|
||||||
|
})
|
||||||
|
class ComponentWithEverything implements OnChanges,
|
||||||
|
OnInit, DoCheck, OnDestroy, AfterContentInit, AfterContentChecked, AfterViewInit,
|
||||||
|
AfterViewChecked {
|
||||||
|
onChanges(changes: StringMap<string, any>): void {}
|
||||||
|
onInit(): void {}
|
||||||
|
doCheck(): void {}
|
||||||
|
onDestroy(): void {}
|
||||||
|
afterContentInit(): void {}
|
||||||
|
afterContentChecked(): void {}
|
||||||
|
afterViewInit(): void {}
|
||||||
|
afterViewChecked(): void {}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
||||||
|
import {StringMap} from 'angular2/src/core/facade/collection';
|
||||||
|
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
|
export class MockSchemaRegistry implements ElementSchemaRegistry {
|
||||||
|
constructor(public existingProperties: StringMap<string, boolean>,
|
||||||
|
public attrPropMapping: StringMap<string, string>) {}
|
||||||
|
hasProperty(tagName: string, property: string): boolean {
|
||||||
|
var result = this.existingProperties[property];
|
||||||
|
return isPresent(result) ? result : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMappedPropName(attrName: string): string {
|
||||||
|
var result = this.attrPropMapping[attrName];
|
||||||
|
return isPresent(result) ? result : attrName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
el,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
it,
|
||||||
|
xit,
|
||||||
|
TestComponentBuilder
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('SourceModule', () => {
|
||||||
|
describe('getSourceWithImports', () => {
|
||||||
|
it('should generate named imports for modules', () => {
|
||||||
|
var sourceWithImports =
|
||||||
|
new SourceModule('some/moda', `${moduleRef('some/modb')}A`).getSourceWithImports();
|
||||||
|
expect(sourceWithImports.source).toEqual('import0.A');
|
||||||
|
expect(sourceWithImports.imports).toEqual([['some/modb', 'import0']]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dedupe imports', () => {
|
||||||
|
var sourceWithImports =
|
||||||
|
new SourceModule('some/moda', `${moduleRef('some/modb')}A + ${moduleRef('some/modb')}B`)
|
||||||
|
.getSourceWithImports();
|
||||||
|
expect(sourceWithImports.source).toEqual('import0.A + import0.B');
|
||||||
|
expect(sourceWithImports.imports).toEqual([['some/modb', 'import0']]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not use an import for the moduleId of the SourceModule', () => {
|
||||||
|
var sourceWithImports =
|
||||||
|
new SourceModule('some/moda', `${moduleRef('some/moda')}A`).getSourceWithImports();
|
||||||
|
expect(sourceWithImports.source).toEqual('A');
|
||||||
|
expect(sourceWithImports.imports).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -9,19 +9,27 @@ import {
|
||||||
beforeEach,
|
beforeEach,
|
||||||
afterEach,
|
afterEach,
|
||||||
AsyncTestCompleter,
|
AsyncTestCompleter,
|
||||||
inject
|
inject,
|
||||||
|
beforeEachBindings
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {IS_DART} from '../platform';
|
import {bind} from 'angular2/src/core/di';
|
||||||
import {SpyXHR} from '../core/spies';
|
import {SpyXHR} from '../core/spies';
|
||||||
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
||||||
|
|
||||||
import {CONST_EXPR, isPresent, StringWrapper} from 'angular2/src/core/facade/lang';
|
import {CONST_EXPR, isPresent, StringWrapper} from 'angular2/src/core/facade/lang';
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||||
import {evalModule} from './eval_module';
|
import {evalModule} from './eval_module';
|
||||||
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
||||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
import {
|
||||||
import {DirectiveMetadata, TemplateMetadata, TypeMetadata} from 'angular2/src/compiler/api';
|
NormalizedDirectiveMetadata,
|
||||||
|
NormalizedTemplateMetadata,
|
||||||
|
TypeMetadata
|
||||||
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
|
import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
|
import {TEST_BINDINGS} from './test_bindings';
|
||||||
|
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
|
||||||
|
|
||||||
// Attention: These module names have to correspond to real modules!
|
// Attention: These module names have to correspond to real modules!
|
||||||
const MODULE_NAME = 'angular2/test/compiler/style_compiler_spec';
|
const MODULE_NAME = 'angular2/test/compiler/style_compiler_spec';
|
||||||
|
@ -33,19 +41,21 @@ const IMPORT_ABS_MODULE_NAME_WITH_IMPORT =
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('StyleCompiler', () => {
|
describe('StyleCompiler', () => {
|
||||||
var compiler: StyleCompiler;
|
var xhr: SpyXHR;
|
||||||
var xhr;
|
beforeEachBindings(() => {
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
xhr = <any>new SpyXHR();
|
xhr = <any>new SpyXHR();
|
||||||
compiler = new StyleCompiler(xhr, new UrlResolver());
|
return [TEST_BINDINGS, bind(XHR).toValue(xhr)];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var compiler: StyleCompiler;
|
||||||
|
|
||||||
|
beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
|
||||||
|
|
||||||
function comp(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
function comp(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
||||||
DirectiveMetadata {
|
NormalizedDirectiveMetadata {
|
||||||
return new DirectiveMetadata({
|
return new NormalizedDirectiveMetadata({
|
||||||
type: new TypeMetadata({id: 23, typeUrl: 'someUrl'}),
|
type: new TypeMetadata({id: 23, moduleId: 'someUrl'}),
|
||||||
template: new TemplateMetadata(
|
template: new NormalizedTemplateMetadata(
|
||||||
{styles: styles, styleAbsUrls: styleAbsUrls, encapsulation: encapsulation})
|
{styles: styles, styleAbsUrls: styleAbsUrls, encapsulation: encapsulation})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -117,9 +127,10 @@ export function main() {
|
||||||
function runTest(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation,
|
function runTest(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation,
|
||||||
expectedStyles: string[]) {
|
expectedStyles: string[]) {
|
||||||
return inject([AsyncTestCompleter], (async) => {
|
return inject([AsyncTestCompleter], (async) => {
|
||||||
var sourceModule =
|
var sourceExpression =
|
||||||
compiler.compileComponentCodeGen(comp(styles, styleAbsUrls, encapsulation));
|
compiler.compileComponentCodeGen(comp(styles, styleAbsUrls, encapsulation));
|
||||||
evalModule(testableModule(sourceModule.source), sourceModule.imports, null)
|
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
|
||||||
|
evalModule(sourceWithImports.source, sourceWithImports.imports, null)
|
||||||
.then((value) => {
|
.then((value) => {
|
||||||
compareStyles(value, expectedStyles);
|
compareStyles(value, expectedStyles);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -163,9 +174,12 @@ export function main() {
|
||||||
function runTest(style: string, expectedStyles: string[], expectedShimStyles: string[]) {
|
function runTest(style: string, expectedStyles: string[], expectedShimStyles: string[]) {
|
||||||
return inject([AsyncTestCompleter], (async) => {
|
return inject([AsyncTestCompleter], (async) => {
|
||||||
var sourceModules = compiler.compileStylesheetCodeGen(MODULE_NAME, style);
|
var sourceModules = compiler.compileStylesheetCodeGen(MODULE_NAME, style);
|
||||||
PromiseWrapper.all(sourceModules.map(sourceModule =>
|
PromiseWrapper.all(sourceModules.map(sourceModule => {
|
||||||
evalModule(testableModule(sourceModule.source),
|
var sourceWithImports =
|
||||||
sourceModule.imports, null)))
|
testableModule(sourceModule).getSourceWithImports();
|
||||||
|
return evalModule(sourceWithImports.source, sourceWithImports.imports,
|
||||||
|
null);
|
||||||
|
}))
|
||||||
.then((values) => {
|
.then((values) => {
|
||||||
compareStyles(values[0], expectedStyles);
|
compareStyles(values[0], expectedStyles);
|
||||||
compareStyles(values[1], expectedShimStyles);
|
compareStyles(values[1], expectedShimStyles);
|
||||||
|
@ -188,16 +202,17 @@ export function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testableModule(sourceModule: string) {
|
|
||||||
if (IS_DART) {
|
function testableExpression(source: SourceExpression): SourceModule {
|
||||||
return `${sourceModule}
|
var testableSource = `${source.declarations.join('\n')}
|
||||||
run(_) { return STYLES; }
|
${codeGenExportVariable('run')}${codeGenValueFn(['_'], source.expression)};`;
|
||||||
`;
|
return new SourceModule(null, testableSource);
|
||||||
} else {
|
}
|
||||||
return `${sourceModule}
|
|
||||||
exports.run = function(_) { return STYLES; };
|
function testableModule(sourceModule: SourceModule): SourceModule {
|
||||||
`;
|
var testableSource = `${sourceModule.source}
|
||||||
}
|
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
|
||||||
|
return new SourceModule(sourceModule.moduleId, testableSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needed for Android browsers which add an extra space at the end of some lines
|
// Needed for Android browsers which add an extra space at the end of some lines
|
||||||
|
|
|
@ -0,0 +1,334 @@
|
||||||
|
import {
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
xdescribe,
|
||||||
|
it,
|
||||||
|
iit,
|
||||||
|
xit,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
AsyncTestCompleter,
|
||||||
|
inject,
|
||||||
|
beforeEachBindings
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||||
|
import {Type, isPresent, isBlank, stringify, isString} from 'angular2/src/core/facade/lang';
|
||||||
|
import {MapWrapper, SetWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
||||||
|
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||||
|
import {
|
||||||
|
TemplateCompiler,
|
||||||
|
NormalizedComponentWithViewDirectives
|
||||||
|
} from 'angular2/src/compiler/template_compiler';
|
||||||
|
import {
|
||||||
|
DirectiveMetadata,
|
||||||
|
NormalizedDirectiveMetadata,
|
||||||
|
INormalizedDirectiveMetadata
|
||||||
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
|
import {evalModule} from './eval_module';
|
||||||
|
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
|
||||||
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
|
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
|
||||||
|
|
||||||
|
import {Locals} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CommandVisitor,
|
||||||
|
TextCmd,
|
||||||
|
NgContentCmd,
|
||||||
|
BeginElementCmd,
|
||||||
|
BeginComponentCmd,
|
||||||
|
EmbeddedTemplateCmd,
|
||||||
|
TemplateCmd,
|
||||||
|
visitAllCommands,
|
||||||
|
CompiledTemplate
|
||||||
|
} from 'angular2/src/core/compiler/template_commands';
|
||||||
|
|
||||||
|
import {Component, View, Directive} from 'angular2/core';
|
||||||
|
|
||||||
|
import {TEST_BINDINGS} from './test_bindings';
|
||||||
|
import {TestContext, TestDispatcher, TestPipes} from './change_detector_mocks';
|
||||||
|
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
|
||||||
|
|
||||||
|
// Attention: This path has to point to this test file!
|
||||||
|
const THIS_MODULE = 'angular2/test/compiler/template_compiler_spec';
|
||||||
|
var THIS_MODULE_REF = moduleRef(THIS_MODULE);
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('TemplateCompiler', () => {
|
||||||
|
var compiler: TemplateCompiler;
|
||||||
|
var runtimeMetadataResolver: RuntimeMetadataResolver;
|
||||||
|
|
||||||
|
beforeEachBindings(() => TEST_BINDINGS);
|
||||||
|
beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver],
|
||||||
|
(_compiler, _runtimeMetadataResolver) => {
|
||||||
|
compiler = _compiler;
|
||||||
|
runtimeMetadataResolver = _runtimeMetadataResolver;
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('compile templates', () => {
|
||||||
|
|
||||||
|
function runTests(compile) {
|
||||||
|
it('should compile host components', inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile([CompWithBindingsAndStyles])
|
||||||
|
.then((humanizedTemplate) => {
|
||||||
|
expect(humanizedTemplate['styles']).toEqual([]);
|
||||||
|
expect(humanizedTemplate['commands'][0]).toEqual('<comp-a>');
|
||||||
|
expect(humanizedTemplate['cd']).toEqual(['elementProperty(title)=someDirValue']);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should compile nested components', inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile([CompWithBindingsAndStyles])
|
||||||
|
.then((humanizedTemplate) => {
|
||||||
|
var nestedTemplate = humanizedTemplate['commands'][1];
|
||||||
|
expect(nestedTemplate['styles']).toEqual(['div {color: red}']);
|
||||||
|
expect(nestedTemplate['commands'][0]).toEqual('<a>');
|
||||||
|
expect(nestedTemplate['cd']).toEqual(['elementProperty(href)=someCtxValue']);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should compile recursive components', inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile([TreeComp])
|
||||||
|
.then((humanizedTemplate) => {
|
||||||
|
expect(humanizedTemplate['commands'][0]).toEqual('<tree>');
|
||||||
|
expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual('<tree>');
|
||||||
|
expect(humanizedTemplate['commands'][1]['commands'][1]['commands'][0])
|
||||||
|
.toEqual('<tree>');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('compileHostComponentRuntime', () => {
|
||||||
|
function compile(components: Type[]): Promise<any[]> {
|
||||||
|
return compiler.compileHostComponentRuntime(components[0]).then(humanizeTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(compile);
|
||||||
|
|
||||||
|
it('should cache components', inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||||
|
// we expect only one request!
|
||||||
|
xhr.expect('angular2/test/compiler/compUrl.html', '');
|
||||||
|
PromiseWrapper.all([
|
||||||
|
compiler.compileHostComponentRuntime(CompWithTemplateUrl),
|
||||||
|
compiler.compileHostComponentRuntime(CompWithTemplateUrl)
|
||||||
|
])
|
||||||
|
.then((humanizedTemplates) => {
|
||||||
|
expect(humanizedTemplates[0]).toEqual(humanizedTemplates[1]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should only allow dynamic loadable components', () => {
|
||||||
|
expect(() => compiler.compileHostComponentRuntime(PlainDirective))
|
||||||
|
.toThrowError(
|
||||||
|
`Could not compile '${stringify(PlainDirective)}' because it is not dynamically loadable.`);
|
||||||
|
expect(() => compiler.compileHostComponentRuntime(CompWithoutHost))
|
||||||
|
.toThrowError(
|
||||||
|
`Could not compile '${stringify(CompWithoutHost)}' because it is not dynamically loadable.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('compileTemplatesCodeGen', () => {
|
||||||
|
function normalizeComponent(component: Type):
|
||||||
|
Promise<NormalizedComponentWithViewDirectives> {
|
||||||
|
var compAndViewDirMetas = [runtimeMetadataResolver.getMetadata(component)].concat(
|
||||||
|
runtimeMetadataResolver.getViewDirectivesMetadata(component));
|
||||||
|
return PromiseWrapper.all(compAndViewDirMetas.map(meta =>
|
||||||
|
compiler.normalizeDirective(meta)))
|
||||||
|
.then((normalizedCompAndViewDirMetas: NormalizedDirectiveMetadata[]) =>
|
||||||
|
new NormalizedComponentWithViewDirectives(
|
||||||
|
normalizedCompAndViewDirMetas[0],
|
||||||
|
normalizedCompAndViewDirMetas.slice(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function compile(components: Type[]): Promise<any[]> {
|
||||||
|
return PromiseWrapper.all(components.map(normalizeComponent))
|
||||||
|
.then((normalizedCompWithViewDirMetas: NormalizedComponentWithViewDirectives[]) => {
|
||||||
|
var sourceModule =
|
||||||
|
compiler.compileTemplatesCodeGen(THIS_MODULE, normalizedCompWithViewDirMetas);
|
||||||
|
var sourceWithImports =
|
||||||
|
testableTemplateModule(sourceModule,
|
||||||
|
normalizedCompWithViewDirMetas[0].component)
|
||||||
|
.getSourceWithImports();
|
||||||
|
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(compile);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('serializeTemplateMetadata and deserializeTemplateMetadata', () => {
|
||||||
|
it('should serialize and deserialize', inject([AsyncTestCompleter], (async) => {
|
||||||
|
compiler.normalizeDirective(
|
||||||
|
runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles))
|
||||||
|
.then((meta: NormalizedDirectiveMetadata) => {
|
||||||
|
var json = compiler.serializeTemplateMetadata(meta);
|
||||||
|
expect(isString(json)).toBe(true);
|
||||||
|
// Note: serializing will clear our the runtime type!
|
||||||
|
var clonedMeta =
|
||||||
|
<NormalizedDirectiveMetadata>compiler.deserializeTemplateMetadata(json);
|
||||||
|
expect(meta.changeDetection).toEqual(clonedMeta.changeDetection);
|
||||||
|
expect(meta.template).toEqual(clonedMeta.template);
|
||||||
|
expect(meta.selector).toEqual(clonedMeta.selector);
|
||||||
|
expect(meta.type.name).toEqual(clonedMeta.type.name);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalizeDirective', () => {
|
||||||
|
it('should normalize the template',
|
||||||
|
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||||
|
xhr.expect('angular2/test/compiler/compUrl.html', 'loadedTemplate');
|
||||||
|
compiler.normalizeDirective(runtimeMetadataResolver.getMetadata(CompWithTemplateUrl))
|
||||||
|
.then((meta: NormalizedDirectiveMetadata) => {
|
||||||
|
expect(meta.template.template).toEqual('loadedTemplate');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should copy all the other fields', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var meta = runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles);
|
||||||
|
compiler.normalizeDirective(meta).then((normMeta: NormalizedDirectiveMetadata) => {
|
||||||
|
expect(normMeta.selector).toEqual(meta.selector);
|
||||||
|
expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable);
|
||||||
|
expect(normMeta.isComponent).toEqual(meta.isComponent);
|
||||||
|
expect(normMeta.type).toEqual(meta.type);
|
||||||
|
expect(normMeta.changeDetection).toEqual(meta.changeDetection);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('compileStylesheetCodeGen', () => {
|
||||||
|
it('should compile stylesheets into code', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var cssText = 'div {color: red}';
|
||||||
|
var sourceModule = compiler.compileStylesheetCodeGen('someModuleId', cssText)[0];
|
||||||
|
var sourceWithImports = testableStylesModule(sourceModule).getSourceWithImports();
|
||||||
|
evalModule(sourceWithImports.source, sourceWithImports.imports, null)
|
||||||
|
.then(loadedCssText => {
|
||||||
|
expect(loadedCssText).toEqual([cssText]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'comp-a',
|
||||||
|
dynamicLoadable: true,
|
||||||
|
host: {'[title]': 'someProp'},
|
||||||
|
moduleId: THIS_MODULE
|
||||||
|
})
|
||||||
|
@View({template: '<a [href]="someProp"></a>', styles: ['div {color: red}']})
|
||||||
|
class CompWithBindingsAndStyles {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'tree', dynamicLoadable: true, moduleId: THIS_MODULE})
|
||||||
|
@View({template: '<tree></tree>', directives: [TreeComp]})
|
||||||
|
class TreeComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'comp-url', dynamicLoadable: true, moduleId: THIS_MODULE})
|
||||||
|
@View({templateUrl: 'compUrl.html'})
|
||||||
|
class CompWithTemplateUrl {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: 'plain', moduleId: THIS_MODULE})
|
||||||
|
class PlainDirective {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'comp', moduleId: THIS_MODULE})
|
||||||
|
@View({template: ''})
|
||||||
|
class CompWithoutHost {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testableTemplateModule(sourceModule: SourceModule, comp: INormalizedDirectiveMetadata):
|
||||||
|
SourceModule {
|
||||||
|
var normComp = <NormalizedDirectiveMetadata>comp;
|
||||||
|
var resultExpression = `${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template)`;
|
||||||
|
var testableSource = `${sourceModule.source}
|
||||||
|
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
|
||||||
|
return new SourceModule(sourceModule.moduleId, testableSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testableStylesModule(sourceModule: SourceModule): SourceModule {
|
||||||
|
var testableSource = `${sourceModule.source}
|
||||||
|
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
|
||||||
|
return new SourceModule(sourceModule.moduleId, testableSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attention: read by eval!
|
||||||
|
export function humanizeTemplate(template: CompiledTemplate,
|
||||||
|
humanizedTemplates: Map<number, StringMap<string, any>> = null):
|
||||||
|
StringMap<string, any> {
|
||||||
|
if (isBlank(humanizedTemplates)) {
|
||||||
|
humanizedTemplates = new Map();
|
||||||
|
}
|
||||||
|
var result = humanizedTemplates.get(template.id);
|
||||||
|
if (isPresent(result)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
var commands = [];
|
||||||
|
result = {
|
||||||
|
'styles': template.styles,
|
||||||
|
'commands': commands,
|
||||||
|
'cd': testChangeDetector(template.changeDetectorFactories[0])
|
||||||
|
};
|
||||||
|
humanizedTemplates.set(template.id, result);
|
||||||
|
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function testChangeDetector(changeDetectorFactory: Function): string[] {
|
||||||
|
var ctx = new TestContext();
|
||||||
|
ctx.someProp = 'someCtxValue';
|
||||||
|
var dir1 = new TestContext();
|
||||||
|
dir1.someProp = 'someDirValue';
|
||||||
|
|
||||||
|
var dispatcher = new TestDispatcher([dir1], []);
|
||||||
|
var cd = changeDetectorFactory(dispatcher);
|
||||||
|
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
||||||
|
cd.hydrate(ctx, locals, dispatcher, new TestPipes());
|
||||||
|
cd.detectChanges();
|
||||||
|
return dispatcher.log;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CommandHumanizer implements CommandVisitor {
|
||||||
|
constructor(private result: any[],
|
||||||
|
private humanizedTemplates: Map<number, StringMap<string, any>>) {}
|
||||||
|
visitText(cmd: TextCmd, context: any): any { return null; }
|
||||||
|
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
|
||||||
|
visitBeginElement(cmd: BeginElementCmd, context: any): any {
|
||||||
|
this.result.push(`<${cmd.name}>`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitEndElement(context: any): any {
|
||||||
|
this.result.push('</>');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
|
||||||
|
this.result.push(`<${cmd.name}>`);
|
||||||
|
this.result.push(humanizeTemplate(cmd.template, this.humanizedTemplates));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitEndComponent(context: any): any { return this.visitEndElement(context); }
|
||||||
|
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any { return null; }
|
||||||
|
}
|
|
@ -1,176 +0,0 @@
|
||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
TestComponentBuilder
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
|
||||||
import {TypeMetadata, TemplateMetadata} from 'angular2/src/compiler/api';
|
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
import {TemplateLoader} from 'angular2/src/compiler/template_loader';
|
|
||||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
|
||||||
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('TemplateLoader', () => {
|
|
||||||
var loader: TemplateLoader;
|
|
||||||
var dirType: TypeMetadata;
|
|
||||||
var xhr: MockXHR;
|
|
||||||
var htmlParser: HtmlParser;
|
|
||||||
|
|
||||||
beforeEach(inject([XHR], (mockXhr) => {
|
|
||||||
xhr = mockXhr;
|
|
||||||
htmlParser = new HtmlParser();
|
|
||||||
loader = new TemplateLoader(xhr, new UrlResolver(), htmlParser);
|
|
||||||
dirType = new TypeMetadata({typeUrl: 'http://sometypeurl', typeName: 'SomeComp'});
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('loadTemplate', () => {
|
|
||||||
describe('inline template', () => {
|
|
||||||
it('should store the template', inject([AsyncTestCompleter], (async) => {
|
|
||||||
loader.loadTemplate(dirType, null, 'a', null, [], ['test.css'])
|
|
||||||
.then((template: TemplateMetadata) => {
|
|
||||||
expect(template.template).toEqual('a');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should resolve styles against the typeUrl', inject([AsyncTestCompleter], (async) => {
|
|
||||||
loader.loadTemplate(dirType, null, 'a', null, [], ['test.css'])
|
|
||||||
.then((template: TemplateMetadata) => {
|
|
||||||
expect(template.styleAbsUrls).toEqual(['http://sometypeurl/test.css']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('templateUrl', () => {
|
|
||||||
|
|
||||||
it('should load a template from a url that is resolved against typeUrl',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://sometypeurl/sometplurl', 'a');
|
|
||||||
loader.loadTemplate(dirType, null, null, 'sometplurl', [], ['test.css'])
|
|
||||||
.then((template: TemplateMetadata) => {
|
|
||||||
expect(template.template).toEqual('a');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should resolve styles against the templateUrl',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
xhr.expect('http://sometypeurl/tpl/sometplurl', 'a');
|
|
||||||
loader.loadTemplate(dirType, null, null, 'tpl/sometplurl', [], ['test.css'])
|
|
||||||
.then((template: TemplateMetadata) => {
|
|
||||||
expect(template.styleAbsUrls).toEqual(['http://sometypeurl/tpl/test.css']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
xhr.flush();
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('loadTemplateFromString', () => {
|
|
||||||
it('should store the viewEncapsulationin the result', () => {
|
|
||||||
var viewEncapsulation = ViewEncapsulation.Native;
|
|
||||||
var template = loader.createTemplateFromString(dirType, viewEncapsulation, '',
|
|
||||||
'http://someurl/', [], []);
|
|
||||||
expect(template.encapsulation).toBe(viewEncapsulation);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should keep the template as html', () => {
|
|
||||||
var template =
|
|
||||||
loader.createTemplateFromString(dirType, null, 'a', 'http://someurl/', [], []);
|
|
||||||
expect(template.template).toEqual('a')
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should collect and keep ngContent', () => {
|
|
||||||
var template = loader.createTemplateFromString(
|
|
||||||
dirType, null, '<ng-content select="a"></ng-content>', 'http://someurl/', [], []);
|
|
||||||
expect(template.ngContentSelectors).toEqual(['a']);
|
|
||||||
expect(template.template).toEqual('<ng-content select="a"></ng-content>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should normalize ngContent wildcard selector', () => {
|
|
||||||
var template = loader.createTemplateFromString(
|
|
||||||
dirType, null,
|
|
||||||
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
|
|
||||||
'http://someurl/', [], []);
|
|
||||||
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should collect and remove top level styles in the template', () => {
|
|
||||||
var template = loader.createTemplateFromString(dirType, null, '<style>a</style>',
|
|
||||||
'http://someurl/', [], []);
|
|
||||||
expect(template.styles).toEqual(['a']);
|
|
||||||
expect(template.template).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should collect and remove styles inside in elements', () => {
|
|
||||||
var template = loader.createTemplateFromString(dirType, null, '<div><style>a</style></div>',
|
|
||||||
'http://someurl/', [], []);
|
|
||||||
expect(template.styles).toEqual(['a']);
|
|
||||||
expect(template.template).toEqual('<div></div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should collect and remove styleUrls in the template', () => {
|
|
||||||
var template = loader.createTemplateFromString(
|
|
||||||
dirType, null, '<link rel="stylesheet" href="aUrl">', 'http://someurl/', [], []);
|
|
||||||
expect(template.styleAbsUrls).toEqual(['http://someurl/aUrl']);
|
|
||||||
expect(template.template).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should collect and remove styleUrls in elements', () => {
|
|
||||||
var template = loader.createTemplateFromString(
|
|
||||||
dirType, null, '<div><link rel="stylesheet" href="aUrl"></div>', 'http://someurl/', [],
|
|
||||||
[]);
|
|
||||||
expect(template.styleAbsUrls).toEqual(['http://someurl/aUrl']);
|
|
||||||
expect(template.template).toEqual('<div></div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should keep link elements with non stylesheet rel attribute', () => {
|
|
||||||
var template = loader.createTemplateFromString(
|
|
||||||
dirType, null, '<link href="b" rel="a"></link>', 'http://someurl/', [], []);
|
|
||||||
expect(template.styleAbsUrls).toEqual([]);
|
|
||||||
expect(template.template).toEqual('<link href="b" rel="a"></link>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should extract @import style urls into styleAbsUrl', () => {
|
|
||||||
var template = loader.createTemplateFromString(dirType, null, '', 'http://someurl',
|
|
||||||
['@import "test.css";'], []);
|
|
||||||
expect(template.styles).toEqual(['']);
|
|
||||||
expect(template.styleAbsUrls).toEqual(['http://someurl/test.css']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve relative urls in inline styles', () => {
|
|
||||||
var template =
|
|
||||||
loader.createTemplateFromString(dirType, null, '', 'http://someurl',
|
|
||||||
['.foo{background-image: url(\'double.jpg\');'], []);
|
|
||||||
expect(template.styles)
|
|
||||||
.toEqual(['.foo{background-image: url(\'http://someurl/double.jpg\');']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve relative style urls in styleUrls', () => {
|
|
||||||
var template =
|
|
||||||
loader.createTemplateFromString(dirType, null, '', 'http://someurl', [], ['test.css']);
|
|
||||||
expect(template.styles).toEqual([]);
|
|
||||||
expect(template.styleAbsUrls).toEqual(['http://someurl/test.css']);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
el,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
it,
|
||||||
|
xit,
|
||||||
|
TestComponentBuilder,
|
||||||
|
beforeEachBindings
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TypeMetadata,
|
||||||
|
NormalizedTemplateMetadata,
|
||||||
|
TemplateMetadata
|
||||||
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
|
|
||||||
|
import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
|
||||||
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
|
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
|
||||||
|
import {TEST_BINDINGS} from './test_bindings';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('TemplateNormalizer', () => {
|
||||||
|
var dirType: TypeMetadata;
|
||||||
|
|
||||||
|
beforeEachBindings(() => TEST_BINDINGS);
|
||||||
|
|
||||||
|
beforeEach(
|
||||||
|
() => { dirType = new TypeMetadata({moduleId: 'some/module/id', name: 'SomeComp'}); });
|
||||||
|
|
||||||
|
describe('loadTemplate', () => {
|
||||||
|
describe('inline template', () => {
|
||||||
|
it('should store the template',
|
||||||
|
inject([AsyncTestCompleter, TemplateNormalizer],
|
||||||
|
(async, normalizer: TemplateNormalizer) => {
|
||||||
|
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: 'a',
|
||||||
|
templateUrl: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
.then((template: NormalizedTemplateMetadata) => {
|
||||||
|
expect(template.template).toEqual('a');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve styles on the annotation against the moduleId',
|
||||||
|
inject([AsyncTestCompleter, TemplateNormalizer],
|
||||||
|
(async, normalizer: TemplateNormalizer) => {
|
||||||
|
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: '',
|
||||||
|
templateUrl: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
.then((template: NormalizedTemplateMetadata) => {
|
||||||
|
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve styles in the template against the moduleId',
|
||||||
|
inject([AsyncTestCompleter, TemplateNormalizer],
|
||||||
|
(async, normalizer: TemplateNormalizer) => {
|
||||||
|
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: '<style>@import test.css</style>',
|
||||||
|
templateUrl: null,
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
}))
|
||||||
|
.then((template: NormalizedTemplateMetadata) => {
|
||||||
|
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('templateUrl', () => {
|
||||||
|
|
||||||
|
it('should load a template from a url that is resolved against moduleId',
|
||||||
|
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
||||||
|
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
||||||
|
xhr.expect('some/module/sometplurl', 'a');
|
||||||
|
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: null,
|
||||||
|
templateUrl: 'sometplurl',
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
.then((template: NormalizedTemplateMetadata) => {
|
||||||
|
expect(template.template).toEqual('a');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve styles on the annotation against the moduleId',
|
||||||
|
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
||||||
|
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
||||||
|
xhr.expect('some/module/tpl/sometplurl', '');
|
||||||
|
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: null,
|
||||||
|
templateUrl: 'tpl/sometplurl',
|
||||||
|
styles: [],
|
||||||
|
styleUrls: ['test.css']
|
||||||
|
}))
|
||||||
|
.then((template: NormalizedTemplateMetadata) => {
|
||||||
|
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve styles in the template against the templateUrl',
|
||||||
|
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
||||||
|
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
||||||
|
xhr.expect('some/module/tpl/sometplurl', '<style>@import test.css</style>');
|
||||||
|
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
template: null,
|
||||||
|
templateUrl: 'tpl/sometplurl',
|
||||||
|
styles: [],
|
||||||
|
styleUrls: []
|
||||||
|
}))
|
||||||
|
.then((template: NormalizedTemplateMetadata) => {
|
||||||
|
expect(template.styleAbsUrls).toEqual(['some/module/tpl/test.css']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalizeLoadedTemplate', () => {
|
||||||
|
it('should store the viewEncapsulationin the result',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
|
||||||
|
var viewEncapsulation = ViewEncapsulation.Native;
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType,
|
||||||
|
new TemplateMetadata({encapsulation: viewEncapsulation, styles: [], styleUrls: []}),
|
||||||
|
'', 'some/module/');
|
||||||
|
expect(template.encapsulation).toBe(viewEncapsulation);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should keep the template as html',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a',
|
||||||
|
'some/module/');
|
||||||
|
expect(template.template).toEqual('a')
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should collect and keep ngContent',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<ng-content select="a"></ng-content>', 'some/module/');
|
||||||
|
expect(template.ngContentSelectors).toEqual(['a']);
|
||||||
|
expect(template.template).toEqual('<ng-content select="a"></ng-content>');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should normalize ngContent wildcard selector',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
|
||||||
|
'some/module/');
|
||||||
|
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should collect and remove top level styles in the template',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<style>a</style>', 'some/module/');
|
||||||
|
expect(template.styles).toEqual(['a']);
|
||||||
|
expect(template.template).toEqual('');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should collect and remove styles inside in elements',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<div><style>a</style></div>', 'some/module/');
|
||||||
|
expect(template.styles).toEqual(['a']);
|
||||||
|
expect(template.template).toEqual('<div></div>');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should collect and remove styleUrls in the template',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<link rel="stylesheet" href="aUrl">', 'some/module/');
|
||||||
|
expect(template.styleAbsUrls).toEqual(['some/module/aUrl']);
|
||||||
|
expect(template.template).toEqual('');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should collect and remove styleUrls in elements',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<div><link rel="stylesheet" href="aUrl"></div>', 'some/module/');
|
||||||
|
expect(template.styleAbsUrls).toEqual(['some/module/aUrl']);
|
||||||
|
expect(template.template).toEqual('<div></div>');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should keep link elements with non stylesheet rel attribute',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<link href="b" rel="a"></link>', 'some/module/');
|
||||||
|
expect(template.styleAbsUrls).toEqual([]);
|
||||||
|
expect(template.template).toEqual('<link href="b" rel="a"></link>');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should extract @import style urls into styleAbsUrl',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata(
|
||||||
|
{encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}),
|
||||||
|
'', 'some/module/id');
|
||||||
|
expect(template.styles).toEqual(['']);
|
||||||
|
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve relative urls in inline styles',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new TemplateMetadata({
|
||||||
|
encapsulation: null,
|
||||||
|
styles: ['.foo{background-image: url(\'double.jpg\');'],
|
||||||
|
styleUrls: []
|
||||||
|
}),
|
||||||
|
'', 'some/module/id');
|
||||||
|
expect(template.styles)
|
||||||
|
.toEqual(['.foo{background-image: url(\'some/module/double.jpg\');']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resolve relative style urls in styleUrls',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType,
|
||||||
|
new TemplateMetadata({encapsulation: null, styles: [], styleUrls: ['test.css']}), '',
|
||||||
|
'some/module/id');
|
||||||
|
expect(template.styles).toEqual([]);
|
||||||
|
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,15 +1,26 @@
|
||||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
|
||||||
import {Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser';
|
|
||||||
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
|
||||||
import {
|
import {
|
||||||
DirectiveMetadata,
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
iit,
|
||||||
|
xit,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
inject,
|
||||||
|
beforeEachBindings
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
import {bind} from 'angular2/src/core/di';
|
||||||
|
|
||||||
|
import {TEST_BINDINGS} from './test_bindings';
|
||||||
|
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
|
import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser';
|
||||||
|
import {
|
||||||
|
NormalizedDirectiveMetadata,
|
||||||
TypeMetadata,
|
TypeMetadata,
|
||||||
ChangeDetectionMetadata,
|
ChangeDetectionMetadata,
|
||||||
TemplateMetadata
|
NormalizedTemplateMetadata
|
||||||
} from 'angular2/src/compiler/api';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {
|
import {
|
||||||
templateVisitAll,
|
templateVisitAll,
|
||||||
TemplateAstVisitor,
|
TemplateAstVisitor,
|
||||||
|
@ -29,6 +40,7 @@ import {
|
||||||
} from 'angular2/src/compiler/template_ast';
|
} from 'angular2/src/compiler/template_ast';
|
||||||
|
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
||||||
|
import {MockSchemaRegistry} from './schema_registry_mock';
|
||||||
|
|
||||||
import {Unparser} from '../core/change_detection/parser/unparser';
|
import {Unparser} from '../core/change_detection/parser/unparser';
|
||||||
|
|
||||||
|
@ -36,24 +48,26 @@ var expressionUnparser = new Unparser();
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('TemplateParser', () => {
|
describe('TemplateParser', () => {
|
||||||
var domParser: HtmlParser;
|
beforeEachBindings(() => [
|
||||||
|
TEST_BINDINGS,
|
||||||
|
bind(ElementSchemaRegistry)
|
||||||
|
.toValue(new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}))
|
||||||
|
]);
|
||||||
|
|
||||||
var parser: TemplateParser;
|
var parser: TemplateParser;
|
||||||
var ngIf;
|
var ngIf;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(inject([TemplateParser], (_parser) => {
|
||||||
domParser = new HtmlParser();
|
parser = _parser;
|
||||||
parser = new TemplateParser(
|
ngIf = new NormalizedDirectiveMetadata({
|
||||||
new Parser(new Lexer()),
|
|
||||||
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
|
|
||||||
ngIf = new DirectiveMetadata({
|
|
||||||
selector: '[ng-if]',
|
selector: '[ng-if]',
|
||||||
type: new TypeMetadata({typeName: 'NgIf'}),
|
type: new TypeMetadata({name: 'NgIf'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['ngIf']})
|
changeDetection: new ChangeDetectionMetadata({properties: ['ngIf']})
|
||||||
});
|
});
|
||||||
});
|
}));
|
||||||
|
|
||||||
function parse(template: string, directives: DirectiveMetadata[]): TemplateAst[] {
|
function parse(template: string, directives: NormalizedDirectiveMetadata[]): TemplateAst[] {
|
||||||
return parser.parse(domParser.parse(template, 'TestComp'), directives);
|
return parser.parse(template, directives, 'TestComp');
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('parse', () => {
|
describe('parse', () => {
|
||||||
|
@ -341,16 +355,16 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('directives', () => {
|
describe('directives', () => {
|
||||||
it('should locate directives ordered by typeName and components first', () => {
|
it('should locate directives ordered by name and components first', () => {
|
||||||
var dirA = new DirectiveMetadata(
|
var dirA = new NormalizedDirectiveMetadata(
|
||||||
{selector: '[a=b]', type: new TypeMetadata({typeName: 'DirA'})});
|
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
|
||||||
var dirB =
|
var dirB = new NormalizedDirectiveMetadata(
|
||||||
new DirectiveMetadata({selector: '[a]', type: new TypeMetadata({typeName: 'DirB'})});
|
{selector: '[a]', type: new TypeMetadata({name: 'DirB'})});
|
||||||
var comp = new DirectiveMetadata({
|
var comp = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({typeName: 'ZComp'}),
|
type: new TypeMetadata({name: 'ZComp'}),
|
||||||
template: new TemplateMetadata({ngContentSelectors: []})
|
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div a="b">', [dirB, dirA, comp])))
|
expect(humanizeTemplateAsts(parse('<div a="b">', [dirB, dirA, comp])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
|
@ -363,10 +377,10 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should locate directives in property bindings', () => {
|
it('should locate directives in property bindings', () => {
|
||||||
var dirA = new DirectiveMetadata(
|
var dirA = new NormalizedDirectiveMetadata(
|
||||||
{selector: '[a=b]', type: new TypeMetadata({typeName: 'DirA'})});
|
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
|
||||||
var dirB =
|
var dirB = new NormalizedDirectiveMetadata(
|
||||||
new DirectiveMetadata({selector: '[b]', type: new TypeMetadata({typeName: 'DirB'})});
|
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
|
||||||
expect(humanizeTemplateAsts(parse('<div [a]="b">', [dirA, dirB])))
|
expect(humanizeTemplateAsts(parse('<div [a]="b">', [dirA, dirB])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
@ -383,10 +397,10 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should locate directives in variable bindings', () => {
|
it('should locate directives in variable bindings', () => {
|
||||||
var dirA = new DirectiveMetadata(
|
var dirA = new NormalizedDirectiveMetadata(
|
||||||
{selector: '[a=b]', type: new TypeMetadata({typeName: 'DirA'})});
|
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
|
||||||
var dirB =
|
var dirB = new NormalizedDirectiveMetadata(
|
||||||
new DirectiveMetadata({selector: '[b]', type: new TypeMetadata({typeName: 'DirB'})});
|
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
|
||||||
expect(humanizeTemplateAsts(parse('<div #a="b">', [dirA, dirB])))
|
expect(humanizeTemplateAsts(parse('<div #a="b">', [dirA, dirB])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
@ -396,9 +410,9 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse directive host properties', () => {
|
it('should parse directive host properties', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({hostProperties: {'a': 'expr'}})
|
changeDetection: new ChangeDetectionMetadata({hostProperties: {'a': 'expr'}})
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
||||||
|
@ -417,9 +431,9 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse directive host listeners', () => {
|
it('should parse directive host listeners', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({hostListeners: {'a': 'expr'}})
|
changeDetection: new ChangeDetectionMetadata({hostListeners: {'a': 'expr'}})
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
||||||
|
@ -431,9 +445,9 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse directive properties', () => {
|
it('should parse directive properties', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['aProp']})
|
changeDetection: new ChangeDetectionMetadata({properties: ['aProp']})
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div [a-prop]="expr"></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div [a-prop]="expr"></div>', [dirA])))
|
||||||
|
@ -450,9 +464,9 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse renamed directive properties', () => {
|
it('should parse renamed directive properties', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['b:a']})
|
changeDetection: new ChangeDetectionMetadata({properties: ['b:a']})
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div [a]="expr"></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div [a]="expr"></div>', [dirA])))
|
||||||
|
@ -464,9 +478,9 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse literal directive properties', () => {
|
it('should parse literal directive properties', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div a="literal"></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div a="literal"></div>', [dirA])))
|
||||||
|
@ -484,9 +498,9 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support optional directive properties', () => {
|
it('should support optional directive properties', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
||||||
|
@ -549,13 +563,13 @@ export function main() {
|
||||||
|
|
||||||
describe('directives', () => {
|
describe('directives', () => {
|
||||||
it('should locate directives in property bindings', () => {
|
it('should locate directives in property bindings', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: '[a=b]',
|
selector: '[a=b]',
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
||||||
});
|
});
|
||||||
var dirB = new DirectiveMetadata(
|
var dirB = new NormalizedDirectiveMetadata(
|
||||||
{selector: '[b]', type: new TypeMetadata({typeName: 'DirB'})});
|
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
|
||||||
expect(humanizeTemplateAsts(parse('<div template="a b" b>', [dirA, dirB])))
|
expect(humanizeTemplateAsts(parse('<div template="a b" b>', [dirA, dirB])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
|
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
|
||||||
|
@ -573,10 +587,10 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should locate directives in variable bindings', () => {
|
it('should locate directives in variable bindings', () => {
|
||||||
var dirA = new DirectiveMetadata(
|
var dirA = new NormalizedDirectiveMetadata(
|
||||||
{selector: '[a=b]', type: new TypeMetadata({typeName: 'DirA'})});
|
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
|
||||||
var dirB = new DirectiveMetadata(
|
var dirB = new NormalizedDirectiveMetadata(
|
||||||
{selector: '[b]', type: new TypeMetadata({typeName: 'DirB'})});
|
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
|
||||||
expect(humanizeTemplateAsts(parse('<div template="#a=b" b>', [dirA, dirB])))
|
expect(humanizeTemplateAsts(parse('<div template="#a=b" b>', [dirA, dirB])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
|
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
|
||||||
|
@ -609,12 +623,13 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('content projection', () => {
|
describe('content projection', () => {
|
||||||
function createComp(selector: string, ngContentSelectors: string[]): DirectiveMetadata {
|
function createComp(selector: string, ngContentSelectors: string[]):
|
||||||
return new DirectiveMetadata({
|
NormalizedDirectiveMetadata {
|
||||||
|
return new NormalizedDirectiveMetadata({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({typeName: 'SomeComp'}),
|
type: new TypeMetadata({name: 'SomeComp'}),
|
||||||
template: new TemplateMetadata({ngContentSelectors: ngContentSelectors})
|
template: new NormalizedTemplateMetadata({ngContentSelectors: ngContentSelectors})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -710,26 +725,26 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp > div:nth-ch
|
||||||
|
|
||||||
it('should not throw on invalid property names if the property is used by a directive',
|
it('should not throw on invalid property names if the property is used by a directive',
|
||||||
() => {
|
() => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['invalidProp']})
|
changeDetection: new ChangeDetectionMetadata({properties: ['invalidProp']})
|
||||||
});
|
});
|
||||||
expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow();
|
expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow more than 1 component per element', () => {
|
it('should not allow more than 1 component per element', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
template: new TemplateMetadata({ngContentSelectors: []})
|
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
var dirB = new DirectiveMetadata({
|
var dirB = new NormalizedDirectiveMetadata({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({typeName: 'DirB'}),
|
type: new TypeMetadata({name: 'DirB'}),
|
||||||
template: new TemplateMetadata({ngContentSelectors: []})
|
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
|
expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
|
||||||
More than one component: DirA,DirB in TestComp > div:nth-child(0)`);
|
More than one component: DirA,DirB in TestComp > div:nth-child(0)`);
|
||||||
|
@ -737,11 +752,11 @@ More than one component: DirA,DirB in TestComp > div:nth-child(0)`);
|
||||||
|
|
||||||
it('should not allow components or element nor event bindings on explicit embedded templates',
|
it('should not allow components or element nor event bindings on explicit embedded templates',
|
||||||
() => {
|
() => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: '[a]',
|
selector: '[a]',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
template: new TemplateMetadata({ngContentSelectors: []})
|
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
|
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
|
||||||
.toThrowError(`Template parse errors:
|
.toThrowError(`Template parse errors:
|
||||||
|
@ -751,11 +766,11 @@ Event binding e on an embedded template in TestComp > template:nth-child(0)[(e)=
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow components or element bindings on inline embedded templates', () => {
|
it('should not allow components or element bindings on inline embedded templates', () => {
|
||||||
var dirA = new DirectiveMetadata({
|
var dirA = new NormalizedDirectiveMetadata({
|
||||||
selector: '[a]',
|
selector: '[a]',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({typeName: 'DirA'}),
|
type: new TypeMetadata({name: 'DirA'}),
|
||||||
template: new TemplateMetadata({ngContentSelectors: []})
|
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
expect(() => parse('<div *a="b">', [dirA])).toThrowError(`Template parse errors:
|
expect(() => parse('<div *a="b">', [dirA])).toThrowError(`Template parse errors:
|
||||||
Components on an embedded template: DirA in TestComp > div:nth-child(0)
|
Components on an embedded template: DirA in TestComp > div:nth-child(0)
|
||||||
|
@ -887,17 +902,3 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
|
||||||
visitDirective(ast: DirectiveAst, context: any): any { return null; }
|
visitDirective(ast: DirectiveAst, context: any): any { return null; }
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MockSchemaRegistry implements ElementSchemaRegistry {
|
|
||||||
constructor(public existingProperties: StringMap<string, boolean>,
|
|
||||||
public attrPropMapping: StringMap<string, string>) {}
|
|
||||||
hasProperty(tagName: string, property: string): boolean {
|
|
||||||
var result = this.existingProperties[property];
|
|
||||||
return isPresent(result) ? result : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMappedPropName(attrName: string): string {
|
|
||||||
var result = this.attrPropMapping[attrName];
|
|
||||||
return isPresent(result) ? result : attrName;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {bind, Binding} from 'angular2/src/core/di';
|
||||||
|
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||||
|
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
||||||
|
import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
|
||||||
|
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||||
|
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
||||||
|
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
||||||
|
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
||||||
|
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
|
||||||
|
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
import {MockSchemaRegistry} from './schema_registry_mock';
|
||||||
|
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
||||||
|
|
||||||
|
// TODO(tbosch): move this into test_injector once the new compiler pipeline is used fully
|
||||||
|
export var TEST_BINDINGS = [
|
||||||
|
HtmlParser,
|
||||||
|
TemplateParser,
|
||||||
|
TemplateNormalizer,
|
||||||
|
RuntimeMetadataResolver,
|
||||||
|
StyleCompiler,
|
||||||
|
CommandCompiler,
|
||||||
|
ChangeDetectionCompiler,
|
||||||
|
bind(ChangeDetectorGenConfig).toValue(new ChangeDetectorGenConfig(true, true, false, false)),
|
||||||
|
TemplateCompiler,
|
||||||
|
bind(ElementSchemaRegistry).toValue(new MockSchemaRegistry({}, {}))
|
||||||
|
];
|
|
@ -242,6 +242,20 @@ export function main() {
|
||||||
expect(reflector.method("abc")("anything", ["fake"])).toEqual(['fake']);
|
expect(reflector.method("abc")("anything", ["fake"])).toEqual(['fake']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (IS_DART) {
|
||||||
|
describe("moduleId", () => {
|
||||||
|
it("should return the moduleId for a type", () => {
|
||||||
|
expect(reflector.moduleId(TestObjWith00Args))
|
||||||
|
.toEqual('angular2/test/core/reflection/reflector_spec');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return an empty array otherwise", () => {
|
||||||
|
var p = reflector.interfaces(ClassWithDecorators);
|
||||||
|
expect(p).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,11 @@ class Codegen {
|
||||||
/// The names of already generated classes.
|
/// The names of already generated classes.
|
||||||
final Set<String> _names = new Set<String>();
|
final Set<String> _names = new Set<String>();
|
||||||
|
|
||||||
|
/// The module prefix for pregen_proto_change_detector
|
||||||
|
final String _genPrefix;
|
||||||
|
|
||||||
|
Codegen([this._genPrefix = _GEN_PREFIX_WITH_DOT]);
|
||||||
|
|
||||||
/// Generates a change detector class with name `changeDetectorTypeName`,
|
/// Generates a change detector class with name `changeDetectorTypeName`,
|
||||||
/// which must not conflict with other generated classes in the same
|
/// which must not conflict with other generated classes in the same
|
||||||
/// `.ng_deps.dart` file. The change detector is used to detect changes in
|
/// `.ng_deps.dart` file. The change detector is used to detect changes in
|
||||||
|
@ -40,7 +45,7 @@ class Codegen {
|
||||||
'conflicts with an earlier generated change detector class.');
|
'conflicts with an earlier generated change detector class.');
|
||||||
}
|
}
|
||||||
_names.add(changeDetectorTypeName);
|
_names.add(changeDetectorTypeName);
|
||||||
new _CodegenState(typeName, changeDetectorTypeName, def)
|
new _CodegenState(_genPrefix, typeName, changeDetectorTypeName, def)
|
||||||
.._writeToBuf(_buf)
|
.._writeToBuf(_buf)
|
||||||
.._writeInitToBuf(_initBuf);
|
.._writeInitToBuf(_initBuf);
|
||||||
}
|
}
|
||||||
|
@ -86,9 +91,13 @@ class _CodegenState {
|
||||||
final List<BindingTarget> _propertyBindingTargets;
|
final List<BindingTarget> _propertyBindingTargets;
|
||||||
|
|
||||||
String get _changeDetectionStrategyAsCode =>
|
String get _changeDetectionStrategyAsCode =>
|
||||||
_changeDetectionStrategy == null ? 'null' : '${_GEN_PREFIX}.${_changeDetectionStrategy}';
|
_changeDetectionStrategy == null ? 'null' : '${_genPrefix}${_changeDetectionStrategy}';
|
||||||
|
|
||||||
|
/// The module prefix for pregen_proto_change_detector
|
||||||
|
final String _genPrefix;
|
||||||
|
|
||||||
_CodegenState._(
|
_CodegenState._(
|
||||||
|
this._genPrefix,
|
||||||
this._changeDetectorDefId,
|
this._changeDetectorDefId,
|
||||||
this._contextTypeName,
|
this._contextTypeName,
|
||||||
this._changeDetectorTypeName,
|
this._changeDetectorTypeName,
|
||||||
|
@ -101,15 +110,16 @@ class _CodegenState {
|
||||||
this._names,
|
this._names,
|
||||||
this._genConfig);
|
this._genConfig);
|
||||||
|
|
||||||
factory _CodegenState(String typeName, String changeDetectorTypeName,
|
factory _CodegenState(String genPrefix, String typeName, String changeDetectorTypeName,
|
||||||
ChangeDetectorDefinition def) {
|
ChangeDetectorDefinition def) {
|
||||||
var protoRecords = createPropertyRecords(def);
|
var protoRecords = createPropertyRecords(def);
|
||||||
var eventBindings = createEventRecords(def);
|
var eventBindings = createEventRecords(def);
|
||||||
var propertyBindingTargets = def.bindingRecords.map((b) => b.target).toList();
|
var propertyBindingTargets = def.bindingRecords.map((b) => b.target).toList();
|
||||||
|
|
||||||
var names = new CodegenNameUtil(protoRecords, eventBindings, def.directiveRecords, _UTIL);
|
var names = new CodegenNameUtil(protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL');
|
||||||
var logic = new CodegenLogicUtil(names, _UTIL, def.strategy);
|
var logic = new CodegenLogicUtil(names, '$genPrefix$_UTIL', def.strategy);
|
||||||
return new _CodegenState._(
|
return new _CodegenState._(
|
||||||
|
genPrefix,
|
||||||
def.id,
|
def.id,
|
||||||
typeName,
|
typeName,
|
||||||
changeDetectorTypeName,
|
changeDetectorTypeName,
|
||||||
|
@ -125,7 +135,7 @@ class _CodegenState {
|
||||||
|
|
||||||
void _writeToBuf(StringBuffer buf) {
|
void _writeToBuf(StringBuffer buf) {
|
||||||
buf.write('''\n
|
buf.write('''\n
|
||||||
class $_changeDetectorTypeName extends $_BASE_CLASS<$_contextTypeName> {
|
class $_changeDetectorTypeName extends ${_genPrefix}$_BASE_CLASS<$_contextTypeName> {
|
||||||
${_genDeclareFields()}
|
${_genDeclareFields()}
|
||||||
|
|
||||||
$_changeDetectorTypeName(dispatcher)
|
$_changeDetectorTypeName(dispatcher)
|
||||||
|
@ -161,10 +171,10 @@ class _CodegenState {
|
||||||
|
|
||||||
${_genDirectiveIndices()};
|
${_genDirectiveIndices()};
|
||||||
|
|
||||||
static $_GEN_PREFIX.ProtoChangeDetector
|
static ${_genPrefix}ProtoChangeDetector
|
||||||
$PROTO_CHANGE_DETECTOR_FACTORY_METHOD(
|
$PROTO_CHANGE_DETECTOR_FACTORY_METHOD(
|
||||||
$_GEN_PREFIX.ChangeDetectorDefinition def) {
|
${_genPrefix}ChangeDetectorDefinition def) {
|
||||||
return new $_GEN_PREFIX.PregenProtoChangeDetector(
|
return new ${_genPrefix}PregenProtoChangeDetector(
|
||||||
(a) => new $_changeDetectorTypeName(a),
|
(a) => new $_changeDetectorTypeName(a),
|
||||||
def);
|
def);
|
||||||
}
|
}
|
||||||
|
@ -233,7 +243,7 @@ class _CodegenState {
|
||||||
|
|
||||||
void _writeInitToBuf(StringBuffer buf) {
|
void _writeInitToBuf(StringBuffer buf) {
|
||||||
buf.write('''
|
buf.write('''
|
||||||
$_GEN_PREFIX.preGeneratedProtoDetectors['$_changeDetectorDefId'] =
|
${_genPrefix}preGeneratedProtoDetectors['$_changeDetectorDefId'] =
|
||||||
$_changeDetectorTypeName.newProtoChangeDetector;
|
$_changeDetectorTypeName.newProtoChangeDetector;
|
||||||
''');
|
''');
|
||||||
}
|
}
|
||||||
|
@ -336,7 +346,7 @@ class _CodegenState {
|
||||||
var pipeType = r.name;
|
var pipeType = r.name;
|
||||||
|
|
||||||
var init = '''
|
var init = '''
|
||||||
if ($_IDENTICAL_CHECK_FN($pipe, $_UTIL.uninitialized)) {
|
if (${_genPrefix}$_IDENTICAL_CHECK_FN($pipe, ${_genPrefix}$_UTIL.uninitialized)) {
|
||||||
$pipe = ${_names.getPipesAccessorName()}.get('$pipeType');
|
$pipe = ${_names.getPipesAccessorName()}.get('$pipeType');
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
|
@ -350,8 +360,8 @@ class _CodegenState {
|
||||||
var condition = '''!${pipe}.pure || (${contexOrArgCheck.join(" || ")})''';
|
var condition = '''!${pipe}.pure || (${contexOrArgCheck.join(" || ")})''';
|
||||||
|
|
||||||
var check = '''
|
var check = '''
|
||||||
if ($_NOT_IDENTICAL_CHECK_FN($oldValue, $newValue)) {
|
if (${_genPrefix}$_NOT_IDENTICAL_CHECK_FN($oldValue, $newValue)) {
|
||||||
$newValue = $_UTIL.unwrapValue($newValue);
|
$newValue = ${_genPrefix}$_UTIL.unwrapValue($newValue);
|
||||||
${_genChangeMarker(r)}
|
${_genChangeMarker(r)}
|
||||||
${_genUpdateDirectiveOrElement(r)}
|
${_genUpdateDirectiveOrElement(r)}
|
||||||
${_genAddToChanges(r)}
|
${_genAddToChanges(r)}
|
||||||
|
@ -376,7 +386,7 @@ class _CodegenState {
|
||||||
''';
|
''';
|
||||||
|
|
||||||
var check = '''
|
var check = '''
|
||||||
if ($_NOT_IDENTICAL_CHECK_FN($newValue, $oldValue)) {
|
if (${_genPrefix}$_NOT_IDENTICAL_CHECK_FN($newValue, $oldValue)) {
|
||||||
${_genChangeMarker(r)}
|
${_genChangeMarker(r)}
|
||||||
${_genUpdateDirectiveOrElement(r)}
|
${_genUpdateDirectiveOrElement(r)}
|
||||||
${_genAddToChanges(r)}
|
${_genAddToChanges(r)}
|
||||||
|
@ -507,13 +517,14 @@ class _CodegenState {
|
||||||
|
|
||||||
const PROTO_CHANGE_DETECTOR_FACTORY_METHOD = 'newProtoChangeDetector';
|
const PROTO_CHANGE_DETECTOR_FACTORY_METHOD = 'newProtoChangeDetector';
|
||||||
|
|
||||||
const _BASE_CLASS = '$_GEN_PREFIX.AbstractChangeDetector';
|
const _BASE_CLASS = 'AbstractChangeDetector';
|
||||||
const _CHANGES_LOCAL = 'changes';
|
const _CHANGES_LOCAL = 'changes';
|
||||||
const _GEN_PREFIX = '_gen';
|
const _GEN_PREFIX = '_gen';
|
||||||
|
const _GEN_PREFIX_WITH_DOT = _GEN_PREFIX + '.';
|
||||||
const _GEN_RECORDS_METHOD_NAME = '_createRecords';
|
const _GEN_RECORDS_METHOD_NAME = '_createRecords';
|
||||||
const _IDENTICAL_CHECK_FN = '$_GEN_PREFIX.looseIdentical';
|
const _IDENTICAL_CHECK_FN = 'looseIdentical';
|
||||||
const _NOT_IDENTICAL_CHECK_FN = '$_GEN_PREFIX.looseNotIdentical';
|
const _NOT_IDENTICAL_CHECK_FN = 'looseNotIdentical';
|
||||||
const _IS_CHANGED_LOCAL = 'isChanged';
|
const _IS_CHANGED_LOCAL = 'isChanged';
|
||||||
const _PREGEN_PROTO_CHANGE_DETECTOR_IMPORT =
|
const _PREGEN_PROTO_CHANGE_DETECTOR_IMPORT =
|
||||||
'package:angular2/src/core/change_detection/pregen_proto_change_detector.dart';
|
'package:angular2/src/core/change_detection/pregen_proto_change_detector.dart';
|
||||||
const _UTIL = '$_GEN_PREFIX.ChangeDetectionUtil';
|
const _UTIL = 'ChangeDetectionUtil';
|
||||||
|
|
|
@ -29,6 +29,8 @@ class NullReflectionCapabilities implements ReflectionCapabilities {
|
||||||
MethodFn method(String name) => _nullMethod;
|
MethodFn method(String name) => _nullMethod;
|
||||||
|
|
||||||
String importUri(Type type) => './';
|
String importUri(Type type) => './';
|
||||||
|
|
||||||
|
String moduleId(Type type) => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_nullGetter(Object p) => null;
|
_nullGetter(Object p) => null;
|
||||||
|
|
Loading…
Reference in New Issue