Repository: facebook/react
Pull request: [compiler][playground] create playground API in pipeline, and allow spaces in pass names

Stack from [ghstack](https://github.com/ezyang/ghstack) (oldest at bottom):
* __->__ #30988

Summary:
1. Minor refactor to provide a stable API for calling the compiler from the playground
2. Allows spaces in pass names without breaking the appearance of the playground by replacing spaces with & nbsp; in pass tabs

Changed files:
filename: compiler/apps/playground/components/Editor/EditorImpl.tsx
patch: @@ -5,7 +5,7 @@
  * LICENSE file in the root directory of this source tree.
  */
 
-import {parse as babelParse, ParserPlugin} from '@babel/parser';
+import {parse as babelParse} from '@babel/parser';
 import * as HermesParser from 'hermes-parser';
 import traverse, {NodePath} from '@babel/traverse';
 import * as t from '@babel/types';
@@ -15,10 +15,8 @@ import {
   Effect,
   ErrorSeverity,
   parseConfigPragma,
-  printHIR,
-  printReactiveFunction,
-  run,
   ValueKind,
+  runPlayground,
   type Hook,
 } from 'babel-plugin-react-compiler/src';
 import {type ReactFunctionType} from 'babel-plugin-react-compiler/src/HIR/Environment';
@@ -214,17 +212,13 @@ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] {
 
     for (const fn of parseFunctions(source, language)) {
       const id = withIdentifier(getFunctionIdentifier(fn));
-      for (const result of run(
+      for (const result of runPlayground(
         fn,
         {
           ...config,
           customHooks: new Map([...COMMON_HOOKS]),
         },
         getReactFunctionType(id),
-        '_c',
-        null,
-        null,
-        null,
       )) {
         const fnName = id.name;
         switch (result.kind) {

filename: compiler/apps/playground/components/TabbedWindow.tsx
patch: @@ -69,6 +69,9 @@ function TabbedWindowItem({
     setTabsOpen(nextState);
   }, [tabsOpen, name, setTabsOpen]);
 
+  // Replace spaces with non-breaking spaces
+  const displayName = name.replace(/ /g, '\u00A0');
+
   return (
     <div key={name} className="flex flex-row">
       {isShow ? (
@@ -80,7 +83,7 @@ function TabbedWindowItem({
             className={`p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 ${
               hasChanged ? 'font-bold' : 'font-light'
             } text-secondary hover:text-link`}>
-            - {name}
+            - {displayName}
           </h2>
           {tabs.get(name) ?? <div>No output for {name}</div>}
         </Resizable>
@@ -94,7 +97,7 @@ function TabbedWindowItem({
             className={`flex-grow-0 w-5 transition-colors duration-150 ease-in ${
               hasChanged ? 'font-bold' : 'font-light'
             } text-secondary hover:text-link`}>
-            {name}
+            {displayName}
           </button>
         </div>
       )}

filename: compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
patch: @@ -554,3 +554,14 @@ export function log(value: CompilerPipelineValue): CompilerPipelineValue {
   }
   return value;
 }
+
+export function* runPlayground(
+  func: NodePath<
+    t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
+  >,
+  config: EnvironmentConfig,
+  fnType: ReactFunctionType,
+): Generator<CompilerPipelineValue, CodegenFunction> {
+  const ast = yield* run(func, config, fnType, '_c', null, null, null);
+  return ast;
+}

filename: compiler/packages/babel-plugin-react-compiler/src/index.ts
patch: @@ -18,6 +18,7 @@ export {
   compileProgram,
   parsePluginOptions,
   run,
+  runPlayground,
   OPT_OUT_DIRECTIVES,
   type CompilerPipelineValue,
   type PluginOptions,