@samitouri / QOS-React-1 / commits / 865d2c418d

[compiler] Add meta internal option for useMemoCache import (#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.

mofeiZ committed Dec 2, 2024 at 16:42 UTC 865d2c418d5ba6fb4546e4b58616cd9b7701af85
6 files changed +96 -25
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts
+16 -1
@@ -121,7 +121,22 @@ export type PluginOptions = {
121 target: CompilerReactTarget;
122 };
123
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 +]);
140 export type CompilerReactTarget = z.infer<typeof CompilerReactTargetSchema>;
141
142 const CompilationModeSchema = z.enum([
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts
+14 -21
@@ -1123,30 +1123,23 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
1123 return errors.details.length > 0 ? errors : null;
1124 }
1125
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 + {
1137 reason: 'Expected target to already be validated',
1138 description: null,
1139 loc: null,
1140 suggestions: null,
1149 - });
1141 + },
1142 + );
1143 + return opts.target.runtimeModule;
1144 }
1151 - return moduleName;
1145 }
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag-meta-internal.expect.md new
+43
@@ -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 +
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag-meta-internal.js new
+11
@@ -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
+1
@@ -504,6 +504,7 @@ const skipFilter = new Set([
504 // Depends on external functions
505 'idx-method-no-outlining-wildcard',
506 'idx-method-no-outlining',
507 + 'target-flag-meta-internal',
508
509 // needs to be executed as a module
510 'meta-property',
compiler/packages/snap/src/compiler.ts
+11 -3
@@ -18,6 +18,7 @@ import type {
18 LoggerEvent,
19 PanicThresholdOptions,
20 PluginOptions,
21 + CompilerReactTarget,
22 } from 'babel-plugin-react-compiler/src/Entrypoint';
23 import type {Effect, ValueKind} from 'babel-plugin-react-compiler/src/HIR';
24 import type {
@@ -55,7 +56,7 @@ function makePluginOptions(
56 let validatePreserveExistingMemoizationGuarantees = false;
57 let customMacros: null | Array<Macro> = null;
58 let validateBlocklistedImports = null;
58 - let target = '19' as const;
59 + let target: CompilerReactTarget = '19';
60
61 if (firstLine.indexOf('@compilationMode(annotation)') !== -1) {
62 assert(
@@ -81,8 +82,15 @@ function makePluginOptions(
82
83 const targetMatch = /@target="([^"]+)"/.exec(firstLine);
84 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 + }
94 }
95
96 if (firstLine.includes('@panicThreshold(none)')) {