92a41fdae623908e70bf2b10da6b7141bd47073b122d3cc47fad63590cfd18cda718206805ae1dae03b0803fa5d706311183cf7ea2c3aac74dd1c22a27835b 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /* @flow */
  2. import { createPromiseCallback } from '../util'
  3. import { createBundleRunner } from './create-bundle-runner'
  4. import type { Renderer, RenderOptions } from '../create-renderer'
  5. import { createSourceMapConsumers, rewriteErrorTrace } from './source-map-support'
  6. const fs = require('fs')
  7. const path = require('path')
  8. const PassThrough = require('stream').PassThrough
  9. const INVALID_MSG =
  10. 'Invalid server-rendering bundle format. Should be a string ' +
  11. 'or a bundle Object of type:\n\n' +
  12. `{
  13. entry: string;
  14. files: { [filename: string]: string; };
  15. maps: { [filename: string]: string; };
  16. }\n`
  17. // The render bundle can either be a string (single bundled file)
  18. // or a bundle manifest object generated by vue-ssr-webpack-plugin.
  19. type RenderBundle = {
  20. basedir?: string;
  21. entry: string;
  22. files: { [filename: string]: string; };
  23. maps: { [filename: string]: string; };
  24. modules?: { [filename: string]: Array<string> };
  25. };
  26. export function createBundleRendererCreator (
  27. createRenderer: (options?: RenderOptions) => Renderer
  28. ) {
  29. return function createBundleRenderer (
  30. bundle: string | RenderBundle,
  31. rendererOptions?: RenderOptions = {}
  32. ) {
  33. let files, entry, maps
  34. let basedir = rendererOptions.basedir
  35. // load bundle if given filepath
  36. if (
  37. typeof bundle === 'string' &&
  38. /\.js(on)?$/.test(bundle) &&
  39. path.isAbsolute(bundle)
  40. ) {
  41. if (fs.existsSync(bundle)) {
  42. const isJSON = /\.json$/.test(bundle)
  43. basedir = basedir || path.dirname(bundle)
  44. bundle = fs.readFileSync(bundle, 'utf-8')
  45. if (isJSON) {
  46. try {
  47. bundle = JSON.parse(bundle)
  48. } catch (e) {
  49. throw new Error(`Invalid JSON bundle file: ${bundle}`)
  50. }
  51. }
  52. } else {
  53. throw new Error(`Cannot locate bundle file: ${bundle}`)
  54. }
  55. }
  56. if (typeof bundle === 'object') {
  57. entry = bundle.entry
  58. files = bundle.files
  59. basedir = basedir || bundle.basedir
  60. maps = createSourceMapConsumers(bundle.maps)
  61. if (typeof entry !== 'string' || typeof files !== 'object') {
  62. throw new Error(INVALID_MSG)
  63. }
  64. } else if (typeof bundle === 'string') {
  65. entry = '__vue_ssr_bundle__'
  66. files = { '__vue_ssr_bundle__': bundle }
  67. maps = {}
  68. } else {
  69. throw new Error(INVALID_MSG)
  70. }
  71. const renderer = createRenderer(rendererOptions)
  72. const run = createBundleRunner(
  73. entry,
  74. files,
  75. basedir,
  76. rendererOptions.runInNewContext
  77. )
  78. return {
  79. renderToString: (context?: Object, cb: any) => {
  80. if (typeof context === 'function') {
  81. cb = context
  82. context = {}
  83. }
  84. let promise
  85. if (!cb) {
  86. ({ promise, cb } = createPromiseCallback())
  87. }
  88. run(context).catch(err => {
  89. rewriteErrorTrace(err, maps)
  90. cb(err)
  91. }).then(app => {
  92. if (app) {
  93. renderer.renderToString(app, context, (err, res) => {
  94. rewriteErrorTrace(err, maps)
  95. cb(err, res)
  96. })
  97. }
  98. })
  99. return promise
  100. },
  101. renderToStream: (context?: Object) => {
  102. const res = new PassThrough()
  103. run(context).catch(err => {
  104. rewriteErrorTrace(err, maps)
  105. // avoid emitting synchronously before user can
  106. // attach error listener
  107. process.nextTick(() => {
  108. res.emit('error', err)
  109. })
  110. }).then(app => {
  111. if (app) {
  112. const renderStream = renderer.renderToStream(app, context)
  113. renderStream.on('error', err => {
  114. rewriteErrorTrace(err, maps)
  115. res.emit('error', err)
  116. })
  117. // relay HTMLStream special events
  118. if (rendererOptions && rendererOptions.template) {
  119. renderStream.on('beforeStart', () => {
  120. res.emit('beforeStart')
  121. })
  122. renderStream.on('beforeEnd', () => {
  123. res.emit('beforeEnd')
  124. })
  125. }
  126. renderStream.pipe(res)
  127. }
  128. })
  129. return res
  130. }
  131. }
  132. }
  133. }