mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add react-tools-cli commands for each tool on the mcp
This commit is contained in:
@@ -1,4 +1,11 @@
|
||||
import {devDocsTool} from 'react-mcp-server/src';
|
||||
import {
|
||||
PassNameType,
|
||||
assertExhaustive,
|
||||
compileTool,
|
||||
componentTreeTool,
|
||||
devDocsTool,
|
||||
runtimePerfTool,
|
||||
} from 'react-mcp-server/src';
|
||||
|
||||
import yargs from 'yargs';
|
||||
import {hideBin} from 'yargs/helpers';
|
||||
@@ -7,7 +14,7 @@ yargs()
|
||||
.scriptName('react-tools-cli')
|
||||
.usage('$0 <cmd> [args]')
|
||||
.command(
|
||||
'compile [code]',
|
||||
'compile [code] [pass-name]',
|
||||
'Compile React code with React Compiler',
|
||||
yargs => {
|
||||
yargs
|
||||
@@ -23,54 +30,208 @@ yargs()
|
||||
},
|
||||
async function (argv) {
|
||||
const code: string = String(argv['code'] ?? '');
|
||||
// const passName: PassNameType = argv['pass-name'] as PassNameType
|
||||
const passName: PassNameType = argv['pass-name'] as PassNameType;
|
||||
|
||||
const results = devDocsTool(code);
|
||||
const results = await compileTool(code, passName);
|
||||
|
||||
// switch (results.kind) {
|
||||
// case 'success': {
|
||||
// console.log(JSON.stringify({
|
||||
// isError: false,
|
||||
// content: results.content.map(text => {
|
||||
// return {
|
||||
// type: 'text' as const,
|
||||
// text,
|
||||
// };
|
||||
// }),
|
||||
// }));
|
||||
// break
|
||||
// }
|
||||
// case 'bailout': {
|
||||
// console.log(JSON.stringify({
|
||||
// isError: true,
|
||||
// content: results.content.map(text => {
|
||||
// return {
|
||||
// type: 'text' as const,
|
||||
// text,
|
||||
// };
|
||||
// }),
|
||||
// }));
|
||||
// break;
|
||||
// }
|
||||
// case 'error':
|
||||
// case 'compile-error':
|
||||
// console.log(JSON.stringify({
|
||||
// isError: true,
|
||||
// content: [
|
||||
// {
|
||||
// type: 'text' as const,
|
||||
// text: results.text,
|
||||
// },
|
||||
// ],
|
||||
// }));
|
||||
// break;
|
||||
// default:
|
||||
// assertExhaustive(
|
||||
// results,
|
||||
// `Unhandled result ${JSON.stringify(results)}`,
|
||||
// );
|
||||
// }
|
||||
switch (results.kind) {
|
||||
case 'success': {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
isError: false,
|
||||
content: results.content.map(text => {
|
||||
return {
|
||||
type: 'text' as const,
|
||||
text,
|
||||
};
|
||||
}),
|
||||
}),
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'bailout': {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
isError: true,
|
||||
content: results.content.map(text => {
|
||||
return {
|
||||
type: 'text' as const,
|
||||
text,
|
||||
};
|
||||
}),
|
||||
}),
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'error':
|
||||
case 'compile-error':
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
isError: true,
|
||||
content: [
|
||||
{
|
||||
type: 'text' as const,
|
||||
text: results.text,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
assertExhaustive(
|
||||
results,
|
||||
`Unhandled result ${JSON.stringify(results)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
.command(
|
||||
'query-docs [query]',
|
||||
'Compile React code with React Compiler',
|
||||
yargs => {
|
||||
yargs.positional('query', {
|
||||
type: 'string',
|
||||
describe: 'Browse oficcial React documentation for a given query',
|
||||
});
|
||||
},
|
||||
async function (argv) {
|
||||
const query: string = String(argv['query'] ?? '');
|
||||
|
||||
const result = await devDocsTool(query);
|
||||
|
||||
switch (result.kind) {
|
||||
case 'success':
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
isError: false,
|
||||
content: result.content.map(text => {
|
||||
return {
|
||||
type: 'text' as const,
|
||||
text: text,
|
||||
};
|
||||
}),
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case 'error':
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
isError: true,
|
||||
content: [{type: 'text' as const, text: result.text}],
|
||||
}),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
assertExhaustive(
|
||||
result,
|
||||
`Unhandled result ${JSON.stringify(result)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
.command(
|
||||
'get-component-tree [url]',
|
||||
'Get the React component tree for a given URL',
|
||||
yargs => {
|
||||
yargs.positional('url', {
|
||||
type: 'string',
|
||||
default: 'https://localhost:3000',
|
||||
describe: 'URL for a React App to get the component tree for',
|
||||
});
|
||||
},
|
||||
async function (argv) {
|
||||
const url: string = String(argv['url']);
|
||||
try {
|
||||
const result = await componentTreeTool(url);
|
||||
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
content: [
|
||||
{
|
||||
type: 'text' as const,
|
||||
text: result,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
isError: true,
|
||||
content: [{type: 'text' as const, text: `Error: ${err.stack}`}],
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
.command(
|
||||
'review-code-runtime [code] [iterations',
|
||||
'Get the React component tree for a given URL',
|
||||
yargs => {
|
||||
yargs.positional('code', {
|
||||
type: 'string',
|
||||
default: '',
|
||||
describe: 'React code to run',
|
||||
});
|
||||
yargs.positional('iterations', {
|
||||
type: 'number',
|
||||
default: 10,
|
||||
describe: 'Number of iterations to run the code for',
|
||||
});
|
||||
},
|
||||
async function (argv) {
|
||||
const code: string = String(argv['code']);
|
||||
const iterations: number = Number(argv['iterations']);
|
||||
|
||||
try {
|
||||
const results = await runtimePerfTool(code, iterations);
|
||||
|
||||
const formattedResults = `
|
||||
# React Component Performance Results
|
||||
|
||||
## Mean Render Time
|
||||
${calculateMean(results.renderTime)}
|
||||
|
||||
## Mean Web Vitals
|
||||
- Cumulative Layout Shift (CLS): ${calculateMean(results.webVitals.cls)}
|
||||
- Largest Contentful Paint (LCP): ${calculateMean(results.webVitals.lcp)}
|
||||
- Interaction to Next Paint (INP): ${calculateMean(results.webVitals.inp)}
|
||||
|
||||
## Mean React Profiler
|
||||
- Actual Duration: ${calculateMean(results.reactProfiler.actualDuration)}
|
||||
- Base Duration: ${calculateMean(results.reactProfiler.baseDuration)}
|
||||
`;
|
||||
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
content: [
|
||||
{
|
||||
type: 'text' as const,
|
||||
text: formattedResults,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
isError: true,
|
||||
content: [
|
||||
{
|
||||
type: 'text' as const,
|
||||
text: `Error measuring performance: ${error.message}\n\n${error.stack}`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
.help()
|
||||
.parse(hideBin(process.argv));
|
||||
|
||||
function calculateMean(values: number[]): string {
|
||||
return values.length > 0
|
||||
? values.reduce((acc, curr) => acc + curr, 0) / values.length + 'ms'
|
||||
: 'could not collect';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user