Repository: facebook/react
Pull request: [rcr] Add target flag to compiler


Changed files:
filename: compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts
patch: @@ -14,6 +14,7 @@ import {
   parseEnvironmentConfig,
 } from '../HIR/Environment';
 import {hasOwnProperty} from '../Utils/utils';
+import {fromZodError} from 'zod-validation-error';
 
 const PanicThresholdOptionsSchema = z.enum([
   /*
@@ -121,8 +122,19 @@ export type PluginOptions = {
    * Set this flag (on by default) to automatically check for this library and activate the support.
    */
   enableReanimatedCheck: boolean;
+
+  /**
+   * The minimum major version of React that the compiler should emit code for. If the target is 19
+   * or higher, the compiler emits direct imports of React runtime APIs needed by the compiler. On
+   * versions prior to 19, an extra runtime package react-compiler-runtime is necessary to provide
+   * a userspace approximation of runtime APIs.
+   */
+  target: CompilerReactTarget;
 };
 
+const CompilerReactTargetSchema = z.enum(['17', '18', '19']);
+export type CompilerReactTarget = z.infer<typeof CompilerReactTargetSchema>;
+
 const CompilationModeSchema = z.enum([
   /*
    * Compiles functions annotated with "use forget" or component/hook-like functions.
@@ -210,6 +222,7 @@ export const defaultOptions: PluginOptions = {
     return filename.indexOf('node_modules') === -1;
   },
   enableReanimatedCheck: true,
+  target: '19',
 } as const;
 
 export function parsePluginOptions(obj: unknown): PluginOptions {
@@ -222,25 +235,49 @@ export function parsePluginOptions(obj: unknown): PluginOptions {
       // normalize string configs to be case insensitive
       value = value.toLowerCase();
     }
-    if (key === 'environment') {
-      const environmentResult = parseEnvironmentConfig(value);
-      if (environmentResult.isErr()) {
-        CompilerError.throwInvalidConfig({
-          reason:
-            'Error in validating environment config. This is an advanced setting and not meant to be used directly',
-          description: environmentResult.unwrapErr().toString(),
-          suggestions: null,
-          loc: null,
-        });
+    if (isCompilerFlag(key)) {
+      switch (key) {
+        case 'environment': {
+          const environmentResult = parseEnvironmentConfig(value);
+          if (environmentResult.isErr()) {
+            CompilerError.throwInvalidConfig({
+              reason:
+                'Error in validating environment config. This is an advanced setting and not meant to be used directly',
+              description: environmentResult.unwrapErr().toString(),
+              suggestions: null,
+              loc: null,
+            });
+          }
+          parsedOptions[key] = environmentResult.unwrap();
+          break;
+        }
+        case 'target': {
+          parsedOptions[key] = parseTargetConfig(value);
+          break;
+        }
+        default: {
+          parsedOptions[key] = value;
+        }
       }
-      parsedOptions[key] = environmentResult.unwrap();
-    } else if (isCompilerFlag(key)) {
-      parsedOptions[key] = value;
     }
   }
   return {...defaultOptions, ...parsedOptions};
 }
 
+export function parseTargetConfig(value: unknown): CompilerReactTarget {
+  const parsed = CompilerReactTargetSchema.safeParse(value);
+  if (parsed.success) {
+    return parsed.data;
+  } else {
+    CompilerError.throwInvalidConfig({
+      reason: 'Not a valid target',
+      description: `${fromZodError(parsed.error)}`,
+      suggestions: null,
+      loc: null,
+    });
+  }
+}
+
 function isCompilerFlag(s: string): s is keyof PluginOptions {
   return hasOwnProperty(defaultOptions, s);
 }

filename: compiler/packages/snap/src/compiler.ts
patch: @@ -56,6 +56,7 @@ function makePluginOptions(
   let enableChangeDetectionForDebugging = null;
   let customMacros: null | Array<Macro> = null;
   let validateBlocklistedImports = null;
+  let target = '19' as const;
 
   if (firstLine.indexOf('@compilationMode(annotation)') !== -1) {
     assert(
@@ -107,6 +108,13 @@ function makePluginOptions(
   if (runtimeModuleMatch) {
     runtimeModule = runtimeModuleMatch[1];
   }
+
+  const targetMatch = /@target="([^"]+)"/.exec(firstLine);
+  if (targetMatch) {
+    // @ts-ignore
+    target = targetMatch[1];
+  }
+
   if (firstLine.includes('@panicThreshold(none)')) {
     panicThreshold = 'none';
   }
@@ -248,6 +256,7 @@ function makePluginOptions(
     flowSuppressions,
     ignoreUseNoForget,
     enableReanimatedCheck: false,
+    target,
   };
   return [options, logs];
 }