Skip to content

Commit 8d88512

Browse files
committed
ssr: resolve require() calls relative to bundle (fix #4936)
1 parent 6977109 commit 8d88512

File tree

7 files changed

+85
-17
lines changed

7 files changed

+85
-17
lines changed

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
"nightwatch": "^0.9.9",
102102
"nightwatch-helpers": "^1.2.0",
103103
"phantomjs-prebuilt": "^2.1.1",
104+
"resolve": "^1.2.0",
104105
"rollup": "^0.41.4",
105106
"rollup-plugin-alias": "^1.2.0",
106107
"rollup-plugin-babel": "^2.4.0",

Diff for: packages/vue-server-renderer/README.md

+35-6
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,31 @@ app.get('/', (req, res) => {
7676

7777
---
7878

79-
### createBundleRenderer([bundle](#creating-the-server-bundle), [[rendererOptions](#renderer-options)])
79+
### createBundleRenderer(bundle, [[rendererOptions](#renderer-options)])
8080

81-
Creates a `bundleRenderer` instance using pre-compiled application bundle (see [Creating the Server Bundle](#creating-the-server-bundle)). For each render call, the code will be re-run in a new context using Node.js' `vm` module. This ensures your application state is discrete between requests, and you don't need to worry about structuring your application in a limiting pattern just for the sake of SSR.
81+
Creates a `bundleRenderer` instance using pre-compiled application bundle. The `bundle` argument can be one of the following:
82+
83+
- An absolute path to generated bundle file (`.js` or `.json`). Must start with `/` to be treated as a file path.
84+
85+
- A bundle object generated by `vue-ssr-webpack-plugin`.
86+
87+
- A string of JavaScript code.
88+
89+
See [Creating the Server Bundle](#creating-the-server-bundle) for more details.
90+
91+
For each render call, the code will be re-run in a new context using Node.js' `vm` module. This ensures your application state is discrete between requests, and you don't need to worry about structuring your application in a limiting pattern just for the sake of SSR.
8292

8393
``` js
84-
const bundleRenderer = require('vue-server-renderer').createBundleRenderer(bundle)
94+
const createBundleRenderer = require('vue-server-renderer').createBundleRenderer
95+
96+
// absolute filepath
97+
let renderer = createBundleRenderer('/path/to/bundle.json')
98+
99+
// bundle object
100+
let renderer = createBundleRenderer({ ... })
101+
102+
// code (not recommended for lack of source map support)
103+
let renderer = createBundleRenderer(bundledCode)
85104
```
86105

87106
---
@@ -190,6 +209,16 @@ const renderer = createRenderer({
190209

191210
---
192211

212+
### basedir
213+
214+
> New in 2.2.0
215+
216+
Explicitly declare the base directory for the server bundle to resolve node_modules from. This is only needed if your generated bundle file is placed in a different location from where the externalized NPM dependencies are installed.
217+
218+
Note that the `basedir` is automatically inferred if you use `vue-ssr-webpack-plugin` or provide an absolute path to `createBundleRenderer` as the first argument, so in most cases you don't need to provide this option. However, this option does allow you to explicitly overwrite the inferred value.
219+
220+
---
221+
193222
### directives
194223

195224
Allows you to provide server-side implementations for your custom directives:
@@ -220,7 +249,7 @@ Instead, it's more straightforward to run our app "fresh", in a sandboxed contex
220249

221250
<img width="973" alt="screen shot 2016-08-11 at 6 06 57 pm" src="https://cloud.githubusercontent.com/assets/499550/17607895/786a415a-5fee-11e6-9c11-45a2cfdf085c.png">
222251

223-
The application bundle can be either a string of bundled code, or a special object of the following type:
252+
The application bundle can be either a string of bundled code (not recommended due to lack of source map support), or a special object of the following type:
224253

225254
``` js
226255
type RenderBundle = {
@@ -230,9 +259,9 @@ type RenderBundle = {
230259
}
231260
```
232261

233-
Although theoretically you can use any build tool to generate the bundle, it is recommended to use webpack + `vue-loader` + [vue-ssr-webpack-plugin](https://github.com/vuejs/vue-ssr-webpack-plugin) for this purpose. This setup works seamlessly even if you use webpack's on-demand code splitting features such as dynamic `import()`.
262+
Although theoretically you can use any build tool to generate the bundle, it is recommended to use webpack + `vue-loader` + [vue-ssr-webpack-plugin](https://github.com/vuejs/vue-ssr-webpack-plugin) for this purpose. The plugin will automatically turn the build output into a single JSON file that you can then pass to `createBundleRenderer`. This setup works seamlessly even if you use webpack's on-demand code splitting features such as dynamic `import()`.
234263

235-
The usual workflow is setting up a base webpack configuration file for the client-side, then modify it to generate the server-side bundle with the following changes:
264+
The typical workflow is setting up a base webpack configuration file for the client-side, then modify it to generate the server-side bundle with the following changes:
236265

237266
1. Set `target: 'node'` and `output: { libraryTarget: 'commonjs2' }` in your webpack config.
238267

Diff for: packages/vue-server-renderer/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"dependencies": {
2121
"he": "^1.1.0",
2222
"de-indent": "^1.0.2",
23+
"resolve": "^1.2.0",
2324
"source-map": "0.5.6",
2425
"vue-ssr-html-stream": "^2.1.0"
2526
},

Diff for: src/server/create-bundle-renderer.js

+26-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { createBundleRunner } from './create-bundle-runner'
44
import type { Renderer, RenderOptions } from './create-renderer'
55
import { createSourceMapConsumers, rewriteErrorTrace } from './source-map-support'
66

7+
const fs = require('fs')
8+
const path = require('path')
79
const PassThrough = require('stream').PassThrough
810

911
const INVALID_MSG =
@@ -18,6 +20,7 @@ const INVALID_MSG =
1820
// The render bundle can either be a string (single bundled file)
1921
// or a bundle manifest object generated by vue-ssr-webpack-plugin.
2022
type RenderBundle = {
23+
basedir?: string;
2124
entry: string;
2225
files: { [filename: string]: string; };
2326
maps: { [filename: string]: string; };
@@ -31,9 +34,29 @@ export function createBundleRendererCreator (createRenderer: () => Renderer) {
3134
const renderer = createRenderer(rendererOptions)
3235

3336
let files, entry, maps
37+
let basedir = rendererOptions && rendererOptions.basedir
38+
39+
// load bundle if given filepath
40+
if (typeof bundle === 'string' && bundle.charAt(0) === '/') {
41+
if (fs.existsSync(bundle)) {
42+
basedir = basedir || path.dirname(bundle)
43+
bundle = fs.readFileSync(bundle, 'utf-8')
44+
if (/\.json$/.test(bundle)) {
45+
try {
46+
bundle = JSON.parse(bundle)
47+
} catch (e) {
48+
throw new Error(`Invalid JSON bundle file: ${bundle}`)
49+
}
50+
}
51+
} else {
52+
throw new Error(`Cannot locate bundle file: ${bundle}`)
53+
}
54+
}
55+
3456
if (typeof bundle === 'object') {
3557
entry = bundle.entry
3658
files = bundle.files
59+
basedir = basedir || bundle.basedir
3760
maps = createSourceMapConsumers(bundle.maps)
3861
if (typeof entry !== 'string' || typeof files !== 'object') {
3962
throw new Error(INVALID_MSG)
@@ -45,7 +68,9 @@ export function createBundleRendererCreator (createRenderer: () => Renderer) {
4568
} else {
4669
throw new Error(INVALID_MSG)
4770
}
48-
const run = createBundleRunner(entry, files)
71+
72+
const run = createBundleRunner(entry, files, basedir)
73+
4974
return {
5075
renderToString: (context?: Object, cb: (err: ?Error, res: ?string) => void) => {
5176
if (typeof context === 'function') {

Diff for: src/server/create-bundle-runner.js

+17-10
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
1-
const NativeModule = require('module')
21
const vm = require('vm')
32
const path = require('path')
3+
const resolve = require('resolve')
4+
const NativeModule = require('module')
45

56
function createContext (context) {
67
const sandbox = {
78
Buffer,
8-
clearImmediate,
9-
clearInterval,
10-
clearTimeout,
11-
setImmediate,
12-
setInterval,
13-
setTimeout,
149
console,
1510
process,
11+
setTimeout,
12+
setInterval,
13+
setImmediate,
14+
clearTimeout,
15+
clearInterval,
16+
clearImmediate,
1617
__VUE_SSR_CONTEXT__: context
1718
}
1819
sandbox.global = sandbox
1920
return sandbox
2021
}
2122

22-
function compileModule (files) {
23+
function compileModule (files, basedir) {
2324
const compiledScripts = {}
25+
const reoslvedModules = {}
2426

2527
function getCompiledScript (filename) {
2628
if (compiledScripts[filename]) {
@@ -48,6 +50,11 @@ function compileModule (files) {
4850
file = path.join('.', file)
4951
if (files[file]) {
5052
return evaluateModule(file, context, evaluatedModules)
53+
} else if (basedir) {
54+
return require(
55+
reoslvedModules[file] ||
56+
(reoslvedModules[file] = resolve.sync(file, { basedir }))
57+
)
5158
} else {
5259
return require(file)
5360
}
@@ -63,8 +70,8 @@ function compileModule (files) {
6370
return evaluateModule
6471
}
6572

66-
export function createBundleRunner (entry, files) {
67-
const evaluate = compileModule(files)
73+
export function createBundleRunner (entry, files, basedir) {
74+
const evaluate = compileModule(files, basedir)
6875
return (_context = {}) => new Promise((resolve, reject) => {
6976
const context = createContext(_context)
7077
const res = evaluate(entry, context, {})

Diff for: src/server/create-renderer.js

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type RenderOptions = {
2323
isUnaryTag?: Function;
2424
cache?: RenderCache;
2525
template?: string;
26+
basedir?: string;
2627
};
2728

2829
export function createRenderer ({

Diff for: yarn.lock

+4
Original file line numberDiff line numberDiff line change
@@ -4163,6 +4163,10 @@ [email protected], resolve@^1.1.6:
41634163
version "1.1.7"
41644164
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
41654165

4166+
resolve@^1.2.0:
4167+
version "1.2.0"
4168+
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c"
4169+
41664170
restore-cursor@^1.0.1:
41674171
version "1.0.1"
41684172
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"

0 commit comments

Comments
 (0)