2014-12-02 13:21:39 -08:00
|
|
|
import {Type, FIELD, isBlank, isPresent, BaseException, stringify} from 'facade/lang';
|
2014-11-11 17:33:47 -08:00
|
|
|
import {Promise, PromiseWrapper} from 'facade/async';
|
2014-12-02 13:21:39 -08:00
|
|
|
import {List, ListWrapper, MapWrapper} from 'facade/collection';
|
2014-11-11 17:33:47 -08:00
|
|
|
import {DOM, Element} from 'facade/dom';
|
|
|
|
|
|
|
|
import {Parser} from 'change_detection/parser/parser';
|
|
|
|
|
2014-11-20 12:07:48 -08:00
|
|
|
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
2014-11-11 17:33:47 -08:00
|
|
|
import {ProtoView} from './view';
|
|
|
|
import {CompilePipeline} from './pipeline/compile_pipeline';
|
|
|
|
import {CompileElement} from './pipeline/compile_element';
|
|
|
|
import {createDefaultSteps} from './pipeline/default_steps';
|
2014-09-19 16:38:37 -07:00
|
|
|
import {TemplateLoader} from './template_loader';
|
2014-11-11 17:33:47 -08:00
|
|
|
import {AnnotatedType} from './annotated_type';
|
2014-11-21 21:19:23 -08:00
|
|
|
import {Component} from '../annotations/annotations';
|
2014-09-19 23:03:36 +00:00
|
|
|
|
2014-12-02 13:21:39 -08:00
|
|
|
/**
|
|
|
|
* Cache that stores the ProtoView of the template of a component.
|
|
|
|
* Used to prevent duplicate work and resolve cyclic dependencies.
|
|
|
|
*/
|
|
|
|
export class CompilerCache {
|
|
|
|
_cache:Map;
|
|
|
|
constructor() {
|
|
|
|
this._cache = MapWrapper.create();
|
|
|
|
}
|
|
|
|
|
|
|
|
set(component:Type, protoView:ProtoView) {
|
|
|
|
MapWrapper.set(this._cache, component, protoView);
|
|
|
|
}
|
|
|
|
|
|
|
|
get(component:Type):ProtoView {
|
|
|
|
var result = MapWrapper.get(this._cache, component);
|
|
|
|
if (isBlank(result)) {
|
|
|
|
// need to normalize undefined to null so that type checking passes :-(
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2014-12-22 17:50:10 -08:00
|
|
|
|
|
|
|
clear() {
|
|
|
|
this._cache = MapWrapper.create();
|
|
|
|
}
|
2014-12-02 13:21:39 -08:00
|
|
|
}
|
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
/**
|
|
|
|
* The compiler loads and translates the html templates of components into
|
|
|
|
* nested ProtoViews. To decompose its functionality it uses
|
|
|
|
* the CompilePipeline and the CompileSteps.
|
|
|
|
*/
|
2014-09-19 23:03:36 +00:00
|
|
|
export class Compiler {
|
2014-11-21 21:19:23 -08:00
|
|
|
_templateLoader:TemplateLoader;
|
2014-11-20 12:07:48 -08:00
|
|
|
_reader: DirectiveMetadataReader;
|
2014-11-21 21:19:23 -08:00
|
|
|
_parser:Parser;
|
2014-12-02 13:21:39 -08:00
|
|
|
_compilerCache:CompilerCache;
|
|
|
|
constructor(templateLoader:TemplateLoader, reader: DirectiveMetadataReader, parser:Parser, cache:CompilerCache) {
|
2014-09-19 16:38:37 -07:00
|
|
|
this._templateLoader = templateLoader;
|
2014-11-20 12:07:48 -08:00
|
|
|
this._reader = reader;
|
2014-11-11 17:33:47 -08:00
|
|
|
this._parser = parser;
|
2014-12-02 13:21:39 -08:00
|
|
|
this._compilerCache = cache;
|
2014-09-19 16:38:37 -07:00
|
|
|
}
|
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
createSteps(component:AnnotatedType):List<CompileStep> {
|
2014-11-07 14:30:04 -08:00
|
|
|
var annotation: Component = component.annotation;
|
|
|
|
var directives = annotation.template.directives;
|
2014-11-11 17:33:47 -08:00
|
|
|
var annotatedDirectives = ListWrapper.create();
|
|
|
|
for (var i=0; i<directives.length; i++) {
|
2014-11-20 12:07:48 -08:00
|
|
|
ListWrapper.push(annotatedDirectives, this._reader.annotatedType(directives[i]));
|
2014-11-11 17:33:47 -08:00
|
|
|
}
|
2014-12-10 19:21:15 -08:00
|
|
|
return createDefaultSteps(this._parser, component, annotatedDirectives);
|
2014-09-19 23:03:36 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
compile(component:Type, templateRoot:Element = null):Promise<ProtoView> {
|
2014-12-02 13:21:39 -08:00
|
|
|
var templateCache = null;
|
|
|
|
// TODO load all components that have urls
|
|
|
|
// transitively via the _templateLoader and store them in templateCache
|
|
|
|
|
|
|
|
return PromiseWrapper.resolve(this.compileAllLoaded(
|
|
|
|
templateCache, this._reader.annotatedType(component), templateRoot)
|
2014-11-11 17:33:47 -08:00
|
|
|
);
|
|
|
|
}
|
2014-09-19 16:38:37 -07:00
|
|
|
|
2014-11-14 14:03:03 -08:00
|
|
|
// public so that we can compile in sync in performance tests.
|
2014-12-02 13:21:39 -08:00
|
|
|
compileAllLoaded(templateCache, component:AnnotatedType, templateRoot:Element = null):ProtoView {
|
|
|
|
var rootProtoView = this._compilerCache.get(component.type);
|
|
|
|
if (isPresent(rootProtoView)) {
|
|
|
|
return rootProtoView;
|
|
|
|
}
|
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
if (isBlank(templateRoot)) {
|
|
|
|
// TODO: read out the cache if templateRoot = null. Could contain:
|
|
|
|
// - templateRoot string
|
|
|
|
// - precompiled template
|
|
|
|
// - ProtoView
|
2014-11-21 21:19:23 -08:00
|
|
|
var annotation:any = component.annotation;
|
2014-11-07 14:30:04 -08:00
|
|
|
templateRoot = DOM.createTemplate(annotation.template.inline);
|
2014-11-11 17:33:47 -08:00
|
|
|
}
|
2014-12-02 13:21:39 -08:00
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
var pipeline = new CompilePipeline(this.createSteps(component));
|
|
|
|
var compileElements = pipeline.process(templateRoot);
|
2014-12-02 13:21:39 -08:00
|
|
|
rootProtoView = compileElements[0].inheritedProtoView;
|
|
|
|
// Save the rootProtoView before we recurse so that we are able
|
|
|
|
// to compile components that use themselves in their template.
|
|
|
|
this._compilerCache.set(component.type, rootProtoView);
|
2014-11-11 17:33:47 -08:00
|
|
|
|
|
|
|
for (var i=0; i<compileElements.length; i++) {
|
|
|
|
var ce = compileElements[i];
|
|
|
|
if (isPresent(ce.componentDirective)) {
|
2014-12-02 13:21:39 -08:00
|
|
|
ce.inheritedElementBinder.nestedProtoView = this.compileAllLoaded(templateCache, ce.componentDirective, null);
|
2014-11-11 17:33:47 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rootProtoView;
|
|
|
|
}
|
2014-09-28 16:29:11 -07:00
|
|
|
}
|