diff --git a/e2e/cases/preact/prefresh-context/index.test.ts b/e2e/cases/preact/prefresh-context/index.test.ts new file mode 100644 index 0000000000..ac483ccaf6 --- /dev/null +++ b/e2e/cases/preact/prefresh-context/index.test.ts @@ -0,0 +1,61 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { dev, rspackOnlyTest } from '@e2e/helper'; +import { expect, test } from '@playwright/test'; + +rspackOnlyTest( + 'HMR should work properly with `createContext`', + async ({ page }) => { + // HMR cases will fail in Windows + if (process.platform === 'win32') { + test.skip(); + } + + const root = __dirname; + const compFilePath = path.join(root, 'src/test-temp-B.jsx'); + const compSourceCode = `const B = (props) => { + return
B: {props.count}
; +}; + +export default B; +`; + + fs.writeFileSync(compFilePath, compSourceCode, 'utf-8'); + + const rsbuild = await dev({ + cwd: root, + page, + }); + + const a = page.locator('#A'); + const b = page.locator('#B'); + + await expect(a).toHaveText('A: 0'); + await expect(b).toHaveText('B: 0'); + + await a.click({ clickCount: 5 }); + await expect(a).toHaveText('A: 5'); + await expect(b).toHaveText('B: 5'); + + // simulate a change to component B's source code + fs.writeFileSync( + compFilePath, + compSourceCode.replace('B:', 'Beep:'), + 'utf-8', + ); + + await page.waitForFunction(() => { + const aText = document.querySelector('#A')!.textContent; + const bText = document.querySelector('#B')!.textContent; + + return ( + // the state (count) of A should be kept + aText === 'A: 5' && + // content of B changed to `Beep: 5` means HMR has taken effect + bText === 'Beep: 5' + ); + }); + + await rsbuild.close(); + }, +); diff --git a/e2e/cases/preact/prefresh-context/rsbuild.config.ts b/e2e/cases/preact/prefresh-context/rsbuild.config.ts new file mode 100644 index 0000000000..314619547e --- /dev/null +++ b/e2e/cases/preact/prefresh-context/rsbuild.config.ts @@ -0,0 +1,13 @@ +import { pluginPreact } from '@rsbuild/plugin-preact'; + +export default { + plugins: [ + pluginPreact({ + exclude: [ + /node_modules/, + // exclude Rsbuild internal HMR client + /packages\/core\/dist/, + ], + }), + ], +}; diff --git a/e2e/cases/preact/prefresh-context/src/A.jsx b/e2e/cases/preact/prefresh-context/src/A.jsx new file mode 100644 index 0000000000..913235820f --- /dev/null +++ b/e2e/cases/preact/prefresh-context/src/A.jsx @@ -0,0 +1,34 @@ +import { createContext } from 'preact'; +import { useContext, useState } from 'preact/hooks'; +import B from './test-temp-B'; + +const MyContext = createContext(); + +export const MyProvider = ({ children }) => { + const [value, setValue] = useState(0); + return ( + + {children} + + ); +}; + +const App = () => { + const { value, setValue } = useContext(MyContext); + return ( + <> + + + + ); +}; + +export default () => { + return ( + + + + ); +}; diff --git a/e2e/cases/preact/prefresh-context/src/index.js b/e2e/cases/preact/prefresh-context/src/index.js new file mode 100644 index 0000000000..3d08df867a --- /dev/null +++ b/e2e/cases/preact/prefresh-context/src/index.js @@ -0,0 +1,4 @@ +import { h, render } from 'preact'; +import A from './A'; + +render(h(A), document.getElementById('root')); diff --git a/packages/plugin-preact/package.json b/packages/plugin-preact/package.json index 964d7b652d..a295a72a54 100644 --- a/packages/plugin-preact/package.json +++ b/packages/plugin-preact/package.json @@ -28,7 +28,8 @@ "dependencies": { "@prefresh/core": "^1.5.2", "@prefresh/utils": "^1.2.0", - "@rspack/plugin-preact-refresh": "^1.1.0" + "@rspack/plugin-preact-refresh": "^1.1.0", + "@swc/plugin-prefresh": "^3.0.3" }, "devDependencies": { "@rsbuild/core": "workspace:*", diff --git a/packages/plugin-preact/src/index.ts b/packages/plugin-preact/src/index.ts index 46a19b4d45..965e0ad9f5 100644 --- a/packages/plugin-preact/src/index.ts +++ b/packages/plugin-preact/src/index.ts @@ -68,6 +68,11 @@ export const pluginPreact = ( tools: { swc: { jsc: { + experimental: { + plugins: usePrefresh + ? [[require.resolve('@swc/plugin-prefresh'), {}]] + : undefined, + }, parser: { syntax: 'typescript', // enable supports for JSX/TSX compilation diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad7702043b..ea9eac4ea8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -864,6 +864,9 @@ importers: '@rspack/plugin-preact-refresh': specifier: ^1.1.0 version: 1.1.0(@prefresh/core@1.5.2(preact@10.24.3))(@prefresh/utils@1.2.0) + '@swc/plugin-prefresh': + specifier: ^3.0.3 + version: 3.0.3 devDependencies: '@rsbuild/core': specifier: workspace:* @@ -3289,6 +3292,9 @@ packages: '@swc/helpers@0.5.13': resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} + '@swc/plugin-prefresh@3.0.3': + resolution: {integrity: sha512-HXRBKFwGcUuEgPsYz4tEfZ97nM0V8raDvn322tSx7vBWSxN+XdtJx4xtoIlLZNr+YSV/NXV9UpfskQZ1Dfa9bQ==} + '@swc/plugin-remove-console@3.0.3': resolution: {integrity: sha512-Q7Gp0KzjBgXSJA2jOcAraJ2I/phK/RnpZDWewgqSPsElFe/Pdo3A+w2FVCEoXAd3rUtPbrav1kjpBrGmeFBpFQ==} @@ -9975,6 +9981,10 @@ snapshots: dependencies: tslib: 2.6.2 + '@swc/plugin-prefresh@3.0.3': + dependencies: + '@swc/counter': 0.1.3 + '@swc/plugin-remove-console@3.0.3': dependencies: '@swc/counter': 0.1.3