diff --git a/src/server/create-renderer.js b/src/server/create-renderer.js index 1cfbfb0ddd9..502b6fa6d1d 100644 --- a/src/server/create-renderer.js +++ b/src/server/create-renderer.js @@ -23,7 +23,7 @@ export type RenderOptions = { directives?: Object; isUnaryTag?: Function; cache?: RenderCache; - template?: string; + template?: string | (content: string, context: any) => string; inject?: boolean; basedir?: string; shouldPreload?: Function; @@ -82,14 +82,26 @@ export function createRenderer ({ }, cb) try { render(component, write, context, err => { + if (err) { + return cb(err) + } if (context && context.rendered) { context.rendered(context) } if (template) { - result = templateRenderer.renderSync(result, context) - } - if (err) { - cb(err) + try { + const res = templateRenderer.render(result, context) + if (typeof res !== 'string') { + // function template returning promise + res + .then(html => cb(null, html)) + .catch(cb) + } else { + cb(null, res) + } + } catch (e) { + cb(e) + } } else { cb(null, result) } @@ -119,6 +131,8 @@ export function createRenderer ({ }) } return renderStream + } else if (typeof template === 'function') { + throw new Error(`function template is only supported in renderToString.`) } else { const templateStream = templateRenderer.createStream(context) renderStream.on('error', err => { diff --git a/src/server/template-renderer/index.js b/src/server/template-renderer/index.js index 269673fb3d7..c8927b2de2c 100644 --- a/src/server/template-renderer/index.js +++ b/src/server/template-renderer/index.js @@ -11,7 +11,7 @@ import type { ParsedTemplate } from './parse-template' import type { AsyncFileMapper } from './create-async-file-mapper' type TemplateRendererOptions = { - template: ?string; + template?: string | (content: string, context: any) => string; inject?: boolean; clientManifest?: ClientManifest; shouldPreload?: (file: string, type: string) => boolean; @@ -42,7 +42,7 @@ type Resource = { export default class TemplateRenderer { options: TemplateRendererOptions; inject: boolean; - parsedTemplate: ParsedTemplate | null; + parsedTemplate: ParsedTemplate | Function | null; publicPath: string; clientManifest: ClientManifest; preloadFiles: Array; @@ -55,8 +55,12 @@ export default class TemplateRenderer { 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) + + const { template } = options + this.parsedTemplate = template + ? typeof template === 'string' + ? parseTemplate(template) + : template : null // function used to serialize initial state JSON @@ -89,12 +93,17 @@ export default class TemplateRenderer { } // render synchronously given rendered app content and render context - renderSync (content: string, context: ?Object) { + render (content: string, context: ?Object): string | Promise { const template = this.parsedTemplate if (!template) { - throw new Error('renderSync cannot be called without a template.') + throw new Error('render cannot be called without a template.') } context = context || {} + + if (typeof template === 'function') { + return template(content, context) + } + if (this.inject) { return ( template.head(context) +