Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f242ba4
feat: support react compiler
upupming Jul 10, 2025
3dd46d9
feat: using isReactCompilerRequiredSync
upupming Jul 10, 2025
da7ffeb
chore: clean files
upupming Jul 10, 2025
acc732c
feat: using @babel/core from user project
upupming Jul 14, 2025
cfcc727
feat: add basic test for react compiler
upupming Jul 14, 2025
5b579e8
feat: migrate all test cases from react to examples/react-compiler
upupming Jul 15, 2025
c019700
Merge remote-tracking branch 'gh/main' into feat/react-compiler
upupming Jul 15, 2025
04c3b39
fix: resolve rabbitai comments
upupming Jul 15, 2025
fbb1e51
chore: remove eslint config
upupming Jul 15, 2025
60f4895
feat: update api.md
upupming Jul 16, 2025
eb7a111
fix: no map in build
upupming Jul 16, 2025
c6cf035
fix: alias in windows
upupming Jul 16, 2025
ce25b02
fix: preact alias to mjs
upupming Jul 16, 2025
675e03c
Merge remote-tracking branch 'gh/main' into feat/react-compiler
upupming Jul 16, 2025
2cd7cda
feat: add `react-compiler-runtime` alias to make 3rd lib works
upupming Jul 22, 2025
ec8da4a
feat: use isReactCompilerRequired from @swc/react-compiler
upupming Jul 22, 2025
fc294a7
chore: update package.json
upupming Jul 25, 2025
3341d02
Merge remote-tracking branch 'gh/main' into feat/react-compiler
upupming Sep 3, 2025
828aa71
fix: test cases
upupming Sep 4, 2025
3b6992a
Merge remote-tracking branch 'gh/main' into feat/react-compiler
upupming Sep 4, 2025
6dd913b
fix: eslint error
upupming Sep 4, 2025
6f8ada6
Merge remote-tracking branch 'gh/main' into feat/react-compiler
upupming Oct 28, 2025
26b8200
chore: update changeset
upupming Oct 28, 2025
ade0dcc
fix: vitest.config.js
upupming Oct 29, 2025
1b224c8
feat: support compiler ts for vitest.config
upupming Oct 29, 2025
0dff201
Update packages/react/testing-library/src/vitest.config.js
upupming Oct 29, 2025
6061e0f
Merge remote-tracking branch 'gh/main' into feat/react-compiler
upupming Oct 31, 2025
b97cf8b
feat: process.cwd() -> rootContext
upupming Nov 3, 2025
566c6e6
feat: handle input sourcemap in react compiler loader
upupming Nov 3, 2025
068a3bf
Merge remote-tracking branch 'gh/main' into feat/react-compiler
upupming Nov 4, 2025
eb47053
feat: use @rsbuild/plugin-babel
upupming Nov 4, 2025
a52d7b6
feat: add dedupe test
upupming Nov 5, 2025
7b24c48
Update .changeset/sour-weeks-wait.md
upupming Nov 5, 2025
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/plenty-baboons-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/react-webpack-plugin": patch
---

Add React Compiler loader, which uses `babel-plugin-react-compiler` to transform user code to optimized code before ReactLynx transform.
5 changes: 5 additions & 0 deletions .changeset/quick-turtles-make.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/react": patch
---

Fix the `rerender` function in the testing library to keep the current `<page/>` element used. After this fix it will behavior the same with `react-testing-library`'s `rerender` function.
15 changes: 15 additions & 0 deletions .changeset/sour-weeks-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@lynx-js/react-rsbuild-plugin": patch
---

Support [React Compiler](https://react.dev/learn/react-compiler) for ReactLynx, enable it by set `experimental_enableReactCompiler` to `true` in `lynx.config.js`:

```js
export default defineConfig({
plugins: [
pluginReactLynx({
experimental_enableReactCompiler: true,
}),
],
});
```
17 changes: 17 additions & 0 deletions .changeset/tame-forks-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@lynx-js/react": patch
---

Support testing React Compiler in testing library. Enable React Compiler by setting the `experimental_enableReactCompiler` option of `createVitestConfig` to `true`.

```js
import { defineConfig, mergeConfig } from 'vitest/config';
import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config';

const defaultConfig = await createVitestConfig({
runtimePkgName: '@lynx-js/react',
experimental_enableReactCompiler: true,
});

export default mergeConfig(defaultConfig, config);
```
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions examples/react-compiler/lynx.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineConfig } from '@lynx-js/rspeedy';
import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';

const enableBundleAnalysis = !!process.env['RSPEEDY_BUNDLE_ANALYSIS'];

export default defineConfig({
plugins: [
pluginReactLynx({
experimental_enableReactCompiler: true,
}),
pluginQRCode({
schema(url) {
// We use `?fullscreen=true` to open the page in LynxExplorer in full screen mode
return `${url}?fullscreen=true`;
},
}),
],
performance: {
profile: enableBundleAnalysis,
},
});
25 changes: 25 additions & 0 deletions examples/react-compiler/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@lynx-js/example-react-compiler",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "rspeedy build",
"dev": "rspeedy dev"
},
"dependencies": {
"@lynx-js/react": "workspace:*"
},
"devDependencies": {
"@babel/core": "^7.28.0",
"@babel/plugin-syntax-jsx": "^7.27.1",
"@babel/plugin-syntax-typescript": "^7.27.1",
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
"@lynx-js/types": "3.3.0",
"@types/react": "^18.3.23",
"babel-plugin-react-compiler": "0.0.0-experimental-0566679-20250709",
"react-compiler-runtime": "0.0.0-experimental-0566679-20250709"
}
}
119 changes: 119 additions & 0 deletions examples/react-compiler/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
:root {
background-color: #000;
--color-text: #fff;
}

.Background {
position: fixed;
background: radial-gradient(
71.43% 62.3% at 46.43% 36.43%,
rgba(18, 229, 229, 0) 15%,
rgba(239, 155, 255, 0.3) 56.35%,
#ff6448 100%
);
box-shadow: 0px 12.93px 28.74px 0px #ffd28db2 inset;
border-radius: 50%;
width: 200vw;
height: 200vw;
top: -60vw;
left: -14.27vw;
transform: rotate(15.25deg);
}

.App {
position: relative;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

text {
color: var(--color-text);
}

.Banner {
flex: 5;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 100;
}

.Logo {
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 8px;
}

.Logo--react {
width: 100px;
height: 100px;
animation: Logo--spin infinite 20s linear;
}

.Logo--lynx {
width: 100px;
height: 100px;
animation: Logo--shake infinite 0.5s ease;
}

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

@keyframes Logo--shake {
0% {
transform: scale(1);
}
50% {
transform: scale(0.9);
}
100% {
transform: scale(1);
}
}

.Content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.Arrow {
width: 24px;
height: 24px;
}

.Title {
font-size: 36px;
font-weight: 700;
}

.Subtitle {
font-style: italic;
font-size: 22px;
font-weight: 600;
margin-bottom: 8px;
}

.Description {
font-size: 20px;
color: rgba(255, 255, 255, 0.85);
margin: 15rpx;
}

.Hint {
font-size: 12px;
margin: 5px;
color: rgba(255, 255, 255, 0.65);
}
67 changes: 67 additions & 0 deletions examples/react-compiler/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useCallback, useEffect, useState } from '@lynx-js/react';

import './App.css';
import arrow from './assets/arrow.png';
import lynxLogo from './assets/lynx-logo.png';
import reactLynxLogo from './assets/react-logo.png';

function Hello({ name }: {
name: string;
}) {
const items = [1, 2, 3].map(item => {
return <text key={item}>Item {item}</text>;
});
return (
<text>
Hello {name}
{items}
</text>
);
}

export function App() {
const [alterLogo, setAlterLogo] = useState(false);

useEffect(() => {
console.info('Hello, ReactLynx');
}, []);

const onTap = useCallback(() => {
'background-only';
setAlterLogo(!alterLogo);
}, [alterLogo]);

return (
<view>
<view className='Background' />
<view className='App'>
<view className='Banner'>
<view className='Logo' bindtap={onTap}>
{alterLogo
? <image src={reactLynxLogo} className='Logo--react' />
: <image src={lynxLogo} className='Logo--lynx' />}
</view>
<text className='Title'>React</text>
<text className='Subtitle'>on Lynx</text>
<Hello name='Lynx' />
</view>
<view className='Content'>
<image src={arrow} className='Arrow' />
<text className='Description'>Tap the logo and have fun!</text>
<text className='Hint'>
Edit<text
style={{
fontStyle: 'italic',
color: 'rgba(255, 255, 255, 0.85)',
}}
>
{' src/App.tsx '}
</text>
to see updates!
</text>
</view>
<view style={{ flex: 1 }}></view>
</view>
</view>
);
}
Binary file added examples/react-compiler/src/assets/arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/react-compiler/src/assets/lynx-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/react-compiler/src/assets/react-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions examples/react-compiler/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { root } from '@lynx-js/react';

import { App } from './App.js';

root.render(
<App />,
);

if (import.meta.webpackHot) {
import.meta.webpackHot.accept();
}
1 change: 1 addition & 0 deletions examples/react-compiler/src/rspeedy-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="@lynx-js/rspeedy/client" />
19 changes: 19 additions & 0 deletions examples/react-compiler/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@lynx-js/react",
"noEmit": true,

"allowJs": true,
"checkJs": true,
"isolatedDeclarations": false,
},
"include": ["src", "lynx.config.ts", "test"],
"references": [
{ "path": "../../packages/react/tsconfig.json" },
{ "path": "../../packages/rspeedy/core/tsconfig.build.json" },
{ "path": "../../packages/rspeedy/plugin-qrcode/tsconfig.build.json" },
{ "path": "../../packages/rspeedy/plugin-react/tsconfig.build.json" },
],
}
13 changes: 7 additions & 6 deletions packages/react/testing-library/src/__tests__/rerender.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useEffect, useState } from '@lynx-js/react';

test('rerender will re-render the element', async () => {
const Greeting = (props) => <text>{props.message}</text>;
const { container, rerender } = render(<Greeting message='hi' />);
const { container, rerender, asFragment } = render(<Greeting message='hi' />);
expect(container).toMatchInlineSnapshot(`
<page>
<text>
Expand All @@ -16,15 +16,16 @@ test('rerender will re-render the element', async () => {
expect(container.firstChild).toHaveTextContent('hi');

{
const { container } = rerender(<Greeting message='hey' />);
rerender(<Greeting message='hey' />);
const container = asFragment();
expect(container.firstChild).toHaveTextContent('hey');

expect(container).toMatchInlineSnapshot(`
<page>
<DocumentFragment>
<text>
hey
</text>
</page>
</DocumentFragment>
`);
}
});
Expand All @@ -42,8 +43,8 @@ test('rerender will flush pending hooks effects', async () => {
return value;
};

const { rerender } = render(<Component />);
const { findByText } = rerender(<Component />);
const { rerender, findByText } = render(<Component />);
rerender(<Component />);
vi.spyOn(lynx.getNativeApp(), 'callLepusMethod');
const callLepusMethod = lynxTestingEnv.backgroundThread.lynx.getNativeApp().callLepusMethod;
expect(callLepusMethod.mock.calls).toMatchInlineSnapshot(`[]`);
Expand Down
Loading
Loading