diff --git a/.changeset/five-turtles-turn.md b/.changeset/five-turtles-turn.md new file mode 100644 index 0000000000..005f3ee600 --- /dev/null +++ b/.changeset/five-turtles-turn.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/react-rsbuild-plugin": patch +--- + +Avoid injecting hot update runtime when dev.hmr or dev.liveReload is set to false. diff --git a/CODEOWNERS b/CODEOWNERS index 6a355e4d75..8522741568 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,6 +1,7 @@ packages/web-platform/** @pupiltong packages/webpack/** @colinaaa @upupming packages/rspeedy/** @colinaaa @upupming +packages/rspeedy/plugin-react/** @upupming packages/react/** @hzy @HuJean packages/react/transform/** @gaoachao benchmark/react/** @hzy @HuJean diff --git a/packages/rspeedy/plugin-react/src/entry.ts b/packages/rspeedy/plugin-react/src/entry.ts index 73d5889b42..7276068ec4 100644 --- a/packages/rspeedy/plugin-react/src/entry.ts +++ b/packages/rspeedy/plugin-react/src/entry.ts @@ -62,6 +62,9 @@ export function applyEntry( const entries = chain.entryPoints.entries() ?? {} const isLynx = environment.name === 'lynx' const isWeb = environment.name === 'web' + const { hmr, liveReload } = environment.config.dev ?? {} + const enabledHMR = isDev && !isWeb && hmr !== false + const enabledLiveReload = isDev && !isWeb && liveReload !== false chain.entryPoints.clear() @@ -116,7 +119,7 @@ export function applyEntry( import: imports, filename: mainThreadName, }) - .when(isDev && !isWeb, entry => { + .when(enabledHMR, entry => { const require = createRequire(import.meta.url) // use prepend to make sure it does not affect the exports // from the entry @@ -137,7 +140,7 @@ export function applyEntry( }) // in standalone lazy bundle mode, we do not add // other entries to avoid wrongly exporting from other entries - .when(isDev && !isWeb, entry => { + .when(enabledHMR, entry => { // use prepend to make sure it does not affect the exports // from the entry entry @@ -146,14 +149,18 @@ export function applyEntry( layer: LAYERS.BACKGROUND, import: '@rspack/core/hot/dev-server', }) + // This is aliased in `./refresh.ts` .prepend({ layer: LAYERS.BACKGROUND, - import: '@lynx-js/webpack-dev-transport/client', + import: '@lynx-js/react/refresh', }) - // This is aliased in `./refresh.ts` + }) + .when(enabledHMR || enabledLiveReload, entry => { + // This is aliased in `@lynx-js/rspeedy` + entry .prepend({ layer: LAYERS.BACKGROUND, - import: '@lynx-js/react/refresh', + import: '@lynx-js/webpack-dev-transport/client', }) }) .end() diff --git a/packages/rspeedy/plugin-react/test/config.test.ts b/packages/rspeedy/plugin-react/test/config.test.ts index ca739b1888..3d8a540495 100644 --- a/packages/rspeedy/plugin-react/test/config.test.ts +++ b/packages/rspeedy/plugin-react/test/config.test.ts @@ -1356,8 +1356,8 @@ describe('Config', () => { "main": { "filename": ".rspeedy/main/background.js", "import": [ - "@lynx-js/react/refresh", "@lynx-js/webpack-dev-transport/client", + "@lynx-js/react/refresh", "@rspack/core/hot/dev-server", "./fixtures/basic.tsx", ], @@ -1406,8 +1406,8 @@ describe('Config', () => { "main": { "filename": ".rspeedy/main/background.[contenthash].js", "import": [ - "@lynx-js/react/refresh", "@lynx-js/webpack-dev-transport/client", + "@lynx-js/react/refresh", "@rspack/core/hot/dev-server", "./fixtures/basic.tsx", ], @@ -2076,8 +2076,8 @@ describe('Config', () => { "main": { "filename": ".rspeedy/main/background.[contenthash:8].js", "import": [ - "@lynx-js/react/refresh", "@lynx-js/webpack-dev-transport/client", + "@lynx-js/react/refresh", "@rspack/core/hot/dev-server", "./src/index.js", ], diff --git a/packages/rspeedy/plugin-react/test/hotUpdate.test.ts b/packages/rspeedy/plugin-react/test/hotUpdate.test.ts new file mode 100644 index 0000000000..fa2473227a --- /dev/null +++ b/packages/rspeedy/plugin-react/test/hotUpdate.test.ts @@ -0,0 +1,153 @@ +// Copyright 2025 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import { describe, expect, test, vi } from 'vitest' + +import { createStubRspeedy as createRspeedy } from './createRspeedy.js' + +vi.stubEnv('USE_RSPACK', 'true').stubEnv('NODE_ENV', 'development') + +describe('hot update', () => { + test('should prepend hot update runtime in development mode', async () => { + const { pluginReactLynx } = await import('../src/pluginReactLynx.js') + const rsbuild = await createRspeedy({ + rspeedyConfig: { + plugins: [ + pluginReactLynx(), + ], + }, + }) + + const [config] = await rsbuild.initConfigs() + expect(config.entry).toMatchInlineSnapshot(` + { + "main": { + "filename": ".rspeedy/main/background.js", + "import": [ + "@lynx-js/webpack-dev-transport/client", + "@lynx-js/react/refresh", + "@rspack/core/hot/dev-server", + "./src/index.js", + ], + "layer": "react:background", + }, + "main__main-thread": { + "filename": ".rspeedy/main/main-thread.js", + "import": [ + "/packages/webpack/css-extract-webpack-plugin/runtime/hotModuleReplacement.lepus.cjs", + "./src/index.js", + ], + "layer": "react:main-thread", + }, + } + `) + }) + + test('should prepend hot update runtime when liveReload is set to false', async () => { + const { pluginReactLynx } = await import('../src/pluginReactLynx.js') + const rsbuild = await createRspeedy({ + rspeedyConfig: { + dev: { + liveReload: false, + }, + plugins: [ + pluginReactLynx(), + ], + }, + }) + + const [config] = await rsbuild.initConfigs() + expect(config.entry).toMatchInlineSnapshot(` + { + "main": { + "filename": ".rspeedy/main/background.js", + "import": [ + "@lynx-js/webpack-dev-transport/client", + "@lynx-js/react/refresh", + "@rspack/core/hot/dev-server", + "./src/index.js", + ], + "layer": "react:background", + }, + "main__main-thread": { + "filename": ".rspeedy/main/main-thread.js", + "import": [ + "/packages/webpack/css-extract-webpack-plugin/runtime/hotModuleReplacement.lepus.cjs", + "./src/index.js", + ], + "layer": "react:main-thread", + }, + } + `) + }) + + test('should not prepend refresh runtime when hmr is set to false', async () => { + const { pluginReactLynx } = await import('../src/pluginReactLynx.js') + const rsbuild = await createRspeedy({ + rspeedyConfig: { + dev: { + hmr: false, + }, + plugins: [ + pluginReactLynx(), + ], + }, + }) + + const [config] = await rsbuild.initConfigs() + expect(config.entry).toMatchInlineSnapshot(` + { + "main": { + "filename": ".rspeedy/main/background.js", + "import": [ + "@lynx-js/webpack-dev-transport/client", + "./src/index.js", + ], + "layer": "react:background", + }, + "main__main-thread": { + "filename": ".rspeedy/main/main-thread.js", + "import": [ + "./src/index.js", + ], + "layer": "react:main-thread", + }, + } + `) + }) + + test('should not prepend dev runtime when hmr and liveReload is set to false both', async () => { + const { pluginReactLynx } = await import('../src/pluginReactLynx.js') + const rsbuild = await createRspeedy({ + rspeedyConfig: { + dev: { + hmr: false, + liveReload: false, + }, + plugins: [ + pluginReactLynx(), + ], + }, + }) + + const [config] = await rsbuild.initConfigs() + expect(config.entry).toMatchInlineSnapshot(` + { + "main": { + "filename": ".rspeedy/main/background.js", + "import": [ + "./src/index.js", + ], + "layer": "react:background", + }, + "main__main-thread": { + "filename": ".rspeedy/main/main-thread.js", + "import": [ + "./src/index.js", + ], + "layer": "react:main-thread", + }, + } + `) + }) +})