diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
index c75b0a15b037b..cda0aeded666b 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
@@ -11,7 +11,6 @@ import { fromZodError } from "zod-validation-error";
import { CompilerError } from "../CompilerError";
import { Logger } from "../Entrypoint";
import { Err, Ok, Result } from "../Utils/Result";
-import { log } from "../Utils/logger";
import {
DEFAULT_GLOBALS,
DEFAULT_SHAPES,
@@ -320,6 +319,8 @@ const EnvironmentConfigSchema = z.object({
*/
throwUnknownException__testonly: z.boolean().default(false),
+ enableSharedRuntime__testonly: z.boolean().default(false),
+
/**
* Enables deps of a function epxression to be treated as conditional. This
* makes sure we don't load a dep when it's a property (to check if it has
@@ -514,7 +515,6 @@ export class Environment {
getGlobalDeclaration(binding: NonLocalBinding): Global | null {
const name = binding.name;
- let resolvedName = name;
if (this.config.hookPattern != null) {
const match = new RegExp(this.config.hookPattern).exec(name);
@@ -523,21 +523,45 @@ export class Environment {
typeof match[1] === "string" &&
isHookName(match[1])
) {
- resolvedName = match[1];
+ const resolvedName = match[1];
+ return this.#globals.get(resolvedName) ?? this.#getCustomHookType();
}
}
- let resolvedGlobal: Global | null = this.#globals.get(resolvedName) ?? null;
- if (resolvedGlobal === null) {
- // Hack, since we don't track module level declarations and imports
- if (isHookName(resolvedName)) {
- return this.#getCustomHookType();
- } else {
- log(() => `Undefined global \`${name}\``);
+ let global: Global | null = null;
+ switch (binding.kind) {
+ case "ModuleLocal": {
+ // don't resolve module locals
+ break;
+ }
+ case "Global": {
+ global = this.#globals.get(name) ?? null;
+ break;
+ }
+ case "ImportDefault":
+ case "ImportNamespace":
+ case "ImportSpecifier": {
+ if (
+ binding.module.toLowerCase() === "react" ||
+ binding.module.toLowerCase() === "react-dom" ||
+ (this.config.enableSharedRuntime__testonly &&
+ binding.module === "shared-runtime")
+ ) {
+ // only resolve imports to modules we know about
+ global = this.#globals.get(name) ?? null;
+ }
+ break;
}
}
- return resolvedGlobal;
+ if (global === null && isHookName(name)) {
+ /**
+ * Type inference relies on all hooks being resolved as such, so if we don't have
+ * a global declaration and its a hook name, return the default custom hook type.
+ */
+ return this.#getCustomHookType();
+ }
+ return global;
}
getPropertyType(
diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
index 126e78f8002fc..b0a6fb1635b00 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
@@ -588,7 +588,38 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
break;
}
case "LoadGlobal": {
- value = `LoadGlobal ${instrValue.binding.name}`;
+ switch (instrValue.binding.kind) {
+ case "Global": {
+ value = `LoadGlobal(global) ${instrValue.binding.name}`;
+ break;
+ }
+ case "ModuleLocal": {
+ value = `LoadGlobal(module) ${instrValue.binding.name}`;
+ break;
+ }
+ case "ImportDefault": {
+ value = `LoadGlobal import ${instrValue.binding.name} from '${instrValue.binding.module}'`;
+ break;
+ }
+ case "ImportNamespace": {
+ value = `LoadGlobal import * as ${instrValue.binding.name} from '${instrValue.binding.module}'`;
+ break;
+ }
+ case "ImportSpecifier": {
+ if (instrValue.binding.imported !== instrValue.binding.name) {
+ value = `LoadGlobal import { ${instrValue.binding.imported} as ${instrValue.binding.name} } from '${instrValue.binding.module}'`;
+ } else {
+ value = `LoadGlobal import { ${instrValue.binding.name} } from '${instrValue.binding.module}'`;
+ }
+ break;
+ }
+ default: {
+ assertExhaustive(
+ instrValue.binding,
+ `Unexpected binding kind \`${(instrValue.binding as any).kind}\``
+ );
+ }
+ }
break;
}
case "StoreGlobal": {
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/destructuring-mixed-scope-and-local-variables-with-default.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/destructuring-mixed-scope-and-local-variables-with-default.expect.md
index 7fb336c467607..e93fbb3b45fcf 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/destructuring-mixed-scope-and-local-variables-with-default.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/destructuring-mixed-scope-and-local-variables-with-default.expect.md
@@ -56,71 +56,78 @@ function useFragment(_arg1, _arg2) {
}
function Component(props) {
- const $ = _c(14);
- const post = useFragment(graphql`...`, props.post);
+ const $ = _c(15);
+ let t0;
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
+ t0 = graphql`...`;
+ $[0] = t0;
+ } else {
+ t0 = $[0];
+ }
+ const post = useFragment(t0, props.post);
let media;
let allUrls;
let onClick;
- if ($[0] !== post) {
+ if ($[1] !== post) {
allUrls = [];
- const { media: t0, comments: t1, urls: t2 } = post;
- media = t0 === undefined ? null : t0;
- let t3;
- if ($[4] !== t1) {
- t3 = t1 === undefined ? [] : t1;
- $[4] = t1;
- $[5] = t3;
- } else {
- t3 = $[5];
- }
- const comments = t3;
+ const { media: t1, comments: t2, urls: t3 } = post;
+ media = t1 === undefined ? null : t1;
let t4;
- if ($[6] !== t2) {
+ if ($[5] !== t2) {
t4 = t2 === undefined ? [] : t2;
- $[6] = t2;
- $[7] = t4;
+ $[5] = t2;
+ $[6] = t4;
} else {
- t4 = $[7];
+ t4 = $[6];
}
- const urls = t4;
+ const comments = t4;
let t5;
- if ($[8] !== comments.length) {
- t5 = (e) => {
+ if ($[7] !== t3) {
+ t5 = t3 === undefined ? [] : t3;
+ $[7] = t3;
+ $[8] = t5;
+ } else {
+ t5 = $[8];
+ }
+ const urls = t5;
+ let t6;
+ if ($[9] !== comments.length) {
+ t6 = (e) => {
if (!comments.length) {
return;
}
console.log(comments.length);
};
- $[8] = comments.length;
- $[9] = t5;
+ $[9] = comments.length;
+ $[10] = t6;
} else {
- t5 = $[9];
+ t6 = $[10];
}
- onClick = t5;
+ onClick = t6;
allUrls.push(...urls);
- $[0] = post;
- $[1] = media;
- $[2] = allUrls;
- $[3] = onClick;
+ $[1] = post;
+ $[2] = media;
+ $[3] = allUrls;
+ $[4] = onClick;
} else {
- media = $[1];
- allUrls = $[2];
- onClick = $[3];
+ media = $[2];
+ allUrls = $[3];
+ onClick = $[4];
}
- let t0;
- if ($[10] !== media || $[11] !== allUrls || $[12] !== onClick) {
- t0 =