From b443f4df9f38502b73707073a6e2a21e1a9c684a Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Thu, 15 Aug 2024 17:49:42 +0300 Subject: [PATCH] fix: support `devServer: false` --- src/middleware.js | 6 +- src/utils/getPaths.js | 5 + src/utils/ready.js | 1 - src/utils/setupHooks.js | 11 +- src/utils/setupOutputFileSystem.js | 11 +- src/utils/setupWriteToDisk.js | 5 + .../logging.test.js.snap.webpack5 | 24 ++- .../webpack.array.dev-server-false.js | 44 ++++++ test/logging.test.js | 53 ++++++- test/middleware.test.js | 137 ++++++++++++++++++ test/utils/setupOutputFileSystem.test.js | 6 +- 11 files changed, 282 insertions(+), 21 deletions(-) create mode 100644 test/fixtures/webpack.array.dev-server-false.js diff --git a/src/middleware.js b/src/middleware.js index 2438df9da..032e28273 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -519,9 +519,9 @@ function wrapper(context) { headers = allHeaders; } - headers.forEach((header) => { - setResponseHeader(res, header.key, header.value); - }); + for (const { key, value } of headers) { + setResponseHeader(res, key, value); + } } if ( diff --git a/src/utils/getPaths.js b/src/utils/getPaths.js index 93dafa82f..7b538ea4f 100644 --- a/src/utils/getPaths.js +++ b/src/utils/getPaths.js @@ -20,6 +20,11 @@ function getPaths(context) { const publicPaths = []; for (const { compilation } of childStats) { + if (compilation.options.devServer === false) { + // eslint-disable-next-line no-continue + continue; + } + // The `output.path` is always present and always absolute const outputPath = compilation.getPath( compilation.outputOptions.path || "", diff --git a/src/utils/ready.js b/src/utils/ready.js index 741bcaa9e..a3a4ef97e 100644 --- a/src/utils/ready.js +++ b/src/utils/ready.js @@ -19,7 +19,6 @@ function ready(context, callback, req) { const name = (req && req.url) || callback.name; context.logger.info(`wait until bundle finished${name ? `: ${name}` : ""}`); - context.callbacks.push(callback); } diff --git a/src/utils/setupHooks.js b/src/utils/setupHooks.js index 7fe71435e..38bd5bc8a 100644 --- a/src/utils/setupHooks.js +++ b/src/utils/setupHooks.js @@ -144,14 +144,9 @@ function setupHooks(context) { context.callbacks = []; // Execute callback that are delayed - callbacks.forEach( - /** - * @param {(...args: any[]) => Stats | MultiStats} callback - */ - (callback) => { - callback(stats); - }, - ); + for (const callback of callbacks) { + callback(stats); + } }); } diff --git a/src/utils/setupOutputFileSystem.js b/src/utils/setupOutputFileSystem.js index 9fb315b31..9c6bbc316 100644 --- a/src/utils/setupOutputFileSystem.js +++ b/src/utils/setupOutputFileSystem.js @@ -30,8 +30,10 @@ function setupOutputFileSystem(context) { // TODO we need to support webpack-dev-server as a plugin or revisit it const compiler = /** @type {MultiCompiler} */ - (context.compiler).compilers.filter((item) => - Object.prototype.hasOwnProperty.call(item.options, "devServer"), + (context.compiler).compilers.filter( + (item) => + Object.prototype.hasOwnProperty.call(item.options, "devServer") && + item.options.devServer !== false, ); ({ outputFileSystem } = @@ -48,6 +50,11 @@ function setupOutputFileSystem(context) { (context.compiler).compilers || [context.compiler]; for (const compiler of compilers) { + if (compiler.options.devServer === false) { + // eslint-disable-next-line no-continue + continue; + } + // @ts-ignore compiler.outputFileSystem = outputFileSystem; } diff --git a/src/utils/setupWriteToDisk.js b/src/utils/setupWriteToDisk.js index e70becffa..94bfedce7 100644 --- a/src/utils/setupWriteToDisk.js +++ b/src/utils/setupWriteToDisk.js @@ -21,6 +21,11 @@ function setupWriteToDisk(context) { (context.compiler).compilers || [context.compiler]; for (const compiler of compilers) { + if (compiler.options.devServer === false) { + // eslint-disable-next-line no-continue + continue; + } + compiler.hooks.emit.tap("DevMiddleware", () => { // @ts-ignore if (compiler.hasWebpackDevMiddlewareAssetEmittedCallback) { diff --git a/test/__snapshots__/logging.test.js.snap.webpack5 b/test/__snapshots__/logging.test.js.snap.webpack5 index 86d30f333..dd3bd29a4 100644 --- a/test/__snapshots__/logging.test.js.snap.webpack5 +++ b/test/__snapshots__/logging.test.js.snap.webpack5 @@ -47,8 +47,6 @@ success (webpack x.x.x) compiled successfully in x ms" exports[`logging should logging in multi-compiler and respect the "stats" option from configuration #3: stderr 1`] = `""`; -exports[`logging should logging in multi-compiler and respect the "stats" option from configuration #3: stderr 2`] = `""`; - exports[`logging should logging in multi-compiler and respect the "stats" option from configuration #3: stdout 1`] = ` "asset bundle.js x KiB [emitted] (name: main) ./broken.js x bytes [built] [code generated] [1 error] @@ -79,7 +77,9 @@ cacheable modules x bytes webpack x.x.x compiled successfully in x ms" `; -exports[`logging should logging in multi-compiler and respect the "stats" option from configuration #3: stdout 2`] = ` +exports[`logging should logging in multi-compiler and respect the "stats" option from configuration #4: stderr 1`] = `""`; + +exports[`logging should logging in multi-compiler and respect the "stats" option from configuration #4: stdout 1`] = ` "asset bundle.js x KiB [emitted] (name: main) ./broken.js x bytes [built] [code generated] [1 error] @@ -103,6 +103,24 @@ asset svg.svg x KiB [emitted] [from: svg.svg] (auxiliary name: main) asset index.html x bytes [emitted] [from: index.html] (auxiliary name: main)" `; +exports[`logging should logging in multi-compiler and respect the "stats" option from configuration #5: stderr 1`] = `""`; + +exports[`logging should logging in multi-compiler and respect the "stats" option from configuration #5: stdout 1`] = ` +"asset bundle.js x KiB [emitted] (name: main) +./bar.js x bytes [built] [code generated] +webpack x.x.x compiled successfully in x ms + +asset bundle.js x KiB [emitted] (name: main) +asset svg.svg x KiB [emitted] [from: svg.svg] (auxiliary name: main) +asset index.html x bytes [emitted] [from: index.html] (auxiliary name: main) +runtime modules x bytes x modules +cacheable modules x bytes +./foo.js x bytes [built] [code generated] +./svg.svg x bytes [built] [code generated] +./index.html x bytes [built] [code generated] +webpack x.x.x compiled successfully in x ms" +`; + exports[`logging should logging in multi-compiler and respect the "stats" option from configuration: stderr 1`] = `""`; exports[`logging should logging in multi-compiler and respect the "stats" option from configuration: stdout 1`] = ` diff --git a/test/fixtures/webpack.array.dev-server-false.js b/test/fixtures/webpack.array.dev-server-false.js new file mode 100644 index 000000000..502a39f60 --- /dev/null +++ b/test/fixtures/webpack.array.dev-server-false.js @@ -0,0 +1,44 @@ +'use strict'; + +const path = require('path'); + +module.exports = [ + { + mode: 'development', + context: path.resolve(__dirname), + entry: './bar.js', + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, '../outputs/array/js3'), + publicPath: '/static-two/', + }, + infrastructureLogging: { + level: 'none' + }, + stats: 'normal', + devServer: false, + }, + { + mode: 'development', + context: path.resolve(__dirname), + entry: './foo.js', + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, '../outputs/array/js4'), + publicPath: '/static-one/', + }, + module: { + rules: [ + { + test: /\.(svg|html)$/, + loader: 'file-loader', + options: { name: '[name].[ext]' }, + }, + ], + }, + infrastructureLogging: { + level: 'none' + }, + stats: 'normal' + } +]; diff --git a/test/logging.test.js b/test/logging.test.js index 0e3ab411a..ff3f8fae5 100644 --- a/test/logging.test.js +++ b/test/logging.test.js @@ -65,6 +65,13 @@ function stderrToSnapshot(stderr) { const runner = path.resolve(__dirname, "./helpers/runner.js"); describe("logging", () => { + beforeEach(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + }); + it("should logging on successfully build", (done) => { let proc; @@ -854,7 +861,7 @@ describe("logging", () => { }); }); - it('should logging in multi-compiler and respect the "stats" option from configuration #3', (done) => { + it('should logging in multi-compiler and respect the "stats" option from configuration #4', (done) => { let proc; try { @@ -898,6 +905,50 @@ describe("logging", () => { }); }); + it('should logging in multi-compiler and respect the "stats" option from configuration #5', (done) => { + let proc; + + try { + proc = execa(runner, [], { + stdio: "pipe", + env: { + WEBPACK_CONFIG: "webpack.array.dev-server-false", + FORCE_COLOR: true, + }, + }); + } catch (error) { + throw error; + } + + let stdout = ""; + let stderr = ""; + + proc.stdout.on("data", (chunk) => { + stdout += chunk.toString(); + + if (/compiled-for-tests/gi.test(stdout)) { + proc.stdin.write("|exit|"); + } + }); + + proc.stderr.on("data", (chunk) => { + stderr += chunk.toString(); + proc.stdin.write("|exit|"); + }); + + proc.on("error", (error) => { + done(error); + }); + + proc.on("exit", () => { + expect(stdout).toContain("\u001b[1m"); + expect(stdoutToSnapshot(stdout)).toMatchSnapshot("stdout"); + expect(stderrToSnapshot(stderr)).toMatchSnapshot("stderr"); + + done(); + }); + }); + it('should logging an error in "watch" method', (done) => { let proc; diff --git a/test/middleware.test.js b/test/middleware.test.js index a52c4a77c..2790f4495 100644 --- a/test/middleware.test.js +++ b/test/middleware.test.js @@ -28,6 +28,7 @@ import webpackQueryStringConfig from "./fixtures/webpack.querystring.config"; import webpackClientServerConfig from "./fixtures/webpack.client.server.config"; import getCompilerHooks from "./helpers/getCompilerHooks"; import webpackPublicPathConfig from "./fixtures/webpack.public-path.config"; +import webpackMultiDevServerFalseConfig from "./fixtures/webpack.array.dev-server-false"; // Suppress unnecessary stats output global.console.log = jest.fn(); @@ -840,6 +841,11 @@ describe.each([ const outputPath = path.resolve(__dirname, "./outputs/basic-test"); beforeAll(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + compiler = getCompiler({ ...webpackConfig, output: { @@ -1416,6 +1422,102 @@ describe.each([ }); }); + describe("should work in multi-compiler mode with `devServer` false", () => { + beforeAll(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + + const compiler = getCompiler(webpackMultiDevServerFalseConfig); + + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + ); + }); + + afterAll(async () => { + await close(server, instance); + }); + + it('should return "200" code for GET request to the bundle file for the first compiler', async () => { + const outputPath = path.resolve(__dirname, "./outputs/array/js4/"); + + expect(fs.existsSync(path.resolve(outputPath, "bundle.js"))).toBe( + false, + ); + + const response = await req.get("/static-one/bundle.js"); + + expect(response.statusCode).toEqual(200); + }); + + it('should return "404" code for GET request to a non existing file for the first compiler', async () => { + const response = await req.get("/static-one/invalid.js"); + + expect(response.statusCode).toEqual(404); + }); + + it('should return "200" code for GET request to the "public" path for the first compiler', async () => { + const response = await req.get("/static-one/"); + + expect(response.statusCode).toEqual(200); + expect(response.headers["content-type"]).toEqual( + "text/html; charset=utf-8", + ); + }); + + it('should return "200" code for GET request to the "index" option for the first compiler', async () => { + const response = await req.get("/static-one/index.html"); + + expect(response.statusCode).toEqual(200); + expect(response.headers["content-type"]).toEqual( + "text/html; charset=utf-8", + ); + }); + + it('should return "200" code for GET request for the bundle file for the second compiler', async () => { + const outputPath = path.resolve(__dirname, "./outputs/array/js3/"); + + expect(fs.existsSync(path.resolve(outputPath, "bundle.js"))).toBe( + true, + ); + + const response = await req.get("/static-two/bundle.js"); + + expect(response.statusCode).toEqual(404); + }); + + it('should return "404" code for GET request to a non existing file for the second compiler', async () => { + const response = await req.get("/static-two/invalid.js"); + + expect(response.statusCode).toEqual(404); + }); + + it('should return "404" code for GET request to the "public" path for the second compiler', async () => { + const response = await req.get("/static-two/"); + + expect(response.statusCode).toEqual(404); + }); + + it('should return "404" code for GET request to the "index" option for the second compiler', async () => { + const response = await req.get("/static-two/index.html"); + + expect(response.statusCode).toEqual(404); + }); + + it('should return "404" code for GET request to the non-public path', async () => { + const response = await req.get("/static-three/"); + + expect(response.statusCode).toEqual(404); + expect(response.headers["content-type"]).toEqual( + get404ContentTypeHeader(name), + ); + }); + }); + describe("should work with difference requests", () => { const basicOutputPath = path.resolve(__dirname, "./outputs/basic"); const fixtures = [ @@ -3513,6 +3615,11 @@ describe.each([ let compiler; beforeAll(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + compiler = getCompiler({ ...webpackConfig, output: { @@ -3602,6 +3709,11 @@ describe.each([ let compiler; beforeAll(async () => { + await fs.promises.rm(outputPath, { + recursive: true, + force: true, + }); + compiler = getCompiler({ ...webpackConfig, output: { @@ -3731,6 +3843,11 @@ describe.each([ let compiler; beforeAll(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + compiler = getCompiler({ ...webpackConfig, output: { @@ -3781,6 +3898,11 @@ describe.each([ let compiler; beforeAll(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + compiler = getCompiler({ ...webpackConfig, output: { @@ -3831,6 +3953,11 @@ describe.each([ let compiler; beforeAll(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + compiler = getCompiler({ ...webpackQueryStringConfig, output: { @@ -3879,6 +4006,11 @@ describe.each([ let compiler; beforeAll(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + compiler = getCompiler([ { ...webpackMultiWatchOptionsConfig[0], @@ -3952,6 +4084,11 @@ describe.each([ let hash; beforeAll(async () => { + await fs.promises.rm(path.resolve(__dirname, "./outputs/"), { + recursive: true, + force: true, + }); + compiler = getCompiler({ ...webpackConfig, ...{ diff --git a/test/utils/setupOutputFileSystem.test.js b/test/utils/setupOutputFileSystem.test.js index efcb71520..ce681bf57 100644 --- a/test/utils/setupOutputFileSystem.test.js +++ b/test/utils/setupOutputFileSystem.test.js @@ -17,7 +17,7 @@ describe("setupOutputFileSystem", () => { it("should create default fs if not provided", () => { const context = { - compiler: {}, + compiler: { options: {} }, options: {}, }; @@ -32,7 +32,7 @@ describe("setupOutputFileSystem", () => { it("should set fs for multi compiler", () => { const context = { compiler: { - compilers: [{}, {}], + compilers: [{ options: {} }, { options: {} }], }, options: {}, }; @@ -46,7 +46,7 @@ describe("setupOutputFileSystem", () => { it("should use provided fs with correct methods", () => { const context = { - compiler: {}, + compiler: { options: {} }, options: { outputFileSystem: { join: () => {},