Skip to content

Commit cb99053

Browse files
author
i
committed
feat: add option reactRefreshPrefix for HMR module federation
1 parent c52a1c9 commit cb99053

File tree

4 files changed

+44
-7
lines changed

4 files changed

+44
-7
lines changed

packages/plugin-react/CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
## Unreleased
44

5+
### Add option `reactRefreshHost`
6+
7+
Add option `reactRefreshHost` to set React refresh runtime url prefix.
8+
This is useful in module federation context to enable HMR by setting the host url on a vite config which is serving a remote app.
9+
See full discussion here: https://github.com/module-federation/vite/issues/183#issuecomment-2751825367
10+
11+
```ts
12+
export default defineConfig({
13+
plugins: [react({ reactRefreshHost: 'http://localhost:3000' })],
14+
})
15+
```
16+
517
## 4.3.4 (2024-11-26)
618

719
### Add Vite 6 to peerDependencies range

packages/plugin-react/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ This option does not enable _code transformation_. That is handled by esbuild.
9494

9595
Here's the [complete list of Babel parser plugins](https://babeljs.io/docs/en/babel-parser#ecmascript-proposalshttpsgithubcombabelproposals).
9696

97+
### reactRefreshHost
98+
99+
The `reactRefreshHost` option is only necessary in a module federation context. It allows HMR to work between a remote & host server. In your remote vite config you would add your host origin:
100+
101+
```js
102+
react({ reactRefreshHost: 'http://localhost:3000' })
103+
```
104+
105+
Under the hood this simply updates the react refresh url from "/@react-refresh" to "http://localhost:3000/@react-refresh" allowing HMR updates to flow from your remote to your host.
106+
97107
## Middleware mode
98108

99109
In [middleware mode](https://vitejs.dev/config/server-options.html#server-middlewaremode), you should make sure your entry `index.html` file is transformed by Vite. Here's an example for an Express server:

packages/plugin-react/src/fast-refresh.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from 'node:fs'
22
import path from 'node:path'
33
import { createRequire } from 'node:module'
4+
import { type Options } from './index'
45

56
export const runtimePublicPath = '/@react-refresh'
67

@@ -28,8 +29,9 @@ window.$RefreshSig$ = () => (type) => type
2829
window.__vite_plugin_react_preamble_installed__ = true
2930
`
3031

31-
const sharedHeader = `
32-
import RefreshRuntime from "${runtimePublicPath}";
32+
const sharedHeader = (reactRefreshHost: Options['reactRefreshHost']) =>
33+
`
34+
import RefreshRuntime from "${reactRefreshHost ?? ''}${runtimePublicPath}";
3335
3436
const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
3537
`.replace(/\n+/g, '')
@@ -74,9 +76,13 @@ if (import.meta.hot && !inWebWorker) {
7476
});
7577
}`
7678

77-
export function addRefreshWrapper(code: string, id: string): string {
79+
export function addRefreshWrapper(
80+
code: string,
81+
id: string,
82+
opts: Options,
83+
): string {
7884
return (
79-
sharedHeader +
85+
sharedHeader(opts.reactRefreshHost) +
8086
functionHeader.replace('__SOURCE__', JSON.stringify(id)) +
8187
code +
8288
functionFooter +
@@ -87,6 +93,7 @@ export function addRefreshWrapper(code: string, id: string): string {
8793
export function addClassComponentRefreshWrapper(
8894
code: string,
8995
id: string,
96+
opts: Options,
9097
): string {
91-
return sharedHeader + code + sharedFooter(id)
98+
return sharedHeader(opts.reactRefreshHost) + code + sharedFooter(id)
9299
}

packages/plugin-react/src/index.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ export interface Options {
4747
babel?:
4848
| BabelOptions
4949
| ((id: string, options: { ssr?: boolean }) => BabelOptions)
50+
/**
51+
* React refresh runtime url prefix.
52+
* Useful in module federation context to enable HMR by
53+
* setting the host url on a vite config which is serving a remote app.
54+
* @example
55+
* reactRefreshHost: 'http://localhost:3000'
56+
*/
57+
reactRefreshHost?: string
5058
}
5159

5260
export type BabelOptions = Omit<
@@ -261,9 +269,9 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
261269
let code = result.code!
262270
if (useFastRefresh) {
263271
if (refreshContentRE.test(code)) {
264-
code = addRefreshWrapper(code, id)
272+
code = addRefreshWrapper(code, id, opts)
265273
} else if (reactCompRE.test(code)) {
266-
code = addClassComponentRefreshWrapper(code, id)
274+
code = addClassComponentRefreshWrapper(code, id, opts)
267275
}
268276
}
269277
return { code, map: result.map }

0 commit comments

Comments
 (0)