[Babel] Desugar ArrowFunctionExpression
Rewrite ArrowFunctionExpression to FunctionDeclaration and compile it. This lets us reuse all the export gating logic, rather than writing separate, specific logic for ArrowFunctionExpression.
Sathya Gunasekaran committed
Mar 20, 2023 at 17:42 UTC
9b5ff25b3bcdd3a4433751ffda39eca060d06585
9 files changed
+314
compiler/forget/src/Babel/BabelPlugin.ts
+59
@@ -80,6 +80,17 @@ export default function ReactForgetBabelPlugin(
80
81
visitFn(fn, pass);
82
},
83
+
84
+ ArrowFunctionExpression(
85
+ fn: BabelCore.NodePath<t.ArrowFunctionExpression>,
86
+ pass: BabelPluginPass
87
+ ): void {
88
+ if (!shouldCompile(fn, pass)) {
89
+ return;
90
+ }
91
+
92
+ visitFn(buildFunctionDeclaration(fn), pass);
93
+ },
94
};
95
96
return {
@@ -135,6 +146,54 @@ function shouldCompile(
146
return true;
147
}
148
149
+function buildFunctionDeclaration(
150
+ fn: BabelCore.NodePath<t.ArrowFunctionExpression>
151
+): BabelCore.NodePath<t.FunctionDeclaration> {
152
+ invariant(
153
+ fn.parentPath.isVariableDeclarator(),
154
+ "ArrowFunctionExpression must be declared in variable declaration"
155
+ );
156
+ const variableDeclarator = fn.parentPath;
157
+
158
+ invariant(
159
+ variableDeclarator.parentPath.isVariableDeclaration(),
160
+ "ArrowFunctionExpression must be a single declaration"
161
+ );
162
+ const variableDeclaration = variableDeclarator.parentPath;
163
+
164
+ const id = variableDeclarator.get("id");
165
+ invariant(id.isIdentifier(), "ArrowFunctionExpression must have an id");
166
+
167
+ const rewrittenFn = variableDeclaration.replaceWith(
168
+ t.functionDeclaration(
169
+ id.node,
170
+ fn.node.params,
171
+ buildBlockStatement(fn),
172
+ fn.node.generator,
173
+ fn.node.async
174
+ )
175
+ )[0];
176
+ fn.skip();
177
+ return rewrittenFn;
178
+}
179
+
180
+function buildBlockStatement(
181
+ fn: BabelCore.NodePath<t.ArrowFunctionExpression>
182
+): t.BlockStatement {
183
+ const body = fn.get("body");
184
+ if (body.isExpression()) {
185
+ const wrappedBody = body.replaceWith(
186
+ t.blockStatement([t.returnStatement(body.node)])
187
+ )[0];
188
+ body.skip();
189
+
190
+ return wrappedBody.node;
191
+ }
192
+
193
+ invariant(body.isBlockStatement(), "Body must be a BlockStatement");
194
+ return body.node;
195
+}
196
+
197
function buildGatingTest(
198
uncompiled: BabelCore.NodePath<t.FunctionDeclaration>,
199
compiled: t.Identifier,
compiler/forget/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md
new
+37
@@ -0,0 +1,37 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @gatingModule
6
+const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>;
7
+
8
+export default ErrorView;
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+import isForgetEnabled from "ReactForgetFeatureFlag"; // @gatingModule
16
+function ErrorView_uncompiled(error, _retry) {
17
+ return <MessageBox error={error}></MessageBox>;
18
+}
19
+function ErrorView_forget(error, _retry) {
20
+ const $ = React.unstable_useMemoCache(2);
21
+ const c_0 = $[0] !== error;
22
+ let t0;
23
+ if (c_0) {
24
+ t0 = <MessageBox error={error}></MessageBox>;
25
+ $[0] = error;
26
+ $[1] = t0;
27
+ } else {
28
+ t0 = $[1];
29
+ }
30
+ return t0;
31
+}
32
+const ErrorView = isForgetEnabled ? ErrorView_forget : ErrorView_uncompiled;
33
+
34
+export default ErrorView;
35
+
36
+```
37
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.js
new
+4
@@ -0,0 +1,4 @@
1
+// @gatingModule
2
+const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>;
3
+
4
+export default ErrorView;
compiler/forget/src/__tests__/fixtures/compiler/error._todo.multi-arrow-expr-export-default-gating-test.expect.md
new
+24
@@ -0,0 +1,24 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @gatingModule
6
+const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>;
7
+
8
+export default Renderer = (props) => (
9
+ <Foo>
10
+ <Bar></Bar>
11
+ <ErrorView></ErrorView>
12
+ </Foo>
13
+);
14
+
15
+```
16
+
17
+
18
+## Error
19
+
20
+```
21
+ArrowFunctionExpression must be declared in variable declaration
22
+```
23
+
24
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/error._todo.multi-arrow-expr-export-default-gating-test.js
new
+9
@@ -0,0 +1,9 @@
1
+// @gatingModule
2
+const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>;
3
+
4
+export default Renderer = (props) => (
5
+ <Foo>
6
+ <Bar></Bar>
7
+ <ErrorView></ErrorView>
8
+ </Foo>
9
+);
compiler/forget/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.expect.md
new
+79
@@ -0,0 +1,79 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @gatingModule
6
+const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>;
7
+
8
+export const Renderer = (props) => (
9
+ <Foo>
10
+ <Bar></Bar>
11
+ <ErrorView></ErrorView>
12
+ </Foo>
13
+);
14
+
15
+```
16
+
17
+## Code
18
+
19
+```javascript
20
+import isForgetEnabled from "ReactForgetFeatureFlag"; // @gatingModule
21
+function ErrorView_uncompiled(error, _retry) {
22
+ return <MessageBox error={error}></MessageBox>;
23
+}
24
+function ErrorView_forget(error, _retry) {
25
+ const $ = React.unstable_useMemoCache(2);
26
+ const c_0 = $[0] !== error;
27
+ let t0;
28
+ if (c_0) {
29
+ t0 = <MessageBox error={error}></MessageBox>;
30
+ $[0] = error;
31
+ $[1] = t0;
32
+ } else {
33
+ t0 = $[1];
34
+ }
35
+ return t0;
36
+}
37
+const ErrorView = isForgetEnabled ? ErrorView_forget : ErrorView_uncompiled;
38
+function Renderer_uncompiled(props) {
39
+ return (
40
+ <Foo>
41
+ <Bar></Bar>
42
+ <ErrorView></ErrorView>
43
+ </Foo>
44
+ );
45
+}
46
+function Renderer_forget(props) {
47
+ const $ = React.unstable_useMemoCache(3);
48
+ let t0;
49
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
50
+ t0 = <Bar></Bar>;
51
+ $[0] = t0;
52
+ } else {
53
+ t0 = $[0];
54
+ }
55
+ let t1;
56
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
57
+ t1 = <ErrorView></ErrorView>;
58
+ $[1] = t1;
59
+ } else {
60
+ t1 = $[1];
61
+ }
62
+ let t2;
63
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
64
+ t2 = (
65
+ <Foo>
66
+ {t0}
67
+ {t1}
68
+ </Foo>
69
+ );
70
+ $[2] = t2;
71
+ } else {
72
+ t2 = $[2];
73
+ }
74
+ return t2;
75
+}
76
+export const Renderer = isForgetEnabled ? Renderer_forget : Renderer_uncompiled;
77
+
78
+```
79
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.js
new
+9
@@ -0,0 +1,9 @@
1
+// @gatingModule
2
+const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>;
3
+
4
+export const Renderer = (props) => (
5
+ <Foo>
6
+ <Bar></Bar>
7
+ <ErrorView></ErrorView>
8
+ </Foo>
9
+);
compiler/forget/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.expect.md
new
+82
@@ -0,0 +1,82 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @gatingModule
6
+const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>;
7
+
8
+const Renderer = (props) => (
9
+ <Foo>
10
+ <Bar></Bar>
11
+ <ErrorView></ErrorView>
12
+ </Foo>
13
+);
14
+
15
+export default Renderer;
16
+
17
+```
18
+
19
+## Code
20
+
21
+```javascript
22
+import isForgetEnabled from "ReactForgetFeatureFlag"; // @gatingModule
23
+function ErrorView_uncompiled(error, _retry) {
24
+ return <MessageBox error={error}></MessageBox>;
25
+}
26
+function ErrorView_forget(error, _retry) {
27
+ const $ = React.unstable_useMemoCache(2);
28
+ const c_0 = $[0] !== error;
29
+ let t0;
30
+ if (c_0) {
31
+ t0 = <MessageBox error={error}></MessageBox>;
32
+ $[0] = error;
33
+ $[1] = t0;
34
+ } else {
35
+ t0 = $[1];
36
+ }
37
+ return t0;
38
+}
39
+const ErrorView = isForgetEnabled ? ErrorView_forget : ErrorView_uncompiled;
40
+function Renderer_uncompiled(props) {
41
+ return (
42
+ <Foo>
43
+ <Bar></Bar>
44
+ <ErrorView></ErrorView>
45
+ </Foo>
46
+ );
47
+}
48
+function Renderer_forget(props) {
49
+ const $ = React.unstable_useMemoCache(3);
50
+ let t0;
51
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
52
+ t0 = <Bar></Bar>;
53
+ $[0] = t0;
54
+ } else {
55
+ t0 = $[0];
56
+ }
57
+ let t1;
58
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
59
+ t1 = <ErrorView></ErrorView>;
60
+ $[1] = t1;
61
+ } else {
62
+ t1 = $[1];
63
+ }
64
+ let t2;
65
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
66
+ t2 = (
67
+ <Foo>
68
+ {t0}
69
+ {t1}
70
+ </Foo>
71
+ );
72
+ $[2] = t2;
73
+ } else {
74
+ t2 = $[2];
75
+ }
76
+ return t2;
77
+}
78
+const Renderer = isForgetEnabled ? Renderer_forget : Renderer_uncompiled;
79
+export default Renderer;
80
+
81
+```
82
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.js
new
+11
@@ -0,0 +1,11 @@
1
+// @gatingModule
2
+const ErrorView = (error, _retry) => <MessageBox error={error}></MessageBox>;
3
+
4
+const Renderer = (props) => (
5
+ <Foo>
6
+ <Bar></Bar>
7
+ <ErrorView></ErrorView>
8
+ </Foo>
9
+);
10
+
11
+export default Renderer;