diff --git a/e2e/cases/lazy-compilation/add-initial-chunk/index.test.ts b/e2e/cases/lazy-compilation/add-initial-chunk/index.test.ts new file mode 100644 index 0000000000..0b65ec2d7e --- /dev/null +++ b/e2e/cases/lazy-compilation/add-initial-chunk/index.test.ts @@ -0,0 +1,17 @@ +import { dev, gotoPage, rspackOnlyTest } from '@e2e/helper'; +import { expect } from '@playwright/test'; + +// https://github.com/web-infra-dev/rspack/issues/6633 +rspackOnlyTest( + 'should render pages correctly when using lazy compilation and add new initial chunk', + async ({ page }) => { + const rsbuild = await dev({ + cwd: __dirname, + }); + + await gotoPage(page, rsbuild); + await expect(page.locator('#test')).toHaveText('Hello World!'); + + rsbuild.close(); + }, +); diff --git a/e2e/cases/lazy-compilation/add-initial-chunk/rsbuild.config.ts b/e2e/cases/lazy-compilation/add-initial-chunk/rsbuild.config.ts new file mode 100644 index 0000000000..77574b0392 --- /dev/null +++ b/e2e/cases/lazy-compilation/add-initial-chunk/rsbuild.config.ts @@ -0,0 +1,41 @@ +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + plugins: [pluginReact()], + source: { + entry: { + index: './src/index.js', + }, + }, + tools: { + rspack(config) { + config.output ??= {}; + config.output.asyncChunks = false; + }, + }, + performance: { + chunkSplit: { + strategy: 'custom', + override: { + chunks: 'all', + cacheGroups: { + lib: { + enforce: true, + test: /(initial\.js|core-js)/, + name: 'lib', + chunks: 'all', + }, + default: false, + defaultVendors: false, + }, + }, + }, + }, + dev: { + lazyCompilation: true, + }, + output: { + polyfill: 'usage', + }, +}); diff --git a/e2e/cases/lazy-compilation/add-initial-chunk/src/index.js b/e2e/cases/lazy-compilation/add-initial-chunk/src/index.js new file mode 100644 index 0000000000..bdb8296b74 --- /dev/null +++ b/e2e/cases/lazy-compilation/add-initial-chunk/src/index.js @@ -0,0 +1,7 @@ +import x from './initial'; +import 'core-js'; + +const ele = document.createElement('div'); +ele.innerHTML = `Hello ${x}`; +ele.setAttribute('id', 'test'); +document.body.append(ele); diff --git a/e2e/cases/lazy-compilation/add-initial-chunk/src/initial.js b/e2e/cases/lazy-compilation/add-initial-chunk/src/initial.js new file mode 100644 index 0000000000..43a5d6c1b4 --- /dev/null +++ b/e2e/cases/lazy-compilation/add-initial-chunk/src/initial.js @@ -0,0 +1,3 @@ +import 'core-js/package.json'; + +export default 'World!'; diff --git a/packages/core/src/server/socketServer.ts b/packages/core/src/server/socketServer.ts index 831aef6fba..44159bd6a7 100644 --- a/packages/core/src/server/socketServer.ts +++ b/packages/core/src/server/socketServer.ts @@ -9,6 +9,19 @@ interface ExtWebSocket extends Ws { isAlive: boolean; } +function isEqualSet(a: Set, b: Set): boolean { + if (a.size !== b.size) { + return false; + } + + for (const v of a.values()) { + if (!b.has(v)) { + return false; + } + } + return true; +} + export class SocketServer { private wsServer!: Ws.Server; @@ -17,6 +30,7 @@ export class SocketServer { private readonly options: DevConfig; private stats?: Stats; + private initialChunks?: Set; private timer: ReturnType | null = null; @@ -151,6 +165,7 @@ export class SocketServer { errors: true, errorsCount: true, errorDetails: false, + entrypoints: true, children: true, }; @@ -166,6 +181,29 @@ export class SocketServer { return null; } + // web-infra-dev/rspack#6633 + // when initial-chunks change, reload the page + // e.g: ['index.js'] -> ['index.js', 'lib-polyfill.js'] + const newInitialChunks: Set = new Set(); + if (stats.entrypoints) { + for (const entrypoint of Object.values(stats.entrypoints)) { + const chunks = entrypoint.chunks; + if (Array.isArray(chunks)) { + for (const chunkName of chunks) { + chunkName && newInitialChunks.add(chunkName); + } + } + } + } + const shouldReload = + Boolean(stats.entrypoints) && + Boolean(this.initialChunks) && + !isEqualSet(this.initialChunks as Set, newInitialChunks); + this.initialChunks = newInitialChunks; + if (shouldReload) { + return this.sockWrite('content-changed'); + } + const shouldEmit = !force && stats &&