aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vue/src/server')
-rw-r--r--node_modules/vue/src/server/bundle-renderer/create-bundle-renderer.js151
-rw-r--r--node_modules/vue/src/server/bundle-renderer/create-bundle-runner.js150
-rw-r--r--node_modules/vue/src/server/bundle-renderer/source-map-support.js45
-rw-r--r--node_modules/vue/src/server/create-basic-renderer.js37
-rw-r--r--node_modules/vue/src/server/create-renderer.js120
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/codegen.js260
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/index.js20
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/modules.js126
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/optimizer.js140
-rw-r--r--node_modules/vue/src/server/optimizing-compiler/runtime-helpers.js150
-rw-r--r--node_modules/vue/src/server/render-context.js130
-rw-r--r--node_modules/vue/src/server/render-stream.js94
-rw-r--r--node_modules/vue/src/server/render.js396
-rw-r--r--node_modules/vue/src/server/template-renderer/create-async-file-mapper.js53
-rw-r--r--node_modules/vue/src/server/template-renderer/index.js257
-rw-r--r--node_modules/vue/src/server/template-renderer/parse-template.js42
-rw-r--r--node_modules/vue/src/server/template-renderer/template-stream.js82
-rw-r--r--node_modules/vue/src/server/util.js18
-rw-r--r--node_modules/vue/src/server/webpack-plugin/client.js70
-rw-r--r--node_modules/vue/src/server/webpack-plugin/server.js66
-rw-r--r--node_modules/vue/src/server/webpack-plugin/util.js24
-rw-r--r--node_modules/vue/src/server/write.js50
22 files changed, 2481 insertions, 0 deletions
diff --git a/node_modules/vue/src/server/bundle-renderer/create-bundle-renderer.js b/node_modules/vue/src/server/bundle-renderer/create-bundle-renderer.js
new file mode 100644
index 00000000..05c60c35
--- /dev/null
+++ b/node_modules/vue/src/server/bundle-renderer/create-bundle-renderer.js
@@ -0,0 +1,151 @@
+/* @flow */
+
+import { createPromiseCallback } from '../util'
+import { createBundleRunner } from './create-bundle-runner'
+import type { Renderer, RenderOptions } from '../create-renderer'
+import { createSourceMapConsumers, rewriteErrorTrace } from './source-map-support'
+
+const fs = require('fs')
+const path = require('path')
+const PassThrough = require('stream').PassThrough
+
+const INVALID_MSG =
+ 'Invalid server-rendering bundle format. Should be a string ' +
+ 'or a bundle Object of type:\n\n' +
+`{
+ entry: string;
+ files: { [filename: string]: string; };
+ maps: { [filename: string]: string; };
+}\n`
+
+// The render bundle can either be a string (single bundled file)
+// or a bundle manifest object generated by vue-ssr-webpack-plugin.
+type RenderBundle = {
+ basedir?: string;
+ entry: string;
+ files: { [filename: string]: string; };
+ maps: { [filename: string]: string; };
+ modules?: { [filename: string]: Array<string> };
+};
+
+export function createBundleRendererCreator (
+ createRenderer: (options?: RenderOptions) => Renderer
+) {
+ return function createBundleRenderer (
+ bundle: string | RenderBundle,
+ rendererOptions?: RenderOptions = {}
+ ) {
+ let files, entry, maps
+ let basedir = rendererOptions.basedir
+
+ // load bundle if given filepath
+ if (
+ typeof bundle === 'string' &&
+ /\.js(on)?$/.test(bundle) &&
+ path.isAbsolute(bundle)
+ ) {
+ if (fs.existsSync(bundle)) {
+ const isJSON = /\.json$/.test(bundle)
+ basedir = basedir || path.dirname(bundle)
+ bundle = fs.readFileSync(bundle, 'utf-8')
+ if (isJSON) {
+ try {
+ bundle = JSON.parse(bundle)
+ } catch (e) {
+ throw new Error(`Invalid JSON bundle file: ${bundle}`)
+ }
+ }
+ } else {
+ throw new Error(`Cannot locate bundle file: ${bundle}`)
+ }
+ }
+
+ if (typeof bundle === 'object') {
+ entry = bundle.entry
+ files = bundle.files
+ basedir = basedir || bundle.basedir
+ maps = createSourceMapConsumers(bundle.maps)
+ if (typeof entry !== 'string' || typeof files !== 'object') {
+ throw new Error(INVALID_MSG)
+ }
+ } else if (typeof bundle === 'string') {
+ entry = '__vue_ssr_bundle__'
+ files = { '__vue_ssr_bundle__': bundle }
+ maps = {}
+ } else {
+ throw new Error(INVALID_MSG)
+ }
+
+ const renderer = createRenderer(rendererOptions)
+
+ const run = createBundleRunner(
+ entry,
+ files,
+ basedir,
+ rendererOptions.runInNewContext
+ )
+
+ return {
+ renderToString: (context?: Object, cb: any) => {
+ if (typeof context === 'function') {
+ cb = context
+ context = {}
+ }
+
+ let promise
+ if (!cb) {
+ ({ promise, cb } = createPromiseCallback())
+ }
+
+ run(context).catch(err => {
+ rewriteErrorTrace(err, maps)
+ cb(err)
+ }).then(app => {
+ if (app) {
+ renderer.renderToString(app, context, (err, res) => {
+ rewriteErrorTrace(err, maps)
+ cb(err, res)
+ })
+ }
+ })
+
+ return promise
+ },
+
+ renderToStream: (context?: Object) => {
+ const res = new PassThrough()
+ run(context).catch(err => {
+ rewriteErrorTrace(err, maps)
+ // avoid emitting synchronously before user can
+ // attach error listener
+ process.nextTick(() => {
+ res.emit('error', err)
+ })
+ }).then(app => {
+ if (app) {
+ const renderStream = renderer.renderToStream(app, context)
+
+ renderStream.on('error', err => {
+ rewriteErrorTrace(err, maps)
+ res.emit('error', err)
+ })
+
+ // relay HTMLStream special events
+ if (rendererOptions && rendererOptions.template) {
+ renderStream.on('beforeStart', () => {
+ res.emit('beforeStart')
+ })
+ renderStream.on('beforeEnd', () => {
+ res.emit('beforeEnd')
+ })
+ }
+
+ renderStream.pipe(res)
+ }
+ })
+
+ return res
+ }
+ }
+ }
+}
diff --git a/node_modules/vue/src/server/bundle-renderer/create-bundle-runner.js b/node_modules/vue/src/server/bundle-renderer/create-bundle-runner.js
new file mode 100644
index 00000000..16c33c41
--- /dev/null
+++ b/node_modules/vue/src/server/bundle-renderer/create-bundle-runner.js
@@ -0,0 +1,150 @@
+import { isPlainObject } from 'shared/util'
+
+const vm = require('vm')
+const path = require('path')
+const resolve = require('resolve')
+const NativeModule = require('module')
+
+function createSandbox (context) {
+ const sandbox = {
+ Buffer,
+ console,
+ process,
+ setTimeout,
+ setInterval,
+ setImmediate,
+ clearTimeout,
+ clearInterval,
+ clearImmediate,
+ __VUE_SSR_CONTEXT__: context
+ }
+ sandbox.global = sandbox
+ return sandbox
+}
+
+function compileModule (files, basedir, runInNewContext) {
+ const compiledScripts = {}
+ const resolvedModules = {}
+
+ function getCompiledScript (filename) {
+ if (compiledScripts[filename]) {
+ return compiledScripts[filename]
+ }
+ const code = files[filename]
+ const wrapper = NativeModule.wrap(code)
+ const script = new vm.Script(wrapper, {
+ filename,
+ displayErrors: true
+ })
+ compiledScripts[filename] = script
+ return script
+ }
+
+ function evaluateModule (filename, sandbox, evaluatedFiles = {}) {
+ if (evaluatedFiles[filename]) {
+ return evaluatedFiles[filename]
+ }
+
+ const script = getCompiledScript(filename)
+ const compiledWrapper = runInNewContext === false
+ ? script.runInThisContext()
+ : script.runInNewContext(sandbox)
+ const m = { exports: {}}
+ const r = file => {
+ file = path.posix.join('.', file)
+ if (files[file]) {
+ return evaluateModule(file, sandbox, evaluatedFiles)
+ } else if (basedir) {
+ return require(
+ resolvedModules[file] ||
+ (resolvedModules[file] = resolve.sync(file, { basedir }))
+ )
+ } else {
+ return require(file)
+ }
+ }
+ compiledWrapper.call(m.exports, m.exports, r, m)
+
+ const res = Object.prototype.hasOwnProperty.call(m.exports, 'default')
+ ? m.exports.default
+ : m.exports
+ evaluatedFiles[filename] = res
+ return res
+ }
+ return evaluateModule
+}
+
+function deepClone (val) {
+ if (isPlainObject(val)) {
+ const res = {}
+ for (const key in val) {
+ res[key] = deepClone(val[key])
+ }
+ return res
+ } else if (Array.isArray(val)) {
+ return val.slice()
+ } else {
+ return val
+ }
+}
+
+export function createBundleRunner (entry, files, basedir, runInNewContext) {
+ const evaluate = compileModule(files, basedir, runInNewContext)
+ if (runInNewContext !== false && runInNewContext !== 'once') {
+ // new context mode: creates a fresh context and re-evaluate the bundle
+ // on each render. Ensures entire application state is fresh for each
+ // render, but incurs extra evaluation cost.
+ return (userContext = {}) => new Promise(resolve => {
+ userContext._registeredComponents = new Set()
+ const res = evaluate(entry, createSandbox(userContext))
+ resolve(typeof res === 'function' ? res(userContext) : res)
+ })
+ } else {
+ // direct mode: instead of re-evaluating the whole bundle on
+ // each render, it simply calls the exported function. This avoids the
+ // module evaluation costs but requires the source code to be structured
+ // slightly differently.
+ let runner // lazy creation so that errors can be caught by user
+ let initialContext
+ return (userContext = {}) => new Promise(resolve => {
+ if (!runner) {
+ const sandbox = runInNewContext === 'once'
+ ? createSandbox()
+ : global
+ // the initial context is only used for collecting possible non-component
+ // styles injected by vue-style-loader.
+ initialContext = sandbox.__VUE_SSR_CONTEXT__ = {}
+ runner = evaluate(entry, sandbox)
+ // On subsequent renders, __VUE_SSR_CONTEXT__ will not be available
+ // to prevent cross-request pollution.
+ delete sandbox.__VUE_SSR_CONTEXT__
+ if (typeof runner !== 'function') {
+ throw new Error(
+ 'bundle export should be a function when using ' +
+ '{ runInNewContext: false }.'
+ )
+ }
+ }
+ userContext._registeredComponents = new Set()
+
+ // vue-style-loader styles imported outside of component lifecycle hooks
+ if (initialContext._styles) {
+ userContext._styles = deepClone(initialContext._styles)
+ // #6353 ensure "styles" is exposed even if no styles are injected
+ // in component lifecycles.
+ // the renderStyles fn is exposed by vue-style-loader >= 3.0.3
+ const renderStyles = initialContext._renderStyles
+ if (renderStyles) {
+ Object.defineProperty(userContext, 'styles', {
+ enumerable: true,
+ get () {
+ return renderStyles(userContext._styles)
+ }
+ })
+ }
+ }
+
+ resolve(runner(userContext))
+ })
+ }
+}
diff --git a/node_modules/vue/src/server/bundle-renderer/source-map-support.js b/node_modules/vue/src/server/bundle-renderer/source-map-support.js
new file mode 100644
index 00000000..1010532e
--- /dev/null
+++ b/node_modules/vue/src/server/bundle-renderer/source-map-support.js
@@ -0,0 +1,45 @@
+/* @flow */
+
+const SourceMapConsumer = require('source-map').SourceMapConsumer
+
+const filenameRE = /\(([^)]+\.js):(\d+):(\d+)\)$/
+
+export function createSourceMapConsumers (rawMaps: Object) {
+ const maps = {}
+ Object.keys(rawMaps).forEach(file => {
+ maps[file] = new SourceMapConsumer(rawMaps[file])
+ })
+ return maps
+}
+
+export function rewriteErrorTrace (e: any, mapConsumers: {
+ [key: string]: SourceMapConsumer
+}) {
+ if (e && typeof e.stack === 'string') {
+ e.stack = e.stack.split('\n').map(line => {
+ return rewriteTraceLine(line, mapConsumers)
+ }).join('\n')
+ }
+}
+
+function rewriteTraceLine (trace: string, mapConsumers: {
+ [key: string]: SourceMapConsumer
+}) {
+ const m = trace.match(filenameRE)
+ const map = m && mapConsumers[m[1]]
+ if (m != null && map) {
+ const originalPosition = map.originalPositionFor({
+ line: Number(m[2]),
+ column: Number(m[3])
+ })
+ if (originalPosition.source != null) {
+ const { source, line, column } = originalPosition
+ const mappedPosition = `(${source.replace(/^webpack:\/\/\//, '')}:${String(line)}:${String(column)})`
+ return trace.replace(filenameRE, mappedPosition)
+ } else {
+ return trace
+ }
+ } else {
+ return trace
+ }
+}
diff --git a/node_modules/vue/src/server/create-basic-renderer.js b/node_modules/vue/src/server/create-basic-renderer.js
new file mode 100644
index 00000000..2d53976d
--- /dev/null
+++ b/node_modules/vue/src/server/create-basic-renderer.js
@@ -0,0 +1,37 @@
+/* @flow */
+
+import { createWriteFunction } from './write'
+import { createRenderFunction } from './render'
+import type { RenderOptions } from './create-renderer'
+
+export function createBasicRenderer ({
+ modules = [],
+ directives = {},
+ isUnaryTag = (() => false),
+ cache
+}: RenderOptions = {}) {
+ const render = createRenderFunction(modules, directives, isUnaryTag, cache)
+
+ return function renderToString (
+ component: Component,
+ context: any,
+ done: any
+ ): void {
+ if (typeof context === 'function') {
+ done = context
+ context = {}
+ }
+ let result = ''
+ const write = createWriteFunction(text => {
+ result += text
+ return false
+ }, done)
+ try {
+ render(component, write, context, () => {
+ done(null, result)
+ })
+ } catch (e) {
+ done(e)
+ }
+ }
+}
diff --git a/node_modules/vue/src/server/create-renderer.js b/node_modules/vue/src/server/create-renderer.js
new file mode 100644
index 00000000..c045a426
--- /dev/null
+++ b/node_modules/vue/src/server/create-renderer.js
@@ -0,0 +1,120 @@
+/* @flow */
+
+import RenderStream from './render-stream'
+import { createWriteFunction } from './write'
+import { createRenderFunction } from './render'
+import { createPromiseCallback } from './util'
+import TemplateRenderer from './template-renderer/index'
+import type { ClientManifest } from './template-renderer/index'
+
+export type Renderer = {
+ renderToString: (component: Component, context: any, cb: any) => ?Promise<string>;
+ renderToStream: (component: Component, context?: Object) => stream$Readable;
+};
+
+type RenderCache = {
+ get: (key: string, cb?: Function) => string | void;
+ set: (key: string, val: string) => void;
+ has?: (key: string, cb?: Function) => boolean | void;
+};
+
+export type RenderOptions = {
+ modules?: Array<(vnode: VNode) => ?string>;
+ directives?: Object;
+ isUnaryTag?: Function;
+ cache?: RenderCache;
+ template?: string;
+ inject?: boolean;
+ basedir?: string;
+ shouldPreload?: Function;
+ shouldPrefetch?: Function;
+ clientManifest?: ClientManifest;
+ runInNewContext?: boolean | 'once';
+};
+
+export function createRenderer ({
+ modules = [],
+ directives = {},
+ isUnaryTag = (() => false),
+ template,
+ inject,
+ cache,
+ shouldPreload,
+ shouldPrefetch,
+ clientManifest
+}: RenderOptions = {}): Renderer {
+ const render = createRenderFunction(modules, directives, isUnaryTag, cache)
+ const templateRenderer = new TemplateRenderer({
+ template,
+ inject,
+ shouldPreload,
+ shouldPrefetch,
+ clientManifest
+ })
+
+ return {
+ renderToString (
+ component: Component,
+ context: any,
+ cb: any
+ ): ?Promise<string> {
+ if (typeof context === 'function') {
+ cb = context
+ context = {}
+ }
+ if (context) {
+ templateRenderer.bindRenderFns(context)
+ }
+
+ // no callback, return Promise
+ let promise
+ if (!cb) {
+ ({ promise, cb } = createPromiseCallback())
+ }
+
+ let result = ''
+ const write = createWriteFunction(text => {
+ result += text
+ return false
+ }, cb)
+ try {
+ render(component, write, context, err => {
+ if (template) {
+ result = templateRenderer.renderSync(result, context)
+ }
+ if (err) {
+ cb(err)
+ } else {
+ cb(null, result)
+ }
+ })
+ } catch (e) {
+ cb(e)
+ }
+
+ return promise
+ },
+
+ renderToStream (
+ component: Component,
+ context?: Object
+ ): stream$Readable {
+ if (context) {
+ templateRenderer.bindRenderFns(context)
+ }
+ const renderStream = new RenderStream((write, done) => {
+ render(component, write, context, done)
+ })
+ if (!template) {
+ return renderStream
+ } else {
+ const templateStream = templateRenderer.createStream(context)
+ renderStream.on('error', err => {
+ templateStream.emit('error', err)
+ })
+ renderStream.pipe(templateStream)
+ return templateStream
+ }
+ }
+ }
+}
diff --git a/node_modules/vue/src/server/optimizing-compiler/codegen.js b/node_modules/vue/src/server/optimizing-compiler/codegen.js
new file mode 100644
index 00000000..26097c07
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/codegen.js
@@ -0,0 +1,260 @@
+/* @flow */
+
+// The SSR codegen is essentially extending the default codegen to handle
+// SSR-optimizable nodes and turn them into string render fns. In cases where
+// a node is not optimizable it simply falls back to the default codegen.
+
+import {
+ genIf,
+ genFor,
+ genData,
+ genText,
+ genElement,
+ genChildren,
+ CodegenState
+} from 'compiler/codegen/index'
+
+import {
+ genAttrSegments,
+ genDOMPropSegments,
+ genClassSegments,
+ genStyleSegments,
+ applyModelTransform
+} from './modules'
+
+import { escape } from 'web/server/util'
+import { optimizability } from './optimizer'
+import type { CodegenResult } from 'compiler/codegen/index'
+
+export type StringSegment = {
+ type: number;
+ value: string;
+};
+
+// segment types
+export const RAW = 0
+export const INTERPOLATION = 1
+export const EXPRESSION = 2
+
+export function generate (
+ ast: ASTElement | void,
+ options: CompilerOptions
+): CodegenResult {
+ const state = new CodegenState(options)
+ const code = ast ? genSSRElement(ast, state) : '_c("div")'
+ return {
+ render: `with(this){return ${code}}`,
+ staticRenderFns: state.staticRenderFns
+ }
+}
+
+function genSSRElement (el: ASTElement, state: CodegenState): string {
+ if (el.for && !el.forProcessed) {
+ return genFor(el, state, genSSRElement)
+ } else if (el.if && !el.ifProcessed) {
+ return genIf(el, state, genSSRElement)
+ } else if (el.tag === 'template' && !el.slotTarget) {
+ return el.ssrOptimizability === optimizability.FULL
+ ? genChildrenAsStringNode(el, state)
+ : genSSRChildren(el, state) || 'void 0'
+ }
+
+ switch (el.ssrOptimizability) {
+ case optimizability.FULL:
+ // stringify whole tree
+ return genStringElement(el, state)
+ case optimizability.SELF:
+ // stringify self and check children
+ return genStringElementWithChildren(el, state)
+ case optimizability.CHILDREN:
+ // generate self as VNode and stringify children
+ return genNormalElement(el, state, true)
+ case optimizability.PARTIAL:
+ // generate self as VNode and check children
+ return genNormalElement(el, state, false)
+ default:
+ // bail whole tree
+ return genElement(el, state)
+ }
+}
+
+function genNormalElement (el, state, stringifyChildren) {
+ const data = el.plain ? undefined : genData(el, state)
+ const children = stringifyChildren
+ ? `[${genChildrenAsStringNode(el, state)}]`
+ : genSSRChildren(el, state, true)
+ return `_c('${el.tag}'${
+ data ? `,${data}` : ''
+ }${
+ children ? `,${children}` : ''
+ })`
+}
+
+function genSSRChildren (el, state, checkSkip) {
+ return genChildren(el, state, checkSkip, genSSRElement, genSSRNode)
+}
+
+function genSSRNode (el, state) {
+ return el.type === 1
+ ? genSSRElement(el, state)
+ : genText(el)
+}
+
+function genChildrenAsStringNode (el, state) {
+ return el.children.length
+ ? `_ssrNode(${flattenSegments(childrenToSegments(el, state))})`
+ : ''
+}
+
+function genStringElement (el, state) {
+ return `_ssrNode(${elementToString(el, state)})`
+}
+
+function genStringElementWithChildren (el, state) {
+ const children = genSSRChildren(el, state, true)
+ return `_ssrNode(${
+ flattenSegments(elementToOpenTagSegments(el, state))
+ },"</${el.tag}>"${
+ children ? `,${children}` : ''
+ })`
+}
+
+function elementToString (el, state) {
+ return `(${flattenSegments(elementToSegments(el, state))})`
+}
+
+function elementToSegments (el, state): Array<StringSegment> {
+ // v-for / v-if
+ if (el.for && !el.forProcessed) {
+ el.forProcessed = true
+ return [{
+ type: EXPRESSION,
+ value: genFor(el, state, elementToString, '_ssrList')
+ }]
+ } else if (el.if && !el.ifProcessed) {
+ el.ifProcessed = true
+ return [{
+ type: EXPRESSION,
+ value: genIf(el, state, elementToString, '"<!---->"')
+ }]
+ } else if (el.tag === 'template') {
+ return childrenToSegments(el, state)
+ }
+
+ const openSegments = elementToOpenTagSegments(el, state)
+ const childrenSegments = childrenToSegments(el, state)
+ const { isUnaryTag } = state.options
+ const close = (isUnaryTag && isUnaryTag(el.tag))
+ ? []
+ : [{ type: RAW, value: `</${el.tag}>` }]
+ return openSegments.concat(childrenSegments, close)
+}
+
+function elementToOpenTagSegments (el, state): Array<StringSegment> {
+ applyModelTransform(el, state)
+ let binding
+ const segments = [{ type: RAW, value: `<${el.tag}` }]
+ // attrs
+ if (el.attrs) {
+ segments.push.apply(segments, genAttrSegments(el.attrs))
+ }
+ // domProps
+ if (el.props) {
+ segments.push.apply(segments, genDOMPropSegments(el.props, el.attrs))
+ }
+ // v-bind="object"
+ if ((binding = el.attrsMap['v-bind'])) {
+ segments.push({ type: EXPRESSION, value: `_ssrAttrs(${binding})` })
+ }
+ // v-bind.prop="object"
+ if ((binding = el.attrsMap['v-bind.prop'])) {
+ segments.push({ type: EXPRESSION, value: `_ssrDOMProps(${binding})` })
+ }
+ // class
+ if (el.staticClass || el.classBinding) {
+ segments.push.apply(
+ segments,
+ genClassSegments(el.staticClass, el.classBinding)
+ )
+ }
+ // style & v-show
+ if (el.staticStyle || el.styleBinding || el.attrsMap['v-show']) {
+ segments.push.apply(
+ segments,
+ genStyleSegments(
+ el.attrsMap.style,
+ el.staticStyle,
+ el.styleBinding,
+ el.attrsMap['v-show']
+ )
+ )
+ }
+ // _scopedId
+ if (state.options.scopeId) {
+ segments.push({ type: RAW, value: ` ${state.options.scopeId}` })
+ }
+ segments.push({ type: RAW, value: `>` })
+ return segments
+}
+
+function childrenToSegments (el, state): Array<StringSegment> {
+ let binding
+ if ((binding = el.attrsMap['v-html'])) {
+ return [{ type: EXPRESSION, value: `_s(${binding})` }]
+ }
+ if ((binding = el.attrsMap['v-text'])) {
+ return [{ type: INTERPOLATION, value: `_s(${binding})` }]
+ }
+ if (el.tag === 'textarea' && (binding = el.attrsMap['v-model'])) {
+ return [{ type: INTERPOLATION, value: `_s(${binding})` }]
+ }
+ return el.children
+ ? nodesToSegments(el.children, state)
+ : []
+}
+
+function nodesToSegments (
+ children: Array<ASTNode>,
+ state: CodegenState
+): Array<StringSegment> {
+ const segments = []
+ for (let i = 0; i < children.length; i++) {
+ const c = children[i]
+ if (c.type === 1) {
+ segments.push.apply(segments, elementToSegments(c, state))
+ } else if (c.type === 2) {
+ segments.push({ type: INTERPOLATION, value: c.expression })
+ } else if (c.type === 3) {
+ segments.push({ type: RAW, value: escape(c.text) })
+ }
+ }
+ return segments
+}
+
+function flattenSegments (segments: Array<StringSegment>): string {
+ const mergedSegments = []
+ let textBuffer = ''
+
+ const pushBuffer = () => {
+ if (textBuffer) {
+ mergedSegments.push(JSON.stringify(textBuffer))
+ textBuffer = ''
+ }
+ }
+
+ for (let i = 0; i < segments.length; i++) {
+ const s = segments[i]
+ if (s.type === RAW) {
+ textBuffer += s.value
+ } else if (s.type === INTERPOLATION) {
+ pushBuffer()
+ mergedSegments.push(`_ssrEscape(${s.value})`)
+ } else if (s.type === EXPRESSION) {
+ pushBuffer()
+ mergedSegments.push(`(${s.value})`)
+ }
+ }
+ pushBuffer()
+
+ return mergedSegments.join('+')
+}
diff --git a/node_modules/vue/src/server/optimizing-compiler/index.js b/node_modules/vue/src/server/optimizing-compiler/index.js
new file mode 100644
index 00000000..893630d6
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/index.js
@@ -0,0 +1,20 @@
+/* @flow */
+
+import { parse } from 'compiler/parser/index'
+import { generate } from './codegen'
+import { optimize } from './optimizer'
+import { createCompilerCreator } from 'compiler/create-compiler'
+
+export const createCompiler = createCompilerCreator(function baseCompile (
+ template: string,
+ options: CompilerOptions
+): CompiledResult {
+ const ast = parse(template.trim(), options)
+ optimize(ast, options)
+ const code = generate(ast, options)
+ return {
+ ast,
+ render: code.render,
+ staticRenderFns: code.staticRenderFns
+ }
+})
diff --git a/node_modules/vue/src/server/optimizing-compiler/modules.js b/node_modules/vue/src/server/optimizing-compiler/modules.js
new file mode 100644
index 00000000..69a7be87
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/modules.js
@@ -0,0 +1,126 @@
+/* @flow */
+
+import {
+ RAW,
+ // INTERPOLATION,
+ EXPRESSION
+} from './codegen'
+
+import {
+ propsToAttrMap,
+ isRenderableAttr
+} from 'web/server/util'
+
+import {
+ isBooleanAttr,
+ isEnumeratedAttr
+} from 'web/util/attrs'
+
+import type { StringSegment } from './codegen'
+import type { CodegenState } from 'compiler/codegen/index'
+
+type Attr = { name: string; value: string };
+
+const plainStringRE = /^"(?:[^"\\]|\\.)*"$|^'(?:[^'\\]|\\.)*'$/
+
+// let the model AST transform translate v-model into appropriate
+// props bindings
+export function applyModelTransform (el: ASTElement, state: CodegenState) {
+ if (el.directives) {
+ for (let i = 0; i < el.directives.length; i++) {
+ const dir = el.directives[i]
+ if (dir.name === 'model') {
+ state.directives.model(el, dir, state.warn)
+ // remove value for textarea as its converted to text
+ if (el.tag === 'textarea' && el.props) {
+ el.props = el.props.filter(p => p.name !== 'value')
+ }
+ break
+ }
+ }
+ }
+}
+
+export function genAttrSegments (
+ attrs: Array<Attr>
+): Array<StringSegment> {
+ return attrs.map(({ name, value }) => genAttrSegment(name, value))
+}
+
+export function genDOMPropSegments (
+ props: Array<Attr>,
+ attrs: ?Array<Attr>
+): Array<StringSegment> {
+ const segments = []
+ props.forEach(({ name, value }) => {
+ name = propsToAttrMap[name] || name.toLowerCase()
+ if (isRenderableAttr(name) &&
+ !(attrs && attrs.some(a => a.name === name))
+ ) {
+ segments.push(genAttrSegment(name, value))
+ }
+ })
+ return segments
+}
+
+function genAttrSegment (name: string, value: string): StringSegment {
+ if (plainStringRE.test(value)) {
+ // force double quote
+ value = value.replace(/^'|'$/g, '"')
+ // force enumerated attr to "true"
+ if (isEnumeratedAttr(name) && value !== `"false"`) {
+ value = `"true"`
+ }
+ return {
+ type: RAW,
+ value: isBooleanAttr(name)
+ ? ` ${name}="${name}"`
+ : value === '""'
+ ? ` ${name}`
+ : ` ${name}="${JSON.parse(value)}"`
+ }
+ } else {
+ return {
+ type: EXPRESSION,
+ value: `_ssrAttr(${JSON.stringify(name)},${value})`
+ }
+ }
+}
+
+export function genClassSegments (
+ staticClass: ?string,
+ classBinding: ?string
+): Array<StringSegment> {
+ if (staticClass && !classBinding) {
+ return [{ type: RAW, value: ` class=${staticClass}` }]
+ } else {
+ return [{
+ type: EXPRESSION,
+ value: `_ssrClass(${staticClass || 'null'},${classBinding || 'null'})`
+ }]
+ }
+}
+
+export function genStyleSegments (
+ staticStyle: ?string,
+ parsedStaticStyle: ?string,
+ styleBinding: ?string,
+ vShowExpression: ?string
+): Array<StringSegment> {
+ if (staticStyle && !styleBinding && !vShowExpression) {
+ return [{ type: RAW, value: ` style=${JSON.stringify(staticStyle)}` }]
+ } else {
+ return [{
+ type: EXPRESSION,
+ value: `_ssrStyle(${
+ parsedStaticStyle || 'null'
+ },${
+ styleBinding || 'null'
+ }, ${
+ vShowExpression
+ ? `{ display: (${vShowExpression}) ? '' : 'none' }`
+ : 'null'
+ })`
+ }]
+ }
+}
diff --git a/node_modules/vue/src/server/optimizing-compiler/optimizer.js b/node_modules/vue/src/server/optimizing-compiler/optimizer.js
new file mode 100644
index 00000000..d24acc55
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/optimizer.js
@@ -0,0 +1,140 @@
+/* @flow */
+
+/**
+ * In SSR, the vdom tree is generated only once and never patched, so
+ * we can optimize most element / trees into plain string render functions.
+ * The SSR optimizer walks the AST tree to detect optimizable elements and trees.
+ *
+ * The criteria for SSR optimizability is quite a bit looser than static tree
+ * detection (which is designed for client re-render). In SSR we bail only for
+ * components/slots/custom directives.
+ */
+
+import { no, makeMap, isBuiltInTag } from 'shared/util'
+
+// optimizability constants
+export const optimizability = {
+ FALSE: 0, // whole sub tree un-optimizable
+ FULL: 1, // whole sub tree optimizable
+ SELF: 2, // self optimizable but has some un-optimizable children
+ CHILDREN: 3, // self un-optimizable but have fully optimizable children
+ PARTIAL: 4 // self un-optimizable with some un-optimizable children
+}
+
+let isPlatformReservedTag
+
+export function optimize (root: ?ASTElement, options: CompilerOptions) {
+ if (!root) return
+ isPlatformReservedTag = options.isReservedTag || no
+ walk(root, true)
+}
+
+function walk (node: ASTNode, isRoot?: boolean) {
+ if (isUnOptimizableTree(node)) {
+ node.ssrOptimizability = optimizability.FALSE
+ return
+ }
+ // root node or nodes with custom directives should always be a VNode
+ const selfUnoptimizable = isRoot || hasCustomDirective(node)
+ const check = child => {
+ if (child.ssrOptimizability !== optimizability.FULL) {
+ node.ssrOptimizability = selfUnoptimizable
+ ? optimizability.PARTIAL
+ : optimizability.SELF
+ }
+ }
+ if (selfUnoptimizable) {
+ node.ssrOptimizability = optimizability.CHILDREN
+ }
+ if (node.type === 1) {
+ for (let i = 0, l = node.children.length; i < l; i++) {
+ const child = node.children[i]
+ walk(child)
+ check(child)
+ }
+ if (node.ifConditions) {
+ for (let i = 1, l = node.ifConditions.length; i < l; i++) {
+ const block = node.ifConditions[i].block
+ walk(block, isRoot)
+ check(block)
+ }
+ }
+ if (node.ssrOptimizability == null ||
+ (!isRoot && (node.attrsMap['v-html'] || node.attrsMap['v-text']))
+ ) {
+ node.ssrOptimizability = optimizability.FULL
+ } else {
+ node.children = optimizeSiblings(node)
+ }
+ } else {
+ node.ssrOptimizability = optimizability.FULL
+ }
+}
+
+function optimizeSiblings (el) {
+ const children = el.children
+ const optimizedChildren = []
+
+ let currentOptimizableGroup = []
+ const pushGroup = () => {
+ if (currentOptimizableGroup.length) {
+ optimizedChildren.push({
+ type: 1,
+ parent: el,
+ tag: 'template',
+ attrsList: [],
+ attrsMap: {},
+ children: currentOptimizableGroup,
+ ssrOptimizability: optimizability.FULL
+ })
+ }
+ currentOptimizableGroup = []
+ }
+
+ for (let i = 0; i < children.length; i++) {
+ const c = children[i]
+ if (c.ssrOptimizability === optimizability.FULL) {
+ currentOptimizableGroup.push(c)
+ } else {
+ // wrap fully-optimizable adjacent siblings inside a template tag
+ // so that they can be optimized into a single ssrNode by codegen
+ pushGroup()
+ optimizedChildren.push(c)
+ }
+ }
+ pushGroup()
+ return optimizedChildren
+}
+
+function isUnOptimizableTree (node: ASTNode): boolean {
+ if (node.type === 2 || node.type === 3) { // text or expression
+ return false
+ }
+ return (
+ isBuiltInTag(node.tag) || // built-in (slot, component)
+ !isPlatformReservedTag(node.tag) || // custom component
+ !!node.component || // "is" component
+ isSelectWithModel(node) // <select v-model> requires runtime inspection
+ )
+}
+
+const isBuiltInDir = makeMap('text,html,show,on,bind,model,pre,cloak,once')
+
+function hasCustomDirective (node: ASTNode): ?boolean {
+ return (
+ node.type === 1 &&
+ node.directives &&
+ node.directives.some(d => !isBuiltInDir(d.name))
+ )
+}
+
+// <select v-model> cannot be optimized because it requires a runtime check
+// to determine proper selected option
+function isSelectWithModel (node: ASTNode): boolean {
+ return (
+ node.type === 1 &&
+ node.tag === 'select' &&
+ node.directives != null &&
+ node.directives.some(d => d.name === 'model')
+ )
+}
diff --git a/node_modules/vue/src/server/optimizing-compiler/runtime-helpers.js b/node_modules/vue/src/server/optimizing-compiler/runtime-helpers.js
new file mode 100644
index 00000000..6f65de04
--- /dev/null
+++ b/node_modules/vue/src/server/optimizing-compiler/runtime-helpers.js
@@ -0,0 +1,150 @@
+/* @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))}`
+}
diff --git a/node_modules/vue/src/server/render-context.js b/node_modules/vue/src/server/render-context.js
new file mode 100644
index 00000000..178140cd
--- /dev/null
+++ b/node_modules/vue/src/server/render-context.js
@@ -0,0 +1,130 @@
+/* @flow */
+
+import { isUndef } from 'shared/util'
+
+type RenderState = {
+ type: 'Element';
+ rendered: number;
+ total: number;
+ children: Array<VNode>;
+ endTag: string;
+} | {
+ type: 'Fragment';
+ rendered: number;
+ total: number;
+ children: Array<VNode>;
+} | {
+ type: 'Component';
+ prevActive: Component;
+} | {
+ type: 'ComponentWithCache';
+ buffer: Array<string>;
+ bufferIndex: number;
+ componentBuffer: Array<Set<Class<Component>>>;
+ key: string;
+};
+
+export class RenderContext {
+ userContext: ?Object;
+ activeInstance: Component;
+ renderStates: Array<RenderState>;
+ write: (text: string, next: Function) => void;
+ renderNode: (node: VNode, isRoot: boolean, context: RenderContext) => void;
+ next: () => void;
+ done: (err: ?Error) => void;
+
+ modules: Array<(node: VNode) => ?string>;
+ directives: Object;
+ isUnaryTag: (tag: string) => boolean;
+
+ cache: any;
+ get: ?(key: string, cb: Function) => void;
+ has: ?(key: string, cb: Function) => void;
+
+ constructor (options: Object) {
+ this.userContext = options.userContext
+ this.activeInstance = options.activeInstance
+ this.renderStates = []
+
+ this.write = options.write
+ this.done = options.done
+ this.renderNode = options.renderNode
+
+ this.isUnaryTag = options.isUnaryTag
+ this.modules = options.modules
+ this.directives = options.directives
+
+ const cache = options.cache
+ if (cache && (!cache.get || !cache.set)) {
+ throw new Error('renderer cache must implement at least get & set.')
+ }
+ this.cache = cache
+ this.get = cache && normalizeAsync(cache, 'get')
+ this.has = cache && normalizeAsync(cache, 'has')
+
+ this.next = this.next.bind(this)
+ }
+
+ next () {
+ const lastState = this.renderStates[this.renderStates.length - 1]
+ if (isUndef(lastState)) {
+ return this.done()
+ }
+ switch (lastState.type) {
+ case 'Element':
+ case 'Fragment':
+ const { children, total } = lastState
+ const rendered = lastState.rendered++
+ if (rendered < total) {
+ this.renderNode(children[rendered], false, this)
+ } else {
+ this.renderStates.pop()
+ if (lastState.type === 'Element') {
+ this.write(lastState.endTag, this.next)
+ } else {
+ this.next()
+ }
+ }
+ break
+ case 'Component':
+ this.renderStates.pop()
+ this.activeInstance = lastState.prevActive
+ this.next()
+ break
+ case 'ComponentWithCache':
+ this.renderStates.pop()
+ const { buffer, bufferIndex, componentBuffer, key } = lastState
+ const result = {
+ html: buffer[bufferIndex],
+ components: componentBuffer[bufferIndex]
+ }
+ this.cache.set(key, result)
+ if (bufferIndex === 0) {
+ // this is a top-level cached component,
+ // exit caching mode.
+ this.write.caching = false
+ } else {
+ // parent component is also being cached,
+ // merge self into parent's result
+ buffer[bufferIndex - 1] += result.html
+ const prev = componentBuffer[bufferIndex - 1]
+ result.components.forEach(c => prev.add(c))
+ }
+ buffer.length = bufferIndex
+ componentBuffer.length = bufferIndex
+ this.next()
+ break
+ }
+ }
+}
+
+function normalizeAsync (cache, method) {
+ const fn = cache[method]
+ if (isUndef(fn)) {
+ return
+ } else if (fn.length > 1) {
+ return (key, cb) => fn.call(cache, key, cb)
+ } else {
+ return (key, cb) => cb(fn.call(cache, key))
+ }
+}
diff --git a/node_modules/vue/src/server/render-stream.js b/node_modules/vue/src/server/render-stream.js
new file mode 100644
index 00000000..d76012af
--- /dev/null
+++ b/node_modules/vue/src/server/render-stream.js
@@ -0,0 +1,94 @@
+/* @flow */
+
+/**
+ * Original RenderStream implementation by Sasha Aickin (@aickin)
+ * Licensed under the Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Modified by Evan You (@yyx990803)
+ */
+
+const stream = require('stream')
+
+import { isTrue, isUndef } from 'shared/util'
+import { createWriteFunction } from './write'
+
+export default class RenderStream extends stream.Readable {
+ buffer: string;
+ render: (write: Function, done: Function) => void;
+ expectedSize: number;
+ write: Function;
+ next: Function;
+ end: Function;
+ done: boolean;
+
+ constructor (render: Function) {
+ super()
+ this.buffer = ''
+ this.render = render
+ this.expectedSize = 0
+
+ this.write = createWriteFunction((text, next) => {
+ const n = this.expectedSize
+ this.buffer += text
+ if (this.buffer.length >= n) {
+ this.next = next
+ this.pushBySize(n)
+ return true // we will decide when to call next
+ }
+ return false
+ }, err => {
+ this.emit('error', err)
+ })
+
+ this.end = () => {
+ // the rendering is finished; we should push out the last of the buffer.
+ this.done = true
+ this.push(this.buffer)
+ }
+ }
+
+ pushBySize (n: number) {
+ const bufferToPush = this.buffer.substring(0, n)
+ this.buffer = this.buffer.substring(n)
+ this.push(bufferToPush)
+ }
+
+ tryRender () {
+ try {
+ this.render(this.write, this.end)
+ } catch (e) {
+ this.emit('error', e)
+ }
+ }
+
+ tryNext () {
+ try {
+ this.next()
+ } catch (e) {
+ this.emit('error', e)
+ }
+ }
+
+ _read (n: number) {
+ this.expectedSize = n
+ // it's possible that the last chunk added bumped the buffer up to > 2 * n,
+ // which means we will need to go through multiple read calls to drain it
+ // down to < n.
+ if (isTrue(this.done)) {
+ this.push(null)
+ return
+ }
+ if (this.buffer.length >= n) {
+ this.pushBySize(n)
+ return
+ }
+ if (isUndef(this.next)) {
+ // start the rendering chain.
+ this.tryRender()
+ } else {
+ // continue with the rendering.
+ this.tryNext()
+ }
+ }
+}
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)
+ }
+}
diff --git a/node_modules/vue/src/server/template-renderer/create-async-file-mapper.js b/node_modules/vue/src/server/template-renderer/create-async-file-mapper.js
new file mode 100644
index 00000000..64c0647a
--- /dev/null
+++ b/node_modules/vue/src/server/template-renderer/create-async-file-mapper.js
@@ -0,0 +1,53 @@
+/* @flow */
+
+/**
+ * Creates a mapper that maps components used during a server-side render
+ * to async chunk files in the client-side build, so that we can inline them
+ * directly in the rendered HTML to avoid waterfall requests.
+ */
+
+import type { ClientManifest } from './index'
+
+export type AsyncFileMapper = (files: Array<string>) => Array<string>;
+
+export function createMapper (
+ clientManifest: ClientManifest
+): AsyncFileMapper {
+ const map = createMap(clientManifest)
+ // map server-side moduleIds to client-side files
+ return function mapper (moduleIds: Array<string>): Array<string> {
+ const res = new Set()
+ for (let i = 0; i < moduleIds.length; i++) {
+ const mapped = map.get(moduleIds[i])
+ if (mapped) {
+ for (let j = 0; j < mapped.length; j++) {
+ res.add(mapped[j])
+ }
+ }
+ }
+ return Array.from(res)
+ }
+}
+
+function createMap (clientManifest) {
+ const map = new Map()
+ Object.keys(clientManifest.modules).forEach(id => {
+ map.set(id, mapIdToFile(id, clientManifest))
+ })
+ return map
+}
+
+function mapIdToFile (id, clientManifest) {
+ const files = []
+ const fileIndices = clientManifest.modules[id]
+ if (fileIndices) {
+ fileIndices.forEach(index => {
+ const file = clientManifest.all[index]
+ // only include async files or non-js assets
+ if (clientManifest.async.indexOf(file) > -1 || !(/\.js($|\?)/.test(file))) {
+ files.push(file)
+ }
+ })
+ }
+ return files
+}
diff --git a/node_modules/vue/src/server/template-renderer/index.js b/node_modules/vue/src/server/template-renderer/index.js
new file mode 100644
index 00000000..dabc08a1
--- /dev/null
+++ b/node_modules/vue/src/server/template-renderer/index.js
@@ -0,0 +1,257 @@
+/* @flow */
+
+const path = require('path')
+const serialize = require('serialize-javascript')
+
+import { isJS, isCSS } from '../util'
+import TemplateStream from './template-stream'
+import { parseTemplate } from './parse-template'
+import { createMapper } from './create-async-file-mapper'
+import type { ParsedTemplate } from './parse-template'
+import type { AsyncFileMapper } from './create-async-file-mapper'
+
+type TemplateRendererOptions = {
+ template: ?string;
+ inject?: boolean;
+ clientManifest?: ClientManifest;
+ shouldPreload?: (file: string, type: string) => boolean;
+ shouldPrefetch?: (file: string, type: string) => boolean;
+};
+
+export type ClientManifest = {
+ publicPath: string;
+ all: Array<string>;
+ initial: Array<string>;
+ async: Array<string>;
+ modules: {
+ [id: string]: Array<number>;
+ },
+ hasNoCssVersion?: {
+ [file: string]: boolean;
+ }
+};
+
+type Resource = {
+ file: string;
+ extension: string;
+ fileWithoutQuery: string;
+ asType: string;
+};
+
+export default class TemplateRenderer {
+ options: TemplateRendererOptions;
+ inject: boolean;
+ parsedTemplate: ParsedTemplate | null;
+ publicPath: string;
+ clientManifest: ClientManifest;
+ preloadFiles: Array<Resource>;
+ prefetchFiles: Array<Resource>;
+ mapFiles: AsyncFileMapper;
+
+ constructor (options: TemplateRendererOptions) {
+ this.options = options
+ this.inject = options.inject !== false
+ // if no template option is provided, the renderer is created
+ // as a utility object for rendering assets like preload links and scripts.
+ this.parsedTemplate = options.template
+ ? parseTemplate(options.template)
+ : null
+
+ // extra functionality with client manifest
+ if (options.clientManifest) {
+ const clientManifest = this.clientManifest = options.clientManifest
+ this.publicPath = clientManifest.publicPath.replace(/\/$/, '')
+ // preload/prefetch directives
+ this.preloadFiles = (clientManifest.initial || []).map(normalizeFile)
+ this.prefetchFiles = (clientManifest.async || []).map(normalizeFile)
+ // initial async chunk mapping
+ this.mapFiles = createMapper(clientManifest)
+ }
+ }
+
+ bindRenderFns (context: Object) {
+ const renderer: any = this
+ ;['ResourceHints', 'State', 'Scripts', 'Styles'].forEach(type => {
+ context[`render${type}`] = renderer[`render${type}`].bind(renderer, context)
+ })
+ // also expose getPreloadFiles, useful for HTTP/2 push
+ context.getPreloadFiles = renderer.getPreloadFiles.bind(renderer, context)
+ }
+
+ // render synchronously given rendered app content and render context
+ renderSync (content: string, context: ?Object) {
+ const template = this.parsedTemplate
+ if (!template) {
+ throw new Error('renderSync cannot be called without a template.')
+ }
+ context = context || {}
+ if (this.inject) {
+ return (
+ template.head(context) +
+ (context.head || '') +
+ this.renderResourceHints(context) +
+ this.renderStyles(context) +
+ template.neck(context) +
+ content +
+ this.renderState(context) +
+ this.renderScripts(context) +
+ template.tail(context)
+ )
+ } else {
+ return (
+ template.head(context) +
+ template.neck(context) +
+ content +
+ template.tail(context)
+ )
+ }
+ }
+
+ renderStyles (context: Object): string {
+ const cssFiles = this.clientManifest
+ ? this.clientManifest.all.filter(isCSS)
+ : []
+ return (
+ // render links for css files
+ (cssFiles.length
+ ? cssFiles.map(file => `<link rel="stylesheet" href="${this.publicPath}/${file}">`).join('')
+ : '') +
+ // context.styles is a getter exposed by vue-style-loader which contains
+ // the inline component styles collected during SSR
+ (context.styles || '')
+ )
+ }
+
+ renderResourceHints (context: Object): string {
+ return this.renderPreloadLinks(context) + this.renderPrefetchLinks(context)
+ }
+
+ getPreloadFiles (context: Object): Array<Resource> {
+ const usedAsyncFiles = this.getUsedAsyncFiles(context)
+ if (this.preloadFiles || usedAsyncFiles) {
+ return (this.preloadFiles || []).concat(usedAsyncFiles || [])
+ } else {
+ return []
+ }
+ }
+
+ renderPreloadLinks (context: Object): string {
+ const files = this.getPreloadFiles(context)
+ const shouldPreload = this.options.shouldPreload
+ if (files.length) {
+ return files.map(({ file, extension, fileWithoutQuery, asType }) => {
+ let extra = ''
+ // by default, we only preload scripts or css
+ if (!shouldPreload && asType !== 'script' && asType !== 'style') {
+ return ''
+ }
+ // user wants to explicitly control what to preload
+ if (shouldPreload && !shouldPreload(fileWithoutQuery, asType)) {
+ return ''
+ }
+ if (asType === 'font') {
+ extra = ` type="font/${extension}" crossorigin`
+ }
+ return `<link rel="preload" href="${
+ this.publicPath}/${file
+ }"${
+ asType !== '' ? ` as="${asType}"` : ''
+ }${
+ extra
+ }>`
+ }).join('')
+ } else {
+ return ''
+ }
+ }
+
+ renderPrefetchLinks (context: Object): string {
+ const shouldPrefetch = this.options.shouldPrefetch
+ if (this.prefetchFiles) {
+ const usedAsyncFiles = this.getUsedAsyncFiles(context)
+ const alreadyRendered = file => {
+ return usedAsyncFiles && usedAsyncFiles.some(f => f.file === file)
+ }
+ return this.prefetchFiles.map(({ file, fileWithoutQuery, asType }) => {
+ if (shouldPrefetch && !shouldPrefetch(fileWithoutQuery, asType)) {
+ return ''
+ }
+ if (alreadyRendered(file)) {
+ return ''
+ }
+ return `<link rel="prefetch" href="${this.publicPath}/${file}">`
+ }).join('')
+ } else {
+ return ''
+ }
+ }
+
+ renderState (context: Object, options?: Object): string {
+ const {
+ contextKey = 'state',
+ windowKey = '__INITIAL_STATE__'
+ } = options || {}
+ const state = serialize(context[contextKey], { isJSON: true })
+ const autoRemove = process.env.NODE_ENV === 'production'
+ ? ';(function(){var s;(s=document.currentScript||document.scripts[document.scripts.length-1]).parentNode.removeChild(s);}());'
+ : ''
+ return context[contextKey]
+ ? `<script>window.${windowKey}=${state}${autoRemove}</script>`
+ : ''
+ }
+
+ renderScripts (context: Object): string {
+ if (this.clientManifest) {
+ const initial = this.preloadFiles
+ const async = this.getUsedAsyncFiles(context)
+ const needed = [initial[0]].concat(async || [], initial.slice(1))
+ return needed.filter(({ file }) => isJS(file)).map(({ file }) => {
+ return `<script src="${this.publicPath}/${file}" defer></script>`
+ }).join('')
+ } else {
+ return ''
+ }
+ }
+
+ getUsedAsyncFiles (context: Object): ?Array<Resource> {
+ if (!context._mappedFiles && context._registeredComponents && this.mapFiles) {
+ const registered = Array.from(context._registeredComponents)
+ context._mappedFiles = this.mapFiles(registered).map(normalizeFile)
+ }
+ return context._mappedFiles
+ }
+
+ // create a transform stream
+ createStream (context: ?Object): TemplateStream {
+ if (!this.parsedTemplate) {
+ throw new Error('createStream cannot be called without a template.')
+ }
+ return new TemplateStream(this, this.parsedTemplate, context || {})
+ }
+}
+
+function normalizeFile (file: string): Resource {
+ const withoutQuery = file.replace(/\?.*/, '')
+ const extension = path.extname(withoutQuery).slice(1)
+ return {
+ file,
+ extension,
+ fileWithoutQuery: withoutQuery,
+ asType: getPreloadType(extension)
+ }
+}
+
+function getPreloadType (ext: string): string {
+ if (ext === 'js') {
+ return 'script'
+ } else if (ext === 'css') {
+ return 'style'
+ } else if (/jpe?g|png|svg|gif|webp|ico/.test(ext)) {
+ return 'image'
+ } else if (/woff2?|ttf|otf|eot/.test(ext)) {
+ return 'font'
+ } else {
+ // not exhausting all possibilities here, but above covers common cases
+ return ''
+ }
+}
diff --git a/node_modules/vue/src/server/template-renderer/parse-template.js b/node_modules/vue/src/server/template-renderer/parse-template.js
new file mode 100644
index 00000000..1ccfe899
--- /dev/null
+++ b/node_modules/vue/src/server/template-renderer/parse-template.js
@@ -0,0 +1,42 @@
+/* @flow */
+
+const compile = require('lodash.template')
+const compileOptions = {
+ escape: /{{([^{][\s\S]+?[^}])}}/g,
+ interpolate: /{{{([\s\S]+?)}}}/g
+}
+
+export type ParsedTemplate = {
+ head: (data: any) => string;
+ neck: (data: any) => string;
+ tail: (data: any) => string;
+};
+
+export function parseTemplate (
+ template: string,
+ contentPlaceholder?: string = '<!--vue-ssr-outlet-->'
+): ParsedTemplate {
+ if (typeof template === 'object') {
+ return template
+ }
+
+ let i = template.indexOf('</head>')
+ const j = template.indexOf(contentPlaceholder)
+
+ if (j < 0) {
+ throw new Error(`Content placeholder not found in template.`)
+ }
+
+ if (i < 0) {
+ i = template.indexOf('<body>')
+ if (i < 0) {
+ i = j
+ }
+ }
+
+ return {
+ head: compile(template.slice(0, i), compileOptions),
+ neck: compile(template.slice(i, j), compileOptions),
+ tail: compile(template.slice(j + contentPlaceholder.length), compileOptions)
+ }
+}
diff --git a/node_modules/vue/src/server/template-renderer/template-stream.js b/node_modules/vue/src/server/template-renderer/template-stream.js
new file mode 100644
index 00000000..ed4db78f
--- /dev/null
+++ b/node_modules/vue/src/server/template-renderer/template-stream.js
@@ -0,0 +1,82 @@
+/* @flow */
+
+const Transform = require('stream').Transform
+import type TemplateRenderer from './index'
+import type { ParsedTemplate } from './parse-template'
+
+export default class TemplateStream extends Transform {
+ started: boolean;
+ renderer: TemplateRenderer;
+ template: ParsedTemplate;
+ context: Object;
+ inject: boolean;
+
+ constructor (
+ renderer: TemplateRenderer,
+ template: ParsedTemplate,
+ context: Object
+ ) {
+ super()
+ this.started = false
+ this.renderer = renderer
+ this.template = template
+ this.context = context || {}
+ this.inject = renderer.inject
+ }
+
+ _transform (data: Buffer | string, encoding: string, done: Function) {
+ if (!this.started) {
+ this.emit('beforeStart')
+ this.start()
+ }
+ this.push(data)
+ done()
+ }
+
+ start () {
+ this.started = true
+ this.push(this.template.head(this.context))
+
+ if (this.inject) {
+ // inline server-rendered head meta information
+ if (this.context.head) {
+ this.push(this.context.head)
+ }
+
+ // inline preload/prefetch directives for initial/async chunks
+ const links = this.renderer.renderResourceHints(this.context)
+ if (links) {
+ this.push(links)
+ }
+
+ // CSS files and inline server-rendered CSS collected by vue-style-loader
+ const styles = this.renderer.renderStyles(this.context)
+ if (styles) {
+ this.push(styles)
+ }
+ }
+
+ this.push(this.template.neck(this.context))
+ }
+
+ _flush (done: Function) {
+ this.emit('beforeEnd')
+
+ if (this.inject) {
+ // inline initial store state
+ const state = this.renderer.renderState(this.context)
+ if (state) {
+ this.push(state)
+ }
+
+ // embed scripts needed
+ const scripts = this.renderer.renderScripts(this.context)
+ if (scripts) {
+ this.push(scripts)
+ }
+ }
+
+ this.push(this.template.tail(this.context))
+ done()
+ }
+}
diff --git a/node_modules/vue/src/server/util.js b/node_modules/vue/src/server/util.js
new file mode 100644
index 00000000..908f8c9d
--- /dev/null
+++ b/node_modules/vue/src/server/util.js
@@ -0,0 +1,18 @@
+/* @flow */
+
+export const isJS = (file: string): boolean => /\.js(\?[^.]+)?$/.test(file)
+
+export const isCSS = (file: string): boolean => /\.css(\?[^.]+)?$/.test(file)
+
+export function createPromiseCallback () {
+ let resolve, reject
+ const promise: Promise<string> = new Promise((_resolve, _reject) => {
+ resolve = _resolve
+ reject = _reject
+ })
+ const cb = (err: Error, res?: string) => {
+ if (err) return reject(err)
+ resolve(res || '')
+ }
+ return { promise, cb }
+}
diff --git a/node_modules/vue/src/server/webpack-plugin/client.js b/node_modules/vue/src/server/webpack-plugin/client.js
new file mode 100644
index 00000000..5f2cd4bd
--- /dev/null
+++ b/node_modules/vue/src/server/webpack-plugin/client.js
@@ -0,0 +1,70 @@
+const hash = require('hash-sum')
+const uniq = require('lodash.uniq')
+import { isJS } from './util'
+
+export default class VueSSRClientPlugin {
+ constructor (options = {}) {
+ this.options = Object.assign({
+ filename: 'vue-ssr-client-manifest.json'
+ }, options)
+ }
+
+ apply (compiler) {
+ compiler.plugin('emit', (compilation, cb) => {
+ const stats = compilation.getStats().toJson()
+
+ const allFiles = uniq(stats.assets
+ .map(a => a.name))
+
+ const initialFiles = uniq(Object.keys(stats.entrypoints)
+ .map(name => stats.entrypoints[name].assets)
+ .reduce((assets, all) => all.concat(assets), [])
+ .filter(isJS))
+
+ const asyncFiles = allFiles
+ .filter(isJS)
+ .filter(file => initialFiles.indexOf(file) < 0)
+
+ const manifest = {
+ publicPath: stats.publicPath,
+ all: allFiles,
+ initial: initialFiles,
+ async: asyncFiles,
+ modules: { /* [identifier: string]: Array<index: number> */ }
+ }
+
+ const assetModules = stats.modules.filter(m => m.assets.length)
+ const fileToIndex = file => manifest.all.indexOf(file)
+ stats.modules.forEach(m => {
+ // ignore modules duplicated in multiple chunks
+ if (m.chunks.length === 1) {
+ const cid = m.chunks[0]
+ const chunk = stats.chunks.find(c => c.id === cid)
+ if (!chunk || !chunk.files) {
+ return
+ }
+ const files = manifest.modules[hash(m.identifier)] = chunk.files.map(fileToIndex)
+ // find all asset modules associated with the same chunk
+ assetModules.forEach(m => {
+ if (m.chunks.some(id => id === cid)) {
+ files.push.apply(files, m.assets.map(fileToIndex))
+ }
+ })
+ }
+ })
+
+ // const debug = (file, obj) => {
+ // require('fs').writeFileSync(__dirname + '/' + file, JSON.stringify(obj, null, 2))
+ // }
+ // debug('stats.json', stats)
+ // debug('client-manifest.json', manifest)
+
+ const json = JSON.stringify(manifest, null, 2)
+ compilation.assets[this.options.filename] = {
+ source: () => json,
+ size: () => json.length
+ }
+ cb()
+ })
+ }
+}
diff --git a/node_modules/vue/src/server/webpack-plugin/server.js b/node_modules/vue/src/server/webpack-plugin/server.js
new file mode 100644
index 00000000..8ffa58b0
--- /dev/null
+++ b/node_modules/vue/src/server/webpack-plugin/server.js
@@ -0,0 +1,66 @@
+import { validate, isJS } from './util'
+
+export default class VueSSRServerPlugin {
+ constructor (options = {}) {
+ this.options = Object.assign({
+ filename: 'vue-ssr-server-bundle.json'
+ }, options)
+ }
+
+ apply (compiler) {
+ validate(compiler)
+
+ compiler.plugin('emit', (compilation, cb) => {
+ const stats = compilation.getStats().toJson()
+ const entryName = Object.keys(stats.entrypoints)[0]
+ const entryInfo = stats.entrypoints[entryName]
+
+ if (!entryInfo) {
+ // #5553
+ return cb()
+ }
+
+ const entryAssets = entryInfo.assets.filter(isJS)
+
+ if (entryAssets.length > 1) {
+ throw new Error(
+ `Server-side bundle should have one single entry file. ` +
+ `Avoid using CommonsChunkPlugin in the server config.`
+ )
+ }
+
+ const entry = entryAssets[0]
+ if (!entry || typeof entry !== 'string') {
+ throw new Error(
+ `Entry "${entryName}" not found. Did you specify the correct entry option?`
+ )
+ }
+
+ const bundle = {
+ entry,
+ files: {},
+ maps: {}
+ }
+
+ stats.assets.forEach(asset => {
+ if (asset.name.match(/\.js$/)) {
+ bundle.files[asset.name] = compilation.assets[asset.name].source()
+ } else if (asset.name.match(/\.js\.map$/)) {
+ bundle.maps[asset.name.replace(/\.map$/, '')] = JSON.parse(compilation.assets[asset.name].source())
+ }
+ // do not emit anything else for server
+ delete compilation.assets[asset.name]
+ })
+
+ const json = JSON.stringify(bundle, null, 2)
+ const filename = this.options.filename
+
+ compilation.assets[filename] = {
+ source: () => json,
+ size: () => json.length
+ }
+
+ cb()
+ })
+ }
+}
diff --git a/node_modules/vue/src/server/webpack-plugin/util.js b/node_modules/vue/src/server/webpack-plugin/util.js
new file mode 100644
index 00000000..d22e4b2c
--- /dev/null
+++ b/node_modules/vue/src/server/webpack-plugin/util.js
@@ -0,0 +1,24 @@
+const { red, yellow } = require('chalk')
+
+const prefix = `[vue-server-renderer-webpack-plugin]`
+const warn = exports.warn = msg => console.error(red(`${prefix} ${msg}\n`))
+const tip = exports.tip = msg => console.log(yellow(`${prefix} ${msg}\n`))
+
+export const validate = compiler => {
+ if (compiler.options.target !== 'node') {
+ warn('webpack config `target` should be "node".')
+ }
+
+ if (compiler.options.output && compiler.options.output.libraryTarget !== 'commonjs2') {
+ warn('webpack config `output.libraryTarget` should be "commonjs2".')
+ }
+
+ if (!compiler.options.externals) {
+ tip(
+ 'It is recommended to externalize dependencies in the server build for ' +
+ 'better build performance.'
+ )
+ }
+}
+
+export { isJS, isCSS } from '../util'
diff --git a/node_modules/vue/src/server/write.js b/node_modules/vue/src/server/write.js
new file mode 100644
index 00000000..e6420818
--- /dev/null
+++ b/node_modules/vue/src/server/write.js
@@ -0,0 +1,50 @@
+/* @flow */
+
+const MAX_STACK_DEPTH = 1000
+const noop = _ => _
+
+const defer = typeof process !== 'undefined' && process.nextTick
+ ? process.nextTick
+ : typeof Promise !== 'undefined'
+ ? fn => Promise.resolve().then(fn)
+ : typeof setTimeout !== 'undefined'
+ ? setTimeout
+ : noop
+
+if (defer === noop) {
+ throw new Error(
+ 'Your JavaScript runtime does not support any asynchronous primitives ' +
+ 'that are required by vue-server-renderer. Please use a polyfill for ' +
+ 'either Promise or setTimeout.'
+ )
+}
+
+export function createWriteFunction (
+ write: (text: string, next: Function) => boolean,
+ onError: Function
+): Function {
+ let stackDepth = 0
+ const cachedWrite = (text, next) => {
+ if (text && cachedWrite.caching) {
+ cachedWrite.cacheBuffer[cachedWrite.cacheBuffer.length - 1] += text
+ }
+ const waitForNext = write(text, next)
+ if (waitForNext !== true) {
+ if (stackDepth >= MAX_STACK_DEPTH) {
+ defer(() => {
+ try { next() } catch (e) {
+ onError(e)
+ }
+ })
+ } else {
+ stackDepth++
+ next()
+ stackDepth--
+ }
+ }
+ }
+ cachedWrite.caching = false
+ cachedWrite.cacheBuffer = []
+ cachedWrite.componentBuffer = []
+ return cachedWrite
+}