diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts index 263f76395f0..16ef7986ef1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Gating.ts @@ -10,6 +10,117 @@ import * as t from '@babel/types'; import {PluginOptions} from './Options'; import {CompilerError} from '../CompilerError'; +/** + * Gating rewrite for function declarations which are referenced before their + * declaration site. + * + * ```js + * // original + * export default React.memo(Foo); + * function Foo() { ... } + * + * // React compiler optimized + gated + * import {gating} from 'myGating'; + * export default React.memo(Foo); + * const gating_result = gating(); <- inserted + * function Foo_optimized() {} <- inserted + * function Foo_unoptimized() {} <- renamed from Foo + * function Foo() { <- inserted function, which can be hoisted by JS engines + * if (gating_result) return Foo_optimized(); + * else return Foo_unoptimized(); + * } + * ``` + */ +function insertAdditionalFunctionDeclaration( + fnPath: NodePath, + compiled: t.FunctionDeclaration, + gating: NonNullable, +): void { + const originalFnName = fnPath.node.id; + const originalFnParams = fnPath.node.params; + const compiledParams = fnPath.node.params; + /** + * Note that other than `export default function() {}`, all other function + * declarations must have a binding identifier. Since default exports cannot + * be referenced, it's safe to assume that all function declarations passed + * here will have an identifier. + * https://tc39.es/ecma262/multipage/ecmascript-language-functions-and-classes.html#sec-function-definitions + */ + CompilerError.invariant(originalFnName != null && compiled.id != null, { + reason: + 'Expected function declarations that are referenced elsewhere to have a named identifier', + loc: fnPath.node.loc ?? null, + }); + CompilerError.invariant(originalFnParams.length === compiledParams.length, { + reason: + 'Expected React Compiler optimized function declarations to have the same number of parameters as source', + loc: fnPath.node.loc ?? null, + }); + + const gatingCondition = fnPath.scope.generateUidIdentifier( + `${gating.importSpecifierName}_result`, + ); + const unoptimizedFnName = fnPath.scope.generateUidIdentifier( + `${originalFnName.name}_unoptimized`, + ); + const optimizedFnName = fnPath.scope.generateUidIdentifier( + `${originalFnName.name}_optimized`, + ); + /** + * Step 1: rename existing functions + */ + compiled.id.name = optimizedFnName.name; + fnPath.get('id').replaceInline(unoptimizedFnName); + + /** + * Step 2: insert new function declaration + */ + const newParams: Array = []; + const genNewArgs: Array<() => t.Identifier | t.SpreadElement> = []; + for (let i = 0; i < originalFnParams.length; i++) { + const argName = `arg${i}`; + if (originalFnParams[i].type === 'RestElement') { + newParams.push(t.restElement(t.identifier(argName))); + genNewArgs.push(() => t.spreadElement(t.identifier(argName))); + } else { + newParams.push(t.identifier(argName)); + genNewArgs.push(() => t.identifier(argName)); + } + } + // insertAfter called in reverse order of how nodes should appear in program + fnPath.insertAfter( + t.functionDeclaration( + originalFnName, + newParams, + t.blockStatement([ + t.ifStatement( + gatingCondition, + t.returnStatement( + t.callExpression( + compiled.id, + genNewArgs.map(fn => fn()), + ), + ), + t.returnStatement( + t.callExpression( + unoptimizedFnName, + genNewArgs.map(fn => fn()), + ), + ), + ), + ]), + ), + ); + fnPath.insertBefore( + t.variableDeclaration('const', [ + t.variableDeclarator( + gatingCondition, + t.callExpression(t.identifier(gating.importSpecifierName), []), + ), + ]), + ); + fnPath.insertBefore(compiled); +} export function insertGatedFunctionDeclaration( fnPath: NodePath< t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression @@ -21,15 +132,13 @@ export function insertGatedFunctionDeclaration( gating: NonNullable, referencedBeforeDeclaration: boolean, ): void { - if (referencedBeforeDeclaration) { - const identifier = - fnPath.node.type === 'FunctionDeclaration' ? fnPath.node.id : null; - CompilerError.invariant(false, { - reason: `Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting`, - description: `Rewrite the reference to ${identifier?.name ?? 'this function'} to not rely on hoisting to fix this issue`, - loc: identifier?.loc ?? null, - suggestions: null, + if (referencedBeforeDeclaration && fnPath.isFunctionDeclaration()) { + CompilerError.invariant(compiled.type === 'FunctionDeclaration', { + reason: 'Expected compiled node type to match input type', + description: `Got ${compiled.type} but expected FunctionDeclaration`, + loc: fnPath.node.loc ?? null, }); + insertAdditionalFunctionDeclaration(fnPath, compiled, gating); } else { const gatingExpression = t.conditionalExpression( t.callExpression(t.identifier(gating.importSpecifierName), []), diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md new file mode 100644 index 00000000000..25215580c81 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.expect.md @@ -0,0 +1,62 @@ + +## Input + +```javascript +// @flow @gating +import {Stringify} from 'shared-runtime'; +import * as React from 'react'; + +component Foo(ref: React.RefSetter) { + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('(...args) => React.createElement(Foo, args)'), + params: [{ref: React.createRef()}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; +import { Stringify } from "shared-runtime"; +import * as React from "react"; + +const Foo = React.forwardRef(Foo_withRef); +const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function _Foo_withRef_optimized(_$$empty_props_placeholder$$, ref) { + const $ = _c(2); + let t0; + if ($[0] !== ref) { + t0 = ; + $[0] = ref; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} +function _Foo_withRef_unoptimized( + _$$empty_props_placeholder$$: $ReadOnly<{}>, + ref: React.RefSetter, +): React.Node { + return ; +} +function Foo_withRef(arg0, arg1) { + if (_isForgetEnabled_Fixtures_result) + return _Foo_withRef_optimized(arg0, arg1); + else return _Foo_withRef_unoptimized(arg0, arg1); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval("(...args) => React.createElement(Foo, args)"), + params: [{ ref: React.createRef() }], +}; + +``` + +### Eval output +(kind: ok)
{"ref":null}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.js new file mode 100644 index 00000000000..02bed83cb21 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/component-syntax-ref-gating.flow.js @@ -0,0 +1,12 @@ +// @flow @gating +import {Stringify} from 'shared-runtime'; +import * as React from 'react'; + +component Foo(ref: React.RefSetter) { + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('(...args) => React.createElement(Foo, args)'), + params: [{ref: React.createRef()}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md deleted file mode 100644 index 3f1576b0228..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.expect.md +++ /dev/null @@ -1,24 +0,0 @@ - -## Input - -```javascript -// @flow @gating -component Foo(ref: React.RefSetter) { - return ; -} - -``` - - -## Error - -``` - 1 | // @flow @gating -> 2 | component Foo(ref: React.RefSetter) { - | ^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo_withRef to not rely on hoisting to fix this issue (2:2) - 3 | return ; - 4 | } - 5 | -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js deleted file mode 100644 index 7cb271dd06c..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.component-syntax-ref-gating.flow.js +++ /dev/null @@ -1,4 +0,0 @@ -// @flow @gating -component Foo(ref: React.RefSetter) { - return ; -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md deleted file mode 100644 index 00084b56f30..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.expect.md +++ /dev/null @@ -1,26 +0,0 @@ - -## Input - -```javascript -// @gating -const Foo = React.forwardRef(Foo_withRef); -function Foo_withRef(props, ref) { - return ; -} - -``` - - -## Error - -``` - 1 | // @gating - 2 | const Foo = React.forwardRef(Foo_withRef); -> 3 | function Foo_withRef(props, ref) { - | ^^^^^^^^^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo_withRef to not rely on hoisting to fix this issue (3:3) - 4 | return ; - 5 | } - 6 | -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js deleted file mode 100644 index f0a2879c098..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-hoisting.js +++ /dev/null @@ -1,5 +0,0 @@ -// @gating -const Foo = React.forwardRef(Foo_withRef); -function Foo_withRef(props, ref) { - return ; -} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md deleted file mode 100644 index a1b5a61c210..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.expect.md +++ /dev/null @@ -1,24 +0,0 @@ - -## Input - -```javascript -// @gating -import {memo} from 'react'; - -export default memo(Foo); -function Foo() {} - -``` - - -## Error - -``` - 3 | - 4 | export default memo(Foo); -> 5 | function Foo() {} - | ^^^ Invariant: Encountered a function used before its declaration, which breaks Forget's gating codegen due to hoisting. Rewrite the reference to Foo to not rely on hoisting to fix this issue (5:5) - 6 | -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js deleted file mode 100644 index 7f778176e28..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/error.gating-use-before-decl.js +++ /dev/null @@ -1,5 +0,0 @@ -// @gating -import {memo} from 'react'; - -export default memo(Foo); -function Foo() {} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md new file mode 100644 index 00000000000..d09c98dffd9 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.expect.md @@ -0,0 +1,61 @@ + +## Input + +```javascript +// @gating +import {createRef, forwardRef} from 'react'; +import {Stringify} from 'shared-runtime'; + +const Foo = forwardRef(Foo_withRef); +function Foo_withRef(props, ref) { + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('(...args) => React.createElement(Foo, args)'), + params: [{prop1: 1, prop2: 2, ref: createRef()}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import { createRef, forwardRef } from "react"; +import { Stringify } from "shared-runtime"; + +const Foo = forwardRef(Foo_withRef); +const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function _Foo_withRef_optimized(props, ref) { + const $ = _c(3); + let t0; + if ($[0] !== props || $[1] !== ref) { + t0 = ; + $[0] = props; + $[1] = ref; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} +function _Foo_withRef_unoptimized(props, ref) { + return ; +} +function Foo_withRef(arg0, arg1) { + if (_isForgetEnabled_Fixtures_result) + return _Foo_withRef_optimized(arg0, arg1); + else return _Foo_withRef_unoptimized(arg0, arg1); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval("(...args) => React.createElement(Foo, args)"), + params: [{ prop1: 1, prop2: 2, ref: createRef() }], +}; + +``` + +### Eval output +(kind: ok)
{"0":{"prop1":1,"prop2":2,"ref":{"current":null}},"ref":"[[ cyclic ref *3 ]]"}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.js new file mode 100644 index 00000000000..a382497d992 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl-ref.js @@ -0,0 +1,13 @@ +// @gating +import {createRef, forwardRef} from 'react'; +import {Stringify} from 'shared-runtime'; + +const Foo = forwardRef(Foo_withRef); +function Foo_withRef(props, ref) { + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('(...args) => React.createElement(Foo, args)'), + params: [{prop1: 1, prop2: 2, ref: createRef()}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md new file mode 100644 index 00000000000..0bbfc967567 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.expect.md @@ -0,0 +1,64 @@ + +## Input + +```javascript +// @gating +import {memo} from 'react'; +import {Stringify} from 'shared-runtime'; + +export default memo(Foo); +function Foo({prop1, prop2}) { + 'use memo'; + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Foo'), + params: [{prop1: 1, prop2: 2}], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import { memo } from "react"; +import { Stringify } from "shared-runtime"; + +export default memo(Foo); +const _isForgetEnabled_Fixtures_result = isForgetEnabled_Fixtures(); +function _Foo_optimized(t0) { + "use memo"; + const $ = _c(3); + const { prop1, prop2 } = t0; + let t1; + if ($[0] !== prop1 || $[1] !== prop2) { + t1 = ; + $[0] = prop1; + $[1] = prop2; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} +function _Foo_unoptimized({ prop1, prop2 }) { + "use memo"; + return ; +} +function Foo(arg0) { + if (_isForgetEnabled_Fixtures_result) return _Foo_optimized(arg0); + else return _Foo_unoptimized(arg0); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval("Foo"), + params: [{ prop1: 1, prop2: 2 }], +}; + +``` + +### Eval output +(kind: ok)
{"prop1":1,"prop2":2}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.js new file mode 100644 index 00000000000..d51a7fcac0a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-use-before-decl.js @@ -0,0 +1,14 @@ +// @gating +import {memo} from 'react'; +import {Stringify} from 'shared-runtime'; + +export default memo(Foo); +function Foo({prop1, prop2}) { + 'use memo'; + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('Foo'), + params: [{prop1: 1, prop2: 2}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md new file mode 100644 index 00000000000..47b58453ca0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +// @gating +import * as React from 'react'; + +let Foo; +const MemoFoo = React.memo(Foo); +Foo = () =>
hello world!
; + +/** + * Evaluate this fixture module to assert that compiler + original have the same + * runtime error message. + */ +export const FIXTURE_ENTRYPOINT = { + fn: () => {}, + params: [], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import * as React from "react"; + +let Foo; +const MemoFoo = React.memo(Foo); +Foo = isForgetEnabled_Fixtures() + ? () => { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
hello world!
; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + : () =>
hello world!
; + +/** + * Evaluate this fixture module to assert that compiler + original have the same + * runtime error message. + */ +export const FIXTURE_ENTRYPOINT = { + fn: isForgetEnabled_Fixtures() ? () => {} : () => {}, + params: [], +}; + +``` + +### Eval output +(kind: ok) +logs: ['memo: The first argument must be a component. Instead received: %s','undefined'] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.js new file mode 100644 index 00000000000..2bdeb76f589 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/invalid-fnexpr-reference.js @@ -0,0 +1,15 @@ +// @gating +import * as React from 'react'; + +let Foo; +const MemoFoo = React.memo(Foo); +Foo = () =>
hello world!
; + +/** + * Evaluate this fixture module to assert that compiler + original have the same + * runtime error message. + */ +export const FIXTURE_ENTRYPOINT = { + fn: () => {}, + params: [], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md new file mode 100644 index 00000000000..5f18d98491f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.expect.md @@ -0,0 +1,86 @@ + +## Input + +```javascript +// @gating +import * as React from 'react'; + +/** + * Test that the correct `Foo` is printed + */ +let Foo = () =>
hello world 1!
; +const MemoOne = React.memo(Foo); +Foo = () =>
hello world 2!
; +const MemoTwo = React.memo(Foo); + +export const FIXTURE_ENTRYPOINT = { + fn: () => { + 'use no memo'; + return ( + <> + + + + ); + }, + params: [], +}; + +``` + +## Code + +```javascript +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; +import { c as _c } from "react/compiler-runtime"; // @gating +import * as React from "react"; + +/** + * Test that the correct `Foo` is printed + */ +let Foo = isForgetEnabled_Fixtures() + ? () => { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
hello world 1!
; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + : () =>
hello world 1!
; +const MemoOne = React.memo(Foo); +Foo = isForgetEnabled_Fixtures() + ? () => { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
hello world 2!
; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + : () =>
hello world 2!
; +const MemoTwo = React.memo(Foo); + +export const FIXTURE_ENTRYPOINT = { + fn: () => { + "use no memo"; + return ( + <> + + + + ); + }, + params: [], +}; + +``` + +### Eval output +(kind: ok)
hello world 1!
hello world 2!
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.js new file mode 100644 index 00000000000..f2275da7a2c --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/reassigned-fnexpr-variable.js @@ -0,0 +1,23 @@ +// @gating +import * as React from 'react'; + +/** + * Test that the correct `Foo` is printed + */ +let Foo = () =>
hello world 1!
; +const MemoOne = React.memo(Foo); +Foo = () =>
hello world 2!
; +const MemoTwo = React.memo(Foo); + +export const FIXTURE_ENTRYPOINT = { + fn: () => { + 'use no memo'; + return ( + <> + + + + ); + }, + params: [], +};