aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/server/render.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vue/src/server/render.js')
-rw-r--r--node_modules/vue/src/server/render.js396
1 files changed, 396 insertions, 0 deletions
diff --git a/node_modules/vue/src/server/render.js b/node_modules/vue/src/server/render.js
new file mode 100644
index 00000000..592a6175
--- /dev/null
+++ b/node_modules/vue/src/server/render.js
@@ -0,0 +1,396 @@
+/* @flow */
+
+import { escape } from 'web/server/util'
+import { SSR_ATTR } from 'shared/constants'
+import { RenderContext } from './render-context'
+import { generateComponentTrace } from 'core/util/debug'
+import { ssrCompileToFunctions } from 'web/server/compiler'
+import { installSSRHelpers } from './optimizing-compiler/runtime-helpers'
+
+import { isDef, isUndef, isTrue } from 'shared/util'
+
+import {
+ createComponent,
+ createComponentInstanceForVnode
+} from 'core/vdom/create-component'
+
+let warned = Object.create(null)
+const warnOnce = msg => {
+ if (!warned[msg]) {
+ warned[msg] = true
+ console.warn(`\n\u001b[31m${msg}\u001b[39m\n`)
+ }
+}
+
+const onCompilationError = (err, vm) => {
+ const trace = vm ? generateComponentTrace(vm) : ''
+ throw new Error(`\n\u001b[31m${err}${trace}\u001b[39m\n`)
+}
+
+const normalizeRender = vm => {
+ const { render, template, _scopeId } = vm.$options
+ if (isUndef(render)) {
+ if (template) {
+ const compiled = ssrCompileToFunctions(template, {
+ scopeId: _scopeId,
+ warn: onCompilationError
+ }, vm)
+
+ vm.$options.render = compiled.render
+ vm.$options.staticRenderFns = compiled.staticRenderFns
+ } else {
+ throw new Error(
+ `render function or template not defined in component: ${
+ vm.$options.name || vm.$options._componentTag || 'anonymous'
+ }`
+ )
+ }
+ }
+}
+
+function renderNode (node, isRoot, context) {
+ if (node.isString) {
+ renderStringNode(node, context)
+ } else if (isDef(node.componentOptions)) {
+ renderComponent(node, isRoot, context)
+ } else if (isDef(node.tag)) {
+ renderElement(node, isRoot, context)
+ } else if (isTrue(node.isComment)) {
+ if (isDef(node.asyncFactory)) {
+ // async component
+ renderAsyncComponent(node, isRoot, context)
+ } else {
+ context.write(`<!--${node.text}-->`, context.next)
+ }
+ } else {
+ context.write(
+ node.raw ? node.text : escape(String(node.text)),
+ context.next
+ )
+ }
+}
+
+function registerComponentForCache (options, write) {
+ // exposed by vue-loader, need to call this if cache hit because
+ // component lifecycle hooks will not be called.
+ const register = options._ssrRegister
+ if (write.caching && isDef(register)) {
+ write.componentBuffer[write.componentBuffer.length - 1].add(register)
+ }
+ return register
+}
+
+function renderComponent (node, isRoot, context) {
+ const { write, next, userContext } = context
+
+ // check cache hit
+ const Ctor = node.componentOptions.Ctor
+ const getKey = Ctor.options.serverCacheKey
+ const name = Ctor.options.name
+ const cache = context.cache
+ const registerComponent = registerComponentForCache(Ctor.options, write)
+
+ if (isDef(getKey) && isDef(cache) && isDef(name)) {
+ const key = name + '::' + getKey(node.componentOptions.propsData)
+ const { has, get } = context
+ if (isDef(has)) {
+ has(key, hit => {
+ if (hit === true && isDef(get)) {
+ get(key, res => {
+ if (isDef(registerComponent)) {
+ registerComponent(userContext)
+ }
+ res.components.forEach(register => register(userContext))
+ write(res.html, next)
+ })
+ } else {
+ renderComponentWithCache(node, isRoot, key, context)
+ }
+ })
+ } else if (isDef(get)) {
+ get(key, res => {
+ if (isDef(res)) {
+ if (isDef(registerComponent)) {
+ registerComponent(userContext)
+ }
+ res.components.forEach(register => register(userContext))
+ write(res.html, next)
+ } else {
+ renderComponentWithCache(node, isRoot, key, context)
+ }
+ })
+ }
+ } else {
+ if (isDef(getKey) && isUndef(cache)) {
+ warnOnce(
+ `[vue-server-renderer] Component ${
+ Ctor.options.name || '(anonymous)'
+ } implemented serverCacheKey, ` +
+ 'but no cache was provided to the renderer.'
+ )
+ }
+ if (isDef(getKey) && isUndef(name)) {
+ warnOnce(
+ `[vue-server-renderer] Components that implement "serverCacheKey" ` +
+ `must also define a unique "name" option.`
+ )
+ }
+ renderComponentInner(node, isRoot, context)
+ }
+}
+
+function renderComponentWithCache (node, isRoot, key, context) {
+ const write = context.write
+ write.caching = true
+ const buffer = write.cacheBuffer
+ const bufferIndex = buffer.push('') - 1
+ const componentBuffer = write.componentBuffer
+ componentBuffer.push(new Set())
+ context.renderStates.push({
+ type: 'ComponentWithCache',
+ key,
+ buffer,
+ bufferIndex,
+ componentBuffer
+ })
+ renderComponentInner(node, isRoot, context)
+}
+
+function renderComponentInner (node, isRoot, context) {
+ const prevActive = context.activeInstance
+ // expose userContext on vnode
+ node.ssrContext = context.userContext
+ const child = context.activeInstance = createComponentInstanceForVnode(
+ node,
+ context.activeInstance
+ )
+ normalizeRender(child)
+ const childNode = child._render()
+ childNode.parent = node
+ context.renderStates.push({
+ type: 'Component',
+ prevActive
+ })
+ renderNode(childNode, isRoot, context)
+}
+
+function renderAsyncComponent (node, isRoot, context) {
+ const factory = node.asyncFactory
+
+ const resolve = comp => {
+ if (comp.__esModule && comp.default) {
+ comp = comp.default
+ }
+ const { data, children, tag } = node.asyncMeta
+ const nodeContext = node.asyncMeta.context
+ const resolvedNode: any = createComponent(
+ comp,
+ data,
+ nodeContext,
+ children,
+ tag
+ )
+ if (resolvedNode) {
+ if (resolvedNode.componentOptions) {
+ // normal component
+ renderComponent(resolvedNode, isRoot, context)
+ } else if (!Array.isArray(resolvedNode)) {
+ // single return node from functional component
+ renderNode(resolvedNode, isRoot, context)
+ } else {
+ // multiple return nodes from functional component
+ context.renderStates.push({
+ type: 'Fragment',
+ children: resolvedNode,
+ rendered: 0,
+ total: resolvedNode.length
+ })
+ context.next()
+ }
+ } else {
+ // invalid component, but this does not throw on the client
+ // so render empty comment node
+ context.write(`<!---->`, context.next)
+ }
+ }
+
+ if (factory.resolved) {
+ resolve(factory.resolved)
+ return
+ }
+
+ const reject = context.done
+ let res
+ try {
+ res = factory(resolve, reject)
+ } catch (e) {
+ reject(e)
+ }
+ if (res) {
+ if (typeof res.then === 'function') {
+ res.then(resolve, reject).catch(reject)
+ } else {
+ // new syntax in 2.3
+ const comp = res.component
+ if (comp && typeof comp.then === 'function') {
+ comp.then(resolve, reject).catch(reject)
+ }
+ }
+ }
+}
+
+function renderStringNode (el, context) {
+ const { write, next } = context
+ if (isUndef(el.children) || el.children.length === 0) {
+ write(el.open + (el.close || ''), next)
+ } else {
+ const children: Array<VNode> = el.children
+ context.renderStates.push({
+ type: 'Element',
+ children,
+ rendered: 0,
+ total: children.length,
+ endTag: el.close
+ })
+ write(el.open, next)
+ }
+}
+
+function renderElement (el, isRoot, context) {
+ const { write, next } = context
+
+ if (isTrue(isRoot)) {
+ if (!el.data) el.data = {}
+ if (!el.data.attrs) el.data.attrs = {}
+ el.data.attrs[SSR_ATTR] = 'true'
+ }
+
+ if (el.fnOptions) {
+ registerComponentForCache(el.fnOptions, write)
+ }
+
+ const startTag = renderStartingTag(el, context)
+ const endTag = `</${el.tag}>`
+ if (context.isUnaryTag(el.tag)) {
+ write(startTag, next)
+ } else if (isUndef(el.children) || el.children.length === 0) {
+ write(startTag + endTag, next)
+ } else {
+ const children: Array<VNode> = el.children
+ context.renderStates.push({
+ type: 'Element',
+ children,
+ rendered: 0,
+ total: children.length,
+ endTag
+ })
+ write(startTag, next)
+ }
+}
+
+function hasAncestorData (node: VNode) {
+ const parentNode = node.parent
+ return isDef(parentNode) && (isDef(parentNode.data) || hasAncestorData(parentNode))
+}
+
+function getVShowDirectiveInfo (node: VNode): ?VNodeDirective {
+ let dir: VNodeDirective
+ let tmp
+
+ while (isDef(node)) {
+ if (node.data && node.data.directives) {
+ tmp = node.data.directives.find(dir => dir.name === 'show')
+ if (tmp) {
+ dir = tmp
+ }
+ }
+ node = node.parent
+ }
+ return dir
+}
+
+function renderStartingTag (node: VNode, context) {
+ let markup = `<${node.tag}`
+ const { directives, modules } = context
+
+ // construct synthetic data for module processing
+ // because modules like style also produce code by parent VNode data
+ if (isUndef(node.data) && hasAncestorData(node)) {
+ node.data = {}
+ }
+ if (isDef(node.data)) {
+ // check directives
+ const dirs = node.data.directives
+ if (dirs) {
+ for (let i = 0; i < dirs.length; i++) {
+ const name = dirs[i].name
+ const dirRenderer = directives[name]
+ if (dirRenderer && name !== 'show') {
+ // directives mutate the node's data
+ // which then gets rendered by modules
+ dirRenderer(node, dirs[i])
+ }
+ }
+ }
+
+ // v-show directive needs to be merged from parent to child
+ const vshowDirectiveInfo = getVShowDirectiveInfo(node)
+ if (vshowDirectiveInfo) {
+ directives.show(node, vshowDirectiveInfo)
+ }
+
+ // apply other modules
+ for (let i = 0; i < modules.length; i++) {
+ const res = modules[i](node)
+ if (res) {
+ markup += res
+ }
+ }
+ }
+ // attach scoped CSS ID
+ let scopeId
+ const activeInstance = context.activeInstance
+ if (isDef(activeInstance) &&
+ activeInstance !== node.context &&
+ isDef(scopeId = activeInstance.$options._scopeId)
+ ) {
+ markup += ` ${(scopeId: any)}`
+ }
+ if (isDef(node.fnScopeId)) {
+ markup += ` ${node.fnScopeId}`
+ } else {
+ while (isDef(node)) {
+ if (isDef(scopeId = node.context.$options._scopeId)) {
+ markup += ` ${scopeId}`
+ }
+ node = node.parent
+ }
+ }
+ return markup + '>'
+}
+
+export function createRenderFunction (
+ modules: Array<(node: VNode) => ?string>,
+ directives: Object,
+ isUnaryTag: Function,
+ cache: any
+) {
+ return function render (
+ component: Component,
+ write: (text: string, next: Function) => void,
+ userContext: ?Object,
+ done: Function
+ ) {
+ warned = Object.create(null)
+ const context = new RenderContext({
+ activeInstance: component,
+ userContext,
+ write, done, renderNode,
+ isUnaryTag, modules, directives,
+ cache
+ })
+ installSSRHelpers(component)
+ normalizeRender(component)
+ renderNode(component._render(), true, context)
+ }
+}