diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 2880e9283c..9dc34d62ba 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -1258,6 +1258,15 @@ function getFunctionReferencedBeforeDeclarationAtTopLevel( TSTypeAliasDeclaration(path) { path.skip(); }, + TSDeclareFunction(path) { + /* + * Skip TypeScript overload signatures (function declarations without a body) + * to avoid false positives in reference-before-declaration detection. + * TSDeclareFunction.id is treated as isReferencedIdentifier() by Babel, + * but these are not actual runtime references. + */ + path.skip(); + }, Identifier(id) { const fn = fnNames.get(id.node.name); // We're not tracking this identifier. diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-ts-overload-export.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-ts-overload-export.expect.md new file mode 100644 index 0000000000..2396bb0fa1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-ts-overload-export.expect.md @@ -0,0 +1,86 @@ + +## Input + +```javascript +// @gating @compilationMode:"annotation" +import {useStore} from 'shared-runtime'; + +interface Session { + user: string; +} + +// Overload signatures +export function useSession(): Session | null; +export function useSession(selector: (session: Session) => T): T | null; +// Implementation +export function useSession( + selector?: (session: Session) => T, +): Session | T | null { + 'use forget'; + return useStore((s: {session: Session | null}) => { + const session = s.session; + if (!session) return null; + return selector ? selector(session) : session; + }); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('useSession'), + params: [[]], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating @compilationMode:"annotation" +import { useStore } from "shared-runtime"; + +interface Session { + user: string; +} + +// Overload signatures +export function useSession(): Session | null; +export function useSession(selector: (session: Session) => T): T | null; +// Implementation +export const useSession = isForgetEnabled_Fixtures() + ? function useSession(selector) { + "use forget"; + const $ = _c(2); + let t0; + if ($[0] !== selector) { + t0 = (s) => { + const session = s.session; + if (!session) { + return null; + } + return selector ? selector(session) : session; + }; + $[0] = selector; + $[1] = t0; + } else { + t0 = $[1]; + } + return useStore(t0); + } + : function useSession(selector?: (session: Session) => T) { + "use forget"; + return useStore((s: { session: Session | null }) => { + const session = s.session; + if (!session) return null; + return selector ? selector(session) : session; + }); + }; + +export const FIXTURE_ENTRYPOINT = { + fn: eval("useSession"), + params: [[]], +}; + +``` + +### Eval output +(kind: exception) (0 , _sharedRuntime.useStore) is not a function \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-ts-overload-export.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-ts-overload-export.tsx new file mode 100644 index 0000000000..2464e049c3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-ts-overload-export.tsx @@ -0,0 +1,26 @@ +// @gating @compilationMode:"annotation" +import {useStore} from 'shared-runtime'; + +interface Session { + user: string; +} + +// Overload signatures +export function useSession(): Session | null; +export function useSession(selector: (session: Session) => T): T | null; +// Implementation +export function useSession( + selector?: (session: Session) => T, +): Session | T | null { + 'use forget'; + return useStore((s: {session: Session | null}) => { + const session = s.session; + if (!session) return null; + return selector ? selector(session) : session; + }); +} + +export const FIXTURE_ENTRYPOINT = { + fn: eval('useSession'), + params: [[]], +};