@samitouri / QOS-React-2 / commits / d5eca2ed85

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 +}