Compare commits

...

5 Commits

Author SHA1 Message Date
Jorge Cabiedes
555f844195 Formatting 2025-07-03 13:32:15 -07:00
Jorge Cabiedes
c94e8b4461 Implement all tools to react-tools-cli 2025-07-03 13:06:21 -07:00
Jorge Cabiedes
6b04874535 Add react-tools-cli commands for each tool on the mcp 2025-07-01 11:31:30 -07:00
Jorge Cabiedes Acosta
1ba1485a65 Create react-tools-cli package and import react-mcp-server 2025-07-01 09:01:36 -07:00
Jorge Cabiedes Acosta
2c1e4e4513 Reformat code to modularize tools 2025-06-24 22:56:21 -07:00
11 changed files with 755 additions and 272 deletions

View File

@@ -8,20 +8,14 @@
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
import {z} from 'zod';
import {compile, type PrintedCompilerPipelineValue} from './compiler';
import {
CompilerPipelineValue,
printReactiveFunctionWithOutlined,
printFunctionWithOutlined,
PluginOptions,
SourceLocation,
} from 'babel-plugin-react-compiler/src';
import * as cheerio from 'cheerio';
import {queryAlgolia} from './utils/algolia';
import assertExhaustive from './utils/assertExhaustive';
import {convert} from 'html-to-text';
import {measurePerformance} from './tools/runtimePerf';
import {parseReactComponentTree} from './tools/componentTree';
import {
runtimePerfTool,
componentTreeTool,
compileTool,
devDocsTool,
} from './tools';
export type {PassNameType} from './tools';
function calculateMean(values: number[]): string {
return values.length > 0
@@ -41,38 +35,27 @@ server.tool(
query: z.string(),
},
async ({query}) => {
try {
const pages = await queryAlgolia(query);
if (pages.length === 0) {
const result = await devDocsTool(query);
switch (result.kind) {
case 'success': {
return {
content: [{type: 'text' as const, text: `No results`}],
isError: false,
content: result.content.map(text => {
return {
type: 'text' as const,
text: text,
};
}),
};
}
const content = pages.map(html => {
const $ = cheerio.load(html);
// react.dev should always have at least one <article> with the main content
const article = $('article').html();
if (article != null) {
return {
type: 'text' as const,
text: convert(article),
};
} else {
return {
type: 'text' as const,
// Fallback to converting the whole page to text.
text: convert($.html()),
};
}
});
return {
content,
};
} catch (err) {
return {
isError: true,
content: [{type: 'text' as const, text: `Error: ${err.stack}`}],
};
case 'error':
return {
isError: true,
content: [{type: 'text' as const, text: result.text}],
};
default:
assertExhaustive(result, `Unhandled result ${JSON.stringify(result)}`);
}
},
);
@@ -93,199 +76,47 @@ server.tool(
passName: z.enum(['HIR', 'ReactiveFunction', 'All', '@DEBUG']).optional(),
},
async ({text, passName}) => {
const pipelinePasses = new Map<
string,
Array<PrintedCompilerPipelineValue>
>();
const recordPass: (
result: PrintedCompilerPipelineValue,
) => void = result => {
const entry = pipelinePasses.get(result.name);
if (Array.isArray(entry)) {
entry.push(result);
} else {
pipelinePasses.set(result.name, [result]);
}
};
const logIR = (result: CompilerPipelineValue): void => {
switch (result.kind) {
case 'ast': {
break;
}
case 'hir': {
recordPass({
kind: 'hir',
fnName: result.value.id,
name: result.name,
value: printFunctionWithOutlined(result.value),
});
break;
}
case 'reactive': {
recordPass({
kind: 'reactive',
fnName: result.value.id,
name: result.name,
value: printReactiveFunctionWithOutlined(result.value),
});
break;
}
case 'debug': {
recordPass({
kind: 'debug',
fnName: null,
name: result.name,
value: result.value,
});
break;
}
default: {
assertExhaustive(result, `Unhandled result ${result}`);
}
}
};
const errors: Array<{message: string; loc: SourceLocation | null}> = [];
const compilerOptions: Partial<PluginOptions> = {
panicThreshold: 'none',
logger: {
debugLogIRs: logIR,
logEvent: (_filename, event): void => {
if (event.kind === 'CompileError') {
const detail = event.detail;
const loc =
detail.loc == null || typeof detail.loc == 'symbol'
? event.fnLoc
: detail.loc;
errors.push({
message: detail.reason,
loc,
});
}
},
},
};
try {
const result = await compile({
text,
file: 'anonymous.tsx',
options: compilerOptions,
});
if (result.code == null) {
const results = await compileTool(text, passName);
switch (results.kind) {
case 'success': {
return {
isError: true,
content: [{type: 'text' as const, text: 'Error: Could not compile'}],
};
}
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) {
if (pipelineValue.name === passName) {
requestedPasses.push({
type: 'text' as const,
text: pipelineValue.value,
});
}
}
}
}
if (errors.length > 0) {
return {
content: errors.map(err => {
isError: false,
content: results.content.map(text => {
return {
type: 'text' as const,
text:
err.loc === null || typeof err.loc === 'symbol'
? `React Compiler bailed out:\n\n${err.message}`
: `React Compiler bailed out:\n\n${err.message}@${err.loc.start.line}:${err.loc.end.line}`,
text,
};
}),
};
}
return {
content: [
{type: 'text' as const, text: result.code},
...requestedPasses,
],
};
} catch (err) {
return {
isError: true,
content: [{type: 'text' as const, text: `Error: ${err.stack}`}],
};
case 'bailout': {
return {
isError: true,
content: results.content.map(text => {
return {
type: 'text' as const,
text,
};
}),
};
}
case 'error':
case 'compile-error':
return {
isError: true,
content: [
{
type: 'text' as const,
text: results.text,
},
],
};
default:
assertExhaustive(
results,
`Unhandled result ${JSON.stringify(results)}`,
);
}
},
);
@@ -328,7 +159,7 @@ server.tool(
},
async ({text, iterations}) => {
try {
const results = await measurePerformance(text, iterations);
const results = await runtimePerfTool(text, iterations);
const formattedResults = `
# React Component Performance Results
@@ -387,7 +218,7 @@ server.tool(
},
async ({url}) => {
try {
const componentTree = await parseReactComponentTree(url);
const componentTree = await componentTreeTool(url);
return {
content: [
@@ -491,7 +322,17 @@ async function main() {
console.error('React Compiler MCP Server running on stdio');
}
main().catch(error => {
console.error('Fatal error in main():', error);
process.exit(1);
});
if (require.main !== module) {
main().catch(error => {
console.error('Fatal error in main():', error);
process.exit(1);
});
}
export {
compileTool,
componentTreeTool,
devDocsTool,
runtimePerfTool,
assertExhaustive,
};

View File

@@ -0,0 +1,203 @@
import {compile, type PrintedCompilerPipelineValue} from '../compiler';
import {
CompilerPipelineValue,
printReactiveFunctionWithOutlined,
printFunctionWithOutlined,
PluginOptions,
SourceLocation,
} from 'babel-plugin-react-compiler/src';
import assertExhaustive from '../utils/assertExhaustive';
export type PassNameType =
| 'HIR'
| 'ReactiveFunction'
| 'All'
| '@DEBUG'
| undefined;
type CompilerToolOutput =
| {
kind: 'success';
content: Array<string>;
}
| {
kind: 'bailout';
content: Array<string>;
}
| {
kind: 'compile-error';
text: string;
}
| {
kind: 'error';
text: string;
};
export async function compileTool(
text: string,
passName: PassNameType,
): Promise<CompilerToolOutput> {
const pipelinePasses = new Map<string, Array<PrintedCompilerPipelineValue>>();
const recordPass: (result: PrintedCompilerPipelineValue) => void = result => {
const entry = pipelinePasses.get(result.name);
if (Array.isArray(entry)) {
entry.push(result);
} else {
pipelinePasses.set(result.name, [result]);
}
};
const logIR = (result: CompilerPipelineValue): void => {
switch (result.kind) {
case 'ast': {
break;
}
case 'hir': {
recordPass({
kind: 'hir',
fnName: result.value.id,
name: result.name,
value: printFunctionWithOutlined(result.value),
});
break;
}
case 'reactive': {
recordPass({
kind: 'reactive',
fnName: result.value.id,
name: result.name,
value: printReactiveFunctionWithOutlined(result.value),
});
break;
}
case 'debug': {
recordPass({
kind: 'debug',
fnName: null,
name: result.name,
value: result.value,
});
break;
}
default: {
assertExhaustive(result, `Unhandled result ${result}`);
}
}
};
const errors: Array<{message: string; loc: SourceLocation | null}> = [];
const compilerOptions: Partial<PluginOptions> = {
panicThreshold: 'none',
logger: {
debugLogIRs: logIR,
logEvent: (_filename, event): void => {
if (event.kind === 'CompileError') {
const detail = event.detail;
const loc =
detail.loc == null || typeof detail.loc == 'symbol'
? event.fnLoc
: detail.loc;
errors.push({
message: detail.reason,
loc,
});
}
},
},
};
try {
const result = await compile({
text,
file: 'anonymous.tsx',
options: compilerOptions,
});
if (result.code == null) {
return {
kind: 'compile-error',
text: 'Error: Could not compile',
};
}
const requestedPasses: Array<string> = [];
if (passName != null) {
switch (passName) {
case 'All': {
const hir = pipelinePasses.get('PropagateScopeDependenciesHIR');
if (hir !== undefined) {
for (const pipelineValue of hir) {
requestedPasses.push(pipelineValue.value);
}
}
const reactiveFunc = pipelinePasses.get('PruneHoistedContexts');
if (reactiveFunc !== undefined) {
for (const pipelineValue of reactiveFunc) {
requestedPasses.push(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(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(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(`${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) {
if (pipelineValue.name === passName) {
requestedPasses.push(pipelineValue.value);
}
}
}
}
if (errors.length > 0) {
return {
kind: 'bailout',
content: errors.map(err => {
return err.loc === null || typeof err.loc === 'symbol'
? `React Compiler bailed out:\n\n${err.message}`
: `React Compiler bailed out:\n\n${err.message}@${err.loc.start.line}:${err.loc.end.line}`;
}),
};
}
return {
kind: 'success',
content: [result.code, ...requestedPasses],
};
} catch (err) {
return {
kind: 'error',
text: `Error: ${err.stack}`,
};
}
}

View File

@@ -1,6 +1,6 @@
import puppeteer from 'puppeteer';
export async function parseReactComponentTree(url: string): Promise<string> {
export async function componentTreeTool(url: string): Promise<string> {
try {
const browser = await puppeteer.connect({
browserURL: 'http://127.0.0.1:9222',

View File

@@ -0,0 +1,49 @@
import * as cheerio from 'cheerio';
import {convert} from 'html-to-text';
import {queryAlgolia} from '../utils/algolia';
type DevDocsToolOutput =
| {
kind: 'success';
content: Array<string>;
}
| {
kind: 'error';
text: string;
};
/**
* Tool for querying React dev docs from react.dev
* @param query The search query to look up in the React documentation
* @returns A promise that resolves to the search results
*/
export async function devDocsTool(query: string): Promise<DevDocsToolOutput> {
try {
const pages = await queryAlgolia(query);
if (pages.length === 0) {
return {
kind: 'error',
text: `No results`,
};
}
const content = pages.map(html => {
const $ = cheerio.load(html);
// react.dev should always have at least one <article> with the main content
const article = $('article').html();
if (article != null) {
return convert(article);
} else {
return convert($.html());
}
});
return {
kind: 'success',
content,
};
} catch (err) {
return {
kind: 'error',
text: `Error: ${err.stack}`,
};
}
}

View File

@@ -0,0 +1,11 @@
/**
* 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.
*/
export * from './runtimePerfTool';
export * from './componentTreeTool';
export * from './compileTool';
export * from './devDocsTool';

View File

@@ -53,7 +53,7 @@ function delay(time: number) {
});
}
export async function measurePerformance(
export async function runtimePerfTool(
code: string,
iterations: number,
): Promise<PerformanceResults> {
@@ -69,23 +69,27 @@ export async function measurePerformance(
throw new Error('Failed to parse code');
}
const transformResult = await babel.transformFromAstAsync(parsed, undefined, {
...babelOptions,
plugins: [
() => ({
visitor: {
ImportDeclaration(
path: babel.NodePath<babel.types.ImportDeclaration>,
) {
const value = path.node.source.value;
if (value === 'react' || value === 'react-dom') {
path.remove();
}
const transformResult = await babel.transformFromAstAsync(
parsed as babel.types.Node,
undefined,
{
...babelOptions,
plugins: [
() => ({
visitor: {
ImportDeclaration(
path: babel.NodePath<babel.types.ImportDeclaration>,
) {
const value = path.node.source.value;
if (value === 'react' || value === 'react-dom') {
path.remove();
}
},
},
},
}),
],
});
}),
],
},
);
const transpiled = transformResult?.code || undefined;
if (!transpiled) {
@@ -125,14 +129,16 @@ export async function measurePerformance(
// ui chaos monkey
const selectors = await page.evaluate(() => {
window.__INTERACTABLE_SELECTORS__ = [];
(window as any).__INTERACTABLE_SELECTORS__ = [];
const elements = Array.from(document.querySelectorAll('a')).concat(
Array.from(document.querySelectorAll('button')),
Array.from(document.querySelectorAll('button')) as any,
);
for (const el of elements) {
window.__INTERACTABLE_SELECTORS__.push(el.tagName.toLowerCase());
(window as any).__INTERACTABLE_SELECTORS__.push(
el.tagName.toLowerCase(),
);
}
return window.__INTERACTABLE_SELECTORS__;
return (window as any).__INTERACTABLE_SELECTORS__;
});
await Promise.all(

View File

@@ -0,0 +1,38 @@
{
"name": "react-tools-cli",
"version": "0.0.0",
"description": "CLI to execute react-mcp-server tools in isolation from the full mcp server",
"bin": {
"react-tools-cli": "./dist/index.js"
},
"scripts": {
"build": "rimraf dist && tsup"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/facebook/react.git",
"directory": "compiler/packages/react-tools-cli"
},
"dependencies": {
"@babel/core": "^7.26.0",
"@babel/parser": "^7.26",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.27.1",
"@modelcontextprotocol/sdk": "^1.9.0",
"algoliasearch": "^5.23.3",
"cheerio": "^1.0.0",
"html-to-text": "^9.0.5",
"prettier": "^3.3.3",
"puppeteer": "^24.7.2",
"yargs": "^18.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/html-to-text": "^9.0.4",
"@types/jest": "^29.5.14",
"jest": "^29.7.0",
"ts-jest": "^29.3.2"
}
}

View File

@@ -0,0 +1,240 @@
import {
PassNameType,
assertExhaustive,
compileTool,
componentTreeTool,
devDocsTool,
runtimePerfTool,
} from 'react-mcp-server/src';
import yargs from 'yargs';
import {hideBin} from 'yargs/helpers';
yargs(hideBin(process.argv))
.scriptName('react-tools-cli')
.usage('$0 <cmd> [args]')
.command(
'compile [code] [pass-name]',
'Compile React code with React Compiler',
yargs => {
yargs
.positional('code', {
type: 'string',
describe: 'The code to compile',
})
.option('pass-name', {
type: 'string',
choices: ['HIR', 'ReactiveFunction', 'All', '@DEBUG'],
describe: 'Compiler pass to run',
});
},
async function (argv) {
const code: string = String(argv['code'] ?? '');
const passName: PassNameType = argv['pass-name'] as PassNameType;
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,
};
}),
}),
);
process.exit(1);
}
case 'error':
case 'compile-error':
console.log(
JSON.stringify({
isError: true,
content: [
{
type: 'text' as const,
text: results.text,
},
],
}),
);
process.exit(1);
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)}`,
);
}
process.exit(1);
},
)
.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}`}],
}),
);
}
process.exit(1);
},
)
.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}`,
},
],
}),
);
}
process.exit(1);
},
)
.help()
.parse();
function calculateMean(values: number[]): string {
return values.length > 0
? values.reduce((acc, curr) => acc + curr, 0) / values.length + 'ms'
: 'could not collect';
}

View File

@@ -0,0 +1,22 @@
{
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16",
"rootDir": "../",
"noEmit": true,
"jsx": "react-jsxdev",
"lib": ["ES2022"],
// weaken strictness from preset
"importsNotUsedAsValues": "remove",
"noUncheckedIndexedAccess": false,
"noUnusedParameters": false,
"useUnknownInCatchVariables": false,
"target": "ES2022",
// ideally turn off only during dev, or on a per-file basis
"noUnusedLocals": false,
},
"exclude": ["node_modules"],
"include": ["src/**/*.ts"],
}

View File

@@ -0,0 +1,37 @@
/**
* 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.
*/
import {defineConfig} from 'tsup';
export default defineConfig({
entry: ['./src/index.ts'],
outDir: './dist',
external: [],
splitting: false,
sourcemap: false,
dts: false,
bundle: true,
format: 'cjs',
platform: 'node',
target: 'es2022',
banner: {
js: `#!/usr/bin/env node
/**
* 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.
*
* @lightSyntaxTransform
* @noflow
* @nolint
* @preventMunge
* @preserve-invariant-messages
*/`,
},
});

View File

@@ -542,11 +542,6 @@
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz"
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
"@babel/helper-string-parser@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.25.9":
version "7.25.9"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz"
@@ -1605,7 +1600,7 @@
debug "^4.3.1"
globals "^11.1.0"
"@babel/types@^7.0.0", "@babel/types@^7.19.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.2", "@babel/types@^7.24.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.4":
"@babel/types@7.26.3", "@babel/types@^7.0.0", "@babel/types@^7.19.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.2", "@babel/types@^7.24.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.10", "@babel/types@^7.26.3", "@babel/types@^7.27.0", "@babel/types@^7.27.1", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.4":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0"
integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==
@@ -1613,14 +1608,6 @@
"@babel/helper-string-parser" "^7.25.9"
"@babel/helper-validator-identifier" "^7.25.9"
"@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560"
integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.27.1"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz"
@@ -3745,7 +3732,7 @@ ansi-styles@^5.0.0:
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
ansi-styles@^6.1.0:
ansi-styles@^6.1.0, ansi-styles@^6.2.1:
version "6.2.1"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
@@ -4435,6 +4422,15 @@ cliui@^8.0.1:
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
cliui@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-9.0.1.tgz#6f7890f386f6f1f79953adc1f78dec46fcc2d291"
integrity sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==
dependencies:
string-width "^7.2.0"
strip-ansi "^7.1.0"
wrap-ansi "^9.0.0"
clone-deep@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz"
@@ -4963,7 +4959,7 @@ emittery@^0.13.1:
resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz"
integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
emoji-regex@^10.2.1:
emoji-regex@^10.2.1, emoji-regex@^10.3.0:
version "10.4.0"
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz"
integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==
@@ -5798,6 +5794,11 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-east-asian-width@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389"
integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==
get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
@@ -9825,6 +9826,15 @@ string-width@^6.1.0:
emoji-regex "^10.2.1"
strip-ansi "^7.0.1"
string-width@^7.0.0, string-width@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc"
integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==
dependencies:
emoji-regex "^10.3.0"
get-east-asian-width "^1.0.0"
strip-ansi "^7.1.0"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
@@ -10551,6 +10561,15 @@ wrap-ansi@^8.1.0:
string-width "^5.0.1"
strip-ansi "^7.0.1"
wrap-ansi@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e"
integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==
dependencies:
ansi-styles "^6.2.1"
string-width "^7.0.0"
strip-ansi "^7.1.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
@@ -10617,6 +10636,11 @@ yargs-parser@^21.0.1, yargs-parser@^21.1.1:
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
yargs-parser@^22.0.0:
version "22.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-22.0.0.tgz#87b82094051b0567717346ecd00fd14804b357c8"
integrity sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==
yargs-unparser@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz"
@@ -10670,6 +10694,18 @@ yargs@^17.3.1, yargs@^17.7.1, yargs@^17.7.2:
y18n "^5.0.5"
yargs-parser "^21.1.1"
yargs@^18.0.0:
version "18.0.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-18.0.0.tgz#6c84259806273a746b09f579087b68a3c2d25bd1"
integrity sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==
dependencies:
cliui "^9.0.1"
escalade "^3.1.1"
get-caller-file "^2.0.5"
string-width "^7.2.0"
y18n "^5.0.5"
yargs-parser "^22.0.0"
yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz"