UNPKG

4.05 kBJavaScriptView Raw
1/* @flow */
2
3import RenderStream from './render-stream'
4import { createWriteFunction } from './write'
5import { createRenderFunction } from './render'
6import { createPromiseCallback } from './util'
7import TemplateRenderer from './template-renderer/index'
8import type { ClientManifest } from './template-renderer/index'
9
10export type Renderer = {
11 renderToString: (component: Component, context: any, cb: any) => ?Promise<string>;
12 renderToStream: (component: Component, context?: Object) => stream$Readable;
13};
14
15type RenderCache = {
16 get: (key: string, cb?: Function) => string | void;
17 set: (key: string, val: string) => void;
18 has?: (key: string, cb?: Function) => boolean | void;
19};
20
21export type RenderOptions = {
22 modules?: Array<(vnode: VNode) => ?string>;
23 directives?: Object;
24 isUnaryTag?: Function;
25 cache?: RenderCache;
26 template?: string | (content: string, context: any) => string;
27 inject?: boolean;
28 basedir?: string;
29 shouldPreload?: Function;
30 shouldPrefetch?: Function;
31 clientManifest?: ClientManifest;
32 serializer?: Function;
33 runInNewContext?: boolean | 'once';
34};
35
36export function createRenderer ({
37 modules = [],
38 directives = {},
39 isUnaryTag = (() => false),
40 template,
41 inject,
42 cache,
43 shouldPreload,
44 shouldPrefetch,
45 clientManifest,
46 serializer
47}: RenderOptions = {}): Renderer {
48 const render = createRenderFunction(modules, directives, isUnaryTag, cache)
49 const templateRenderer = new TemplateRenderer({
50 template,
51 inject,
52 shouldPreload,
53 shouldPrefetch,
54 clientManifest,
55 serializer
56 })
57
58 return {
59 renderToString (
60 component: Component,
61 context: any,
62 cb: any
63 ): ?Promise<string> {
64 if (typeof context === 'function') {
65 cb = context
66 context = {}
67 }
68 if (context) {
69 templateRenderer.bindRenderFns(context)
70 }
71
72 // no callback, return Promise
73 let promise
74 if (!cb) {
75 ({ promise, cb } = createPromiseCallback())
76 }
77
78 let result = ''
79 const write = createWriteFunction(text => {
80 result += text
81 return false
82 }, cb)
83 try {
84 render(component, write, context, err => {
85 if (err) {
86 return cb(err)
87 }
88 if (context && context.rendered) {
89 context.rendered(context)
90 }
91 if (template) {
92 try {
93 const res = templateRenderer.render(result, context)
94 if (typeof res !== 'string') {
95 // function template returning promise
96 res
97 .then(html => cb(null, html))
98 .catch(cb)
99 } else {
100 cb(null, res)
101 }
102 } catch (e) {
103 cb(e)
104 }
105 } else {
106 cb(null, result)
107 }
108 })
109 } catch (e) {
110 cb(e)
111 }
112
113 return promise
114 },
115
116 renderToStream (
117 component: Component,
118 context?: Object
119 ): stream$Readable {
120 if (context) {
121 templateRenderer.bindRenderFns(context)
122 }
123 const renderStream = new RenderStream((write, done) => {
124 render(component, write, context, done)
125 })
126 if (!template) {
127 if (context && context.rendered) {
128 const rendered = context.rendered
129 renderStream.once('beforeEnd', () => {
130 rendered(context)
131 })
132 }
133 return renderStream
134 } else if (typeof template === 'function') {
135 throw new Error(`function template is only supported in renderToString.`)
136 } else {
137 const templateStream = templateRenderer.createStream(context)
138 renderStream.on('error', err => {
139 templateStream.emit('error', err)
140 })
141 renderStream.pipe(templateStream)
142 if (context && context.rendered) {
143 const rendered = context.rendered
144 renderStream.once('beforeEnd', () => {
145 rendered(context)
146 })
147 }
148 return templateStream
149 }
150 }
151 }
152}