Skip to content

Commit 1911c37

Browse files
knagaitsevhiroppy
authored andcommitted
HMR plugin insertion for hot option and inline entry insertion in Server (#1738)
1 parent a827a65 commit 1911c37

File tree

10 files changed

+482
-41
lines changed

10 files changed

+482
-41
lines changed

lib/Server.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const historyApiFallback = require('connect-history-api-fallback');
3333
const webpack = require('webpack');
3434
const webpackDevMiddleware = require('webpack-dev-middleware');
3535

36+
const updateCompiler = require('./utils/updateCompiler');
3637
const createLogger = require('./utils/createLogger');
3738
const createCertificate = require('./utils/createCertificate');
3839

@@ -89,6 +90,8 @@ class Server {
8990
throw new Error("'filename' option must be set in lazy mode.");
9091
}
9192

93+
updateCompiler(compiler, options);
94+
9295
this.stats =
9396
options.stats && Object.keys(options.stats).length
9497
? options.stats

lib/utils/addEntries.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ function addEntries(config, options, server) {
88
// we're stubbing the app in this method as it's static and doesn't require
99
// a server to be supplied. createDomain requires an app with the
1010
// address() signature.
11+
1112
const app = server || {
1213
address() {
1314
return { port: options.port };
@@ -35,13 +36,22 @@ function addEntries(config, options, server) {
3536
const clone = {};
3637

3738
Object.keys(entry).forEach((key) => {
38-
clone[key] = entries.concat(entry[key]);
39+
// entry[key] should be a string here
40+
clone[key] = prependEntry(entry[key]);
3941
});
4042

4143
return clone;
4244
}
4345

44-
return entries.concat(entry);
46+
// in this case, entry is a string or an array.
47+
// make sure that we do not add duplicates.
48+
const entriesClone = entries.slice(0);
49+
[].concat(entry).forEach((newEntry) => {
50+
if (!entriesClone.includes(newEntry)) {
51+
entriesClone.push(newEntry);
52+
}
53+
});
54+
return entriesClone;
4555
};
4656

4757
// eslint-disable-next-line no-shadow

lib/utils/updateCompiler.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
/* eslint-disable
4+
no-shadow,
5+
no-undefined
6+
*/
7+
const webpack = require('webpack');
8+
const addEntries = require('./addEntries');
9+
10+
function updateCompiler(compiler, options) {
11+
if (options.inline !== false) {
12+
const findHMRPlugin = (config) => {
13+
if (!config.plugins) {
14+
return undefined;
15+
}
16+
17+
return config.plugins.find(
18+
(plugin) => plugin.constructor === webpack.HotModuleReplacementPlugin
19+
);
20+
};
21+
22+
const compilers = [];
23+
const compilersWithoutHMR = [];
24+
let webpackConfig;
25+
if (compiler.compilers) {
26+
webpackConfig = [];
27+
compiler.compilers.forEach((compiler) => {
28+
webpackConfig.push(compiler.options);
29+
compilers.push(compiler);
30+
if (!findHMRPlugin(compiler.options)) {
31+
compilersWithoutHMR.push(compiler);
32+
}
33+
});
34+
} else {
35+
webpackConfig = compiler.options;
36+
compilers.push(compiler);
37+
if (!findHMRPlugin(compiler.options)) {
38+
compilersWithoutHMR.push(compiler);
39+
}
40+
}
41+
42+
// it's possible that we should clone the config before doing
43+
// this, but it seems safe not to since it actually reflects
44+
// the changes we are making to the compiler
45+
// important: this relies on the fact that addEntries now
46+
// prevents duplicate new entries.
47+
addEntries(webpackConfig, options);
48+
compilers.forEach((compiler) => {
49+
const config = compiler.options;
50+
compiler.hooks.entryOption.call(config.context, config.entry);
51+
});
52+
53+
// do not apply the plugin unless it didn't exist before.
54+
if (options.hot || options.hotOnly) {
55+
compilersWithoutHMR.forEach((compiler) => {
56+
// addDevServerEntrypoints above should have added the plugin
57+
// to the compiler options
58+
const plugin = findHMRPlugin(compiler.options);
59+
if (plugin) {
60+
plugin.apply(compiler);
61+
}
62+
});
63+
}
64+
}
65+
}
66+
67+
module.exports = updateCompiler;

package-lock.json

Lines changed: 11 additions & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/Client.test.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const express = require('express');
44
const httpProxy = require('http-proxy-middleware');
55
const request = require('supertest');
6-
const addEntries = require('../lib/utils/addEntries');
76
const helper = require('./helper');
87
const config = require('./fixtures/client-config/webpack.config');
98
const runBrowser = require('./helpers/run-browser');
@@ -28,13 +27,13 @@ describe('Client code', () => {
2827
port: 9001,
2928
host: '0.0.0.0',
3029
disableHostCheck: true,
30+
inline: true,
3131
hot: true,
3232
watchOptions: {
3333
poll: true,
3434
},
3535
};
36-
addEntries(config, options);
37-
helper.start(config, options, done);
36+
helper.startAwaitingCompilation(config, options, done);
3837
});
3938

4039
afterAll(helper.close);
@@ -65,8 +64,7 @@ describe('Client code', () => {
6564
expect(requestObj.url()).toMatch(
6665
/^http:\/\/localhost:9000\/sockjs-node/
6766
);
68-
browser.close();
69-
done();
67+
browser.close().then(done);
7068
});
7169
page.goto('http://localhost:9000/main');
7270
});

test/Entry.test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,21 @@ describe('Entry', () => {
254254
]);
255255
});
256256

257+
it('can prevent duplicate entries from successive calls', () => {
258+
const webpackOptions = Object.assign({}, config);
259+
const devServerOptions = { hot: true };
260+
261+
addEntries(webpackOptions, devServerOptions);
262+
addEntries(webpackOptions, devServerOptions);
263+
264+
expect(webpackOptions.entry.length).toEqual(3);
265+
266+
const result = webpackOptions.entry.filter((entry) =>
267+
normalize(entry).includes('webpack/hot/dev-server')
268+
);
269+
expect(result.length).toEqual(1);
270+
});
271+
257272
it('supports entry as Function', () => {
258273
const webpackOptions = Object.assign({}, configEntryAsFunction);
259274
const devServerOptions = {};

0 commit comments

Comments
 (0)