@samitouri / QOS-React / commits / 3e04b2a214

[mcp] Refine passes returned (#32930)

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

lauren committed Apr 16, 2025 at 17:49 UTC 3e04b2a214cdc962dd5acde412c7107321ec7a56
3 files changed +99 -15
compiler/packages/react-mcp-server/src/compiler/index.ts
+10
@@ -14,6 +14,16 @@ import * as prettier from 'prettier';
14
15 export let lastResult: BabelCore.BabelFileResult | null = null;
16
17 +export type PrintedCompilerPipelineValue =
18 + | {
19 + kind: 'hir';
20 + name: string;
21 + fnName: string | null;
22 + value: string;
23 + }
24 + | {kind: 'reactive'; name: string; fnName: string | null; value: string}
25 + | {kind: 'debug'; name: string; fnName: string | null; value: string};
26 +
27 type CompileOptions = {
28 text: string;
29 file: string;
compiler/packages/react-mcp-server/src/index.ts
+76 -15
@@ -11,7 +11,7 @@ import {
11 } from '@modelcontextprotocol/sdk/server/mcp.js';
12 import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
13 import {z} from 'zod';
14 -import {compile} from './compiler';
14 +import {compile, type PrintedCompilerPipelineValue} from './compiler';
15 import {
16 CompilerPipelineValue,
17 printReactiveFunctionWithOutlined,
@@ -22,19 +22,9 @@ import {
22 import * as cheerio from 'cheerio';
23 import TurndownService from 'turndown';
24 import {queryAlgolia} from './utils/algolia';
25 +import assertExhaustive from './utils/assertExhaustive';
26
27 const turndownService = new TurndownService();
27 -
28 -export type PrintedCompilerPipelineValue =
29 - | {
30 - kind: 'hir';
31 - name: string;
32 - fnName: string | null;
33 - value: string;
34 - }
35 - | {kind: 'reactive'; name: string; fnName: string | null; value: string}
36 - | {kind: 'debug'; name: string; fnName: string | null; value: string};
37 -
28 const server = new McpServer({
29 name: 'React',
30 version: '0.0.0',
@@ -114,7 +104,7 @@ server.tool(
104 'Compile code with React Compiler. Optionally, for debugging provide a pass name like "HIR" to see more information.',
105 {
106 text: z.string(),
117 - passName: z.string().optional(),
107 + passName: z.enum(['HIR', 'ReactiveFunction', 'All', '@DEBUG']).optional(),
108 },
109 async ({text, passName}) => {
110 const pipelinePasses = new Map<
@@ -164,8 +154,7 @@ server.tool(
154 break;
155 }
156 default: {
167 - const _: never = result;
168 - throw new Error(`Unhandled result ${result}`);
157 + assertExhaustive(result, `Unhandled result ${result}`);
158 }
159 }
160 };
@@ -205,6 +194,78 @@ server.tool(
194 }
195 const requestedPasses: Array<{type: 'text'; text: string}> = [];
196 if (passName != null) {
197 + switch (passName) {
198 + case 'All': {
199 + const hir = pipelinePasses.get('PropagateScopeDependenciesHIR');
200 + if (hir !== undefined) {
201 + for (const pipelineValue of hir) {
202 + requestedPasses.push({
203 + type: 'text' as const,
204 + text: pipelineValue.value,
205 + });
206 + }
207 + }
208 + const reactiveFunc = pipelinePasses.get('PruneHoistedContexts');
209 + if (reactiveFunc !== undefined) {
210 + for (const pipelineValue of reactiveFunc) {
211 + requestedPasses.push({
212 + type: 'text' as const,
213 + text: pipelineValue.value,
214 + });
215 + }
216 + }
217 + break;
218 + }
219 + case 'HIR': {
220 + // Last pass before HIR -> ReactiveFunction
221 + const requestedPass = pipelinePasses.get(
222 + 'PropagateScopeDependenciesHIR',
223 + );
224 + if (requestedPass !== undefined) {
225 + for (const pipelineValue of requestedPass) {
226 + requestedPasses.push({
227 + type: 'text' as const,
228 + text: pipelineValue.value,
229 + });
230 + }
231 + } else {
232 + console.error(`Could not find requested pass ${passName}`);
233 + }
234 + break;
235 + }
236 + case 'ReactiveFunction': {
237 + // Last pass
238 + const requestedPass = pipelinePasses.get('PruneHoistedContexts');
239 + if (requestedPass !== undefined) {
240 + for (const pipelineValue of requestedPass) {
241 + requestedPasses.push({
242 + type: 'text' as const,
243 + text: pipelineValue.value,
244 + });
245 + }
246 + } else {
247 + console.error(`Could not find requested pass ${passName}`);
248 + }
249 + break;
250 + }
251 + case '@DEBUG': {
252 + for (const [, pipelinePass] of pipelinePasses) {
253 + for (const pass of pipelinePass) {
254 + requestedPasses.push({
255 + type: 'text' as const,
256 + text: `${pass.name}\n\n${pass.value}`,
257 + });
258 + }
259 + }
260 + break;
261 + }
262 + default: {
263 + assertExhaustive(
264 + passName,
265 + `Unhandled passName option: ${passName}`,
266 + );
267 + }
268 + }
269 const requestedPass = pipelinePasses.get(passName);
270 if (requestedPass !== undefined) {
271 for (const pipelineValue of requestedPass) {
compiler/packages/react-mcp-server/src/utils/assertExhaustive.ts new
+13
@@ -0,0 +1,13 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +/**
9 + * Trigger an exhaustiveness check in TypeScript and throw at runtime.
10 + */
11 +export default function assertExhaustive(_: never, errorMsg: string): never {
12 + throw new Error(errorMsg);
13 +}