Repro for scope with no declarations (already fixed on this stack)
Repro from T180504728 which reproduced internally and on playground, neither of which have #2687 yet. That PR (earlier in this stack) already fixes the issue, so i'm just adding the repro to help prevent regressions.
Joe Savona committed
Mar 15, 2024 at 08:26 UTC
d5eca2ed8584fd9cbb75403b34741c0f058db395
3 files changed
+151
-1
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+1
-1
@@ -504,7 +504,7 @@ function codegenReactiveScope(
504
if (testCondition === null) {
505
CompilerError.invariant(firstOutputIndex !== null, {
506
reason: `Expected scope to have at least one declaration`,
507
- description: `Scope '@{scope.id} has no declarations`,
507
+ description: `Scope '@${scope.id}' has no declarations`,
508
loc: null,
509
suggestions: null,
510
});
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/repro-no-declarations-in-reactive-scope-with-early-return.expect.md
new
+121
@@ -0,0 +1,121 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
6
+function Component() {
7
+ const items = useItems();
8
+ const filteredItems = useMemo(
9
+ () =>
10
+ items.filter(([item]) => {
11
+ return item.name != null;
12
+ }),
13
+ [item]
14
+ );
15
+
16
+ if (filteredItems.length === 0) {
17
+ // note: this must return nested JSX to create the right scope
18
+ // shape that causes no declarations to be emitted
19
+ return (
20
+ <div>
21
+ <span />
22
+ </div>
23
+ );
24
+ }
25
+
26
+ return (
27
+ <>
28
+ {filteredItems.map(([item]) => (
29
+ <Stringify item={item} />
30
+ ))}
31
+ </>
32
+ );
33
+}
34
+
35
+```
36
+
37
+## Code
38
+
39
+```javascript
40
+import { unstable_useMemoCache as useMemoCache } from "react"; // @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
41
+function Component() {
42
+ const $ = useMemoCache(9);
43
+ const items = useItems();
44
+ let t0;
45
+ let t1;
46
+ let t2;
47
+ if ($[0] !== items) {
48
+ t2 = Symbol.for("react.early_return_sentinel");
49
+ bb14: {
50
+ let t3;
51
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
52
+ t3 = (t4) => {
53
+ const [item] = t4;
54
+ return item.name != null;
55
+ };
56
+ $[4] = t3;
57
+ } else {
58
+ t3 = $[4];
59
+ }
60
+ t0 = items.filter(t3);
61
+ const filteredItems = t0;
62
+ if (filteredItems.length === 0) {
63
+ let t4;
64
+ if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
65
+ t4 = (
66
+ <div>
67
+ <span />
68
+ </div>
69
+ );
70
+ $[5] = t4;
71
+ } else {
72
+ t4 = $[5];
73
+ }
74
+ t2 = t4;
75
+ break bb14;
76
+ }
77
+ let t4;
78
+ if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
79
+ t4 = (t5) => {
80
+ const [item_0] = t5;
81
+ return <Stringify item={item_0} />;
82
+ };
83
+ $[6] = t4;
84
+ } else {
85
+ t4 = $[6];
86
+ }
87
+ t1 = filteredItems.map(t4);
88
+ }
89
+ $[0] = items;
90
+ $[1] = t1;
91
+ $[2] = t2;
92
+ $[3] = t0;
93
+ } else {
94
+ t1 = $[1];
95
+ t2 = $[2];
96
+ t0 = $[3];
97
+ }
98
+ if (t2 !== Symbol.for("react.early_return_sentinel")) {
99
+ return t2;
100
+ }
101
+ let t3;
102
+ if ($[7] !== t1) {
103
+ t3 = <>{t1}</>;
104
+ $[7] = t1;
105
+ $[8] = t3;
106
+ } else {
107
+ t3 = $[8];
108
+ }
109
+ return t3;
110
+}
111
+
112
+```
113
+
114
+### Eval output
115
+(kind: exception) Fixture not implemented!
116
+logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
117
+ '\n' +
118
+ ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
119
+ '\n' +
120
+ 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
121
+ 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/repro-no-declarations-in-reactive-scope-with-early-return.js
new
+29
@@ -0,0 +1,29 @@
1
+// @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
2
+function Component() {
3
+ const items = useItems();
4
+ const filteredItems = useMemo(
5
+ () =>
6
+ items.filter(([item]) => {
7
+ return item.name != null;
8
+ }),
9
+ [item]
10
+ );
11
+
12
+ if (filteredItems.length === 0) {
13
+ // note: this must return nested JSX to create the right scope
14
+ // shape that causes no declarations to be emitted
15
+ return (
16
+ <div>
17
+ <span />
18
+ </div>
19
+ );
20
+ }
21
+
22
+ return (
23
+ <>
24
+ {filteredItems.map(([item]) => (
25
+ <Stringify item={item} />
26
+ ))}
27
+ </>
28
+ );
29
+}