create-renderer.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /* @flow */
  2. import RenderStream from './render-stream'
  3. import TemplateRenderer from './template-renderer/index'
  4. import { createWriteFunction } from './write'
  5. import { createRenderFunction } from './render'
  6. import type { ClientManifest, ServerManifest } from './template-renderer/index'
  7. export type Renderer = {
  8. renderToString: (component: Component, cb: (err: ?Error, res: ?string) => void) => void;
  9. renderToStream: (component: Component) => stream$Readable;
  10. };
  11. type RenderCache = {
  12. get: (key: string, cb?: Function) => string | void;
  13. set: (key: string, val: string) => void;
  14. has?: (key: string, cb?: Function) => boolean | void;
  15. };
  16. export type RenderOptions = {
  17. modules?: Array<(vnode: VNode) => string>;
  18. directives?: Object;
  19. isUnaryTag?: Function;
  20. cache?: RenderCache;
  21. template?: string;
  22. basedir?: string;
  23. shouldPreload?: Function;
  24. serverManifest?: ServerManifest;
  25. clientManifest?: ClientManifest;
  26. };
  27. export function createRenderer ({
  28. modules = [],
  29. directives = {},
  30. isUnaryTag = (() => false),
  31. template,
  32. cache,
  33. shouldPreload,
  34. serverManifest,
  35. clientManifest
  36. }: RenderOptions = {}): Renderer {
  37. const render = createRenderFunction(modules, directives, isUnaryTag, cache)
  38. const templateRenderer = new TemplateRenderer({
  39. template,
  40. shouldPreload,
  41. serverManifest,
  42. clientManifest
  43. })
  44. return {
  45. renderToString (
  46. component: Component,
  47. done: (err: ?Error, res: ?string) => any,
  48. context?: ?Object
  49. ): void {
  50. if (!template && context && clientManifest) {
  51. exposeAssetRenderFns(context, templateRenderer)
  52. }
  53. let result = ''
  54. const write = createWriteFunction(text => {
  55. result += text
  56. return false
  57. }, done)
  58. try {
  59. render(component, write, () => {
  60. if (template) {
  61. result = templateRenderer.renderSync(result, context)
  62. }
  63. done(null, result)
  64. })
  65. } catch (e) {
  66. done(e)
  67. }
  68. },
  69. renderToStream (
  70. component: Component,
  71. context?: ?Object
  72. ): stream$Readable {
  73. const renderStream = new RenderStream((write, done) => {
  74. render(component, write, done)
  75. })
  76. if (!template) {
  77. if (context && clientManifest) {
  78. exposeAssetRenderFns(context, templateRenderer)
  79. }
  80. return renderStream
  81. } else {
  82. const templateStream = templateRenderer.createStream(context)
  83. renderStream.on('error', err => {
  84. templateStream.emit('error', err)
  85. })
  86. renderStream.pipe(templateStream)
  87. return templateStream
  88. }
  89. }
  90. }
  91. }
  92. // Expose preload/prefetch and script render fns when client manifest is
  93. // available.
  94. function exposeAssetRenderFns (context: Object, renderer: TemplateRenderer) {
  95. context.renderPreloadLinks = renderer.renderPreloadLinks.bind(renderer, context)
  96. context.renderPrefetchLinks = renderer.renderPrefetchLinks.bind(renderer, context)
  97. context.renderScripts = renderer.renderScripts.bind(renderer, context)
  98. }