d596435045d1cbda8811d6a66ea442fe1da5d05034e3f446c038a68c73fb90fedd3271a8337ddb191d03ccad1bac3e79bf6d97aa6640481807a12e6607291e 4.0 KB

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