The library for web and native user interfaces.
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
+}