import fs from 'node:fs'; import path from 'node:path'; import { z } from 'zod'; import { camelCase, pascalCase } from '../../lib/strings.js'; import { resolveWorkspacePath } from '../../lib/sandbox.js'; import { errorResult, jsonResult, toErrorMessage } from '../shared.js'; import { requireReact } from './requireReact.js'; export function registerGenerateReactHookTool(server) { server.registerTool('generate_react_hook', { title: 'Generate React Hook', description: 'Scaffold a custom React hook (useXyz.ts) and a matching test file. Detects TypeScript from tsconfig.json.', inputSchema: { name: z .string() .min(1) .describe('Hook name without the "use" prefix, e.g. "Counter" or "fetchUser" (camelCase is fine).'), directory: z .string() .optional() .describe('Directory relative to the React project root. Defaults to "src/hooks".'), withTest: z.boolean().optional().default(true), overwrite: z.boolean().optional().default(false), }, }, async ({ name, directory, withTest, overwrite }) => { try { const react = requireReact(); const typescript = fs.existsSync(path.join(react.root, 'tsconfig.json')); const ext = typescript ? 'ts' : 'js'; const hookName = name.startsWith('use') ? camelCase(name) : `use${pascalCase(name)}`; const dir = resolveWorkspacePath(react.root, directory ?? 'src/hooks'); const hookFile = path.join(dir, `${hookName}.${ext}`); assertNotExists(hookFile, overwrite); fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(hookFile, `import { useState } from 'react'; export function ${hookName}() { const [count, setCount] = useState(0); return { count, increment: () => setCount((c) => c + 1) }; } `); const files = [hookFile]; if (withTest) { const testFile = path.join(dir, `${hookName}.test.${ext}`); assertNotExists(testFile, overwrite); fs.writeFileSync(testFile, `import { describe, expect, it } from 'vitest'; import { renderHook, act } from '@testing-library/react'; import { ${hookName} } from './${hookName}'; describe('${hookName}', () => { it('increments count', () => { const { result } = renderHook(() => ${hookName}()); act(() => result.current.increment()); expect(result.current.count).toBe(1); }); }); `); files.push(testFile); } return jsonResult({ name: hookName, files }); } catch (error) { return errorResult(toErrorMessage(error)); } }); } function assertNotExists(file, overwrite) { if (!overwrite && fs.existsSync(file)) { throw new Error(`"${file}" already exists. Pass overwrite: true to replace it.`); } } //# sourceMappingURL=generateReactHook.js.map