Repository: facebook/react
Pull request: [mcp] Refine passes returned


Adds some new options to request the HIR, ReactiveFunction passes
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32930).
* #32932
* #32931
* __->__ #32930
* #32929
* #32928

Changed files:
filename: compiler/packages/react-mcp-server/src/compiler/index.ts
patch: @@ -14,6 +14,16 @@ import * as prettier from 'prettier';
 
 export let lastResult: BabelCore.BabelFileResult | null = null;
 
+export type PrintedCompilerPipelineValue =
+  | {
+      kind: 'hir';
+      name: string;
+      fnName: string | null;
+      value: string;
+    }
+  | {kind: 'reactive'; name: string; fnName: string | null; value: string}
+  | {kind: 'debug'; name: string; fnName: string | null; value: string};
+
 type CompileOptions = {
   text: string;
   file: string;

filename: compiler/packages/react-mcp-server/src/index.ts
patch: @@ -11,7 +11,7 @@ import {
 } from '@modelcontextprotocol/sdk/server/mcp.js';
 import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
 import {z} from 'zod';
-import {compile} from './compiler';
+import {compile, type PrintedCompilerPipelineValue} from './compiler';
 import {
   CompilerPipelineValue,
   printReactiveFunctionWithOutlined,
@@ -22,19 +22,9 @@ import {
 import * as cheerio from 'cheerio';
 import TurndownService from 'turndown';
 import {queryAlgolia} from './utils/algolia';
+import assertExhaustive from './utils/assertExhaustive';
 
 const turndownService = new TurndownService();
-
-export type PrintedCompilerPipelineValue =
-  | {
-      kind: 'hir';
-      name: string;
-      fnName: string | null;
-      value: string;
-    }
-  | {kind: 'reactive'; name: string; fnName: string | null; value: string}
-  | {kind: 'debug'; name: string; fnName: string | null; value: string};
-
 const server = new McpServer({
   name: 'React',
   version: '0.0.0',
@@ -114,7 +104,7 @@ server.tool(
   'Compile code with React Compiler. Optionally, for debugging provide a pass name like "HIR" to see more information.',
   {
     text: z.string(),
-    passName: z.string().optional(),
+    passName: z.enum(['HIR', 'ReactiveFunction', 'All', '@DEBUG']).optional(),
   },
   async ({text, passName}) => {
     const pipelinePasses = new Map<
@@ -164,8 +154,7 @@ server.tool(
           break;
         }
         default: {
-          const _: never = result;
-          throw new Error(`Unhandled result ${result}`);
+          assertExhaustive(result, `Unhandled result ${result}`);
         }
       }
     };
@@ -205,6 +194,78 @@ server.tool(
       }
       const requestedPasses: Array<{type: 'text'; text: string}> = [];
       if (passName != null) {
+        switch (passName) {
+          case 'All': {
+            const hir = pipelinePasses.get('PropagateScopeDependenciesHIR');
+            if (hir !== undefined) {
+              for (const pipelineValue of hir) {
+                requestedPasses.push({
+                  type: 'text' as const,
+                  text: pipelineValue.value,
+                });
+              }
+            }
+            const reactiveFunc = pipelinePasses.get('PruneHoistedContexts');
+            if (reactiveFunc !== undefined) {
+              for (const pipelineValue of reactiveFunc) {
+                requestedPasses.push({
+                  type: 'text' as const,
+                  text: pipelineValue.value,
+                });
+              }
+            }
+            break;
+          }
+          case 'HIR': {
+            // Last pass before HIR -> ReactiveFunction
+            const requestedPass = pipelinePasses.get(
+              'PropagateScopeDependenciesHIR',
+            );
+            if (requestedPass !== undefined) {
+              for (const pipelineValue of requestedPass) {
+                requestedPasses.push({
+                  type: 'text' as const,
+                  text: pipelineValue.value,
+                });
+              }
+            } else {
+              console.error(`Could not find requested pass ${passName}`);
+            }
+            break;
+          }
+          case 'ReactiveFunction': {
+            // Last pass
+            const requestedPass = pipelinePasses.get('PruneHoistedContexts');
+            if (requestedPass !== undefined) {
+              for (const pipelineValue of requestedPass) {
+                requestedPasses.push({
+                  type: 'text' as const,
+                  text: pipelineValue.value,
+                });
+              }
+            } else {
+              console.error(`Could not find requested pass ${passName}`);
+            }
+            break;
+          }
+          case '@DEBUG': {
+            for (const [, pipelinePass] of pipelinePasses) {
+              for (const pass of pipelinePass) {
+                requestedPasses.push({
+                  type: 'text' as const,
+                  text: `${pass.name}\n\n${pass.value}`,
+                });
+              }
+            }
+            break;
+          }
+          default: {
+            assertExhaustive(
+              passName,
+              `Unhandled passName option: ${passName}`,
+            );
+          }
+        }
         const requestedPass = pipelinePasses.get(passName);
         if (requestedPass !== undefined) {
           for (const pipelineValue of requestedPass) {

filename: compiler/packages/react-mcp-server/src/utils/assertExhaustive.ts
patch: @@ -0,0 +1,13 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * Trigger an exhaustiveness check in TypeScript and throw at runtime.
+ */
+export default function assertExhaustive(_: never, errorMsg: string): never {
+  throw new Error(errorMsg);
+}