Skip to content

Commit 9a9bb26

Browse files
authored
perf(react): improve react compiler preset so that slightly more modules are filtered out (#1138)
1 parent 5764f96 commit 9a9bb26

2 files changed

Lines changed: 170 additions & 1 deletion

File tree

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { describe, expect, test } from 'vitest'
2+
import { defaultCodeFilter } from './reactCompilerPreset'
3+
4+
describe('defaultCodeFilter', () => {
5+
const cases: Record<string, [code: string, expected: boolean]> = {
6+
directive: ['"use memo";', true],
7+
8+
'component declaration': ['function App() { return <></> }', true],
9+
'component declaration with types': [
10+
'function App(): Type { return <></> }',
11+
true,
12+
],
13+
'component arrow expression': [
14+
'const MyComponent = () => { return <></> }',
15+
true,
16+
],
17+
'component arrow expression with types (1)': [
18+
'const MyComponent = (): Type => { return <></> }',
19+
true,
20+
],
21+
'component arrow expression with types (2)': [
22+
'const MyComponent: Type = () => { return <></> }',
23+
true,
24+
],
25+
'component arrow expression with types (3)': [
26+
'const MyComponent: SomeComplexType<Generic, number> = () => { return <></> }',
27+
true,
28+
],
29+
'component arrow expressions': [
30+
'const a = 0, MyComponent = () => { return <></> }',
31+
true,
32+
],
33+
'component arrow expressions (let)': [
34+
'let a = 0, MyComponent = () => { return <></> }',
35+
true,
36+
],
37+
'component function expression': [
38+
'const MyComponent = function() { return <></> }',
39+
true,
40+
],
41+
'component function expression with types (1)': [
42+
'const MyComponent = function(): Type { return <></> }',
43+
true,
44+
],
45+
'component function expression with types (2)': [
46+
'const MyComponent: Type = function() { return <></> }',
47+
true,
48+
],
49+
'component function expression with types (3)': [
50+
'const MyComponent: SomeComplexType<Generic, number> = function() { return <></> }',
51+
true,
52+
],
53+
'component function expression (let)': [
54+
'let MyComponent = function() { return <></> }',
55+
true,
56+
],
57+
'component function expression (var)': [
58+
'var MyComponent = function() { return <></> }',
59+
true,
60+
],
61+
'exported component declaration': [
62+
'export default function Page() { return <></> }',
63+
true,
64+
],
65+
'exported component declaration with types': [
66+
'export default function Page(): Type { return <></> }',
67+
true,
68+
],
69+
'component assignment': [
70+
'let MyComponent; MyComponent = function() { return <></> }',
71+
true,
72+
],
73+
'component default declaration': [
74+
'const { MyComponent = function() { return <></> } } = {}',
75+
true,
76+
],
77+
'component default assignment': [
78+
'let MyComponent; ({ MyComponent = function() { return <></> } }) = {}',
79+
true,
80+
],
81+
'component property function expression': [
82+
'const components = { MyComponent: function() { return <></> } }',
83+
true,
84+
],
85+
'component property arrow function expression': [
86+
'const components = { MyComponent: () => <></> }',
87+
true,
88+
],
89+
'component method': [
90+
'const components = { MyComponent() { return <></> } }',
91+
true,
92+
],
93+
94+
'hook declaration': ['function useEffect() { return <></> }', true],
95+
'hook arrow expression': ['const useMyHook = () => { return <></> }', true],
96+
'hook function expression': [
97+
'const useMyHook = function() { return <></> }',
98+
true,
99+
],
100+
'hook with digit': ['function use0() { return <></> }', true],
101+
'hook using hooks': [
102+
'function useMyHook() { return useOtherHook() }',
103+
true,
104+
],
105+
'hook using nested hooks': [
106+
'function useMyHook() { return Foo.useOtherHook() }',
107+
true,
108+
],
109+
110+
'React.forwardRef': ['React.forwardRef(() => <></>)', true],
111+
'React.memo': ['React.memo(() => <></>)', true],
112+
forwardRef: [
113+
'import { forwardRef } from "react"; forwardRef(() => <></>)',
114+
true,
115+
],
116+
memo: ['import { memo } from "react"; memo(() => <></>)', true],
117+
118+
'edge case: memo callback with hooks': [
119+
`import React, { useState } from "react";
120+
import { jsx } from "react/jsx-runtime"
121+
122+
export const components = {
123+
A: React.memo(() => {
124+
const [state, setState] = useState(0);
125+
126+
return jsx("div", { children: state })
127+
})
128+
}`,
129+
true,
130+
],
131+
'edge case: memo without namespace': [
132+
`import { memo, useState } from "react";
133+
134+
export default memo(() => {
135+
const [count, setCount] = useState(0);
136+
return <div>{count}</div>
137+
})`,
138+
true,
139+
],
140+
'edge case: memo without namespace from re-export': [
141+
`import { memo, useState } from "my-react";
142+
143+
export default memo(() => {
144+
const [count, setCount] = useState(0);
145+
return <div>{count}</div>
146+
})`,
147+
true,
148+
],
149+
150+
'simple variable': ['const foo = 1', false],
151+
'lowercase function': ['function bar() {}', false],
152+
'lowercase arrow function': ['let baz = () => {}', false],
153+
'non assignments (1)': ['(0,useState)()', false],
154+
'non assignments (2)': ['[useState][0]()', false],
155+
'non assignments (3)': ['useState;s()', false],
156+
'non assignments (4)': ['useState,s()', false],
157+
'object without methods (1)': ['const obj = { useState: 1 }', false],
158+
'object without methods (2)': ['const obj = { Foo: 1 }', false],
159+
}
160+
161+
for (const [name, [code, expected]] of Object.entries(cases)) {
162+
test(name, () => {
163+
expect(defaultCodeFilter.test(code)).toBe(expected)
164+
})
165+
}
166+
})

packages/plugin-react/src/reactCompilerPreset.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import type {
33
RolldownBabelPreset,
44
} from '#optionalTypes'
55

6+
export const defaultCodeFilter =
7+
/forwardRef|memo|(?:const|let|var|function)\s+(?:[A-Z]|use[A-Z0-9])|(?:[A-Z]|use[A-Z0-9])[^\s:=(){}[\],;]*\s*(?:\(|[:=]\s*(?:function|\())/
8+
69
export const reactCompilerPreset = (
710
options: ReactCompilerBabelPluginOptions = {},
811
): RolldownBabelPreset => ({
@@ -15,7 +18,7 @@ export const reactCompilerPreset = (
1518
code:
1619
options.compilationMode === 'annotation'
1720
? /['"]use memo['"]/
18-
: /\b[A-Z]|\buse/,
21+
: defaultCodeFilter,
1922
},
2023
applyToEnvironmentHook: (env) => env.config.consumer === 'client',
2124
optimizeDeps: {

0 commit comments

Comments
 (0)