├── .github ├── renovate.json5 └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── biome.jsonc ├── client-src ├── index.js └── utils │ └── ansiHTML.ts ├── jest.config.js ├── package.json ├── pnpm-lock.yaml ├── scripts ├── patch-node-env.cjs └── release.mjs ├── src ├── config.ts ├── index.ts ├── patch.ts └── server.ts ├── tests ├── ansiHTML.test.ts ├── e2e │ ├── __snapshots__ │ │ ├── allowed-hosts.test.js.snap.webpack5 │ │ ├── api.test.js.snap.webpack5 │ │ ├── app.test.js.snap.webpack5 │ │ ├── bonjour.test.js.snap.webpack5 │ │ ├── built-in-routes.test.js.snap.webpack5 │ │ ├── client-reconnect.test.js.snap.webpack5 │ │ ├── client.test.js.snap.webpack5 │ │ ├── compress.test.js.snap.webpack5 │ │ ├── entry.test.js.snap.webpack5 │ │ ├── headers.test.js.snap.webpack5 │ │ ├── history-api-fallback.test.js.snap.webpack5 │ │ ├── host.test.js.snap.webpack5 │ │ ├── hot-and-live-reload.test.js.snap.webpack5 │ │ ├── ipc.test.js.snap.webpack5 │ │ ├── logging.test.js.snap.webpack5 │ │ ├── mime-types.test.js.snap.webpack5 │ │ ├── module-federation.test.js.snap.webpack5 │ │ ├── multi-compiler.test.js.snap.webpack5 │ │ ├── on-listening.test.js.snap.webpack5 │ │ ├── overlay.test.js.snap.webpack5 │ │ ├── port.test.js.snap.webpack5 │ │ ├── server-and-client-transport.test.js.snap.webpack5 │ │ ├── server.test.js.snap.webpack5 │ │ ├── setup-exit-signals.test.js.snap.webpack5 │ │ ├── setup-middlewares.test.js.snap.webpack5 │ │ ├── static-directory.test.js.snap.webpack5 │ │ ├── static-public-path.test.js.snap.webpack5 │ │ ├── stats.test.js.snap.webpack5 │ │ ├── target.test.js.snap.webpack5 │ │ ├── watch-files.test.js.snap.webpack5 │ │ ├── web-socket-communication.test.js.snap.webpack5 │ │ ├── web-socket-server-url.test.js.snap.webpack5 │ │ └── web-socket-server.test.js.snap.webpack5 │ ├── allowed-hosts.test.js │ ├── api.test.js │ ├── app.test.js │ ├── bonjour.test.js │ ├── built-in-routes.test.js │ ├── client-reconnect.test.js │ ├── client.test.js │ ├── compress.test.js │ ├── entry.test.js │ ├── headers.test.js │ ├── history-api-fallback.test.js │ ├── host.test.js │ ├── hot-and-live-reload.test.js │ ├── ipc.test.js │ ├── lazy-compilation.test.js │ ├── logging.test.js │ ├── mime-types.test.js │ ├── module-federation.test.js │ ├── multi-compiler.test.js │ ├── on-listening.test.js │ ├── overlay.test.js │ ├── port.test.js │ ├── progress.test.js │ ├── range-header.test.js │ ├── server-and-client-transport.test.js │ ├── server.test.js │ ├── setup-exit-signals.test.js │ ├── setup-middlewares.test.js │ ├── static-directory.test.js │ ├── static-public-path.test.js │ ├── stats.test.js │ ├── target.test.js │ ├── watch-files.test.js │ ├── web-socket-communication.test.js │ ├── web-socket-server-url.test.js │ └── web-socket-server.test.js ├── fixtures │ ├── client-config │ │ ├── bar.js │ │ ├── foo.js │ │ ├── index.html │ │ ├── static │ │ │ └── foo.txt │ │ └── webpack.config.js │ ├── custom-client │ │ ├── CustomClientEntry.js │ │ ├── CustomClientHotEntry.js │ │ └── CustomSockJSClient.js │ ├── historyapifallback-2-config │ │ ├── bar.html │ │ ├── foo.js │ │ ├── other.html │ │ ├── random-file.txt │ │ └── webpack.config.js │ ├── historyapifallback-3-config │ │ ├── bar.html │ │ ├── foo.js │ │ ├── index.html │ │ └── webpack.config.js │ ├── historyapifallback-config │ │ ├── bar.html │ │ ├── foo.js │ │ ├── index.html │ │ └── webpack.config.js │ ├── https-certificate │ │ ├── ca-symlink.pem │ │ ├── ca.pem │ │ ├── server-symlink.crt │ │ ├── server-symlink.key │ │ ├── server-symlink.pfx │ │ ├── server.crt │ │ ├── server.key │ │ └── server.pfx │ ├── lazy-compilation-multiple-entries │ │ ├── one.js │ │ ├── two.js │ │ └── webpack.config.js │ ├── lazy-compilation-single-entry │ │ ├── entry.js │ │ └── webpack.config.js │ ├── mime-types-config │ │ ├── file.custom │ │ ├── foo.js │ │ └── webpack.config.js │ ├── module-federation-config │ │ ├── entry1.js │ │ ├── entry2.js │ │ ├── webpack.config.js │ │ ├── webpack.multi.config.js │ │ ├── webpack.object-entry.config.js │ │ └── webpack.plugin.js │ ├── multi-compiler-one-configuration │ │ ├── foo.js │ │ └── webpack.config.js │ ├── multi-compiler-two-configurations │ │ ├── one.js │ │ ├── two.js │ │ └── webpack.config.js │ ├── multi-public-path-config │ │ ├── bar.js │ │ ├── baz.js │ │ ├── foo.js │ │ ├── test.html │ │ └── webpack.config.js │ ├── overlay-config │ │ ├── foo.js │ │ ├── trusted-types.webpack.config.js │ │ └── webpack.config.js │ ├── provide-plugin-custom │ │ ├── foo.js │ │ └── webpack.config.js │ ├── provide-plugin-default │ │ ├── foo.js │ │ └── webpack.config.js │ ├── provide-plugin-sockjs-config │ │ ├── foo.js │ │ └── webpack.config.js │ ├── provide-plugin-ws-config │ │ ├── foo.js │ │ └── webpack.config.js │ ├── reload-config-2 │ │ ├── foo.js │ │ └── webpack.config.js │ ├── reload-config │ │ ├── foo.js │ │ └── webpack.config.js │ ├── simple-config-other │ │ ├── foo.js │ │ └── webpack.config.js │ ├── simple-config │ │ ├── foo.js │ │ └── webpack.config.js │ ├── ssl │ │ ├── localhost-cert.pem │ │ └── localhost-privkey.pem │ ├── static-config │ │ ├── foo.js │ │ ├── other │ │ │ └── foo.html │ │ ├── public │ │ │ ├── assets │ │ │ │ ├── example.txt │ │ │ │ └── other.txt │ │ │ ├── bar │ │ │ │ └── index.html │ │ │ ├── foo.wasm │ │ │ ├── index.html │ │ │ ├── node_modules │ │ │ │ ├── .gitkeep │ │ │ │ └── index.html │ │ │ └── other.html │ │ ├── static │ │ │ └── index.html │ │ └── webpack.config.js │ ├── universal-compiler-config │ │ ├── browser.js │ │ ├── server.js │ │ └── webpack.config.js │ ├── watch-files-config │ │ ├── foo.js │ │ ├── other │ │ │ └── foo.html │ │ ├── public │ │ │ ├── assets │ │ │ │ ├── example.txt │ │ │ │ ├── non-exist.txt │ │ │ │ └── other.txt │ │ │ ├── bar │ │ │ │ └── index.html │ │ │ └── other.html │ │ ├── static │ │ │ └── index.html │ │ └── webpack.config.js │ ├── worker-config-dev-server-false │ │ ├── index.js │ │ ├── public │ │ │ └── worker-bundle.js │ │ ├── webpack.config.js │ │ └── worker.js │ └── worker-config │ │ ├── index.js │ │ ├── webpack.config.js │ │ └── worker.js ├── helpers │ ├── conditional-test.js │ ├── custom-http.js │ ├── get-port.js │ ├── global-setup-test.js │ ├── html-generator-plugin.js │ ├── normalize-options.js │ ├── normalize.js │ ├── ports-map.js │ ├── puppeteer-constants.js │ ├── run-browser.js │ ├── sequencer.js │ ├── session-subscribe.js │ ├── setup-test.js │ ├── snapshot-resolver.js │ ├── test-server.js │ └── trusted-types-html-generator-plugin.js ├── normalizeOptions.test.ts ├── placeholder.js ├── placeholder1.js └── tsconfig.json ├── tsconfig.build.json ├── tsconfig.client.json └── tsconfig.json /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base", "schedule:monthly", "group:allNonMajor"], 4 | "rangeStrategy": "bump", 5 | "packageRules": [{ "depTypeList": ["peerDependencies"], "enabled": false }] 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Release Full 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | version: 8 | type: choice 9 | description: "Release Version Type" 10 | required: true 11 | default: "patch" 12 | options: 13 | - major 14 | - premajor 15 | - minor 16 | - preminor 17 | - patch 18 | - prepatch 19 | - prerelease 20 | 21 | tag: 22 | type: choice 23 | description: "Release Npm Tag" 24 | required: true 25 | default: "latest" 26 | options: 27 | - canary 28 | - nightly 29 | - latest 30 | - beta 31 | - alpha 32 | 33 | dry_run: 34 | type: boolean 35 | description: "DryRun release" 36 | required: true 37 | default: false 38 | 39 | permissions: 40 | # To publish packages with provenance 41 | id-token: write 42 | 43 | jobs: 44 | release: 45 | name: Release 46 | permissions: 47 | contents: write 48 | # To publish packages with provenance 49 | id-token: write 50 | runs-on: ubuntu-latest 51 | 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@v4 55 | 56 | - name: Install Pnpm 57 | run: | 58 | npm install -g corepack@latest 59 | corepack enable 60 | 61 | - name: Setup Node.js 62 | uses: actions/setup-node@v4 63 | with: 64 | node-version: 20 65 | cache: "pnpm" 66 | 67 | - name: Install Dependencies 68 | run: pnpm install 69 | 70 | - name: Run Test 71 | run: pnpm run test 72 | 73 | - name: Try release to npm 74 | run: pnpm run release 75 | env: 76 | DRY_RUN: ${{ inputs.dry_run }} 77 | TAG: ${{ inputs.tag }} 78 | VERSION: ${{ inputs.version }} 79 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 80 | 81 | 82 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on pull request events but only for the main branch 6 | pull_request: 7 | branches: [main] 8 | push: 9 | branches: [main] 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | test: 16 | name: Test - ${{ matrix.os }} - Node v${{ matrix.node }}) 17 | 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest, windows-latest, macos-latest] 21 | node: [18.x] 22 | 23 | runs-on: ${{ matrix.os }} 24 | 25 | concurrency: 26 | group: test-${{ matrix.os }}-v${{ matrix.node }}-${{ github.ref }} 27 | cancel-in-progress: true 28 | 29 | # Steps represent a sequence of tasks that will be executed as part of the job 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | 34 | - name: Install Pnpm 35 | run: | 36 | npm install -g corepack@latest 37 | corepack enable 38 | 39 | - name: Setup Node.js 40 | uses: actions/setup-node@v4 41 | with: 42 | node-version: ${{ matrix.node }} 43 | cache: "pnpm" 44 | 45 | - name: Install Dependencies 46 | run: pnpm install 47 | 48 | - name: Run Test 49 | run: pnpm run test 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local 2 | .DS_Store 3 | *.local 4 | *.log* 5 | *.tsbuildinfo 6 | 7 | # Dist 8 | node_modules 9 | dist/ 10 | client/ 11 | test-results 12 | 13 | # Test 14 | tests/js/ 15 | !tests/**/node_modules 16 | 17 | # IDE 18 | .vscode/* 19 | !.vscode/settings.json 20 | !.vscode/extensions.json 21 | .idea 22 | *.tsbuildinfo 23 | 24 | 25 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ 2 | 3 | # The package of the same name conflicts with node:buffer and it will affect our ci results. https://www.npmjs.com/package/buffer 4 | hoist-pattern[]=!buffer -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "streetsidesoftware.code-spell-checker", 4 | "tamasfe.even-better-toml", 5 | "rust-lang.rust-analyzer", 6 | "fabiospampinato.vscode-debug-launcher" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.useIgnoreFiles": true, 3 | "[json]": { 4 | "editor.defaultFormatter": "biomejs.biome" 5 | }, 6 | "[typescript]": { 7 | "editor.defaultFormatter": "biomejs.biome" 8 | }, 9 | "[javascript]": { 10 | "editor.defaultFormatter": "biomejs.biome" 11 | }, 12 | "[javascriptreact]": { 13 | "editor.defaultFormatter": "biomejs.biome" 14 | }, 15 | "[css]": { 16 | "editor.defaultFormatter": "biomejs.biome" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @rspack/dev-server 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-present Bytedance, Inc. and its affiliates. 4 | 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Rspack Banner 4 | 5 | 6 | # @rspack/dev-server 7 | 8 |

9 | npm version 10 | downloads 11 | node version 12 | license 13 |

14 | 15 | Use Rspack with a development server that provides live reloading. This should be used for development only. 16 | 17 | > `@rspack/dev-server` is based on `webpack-dev-server@5` 18 | 19 | ## Installation 20 | 21 | First of all, install `@rspack/dev-server` and `@rspack/core` by your favorite package manager: 22 | 23 | ```bash 24 | # npm 25 | $ npm install @rspack/dev-server @rspack/core --save-dev 26 | 27 | # yarn 28 | $ yarn add @rspack/dev-server @rspack/core --dev 29 | 30 | # pnpm 31 | $ pnpm add @rspack/dev-server @rspack/core --save-dev 32 | 33 | # bun 34 | $ bun add @rspack/dev-server @rspack/core -D 35 | ``` 36 | 37 | ## Usage 38 | 39 | There are two recommended ways to use `@rspack/dev-server`: 40 | 41 | ### With the CLI 42 | 43 | The easiest way to use it is with the [`@rspack/cli`](https://www.npmjs.com/package/@rspack/cli). 44 | 45 | You can install it in your project by: 46 | 47 | ```bash 48 | # npm 49 | $ npm install @rspack/cli --save-dev 50 | 51 | # yarn 52 | $ yarn add @rspack/cli --dev 53 | 54 | # pnpm 55 | $ pnpm add @rspack/cli --save-dev 56 | 57 | # bun 58 | $ bun add @rspack/cli -D 59 | ``` 60 | 61 | And then start the development server by: 62 | 63 | ```bash 64 | # with rspack.config.js 65 | $ rspack serve 66 | 67 | # with custom config file 68 | $ rspack serve -c ./your.config.js 69 | ``` 70 | 71 | > See [CLI](https://rspack.dev/api/cli) for more details. 72 | 73 | While starting the development server, you can specify the configuration by the `devServer` field of your Rspack config file: 74 | 75 | ```js 76 | // rspack.config.js 77 | module.exports = { 78 | // ... 79 | devServer: { 80 | // the configuration of the development server 81 | port: 8080 82 | }, 83 | }; 84 | ``` 85 | 86 | > See [DevServer](https://rspack.dev/config/dev-server) for all configuration options. 87 | 88 | ### With the API 89 | 90 | While it's recommended to run `@rspack/dev-server` via the CLI, you may also choose to start a server via the API. 91 | 92 | ```js 93 | import { RspackDevServer } from "@rspack/dev-server"; 94 | import rspack from "@rspack/core"; 95 | import rspackConfig from './rspack.config.js'; 96 | 97 | const compiler = rspack(rspackConfig); 98 | const devServerOptions = { 99 | ...rspackConfig.devServer, 100 | // override 101 | port: 8888 102 | }; 103 | 104 | const server = new RspackDevServer(devServerOptions, compiler); 105 | 106 | server.startCallback(() => { 107 | console.log('Successfully started server on http://localhost:8888'); 108 | }); 109 | ``` 110 | 111 | > Cause `@rspack/dev-server` is based on `webpack-dev-server@5`, you can see the [webpack-dev-server API](https://webpack.js.org/api/webpack-dev-server/) for more methods of the server instance. 112 | 113 | ## Credits 114 | 115 | Thanks to the [webpack-dev-server](https://github.com/webpack/webpack-dev-server) project created by [@sokra](https://github.com/sokra) 116 | 117 | ## License 118 | 119 | [MIT licensed](https://github.com/web-infra-dev/rspack-dev-server/blob/main/LICENSE). 120 | -------------------------------------------------------------------------------- /biome.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", 3 | "files": { 4 | "ignore": [ 5 | "client-src/**/*", 6 | "client/**/*.js", 7 | "dist/**/*", 8 | "tests/fixtures/**/*" 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const os = require("node:os"); 2 | const isWin = os.platform() === "win32"; 3 | /** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */ 4 | const config = { 5 | preset: "ts-jest", 6 | testEnvironmentOptions: { 7 | url: "http://localhost/", 8 | }, 9 | testMatch: [ 10 | "/tests/*.test.ts", 11 | // TODO: enable after migrating to separated repo 12 | "/tests/e2e/*.test.js", 13 | ], 14 | testPathIgnorePatterns: [ 15 | // TODO: check why http proxy server throw error with websocket server 16 | "/tests/e2e/allowed-hosts.test.js", 17 | // TODO: check why this test timeout 18 | "/tests/e2e/host.test.js", 19 | // TODO: check why this test throw error when run with other tests 20 | "/tests/e2e/watch-files.test.js", 21 | // TODO: check why this test timeout 22 | "/tests/e2e/web-socket-server-url.test.js", 23 | ], 24 | cache: false, 25 | testTimeout: process.env.CI ? 120000 : 30000, 26 | transform: { 27 | "(.*)\\.{js,ts}": [ 28 | "ts-jest", 29 | { 30 | tsconfig: "/tests/tsconfig.json", 31 | }, 32 | ], 33 | }, 34 | // Add this to find out which test timeouts 35 | // testSequencer: "/tests/helpers/sequencer.js", 36 | snapshotResolver: "/tests/helpers/snapshot-resolver.js", 37 | setupFilesAfterEnv: ["/tests/helpers/setup-test.js"], 38 | globalSetup: "/tests/helpers/global-setup-test.js", 39 | moduleNameMapper: { 40 | "^uuid$": require.resolve("uuid"), 41 | }, 42 | }; 43 | 44 | module.exports = config; 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rspack/dev-server", 3 | "version": "1.1.3", 4 | "license": "MIT", 5 | "description": "Development server for rspack", 6 | "main": "./dist/index.js", 7 | "types": "./dist/index.d.ts", 8 | "publishConfig": { 9 | "access": "public", 10 | "registry": "https://registry.npmjs.org/", 11 | "provenance": true 12 | }, 13 | "exports": { 14 | ".": { 15 | "default": "./dist/index.js" 16 | }, 17 | "./client/*": "./client/*.js", 18 | "./client/*.js": "./client/*.js", 19 | "./package.json": "./package.json" 20 | }, 21 | "scripts": { 22 | "build": "pnpm run build:server && pnpm run build:client", 23 | "build:server": "tsc -b ./tsconfig.build.json", 24 | "build:client": "tsc -b ./tsconfig.client.json", 25 | "dev": "tsc -b -w", 26 | "lint": "biome check .", 27 | "lint:write": "biome check . --write", 28 | "format": "node ./node_modules/prettier/bin/prettier.cjs \"packages/**/src/**/*.{ts,tsx,js}\" \"crates/rspack_plugin_runtime/**/*.{ts,js}\" \"x.mjs\" --check", 29 | "test:install": "cross-env ./node_modules/.bin/puppeteer browsers install chrome", 30 | "test": "pnpm run test:install && pnpm run build && cross-env NO_COLOR=1 node --expose-gc --max-old-space-size=8192 --experimental-vm-modules ./node_modules/jest-cli/bin/jest --colors", 31 | "release": "node ./scripts/release.mjs" 32 | }, 33 | "simple-git-hooks": { 34 | "pre-commit": "npx nano-staged" 35 | }, 36 | "nano-staged": { 37 | "*.{js,jsx,ts,tsx,mjs,cjs}": [ 38 | "biome check --write --no-errors-on-unmatched" 39 | ] 40 | }, 41 | "files": [ 42 | "dist", 43 | "client" 44 | ], 45 | "engines": { 46 | "node": ">= 18.12.0" 47 | }, 48 | "packageManager": "pnpm@9.6.0", 49 | "homepage": "https://rspack.dev", 50 | "bugs": "https://github.com/web-infra-dev/rspack-dev-server/issues", 51 | "repository": { 52 | "type": "git", 53 | "url": "https://github.com/web-infra-dev/rspack-dev-server" 54 | }, 55 | "devDependencies": { 56 | "@biomejs/biome": "^1.8.3", 57 | "@jest/reporters": "29.7.0", 58 | "@jest/test-sequencer": "^29.7.0", 59 | "@rspack/core": "1.3.11", 60 | "@rspack/plugin-react-refresh": "1.0.0", 61 | "@types/express": "4.17.21", 62 | "@types/jest": "29.5.12", 63 | "@types/mime-types": "2.1.4", 64 | "@types/ws": "8.5.10", 65 | "@hono/node-server": "^1.13.3", 66 | "cross-env": "^7.0.3", 67 | "css-loader": "^7.1.2", 68 | "connect": "^3.7.0", 69 | "execa": "9.3.0", 70 | "fs-extra": "11.2.0", 71 | "graceful-fs": "4.2.10", 72 | "http-proxy": "^1.18.1", 73 | "hono": "^4.6.8", 74 | "jest": "29.7.0", 75 | "jest-cli": "29.7.0", 76 | "jest-environment-node": "29.7.0", 77 | "jest-serializer-path": "^0.1.15", 78 | "nano-staged": "^0.8.0", 79 | "prettier": "3.2.5", 80 | "puppeteer": "^23.2.2", 81 | "react-refresh": "0.14.0", 82 | "require-from-string": "^2.0.2", 83 | "semver": "7.6.3", 84 | "simple-git-hooks": "^2.11.1", 85 | "sockjs-client": "^1.6.1", 86 | "style-loader": "^3.3.3", 87 | "supertest": "^6.1.3", 88 | "tcp-port-used": "^1.0.2", 89 | "ts-jest": "29.1.2", 90 | "typescript": "5.0.2", 91 | "wait-for-expect": "^3.0.2", 92 | "webpack": "^5.94.0", 93 | "webpack-dev-middleware": "^7.4.2", 94 | "express": "^4.21.2" 95 | }, 96 | "dependencies": { 97 | "chokidar": "^3.6.0", 98 | "http-proxy-middleware": "^2.0.9", 99 | "p-retry": "^6.2.0", 100 | "webpack-dev-server": "5.2.2", 101 | "ws": "^8.18.0" 102 | }, 103 | "peerDependencies": { 104 | "@rspack/core": "*" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /scripts/patch-node-env.cjs: -------------------------------------------------------------------------------- 1 | // Copied from 2 | // 3 | const NodeEnvironment = 4 | // For jest@29 5 | require("jest-environment-node").TestEnvironment || 6 | // For jest@27 7 | require("jest-environment-node"); 8 | 9 | class CustomEnvironment extends NodeEnvironment { 10 | // Workaround for `Symbol('JEST_STATE_SYMBOL')` 11 | async handleTestEvent(event, state) { 12 | if (!this.global.JEST_STATE_SYMBOL) { 13 | this.global.JEST_STATE_SYMBOL = state; 14 | } 15 | } 16 | } 17 | 18 | module.exports = CustomEnvironment; 19 | -------------------------------------------------------------------------------- /scripts/release.mjs: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import * as url from "node:url"; 3 | import { $ } from "execa"; 4 | import fs from "fs-extra"; 5 | import { inc } from "semver"; 6 | 7 | const RELEASE_TAG = process.env.TAG || "beta"; 8 | const RELEASE_DRY_RUN = process.env.DRY_RUN || "true"; 9 | const RELEASE_VERSION_TYPE = process.env.VERSION || "prerelease"; 10 | const RELEASE_NPM_TOKEN = process.env.NPM_TOKEN || ""; 11 | 12 | const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); 13 | const PKG_PATH = path.resolve(__dirname, "../package.json"); 14 | const pkg = fs.readJsonSync(PKG_PATH); 15 | const currentVersion = pkg.version; 16 | const nextVersion = inc(currentVersion, RELEASE_VERSION_TYPE); 17 | if (!nextVersion) { 18 | throw new Error( 19 | `Failed to generate next version from "${currentVersion}" with type "${RELEASE_VERSION_TYPE}"`, 20 | ); 21 | } 22 | 23 | console.info(`Release ${RELEASE_TAG} version ${nextVersion}`); 24 | 25 | // Update pkg version 26 | console.info(`Updating version from ${currentVersion} to ${nextVersion}`); 27 | pkg.version = nextVersion; 28 | fs.writeJsonSync(PKG_PATH, pkg, { spaces: 2 }); 29 | 30 | // Write npmrc 31 | const npmrcPath = `${process.env.HOME}/.npmrc`; 32 | console.info(`Writing npmrc to ${npmrcPath}`); 33 | fs.writeFileSync( 34 | npmrcPath, 35 | `//registry.npmjs.org/:_authToken=${RELEASE_NPM_TOKEN}`, 36 | ); 37 | 38 | // Publish to npm 39 | console.info(`Publishing to npm with tag ${RELEASE_TAG}`); 40 | const dryRun = RELEASE_DRY_RUN === "true" ? ["--dry-run"] : []; 41 | try { 42 | await $`pnpm publish ${dryRun} --tag ${RELEASE_TAG} --no-git-checks --provenance`; 43 | console.info("Published successfully"); 44 | } catch (e) { 45 | console.error(`Publish failed: ${e.message}`); 46 | process.exit(1); 47 | } finally { 48 | fs.removeSync(npmrcPath); 49 | } 50 | 51 | // Push tag to github 52 | if (RELEASE_DRY_RUN !== "true") { 53 | console.info("Pushing tag to github"); 54 | const tagName = `v${nextVersion}`; 55 | try { 56 | await $`git config --global --add safe.directory /github/workspace`; 57 | await $`git config --global user.name "github-actions[bot]"`; 58 | await $`git config --global user.email "github-actions[bot]@users.noreply.github.com"`; 59 | await $`git status`; 60 | await $`git tag ${tagName}`; 61 | await $`git push origin ${tagName}`; 62 | console.info("Pushed tag successfully"); 63 | } catch (e) { 64 | console.error(`Push tag failed: ${e.message}`); 65 | process.exit(1); 66 | } 67 | 68 | try { 69 | await $`git add --all`; 70 | const commitMsg = `release ${tagName}`; 71 | await $`git commit -m ${commitMsg}`; 72 | await $`git push`; 73 | console.info("Pushed branch successfully"); 74 | } catch (e) { 75 | console.error(`Update branch failed: ${e.message}`); 76 | process.exit(1); 77 | } 78 | } 79 | 80 | console.info("Release completed"); 81 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import type { DevServer } from "@rspack/core"; 2 | import type WebpackDevServer from "webpack-dev-server"; 3 | 4 | export type { DevServer }; 5 | 6 | export interface ResolvedDevServer extends DevServer { 7 | port: number | string; 8 | static: false | Array; 9 | devMiddleware: DevServer["devMiddleware"]; 10 | hot: boolean | "only"; 11 | host?: string; 12 | open: WebpackDevServer.Open[]; 13 | magicHtml: boolean; 14 | liveReload: boolean; 15 | webSocketServer: false | WebpackDevServer.WebSocketServerConfiguration; 16 | proxy: Required; 17 | client: WebpackDevServer.ClientConfiguration; 18 | allowedHosts: "auto" | string[] | "all"; 19 | bonjour: false | Record | WebpackDevServer.BonjourOptions; 20 | compress: boolean; 21 | historyApiFallback: false | WebpackDevServer.ConnectHistoryApiFallbackOptions; 22 | server: WebpackDevServer.ServerConfiguration; 23 | ipc: string | undefined; 24 | setupExitSignals: boolean; 25 | watchFiles: WebpackDevServer.WatchFiles[]; 26 | } 27 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { RspackDevServer } from "./server"; 2 | export type { DevServer as Configuration } from "@rspack/core"; 3 | -------------------------------------------------------------------------------- /src/patch.ts: -------------------------------------------------------------------------------- 1 | import WebpackDevServer from "webpack-dev-server"; 2 | 3 | let old: InstanceType["sendStats"] | undefined; 4 | 5 | function restoreDevServerPatch() { 6 | // @ts-expect-error private API 7 | WebpackDevServer.prototype.sendStats = old; 8 | } 9 | 10 | // Patch webpack-dev-server to prevent it from failing to send stats. 11 | // See https://github.com/web-infra-dev/rspack/pull/4028 for details. 12 | function applyDevServerPatch() { 13 | if (old) return restoreDevServerPatch; 14 | 15 | // @ts-expect-error private API 16 | old = WebpackDevServer.prototype.sendStats; 17 | 18 | // @ts-expect-error private API 19 | WebpackDevServer.prototype.sendStats = function sendStats__rspack_patched( 20 | // @ts-expect-error 21 | ...args 22 | ) { 23 | const stats = args[1]; 24 | 25 | if (!stats) { 26 | return; 27 | } 28 | 29 | return old.apply(this, args); 30 | }; 31 | 32 | return restoreDevServerPatch; 33 | } 34 | 35 | export { applyDevServerPatch }; 36 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The following code is modified based on 3 | * https://github.com/webpack/webpack-dev-server/blob/b0f15ace0123c125d5870609ef4691c141a6d187/lib/Server.js 4 | * 5 | * MIT Licensed 6 | * Author Tobias Koppers @sokra 7 | * Copyright (c) JS Foundation and other contributors 8 | * https://github.com/webpack/webpack-dev-server/blob/b0f15ace0123c125d5870609ef4691c141a6d187/LICENSE 9 | */ 10 | import type { Server } from "node:http"; 11 | import type { Socket } from "node:net"; 12 | import { type Compiler, MultiCompiler } from "@rspack/core"; 13 | import type { FSWatcher } from "chokidar"; 14 | import WebpackDevServer from "webpack-dev-server"; 15 | // @ts-ignore 'package.json' is not under 'rootDir' 16 | import { version } from "../package.json"; 17 | 18 | import type { DevServer, ResolvedDevServer } from "./config"; 19 | import { applyDevServerPatch } from "./patch"; 20 | 21 | applyDevServerPatch(); 22 | 23 | const getFreePort = async function getFreePort(port: string, host: string) { 24 | if (typeof port !== "undefined" && port !== null && port !== "auto") { 25 | return port; 26 | } 27 | 28 | const { default: pRetry } = await import("p-retry"); 29 | const getPort = require("webpack-dev-server/lib/getPort"); 30 | const basePort = 31 | typeof process.env.WEBPACK_DEV_SERVER_BASE_PORT !== "undefined" 32 | ? Number.parseInt(process.env.WEBPACK_DEV_SERVER_BASE_PORT, 10) 33 | : 8080; 34 | 35 | // Try to find unused port and listen on it for 3 times, 36 | // if port is not specified in options. 37 | const defaultPortRetry = 38 | typeof process.env.WEBPACK_DEV_SERVER_PORT_RETRY !== "undefined" 39 | ? Number.parseInt(process.env.WEBPACK_DEV_SERVER_PORT_RETRY, 10) 40 | : 3; 41 | 42 | return pRetry(() => getPort(basePort, host), { 43 | retries: defaultPortRetry, 44 | }); 45 | }; 46 | 47 | WebpackDevServer.getFreePort = getFreePort; 48 | 49 | export class RspackDevServer extends WebpackDevServer { 50 | static getFreePort = getFreePort; 51 | /** 52 | * resolved after `normalizedOptions` 53 | */ 54 | /** @ts-ignore: types of path data of rspack is not compatible with webpack */ 55 | declare options: ResolvedDevServer; 56 | 57 | declare staticWatchers: FSWatcher[]; 58 | 59 | declare sockets: Socket[]; 60 | 61 | declare server: Server; 62 | // TODO: remove @ts-ignore here 63 | /** @ts-ignore */ 64 | public compiler: Compiler | MultiCompiler; 65 | public webSocketServer: 66 | | WebpackDevServer.WebSocketServerImplementation 67 | | undefined; 68 | static version: string = version; 69 | 70 | constructor(options: DevServer, compiler: Compiler | MultiCompiler) { 71 | // biome-ignore lint/suspicious/noExplicitAny: _ 72 | super(options as WebpackDevServer.Configuration, compiler as any); 73 | // override 74 | } 75 | 76 | async initialize() { 77 | const compilers = 78 | this.compiler instanceof MultiCompiler 79 | ? this.compiler.compilers 80 | : [this.compiler]; 81 | 82 | for (const compiler of compilers) { 83 | const mode = compiler.options.mode || process.env.NODE_ENV; 84 | if (this.options.hot) { 85 | if (mode === "production") { 86 | this.logger.warn( 87 | "Hot Module Replacement (HMR) is enabled for the production build. \n" + 88 | "Make sure to disable HMR for production by setting `devServer.hot` to `false` in the configuration.", 89 | ); 90 | } 91 | 92 | compiler.options.resolve.alias = { 93 | "ansi-html-community": require.resolve( 94 | "@rspack/dev-server/client/utils/ansiHTML", 95 | ), 96 | ...compiler.options.resolve.alias, 97 | }; 98 | } 99 | } 100 | 101 | // @ts-expect-error 102 | await super.initialize(); 103 | } 104 | 105 | getClientEntry(): string { 106 | return require.resolve("@rspack/dev-server/client/index"); 107 | } 108 | 109 | getClientHotEntry(): string | undefined { 110 | if (this.options.hot === "only") { 111 | return require.resolve("@rspack/core/hot/only-dev-server"); 112 | } 113 | if (this.options.hot) { 114 | return require.resolve("@rspack/core/hot/dev-server"); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /tests/ansiHTML.test.ts: -------------------------------------------------------------------------------- 1 | import ansiHTML from "../client-src/utils/ansiHTML"; 2 | 3 | describe("ansi-html", () => { 4 | it("should transform 24-bit rgb ansi colors", () => { 5 | expect(ansiHTML("\u001b[38;2;255;30;30m×\u001b[0m")).toMatchInlineSnapshot( 6 | `"×"`, 7 | ); 8 | }); 9 | 10 | it("should transform 24-bit rgb ansi colors with additional properties", () => { 11 | expect( 12 | ansiHTML("[\u001b[38;2;92;157;255;1;4m/root/index.js\u001b[0m:1:1]"), 13 | ).toMatchInlineSnapshot( 14 | `"[/root/index.js:1:1]"`, 15 | ); 16 | }); 17 | 18 | it("should transform basic ansi colors", () => { 19 | expect(ansiHTML("\u001b[0mcontent")).toMatchInlineSnapshot( 20 | `"content"`, 21 | ); 22 | expect( 23 | ansiHTML( 24 | "\u001b[33m<\u001b[39m\u001b[33mheader\u001b[39m\u001b[33m>\u001b[39m", 25 | ), 26 | ).toMatchInlineSnapshot( 27 | `"<header>"`, 28 | ); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/bonjour.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`bonjour option as object should apply bonjour options: console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | ] 9 | `; 10 | 11 | exports[`bonjour option as object should apply bonjour options: page errors 1`] = `[]`; 12 | 13 | exports[`bonjour option as object should apply bonjour options: response status 1`] = `200`; 14 | 15 | exports[`bonjour option as true should call bonjour with correct params: console messages 1`] = ` 16 | [ 17 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 18 | "[HMR] Waiting for update signal from WDS...", 19 | "Hey.", 20 | ] 21 | `; 22 | 23 | exports[`bonjour option as true should call bonjour with correct params: page errors 1`] = `[]`; 24 | 25 | exports[`bonjour option as true should call bonjour with correct params: response status 1`] = `200`; 26 | 27 | exports[`bonjour option bonjour object and 'server' option should apply bonjour options: console messages 1`] = ` 28 | [ 29 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 30 | "[HMR] Waiting for update signal from WDS...", 31 | "Hey.", 32 | ] 33 | `; 34 | 35 | exports[`bonjour option bonjour object and 'server' option should apply bonjour options: page errors 1`] = `[]`; 36 | 37 | exports[`bonjour option bonjour object and 'server' option should apply bonjour options: response status 1`] = `200`; 38 | 39 | exports[`bonjour option with 'server' option should call bonjour with 'https' type: console messages 1`] = ` 40 | [ 41 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 42 | "[HMR] Waiting for update signal from WDS...", 43 | "Hey.", 44 | ] 45 | `; 46 | 47 | exports[`bonjour option with 'server' option should call bonjour with 'https' type: page errors 1`] = `[]`; 48 | 49 | exports[`bonjour option with 'server' option should call bonjour with 'https' type: response status 1`] = `200`; 50 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/built-in-routes.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Built in routes with multi config should handle GET request to directory index and list all middleware directories: console messages 1`] = `[]`; 4 | 5 | exports[`Built in routes with multi config should handle GET request to directory index and list all middleware directories: directory list 1`] = ` 6 | "

Assets Report:

Compilation: unnamed[0]

Compilation: named

Compilation: other

" 15 | `; 16 | 17 | exports[`Built in routes with multi config should handle GET request to directory index and list all middleware directories: page errors 1`] = `[]`; 18 | 19 | exports[`Built in routes with multi config should handle GET request to directory index and list all middleware directories: response headers content-type 1`] = `"text/html; charset=utf-8"`; 20 | 21 | exports[`Built in routes with multi config should handle GET request to directory index and list all middleware directories: response status 1`] = `200`; 22 | 23 | exports[`Built in routes with simple config should handle GET request to directory index and list all middleware directories: console messages 1`] = `[]`; 24 | 25 | exports[`Built in routes with simple config should handle GET request to directory index and list all middleware directories: directory list 1`] = ` 26 | "

Assets Report:

Compilation: unnamed

" 33 | `; 34 | 35 | exports[`Built in routes with simple config should handle GET request to directory index and list all middleware directories: page errors 1`] = `[]`; 36 | 37 | exports[`Built in routes with simple config should handle GET request to directory index and list all middleware directories: response headers content-type 1`] = `"text/html; charset=utf-8"`; 38 | 39 | exports[`Built in routes with simple config should handle GET request to directory index and list all middleware directories: response status 1`] = `200`; 40 | 41 | exports[`Built in routes with simple config should handle GET request to invalidate endpoint: console messages 1`] = `[]`; 42 | 43 | exports[`Built in routes with simple config should handle GET request to invalidate endpoint: page errors 1`] = `[]`; 44 | 45 | exports[`Built in routes with simple config should handle GET request to invalidate endpoint: response status 1`] = `200`; 46 | 47 | exports[`Built in routes with simple config should handle GET request to magic async chunk: console messages 1`] = `[]`; 48 | 49 | exports[`Built in routes with simple config should handle GET request to magic async chunk: response headers content-type 1`] = `"application/javascript; charset=utf-8"`; 50 | 51 | exports[`Built in routes with simple config should handle GET request to magic async chunk: response status 1`] = `200`; 52 | 53 | exports[`Built in routes with simple config should handle HEAD request to directory index: console messages 1`] = `[]`; 54 | 55 | exports[`Built in routes with simple config should handle HEAD request to directory index: directory list 1`] = `""`; 56 | 57 | exports[`Built in routes with simple config should handle HEAD request to directory index: page errors 1`] = `[]`; 58 | 59 | exports[`Built in routes with simple config should handle HEAD request to directory index: response headers content-type 1`] = `"text/html; charset=utf-8"`; 60 | 61 | exports[`Built in routes with simple config should handle HEAD request to directory index: response status 1`] = `200`; 62 | 63 | exports[`Built in routes with simple config should handle HEAD request to magic async chunk: console messages 1`] = `[]`; 64 | 65 | exports[`Built in routes with simple config should handle HEAD request to magic async chunk: response headers content-type 1`] = `"application/javascript; charset=utf-8"`; 66 | 67 | exports[`Built in routes with simple config should handle HEAD request to magic async chunk: response status 1`] = `200`; 68 | 69 | exports[`Built in routes with simple config should handles GET request to sockjs bundle: console messages 1`] = `[]`; 70 | 71 | exports[`Built in routes with simple config should handles GET request to sockjs bundle: page errors 1`] = `[]`; 72 | 73 | exports[`Built in routes with simple config should handles GET request to sockjs bundle: response headers content-type 1`] = `"application/javascript; charset=UTF-8"`; 74 | 75 | exports[`Built in routes with simple config should handles GET request to sockjs bundle: response status 1`] = `200`; 76 | 77 | exports[`Built in routes with simple config should handles HEAD request to sockjs bundle: console messages 1`] = `[]`; 78 | 79 | exports[`Built in routes with simple config should handles HEAD request to sockjs bundle: page errors 1`] = `[]`; 80 | 81 | exports[`Built in routes with simple config should handles HEAD request to sockjs bundle: response headers content-type 1`] = `"application/javascript; charset=UTF-8"`; 82 | 83 | exports[`Built in routes with simple config should handles HEAD request to sockjs bundle: response status 1`] = `200`; 84 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/client-reconnect.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`client.reconnect option specified as false should not try to reconnect: console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | "[webpack-dev-server] Disconnected!", 9 | ] 10 | `; 11 | 12 | exports[`client.reconnect option specified as false should not try to reconnect: page errors 1`] = `[]`; 13 | 14 | exports[`client.reconnect option specified as false should not try to reconnect: response status 1`] = `200`; 15 | 16 | exports[`client.reconnect option specified as number should try to reconnect 2 times: console messages 1`] = ` 17 | [ 18 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 19 | "[HMR] Waiting for update signal from WDS...", 20 | "Hey.", 21 | "[webpack-dev-server] Disconnected!", 22 | "[webpack-dev-server] Trying to reconnect...", 23 | "WebSocket connection to 'ws://127.0.0.1:8163/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED", 24 | "[webpack-dev-server] JSHandle@object", 25 | "[webpack-dev-server] Trying to reconnect...", 26 | "WebSocket connection to 'ws://127.0.0.1:8163/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED", 27 | "[webpack-dev-server] JSHandle@object", 28 | ] 29 | `; 30 | 31 | exports[`client.reconnect option specified as number should try to reconnect 2 times: page errors 1`] = `[]`; 32 | 33 | exports[`client.reconnect option specified as number should try to reconnect 2 times: response status 1`] = `200`; 34 | 35 | exports[`client.reconnect option specified as true should try to reconnect unlimited times: page errors 1`] = `[]`; 36 | 37 | exports[`client.reconnect option specified as true should try to reconnect unlimited times: response status 1`] = `200`; 38 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/client.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`client option configure client entry should disable client entry: console messages 1`] = `[]`; 4 | 5 | exports[`client option configure client entry should disable client entry: page errors 1`] = `[]`; 6 | 7 | exports[`client option configure client entry should disable client entry: response status 1`] = `200`; 8 | 9 | exports[`client option configure client entry should redirect client entry to rspack: console messages 1`] = `[]`; 10 | 11 | exports[`client option configure client entry should redirect client entry to rspack: page errors 1`] = `[]`; 12 | 13 | exports[`client option configure client entry should redirect client entry to rspack: response status 1`] = `200`; 14 | 15 | exports[`client option default behaviour responds with a 200 status code for /ws path: console messages 1`] = `[]`; 16 | 17 | exports[`client option default behaviour responds with a 200 status code for /ws path: page errors 1`] = `[]`; 18 | 19 | exports[`client option default behaviour responds with a 200 status code for /ws path: response status 1`] = `200`; 20 | 21 | exports[`client option override client entry should disable client entry: response status 1`] = `200`; 22 | 23 | exports[`client option should respect path option responds with a 200 status code for /foo/test/bar path: console messages 1`] = `[]`; 24 | 25 | exports[`client option should respect path option responds with a 200 status code for /foo/test/bar path: page errors 1`] = `[]`; 26 | 27 | exports[`client option should respect path option responds with a 200 status code for /foo/test/bar path: response status 1`] = `200`; 28 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/compress.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`compress option as false should handle GET request to bundle file: console messages 1`] = `[]`; 4 | 5 | exports[`compress option as false should handle GET request to bundle file: page errors 1`] = `[]`; 6 | 7 | exports[`compress option as false should handle GET request to bundle file: response headers content-encoding 1`] = `undefined`; 8 | 9 | exports[`compress option as false should handle GET request to bundle file: response status 1`] = `200`; 10 | 11 | exports[`compress option as true should handle GET request to bundle file: console messages 1`] = `[]`; 12 | 13 | exports[`compress option as true should handle GET request to bundle file: page errors 1`] = `[]`; 14 | 15 | exports[`compress option as true should handle GET request to bundle file: response headers content-encoding 1`] = `"gzip"`; 16 | 17 | exports[`compress option as true should handle GET request to bundle file: response status 1`] = `200`; 18 | 19 | exports[`compress option enabled by default when not specified should handle GET request to bundle file: console messages 1`] = `[]`; 20 | 21 | exports[`compress option enabled by default when not specified should handle GET request to bundle file: page errors 1`] = `[]`; 22 | 23 | exports[`compress option enabled by default when not specified should handle GET request to bundle file: response headers content-encoding 1`] = `"gzip"`; 24 | 25 | exports[`compress option enabled by default when not specified should handle GET request to bundle file: response status 1`] = `200`; 26 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/entry.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`entry should work with dynamic async entry: console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | ] 9 | `; 10 | 11 | exports[`entry should work with dynamic async entry: page errors 1`] = `[]`; 12 | 13 | exports[`entry should work with dynamic entry: console messages 1`] = ` 14 | [ 15 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 16 | "[HMR] Waiting for update signal from WDS...", 17 | "Hey.", 18 | ] 19 | `; 20 | 21 | exports[`entry should work with dynamic entry: page errors 1`] = `[]`; 22 | 23 | exports[`entry should work with empty: console messages 1`] = ` 24 | [ 25 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 26 | "[HMR] Waiting for update signal from WDS...", 27 | "Hey.", 28 | ] 29 | `; 30 | 31 | exports[`entry should work with empty: page errors 1`] = `[]`; 32 | 33 | exports[`entry should work with multiple entries #2: console messages 1`] = ` 34 | [ 35 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 36 | "[HMR] Waiting for update signal from WDS...", 37 | "Bar.", 38 | ] 39 | `; 40 | 41 | exports[`entry should work with multiple entries #2: page errors 1`] = `[]`; 42 | 43 | exports[`entry should work with multiple entries and "dependOn": console messages 1`] = ` 44 | [ 45 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 46 | "[HMR] Waiting for update signal from WDS...", 47 | "Bar.", 48 | "Hey.", 49 | ] 50 | `; 51 | 52 | exports[`entry should work with multiple entries and "dependOn": page errors 1`] = `[]`; 53 | 54 | exports[`entry should work with multiple entries: console messages 1`] = ` 55 | [ 56 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 57 | "[HMR] Waiting for update signal from WDS...", 58 | "Hey.", 59 | ] 60 | `; 61 | 62 | exports[`entry should work with multiple entries: page errors 1`] = `[]`; 63 | 64 | exports[`entry should work with object entry: console messages 1`] = ` 65 | [ 66 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 67 | "[HMR] Waiting for update signal from WDS...", 68 | "Hey.", 69 | ] 70 | `; 71 | 72 | exports[`entry should work with object entry: page errors 1`] = `[]`; 73 | 74 | exports[`entry should work with single array entry: console messages 1`] = ` 75 | [ 76 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 77 | "[HMR] Waiting for update signal from WDS...", 78 | "Hey.", 79 | "Bar.", 80 | ] 81 | `; 82 | 83 | exports[`entry should work with single array entry: page errors 1`] = `[]`; 84 | 85 | exports[`entry should work with single entry: console messages 1`] = ` 86 | [ 87 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 88 | "[HMR] Waiting for update signal from WDS...", 89 | "Hey.", 90 | ] 91 | `; 92 | 93 | exports[`entry should work with single entry: page errors 1`] = `[]`; 94 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/headers.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`headers option as a function returning an array should handle GET request with headers: console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | ] 9 | `; 10 | 11 | exports[`headers option as a function returning an array should handle GET request with headers: page errors 1`] = `[]`; 12 | 13 | exports[`headers option as a function returning an array should handle GET request with headers: response headers x-bar 1`] = `"value2"`; 14 | 15 | exports[`headers option as a function returning an array should handle GET request with headers: response headers x-foo 1`] = `"value1"`; 16 | 17 | exports[`headers option as a function returning an array should handle GET request with headers: response status 1`] = `200`; 18 | 19 | exports[`headers option as a function should handle GET request with headers as a function: console messages 1`] = ` 20 | [ 21 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 22 | "[HMR] Waiting for update signal from WDS...", 23 | "Hey.", 24 | ] 25 | `; 26 | 27 | exports[`headers option as a function should handle GET request with headers as a function: page errors 1`] = `[]`; 28 | 29 | exports[`headers option as a function should handle GET request with headers as a function: response headers x-bar 1`] = ` 30 | "key1=value1 31 | key2=value2" 32 | `; 33 | 34 | exports[`headers option as a function should handle GET request with headers as a function: response status 1`] = `200`; 35 | 36 | exports[`headers option as a string and support HEAD request should handle HEAD request with headers: console messages 1`] = ` 37 | [ 38 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 39 | "[HMR] Waiting for update signal from WDS...", 40 | "Hey.", 41 | ] 42 | `; 43 | 44 | exports[`headers option as a string and support HEAD request should handle HEAD request with headers: page errors 1`] = `[]`; 45 | 46 | exports[`headers option as a string and support HEAD request should handle HEAD request with headers: response headers x-foo 1`] = `"dev-server headers"`; 47 | 48 | exports[`headers option as a string and support HEAD request should handle HEAD request with headers: response status 1`] = `200`; 49 | 50 | exports[`headers option as a string should handle GET request with headers: console messages 1`] = ` 51 | [ 52 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 53 | "[HMR] Waiting for update signal from WDS...", 54 | "Hey.", 55 | ] 56 | `; 57 | 58 | exports[`headers option as a string should handle GET request with headers: page errors 1`] = `[]`; 59 | 60 | exports[`headers option as a string should handle GET request with headers: response headers x-foo 1`] = `"dev-server headers"`; 61 | 62 | exports[`headers option as a string should handle GET request with headers: response status 1`] = `200`; 63 | 64 | exports[`headers option as an array of objects should handle GET request with headers: console messages 1`] = ` 65 | [ 66 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 67 | "[HMR] Waiting for update signal from WDS...", 68 | "Hey.", 69 | ] 70 | `; 71 | 72 | exports[`headers option as an array of objects should handle GET request with headers: page errors 1`] = `[]`; 73 | 74 | exports[`headers option as an array of objects should handle GET request with headers: response headers x-bar 1`] = `"value2"`; 75 | 76 | exports[`headers option as an array of objects should handle GET request with headers: response headers x-foo 1`] = `"value1"`; 77 | 78 | exports[`headers option as an array of objects should handle GET request with headers: response status 1`] = `200`; 79 | 80 | exports[`headers option as an array should handle GET request with headers as an array: console messages 1`] = ` 81 | [ 82 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 83 | "[HMR] Waiting for update signal from WDS...", 84 | "Hey.", 85 | ] 86 | `; 87 | 88 | exports[`headers option as an array should handle GET request with headers as an array: page errors 1`] = `[]`; 89 | 90 | exports[`headers option as an array should handle GET request with headers as an array: response headers x-bar 1`] = ` 91 | "key1=value1 92 | key2=value2" 93 | `; 94 | 95 | exports[`headers option as an array should handle GET request with headers as an array: response status 1`] = `200`; 96 | 97 | exports[`headers option dev middleware headers take precedence for dev middleware output files should handle GET request with headers as a function: console messages 1`] = ` 98 | [ 99 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 100 | "[HMR] Waiting for update signal from WDS...", 101 | "Hey.", 102 | ] 103 | `; 104 | 105 | exports[`headers option dev middleware headers take precedence for dev middleware output files should handle GET request with headers as a function: page errors 1`] = `[]`; 106 | 107 | exports[`headers option dev middleware headers take precedence for dev middleware output files should handle GET request with headers as a function: response headers x-foo 1`] = `"dev-middleware-headers"`; 108 | 109 | exports[`headers option dev middleware headers take precedence for dev middleware output files should handle GET request with headers as a function: response status 1`] = `200`; 110 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/host.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`host should work using "127.0.0.1" host and "auto" port: console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | ] 9 | `; 10 | 11 | exports[`host should work using "127.0.0.1" host and "auto" port: page errors 1`] = `[]`; 12 | 13 | exports[`host should work using "127.0.0.1" host and port as number: console messages 1`] = ` 14 | [ 15 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 16 | "[HMR] Waiting for update signal from WDS...", 17 | "Hey.", 18 | ] 19 | `; 20 | 21 | exports[`host should work using "127.0.0.1" host and port as number: page errors 1`] = `[]`; 22 | 23 | exports[`host should work using "127.0.0.1" host and port as string: console messages 1`] = ` 24 | [ 25 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 26 | "[HMR] Waiting for update signal from WDS...", 27 | "Hey.", 28 | ] 29 | `; 30 | 31 | exports[`host should work using "127.0.0.1" host and port as string: page errors 1`] = `[]`; 32 | 33 | exports[`host should work using "localhost" host and "auto" port: console messages 1`] = ` 34 | [ 35 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 36 | "[HMR] Waiting for update signal from WDS...", 37 | "Hey.", 38 | ] 39 | `; 40 | 41 | exports[`host should work using "localhost" host and "auto" port: page errors 1`] = `[]`; 42 | 43 | exports[`host should work using "localhost" host and port as number: console messages 1`] = ` 44 | [ 45 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 46 | "[HMR] Waiting for update signal from WDS...", 47 | "Hey.", 48 | ] 49 | `; 50 | 51 | exports[`host should work using "localhost" host and port as number: page errors 1`] = `[]`; 52 | 53 | exports[`host should work using "localhost" host and port as string: console messages 1`] = ` 54 | [ 55 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 56 | "[HMR] Waiting for update signal from WDS...", 57 | "Hey.", 58 | ] 59 | `; 60 | 61 | exports[`host should work using "localhost" host and port as string: page errors 1`] = `[]`; 62 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/ipc.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`web socket server URL should work with the "ipc" option using "string" value ("sockjs"): console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | ] 9 | `; 10 | 11 | exports[`web socket server URL should work with the "ipc" option using "string" value ("sockjs"): page errors 1`] = `[]`; 12 | 13 | exports[`web socket server URL should work with the "ipc" option using "string" value ("ws"): console messages 1`] = ` 14 | [ 15 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 16 | "[HMR] Waiting for update signal from WDS...", 17 | "Hey.", 18 | ] 19 | `; 20 | 21 | exports[`web socket server URL should work with the "ipc" option using "string" value ("ws"): page errors 1`] = `[]`; 22 | 23 | exports[`web socket server URL should work with the "ipc" option using "true" value ("sockjs"): console messages 1`] = ` 24 | [ 25 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 26 | "[HMR] Waiting for update signal from WDS...", 27 | "Hey.", 28 | ] 29 | `; 30 | 31 | exports[`web socket server URL should work with the "ipc" option using "true" value ("sockjs"): page errors 1`] = `[]`; 32 | 33 | exports[`web socket server URL should work with the "ipc" option using "true" value ("ws"): console messages 1`] = ` 34 | [ 35 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 36 | "[HMR] Waiting for update signal from WDS...", 37 | "Hey.", 38 | ] 39 | `; 40 | 41 | exports[`web socket server URL should work with the "ipc" option using "true" value ("ws"): page errors 1`] = `[]`; 42 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/mime-types.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`mimeTypes option as an object with a custom type should request file with different js mime type: console messages 1`] = `[]`; 4 | 5 | exports[`mimeTypes option as an object with a custom type should request file with different js mime type: page errors 1`] = `[]`; 6 | 7 | exports[`mimeTypes option as an object with a custom type should request file with different js mime type: response headers content-type 1`] = `"text/html; charset=utf-8"`; 8 | 9 | exports[`mimeTypes option as an object with a custom type should request file with different js mime type: response status 1`] = `200`; 10 | 11 | exports[`mimeTypes option as an object with a remapped type should request file with different js mime type: console messages 1`] = `[]`; 12 | 13 | exports[`mimeTypes option as an object with a remapped type should request file with different js mime type: page errors 1`] = `[]`; 14 | 15 | exports[`mimeTypes option as an object with a remapped type should request file with different js mime type: response headers content-type 1`] = `"text/plain; charset=utf-8"`; 16 | 17 | exports[`mimeTypes option as an object with a remapped type should request file with different js mime type: response status 1`] = `200`; 18 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/module-federation.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Module federation should use plugin should contain hot script in main.js: console messages 1`] = `[]`; 4 | 5 | exports[`Module federation should use plugin should contain hot script in main.js: page errors 1`] = `[]`; 6 | 7 | exports[`Module federation should use plugin should contain hot script in remoteEntry.js: console messages 1`] = `[]`; 8 | 9 | exports[`Module federation should use plugin should contain hot script in remoteEntry.js: page errors 1`] = `[]`; 10 | 11 | exports[`Module federation should work with multi compiler config should use the last entry export: console messages 1`] = `[]`; 12 | 13 | exports[`Module federation should work with multi compiler config should use the last entry export: page errors 1`] = `[]`; 14 | 15 | exports[`Module federation should work with object multi-entry config should support the named entry export: console messages 1`] = `[]`; 16 | 17 | exports[`Module federation should work with object multi-entry config should support the named entry export: page errors 1`] = `[]`; 18 | 19 | exports[`Module federation should work with object multi-entry config should use the last entry export: console messages 1`] = `[]`; 20 | 21 | exports[`Module federation should work with object multi-entry config should use the last entry export: page errors 1`] = `[]`; 22 | 23 | exports[`Module federation should work with simple multi-entry config should use the last entry export: console messages 1`] = `[]`; 24 | 25 | exports[`Module federation should work with simple multi-entry config should use the last entry export: page errors 1`] = `[]`; 26 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/on-listening.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`onListening option should handle GET request to /listening/some/path route: console messages 1`] = `[]`; 4 | 5 | exports[`onListening option should handle GET request to /listening/some/path route: page errors 1`] = `[]`; 6 | 7 | exports[`onListening option should handle GET request to /listening/some/path route: response headers content-type 1`] = `"text/html; charset=utf-8"`; 8 | 9 | exports[`onListening option should handle GET request to /listening/some/path route: response status 1`] = `200`; 10 | 11 | exports[`onListening option should handle GET request to /listening/some/path route: response text 1`] = `"listening"`; 12 | 13 | exports[`onListening option should handle POST request to /listening/some/path route: console messages 1`] = `[]`; 14 | 15 | exports[`onListening option should handle POST request to /listening/some/path route: page errors 1`] = `[]`; 16 | 17 | exports[`onListening option should handle POST request to /listening/some/path route: response headers content-type 1`] = `"text/html; charset=utf-8"`; 18 | 19 | exports[`onListening option should handle POST request to /listening/some/path route: response status 1`] = `200`; 20 | 21 | exports[`onListening option should handle POST request to /listening/some/path route: response text 1`] = `"listening POST"`; 22 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/port.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`port should work using "" port : console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | ] 9 | `; 10 | 11 | exports[`port should work using "" port : page errors 1`] = `[]`; 12 | 13 | exports[`port should work using "0" port : console messages 1`] = ` 14 | [ 15 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 16 | "[HMR] Waiting for update signal from WDS...", 17 | "Hey.", 18 | ] 19 | `; 20 | 21 | exports[`port should work using "0" port : page errors 1`] = `[]`; 22 | 23 | exports[`port should work using "8161" port : console messages 1`] = ` 24 | [ 25 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 26 | "[HMR] Waiting for update signal from WDS...", 27 | "Hey.", 28 | ] 29 | `; 30 | 31 | exports[`port should work using "8161" port : console messages 2`] = ` 32 | [ 33 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 34 | "[HMR] Waiting for update signal from WDS...", 35 | "Hey.", 36 | ] 37 | `; 38 | 39 | exports[`port should work using "8161" port : page errors 1`] = `[]`; 40 | 41 | exports[`port should work using "8161" port : page errors 2`] = `[]`; 42 | 43 | exports[`port should work using "auto" port : console messages 1`] = ` 44 | [ 45 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 46 | "[HMR] Waiting for update signal from WDS...", 47 | "Hey.", 48 | ] 49 | `; 50 | 51 | exports[`port should work using "auto" port : page errors 1`] = `[]`; 52 | 53 | exports[`port should work using "undefined" port : console messages 1`] = ` 54 | [ 55 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 56 | "[HMR] Waiting for update signal from WDS...", 57 | "Hey.", 58 | ] 59 | `; 60 | 61 | exports[`port should work using "undefined" port : page errors 1`] = `[]`; 62 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/server-and-client-transport.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`server and client transport should throw an error on invalid path to client transport 1`] = `"client.webSocketTransport must be a string denoting a default implementation (e.g. 'sockjs', 'ws') or a full path to a JS file via require.resolve(...) which exports a class "`; 4 | 5 | exports[`server and client transport should throw an error on invalid path to server transport 1`] = `"webSocketServer (webSocketServer.type) must be a string denoting a default implementation (e.g. 'ws', 'sockjs'), a full path to a JS file which exports a class extending BaseServer (webpack-dev-server/lib/servers/BaseServer.js) via require.resolve(...), or the class itself which extends BaseServer"`; 6 | 7 | exports[`server and client transport should throw an error on wrong path 1`] = `"webSocketServer (webSocketServer.type) must be a string denoting a default implementation (e.g. 'ws', 'sockjs'), a full path to a JS file which exports a class extending BaseServer (webpack-dev-server/lib/servers/BaseServer.js) via require.resolve(...), or the class itself which extends BaseServer"`; 8 | 9 | exports[`server and client transport should use "sockjs" transport and "sockjs" web socket server 1`] = ` 10 | [ 11 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 12 | "[HMR] Waiting for update signal from WDS...", 13 | ] 14 | `; 15 | 16 | exports[`server and client transport should use "sockjs" transport, when web socket server is not specify 1`] = `[]`; 17 | 18 | exports[`server and client transport should use "sockjs" web socket server when specify "sockjs" value 1`] = ` 19 | [ 20 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 21 | "[HMR] Waiting for update signal from WDS...", 22 | ] 23 | `; 24 | 25 | exports[`server and client transport should use "sockjs" web socket server when specify "sockjs" value using object 1`] = ` 26 | [ 27 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 28 | "[HMR] Waiting for update signal from WDS...", 29 | ] 30 | `; 31 | 32 | exports[`server and client transport should use "ws" transport and "ws" web socket server 1`] = ` 33 | [ 34 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 35 | "[HMR] Waiting for update signal from WDS...", 36 | ] 37 | `; 38 | 39 | exports[`server and client transport should use "ws" transport, when web socket server is not specify 1`] = ` 40 | [ 41 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 42 | "[HMR] Waiting for update signal from WDS...", 43 | ] 44 | `; 45 | 46 | exports[`server and client transport should use "ws" web socket server when specify "ws" value 1`] = ` 47 | [ 48 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 49 | "[HMR] Waiting for update signal from WDS...", 50 | ] 51 | `; 52 | 53 | exports[`server and client transport should use "ws" web socket server when specify "ws" value using object 1`] = ` 54 | [ 55 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 56 | "[HMR] Waiting for update signal from WDS...", 57 | ] 58 | `; 59 | 60 | exports[`server and client transport should use custom transport and "sockjs" web socket server 1`] = ` 61 | [ 62 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 63 | "[HMR] Waiting for update signal from WDS...", 64 | "open", 65 | "hot", 66 | "liveReload", 67 | "reconnect", 68 | "overlay", 69 | "hash", 70 | "ok", 71 | ] 72 | `; 73 | 74 | exports[`server and client transport should use custom web socket server when specify class 1`] = ` 75 | [ 76 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 77 | "[HMR] Waiting for update signal from WDS...", 78 | ] 79 | `; 80 | 81 | exports[`server and client transport should use custom web socket server when specify class using object 1`] = ` 82 | [ 83 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 84 | "[HMR] Waiting for update signal from WDS...", 85 | ] 86 | `; 87 | 88 | exports[`server and client transport should use custom web socket server when specify path to class 1`] = ` 89 | [ 90 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 91 | "[HMR] Waiting for update signal from WDS...", 92 | ] 93 | `; 94 | 95 | exports[`server and client transport should use custom web socket server when specify path to class using object 1`] = ` 96 | [ 97 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 98 | "[HMR] Waiting for update signal from WDS...", 99 | ] 100 | `; 101 | 102 | exports[`server and client transport should use default web socket server ("ws") 1`] = ` 103 | [ 104 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 105 | "[HMR] Waiting for update signal from WDS...", 106 | ] 107 | `; 108 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/setup-exit-signals.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`setupExitSignals option should handle 'SIGINT' and 'SIGTERM' signals should close and exit on SIGINT: console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | ] 9 | `; 10 | 11 | exports[`setupExitSignals option should handle 'SIGINT' and 'SIGTERM' signals should close and exit on SIGINT: page errors 1`] = `[]`; 12 | 13 | exports[`setupExitSignals option should handle 'SIGINT' and 'SIGTERM' signals should close and exit on SIGINT: response status 1`] = `200`; 14 | 15 | exports[`setupExitSignals option should handle 'SIGINT' and 'SIGTERM' signals should close and exit on SIGTERM: console messages 1`] = ` 16 | [ 17 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 18 | "[HMR] Waiting for update signal from WDS...", 19 | "Hey.", 20 | ] 21 | `; 22 | 23 | exports[`setupExitSignals option should handle 'SIGINT' and 'SIGTERM' signals should close and exit on SIGTERM: page errors 1`] = `[]`; 24 | 25 | exports[`setupExitSignals option should handle 'SIGINT' and 'SIGTERM' signals should close and exit on SIGTERM: response status 1`] = `200`; 26 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/setup-middlewares.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: console messages 1`] = `[]`; 4 | 5 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: page errors 1`] = `[]`; 6 | 7 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response headers content-type 1`] = `"text/html; charset=utf-8"`; 8 | 9 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response headers content-type 2`] = `"text/html; charset=utf-8"`; 10 | 11 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response headers content-type 3`] = `"text/html; charset=utf-8"`; 12 | 13 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response headers content-type 4`] = `"text/html; charset=utf-8"`; 14 | 15 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response status 1`] = `200`; 16 | 17 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response status 2`] = `200`; 18 | 19 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response status 3`] = `200`; 20 | 21 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response status 4`] = `200`; 22 | 23 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response text 1`] = `"setup-middlewares option GET"`; 24 | 25 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response text 2`] = `"Hello World with path!"`; 26 | 27 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response text 3`] = `"Hello World without path!"`; 28 | 29 | exports[`setupMiddlewares option should handle GET request to /setup-middleware/some/path route: response text 4`] = `"Hello World as function!"`; 30 | 31 | exports[`setupMiddlewares option should handle POST request to /setup-middleware/some/path route: console messages 1`] = `[]`; 32 | 33 | exports[`setupMiddlewares option should handle POST request to /setup-middleware/some/path route: page errors 1`] = `[]`; 34 | 35 | exports[`setupMiddlewares option should handle POST request to /setup-middleware/some/path route: response headers content-type 1`] = `"text/html; charset=utf-8"`; 36 | 37 | exports[`setupMiddlewares option should handle POST request to /setup-middleware/some/path route: response status 1`] = `200`; 38 | 39 | exports[`setupMiddlewares option should handle POST request to /setup-middleware/some/path route: response text 1`] = `"setup-middlewares option POST"`; 40 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/stats.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`stats should work and respect the "ignoreWarnings" option 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | ] 9 | `; 10 | 11 | exports[`stats should work using "{ assets: false }" value for the "stats" option 1`] = ` 12 | [ 13 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 14 | "[HMR] Waiting for update signal from WDS...", 15 | "Hey.", 16 | ] 17 | `; 18 | 19 | exports[`stats should work using "{}" value for the "stats" option 1`] = ` 20 | [ 21 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 22 | "[HMR] Waiting for update signal from WDS...", 23 | "Hey.", 24 | ] 25 | `; 26 | 27 | exports[`stats should work using "errors-only" value for the "stats" option 1`] = ` 28 | [ 29 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 30 | "[HMR] Waiting for update signal from WDS...", 31 | "Hey.", 32 | ] 33 | `; 34 | 35 | exports[`stats should work using "false" value for the "stats" option 1`] = ` 36 | [ 37 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 38 | "[HMR] Waiting for update signal from WDS...", 39 | "Hey.", 40 | ] 41 | `; 42 | 43 | exports[`stats should work using "undefined" value for the "stats" option 1`] = ` 44 | [ 45 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 46 | "[HMR] Waiting for update signal from WDS...", 47 | "Hey.", 48 | ] 49 | `; 50 | 51 | exports[`stats should work when "stats" is not specified 1`] = ` 52 | [ 53 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 54 | "[HMR] Waiting for update signal from WDS...", 55 | "Hey.", 56 | ] 57 | `; 58 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/target.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`target should work using "async-node" target: console messages 1`] = ` 4 | [ 5 | "[HMR] Waiting for update signal from WDS...", 6 | "Hey.", 7 | ] 8 | `; 9 | 10 | exports[`target should work using "browserslist:defaults" target: console messages 1`] = ` 11 | [ 12 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 13 | "[HMR] Waiting for update signal from WDS...", 14 | "Hey.", 15 | ] 16 | `; 17 | 18 | exports[`target should work using "electron-main" target: console messages 1`] = ` 19 | [ 20 | "[HMR] Waiting for update signal from WDS...", 21 | "Hey.", 22 | ] 23 | `; 24 | 25 | exports[`target should work using "electron-preload" target: console messages 1`] = ` 26 | [ 27 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 28 | "[HMR] Waiting for update signal from WDS...", 29 | "Hey.", 30 | ] 31 | `; 32 | 33 | exports[`target should work using "electron-renderer" target: console messages 1`] = `[]`; 34 | 35 | exports[`target should work using "es5" target: console messages 1`] = ` 36 | [ 37 | "[HMR] Waiting for update signal from WDS...", 38 | "Hey.", 39 | ] 40 | `; 41 | 42 | exports[`target should work using "false" target: console messages 1`] = ` 43 | [ 44 | "[HMR] Waiting for update signal from WDS...", 45 | "Hey.", 46 | ] 47 | `; 48 | 49 | exports[`target should work using "node" target: console messages 1`] = ` 50 | [ 51 | "[HMR] Waiting for update signal from WDS...", 52 | "Hey.", 53 | ] 54 | `; 55 | 56 | exports[`target should work using "node-webkit" target: console messages 1`] = ` 57 | [ 58 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 59 | "[HMR] Waiting for update signal from WDS...", 60 | "Hey.", 61 | ] 62 | `; 63 | 64 | exports[`target should work using "nwjs" target: console messages 1`] = ` 65 | [ 66 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 67 | "[HMR] Waiting for update signal from WDS...", 68 | "Hey.", 69 | ] 70 | `; 71 | 72 | exports[`target should work using "web" target: console messages 1`] = ` 73 | [ 74 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 75 | "[HMR] Waiting for update signal from WDS...", 76 | "Hey.", 77 | ] 78 | `; 79 | 80 | exports[`target should work using "web,es5" target: console messages 1`] = ` 81 | [ 82 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 83 | "[HMR] Waiting for update signal from WDS...", 84 | "Hey.", 85 | ] 86 | `; 87 | 88 | exports[`target should work using "webworker" target: console messages 1`] = ` 89 | [ 90 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 91 | "[HMR] Waiting for update signal from WDS...", 92 | "Hey.", 93 | ] 94 | `; 95 | 96 | exports[`target should work using multi compiler mode with \`web\` and \`webworker\` targets with \`devServer: false\`: console messages 1`] = ` 97 | [ 98 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 99 | "[HMR] Waiting for update signal from WDS...", 100 | "Worker said: I'm working before postMessage", 101 | "Worker said: Message sent: message", 102 | ] 103 | `; 104 | 105 | exports[`target should work using multi compiler mode with \`web\` and \`webworker\` targets with \`devServer: false\`: page errors 1`] = `[]`; 106 | 107 | exports[`target should work using multi compiler mode with \`web\` and \`webworker\` targets: console messages 1`] = ` 108 | [ 109 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 110 | "[HMR] Waiting for update signal from WDS...", 111 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 112 | "[HMR] Waiting for update signal from WDS...", 113 | "Worker said: I'm working before postMessage", 114 | "Worker said: Message sent: message", 115 | ] 116 | `; 117 | 118 | exports[`target should work using multi compiler mode with \`web\` and \`webworker\` targets: page errors 1`] = `[]`; 119 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/web-socket-communication.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`web socket communication should work and close web socket client connection when web socket server closed ("sockjs"): console messages 1`] = ` 4 | [ 5 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 6 | "[HMR] Waiting for update signal from WDS...", 7 | "Hey.", 8 | "[webpack-dev-server] Disconnected!", 9 | "[webpack-dev-server] Trying to reconnect...", 10 | ] 11 | `; 12 | 13 | exports[`web socket communication should work and close web socket client connection when web socket server closed ("sockjs"): page errors 1`] = `[]`; 14 | 15 | exports[`web socket communication should work and close web socket client connection when web socket server closed ("ws"): console messages 1`] = ` 16 | [ 17 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 18 | "[HMR] Waiting for update signal from WDS...", 19 | "Hey.", 20 | "[webpack-dev-server] Disconnected!", 21 | "[webpack-dev-server] Trying to reconnect...", 22 | ] 23 | `; 24 | 25 | exports[`web socket communication should work and close web socket client connection when web socket server closed ("ws"): page errors 1`] = `[]`; 26 | 27 | exports[`web socket communication should work and reconnect when the connection is lost ("sockjs"): console messages 1`] = ` 28 | [ 29 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 30 | "[HMR] Waiting for update signal from WDS...", 31 | "Hey.", 32 | "[webpack-dev-server] Disconnected!", 33 | "[webpack-dev-server] Trying to reconnect...", 34 | "[webpack-dev-server] App hot update...", 35 | "[HMR] Checking for updates on the server...", 36 | "Failed to load resource: the server responded with a status of 404 (Not Found)", 37 | "[HMR] Cannot find update. Need to do a full reload!", 38 | "[HMR] (Probably because of restarting the webpack-dev-server)", 39 | "Failed to load resource: the server responded with a status of 404 (Not Found)", 40 | ] 41 | `; 42 | 43 | exports[`web socket communication should work and reconnect when the connection is lost ("sockjs"): page errors 1`] = `[]`; 44 | 45 | exports[`web socket communication should work and reconnect when the connection is lost ("ws"): console messages 1`] = ` 46 | [ 47 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 48 | "[HMR] Waiting for update signal from WDS...", 49 | "Hey.", 50 | "[webpack-dev-server] Disconnected!", 51 | "[webpack-dev-server] Trying to reconnect...", 52 | "[webpack-dev-server] App hot update...", 53 | "[HMR] Checking for updates on the server...", 54 | "Failed to load resource: the server responded with a status of 404 (Not Found)", 55 | "[HMR] Cannot find update. Need to do a full reload!", 56 | "[HMR] (Probably because of restarting the webpack-dev-server)", 57 | "Failed to load resource: the server responded with a status of 404 (Not Found)", 58 | ] 59 | `; 60 | 61 | exports[`web socket communication should work and reconnect when the connection is lost ("ws"): page errors 1`] = `[]`; 62 | 63 | exports[`web socket communication should work and terminate client that is not alive ("sockjs"): console messages 1`] = ` 64 | [ 65 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 66 | "[HMR] Waiting for update signal from WDS...", 67 | "Hey.", 68 | ] 69 | `; 70 | 71 | exports[`web socket communication should work and terminate client that is not alive ("sockjs"): page errors 1`] = `[]`; 72 | 73 | exports[`web socket communication should work and terminate client that is not alive ("ws"): console messages 1`] = ` 74 | [ 75 | "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", 76 | "[HMR] Waiting for update signal from WDS...", 77 | "Hey.", 78 | ] 79 | `; 80 | 81 | exports[`web socket communication should work and terminate client that is not alive ("ws"): page errors 1`] = `[]`; 82 | -------------------------------------------------------------------------------- /tests/e2e/__snapshots__/web-socket-server.test.js.snap.webpack5: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`web socket server should work allow to disable: console messages 1`] = ` 4 | [ 5 | "Hey.", 6 | ] 7 | `; 8 | 9 | exports[`web socket server should work allow to disable: page errors 1`] = `[]`; 10 | -------------------------------------------------------------------------------- /tests/e2e/app.test.js: -------------------------------------------------------------------------------- 1 | const fs = require("node:fs"); 2 | const path = require("node:path"); 3 | const webpack = require("@rspack/core"); 4 | const wdm = require("webpack-dev-middleware"); 5 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 6 | const config = require("../fixtures/client-config/webpack.config"); 7 | const runBrowser = require("../helpers/run-browser"); 8 | const port = require("../helpers/ports-map").app; 9 | 10 | const staticDirectory = path.resolve( 11 | __dirname, 12 | "../fixtures/static-config/public", 13 | ); 14 | 15 | const apps = [ 16 | ["express", () => require("express")(), "http"], 17 | ["express", () => require("express")(), "https"], 18 | ["express", () => require("express")(), "spdy"], 19 | ["connect", () => require("connect")(), "http"], 20 | ["connect", () => require("connect")(), "https"], 21 | ["connect", () => require("connect")(), "spdy"], 22 | ["connect", () => require("connect")(), "http2"], 23 | ["connect (async)", () => require("connect")(), "http"], 24 | [ 25 | "hono", 26 | () => new (require("hono").Hono)(), 27 | (options, app) => 28 | require("@hono/node-server").createAdaptorServer({ 29 | fetch: app.fetch, 30 | }), 31 | (_, devServer) => [ 32 | { 33 | name: "webpack-dev-middleware", 34 | middleware: wdm.honoWrapper(devServer.compiler), 35 | }, 36 | ], 37 | ], 38 | [ 39 | "hono", 40 | () => new (require("hono").Hono)(), 41 | (_, app) => 42 | require("@hono/node-server").createAdaptorServer({ 43 | fetch: app.fetch, 44 | createServer: require("node:https").createServer, 45 | serverOptions: { 46 | key: fs.readFileSync( 47 | path.resolve(__dirname, "../fixtures/ssl/localhost-privkey.pem"), 48 | ), 49 | cert: fs.readFileSync( 50 | path.resolve(__dirname, "../fixtures/ssl/localhost-cert.pem"), 51 | ), 52 | }, 53 | }), 54 | (_, devServer) => [ 55 | { 56 | name: "webpack-dev-middleware", 57 | middleware: wdm.honoWrapper(devServer.compiler), 58 | }, 59 | ], 60 | ], 61 | [ 62 | "hono", 63 | () => new (require("hono").Hono)(), 64 | { 65 | type: (options, app) => 66 | require("@hono/node-server").createAdaptorServer({ 67 | fetch: app.fetch, 68 | createServer: require("node:http2").createSecureServer, 69 | serverOptions: options, 70 | }), 71 | options: { 72 | allowHTTP1: true, 73 | key: fs.readFileSync( 74 | path.resolve(__dirname, "../fixtures/ssl/localhost-privkey.pem"), 75 | ), 76 | cert: fs.readFileSync( 77 | path.resolve(__dirname, "../fixtures/ssl/localhost-cert.pem"), 78 | ), 79 | }, 80 | }, 81 | (_, devServer) => [ 82 | { 83 | name: "webpack-dev-middleware", 84 | middleware: wdm.honoWrapper(devServer.compiler), 85 | }, 86 | ], 87 | ], 88 | ]; 89 | 90 | describe("app option", () => { 91 | for (const [appName, app, server, setupMiddlewares] of apps) { 92 | let compiler; 93 | let devServer; 94 | let page; 95 | let browser; 96 | let pageErrors; 97 | let consoleMessages; 98 | 99 | describe(`should work using "${appName}" application and "${typeof server === "function" ? "custom server" : server}" server`, () => { 100 | beforeEach(async () => { 101 | compiler = webpack(config); 102 | 103 | devServer = new Server( 104 | { 105 | static: { 106 | directory: staticDirectory, 107 | watch: false, 108 | }, 109 | app, 110 | server, 111 | port, 112 | setupMiddlewares: 113 | typeof setupMiddlewares !== "undefined" 114 | ? setupMiddlewares 115 | : // eslint-disable-next-line no-undefined 116 | undefined, 117 | }, 118 | compiler, 119 | ); 120 | 121 | await devServer.start(); 122 | 123 | ({ page, browser } = await runBrowser()); 124 | 125 | pageErrors = []; 126 | consoleMessages = []; 127 | }); 128 | 129 | afterEach(async () => { 130 | await browser.close(); 131 | await devServer.stop(); 132 | await new Promise((resolve) => { 133 | compiler.close(() => { 134 | resolve(); 135 | }); 136 | }); 137 | }); 138 | 139 | it("should handle GET request to index route (/)", async () => { 140 | page 141 | .on("console", (message) => { 142 | consoleMessages.push(message); 143 | }) 144 | .on("pageerror", (error) => { 145 | pageErrors.push(error); 146 | }); 147 | 148 | const pageUrl = devServer.isTlsServer 149 | ? `https://127.0.0.1:${port}/` 150 | : `http://127.0.0.1:${port}/`; 151 | 152 | const response = await page.goto(pageUrl, { 153 | waitUntil: "networkidle0", 154 | }); 155 | 156 | const HTTPVersion = await page.evaluate( 157 | () => performance.getEntries()[0].nextHopProtocol, 158 | ); 159 | 160 | if ( 161 | server === "spdy" || 162 | server === "http2" || 163 | server.options?.allowHTTP1 164 | ) { 165 | expect(HTTPVersion).toEqual("h2"); 166 | } else { 167 | expect(HTTPVersion).toEqual("http/1.1"); 168 | } 169 | 170 | expect(response.status()).toMatchSnapshot("response status"); 171 | expect(await response.text()).toMatchSnapshot("response text"); 172 | expect( 173 | consoleMessages.map((message) => message.text()), 174 | ).toMatchSnapshot("console messages"); 175 | expect(pageErrors).toMatchSnapshot("page errors"); 176 | }); 177 | }); 178 | } 179 | }); 180 | -------------------------------------------------------------------------------- /tests/e2e/client-reconnect.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/simple-config/webpack.config"); 4 | const runBrowser = require("../helpers/run-browser"); 5 | const port = require("../helpers/ports-map")["client-reconnect-option"]; 6 | 7 | describe("client.reconnect option", () => { 8 | describe("specified as true", () => { 9 | let compiler; 10 | let server; 11 | let page; 12 | let browser; 13 | let pageErrors; 14 | let consoleMessages; 15 | 16 | beforeEach(async () => { 17 | compiler = webpack(config); 18 | 19 | server = new Server({ port, client: { reconnect: true } }, compiler); 20 | 21 | await server.start(); 22 | 23 | ({ page, browser } = await runBrowser()); 24 | 25 | pageErrors = []; 26 | consoleMessages = []; 27 | }); 28 | 29 | afterEach(async () => { 30 | await browser.close(); 31 | }); 32 | 33 | it("should try to reconnect unlimited times", async () => { 34 | page 35 | .on("console", (message) => { 36 | consoleMessages.push(message); 37 | }) 38 | .on("pageerror", (error) => { 39 | pageErrors.push(error); 40 | }); 41 | 42 | const response = await page.goto(`http://127.0.0.1:${port}/`, { 43 | waitUntil: "networkidle0", 44 | }); 45 | 46 | try { 47 | expect(response.status()).toMatchSnapshot("response status"); 48 | } finally { 49 | await server.stop(); 50 | } 51 | 52 | let interval; 53 | 54 | await new Promise((resolve) => { 55 | interval = setInterval(() => { 56 | const retryingMessages = consoleMessages.filter((message) => 57 | message.text().includes("Trying to reconnect..."), 58 | ); 59 | 60 | if (retryingMessages.length >= 5) { 61 | clearInterval(interval); 62 | 63 | resolve(); 64 | } 65 | }, 1000); 66 | }); 67 | 68 | expect(pageErrors).toMatchSnapshot("page errors"); 69 | }); 70 | }); 71 | 72 | describe("specified as false", () => { 73 | let compiler; 74 | let server; 75 | let page; 76 | let browser; 77 | let pageErrors; 78 | let consoleMessages; 79 | 80 | beforeEach(async () => { 81 | compiler = webpack(config); 82 | 83 | server = new Server({ port, client: { reconnect: false } }, compiler); 84 | 85 | await server.start(); 86 | 87 | ({ page, browser } = await runBrowser()); 88 | 89 | pageErrors = []; 90 | consoleMessages = []; 91 | }); 92 | 93 | afterEach(async () => { 94 | await browser.close(); 95 | }); 96 | 97 | it("should not try to reconnect", async () => { 98 | page 99 | .on("console", (message) => { 100 | consoleMessages.push(message); 101 | }) 102 | .on("pageerror", (error) => { 103 | pageErrors.push(error); 104 | }); 105 | 106 | const response = await page.goto(`http://127.0.0.1:${port}/`, { 107 | waitUntil: "networkidle0", 108 | }); 109 | 110 | try { 111 | expect(response.status()).toMatchSnapshot("response status"); 112 | } finally { 113 | await server.stop(); 114 | } 115 | 116 | // Can't wait to check for unlimited times so wait only for couple retries 117 | await new Promise((resolve) => 118 | setTimeout( 119 | () => { 120 | resolve(); 121 | }, 122 | // eslint-disable-next-line no-restricted-properties 123 | 1000 * 2 ** 3, 124 | ), 125 | ); 126 | 127 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 128 | "console messages", 129 | ); 130 | 131 | expect(pageErrors).toMatchSnapshot("page errors"); 132 | }); 133 | }); 134 | 135 | describe("specified as number", () => { 136 | let compiler; 137 | let server; 138 | let page; 139 | let browser; 140 | let pageErrors; 141 | let consoleMessages; 142 | 143 | beforeEach(async () => { 144 | compiler = webpack(config); 145 | 146 | server = new Server({ port, client: { reconnect: 2 } }, compiler); 147 | 148 | await server.start(); 149 | 150 | ({ page, browser } = await runBrowser()); 151 | 152 | pageErrors = []; 153 | consoleMessages = []; 154 | }); 155 | 156 | afterEach(async () => { 157 | await browser.close(); 158 | }); 159 | 160 | it("should try to reconnect 2 times", async () => { 161 | page 162 | .on("console", (message) => { 163 | consoleMessages.push(message); 164 | }) 165 | .on("pageerror", (error) => { 166 | pageErrors.push(error); 167 | }); 168 | 169 | const response = await page.goto(`http://127.0.0.1:${port}/`, { 170 | waitUntil: "networkidle0", 171 | }); 172 | 173 | try { 174 | expect(response.status()).toMatchSnapshot("response status"); 175 | } finally { 176 | await server.stop(); 177 | } 178 | 179 | // Can't wait to check for unlimited times so wait only for couple retries 180 | await new Promise((resolve) => 181 | setTimeout( 182 | () => { 183 | resolve(); 184 | }, 185 | // eslint-disable-next-line no-restricted-properties 186 | 1000 * 2 ** 3, 187 | ), 188 | ); 189 | 190 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 191 | "console messages", 192 | ); 193 | 194 | expect(pageErrors).toMatchSnapshot("page errors"); 195 | }); 196 | }); 197 | }); 198 | -------------------------------------------------------------------------------- /tests/e2e/compress.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/simple-config-other/webpack.config"); 4 | const runBrowser = require("../helpers/run-browser"); 5 | const port = require("../helpers/ports-map")["compress-option"]; 6 | 7 | describe("compress option", () => { 8 | describe("enabled by default when not specified", () => { 9 | let compiler; 10 | let server; 11 | let page; 12 | let browser; 13 | let pageErrors; 14 | let consoleMessages; 15 | 16 | beforeEach(async () => { 17 | compiler = webpack(config); 18 | 19 | server = new Server({ port }, compiler); 20 | 21 | await server.start(); 22 | 23 | ({ page, browser } = await runBrowser()); 24 | 25 | pageErrors = []; 26 | consoleMessages = []; 27 | }); 28 | 29 | afterEach(async () => { 30 | await browser.close(); 31 | await server.stop(); 32 | }); 33 | 34 | it("should handle GET request to bundle file", async () => { 35 | page 36 | .on("console", (message) => { 37 | consoleMessages.push(message); 38 | }) 39 | .on("pageerror", (error) => { 40 | pageErrors.push(error); 41 | }); 42 | 43 | const response = await page.goto(`http://127.0.0.1:${port}/main.js`, { 44 | waitUntil: "networkidle0", 45 | }); 46 | 47 | expect(response.status()).toMatchSnapshot("response status"); 48 | 49 | expect(response.headers()["content-encoding"]).toMatchSnapshot( 50 | "response headers content-encoding", 51 | ); 52 | 53 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 54 | "console messages", 55 | ); 56 | 57 | expect(pageErrors).toMatchSnapshot("page errors"); 58 | }); 59 | }); 60 | 61 | describe("as true", () => { 62 | let compiler; 63 | let server; 64 | let page; 65 | let browser; 66 | let pageErrors; 67 | let consoleMessages; 68 | 69 | beforeEach(async () => { 70 | compiler = webpack(config); 71 | 72 | server = new Server( 73 | { 74 | compress: true, 75 | port, 76 | }, 77 | compiler, 78 | ); 79 | 80 | await server.start(); 81 | 82 | ({ page, browser } = await runBrowser()); 83 | 84 | pageErrors = []; 85 | consoleMessages = []; 86 | }); 87 | 88 | afterEach(async () => { 89 | await browser.close(); 90 | await server.stop(); 91 | }); 92 | 93 | it("should handle GET request to bundle file", async () => { 94 | page 95 | .on("console", (message) => { 96 | consoleMessages.push(message); 97 | }) 98 | .on("pageerror", (error) => { 99 | pageErrors.push(error); 100 | }); 101 | 102 | const response = await page.goto(`http://127.0.0.1:${port}/main.js`, { 103 | waitUntil: "networkidle0", 104 | }); 105 | 106 | expect(response.status()).toMatchSnapshot("response status"); 107 | 108 | expect(response.headers()["content-encoding"]).toMatchSnapshot( 109 | "response headers content-encoding", 110 | ); 111 | 112 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 113 | "console messages", 114 | ); 115 | 116 | expect(pageErrors).toMatchSnapshot("page errors"); 117 | }); 118 | }); 119 | 120 | describe("as false", () => { 121 | let compiler; 122 | let server; 123 | let page; 124 | let browser; 125 | let pageErrors; 126 | let consoleMessages; 127 | 128 | beforeEach(async () => { 129 | compiler = webpack(config); 130 | 131 | server = new Server( 132 | { 133 | compress: false, 134 | port, 135 | }, 136 | compiler, 137 | ); 138 | 139 | await server.start(); 140 | 141 | ({ page, browser } = await runBrowser()); 142 | 143 | pageErrors = []; 144 | consoleMessages = []; 145 | }); 146 | 147 | afterEach(async () => { 148 | await browser.close(); 149 | await server.stop(); 150 | }); 151 | 152 | it("should handle GET request to bundle file", async () => { 153 | page 154 | .on("console", (message) => { 155 | consoleMessages.push(message); 156 | }) 157 | .on("pageerror", (error) => { 158 | pageErrors.push(error); 159 | }); 160 | 161 | const response = await page.goto(`http://127.0.0.1:${port}/main.js`, { 162 | waitUntil: "networkidle0", 163 | }); 164 | 165 | expect(response.status()).toMatchSnapshot("response status"); 166 | 167 | expect(response.headers()["content-encoding"]).toMatchSnapshot( 168 | "response headers content-encoding", 169 | ); 170 | 171 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 172 | "console messages", 173 | ); 174 | 175 | expect(pageErrors).toMatchSnapshot("page errors"); 176 | }); 177 | }); 178 | }); 179 | -------------------------------------------------------------------------------- /tests/e2e/lazy-compilation.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const lazyCompilationSingleEntryConfig = require("../fixtures/lazy-compilation-single-entry/webpack.config"); 4 | const lazyCompilationMultipleEntriesConfig = require("../fixtures/lazy-compilation-multiple-entries/webpack.config"); 5 | const runBrowser = require("../helpers/run-browser"); 6 | const port = require("../helpers/ports-map")["lazy-compilation"]; 7 | 8 | describe("lazy compilation", () => { 9 | // TODO jest freeze due webpack do not close `eventsource`, we should uncomment this after fix it on webpack side 10 | it.skip("should work with single entry", async () => { 11 | const compiler = webpack(lazyCompilationSingleEntryConfig); 12 | const server = new Server({ port }, compiler); 13 | 14 | await server.start(); 15 | 16 | const { page, browser } = await runBrowser(); 17 | 18 | try { 19 | const pageErrors = []; 20 | const consoleMessages = []; 21 | 22 | page 23 | .on("console", (message) => { 24 | consoleMessages.push(message.text()); 25 | }) 26 | .on("pageerror", (error) => { 27 | pageErrors.push(error); 28 | }); 29 | 30 | await page.goto(`http://127.0.0.1:${port}/test.html`, { 31 | waitUntil: "domcontentloaded", 32 | }); 33 | await new Promise((resolve) => { 34 | const interval = setInterval(() => { 35 | if (consoleMessages.includes("Hey.")) { 36 | clearInterval(interval); 37 | 38 | resolve(); 39 | } 40 | }, 100); 41 | }); 42 | 43 | expect(consoleMessages).toMatchSnapshot("console messages"); 44 | expect(pageErrors).toMatchSnapshot("page errors"); 45 | } finally { 46 | await browser.close(); 47 | await server.stop(); 48 | } 49 | }); 50 | 51 | it.skip("should work with multiple entries", async () => { 52 | const compiler = webpack(lazyCompilationMultipleEntriesConfig); 53 | const server = new Server({ port }, compiler); 54 | 55 | await server.start(); 56 | 57 | const { page, browser } = await runBrowser(); 58 | 59 | try { 60 | const pageErrors = []; 61 | const consoleMessages = []; 62 | 63 | page 64 | .on("console", (message) => { 65 | consoleMessages.push(message.text()); 66 | }) 67 | .on("pageerror", (error) => { 68 | pageErrors.push(error); 69 | }); 70 | 71 | await page.goto(`http://127.0.0.1:${port}/test-one.html`, { 72 | waitUntil: "domcontentloaded", 73 | }); 74 | await new Promise((resolve) => { 75 | const interval = setInterval(() => { 76 | console.log(consoleMessages); 77 | if (consoleMessages.includes("One.")) { 78 | clearInterval(interval); 79 | 80 | resolve(); 81 | } 82 | }, 100); 83 | }); 84 | 85 | await page.goto(`http://127.0.0.1:${port}/test-two.html`, { 86 | waitUntil: "domcontentloaded", 87 | }); 88 | await new Promise((resolve) => { 89 | const interval = setInterval(() => { 90 | console.log(consoleMessages); 91 | if (consoleMessages.includes("Two.")) { 92 | clearInterval(interval); 93 | 94 | resolve(); 95 | } 96 | }, 100); 97 | }); 98 | 99 | expect(consoleMessages).toMatchSnapshot("console messages"); 100 | expect(pageErrors).toMatchSnapshot("page errors"); 101 | } finally { 102 | await browser.close(); 103 | await server.stop(); 104 | } 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /tests/e2e/mime-types.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/mime-types-config/webpack.config"); 4 | const runBrowser = require("../helpers/run-browser"); 5 | const port = require("../helpers/ports-map")["mime-types-option"]; 6 | 7 | describe("mimeTypes option", () => { 8 | describe("as an object with a remapped type", () => { 9 | let compiler; 10 | let server; 11 | let page; 12 | let browser; 13 | let pageErrors; 14 | let consoleMessages; 15 | 16 | beforeEach(async () => { 17 | compiler = webpack(config); 18 | 19 | server = new Server( 20 | { 21 | devMiddleware: { 22 | mimeTypes: { 23 | js: "text/plain", 24 | }, 25 | }, 26 | port, 27 | }, 28 | compiler, 29 | ); 30 | 31 | await server.start(); 32 | 33 | ({ page, browser } = await runBrowser()); 34 | 35 | pageErrors = []; 36 | consoleMessages = []; 37 | }); 38 | 39 | afterEach(async () => { 40 | await browser.close(); 41 | await server.stop(); 42 | }); 43 | 44 | it("should request file with different js mime type", async () => { 45 | page 46 | .on("console", (message) => { 47 | consoleMessages.push(message); 48 | }) 49 | .on("pageerror", (error) => { 50 | pageErrors.push(error); 51 | }); 52 | 53 | const response = await page.goto(`http://127.0.0.1:${port}/main.js`, { 54 | waitUntil: "networkidle0", 55 | }); 56 | 57 | expect(response.status()).toMatchSnapshot("response status"); 58 | 59 | expect(response.headers()["content-type"]).toMatchSnapshot( 60 | "response headers content-type", 61 | ); 62 | 63 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 64 | "console messages", 65 | ); 66 | 67 | expect(pageErrors).toMatchSnapshot("page errors"); 68 | }); 69 | }); 70 | 71 | describe("as an object with a custom type", () => { 72 | let compiler; 73 | let server; 74 | let page; 75 | let browser; 76 | let pageErrors; 77 | let consoleMessages; 78 | 79 | beforeEach(async () => { 80 | compiler = webpack(config); 81 | 82 | server = new Server( 83 | { 84 | devMiddleware: { 85 | mimeTypes: { 86 | custom: "text/html", 87 | }, 88 | }, 89 | port, 90 | }, 91 | compiler, 92 | ); 93 | 94 | await server.start(); 95 | 96 | ({ page, browser } = await runBrowser()); 97 | 98 | pageErrors = []; 99 | consoleMessages = []; 100 | }); 101 | 102 | afterEach(async () => { 103 | await browser.close(); 104 | await server.stop(); 105 | }); 106 | 107 | it("should request file with different js mime type", async () => { 108 | page 109 | .on("console", (message) => { 110 | consoleMessages.push(message); 111 | }) 112 | .on("pageerror", (error) => { 113 | pageErrors.push(error); 114 | }); 115 | 116 | const response = await page.goto(`http://127.0.0.1:${port}/file.custom`, { 117 | waitUntil: "networkidle0", 118 | }); 119 | 120 | expect(response.status()).toMatchSnapshot("response status"); 121 | 122 | expect(response.headers()["content-type"]).toMatchSnapshot( 123 | "response headers content-type", 124 | ); 125 | 126 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 127 | "console messages", 128 | ); 129 | 130 | expect(pageErrors).toMatchSnapshot("page errors"); 131 | }); 132 | }); 133 | }); 134 | -------------------------------------------------------------------------------- /tests/e2e/on-listening.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/client-config/webpack.config"); 4 | const runBrowser = require("../helpers/run-browser"); 5 | const port = require("../helpers/ports-map")["on-listening-option"]; 6 | 7 | describe("onListening option", () => { 8 | let compiler; 9 | let server; 10 | let page; 11 | let browser; 12 | let pageErrors; 13 | let consoleMessages; 14 | let onListeningIsRunning = false; 15 | 16 | beforeEach(async () => { 17 | compiler = webpack(config); 18 | server = new Server( 19 | { 20 | onListening: (devServer) => { 21 | if (!devServer) { 22 | throw new Error("webpack-dev-server is not defined"); 23 | } 24 | 25 | onListeningIsRunning = true; 26 | 27 | devServer.app.get("/listening/some/path", (_, response) => { 28 | response.send("listening"); 29 | }); 30 | 31 | devServer.app.post("/listening/some/path", (_, response) => { 32 | response.send("listening POST"); 33 | }); 34 | }, 35 | port, 36 | }, 37 | compiler, 38 | ); 39 | 40 | await server.start(); 41 | 42 | ({ page, browser } = await runBrowser()); 43 | 44 | pageErrors = []; 45 | consoleMessages = []; 46 | }); 47 | 48 | afterEach(async () => { 49 | await browser.close(); 50 | await server.stop(); 51 | }); 52 | 53 | it("should handle GET request to /listening/some/path route", async () => { 54 | page 55 | .on("console", (message) => { 56 | consoleMessages.push(message); 57 | }) 58 | .on("pageerror", (error) => { 59 | pageErrors.push(error); 60 | }); 61 | 62 | const response = await page.goto( 63 | `http://127.0.0.1:${port}/listening/some/path`, 64 | { 65 | waitUntil: "networkidle0", 66 | }, 67 | ); 68 | 69 | expect(onListeningIsRunning).toBe(true); 70 | 71 | expect(response.headers()["content-type"]).toMatchSnapshot( 72 | "response headers content-type", 73 | ); 74 | 75 | expect(response.status()).toMatchSnapshot("response status"); 76 | 77 | expect(await response.text()).toMatchSnapshot("response text"); 78 | 79 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 80 | "console messages", 81 | ); 82 | 83 | expect(pageErrors).toMatchSnapshot("page errors"); 84 | }); 85 | 86 | it("should handle POST request to /listening/some/path route", async () => { 87 | await page.setRequestInterception(true); 88 | 89 | page 90 | .on("console", (message) => { 91 | consoleMessages.push(message); 92 | }) 93 | .on("pageerror", (error) => { 94 | pageErrors.push(error); 95 | }) 96 | .on("request", (interceptedRequest) => { 97 | if (interceptedRequest.isInterceptResolutionHandled()) return; 98 | 99 | interceptedRequest.continue({ method: "POST" }); 100 | }); 101 | 102 | const response = await page.goto( 103 | `http://127.0.0.1:${port}/listening/some/path`, 104 | { 105 | waitUntil: "networkidle0", 106 | }, 107 | ); 108 | 109 | expect(onListeningIsRunning).toBe(true); 110 | 111 | expect(response.headers()["content-type"]).toMatchSnapshot( 112 | "response headers content-type", 113 | ); 114 | 115 | expect(response.status()).toMatchSnapshot("response status"); 116 | 117 | expect(await response.text()).toMatchSnapshot("response text"); 118 | 119 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 120 | "console messages", 121 | ); 122 | 123 | expect(pageErrors).toMatchSnapshot("page errors"); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /tests/e2e/port.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/client-config/webpack.config"); 4 | const runBrowser = require("../helpers/run-browser"); 5 | const port = require("../helpers/ports-map").port; 6 | 7 | describe("port", () => { 8 | const ports = [ 9 | "", 10 | // eslint-disable-next-line no-undefined 11 | undefined, 12 | "auto", 13 | port, 14 | `${port}`, 15 | 0, 16 | "-1", 17 | "99999", 18 | ]; 19 | 20 | for (const testedPort of ports) { 21 | it(`should work using "${testedPort}" port `, async () => { 22 | const compiler = webpack(config); 23 | const devServerOptions = {}; 24 | 25 | let usedPort; 26 | 27 | if ( 28 | testedPort === "" || 29 | typeof testedPort === "undefined" 30 | ) { 31 | process.env.WEBPACK_DEV_SERVER_BASE_PORT = port; 32 | usedPort = port; 33 | } else if (testedPort === "auto") { 34 | process.env.WEBPACK_DEV_SERVER_BASE_PORT = port; 35 | devServerOptions.port = testedPort; 36 | usedPort = port; 37 | } else { 38 | devServerOptions.port = testedPort; 39 | usedPort = testedPort; 40 | } 41 | 42 | const server = new Server(devServerOptions, compiler); 43 | 44 | let errored; 45 | 46 | try { 47 | await server.start(); 48 | } catch (error) { 49 | errored = error; 50 | } 51 | 52 | if (testedPort === "-1" || testedPort === "99999") { 53 | const errorMessageRegExp = /options.port should be >= 0 and < 65536/; 54 | 55 | try { 56 | expect(errored.message).toMatch(errorMessageRegExp); 57 | } finally { 58 | await server.stop(); 59 | } 60 | 61 | return; 62 | } 63 | 64 | const address = server.server.address(); 65 | 66 | if (testedPort === 0) { 67 | expect(typeof address.port).toBe("number"); 68 | } else { 69 | expect(address.port).toBe(Number(usedPort)); 70 | } 71 | 72 | const { page, browser } = await runBrowser(); 73 | 74 | try { 75 | const pageErrors = []; 76 | const consoleMessages = []; 77 | 78 | page 79 | .on("console", (message) => { 80 | consoleMessages.push(message); 81 | }) 82 | .on("pageerror", (error) => { 83 | pageErrors.push(error); 84 | }); 85 | 86 | await page.goto(`http://127.0.0.1:${address.port}/`, { 87 | waitUntil: "networkidle0", 88 | }); 89 | 90 | expect( 91 | consoleMessages.map((message) => message.text()), 92 | ).toMatchSnapshot("console messages"); 93 | expect(pageErrors).toMatchSnapshot("page errors"); 94 | } finally { 95 | await browser.close(); 96 | await server.stop(); 97 | } 98 | 99 | if ( 100 | testedPort === "" || 101 | typeof testedPort === "undefined" 102 | ) { 103 | process.env.WEBPACK_DEV_SERVER_BASE_PORT = undefined; 104 | } 105 | }); 106 | } 107 | }); 108 | -------------------------------------------------------------------------------- /tests/e2e/progress.test.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | const fs = require("graceful-fs"); 3 | const webpack = require("@rspack/core"); 4 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 5 | const reloadConfig = require("../fixtures/reload-config-2/webpack.config"); 6 | const runBrowser = require("../helpers/run-browser"); 7 | const port = require("../helpers/ports-map").progress; 8 | 9 | const cssFilePath = path.resolve( 10 | __dirname, 11 | "../fixtures/reload-config-2/main.css", 12 | ); 13 | 14 | describe("progress", () => { 15 | it("should work and log progress in a browser console", async () => { 16 | fs.writeFileSync(cssFilePath, "body { background-color: rgb(0, 0, 255); }"); 17 | 18 | const compiler = webpack(reloadConfig); 19 | const devServerOptions = { 20 | port, 21 | client: { 22 | progress: true, 23 | }, 24 | }; 25 | const server = new Server(devServerOptions, compiler); 26 | 27 | await server.start(); 28 | 29 | try { 30 | const { page, browser } = await runBrowser(); 31 | 32 | const consoleMessages = []; 33 | 34 | try { 35 | let doHotUpdate = false; 36 | 37 | page 38 | .on("console", (message) => { 39 | consoleMessages.push(message); 40 | }) 41 | .on("request", (interceptedRequest) => { 42 | if (interceptedRequest.isInterceptResolutionHandled()) return; 43 | 44 | if (/\.hot-update\.(json|js)$/.test(interceptedRequest.url())) { 45 | doHotUpdate = true; 46 | } 47 | }); 48 | 49 | await page.goto(`http://localhost:${port}/`, { 50 | waitUntil: "networkidle0", 51 | }); 52 | 53 | fs.writeFileSync( 54 | cssFilePath, 55 | "body { background-color: rgb(255, 0, 0); }", 56 | ); 57 | 58 | await new Promise((resolve) => { 59 | const timer = setInterval(() => { 60 | if (doHotUpdate) { 61 | clearInterval(timer); 62 | 63 | resolve(); 64 | } 65 | }, 100); 66 | }); 67 | } finally { 68 | await browser.close(); 69 | } 70 | 71 | const progressConsoleMessage = consoleMessages.filter((message) => 72 | /^\[webpack-dev-server\] (\[[a-zA-Z]+\] )?[0-9]{1,3}% - /.test( 73 | message.text(), 74 | ), 75 | ); 76 | 77 | expect(progressConsoleMessage.length > 0).toBe(true); 78 | } finally { 79 | fs.unlinkSync(cssFilePath); 80 | 81 | await server.stop(); 82 | } 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /tests/e2e/range-header.test.js: -------------------------------------------------------------------------------- 1 | const request = require("supertest"); 2 | const webpack = require("@rspack/core"); 3 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 4 | const config = require("../fixtures/static-config/webpack.config"); 5 | const port = require("../helpers/ports-map")["range-header"]; 6 | 7 | describe("'Range' header", () => { 8 | let compiler; 9 | let server; 10 | 11 | beforeAll(async () => { 12 | compiler = webpack(config); 13 | 14 | server = new Server({ port }, compiler); 15 | 16 | await server.start(); 17 | }); 18 | 19 | afterAll(async () => { 20 | await server.stop(); 21 | }); 22 | 23 | it('should work with "Range" header using "GET" method', async () => { 24 | const response = await request(server.app).get("/main.js"); 25 | 26 | expect(response.status).toBe(200); 27 | expect(response.headers["content-type"]).toBe( 28 | "application/javascript; charset=utf-8", 29 | ); 30 | expect(response.headers["accept-ranges"]).toBe("bytes"); 31 | 32 | const responseContent = response.text; 33 | const responseRange = await request(server.app) 34 | .get("/main.js") 35 | .set("Range", "bytes=0-499"); 36 | 37 | expect(responseRange.status).toBe(206); 38 | expect(responseRange.headers["content-type"]).toBe( 39 | "application/javascript; charset=utf-8", 40 | ); 41 | expect(responseRange.headers["content-length"]).toBe("500"); 42 | expect(responseRange.headers["content-range"]).toMatch(/^bytes 0-499\//); 43 | expect(responseRange.text).toBe(responseContent.slice(0, 500)); 44 | expect(responseRange.text.length).toBe(500); 45 | }); 46 | 47 | it('should work with "Range" header using "HEAD" method', async () => { 48 | const response = await request(server.app).head("/main.js"); 49 | 50 | expect(response.status).toBe(200); 51 | expect(response.headers["content-type"]).toBe( 52 | "application/javascript; charset=utf-8", 53 | ); 54 | expect(response.headers["accept-ranges"]).toBe("bytes"); 55 | 56 | const responseRange = await request(server.app) 57 | .head("/main.js") 58 | .set("Range", "bytes=0-499"); 59 | 60 | expect(responseRange.status).toBe(206); 61 | expect(responseRange.headers["content-type"]).toBe( 62 | "application/javascript; charset=utf-8", 63 | ); 64 | expect(responseRange.headers["content-length"]).toBe("500"); 65 | expect(responseRange.headers["content-range"]).toMatch(/^bytes 0-499\//); 66 | }); 67 | 68 | it('should work with unsatisfiable "Range" header using "GET" method', async () => { 69 | const response = await request(server.app).get("/main.js"); 70 | 71 | expect(response.status).toBe(200); 72 | expect(response.headers["content-type"]).toBe( 73 | "application/javascript; charset=utf-8", 74 | ); 75 | expect(response.headers["accept-ranges"]).toBe("bytes"); 76 | 77 | const responseRange = await request(server.app) 78 | .get("/main.js") 79 | .set("Range", "bytes=99999999999-"); 80 | 81 | expect(responseRange.status).toBe(416); 82 | expect(responseRange.headers["content-type"]).toBe( 83 | "text/html; charset=utf-8", 84 | ); 85 | expect(responseRange.headers["content-range"]).toMatch(/^bytes \*\//); 86 | }); 87 | 88 | it('should work with malformed "Range" header using "GET" method', async () => { 89 | const response = await request(server.app).get("/main.js"); 90 | 91 | expect(response.status).toBe(200); 92 | expect(response.headers["content-type"]).toBe( 93 | "application/javascript; charset=utf-8", 94 | ); 95 | expect(response.headers["accept-ranges"]).toBe("bytes"); 96 | 97 | const responseContent = response.text; 98 | const responseRange = await request(server.app) 99 | .get("/main.js") 100 | .set("Range", "bytes"); 101 | 102 | expect(responseRange.status).toBe(200); 103 | expect(responseRange.headers["content-type"]).toBe( 104 | "application/javascript; charset=utf-8", 105 | ); 106 | expect(responseRange.text).toBe(responseContent); 107 | expect(responseRange.text.length).toBe(responseContent.length); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /tests/e2e/setup-exit-signals.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/simple-config/webpack.config"); 4 | const runBrowser = require("../helpers/run-browser"); 5 | const port = require("../helpers/ports-map")["setup-exit-signals-option"]; 6 | 7 | describe("setupExitSignals option", () => { 8 | describe("should handle 'SIGINT' and 'SIGTERM' signals", () => { 9 | let compiler; 10 | let server; 11 | let page; 12 | let browser; 13 | let pageErrors; 14 | let consoleMessages; 15 | let doExit; 16 | let exitSpy; 17 | let stopCallbackSpy; 18 | let stdinResumeSpy; 19 | let closeCallbackSpy; 20 | 21 | const signals = ["SIGINT", "SIGTERM"]; 22 | 23 | beforeEach(async () => { 24 | compiler = webpack(config); 25 | 26 | server = new Server( 27 | { 28 | setupExitSignals: true, 29 | port, 30 | }, 31 | compiler, 32 | ); 33 | 34 | await server.start(); 35 | 36 | ({ page, browser } = await runBrowser()); 37 | 38 | pageErrors = []; 39 | consoleMessages = []; 40 | doExit = false; 41 | 42 | exitSpy = jest.spyOn(process, "exit").mockImplementation(() => { 43 | doExit = true; 44 | }); 45 | 46 | stdinResumeSpy = jest 47 | .spyOn(process.stdin, "resume") 48 | .mockImplementation(() => {}); 49 | 50 | stopCallbackSpy = jest.spyOn(server, "stopCallback"); 51 | 52 | if (server.compiler.close) { 53 | closeCallbackSpy = jest.spyOn(server.compiler, "close"); 54 | } 55 | }); 56 | 57 | afterEach(async () => { 58 | exitSpy.mockReset(); 59 | stdinResumeSpy.mockReset(); 60 | for (const signal of signals) { 61 | process.removeAllListeners(signal); 62 | } 63 | process.stdin.removeAllListeners("end"); 64 | await browser.close(); 65 | await server.stop(); 66 | }); 67 | 68 | it.each(signals)("should close and exit on %s", async (signal) => { 69 | page 70 | .on("console", (message) => { 71 | consoleMessages.push(message); 72 | }) 73 | .on("pageerror", (error) => { 74 | pageErrors.push(error); 75 | }); 76 | 77 | const response = await page.goto(`http://127.0.0.1:${port}/`, { 78 | waitUntil: "networkidle0", 79 | }); 80 | 81 | expect(response.status()).toMatchSnapshot("response status"); 82 | 83 | process.emit(signal); 84 | 85 | await new Promise((resolve) => { 86 | const interval = setInterval(() => { 87 | if (doExit) { 88 | expect(stopCallbackSpy.mock.calls.length).toEqual(1); 89 | 90 | if (server.compiler.close) { 91 | expect(closeCallbackSpy.mock.calls.length).toEqual(1); 92 | } 93 | 94 | clearInterval(interval); 95 | 96 | resolve(); 97 | } 98 | }, 100); 99 | }); 100 | 101 | consoleMessages = consoleMessages.filter( 102 | (message) => 103 | !( 104 | message.text().includes("Trying to reconnect...") || 105 | message.text().includes("Disconnected") 106 | ), 107 | ); 108 | 109 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 110 | "console messages", 111 | ); 112 | 113 | expect(pageErrors).toMatchSnapshot("page errors"); 114 | }); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /tests/e2e/setup-middlewares.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/client-config/webpack.config"); 4 | const runBrowser = require("../helpers/run-browser"); 5 | const port = require("../helpers/ports-map")["setup-middlewares-option"]; 6 | 7 | describe("setupMiddlewares option", () => { 8 | let compiler; 9 | let server; 10 | let page; 11 | let browser; 12 | let pageErrors; 13 | let consoleMessages; 14 | 15 | beforeEach(async () => { 16 | compiler = webpack(config); 17 | server = new Server( 18 | { 19 | setupMiddlewares: (middlewares, devServer) => { 20 | if (!devServer) { 21 | throw new Error("webpack-dev-server is not defined"); 22 | } 23 | 24 | devServer.app.use("/setup-middleware/some/path", (req, res, next) => { 25 | if (req.method === "GET") { 26 | res.setHeader("Content-Type", "text/html; charset=utf-8"); 27 | res.end("setup-middlewares option GET"); 28 | return; 29 | } 30 | if (req.method === "POST") { 31 | res.setHeader("Content-Type", "text/html; charset=utf-8"); 32 | res.end("setup-middlewares option POST"); 33 | return; 34 | } 35 | 36 | return next(); 37 | }); 38 | 39 | middlewares.push({ 40 | name: "hello-world-test-two", 41 | middleware: (req, res, next) => { 42 | if (req.url !== "/foo/bar/baz") { 43 | next(); 44 | 45 | return; 46 | } 47 | 48 | res.setHeader("Content-Type", "text/html; charset=utf-8"); 49 | res.end("Hello World without path!"); 50 | }, 51 | }); 52 | middlewares.push({ 53 | name: "hello-world-test-one", 54 | path: "/foo/bar", 55 | middleware: (req, res) => { 56 | res.setHeader("Content-Type", "text/html; charset=utf-8"); 57 | res.end("Hello World with path!"); 58 | }, 59 | }); 60 | middlewares.push((req, res) => { 61 | res.setHeader("Content-Type", "text/html; charset=utf-8"); 62 | res.end("Hello World as function!"); 63 | }); 64 | 65 | return middlewares; 66 | }, 67 | port, 68 | }, 69 | compiler, 70 | ); 71 | 72 | await server.start(); 73 | 74 | ({ page, browser } = await runBrowser()); 75 | 76 | pageErrors = []; 77 | consoleMessages = []; 78 | }); 79 | 80 | afterEach(async () => { 81 | await browser.close(); 82 | await server.stop(); 83 | }); 84 | 85 | it("should handle GET request to /setup-middleware/some/path route", async () => { 86 | page 87 | .on("console", (message) => { 88 | consoleMessages.push(message); 89 | }) 90 | .on("pageerror", (error) => { 91 | pageErrors.push(error); 92 | }); 93 | 94 | const response = await page.goto( 95 | `http://127.0.0.1:${port}/setup-middleware/some/path`, 96 | { 97 | waitUntil: "networkidle0", 98 | }, 99 | ); 100 | 101 | expect(response.headers()["content-type"]).toMatchSnapshot( 102 | "response headers content-type", 103 | ); 104 | expect(response.status()).toMatchSnapshot("response status"); 105 | expect(await response.text()).toMatchSnapshot("response text"); 106 | 107 | const response1 = await page.goto(`http://127.0.0.1:${port}/foo/bar`, { 108 | waitUntil: "networkidle0", 109 | }); 110 | 111 | expect(response1.headers()["content-type"]).toMatchSnapshot( 112 | "response headers content-type", 113 | ); 114 | expect(response1.status()).toMatchSnapshot("response status"); 115 | expect(await response1.text()).toMatchSnapshot("response text"); 116 | 117 | const response2 = await page.goto(`http://127.0.0.1:${port}/foo/bar/baz`, { 118 | waitUntil: "networkidle0", 119 | }); 120 | 121 | expect(response2.headers()["content-type"]).toMatchSnapshot( 122 | "response headers content-type", 123 | ); 124 | expect(response2.status()).toMatchSnapshot("response status"); 125 | expect(await response2.text()).toMatchSnapshot("response text"); 126 | 127 | const response3 = await page.goto( 128 | `http://127.0.0.1:${port}/setup-middleware/unknown`, 129 | { 130 | waitUntil: "networkidle0", 131 | }, 132 | ); 133 | 134 | expect(response3.headers()["content-type"]).toMatchSnapshot( 135 | "response headers content-type", 136 | ); 137 | expect(response3.status()).toMatchSnapshot("response status"); 138 | expect(await response3.text()).toMatchSnapshot("response text"); 139 | 140 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 141 | "console messages", 142 | ); 143 | expect(pageErrors).toMatchSnapshot("page errors"); 144 | }); 145 | 146 | it("should handle POST request to /setup-middleware/some/path route", async () => { 147 | await page.setRequestInterception(true); 148 | 149 | page 150 | .on("console", (message) => { 151 | consoleMessages.push(message); 152 | }) 153 | .on("pageerror", (error) => { 154 | pageErrors.push(error); 155 | }) 156 | .on("request", (interceptedRequest) => { 157 | if (interceptedRequest.isInterceptResolutionHandled()) return; 158 | 159 | interceptedRequest.continue({ method: "POST" }); 160 | }); 161 | 162 | const response = await page.goto( 163 | `http://127.0.0.1:${port}/setup-middleware/some/path`, 164 | { 165 | waitUntil: "networkidle0", 166 | }, 167 | ); 168 | 169 | expect(response.headers()["content-type"]).toMatchSnapshot( 170 | "response headers content-type", 171 | ); 172 | expect(response.status()).toMatchSnapshot("response status"); 173 | expect(await response.text()).toMatchSnapshot("response text"); 174 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 175 | "console messages", 176 | ); 177 | expect(pageErrors).toMatchSnapshot("page errors"); 178 | }); 179 | }); 180 | -------------------------------------------------------------------------------- /tests/e2e/stats.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/client-config/webpack.config"); 4 | const HTMLGeneratorPlugin = require("../helpers/html-generator-plugin"); 5 | const runBrowser = require("../helpers/run-browser"); 6 | const port = require("../helpers/ports-map").stats; 7 | 8 | global.console.log = jest.fn(); 9 | 10 | describe("stats", () => { 11 | const cases = [ 12 | { 13 | title: 'should work when "stats" is not specified', 14 | webpackOptions: {}, 15 | }, 16 | { 17 | title: 'should work using "{}" value for the "stats" option', 18 | webpackOptions: { 19 | stats: {}, 20 | }, 21 | }, 22 | { 23 | title: 'should work using "undefined" value for the "stats" option', 24 | webpackOptions: { 25 | // eslint-disable-next-line no-undefined 26 | stats: undefined, 27 | }, 28 | }, 29 | { 30 | title: 'should work using "false" value for the "stats" option', 31 | webpackOptions: { 32 | stats: false, 33 | }, 34 | }, 35 | { 36 | title: 'should work using "errors-only" value for the "stats" option', 37 | webpackOptions: { 38 | stats: "errors-only", 39 | }, 40 | }, 41 | { 42 | title: 43 | 'should work using "{ assets: false }" value for the "stats" option', 44 | webpackOptions: { 45 | stats: { 46 | assets: false, 47 | }, 48 | }, 49 | }, 50 | // TODO: support object `config.stats.colors` 51 | // { 52 | // title: 53 | // 'should work using "{ assets: false }" value for the "stats" option', 54 | // webpackOptions: { 55 | // stats: { 56 | // colors: { 57 | // green: "\u001b[32m", 58 | // }, 59 | // }, 60 | // }, 61 | // }, 62 | // `config.stats.warningsFilter` is deprecated in favor of config.ignoreWarnings 63 | // { 64 | // title: 65 | // 'should work using "{ warningsFilter: \'test\' }" value for the "stats" option', 66 | // webpackOptions: { 67 | // plugins: [ 68 | // { 69 | // apply(compiler) { 70 | // compiler.hooks.thisCompilation.tap( 71 | // "warnings-webpack-plugin", 72 | // (compilation) => { 73 | // compilation.warnings.push( 74 | // new Error("Warning from compilation"), 75 | // ); 76 | // }, 77 | // ); 78 | // }, 79 | // }, 80 | // new HTMLGeneratorPlugin(), 81 | // ], 82 | // stats: { warningsFilter: /Warning from compilation/ }, 83 | // }, 84 | // }, 85 | ]; 86 | 87 | if (webpack.version.startsWith("5")) { 88 | cases.push({ 89 | title: 'should work and respect the "ignoreWarnings" option', 90 | webpackOptions: { 91 | plugins: [ 92 | { 93 | apply(compiler) { 94 | compiler.hooks.thisCompilation.tap( 95 | "warnings-webpack-plugin", 96 | (compilation) => { 97 | compilation.warnings.push( 98 | new Error("Warning from compilation"), 99 | ); 100 | }, 101 | ); 102 | }, 103 | }, 104 | new HTMLGeneratorPlugin(), 105 | ], 106 | ignoreWarnings: [/Warning from compilation/], 107 | }, 108 | }); 109 | } 110 | 111 | for (const testCase of cases) { 112 | it(testCase.title, async () => { 113 | const compiler = webpack({ ...config, ...testCase.webpackOptions }); 114 | const devServerOptions = { 115 | port, 116 | }; 117 | const server = new Server(devServerOptions, compiler); 118 | 119 | await server.start(); 120 | 121 | const { page, browser } = await runBrowser(); 122 | 123 | try { 124 | const consoleMessages = []; 125 | 126 | page.on("console", (message) => { 127 | consoleMessages.push(message); 128 | }); 129 | 130 | await page.goto(`http://localhost:${port}/`, { 131 | waitUntil: "networkidle0", 132 | }); 133 | 134 | expect( 135 | consoleMessages.map((message) => message.text()), 136 | ).toMatchSnapshot(); 137 | } finally { 138 | await browser.close(); 139 | await server.stop(); 140 | } 141 | }); 142 | } 143 | }); 144 | -------------------------------------------------------------------------------- /tests/e2e/target.test.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | const webpack = require("@rspack/core"); 3 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 4 | const config = require("../fixtures/client-config/webpack.config"); 5 | const runBrowser = require("../helpers/run-browser"); 6 | const port = require("../helpers/ports-map").target; 7 | const workerConfig = require("../fixtures/worker-config/webpack.config"); 8 | const workerConfigDevServerFalse = require("../fixtures/worker-config-dev-server-false/webpack.config"); 9 | 10 | const sortByTerm = (data, term) => 11 | data.sort((a, b) => (a.indexOf(term) < b.indexOf(term) ? -1 : 1)); 12 | 13 | describe("target", () => { 14 | const targets = [ 15 | false, 16 | "browserslist:defaults", 17 | "web", 18 | "webworker", 19 | "node", 20 | "async-node", 21 | "electron-main", 22 | "electron-preload", 23 | "electron-renderer", 24 | "nwjs", 25 | "node-webkit", 26 | "es5", 27 | ["web", "es5"], 28 | ]; 29 | 30 | for (const target of targets) { 31 | it(`should work using "${target}" target`, async () => { 32 | const compiler = webpack({ 33 | ...config, 34 | target, 35 | ...(target === false || target === "es5" 36 | ? { 37 | output: { chunkFormat: "array-push", path: "/" }, 38 | } 39 | : {}), 40 | }); 41 | const server = new Server({ port }, compiler); 42 | 43 | await server.start(); 44 | 45 | const { page, browser } = await runBrowser(); 46 | 47 | try { 48 | const pageErrors = []; 49 | const consoleMessages = []; 50 | 51 | page 52 | .on("console", (message) => { 53 | consoleMessages.push(message); 54 | }) 55 | .on("pageerror", (error) => { 56 | pageErrors.push(error); 57 | }); 58 | 59 | await page.goto(`http://127.0.0.1:${port}/`, { 60 | waitUntil: "networkidle0", 61 | }); 62 | 63 | expect( 64 | consoleMessages.map((message) => message.text()), 65 | ).toMatchSnapshot("console messages"); 66 | 67 | // TODO: check why require is defined in theses target 68 | // if ( 69 | // target === "node" || 70 | // target === "async-node" || 71 | // target === "electron-main" || 72 | // target === "electron-preload" || 73 | // target === "electron-renderer" || 74 | // target === "nwjs" || 75 | // target === "node-webkit" 76 | // ) { 77 | // console.log(pageErrors); 78 | // const hasRequireOrGlobalError = 79 | // pageErrors.filter((pageError) => 80 | // /require is not defined|global is not defined/.test(pageError), 81 | // ).length === 1; 82 | 83 | // expect(hasRequireOrGlobalError).toBe(true); 84 | // } else { 85 | // expect(pageErrors).toMatchSnapshot("page errors"); 86 | // } 87 | } finally { 88 | await browser.close(); 89 | await server.stop(); 90 | } 91 | }); 92 | } 93 | 94 | it("should work using multi compiler mode with `web` and `webworker` targets", async () => { 95 | const compiler = webpack(workerConfig); 96 | const server = new Server({ port }, compiler); 97 | 98 | await server.start(); 99 | 100 | const { page, browser } = await runBrowser(); 101 | 102 | try { 103 | const pageErrors = []; 104 | const consoleMessages = []; 105 | 106 | page 107 | .on("console", (message) => { 108 | consoleMessages.push(message); 109 | }) 110 | .on("pageerror", (error) => { 111 | pageErrors.push(error); 112 | }); 113 | 114 | await page.goto(`http://127.0.0.1:${port}/`, { 115 | waitUntil: "networkidle0", 116 | }); 117 | 118 | expect( 119 | sortByTerm( 120 | consoleMessages.map((message) => message.text()), 121 | "Worker said:", 122 | ), 123 | ).toMatchSnapshot("console messages"); 124 | 125 | expect(pageErrors).toMatchSnapshot("page errors"); 126 | } finally { 127 | await browser.close(); 128 | await server.stop(); 129 | } 130 | }); 131 | 132 | it("should work using multi compiler mode with `web` and `webworker` targets with `devServer: false`", async () => { 133 | const compiler = webpack(workerConfigDevServerFalse); 134 | const server = new Server( 135 | { 136 | port, 137 | static: { 138 | directory: path.resolve( 139 | __dirname, 140 | "../fixtures/worker-config-dev-server-false/public/", 141 | ), 142 | }, 143 | }, 144 | compiler, 145 | ); 146 | 147 | await server.start(); 148 | 149 | const { page, browser } = await runBrowser(); 150 | 151 | try { 152 | const pageErrors = []; 153 | const consoleMessages = []; 154 | 155 | page 156 | .on("console", (message) => { 157 | consoleMessages.push(message); 158 | }) 159 | .on("pageerror", (error) => { 160 | pageErrors.push(error); 161 | }); 162 | 163 | await page.goto(`http://127.0.0.1:${port}/`, { 164 | waitUntil: "networkidle0", 165 | }); 166 | 167 | expect( 168 | sortByTerm( 169 | consoleMessages.map((message) => message.text()), 170 | "Worker said:", 171 | ), 172 | ).toMatchSnapshot("console messages"); 173 | 174 | expect(pageErrors).toMatchSnapshot("page errors"); 175 | } finally { 176 | await browser.close(); 177 | await server.stop(); 178 | } 179 | }); 180 | }); 181 | -------------------------------------------------------------------------------- /tests/e2e/web-socket-server.test.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | const config = require("../fixtures/client-config/webpack.config"); 4 | const runBrowser = require("../helpers/run-browser"); 5 | const sessionSubscribe = require("../helpers/session-subscribe"); 6 | const port = require("../helpers/ports-map")["web-socket-server-test"]; 7 | 8 | describe("web socket server", () => { 9 | it("should work allow to disable", async () => { 10 | const devServerPort = port; 11 | 12 | const compiler = webpack(config); 13 | const devServerOptions = { 14 | webSocketServer: false, 15 | port: devServerPort, 16 | }; 17 | const server = new Server(devServerOptions, compiler); 18 | 19 | await server.start(); 20 | 21 | const { page, browser } = await runBrowser(); 22 | 23 | try { 24 | const pageErrors = []; 25 | const consoleMessages = []; 26 | 27 | page 28 | .on("console", (message) => { 29 | consoleMessages.push(message); 30 | }) 31 | .on("pageerror", (error) => { 32 | pageErrors.push(error); 33 | }); 34 | 35 | const webSocketRequests = []; 36 | const session = await page.target().createCDPSession(); 37 | 38 | session.on("Network.webSocketCreated", (test) => { 39 | webSocketRequests.push(test); 40 | }); 41 | 42 | await session.send("Target.setAutoAttach", { 43 | autoAttach: true, 44 | flatten: true, 45 | waitForDebuggerOnStart: true, 46 | }); 47 | 48 | sessionSubscribe(session); 49 | 50 | await page.goto(`http://127.0.0.1:${port}/`, { 51 | waitUntil: "networkidle0", 52 | }); 53 | 54 | expect(webSocketRequests).toHaveLength(0); 55 | expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 56 | "console messages", 57 | ); 58 | expect(pageErrors).toMatchSnapshot("page errors"); 59 | } finally { 60 | await browser.close(); 61 | await server.stop(); 62 | } 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /tests/fixtures/client-config/bar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Bar."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/client-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/client-config/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/fixtures/client-config/static/foo.txt: -------------------------------------------------------------------------------- 1 | Text -------------------------------------------------------------------------------- /tests/fixtures/client-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | devtool: false, 7 | mode: "development", 8 | context: __dirname, 9 | stats: "none", 10 | entry: "./foo.js", 11 | output: { 12 | path: "/" 13 | }, 14 | infrastructureLogging: { 15 | level: "info", 16 | stream: { 17 | write: () => {} 18 | } 19 | }, 20 | plugins: [new HTMLGeneratorPlugin()] 21 | }; 22 | -------------------------------------------------------------------------------- /tests/fixtures/custom-client/CustomClientEntry.js: -------------------------------------------------------------------------------- 1 | console.log("custom client entry"); 2 | -------------------------------------------------------------------------------- /tests/fixtures/custom-client/CustomClientHotEntry.js: -------------------------------------------------------------------------------- 1 | console.log("custom client hot entry"); 2 | -------------------------------------------------------------------------------- /tests/fixtures/custom-client/CustomSockJSClient.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copy from webpack-dev-server 3 | */ 4 | 5 | "use strict"; 6 | 7 | const SockJS = require("sockjs-client/dist/sockjs"); 8 | 9 | module.exports = class SockJSClient { 10 | constructor(url) { 11 | this.sock = new SockJS( 12 | url.replace(/^ws:/i, "http://").replace(/^wss:/i, "https://") 13 | ); 14 | } 15 | 16 | onOpen(f) { 17 | this.sock.onopen = () => { 18 | console.log("open"); 19 | f(); 20 | }; 21 | } 22 | 23 | onClose(f) { 24 | this.sock.onclose = () => { 25 | console.log("close"); 26 | f(); 27 | }; 28 | } 29 | 30 | // call f with the message string as the first argument 31 | onMessage(f) { 32 | this.sock.onmessage = e => { 33 | const obj = JSON.parse(e.data); 34 | console.log(obj.type); 35 | f(e.data); 36 | }; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-2-config/bar.html: -------------------------------------------------------------------------------- 1 | Foobar 2 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-2-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-2-config/other.html: -------------------------------------------------------------------------------- 1 | Other file 2 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-2-config/random-file.txt: -------------------------------------------------------------------------------- 1 | Random file 2 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-2-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | mode: "development", 5 | context: __dirname, 6 | stats: "none", 7 | entry: "./foo.js", 8 | output: { 9 | path: "/" 10 | }, 11 | infrastructureLogging: { 12 | level: "warn" 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-3-config/bar.html: -------------------------------------------------------------------------------- 1 | In-memory file 2 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-3-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("./bar.html"); 4 | 5 | console.log("Hey."); 6 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-3-config/index.html: -------------------------------------------------------------------------------- 1 | static file 2 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-3-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const moduleRuleForHTML = { 4 | test: /\.html$/, 5 | type: "asset/resource", 6 | generator: { 7 | filename: "index.html" 8 | } 9 | }; 10 | 11 | module.exports = { 12 | mode: "development", 13 | context: __dirname, 14 | stats: "none", 15 | entry: "./foo.js", 16 | output: { 17 | path: "/" 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | ...moduleRuleForHTML 23 | } 24 | ] 25 | }, 26 | infrastructureLogging: { 27 | level: "warn" 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-config/bar.html: -------------------------------------------------------------------------------- 1 | Foobar 2 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("./index.html"); 4 | require("./bar.html"); 5 | 6 | console.log("Hey."); 7 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-config/index.html: -------------------------------------------------------------------------------- 1 | Heyyy 2 | -------------------------------------------------------------------------------- /tests/fixtures/historyapifallback-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const moduleRuleForHTML = { 4 | test: /\.html$/, 5 | type: "asset/resource", 6 | generator: { 7 | filename: "[name][ext]" 8 | } 9 | }; 10 | 11 | module.exports = { 12 | mode: "development", 13 | context: __dirname, 14 | stats: "none", 15 | entry: "./foo.js", 16 | output: { 17 | path: "/" 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | ...moduleRuleForHTML 23 | } 24 | ] 25 | }, 26 | infrastructureLogging: { 27 | level: "warn" 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /tests/fixtures/https-certificate/ca-symlink.pem: -------------------------------------------------------------------------------- 1 | ca.pem -------------------------------------------------------------------------------- /tests/fixtures/https-certificate/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEAxAUVLFM+K3XDLQkBi7xt0s1Ip7JoHYDskzUDQNHjjMkUq5kv 3 | C/hf5Ei1J6qruJs3Xqg86Nl4+ed4ynUajAkRRibhp0P1SG1tgPssIK6iC7g8heYu 4 | Dy9WkFuMie0513zjSn6bMEAK5TegxYAWCbaCZX/Fw9bDniabL/zuOv4sf8J4EPhs 5 | EENnH6sUE9HxPUgQmNt1Tbd0j1Cd5PXrSTLyfVPRh0m9QhXTUHuxsse8XSn9U2sw 6 | duxJTWRINmhffYn+O7kbJGI77xYr8u58Rsf3HCMI8DTKZNvQLChvvtLJ9ckyu7Q+ 7 | T8emgklStASm3V2UtnriaK/IQEhgSdxqVRib3QIDAQABAoIBAGqWKPE1QnT3T+3J 8 | G+ITz9P0dDFbvWltlTZmeSJh/s2q+WZloUNtBxdmwbqT/1QecnkyGgyzVCjvSKsu 9 | CgVjWNVAhysgtNtxRT4BVflffBXLVH2qsBjpsLRGU6EcMXuPGTiEp3YRHNuO6Aj8 10 | oP8fEsCGPc9DlJMGgxQRAKlrVF8TN/0j6Qk+YpS4MZ0YFQfBY+WdKu04Z8TVTplQ 11 | tTkiGpBI+Oj85jF59aQiizglJgADkAZ6zmbrctm/G9jPxh7JLS2cKI0ECZgK5yAc 12 | pk10E1YWhoCksjr9arxy6fS9TiX9P15vv06k+s7c4c5X7XDm3X0GWeSbqBMJb8q7 13 | BhZQNzECgYEA4kAtymDBvFYiZFq7+lzQBRKAI1RCq7YqxlieumH0PSkie2bba3dW 14 | NVdTi7at8+GDB9/cHUPKzg/skfJllek57MZmusiVwB/Lmp/IlW8YyGShdYZ7zQsV 15 | KMWJljpky3BEDM5sb08wIkfrOkelI/S4Bqqabd9JzOMJzoTiVOlMam8CgYEA3ctN 16 | yonWz2bsnCUstQvQCLdI5a8Q7GJvlH2awephxGXIKGUuRmyyop0AnRnIBEWtOQV7 17 | yZjW32bU+Wt+2BJ247EyJiIQ4gT+T51t+v/Wt1YNbL3dSj9ttOvwYd4H2W4E7EIO 18 | GKIF4I39FM7r8NfG7YE7S1aVcnrqs01N3nhd9HMCgYEAjepbzpmqbAxLPk97oase 19 | AFB+d6qetz5ozklAJwDSRprKukTmVR5hwMup5/UKX/OQURwl4WVojKCIb3NwLPxC 20 | DTbVsUuoQv6uo6qeEr3A+dHFRQa6GP9eolhl2Ql/t+wPg0jn01oEgzxBXCkceNVD 21 | qUrR2yE4FYBD4nqPzVsZR5kCgYEA1yTi7NkQeldIpZ6Z43T18753A/Xx4JsLyWqd 22 | uAT3mV9x7V1Yqg++qGbLtZjQoPRFt85N6ZxMsqA5b0iK3mXq1auJDdx1rAlT9z6q 23 | 9JM/YNAkbZsvEVq9vIYxw31w98T1GYhpzBM+yDhzir+9tv5YhQKa1dXDWi1JhWwz 24 | YN45pWkCgYEAxuVsJ4D4Th5o050ppWpnxM/WuMhIUKqaoFTVucMKFzn+g24y9pv5 25 | miYdNYIk4Y+4pzHG6ZGZSHJcQ9BLui6H/nLQnqkgCb2lT5nfp7/GKdus7BdcjPGs 26 | fcV46yL7/X0m8nDb3hkwwrDTU4mKFkMrzKpjdZBsttEmW0Aw/3y36gU= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/fixtures/https-certificate/server-symlink.crt: -------------------------------------------------------------------------------- 1 | server.crt -------------------------------------------------------------------------------- /tests/fixtures/https-certificate/server-symlink.key: -------------------------------------------------------------------------------- 1 | server.key -------------------------------------------------------------------------------- /tests/fixtures/https-certificate/server-symlink.pfx: -------------------------------------------------------------------------------- 1 | server.pfx -------------------------------------------------------------------------------- /tests/fixtures/https-certificate/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJALz8gD/gAt0OMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTgxMDIzMTgyMTQ5WhcNMTkxMDIzMTgyMTQ5WjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEAxAUVLFM+K3XDLQkBi7xt0s1Ip7JoHYDskzUDQNHjjMkUq5kvC/hf5Ei1 8 | J6qruJs3Xqg86Nl4+ed4ynUajAkRRibhp0P1SG1tgPssIK6iC7g8heYuDy9WkFuM 9 | ie0513zjSn6bMEAK5TegxYAWCbaCZX/Fw9bDniabL/zuOv4sf8J4EPhsEENnH6sU 10 | E9HxPUgQmNt1Tbd0j1Cd5PXrSTLyfVPRh0m9QhXTUHuxsse8XSn9U2swduxJTWRI 11 | NmhffYn+O7kbJGI77xYr8u58Rsf3HCMI8DTKZNvQLChvvtLJ9ckyu7Q+T8emgklS 12 | tASm3V2UtnriaK/IQEhgSdxqVRib3QIDAQABo1AwTjAdBgNVHQ4EFgQUDZBhVKdb 13 | 3BRhLIhuuE522Vsul0IwHwYDVR0jBBgwFoAUDZBhVKdb3BRhLIhuuE522Vsul0Iw 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEABh9WWZwWLgb9/DcTxL72 15 | 6pI96t4jiF79Q+pPefkaIIi0mE6yodWrTAsBQu9I6bNRaEcCSoiXkP2bqskD/UGg 16 | LwUFgSrDOAA3UjdHw3QU5g2NocduG7mcFwA40TB98sOsxsUyYlzSyWzoiQWwPYwb 17 | hek1djuWkqPXsTjlj54PTPN/SjTFmo4p5Ip6nbRf2nOREl7v0rJpGbJvXiCMYyd+ 18 | Zv+j4mRjCGo8ysMR2HjCUGkYReLAgKyyz3M7i8vevJhKslyOmy6Txn4F0nPVumaU 19 | DDIy4xXPW1STWfsmSYJfYW3wa0wk+pJQ3j2cTzkPQQ8gwpvM3U9DJl43uwb37v6I 20 | 7Q== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /tests/fixtures/https-certificate/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDEBRUsUz4rdcMt 3 | CQGLvG3SzUinsmgdgOyTNQNA0eOMyRSrmS8L+F/kSLUnqqu4mzdeqDzo2Xj553jK 4 | dRqMCRFGJuGnQ/VIbW2A+ywgrqILuDyF5i4PL1aQW4yJ7TnXfONKfpswQArlN6DF 5 | gBYJtoJlf8XD1sOeJpsv/O46/ix/wngQ+GwQQ2cfqxQT0fE9SBCY23VNt3SPUJ3k 6 | 9etJMvJ9U9GHSb1CFdNQe7Gyx7xdKf1TazB27ElNZEg2aF99if47uRskYjvvFivy 7 | 7nxGx/ccIwjwNMpk29AsKG++0sn1yTK7tD5Px6aCSVK0BKbdXZS2euJor8hASGBJ 8 | 3GpVGJvdAgMBAAECggEAapYo8TVCdPdP7ckb4hPP0/R0MVu9aW2VNmZ5ImH+zar5 9 | ZmWhQ20HF2bBupP/VB5yeTIaDLNUKO9Iqy4KBWNY1UCHKyC023FFPgFV+V98FctU 10 | faqwGOmwtEZToRwxe48ZOISndhEc247oCPyg/x8SwIY9z0OUkwaDFBEAqWtUXxM3 11 | /SPpCT5ilLgxnRgVB8Fj5Z0q7ThnxNVOmVC1OSIakEj46PzmMXn1pCKLOCUmAAOQ 12 | BnrOZuty2b8b2M/GHsktLZwojQQJmArnIBymTXQTVhaGgKSyOv1qvHLp9L1OJf0/ 13 | Xm+/TqT6ztzhzlftcObdfQZZ5JuoEwlvyrsGFlA3MQKBgQDiQC3KYMG8ViJkWrv6 14 | XNAFEoAjVEKrtirGWJ66YfQ9KSJ7Zttrd1Y1V1OLtq3z4YMH39wdQ8rOD+yR8mWV 15 | 6Tnsxma6yJXAH8uan8iVbxjIZKF1hnvNCxUoxYmWOmTLcEQMzmxvTzAiR+s6R6Uj 16 | 9LgGqppt30nM4wnOhOJU6UxqbwKBgQDdy03KidbPZuycJSy1C9AIt0jlrxDsYm+U 17 | fZrB6mHEZcgoZS5GbLKinQCdGcgERa05BXvJmNbfZtT5a37YEnbjsTImIhDiBP5P 18 | nW36/9a3Vg1svd1KP2206/Bh3gfZbgTsQg4YogXgjf0Uzuvw18btgTtLVpVyeuqz 19 | TU3eeF30cwKBgQCN6lvOmapsDEs+T3uhqx4AUH53qp63PmjOSUAnANJGmsq6ROZV 20 | HmHAy6nn9Qpf85BRHCXhZWiMoIhvc3As/EINNtWxS6hC/q6jqp4SvcD50cVFBroY 21 | /16iWGXZCX+37A+DSOfTWgSDPEFcKRx41UOpStHbITgVgEPieo/NWxlHmQKBgQDX 22 | JOLs2RB6V0ilnpnjdPXzvncD9fHgmwvJap24BPeZX3HtXViqD76oZsu1mNCg9EW3 23 | zk3pnEyyoDlvSIreZerVq4kN3HWsCVP3Pqr0kz9g0CRtmy8RWr28hjHDfXD3xPUZ 24 | iGnMEz7IOHOKv722/liFAprV1cNaLUmFbDNg3jmlaQKBgQDG5WwngPhOHmjTnSml 25 | amfEz9a4yEhQqpqgVNW5wwoXOf6DbjL2m/maJh01giThj7inMcbpkZlIclxD0Eu6 26 | Lof+ctCeqSAJvaVPmd+nv8Yp26zsF1yM8ax9xXjrIvv9fSbycNveGTDCsNNTiYoW 27 | QyvMqmN1kGy20SZbQDD/fLfqBQ== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /tests/fixtures/https-certificate/server.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/fixtures/https-certificate/server.pfx -------------------------------------------------------------------------------- /tests/fixtures/lazy-compilation-multiple-entries/one.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("One."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/lazy-compilation-multiple-entries/two.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Two."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/lazy-compilation-multiple-entries/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const oneHTMLContent = ` 4 | 5 | 6 | 7 | 8 | test 9 | 10 | 11 | 12 | 13 | `; 14 | const twoHTMLContent = ` 15 | 16 | 17 | 18 | 19 | test 20 | 21 | 22 | 23 | 24 | `; 25 | 26 | module.exports = { 27 | devtool: false, 28 | mode: "development", 29 | context: __dirname, 30 | stats: "none", 31 | entry: { 32 | one: "./one.js", 33 | two: "./two.js" 34 | }, 35 | output: { 36 | path: "/" 37 | }, 38 | experiments: { 39 | lazyCompilation: true 40 | }, 41 | infrastructureLogging: { 42 | level: "info", 43 | stream: { 44 | write: () => {} 45 | } 46 | }, 47 | plugins: [ 48 | { 49 | apply(compiler) { 50 | const pluginName = "html-generator-plugin-test"; 51 | const oneFilename = "test-one.html"; 52 | const twoFilename = "test-two.html"; 53 | 54 | compiler.hooks.thisCompilation.tap(pluginName, compilation => { 55 | const { RawSource } = compiler.webpack.sources; 56 | 57 | compilation.hooks.processAssets.tap( 58 | { 59 | name: pluginName, 60 | stage: 61 | compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL 62 | }, 63 | () => { 64 | const oneSource = new RawSource(oneHTMLContent); 65 | 66 | compilation.emitAsset(oneFilename, oneSource); 67 | 68 | const twoSource = new RawSource(twoHTMLContent); 69 | 70 | compilation.emitAsset(twoFilename, twoSource); 71 | } 72 | ); 73 | }); 74 | } 75 | } 76 | ] 77 | }; 78 | -------------------------------------------------------------------------------- /tests/fixtures/lazy-compilation-single-entry/entry.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/lazy-compilation-single-entry/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLContent = ` 4 | 5 | 6 | 7 | 8 | test 9 | 10 | 11 | 12 | 13 | `; 14 | 15 | module.exports = { 16 | devtool: false, 17 | mode: "development", 18 | context: __dirname, 19 | stats: "none", 20 | entry: "./entry.js", 21 | output: { 22 | path: "/" 23 | }, 24 | experiments: { 25 | lazyCompilation: true 26 | }, 27 | infrastructureLogging: { 28 | level: "info", 29 | stream: { 30 | write: () => {} 31 | } 32 | }, 33 | plugins: [ 34 | { 35 | apply(compiler) { 36 | const pluginName = "html-generator-plugin-test"; 37 | const filename = "test.html"; 38 | 39 | compiler.hooks.thisCompilation.tap(pluginName, compilation => { 40 | const { RawSource } = compiler.webpack.sources; 41 | 42 | compilation.hooks.processAssets.tap( 43 | { 44 | name: pluginName, 45 | stage: 46 | compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL 47 | }, 48 | () => { 49 | const source = new RawSource(HTMLContent); 50 | 51 | compilation.emitAsset(filename, source); 52 | } 53 | ); 54 | }); 55 | } 56 | } 57 | ] 58 | }; 59 | -------------------------------------------------------------------------------- /tests/fixtures/mime-types-config/file.custom: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /tests/fixtures/mime-types-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("./file.custom"); 4 | 5 | console.log("Hey."); 6 | -------------------------------------------------------------------------------- /tests/fixtures/mime-types-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const moduleRuleForCustom = { 4 | test: /\.custom$/, 5 | type: "asset/resource", 6 | generator: { 7 | filename: "[name][ext]" 8 | } 9 | }; 10 | 11 | module.exports = { 12 | mode: "development", 13 | context: __dirname, 14 | stats: "none", 15 | entry: "./foo.js", 16 | output: { 17 | path: "/" 18 | }, 19 | node: false, 20 | infrastructureLogging: { 21 | level: "warn" 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | ...moduleRuleForCustom 27 | } 28 | ] 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /tests/fixtures/module-federation-config/entry1.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "entry1"; 4 | -------------------------------------------------------------------------------- /tests/fixtures/module-federation-config/entry2.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = "entry2"; 4 | -------------------------------------------------------------------------------- /tests/fixtures/module-federation-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | mode: "development", 5 | target: "node", 6 | stats: "none", 7 | context: __dirname, 8 | entry: ["./entry1.js", "./entry2.js"], 9 | output: { 10 | path: "/", 11 | libraryTarget: "umd" 12 | }, 13 | infrastructureLogging: { 14 | level: "warn" 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /tests/fixtures/module-federation-config/webpack.multi.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = [ 4 | { 5 | mode: "development", 6 | target: "node", 7 | context: __dirname, 8 | stats: "none", 9 | entry: ["./entry1.js", "./entry2.js"], 10 | output: { 11 | path: "/", 12 | libraryTarget: "umd" 13 | }, 14 | infrastructureLogging: { 15 | level: "warn" 16 | } 17 | } 18 | ]; 19 | -------------------------------------------------------------------------------- /tests/fixtures/module-federation-config/webpack.object-entry.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | mode: "development", 5 | target: "node", 6 | stats: "none", 7 | context: __dirname, 8 | entry: { 9 | foo: "./entry1.js", 10 | main: ["./entry1.js", "./entry2.js"] 11 | }, 12 | output: { 13 | path: "/", 14 | libraryTarget: "umd" 15 | }, 16 | infrastructureLogging: { 17 | level: "warn" 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /tests/fixtures/module-federation-config/webpack.plugin.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ModuleFederationPlugin = 4 | require("@rspack/core").container.ModuleFederationPlugin; 5 | 6 | module.exports = { 7 | mode: "development", 8 | target: "node", 9 | stats: "none", 10 | context: __dirname, 11 | entry: ["./entry1.js"], 12 | plugins: [ 13 | new ModuleFederationPlugin({ 14 | name: "app1", 15 | library: { type: "var", name: "app1" }, 16 | filename: "remoteEntry.js", 17 | exposes: { 18 | "./entry1": "./entry1" 19 | } 20 | }) 21 | ], 22 | infrastructureLogging: { 23 | level: "warn" 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /tests/fixtures/multi-compiler-one-configuration/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/multi-compiler-one-configuration/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = [ 6 | { 7 | target: "web", 8 | mode: "development", 9 | context: __dirname, 10 | stats: "none", 11 | entry: "./foo.js", 12 | output: { 13 | path: "/" 14 | }, 15 | node: false, 16 | infrastructureLogging: { 17 | level: "info", 18 | stream: { 19 | write: () => {} 20 | } 21 | }, 22 | plugins: [new HTMLGeneratorPlugin()] 23 | } 24 | ]; 25 | -------------------------------------------------------------------------------- /tests/fixtures/multi-compiler-two-configurations/one.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("one"); 4 | // comment 5 | // comment -------------------------------------------------------------------------------- /tests/fixtures/multi-compiler-two-configurations/two.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("two"); 4 | -------------------------------------------------------------------------------- /tests/fixtures/multi-compiler-two-configurations/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = [ 6 | { 7 | target: "web", 8 | name: "one", 9 | mode: "development", 10 | context: __dirname, 11 | entry: "./one.js", 12 | stats: "none", 13 | output: { 14 | path: "/", 15 | filename: "one-[name].js" 16 | }, 17 | plugins: [new HTMLGeneratorPlugin()], 18 | infrastructureLogging: { 19 | level: "info", 20 | stream: { 21 | write: () => {} 22 | } 23 | } 24 | }, 25 | { 26 | target: "web", 27 | name: "two", 28 | mode: "development", 29 | context: __dirname, 30 | entry: "./two.js", 31 | stats: "none", 32 | output: { 33 | path: "/", 34 | filename: "two-[name].js" 35 | }, 36 | plugins: [new HTMLGeneratorPlugin()], 37 | infrastructureLogging: { 38 | level: "info", 39 | stream: { 40 | write: () => {} 41 | } 42 | } 43 | } 44 | ]; 45 | -------------------------------------------------------------------------------- /tests/fixtures/multi-public-path-config/bar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/multi-public-path-config/baz.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/multi-public-path-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("./test.html"); 4 | -------------------------------------------------------------------------------- /tests/fixtures/multi-public-path-config/test.html: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /tests/fixtures/multi-public-path-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require("path"); 4 | 5 | const moduleRuleForHTML = { 6 | test: /\.html$/, 7 | type: "asset/resource", 8 | generator: { 9 | filename: "path/to/file.html" 10 | } 11 | }; 12 | 13 | module.exports = [ 14 | { 15 | mode: "development", 16 | context: __dirname, 17 | stats: "none", 18 | entry: "./foo.js", 19 | output: { 20 | path: __dirname, 21 | filename: "foo.js", 22 | publicPath: "/bundle1/" 23 | }, 24 | infrastructureLogging: { 25 | level: "warn" 26 | }, 27 | module: { 28 | rules: [ 29 | { 30 | ...moduleRuleForHTML 31 | } 32 | ] 33 | } 34 | }, 35 | { 36 | mode: "development", 37 | context: __dirname, 38 | stats: "none", 39 | entry: "./bar.js", 40 | output: { 41 | path: path.join(__dirname, "named"), 42 | filename: "bar.js", 43 | publicPath: "/bundle2/" 44 | }, 45 | name: "named", 46 | infrastructureLogging: { 47 | level: "warn" 48 | } 49 | }, 50 | { 51 | mode: "development", 52 | context: __dirname, 53 | entry: "./bar.js", 54 | output: { 55 | path: path.join(__dirname, "dist"), 56 | filename: "bar.js", 57 | publicPath: "auto" 58 | }, 59 | name: "other", 60 | stats: false 61 | } 62 | ]; 63 | -------------------------------------------------------------------------------- /tests/fixtures/overlay-config/foo.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/fixtures/overlay-config/foo.js -------------------------------------------------------------------------------- /tests/fixtures/overlay-config/trusted-types.webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/trusted-types-html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | stats: "none", 9 | entry: "./foo.js", 10 | output: { 11 | path: "/", 12 | trustedTypes: { policyName: "webpack" } 13 | }, 14 | infrastructureLogging: { 15 | level: "info", 16 | stream: { 17 | write: () => {} 18 | } 19 | }, 20 | plugins: [new HTMLGeneratorPlugin()] 21 | }; 22 | -------------------------------------------------------------------------------- /tests/fixtures/overlay-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | stats: "none", 9 | entry: "./foo.js", 10 | output: { 11 | path: "/" 12 | }, 13 | infrastructureLogging: { 14 | level: "info", 15 | stream: { 16 | write: () => {} 17 | } 18 | }, 19 | plugins: [new HTMLGeneratorPlugin()] 20 | }; 21 | -------------------------------------------------------------------------------- /tests/fixtures/provide-plugin-custom/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // 'npm run prepare' must be run for this to work during testing 4 | const CustomClient = require("../custom-client/CustomSockJSClient"); 5 | 6 | window.expectedClient = CustomClient; 7 | // eslint-disable-next-line camelcase, no-undef 8 | window.injectedClient = __webpack_dev_server_client__; 9 | -------------------------------------------------------------------------------- /tests/fixtures/provide-plugin-custom/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | /** 6 | * @type {import('@rspack/core').RspackOptions} 7 | */ 8 | const config = { 9 | mode: "development", 10 | context: __dirname, 11 | stats: "none", 12 | entry: "./foo.js", 13 | output: { 14 | path: "/" 15 | }, 16 | node: false, 17 | infrastructureLogging: { 18 | level: "info", 19 | stream: { 20 | write: () => {} 21 | } 22 | }, 23 | plugins: [new HTMLGeneratorPlugin()] 24 | }; 25 | 26 | module.exports = config; 27 | -------------------------------------------------------------------------------- /tests/fixtures/provide-plugin-default/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // 'npm run prepare' must be run for this to work during testing 4 | const WebsocketClient = 5 | require("webpack-dev-server/client/clients/WebSocketClient").default; 6 | 7 | window.expectedClient = WebsocketClient; 8 | // eslint-disable-next-line camelcase, no-undef 9 | window.injectedClient = __webpack_dev_server_client__.default; 10 | -------------------------------------------------------------------------------- /tests/fixtures/provide-plugin-default/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | stats: "none", 9 | entry: "./foo.js", 10 | output: { 11 | path: "/" 12 | }, 13 | node: false, 14 | infrastructureLogging: { 15 | level: "info", 16 | stream: { 17 | write: () => {} 18 | } 19 | }, 20 | plugins: [new HTMLGeneratorPlugin()] 21 | }; 22 | -------------------------------------------------------------------------------- /tests/fixtures/provide-plugin-sockjs-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // 'npm run prepare' must be run for this to work during testing 4 | const SockJSClient = 5 | require("webpack-dev-server/client/clients/SockJSClient").default; 6 | 7 | window.expectedClient = SockJSClient; 8 | // eslint-disable-next-line camelcase, no-undef 9 | window.injectedClient = __webpack_dev_server_client__.default; 10 | -------------------------------------------------------------------------------- /tests/fixtures/provide-plugin-sockjs-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | stats: "none", 9 | entry: "./foo.js", 10 | output: { 11 | path: "/" 12 | }, 13 | node: false, 14 | infrastructureLogging: { 15 | level: "info", 16 | stream: { 17 | write: () => {} 18 | } 19 | }, 20 | plugins: [new HTMLGeneratorPlugin()] 21 | }; 22 | -------------------------------------------------------------------------------- /tests/fixtures/provide-plugin-ws-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // 'npm run prepare' must be run for this to work during testing 4 | const WebsocketClient = 5 | require("webpack-dev-server/client/clients/WebSocketClient").default; 6 | 7 | window.expectedClient = WebsocketClient; 8 | // eslint-disable-next-line camelcase, no-undef 9 | window.injectedClient = __webpack_dev_server_client__.default; 10 | -------------------------------------------------------------------------------- /tests/fixtures/provide-plugin-ws-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | stats: "none", 9 | entry: "./foo.js", 10 | output: { 11 | path: "/" 12 | }, 13 | node: false, 14 | infrastructureLogging: { 15 | level: "info", 16 | stream: { 17 | write: () => {} 18 | } 19 | }, 20 | plugins: [new HTMLGeneratorPlugin()] 21 | }; 22 | -------------------------------------------------------------------------------- /tests/fixtures/reload-config-2/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // eslint-disable-next-line import/no-unresolved 4 | require("./main.css"); 5 | -------------------------------------------------------------------------------- /tests/fixtures/reload-config-2/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | stats: "none", 9 | entry: "./foo.js", 10 | output: { 11 | path: "/" 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.css$/, 17 | use: [{ loader: "style-loader" }, { loader: "css-loader" }] 18 | } 19 | ] 20 | }, 21 | node: false, 22 | infrastructureLogging: { 23 | level: "info", 24 | stream: { 25 | write: () => {} 26 | } 27 | }, 28 | plugins: [new HTMLGeneratorPlugin()] 29 | }; 30 | -------------------------------------------------------------------------------- /tests/fixtures/reload-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("./main.css"); 4 | -------------------------------------------------------------------------------- /tests/fixtures/reload-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | entry: "./foo.js", 9 | stats: "none", 10 | output: { 11 | path: "/" 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.css$/, 17 | use: [{ loader: "style-loader" }, { loader: "css-loader" }] 18 | } 19 | ] 20 | }, 21 | infrastructureLogging: { 22 | level: "info", 23 | stream: { 24 | write: () => {} 25 | } 26 | }, 27 | plugins: [new HTMLGeneratorPlugin()] 28 | }; 29 | -------------------------------------------------------------------------------- /tests/fixtures/simple-config-other/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log( 4 | "Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Very Long Line." 5 | ); 6 | -------------------------------------------------------------------------------- /tests/fixtures/simple-config-other/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | mode: "development", 5 | context: __dirname, 6 | stats: "none", 7 | entry: "./foo.js", 8 | output: { 9 | path: "/" 10 | }, 11 | node: false, 12 | infrastructureLogging: { 13 | level: "warn" 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /tests/fixtures/simple-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/simple-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | stats: "none", 9 | entry: "./foo.js", 10 | output: { 11 | path: "/" 12 | }, 13 | node: false, 14 | infrastructureLogging: { 15 | level: "info", 16 | stream: { 17 | write: () => {} 18 | } 19 | }, 20 | plugins: [new HTMLGeneratorPlugin()] 21 | }; 22 | -------------------------------------------------------------------------------- /tests/fixtures/ssl/localhost-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDCTCCAfGgAwIBAgIUevWiuCfenWuq9KyC8aQ/tc1Io14wDQYJKoZIhvcNAQEL 3 | BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDQyNDE2MDYyMloXDTI0MDUy 4 | NDE2MDYyMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF 5 | AAOCAQ8AMIIBCgKCAQEA1v/lb9u9WkqkF7zjIKe2R+b4S0sQnWIfBFZ0ggtaOL0a 6 | ntud/EuaGQgLtJgSwO2M2xIqKx+yoLhoM+273EJe0KmfJMxYNAkhwP9h6vrKnaQJ 7 | mpAhoalfEGyCrnHHMKISAAn4Rlc8NXnULoFhHzNm8bdqvP33rCmsJ+tNYC5kwzyt 8 | HvRNFyg9BOUfACiPW17opFH0rao3IfZrQ6yRbknef1pX1x2pbDAH14rCT/vXaTs6 9 | VGuqLE/wRsSt+7nMHy/PmXxMyb4G4/UflYtnKfmXpDRw+TDEGzvTZedtoOz+rrJC 10 | e989R9qYGrlPfyfZbI+O348FV66I+jcD+/EUQs+HkwIDAQABo1MwUTAdBgNVHQ4E 11 | FgQU6bk4LSwtVQEt7V/ev+Zj270zdAkwHwYDVR0jBBgwFoAU6bk4LSwtVQEt7V/e 12 | v+Zj270zdAkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAUBgo 13 | E3CZrrc/MaadFg1meNk+eKACmTsIa5cT6zi7MsvoKakXEd4bGd+iLifUzlAa1ygj 14 | dQppfprb5t68I7oO9/lkh2DfKrXxW/RpdhB05KslUd8q/3XY5kyao5quzeiVoMHR 15 | u+XYjoy2mTwdUC2uzFy6rkHsAkJy2vJJoDdlNsrKn6AZmh+voHHKrAtOL4gnanQV 16 | wR1u8eBVfk2MKIl2pNSCA4bD16uZyp3+oqq097BEoVa1pR+l8nwbsh/YfALifq/d 17 | P3yiN5+EqgiOIF9b8PZORe+Ry1O7uvPnU2ZRkVWPJ1S17Ms0lnr7IY3qjSBTuK66 18 | 5uYi7ojrb5Vf0UL5oQ== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /tests/fixtures/ssl/localhost-privkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQDW/+Vv271aSqQX 3 | vOMgp7ZH5vhLSxCdYh8EVnSCC1o4vRqe2538S5oZCAu0mBLA7YzbEiorH7KguGgz 4 | 7bvcQl7QqZ8kzFg0CSHA/2Hq+sqdpAmakCGhqV8QbIKucccwohIACfhGVzw1edQu 5 | gWEfM2bxt2q8/fesKawn601gLmTDPK0e9E0XKD0E5R8AKI9bXuikUfStqjch9mtD 6 | rJFuSd5/WlfXHalsMAfXisJP+9dpOzpUa6osT/BGxK37ucwfL8+ZfEzJvgbj9R+V 7 | i2cp+ZekNHD5MMQbO9Nl522g7P6uskJ73z1H2pgauU9/J9lsj47fjwVXroj6NwP7 8 | 8RRCz4eTAgMBAAECggEAA+zbFv43iEj5kvdfXC7DrK9iVBmUPZNXhqA/c0paxNNr 9 | A4B182+76f4UHKF0IjKUEkHUJEJpY/bJ7DzIY76QdZXLMoRKjfSmuZvQAVa/0T33 10 | 8Or1ujpZ4nZgsmegX9ptorOL5VjdYAqP3aN+DvBEzl/vYnDujyWZn4bzvDBMpaXS 11 | 39qW1MkcZ8UiP1fRad76+S57WnieBV+NRHYEAiDdMFKXLuw/igX/xOSZgq5Jh3I2 12 | hLS49S41dN1P9l9H2bPMw0CthNvMPPaemwKHz+84hSS+P4VJOWJzlGnXEdIFuqBR 13 | GFBESQzcemfS9DDB22Yt06YujBCbwTVVAxj73lnKkQKBgQDvYXK36J9y/NphDAWi 14 | Cwti5oE3eSfV0YazQwm+rRwC64wbpBFAm9ujwjUmaYBg75lBLF5nOOe8s1n95g5I 15 | tLfFb+zuZh8NNLjhfNE9/SNmRnnMvbcaDHeIE2RMAz+PuLN/gFLmsVIwK2X1LRC2 16 | 0vHjw9Yzh6JLiOajAchzhZiCEQKBgQDl7R6Wfggo8myETA8Uv5tWot3IcquRkEl/ 17 | TRCyao2/79rAGexS7piwD7FPdSDOk1zfZFYUOMzyMjj60sGcPRPqRX6D0usEODLQ 18 | TwsTJSCNgPnIOkqKkccwtqlTimbRIrPUSQfFPj56RzKKWdrJ/P3LPRjzkK7i3vLV 19 | EGlAENaLYwKBgHKSOnzpWr+HY+IFBgErthRs7LWnSDifYxATauuXIQwIvvNP0G4S 20 | 6snzHss2vZonszstSDWxV8DKOq052eZUkIxv6H+l4wDIFiDeQ6uep73Ax3UF7EgM 21 | ZX18gombGGXqagcBXSxK/GJPsynomtJWHi38Ql5BcZ0jdffY157q9zZxAoGAPZtD 22 | Tt+GIDKUkP4wLEcKwDPzaPoQrngSuWFUz/ls8bi6zC4l/DKiBsqtn7Sqja8+ezzP 23 | M6vkfiCm084UwmA7LdJhC8E/52mHc/k55m9UQZYFV3kG8AoPbSYESLYUxoSd2ouW 24 | 4WrEIs9g42EgFm8LMaG1Rc3GjlNejWhQSzI3yjECf3v7VoAcUwVfuVkwbm9W24vR 25 | neFTF8QBl//fxIdxZwoj5SrSgMOjmZ3pXA/ZbFJ0pB4Rh5dmKTYqdpfXsOTiBuwB 26 | XlqPVpN8UZEl3edpufLDyPldNej/9kEAkK5FS3YVyIQEg75739bCTlfzzCX1HdMx 27 | q98XYm/n5LWYFezsAt0= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /tests/fixtures/static-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/static-config/other/foo.html: -------------------------------------------------------------------------------- 1 | Foo! 2 | -------------------------------------------------------------------------------- /tests/fixtures/static-config/public/assets/example.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/fixtures/static-config/public/assets/example.txt -------------------------------------------------------------------------------- /tests/fixtures/static-config/public/assets/other.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/fixtures/static-config/public/assets/other.txt -------------------------------------------------------------------------------- /tests/fixtures/static-config/public/bar/index.html: -------------------------------------------------------------------------------- 1 | Heyo 2 | -------------------------------------------------------------------------------- /tests/fixtures/static-config/public/foo.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/fixtures/static-config/public/foo.wasm -------------------------------------------------------------------------------- /tests/fixtures/static-config/public/index.html: -------------------------------------------------------------------------------- 1 | Heyo. 2 | -------------------------------------------------------------------------------- /tests/fixtures/static-config/public/node_modules/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/fixtures/static-config/public/node_modules/.gitkeep -------------------------------------------------------------------------------- /tests/fixtures/static-config/public/node_modules/index.html: -------------------------------------------------------------------------------- 1 | bar -------------------------------------------------------------------------------- /tests/fixtures/static-config/public/other.html: -------------------------------------------------------------------------------- 1 | Other html 2 | -------------------------------------------------------------------------------- /tests/fixtures/static-config/static/index.html: -------------------------------------------------------------------------------- 1 | Heyo. 2 | -------------------------------------------------------------------------------- /tests/fixtures/static-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | mode: "development", 5 | context: __dirname, 6 | stats: "none", 7 | entry: "./foo.js", 8 | output: { 9 | publicPath: "/" 10 | }, 11 | infrastructureLogging: { 12 | level: "warn" 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /tests/fixtures/universal-compiler-config/browser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hello from the browser"); 4 | -------------------------------------------------------------------------------- /tests/fixtures/universal-compiler-config/server.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hello from the server"); 4 | -------------------------------------------------------------------------------- /tests/fixtures/universal-compiler-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = [ 6 | { 7 | name: "browser", 8 | mode: "development", 9 | context: __dirname, 10 | stats: "none", 11 | entry: "./browser.js", 12 | output: { 13 | path: "/", 14 | filename: "browser.js" 15 | }, 16 | plugins: [new HTMLGeneratorPlugin()], 17 | infrastructureLogging: { 18 | level: "info", 19 | stream: { 20 | write: () => {} 21 | } 22 | } 23 | }, 24 | { 25 | name: "server", 26 | mode: "development", 27 | context: __dirname, 28 | target: "node", 29 | stats: "none", 30 | entry: "./server.js", 31 | output: { 32 | path: "/", 33 | filename: "server.js" 34 | }, 35 | plugins: [new HTMLGeneratorPlugin()], 36 | infrastructureLogging: { 37 | level: "info", 38 | stream: { 39 | write: () => {} 40 | } 41 | } 42 | } 43 | ]; 44 | -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/foo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | console.log("Hey."); 4 | -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/other/foo.html: -------------------------------------------------------------------------------- 1 | Foo! 2 | -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/public/assets/example.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/fixtures/watch-files-config/public/assets/example.txt -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/public/assets/non-exist.txt: -------------------------------------------------------------------------------- 1 | Kurosaki Ichigo -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/public/assets/other.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/fixtures/watch-files-config/public/assets/other.txt -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/public/bar/index.html: -------------------------------------------------------------------------------- 1 | Heyo 2 | -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/public/other.html: -------------------------------------------------------------------------------- 1 | Other html 2 | -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/static/index.html: -------------------------------------------------------------------------------- 1 | Heyo. 2 | -------------------------------------------------------------------------------- /tests/fixtures/watch-files-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | stats: "none", 9 | entry: "./foo.js", 10 | output: { 11 | publicPath: "/" 12 | }, 13 | infrastructureLogging: { 14 | level: "warn" 15 | }, 16 | plugins: [new HTMLGeneratorPlugin()] 17 | }; 18 | -------------------------------------------------------------------------------- /tests/fixtures/worker-config-dev-server-false/index.js: -------------------------------------------------------------------------------- 1 | const myWorker = new Worker("./worker-bundle.js"); 2 | 3 | myWorker.onmessage = (event) => { 4 | console.log(`Worker said: ${event.data}`); 5 | }; 6 | 7 | myWorker.postMessage("message"); 8 | -------------------------------------------------------------------------------- /tests/fixtures/worker-config-dev-server-false/public/worker-bundle.js: -------------------------------------------------------------------------------- 1 | (() => { // webpackBootstrap 2 | var __webpack_modules__ = ({}); 3 | /************************************************************************/ 4 | // The module cache 5 | var __webpack_module_cache__ = {}; 6 | 7 | // The require function 8 | function __webpack_require__(moduleId) { 9 | 10 | // Check if module is in cache 11 | var cachedModule = __webpack_module_cache__[moduleId]; 12 | if (cachedModule !== undefined) { 13 | return cachedModule.exports; 14 | } 15 | // Create a new module (and put it into the cache) 16 | var module = (__webpack_module_cache__[moduleId] = { 17 | exports: {} 18 | }); 19 | // Execute the module function 20 | __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 21 | 22 | // Return the exports of the module 23 | return module.exports; 24 | 25 | } 26 | 27 | /************************************************************************/ 28 | // webpack/runtime/rspack_version 29 | (() => { 30 | __webpack_require__.rv = () => ("1.3.11") 31 | })(); 32 | // webpack/runtime/rspack_unique_id 33 | (() => { 34 | __webpack_require__.ruid = "bundler=rspack@1.3.11"; 35 | 36 | })(); 37 | /************************************************************************/ 38 | 39 | /*!*******************!*\ 40 | !*** ./worker.js ***! 41 | \*******************/ 42 | postMessage("I'm working before postMessage"); 43 | 44 | onmessage = (event) => { 45 | postMessage(`Message sent: ${event.data}`); 46 | }; 47 | 48 | })() 49 | ; -------------------------------------------------------------------------------- /tests/fixtures/worker-config-dev-server-false/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 3 | 4 | module.exports = [ 5 | { 6 | name: "app", 7 | // dependencies: ["worker"], 8 | devtool: false, 9 | target: "web", 10 | entry: "./index.js", 11 | mode: "development", 12 | context: __dirname, 13 | stats: "none", 14 | output: { 15 | path: path.resolve(__dirname, "./dist/"), 16 | }, 17 | infrastructureLogging: { 18 | level: "info", 19 | stream: { 20 | write: () => {}, 21 | }, 22 | }, 23 | plugins: [new HTMLGeneratorPlugin()], 24 | }, 25 | { 26 | name: "worker", 27 | devtool: false, 28 | target: "webworker", 29 | entry: "./worker.js", 30 | mode: "development", 31 | context: __dirname, 32 | stats: "none", 33 | output: { 34 | path: path.resolve(__dirname, "public"), 35 | filename: "worker-bundle.js", 36 | }, 37 | infrastructureLogging: { 38 | level: "info", 39 | stream: { 40 | write: () => {}, 41 | }, 42 | }, 43 | devServer: false, 44 | }, 45 | ]; 46 | -------------------------------------------------------------------------------- /tests/fixtures/worker-config-dev-server-false/worker.js: -------------------------------------------------------------------------------- 1 | postMessage("I'm working before postMessage"); 2 | 3 | onmessage = (event) => { 4 | postMessage(`Message sent: ${event.data}`); 5 | }; 6 | -------------------------------------------------------------------------------- /tests/fixtures/worker-config/index.js: -------------------------------------------------------------------------------- 1 | const myWorker = new Worker("./worker.js"); 2 | 3 | myWorker.onmessage = (event) => { 4 | console.log(`Worker said: ${event.data}`); 5 | }; 6 | 7 | myWorker.postMessage("message"); 8 | -------------------------------------------------------------------------------- /tests/fixtures/worker-config/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); 2 | 3 | module.exports = [ 4 | { 5 | name: "app", 6 | dependencies: ["worker"], 7 | devtool: false, 8 | target: "web", 9 | entry: "./index.js", 10 | mode: "development", 11 | context: __dirname, 12 | stats: "none", 13 | output: { 14 | path: "/", 15 | }, 16 | infrastructureLogging: { 17 | level: "info", 18 | stream: { 19 | write: () => {}, 20 | }, 21 | }, 22 | plugins: [new HTMLGeneratorPlugin()], 23 | }, 24 | { 25 | name: "worker", 26 | devtool: false, 27 | target: "webworker", 28 | entry: "./worker.js", 29 | mode: "development", 30 | context: __dirname, 31 | stats: "none", 32 | output: { 33 | path: "/", 34 | filename: "worker.js", 35 | }, 36 | infrastructureLogging: { 37 | level: "info", 38 | stream: { 39 | write: () => {}, 40 | }, 41 | }, 42 | }, 43 | ]; 44 | -------------------------------------------------------------------------------- /tests/fixtures/worker-config/worker.js: -------------------------------------------------------------------------------- 1 | postMessage("I'm working before postMessage"); 2 | 3 | onmessage = (event) => { 4 | postMessage(`Message sent: ${event.data}`); 5 | }; 6 | -------------------------------------------------------------------------------- /tests/helpers/conditional-test.js: -------------------------------------------------------------------------------- 1 | const isWindows = process.platform === "win32"; 2 | 3 | function skipTestOnWindows(reason) { 4 | if (isWindows) { 5 | test.skip(reason, () => {}); 6 | } 7 | return isWindows; 8 | } 9 | 10 | module.exports.skipTestOnWindows = skipTestOnWindows; 11 | -------------------------------------------------------------------------------- /tests/helpers/custom-http.js: -------------------------------------------------------------------------------- 1 | const customHTTP = require("node:http"); 2 | 3 | module.exports = customHTTP; 4 | -------------------------------------------------------------------------------- /tests/helpers/get-port.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on the packages get-port https://www.npmjs.com/package/get-port 3 | * and portfinder https://www.npmjs.com/package/portfinder 4 | * The code structure is similar to get-port, but it searches 5 | * ports deterministically like portfinder 6 | */ 7 | const net = require("node:net"); 8 | const os = require("node:os"); 9 | 10 | const minPort = 1024; 11 | const maxPort = 65_535; 12 | 13 | /** 14 | * @return {Set} 15 | */ 16 | const getLocalHosts = () => { 17 | const interfaces = os.networkInterfaces(); 18 | 19 | // Add undefined value for createServer function to use default host, 20 | // and default IPv4 host in case createServer defaults to IPv6. 21 | // eslint-disable-next-line no-undefined 22 | const results = new Set([undefined, "0.0.0.0"]); 23 | 24 | for (const _interface of Object.values(interfaces)) { 25 | if (_interface) { 26 | for (const config of _interface) { 27 | results.add(config.address); 28 | } 29 | } 30 | } 31 | 32 | return results; 33 | }; 34 | 35 | /** 36 | * @param {number} basePort 37 | * @param {string | undefined} host 38 | * @return {Promise} 39 | */ 40 | const checkAvailablePort = (basePort, host) => 41 | new Promise((resolve, reject) => { 42 | const server = net.createServer(); 43 | server.unref(); 44 | server.on("error", reject); 45 | 46 | server.listen(basePort, host, () => { 47 | // Next line should return AddressInfo because we're calling it after listen() and before close() 48 | const { port } = /** @type {import("net").AddressInfo} */ ( 49 | server.address() 50 | ); 51 | server.close(() => { 52 | resolve(port); 53 | }); 54 | }); 55 | }); 56 | 57 | /** 58 | * @param {number} port 59 | * @param {Set} hosts 60 | * @return {Promise} 61 | */ 62 | const getAvailablePort = async (port, hosts) => { 63 | /** 64 | * Errors that mean that host is not available. 65 | * @type {Set} 66 | */ 67 | const nonExistentInterfaceErrors = new Set(["EADDRNOTAVAIL", "EINVAL"]); 68 | /* Check if the post is available on every local host name */ 69 | for (const host of hosts) { 70 | try { 71 | await checkAvailablePort(port, host); // eslint-disable-line no-await-in-loop 72 | } catch (error) { 73 | /* We throw an error only if the interface exists */ 74 | if ( 75 | !nonExistentInterfaceErrors.has( 76 | /** @type {NodeJS.ErrnoException} */ (error).code, 77 | ) 78 | ) { 79 | throw error; 80 | } 81 | } 82 | } 83 | 84 | return port; 85 | }; 86 | 87 | /** 88 | * @param {number} basePort 89 | * @param {string=} host 90 | * @return {Promise} 91 | */ 92 | async function getPorts(basePort, host) { 93 | if (basePort < minPort || basePort > maxPort) { 94 | throw new Error(`Port number must lie between ${minPort} and ${maxPort}`); 95 | } 96 | 97 | let port = basePort; 98 | const localhosts = getLocalHosts(); 99 | let hosts; 100 | if (host && !localhosts.has(host)) { 101 | hosts = new Set([host]); 102 | } else { 103 | /* If the host is equivalent to localhost 104 | we need to check every equivalent host 105 | else the port might falsely appear as available 106 | on some operating systems */ 107 | hosts = localhosts; 108 | } 109 | /** @type {Set} */ 110 | const portUnavailableErrors = new Set(["EADDRINUSE", "EACCES"]); 111 | while (port <= maxPort) { 112 | try { 113 | const availablePort = await getAvailablePort(port, hosts); // eslint-disable-line no-await-in-loop 114 | return availablePort; 115 | } catch (error) { 116 | /* Try next port if port is busy; throw for any other error */ 117 | if ( 118 | !portUnavailableErrors.has( 119 | /** @type {NodeJS.ErrnoException} */ (error).code, 120 | ) 121 | ) { 122 | throw error; 123 | } 124 | port += 1; 125 | } 126 | } 127 | 128 | throw new Error("No available ports found"); 129 | } 130 | 131 | module.exports = getPorts; 132 | -------------------------------------------------------------------------------- /tests/helpers/global-setup-test.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-extraneous-dependencies 2 | const tcpPortUsed = require("tcp-port-used"); 3 | const { webpackVersion } = require("@rspack/core/package.json"); 4 | const ports = require("./ports-map"); 5 | 6 | // eslint-disable-next-line no-console 7 | console.log(`\n Running tests for webpack @${webpackVersion} \n`); 8 | 9 | async function validatePorts() { 10 | const samples = []; 11 | 12 | for (const key of Object.keys(ports)) { 13 | const value = ports[key]; 14 | const arr = Array.isArray(value) ? value : [value]; 15 | 16 | for (const port of arr) { 17 | const check = tcpPortUsed.check(port, "localhost").then((inUse) => { 18 | if (inUse) { 19 | throw new Error(`${port} has already used. [${key}]`); 20 | } 21 | }); 22 | 23 | samples.push(check); 24 | } 25 | } 26 | 27 | try { 28 | await Promise.all(samples); 29 | } catch (e) { 30 | // eslint-disable-next-line no-console 31 | console.error(e); 32 | process.exit(1); 33 | } 34 | } 35 | 36 | module.exports = validatePorts; 37 | -------------------------------------------------------------------------------- /tests/helpers/html-generator-plugin.js: -------------------------------------------------------------------------------- 1 | const HTMLContentForIndex = ` 2 | 3 | 4 | 5 | 6 | webpack-dev-server 7 | 8 | 9 |

webpack-dev-server is running...

10 | 11 | 12 | 13 | `; 14 | 15 | const HTMLContentForAssets = (assetName) => ` 16 | 17 | 18 | 19 | 20 | webpack-dev-server 21 | 22 | 23 |

(${assetName}>)webpack-dev-server is running...

24 | 25 | 26 | 27 | `; 28 | 29 | const HTMLContentForTest = ` 30 | 31 | 32 | 33 | 34 | test 35 | 36 | 37 |

Created via HTMLGeneratorPlugin

38 | 39 | 40 | `; 41 | 42 | module.exports = class HTMLGeneratorPlugin { 43 | // eslint-disable-next-line class-methods-use-this 44 | apply(compiler) { 45 | const pluginName = "html-generator-plugin"; 46 | 47 | compiler.hooks.thisCompilation.tap(pluginName, (compilation) => { 48 | const { RawSource } = compiler.webpack.sources; 49 | 50 | compilation.hooks.processAssets.tap( 51 | { 52 | name: pluginName, 53 | stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL, 54 | }, 55 | () => { 56 | const indexSource = new RawSource(HTMLContentForIndex); 57 | const testSource = new RawSource(HTMLContentForTest); 58 | const assets = compilation.getAssets(); 59 | 60 | compilation.emitAsset("index.html", indexSource); 61 | compilation.emitAsset("test.html", testSource); 62 | 63 | for (const asset of assets) { 64 | const assetName = asset.name; 65 | 66 | if (assetName !== "main.js" && assetName.endsWith(".js")) { 67 | const assetSource = new RawSource( 68 | HTMLContentForAssets(assetName), 69 | ); 70 | compilation.emitAsset( 71 | assetName.replace(".js", ".html"), 72 | assetSource, 73 | ); 74 | } 75 | } 76 | }, 77 | ); 78 | }); 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /tests/helpers/normalize-options.js: -------------------------------------------------------------------------------- 1 | function normalizeOptions(options) { 2 | const normalizedOptions = {}; 3 | 4 | // eslint-disable-next-line guard-for-in 5 | for (const propertyName in options) { 6 | let value = options[propertyName]; 7 | 8 | if (Array.isArray(value)) { 9 | value = value.map((item) => { 10 | if (Buffer.isBuffer(item)) { 11 | return ""; 12 | } 13 | if (typeof item.pem !== "undefined" && Buffer.isBuffer(item.pem)) { 14 | item.pem = ""; 15 | } else if ( 16 | typeof item.buf !== "undefined" && 17 | Buffer.isBuffer(item.buf) 18 | ) { 19 | item.buf = ""; 20 | } 21 | 22 | return item; 23 | }); 24 | } else if (Buffer.isBuffer(value)) { 25 | value = ""; 26 | } 27 | 28 | normalizedOptions[propertyName] = value; 29 | } 30 | 31 | return normalizedOptions; 32 | } 33 | 34 | module.exports = normalizeOptions; 35 | -------------------------------------------------------------------------------- /tests/helpers/normalize.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | 3 | const CURRENT_CWD = process.cwd(); 4 | const ROOT = path.resolve(__dirname, "../../"); 5 | 6 | const CSS_LOADER = path.dirname(require.resolve("css-loader")); 7 | const RELATIVE_CSS_LOADER = path.relative( 8 | path.dirname( 9 | path.resolve(__dirname, "../fixtures/reload-config/webpack.config"), 10 | ), 11 | CSS_LOADER, 12 | ); 13 | const RSPACK = path.dirname(require.resolve("@rspack/core/package.json")); 14 | 15 | const rspack = require("@rspack/core"); 16 | const RSPACK_MODULE = require.cache[require.resolve("@rspack/core")]; 17 | const TAPABLE = path.dirname( 18 | RSPACK_MODULE.require.resolve("@rspack/lite-tapable"), 19 | ); 20 | 21 | const normalize = (str) => { 22 | let normalizedStr = str.replace(/(\\)+/g, "/"); 23 | 24 | normalizedStr = normalizedStr 25 | .split(RSPACK.replace(/(\\)+/g, "/")) 26 | .join(""); 27 | 28 | normalizedStr = normalizedStr 29 | .split(CSS_LOADER.replace(/(\\)+/g, "/")) 30 | .join(""); 31 | 32 | normalizedStr = normalizedStr 33 | .split(RELATIVE_CSS_LOADER.replace(/(\\)+/g, "/")) 34 | .join(""); 35 | 36 | normalizedStr = normalizedStr 37 | .split(RELATIVE_CSS_LOADER.replace(/(\\)+/g, "/")) 38 | .join(""); 39 | 40 | normalizedStr = normalizedStr 41 | .split(TAPABLE.replace(/(\\)+/g, "/")) 42 | .join(""); 43 | 44 | normalizedStr = normalizedStr 45 | .split(ROOT.replace(/(\\)+/g, "/")) 46 | .join(""); 47 | 48 | normalizedStr = normalizedStr 49 | .split(CURRENT_CWD.replace(/(\\)+/g, "/")) 50 | .join(""); 51 | 52 | normalizedStr = normalizedStr.replace(/:\d+:\d+/g, "::"); 53 | normalizedStr = normalizedStr.replace( 54 | /@@ -\d+,\d+ \+\d+,\d+ @@/g, 55 | "@@ ... @@", 56 | ); 57 | return normalizedStr; 58 | }; 59 | 60 | expect.addSnapshotSerializer({ 61 | test(value) { 62 | return typeof value === "string"; 63 | }, 64 | print(received) { 65 | return normalize(received); 66 | }, 67 | }); 68 | -------------------------------------------------------------------------------- /tests/helpers/ports-map.js: -------------------------------------------------------------------------------- 1 | // important: new port mappings must be added to the bottom of this list 2 | const listOfTests = { 3 | // CLI tests 4 | "cli-basic": 1, 5 | "cli-port-option": 1, 6 | // e2e tests 7 | bundle: 1, 8 | "sockjs-client": 1, 9 | "web-socket-client": 1, 10 | "hot-and-live-reload": 1, 11 | logging: 1, 12 | overlay: 1, 13 | progress: 1, 14 | "server-and-client-transport": 1, 15 | "web-socket-server-url": 2, 16 | // integration tests 17 | "module-federation": 1, 18 | "multi-compiler": 1, 19 | // unit tests 20 | bonjour: 1, 21 | "client-option": 1, 22 | "compress-option": 1, 23 | "headers-option": 1, 24 | "history-api-fallback-option": 1, 25 | "host-option": 1, 26 | "hot-option": 1, 27 | "http2-option": 1, 28 | "https-option": 1, 29 | "mime-types-option": 1, 30 | "magic-html-option": 1, 31 | "on-after-setup-middleware-option": 1, 32 | "on-before-setup-middleware-option": 1, 33 | "on-listening-option": 1, 34 | "open-option": 1, 35 | "port-option": 1, 36 | "proxy-option": 4, 37 | server: 1, 38 | "setup-exit-signals-option": 1, 39 | "static-directory-option": 1, 40 | "static-public-path-option": 1, 41 | "stats-option": 1, 42 | "watch-files-option": 1, 43 | "web-socket-server-option": 1, 44 | "sockjs-server": 1, 45 | "web-socket-server": 1, 46 | routes: 1, 47 | "web-socket-communication": 1, 48 | ipc: 1, 49 | stats: 1, 50 | "cli-allowed-hosts": 1, 51 | "cli-bonjour": 1, 52 | "cli-client": 1, 53 | "cli-colors": 1, 54 | "cli-compress": 1, 55 | "cli-history-api-fallback": 1, 56 | "cli-host": 1, 57 | "cli-hot": 1, 58 | "cli-http2": 1, 59 | "cli-https": 1, 60 | "cli-live-reload": 1, 61 | "cli-magic-html": 1, 62 | "cli-open": 1, 63 | "cli-static": 1, 64 | "cli-watch-files": 1, 65 | "cli-web-socket-server": 1, 66 | target: 1, 67 | entry: 1, 68 | "allowed-hosts": 2, 69 | host: 1, 70 | api: 1, 71 | "lazy-compilation": 1, 72 | "range-header": 1, 73 | port: 1, 74 | "web-socket-server-test": 1, 75 | "client-reconnect-option": 1, 76 | "cli-server": 1, 77 | "server-option": 1, 78 | "normalize-option": 1, 79 | "setup-middlewares-option": 1, 80 | "options-request-response": 2, 81 | app: 1, 82 | }; 83 | 84 | let startPort = 8089; 85 | 86 | const ports = {}; 87 | 88 | for (const key of Object.keys(listOfTests)) { 89 | const value = listOfTests[key]; 90 | 91 | ports[key] = 92 | value === 1 93 | ? // biome-ignore lint/suspicious/noAssignInExpressions: _ 94 | (startPort += 1) 95 | : // biome-ignore lint/suspicious/noAssignInExpressions: _ 96 | [...new Array(value)].map(() => (startPort += 1)); 97 | } 98 | 99 | const busy = {}; 100 | 101 | module.exports = new Proxy(ports, { 102 | get(target, name) { 103 | if (!target[name]) { 104 | throw new Error( 105 | `Requested "${name}" port(s) for tests not found, please update "test/ports-map.js".`, 106 | ); 107 | } 108 | 109 | if (busy[name]) { 110 | throw new Error( 111 | `The "${name}" port is already in use in another test, please add a new one.`, 112 | ); 113 | } 114 | 115 | busy[name] = true; 116 | 117 | return target[name]; 118 | }, 119 | }); 120 | -------------------------------------------------------------------------------- /tests/helpers/puppeteer-constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reloadReadyDelay: 5000, 3 | completeReloadDelay: 10000, 4 | initConsoleDelay: 3000, 5 | awaitServerCloseDelay: 1000, 6 | beforeBrowserCloseDelay: 3000, 7 | puppeteerArgs: [ 8 | "--disable-background-timer-throttling", 9 | "--disable-breakpad", 10 | "--disable-client-side-phishing-detection", 11 | "--disable-cloud-import", 12 | "--disable-default-apps", 13 | "--disable-dev-shm-usage", 14 | "--disable-extensions", 15 | "--disable-gesture-typing", 16 | "--disable-hang-monitor", 17 | "--disable-infobars", 18 | "--disable-notifications", 19 | "--disable-offer-store-unmasked-wallet-cards", 20 | "--disable-offer-upload-credit-cards", 21 | "--disable-popup-blocking", 22 | "--disable-print-preview", 23 | "--disable-prompt-on-repost", 24 | "--disable-setuid-sandbox", 25 | "--disable-speech-api", 26 | "--disable-sync", 27 | "--disable-tab-for-desktop-share", 28 | "--disable-translate", 29 | "--disable-voice-input", 30 | "--disable-wake-on-wifi", 31 | "--enable-async-dns", 32 | "--enable-simple-cache-backend", 33 | "--enable-tcp-fast-open", 34 | "--enable-webgl", 35 | "--hide-scrollbars", 36 | "--ignore-gpu-blacklist", 37 | "--media-cache-size=33554432", 38 | "--metrics-recording-only", 39 | "--mute-audio", 40 | "--no-default-browser-check", 41 | "--no-first-run", 42 | "--no-pings", 43 | "--no-sandbox", 44 | "--no-zygote", 45 | "--password-store=basic", 46 | "--prerender-from-omnibox=disabled", 47 | "--use-gl=swiftshader", 48 | "--use-mock-keychain", 49 | "--disable-field-trial-config", 50 | ], 51 | }; 52 | -------------------------------------------------------------------------------- /tests/helpers/run-browser.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require("puppeteer"); 2 | const { puppeteerArgs } = require("./puppeteer-constants"); 3 | 4 | /** 5 | * @typedef {Object} RunBrowserResult 6 | * @property {import('puppeteer').Page} page 7 | * @property {import('puppeteer').Browser} browser 8 | */ 9 | 10 | /** 11 | * @param {Parameters[0]} config 12 | * @returns {Promise} 13 | */ 14 | function runBrowser(config) { 15 | return new Promise((resolve, reject) => { 16 | /** 17 | * @type {import('puppeteer').Page} 18 | */ 19 | let page; 20 | /** 21 | * @type {import('puppeteer').Browser} 22 | */ 23 | let browser; 24 | 25 | puppeteer 26 | .launch({ 27 | headless: "new", 28 | // because of invalid localhost certificate 29 | acceptInsecureCerts: true, 30 | // args come from: https://github.com/alixaxel/chrome-aws-lambda/blob/master/source/index.js 31 | args: puppeteerArgs, 32 | }) 33 | .then((launchedBrowser) => { 34 | browser = launchedBrowser; 35 | 36 | return runPage(launchedBrowser, config); 37 | }) 38 | .then((newPage) => { 39 | page = newPage; 40 | 41 | resolve({ page, browser }); 42 | }) 43 | .catch(reject); 44 | }); 45 | } 46 | 47 | function runPage(browser, config) { 48 | /** 49 | * @type {import('puppeteer').Page} 50 | */ 51 | let page; 52 | 53 | const options = { 54 | viewport: { 55 | width: 500, 56 | height: 500, 57 | }, 58 | userAgent: "", 59 | ...config, 60 | }; 61 | 62 | return Promise.resolve() 63 | .then(() => browser.newPage()) 64 | .then((newPage) => { 65 | page = newPage; 66 | page.emulate(options); 67 | 68 | return page.setRequestInterception(true); 69 | }) 70 | .then(() => { 71 | page.on("request", (interceptedRequest) => { 72 | if (interceptedRequest.isInterceptResolutionHandled()) return; 73 | if (interceptedRequest.url().includes("favicon.ico")) { 74 | interceptedRequest.respond({ 75 | status: 200, 76 | contentType: "image/png", 77 | body: "Empty", 78 | }); 79 | } else { 80 | interceptedRequest.continue( 81 | interceptedRequest.continueRequestOverrides(), 82 | 10, 83 | ); 84 | } 85 | }); 86 | 87 | return page; 88 | }); 89 | } 90 | 91 | module.exports = runBrowser; 92 | module.exports.runPage = runPage; 93 | -------------------------------------------------------------------------------- /tests/helpers/sequencer.js: -------------------------------------------------------------------------------- 1 | const Sequencer = require("@jest/test-sequencer").default; 2 | 3 | class NamedSequencer extends Sequencer { 4 | sort(tests) { 5 | const copyTests = [...tests]; 6 | return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1)); 7 | } 8 | } 9 | 10 | module.exports = NamedSequencer; 11 | -------------------------------------------------------------------------------- /tests/helpers/session-subscribe.js: -------------------------------------------------------------------------------- 1 | module.exports = async function sessionSubscribe(session) { 2 | session.on("sessionattached", (s) => { 3 | sessionSubscribe(s); 4 | }); 5 | session.send("Network.enable"); 6 | session.send("Runtime.runIfWaitingForDebugger"); 7 | }; 8 | -------------------------------------------------------------------------------- /tests/helpers/setup-test.js: -------------------------------------------------------------------------------- 1 | process.env.CHOKIDAR_USEPOLLING = true; 2 | 3 | jest.setTimeout(400000); 4 | -------------------------------------------------------------------------------- /tests/helpers/snapshot-resolver.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | const webpack = require("@rspack/core"); 3 | 4 | const [webpackVersion] = webpack.version; 5 | const snapshotExtension = `.snap.webpack${webpackVersion}`; 6 | 7 | module.exports = { 8 | resolveSnapshotPath: (testPath) => 9 | path.join( 10 | path.dirname(testPath), 11 | "__snapshots__", 12 | `${path.basename(testPath)}${snapshotExtension}`, 13 | ), 14 | resolveTestPath: (snapshotPath) => 15 | snapshotPath 16 | .replace(`${path.sep}__snapshots__`, "") 17 | .slice(0, -snapshotExtension.length), 18 | testPathForConsistencyCheck: path.join( 19 | "consistency_check", 20 | "__tests__", 21 | "example.test.js", 22 | ), 23 | }; 24 | -------------------------------------------------------------------------------- /tests/helpers/test-server.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@rspack/core"); 2 | const { RspackDevServer: Server } = require("@rspack/dev-server"); 3 | 4 | let server; 5 | 6 | // start server, returning the full setup of the server 7 | // (both the server and the compiler) 8 | function startFullSetup(config, options, done) { 9 | // disable watching by default for tests 10 | if (typeof options.static === "undefined") { 11 | options.static = false; 12 | } else if (options.static === null) { 13 | // this provides a way of using the default static value 14 | options.static = undefined; 15 | } 16 | 17 | const compiler = webpack(config); 18 | 19 | server = new Server(options, compiler); 20 | 21 | server.startCallback((error) => { 22 | if (error && done) { 23 | return done(error); 24 | } 25 | 26 | if (done) { 27 | done(); 28 | } 29 | }); 30 | 31 | return { 32 | server, 33 | compiler, 34 | }; 35 | } 36 | 37 | function startAwaitingCompilationFullSetup(config, options, done) { 38 | let readyCount = 0; 39 | 40 | const ready = (error) => { 41 | if (error && done) { 42 | done(error); 43 | 44 | return; 45 | } 46 | 47 | readyCount += 1; 48 | 49 | if (readyCount === 2) { 50 | done(); 51 | } 52 | }; 53 | 54 | const fullSetup = startFullSetup(config, options, ready); 55 | 56 | // wait for compilation, since dev server can start before this 57 | // https://github.com/webpack/webpack-dev-server/issues/847 58 | fullSetup.compiler.hooks.done.tap("done", () => { 59 | ready(); 60 | }); 61 | 62 | return fullSetup; 63 | } 64 | 65 | function startAwaitingCompilation(config, options, done) { 66 | return startAwaitingCompilationFullSetup(config, options, done).server; 67 | } 68 | 69 | function start(config, options, done) { 70 | // I suspect that almost all tests need to wait for compilation to 71 | // finish, because not doing so leaves open handles for jest, 72 | // in the case where a compilation didn't finish before destroying 73 | // the server and moving on. Thus, the default "start" should wait 74 | // for compilation, and only special cases where you don't expect 75 | // a compilation happen should use startBeforeCompilation 76 | return startAwaitingCompilation(config, options, done); 77 | } 78 | 79 | function close(done) { 80 | if (server) { 81 | server.stopCallback(() => { 82 | server = null; 83 | done(); 84 | }); 85 | } else { 86 | done(); 87 | } 88 | } 89 | 90 | module.exports = { 91 | start, 92 | close, 93 | }; 94 | -------------------------------------------------------------------------------- /tests/helpers/trusted-types-html-generator-plugin.js: -------------------------------------------------------------------------------- 1 | const HTMLContentForIndex = ` 2 | 3 | 4 | 5 | 9 | 10 | webpack-dev-server 11 | 12 | 13 |

webpack-dev-server is running...

14 | 15 | 16 | 17 | `; 18 | 19 | const HTMLContentForTest = ` 20 | 21 | 22 | 23 | 27 | 28 | test 29 | 30 | 31 |

Created via HTMLGeneratorPlugin

32 | 33 | 34 | `; 35 | 36 | module.exports = class HTMLGeneratorPlugin { 37 | // eslint-disable-next-line class-methods-use-this 38 | apply(compiler) { 39 | const pluginName = "html-generator-plugin"; 40 | 41 | compiler.hooks.thisCompilation.tap(pluginName, (compilation) => { 42 | if (compiler.webpack) { 43 | const { RawSource } = compiler.webpack.sources; 44 | 45 | compilation.hooks.processAssets.tap( 46 | { 47 | name: pluginName, 48 | stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL, 49 | }, 50 | () => { 51 | const indexSource = new RawSource(HTMLContentForIndex); 52 | const testSource = new RawSource(HTMLContentForTest); 53 | 54 | compilation.emitAsset("index.html", indexSource); 55 | compilation.emitAsset("test.html", testSource); 56 | }, 57 | ); 58 | } else { 59 | compilation.hooks.additionalAssets.tap(pluginName, () => { 60 | compilation.emitAsset("index.html", { 61 | source() { 62 | return HTMLContentForIndex; 63 | }, 64 | size() { 65 | return HTMLContentForIndex.length; 66 | }, 67 | }); 68 | compilation.emitAsset("test.html", { 69 | source() { 70 | return HTMLContentForTest; 71 | }, 72 | size() { 73 | return HTMLContentForTest.length; 74 | }, 75 | }); 76 | }); 77 | } 78 | }); 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /tests/normalizeOptions.test.ts: -------------------------------------------------------------------------------- 1 | import { type RspackOptions, rspack } from "@rspack/core"; 2 | import { type Configuration, RspackDevServer } from "@rspack/dev-server"; 3 | import ReactRefreshPlugin from "@rspack/plugin-react-refresh"; 4 | // @ts-expect-error 5 | import serializer from "jest-serializer-path"; 6 | import customConfig from "./fixtures/provide-plugin-custom/webpack.config"; 7 | 8 | expect.addSnapshotSerializer(serializer); 9 | 10 | // The aims of use a cutstom value rather than 11 | // default is to avoid stack overflow trigged 12 | // by `webpack/schemas/WebpackOption.check.js` in debug mode 13 | const ENTRY = "./placeholder.js"; 14 | const ENTRY1 = "./placeholder1.js"; 15 | 16 | // TODO: node 20 works but node 18 and 16 failed 17 | describe.skip("normalize options snapshot", () => { 18 | it("no options", async () => { 19 | await match({}); 20 | }); 21 | 22 | it("port string", async () => { 23 | await match({ 24 | devServer: { 25 | port: "9000", 26 | }, 27 | }); 28 | }); 29 | 30 | it("additional entires should added", async () => { 31 | expect( 32 | await getAdditionEntries({}, { entry: ["something"] }), 33 | ).toMatchSnapshot(); 34 | }); 35 | 36 | it("shouldn't have reactRefreshEntry.js by default when in production mode", async () => { 37 | const reactRefreshEntry = 38 | "plugin-react-refresh/client/reactRefreshEntry.js"; 39 | const entries1 = await getAdditionEntries( 40 | {}, 41 | { 42 | mode: "production", 43 | entry: ["something"], 44 | }, 45 | ); 46 | expect(entries1.undefined).not.toContain(reactRefreshEntry); 47 | const entries2 = await getAdditionEntries( 48 | {}, 49 | { 50 | mode: "production", 51 | entry: ["something"], 52 | plugins: [new ReactRefreshPlugin({ forceEnable: true })], 53 | }, 54 | ); 55 | expect(entries2.undefined).toContain(reactRefreshEntry); 56 | const entries3 = await getAdditionEntries( 57 | {}, 58 | { 59 | mode: "development", 60 | entry: ["something"], 61 | plugins: [new ReactRefreshPlugin()], 62 | }, 63 | ); 64 | expect(entries3.undefined).toContain(reactRefreshEntry); 65 | const entries4 = await getAdditionEntries( 66 | {}, 67 | { 68 | mode: "production", 69 | entry: ["something"], 70 | plugins: [new ReactRefreshPlugin()], 71 | }, 72 | ); 73 | expect(entries4.undefined).not.toContain(reactRefreshEntry); 74 | }); 75 | 76 | it("should apply HMR plugin by default", async () => { 77 | const compiler = rspack({ 78 | entry: ENTRY, 79 | stats: "none", 80 | }); 81 | const server = new RspackDevServer({}, compiler); 82 | await server.start(); 83 | const hmrPlugins = compiler.__internal__builtinPlugins.filter( 84 | (p) => p.name === "HotModuleReplacementPlugin", 85 | ); 86 | expect(hmrPlugins.length).toBe(1); 87 | expect(server.options.hot).toBe(true); 88 | await server.stop(); 89 | }); 90 | 91 | it("should support multi-compiler", async () => { 92 | const compiler = rspack([ 93 | { 94 | entry: ENTRY, 95 | stats: "none", 96 | }, 97 | { 98 | entry: ENTRY1, 99 | stats: "none", 100 | }, 101 | ]); 102 | const server = new RspackDevServer({}, compiler); 103 | await server.start(); 104 | await server.stop(); 105 | }); 106 | 107 | it("should support custom client transport", async () => { 108 | const compiler = rspack(customConfig); 109 | const devServerOptions = { 110 | client: { 111 | webSocketTransport: require.resolve( 112 | "./fixtures/custom-client/CustomSockJSClient", 113 | ), 114 | }, 115 | webSocketServer: "sockjs", 116 | }; 117 | const server = new RspackDevServer(devServerOptions, compiler); 118 | await server.start(); 119 | await server.stop(); 120 | }); 121 | }); 122 | 123 | async function match(config: RspackOptions) { 124 | const compiler = rspack({ 125 | ...config, 126 | entry: ENTRY, 127 | stats: "none", 128 | }); 129 | const server = new RspackDevServer( 130 | compiler.options.devServer ?? {}, 131 | compiler, 132 | ); 133 | await server.start(); 134 | // it will break ci 135 | //@ts-ignore 136 | server.options.port = undefined; 137 | expect(server.options).toMatchSnapshot(); 138 | await server.stop(); 139 | } 140 | 141 | async function getAdditionEntries( 142 | serverConfig: Configuration, 143 | config: RspackOptions, 144 | ) { 145 | const compiler = rspack({ 146 | ...config, 147 | stats: "none", 148 | entry: ENTRY, 149 | }); 150 | 151 | const server = new RspackDevServer(serverConfig, compiler); 152 | await server.start(); 153 | const entries = compiler.__internal__builtinPlugins 154 | .filter((p) => p.name === "EntryPlugin") 155 | .map((p) => p.options) 156 | // biome-ignore lint/suspicious/noExplicitAny: _ 157 | .reduce>((acc: any, cur: any) => { 158 | const name = cur.options.name; 159 | const request = cur.entry; 160 | if (acc[name]) { 161 | acc[name].import.push(request); 162 | } else { 163 | acc[name] = { import: [request] }; 164 | } 165 | return acc; 166 | }, {}); 167 | // some hack for snapshot 168 | const value = Object.fromEntries( 169 | Object.entries(entries).map(([key, item]) => { 170 | // @ts-expect-error 171 | const replaced = item.import?.map((entry) => { 172 | const array = entry 173 | .replace(/\\/g, "/") 174 | .replace(/port=\d+/g, "") 175 | .split("/"); 176 | return `/${array.slice(-3).join("/")}`; 177 | }); 178 | return [key, replaced]; 179 | }), 180 | ); 181 | await server.stop(); 182 | return value; 183 | } 184 | -------------------------------------------------------------------------------- /tests/placeholder.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/placeholder.js -------------------------------------------------------------------------------- /tests/placeholder1.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-infra-dev/rspack-dev-server/738b8a2e076a43c07e3d56fa71e48f7c5663617f/tests/placeholder1.js -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": false, 6 | "rootDir": "../" 7 | }, 8 | "include": ["../src", "../tests", "../client-src"], 9 | "references": [] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "declarationMap": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.client.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ES2020", 5 | "target": "ES5", 6 | "outDir": "client", 7 | "rootDir": "client-src", 8 | "composite": false, 9 | "sourceMap": false, 10 | "declarationMap": false, 11 | "declaration": false 12 | }, 13 | "include": ["client-src"] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "NodeNext", 4 | "moduleResolution": "NodeNext", 5 | "target": "ES2021", 6 | "esModuleInterop": true, 7 | "declaration": true, 8 | "isolatedModules": true, 9 | "sourceMap": true, 10 | "declarationMap": true, 11 | "composite": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "allowJs": true, 14 | "checkJs": true, 15 | "strict": true, 16 | "skipLibCheck": true, 17 | "noUnusedLocals": true, 18 | "outDir": "dist", 19 | "rootDir": "src" 20 | }, 21 | "ts-node": { 22 | "transpileOnly": true 23 | }, 24 | "include": ["src"] 25 | } 26 | --------------------------------------------------------------------------------