Skip to content

Commit 8ff41ae

Browse files
committed
Add webpack and dev server config for ssr
1 parent fe1f935 commit 8ff41ae

File tree

8 files changed

+17068
-4
lines changed

8 files changed

+17068
-4
lines changed

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.idea/
2+
.vscode/
3+
node_modules/
4+
build
5+
.DS_Store
6+
*.tgz
7+
my-app*
8+
template/src/__tests__/__snapshots__/
9+
lerna-debug.log
10+
npm-debug.log*
11+
yarn-debug.log*
12+
yarn-error.log*
13+
/.changelog
14+
.npm/

config/paths.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ module.exports = {
8181
appPublic: resolveApp('public'),
8282
appHtml: resolveApp('public/index.html'),
8383
appIndexJs: resolveModule(resolveApp, 'src/index'),
84+
appIndexSsrJs: resolveModule(resolveApp, 'src/index.ssr'),
8485
appPackageJson: resolveApp('package.json'),
8586
appSrc: resolveApp('src'),
8687
appTsConfig: resolveApp('tsconfig.json'),
@@ -103,6 +104,7 @@ module.exports = {
103104
appPublic: resolveApp('public'),
104105
appHtml: resolveApp('public/index.html'),
105106
appIndexJs: resolveModule(resolveApp, 'src/index'),
107+
appIndexSsrJs: resolveModule(resolveApp, 'src/index.ssr'),
106108
appPackageJson: resolveApp('package.json'),
107109
appSrc: resolveApp('src'),
108110
appTsConfig: resolveApp('tsconfig.json'),
@@ -137,6 +139,7 @@ if (
137139
appPublic: resolveOwn('template/public'),
138140
appHtml: resolveOwn('template/public/index.html'),
139141
appIndexJs: resolveModule(resolveOwn, 'template/src/index'),
142+
appIndexSsrJs: resolveModule(resolveOwn, 'src/index.ssr'),
140143
appPackageJson: resolveOwn('package.json'),
141144
appSrc: resolveOwn('template/src'),
142145
appTsConfig: resolveOwn('template/tsconfig.json'),

config/webpack.ssr.config.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use strict';
2+
3+
const webpack = require('webpack');
4+
const HtmlWebpackPlugin = require('html-webpack-plugin');
5+
const webpackConfigFactory = require('./webpack.config.js');
6+
const paths = require('./paths');
7+
8+
// decorate original webpack config
9+
module.exports = function (webpackEnv) {
10+
const template = webpackConfigFactory(webpackEnv);
11+
return Object.assign(
12+
// multi compiler config (client + server)
13+
[
14+
// ssr compiler config
15+
{
16+
...template,
17+
name: 'ssr',
18+
target: 'node',
19+
entry: [
20+
// ssr entry point, usually s.th. like `./src/index.ssr.tsx` with `export default (req,res) => {}`
21+
paths.appIndexSsrJs
22+
],
23+
output: {
24+
...template.output,
25+
filename: 'ssr.js',
26+
libraryTarget: 'commonjs2',
27+
// avoid using absolute path '/' defined by template
28+
publicPath: ''
29+
},
30+
optimization: {
31+
...template.optimization,
32+
// disable chunk splitting
33+
splitChunks: {},
34+
runtimeChunk: false
35+
},
36+
// filter out some plugins
37+
plugins: template.plugins.filter(plugin =>
38+
[
39+
// do not generate an additional index.html for ssr
40+
HtmlWebpackPlugin,
41+
// Avoid shadowing of process.env for ssr handler
42+
webpack.DefinePlugin
43+
].every(pluginClass => !(plugin instanceof pluginClass))
44+
)
45+
},
46+
// client compiler config
47+
{
48+
// use original config
49+
...template,
50+
name: 'client'
51+
},
52+
],
53+
// workarounds
54+
{
55+
// at least one script expects to find `config.output.publicPath` but `config` is now an array
56+
output: {
57+
publicPath: template.output.publicPath
58+
}
59+
}
60+
);
61+
};

config/webpackDevServer.ssr.config.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
const webpackDevServerConfigFactory = require('./webpackDevServer.config.js');
4+
const requireFromString = require('require-from-string');
5+
const path = require('path');
6+
const paths = require('./paths');
7+
8+
// decorate original webpack dev server config
9+
module.exports = function(proxy, allowedHost) {
10+
const template = webpackDevServerConfigFactory(proxy, allowedHost);
11+
12+
return {
13+
...template,
14+
serverSideRender: true,
15+
before(app, server) {
16+
const multiCompiler = server.middleware.context.compiler;
17+
const compiler = multiCompiler.compilers.filter(c => c.name === 'ssr')[0];
18+
if(!compiler) {
19+
throw new Error("Webpack compiler with name 'ssr' not found.");
20+
}
21+
22+
// stores current request handler, changes after every compilation
23+
let ssrHandler;
24+
25+
// fetch ssr handler after every compilation
26+
multiCompiler.hooks.done.tap('webpackDevServer.ssr', () => {
27+
// fetch ssr entry point file name
28+
const filename = path.resolve(paths.appBuild, "..", "dist", "ssr.js");
29+
// read code from in memory fs
30+
const code = compiler.outputFileSystem.readFileSync(filename).toString();
31+
// compile code to node module
32+
const exports = requireFromString(code, filename);
33+
34+
if (exports.devServerHandler) {
35+
// install dev server handler
36+
ssrHandler = exports.devServerHandler(compiler);
37+
} else if (exports.default) {
38+
// install production handler
39+
ssrHandler = exports.default;
40+
} else {
41+
// no handler found
42+
throw new Error("SSR entry point does not export a handler.");
43+
}
44+
});
45+
46+
// install request handler in webpack dev server
47+
app.use((request, response, next) => {
48+
// forward to handler if it exists
49+
if (ssrHandler) {
50+
ssrHandler(request, response, next);
51+
} else {
52+
// skip to dev middleware
53+
next();
54+
}
55+
});
56+
}
57+
};
58+
};

0 commit comments

Comments
 (0)