init project
This commit is contained in:
+71
@@ -0,0 +1,71 @@
|
||||
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
|
||||
Reference in New Issue
Block a user