refactor(render): user render compiler

This commit is contained in:
Tobias Bosch
2015-04-02 14:40:49 -07:00
parent 069bbf3ed0
commit 1d4d18d9db
72 changed files with 1052 additions and 4739 deletions

View File

@ -19,8 +19,6 @@ export class ElementBinder {
index:number;
parentIndex:number;
distanceToParent:number;
parentWithDirectivesIndex:number;
distanceToParentWithDirectives:number;
directives:List<DirectiveBinder>;
nestedProtoView:ProtoView;
propertyBindings: Map<string, ASTWithSource>;
@ -30,24 +28,25 @@ export class ElementBinder {
// with a local name
eventBindings: Map<string, ASTWithSource>;
textBindings: List<ASTWithSource>;
readAttributes: Map<string, string>;
constructor({
index, parentIndex, distanceToParent, parentWithDirectivesIndex,
distanceToParentWithDirectives, directives, nestedProtoView,
index, parentIndex, distanceToParent,
directives, nestedProtoView,
propertyBindings, variableBindings,
eventBindings, textBindings
eventBindings, textBindings,
readAttributes
}) {
this.index = index;
this.parentIndex = parentIndex;
this.distanceToParent = distanceToParent;
this.parentWithDirectivesIndex = parentWithDirectivesIndex;
this.distanceToParentWithDirectives = distanceToParentWithDirectives;
this.directives = directives;
this.nestedProtoView = nestedProtoView;
this.propertyBindings = propertyBindings;
this.variableBindings = variableBindings;
this.eventBindings = eventBindings;
this.textBindings = textBindings;
this.readAttributes = readAttributes;
}
}
@ -73,7 +72,7 @@ export class ProtoView {
elementBinders:List<ElementBinder>;
variableBindings: Map<string, string>;
constructor({render, elementBinders, variableBindings}) {
constructor({render, elementBinders, variableBindings}={}) {
this.render = render;
this.elementBinders = elementBinders;
this.variableBindings = variableBindings;
@ -90,14 +89,16 @@ export class DirectiveMetadata {
events:Map<string, string>;
bind:Map<string, string>;
setters:List<string>;
readAttributes:List<string>;
type:number;
constructor({id, selector, compileChildren, events, bind, setters, type}) {
constructor({id, selector, compileChildren, events, bind, setters, readAttributes, type}) {
this.id = id;
this.selector = selector;
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
this.events = events;
this.bind = bind;
this.setters = setters;
this.readAttributes = readAttributes;
this.type = type;
}
}

View File

@ -3,9 +3,14 @@ import {BaseException} from 'angular2/src/facade/lang';
import {Template, ProtoView} from '../../api';
import {CompilePipeline} from './compile_pipeline';
import {TemplateLoader} from './template_loader';
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
import {CompileStepFactory} from './compile_step_factory';
/**
* The compiler loads and translates the html templates of components into
* nested ProtoViews. To decompose its functionality it uses
* the CompilePipeline and the CompileSteps.
*/
export class Compiler {
_templateLoader: TemplateLoader;
_stepFactory: CompileStepFactory;

View File

@ -9,8 +9,6 @@ import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {setterFactory} from 'angular2/src/render/dom/compiler/property_setter_factory';
import {DirectiveMetadata} from '../../api';
import {dashCaseToCamelCase, camelCaseToDashCase} from '../util';
@ -72,7 +70,12 @@ export class DirectiveParser extends CompileStep {
}
if (isPresent(directive.setters)) {
ListWrapper.forEach(directive.setters, (propertyName) => {
directiveBinder.bindPropertySetter(propertyName, setterFactory(propertyName));
elementBinder.bindPropertySetter(propertyName);
});
}
if (isPresent(directive.readAttributes)) {
ListWrapper.forEach(directive.readAttributes, (attrName) => {
elementBinder.readAttribute(attrName);
});
}
if (directive.type === DirectiveMetadata.VIEWPORT_TYPE) {

View File

@ -8,7 +8,6 @@ import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {dashCaseToCamelCase} from '../util';
import {setterFactory} from 'angular2/src/render/dom/compiler/property_setter_factory';
// Group 1 = "bind-"
// Group 2 = "var-" or "#"
@ -92,7 +91,6 @@ export class PropertyBindingParser extends CompileStep {
var binder = current.bindElement();
var camelCaseName = dashCaseToCamelCase(name);
binder.bindProperty(camelCaseName, ast);
binder.bindPropertySetter(camelCaseName, setterFactory(camelCaseName));
MapWrapper.set(newAttrs, name, ast.source);
}

View File

@ -1,3 +1,4 @@
import {Injectable} from 'angular2/di';
import {isBlank, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
import {Map, MapWrapper, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
@ -12,6 +13,7 @@ import {UrlResolver} from 'angular2/src/services/url_resolver';
* Strategy to load component templates.
* @publicModule angular2/angular2
*/
@Injectable()
export class TemplateLoader {
_xhr: XHR;
_htmlCache: StringMap;

View File

@ -7,6 +7,8 @@ import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control';
import {dashCaseToCamelCase} from '../util';
/**
* Splits views at `<template>` elements or elements with `template` attribute:
* For `<template>` elements:
@ -105,10 +107,14 @@ export class ViewSplitter extends CompileStep {
for (var i=0; i<bindings.length; i++) {
var binding = bindings[i];
if (binding.keyIsVar) {
compileElement.bindElement().bindVariable(binding.key, binding.name);
compileElement.bindElement().bindVariable(
dashCaseToCamelCase(binding.key), binding.name
);
MapWrapper.set(compileElement.attrs(), binding.key, binding.name);
} else if (isPresent(binding.expression)) {
compileElement.bindElement().bindProperty(binding.key, binding.expression);
compileElement.bindElement().bindProperty(
dashCaseToCamelCase(binding.key), binding.expression
);
MapWrapper.set(compileElement.attrs(), binding.key, binding.expression.source);
} else {
DOM.setAttribute(compileElement.element, binding.key, '');

View File

@ -26,7 +26,7 @@ export class ShadowDomCompileStep extends CompileStep {
if (current.ignoreBindings) {
return;
}
var tagName = DOM.tagName(current.element);
var tagName = DOM.tagName(current.element).toUpperCase();
if (tagName == 'STYLE') {
this._processStyleElement(current);
} else if (tagName == 'CONTENT') {

View File

@ -1,4 +1,5 @@
import {AST} from 'angular2/change_detection';
import {SetterFn} from 'angular2/src/reflection/types';
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import * as protoViewModule from './proto_view';
@ -15,6 +16,7 @@ export class ElementBinder {
componentId: string;
parentIndex:number;
distanceToParent:number;
propertySetters: Map<string, SetterFn>;
constructor({
textNodeIndices,
@ -24,7 +26,8 @@ export class ElementBinder {
eventLocals,
eventNames,
parentIndex,
distanceToParent
distanceToParent,
propertySetters
}) {
this.textNodeIndices = textNodeIndices;
this.contentTagSelector = contentTagSelector;
@ -34,6 +37,7 @@ export class ElementBinder {
this.eventNames = eventNames;
this.parentIndex = parentIndex;
this.distanceToParent = distanceToParent;
this.propertySetters = propertySetters;
}
mergeChildComponentProtoViews(protoViews:List<protoViewModule.ProtoView>, target:List<protoViewModule.ProtoView>):ElementBinder {
@ -45,15 +49,14 @@ export class ElementBinder {
}
return new ElementBinder({
parentIndex: this.parentIndex,
// Don't clone as we assume immutability!
textNodeIndices: this.textNodeIndices,
contentTagSelector: this.contentTagSelector,
nestedProtoView: nestedProtoView,
componentId: this.componentId,
// Don't clone as we assume immutability!
eventLocals: this.eventLocals,
eventNames: this.eventNames,
distanceToParent: this.distanceToParent
distanceToParent: this.distanceToParent,
propertySetters: this.propertySetters
});
}
}

View File

@ -1,6 +1,5 @@
import {isPresent} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {SetterFn} from 'angular2/src/reflection/types';
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
@ -16,20 +15,17 @@ export class ProtoView {
isTemplateElement:boolean;
isRootView:boolean;
rootBindingOffset:int;
propertySetters: Map<string, SetterFn>;
constructor({
elementBinders,
element,
isRootView,
propertySetters
isRootView
}) {
this.element = element;
this.elementBinders = elementBinders;
this.isTemplateElement = DOM.isTemplateElement(this.element);
this.isRootView = isRootView;
this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0;
this.propertySetters = propertySetters;
}
mergeChildComponentProtoViews(protoViews:List<ProtoView>, target:List<ProtoView>):ProtoView {
@ -45,9 +41,7 @@ export class ProtoView {
var result = new ProtoView({
elementBinders: elementBinders,
element: this.element,
isRootView: this.isRootView,
// Don't clone as we assume immutability!
propertySetters: this.propertySetters
isRootView: this.isRootView
});
ListWrapper.insert(target, 0, result);
return result

View File

@ -1,4 +1,4 @@
import {isPresent, BaseException} from 'angular2/src/facade/lang';
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper, Set, SetWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/dom/dom_adapter';
@ -9,6 +9,7 @@ import {SetterFn} from 'angular2/src/reflection/types';
import {ProtoView} from './proto_view';
import {ElementBinder} from './element_binder';
import {setterFactory} from './property_setter_factory';
import * as api from '../../api';
import * as directDomRenderer from '../direct_dom_renderer';
@ -20,14 +21,12 @@ export class ProtoViewBuilder {
variableBindings: Map<string, string>;
elements:List<ElementBinderBuilder>;
isRootView:boolean;
propertySetters:Set<string>;
constructor(rootElement) {
this.rootElement = rootElement;
this.elements = [];
this.isRootView = false;
this.variableBindings = MapWrapper.create();
this.propertySetters = new Set();
}
bindElement(element, description = null):ElementBinderBuilder {
@ -55,13 +54,10 @@ export class ProtoViewBuilder {
var renderElementBinders = [];
var apiElementBinders = [];
var propertySetters = MapWrapper.create();
ListWrapper.forEach(this.elements, (ebb) => {
var propertySetters = MapWrapper.create();
var eventLocalsAstSplitter = new EventLocalsAstSplitter();
var apiDirectiveBinders = ListWrapper.map(ebb.directives, (db) => {
MapWrapper.forEach(db.propertySetters, (setter, propertyName) => {
MapWrapper.set(propertySetters, propertyName, setter);
});
return new api.DirectiveBinder({
directiveIndex: db.directiveIndex,
propertyBindings: db.propertyBindings,
@ -74,15 +70,14 @@ export class ProtoViewBuilder {
var nestedProtoView =
isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build() : null;
var parentIndex = isPresent(ebb.parent) ? ebb.parent.index : -1;
var parentWithDirectivesIndex = isPresent(ebb.parentWithDirectives) ? ebb.parentWithDirectives.index : -1;
ListWrapper.push(apiElementBinders, new api.ElementBinder({
index: ebb.index, parentIndex:parentIndex, distanceToParent:ebb.distanceToParent,
parentWithDirectivesIndex: parentWithDirectivesIndex, distanceToParentWithDirectives: ebb.distanceToParentWithDirectives,
directives: apiDirectiveBinders,
nestedProtoView: nestedProtoView,
propertyBindings: ebb.propertyBindings, variableBindings: ebb.variableBindings,
eventBindings: eventLocalsAstSplitter.splitEventAstIntoLocals(ebb.eventBindings),
textBindings: ebb.textBindings
textBindings: ebb.textBindings,
readAttributes: ebb.readAttributes
}));
ListWrapper.push(renderElementBinders, new ElementBinder({
textNodeIndices: ebb.textBindingIndices,
@ -92,15 +87,15 @@ export class ProtoViewBuilder {
nestedProtoView: isPresent(nestedProtoView) ? nestedProtoView.render.delegate : null,
componentId: ebb.componentId,
eventLocals: eventLocalsAstSplitter.buildEventLocals(),
eventNames: eventLocalsAstSplitter.buildEventNames()
eventNames: eventLocalsAstSplitter.buildEventNames(),
propertySetters: propertySetters
}));
});
return new api.ProtoView({
render: new directDomRenderer.DirectDomProtoViewRef(new ProtoView({
element: this.rootElement,
elementBinders: renderElementBinders,
isRootView: this.isRootView,
propertySetters: propertySetters
isRootView: this.isRootView
})),
elementBinders: apiElementBinders,
variableBindings: this.variableBindings
@ -113,8 +108,6 @@ export class ElementBinderBuilder {
index:number;
parent:ElementBinderBuilder;
distanceToParent:number;
parentWithDirectives:ElementBinderBuilder;
distanceToParentWithDirectives:number;
directives:List<DirectiveBuilder>;
nestedProtoView:ProtoViewBuilder;
propertyBindings: Map<string, ASTWithSource>;
@ -124,6 +117,7 @@ export class ElementBinderBuilder {
textBindings: List<ASTWithSource>;
contentTagSelector:string;
propertySetters: Map<string, SetterFn>;
readAttributes: Map<string, string>;
componentId: string;
constructor(index, element, description) {
@ -131,8 +125,6 @@ export class ElementBinderBuilder {
this.index = index;
this.parent = null;
this.distanceToParent = 0;
this.parentWithDirectives = null;
this.distanceToParentWithDirectives = 0;
this.directives = [];
this.nestedProtoView = null;
this.propertyBindings = MapWrapper.create();
@ -143,25 +135,23 @@ export class ElementBinderBuilder {
this.contentTagSelector = null;
this.propertySetters = MapWrapper.create();
this.componentId = null;
this.readAttributes = MapWrapper.create();
}
setParent(parent:ElementBinderBuilder, distanceToParent):ElementBinderBuilder {
this.parent = parent;
if (isPresent(parent)) {
this.distanceToParent = distanceToParent;
if (parent.directives.length > 0) {
this.parentWithDirectives = parent;
this.distanceToParentWithDirectives = distanceToParent;
} else {
this.parentWithDirectives = parent.parentWithDirectives;
if (isPresent(this.parentWithDirectives)) {
this.distanceToParentWithDirectives = distanceToParent + parent.distanceToParentWithDirectives;
}
}
}
return this;
}
readAttribute(attrName:string) {
if (isBlank(MapWrapper.get(this.readAttributes, attrName))) {
MapWrapper.set(this.readAttributes, attrName, DOM.getAttribute(this.element, attrName));
}
}
bindDirective(directiveIndex:number):DirectiveBuilder {
var directive = new DirectiveBuilder(directiveIndex);
ListWrapper.push(this.directives, directive);
@ -178,6 +168,11 @@ export class ElementBinderBuilder {
bindProperty(name, expression) {
MapWrapper.set(this.propertyBindings, name, expression);
this.bindPropertySetter(name);
}
bindPropertySetter(name) {
MapWrapper.set(this.propertySetters, name, setterFactory(name));
}
bindVariable(name, value) {
@ -209,10 +204,6 @@ export class ElementBinderBuilder {
this.contentTagSelector = value;
}
bindPropertySetter(propertyName, setter) {
MapWrapper.set(this.propertySetters, propertyName, setter);
}
setComponentId(componentId:string) {
this.componentId = componentId;
}
@ -222,13 +213,11 @@ export class DirectiveBuilder {
directiveIndex:number;
propertyBindings: Map<string, ASTWithSource>;
eventBindings: Map<string, ASTWithSource>;
propertySetters: Map<string, SetterFn>;
constructor(directiveIndex) {
this.directiveIndex = directiveIndex;
this.propertyBindings = MapWrapper.create();
this.eventBindings = MapWrapper.create();
this.propertySetters = MapWrapper.create();
}
bindProperty(name, expression) {
@ -238,10 +227,6 @@ export class DirectiveBuilder {
bindEvent(name, expression) {
MapWrapper.set(this.eventBindings, name, expression);
}
bindPropertySetter(propertyName, setter) {
MapWrapper.set(this.propertySetters, propertyName, setter);
}
}
export class EventLocalsAstSplitter extends AstTransformer {
@ -257,15 +242,19 @@ export class EventLocalsAstSplitter extends AstTransformer {
}
splitEventAstIntoLocals(eventBindings:Map<string, ASTWithSource>):Map<string, ASTWithSource> {
if (isPresent(eventBindings)) {
var result = MapWrapper.create();
MapWrapper.forEach(eventBindings, (astWithSource, eventName) => {
MapWrapper.set(result, eventName, astWithSource.ast.visit(this));
ListWrapper.push(this.eventNames, eventName);
});
return result;
}
return null;
// TODO(tbosch): reenable this when we are using
// the render views
return eventBindings;
// if (isPresent(eventBindings)) {
// var result = MapWrapper.create();
// MapWrapper.forEach(eventBindings, (astWithSource, eventName) => {
// var adjustedAst = astWithSource.ast.visit(this);
// MapWrapper.set(result, eventName, new ASTWithSource(adjustedAst, astWithSource.source, ''));
// ListWrapper.push(this.eventNames, eventName);
// });
// return result;
// }
// return null;
}
visitAccessMember(ast:AccessMember) {

View File

@ -52,7 +52,7 @@ export class View {
}
setElementProperty(elementIndex:number, propertyName:string, value:any) {
var setter = MapWrapper.get(this.proto.propertySetters, propertyName);
var setter = MapWrapper.get(this.proto.elementBinders[elementIndex].propertySetters, propertyName);
setter(this.boundElements[elementIndex], value);
}