UNPKG

angular2

Version:

Angular 2 - a web framework for modern web apps

412 lines (410 loc) 22.8 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 { isPresent, isBlank, IS_DART, CONST_EXPR } from 'angular2/src/facade/lang'; import { ListWrapper } from 'angular2/src/facade/collection'; import { templateVisitAll } from './template_ast'; import { SourceExpression } from './source_module'; import { AppView, flattenNestedViewRenderNodes, checkSlotCount } from 'angular2/src/core/linker/view'; import { ViewType } from 'angular2/src/core/linker/view_type'; import { AppElement } from 'angular2/src/core/linker/element'; import { ViewEncapsulation } from 'angular2/src/core/metadata/view'; import { escapeSingleQuoteString, codeGenValueFn, codeGenFnHeader, Statement, escapeValue, codeGenArray, codeGenFlatArray, Expression, flattenArray, CONST_VAR } from './util'; import { Injectable } from 'angular2/src/core/di'; import { APP_VIEW_MODULE_REF, APP_EL_MODULE_REF, METADATA_MODULE_REF } from './proto_view_compiler'; export const VIEW_JIT_IMPORTS = CONST_EXPR({ 'AppView': AppView, 'AppElement': AppElement, 'flattenNestedViewRenderNodes': flattenNestedViewRenderNodes, 'checkSlotCount': checkSlotCount }); export let ViewCompiler = class { constructor() { } compileComponentRuntime(component, template, styles, protoViews, changeDetectorFactories, componentViewFactory) { var viewFactory = new RuntimeViewFactory(component, styles, protoViews, changeDetectorFactories, componentViewFactory); return viewFactory.createViewFactory(template, 0, []); } compileComponentCodeGen(component, template, styles, protoViews, changeDetectorFactoryExpressions, componentViewFactory) { var viewFactory = new CodeGenViewFactory(component, styles, protoViews, changeDetectorFactoryExpressions, componentViewFactory); var targetStatements = []; var viewFactoryExpression = viewFactory.createViewFactory(template, 0, targetStatements); return new SourceExpression(targetStatements.map(stmt => stmt.statement), viewFactoryExpression.expression); } }; ViewCompiler = __decorate([ Injectable(), __metadata('design:paramtypes', []) ], ViewCompiler); class CodeGenViewFactory { constructor(component, styles, protoViews, changeDetectorExpressions, componentViewFactory) { this.component = component; this.styles = styles; this.protoViews = protoViews; this.changeDetectorExpressions = changeDetectorExpressions; this.componentViewFactory = componentViewFactory; this._nextVarId = 0; } _nextVar(prefix) { return `${prefix}${this._nextVarId++}_${this.component.type.name}`; } _nextRenderVar() { return this._nextVar('render'); } _nextAppVar() { return this._nextVar('app'); } _nextDisposableVar() { return `disposable${this._nextVarId++}_${this.component.type.name}`; } createText(renderer, parent, text, targetStatements) { var varName = this._nextRenderVar(); var statement = `var ${varName} = ${renderer.expression}.createText(${isPresent(parent) ? parent.expression : null}, ${escapeSingleQuoteString(text)});`; targetStatements.push(new Statement(statement)); return new Expression(varName); } createElement(renderer, parentRenderNode, name, rootSelector, targetStatements) { var varName = this._nextRenderVar(); var valueExpr; if (isPresent(rootSelector)) { valueExpr = `${rootSelector.expression} == null ? ${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)}) : ${renderer.expression}.selectRootElement(${rootSelector.expression});`; } else { valueExpr = `${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)})`; } var statement = `var ${varName} = ${valueExpr};`; targetStatements.push(new Statement(statement)); return new Expression(varName); } createTemplateAnchor(renderer, parentRenderNode, targetStatements) { var varName = this._nextRenderVar(); var valueExpr = `${renderer.expression}.createTemplateAnchor(${isPresent(parentRenderNode) ? parentRenderNode.expression : null});`; targetStatements.push(new Statement(`var ${varName} = ${valueExpr}`)); return new Expression(varName); } createGlobalEventListener(renderer, appView, boundElementIndex, eventAst, targetStatements) { var disposableVar = this._nextDisposableVar(); var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName); targetStatements.push(new Statement(`var ${disposableVar} = ${renderer.expression}.listenGlobal(${escapeValue(eventAst.target)}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`)); return new Expression(disposableVar); } createElementEventListener(renderer, appView, boundElementIndex, renderNode, eventAst, targetStatements) { var disposableVar = this._nextDisposableVar(); var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName); targetStatements.push(new Statement(`var ${disposableVar} = ${renderer.expression}.listen(${renderNode.expression}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`)); return new Expression(disposableVar); } setElementAttribute(renderer, renderNode, attrName, attrValue, targetStatements) { targetStatements.push(new Statement(`${renderer.expression}.setElementAttribute(${renderNode.expression}, ${escapeSingleQuoteString(attrName)}, ${escapeSingleQuoteString(attrValue)});`)); } createAppElement(appProtoEl, appView, renderNode, parentAppEl, embeddedViewFactory, targetStatements) { var appVar = this._nextAppVar(); var varValue = `new ${APP_EL_MODULE_REF}AppElement(${appProtoEl.expression}, ${appView.expression}, ${isPresent(parentAppEl) ? parentAppEl.expression : null}, ${renderNode.expression}, ${isPresent(embeddedViewFactory) ? embeddedViewFactory.expression : null})`; targetStatements.push(new Statement(`var ${appVar} = ${varValue};`)); return new Expression(appVar); } createAndSetComponentView(renderer, viewManager, view, appEl, component, contentNodesByNgContentIndex, targetStatements) { var codeGenContentNodes; if (this.component.type.isHost) { codeGenContentNodes = `${view.expression}.projectableNodes`; } else { codeGenContentNodes = `[${contentNodesByNgContentIndex.map(nodes => codeGenFlatArray(nodes)).join(',')}]`; } targetStatements.push(new Statement(`${this.componentViewFactory(component)}(${renderer.expression}, ${viewManager.expression}, ${appEl.expression}, ${codeGenContentNodes}, null, null, null);`)); } getProjectedNodes(projectableNodes, ngContentIndex) { return new Expression(`${projectableNodes.expression}[${ngContentIndex}]`, true); } appendProjectedNodes(renderer, parent, nodes, targetStatements) { targetStatements.push(new Statement(`${renderer.expression}.projectNodes(${parent.expression}, ${APP_VIEW_MODULE_REF}flattenNestedViewRenderNodes(${nodes.expression}));`)); } createViewFactory(asts, embeddedTemplateIndex, targetStatements) { var compileProtoView = this.protoViews[embeddedTemplateIndex]; var isHostView = this.component.type.isHost; var isComponentView = embeddedTemplateIndex === 0 && !isHostView; var visitor = new ViewBuilderVisitor(new Expression('renderer'), new Expression('viewManager'), new Expression('projectableNodes'), isHostView ? new Expression('rootSelector') : null, new Expression('view'), compileProtoView, targetStatements, this); templateVisitAll(visitor, asts, new ParentElement(isComponentView ? new Expression('parentRenderNode') : null, null, null)); var appProtoView = compileProtoView.protoView.expression; var viewFactoryName = codeGenViewFactoryName(this.component, embeddedTemplateIndex); var changeDetectorFactory = this.changeDetectorExpressions.expressions[embeddedTemplateIndex]; var factoryArgs = [ 'parentRenderer', 'viewManager', 'containerEl', 'projectableNodes', 'rootSelector', 'dynamicallyCreatedProviders', 'rootInjector' ]; var initRendererStmts = []; var rendererExpr = `parentRenderer`; if (embeddedTemplateIndex === 0) { var renderCompTypeVar = this._nextVar('renderType'); targetStatements.push(new Statement(`var ${renderCompTypeVar} = null;`)); var stylesVar = this._nextVar('styles'); targetStatements.push(new Statement(`${CONST_VAR} ${stylesVar} = ${this.styles.expression};`)); var encapsulation = this.component.template.encapsulation; initRendererStmts.push(`if (${renderCompTypeVar} == null) { ${renderCompTypeVar} = viewManager.createRenderComponentType(${codeGenViewEncapsulation(encapsulation)}, ${stylesVar}); }`); rendererExpr = `parentRenderer.renderComponent(${renderCompTypeVar})`; } var statement = ` ${codeGenFnHeader(factoryArgs, viewFactoryName)}{ ${initRendererStmts.join('\n')} var renderer = ${rendererExpr}; var view = new ${APP_VIEW_MODULE_REF}AppView( ${appProtoView}, renderer, viewManager, projectableNodes, containerEl, dynamicallyCreatedProviders, rootInjector, ${changeDetectorFactory}() ); ${APP_VIEW_MODULE_REF}checkSlotCount(${escapeValue(this.component.type.name)}, ${this.component.template.ngContentSelectors.length}, projectableNodes); ${isComponentView ? 'var parentRenderNode = renderer.createViewRoot(view.containerAppElement.nativeElement);' : ''} ${visitor.renderStmts.map(stmt => stmt.statement).join('\n')} ${visitor.appStmts.map(stmt => stmt.statement).join('\n')} view.init(${codeGenFlatArray(visitor.rootNodesOrAppElements)}, ${codeGenArray(visitor.renderNodes)}, ${codeGenArray(visitor.appDisposables)}, ${codeGenArray(visitor.appElements)}); return view; }`; targetStatements.push(new Statement(statement)); return new Expression(viewFactoryName); } } class RuntimeViewFactory { constructor(component, styles, protoViews, changeDetectorFactories, componentViewFactory) { this.component = component; this.styles = styles; this.protoViews = protoViews; this.changeDetectorFactories = changeDetectorFactories; this.componentViewFactory = componentViewFactory; } createText(renderer, parent, text, targetStatements) { return renderer.createText(parent, text); } createElement(renderer, parent, name, rootSelector, targetStatements) { var el; if (isPresent(rootSelector)) { el = renderer.selectRootElement(rootSelector); } else { el = renderer.createElement(parent, name); } return el; } createTemplateAnchor(renderer, parent, targetStatements) { return renderer.createTemplateAnchor(parent); } createGlobalEventListener(renderer, appView, boundElementIndex, eventAst, targetStatements) { return renderer.listenGlobal(eventAst.target, eventAst.name, (event) => appView.triggerEventHandlers(eventAst.fullName, event, boundElementIndex)); } createElementEventListener(renderer, appView, boundElementIndex, renderNode, eventAst, targetStatements) { return renderer.listen(renderNode, eventAst.name, (event) => appView.triggerEventHandlers(eventAst.fullName, event, boundElementIndex)); } setElementAttribute(renderer, renderNode, attrName, attrValue, targetStatements) { renderer.setElementAttribute(renderNode, attrName, attrValue); } createAppElement(appProtoEl, appView, renderNode, parentAppEl, embeddedViewFactory, targetStatements) { return new AppElement(appProtoEl, appView, parentAppEl, renderNode, embeddedViewFactory); } createAndSetComponentView(renderer, viewManager, appView, appEl, component, contentNodesByNgContentIndex, targetStatements) { var flattenedContentNodes; if (this.component.type.isHost) { flattenedContentNodes = appView.projectableNodes; } else { flattenedContentNodes = ListWrapper.createFixedSize(contentNodesByNgContentIndex.length); for (var i = 0; i < contentNodesByNgContentIndex.length; i++) { flattenedContentNodes[i] = flattenArray(contentNodesByNgContentIndex[i], []); } } this.componentViewFactory(component)(renderer, viewManager, appEl, flattenedContentNodes); } getProjectedNodes(projectableNodes, ngContentIndex) { return projectableNodes[ngContentIndex]; } appendProjectedNodes(renderer, parent, nodes, targetStatements) { renderer.projectNodes(parent, flattenNestedViewRenderNodes(nodes)); } createViewFactory(asts, embeddedTemplateIndex, targetStatements) { var compileProtoView = this.protoViews[embeddedTemplateIndex]; var isComponentView = compileProtoView.protoView.type === ViewType.COMPONENT; var renderComponentType = null; return (parentRenderer, viewManager, containerEl, projectableNodes, rootSelector = null, dynamicallyCreatedProviders = null, rootInjector = null) => { checkSlotCount(this.component.type.name, this.component.template.ngContentSelectors.length, projectableNodes); var renderer; if (embeddedTemplateIndex === 0) { if (isBlank(renderComponentType)) { renderComponentType = viewManager.createRenderComponentType(this.component.template.encapsulation, this.styles); } renderer = parentRenderer.renderComponent(renderComponentType); } else { renderer = parentRenderer; } var changeDetector = this.changeDetectorFactories[embeddedTemplateIndex](); var view = new AppView(compileProtoView.protoView, renderer, viewManager, projectableNodes, containerEl, dynamicallyCreatedProviders, rootInjector, changeDetector); var visitor = new ViewBuilderVisitor(renderer, viewManager, projectableNodes, rootSelector, view, compileProtoView, [], this); var parentRenderNode = isComponentView ? renderer.createViewRoot(containerEl.nativeElement) : null; templateVisitAll(visitor, asts, new ParentElement(parentRenderNode, null, null)); view.init(flattenArray(visitor.rootNodesOrAppElements, []), visitor.renderNodes, visitor.appDisposables, visitor.appElements); return view; }; } } class ParentElement { constructor(renderNode, appEl, component) { this.renderNode = renderNode; this.appEl = appEl; this.component = component; if (isPresent(component)) { this.contentNodesByNgContentIndex = ListWrapper.createFixedSize(component.template.ngContentSelectors.length); for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) { this.contentNodesByNgContentIndex[i] = []; } } else { this.contentNodesByNgContentIndex = null; } } addContentNode(ngContentIndex, nodeExpr) { this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr); } } class ViewBuilderVisitor { constructor(renderer, viewManager, projectableNodes, rootSelector, view, protoView, targetStatements, factory) { this.renderer = renderer; this.viewManager = viewManager; this.projectableNodes = projectableNodes; this.rootSelector = rootSelector; this.view = view; this.protoView = protoView; this.targetStatements = targetStatements; this.factory = factory; this.renderStmts = []; this.renderNodes = []; this.appStmts = []; this.appElements = []; this.appDisposables = []; this.rootNodesOrAppElements = []; this.elementCount = 0; } _addRenderNode(renderNode, appEl, ngContentIndex, parent) { this.renderNodes.push(renderNode); if (isPresent(parent.component)) { if (isPresent(ngContentIndex)) { parent.addContentNode(ngContentIndex, isPresent(appEl) ? appEl : renderNode); } } else if (isBlank(parent.renderNode)) { this.rootNodesOrAppElements.push(isPresent(appEl) ? appEl : renderNode); } } _getParentRenderNode(ngContentIndex, parent) { return isPresent(parent.component) && parent.component.template.encapsulation !== ViewEncapsulation.Native ? null : parent.renderNode; } visitBoundText(ast, parent) { return this._visitText('', ast.ngContentIndex, parent); } visitText(ast, parent) { return this._visitText(ast.value, ast.ngContentIndex, parent); } _visitText(value, ngContentIndex, parent) { var renderNode = this.factory.createText(this.renderer, this._getParentRenderNode(ngContentIndex, parent), value, this.renderStmts); this._addRenderNode(renderNode, null, ngContentIndex, parent); return null; } visitNgContent(ast, parent) { var nodesExpression = this.factory.getProjectedNodes(this.projectableNodes, ast.index); if (isPresent(parent.component)) { if (isPresent(ast.ngContentIndex)) { parent.addContentNode(ast.ngContentIndex, nodesExpression); } } else { if (isPresent(parent.renderNode)) { this.factory.appendProjectedNodes(this.renderer, parent.renderNode, nodesExpression, this.renderStmts); } else { this.rootNodesOrAppElements.push(nodesExpression); } } return null; } visitElement(ast, parent) { var renderNode = this.factory.createElement(this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), ast.name, this.rootSelector, this.renderStmts); var component = ast.getComponent(); var elementIndex = this.elementCount++; var protoEl = this.protoView.protoElements[elementIndex]; protoEl.renderEvents.forEach((eventAst) => { var disposable; if (isPresent(eventAst.target)) { disposable = this.factory.createGlobalEventListener(this.renderer, this.view, protoEl.boundElementIndex, eventAst, this.renderStmts); } else { disposable = this.factory.createElementEventListener(this.renderer, this.view, protoEl.boundElementIndex, renderNode, eventAst, this.renderStmts); } this.appDisposables.push(disposable); }); for (var i = 0; i < protoEl.attrNameAndValues.length; i++) { var attrName = protoEl.attrNameAndValues[i][0]; var attrValue = protoEl.attrNameAndValues[i][1]; this.factory.setElementAttribute(this.renderer, renderNode, attrName, attrValue, this.renderStmts); } var appEl = null; if (isPresent(protoEl.appProtoEl)) { appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode, parent.appEl, null, this.appStmts); this.appElements.push(appEl); } this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent); var newParent = new ParentElement(renderNode, isPresent(appEl) ? appEl : parent.appEl, component); templateVisitAll(this, ast.children, newParent); if (isPresent(appEl) && isPresent(component)) { this.factory.createAndSetComponentView(this.renderer, this.viewManager, this.view, appEl, component, newParent.contentNodesByNgContentIndex, this.appStmts); } return null; } visitEmbeddedTemplate(ast, parent) { var renderNode = this.factory.createTemplateAnchor(this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), this.renderStmts); var elementIndex = this.elementCount++; var protoEl = this.protoView.protoElements[elementIndex]; var embeddedViewFactory = this.factory.createViewFactory(ast.children, protoEl.embeddedTemplateIndex, this.targetStatements); var appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode, parent.appEl, embeddedViewFactory, this.appStmts); this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent); this.appElements.push(appEl); return null; } visitVariable(ast, ctx) { return null; } visitAttr(ast, ctx) { return null; } visitDirective(ast, ctx) { return null; } visitEvent(ast, ctx) { return null; } visitDirectiveProperty(ast, context) { return null; } visitElementProperty(ast, context) { return null; } } function codeGenEventHandler(view, boundElementIndex, eventName) { return codeGenValueFn(['event'], `${view.expression}.triggerEventHandlers(${escapeValue(eventName)}, event, ${boundElementIndex})`); } function codeGenViewFactoryName(component, embeddedTemplateIndex) { return `viewFactory_${component.type.name}${embeddedTemplateIndex}`; } function codeGenViewEncapsulation(value) { if (IS_DART) { return `${METADATA_MODULE_REF}${value}`; } else { return `${value}`; } }