Reodering of unary and arrow function expressions
Supports reordering of unary and arrow function expressions: * Supports a trivially safe subset of unary operators, rejects things like `void` just because we don't need it yet. * Supports arrow function expressions that are either an empty block statement or a single expression which is itself reorderable.
Joe Savona committed
Sep 11, 2023 at 12:49 UTC
b1f7ebfd47d3ecc1c9561a43c3c68d129b240484
13 files changed
+283
-30
compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+25
@@ -8,6 +8,7 @@
8
import { NodePath, Scope } from "@babel/traverse";
9
import * as t from "@babel/types";
10
import { Expression } from "@babel/types";
11
+import invariant from "invariant";
12
import {
13
CompilerError,
14
CompilerSuggestionOperation,
@@ -2238,6 +2239,19 @@ function isReorderableExpression(
2239
case "BigIntLiteral": {
2240
return true;
2241
}
2242
+ case "UnaryExpression": {
2243
+ const unary = expr as NodePath<t.UnaryExpression>;
2244
+ switch (expr.node.operator) {
2245
+ case "!":
2246
+ case "+":
2247
+ case "-": {
2248
+ return isReorderableExpression(builder, unary.get("argument"));
2249
+ }
2250
+ default: {
2251
+ return false;
2252
+ }
2253
+ }
2254
+ }
2255
case "TypeCastExpression": {
2256
return isReorderableExpression(
2257
builder,
@@ -2293,6 +2307,17 @@ function isReorderableExpression(
2307
return false;
2308
}
2309
}
2310
+ case "ArrowFunctionExpression": {
2311
+ const fn = expr as NodePath<t.ArrowFunctionExpression>;
2312
+ const body = fn.get("body");
2313
+ if (body.node.type === "BlockStatement") {
2314
+ return body.node.body.length === 0;
2315
+ } else {
2316
+ // For TypeScript
2317
+ invariant(body.isExpression(), "Expected an expression");
2318
+ return isReorderableExpression(builder, body);
2319
+ }
2320
+ }
2321
default: {
2322
return false;
2323
}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-array-with-unary.expect.md
new
+41
@@ -0,0 +1,41 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(x = [-1, 1]) {
6
+ return x;
7
+}
8
+
9
+export const FIXTURE_ENTRYPOINT = {
10
+ fn: Component,
11
+ params: [],
12
+};
13
+
14
+```
15
+
16
+## Code
17
+
18
+```javascript
19
+import { unstable_useMemoCache as useMemoCache } from "react";
20
+function Component(t0) {
21
+ const $ = useMemoCache(2);
22
+ const c_0 = $[0] !== t0;
23
+ let t1;
24
+ if (c_0) {
25
+ t1 = t0 === undefined ? [-1, 1] : t0;
26
+ $[0] = t0;
27
+ $[1] = t1;
28
+ } else {
29
+ t1 = $[1];
30
+ }
31
+ const x = t1;
32
+ return x;
33
+}
34
+
35
+export const FIXTURE_ENTRYPOINT = {
36
+ fn: Component,
37
+ params: [],
38
+};
39
+
40
+```
41
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-array-with-unary.js
new
+8
@@ -0,0 +1,8 @@
1
+function Component(x = [-1, 1]) {
2
+ return x;
3
+}
4
+
5
+export const FIXTURE_ENTRYPOINT = {
6
+ fn: Component,
7
+ params: [],
8
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-with-empty-callback.expect.md
new
+41
@@ -0,0 +1,41 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(x = () => {}) {
6
+ return x;
7
+}
8
+
9
+export const FIXTURE_ENTRYPOINT = {
10
+ fn: Component,
11
+ params: [],
12
+};
13
+
14
+```
15
+
16
+## Code
17
+
18
+```javascript
19
+import { unstable_useMemoCache as useMemoCache } from "react";
20
+function Component(t0) {
21
+ const $ = useMemoCache(2);
22
+ const c_0 = $[0] !== t0;
23
+ let t1;
24
+ if (c_0) {
25
+ t1 = t0 === undefined ? () => {} : t0;
26
+ $[0] = t0;
27
+ $[1] = t1;
28
+ } else {
29
+ t1 = $[1];
30
+ }
31
+ const x = t1;
32
+ return x;
33
+}
34
+
35
+export const FIXTURE_ENTRYPOINT = {
36
+ fn: Component,
37
+ params: [],
38
+};
39
+
40
+```
41
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-with-empty-callback.js
new
+8
@@ -0,0 +1,8 @@
1
+function Component(x = () => {}) {
2
+ return x;
3
+}
4
+
5
+export const FIXTURE_ENTRYPOINT = {
6
+ fn: Component,
7
+ params: [],
8
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-with-reorderable-callback.expect.md
new
+41
@@ -0,0 +1,41 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(x = () => [-1, true, 42.0, "hello"]) {
6
+ return x;
7
+}
8
+
9
+export const FIXTURE_ENTRYPOINT = {
10
+ fn: Component,
11
+ params: [],
12
+};
13
+
14
+```
15
+
16
+## Code
17
+
18
+```javascript
19
+import { unstable_useMemoCache as useMemoCache } from "react";
20
+function Component(t0) {
21
+ const $ = useMemoCache(2);
22
+ const c_0 = $[0] !== t0;
23
+ let t1;
24
+ if (c_0) {
25
+ t1 = t0 === undefined ? () => [-1, true, 42, "hello"] : t0;
26
+ $[0] = t0;
27
+ $[1] = t1;
28
+ } else {
29
+ t1 = $[1];
30
+ }
31
+ const x = t1;
32
+ return x;
33
+}
34
+
35
+export const FIXTURE_ENTRYPOINT = {
36
+ fn: Component,
37
+ params: [],
38
+};
39
+
40
+```
41
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/default-param-with-reorderable-callback.js
new
+8
@@ -0,0 +1,8 @@
1
+function Component(x = () => [-1, true, 42.0, "hello"]) {
2
+ return x;
3
+}
4
+
5
+export const FIXTURE_ENTRYPOINT = {
6
+ fn: Component,
7
+ params: [],
8
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-default-array-with-unary.expect.md
new
+43
@@ -0,0 +1,43 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const [x = [-1, 1]] = props.value;
7
+ return x;
8
+}
9
+
10
+export const FIXTURE_ENTRYPOINT = {
11
+ fn: Component,
12
+ params: [{ value: [] }],
13
+};
14
+
15
+```
16
+
17
+## Code
18
+
19
+```javascript
20
+import { unstable_useMemoCache as useMemoCache } from "react";
21
+function Component(props) {
22
+ const $ = useMemoCache(2);
23
+ const [t0] = props.value;
24
+ const c_0 = $[0] !== t0;
25
+ let t1;
26
+ if (c_0) {
27
+ t1 = t0 === undefined ? [-1, 1] : t0;
28
+ $[0] = t0;
29
+ $[1] = t1;
30
+ } else {
31
+ t1 = $[1];
32
+ }
33
+ const x = t1;
34
+ return x;
35
+}
36
+
37
+export const FIXTURE_ENTRYPOINT = {
38
+ fn: Component,
39
+ params: [{ value: [] }],
40
+};
41
+
42
+```
43
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-default-array-with-unary.js
new
+9
@@ -0,0 +1,9 @@
1
+function Component(props) {
2
+ const [x = [-1, 1]] = props.value;
3
+ return x;
4
+}
5
+
6
+export const FIXTURE_ENTRYPOINT = {
7
+ fn: Component,
8
+ params: [{ value: [] }],
9
+};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.nested-function-with-param-as-captured-dep.expect.md
deleted
-29
@@ -1,29 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-function Foo() {
6
- (function t() {
7
- let x = {};
8
- return function a(x = () => {}) {
9
- return x;
10
- };
11
- })();
12
-}
13
-
14
-export const FIXTURE_ENTRYPOINT = {
15
- fn: Foo,
16
- params: [],
17
- isComponent: false,
18
-};
19
-
20
-```
21
-
22
-
23
-## Error
24
-
25
-```
26
-[ReactForget] Todo: (BuildHIR::node.lowerReorderableExpression) Expression type 'ArrowFunctionExpression' cannot be safely reordered (4:4)
27
-```
28
-
29
-
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/nested-function-with-param-as-captured-dep.expect.md
new
+57
@@ -0,0 +1,57 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Foo() {
6
+ return (function t() {
7
+ let x = {};
8
+ return function a(x = () => {}) {
9
+ return x;
10
+ };
11
+ })();
12
+}
13
+
14
+export const FIXTURE_ENTRYPOINT = {
15
+ fn: Foo,
16
+ params: [],
17
+ isComponent: false,
18
+};
19
+
20
+```
21
+
22
+## Code
23
+
24
+```javascript
25
+import { unstable_useMemoCache as useMemoCache } from "react";
26
+function Foo() {
27
+ const $ = useMemoCache(2);
28
+ let t0;
29
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30
+ t0 = function t() {
31
+ return function a(t25) {
32
+ const x_0 = t25 === undefined ? () => {} : t25;
33
+ return x_0;
34
+ };
35
+ };
36
+ $[0] = t0;
37
+ } else {
38
+ t0 = $[0];
39
+ }
40
+ let t1;
41
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
42
+ t1 = t0();
43
+ $[1] = t1;
44
+ } else {
45
+ t1 = $[1];
46
+ }
47
+ return t1;
48
+}
49
+
50
+export const FIXTURE_ENTRYPOINT = {
51
+ fn: Foo,
52
+ params: [],
53
+ isComponent: false,
54
+};
55
+
56
+```
57
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/nested-function-with-param-as-captured-dep.ts
renamed
+1
-1
@@ -1,5 +1,5 @@
1
function Foo() {
2
- (function t() {
2
+ return (function t() {
3
let x = {};
4
return function a(x = () => {}) {
5
return x;
compiler/packages/sprout/src/SproutTodoFilter.ts
+1
@@ -412,6 +412,7 @@ const skipFilter = new Set([
412
"fbt-call-complex-param-value",
413
"fbt-template-string-same-scope",
414
"component-declaration-basic.flow",
415
+ "nested-function-with-param-as-captured-dep",
416
417
// TODO: 🌲
418
"forest-basic",