2015-11-06 17:34:07 -08:00
|
|
|
import {IS_DART, Type, Json, isBlank, stringify} from 'angular2/src/facade/lang';
|
|
|
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
|
|
import {ListWrapper, SetWrapper} from 'angular2/src/facade/collection';
|
|
|
|
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
2015-09-28 10:30:33 -07:00
|
|
|
import {
|
2015-11-02 08:39:14 -08:00
|
|
|
CompiledComponentTemplate,
|
2015-09-28 10:30:33 -07:00
|
|
|
TemplateCmd,
|
2015-11-02 08:39:14 -08:00
|
|
|
CompiledHostTemplate,
|
|
|
|
BeginComponentCmd
|
2015-10-02 07:37:23 -07:00
|
|
|
} from 'angular2/src/core/linker/template_commands';
|
2015-09-14 15:59:09 -07:00
|
|
|
import {
|
|
|
|
createHostComponentMeta,
|
2015-09-18 10:33:23 -07:00
|
|
|
CompileDirectiveMetadata,
|
|
|
|
CompileTypeMetadata,
|
|
|
|
CompileTemplateMetadata
|
2015-09-14 15:59:09 -07:00
|
|
|
} from './directive_metadata';
|
|
|
|
import {TemplateAst} from './template_ast';
|
|
|
|
import {Injectable} from 'angular2/src/core/di';
|
|
|
|
import {SourceModule, moduleRef} from './source_module';
|
|
|
|
import {ChangeDetectionCompiler} from './change_detector_compiler';
|
|
|
|
import {StyleCompiler} from './style_compiler';
|
|
|
|
import {CommandCompiler} from './command_compiler';
|
|
|
|
import {TemplateParser} from './template_parser';
|
|
|
|
import {TemplateNormalizer} from './template_normalizer';
|
|
|
|
import {RuntimeMetadataResolver} from './runtime_metadata';
|
|
|
|
|
|
|
|
import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
|
2015-10-01 10:07:49 -07:00
|
|
|
import {
|
|
|
|
codeGenExportVariable,
|
|
|
|
escapeSingleQuoteString,
|
|
|
|
codeGenValueFn,
|
|
|
|
MODULE_SUFFIX
|
|
|
|
} from './util';
|
2015-09-14 15:59:09 -07:00
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class TemplateCompiler {
|
2015-09-29 11:11:06 -07:00
|
|
|
private _hostCacheKeys = new Map<Type, any>();
|
2015-11-02 08:39:14 -08:00
|
|
|
private _compiledTemplateCache = new Map<any, CompiledComponentTemplate>();
|
|
|
|
private _compiledTemplateDone = new Map<any, Promise<CompiledComponentTemplate>>();
|
|
|
|
private _nextTemplateId: number = 0;
|
2015-09-14 15:59:09 -07:00
|
|
|
|
|
|
|
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
|
|
|
private _templateNormalizer: TemplateNormalizer,
|
|
|
|
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
|
|
|
private _commandCompiler: CommandCompiler,
|
2015-11-02 08:39:14 -08:00
|
|
|
private _cdCompiler: ChangeDetectionCompiler) {}
|
2015-09-14 15:59:09 -07:00
|
|
|
|
2015-10-28 08:59:19 +01:00
|
|
|
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
|
|
|
|
Promise<CompileDirectiveMetadata> {
|
2015-09-18 10:33:23 -07:00
|
|
|
if (!directive.isComponent) {
|
|
|
|
// For non components there is nothing to be normalized yet.
|
|
|
|
return PromiseWrapper.resolve(directive);
|
|
|
|
}
|
2015-10-23 00:17:30 -07:00
|
|
|
|
|
|
|
return this._templateNormalizer.normalizeTemplate(directive.type, directive.template)
|
|
|
|
.then((normalizedTemplate: CompileTemplateMetadata) => new CompileDirectiveMetadata({
|
|
|
|
type: directive.type,
|
|
|
|
isComponent: directive.isComponent,
|
|
|
|
dynamicLoadable: directive.dynamicLoadable,
|
|
|
|
selector: directive.selector,
|
|
|
|
exportAs: directive.exportAs,
|
|
|
|
changeDetection: directive.changeDetection,
|
|
|
|
inputs: directive.inputs,
|
|
|
|
outputs: directive.outputs,
|
|
|
|
hostListeners: directive.hostListeners,
|
|
|
|
hostProperties: directive.hostProperties,
|
|
|
|
hostAttributes: directive.hostAttributes,
|
2015-10-28 08:59:19 +01:00
|
|
|
lifecycleHooks: directive.lifecycleHooks,
|
|
|
|
template: normalizedTemplate
|
2015-10-23 00:17:30 -07:00
|
|
|
}));
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
|
2015-09-28 10:30:33 -07:00
|
|
|
compileHostComponentRuntime(type: Type): Promise<CompiledHostTemplate> {
|
|
|
|
var hostCacheKey = this._hostCacheKeys.get(type);
|
|
|
|
if (isBlank(hostCacheKey)) {
|
|
|
|
hostCacheKey = new Object();
|
|
|
|
this._hostCacheKeys.set(type, hostCacheKey);
|
|
|
|
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
|
|
|
assertComponent(compMeta);
|
|
|
|
var hostMeta: CompileDirectiveMetadata =
|
|
|
|
createHostComponentMeta(compMeta.type, compMeta.selector);
|
2015-09-18 10:33:23 -07:00
|
|
|
|
2015-09-28 10:30:33 -07:00
|
|
|
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set());
|
|
|
|
}
|
|
|
|
return this._compiledTemplateDone.get(hostCacheKey)
|
2015-11-02 08:39:14 -08:00
|
|
|
.then(compiledTemplate => new CompiledHostTemplate(compiledTemplate));
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
|
2015-09-18 10:33:23 -07:00
|
|
|
clearCache() {
|
2015-09-28 10:30:33 -07:00
|
|
|
this._hostCacheKeys.clear();
|
2015-09-18 10:33:23 -07:00
|
|
|
this._styleCompiler.clearCache();
|
|
|
|
this._compiledTemplateCache.clear();
|
|
|
|
this._compiledTemplateDone.clear();
|
|
|
|
}
|
|
|
|
|
2015-11-02 08:39:14 -08:00
|
|
|
private _compileComponentRuntime(
|
|
|
|
cacheKey: any, compMeta: CompileDirectiveMetadata, viewDirectives: CompileDirectiveMetadata[],
|
|
|
|
compilingComponentCacheKeys: Set<any>): CompiledComponentTemplate {
|
2015-09-28 10:30:33 -07:00
|
|
|
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
|
|
|
|
var done = this._compiledTemplateDone.get(cacheKey);
|
2015-09-14 15:59:09 -07:00
|
|
|
if (isBlank(compiledTemplate)) {
|
2015-11-02 08:39:14 -08:00
|
|
|
var styles = [];
|
2015-09-18 10:33:23 -07:00
|
|
|
var changeDetectorFactory;
|
2015-11-02 08:39:14 -08:00
|
|
|
var commands = [];
|
|
|
|
var templateId = `${stringify(compMeta.type.runtime)}Template${this._nextTemplateId++}`;
|
|
|
|
compiledTemplate = new CompiledComponentTemplate(
|
|
|
|
templateId, (dispatcher) => changeDetectorFactory(dispatcher), commands, styles);
|
2015-09-28 10:30:33 -07:00
|
|
|
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
|
|
|
|
compilingComponentCacheKeys.add(cacheKey);
|
2015-11-02 08:39:14 -08:00
|
|
|
done = PromiseWrapper
|
|
|
|
.all([<any>this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
|
|
|
|
viewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
|
|
|
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
|
|
|
var childPromises = [];
|
|
|
|
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
|
|
|
var parsedTemplate = this._templateParser.parse(
|
|
|
|
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
2015-09-18 10:33:23 -07:00
|
|
|
|
2015-11-02 08:39:14 -08:00
|
|
|
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
|
|
|
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
|
|
|
changeDetectorFactory = changeDetectorFactories[0];
|
|
|
|
var tmpStyles: string[] = stylesAndNormalizedViewDirMetas[0];
|
|
|
|
tmpStyles.forEach(style => styles.push(style));
|
|
|
|
var tmpCommands: TemplateCmd[] = this._compileCommandsRuntime(
|
|
|
|
compMeta, parsedTemplate, changeDetectorFactories,
|
|
|
|
compilingComponentCacheKeys, childPromises);
|
|
|
|
tmpCommands.forEach(cmd => commands.push(cmd));
|
|
|
|
return PromiseWrapper.all(childPromises);
|
|
|
|
})
|
|
|
|
.then((_) => {
|
|
|
|
SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
|
|
|
|
return compiledTemplate;
|
|
|
|
});
|
2015-09-28 10:30:33 -07:00
|
|
|
this._compiledTemplateDone.set(cacheKey, done);
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
return compiledTemplate;
|
|
|
|
}
|
|
|
|
|
2015-11-02 08:39:14 -08:00
|
|
|
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
|
2015-09-18 10:33:23 -07:00
|
|
|
changeDetectorFactories: Function[],
|
2015-09-28 10:30:33 -07:00
|
|
|
compilingComponentCacheKeys: Set<Type>,
|
2015-09-14 15:59:09 -07:00
|
|
|
childPromises: Promise<any>[]): TemplateCmd[] {
|
2015-11-02 08:39:14 -08:00
|
|
|
var cmds: TemplateCmd[] = this._commandCompiler.compileComponentRuntime(
|
|
|
|
compMeta, parsedTemplate, changeDetectorFactories,
|
2015-09-18 10:33:23 -07:00
|
|
|
(childComponentDir: CompileDirectiveMetadata) => {
|
2015-09-28 10:30:33 -07:00
|
|
|
var childCacheKey = childComponentDir.type.runtime;
|
2015-09-18 10:33:23 -07:00
|
|
|
var childViewDirectives: CompileDirectiveMetadata[] =
|
2015-09-14 15:59:09 -07:00
|
|
|
this._runtimeMetadataResolver.getViewDirectivesMetadata(
|
|
|
|
childComponentDir.type.runtime);
|
2015-09-28 10:30:33 -07:00
|
|
|
var childIsRecursive = SetWrapper.has(compilingComponentCacheKeys, childCacheKey);
|
|
|
|
var childTemplate = this._compileComponentRuntime(
|
|
|
|
childCacheKey, childComponentDir, childViewDirectives, compilingComponentCacheKeys);
|
2015-09-14 15:59:09 -07:00
|
|
|
if (!childIsRecursive) {
|
|
|
|
// Only wait for a child if it is not a cycle
|
2015-09-28 10:30:33 -07:00
|
|
|
childPromises.push(this._compiledTemplateDone.get(childCacheKey));
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
2015-11-02 08:39:14 -08:00
|
|
|
return () => childTemplate;
|
2015-09-14 15:59:09 -07:00
|
|
|
});
|
2015-11-02 08:39:14 -08:00
|
|
|
cmds.forEach(cmd => {
|
|
|
|
if (cmd instanceof BeginComponentCmd) {
|
|
|
|
cmd.templateGetter();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return cmds;
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
|
2015-10-01 10:07:49 -07:00
|
|
|
compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
|
|
|
if (components.length === 0) {
|
|
|
|
throw new BaseException('No components given');
|
|
|
|
}
|
2015-09-14 15:59:09 -07:00
|
|
|
var declarations = [];
|
|
|
|
var templateArguments = [];
|
2015-09-18 10:33:23 -07:00
|
|
|
var componentMetas: CompileDirectiveMetadata[] = [];
|
2015-09-14 15:59:09 -07:00
|
|
|
components.forEach(componentWithDirs => {
|
2015-09-18 10:33:23 -07:00
|
|
|
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
2015-09-18 10:33:23 -07:00
|
|
|
assertComponent(compMeta);
|
2015-09-14 15:59:09 -07:00
|
|
|
componentMetas.push(compMeta);
|
2015-11-02 08:39:14 -08:00
|
|
|
this._processTemplateCodeGen(compMeta,
|
2015-09-18 10:33:23 -07:00
|
|
|
<CompileDirectiveMetadata[]>componentWithDirs.directives,
|
2015-09-14 15:59:09 -07:00
|
|
|
declarations, templateArguments);
|
|
|
|
if (compMeta.dynamicLoadable) {
|
|
|
|
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
|
|
|
componentMetas.push(hostMeta);
|
2015-11-02 08:39:14 -08:00
|
|
|
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
});
|
2015-09-18 10:33:23 -07:00
|
|
|
ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
|
2015-09-14 15:59:09 -07:00
|
|
|
index: number) => {
|
2015-11-02 08:39:14 -08:00
|
|
|
var templateId = `${compMeta.type.moduleUrl}|${compMeta.type.name}`;
|
|
|
|
var constructionKeyword = IS_DART ? 'const' : 'new';
|
2015-09-28 10:30:33 -07:00
|
|
|
var compiledTemplateExpr =
|
2015-11-02 08:39:14 -08:00
|
|
|
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledComponentTemplate('${templateId}',${(<any[]>templateArguments[index]).join(',')})`;
|
2015-09-28 10:30:33 -07:00
|
|
|
var variableValueExpr;
|
2015-10-01 10:07:49 -07:00
|
|
|
if (compMeta.type.isHost) {
|
2015-09-28 10:30:33 -07:00
|
|
|
variableValueExpr =
|
2015-11-02 08:39:14 -08:00
|
|
|
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${compiledTemplateExpr})`;
|
2015-09-28 10:30:33 -07:00
|
|
|
} else {
|
|
|
|
variableValueExpr = compiledTemplateExpr;
|
|
|
|
}
|
2015-11-02 08:39:14 -08:00
|
|
|
var varName = templateVariableName(compMeta.type);
|
|
|
|
declarations.push(`${codeGenExportVariable(varName)}${variableValueExpr};`);
|
|
|
|
declarations.push(`${codeGenValueFn([], varName, templateGetterName(compMeta.type))};`);
|
2015-09-14 15:59:09 -07:00
|
|
|
});
|
2015-10-01 10:07:49 -07:00
|
|
|
var moduleUrl = components[0].component.type.moduleUrl;
|
|
|
|
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
|
2015-10-01 10:07:49 -07:00
|
|
|
compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] {
|
|
|
|
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
|
2015-11-02 08:39:14 -08:00
|
|
|
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
|
|
|
|
directives: CompileDirectiveMetadata[],
|
2015-09-14 15:59:09 -07:00
|
|
|
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
2015-11-02 08:39:14 -08:00
|
|
|
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template);
|
2015-09-14 15:59:09 -07:00
|
|
|
var parsedTemplate =
|
|
|
|
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
2015-09-18 10:33:23 -07:00
|
|
|
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
|
|
|
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
2015-09-14 15:59:09 -07:00
|
|
|
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
2015-11-02 08:39:14 -08:00
|
|
|
compMeta, parsedTemplate, changeDetectorsExprs.expressions,
|
2015-09-18 10:33:23 -07:00
|
|
|
codeGenComponentTemplateFactory);
|
2015-09-14 15:59:09 -07:00
|
|
|
|
|
|
|
addAll(styleExpr.declarations, targetDeclarations);
|
2015-09-18 10:33:23 -07:00
|
|
|
addAll(changeDetectorsExprs.declarations, targetDeclarations);
|
2015-09-14 15:59:09 -07:00
|
|
|
addAll(commandsExpr.declarations, targetDeclarations);
|
|
|
|
|
|
|
|
targetTemplateArguments.push(
|
2015-09-18 10:33:23 -07:00
|
|
|
[changeDetectorsExprs.expressions[0], commandsExpr.expression, styleExpr.expression]);
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class NormalizedComponentWithViewDirectives {
|
2015-09-18 10:33:23 -07:00
|
|
|
constructor(public component: CompileDirectiveMetadata,
|
|
|
|
public directives: CompileDirectiveMetadata[]) {}
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
|
2015-09-18 10:33:23 -07:00
|
|
|
function assertComponent(meta: CompileDirectiveMetadata) {
|
|
|
|
if (!meta.isComponent) {
|
|
|
|
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-18 10:33:23 -07:00
|
|
|
function templateVariableName(type: CompileTypeMetadata): string {
|
2015-09-14 15:59:09 -07:00
|
|
|
return `${type.name}Template`;
|
|
|
|
}
|
|
|
|
|
2015-11-02 08:39:14 -08:00
|
|
|
function templateGetterName(type: CompileTypeMetadata): string {
|
|
|
|
return `${templateVariableName(type)}Getter`;
|
|
|
|
}
|
|
|
|
|
2015-10-01 10:07:49 -07:00
|
|
|
function templateModuleUrl(moduleUrl: string): string {
|
|
|
|
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
|
|
|
|
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
function addAll(source: any[], target: any[]) {
|
|
|
|
for (var i = 0; i < source.length; i++) {
|
|
|
|
target.push(source[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-18 10:33:23 -07:00
|
|
|
function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string {
|
2015-11-02 08:39:14 -08:00
|
|
|
return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}${templateGetterName(nestedCompType.type)}`;
|
2015-09-14 15:59:09 -07:00
|
|
|
}
|