1 | const path = require('path');
|
2 | const webpack = require('webpack');
|
3 | const makeDir = require('make-dir');
|
4 | const openBrowser = require('open');
|
5 |
|
6 | const {getWebpackConfig} = require('./webpack');
|
7 | const {getConfig} = require('./default');
|
8 |
|
9 | function emitVueSSR(ctx) {
|
10 | if (!ctx.emitter) return;
|
11 | setImmediate(() => {
|
12 | if (!ctx.serverCompiling && !ctx.clientCompiling) {
|
13 | ctx.emitter.emit('vue-ssr', {
|
14 | serverBundle: ctx.serverBundle,
|
15 | clientManifest: ctx.clientManifest,
|
16 | });
|
17 | }
|
18 | });
|
19 | }
|
20 |
|
21 | function emitVueSSRClientManifest(ctx, assets) {
|
22 | if (!ctx.emitter) return;
|
23 |
|
24 | const fileName = 'vue-ssr-client-manifest.json';
|
25 | if (!assets[fileName]) return;
|
26 |
|
27 | ctx.clientManifest = JSON.parse(assets[fileName].source());
|
28 | ctx.emitter.emit(
|
29 | 'vue-ssr-client-manifest',
|
30 | ctx.clientManifest,
|
31 | );
|
32 | emitVueSSR(ctx);
|
33 | }
|
34 |
|
35 | function emitVueSSRServerBundle(ctx, assets) {
|
36 | if (!ctx.emitter) return;
|
37 |
|
38 | const fileName = 'vue-ssr-server-bundle.json';
|
39 | if (!assets[fileName]) return;
|
40 |
|
41 | ctx.serverBundle = JSON.parse(assets[fileName].source());
|
42 | ctx.emitter.emit(
|
43 | 'vue-ssr-server-bundle',
|
44 | ctx.serverBundle,
|
45 | );
|
46 | emitVueSSR(ctx);
|
47 | }
|
48 |
|
49 | function initSSRCompiler(options = {}, ctx) {
|
50 | const config = options.config || {};
|
51 | const webpackConfig = options.webpackConfig || {};
|
52 |
|
53 | const finalSSRConfig = getConfig(config, 'development');
|
54 | finalSSRConfig.destPath = ctx.devServerSSRPath;
|
55 | finalSSRConfig.publicUrl = '/';
|
56 | finalSSRConfig.isSSR = true;
|
57 | finalSSRConfig.isSSRDevServer = true;
|
58 |
|
59 | const finalSSRWebpackConfig = getWebpackConfig({
|
60 | env: 'development',
|
61 | config: finalSSRConfig,
|
62 | webpackConfig,
|
63 | });
|
64 |
|
65 | const ssrCompiler = webpack(finalSSRWebpackConfig);
|
66 | ssrCompiler.watch({}, (err, stats) => {
|
67 | if (err) {
|
68 | console.error(err);
|
69 | return;
|
70 | }
|
71 |
|
72 | if (stats.hasErrors()) {
|
73 |
|
74 | }
|
75 | });
|
76 |
|
77 | ssrCompiler.hooks.done.tap('beforeCompile', () => {
|
78 | ctx.serverCompiling = true;
|
79 | });
|
80 |
|
81 | ssrCompiler.hooks.emit.tapAsync('sm-webpack-dev-server', (compilation, callback) => {
|
82 | emitVueSSRServerBundle(ctx, compilation.assets);
|
83 | ctx.serverCompiling = false;
|
84 | compilation.assets = {};
|
85 | callback();
|
86 | });
|
87 |
|
88 | return ssrCompiler;
|
89 | }
|
90 |
|
91 | function getDevServerSSRPath(config, originalDestPath) {
|
92 | const compileSSR = config.hasSSR && config.devServer.buildSSR;
|
93 | const devServerSSRPath = path.join(originalDestPath, 'dev_server');
|
94 | if (compileSSR) {
|
95 |
|
96 | makeDir(devServerSSRPath)
|
97 | .then(() => {})
|
98 | .catch(err => console.error(err));
|
99 | }
|
100 |
|
101 | return devServerSSRPath;
|
102 | }
|
103 |
|
104 | function getCompiler(options = {}) {
|
105 | const config = options.config || {};
|
106 | const webpackConfig = options.webpackConfig || {};
|
107 | const emitter = options.emitter;
|
108 |
|
109 | const finalConfig = getConfig(config, 'development');
|
110 | const originalDestPath = finalConfig.destPath;
|
111 | finalConfig.isDevServer = true;
|
112 | finalConfig.hasHotClient = false;
|
113 | finalConfig.destPath = '.';
|
114 | finalConfig.publicUrl = '/';
|
115 |
|
116 | const compileSSR = finalConfig.hasSSR && finalConfig.devServer.buildSSR;
|
117 |
|
118 |
|
119 | const ctx = {
|
120 | emitter,
|
121 | devServerSSRPath: getDevServerSSRPath(finalConfig, originalDestPath),
|
122 | serverBundle: null,
|
123 | clientManifest: null,
|
124 | clientCompiling: false,
|
125 | serverCompiling: false,
|
126 | };
|
127 |
|
128 | const finalWebpackConfig = getWebpackConfig({
|
129 | env: 'development',
|
130 | devServer: true,
|
131 | config: finalConfig,
|
132 | webpackConfig,
|
133 | });
|
134 |
|
135 | let isFirstCompile = true;
|
136 | const compiler = webpack(finalWebpackConfig);
|
137 |
|
138 | compiler.hooks.done.tap('beforeCompile', () => {
|
139 | ctx.clientCompiling = true;
|
140 | });
|
141 |
|
142 | compiler.hooks.emit.tapAsync('sm-webpack-dev-server', (compilation, callback) => {
|
143 | if (compileSSR) {
|
144 | emitVueSSRClientManifest(ctx, compilation.assets);
|
145 | }
|
146 |
|
147 | ctx.clientCompiling = false;
|
148 | callback();
|
149 | });
|
150 |
|
151 | const devServerOpts = finalConfig.devServer || {};
|
152 | compiler.hooks.done.tap('sm-webpack-dev-server', (stats) => {
|
153 | if (stats.hasErrors()) return;
|
154 |
|
155 | if (isFirstCompile) {
|
156 | isFirstCompile = false;
|
157 | if (finalConfig.openBrowser) {
|
158 | const host = devServerOpts.wwwHost || 'localhost';
|
159 | const port = devServerOpts.port || 3001;
|
160 | const protocol = devServerOpts.https ? 'https' : 'http';
|
161 | openBrowser(`${protocol}://${host}:${port}`);
|
162 | }
|
163 | }
|
164 | });
|
165 |
|
166 | if (compileSSR) {
|
167 | initSSRCompiler(options, ctx);
|
168 | }
|
169 |
|
170 | return {
|
171 | compiler,
|
172 | config: finalConfig,
|
173 | webpackConfig: finalWebpackConfig,
|
174 | };
|
175 | }
|
176 |
|
177 | module.exports = {
|
178 | getCompiler,
|
179 | };
|