Skip to content

Commit 8eadf4c

Browse files
committed
Fix #35105: Continue analysis in ESLint mode despite suppressions
- Modified tryCompileFunction to differentiate between ESLint mode and build mode - In ESLint mode (noEmit: true): Log suppressions but continue compilation - In build mode (noEmit: false): Maintain existing behavior (skip on suppression) - Added 4 test fixtures with snapshots to verify the fix - Fixes issue where eslint-disable comments prevented incompatible-library warnings
1 parent f2860cb commit 8eadf4c

9 files changed

+253
-20
lines changed

compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,10 @@ function tryCompileFunction(
719719
* See: https://github.com/facebook/react/issues/35105
720720
*/
721721
if (!programContext.opts.noEmit) {
722-
// Build mode: maintain existing behavior
723-
// If suppressions exist, skip compilation entirely
722+
/*
723+
* Build mode: maintain existing behavior
724+
* If suppressions exist, skip compilation entirely
725+
*/
724726
return {
725727
kind: 'error',
726728
error: suppressionsToCompilerError(suppressionsInFunction),
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
## Input
3+
4+
```javascript
5+
// Build mode (no @noEmit) - should skip compilation when suppressions exist
6+
import {useEffect, useState} from 'react';
7+
8+
function Component() {
9+
const [count, setCount] = useState(0);
10+
11+
useEffect(() => {
12+
console.log(count);
13+
// eslint-disable-next-line react-hooks/exhaustive-deps
14+
}, []);
15+
16+
return <div>{count}</div>;
17+
}
18+
19+
export const FIXTURE_ENTRYPOINT = {
20+
fn: Component,
21+
params: [],
22+
};
23+
24+
```
25+
26+
27+
## Error
28+
29+
```
30+
Found 1 error:
31+
32+
Error: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled
33+
34+
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable-next-line react-hooks/exhaustive-deps`.
35+
36+
error.build-mode-with-suppression-skips-compilation.ts:9:4
37+
7 | useEffect(() => {
38+
8 | console.log(count);
39+
> 9 | // eslint-disable-next-line react-hooks/exhaustive-deps
40+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Found React rule suppression
41+
10 | }, []);
42+
11 |
43+
12 | return <div>{count}</div>;
44+
```
45+
46+
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
// Build mode (no @noEmit) - should skip compilation when suppressions exist
2-
import { useEffect, useState } from 'react';
2+
import {useEffect, useState} from 'react';
33

44
function Component() {
55
const [count, setCount] = useState(0);
6-
6+
77
useEffect(() => {
88
console.log(count);
99
// eslint-disable-next-line react-hooks/exhaustive-deps
1010
}, []);
11-
11+
1212
return <div>{count}</div>;
1313
}
1414

1515
export const FIXTURE_ENTRYPOINT = {
1616
fn: Component,
1717
params: [],
1818
};
19-
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @noEmit
6+
import {useEffect} from 'react';
7+
8+
// Simulating an incompatible library API
9+
function useVirtualizer(config) {
10+
return {scrollToIndex: () => {}};
11+
}
12+
13+
function MyHook() {
14+
const virtualizer = useVirtualizer({
15+
count: 100,
16+
getScrollElement: () => null,
17+
estimateSize: () => 35,
18+
});
19+
20+
useEffect(() => {
21+
// eslint-disable-next-line react-hooks/exhaustive-deps
22+
}, []);
23+
24+
return virtualizer;
25+
}
26+
27+
export const FIXTURE_ENTRYPOINT = {
28+
fn: MyHook,
29+
params: [],
30+
};
31+
32+
```
33+
34+
## Code
35+
36+
```javascript
37+
// @noEmit
38+
import { useEffect } from "react";
39+
40+
// Simulating an incompatible library API
41+
function useVirtualizer(config) {
42+
return { scrollToIndex: () => {} };
43+
}
44+
45+
function MyHook() {
46+
const virtualizer = useVirtualizer({
47+
count: 100,
48+
getScrollElement: () => null,
49+
estimateSize: () => 35,
50+
});
51+
52+
useEffect(() => {
53+
// eslint-disable-next-line react-hooks/exhaustive-deps
54+
}, []);
55+
return virtualizer;
56+
}
57+
58+
export const FIXTURE_ENTRYPOINT = {
59+
fn: MyHook,
60+
params: [],
61+
};
62+
63+
```
64+
65+
### Eval output
66+
(kind: ok) {"scrollToIndex":"[[ function params=0 ]]"}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// @noEmit
2-
import { useEffect } from 'react';
2+
import {useEffect} from 'react';
33

44
// Simulating an incompatible library API
55
function useVirtualizer(config) {
6-
return { scrollToIndex: () => {} };
6+
return {scrollToIndex: () => {}};
77
}
88

99
function MyHook() {
@@ -12,16 +12,15 @@ function MyHook() {
1212
getScrollElement: () => null,
1313
estimateSize: () => 35,
1414
});
15-
15+
1616
useEffect(() => {
1717
// eslint-disable-next-line react-hooks/exhaustive-deps
1818
}, []);
19-
19+
2020
return virtualizer;
2121
}
2222

2323
export const FIXTURE_ENTRYPOINT = {
2424
fn: MyHook,
2525
params: [],
2626
};
27-
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @noEmit
6+
import {useEffect, useState} from 'react';
7+
8+
function Component() {
9+
const [x, setX] = useState(0);
10+
11+
// eslint-disable-next-line react-hooks/exhaustive-deps
12+
useEffect(() => {
13+
console.log('First effect');
14+
}, []);
15+
16+
// eslint-disable-next-line react-hooks/exhaustive-deps
17+
useEffect(() => {
18+
console.log('Second effect');
19+
}, []);
20+
21+
// eslint-disable-next-line no-console
22+
console.log('test');
23+
24+
return <div>{x}</div>;
25+
}
26+
27+
export const FIXTURE_ENTRYPOINT = {
28+
fn: Component,
29+
params: [],
30+
};
31+
32+
```
33+
34+
## Code
35+
36+
```javascript
37+
// @noEmit
38+
import { useEffect, useState } from "react";
39+
40+
function Component() {
41+
const [x, setX] = useState(0);
42+
43+
// eslint-disable-next-line react-hooks/exhaustive-deps
44+
useEffect(() => {
45+
console.log("First effect");
46+
}, []);
47+
48+
// eslint-disable-next-line react-hooks/exhaustive-deps
49+
useEffect(() => {
50+
console.log("Second effect");
51+
}, []);
52+
53+
// eslint-disable-next-line no-console
54+
console.log("test");
55+
56+
return <div>{x}</div>;
57+
}
58+
59+
export const FIXTURE_ENTRYPOINT = {
60+
fn: Component,
61+
params: [],
62+
};
63+
64+
```
65+
66+
### Eval output
67+
(kind: ok) <div>0</div>
68+
logs: ['test','First effect','Second effect']

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/eslint-mode-multiple-suppressions-continues-analysis.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
// @noEmit
2-
import { useEffect, useState } from 'react';
2+
import {useEffect, useState} from 'react';
33

44
function Component() {
55
const [x, setX] = useState(0);
6-
6+
77
// eslint-disable-next-line react-hooks/exhaustive-deps
88
useEffect(() => {
99
console.log('First effect');
1010
}, []);
11-
11+
1212
// eslint-disable-next-line react-hooks/exhaustive-deps
1313
useEffect(() => {
1414
console.log('Second effect');
1515
}, []);
16-
16+
1717
// eslint-disable-next-line no-console
1818
console.log('test');
19-
19+
2020
return <div>{x}</div>;
2121
}
2222

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @noEmit
6+
import {useEffect, useState} from 'react';
7+
8+
function Component() {
9+
const [count, setCount] = useState(0);
10+
11+
useEffect(() => {
12+
console.log(count);
13+
// eslint-disable-next-line react-hooks/exhaustive-deps
14+
}, []);
15+
16+
const result = count * 2;
17+
return <div>{result}</div>;
18+
}
19+
20+
export const FIXTURE_ENTRYPOINT = {
21+
fn: Component,
22+
params: [],
23+
};
24+
25+
```
26+
27+
## Code
28+
29+
```javascript
30+
// @noEmit
31+
import { useEffect, useState } from "react";
32+
33+
function Component() {
34+
const [count, setCount] = useState(0);
35+
36+
useEffect(() => {
37+
console.log(count);
38+
// eslint-disable-next-line react-hooks/exhaustive-deps
39+
}, []);
40+
41+
const result = count * 2;
42+
return <div>{result}</div>;
43+
}
44+
45+
export const FIXTURE_ENTRYPOINT = {
46+
fn: Component,
47+
params: [],
48+
};
49+
50+
```
51+
52+
### Eval output
53+
(kind: ok) <div>0</div>
54+
logs: [0]
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// @noEmit
2-
import { useEffect, useState } from 'react';
2+
import {useEffect, useState} from 'react';
33

44
function Component() {
55
const [count, setCount] = useState(0);
6-
6+
77
useEffect(() => {
88
console.log(count);
99
// eslint-disable-next-line react-hooks/exhaustive-deps
1010
}, []);
11-
11+
1212
const result = count * 2;
1313
return <div>{result}</div>;
1414
}
@@ -17,4 +17,3 @@ export const FIXTURE_ENTRYPOINT = {
1717
fn: Component,
1818
params: [],
1919
};
20-

0 commit comments

Comments
 (0)