Skip to content

Commit 865d2c4

Browse files
authored
[compiler] Add meta internal option for useMemoCache import (facebook#31654)
Adds `target: 'donotuse_meta_internal'`, which inserts useMemoCache imports directly from `react`. Note that this is only valid for Meta bundles, as others do not [re-export the `c` function](https://github.com/facebook/react/blob/5b0ef217ef32333a8e56f39be04327c89efa346f/packages/react/index.fb.js#L68-L70). ```js // target=donotuse_meta_internal import {c as _c} from 'react'; // target=19 import {c as _c} from 'react/compiler-runtime'; // target=17,18 import {c as _c} from 'react-compiler-runtime'; ``` Meta is a bit special in that react runtime and compiler are guaranteed to be up-to-date and compatible. It also has its own bundling and module resolution logic, which makes importing from `react/compiler-runtime` tricky. I'm also fine with implementing the alternative which adds an internal stub for `react-compiler-runtime` and [bundles](https://github.com/facebook/react/blob/5b0ef217ef32333a8e56f39be04327c89efa346f/scripts/rollup/bundles.js#L120) the runtime for internal builds.
1 parent 5b0ef21 commit 865d2c4

File tree

6 files changed

+96
-25
lines changed

6 files changed

+96
-25
lines changed

compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,22 @@ export type PluginOptions = {
121121
target: CompilerReactTarget;
122122
};
123123

124-
const CompilerReactTargetSchema = z.enum(['17', '18', '19']);
124+
const CompilerReactTargetSchema = z.union([
125+
z.literal('17'),
126+
z.literal('18'),
127+
z.literal('19'),
128+
/**
129+
* Used exclusively for Meta apps which are guaranteed to have compatible
130+
* react runtime and compiler versions. Note that only the FB-internal bundles
131+
* re-export useMemoCache (see
132+
* https://github.com/facebook/react/blob/5b0ef217ef32333a8e56f39be04327c89efa346f/packages/react/index.fb.js#L68-L70),
133+
* so this option is invalid / creates runtime errors for open-source users.
134+
*/
135+
z.object({
136+
kind: z.literal('donotuse_meta_internal'),
137+
runtimeModule: z.string().default('react'),
138+
}),
139+
]);
125140
export type CompilerReactTarget = z.infer<typeof CompilerReactTargetSchema>;
126141

127142
const CompilationModeSchema = z.enum([

compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,30 +1123,23 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
11231123
return errors.details.length > 0 ? errors : null;
11241124
}
11251125

1126-
type ReactCompilerRuntimeModule =
1127-
| 'react/compiler-runtime' // from react namespace
1128-
| 'react-compiler-runtime'; // npm package
1129-
function getReactCompilerRuntimeModule(
1130-
opts: PluginOptions,
1131-
): ReactCompilerRuntimeModule {
1132-
let moduleName: ReactCompilerRuntimeModule | null = null;
1133-
switch (opts.target) {
1134-
case '17':
1135-
case '18': {
1136-
moduleName = 'react-compiler-runtime';
1137-
break;
1138-
}
1139-
case '19': {
1140-
moduleName = 'react/compiler-runtime';
1141-
break;
1142-
}
1143-
default:
1144-
CompilerError.invariant(moduleName != null, {
1126+
function getReactCompilerRuntimeModule(opts: PluginOptions): string {
1127+
if (opts.target === '19') {
1128+
return 'react/compiler-runtime'; // from react namespace
1129+
} else if (opts.target === '17' || opts.target === '18') {
1130+
return 'react-compiler-runtime'; // npm package
1131+
} else {
1132+
CompilerError.invariant(
1133+
opts.target != null &&
1134+
opts.target.kind === 'donotuse_meta_internal' &&
1135+
typeof opts.target.runtimeModule === 'string',
1136+
{
11451137
reason: 'Expected target to already be validated',
11461138
description: null,
11471139
loc: null,
11481140
suggestions: null,
1149-
});
1141+
},
1142+
);
1143+
return opts.target.runtimeModule;
11501144
}
1151-
return moduleName;
11521145
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @target="donotuse_meta_internal"
6+
7+
function Component() {
8+
return <div>Hello world</div>;
9+
}
10+
11+
export const FIXTURE_ENTRYPOINT = {
12+
fn: Component,
13+
params: [],
14+
isComponent: true,
15+
};
16+
17+
```
18+
19+
## Code
20+
21+
```javascript
22+
import { c as _c } from "react"; // @target="donotuse_meta_internal"
23+
24+
function Component() {
25+
const $ = _c(1);
26+
let t0;
27+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
28+
t0 = <div>Hello world</div>;
29+
$[0] = t0;
30+
} else {
31+
t0 = $[0];
32+
}
33+
return t0;
34+
}
35+
36+
export const FIXTURE_ENTRYPOINT = {
37+
fn: Component,
38+
params: [],
39+
isComponent: true,
40+
};
41+
42+
```
43+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @target="donotuse_meta_internal"
2+
3+
function Component() {
4+
return <div>Hello world</div>;
5+
}
6+
7+
export const FIXTURE_ENTRYPOINT = {
8+
fn: Component,
9+
params: [],
10+
isComponent: true,
11+
};

compiler/packages/snap/src/SproutTodoFilter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ const skipFilter = new Set([
504504
// Depends on external functions
505505
'idx-method-no-outlining-wildcard',
506506
'idx-method-no-outlining',
507+
'target-flag-meta-internal',
507508

508509
// needs to be executed as a module
509510
'meta-property',

compiler/packages/snap/src/compiler.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
LoggerEvent,
1919
PanicThresholdOptions,
2020
PluginOptions,
21+
CompilerReactTarget,
2122
} from 'babel-plugin-react-compiler/src/Entrypoint';
2223
import type {Effect, ValueKind} from 'babel-plugin-react-compiler/src/HIR';
2324
import type {
@@ -55,7 +56,7 @@ function makePluginOptions(
5556
let validatePreserveExistingMemoizationGuarantees = false;
5657
let customMacros: null | Array<Macro> = null;
5758
let validateBlocklistedImports = null;
58-
let target = '19' as const;
59+
let target: CompilerReactTarget = '19';
5960

6061
if (firstLine.indexOf('@compilationMode(annotation)') !== -1) {
6162
assert(
@@ -81,8 +82,15 @@ function makePluginOptions(
8182

8283
const targetMatch = /@target="([^"]+)"/.exec(firstLine);
8384
if (targetMatch) {
84-
// @ts-ignore
85-
target = targetMatch[1];
85+
if (targetMatch[1] === 'donotuse_meta_internal') {
86+
target = {
87+
kind: targetMatch[1],
88+
runtimeModule: 'react',
89+
};
90+
} else {
91+
// @ts-ignore
92+
target = targetMatch[1];
93+
}
8694
}
8795

8896
if (firstLine.includes('@panicThreshold(none)')) {

0 commit comments

Comments
 (0)