diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index d51cd833ee5..2422ee25a29 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -129,6 +129,7 @@ export const enum BuiltinPluginName { CommonJsChunkFormatPlugin = 'CommonJsChunkFormatPlugin', ArrayPushCallbackChunkFormatPlugin = 'ArrayPushCallbackChunkFormatPlugin', ModuleChunkFormatPlugin = 'ModuleChunkFormatPlugin', + HotModuleReplacementPlugin = 'HotModuleReplacementPlugin', HttpExternalsRspackPlugin = 'HttpExternalsRspackPlugin', CopyRspackPlugin = 'CopyRspackPlugin', HtmlRspackPlugin = 'HtmlRspackPlugin', diff --git a/crates/rspack_binding_options/src/options/mod.rs b/crates/rspack_binding_options/src/options/mod.rs index 21ee026e89a..e7e1810497b 100644 --- a/crates/rspack_binding_options/src/options/mod.rs +++ b/crates/rspack_binding_options/src/options/mod.rs @@ -125,9 +125,6 @@ impl RawOptionsApply for RawOptions { .boxed(), ); plugins.push(rspack_plugin_json::JsonPlugin {}.boxed()); - if dev_server.hot { - plugins.push(rspack_plugin_hmr::HotModuleReplacementPlugin {}.boxed()); - } plugins.push(rspack_plugin_runtime::RuntimePlugin {}.boxed()); if experiments.lazy_compilation { plugins.push(rspack_plugin_runtime::LazyCompilationPlugin {}.boxed()); diff --git a/crates/rspack_binding_options/src/options/raw_builtins/mod.rs b/crates/rspack_binding_options/src/options/raw_builtins/mod.rs index 50844ca53a1..dc028bb5bea 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/mod.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/mod.rs @@ -19,6 +19,7 @@ use rspack_plugin_entry::EntryPlugin; use rspack_plugin_externals::{ electron_target_plugin, http_externals_rspack_plugin, node_target_plugin, ExternalsPlugin, }; +use rspack_plugin_hmr::HotModuleReplacementPlugin; use rspack_plugin_html::HtmlRspackPlugin; use rspack_plugin_library::enable_library_plugin; use rspack_plugin_progress::ProgressPlugin; @@ -58,6 +59,7 @@ pub enum BuiltinPluginName { CommonJsChunkFormatPlugin, ArrayPushCallbackChunkFormatPlugin, ModuleChunkFormatPlugin, + HotModuleReplacementPlugin, // rspack specific plugins HttpExternalsRspackPlugin, @@ -148,6 +150,9 @@ impl RawOptionsApply for BuiltinPlugin { BuiltinPluginName::ModuleChunkFormatPlugin => { plugins.push(ModuleChunkFormatPlugin.boxed()); } + BuiltinPluginName::HotModuleReplacementPlugin => { + plugins.push(HotModuleReplacementPlugin.boxed()); + } // rspack specific plugins BuiltinPluginName::HttpExternalsRspackPlugin => { diff --git a/packages/playground/fixtures/index.ts b/packages/playground/fixtures/index.ts index 5358095e7c7..57c677c5fec 100644 --- a/packages/playground/fixtures/index.ts +++ b/packages/playground/fixtures/index.ts @@ -5,7 +5,8 @@ import { fileActionFixtures } from "./fileAction"; const test = base .extend(pathInfoFixtures) - .extend(rspackFixtures) + .extend(rspackFixtures(true)) + .extend(rspackFixtures(false)) .extend(fileActionFixtures); export type { RspackOptions }; diff --git a/packages/playground/fixtures/rspack.ts b/packages/playground/fixtures/rspack.ts index 5098b5fd90a..41dd4c21126 100644 --- a/packages/playground/fixtures/rspack.ts +++ b/packages/playground/fixtures/rspack.ts @@ -2,16 +2,18 @@ import path from "path"; import { Fixtures, PlaywrightTestArgs } from "@playwright/test"; import { Compiler, Configuration, createCompiler } from "@rspack/core"; import { RspackDevServer } from "@rspack/dev-server"; +import WebpackDevServer from "webpack-dev-server"; import type { PathInfoFixtures } from "./pathInfo"; import { sleep } from "@/utils/sleep"; class Rspack { projectDir: string; compiler: Compiler; - devServer: RspackDevServer; + devServer: RspackDevServer | WebpackDevServer; private onDone: Array<() => void> = []; constructor( projectDir: string, + wds: boolean, handleRspackConfig: (config: Configuration) => Configuration ) { const configPath = path.resolve(projectDir, "rspack.config.js"); @@ -28,7 +30,8 @@ class Rspack { item(); } }); - this.devServer = new RspackDevServer( + const DevServerConstructor = wds ? WebpackDevServer : RspackDevServer; + this.devServer = new DevServerConstructor( compiler.options.devServer ?? {}, compiler ); @@ -79,84 +82,88 @@ type RspackWorkerFixtures = { ) => Promise; }; -export const rspackFixtures: Fixtures< +export const rspackFixtures = ( + wds: boolean +): Fixtures< RspackOptions & RspackFixtures, RspackWorkerFixtures, PlaywrightTestArgs & PathInfoFixtures -> = { - defaultRspackConfig: [{ handleConfig: c => c }, { option: true }], - rspack: [ - async ( - { page, pathInfo, _startRspackServer, defaultRspackConfig }, - use - ) => { - const rspack = await _startRspackServer( - pathInfo.testFile, - pathInfo.tempProjectDir, - defaultRspackConfig.handleConfig - ); - const port = rspack.devServer.options.port; - await rspack.waitingForBuild(); - await page.goto(`http://localhost:${port}`); - await use(rspack); - }, - { - auto: true - } - ], +> => { + return { + defaultRspackConfig: [{ handleConfig: c => c }, { option: true }], + rspack: [ + async ( + { page, pathInfo, _startRspackServer, defaultRspackConfig }, + use + ) => { + const rspack = await _startRspackServer( + pathInfo.testFile, + pathInfo.tempProjectDir, + defaultRspackConfig.handleConfig + ); + const port = rspack.devServer.options.port; + await rspack.waitingForBuild(); + await page.goto(`http://localhost:${port}`); + await use(rspack); + }, + { + auto: true + } + ], - _startRspackServer: [ - async ({}, use, { workerIndex }) => { - let currentTestFile = ""; - let rspack: Rspack | null = null as any; - await use(async function (testFile, projectDir, handleRspackConfig) { - if (rspack && currentTestFile !== testFile) { - await rspack.devServer.stop(); - rspack = null; - currentTestFile = testFile; - } - if (!rspack) { - const port = 8000 + workerIndex; - rspack = new Rspack(projectDir, function (config) { - // rewrite port - if (!config.devServer) { - config.devServer = {}; - } - config.devServer.port = port; + _startRspackServer: [ + async ({}, use, { workerIndex }) => { + let currentTestFile = ""; + let rspack: Rspack | null = null as any; + await use(async function (testFile, projectDir, handleRspackConfig) { + if (rspack && currentTestFile !== testFile) { + await rspack.devServer.stop(); + rspack = null; + currentTestFile = testFile; + } + if (!rspack) { + const port = 8000 + workerIndex; + rspack = new Rspack(projectDir, wds, function (config) { + // rewrite port + if (!config.devServer) { + config.devServer = {}; + } + config.devServer.port = port; - // set default context - if (!config.context) { - config.context = projectDir; - } + // set default context + if (!config.context) { + config.context = projectDir; + } - // set default define - if (!config.builtins) { - config.builtins = {}; - } - config.builtins.define = Object.assign( - { - "process.env.NODE_ENV": JSON.stringify( - config.mode || "development" - ) - }, - config.builtins.define - ); + // set default define + if (!config.builtins) { + config.builtins = {}; + } + config.builtins.define = Object.assign( + { + "process.env.NODE_ENV": JSON.stringify( + config.mode || "development" + ) + }, + config.builtins.define + ); - return handleRspackConfig(config); - }); - await rspack.devServer.start(); - } + return handleRspackConfig(config); + }); + await rspack.devServer.start(); + } - return rspack; - }); + return rspack; + }); - if (rspack?.projectDir) { - await rspack.devServer.stop(); + if (rspack?.projectDir) { + await rspack.devServer.stop(); + } + }, + { + scope: "worker", + timeout: 60000 } - }, - { - scope: "worker", - timeout: 60000 - } - ] + ] + }; }; diff --git a/packages/playground/package.json b/packages/playground/package.json index 727b958a185..9a70b16b4ba 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -14,9 +14,9 @@ "devDependencies": { "@playwright/test": "1.35.0", "@rspack/core": "workspace:*", - "@rspack/plugin-react-refresh": "workspace:*", "@rspack/dev-client": "workspace:*", "@rspack/dev-server": "workspace:*", + "@rspack/plugin-react-refresh": "workspace:*", "@types/fs-extra": "11.0.1", "fs-extra": "11.1.1", "postcss": "^8.4.21", @@ -24,8 +24,9 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "tailwindcss": "^3.3.0", - "ws": "8.8.1", "vue": "3.2.47", - "vue-loader": "^17.2.2" + "vue-loader": "^17.2.2", + "webpack-dev-server": "4.13.1", + "ws": "8.8.1" } } \ No newline at end of file diff --git a/packages/rspack/src/Compiler.ts b/packages/rspack/src/Compiler.ts index 048783a7eb8..8b2b128d56a 100644 --- a/packages/rspack/src/Compiler.ts +++ b/packages/rspack/src/Compiler.ts @@ -47,10 +47,6 @@ import { } from "./builtin-plugin"; import { optionsApply_compat } from "./rspackOptionsApply"; -class HotModuleReplacementPlugin { - apply() {} -} - class Compiler { #_instance?: binding.Rspack; @@ -123,7 +119,6 @@ class Compiler { this.builtinPlugins = []; // to workaround some plugin access webpack, we may change dev-server to avoid this hack in the future this.webpack = { - HotModuleReplacementPlugin, // modernjs/server will auto inject this plugin not set NormalModule, get sources(): typeof import("webpack-sources") { return require("webpack-sources"); @@ -153,6 +148,9 @@ class Compiler { get ExternalsPlugin() { return require("./builtin-plugin").ExternalsPlugin; }, + get HotModuleReplacementPlugin() { + return require("./builtin-plugin").HotModuleReplacementPlugin; + }, get LoaderOptionsPlugin() { return require("./lib/LoaderOptionsPlugin").LoaderOptionsPlugin; }, @@ -161,6 +159,11 @@ class Compiler { }, WebpackError: Error, ModuleFilenameHelpers, + javascript: { + get EnableChunkLoadingPlugin() { + return require("./builtin-plugin").EnableChunkLoadingPlugin; + } + }, node: { get NodeTargetPlugin() { return require("./builtin-plugin").NodeTargetPlugin; diff --git a/packages/rspack/src/builtin-plugin/HotModuleReplacementPlugin.ts b/packages/rspack/src/builtin-plugin/HotModuleReplacementPlugin.ts new file mode 100644 index 00000000000..acb9a9738ee --- /dev/null +++ b/packages/rspack/src/builtin-plugin/HotModuleReplacementPlugin.ts @@ -0,0 +1,6 @@ +import { BuiltinPluginName, create } from "./base"; + +export const HotModuleReplacementPlugin = create( + BuiltinPluginName.HotModuleReplacementPlugin, + () => undefined +); diff --git a/packages/rspack/src/builtin-plugin/base.ts b/packages/rspack/src/builtin-plugin/base.ts index f35a52e4db7..24b246f846d 100644 --- a/packages/rspack/src/builtin-plugin/base.ts +++ b/packages/rspack/src/builtin-plugin/base.ts @@ -17,6 +17,7 @@ export enum BuiltinPluginName { CommonJsChunkFormatPlugin = "CommonJsChunkFormatPlugin", ArrayPushCallbackChunkFormatPlugin = "ArrayPushCallbackChunkFormatPlugin", ModuleChunkFormatPlugin = "ModuleChunkFormatPlugin", + HotModuleReplacementPlugin = "HotModuleReplacementPlugin", HttpExternalsRspackPlugin = "HttpExternalsRspackPlugin", CopyRspackPlugin = "CopyRspackPlugin", HtmlRspackPlugin = "HtmlRspackPlugin", diff --git a/packages/rspack/src/builtin-plugin/index.ts b/packages/rspack/src/builtin-plugin/index.ts index 2e1adfb6ea9..9724658e7ad 100644 --- a/packages/rspack/src/builtin-plugin/index.ts +++ b/packages/rspack/src/builtin-plugin/index.ts @@ -15,6 +15,7 @@ export * from "./EnableWasmLoadingPlugin"; export * from "./ArrayPushCallbackChunkFormatPlugin"; export * from "./CommonJsChunkFormatPlugin"; export * from "./ModuleChunkFormatPlugin"; +export * from "./HotModuleReplacementPlugin"; export * from "./HtmlRspackPlugin"; export * from "./CopyRspackPlugin"; diff --git a/packages/rspack/src/index.ts b/packages/rspack/src/index.ts index c79f068792f..4cc90f3238a 100644 --- a/packages/rspack/src/index.ts +++ b/packages/rspack/src/index.ts @@ -33,7 +33,8 @@ export { CopyRspackPlugin, EntryPlugin, ExternalsPlugin, - EnableChunkLoadingPlugin + EnableChunkLoadingPlugin, + HotModuleReplacementPlugin } from "./builtin-plugin"; export type { BannerPluginArgument, diff --git a/packages/rspack/src/rspackOptionsApply.ts b/packages/rspack/src/rspackOptionsApply.ts index 4ef8af8027e..2d3b9ba5d45 100644 --- a/packages/rspack/src/rspackOptionsApply.ts +++ b/packages/rspack/src/rspackOptionsApply.ts @@ -135,6 +135,10 @@ export function optionsApply_compat( compiler.context, options.entry ); + + if (options.devServer?.hot) { + new compiler.webpack.HotModuleReplacementPlugin().apply(compiler); + } } } diff --git a/packages/rspack/tests/HotTestCases.template.ts b/packages/rspack/tests/HotTestCases.template.ts index d9c038da5d6..bfa897cd8a0 100644 --- a/packages/rspack/tests/HotTestCases.template.ts +++ b/packages/rspack/tests/HotTestCases.template.ts @@ -4,7 +4,12 @@ import vm from "vm"; import rimraf from "rimraf"; import checkArrayExpectation from "./checkArrayExpectation"; import createLazyTestEnv from "./helpers/createLazyTestEnv"; -import { Compiler, rspack, Stats } from "@rspack/core"; +import { + Compiler, + rspack, + Stats, + HotModuleReplacementPlugin +} from "@rspack/core"; export function describeCases(config: { name: string; @@ -460,5 +465,8 @@ function getOptions( ...options.devServer, hot }; + if (hot) { + options.plugins.push(new HotModuleReplacementPlugin()); + } return options; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 291450aff40..5da82ac5a37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -892,6 +892,7 @@ importers: tailwindcss: ^3.3.0 vue: 3.2.47 vue-loader: ^17.2.2 + webpack-dev-server: 4.13.1 ws: 8.8.1 devDependencies: '@playwright/test': 1.35.0 @@ -908,6 +909,7 @@ importers: tailwindcss: 3.3.1_postcss@8.4.21 vue: 3.2.47 vue-loader: 17.2.2_vue@3.2.47 + webpack-dev-server: 4.13.1 ws: 8.8.1 packages/rspack: @@ -13220,6 +13222,7 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color + dev: false /body-parser/1.20.2: resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} @@ -16385,6 +16388,7 @@ packages: vary: 1.1.2 transitivePeerDependencies: - supports-color + dev: false /ext-list/2.2.2: resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} @@ -21671,14 +21675,6 @@ packages: is-wsl: 2.2.0 dev: true - /open/8.4.0: - resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} - engines: {node: '>=12'} - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 - /open/8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -21686,7 +21682,6 @@ packages: define-lazy-prop: 2.0.0 is-docker: 2.2.1 is-wsl: 2.2.0 - dev: true /opener/1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} @@ -27824,16 +27819,16 @@ packages: compression: 1.7.4 connect-history-api-fallback: 2.0.0 default-gateway: 6.0.3 - express: 4.18.2 + express: 4.18.1 graceful-fs: 4.2.10 html-entities: 2.3.3 http-proxy-middleware: 2.0.6_@types+express@4.17.14 ipaddr.js: 2.0.1 launch-editor: 2.6.0 - open: 8.4.0 + open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 - schema-utils: 4.0.0 + schema-utils: 4.0.1 selfsigned: 2.1.1 serve-index: 1.9.1 sockjs: 0.3.24 @@ -27874,16 +27869,16 @@ packages: compression: 1.7.4 connect-history-api-fallback: 2.0.0 default-gateway: 6.0.3 - express: 4.18.2 + express: 4.18.1 graceful-fs: 4.2.10 html-entities: 2.3.3 http-proxy-middleware: 2.0.6_@types+express@4.17.14 ipaddr.js: 2.0.1 launch-editor: 2.6.0 - open: 8.4.0 + open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 - schema-utils: 4.0.0 + schema-utils: 4.0.1 selfsigned: 2.1.1 serve-index: 1.9.1 sockjs: 0.3.24