UNPKG

angular2

Version:

Angular 2 - a web framework for modern web apps

335 lines (334 loc) 18 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { IS_DART, isBlank, evalExpression } from 'angular2/src/facade/lang'; import { BaseException } from 'angular2/src/facade/exceptions'; import { ListWrapper, SetWrapper, StringMapWrapper } from 'angular2/src/facade/collection'; import { PromiseWrapper } from 'angular2/src/facade/async'; import { createHostComponentMeta, CompileDirectiveMetadata } from './directive_metadata'; import { templateVisitAll } from './template_ast'; import { Injectable } from 'angular2/src/core/di'; import { SourceModule, moduleRef, SourceExpression } from './source_module'; import { ChangeDetectionCompiler, CHANGE_DETECTION_JIT_IMPORTS } from './change_detector_compiler'; import { StyleCompiler } from './style_compiler'; import { ViewCompiler, VIEW_JIT_IMPORTS } from './view_compiler'; import { ProtoViewCompiler, APP_VIEW_MODULE_REF, PROTO_VIEW_JIT_IMPORTS } from './proto_view_compiler'; import { TemplateParser, PipeCollector } from './template_parser'; import { TemplateNormalizer } from './template_normalizer'; import { RuntimeMetadataResolver } from './runtime_metadata'; import { HostViewFactory } from 'angular2/src/core/linker/view'; import { ChangeDetectorGenConfig } from 'angular2/src/core/change_detection/change_detection'; import { ResolvedMetadataCache } from 'angular2/src/core/linker/resolved_metadata_cache'; import { codeGenExportVariable, MODULE_SUFFIX, addAll, Expression } from './util'; export var METADATA_CACHE_MODULE_REF = moduleRef('package:angular2/src/core/linker/resolved_metadata_cache' + MODULE_SUFFIX); /** * An internal module of the Angular compiler that begins with component types, * extracts templates, and eventually produces a compiled version of the component * ready for linking into an application. */ export let TemplateCompiler = class { constructor(_runtimeMetadataResolver, _templateNormalizer, _templateParser, _styleCompiler, _cdCompiler, _protoViewCompiler, _viewCompiler, _resolvedMetadataCache, _genConfig) { this._runtimeMetadataResolver = _runtimeMetadataResolver; this._templateNormalizer = _templateNormalizer; this._templateParser = _templateParser; this._styleCompiler = _styleCompiler; this._cdCompiler = _cdCompiler; this._protoViewCompiler = _protoViewCompiler; this._viewCompiler = _viewCompiler; this._resolvedMetadataCache = _resolvedMetadataCache; this._genConfig = _genConfig; this._hostCacheKeys = new Map(); this._compiledTemplateCache = new Map(); this._compiledTemplateDone = new Map(); } normalizeDirectiveMetadata(directive) { if (!directive.isComponent) { // For non components there is nothing to be normalized yet. return PromiseWrapper.resolve(directive); } return this._templateNormalizer.normalizeTemplate(directive.type, directive.template) .then((normalizedTemplate) => 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, lifecycleHooks: directive.lifecycleHooks, providers: directive.providers, template: normalizedTemplate })); } compileHostComponentRuntime(type) { var compMeta = this._runtimeMetadataResolver.getDirectiveMetadata(type); var hostCacheKey = this._hostCacheKeys.get(type); if (isBlank(hostCacheKey)) { hostCacheKey = new Object(); this._hostCacheKeys.set(type, hostCacheKey); assertComponent(compMeta); var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], [], []); } return this._compiledTemplateDone.get(hostCacheKey) .then((compiledTemplate) => new HostViewFactory(compMeta.selector, compiledTemplate.viewFactory)); } clearCache() { this._styleCompiler.clearCache(); this._compiledTemplateCache.clear(); this._compiledTemplateDone.clear(); this._hostCacheKeys.clear(); } compileTemplatesCodeGen(components) { if (components.length === 0) { throw new BaseException('No components given'); } var declarations = []; components.forEach(componentWithDirs => { var compMeta = componentWithDirs.component; assertComponent(compMeta); this._compileComponentCodeGen(compMeta, componentWithDirs.directives, componentWithDirs.pipes, declarations); if (compMeta.dynamicLoadable) { var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); var viewFactoryExpression = this._compileComponentCodeGen(hostMeta, [compMeta], [], declarations); var constructionKeyword = IS_DART ? 'const' : 'new'; var compiledTemplateExpr = `${constructionKeyword} ${APP_VIEW_MODULE_REF}HostViewFactory('${compMeta.selector}',${viewFactoryExpression})`; var varName = codeGenHostViewFactoryName(compMeta.type); declarations.push(`${codeGenExportVariable(varName)}${compiledTemplateExpr};`); } }); var moduleUrl = components[0].component.type.moduleUrl; return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n')); } compileStylesheetCodeGen(stylesheetUrl, cssText) { return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText); } _compileComponentRuntime(cacheKey, compMeta, viewDirectives, pipes, compilingComponentsPath) { let uniqViewDirectives = removeDuplicates(viewDirectives); let uniqViewPipes = removeDuplicates(pipes); var compiledTemplate = this._compiledTemplateCache.get(cacheKey); var done = this._compiledTemplateDone.get(cacheKey); if (isBlank(compiledTemplate)) { compiledTemplate = new CompiledTemplate(); this._compiledTemplateCache.set(cacheKey, compiledTemplate); done = PromiseWrapper .all([this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(uniqViewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta)))) .then((stylesAndNormalizedViewDirMetas) => { var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1); var styles = stylesAndNormalizedViewDirMetas[0]; var parsedTemplate = this._templateParser.parse(compMeta.template.template, normalizedViewDirMetas, uniqViewPipes, compMeta.type.name); var childPromises = []; var usedDirectives = DirectiveCollector.findUsedDirectives(parsedTemplate); usedDirectives.components.forEach(component => this._compileNestedComponentRuntime(component, compilingComponentsPath, childPromises)); return PromiseWrapper.all(childPromises) .then((_) => { var filteredPipes = filterPipes(parsedTemplate, uniqViewPipes); compiledTemplate.init(this._createViewFactoryRuntime(compMeta, parsedTemplate, usedDirectives.directives, styles, filteredPipes)); return compiledTemplate; }); }); this._compiledTemplateDone.set(cacheKey, done); } return compiledTemplate; } _compileNestedComponentRuntime(childComponentDir, parentCompilingComponentsPath, childPromises) { var compilingComponentsPath = ListWrapper.clone(parentCompilingComponentsPath); var childCacheKey = childComponentDir.type.runtime; var childViewDirectives = this._runtimeMetadataResolver.getViewDirectivesMetadata(childComponentDir.type.runtime); var childViewPipes = this._runtimeMetadataResolver.getViewPipesMetadata(childComponentDir.type.runtime); var childIsRecursive = ListWrapper.contains(compilingComponentsPath, childCacheKey); compilingComponentsPath.push(childCacheKey); this._compileComponentRuntime(childCacheKey, childComponentDir, childViewDirectives, childViewPipes, compilingComponentsPath); if (!childIsRecursive) { // Only wait for a child if it is not a cycle childPromises.push(this._compiledTemplateDone.get(childCacheKey)); } } _createViewFactoryRuntime(compMeta, parsedTemplate, directives, styles, pipes) { if (IS_DART || !this._genConfig.useJit) { var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(compMeta.type, compMeta.changeDetection, parsedTemplate); var protoViews = this._protoViewCompiler.compileProtoViewRuntime(this._resolvedMetadataCache, compMeta, parsedTemplate, pipes); return this._viewCompiler.compileComponentRuntime(compMeta, parsedTemplate, styles, protoViews.protoViews, changeDetectorFactories, (compMeta) => this._getNestedComponentViewFactory(compMeta)); } else { var declarations = []; var viewFactoryExpr = this._createViewFactoryCodeGen('resolvedMetadataCache', compMeta, new SourceExpression([], 'styles'), parsedTemplate, pipes, declarations); var vars = { 'exports': {}, 'styles': styles, 'resolvedMetadataCache': this._resolvedMetadataCache }; directives.forEach(dirMeta => { vars[dirMeta.type.name] = dirMeta.type.runtime; if (dirMeta.isComponent && dirMeta.type.runtime !== compMeta.type.runtime) { vars[`viewFactory_${dirMeta.type.name}0`] = this._getNestedComponentViewFactory(dirMeta); } }); pipes.forEach(pipeMeta => vars[pipeMeta.type.name] = pipeMeta.type.runtime); var declarationsWithoutImports = SourceModule.getSourceWithoutImports(declarations.join('\n')); return evalExpression(`viewFactory_${compMeta.type.name}`, viewFactoryExpr, declarationsWithoutImports, mergeStringMaps([vars, CHANGE_DETECTION_JIT_IMPORTS, PROTO_VIEW_JIT_IMPORTS, VIEW_JIT_IMPORTS])); } } _getNestedComponentViewFactory(compMeta) { return this._compiledTemplateCache.get(compMeta.type.runtime).viewFactory; } _compileComponentCodeGen(compMeta, directives, pipes, targetDeclarations) { let uniqueDirectives = removeDuplicates(directives); let uniqPipes = removeDuplicates(pipes); var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template); var parsedTemplate = this._templateParser.parse(compMeta.template.template, uniqueDirectives, uniqPipes, compMeta.type.name); var filteredPipes = filterPipes(parsedTemplate, uniqPipes); return this._createViewFactoryCodeGen(`${METADATA_CACHE_MODULE_REF}CODEGEN_RESOLVED_METADATA_CACHE`, compMeta, styleExpr, parsedTemplate, filteredPipes, targetDeclarations); } _createViewFactoryCodeGen(resolvedMetadataCacheExpr, compMeta, styleExpr, parsedTemplate, pipes, targetDeclarations) { var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(compMeta.type, compMeta.changeDetection, parsedTemplate); var protoViewExprs = this._protoViewCompiler.compileProtoViewCodeGen(new Expression(resolvedMetadataCacheExpr), compMeta, parsedTemplate, pipes); var viewFactoryExpr = this._viewCompiler.compileComponentCodeGen(compMeta, parsedTemplate, styleExpr, protoViewExprs.protoViews, changeDetectorsExprs, codeGenComponentViewFactoryName); addAll(changeDetectorsExprs.declarations, targetDeclarations); addAll(protoViewExprs.declarations, targetDeclarations); addAll(viewFactoryExpr.declarations, targetDeclarations); return viewFactoryExpr.expression; } }; TemplateCompiler = __decorate([ Injectable(), __metadata('design:paramtypes', [RuntimeMetadataResolver, TemplateNormalizer, TemplateParser, StyleCompiler, ChangeDetectionCompiler, ProtoViewCompiler, ViewCompiler, ResolvedMetadataCache, ChangeDetectorGenConfig]) ], TemplateCompiler); export class NormalizedComponentWithViewDirectives { constructor(component, directives, pipes) { this.component = component; this.directives = directives; this.pipes = pipes; } } class CompiledTemplate { constructor() { this.viewFactory = null; } init(viewFactory) { this.viewFactory = viewFactory; } } function assertComponent(meta) { if (!meta.isComponent) { throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`); } } function templateModuleUrl(moduleUrl) { var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length); return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`; } function codeGenHostViewFactoryName(type) { return `hostViewFactory_${type.name}`; } function codeGenComponentViewFactoryName(nestedCompType) { return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}viewFactory_${nestedCompType.type.name}0`; } function mergeStringMaps(maps) { var result = {}; maps.forEach((map) => { StringMapWrapper.forEach(map, (value, key) => { result[key] = value; }); }); return result; } function removeDuplicates(items) { let res = []; items.forEach(item => { let hasMatch = res.filter(r => r.type.name == item.type.name && r.type.moduleUrl == item.type.moduleUrl && r.type.runtime == item.type.runtime) .length > 0; if (!hasMatch) { res.push(item); } }); return res; } class DirectiveCollector { constructor() { this.directives = []; this.components = []; } static findUsedDirectives(parsedTemplate) { var collector = new DirectiveCollector(); templateVisitAll(collector, parsedTemplate); return collector; } visitBoundText(ast, context) { return null; } visitText(ast, context) { return null; } visitNgContent(ast, context) { return null; } visitElement(ast, context) { templateVisitAll(this, ast.directives); templateVisitAll(this, ast.children); return null; } visitEmbeddedTemplate(ast, context) { templateVisitAll(this, ast.directives); templateVisitAll(this, ast.children); return null; } visitVariable(ast, ctx) { return null; } visitAttr(ast, attrNameAndValues) { return null; } visitDirective(ast, ctx) { if (ast.directive.isComponent) { this.components.push(ast.directive); } this.directives.push(ast.directive); return null; } visitEvent(ast, eventTargetAndNames) { return null; } visitDirectiveProperty(ast, context) { return null; } visitElementProperty(ast, context) { return null; } } function filterPipes(template, allPipes) { var visitor = new PipeVisitor(); templateVisitAll(visitor, template); return allPipes.filter((pipeMeta) => SetWrapper.has(visitor.collector.pipes, pipeMeta.name)); } class PipeVisitor { constructor() { this.collector = new PipeCollector(); } visitBoundText(ast, context) { ast.value.visit(this.collector); return null; } visitText(ast, context) { return null; } visitNgContent(ast, context) { return null; } visitElement(ast, context) { templateVisitAll(this, ast.inputs); templateVisitAll(this, ast.outputs); templateVisitAll(this, ast.directives); templateVisitAll(this, ast.children); return null; } visitEmbeddedTemplate(ast, context) { templateVisitAll(this, ast.outputs); templateVisitAll(this, ast.directives); templateVisitAll(this, ast.children); return null; } visitVariable(ast, ctx) { return null; } visitAttr(ast, attrNameAndValues) { return null; } visitDirective(ast, ctx) { templateVisitAll(this, ast.inputs); templateVisitAll(this, ast.hostEvents); templateVisitAll(this, ast.hostProperties); return null; } visitEvent(ast, eventTargetAndNames) { ast.handler.visit(this.collector); return null; } visitDirectiveProperty(ast, context) { ast.value.visit(this.collector); return null; } visitElementProperty(ast, context) { ast.value.visit(this.collector); return null; } }