[be][sprout] Add ErrorBoundary to test exceptions in sequentialRenders
Mofei Zhang committed
Mar 27, 2024 at 20:26 UTC
1515cb32182e48c25369673607abb76ba773bab6
35 files changed
+650
-269
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-condexpr.expect.md
+21
-13
@@ -6,11 +6,18 @@
6
// scope that produces x, since it is accessed unconditionally in all cfg
7
// paths
8
9
-function TestCondDepInConditionalExpr(props, other) {
10
- const x = foo(other) ? bar(props.a.b) : baz(props.a.b);
9
+import { identity, addOne } from "shared-runtime";
10
+
11
+function useCondDepInConditionalExpr(props, cond) {
12
+ const x = identity(cond) ? addOne(props.a.b) : identity(props.a.b);
13
return x;
14
}
15
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: useCondDepInConditionalExpr,
18
+ params: [{ a: { b: 2 } }, true],
19
+};
20
+
21
```
22
23
## Code
@@ -20,12 +27,14 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // props.a.b shou
27
// scope that produces x, since it is accessed unconditionally in all cfg
28
// paths
29
23
-function TestCondDepInConditionalExpr(props, other) {
30
+import { identity, addOne } from "shared-runtime";
31
+
32
+function useCondDepInConditionalExpr(props, cond) {
33
const $ = useMemoCache(3);
34
let t0;
26
- if ($[0] !== other || $[1] !== props.a.b) {
27
- t0 = foo(other) ? bar(props.a.b) : baz(props.a.b);
28
- $[0] = other;
35
+ if ($[0] !== cond || $[1] !== props.a.b) {
36
+ t0 = identity(cond) ? addOne(props.a.b) : identity(props.a.b);
37
+ $[0] = cond;
38
$[1] = props.a.b;
39
$[2] = t0;
40
} else {
@@ -35,13 +44,12 @@ function TestCondDepInConditionalExpr(props, other) {
44
return x;
45
}
46
47
+export const FIXTURE_ENTRYPOINT = {
48
+ fn: useCondDepInConditionalExpr,
49
+ params: [{ a: { b: 2 } }, true],
50
+};
51
+
52
```
53
54
### Eval output
41
-(kind: exception) Fixture not implemented!
42
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
43
- '\n' +
44
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
45
- '\n' +
46
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
47
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
55
+(kind: ok) 3
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-condexpr.js
+9
-2
@@ -2,7 +2,14 @@
2
// scope that produces x, since it is accessed unconditionally in all cfg
3
// paths
4
5
-function TestCondDepInConditionalExpr(props, other) {
6
- const x = foo(other) ? bar(props.a.b) : baz(props.a.b);
5
+import { identity, addOne } from "shared-runtime";
6
+
7
+function useCondDepInConditionalExpr(props, cond) {
8
+ const x = identity(cond) ? addOne(props.a.b) : identity(props.a.b);
9
return x;
10
}
11
+
12
+export const FIXTURE_ENTRYPOINT = {
13
+ fn: useCondDepInConditionalExpr,
14
+ params: [{ a: { b: 2 } }, true],
15
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-ifelse.expect.md
+21
-13
@@ -6,9 +6,11 @@
6
// scope that produces x, since it is accessed unconditionally in all cfg
7
// paths
8
9
-function TestCondDepInDirectIfElse(props, other) {
9
+import { identity } from "shared-runtime";
10
+
11
+function useCondDepInDirectIfElse(props, cond) {
12
const x = {};
11
- if (foo(other)) {
13
+ if (identity(cond)) {
14
x.b = props.a.b;
15
} else {
16
x.c = props.a.b;
@@ -16,6 +18,11 @@ function TestCondDepInDirectIfElse(props, other) {
18
return x;
19
}
20
21
+export const FIXTURE_ENTRYPOINT = {
22
+ fn: useCondDepInDirectIfElse,
23
+ params: [{ a: { b: 2 } }, true],
24
+};
25
+
26
```
27
28
## Code
@@ -25,17 +32,19 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // props.a.b shou
32
// scope that produces x, since it is accessed unconditionally in all cfg
33
// paths
34
28
-function TestCondDepInDirectIfElse(props, other) {
35
+import { identity } from "shared-runtime";
36
+
37
+function useCondDepInDirectIfElse(props, cond) {
38
const $ = useMemoCache(3);
39
let x;
31
- if ($[0] !== other || $[1] !== props.a.b) {
40
+ if ($[0] !== cond || $[1] !== props.a.b) {
41
x = {};
33
- if (foo(other)) {
42
+ if (identity(cond)) {
43
x.b = props.a.b;
44
} else {
45
x.c = props.a.b;
46
}
38
- $[0] = other;
47
+ $[0] = cond;
48
$[1] = props.a.b;
49
$[2] = x;
50
} else {
@@ -44,13 +53,12 @@ function TestCondDepInDirectIfElse(props, other) {
53
return x;
54
}
55
56
+export const FIXTURE_ENTRYPOINT = {
57
+ fn: useCondDepInDirectIfElse,
58
+ params: [{ a: { b: 2 } }, true],
59
+};
60
+
61
```
62
63
### Eval output
50
-(kind: exception) Fixture not implemented!
51
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
52
- '\n' +
53
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
54
- '\n' +
55
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
56
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
64
+(kind: ok) {"b":2}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-ifelse.js
+9
-2
@@ -2,12 +2,19 @@
2
// scope that produces x, since it is accessed unconditionally in all cfg
3
// paths
4
5
-function TestCondDepInDirectIfElse(props, other) {
5
+import { identity } from "shared-runtime";
6
+
7
+function useCondDepInDirectIfElse(props, cond) {
8
const x = {};
7
- if (foo(other)) {
9
+ if (identity(cond)) {
10
x.b = props.a.b;
11
} else {
12
x.c = props.a.b;
13
}
14
return x;
15
}
16
+
17
+export const FIXTURE_ENTRYPOINT = {
18
+ fn: useCondDepInDirectIfElse,
19
+ params: [{ a: { b: 2 } }, true],
20
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-nested-ifelse-missing.expect.md
+23
-15
@@ -5,10 +5,12 @@
5
// props.a.b should NOT be added as a unconditional dependency to the reactive
6
// scope that produces x if it is not accessed in every path
7
8
-function TestCondDepInNestedIfElse(props, other) {
8
+import { identity, getNull } from "shared-runtime";
9
+
10
+function useCondDepInNestedIfElse(props, cond) {
11
const x = {};
10
- if (foo(other)) {
11
- if (bar()) {
12
+ if (identity(cond)) {
13
+ if (getNull()) {
14
x.a = props.a.b;
15
}
16
} else {
@@ -17,6 +19,11 @@ function TestCondDepInNestedIfElse(props, other) {
19
return x;
20
}
21
22
+export const FIXTURE_ENTRYPOINT = {
23
+ fn: useCondDepInNestedIfElse,
24
+ params: [{ a: { b: 2 } }, true],
25
+};
26
+
27
```
28
29
## Code
@@ -25,19 +32,21 @@ function TestCondDepInNestedIfElse(props, other) {
32
import { unstable_useMemoCache as useMemoCache } from "react"; // props.a.b should NOT be added as a unconditional dependency to the reactive
33
// scope that produces x if it is not accessed in every path
34
28
-function TestCondDepInNestedIfElse(props, other) {
35
+import { identity, getNull } from "shared-runtime";
36
+
37
+function useCondDepInNestedIfElse(props, cond) {
38
const $ = useMemoCache(3);
39
let x;
31
- if ($[0] !== other || $[1] !== props) {
40
+ if ($[0] !== cond || $[1] !== props) {
41
x = {};
33
- if (foo(other)) {
34
- if (bar()) {
42
+ if (identity(cond)) {
43
+ if (getNull()) {
44
x.a = props.a.b;
45
}
46
} else {
47
x.d = props.a.b;
48
}
40
- $[0] = other;
49
+ $[0] = cond;
50
$[1] = props;
51
$[2] = x;
52
} else {
@@ -46,13 +55,12 @@ function TestCondDepInNestedIfElse(props, other) {
55
return x;
56
}
57
58
+export const FIXTURE_ENTRYPOINT = {
59
+ fn: useCondDepInNestedIfElse,
60
+ params: [{ a: { b: 2 } }, true],
61
+};
62
+
63
```
64
65
### Eval output
52
-(kind: exception) Fixture not implemented!
53
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
54
- '\n' +
55
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
56
- '\n' +
57
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
58
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
66
+(kind: ok) {}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-nested-ifelse-missing.js
+10
-3
@@ -1,10 +1,12 @@
1
// props.a.b should NOT be added as a unconditional dependency to the reactive
2
// scope that produces x if it is not accessed in every path
3
4
-function TestCondDepInNestedIfElse(props, other) {
4
+import { identity, getNull } from "shared-runtime";
5
+
6
+function useCondDepInNestedIfElse(props, cond) {
7
const x = {};
6
- if (foo(other)) {
7
- if (bar()) {
8
+ if (identity(cond)) {
9
+ if (getNull()) {
10
x.a = props.a.b;
11
}
12
} else {
@@ -12,3 +14,8 @@ function TestCondDepInNestedIfElse(props, other) {
14
}
15
return x;
16
}
17
+
18
+export const FIXTURE_ENTRYPOINT = {
19
+ fn: useCondDepInNestedIfElse,
20
+ params: [{ a: { b: 2 } }, true],
21
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-nested-ifelse.expect.md
+25
-17
@@ -6,15 +6,17 @@
6
// scope that produces x, since it is accessed unconditionally in all cfg
7
// paths
8
9
-function TestCondDepInNestedIfElse(props, other) {
9
+import { getNull, identity } from "shared-runtime";
10
+
11
+function useCondDepInNestedIfElse(props, cond) {
12
const x = {};
11
- if (foo(other)) {
12
- if (bar()) {
13
+ if (identity(cond)) {
14
+ if (getNull()) {
15
x.a = props.a.b;
16
} else {
17
x.b = props.a.b;
18
}
17
- } else if (baz(other)) {
19
+ } else if (identity(cond)) {
20
x.c = props.a.b;
21
} else {
22
x.d = props.a.b;
@@ -22,6 +24,11 @@ function TestCondDepInNestedIfElse(props, other) {
24
return x;
25
}
26
27
+export const FIXTURE_ENTRYPOINT = {
28
+ fn: useCondDepInNestedIfElse,
29
+ params: [{ a: { b: 2 } }, true],
30
+};
31
+
32
```
33
34
## Code
@@ -31,25 +38,27 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // props.a.b shou
38
// scope that produces x, since it is accessed unconditionally in all cfg
39
// paths
40
34
-function TestCondDepInNestedIfElse(props, other) {
41
+import { getNull, identity } from "shared-runtime";
42
+
43
+function useCondDepInNestedIfElse(props, cond) {
44
const $ = useMemoCache(3);
45
let x;
37
- if ($[0] !== other || $[1] !== props.a.b) {
46
+ if ($[0] !== cond || $[1] !== props.a.b) {
47
x = {};
39
- if (foo(other)) {
40
- if (bar()) {
48
+ if (identity(cond)) {
49
+ if (getNull()) {
50
x.a = props.a.b;
51
} else {
52
x.b = props.a.b;
53
}
54
} else {
46
- if (baz(other)) {
55
+ if (identity(cond)) {
56
x.c = props.a.b;
57
} else {
58
x.d = props.a.b;
59
}
60
}
52
- $[0] = other;
61
+ $[0] = cond;
62
$[1] = props.a.b;
63
$[2] = x;
64
} else {
@@ -58,13 +67,12 @@ function TestCondDepInNestedIfElse(props, other) {
67
return x;
68
}
69
70
+export const FIXTURE_ENTRYPOINT = {
71
+ fn: useCondDepInNestedIfElse,
72
+ params: [{ a: { b: 2 } }, true],
73
+};
74
+
75
```
76
77
### Eval output
64
-(kind: exception) Fixture not implemented!
65
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
66
- '\n' +
67
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
68
- '\n' +
69
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
70
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
78
+(kind: ok) {"b":2}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-nested-ifelse.js
+11
-4
@@ -2,18 +2,25 @@
2
// scope that produces x, since it is accessed unconditionally in all cfg
3
// paths
4
5
-function TestCondDepInNestedIfElse(props, other) {
5
+import { getNull, identity } from "shared-runtime";
6
+
7
+function useCondDepInNestedIfElse(props, cond) {
8
const x = {};
7
- if (foo(other)) {
8
- if (bar()) {
9
+ if (identity(cond)) {
10
+ if (getNull()) {
11
x.a = props.a.b;
12
} else {
13
x.b = props.a.b;
14
}
13
- } else if (baz(other)) {
15
+ } else if (identity(cond)) {
16
x.c = props.a.b;
17
} else {
18
x.d = props.a.b;
19
}
20
return x;
21
}
22
+
23
+export const FIXTURE_ENTRYPOINT = {
24
+ fn: useCondDepInNestedIfElse,
25
+ params: [{ a: { b: 2 } }, true],
26
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-switch-exhaustive.expect.md
+19
-11
@@ -6,9 +6,11 @@
6
// scope that produces x, since it is accessed unconditionally in all cfg
7
// paths
8
9
-function TestCondDepInSwitch(props, other) {
9
+import { identity } from "shared-runtime";
10
+
11
+function useCondDepInSwitch(props, other) {
12
const x = {};
11
- switch (foo(other)) {
13
+ switch (identity(other)) {
14
case 1:
15
x.a = props.a.b;
16
break;
@@ -21,6 +23,11 @@ function TestCondDepInSwitch(props, other) {
23
return x;
24
}
25
26
+export const FIXTURE_ENTRYPOINT = {
27
+ fn: useCondDepInSwitch,
28
+ params: [{ a: { b: 2 } }, 2],
29
+};
30
+
31
```
32
33
## Code
@@ -30,12 +37,14 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // props.a.b shou
37
// scope that produces x, since it is accessed unconditionally in all cfg
38
// paths
39
33
-function TestCondDepInSwitch(props, other) {
40
+import { identity } from "shared-runtime";
41
+
42
+function useCondDepInSwitch(props, other) {
43
const $ = useMemoCache(3);
44
let x;
45
if ($[0] !== other || $[1] !== props.a.b) {
46
x = {};
38
- bb1: switch (foo(other)) {
47
+ bb1: switch (identity(other)) {
48
case 1: {
49
x.a = props.a.b;
50
break bb1;
@@ -57,13 +66,12 @@ function TestCondDepInSwitch(props, other) {
66
return x;
67
}
68
69
+export const FIXTURE_ENTRYPOINT = {
70
+ fn: useCondDepInSwitch,
71
+ params: [{ a: { b: 2 } }, 2],
72
+};
73
+
74
```
75
76
### Eval output
63
-(kind: exception) Fixture not implemented!
64
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
65
- '\n' +
66
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
67
- '\n' +
68
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
69
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
77
+(kind: ok) {"b":2}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-switch-exhaustive.js
+9
-2
@@ -2,9 +2,11 @@
2
// scope that produces x, since it is accessed unconditionally in all cfg
3
// paths
4
5
-function TestCondDepInSwitch(props, other) {
5
+import { identity } from "shared-runtime";
6
+
7
+function useCondDepInSwitch(props, other) {
8
const x = {};
7
- switch (foo(other)) {
9
+ switch (identity(other)) {
10
case 1:
11
x.a = props.a.b;
12
break;
@@ -16,3 +18,8 @@ function TestCondDepInSwitch(props, other) {
18
}
19
return x;
20
}
21
+
22
+export const FIXTURE_ENTRYPOINT = {
23
+ fn: useCondDepInSwitch,
24
+ params: [{ a: { b: 2 } }, 2],
25
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-switch-missing-case.expect.md
+19
-11
@@ -5,9 +5,11 @@
5
// props.a.b should NOT be added as a unconditional dependency to the reactive
6
// scope that produces x if it is not accessed in every path
7
8
-function TestCondDepInSwitchMissingCase(props, other) {
8
+import { identity } from "shared-runtime";
9
+
10
+function useCondDepInSwitchMissingCase(props, other) {
11
const x = {};
10
- switch (foo(other)) {
12
+ switch (identity(other)) {
13
case 1:
14
x.a = props.a.b;
15
break;
@@ -21,6 +23,11 @@ function TestCondDepInSwitchMissingCase(props, other) {
23
return x;
24
}
25
26
+export const FIXTURE_ENTRYPOINT = {
27
+ fn: useCondDepInSwitchMissingCase,
28
+ params: [{ a: { b: 2 } }, 2],
29
+};
30
+
31
```
32
33
## Code
@@ -29,12 +36,14 @@ function TestCondDepInSwitchMissingCase(props, other) {
36
import { unstable_useMemoCache as useMemoCache } from "react"; // props.a.b should NOT be added as a unconditional dependency to the reactive
37
// scope that produces x if it is not accessed in every path
38
32
-function TestCondDepInSwitchMissingCase(props, other) {
39
+import { identity } from "shared-runtime";
40
+
41
+function useCondDepInSwitchMissingCase(props, other) {
42
const $ = useMemoCache(3);
43
let x;
44
if ($[0] !== other || $[1] !== props) {
45
x = {};
37
- bb1: switch (foo(other)) {
46
+ bb1: switch (identity(other)) {
47
case 1: {
48
x.a = props.a.b;
49
break bb1;
@@ -56,13 +65,12 @@ function TestCondDepInSwitchMissingCase(props, other) {
65
return x;
66
}
67
68
+export const FIXTURE_ENTRYPOINT = {
69
+ fn: useCondDepInSwitchMissingCase,
70
+ params: [{ a: { b: 2 } }, 2],
71
+};
72
+
73
```
74
75
### Eval output
62
-(kind: exception) Fixture not implemented!
63
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
64
- '\n' +
65
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
66
- '\n' +
67
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
68
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
76
+(kind: ok) {"b":42}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-switch-missing-case.js
+9
-2
@@ -1,9 +1,11 @@
1
// props.a.b should NOT be added as a unconditional dependency to the reactive
2
// scope that produces x if it is not accessed in every path
3
4
-function TestCondDepInSwitchMissingCase(props, other) {
4
+import { identity } from "shared-runtime";
5
+
6
+function useCondDepInSwitchMissingCase(props, other) {
7
const x = {};
6
- switch (foo(other)) {
8
+ switch (identity(other)) {
9
case 1:
10
x.a = props.a.b;
11
break;
@@ -16,3 +18,8 @@ function TestCondDepInSwitchMissingCase(props, other) {
18
}
19
return x;
20
}
21
+
22
+export const FIXTURE_ENTRYPOINT = {
23
+ fn: useCondDepInSwitchMissingCase,
24
+ params: [{ a: { b: 2 } }, 2],
25
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-switch-missing-default.expect.md
+19
-11
@@ -5,9 +5,11 @@
5
// props.a.b should NOT be added as a unconditional dependency to the reactive
6
// scope that produces x if it is not accessed in the default case.
7
8
-function TestCondDepInSwitchMissingDefault(props, other) {
8
+import { identity } from "shared-runtime";
9
+
10
+function useCondDepInSwitchMissingDefault(props, other) {
11
const x = {};
10
- switch (foo(other)) {
12
+ switch (identity(other)) {
13
case 1:
14
x.a = props.a.b;
15
break;
@@ -18,6 +20,11 @@ function TestCondDepInSwitchMissingDefault(props, other) {
20
return x;
21
}
22
23
+export const FIXTURE_ENTRYPOINT = {
24
+ fn: useCondDepInSwitchMissingDefault,
25
+ params: [{ a: { b: 2 } }, 3],
26
+};
27
+
28
```
29
30
## Code
@@ -26,12 +33,14 @@ function TestCondDepInSwitchMissingDefault(props, other) {
33
import { unstable_useMemoCache as useMemoCache } from "react"; // props.a.b should NOT be added as a unconditional dependency to the reactive
34
// scope that produces x if it is not accessed in the default case.
35
29
-function TestCondDepInSwitchMissingDefault(props, other) {
36
+import { identity } from "shared-runtime";
37
+
38
+function useCondDepInSwitchMissingDefault(props, other) {
39
const $ = useMemoCache(3);
40
let x;
41
if ($[0] !== other || $[1] !== props) {
42
x = {};
34
- bb1: switch (foo(other)) {
43
+ bb1: switch (identity(other)) {
44
case 1: {
45
x.a = props.a.b;
46
break bb1;
@@ -49,13 +58,12 @@ function TestCondDepInSwitchMissingDefault(props, other) {
58
return x;
59
}
60
61
+export const FIXTURE_ENTRYPOINT = {
62
+ fn: useCondDepInSwitchMissingDefault,
63
+ params: [{ a: { b: 2 } }, 3],
64
+};
65
+
66
```
67
68
### Eval output
55
-(kind: exception) Fixture not implemented!
56
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
57
- '\n' +
58
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
59
- '\n' +
60
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
61
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
69
+(kind: ok) {}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cfg-switch-missing-default.js
+9
-2
@@ -1,9 +1,11 @@
1
// props.a.b should NOT be added as a unconditional dependency to the reactive
2
// scope that produces x if it is not accessed in the default case.
3
4
-function TestCondDepInSwitchMissingDefault(props, other) {
4
+import { identity } from "shared-runtime";
5
+
6
+function useCondDepInSwitchMissingDefault(props, other) {
7
const x = {};
6
- switch (foo(other)) {
8
+ switch (identity(other)) {
9
case 1:
10
x.a = props.a.b;
11
break;
@@ -13,3 +15,8 @@ function TestCondDepInSwitchMissingDefault(props, other) {
15
}
16
return x;
17
}
18
+
19
+export const FIXTURE_ENTRYPOINT = {
20
+ fn: useCondDepInSwitchMissingDefault,
21
+ params: [{ a: { b: 2 } }, 3],
22
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cond-scope.expect.md
+21
-13
@@ -20,15 +20,22 @@
20
// return x;
21
// ```
22
23
-function TestReactiveDepsInCondScope(props) {
23
+import { CONST_FALSE, identity } from "shared-runtime";
24
+
25
+function useReactiveDepsInCondScope(props) {
26
let x = {};
25
- if (foo) {
26
- let tmp = bar(props.a.b);
27
+ if (CONST_FALSE) {
28
+ let tmp = identity(props.a.b);
29
x.a = tmp;
30
}
31
return x;
32
}
33
34
+export const FIXTURE_ENTRYPOINT = {
35
+ fn: useReactiveDepsInCondScope,
36
+ params: [{}],
37
+};
38
+
39
```
40
41
## Code
@@ -52,15 +59,17 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // Some reactive
59
// return x;
60
// ```
61
55
-function TestReactiveDepsInCondScope(props) {
62
+import { CONST_FALSE, identity } from "shared-runtime";
63
+
64
+function useReactiveDepsInCondScope(props) {
65
const $ = useMemoCache(4);
66
let x;
67
if ($[0] !== props) {
68
x = {};
60
- if (foo) {
69
+ if (CONST_FALSE) {
70
let t0;
71
if ($[2] !== props.a.b) {
63
- t0 = bar(props.a.b);
72
+ t0 = identity(props.a.b);
73
$[2] = props.a.b;
74
$[3] = t0;
75
} else {
@@ -77,13 +86,12 @@ function TestReactiveDepsInCondScope(props) {
86
return x;
87
}
88
89
+export const FIXTURE_ENTRYPOINT = {
90
+ fn: useReactiveDepsInCondScope,
91
+ params: [{}],
92
+};
93
+
94
```
95
96
### Eval output
83
-(kind: exception) Fixture not implemented!
84
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
85
- '\n' +
86
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
87
- '\n' +
88
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
89
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
97
+(kind: ok) {}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/cond-scope.js
+10
-3
@@ -16,11 +16,18 @@
16
// return x;
17
// ```
18
19
-function TestReactiveDepsInCondScope(props) {
19
+import { CONST_FALSE, identity } from "shared-runtime";
20
+
21
+function useReactiveDepsInCondScope(props) {
22
let x = {};
21
- if (foo) {
22
- let tmp = bar(props.a.b);
23
+ if (CONST_FALSE) {
24
+ let tmp = identity(props.a.b);
25
x.a = tmp;
26
}
27
return x;
28
}
29
+
30
+export const FIXTURE_ENTRYPOINT = {
31
+ fn: useReactiveDepsInCondScope,
32
+ params: [{}],
33
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.expect.md
+23
-15
@@ -19,16 +19,23 @@
19
// mutate2(y, props.a.b);
20
// }
21
22
-function TestJoinCondDepsInUncondScopes(props) {
22
+import { CONST_TRUE, setProperty } from "shared-runtime";
23
+
24
+function useJoinCondDepsInUncondScopes(props) {
25
let y = {};
26
let x = {};
25
- if (foo) {
26
- mutate1(x, props.a.b);
27
+ if (CONST_TRUE) {
28
+ setProperty(x, props.a.b);
29
}
28
- mutate2(y, props.a.b);
30
+ setProperty(y, props.a.b);
31
return [x, y];
32
}
33
34
+export const FIXTURE_ENTRYPOINT = {
35
+ fn: useJoinCondDepsInUncondScopes,
36
+ params: [{ a: { b: 3 } }],
37
+};
38
+
39
```
40
41
## Code
@@ -51,7 +58,9 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // This tests an
58
// mutate2(y, props.a.b);
59
// }
60
54
-function TestJoinCondDepsInUncondScopes(props) {
61
+import { CONST_TRUE, setProperty } from "shared-runtime";
62
+
63
+function useJoinCondDepsInUncondScopes(props) {
64
const $ = useMemoCache(8);
65
let x;
66
let y;
@@ -59,8 +68,8 @@ function TestJoinCondDepsInUncondScopes(props) {
68
y = {};
69
if ($[3] !== props) {
70
x = {};
62
- if (foo) {
63
- mutate1(x, props.a.b);
71
+ if (CONST_TRUE) {
72
+ setProperty(x, props.a.b);
73
}
74
$[3] = props;
75
$[4] = x;
@@ -68,7 +77,7 @@ function TestJoinCondDepsInUncondScopes(props) {
77
x = $[4];
78
}
79
71
- mutate2(y, props.a.b);
80
+ setProperty(y, props.a.b);
81
$[0] = props.a.b;
82
$[1] = x;
83
$[2] = y;
@@ -88,13 +97,12 @@ function TestJoinCondDepsInUncondScopes(props) {
97
return t0;
98
}
99
100
+export const FIXTURE_ENTRYPOINT = {
101
+ fn: useJoinCondDepsInUncondScopes,
102
+ params: [{ a: { b: 3 } }],
103
+};
104
+
105
```
106
107
### Eval output
94
-(kind: exception) Fixture not implemented!
95
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
96
- '\n' +
97
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
98
- '\n' +
99
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
100
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
108
+(kind: ok) [{"wat0":3},{"wat0":3}]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/join-uncond-scopes-cond-deps.js
+11
-4
@@ -15,12 +15,19 @@
15
// mutate2(y, props.a.b);
16
// }
17
18
-function TestJoinCondDepsInUncondScopes(props) {
18
+import { CONST_TRUE, setProperty } from "shared-runtime";
19
+
20
+function useJoinCondDepsInUncondScopes(props) {
21
let y = {};
22
let x = {};
21
- if (foo) {
22
- mutate1(x, props.a.b);
23
+ if (CONST_TRUE) {
24
+ setProperty(x, props.a.b);
25
}
24
- mutate2(y, props.a.b);
26
+ setProperty(y, props.a.b);
27
return [x, y];
28
}
29
+
30
+export const FIXTURE_ENTRYPOINT = {
31
+ fn: useJoinCondDepsInUncondScopes,
32
+ params: [{ a: { b: 3 } }],
33
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/no-uncond.expect.md
+47
-13
@@ -3,33 +3,54 @@
3
4
```javascript
5
// When an object's properties are only read conditionally, we should
6
+
7
+import { identity } from "shared-runtime";
8
+
9
// track the base object as a dependency.
7
-function TestOnlyConditionalDependencies(props, other) {
10
+function useOnlyConditionalDependencies({ props, cond }) {
11
const x = {};
9
- if (foo(other)) {
12
+ if (identity(cond)) {
13
x.b = props.a.b;
14
x.c = props.a.b.c;
15
}
16
return x;
17
}
18
19
+export const FIXTURE_ENTRYPOINT = {
20
+ fn: useOnlyConditionalDependencies,
21
+ params: [{ props: { a: { b: 2 } }, cond: true }],
22
+ sequentialRenders: [
23
+ { props: { a: { b: 2 } }, cond: true },
24
+ { props: null, cond: false },
25
+ // check we preserve nullthrows
26
+ { props: { a: { b: { c: undefined } } }, cond: true },
27
+ { props: { a: { b: undefined } }, cond: true },
28
+ { props: { a: { b: { c: undefined } } }, cond: true },
29
+ { props: undefined, cond: true },
30
+ ],
31
+};
32
+
33
```
34
35
## Code
36
37
```javascript
38
import { unstable_useMemoCache as useMemoCache } from "react"; // When an object's properties are only read conditionally, we should
39
+
40
+import { identity } from "shared-runtime";
41
+
42
// track the base object as a dependency.
23
-function TestOnlyConditionalDependencies(props, other) {
43
+function useOnlyConditionalDependencies(t0) {
44
const $ = useMemoCache(3);
45
+ const { props, cond } = t0;
46
let x;
26
- if ($[0] !== other || $[1] !== props) {
47
+ if ($[0] !== cond || $[1] !== props) {
48
x = {};
28
- if (foo(other)) {
49
+ if (identity(cond)) {
50
x.b = props.a.b;
51
x.c = props.a.b.c;
52
}
32
- $[0] = other;
53
+ $[0] = cond;
54
$[1] = props;
55
$[2] = x;
56
} else {
@@ -38,13 +59,26 @@ function TestOnlyConditionalDependencies(props, other) {
59
return x;
60
}
61
62
+export const FIXTURE_ENTRYPOINT = {
63
+ fn: useOnlyConditionalDependencies,
64
+ params: [{ props: { a: { b: 2 } }, cond: true }],
65
+ sequentialRenders: [
66
+ { props: { a: { b: 2 } }, cond: true },
67
+ { props: null, cond: false },
68
+ // check we preserve nullthrows
69
+ { props: { a: { b: { c: undefined } } }, cond: true },
70
+ { props: { a: { b: undefined } }, cond: true },
71
+ { props: { a: { b: { c: undefined } } }, cond: true },
72
+ { props: undefined, cond: true },
73
+ ],
74
+};
75
+
76
```
77
78
### Eval output
44
-(kind: exception) Fixture not implemented!
45
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
46
- '\n' +
47
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
48
- '\n' +
49
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
50
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
79
+(kind: ok) {"b":2}
80
+{}
81
+{"b":{}}
82
+[[ (exception in render) TypeError: Cannot read properties of undefined (reading 'c') ]]
83
+{"b":{}}
84
+[[ (exception in render) TypeError: Cannot read properties of undefined (reading 'a') ]]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/no-uncond.js
+19
-2
@@ -1,10 +1,27 @@
1
// When an object's properties are only read conditionally, we should
2
+
3
+import { identity } from "shared-runtime";
4
+
5
// track the base object as a dependency.
3
-function TestOnlyConditionalDependencies(props, other) {
6
+function useOnlyConditionalDependencies({ props, cond }) {
7
const x = {};
5
- if (foo(other)) {
8
+ if (identity(cond)) {
9
x.b = props.a.b;
10
x.c = props.a.b.c;
11
}
12
return x;
13
}
14
+
15
+export const FIXTURE_ENTRYPOINT = {
16
+ fn: useOnlyConditionalDependencies,
17
+ params: [{ props: { a: { b: 2 } }, cond: true }],
18
+ sequentialRenders: [
19
+ { props: { a: { b: 2 } }, cond: true },
20
+ { props: null, cond: false },
21
+ // check we preserve nullthrows
22
+ { props: { a: { b: { c: undefined } } }, cond: true },
23
+ { props: { a: { b: undefined } }, cond: true },
24
+ { props: { a: { b: { c: undefined } } }, cond: true },
25
+ { props: undefined, cond: true },
26
+ ],
27
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/promote-uncond.expect.md
+21
-11
@@ -4,16 +4,24 @@
4
```javascript
5
// When a conditional dependency `props.a.b.c` has no unconditional dependency
6
// in its subpath or superpath, we should find the nearest unconditional access
7
+
8
+import { identity } from "shared-runtime";
9
+
10
// and promote it to an unconditional dependency.
8
-function TestPromoteUnconditionalAccessToDependency(props, other) {
11
+function usePromoteUnconditionalAccessToDependency(props, other) {
12
const x = {};
13
x.a = props.a.a.a;
11
- if (foo(other)) {
14
+ if (identity(other)) {
15
x.c = props.a.b.c;
16
}
17
return x;
18
}
19
20
+export const FIXTURE_ENTRYPOINT = {
21
+ fn: usePromoteUnconditionalAccessToDependency,
22
+ params: [{ a: { a: { a: 3 } } }, false],
23
+};
24
+
25
```
26
27
## Code
@@ -21,14 +29,17 @@ function TestPromoteUnconditionalAccessToDependency(props, other) {
29
```javascript
30
import { unstable_useMemoCache as useMemoCache } from "react"; // When a conditional dependency `props.a.b.c` has no unconditional dependency
31
// in its subpath or superpath, we should find the nearest unconditional access
32
+
33
+import { identity } from "shared-runtime";
34
+
35
// and promote it to an unconditional dependency.
25
-function TestPromoteUnconditionalAccessToDependency(props, other) {
36
+function usePromoteUnconditionalAccessToDependency(props, other) {
37
const $ = useMemoCache(3);
38
let x;
39
if ($[0] !== props.a || $[1] !== other) {
40
x = {};
41
x.a = props.a.a.a;
31
- if (foo(other)) {
42
+ if (identity(other)) {
43
x.c = props.a.b.c;
44
}
45
$[0] = props.a;
@@ -40,13 +51,12 @@ function TestPromoteUnconditionalAccessToDependency(props, other) {
51
return x;
52
}
53
54
+export const FIXTURE_ENTRYPOINT = {
55
+ fn: usePromoteUnconditionalAccessToDependency,
56
+ params: [{ a: { a: { a: 3 } } }, false],
57
+};
58
+
59
```
60
61
### Eval output
46
-(kind: exception) Fixture not implemented!
47
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
48
- '\n' +
49
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
50
- '\n' +
51
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
52
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
62
+(kind: ok) {"a":3}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/promote-uncond.js
+10
-2
@@ -1,11 +1,19 @@
1
// When a conditional dependency `props.a.b.c` has no unconditional dependency
2
// in its subpath or superpath, we should find the nearest unconditional access
3
+
4
+import { identity } from "shared-runtime";
5
+
6
// and promote it to an unconditional dependency.
4
-function TestPromoteUnconditionalAccessToDependency(props, other) {
7
+function usePromoteUnconditionalAccessToDependency(props, other) {
8
const x = {};
9
x.a = props.a.a.a;
7
- if (foo(other)) {
10
+ if (identity(other)) {
11
x.c = props.a.b.c;
12
}
13
return x;
14
}
15
+
16
+export const FIXTURE_ENTRYPOINT = {
17
+ fn: usePromoteUnconditionalAccessToDependency,
18
+ params: [{ a: { a: { a: 3 } } }, false],
19
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/subpath-order1.expect.md
+23
-13
@@ -6,16 +6,24 @@
6
// dependency `props.a.b`, we can access `props.a` while preserving program
7
// semantics (with respect to nullthrows).
8
// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
9
+
10
+import { identity } from "shared-runtime";
11
+
12
// ordering of accesses should not matter
10
-function TestConditionalSubpath1(props, other) {
13
+function useConditionalSubpath1(props, cond) {
14
const x = {};
15
x.b = props.a.b;
13
- if (foo(other)) {
16
+ if (identity(cond)) {
17
x.a = props.a;
18
}
19
return x;
20
}
21
22
+export const FIXTURE_ENTRYPOINT = {
23
+ fn: useConditionalSubpath1,
24
+ params: [{ a: { b: 3 } }, false],
25
+};
26
+
27
```
28
29
## Code
@@ -25,18 +33,21 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // When a conditi
33
// dependency `props.a.b`, we can access `props.a` while preserving program
34
// semantics (with respect to nullthrows).
35
// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
36
+
37
+import { identity } from "shared-runtime";
38
+
39
// ordering of accesses should not matter
29
-function TestConditionalSubpath1(props, other) {
40
+function useConditionalSubpath1(props, cond) {
41
const $ = useMemoCache(3);
42
let x;
32
- if ($[0] !== props.a || $[1] !== other) {
43
+ if ($[0] !== props.a || $[1] !== cond) {
44
x = {};
45
x.b = props.a.b;
35
- if (foo(other)) {
46
+ if (identity(cond)) {
47
x.a = props.a;
48
}
49
$[0] = props.a;
39
- $[1] = other;
50
+ $[1] = cond;
51
$[2] = x;
52
} else {
53
x = $[2];
@@ -44,13 +55,12 @@ function TestConditionalSubpath1(props, other) {
55
return x;
56
}
57
58
+export const FIXTURE_ENTRYPOINT = {
59
+ fn: useConditionalSubpath1,
60
+ params: [{ a: { b: 3 } }, false],
61
+};
62
+
63
```
64
65
### Eval output
50
-(kind: exception) Fixture not implemented!
51
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
52
- '\n' +
53
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
54
- '\n' +
55
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
56
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
66
+(kind: ok) {"b":3}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/subpath-order1.js
+10
-2
@@ -2,12 +2,20 @@
2
// dependency `props.a.b`, we can access `props.a` while preserving program
3
// semantics (with respect to nullthrows).
4
// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
5
+
6
+import { identity } from "shared-runtime";
7
+
8
// ordering of accesses should not matter
6
-function TestConditionalSubpath1(props, other) {
9
+function useConditionalSubpath1(props, cond) {
10
const x = {};
11
x.b = props.a.b;
9
- if (foo(other)) {
12
+ if (identity(cond)) {
13
x.a = props.a;
14
}
15
return x;
16
}
17
+
18
+export const FIXTURE_ENTRYPOINT = {
19
+ fn: useConditionalSubpath1,
20
+ params: [{ a: { b: 3 } }, false],
21
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/subpath-order2.expect.md
+21
-11
@@ -6,16 +6,24 @@
6
// dependency `props.a.b`, we can access `props.a` while preserving program
7
// semantics (with respect to nullthrows).
8
// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
9
+
10
+import { identity } from "shared-runtime";
11
+
12
// ordering of accesses should not matter
10
-function TestConditionalSubpath2(props, other) {
13
+function useConditionalSubpath2(props, other) {
14
const x = {};
12
- if (foo(other)) {
15
+ if (identity(other)) {
16
x.a = props.a;
17
}
18
x.b = props.a.b;
19
return x;
20
}
21
22
+export const FIXTURE_ENTRYPOINT = {
23
+ fn: useConditionalSubpath2,
24
+ params: [{ a: { b: 3 } }, false],
25
+};
26
+
27
```
28
29
## Code
@@ -25,13 +33,16 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // When a conditi
33
// dependency `props.a.b`, we can access `props.a` while preserving program
34
// semantics (with respect to nullthrows).
35
// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
36
+
37
+import { identity } from "shared-runtime";
38
+
39
// ordering of accesses should not matter
29
-function TestConditionalSubpath2(props, other) {
40
+function useConditionalSubpath2(props, other) {
41
const $ = useMemoCache(3);
42
let x;
43
if ($[0] !== other || $[1] !== props.a) {
44
x = {};
34
- if (foo(other)) {
45
+ if (identity(other)) {
46
x.a = props.a;
47
}
48
@@ -45,13 +56,12 @@ function TestConditionalSubpath2(props, other) {
56
return x;
57
}
58
59
+export const FIXTURE_ENTRYPOINT = {
60
+ fn: useConditionalSubpath2,
61
+ params: [{ a: { b: 3 } }, false],
62
+};
63
+
64
```
65
66
### Eval output
51
-(kind: exception) Fixture not implemented!
52
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
53
- '\n' +
54
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
55
- '\n' +
56
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
57
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
67
+(kind: ok) {"b":3}
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/subpath-order2.js
+10
-2
@@ -2,12 +2,20 @@
2
// dependency `props.a.b`, we can access `props.a` while preserving program
3
// semantics (with respect to nullthrows).
4
// deps: {`props.a`, `props.a.b`} can further reduce to just `props.a`
5
+
6
+import { identity } from "shared-runtime";
7
+
8
// ordering of accesses should not matter
6
-function TestConditionalSubpath2(props, other) {
9
+function useConditionalSubpath2(props, other) {
10
const x = {};
8
- if (foo(other)) {
11
+ if (identity(other)) {
12
x.a = props.a;
13
}
14
x.b = props.a.b;
15
return x;
16
}
17
+
18
+export const FIXTURE_ENTRYPOINT = {
19
+ fn: useConditionalSubpath2,
20
+ params: [{ a: { b: 3 } }, false],
21
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/superpath-order1.expect.md
+47
-13
@@ -5,16 +5,33 @@
5
// When an unconditional dependency `props.a` is the subpath of a conditional
6
// dependency `props.a.b`, we can safely overestimate and only track `props.a`
7
// as a dependency
8
+
9
+import { identity } from "shared-runtime";
10
+
11
// ordering of accesses should not matter
9
-function TestConditionalSuperpath1(props, other) {
12
+function useConditionalSuperpath1({ props, cond }) {
13
const x = {};
14
x.a = props.a;
12
- if (foo(other)) {
15
+ if (identity(cond)) {
16
x.b = props.a.b;
17
}
18
return x;
19
}
20
21
+export const FIXTURE_ENTRYPOINT = {
22
+ fn: useConditionalSuperpath1,
23
+ params: [{ props: { a: null }, cond: false }],
24
+ sequentialRenders: [
25
+ { props: { a: null }, cond: false },
26
+ { props: { a: {} }, cond: true },
27
+ { props: { a: { b: 3 } }, cond: true },
28
+ { props: {}, cond: false },
29
+ // test that we preserve nullthrows
30
+ { props: { a: { b: undefined } }, cond: true },
31
+ { props: { a: undefined }, cond: true },
32
+ ],
33
+};
34
+
35
```
36
37
## Code
@@ -23,18 +40,22 @@ function TestConditionalSuperpath1(props, other) {
40
import { unstable_useMemoCache as useMemoCache } from "react"; // When an unconditional dependency `props.a` is the subpath of a conditional
41
// dependency `props.a.b`, we can safely overestimate and only track `props.a`
42
// as a dependency
43
+
44
+import { identity } from "shared-runtime";
45
+
46
// ordering of accesses should not matter
27
-function TestConditionalSuperpath1(props, other) {
47
+function useConditionalSuperpath1(t0) {
48
const $ = useMemoCache(3);
49
+ const { props, cond } = t0;
50
let x;
30
- if ($[0] !== props.a || $[1] !== other) {
51
+ if ($[0] !== props.a || $[1] !== cond) {
52
x = {};
53
x.a = props.a;
33
- if (foo(other)) {
54
+ if (identity(cond)) {
55
x.b = props.a.b;
56
}
57
$[0] = props.a;
37
- $[1] = other;
58
+ $[1] = cond;
59
$[2] = x;
60
} else {
61
x = $[2];
@@ -42,13 +63,26 @@ function TestConditionalSuperpath1(props, other) {
63
return x;
64
}
65
66
+export const FIXTURE_ENTRYPOINT = {
67
+ fn: useConditionalSuperpath1,
68
+ params: [{ props: { a: null }, cond: false }],
69
+ sequentialRenders: [
70
+ { props: { a: null }, cond: false },
71
+ { props: { a: {} }, cond: true },
72
+ { props: { a: { b: 3 } }, cond: true },
73
+ { props: {}, cond: false },
74
+ // test that we preserve nullthrows
75
+ { props: { a: { b: undefined } }, cond: true },
76
+ { props: { a: undefined }, cond: true },
77
+ ],
78
+};
79
+
80
```
81
82
### Eval output
48
-(kind: exception) Fixture not implemented!
49
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
50
- '\n' +
51
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
52
- '\n' +
53
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
54
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
83
+(kind: ok) {"a":null}
84
+{"a":{}}
85
+{"a":{"b":3},"b":3}
86
+{}
87
+{"a":{}}
88
+[[ (exception in render) TypeError: Cannot read properties of undefined (reading 'b') ]]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/superpath-order1.js
+19
-2
@@ -1,12 +1,29 @@
1
// When an unconditional dependency `props.a` is the subpath of a conditional
2
// dependency `props.a.b`, we can safely overestimate and only track `props.a`
3
// as a dependency
4
+
5
+import { identity } from "shared-runtime";
6
+
7
// ordering of accesses should not matter
5
-function TestConditionalSuperpath1(props, other) {
8
+function useConditionalSuperpath1({ props, cond }) {
9
const x = {};
10
x.a = props.a;
8
- if (foo(other)) {
11
+ if (identity(cond)) {
12
x.b = props.a.b;
13
}
14
return x;
15
}
16
+
17
+export const FIXTURE_ENTRYPOINT = {
18
+ fn: useConditionalSuperpath1,
19
+ params: [{ props: { a: null }, cond: false }],
20
+ sequentialRenders: [
21
+ { props: { a: null }, cond: false },
22
+ { props: { a: {} }, cond: true },
23
+ { props: { a: { b: 3 } }, cond: true },
24
+ { props: {}, cond: false },
25
+ // test that we preserve nullthrows
26
+ { props: { a: { b: undefined } }, cond: true },
27
+ { props: { a: undefined }, cond: true },
28
+ ],
29
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/superpath-order2.expect.md
+47
-13
@@ -5,16 +5,33 @@
5
// When an unconditional dependency `props.a` is the subpath of a conditional
6
// dependency `props.a.b`, we can safely overestimate and only track `props.a`
7
// as a dependency
8
+
9
+import { identity } from "shared-runtime";
10
+
11
// ordering of accesses should not matter
9
-function TestConditionalSuperpath2(props, other) {
12
+function useConditionalSuperpath2({ props, cond }) {
13
const x = {};
11
- if (foo(other)) {
14
+ if (identity(cond)) {
15
x.b = props.a.b;
16
}
17
x.a = props.a;
18
return x;
19
}
20
21
+export const FIXTURE_ENTRYPOINT = {
22
+ fn: useConditionalSuperpath2,
23
+ params: [{ props: { a: null }, cond: false }],
24
+ sequentialRenders: [
25
+ { props: { a: null }, cond: false },
26
+ { props: { a: {} }, cond: true },
27
+ { props: { a: { b: 3 } }, cond: true },
28
+ { props: {}, cond: false },
29
+ // test that we preserve nullthrows
30
+ { props: { a: { b: undefined } }, cond: true },
31
+ { props: { a: undefined }, cond: true },
32
+ ],
33
+};
34
+
35
```
36
37
## Code
@@ -23,18 +40,22 @@ function TestConditionalSuperpath2(props, other) {
40
import { unstable_useMemoCache as useMemoCache } from "react"; // When an unconditional dependency `props.a` is the subpath of a conditional
41
// dependency `props.a.b`, we can safely overestimate and only track `props.a`
42
// as a dependency
43
+
44
+import { identity } from "shared-runtime";
45
+
46
// ordering of accesses should not matter
27
-function TestConditionalSuperpath2(props, other) {
47
+function useConditionalSuperpath2(t0) {
48
const $ = useMemoCache(3);
49
+ const { props, cond } = t0;
50
let x;
30
- if ($[0] !== other || $[1] !== props.a) {
51
+ if ($[0] !== cond || $[1] !== props.a) {
52
x = {};
32
- if (foo(other)) {
53
+ if (identity(cond)) {
54
x.b = props.a.b;
55
}
56
57
x.a = props.a;
37
- $[0] = other;
58
+ $[0] = cond;
59
$[1] = props.a;
60
$[2] = x;
61
} else {
@@ -43,13 +64,26 @@ function TestConditionalSuperpath2(props, other) {
64
return x;
65
}
66
67
+export const FIXTURE_ENTRYPOINT = {
68
+ fn: useConditionalSuperpath2,
69
+ params: [{ props: { a: null }, cond: false }],
70
+ sequentialRenders: [
71
+ { props: { a: null }, cond: false },
72
+ { props: { a: {} }, cond: true },
73
+ { props: { a: { b: 3 } }, cond: true },
74
+ { props: {}, cond: false },
75
+ // test that we preserve nullthrows
76
+ { props: { a: { b: undefined } }, cond: true },
77
+ { props: { a: undefined }, cond: true },
78
+ ],
79
+};
80
+
81
```
82
83
### Eval output
49
-(kind: exception) Fixture not implemented!
50
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
51
- '\n' +
52
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
53
- '\n' +
54
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
55
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
84
+(kind: ok) {"a":null}
85
+{"a":{}}
86
+{"b":3,"a":{"b":3}}
87
+{}
88
+{"a":{}}
89
+[[ (exception in render) TypeError: Cannot read properties of undefined (reading 'b') ]]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/reduce-reactive-deps/superpath-order2.js
+19
-2
@@ -1,12 +1,29 @@
1
// When an unconditional dependency `props.a` is the subpath of a conditional
2
// dependency `props.a.b`, we can safely overestimate and only track `props.a`
3
// as a dependency
4
+
5
+import { identity } from "shared-runtime";
6
+
7
// ordering of accesses should not matter
5
-function TestConditionalSuperpath2(props, other) {
8
+function useConditionalSuperpath2({ props, cond }) {
9
const x = {};
7
- if (foo(other)) {
10
+ if (identity(cond)) {
11
x.b = props.a.b;
12
}
13
x.a = props.a;
14
return x;
15
}
16
+
17
+export const FIXTURE_ENTRYPOINT = {
18
+ fn: useConditionalSuperpath2,
19
+ params: [{ props: { a: null }, cond: false }],
20
+ sequentialRenders: [
21
+ { props: { a: null }, cond: false },
22
+ { props: { a: {} }, cond: true },
23
+ { props: { a: { b: 3 } }, cond: true },
24
+ { props: {}, cond: false },
25
+ // test that we preserve nullthrows
26
+ { props: { a: { b: undefined } }, cond: true },
27
+ { props: { a: undefined }, cond: true },
28
+ ],
29
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/repro-no-declarations-in-reactive-scope-with-early-return.expect.md
+1
-7
@@ -112,10 +112,4 @@ function Component() {
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
115
+(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/repro-no-value-for-temporary.expect.md
+1
-7
@@ -51,10 +51,4 @@ function Component(listItem, thread) {
51
```
52
53
### Eval output
54
-(kind: exception) Fixture not implemented!
55
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
56
- '\n' +
57
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
58
- '\n' +
59
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
60
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
54
+(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/rules-of-hooks/allow-locals-named-like-hooks.expect.md
+1
-7
@@ -75,10 +75,4 @@ export const FIXTURE_ENTRYPOINT = {
75
```
76
77
### Eval output
78
-(kind: exception) Stringify is not defined
79
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
80
- '\n' +
81
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
82
- '\n' +
83
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
84
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
78
+(kind: exception) Stringify is not defined
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ssa-throw.expect.md
+1
-7
@@ -34,10 +34,4 @@ export const FIXTURE_ENTRYPOINT = {
34
```
35
36
### Eval output
37
-(kind: exception) undefined
38
-logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
39
- '\n' +
40
- ' at WrapperTestComponent (<project_root>/packages/snap/dist/sprout/evaluator.js:54:26)\n' +
41
- '\n' +
42
- 'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
43
- 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
\ No newline at end of file
37
+(kind: exception) undefined
\ No newline at end of file
compiler/packages/snap/src/sprout/evaluator.ts
+75
-12
@@ -7,12 +7,11 @@
7
8
import { render } from "@testing-library/react";
9
import { JSDOM } from "jsdom";
10
+import React, { MutableRefObject } from "react";
11
import util from "util";
12
import { z } from "zod";
13
import { fromZodError } from "zod-validation-error";
13
-import { PROJECT_ROOT } from "../constants";
14
import { initFbt, toJSON } from "./shared-runtime";
15
-const React = require("react");
15
16
/**
17
* Set up the global environment for JSDOM tests.
@@ -31,7 +30,6 @@ initFbt();
30
(globalThis as any).placeholderFn = function (..._args: Array<any>) {
31
throw new Error("Fixture not implemented!");
32
};
34
-
33
export type EvaluatorResult = {
34
kind: "ok" | "exception" | "UnexpectedError";
35
value: string;
@@ -57,6 +55,45 @@ const ExportSchema = z.object({
55
FIXTURE_ENTRYPOINT: EntrypointSchema,
56
});
57
58
+/**
59
+ * Wraps WrapperTestComponent in an error boundary to simplify re-rendering
60
+ * when an exception is thrown.
61
+ * A simpler alternative may be to re-mount test components manually.
62
+ */
63
+class WrapperTestComponentWithErrorBoundary extends React.Component<
64
+ { fn: any; params: Array<any> },
65
+ { hasError: boolean; error: any }
66
+> {
67
+ propsErrorMap: MutableRefObject<Map<any, any>>;
68
+ constructor(props: any) {
69
+ super(props);
70
+ this.state = { hasError: false, error: null };
71
+ this.propsErrorMap = React.createRef() as MutableRefObject<Map<any, any>>;
72
+ this.propsErrorMap.current = new Map();
73
+ }
74
+ static getDerivedStateFromError(error: any) {
75
+ return { hasError: true, error: error };
76
+ }
77
+ override componentDidUpdate() {
78
+ if (this.state.hasError) {
79
+ this.setState({ hasError: false, error: null });
80
+ }
81
+ }
82
+ override render() {
83
+ if (this.state.hasError) {
84
+ this.propsErrorMap.current!.set(
85
+ this.props,
86
+ `[[ (exception in render) ${this.state.error?.toString()} ]]`
87
+ );
88
+ }
89
+ const cachedError = this.propsErrorMap.current!.get(this.props);
90
+ if (cachedError != null) {
91
+ return cachedError;
92
+ }
93
+ return React.createElement(WrapperTestComponent, this.props);
94
+ }
95
+}
96
+
97
function WrapperTestComponent(props: { fn: any; params: Array<any> }) {
98
const result = props.fn(...props.params);
99
// Hacky solution to determine whether the fixture returned jsx (which
@@ -81,13 +118,16 @@ function renderComponentSequentiallyForEachProps(
118
const initialProps = sequentialRenders[0]!;
119
const results = [];
120
const { rerender, container } = render(
84
- React.createElement(WrapperTestComponent, { fn, params: [initialProps] })
121
+ React.createElement(WrapperTestComponentWithErrorBoundary, {
122
+ fn,
123
+ params: [initialProps],
124
+ })
125
);
126
results.push(container.innerHTML);
127
128
for (let i = 1; i < sequentialRenders.length; i++) {
129
rerender(
90
- React.createElement(WrapperTestComponent, {
130
+ React.createElement(WrapperTestComponentWithErrorBoundary, {
131
fn,
132
params: [sequentialRenders[i]],
133
})
@@ -127,7 +167,7 @@ type FixtureEvaluatorResult = Omit<EvaluatorResult, "logs">;
167
// Try to run fixture as a react component. This is necessary because not
168
// all components are functions (some are ForwardRef or Memo objects).
169
const result = render(
130
- React.createElement(entrypoint.fn, entrypoint.params[0])
170
+ React.createElement(entrypoint.fn as any, entrypoint.params[0])
171
).container.innerHTML;
172
173
return {
@@ -151,12 +191,14 @@ export function doEval(source: string): EvaluatorResult {
191
const originalConsole = globalThis.console;
192
const logs: Array<string> = [];
193
const mockedLog = (...args: Array<any>) => {
154
- // Some hackery: React will use the JS engine to log source location,
155
- // which doesn't play well with snapshot files.
194
logs.push(
157
- `${args.map((arg) =>
158
- util.inspect(arg).replaceAll(PROJECT_ROOT, "<project_root>")
159
- )}`
195
+ `${args.map((arg) => {
196
+ if (arg instanceof Error) {
197
+ return arg.toString();
198
+ } else {
199
+ return util.inspect(arg);
200
+ }
201
+ })}`
202
);
203
};
204
@@ -164,7 +206,22 @@ export function doEval(source: string): EvaluatorResult {
206
info: mockedLog,
207
log: mockedLog,
208
warn: mockedLog,
167
- error: mockedLog,
209
+ error: (...args: Array<any>) => {
210
+ const stack = new Error().stack?.split("\n", 5) ?? [];
211
+ for (const stackFrame of stack) {
212
+ // React warns on exceptions thrown during render, we avoid printing
213
+ // here to reduce noise in test fixture outputs.
214
+ if (
215
+ (stackFrame.includes("at logCapturedError") &&
216
+ stackFrame.includes("react-dom.development")) ||
217
+ (stackFrame.includes("at defaultOnRecoverableError") &&
218
+ stackFrame.includes("react-dom.development"))
219
+ ) {
220
+ return;
221
+ }
222
+ }
223
+ mockedLog(...args);
224
+ },
225
table: mockedLog,
226
trace: () => {},
227
};
@@ -184,6 +241,12 @@ export function doEval(source: string): EvaluatorResult {
241
// run in an iife to avoid naming collisions
242
(() => {${source}})();
243
reachedInvoke = true;
244
+ if (exports.FIXTURE_ENTRYPOINT?.fn === globalThis.placeholderFn) {
245
+ return {
246
+ kind: "exception",
247
+ value: "Fixture not implemented",
248
+ };
249
+ }
250
return evaluateFixtureExport(exports);
251
} catch (e) {
252
if (!reachedInvoke) {