diff --git a/compiler/packages/react-mcp-server/src/index.ts b/compiler/packages/react-mcp-server/src/index.ts index 0aee12ccc5ad0..657acc542b566 100644 --- a/compiler/packages/react-mcp-server/src/index.ts +++ b/compiler/packages/react-mcp-server/src/index.ts @@ -356,7 +356,14 @@ Server Components - Shift data-heavy logic to the server whenever possible. Brea server.tool( 'review-react-runtime', - 'Review the runtime of the code and get performance data to evaluate the proposed solution, the react code that is passed into this tool MUST contain an App component.', + ` + Run this tool every time you propose a performance related change to verify if your suggestion actually improves performance. + + This tool has some requirements on the code input: + - The react code that is passed into this tool MUST contain an App functional component without arrow function. + - DO NOT export anything since we can't parse export syntax with this tool. + - Only import React from 'react' and use all hooks and imports using the React. prefix like React.useState and React.useEffect + `, { text: z.string(), }, @@ -387,25 +394,24 @@ server.tool( for (let i = 0; i < iterations; i++) { const performanceResults = await measurePerformance(text); perfData.renderTime += performanceResults.renderTime; - perfData.webVitals.cls += performanceResults.webVitals.cls?.value || 0; - perfData.webVitals.lcp += performanceResults.webVitals.lcp?.value || 0; - perfData.webVitals.inp += performanceResults.webVitals.inp?.value || 0; - perfData.webVitals.fid += performanceResults.webVitals.fid?.value || 0; - perfData.webVitals.ttfb += - performanceResults.webVitals.ttfb?.value || 0; + perfData.webVitals.cls += performanceResults.webVitals.cls || 0; + perfData.webVitals.lcp += performanceResults.webVitals.lcp || 0; + perfData.webVitals.inp += performanceResults.webVitals.inp || 0; + perfData.webVitals.fid += performanceResults.webVitals.fid || 0; + perfData.webVitals.ttfb += performanceResults.webVitals.ttfb || 0; perfData.reactProfilerMetrics.id += - performanceResults.reactProfilerMetrics.actualDuration?.value || 0; + performanceResults.reactProfilerMetrics.actualDuration || 0; perfData.reactProfilerMetrics.phase += - performanceResults.reactProfilerMetrics.phase?.value || 0; + performanceResults.reactProfilerMetrics.phase || 0; perfData.reactProfilerMetrics.actualDuration += - performanceResults.reactProfilerMetrics.actualDuration?.value || 0; + performanceResults.reactProfilerMetrics.actualDuration || 0; perfData.reactProfilerMetrics.baseDuration += - performanceResults.reactProfilerMetrics.baseDuration?.value || 0; + performanceResults.reactProfilerMetrics.baseDuration || 0; perfData.reactProfilerMetrics.startTime += - performanceResults.reactProfilerMetrics.startTime?.value || 0; + performanceResults.reactProfilerMetrics.startTime || 0; perfData.reactProfilerMetrics.commitTime += - performanceResults.reactProfilerMetrics.commitTim?.value || 0; + performanceResults.reactProfilerMetrics.commitTime || 0; } const formattedResults = ` diff --git a/compiler/packages/react-mcp-server/src/utils/runtimePerf.ts b/compiler/packages/react-mcp-server/src/utils/runtimePerf.ts index 87dae26d021b1..299650fb20d17 100644 --- a/compiler/packages/react-mcp-server/src/utils/runtimePerf.ts +++ b/compiler/packages/react-mcp-server/src/utils/runtimePerf.ts @@ -1,28 +1,48 @@ import * as babel from '@babel/core'; import puppeteer from 'puppeteer'; -export async function measurePerformance(code: any) { - let options = { +export async function measurePerformance(code: string) { + const babelOptions = { configFile: false, babelrc: false, - presets: [['@babel/preset-env'], '@babel/preset-react'], + presets: [ + require.resolve('@babel/preset-env'), + require.resolve('@babel/preset-react'), + ], }; - const parsed = await babel.parseAsync(code, options); - + // Parse the code to AST + const parsed = await babel.parseAsync(code, babelOptions); if (!parsed) { throw new Error('Failed to parse code'); } - const transpiled = await transformAsync(parsed); + // Transform AST to browser-compatible JavaScript + const transformResult = await babel.transformFromAstAsync(parsed, undefined, { + ...babelOptions, + filename: 'file.jsx', + plugins: [ + () => ({ + visitor: { + ImportDeclaration( + path: babel.NodePath, + ) { + const value = path.node.source.value; + if (value === 'react' || value === 'react-dom') { + path.remove(); + } + }, + }, + }), + ], + }); + const transpiled = transformResult?.code || undefined; if (!transpiled) { throw new Error('Failed to transpile code'); } - const browser = await puppeteer.launch({ - protocolTimeout: 600_000, - }); + const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setViewport({width: 1280, height: 720}); @@ -31,7 +51,6 @@ export async function measurePerformance(code: any) { await page.waitForFunction( 'window.__RESULT__ !== undefined && (window.__RESULT__.renderTime !== null || window.__RESULT__.error !== null)', - {timeout: 600_000}, ); const result = await page.evaluate(() => { @@ -42,33 +61,6 @@ export async function measurePerformance(code: any) { return result; } -/** - * Transform AST into browser-compatible JavaScript - * @param {babel.types.File} ast - The AST to transform - * @param {Object} opts - Transformation options - * @returns {Promise} - The transpiled code - */ -async function transformAsync(ast: babel.types.Node) { - const result = await babel.transformFromAstAsync(ast, undefined, { - filename: 'file.jsx', - presets: [['@babel/preset-env'], '@babel/preset-react'], - plugins: [ - () => ({ - visitor: { - ImportDeclaration(path: any) { - const value = path.node.source.value; - if (value === 'react' || value === 'react-dom') { - path.remove(); - } - }, - }, - }), - ], - }); - - return result?.code || ''; -} - function buildHtml(transpiled: string) { const html = `