/* @flow */ import { escape, isSSRUnsafeAttr } from 'web/server/util' import { isObject, extend } from 'shared/util' import { renderAttr } from 'web/server/modules/attrs' import { renderClass } from 'web/util/class' import { genStyle } from 'web/server/modules/style' import { normalizeStyleBinding } from 'web/util/style' import { normalizeChildren, simpleNormalizeChildren } from 'core/vdom/helpers/normalize-children' import { propsToAttrMap, isRenderableAttr } from 'web/server/util' const ssrHelpers = { _ssrEscape: escape, _ssrNode: renderStringNode, _ssrList: renderStringList, _ssrAttr: renderAttr, _ssrAttrs: renderAttrs, _ssrDOMProps: renderDOMProps, _ssrClass: renderSSRClass, _ssrStyle: renderSSRStyle } export function installSSRHelpers (vm: Component) { if (vm._ssrNode) { return } let Vue = vm.constructor while (Vue.super) { Vue = Vue.super } extend(Vue.prototype, ssrHelpers) if (Vue.FunctionalRenderContext) { extend(Vue.FunctionalRenderContext.prototype, ssrHelpers) } } class StringNode { isString: boolean; open: string; close: ?string; children: ?Array<any>; constructor ( open: string, close?: string, children?: Array<any>, normalizationType?: number ) { this.isString = true this.open = open this.close = close if (children) { this.children = normalizationType === 1 ? simpleNormalizeChildren(children) : normalizationType === 2 ? normalizeChildren(children) : children } else { this.children = void 0 } } } function renderStringNode ( open: string, close?: string, children?: Array<any>, normalizationType?: number ): StringNode { return new StringNode(open, close, children, normalizationType) } function renderStringList ( val: any, render: ( val: any, keyOrIndex: string | number, index?: number ) => string ): string { let ret = '' let i, l, keys, key if (Array.isArray(val) || typeof val === 'string') { for (i = 0, l = val.length; i < l; i++) { ret += render(val[i], i) } } else if (typeof val === 'number') { for (i = 0; i < val; i++) { ret += render(i + 1, i) } } else if (isObject(val)) { keys = Object.keys(val) for (i = 0, l = keys.length; i < l; i++) { key = keys[i] ret += render(val[key], key, i) } } return ret } function renderAttrs (obj: Object): string { let res = '' for (const key in obj) { if (isSSRUnsafeAttr(key)) { continue } res += renderAttr(key, obj[key]) } return res } function renderDOMProps (obj: Object): string { let res = '' for (const key in obj) { const attr = propsToAttrMap[key] || key.toLowerCase() if (isRenderableAttr(attr)) { res += renderAttr(attr, obj[key]) } } return res } function renderSSRClass ( staticClass: ?string, dynamic: any ): string { const res = renderClass(staticClass, dynamic) return res === '' ? res : ` class="${escape(res)}"` } function renderSSRStyle ( staticStyle: ?Object, dynamic: any, extra: ?Object ): string { const style = {} if (staticStyle) extend(style, staticStyle) if (dynamic) extend(style, normalizeStyleBinding(dynamic)) if (extra) extend(style, extra) const res = genStyle(style) return res === '' ? res : ` style=${JSON.stringify(escape(res))}` }