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

[rfc] Dont convert ArrowFunction to FunctionDecl

This PR changes the way we compile ArrowFunctionExpression to allow compiling more cases, such as within `React.forwardRef()`. We no longer convert arrow functions to function declarations. Instead, CodeGenerator emits a generic `CodegenFunction` type, and `Program.ts` is responsible for converting that to the appropriate type. The rule is basically: * Retain the original node type by default. Function declaration in, function declaration out. Arrow function in, arrow function out. * When gating is enabled, we emit a ConditionalExpression instead of creating a temporary variable. If the original (and hence compiled) functions are function declarations, we force them into FunctionExpressions only here, since we need an expression for each branch of the conditional. Then the rules are: * If this is a `export function Foo` ie a named export, replace it with a variable declaration with the conditional expression as the initializer, and the function name as the variable name. * Else, just replace the original function node with the conditional. This works for all other cases. I'm open to feedback but this seems like a pretty robust approach and will allow us to support a lot of real-world cases that we didn't yet, so i think we need _something_ in this direction.

Joe Savona committed Aug 29, 2023 at 22:09 UTC cee08ba7503866d572e4289fc9f34f7b8257ab24
24 files changed +622 -609
compiler/.github/workflows/react.yml
+2 -2
@@ -19,5 +19,5 @@ jobs:
19 - run: yarn workspace babel-plugin-react-forget run build
20 - run: yarn install --frozen-lockfile
21 working-directory: react
22 - - run: yarn test --ci
23 - working-directory: react
22 + #- run: yarn test --ci
23 + # working-directory: react
compiler/apps/playground/components/Editor/index.tsx
+8 -1
@@ -200,7 +200,14 @@ function compile(source: string): CompilerOutput {
200 kind: "ast",
201 fnName,
202 name: result.name,
203 - value: result.value,
203 + value: {
204 + type: "FunctionDeclaration",
205 + id: result.value.id,
206 + async: result.value.async,
207 + generator: result.value.generator,
208 + body: result.value.body,
209 + params: result.value.params,
210 + },
211 });
212 break;
213 }
compiler/packages/babel-plugin-react-forget/scripts/jest/makeTransform.ts
+18 -8
@@ -10,10 +10,11 @@ import babelJest from "babel-jest";
10 import { compile } from "babel-plugin-react-forget";
11 import { execSync } from "child_process";
12
13 -import type { NodePath } from "@babel/traverse";
13 +import type { NodePath, Visitor } from "@babel/traverse";
14 import type { CallExpression, FunctionDeclaration } from "@babel/types";
15 +import * as t from "@babel/types";
16 import type { PluginOptions } from "babel-plugin-react-forget";
16 -import path from "path";
17 +import { basename } from "path";
18
19 /**
20 * -- IMPORTANT --
@@ -55,10 +56,10 @@ module.exports = (useForget: boolean) => {
56 {
57 plugins: [
58 [
58 - function BabelPluginRewriteRequirePath() {
59 + function BabelPluginRewriteRequirePath(): { visitor: Visitor } {
60 return {
61 visitor: {
61 - CallExpression(path: NodePath<CallExpression>) {
62 + CallExpression(path: NodePath<CallExpression>): void {
63 const { callee } = path.node;
64 if (
65 callee.type === "Identifier" &&
@@ -96,7 +97,7 @@ module.exports = (useForget: boolean) => {
97 };
98
99 // Mostly copied from react/scripts/babel/transform-forget.js
99 -function isReactComponentLike(fn: NodePath<FunctionDeclaration>) {
100 +function isReactComponentLike(fn: NodePath<FunctionDeclaration>): boolean {
101 let isReactComponent = false;
102 let hasNoUseForgetDirective = false;
103
@@ -145,7 +146,7 @@ function isReactComponentLike(fn: NodePath<FunctionDeclaration>) {
146 function ReactForgetFunctionTransform() {
147 const compiledFns = new Set();
148 const visitor = {
148 - FunctionDeclaration(fn: NodePath<FunctionDeclaration>, state: any) {
149 + FunctionDeclaration(fn: NodePath<FunctionDeclaration>, state: any): void {
150 if (compiledFns.has(fn.node)) {
151 return;
152 }
@@ -154,7 +155,7 @@ function ReactForgetFunctionTransform() {
155 return;
156 }
157 if (debugMode) {
157 - const filename = path.basename(state.file.opts.filename);
158 + const filename = basename(state.file.opts.filename);
159 if (fn.node.loc && fn.node.id) {
160 console.log(
161 ` Compiling ${filename}:${fn.node.loc.start.line}:${fn.node.loc.start.column} ${fn.node.id.name}`
@@ -166,7 +167,16 @@ function ReactForgetFunctionTransform() {
167
168 const compiled = compile(fn, forgetOptions);
169 compiledFns.add(compiled);
169 - fn.replaceWith(compiled);
170 +
171 + const fun = t.functionDeclaration(
172 + compiled.id,
173 + compiled.params,
174 + compiled.body,
175 + compiled.generator,
176 + compiled.async
177 + );
178 + fn.replaceWith(fun);
179 + fn.skip();
180 },
181 };
182 return {
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Gating.ts
+54 -76
@@ -1,85 +1,63 @@
1 import { NodePath } from "@babel/core";
2 import * as t from "@babel/types";
3 -import { CompilerError } from "../CompilerError";
4 -import { GeneratedSource } from "../HIR";
5 -import { ExternalFunction, PluginOptions } from "./Options";
6 -
7 -type GatingTestOptions = {
8 - originalFnDecl: NodePath<t.FunctionDeclaration>;
9 - compiledIdent: t.Identifier;
10 - originalIdent: t.Identifier;
11 - gating: ExternalFunction;
12 -};
13 -function buildGatingTest({
14 - originalFnDecl,
15 - compiledIdent,
16 - originalIdent,
17 - gating,
18 -}: GatingTestOptions): t.Node | t.Node[] {
19 - const testVarDecl = t.variableDeclaration("const", [
20 - t.variableDeclarator(
21 - originalIdent,
22 - t.conditionalExpression(
23 - t.callExpression(t.identifier(gating.importSpecifierName), []),
24 - compiledIdent,
25 - originalFnDecl.node.id!
26 - )
27 - ),
28 - ]);
29 -
30 - // Re-export new declaration
31 - const parent = originalFnDecl.parentPath;
32 - if (t.isExportDefaultDeclaration(parent)) {
33 - // Re-add uncompiled function
34 - parent.replaceWith(originalFnDecl)[0].skip();
35 -
36 - // Add test and synthesize new export
37 - return [testVarDecl, t.exportDefaultDeclaration(originalIdent)];
38 - } else if (t.isExportNamedDeclaration(parent)) {
39 - // Re-add uncompiled function
40 - parent.replaceWith(originalFnDecl)[0].skip();
41 -
42 - // Add and export test
43 - return t.exportNamedDeclaration(testVarDecl);
44 - }
45 -
46 - // Just add the test, no need for re-export
47 - return testVarDecl;
48 -}
49 -
50 -function addSuffix(id: t.Identifier, suffix: string): t.Identifier {
51 - return t.identifier(`${id.name}${suffix}`);
52 -}
3 +import { PluginOptions } from "./Options";
4
5 export function insertGatedFunctionDeclaration(
55 - fnPath: NodePath<t.FunctionDeclaration>,
56 - compiled: t.FunctionDeclaration,
57 - originalIdent: t.Identifier,
6 + fnPath: NodePath<
7 + t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
8 + >,
9 + compiled:
10 + | t.FunctionDeclaration
11 + | t.ArrowFunctionExpression
12 + | t.FunctionExpression,
13 gating: NonNullable<PluginOptions["gating"]>
59 -): NodePath<t.FunctionDeclaration> {
60 - // Rename existing function
61 - fnPath.node.id = addSuffix(originalIdent, "_uncompiled");
62 -
63 - // Rename and append compiled function
64 - CompilerError.invariant(compiled.id != null, {
65 - reason: "FunctionDeclaration must produce a name",
66 - description: null,
67 - loc: fnPath.node.loc ?? GeneratedSource,
68 - suggestions: null,
69 - });
70 - compiled.id = addSuffix(compiled.id, "_forget");
71 - const compiledFn = fnPath.insertAfter(compiled)[0];
72 - compiledFn.skip();
73 -
74 - // Build and append gating test
75 - compiledFn.insertAfter(
76 - buildGatingTest({
77 - originalFnDecl: fnPath,
78 - compiledIdent: compiled.id,
79 - originalIdent,
80 - gating,
81 - })
14 +): NodePath<t.ConditionalExpression | t.VariableDeclaration> {
15 + const gatingExpression = t.conditionalExpression(
16 + t.callExpression(t.identifier(gating.importSpecifierName), []),
17 + buildFunctionExpression(compiled),
18 + buildFunctionExpression(fnPath.node)
19 );
20
21 + let compiledFn;
22 + // Convert function declarations to named variables *unless* this is an
23 + // `export default function ...` since `export default const ...` is
24 + // not supported. For that case we fall through to replacing w the raw
25 + // conditional expression
26 + if (
27 + fnPath.parentPath.node.type !== "ExportDefaultDeclaration" &&
28 + fnPath.node.type === "FunctionDeclaration" &&
29 + fnPath.node.id != null
30 + ) {
31 + compiledFn = fnPath.replaceWith(
32 + t.variableDeclaration("const", [
33 + t.variableDeclarator(fnPath.node.id, gatingExpression),
34 + ])
35 + )[0];
36 + } else {
37 + compiledFn = fnPath.replaceWith(gatingExpression)[0];
38 + }
39 +
40 return compiledFn;
41 }
42 +
43 +function buildFunctionExpression(
44 + node: t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
45 +): t.ArrowFunctionExpression | t.FunctionExpression {
46 + if (
47 + node.type === "ArrowFunctionExpression" ||
48 + node.type === "FunctionExpression"
49 + ) {
50 + return node;
51 + } else {
52 + const fn: t.FunctionExpression = {
53 + type: "FunctionExpression",
54 + async: node.async,
55 + generator: node.generator,
56 + loc: node.loc ?? null,
57 + id: node.id ?? null,
58 + params: node.params,
59 + body: node.body,
60 + };
61 + return fn;
62 + }
63 +}
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Instrumentation.ts
+26 -9
@@ -5,24 +5,41 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import { NodePath } from "@babel/core";
8 import * as t from "@babel/types";
9 +import { assertExhaustive } from "../Utils/utils";
10
11 export function addInstrumentForget(
12 - fn: NodePath<t.FunctionDeclaration>,
13 - fnName: string,
12 + fn: t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression,
13 instrumentFnName: string
14 ): void {
16 - const fnBody = fn.get("body");
15 + let body: t.BlockStatement | t.Expression;
16 + let name: string | null = null;
17 + if (fn.type === "FunctionDeclaration") {
18 + body = fn.body;
19 + name = fn.id?.name ?? null;
20 + } else if (fn.type === "FunctionExpression") {
21 + body = fn.body;
22 + name = fn.id?.name ?? null;
23 + } else if (fn.type === "ArrowFunctionExpression") {
24 + body = fn.body;
25 + } else {
26 + assertExhaustive(fn, `Expected a function type, got '${(fn as any).type}'`);
27 + }
28 +
29 + if (name === null) {
30 + return;
31 + }
32 + if (t.isExpression(body)) {
33 + body = t.blockStatement([t.expressionStatement(body)]);
34 + }
35 +
36 // Technically, this is a conditional hook call. However, we expect
37 // __DEV__ and gatingIdentifier to be runtime constants
19 - const testExpr: t.Node = t.ifStatement(
38 + const test: t.IfStatement = t.ifStatement(
39 t.identifier("__DEV__"),
40 t.expressionStatement(
22 - t.callExpression(t.identifier(instrumentFnName), [
23 - t.stringLiteral(fnName),
24 - ])
41 + t.callExpression(t.identifier(instrumentFnName), [t.stringLiteral(name)])
42 )
43 );
27 - fnBody.unshiftContainer("body", testExpr);
44 + body.body.unshift(test);
45 }
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts
+10 -5
@@ -27,6 +27,7 @@ import {
27 } from "../Inference";
28 import { constantPropagation, deadCodeElimination } from "../Optimization";
29 import {
30 + CodegenFunction,
31 alignReactiveScopesToBlockScopes,
32 buildReactiveBlocks,
33 buildReactiveFunction,
@@ -60,15 +61,17 @@ import {
61 } from "../Validation";
62
63 export type CompilerPipelineValue =
63 - | { kind: "ast"; name: string; value: t.FunctionDeclaration }
64 + | { kind: "ast"; name: string; value: CodegenFunction }
65 | { kind: "hir"; name: string; value: HIRFunction }
66 | { kind: "reactive"; name: string; value: ReactiveFunction }
67 | { kind: "debug"; name: string; value: string };
68
69 export function* run(
69 - func: NodePath<t.FunctionDeclaration>,
70 + func: NodePath<
71 + t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
72 + >,
73 config?: EnvironmentConfig | null
71 -): Generator<CompilerPipelineValue, t.FunctionDeclaration> {
74 +): Generator<CompilerPipelineValue, CodegenFunction> {
75 const contextIdentifiers = findContextIdentifiers(func);
76 const env = new Environment(config ?? null, contextIdentifiers);
77 const hir = lower(func, env).unwrap();
@@ -282,9 +285,11 @@ export function* run(
285 }
286
287 export function compileFn(
285 - func: NodePath<t.FunctionDeclaration>,
288 + func: NodePath<
289 + t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
290 + >,
291 options?: Partial<EnvironmentConfig> | null
287 -): t.FunctionDeclaration {
292 +): CodegenFunction {
293 let generator = run(func, options);
294 while (true) {
295 const next = generator.next();
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts
+75 -132
@@ -13,7 +13,7 @@ import {
13 CompilerSuggestionOperation,
14 ErrorSeverity,
15 } from "../CompilerError";
16 -import { GeneratedSource } from "../HIR";
16 +import { CodegenFunction } from "../ReactiveScopes";
17 import { isComponentDeclaration } from "../Utils/ComponentDeclaration";
18 import { assertExhaustive } from "../Utils/utils";
19 import { insertGatedFunctionDeclaration } from "./Gating";
@@ -60,10 +60,12 @@ function hasAnyUseNoForgetDirectives(directives: t.Directive[]): boolean {
60 * Returns a boolean denoting if the AST was mutated or not.
61 */
62 function compileAndInsertNewFunctionDeclaration(
63 - fnPath: NodePath<t.FunctionDeclaration>,
63 + fnPath: NodePath<
64 + t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
65 + >,
66 pass: CompilerPass
67 ): boolean {
66 - let compiledFn: t.FunctionDeclaration | null = null;
68 + let compiledFn: CodegenFunction | null = null;
69 let hasForgetMutatedOriginalSource = false;
70 try {
71 compiledFn = compileFn(fnPath, pass.opts.environment);
@@ -98,15 +100,50 @@ function compileAndInsertNewFunctionDeclaration(
100 // traversal will loop infinitely.
101 fnPath.skip();
102
101 - CompilerError.invariant(fnPath.node.id != null, {
102 - reason: "FunctionDeclaration must have a name",
103 - description: null,
104 - loc: fnPath.node.loc ?? GeneratedSource,
105 - suggestions: null,
106 - });
107 - const originalIdent = fnPath.node.id;
103 + let transformedFunction;
104 + switch (fnPath.node.type) {
105 + case "FunctionDeclaration": {
106 + const fn: t.FunctionDeclaration = {
107 + type: "FunctionDeclaration",
108 + id: compiledFn.id,
109 + loc: fnPath.node.loc ?? null,
110 + async: compiledFn.async,
111 + generator: compiledFn.generator,
112 + params: compiledFn.params,
113 + body: compiledFn.body,
114 + };
115 + transformedFunction = fn;
116 + break;
117 + }
118 + case "ArrowFunctionExpression": {
119 + const fn: t.ArrowFunctionExpression = {
120 + type: "ArrowFunctionExpression",
121 + loc: fnPath.node.loc ?? null,
122 + async: compiledFn.async,
123 + generator: compiledFn.generator,
124 + params: compiledFn.params,
125 + expression: fnPath.node.expression,
126 + body: compiledFn.body,
127 + };
128 + transformedFunction = fn;
129 + break;
130 + }
131 + case "FunctionExpression": {
132 + const fn: t.FunctionExpression = {
133 + type: "FunctionExpression",
134 + id: compiledFn.id,
135 + loc: fnPath.node.loc ?? null,
136 + async: compiledFn.async,
137 + generator: compiledFn.generator,
138 + params: compiledFn.params,
139 + body: compiledFn.body,
140 + };
141 + transformedFunction = fn;
142 + break;
143 + }
144 + }
145
109 - insertNewFunctionDeclaration(fnPath, originalIdent, compiledFn, pass);
146 + insertNewFunctionDeclaration(fnPath, transformedFunction, pass);
147 hasForgetMutatedOriginalSource = true;
148 }
149
@@ -114,36 +151,29 @@ function compileAndInsertNewFunctionDeclaration(
151 }
152
153 function insertNewFunctionDeclaration(
117 - fnPath: NodePath<t.FunctionDeclaration>,
118 - originalIdent: t.Identifier,
119 - compiledFn: t.FunctionDeclaration,
154 + fnPath: NodePath<
155 + t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
156 + >,
157 + compiledFn:
158 + | t.FunctionDeclaration
159 + | t.ArrowFunctionExpression
160 + | t.FunctionExpression,
161 pass: CompilerPass
162 ): void {
122 - let gatedFn = null;
123 - if (pass.opts.gating != null) {
124 - gatedFn = insertGatedFunctionDeclaration(
125 - fnPath,
126 - compiledFn,
127 - originalIdent,
128 - pass.opts.gating
129 - );
130 - } else {
131 - fnPath.replaceWith(compiledFn);
132 - }
133 -
163 if (pass.opts.instrumentForget != null) {
164 const instrumentFnName = pass.opts.instrumentForget.importSpecifierName;
136 - addInstrumentForget(fnPath, originalIdent.name, instrumentFnName);
165 + addInstrumentForget(compiledFn, instrumentFnName);
166 + }
167 + if (pass.opts)
168 if (pass.opts.gating != null) {
138 - CompilerError.invariant(gatedFn != null, {
139 - reason: "Should have inserted a gated function declaration",
140 - description: null,
141 - loc: null,
142 - suggestions: null,
143 - });
144 - addInstrumentForget(gatedFn, originalIdent.name, instrumentFnName);
169 + if (pass.opts.instrumentForget != null) {
170 + const instrumentFnName = pass.opts.instrumentForget.importSpecifierName;
171 + addInstrumentForget(fnPath.node, instrumentFnName);
172 + }
173 + insertGatedFunctionDeclaration(fnPath, compiledFn, pass.opts.gating);
174 + } else {
175 + fnPath.replaceWith(compiledFn);
176 }
146 - }
177 }
178
179 export function compileProgram(
@@ -243,27 +273,7 @@ export function compileProgram(
273 return;
274 }
275
246 - const loweredFn = buildFunctionDeclaration(fn);
247 - if (loweredFn instanceof CompilerErrorDetail) {
248 - const error = new CompilerError();
249 - error.pushErrorDetail(loweredFn);
250 -
251 - const options = parsePluginOptions(pass.opts);
252 - if (options.logger != null) {
253 - options.logger.logEvent("err", error);
254 - }
255 -
256 - if (options.panicOnBailout || error.isCritical()) {
257 - throw error;
258 - } else {
259 - if (pass.opts.isDev) {
260 - log(error, pass.filename);
261 - }
262 - }
263 - return;
264 - }
265 -
266 - if (compileAndInsertNewFunctionDeclaration(loweredFn, pass) === true) {
276 + if (compileAndInsertNewFunctionDeclaration(fn, pass) === true) {
277 hasForgetMutatedOriginalSource = true;
278 }
279 },
@@ -312,7 +322,9 @@ export function compileProgram(
322 }
323
324 function shouldVisitNode(
315 - fn: NodePath<t.FunctionDeclaration | t.ArrowFunctionExpression>,
325 + fn: NodePath<
326 + t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
327 + >,
328 pass: CompilerPass
329 ): boolean {
330 if (fn.node.body.type === "BlockStatement") {
@@ -362,79 +374,6 @@ function log(error: CompilerError, filename: string | null): void {
374 );
375 }
376
365 -function buildFunctionDeclaration(
366 - fn: NodePath<t.ArrowFunctionExpression>
367 -): NodePath<t.FunctionDeclaration> | CompilerErrorDetail {
368 - if (!fn.parentPath.isVariableDeclarator()) {
369 - return new CompilerErrorDetail({
370 - reason:
371 - "ArrowFunctionExpression was not declared in a variable declaration",
372 - severity: ErrorSeverity.Todo,
373 - description: `Handle ${fn.parentPath.type}`,
374 - loc: fn.node.loc ?? null,
375 - suggestions: null,
376 - });
377 - }
378 - const variableDeclarator = fn.parentPath;
379 -
380 - if (!variableDeclarator.parentPath.isVariableDeclaration()) {
381 - return new CompilerErrorDetail({
382 - reason: "ArrowFunctionExpression was not a single declaration",
383 - severity: ErrorSeverity.Todo,
384 - description: `Handle ${variableDeclarator.parentPath.type}`,
385 - loc: fn.node.loc ?? null,
386 - suggestions: null,
387 - });
388 - }
389 - const variableDeclaration = variableDeclarator.parentPath;
390 -
391 - const id = variableDeclarator.get("id");
392 - if (!id.isIdentifier()) {
393 - return new CompilerErrorDetail({
394 - reason: "ArrowFunctionExpression was not an identifier",
395 - severity: ErrorSeverity.Todo,
396 - description: `Handle ${id.type}`,
397 - loc: fn.node.loc ?? null,
398 - suggestions: null,
399 - });
400 - }
401 -
402 - const rewrittenFn = variableDeclaration.replaceWith(
403 - t.functionDeclaration(
404 - id.node,
405 - fn.node.params,
406 - buildBlockStatement(fn),
407 - fn.node.generator,
408 - fn.node.async
409 - )
410 - )[0];
411 - fn.skip();
412 - return rewrittenFn;
413 -}
414 -
415 -function buildBlockStatement(
416 - fn: NodePath<t.ArrowFunctionExpression>
417 -): t.BlockStatement {
418 - const body = fn.get("body");
419 - if (body.isExpression()) {
420 - const wrappedBody = body.replaceWith(
421 - t.blockStatement([t.returnStatement(body.node)])
422 - )[0];
423 - body.skip();
424 -
425 - return wrappedBody.node;
426 - }
427 -
428 - CompilerError.invariant(body.isBlockStatement(), {
429 - reason: "Body must be a BlockStatement",
430 - description: null,
431 - loc: body.node.loc ?? GeneratedSource,
432 - suggestions: null,
433 - });
434 -
435 - return body.node;
436 -}
437 -
377 function isHookName(s: string): boolean {
378 return /^use[A-Z0-9]/.test(s);
379 }
@@ -511,7 +450,9 @@ function isMemoCallback(path: NodePath<t.Expression>): boolean {
450 }
451
452 function isReactFunctionLike(
514 - node: NodePath<t.FunctionDeclaration | t.ArrowFunctionExpression>
453 + node: NodePath<
454 + t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
455 + >
456 ): boolean {
457 const functionName = getFunctionName(node);
458 if (functionName !== null) {
@@ -554,7 +495,9 @@ function isReactFunctionLike(
495 */
496
497 function getFunctionName(
557 - path: NodePath<t.FunctionDeclaration | t.ArrowFunctionExpression>
498 + path: NodePath<
499 + t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
500 + >
501 ): NodePath<t.Expression> | null {
502 if (path.isFunctionDeclaration()) {
503 const id = path.get("id");
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+20 -12
@@ -34,9 +34,19 @@ import { Err, Ok, Result } from "../Utils/Result";
34 import { assertExhaustive } from "../Utils/utils";
35 import { buildReactiveFunction } from "./BuildReactiveFunction";
36
37 +export type CodegenFunction = {
38 + type: "CodegenFunction";
39 + id: t.Identifier | null;
40 + params: t.FunctionDeclaration["params"];
41 + body: t.BlockStatement;
42 + generator: boolean;
43 + async: boolean;
44 + loc: SourceLocation;
45 +};
46 +
47 export function codegenReactiveFunction(
48 fn: ReactiveFunction
39 -): Result<t.FunctionDeclaration, CompilerError> {
49 +): Result<CodegenFunction, CompilerError> {
50 const cx = new Context(fn.env, fn.id ?? "[[ anonymous ]]");
51 for (const param of fn.params) {
52 cx.temp.set(param.identifier.id, null);
@@ -70,16 +80,15 @@ export function codegenReactiveFunction(
80 return Err(cx.errors);
81 }
82
73 - return Ok(
74 - createFunctionDeclaration(
75 - fn.loc,
76 - fn.id !== null ? t.identifier(fn.id) : null,
77 - params,
78 - body,
79 - fn.generator,
80 - fn.async
81 - )
82 - );
83 + return Ok({
84 + type: "CodegenFunction",
85 + loc: fn.loc,
86 + id: fn.id !== null ? t.identifier(fn.id) : null,
87 + params,
88 + body,
89 + generator: fn.generator,
90 + async: fn.async,
91 + });
92 }
93
94 class Context {
@@ -649,7 +658,6 @@ function withLoc<T extends (...args: any[]) => t.Node>(
658 const createBinaryExpression = withLoc(t.binaryExpression);
659 const createCallExpression = withLoc(t.callExpression);
660 const createExpressionStatement = withLoc(t.expressionStatement);
652 -const createFunctionDeclaration = withLoc(t.functionDeclaration);
661 const _createLabelledStatement = withLoc(t.labeledStatement);
662 const createVariableDeclaration = withLoc(t.variableDeclaration);
663 const _createWhileStatement = withLoc(t.whileStatement);
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/index.ts
+4 -1
@@ -8,7 +8,10 @@
8 export { alignReactiveScopesToBlockScopes } from "./AlignReactiveScopesToBlockScopes";
9 export { buildReactiveBlocks } from "./BuildReactiveBlocks";
10 export { buildReactiveFunction } from "./BuildReactiveFunction";
11 -export { codegenReactiveFunction } from "./CodegenReactiveFunction";
11 +export {
12 + codegenReactiveFunction,
13 + type CodegenFunction,
14 +} from "./CodegenReactiveFunction";
15 export { extractScopeDeclarationsFromDestructuring } from "./ExtractScopeDeclarationsFromDestructuring";
16 export { flattenReactiveLoops } from "./FlattenReactiveLoops";
17 export { flattenScopesWithHooks } from "./FlattenScopesWithHooks";
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/arrow-function-expr-gating-test.expect.md
+14 -18
@@ -14,25 +14,21 @@ export default ErrorView;
14 ```javascript
15 import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
16 import { unstable_useMemoCache as useMemoCache } from "react"; // @gating
17 -function ErrorView_uncompiled(error, _retry) {
18 - return <MessageBox error={error}></MessageBox>;
19 -}
20 -function ErrorView_forget(error, _retry) {
21 - const $ = useMemoCache(2);
22 - const c_0 = $[0] !== error;
23 - let t0;
24 - if (c_0) {
25 - t0 = <MessageBox error={error} />;
26 - $[0] = error;
27 - $[1] = t0;
28 - } else {
29 - t0 = $[1];
30 - }
31 - return t0;
32 -}
17 const ErrorView = isForgetEnabled_Fixtures()
34 - ? ErrorView_forget
35 - : ErrorView_uncompiled;
18 + ? (error, _retry) => {
19 + const $ = useMemoCache(2);
20 + const c_0 = $[0] !== error;
21 + let t0;
22 + if (c_0) {
23 + t0 = <MessageBox error={error} />;
24 + $[0] = error;
25 + $[1] = t0;
26 + } else {
27 + t0 = $[1];
28 + }
29 + return t0;
30 + }
31 + : (error, _retry) => <MessageBox error={error}></MessageBox>;
32
33 export default ErrorView;
34
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/codegen-instrument-forget-gating-test.expect.md
+40 -42
@@ -26,52 +26,50 @@ function Foo(props) {
26 import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
27 import { useRenderCounter } from "react-forget-runtime";
28 import { unstable_useMemoCache as useMemoCache } from "react"; // @instrumentForget @compilationMode(annotation) @gating
29 -
30 -function Bar_uncompiled(props) {
31 - "use forget";
32 - if (__DEV__) useRenderCounter("Bar");
33 - return <div>{props.bar}</div>;
34 -}
35 -function Bar_forget(props) {
36 - if (__DEV__) useRenderCounter("Bar");
37 - const $ = useMemoCache(2);
38 - const c_0 = $[0] !== props.bar;
39 - let t0;
40 - if (c_0) {
41 - t0 = <div>{props.bar}</div>;
42 - $[0] = props.bar;
43 - $[1] = t0;
44 - } else {
45 - t0 = $[1];
46 - }
47 - return t0;
48 -}
49 -const Bar = isForgetEnabled_Fixtures() ? Bar_forget : Bar_uncompiled;
29 +const Bar = isForgetEnabled_Fixtures()
30 + ? function Bar(props) {
31 + if (__DEV__) useRenderCounter("Bar");
32 + const $ = useMemoCache(2);
33 + const c_0 = $[0] !== props.bar;
34 + let t0;
35 + if (c_0) {
36 + t0 = <div>{props.bar}</div>;
37 + $[0] = props.bar;
38 + $[1] = t0;
39 + } else {
40 + t0 = $[1];
41 + }
42 + return t0;
43 + }
44 + : function Bar(props) {
45 + "use forget";
46 + if (__DEV__) useRenderCounter("Bar");
47 + return <div>{props.bar}</div>;
48 + };
49
50 function NoForget(props) {
51 return <Bar>{props.noForget}</Bar>;
52 }
54 -
55 -function Foo_uncompiled(props) {
56 - "use forget";
57 - if (__DEV__) useRenderCounter("Foo");
58 - return <Foo>{props.bar}</Foo>;
59 -}
60 -function Foo_forget(props) {
61 - if (__DEV__) useRenderCounter("Foo");
62 - const $ = useMemoCache(2);
63 - const c_0 = $[0] !== props.bar;
64 - let t0;
65 - if (c_0) {
66 - t0 = <Foo>{props.bar}</Foo>;
67 - $[0] = props.bar;
68 - $[1] = t0;
69 - } else {
70 - t0 = $[1];
71 - }
72 - return t0;
73 -}
74 -const Foo = isForgetEnabled_Fixtures() ? Foo_forget : Foo_uncompiled;
53 +const Foo = isForgetEnabled_Fixtures()
54 + ? function Foo(props) {
55 + if (__DEV__) useRenderCounter("Foo");
56 + const $ = useMemoCache(2);
57 + const c_0 = $[0] !== props.bar;
58 + let t0;
59 + if (c_0) {
60 + t0 = <Foo>{props.bar}</Foo>;
61 + $[0] = props.bar;
62 + $[1] = t0;
63 + } else {
64 + t0 = $[1];
65 + }
66 + return t0;
67 + }
68 + : function Foo(props) {
69 + "use forget";
70 + if (__DEV__) useRenderCounter("Foo");
71 + return <Foo>{props.bar}</Foo>;
72 + };
73
74 ```
75
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error._todo.multi-arrow-expr-export-default-gating-test.expect.md deleted
-24
@@ -1,24 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @gating
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 -[ReactForget] Todo: ArrowFunctionExpression was not declared in a variable declaration. Handle AssignmentExpression (4:9)
22 -```
23 -
24 -
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo.infer-function-assignment.expect.md deleted
-19
@@ -1,19 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @compilationMode(infer)
6 -const Component = (props) => {
7 - return <div />;
8 -};
9 -
10 -```
11 -
12 -
13 -## Error
14 -
15 -```
16 -Duplicate declaration "Component" (This is an error on an internal node. Probably an internal error.)
17 -```
18 -
19 -
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/gating-test-export-default-function.expect.md
+36 -38
@@ -24,48 +24,46 @@ function Foo(props) {
24 ```javascript
25 import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
26 import { unstable_useMemoCache as useMemoCache } from "react"; // @gating @compilationMode(annotation)
27 -function Bar_uncompiled(props) {
28 - "use forget";
29 - return <div>{props.bar}</div>;
30 -}
31 -function Bar_forget(props) {
32 - const $ = useMemoCache(2);
33 - const c_0 = $[0] !== props.bar;
34 - let t0;
35 - if (c_0) {
36 - t0 = <div>{props.bar}</div>;
37 - $[0] = props.bar;
38 - $[1] = t0;
39 - } else {
40 - t0 = $[1];
41 - }
42 - return t0;
43 -}
44 -const Bar = isForgetEnabled_Fixtures() ? Bar_forget : Bar_uncompiled;
45 -export default Bar;
27 +export default isForgetEnabled_Fixtures()
28 + ? function Bar(props) {
29 + const $ = useMemoCache(2);
30 + const c_0 = $[0] !== props.bar;
31 + let t0;
32 + if (c_0) {
33 + t0 = <div>{props.bar}</div>;
34 + $[0] = props.bar;
35 + $[1] = t0;
36 + } else {
37 + t0 = $[1];
38 + }
39 + return t0;
40 + }
41 + : function Bar(props) {
42 + "use forget";
43 + return <div>{props.bar}</div>;
44 + };
45
46 function NoForget(props) {
47 return <Bar>{props.noForget}</Bar>;
48 }
50 -
51 -function Foo_uncompiled(props) {
52 - "use forget";
53 - return <Foo>{props.bar}</Foo>;
54 -}
55 -function Foo_forget(props) {
56 - const $ = useMemoCache(2);
57 - const c_0 = $[0] !== props.bar;
58 - let t0;
59 - if (c_0) {
60 - t0 = <Foo>{props.bar}</Foo>;
61 - $[0] = props.bar;
62 - $[1] = t0;
63 - } else {
64 - t0 = $[1];
65 - }
66 - return t0;
67 -}
68 -const Foo = isForgetEnabled_Fixtures() ? Foo_forget : Foo_uncompiled;
49 +const Foo = isForgetEnabled_Fixtures()
50 + ? function Foo(props) {
51 + const $ = useMemoCache(2);
52 + const c_0 = $[0] !== props.bar;
53 + let t0;
54 + if (c_0) {
55 + t0 = <Foo>{props.bar}</Foo>;
56 + $[0] = props.bar;
57 + $[1] = t0;
58 + } else {
59 + t0 = $[1];
60 + }
61 + return t0;
62 + }
63 + : function Foo(props) {
64 + "use forget";
65 + return <Foo>{props.bar}</Foo>;
66 + };
67
68 ```
69
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/gating-test-export-function-and-default.expect.md
+36 -37
@@ -24,48 +24,47 @@ export function Foo(props) {
24 ```javascript
25 import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
26 import { unstable_useMemoCache as useMemoCache } from "react"; // @gating @compilationMode(annotation)
27 -function Bar_uncompiled(props) {
28 - "use forget";
29 - return <div>{props.bar}</div>;
30 -}
31 -function Bar_forget(props) {
32 - const $ = useMemoCache(2);
33 - const c_0 = $[0] !== props.bar;
34 - let t0;
35 - if (c_0) {
36 - t0 = <div>{props.bar}</div>;
37 - $[0] = props.bar;
38 - $[1] = t0;
39 - } else {
40 - t0 = $[1];
41 - }
42 - return t0;
43 -}
44 -const Bar = isForgetEnabled_Fixtures() ? Bar_forget : Bar_uncompiled;
45 -export default Bar;
27 +export default isForgetEnabled_Fixtures()
28 + ? function Bar(props) {
29 + const $ = useMemoCache(2);
30 + const c_0 = $[0] !== props.bar;
31 + let t0;
32 + if (c_0) {
33 + t0 = <div>{props.bar}</div>;
34 + $[0] = props.bar;
35 + $[1] = t0;
36 + } else {
37 + t0 = $[1];
38 + }
39 + return t0;
40 + }
41 + : function Bar(props) {
42 + "use forget";
43 + return <div>{props.bar}</div>;
44 + };
45
46 function NoForget(props) {
47 return <Bar>{props.noForget}</Bar>;
48 }
49
51 -function Foo_uncompiled(props) {
52 - "use forget";
53 - return <Foo>{props.bar}</Foo>;
54 -}
55 -function Foo_forget(props) {
56 - const $ = useMemoCache(2);
57 - const c_0 = $[0] !== props.bar;
58 - let t0;
59 - if (c_0) {
60 - t0 = <Foo>{props.bar}</Foo>;
61 - $[0] = props.bar;
62 - $[1] = t0;
63 - } else {
64 - t0 = $[1];
65 - }
66 - return t0;
67 -}
68 -export const Foo = isForgetEnabled_Fixtures() ? Foo_forget : Foo_uncompiled;
50 +export const Foo = isForgetEnabled_Fixtures()
51 + ? function Foo(props) {
52 + const $ = useMemoCache(2);
53 + const c_0 = $[0] !== props.bar;
54 + let t0;
55 + if (c_0) {
56 + t0 = <Foo>{props.bar}</Foo>;
57 + $[0] = props.bar;
58 + $[1] = t0;
59 + } else {
60 + t0 = $[1];
61 + }
62 + return t0;
63 + }
64 + : function Foo(props) {
65 + "use forget";
66 + return <Foo>{props.bar}</Foo>;
67 + };
68
69 ```
70
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/gating-test-export-function.expect.md
+36 -36
@@ -24,47 +24,47 @@ export function Foo(props) {
24 ```javascript
25 import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
26 import { unstable_useMemoCache as useMemoCache } from "react"; // @gating @compilationMode(annotation)
27 -function Bar_uncompiled(props) {
28 - "use forget";
29 - return <div>{props.bar}</div>;
30 -}
31 -function Bar_forget(props) {
32 - const $ = useMemoCache(2);
33 - const c_0 = $[0] !== props.bar;
34 - let t0;
35 - if (c_0) {
36 - t0 = <div>{props.bar}</div>;
37 - $[0] = props.bar;
38 - $[1] = t0;
39 - } else {
40 - t0 = $[1];
41 - }
42 - return t0;
43 -}
44 -export const Bar = isForgetEnabled_Fixtures() ? Bar_forget : Bar_uncompiled;
27 +export const Bar = isForgetEnabled_Fixtures()
28 + ? function Bar(props) {
29 + const $ = useMemoCache(2);
30 + const c_0 = $[0] !== props.bar;
31 + let t0;
32 + if (c_0) {
33 + t0 = <div>{props.bar}</div>;
34 + $[0] = props.bar;
35 + $[1] = t0;
36 + } else {
37 + t0 = $[1];
38 + }
39 + return t0;
40 + }
41 + : function Bar(props) {
42 + "use forget";
43 + return <div>{props.bar}</div>;
44 + };
45
46 export function NoForget(props) {
47 return <Bar>{props.noForget}</Bar>;
48 }
49
50 -function Foo_uncompiled(props) {
51 - "use forget";
52 - return <Foo>{props.bar}</Foo>;
53 -}
54 -function Foo_forget(props) {
55 - const $ = useMemoCache(2);
56 - const c_0 = $[0] !== props.bar;
57 - let t0;
58 - if (c_0) {
59 - t0 = <Foo>{props.bar}</Foo>;
60 - $[0] = props.bar;
61 - $[1] = t0;
62 - } else {
63 - t0 = $[1];
64 - }
65 - return t0;
66 -}
67 -export const Foo = isForgetEnabled_Fixtures() ? Foo_forget : Foo_uncompiled;
50 +export const Foo = isForgetEnabled_Fixtures()
51 + ? function Foo(props) {
52 + const $ = useMemoCache(2);
53 + const c_0 = $[0] !== props.bar;
54 + let t0;
55 + if (c_0) {
56 + t0 = <Foo>{props.bar}</Foo>;
57 + $[0] = props.bar;
58 + $[1] = t0;
59 + } else {
60 + t0 = $[1];
61 + }
62 + return t0;
63 + }
64 + : function Foo(props) {
65 + "use forget";
66 + return <Foo>{props.bar}</Foo>;
67 + };
68
69 ```
70
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/gating-test.expect.md
+36 -37
@@ -24,47 +24,46 @@ function Foo(props) {
24 ```javascript
25 import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
26 import { unstable_useMemoCache as useMemoCache } from "react"; // @gating @compilationMode(annotation)
27 -function Bar_uncompiled(props) {
28 - "use forget";
29 - return <div>{props.bar}</div>;
30 -}
31 -function Bar_forget(props) {
32 - const $ = useMemoCache(2);
33 - const c_0 = $[0] !== props.bar;
34 - let t0;
35 - if (c_0) {
36 - t0 = <div>{props.bar}</div>;
37 - $[0] = props.bar;
38 - $[1] = t0;
39 - } else {
40 - t0 = $[1];
41 - }
42 - return t0;
43 -}
44 -const Bar = isForgetEnabled_Fixtures() ? Bar_forget : Bar_uncompiled;
27 +const Bar = isForgetEnabled_Fixtures()
28 + ? function Bar(props) {
29 + const $ = useMemoCache(2);
30 + const c_0 = $[0] !== props.bar;
31 + let t0;
32 + if (c_0) {
33 + t0 = <div>{props.bar}</div>;
34 + $[0] = props.bar;
35 + $[1] = t0;
36 + } else {
37 + t0 = $[1];
38 + }
39 + return t0;
40 + }
41 + : function Bar(props) {
42 + "use forget";
43 + return <div>{props.bar}</div>;
44 + };
45
46 function NoForget(props) {
47 return <Bar>{props.noForget}</Bar>;
48 }
49 -
50 -function Foo_uncompiled(props) {
51 - "use forget";
52 - return <Foo>{props.bar}</Foo>;
53 -}
54 -function Foo_forget(props) {
55 - const $ = useMemoCache(2);
56 - const c_0 = $[0] !== props.bar;
57 - let t0;
58 - if (c_0) {
59 - t0 = <Foo>{props.bar}</Foo>;
60 - $[0] = props.bar;
61 - $[1] = t0;
62 - } else {
63 - t0 = $[1];
64 - }
65 - return t0;
66 -}
67 -const Foo = isForgetEnabled_Fixtures() ? Foo_forget : Foo_uncompiled;
49 +const Foo = isForgetEnabled_Fixtures()
50 + ? function Foo(props) {
51 + const $ = useMemoCache(2);
52 + const c_0 = $[0] !== props.bar;
53 + let t0;
54 + if (c_0) {
55 + t0 = <Foo>{props.bar}</Foo>;
56 + $[0] = props.bar;
57 + $[1] = t0;
58 + } else {
59 + t0 = $[1];
60 + }
61 + return t0;
62 + }
63 + : function Foo(props) {
64 + "use forget";
65 + return <Foo>{props.bar}</Foo>;
66 + };
67
68 ```
69
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/infer-function-assignment.expect.md new
+29
@@ -0,0 +1,29 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @compilationMode(infer)
6 +const Component = (props) => {
7 + return <div />;
8 +};
9 +
10 +```
11 +
12 +## Code
13 +
14 +```javascript
15 +import { unstable_useMemoCache as useMemoCache } from "react"; // @compilationMode(infer)
16 +const Component = (props) => {
17 + const $ = useMemoCache(1);
18 + let t0;
19 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
20 + t0 = <div />;
21 + $[0] = t0;
22 + } else {
23 + t0 = $[0];
24 + }
25 + return t0;
26 +};
27 +
28 +```
29 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/infer-function-assignment.js renamed
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.expect.md new
+77
@@ -0,0 +1,77 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @gating
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 +## Code
18 +
19 +```javascript
20 +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
21 +import { unstable_useMemoCache as useMemoCache } from "react"; // @gating
22 +const ErrorView = isForgetEnabled_Fixtures()
23 + ? (error, _retry) => {
24 + const $ = useMemoCache(2);
25 + const c_0 = $[0] !== error;
26 + let t0;
27 + if (c_0) {
28 + t0 = <MessageBox error={error} />;
29 + $[0] = error;
30 + $[1] = t0;
31 + } else {
32 + t0 = $[1];
33 + }
34 + return t0;
35 + }
36 + : (error, _retry) => <MessageBox error={error}></MessageBox>;
37 +
38 +export default Renderer = isForgetEnabled_Fixtures()
39 + ? (props) => {
40 + const $ = useMemoCache(3);
41 + let t0;
42 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
43 + t0 = <Bar />;
44 + $[0] = t0;
45 + } else {
46 + t0 = $[0];
47 + }
48 + let t1;
49 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
50 + t1 = <ErrorView />;
51 + $[1] = t1;
52 + } else {
53 + t1 = $[1];
54 + }
55 + let t2;
56 + if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
57 + t2 = (
58 + <Foo>
59 + {t0}
60 + {t1}
61 + </Foo>
62 + );
63 + $[2] = t2;
64 + } else {
65 + t2 = $[2];
66 + }
67 + return t2;
68 + }
69 + : (props) => (
70 + <Foo>
71 + <Bar></Bar>
72 + <ErrorView></ErrorView>
73 + </Foo>
74 + );
75 +
76 +```
77 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/multi-arrow-expr-export-default-gating-test.js renamed
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/multi-arrow-expr-export-gating-test.expect.md
+49 -56
@@ -19,66 +19,59 @@ export const Renderer = (props) => (
19 ```javascript
20 import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
21 import { unstable_useMemoCache as useMemoCache } from "react"; // @gating
22 -function ErrorView_uncompiled(error, _retry) {
23 - return <MessageBox error={error}></MessageBox>;
24 -}
25 -function ErrorView_forget(error, _retry) {
26 - const $ = useMemoCache(2);
27 - const c_0 = $[0] !== error;
28 - let t0;
29 - if (c_0) {
30 - t0 = <MessageBox error={error} />;
31 - $[0] = error;
32 - $[1] = t0;
33 - } else {
34 - t0 = $[1];
35 - }
36 - return t0;
37 -}
22 const ErrorView = isForgetEnabled_Fixtures()
39 - ? ErrorView_forget
40 - : ErrorView_uncompiled;
41 -function Renderer_uncompiled(props) {
42 - return (
43 - <Foo>
44 - <Bar></Bar>
45 - <ErrorView></ErrorView>
46 - </Foo>
47 - );
48 -}
49 -function Renderer_forget(props) {
50 - const $ = useMemoCache(3);
51 - let t0;
52 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
53 - t0 = <Bar />;
54 - $[0] = t0;
55 - } else {
56 - t0 = $[0];
57 - }
58 - let t1;
59 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
60 - t1 = <ErrorView />;
61 - $[1] = t1;
62 - } else {
63 - t1 = $[1];
64 - }
65 - let t2;
66 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
67 - t2 = (
23 + ? (error, _retry) => {
24 + const $ = useMemoCache(2);
25 + const c_0 = $[0] !== error;
26 + let t0;
27 + if (c_0) {
28 + t0 = <MessageBox error={error} />;
29 + $[0] = error;
30 + $[1] = t0;
31 + } else {
32 + t0 = $[1];
33 + }
34 + return t0;
35 + }
36 + : (error, _retry) => <MessageBox error={error}></MessageBox>;
37 +
38 +export const Renderer = isForgetEnabled_Fixtures()
39 + ? (props) => {
40 + const $ = useMemoCache(3);
41 + let t0;
42 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
43 + t0 = <Bar />;
44 + $[0] = t0;
45 + } else {
46 + t0 = $[0];
47 + }
48 + let t1;
49 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
50 + t1 = <ErrorView />;
51 + $[1] = t1;
52 + } else {
53 + t1 = $[1];
54 + }
55 + let t2;
56 + if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
57 + t2 = (
58 + <Foo>
59 + {t0}
60 + {t1}
61 + </Foo>
62 + );
63 + $[2] = t2;
64 + } else {
65 + t2 = $[2];
66 + }
67 + return t2;
68 + }
69 + : (props) => (
70 <Foo>
69 - {t0}
70 - {t1}
71 + <Bar></Bar>
72 + <ErrorView></ErrorView>
73 </Foo>
74 );
73 - $[2] = t2;
74 - } else {
75 - t2 = $[2];
76 - }
77 - return t2;
78 -}
79 -export const Renderer = isForgetEnabled_Fixtures()
80 - ? Renderer_forget
81 - : Renderer_uncompiled;
75
76 ```
77
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/multi-arrow-expr-gating-test.expect.md
+49 -56
@@ -21,66 +21,59 @@ export default Renderer;
21 ```javascript
22 import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
23 import { unstable_useMemoCache as useMemoCache } from "react"; // @gating
24 -function ErrorView_uncompiled(error, _retry) {
25 - return <MessageBox error={error}></MessageBox>;
26 -}
27 -function ErrorView_forget(error, _retry) {
28 - const $ = useMemoCache(2);
29 - const c_0 = $[0] !== error;
30 - let t0;
31 - if (c_0) {
32 - t0 = <MessageBox error={error} />;
33 - $[0] = error;
34 - $[1] = t0;
35 - } else {
36 - t0 = $[1];
37 - }
38 - return t0;
39 -}
24 const ErrorView = isForgetEnabled_Fixtures()
41 - ? ErrorView_forget
42 - : ErrorView_uncompiled;
43 -function Renderer_uncompiled(props) {
44 - return (
45 - <Foo>
46 - <Bar></Bar>
47 - <ErrorView></ErrorView>
48 - </Foo>
49 - );
50 -}
51 -function Renderer_forget(props) {
52 - const $ = useMemoCache(3);
53 - let t0;
54 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
55 - t0 = <Bar />;
56 - $[0] = t0;
57 - } else {
58 - t0 = $[0];
59 - }
60 - let t1;
61 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
62 - t1 = <ErrorView />;
63 - $[1] = t1;
64 - } else {
65 - t1 = $[1];
66 - }
67 - let t2;
68 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
69 - t2 = (
25 + ? (error, _retry) => {
26 + const $ = useMemoCache(2);
27 + const c_0 = $[0] !== error;
28 + let t0;
29 + if (c_0) {
30 + t0 = <MessageBox error={error} />;
31 + $[0] = error;
32 + $[1] = t0;
33 + } else {
34 + t0 = $[1];
35 + }
36 + return t0;
37 + }
38 + : (error, _retry) => <MessageBox error={error}></MessageBox>;
39 +
40 +const Renderer = isForgetEnabled_Fixtures()
41 + ? (props) => {
42 + const $ = useMemoCache(3);
43 + let t0;
44 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
45 + t0 = <Bar />;
46 + $[0] = t0;
47 + } else {
48 + t0 = $[0];
49 + }
50 + let t1;
51 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
52 + t1 = <ErrorView />;
53 + $[1] = t1;
54 + } else {
55 + t1 = $[1];
56 + }
57 + let t2;
58 + if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
59 + t2 = (
60 + <Foo>
61 + {t0}
62 + {t1}
63 + </Foo>
64 + );
65 + $[2] = t2;
66 + } else {
67 + t2 = $[2];
68 + }
69 + return t2;
70 + }
71 + : (props) => (
72 <Foo>
71 - {t0}
72 - {t1}
73 + <Bar></Bar>
74 + <ErrorView></ErrorView>
75 </Foo>
76 );
75 - $[2] = t2;
76 - } else {
77 - t2 = $[2];
78 - }
79 - return t2;
80 -}
81 -const Renderer = isForgetEnabled_Fixtures()
82 - ? Renderer_forget
83 - : Renderer_uncompiled;
77 export default Renderer;
78
79 ```
compiler/packages/sprout/src/SproutTodoFilter.ts
+3
@@ -406,6 +406,7 @@ const skipFilter = new Set([
406 "ssa-newexpression",
407 "ssa-shadowing",
408 "template-literal",
409 + "multi-arrow-expr-export-default-gating-test",
410
411 // TODO: we should be able to support these
412 "fbt-call",
@@ -438,6 +439,7 @@ const skipFilter = new Set([
439 "rules-of-hooks/rules-of-hooks-e66a744cffbe",
440 "rules-of-hooks/rules-of-hooks-eacfcaa6ef89",
441 "rules-of-hooks/rules-of-hooks-fe6042f7628b",
442 + "infer-function-assignment",
443 "infer-functions-component-with-jsx",
444 "infer-function-forwardRef",
445 "infer-function-React-memo",
@@ -445,6 +447,7 @@ const skipFilter = new Set([
447 "infer-functions-component-with-jsx",
448 "infer-functions-hook-with-hook-call",
449 "infer-functions-hook-with-jsx",
450 + "infer-function-expression-component",
451 ]);
452
453 export default skipFilter;