Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: class component hmr #1824

Merged
merged 8 commits into from
Oct 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/red-kids-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@farmfe/plugin-react": patch
---

fix class component hmr
4 changes: 2 additions & 2 deletions e2e/default.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ describe('Default E2E Tests', async () => {
// const examples = ['react-ssr', 'solid-ssr', 'vue-ssr'];
logger(`Running E2E tests for ${examples.length} examples`);

console.log('exclude examples', excludeExamples);

for (const example of examples) {
const examplePath = join('./examples', example);
const hasE2eTestFile = existsSync(join(examplePath, 'e2e.spec.ts'));
Expand All @@ -25,8 +27,6 @@ describe('Default E2E Tests', async () => {
example,
'hasE2eTestFile',
hasE2eTestFile,
'excludeExamples',
excludeExamples,
'hasIndexHtml',
hasIndexHtml
);
Expand Down
18 changes: 18 additions & 0 deletions e2e/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import fs from 'node:fs/promises';
import { isAbsolute, join } from 'node:path';
export type SimpleUnwrapArray<T> = T extends ReadonlyArray<infer P> ? P : T;

export function logger(msg: any, { title = 'FARM INFO', color = 'green' } = {}) {
Expand Down Expand Up @@ -92,3 +94,19 @@ export const concurrentMap = <
>(arr: Arr, maxConcurrent: number, cb: F) => arr.map(
concurrentify(maxConcurrent, cb) as any,
) as ReturnType<F>[];


export async function editFile(_filename: string, matched: string | RegExp, to: string): Promise<undefined | (() => Promise<void>)> {
const filename = isAbsolute(_filename) ? _filename : join(process.cwd(), _filename);
const content = await fs.readFile(filename, 'utf-8')

let newContent = content.replaceAll(matched, to);

if(content.length !== newContent.length || content !== newContent) {
await fs.writeFile(filename, newContent);

return async () => {
await fs.writeFile(filename, content)
}
}
}
9 changes: 4 additions & 5 deletions e2e/vitestGlobalSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import { createServer, Server } from 'http';
let browserServer: BrowserServer | undefined;
let client: Server | undefined;

const buffer = new SharedArrayBuffer(2);
const u16 = new Uint16Array(buffer);
let port = 23000;

function addPort() {
return Atomics.add(u16, 0, 10);
return (port += 10);
}

function setPort(port: number) {
return Atomics.store(u16, 0, port);
function setPort(_port: number) {
return (port = _port);
}

setPort(9100);
Expand Down
37 changes: 37 additions & 0 deletions examples/react-fast-refresh/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Farm + React

This template should help you start developing using React and TypeScript in Farm.

## Setup

Install the dependencies:

```bash
pnpm install
```

## Get Started

Start the dev server:

```bash
pnpm start
```

Build the app for production:

```bash
pnpm build
```

Preview the Production build product:

```bash
pnpm preview
```

Clear persistent cache local files

```bash
pnpm clean
```
83 changes: 83 additions & 0 deletions examples/react-fast-refresh/e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { test, expect, describe } from 'vitest';
import { startProjectAndTest } from '../../e2e/vitestSetup';
import path, { basename, dirname } from 'path';
import { fileURLToPath } from 'url';
import { editFile } from '../../e2e/utils';
import { ConsoleMessage, ElementHandle, Page } from 'playwright-chromium';

const name = basename(import.meta.url);
const projectPath = dirname(fileURLToPath(import.meta.url));

const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

const waitMatchConsole = (page: Page, text: string, timeout = 10000) => new Promise((resolve, reject) => {
let timer: NodeJS.Timeout | null = setTimeout(() => {
reject('wait match console message timeout');
}, timeout);

let cleanTimer = () => {
if (timer) {
clearTimeout(timer);
timer = null;;
}
}
let handler = (message: ConsoleMessage) => {

if(message.text().includes(text)) {
cleanTimer();
resolve(undefined);

page.off('console', handler);
};
};


page.on('console', handler);
})

async function expectTestFileHmr(page: Page, element: ElementHandle<SVGElement | HTMLElement>, filename: string, originText: string, afterText: string) {

const matchUpdateMessage = `[Farm HMR] ${path.posix.normalize(filename)} updated`;

const waitUpdatePromise = waitMatchConsole(page, matchUpdateMessage);

const recover = await editFile(path.join(projectPath, filename), originText, afterText);

try {
await waitUpdatePromise;
await delay(1000);
expect((await element.textContent())).toContain(afterText);
} finally {
await recover?.()
}
}

describe(`e2e tests - ${name}`, async () => {
const runTest = (command: 'start' = 'start') =>
startProjectAndTest(
projectPath,
async (page) => {
const root = (await page.$('#root'))!;

expect(root).not.toBeNull();

const content = await root?.textContent();

expect(content).toContain('class component');

expect(content).toContain('function component');

await expectTestFileHmr(page, root, './src/components/ClassC.tsx', 'class component', 'class component update');

await delay(3000);

await expectTestFileHmr(page, root, './src/components/FnC.tsx', 'function component', 'function component update');
},
command
);

test(`exmaples ${name} run start`, async () => {
await runTest();
})

});
5 changes: 5 additions & 0 deletions examples/react-fast-refresh/farm.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineConfig } from '@farmfe/core';

export default defineConfig({
plugins: ['@farmfe/plugin-react']
});
14 changes: 14 additions & 0 deletions examples/react-fast-refresh/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<title>Farm + React + TS</title>
</head>
<body>
<div id="root"></div>
<script src="./src/index.tsx"></script>
</body>
</html>
24 changes: 24 additions & 0 deletions examples/react-fast-refresh/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "react-fast-refresh",
"version": "1.0.0",
"scripts": {
"dev": "farm start",
"start": "farm start",
"build": "farm build",
"preview": "farm preview",
"clean": "farm clean"
},
"dependencies": {
"react": "18",
"react-dom": "18"
},
"devDependencies": {
"@farmfe/cli": "workspace:",
"@farmfe/core": "workspace:",
"@farmfe/plugin-react": "workspace:",
"@types/react": "18",
"core-js": "^3.36.1",
"@types/react-dom": "18",
"react-refresh": "^0.14.0"
}
}
Binary file added examples/react-fast-refresh/public/favicon.ico
Binary file not shown.
Binary file added examples/react-fast-refresh/src/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/react-fast-refresh/src/assets/react.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions examples/react-fast-refresh/src/components/ClassC.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function ClassC() {
return <div>hello class component</div>
}
3 changes: 3 additions & 0 deletions examples/react-fast-refresh/src/components/FnC.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function FnC() {
return <div>hello function component</div>;
}
69 changes: 69 additions & 0 deletions examples/react-fast-refresh/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;

color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;

font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}

a {
font-weight: 500;
color: #9f1a8f;
text-decoration: inherit;
}
a:hover {
color: #9f1a8f;
}

body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}

h1 {
font-size: 3.2em;
line-height: 1.1;
}

button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #9f1a8f;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #9F1A8F;
}
button {
background-color: #f9f9f9;
}
}
8 changes: 8 additions & 0 deletions examples/react-fast-refresh/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createRoot } from "react-dom/client";
import { Main } from "./main";
import "./index.css";

const container = document.querySelector("#root");
const root = createRoot(container!);

root.render(<Main />);
42 changes: 42 additions & 0 deletions examples/react-fast-refresh/src/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #9F1A8Faa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}
Loading
Loading