152 lines
4 KiB
JavaScript
152 lines
4 KiB
JavaScript
/* @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 | (content: string, context: any) => string;
|
|
inject?: boolean;
|
|
basedir?: string;
|
|
shouldPreload?: Function;
|
|
shouldPrefetch?: Function;
|
|
clientManifest?: ClientManifest;
|
|
serializer?: Function;
|
|
runInNewContext?: boolean | 'once';
|
|
};
|
|
|
|
export function createRenderer ({
|
|
modules = [],
|
|
directives = {},
|
|
isUnaryTag = (() => false),
|
|
template,
|
|
inject,
|
|
cache,
|
|
shouldPreload,
|
|
shouldPrefetch,
|
|
clientManifest,
|
|
serializer
|
|
}: RenderOptions = {}): Renderer {
|
|
const render = createRenderFunction(modules, directives, isUnaryTag, cache)
|
|
const templateRenderer = new TemplateRenderer({
|
|
template,
|
|
inject,
|
|
shouldPreload,
|
|
shouldPrefetch,
|
|
clientManifest,
|
|
serializer
|
|
})
|
|
|
|
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 (err) {
|
|
return cb(err)
|
|
}
|
|
if (context && context.rendered) {
|
|
context.rendered(context)
|
|
}
|
|
if (template) {
|
|
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)
|
|
}
|
|
})
|
|
} 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) {
|
|
if (context && context.rendered) {
|
|
const rendered = context.rendered
|
|
renderStream.once('beforeEnd', () => {
|
|
rendered(context)
|
|
})
|
|
}
|
|
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 => {
|
|
templateStream.emit('error', err)
|
|
})
|
|
renderStream.pipe(templateStream)
|
|
if (context && context.rendered) {
|
|
const rendered = context.rendered
|
|
renderStream.once('beforeEnd', () => {
|
|
rendered(context)
|
|
})
|
|
}
|
|
return templateStream
|
|
}
|
|
}
|
|
}
|
|
}
|