Skip to content

Commit

Permalink
fix: webSocket stats message conflict when multiple web environment (#…
Browse files Browse the repository at this point in the history
…2989)

Co-authored-by: neverland <[email protected]>
  • Loading branch information
9aoy and chenjiahan authored Jul 23, 2024
1 parent 4608236 commit 6b3770f
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 62 deletions.
98 changes: 98 additions & 0 deletions e2e/cases/server/environments-hmr/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import fs from 'node:fs';
import { join } from 'node:path';
import { dev, gotoPage, rspackOnlyTest } from '@e2e/helper';
import { expect, test } from '@playwright/test';
import { pluginReact } from '@rsbuild/plugin-react';

const cwd = __dirname;

rspackOnlyTest(
'Multiple environments HMR should work correctly',
async ({ page, context }) => {
// HMR cases will fail in Windows
if (process.platform === 'win32') {
test.skip();
}

await fs.promises.cp(join(cwd, 'src'), join(cwd, 'test-temp-src'), {
recursive: true,
});

const rsbuild = await dev({
cwd,
rsbuildConfig: {
plugins: [pluginReact()],
environments: {
web: {
source: {
entry: {
index: join(cwd, 'test-temp-src/index.ts'),
},
},
},
web1: {
dev: {
// When generating outputs for multiple web environments,
// if assetPrefix is not added, file search conflicts will occur.
assetPrefix: 'auto',
},
source: {
entry: {
main: join(cwd, 'test-temp-src/web1.js'),
},
},
output: {
distPath: {
root: 'dist/web1',
html: 'html1',
},
},
},
},
},
});

const web1Page = await context.newPage();

await web1Page.goto(`http://localhost:${rsbuild.port}/web1/html1/main`);

const locator1 = web1Page.locator('#test');
await expect(locator1).toHaveText('Hello Rsbuild (web1)!');

await gotoPage(page, rsbuild);

const locator = page.locator('#test');
await expect(locator).toHaveText('Hello Rsbuild!');
await expect(locator).toHaveCSS('color', 'rgb(255, 0, 0)');

const locatorKeep = page.locator('#test-keep');
const keepNum = await locatorKeep.innerHTML();

// web1 live reload correctly and should not trigger index update
const web1JSPath = join(cwd, 'test-temp-src/web1.js');

await fs.promises.writeFile(
web1JSPath,
fs.readFileSync(web1JSPath, 'utf-8').replace('(web1)', '(web1-new)'),
);

await expect(locator1).toHaveText('Hello Rsbuild (web1)!');

await expect(locatorKeep.innerHTML()).resolves.toBe(keepNum);

// index hmr correctly
const appPath = join(cwd, 'test-temp-src/App.tsx');

await fs.promises.writeFile(
appPath,
fs.readFileSync(appPath, 'utf-8').replace('Hello Rsbuild', 'Hello Test'),
);

await expect(locator).toHaveText('Hello Test!');

// #test-keep should unchanged when app.tsx HMR
await expect(locatorKeep.innerHTML()).resolves.toBe(keepNum);

await rsbuild.close();
},
);
3 changes: 3 additions & 0 deletions e2e/cases/server/environments-hmr/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#test {
color: rgb(255, 0, 0);
}
4 changes: 4 additions & 0 deletions e2e/cases/server/environments-hmr/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import './App.css';

const App = () => <div id="test">Hello Rsbuild!</div>;
export default App;
17 changes: 17 additions & 0 deletions e2e/cases/server/environments-hmr/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const num = Math.ceil(Math.random() * 100);
const testEl = document.createElement('div');
testEl.id = 'test-keep';

testEl.innerHTML = String(num);

document.body.appendChild(testEl);

const container = document.getElementById('root');
if (container) {
const root = createRoot(container);
root.render(React.createElement(App));
}
5 changes: 5 additions & 0 deletions e2e/cases/server/environments-hmr/src/web1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const testEl = document.createElement('div');
testEl.id = 'test';
testEl.innerHTML = 'Hello Rsbuild (web1)!';

document.body.appendChild(testEl);
8 changes: 8 additions & 0 deletions packages/core/src/client/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import type { ClientConfig, StatsError } from '../types';
import { formatStatsMessages } from './format';

const compilationName = RSBUILD_COMPILATION_NAME;

function formatURL({
port,
protocol,
Expand All @@ -24,6 +26,7 @@ function formatURL({
url.hostname = hostname;
url.protocol = protocol;
url.pathname = pathname;
url.searchParams.append('compilationName', compilationName);
return url.toString();
}

Expand Down Expand Up @@ -202,6 +205,11 @@ function onOpen() {

function onMessage(e: MessageEvent<string>) {
const message = JSON.parse(e.data);

if (message.compilationName && message.compilationName !== compilationName) {
return;
}

switch (message.type) {
case 'hash':
// Update the last compilation hash
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/client/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ declare let RSBUILD_CLIENT_CONFIG: ClientConfig;
declare let RSBUILD_DEV_LIVE_RELOAD: boolean;

declare let __webpack_hash__: string;

declare let RSBUILD_COMPILATION_NAME: string;
12 changes: 9 additions & 3 deletions packages/core/src/server/compilerDevMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ export class CompilerDevMiddleware {
type: string,
data?: Record<string, any> | string | boolean,
): void {
this.socketServer.sockWrite(type, data);
this.socketServer.sockWrite({
type,
data,
});
}

private setupDevMiddleware(
Expand All @@ -98,8 +101,11 @@ export class CompilerDevMiddleware {
const { devConfig, serverConfig } = this;

const callbacks = {
onInvalid: () => {
this.socketServer.sockWrite('invalid');
onInvalid: (compilationName?: string) => {
this.socketServer.sockWrite({
type: 'invalid',
compilationName,
});
},
onDone: (stats: any) => {
this.socketServer.updateStats(stats);
Expand Down
26 changes: 9 additions & 17 deletions packages/core/src/server/devMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { DevMiddlewareOptions } from '../provider/createCompiler';
import type { DevConfig, NextFunction } from '../types';

type ServerCallbacks = {
onInvalid: () => void;
onInvalid: (compilationName?: string) => void;
onDone: (stats: any) => void;
};

Expand Down Expand Up @@ -37,21 +37,8 @@ const isNodeCompiler = (compiler: {
return false;
};

type CompilerTapFn<CallBack extends (...args: any[]) => void = () => void> = {
tap: (name: string, cb: CallBack) => void;
};

export const setupServerHooks = (
compiler: {
options: {
target?: Compiler['options']['target'];
};
hooks: {
compile: CompilerTapFn<ServerCallbacks['onInvalid']>;
invalid: CompilerTapFn<ServerCallbacks['onInvalid']>;
done: CompilerTapFn<ServerCallbacks['onDone']>;
};
},
compiler: Compiler,
hookCallbacks: ServerCallbacks,
): void => {
// TODO: node SSR HMR is not supported yet
Expand All @@ -61,8 +48,12 @@ export const setupServerHooks = (

const { compile, invalid, done } = compiler.hooks;

compile.tap('rsbuild-dev-server', hookCallbacks.onInvalid);
invalid.tap('rsbuild-dev-server', hookCallbacks.onInvalid);
compile.tap('rsbuild-dev-server', () =>
hookCallbacks.onInvalid(compiler.name),
);
invalid.tap('rsbuild-dev-server', () =>
hookCallbacks.onInvalid(compiler.name),
);
done.tap('rsbuild-dev-server', hookCallbacks.onDone);
};

Expand All @@ -82,6 +73,7 @@ function applyHMREntry({
}

new compiler.webpack.DefinePlugin({
RSBUILD_COMPILATION_NAME: JSON.stringify(compiler.name!),
RSBUILD_CLIENT_CONFIG: JSON.stringify(clientConfig),
RSBUILD_DEV_LIVE_RELOAD: liveReload,
}).apply(compiler);
Expand Down
Loading

0 comments on commit 6b3770f

Please sign in to comment.