From f881dd175a6764f6f80077df20f950dba63ca447 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 5 Oct 2017 22:42:56 -0400 Subject: [PATCH] feat(ssr): renderToString return Promise close #6160 --- .../bundle-renderer/create-bundle-renderer.js | 11 ++++++- src/server/create-renderer.js | 24 +++++++++----- src/server/util.js | 13 ++++++++ test/ssr/ssr-bundle-render.spec.js | 31 ++++++++++++++++++- test/ssr/ssr-string.spec.js | 23 ++++++++++++++ 5 files changed, 93 insertions(+), 9 deletions(-) diff --git a/src/server/bundle-renderer/create-bundle-renderer.js b/src/server/bundle-renderer/create-bundle-renderer.js index 3b6e23769f5..05c60c35740 100644 --- a/src/server/bundle-renderer/create-bundle-renderer.js +++ b/src/server/bundle-renderer/create-bundle-renderer.js @@ -1,5 +1,6 @@ /* @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' @@ -85,11 +86,17 @@ export function createBundleRendererCreator ( ) return { - renderToString: (context?: Object, cb: (err: ?Error, res: ?string) => void) => { + 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) @@ -101,6 +108,8 @@ export function createBundleRendererCreator ( }) } }) + + return promise }, renderToStream: (context?: Object) => { diff --git a/src/server/create-renderer.js b/src/server/create-renderer.js index 5e76bb82555..4345f812649 100644 --- a/src/server/create-renderer.js +++ b/src/server/create-renderer.js @@ -3,11 +3,12 @@ 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) => void; + renderToString: (component: Component, context: any, cb: any) => ?Promise; renderToStream: (component: Component, context?: Object) => stream$Readable; }; @@ -52,30 +53,39 @@ export function createRenderer ({ renderToString ( component: Component, context: any, - done: any - ): void { + cb: any + ): ?Promise { if (typeof context === 'function') { - done = context + 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 - }, done) + }, cb) try { render(component, write, context, () => { if (template) { result = templateRenderer.renderSync(result, context) } - done(null, result) + cb(null, result) }) } catch (e) { - done(e) + cb(e) } + + return promise }, renderToStream ( diff --git a/src/server/util.js b/src/server/util.js index 31a288d2fd4..908f8c9df06 100644 --- a/src/server/util.js +++ b/src/server/util.js @@ -3,3 +3,16 @@ 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 = 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/test/ssr/ssr-bundle-render.spec.js b/test/ssr/ssr-bundle-render.spec.js index 6b9588e926a..8ea05b32172 100644 --- a/test/ssr/ssr-bundle-render.spec.js +++ b/test/ssr/ssr-bundle-render.spec.js @@ -95,7 +95,7 @@ function createAssertions (runInNewContext) { }) it('renderToStream catch Promise rejection', done => { - createRenderer('error.js', { runInNewContext }, renderer => { + createRenderer('promise-rejection.js', { runInNewContext }, renderer => { const stream = renderer.renderToStream() stream.on('error', err => { expect(err.message).toBe('foo') @@ -277,4 +277,33 @@ function createAssertions (runInNewContext) { }) }) }) + + it('renderToString return Promise', done => { + createRenderer('app.js', { runInNewContext }, renderer => { + const context = { url: '/test' } + renderer.renderToString(context).then(res => { + expect(res).toBe('
/test
') + expect(context.msg).toBe('hello') + done() + }) + }) + }) + + it('renderToString return Promise (error)', done => { + createRenderer('error.js', { runInNewContext }, renderer => { + renderer.renderToString().catch(err => { + expect(err.message).toBe('foo') + done() + }) + }) + }) + + it('renderToString return Promise (Promise rejection)', done => { + createRenderer('promise-rejection.js', { runInNewContext }, renderer => { + renderer.renderToString().catch(err => { + expect(err.message).toBe('foo') + done() + }) + }) + }) } diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js index 3f0cc92f6ad..547ba19e39c 100644 --- a/test/ssr/ssr-string.spec.js +++ b/test/ssr/ssr-string.spec.js @@ -957,6 +957,29 @@ describe('SSR: renderToString', () => { done() }) }) + + it('return Promise', done => { + renderToString(new Vue({ + template: `
{{ foo }}
`, + data: { foo: 'bar' } + })).then(res => { + expect(res).toBe(`
bar
`) + done() + }) + }) + + it('should Promise (error)', done => { + Vue.config.silent = true + renderToString(new Vue({ + render () { + throw new Error('foobar') + } + })).catch(err => { + expect(err.toString()).toContain(`foobar`) + Vue.config.silent = false + done() + }) + }) }) function renderVmWithOptions (options, cb) {