refactor(compiler): various cleanups
- use `$implicit` variable value correctly - handle `ng-non-bindable` correctly - add some more assertions to `TemplateCompiler` - make `CompiledTemplate` const - fix default value for `@Directive.moduleId` - add new compiler to application bindings BREAKING CHANGE: - `Compiler.compileInHost` and all methods of `DynamicComponentLoader` don’t take `Binding` any more, only `Type`s. This is in preparation for the new compiler which does not support this. Part of #3605 Closes #4346
This commit is contained in:
parent
bffa2cb59b
commit
7470ad1bd1
|
@ -35,7 +35,8 @@ import {escapeSingleQuoteString} from './util';
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
export var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
|
export var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
|
||||||
const IMPLICIT_VAR = '%implicit';
|
|
||||||
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommandCompiler {
|
export class CommandCompiler {
|
||||||
|
@ -207,7 +208,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||||
var variableNameAndValues = [];
|
var variableNameAndValues = [];
|
||||||
ast.vars.forEach((varAst) => {
|
ast.vars.forEach((varAst) => {
|
||||||
variableNameAndValues.push(varAst.name);
|
variableNameAndValues.push(varAst.name);
|
||||||
variableNameAndValues.push(varAst.value);
|
variableNameAndValues.push(varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR);
|
||||||
});
|
});
|
||||||
var directives = [];
|
var directives = [];
|
||||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||||
|
@ -227,7 +228,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||||
if (isBlank(component)) {
|
if (isBlank(component)) {
|
||||||
ast.exportAsVars.forEach((varAst) => {
|
ast.exportAsVars.forEach((varAst) => {
|
||||||
variableNameAndValues.push(varAst.name);
|
variableNameAndValues.push(varAst.name);
|
||||||
variableNameAndValues.push(IMPLICIT_VAR);
|
variableNameAndValues.push(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var directives = [];
|
var directives = [];
|
||||||
|
|
|
@ -5,3 +5,31 @@ export {
|
||||||
CompileTemplateMetadata
|
CompileTemplateMetadata
|
||||||
} from './directive_metadata';
|
} from './directive_metadata';
|
||||||
export {SourceModule, SourceWithImports} from './source_module';
|
export {SourceModule, SourceWithImports} from './source_module';
|
||||||
|
|
||||||
|
import {assertionsEnabled, Type} from 'angular2/src/core/facade/lang';
|
||||||
|
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';
|
||||||
|
|
||||||
|
export function compilerBindings(): Array<Type | Binding | any[]> {
|
||||||
|
return [
|
||||||
|
HtmlParser,
|
||||||
|
TemplateParser,
|
||||||
|
TemplateNormalizer,
|
||||||
|
RuntimeMetadataResolver,
|
||||||
|
StyleCompiler,
|
||||||
|
CommandCompiler,
|
||||||
|
ChangeDetectionCompiler,
|
||||||
|
bind(ChangeDetectorGenConfig)
|
||||||
|
.toValue(
|
||||||
|
new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled(), false, true)),
|
||||||
|
TemplateCompiler,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
@ -57,12 +57,7 @@ function parseElement(element: Element, indexInParent: number, parentSourceInfo:
|
||||||
var sourceInfo = `${parentSourceInfo} > ${nodeName}:nth-child(${indexInParent})`;
|
var sourceInfo = `${parentSourceInfo} > ${nodeName}:nth-child(${indexInParent})`;
|
||||||
var attrs = parseAttrs(element, sourceInfo);
|
var attrs = parseAttrs(element, sourceInfo);
|
||||||
|
|
||||||
var childNodes;
|
var childNodes = parseChildNodes(element, sourceInfo);
|
||||||
if (ignoreChildren(attrs)) {
|
|
||||||
childNodes = [];
|
|
||||||
} else {
|
|
||||||
childNodes = parseChildNodes(element, sourceInfo);
|
|
||||||
}
|
|
||||||
return new HtmlElementAst(nodeName, attrs, childNodes, sourceInfo);
|
return new HtmlElementAst(nodeName, attrs, childNodes, sourceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,16 +95,6 @@ function parseChildNodes(element: Element, parentSourceInfo: string): HtmlAst[]
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ignoreChildren(attrs: HtmlAttrAst[]): boolean {
|
|
||||||
for (var i = 0; i < attrs.length; i++) {
|
|
||||||
var a = attrs[i];
|
|
||||||
if (a.name == NG_NON_BINDABLE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class UnparseVisitor implements HtmlAstVisitor {
|
class UnparseVisitor implements HtmlAstVisitor {
|
||||||
visitElement(ast: HtmlElementAst, parts: string[]): any {
|
visitElement(ast: HtmlElementAst, parts: string[]): any {
|
||||||
parts.push(`<${ast.name}`);
|
parts.push(`<${ast.name}`);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
|
import {CompileTypeMetadata, CompileTemplateMetadata} from './directive_metadata';
|
||||||
import {SourceModule, SourceExpression, moduleRef} from './source_module';
|
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';
|
||||||
|
@ -29,27 +29,28 @@ export class StyleCompiler {
|
||||||
|
|
||||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
||||||
|
|
||||||
compileComponentRuntime(component: CompileDirectiveMetadata): Promise<string[]> {
|
compileComponentRuntime(type: CompileTypeMetadata,
|
||||||
var styles = component.template.styles;
|
template: CompileTemplateMetadata): Promise<string[]> {
|
||||||
var styleAbsUrls = component.template.styleUrls;
|
var styles = template.styles;
|
||||||
|
var styleAbsUrls = template.styleUrls;
|
||||||
return this._loadStyles(styles, styleAbsUrls,
|
return this._loadStyles(styles, styleAbsUrls,
|
||||||
component.template.encapsulation === ViewEncapsulation.Emulated)
|
template.encapsulation === ViewEncapsulation.Emulated)
|
||||||
.then(styles => styles.map(style => StringWrapper.replaceAll(style, COMPONENT_REGEX,
|
.then(styles => styles.map(
|
||||||
`${component.type.id}`)));
|
style => StringWrapper.replaceAll(style, COMPONENT_REGEX, `${type.id}`)));
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(component: CompileDirectiveMetadata): SourceExpression {
|
compileComponentCodeGen(type: CompileTypeMetadata,
|
||||||
var shim = component.template.encapsulation === ViewEncapsulation.Emulated;
|
template: CompileTemplateMetadata): SourceExpression {
|
||||||
|
var shim = template.encapsulation === ViewEncapsulation.Emulated;
|
||||||
var suffix;
|
var suffix;
|
||||||
if (shim) {
|
if (shim) {
|
||||||
var componentId = `${ component.type.id}`;
|
var componentId = `${ type.id}`;
|
||||||
suffix =
|
suffix =
|
||||||
codeGenMapArray(['style'], `style${codeGenReplaceAll(COMPONENT_VARIABLE, componentId)}`);
|
codeGenMapArray(['style'], `style${codeGenReplaceAll(COMPONENT_VARIABLE, componentId)}`);
|
||||||
} else {
|
} else {
|
||||||
suffix = '';
|
suffix = '';
|
||||||
}
|
}
|
||||||
return this._styleCodeGen(component.template.styles, component.template.styleUrls, shim,
|
return this._styleCodeGen(template.styles, template.styleUrls, shim, suffix);
|
||||||
suffix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileStylesheetCodeGen(moduleId: string, cssText: string): SourceModule[] {
|
compileStylesheetCodeGen(moduleId: string, cssText: string): SourceModule[] {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang';
|
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 {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||||
import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands';
|
import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands';
|
||||||
|
@ -58,18 +59,12 @@ export class TemplateCompiler {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
serializeDirectiveMetadata(metadata: CompileDirectiveMetadata): string {
|
|
||||||
return Json.stringify(metadata.toJson());
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeDirectiveMetadata(data: string): CompileDirectiveMetadata {
|
|
||||||
return CompileDirectiveMetadata.fromJson(Json.parse(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> {
|
compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> {
|
||||||
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
||||||
|
assertComponent(compMeta);
|
||||||
var hostMeta: CompileDirectiveMetadata =
|
var hostMeta: CompileDirectiveMetadata =
|
||||||
createHostComponentMeta(compMeta.type, compMeta.selector);
|
createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
|
|
||||||
this._compileComponentRuntime(hostMeta, [compMeta], new Set());
|
this._compileComponentRuntime(hostMeta, [compMeta], new Set());
|
||||||
return this._compiledTemplateDone.get(hostMeta.type.id);
|
return this._compiledTemplateDone.get(hostMeta.type.id);
|
||||||
}
|
}
|
||||||
|
@ -93,28 +88,30 @@ export class TemplateCompiler {
|
||||||
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactory, commands, styles]);
|
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactory, commands, styles]);
|
||||||
this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate);
|
this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate);
|
||||||
compilingComponentIds.add(compMeta.type.id);
|
compilingComponentIds.add(compMeta.type.id);
|
||||||
done = PromiseWrapper.all([<any>this._styleCompiler.compileComponentRuntime(compMeta)].concat(
|
done =
|
||||||
viewDirectives.map(
|
PromiseWrapper
|
||||||
dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
.all([
|
||||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
<any>this._styleCompiler.compileComponentRuntime(compMeta.type, compMeta.template)
|
||||||
var childPromises = [];
|
].concat(viewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
||||||
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||||
var parsedTemplate = this._templateParser.parse(
|
var childPromises = [];
|
||||||
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
||||||
|
var parsedTemplate = this._templateParser.parse(
|
||||||
|
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
||||||
|
|
||||||
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||||
changeDetectorFactory = changeDetectorFactories[0];
|
changeDetectorFactory = changeDetectorFactories[0];
|
||||||
styles = stylesAndNormalizedViewDirMetas[0];
|
styles = stylesAndNormalizedViewDirMetas[0];
|
||||||
commands = this._compileCommandsRuntime(compMeta, parsedTemplate,
|
commands =
|
||||||
changeDetectorFactories,
|
this._compileCommandsRuntime(compMeta, parsedTemplate, changeDetectorFactories,
|
||||||
compilingComponentIds, childPromises);
|
compilingComponentIds, childPromises);
|
||||||
return PromiseWrapper.all(childPromises);
|
return PromiseWrapper.all(childPromises);
|
||||||
})
|
})
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
SetWrapper.delete(compilingComponentIds, compMeta.type.id);
|
SetWrapper.delete(compilingComponentIds, compMeta.type.id);
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
});
|
});
|
||||||
this._compiledTemplateDone.set(compMeta.type.id, done);
|
this._compiledTemplateDone.set(compMeta.type.id, done);
|
||||||
}
|
}
|
||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
|
@ -148,6 +145,7 @@ export class TemplateCompiler {
|
||||||
var componentMetas: CompileDirectiveMetadata[] = [];
|
var componentMetas: CompileDirectiveMetadata[] = [];
|
||||||
components.forEach(componentWithDirs => {
|
components.forEach(componentWithDirs => {
|
||||||
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
||||||
|
assertComponent(compMeta);
|
||||||
componentMetas.push(compMeta);
|
componentMetas.push(compMeta);
|
||||||
this._processTemplateCodeGen(compMeta,
|
this._processTemplateCodeGen(compMeta,
|
||||||
<CompileDirectiveMetadata[]>componentWithDirs.directives,
|
<CompileDirectiveMetadata[]>componentWithDirs.directives,
|
||||||
|
@ -174,7 +172,7 @@ export class TemplateCompiler {
|
||||||
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
|
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
|
||||||
directives: CompileDirectiveMetadata[],
|
directives: CompileDirectiveMetadata[],
|
||||||
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
||||||
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta);
|
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.type, compMeta.template);
|
||||||
var parsedTemplate =
|
var parsedTemplate =
|
||||||
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
||||||
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
||||||
|
@ -197,6 +195,12 @@ export class NormalizedComponentWithViewDirectives {
|
||||||
public directives: CompileDirectiveMetadata[]) {}
|
public directives: CompileDirectiveMetadata[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assertComponent(meta: CompileDirectiveMetadata) {
|
||||||
|
if (!meta.isComponent) {
|
||||||
|
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function templateVariableName(type: CompileTypeMetadata): string {
|
function templateVariableName(type: CompileTypeMetadata): string {
|
||||||
return `${type.name}Template`;
|
return `${type.name}Template`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
CompileTemplateMetadata
|
CompileTemplateMetadata
|
||||||
} from './directive_metadata';
|
} from './directive_metadata';
|
||||||
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
||||||
|
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||||
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';
|
||||||
|
@ -34,11 +35,13 @@ export class TemplateNormalizer {
|
||||||
if (isPresent(template.template)) {
|
if (isPresent(template.template)) {
|
||||||
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
|
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
|
||||||
directiveType, template, template.template, directiveType.moduleId));
|
directiveType, template, template.template, directiveType.moduleId));
|
||||||
} else {
|
} else if (isPresent(template.templateUrl)) {
|
||||||
var sourceAbsUrl = this._urlResolver.resolve(directiveType.moduleId, template.templateUrl);
|
var sourceAbsUrl = this._urlResolver.resolve(directiveType.moduleId, template.templateUrl);
|
||||||
return this._xhr.get(sourceAbsUrl)
|
return this._xhr.get(sourceAbsUrl)
|
||||||
.then(templateContent => this.normalizeLoadedTemplate(directiveType, template,
|
.then(templateContent => this.normalizeLoadedTemplate(directiveType, template,
|
||||||
templateContent, sourceAbsUrl));
|
templateContent, sourceAbsUrl));
|
||||||
|
} else {
|
||||||
|
throw new BaseException(`No template specified for component ${directiveType.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,12 +82,15 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
|
||||||
ngContentSelectors: string[] = [];
|
ngContentSelectors: string[] = [];
|
||||||
styles: string[] = [];
|
styles: string[] = [];
|
||||||
styleUrls: string[] = [];
|
styleUrls: string[] = [];
|
||||||
|
ngNonBindableStackCount: number = 0;
|
||||||
|
|
||||||
visitElement(ast: HtmlElementAst, context: any): any {
|
visitElement(ast: HtmlElementAst, context: any): any {
|
||||||
var preparsedElement = preparseElement(ast);
|
var preparsedElement = preparseElement(ast);
|
||||||
switch (preparsedElement.type) {
|
switch (preparsedElement.type) {
|
||||||
case PreparsedElementType.NG_CONTENT:
|
case PreparsedElementType.NG_CONTENT:
|
||||||
this.ngContentSelectors.push(preparsedElement.selectAttr);
|
if (this.ngNonBindableStackCount === 0) {
|
||||||
|
this.ngContentSelectors.push(preparsedElement.selectAttr);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PreparsedElementType.STYLE:
|
case PreparsedElementType.STYLE:
|
||||||
var textContent = '';
|
var textContent = '';
|
||||||
|
@ -99,8 +105,12 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
|
||||||
this.styleUrls.push(preparsedElement.hrefAttr);
|
this.styleUrls.push(preparsedElement.hrefAttr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (preparsedElement.type !== PreparsedElementType.NON_BINDABLE) {
|
if (preparsedElement.nonBindable) {
|
||||||
htmlVisitAll(this, ast.children);
|
this.ngNonBindableStackCount++;
|
||||||
|
}
|
||||||
|
htmlVisitAll(this, ast.children);
|
||||||
|
if (preparsedElement.nonBindable) {
|
||||||
|
this.ngNonBindableStackCount--;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,8 +164,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
var preparsedElement = preparseElement(element);
|
var preparsedElement = preparseElement(element);
|
||||||
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||||
preparsedElement.type === PreparsedElementType.STYLE ||
|
preparsedElement.type === PreparsedElementType.STYLE ||
|
||||||
preparsedElement.type === PreparsedElementType.STYLESHEET ||
|
preparsedElement.type === PreparsedElementType.STYLESHEET) {
|
||||||
preparsedElement.type === PreparsedElementType.NON_BINDABLE) {
|
|
||||||
// Skipping <script> for security reasons
|
// Skipping <script> for security reasons
|
||||||
// Skipping <style> and stylesheets as we already processed them
|
// Skipping <style> and stylesheets as we already processed them
|
||||||
// in the StyleCompiler
|
// in the StyleCompiler
|
||||||
|
@ -196,13 +195,14 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var isTemplateElement = nodeName == TEMPLATE_ELEMENT;
|
var isTemplateElement = nodeName == TEMPLATE_ELEMENT;
|
||||||
var elementCssSelector = this._createElementCssSelector(nodeName, matchableAttrs);
|
var elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
|
||||||
var directives = this._createDirectiveAsts(
|
var directives = this._createDirectiveAsts(
|
||||||
element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector),
|
element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector),
|
||||||
elementOrDirectiveProps, isTemplateElement ? [] : vars, element.sourceInfo);
|
elementOrDirectiveProps, isTemplateElement ? [] : vars, element.sourceInfo);
|
||||||
var elementProps: BoundElementPropertyAst[] =
|
var elementProps: BoundElementPropertyAst[] =
|
||||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives);
|
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives);
|
||||||
var children = htmlVisitAll(this, element.children, Component.create(directives));
|
var children = htmlVisitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this,
|
||||||
|
element.children, Component.create(directives));
|
||||||
var elementNgContentIndex =
|
var elementNgContentIndex =
|
||||||
hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector);
|
hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector);
|
||||||
var parsedElement;
|
var parsedElement;
|
||||||
|
@ -221,8 +221,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
children, elementNgContentIndex, element.sourceInfo);
|
children, elementNgContentIndex, element.sourceInfo);
|
||||||
}
|
}
|
||||||
if (hasInlineTemplates) {
|
if (hasInlineTemplates) {
|
||||||
var templateCssSelector =
|
var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
||||||
this._createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
|
||||||
var templateDirectives = this._createDirectiveAsts(
|
var templateDirectives = this._createDirectiveAsts(
|
||||||
element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector),
|
element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector),
|
||||||
templateElementOrDirectiveProps, [], element.sourceInfo);
|
templateElementOrDirectiveProps, [], element.sourceInfo);
|
||||||
|
@ -381,22 +380,6 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
sourceInfo));
|
sourceInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createElementCssSelector(elementName: string, matchableAttrs: string[][]): CssSelector {
|
|
||||||
var cssSelector = new CssSelector();
|
|
||||||
|
|
||||||
cssSelector.setElement(elementName);
|
|
||||||
for (var i = 0; i < matchableAttrs.length; i++) {
|
|
||||||
var attrName = matchableAttrs[i][0].toLowerCase();
|
|
||||||
var attrValue = matchableAttrs[i][1];
|
|
||||||
cssSelector.addAttribute(attrName, attrValue);
|
|
||||||
if (attrName == CLASS_ATTR) {
|
|
||||||
var classes = splitClasses(attrValue);
|
|
||||||
classes.forEach(className => cssSelector.addClassName(className));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cssSelector;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
||||||
elementCssSelector: CssSelector): CompileDirectiveMetadata[] {
|
elementCssSelector: CssSelector): CompileDirectiveMetadata[] {
|
||||||
var directives = [];
|
var directives = [];
|
||||||
|
@ -480,8 +463,14 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
|
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
|
||||||
if (isPresent(directiveProperties)) {
|
if (isPresent(directiveProperties)) {
|
||||||
var boundPropsByName: Map<string, BoundElementOrDirectiveProperty> = new Map();
|
var boundPropsByName: Map<string, BoundElementOrDirectiveProperty> = new Map();
|
||||||
boundProps.forEach(boundProp =>
|
boundProps.forEach(boundProp => {
|
||||||
boundPropsByName.set(dashCaseToCamelCase(boundProp.name), boundProp));
|
var key = dashCaseToCamelCase(boundProp.name);
|
||||||
|
var prevValue = boundPropsByName.get(boundProp.name);
|
||||||
|
if (isBlank(prevValue) || prevValue.isLiteral) {
|
||||||
|
// give [a]="b" a higher precedence thatn a="b" on the same element
|
||||||
|
boundPropsByName.set(key, boundProp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => {
|
StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => {
|
||||||
elProp = dashCaseToCamelCase(elProp);
|
elProp = dashCaseToCamelCase(elProp);
|
||||||
|
@ -585,6 +574,34 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NonBindableVisitor implements HtmlAstVisitor {
|
||||||
|
visitElement(ast: HtmlElementAst, component: Component): ElementAst {
|
||||||
|
var preparsedElement = preparseElement(ast);
|
||||||
|
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||||
|
preparsedElement.type === PreparsedElementType.STYLE ||
|
||||||
|
preparsedElement.type === PreparsedElementType.STYLESHEET) {
|
||||||
|
// Skipping <script> for security reasons
|
||||||
|
// Skipping <style> and stylesheets as we already processed them
|
||||||
|
// in the StyleCompiler
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var attrNameAndValues = ast.attrs.map(attrAst => [attrAst.name, attrAst.value]);
|
||||||
|
var selector = createElementCssSelector(ast.name, attrNameAndValues);
|
||||||
|
var ngContentIndex = component.findNgContentIndex(selector);
|
||||||
|
var children = htmlVisitAll(this, ast.children, EMPTY_COMPONENT);
|
||||||
|
return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], children,
|
||||||
|
ngContentIndex, ast.sourceInfo);
|
||||||
|
}
|
||||||
|
visitAttr(ast: HtmlAttrAst, context: any): AttrAst {
|
||||||
|
return new AttrAst(ast.name, ast.value, ast.sourceInfo);
|
||||||
|
}
|
||||||
|
visitText(ast: HtmlTextAst, component: Component): TextAst {
|
||||||
|
var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||||
|
return new TextAst(ast.value, ngContentIndex, ast.sourceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class BoundElementOrDirectiveProperty {
|
class BoundElementOrDirectiveProperty {
|
||||||
constructor(public name: string, public expression: AST, public isLiteral: boolean,
|
constructor(public name: string, public expression: AST, public isLiteral: boolean,
|
||||||
public sourceInfo: string) {}
|
public sourceInfo: string) {}
|
||||||
|
@ -631,4 +648,21 @@ class Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createElementCssSelector(elementName: string, matchableAttrs: string[][]): CssSelector {
|
||||||
|
var cssSelector = new CssSelector();
|
||||||
|
|
||||||
|
cssSelector.setElement(elementName);
|
||||||
|
for (var i = 0; i < matchableAttrs.length; i++) {
|
||||||
|
var attrName = matchableAttrs[i][0].toLowerCase();
|
||||||
|
var attrValue = matchableAttrs[i][1];
|
||||||
|
cssSelector.addAttribute(attrName, attrValue);
|
||||||
|
if (attrName == CLASS_ATTR) {
|
||||||
|
var classes = splitClasses(attrValue);
|
||||||
|
classes.forEach(className => cssSelector.addClassName(className));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cssSelector;
|
||||||
|
}
|
||||||
|
|
||||||
var EMPTY_COMPONENT = new Component(new SelectorMatcher(), null);
|
var EMPTY_COMPONENT = new Component(new SelectorMatcher(), null);
|
||||||
|
var NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
|
@ -30,9 +30,7 @@ export function preparseElement(ast: HtmlElementAst): PreparsedElement {
|
||||||
selectAttr = normalizeNgContentSelect(selectAttr);
|
selectAttr = normalizeNgContentSelect(selectAttr);
|
||||||
var nodeName = ast.name;
|
var nodeName = ast.name;
|
||||||
var type = PreparsedElementType.OTHER;
|
var type = PreparsedElementType.OTHER;
|
||||||
if (nonBindable) {
|
if (nodeName == NG_CONTENT_ELEMENT) {
|
||||||
type = PreparsedElementType.NON_BINDABLE;
|
|
||||||
} else if (nodeName == NG_CONTENT_ELEMENT) {
|
|
||||||
type = PreparsedElementType.NG_CONTENT;
|
type = PreparsedElementType.NG_CONTENT;
|
||||||
} else if (nodeName == STYLE_ELEMENT) {
|
} else if (nodeName == STYLE_ELEMENT) {
|
||||||
type = PreparsedElementType.STYLE;
|
type = PreparsedElementType.STYLE;
|
||||||
|
@ -41,7 +39,7 @@ export function preparseElement(ast: HtmlElementAst): PreparsedElement {
|
||||||
} else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
} else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
||||||
type = PreparsedElementType.STYLESHEET;
|
type = PreparsedElementType.STYLESHEET;
|
||||||
}
|
}
|
||||||
return new PreparsedElement(type, selectAttr, hrefAttr);
|
return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable);
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PreparsedElementType {
|
export enum PreparsedElementType {
|
||||||
|
@ -49,13 +47,12 @@ export enum PreparsedElementType {
|
||||||
STYLE,
|
STYLE,
|
||||||
STYLESHEET,
|
STYLESHEET,
|
||||||
SCRIPT,
|
SCRIPT,
|
||||||
NON_BINDABLE,
|
|
||||||
OTHER
|
OTHER
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PreparsedElement {
|
export class PreparsedElement {
|
||||||
constructor(public type: PreparsedElementType, public selectAttr: string,
|
constructor(public type: PreparsedElementType, public selectAttr: string, public hrefAttr: string,
|
||||||
public hrefAttr: string) {}
|
public nonBindable: boolean) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ 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 var IS_DART = !isJsObject({});
|
||||||
|
|
||||||
|
@ -33,7 +33,9 @@ export function escapeDoubleQuoteString(input: string): string {
|
||||||
|
|
||||||
function escapeString(input: string, re: RegExp): string {
|
function escapeString(input: string, re: RegExp): string {
|
||||||
return StringWrapper.replaceAllMapped(input, re, (match) => {
|
return StringWrapper.replaceAllMapped(input, re, (match) => {
|
||||||
if (match[0] == '\n') {
|
if (match[0] == '$') {
|
||||||
|
return IS_DART ? '\\$' : '$';
|
||||||
|
} else if (match[0] == '\n') {
|
||||||
return '\\n';
|
return '\\n';
|
||||||
} else {
|
} else {
|
||||||
return `\\${match[0]}`;
|
return `\\${match[0]}`;
|
||||||
|
|
|
@ -43,7 +43,7 @@ import {PipeResolver} from './compiler/pipe_resolver';
|
||||||
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
|
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
|
||||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
|
import {compilerBindings} from 'angular2/src/compiler/compiler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the set of bindings meant for use at the platform level.
|
* Constructs the set of bindings meant for use at the platform level.
|
||||||
|
@ -95,6 +95,7 @@ export function applicationCommonBindings(): Array<Type | Binding | any[]> {
|
||||||
bestChangeDetection = new JitChangeDetection();
|
bestChangeDetection = new JitChangeDetection();
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
|
compilerBindings(),
|
||||||
ProtoViewFactory,
|
ProtoViewFactory,
|
||||||
AppViewPool,
|
AppViewPool,
|
||||||
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||||
|
|
|
@ -129,9 +129,7 @@ export class Compiler {
|
||||||
|
|
||||||
// Create a hostView as if the compiler encountered <hostcmp></hostcmp>.
|
// Create a hostView as if the compiler encountered <hostcmp></hostcmp>.
|
||||||
// Used for bootstrapping.
|
// Used for bootstrapping.
|
||||||
compileInHost(componentTypeOrBinding: Type | Binding): Promise<ProtoViewRef> {
|
compileInHost(componentType: Type): Promise<ProtoViewRef> {
|
||||||
var componentType = isType(componentTypeOrBinding) ? componentTypeOrBinding :
|
|
||||||
(<Binding>componentTypeOrBinding).token;
|
|
||||||
var r = wtfStartTimeRange('Compiler#compile()', stringify(componentType));
|
var r = wtfStartTimeRange('Compiler#compile()', stringify(componentType));
|
||||||
|
|
||||||
var hostAppProtoView = this._compilerCache.getHost(componentType);
|
var hostAppProtoView = this._compilerCache.getHost(componentType);
|
||||||
|
@ -139,7 +137,7 @@ export class Compiler {
|
||||||
if (isPresent(hostAppProtoView)) {
|
if (isPresent(hostAppProtoView)) {
|
||||||
hostPvPromise = PromiseWrapper.resolve(hostAppProtoView);
|
hostPvPromise = PromiseWrapper.resolve(hostAppProtoView);
|
||||||
} else {
|
} else {
|
||||||
var componentBinding: DirectiveBinding = this._bindDirective(componentTypeOrBinding);
|
var componentBinding: DirectiveBinding = this._bindDirective(componentType);
|
||||||
Compiler._assertTypeIsComponent(componentBinding);
|
Compiler._assertTypeIsComponent(componentBinding);
|
||||||
|
|
||||||
var directiveMetadata = componentBinding.metadata;
|
var directiveMetadata = componentBinding.metadata;
|
||||||
|
|
|
@ -6,10 +6,6 @@ import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
import {ElementRef} from './element_ref';
|
import {ElementRef} from './element_ref';
|
||||||
import {ViewRef, HostViewRef} from './view_ref';
|
import {ViewRef, HostViewRef} from './view_ref';
|
||||||
|
|
||||||
function _asType(typeOrBinding: Type | Binding): Type {
|
|
||||||
return isType(typeOrBinding) ? typeOrBinding : (<Binding>typeOrBinding).token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Angular's reference to a component instance.
|
* Angular's reference to a component instance.
|
||||||
*
|
*
|
||||||
|
@ -69,7 +65,7 @@ export class DynamicComponentLoader {
|
||||||
* Loads a root component that is placed at the first element that matches the component's
|
* Loads a root component that is placed at the first element that matches the component's
|
||||||
* selector.
|
* selector.
|
||||||
*
|
*
|
||||||
* - `typeOrBinding` `Type` \ {@link Binding} - representing the component to load.
|
* - `typeOrBinding` `Type` - representing the component to load.
|
||||||
* - `overrideSelector` (optional) selector to load the component at (or use
|
* - `overrideSelector` (optional) selector to load the component at (or use
|
||||||
* `@Component.selector`) The selector can be anywhere (i.e. outside the current component.)
|
* `@Component.selector`) The selector can be anywhere (i.e. outside the current component.)
|
||||||
* - `injector` {@link Injector} - optional injector to use for the component.
|
* - `injector` {@link Injector} - optional injector to use for the component.
|
||||||
|
@ -120,24 +116,22 @@ export class DynamicComponentLoader {
|
||||||
* </my-app>
|
* </my-app>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
loadAsRoot(typeOrBinding: Type | Binding, overrideSelector: string, injector: Injector,
|
loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
|
||||||
onDispose?: () => void): Promise<ComponentRef> {
|
onDispose?: () => void): Promise<ComponentRef> {
|
||||||
return this._compiler.compileInHost(typeOrBinding)
|
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
||||||
.then(hostProtoViewRef => {
|
var hostViewRef =
|
||||||
var hostViewRef =
|
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
||||||
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
|
||||||
|
|
||||||
var dispose = () => {
|
var dispose = () => {
|
||||||
this._viewManager.destroyRootHostView(hostViewRef);
|
this._viewManager.destroyRootHostView(hostViewRef);
|
||||||
if (isPresent(onDispose)) {
|
if (isPresent(onDispose)) {
|
||||||
onDispose();
|
onDispose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return new ComponentRef(newLocation, component, _asType(typeOrBinding), injector,
|
return new ComponentRef(newLocation, component, type, injector, dispose);
|
||||||
dispose);
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,11 +181,10 @@ export class DynamicComponentLoader {
|
||||||
* </my-app>
|
* </my-app>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
loadIntoLocation(typeOrBinding: Type | Binding, hostLocation: ElementRef, anchorName: string,
|
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
|
||||||
bindings: ResolvedBinding[] = null): Promise<ComponentRef> {
|
bindings: ResolvedBinding[] = null): Promise<ComponentRef> {
|
||||||
return this.loadNextToLocation(
|
return this.loadNextToLocation(
|
||||||
typeOrBinding, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
|
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName), bindings);
|
||||||
bindings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -235,23 +228,22 @@ export class DynamicComponentLoader {
|
||||||
* <child-component>Child</child-component>
|
* <child-component>Child</child-component>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
loadNextToLocation(typeOrBinding: Type | Binding, location: ElementRef,
|
loadNextToLocation(type: Type, location: ElementRef,
|
||||||
bindings: ResolvedBinding[] = null): Promise<ComponentRef> {
|
bindings: ResolvedBinding[] = null): Promise<ComponentRef> {
|
||||||
return this._compiler.compileInHost(typeOrBinding)
|
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
||||||
.then(hostProtoViewRef => {
|
var viewContainer = this._viewManager.getViewContainer(location);
|
||||||
var viewContainer = this._viewManager.getViewContainer(location);
|
var hostViewRef =
|
||||||
var hostViewRef =
|
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, bindings);
|
||||||
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, bindings);
|
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
|
||||||
|
|
||||||
var dispose = () => {
|
var dispose = () => {
|
||||||
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
|
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
viewContainer.remove(index);
|
viewContainer.remove(index);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return new ComponentRef(newLocation, component, _asType(typeOrBinding), null, dispose);
|
return new ComponentRef(newLocation, component, type, null, dispose);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Type, CONST_EXPR, isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
||||||
import {
|
import {
|
||||||
RenderTemplateCmd,
|
RenderTemplateCmd,
|
||||||
RenderCommandVisitor,
|
RenderCommandVisitor,
|
||||||
|
@ -9,47 +9,32 @@ import {
|
||||||
RenderEmbeddedTemplateCmd
|
RenderEmbeddedTemplateCmd
|
||||||
} from 'angular2/src/core/render/render';
|
} from 'angular2/src/core/render/render';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compiled template. This is const as we are storing it as annotation
|
||||||
|
* for the compiled component type.
|
||||||
|
*/
|
||||||
|
@CONST()
|
||||||
export class CompiledTemplate {
|
export class CompiledTemplate {
|
||||||
private _changeDetectorFactory: Function = null;
|
|
||||||
private _styles: string[] = null;
|
|
||||||
private _commands: TemplateCmd[] = null;
|
|
||||||
// Note: paramGetter is a function so that we can have cycles between templates!
|
// Note: paramGetter is a function so that we can have cycles between templates!
|
||||||
constructor(public id: number, private _paramGetter: Function) {}
|
// paramGetter returns a tuple with:
|
||||||
|
// - ChangeDetector factory function
|
||||||
private _init() {
|
// - TemplateCmd[]
|
||||||
if (isBlank(this._commands)) {
|
// - styles
|
||||||
var params = this._paramGetter();
|
constructor(public id: number,
|
||||||
this._changeDetectorFactory = params[0];
|
public dataGetter: /*()=>[Function, TemplateCmd[], string[]]*/ Function) {}
|
||||||
this._commands = params[1];
|
|
||||||
this._styles = params[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get changeDetectorFactory(): Function {
|
|
||||||
this._init();
|
|
||||||
return this._changeDetectorFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
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([]);
|
||||||
|
|
||||||
export interface TemplateCmd extends RenderTemplateCmd {
|
export interface TemplateCmd extends RenderTemplateCmd {
|
||||||
visit(visitor: CommandVisitor, context: any): any;
|
visit(visitor: RenderCommandVisitor, context: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TextCmd implements TemplateCmd, RenderTextCmd {
|
export class TextCmd implements TemplateCmd, RenderTextCmd {
|
||||||
constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
|
constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
|
||||||
visit(visitor: CommandVisitor, context: any): any { return visitor.visitText(this, context); }
|
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||||
|
return visitor.visitText(this, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function text(value: string, isBound: boolean, ngContentIndex: number): TextCmd {
|
export function text(value: string, isBound: boolean, ngContentIndex: number): TextCmd {
|
||||||
|
@ -59,7 +44,7 @@ export function text(value: string, isBound: boolean, ngContentIndex: number): T
|
||||||
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
|
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
|
||||||
isBound: boolean = false;
|
isBound: boolean = false;
|
||||||
constructor(public ngContentIndex: number) {}
|
constructor(public ngContentIndex: number) {}
|
||||||
visit(visitor: CommandVisitor, context: any): any {
|
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||||
return visitor.visitNgContent(this, context);
|
return visitor.visitNgContent(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +57,7 @@ export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd {
|
||||||
variableNameAndValues: Array<string | number>;
|
variableNameAndValues: Array<string | number>;
|
||||||
eventTargetAndNames: string[];
|
eventTargetAndNames: string[];
|
||||||
directives: Type[];
|
directives: Type[];
|
||||||
visit(visitor: CommandVisitor, context: any): any;
|
visit(visitor: RenderCommandVisitor, context: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
|
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
|
||||||
|
@ -80,7 +65,7 @@ export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeg
|
||||||
public eventTargetAndNames: string[],
|
public eventTargetAndNames: string[],
|
||||||
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
||||||
public isBound: boolean, public ngContentIndex: number) {}
|
public isBound: boolean, public ngContentIndex: number) {}
|
||||||
visit(visitor: CommandVisitor, context: any): any {
|
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||||
return visitor.visitBeginElement(this, context);
|
return visitor.visitBeginElement(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +79,9 @@ export function beginElement(name: string, attrNameAndValues: string[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EndElementCmd implements TemplateCmd {
|
export class EndElementCmd implements TemplateCmd {
|
||||||
visit(visitor: CommandVisitor, context: any): any { return visitor.visitEndElement(context); }
|
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||||
|
return visitor.visitEndElement(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function endElement(): TemplateCmd {
|
export function endElement(): TemplateCmd {
|
||||||
|
@ -113,7 +100,7 @@ export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderB
|
||||||
this.component = directives[0];
|
this.component = directives[0];
|
||||||
this.templateId = template.id;
|
this.templateId = template.id;
|
||||||
}
|
}
|
||||||
visit(visitor: CommandVisitor, context: any): any {
|
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||||
return visitor.visitBeginComponent(this, context);
|
return visitor.visitBeginComponent(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +114,9 @@ export function beginComponent(
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EndComponentCmd implements TemplateCmd {
|
export class EndComponentCmd implements TemplateCmd {
|
||||||
visit(visitor: CommandVisitor, context: any): any { return visitor.visitEndComponent(context); }
|
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||||
|
return visitor.visitEndComponent(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function endComponent(): TemplateCmd {
|
export function endComponent(): TemplateCmd {
|
||||||
|
@ -142,7 +131,7 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
|
||||||
constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
|
constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
|
||||||
public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
|
public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
|
||||||
public changeDetectorFactory: Function, public children: TemplateCmd[]) {}
|
public changeDetectorFactory: Function, public children: TemplateCmd[]) {}
|
||||||
visit(visitor: CommandVisitor, context: any): any {
|
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||||
return visitor.visitEmbeddedTemplate(this, context);
|
return visitor.visitEmbeddedTemplate(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,7 +144,15 @@ export function embeddedTemplate(attrNameAndValues: string[], variableNameAndVal
|
||||||
ngContentIndex, changeDetectorFactory, children);
|
ngContentIndex, changeDetectorFactory, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandVisitor extends RenderCommandVisitor {}
|
export interface CommandVisitor extends RenderCommandVisitor {
|
||||||
|
visitText(cmd: TextCmd, context: any): any;
|
||||||
|
visitNgContent(cmd: NgContentCmd, context: any): any;
|
||||||
|
visitBeginElement(cmd: BeginElementCmd, context: any): any;
|
||||||
|
visitEndElement(context: any): any;
|
||||||
|
visitBeginComponent(cmd: BeginComponentCmd, context: any): any;
|
||||||
|
visitEndComponent(context: any): any;
|
||||||
|
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
|
export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
|
||||||
context: any = null) {
|
context: any = null) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ class NoReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
|
|
||||||
String importUri(Type type) => './';
|
String importUri(Type type) => './';
|
||||||
|
|
||||||
String moduleId(Type type) => null;
|
String moduleId(Type type) => './';
|
||||||
}
|
}
|
||||||
|
|
||||||
final Reflector reflector = new Reflector(new NoReflectionCapabilities());
|
final Reflector reflector = new Reflector(new NoReflectionCapabilities());
|
||||||
|
|
|
@ -324,6 +324,9 @@ class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
String moduleId(Type type) {
|
String moduleId(Type type) {
|
||||||
return '${MirrorSystem.getName((reflectClass(type).owner as LibraryMirror).qualifiedName).replaceAll(DOT_REGEX, "/")}';
|
var rootUri = currentMirrorSystem().isolate.rootLibrary.uri;
|
||||||
|
var moduleUri = (reflectClass(type).owner as LibraryMirror).uri;
|
||||||
|
var relativeUri = new Uri(pathSegments:moduleUri.pathSegments.sublist(rootUri.pathSegments.length-1)).toString();
|
||||||
|
return relativeUri.substring(0, relativeUri.lastIndexOf('.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,5 +169,5 @@ 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; }
|
moduleId(type: Type): string { return './'; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -401,7 +401,7 @@ export interface RenderBeginCmd extends RenderTemplateCmd {
|
||||||
|
|
||||||
export interface RenderTextCmd extends RenderBeginCmd { value: string; }
|
export interface RenderTextCmd extends RenderBeginCmd { value: string; }
|
||||||
|
|
||||||
export interface RenderNgContentCmd extends RenderBeginCmd { ngContentIndex: number; }
|
export interface RenderNgContentCmd { ngContentIndex: number; }
|
||||||
|
|
||||||
export interface RenderBeginElementCmd extends RenderBeginCmd {
|
export interface RenderBeginElementCmd extends RenderBeginCmd {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -420,13 +420,13 @@ export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RenderCommandVisitor {
|
export interface RenderCommandVisitor {
|
||||||
visitText(cmd: any, context: any): any;
|
visitText(cmd: RenderTextCmd, context: any): any;
|
||||||
visitNgContent(cmd: any, context: any): any;
|
visitNgContent(cmd: RenderNgContentCmd, context: any): any;
|
||||||
visitBeginElement(cmd: any, context: any): any;
|
visitBeginElement(cmd: RenderBeginElementCmd, context: any): any;
|
||||||
visitEndElement(context: any): any;
|
visitEndElement(context: any): any;
|
||||||
visitBeginComponent(cmd: any, context: any): any;
|
visitBeginComponent(cmd: RenderBeginComponentCmd, context: any): any;
|
||||||
visitEndComponent(context: any): any;
|
visitEndComponent(context: any): any;
|
||||||
visitEmbeddedTemplate(cmd: any, context: any): any;
|
visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ import {
|
||||||
} from 'angular2/src/core/render/dom/schema/dom_element_schema_registry';
|
} from 'angular2/src/core/render/dom/schema/dom_element_schema_registry';
|
||||||
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||||
import {Log} from './utils';
|
import {Log} from './utils';
|
||||||
|
import {compilerBindings} from 'angular2/src/compiler/compiler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the root injector bindings.
|
* Returns the root injector bindings.
|
||||||
|
@ -107,8 +108,8 @@ function _getAppBindings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
bind(DOCUMENT)
|
compilerBindings(),
|
||||||
.toValue(appDoc),
|
bind(DOCUMENT).toValue(appDoc),
|
||||||
DomRenderer,
|
DomRenderer,
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
bind(APP_ID).toValue('a'),
|
bind(APP_ID).toValue('a'),
|
||||||
|
|
|
@ -171,7 +171,7 @@ export function main() {
|
||||||
'div',
|
'div',
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
[null, 'click', 'window', 'scroll'],
|
[null, 'click', 'window', 'scroll'],
|
||||||
['someVar', '%implicit'],
|
['someVar', null],
|
||||||
[],
|
[],
|
||||||
true,
|
true,
|
||||||
null
|
null
|
||||||
|
@ -329,29 +329,48 @@ export function main() {
|
||||||
|
|
||||||
describe('embedded templates', () => {
|
describe('embedded templates', () => {
|
||||||
it('should create embedded template commands', inject([AsyncTestCompleter], (async) => {
|
it('should create embedded template commands', inject([AsyncTestCompleter], (async) => {
|
||||||
var rootComp = createComp({
|
var rootComp =
|
||||||
type: RootCompTypeMeta,
|
createComp({type: RootCompTypeMeta, template: '<template a="b"></template>'});
|
||||||
template: '<template a="b" #some-var="someValue"></template>'
|
|
||||||
});
|
|
||||||
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||||
run(rootComp, [dir], 1)
|
run(rootComp, [dir], 1)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
[
|
[EMBEDDED_TEMPLATE, ['a', 'b'], [], ['SomeDirType'], false, null, 'cd1', []]
|
||||||
EMBEDDED_TEMPLATE,
|
|
||||||
['a', 'b'],
|
|
||||||
['someVar', 'someValue'],
|
|
||||||
['SomeDirType'],
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
'cd1',
|
|
||||||
[]
|
|
||||||
]
|
|
||||||
]);
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should keep variable name and value for <template> elements',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var rootComp = createComp({
|
||||||
|
type: RootCompTypeMeta,
|
||||||
|
template: '<template #some-var="someValue" #some-empty-var></template>'
|
||||||
|
});
|
||||||
|
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||||
|
run(rootComp, [dir], 1)
|
||||||
|
.then((data) => {
|
||||||
|
expect(data[0][2])
|
||||||
|
.toEqual(['someEmptyVar', '$implicit', 'someVar', 'someValue']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should keep variable name and value for template attributes',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var rootComp = createComp({
|
||||||
|
type: RootCompTypeMeta,
|
||||||
|
template: '<div template="var someVar=someValue; var someEmptyVar"></div>'
|
||||||
|
});
|
||||||
|
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||||
|
run(rootComp, [dir], 1)
|
||||||
|
.then((data) => {
|
||||||
|
expect(data[0][2])
|
||||||
|
.toEqual(['someVar', 'someValue', 'someEmptyVar', '$implicit']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should created nested nodes', inject([AsyncTestCompleter], (async) => {
|
it('should created nested nodes', inject([AsyncTestCompleter], (async) => {
|
||||||
var rootComp =
|
var rootComp =
|
||||||
createComp({type: RootCompTypeMeta, template: '<template>t</template>'});
|
createComp({type: RootCompTypeMeta, template: '<template>t</template>'});
|
||||||
|
|
|
@ -66,7 +66,8 @@ export function main() {
|
||||||
|
|
||||||
it('should use the moduleId from the reflector if none is given',
|
it('should use the moduleId from the reflector if none is given',
|
||||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
var expectedValue = IS_DART ? 'angular2/test/compiler/runtime_metadata_spec' : null;
|
var expectedValue =
|
||||||
|
IS_DART ? 'base/dist/dart/angular2/test/compiler/runtime_metadata_spec' : './';
|
||||||
expect(resolver.getMetadata(DirectiveWithoutModuleId).type.moduleId)
|
expect(resolver.getMetadata(DirectiveWithoutModuleId).type.moduleId)
|
||||||
.toEqual(expectedValue);
|
.toEqual(expectedValue);
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -42,6 +42,8 @@ const IMPORT_ABS_MODULE_NAME_WITH_IMPORT =
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('StyleCompiler', () => {
|
describe('StyleCompiler', () => {
|
||||||
var xhr: SpyXHR;
|
var xhr: SpyXHR;
|
||||||
|
var typeMeta;
|
||||||
|
|
||||||
beforeEachBindings(() => {
|
beforeEachBindings(() => {
|
||||||
xhr = <any>new SpyXHR();
|
xhr = <any>new SpyXHR();
|
||||||
return [TEST_BINDINGS, bind(XHR).toValue(xhr)];
|
return [TEST_BINDINGS, bind(XHR).toValue(xhr)];
|
||||||
|
@ -49,16 +51,10 @@ export function main() {
|
||||||
|
|
||||||
var compiler: StyleCompiler;
|
var compiler: StyleCompiler;
|
||||||
|
|
||||||
beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
|
beforeEach(inject([StyleCompiler], (_compiler) => {
|
||||||
|
typeMeta = new CompileTypeMetadata({id: 23, moduleId: 'someUrl'});
|
||||||
function comp(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
compiler = _compiler;
|
||||||
CompileDirectiveMetadata {
|
}));
|
||||||
return CompileDirectiveMetadata.create({
|
|
||||||
type: new CompileTypeMetadata({id: 23, moduleId: 'someUrl'}),
|
|
||||||
template: new CompileTemplateMetadata(
|
|
||||||
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('compileComponentRuntime', () => {
|
describe('compileComponentRuntime', () => {
|
||||||
var xhrUrlResults;
|
var xhrUrlResults;
|
||||||
|
@ -84,7 +80,9 @@ export function main() {
|
||||||
}
|
}
|
||||||
return PromiseWrapper.resolve(response);
|
return PromiseWrapper.resolve(response);
|
||||||
});
|
});
|
||||||
return compiler.compileComponentRuntime(comp(styles, styleAbsUrls, encapsulation));
|
return compiler.compileComponentRuntime(
|
||||||
|
typeMeta, new CompileTemplateMetadata(
|
||||||
|
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('no shim', () => {
|
describe('no shim', () => {
|
||||||
|
@ -199,8 +197,9 @@ export function main() {
|
||||||
describe('compileComponentCodeGen', () => {
|
describe('compileComponentCodeGen', () => {
|
||||||
function compile(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
function compile(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
||||||
Promise<string[]> {
|
Promise<string[]> {
|
||||||
var sourceExpression =
|
var sourceExpression = compiler.compileComponentCodeGen(
|
||||||
compiler.compileComponentCodeGen(comp(styles, styleAbsUrls, encapsulation));
|
typeMeta, new CompileTemplateMetadata(
|
||||||
|
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
|
||||||
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
|
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
|
||||||
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
|
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,6 +66,15 @@ export function main() {
|
||||||
describe('compile templates', () => {
|
describe('compile templates', () => {
|
||||||
|
|
||||||
function runTests(compile) {
|
function runTests(compile) {
|
||||||
|
it('should throw for non components', inject([AsyncTestCompleter], (async) => {
|
||||||
|
PromiseWrapper.catchError(PromiseWrapper.wrap(() => compile([NonComponent])), (error) => {
|
||||||
|
expect(error.message)
|
||||||
|
.toEqual(
|
||||||
|
`Could not compile '${stringify(NonComponent)}' because it is not a component.`);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should compile host components', inject([AsyncTestCompleter], (async) => {
|
it('should compile host components', inject([AsyncTestCompleter], (async) => {
|
||||||
compile([CompWithBindingsAndStyles])
|
compile([CompWithBindingsAndStyles])
|
||||||
.then((humanizedTemplate) => {
|
.then((humanizedTemplate) => {
|
||||||
|
@ -202,25 +211,6 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('serializeDirectiveMetadata and deserializeDirectiveMetadata', () => {
|
|
||||||
it('should serialize and deserialize', inject([AsyncTestCompleter], (async) => {
|
|
||||||
compiler.normalizeDirectiveMetadata(
|
|
||||||
runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles))
|
|
||||||
.then((meta: CompileDirectiveMetadata) => {
|
|
||||||
var json = compiler.serializeDirectiveMetadata(meta);
|
|
||||||
expect(isString(json)).toBe(true);
|
|
||||||
// Note: serializing will clear our the runtime type!
|
|
||||||
var clonedMeta = compiler.deserializeDirectiveMetadata(json);
|
|
||||||
expect(meta.changeDetection).toEqual(clonedMeta.changeDetection);
|
|
||||||
expect(meta.template).toEqual(clonedMeta.template);
|
|
||||||
expect(meta.selector).toEqual(clonedMeta.selector);
|
|
||||||
expect(meta.exportAs).toEqual(clonedMeta.exportAs);
|
|
||||||
expect(meta.type.name).toEqual(clonedMeta.type.name);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('normalizeDirectiveMetadata', () => {
|
describe('normalizeDirectiveMetadata', () => {
|
||||||
it('should normalize the template',
|
it('should normalize the template',
|
||||||
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||||
|
@ -297,7 +287,8 @@ class CompWithEmbeddedTemplate {
|
||||||
|
|
||||||
|
|
||||||
@Directive({selector: 'plain', moduleId: THIS_MODULE})
|
@Directive({selector: 'plain', moduleId: THIS_MODULE})
|
||||||
class PlainDirective {
|
@View({template: ''})
|
||||||
|
class NonComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'comp', moduleId: THIS_MODULE})
|
@Component({selector: 'comp', moduleId: THIS_MODULE})
|
||||||
|
@ -331,13 +322,11 @@ export function humanizeTemplate(template: CompiledTemplate,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
var commands = [];
|
var commands = [];
|
||||||
result = {
|
var templateData = template.dataGetter();
|
||||||
'styles': template.styles,
|
result =
|
||||||
'commands': commands,
|
{'styles': templateData[2], 'commands': commands, 'cd': testChangeDetector(templateData[0])};
|
||||||
'cd': testChangeDetector(template.changeDetectorFactory)
|
|
||||||
};
|
|
||||||
humanizedTemplates.set(template.id, result);
|
humanizedTemplates.set(template.id, result);
|
||||||
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands);
|
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), templateData[1]);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,14 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw if no template was specified',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
expect(() => normalizer.normalizeTemplate(
|
||||||
|
dirType, new CompileTemplateMetadata(
|
||||||
|
{encapsulation: null, styles: [], styleUrls: []})))
|
||||||
|
.toThrowError('No template specified for component SomeComp');
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('normalizeLoadedTemplate', () => {
|
describe('normalizeLoadedTemplate', () => {
|
||||||
|
@ -274,16 +282,23 @@ export function main() {
|
||||||
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should ignore elements with ng-non-bindable attribute and their children',
|
it('should ignore ng-content in elements with ng-non-bindable',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
dirType,
|
||||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
'<div ng-non-bindable><ng-content select="a"></ng-content></div><ng-content ng-non-bindable select="b"></ng-content>',
|
'<div ng-non-bindable><ng-content select="a"></ng-content></div>', 'some/module/');
|
||||||
'some/module/');
|
|
||||||
expect(template.ngContentSelectors).toEqual([]);
|
expect(template.ngContentSelectors).toEqual([]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should still collect <style> in elements with ng-non-bindable',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<div ng-non-bindable><style>div {color:red}</style></div>', 'some/module/');
|
||||||
|
expect(template.styles).toEqual(['div {color:red}']);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -464,6 +464,23 @@ export function main() {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should favor explicit bound properties over literal properties', () => {
|
||||||
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
|
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), properties: ['a']});
|
||||||
|
expect(humanizeTemplateAsts(parse('<div a="literal" [a]="\'literal2\'"></div>', [dirA])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[AttrAst, 'a', 'literal', 'TestComp > div:nth-child(0)[a=literal]'],
|
||||||
|
[DirectiveAst, dirA, 'TestComp > div:nth-child(0)'],
|
||||||
|
[
|
||||||
|
BoundDirectivePropertyAst,
|
||||||
|
'a',
|
||||||
|
'"literal2"',
|
||||||
|
'TestComp > div:nth-child(0)[[a]=\'literal2\']'
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should support optional directive properties', () => {
|
it('should support optional directive properties', () => {
|
||||||
var dirA = CompileDirectiveMetadata.create(
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), properties: ['a']});
|
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), properties: ['a']});
|
||||||
|
@ -502,7 +519,7 @@ export function main() {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should assign variables with empty value to element', () => {
|
it('should assign variables with empty value to the element', () => {
|
||||||
expect(humanizeTemplateAsts(parse('<div #a></div>', [])))
|
expect(humanizeTemplateAsts(parse('<div #a></div>', [])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
@ -743,6 +760,11 @@ There is no directive with "exportAs" set to "dirA" at TestComp > div:nth-child(
|
||||||
.toEqual([['a', null], ['b', 0], ['#text(hello)', 0]]);
|
.toEqual([['a', null], ['b', 0], ['#text(hello)', 0]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should project children of components with ng-non-bindable', () => {
|
||||||
|
expect(humanizeContentProjection(parse('<div ng-non-bindable>{{hello}}<span></span></div>',
|
||||||
|
[createComp('div', ['*'])])))
|
||||||
|
.toEqual([['div', null], ['#text({{hello}})', 0], ['span', 0]]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('splitClasses', () => {
|
describe('splitClasses', () => {
|
||||||
|
@ -838,11 +860,74 @@ Property binding a not used by any directive on an embedded template in TestComp
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore elements with ng-non-bindable, including their children, but include them for source info',
|
it('should ignore bindings on children of elements with ng-non-bindable', () => {
|
||||||
() => {
|
expect(humanizeTemplateAsts(parse('<div ng-non-bindable>{{b}}</div>', [])))
|
||||||
expect(humanizeTemplateAsts(parse('<div ng-non-bindable>b</div>a', [])))
|
.toEqual([
|
||||||
.toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]);
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[AttrAst, 'ng-non-bindable', '', 'TestComp > div:nth-child(0)[ng-non-bindable=]'],
|
||||||
|
[TextAst, '{{b}}', 'TestComp > div:nth-child(0) > #text({{b}}):nth-child(0)']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep nested children of elements with ng-non-bindable', () => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<div ng-non-bindable><span>{{b}}</span></div>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[AttrAst, 'ng-non-bindable', '', 'TestComp > div:nth-child(0)[ng-non-bindable=]'],
|
||||||
|
[ElementAst, 'span', 'TestComp > div:nth-child(0) > span:nth-child(0)'],
|
||||||
|
[
|
||||||
|
TextAst,
|
||||||
|
'{{b}}',
|
||||||
|
'TestComp > div:nth-child(0) > span:nth-child(0) > #text({{b}}):nth-child(0)'
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore <script> elements inside of elements with ng-non-bindable but include them for source info',
|
||||||
|
() => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<div ng-non-bindable><script></script>a</div>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[AttrAst, 'ng-non-bindable', '', 'TestComp > div:nth-child(0)[ng-non-bindable=]'],
|
||||||
|
[TextAst, 'a', 'TestComp > div:nth-child(0) > #text(a):nth-child(1)']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore <style> elements inside of elements with ng-non-bindable but include them for source info',
|
||||||
|
() => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<div ng-non-bindable><style></style>a</div>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[AttrAst, 'ng-non-bindable', '', 'TestComp > div:nth-child(0)[ng-non-bindable=]'],
|
||||||
|
[TextAst, 'a', 'TestComp > div:nth-child(0) > #text(a):nth-child(1)']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore <link rel="stylesheet"> elements inside of elements with ng-non-bindable but include them for source info',
|
||||||
|
() => {
|
||||||
|
expect(humanizeTemplateAsts(
|
||||||
|
parse('<div ng-non-bindable><link rel="stylesheet"></link>a</div>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[AttrAst, 'ng-non-bindable', '', 'TestComp > div:nth-child(0)[ng-non-bindable=]'],
|
||||||
|
[TextAst, 'a', 'TestComp > div:nth-child(0) > #text(a):nth-child(1)']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert <ng-content> elements into regular elements inside of elements with ng-non-bindable but include them for source info',
|
||||||
|
() => {
|
||||||
|
expect(humanizeTemplateAsts(
|
||||||
|
parse('<div ng-non-bindable><ng-content></ng-content>a</div>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[AttrAst, 'ng-non-bindable', '', 'TestComp > div:nth-child(0)[ng-non-bindable=]'],
|
||||||
|
[
|
||||||
|
ElementAst,
|
||||||
|
'ng-content',
|
||||||
|
'TestComp > div:nth-child(0) > ng-content:nth-child(0)'
|
||||||
|
],
|
||||||
|
[TextAst, 'a', 'TestComp > div:nth-child(0) > #text(a):nth-child(1)']
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import {
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
xdescribe,
|
||||||
|
it,
|
||||||
|
iit,
|
||||||
|
xit,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
AsyncTestCompleter,
|
||||||
|
inject,
|
||||||
|
beforeEachBindings
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
||||||
|
import {
|
||||||
|
preparseElement,
|
||||||
|
PreparsedElementType,
|
||||||
|
PreparsedElement
|
||||||
|
} from 'angular2/src/compiler/template_preparser';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('preparseElement', () => {
|
||||||
|
var htmlParser;
|
||||||
|
beforeEach(inject([HtmlParser], (_htmlParser: HtmlParser) => { htmlParser = _htmlParser; }));
|
||||||
|
|
||||||
|
function preparse(html: string): PreparsedElement {
|
||||||
|
return preparseElement(htmlParser.parse(html, '')[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should detect script elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
|
||||||
|
expect(preparse('<script>').type).toBe(PreparsedElementType.SCRIPT);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should detect style elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
|
||||||
|
expect(preparse('<style>').type).toBe(PreparsedElementType.STYLE);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should detect stylesheet elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
|
||||||
|
expect(preparse('<link rel="stylesheet">').type).toBe(PreparsedElementType.STYLESHEET);
|
||||||
|
expect(preparse('<link rel="stylesheet" href="someUrl">').hrefAttr).toEqual('someUrl');
|
||||||
|
expect(preparse('<link rel="someRel">').type).toBe(PreparsedElementType.OTHER);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should detect ng-content elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
|
||||||
|
expect(preparse('<ng-content>').type).toBe(PreparsedElementType.NG_CONTENT);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should normalize ng-content.select attribute',
|
||||||
|
inject([HtmlParser], (htmlParser: HtmlParser) => {
|
||||||
|
expect(preparse('<ng-content>').selectAttr).toEqual('*');
|
||||||
|
expect(preparse('<ng-content select>').selectAttr).toEqual('*');
|
||||||
|
expect(preparse('<ng-content select="*">').selectAttr).toEqual('*');
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,26 +1,5 @@
|
||||||
import {bind, Binding} from 'angular2/src/core/di';
|
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 {MockSchemaRegistry} from './schema_registry_mock';
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
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 = [bind(ElementSchemaRegistry).toValue(new MockSchemaRegistry({}, {}))];
|
||||||
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({}, {}))
|
|
||||||
];
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
TestComponentBuilder
|
TestComponentBuilder
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {IS_DART} from '../platform';
|
||||||
import {escapeSingleQuoteString, escapeDoubleQuoteString} from 'angular2/src/compiler/util';
|
import {escapeSingleQuoteString, escapeDoubleQuoteString} from 'angular2/src/compiler/util';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -25,6 +26,12 @@ export function main() {
|
||||||
|
|
||||||
it('should escape newlines',
|
it('should escape newlines',
|
||||||
() => { expect(escapeSingleQuoteString('\n')).toEqual(`'\\n'`); });
|
() => { expect(escapeSingleQuoteString('\n')).toEqual(`'\\n'`); });
|
||||||
|
|
||||||
|
if (IS_DART) {
|
||||||
|
it('should escape $', () => { expect(escapeSingleQuoteString('$')).toEqual(`'\\$'`); });
|
||||||
|
} else {
|
||||||
|
it('should not escape $', () => { expect(escapeSingleQuoteString('$')).toEqual(`'$'`); });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('escapeDoubleQuoteString', () => {
|
describe('escapeDoubleQuoteString', () => {
|
||||||
|
@ -36,6 +43,12 @@ export function main() {
|
||||||
|
|
||||||
it('should escape newlines',
|
it('should escape newlines',
|
||||||
() => { expect(escapeDoubleQuoteString('\n')).toEqual(`"\\n"`); });
|
() => { expect(escapeDoubleQuoteString('\n')).toEqual(`"\\n"`); });
|
||||||
|
|
||||||
|
if (IS_DART) {
|
||||||
|
it('should escape $', () => { expect(escapeDoubleQuoteString('$')).toEqual(`"\\$"`); });
|
||||||
|
} else {
|
||||||
|
it('should not escape $', () => { expect(escapeDoubleQuoteString('$')).toEqual(`"$"`); });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -384,11 +384,7 @@ export function main() {
|
||||||
tcb.overrideView(
|
tcb.overrideView(
|
||||||
MyComp, new ViewMetadata({
|
MyComp, new ViewMetadata({
|
||||||
template: '<p no-duplicate></p>',
|
template: '<p no-duplicate></p>',
|
||||||
directives: [
|
directives: [DuplicateDir, DuplicateDir, [DuplicateDir, [DuplicateDir]]]
|
||||||
DuplicateDir,
|
|
||||||
DuplicateDir,
|
|
||||||
[DuplicateDir, [DuplicateDir, bind(DuplicateDir).toClass(DuplicateDir)]]
|
|
||||||
]
|
|
||||||
}))
|
}))
|
||||||
.createAsync(MyComp)
|
.createAsync(MyComp)
|
||||||
.then((rootTC) => {
|
.then((rootTC) => {
|
||||||
|
|
|
@ -251,7 +251,7 @@ export function main() {
|
||||||
describe("moduleId", () => {
|
describe("moduleId", () => {
|
||||||
it("should return the moduleId for a type", () => {
|
it("should return the moduleId for a type", () => {
|
||||||
expect(reflector.moduleId(TestObjWith00Args))
|
expect(reflector.moduleId(TestObjWith00Args))
|
||||||
.toEqual('angular2/test/core/reflection/reflector_spec');
|
.toEqual('base/dist/dart/angular2/test/core/reflection/reflector_spec');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return an empty array otherwise", () => {
|
it("should return an empty array otherwise", () => {
|
||||||
|
|
Loading…
Reference in New Issue