123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- /* @flow */
- import { createPromiseCallback } from '../util'
- import { createBundleRunner } from './create-bundle-runner'
- import type { Renderer, RenderOptions } from '../create-renderer'
- import { createSourceMapConsumers, rewriteErrorTrace } from './source-map-support'
- const fs = require('fs')
- const path = require('path')
- const PassThrough = require('stream').PassThrough
- const INVALID_MSG =
- 'Invalid server-rendering bundle format. Should be a string ' +
- 'or a bundle Object of type:\n\n' +
- `{
- entry: string;
- files: { [filename: string]: string; };
- maps: { [filename: string]: string; };
- }\n`
- // The render bundle can either be a string (single bundled file)
- // or a bundle manifest object generated by vue-ssr-webpack-plugin.
- type RenderBundle = {
- basedir?: string;
- entry: string;
- files: { [filename: string]: string; };
- maps: { [filename: string]: string; };
- modules?: { [filename: string]: Array<string> };
- };
- export function createBundleRendererCreator (
- createRenderer: (options?: RenderOptions) => Renderer
- ) {
- return function createBundleRenderer (
- bundle: string | RenderBundle,
- rendererOptions?: RenderOptions = {}
- ) {
- let files, entry, maps
- let basedir = rendererOptions.basedir
- // load bundle if given filepath
- if (
- typeof bundle === 'string' &&
- /\.js(on)?$/.test(bundle) &&
- path.isAbsolute(bundle)
- ) {
- if (fs.existsSync(bundle)) {
- const isJSON = /\.json$/.test(bundle)
- basedir = basedir || path.dirname(bundle)
- bundle = fs.readFileSync(bundle, 'utf-8')
- if (isJSON) {
- try {
- bundle = JSON.parse(bundle)
- } catch (e) {
- throw new Error(`Invalid JSON bundle file: ${bundle}`)
- }
- }
- } else {
- throw new Error(`Cannot locate bundle file: ${bundle}`)
- }
- }
- if (typeof bundle === 'object') {
- entry = bundle.entry
- files = bundle.files
- basedir = basedir || bundle.basedir
- maps = createSourceMapConsumers(bundle.maps)
- if (typeof entry !== 'string' || typeof files !== 'object') {
- throw new Error(INVALID_MSG)
- }
- } else if (typeof bundle === 'string') {
- entry = '__vue_ssr_bundle__'
- files = { '__vue_ssr_bundle__': bundle }
- maps = {}
- } else {
- throw new Error(INVALID_MSG)
- }
- const renderer = createRenderer(rendererOptions)
- const run = createBundleRunner(
- entry,
- files,
- basedir,
- rendererOptions.runInNewContext
- )
- return {
- renderToString: (context?: Object, cb: any) => {
- if (typeof context === 'function') {
- cb = context
- context = {}
- }
- let promise
- if (!cb) {
- ({ promise, cb } = createPromiseCallback())
- }
- run(context).catch(err => {
- rewriteErrorTrace(err, maps)
- cb(err)
- }).then(app => {
- if (app) {
- renderer.renderToString(app, context, (err, res) => {
- rewriteErrorTrace(err, maps)
- cb(err, res)
- })
- }
- })
- return promise
- },
- renderToStream: (context?: Object) => {
- const res = new PassThrough()
- run(context).catch(err => {
- rewriteErrorTrace(err, maps)
- // avoid emitting synchronously before user can
- // attach error listener
- process.nextTick(() => {
- res.emit('error', err)
- })
- }).then(app => {
- if (app) {
- const renderStream = renderer.renderToStream(app, context)
- renderStream.on('error', err => {
- rewriteErrorTrace(err, maps)
- res.emit('error', err)
- })
- // relay HTMLStream special events
- if (rendererOptions && rendererOptions.template) {
- renderStream.on('beforeStart', () => {
- res.emit('beforeStart')
- })
- renderStream.on('beforeEnd', () => {
- res.emit('beforeEnd')
- })
- }
- renderStream.pipe(res)
- }
- })
- return res
- }
- }
- }
- }
|