diff --git a/lib/Server.js b/lib/Server.js index a3a4803cd2..659dc03ea0 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -1056,16 +1056,20 @@ class Server { // TODO remove after drop webpack v4 support compiler.options.plugins = compiler.options.plugins || []; - if ( - this.options.hot && - !compiler.options.plugins.find( + if (this.options.hot) { + const HMRPluginExists = compiler.options.plugins.find( (p) => p.constructor === webpack.HotModuleReplacementPlugin - ) - ) { - // apply the HMR plugin, if it didn't exist before. - const plugin = new webpack.HotModuleReplacementPlugin(); + ); - plugin.apply(compiler); + if (HMRPluginExists) { + this.logger.warn( + `"hot: true" automatically applies HMR plugin, you don't have to add it manually to your webpack configuration.` + ); + } else { + // apply the HMR plugin + const plugin = new webpack.HotModuleReplacementPlugin(); + plugin.apply(compiler); + } } }); diff --git a/test/server/hot-option.test.js b/test/server/hot-option.test.js index 589259f03b..d19938697c 100644 --- a/test/server/hot-option.test.js +++ b/test/server/hot-option.test.js @@ -166,6 +166,73 @@ describe("hot option", () => { }); }); + describe("simple config with already added HMR plugin", () => { + let loggerWarnSpy; + let getInfrastructureLoggerSpy; + let compiler; + + beforeEach(() => { + compiler = webpack({ + ...config, + devServer: { hot: false }, + plugins: [...config.plugins, new webpack.HotModuleReplacementPlugin()], + }); + + loggerWarnSpy = jest.fn(); + + getInfrastructureLoggerSpy = jest + .spyOn(compiler, "getInfrastructureLogger") + .mockImplementation(() => { + return { + warn: loggerWarnSpy, + info: () => {}, + log: () => {}, + }; + }); + }); + + afterEach(() => { + getInfrastructureLoggerSpy.mockRestore(); + loggerWarnSpy.mockRestore(); + }); + + it("should show warning with hot normalized as true", async () => { + server = new Server({ port }, compiler); + + await server.start(); + + expect(loggerWarnSpy).toHaveBeenCalledWith( + `"hot: true" automatically applies HMR plugin, you don't have to add it manually to your webpack configuration.` + ); + + await server.stop(); + }); + + it(`should show warning with "hot: true"`, async () => { + server = new Server({ port, hot: true }, compiler); + + await server.start(); + + expect(loggerWarnSpy).toHaveBeenCalledWith( + `"hot: true" automatically applies HMR plugin, you don't have to add it manually to your webpack configuration.` + ); + + await server.stop(); + }); + + it(`should show warning with "hot: false"`, async () => { + server = new Server({ port, hot: false }, compiler); + + await server.start(); + + expect(loggerWarnSpy).not.toHaveBeenCalledWith( + `"hot: true" automatically applies HMR plugin, you don't have to add it manually to your webpack configuration.` + ); + + await server.stop(); + }); + }); + describe("multi compiler hot config HMR plugin", () => { it("should register the HMR plugin before compilation is complete", async () => { let pluginFound = false;