init project
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { z } from 'zod';
|
||||
import { config } from '../../config.js';
|
||||
import { runCommand } from '../../lib/runCommand.js';
|
||||
import { kebabCase, pascalCase } from '../../lib/strings.js';
|
||||
import { errorResult, jsonResult, toErrorMessage } from '../shared.js';
|
||||
import { requireLaravel } from './requireLaravel.js';
|
||||
function pluralize(word) {
|
||||
const lower = word.toLowerCase();
|
||||
if (lower.endsWith('ies'))
|
||||
return word;
|
||||
if (lower.endsWith('y') && !/[aeiou]y$/i.test(word))
|
||||
return `${word.slice(0, -1)}ies`;
|
||||
if (/(s|x|z|ch|sh)$/i.test(word))
|
||||
return `${word}es`;
|
||||
if (lower.endsWith('s'))
|
||||
return word;
|
||||
return `${word}s`;
|
||||
}
|
||||
export function registerGenerateLaravelResourceTool(server) {
|
||||
server.registerTool('generate_laravel_resource', {
|
||||
title: 'Generate Laravel Resource',
|
||||
description: 'One-shot scaffold of a full Laravel resource: model + migration + factory + seeder + controller, ' +
|
||||
'then wire an apiResource/resource route. Wraps the relevant `php artisan make:*` calls.',
|
||||
inputSchema: {
|
||||
name: z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe('Resource name, e.g. "Post" or "blog_post". Converted to PascalCase for the model/controller.'),
|
||||
api: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(true)
|
||||
.describe('If true (default), generate an API controller (--api) and register Route::apiResource in routes/api.php. If false, generate a web resource controller and register Route::resource in routes/web.php.'),
|
||||
migrate: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe('If true, run `php artisan migrate` after scaffolding. Requires a configured database.'),
|
||||
},
|
||||
}, async ({ name, api, migrate }) => {
|
||||
try {
|
||||
const laravel = requireLaravel();
|
||||
const model = pascalCase(name);
|
||||
const controller = `${model}Controller`;
|
||||
const routeName = kebabCase(pluralize(model));
|
||||
const steps = [];
|
||||
// Model + migration + factory + seeder in one go.
|
||||
const modelResult = await runCommand('php', ['artisan', 'make:model', model, '-mfs', '--no-interaction'], { cwd: laravel.root, timeoutMs: config.scaffoldCommandTimeoutMs });
|
||||
steps.push({ step: 'make:model -mfs', ...modelResult });
|
||||
const controllerArgs = api
|
||||
? ['artisan', 'make:controller', controller, '--api', '--model=' + model, '--no-interaction']
|
||||
: ['artisan', 'make:controller', controller, '--resource', '--model=' + model, '--no-interaction'];
|
||||
const controllerResult = await runCommand('php', controllerArgs, {
|
||||
cwd: laravel.root,
|
||||
timeoutMs: config.scaffoldCommandTimeoutMs,
|
||||
});
|
||||
steps.push({ step: 'make:controller', ...controllerResult });
|
||||
const routesFile = path.join(laravel.root, 'routes', api ? 'api.php' : 'web.php');
|
||||
const routeLine = api
|
||||
? `Route::apiResource('${routeName}', \\App\\Http\\Controllers\\${controller}::class);`
|
||||
: `Route::resource('${routeName}', \\App\\Http\\Controllers\\${controller}::class);`;
|
||||
const routeWired = wireRoute(routesFile, routeLine, api);
|
||||
steps.push({ step: 'wire-route', routesFile, routeLine, wired: routeWired });
|
||||
let migrateResult = null;
|
||||
if (migrate) {
|
||||
migrateResult = await runCommand('php', ['artisan', 'migrate', '--no-interaction'], {
|
||||
cwd: laravel.root,
|
||||
timeoutMs: config.scaffoldCommandTimeoutMs,
|
||||
});
|
||||
steps.push({ step: 'migrate', ...migrateResult });
|
||||
}
|
||||
return jsonResult({
|
||||
model,
|
||||
controller,
|
||||
route: `${api ? 'apiResource' : 'resource'} /${routeName}`,
|
||||
routesFile,
|
||||
routeWired,
|
||||
steps,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
return errorResult(toErrorMessage(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
function wireRoute(routesFile, routeLine, api) {
|
||||
fs.mkdirSync(path.dirname(routesFile), { recursive: true });
|
||||
if (!fs.existsSync(routesFile)) {
|
||||
const header = api
|
||||
? `<?php\n\nuse Illuminate\\Support\\Facades\\Route;\n\n`
|
||||
: `<?php\n\nuse Illuminate\\Support\\Facades\\Route;\n\n`;
|
||||
fs.writeFileSync(routesFile, header + routeLine + '\n');
|
||||
return true;
|
||||
}
|
||||
const existing = fs.readFileSync(routesFile, 'utf8');
|
||||
if (existing.includes(routeLine.trim()))
|
||||
return false;
|
||||
// Ensure the Route facade is imported if the file already exists without it.
|
||||
let next = existing;
|
||||
if (!/use\s+Illuminate\\Support\\Facades\\Route\s*;/.test(next)) {
|
||||
if (next.startsWith('<?php')) {
|
||||
next = next.replace('<?php', "<?php\n\nuse Illuminate\\Support\\Facades\\Route;");
|
||||
}
|
||||
else {
|
||||
next = `<?php\n\nuse Illuminate\\Support\\Facades\\Route;\n\n${next}`;
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(routesFile, `${next.trimEnd()}\n${routeLine}\n`);
|
||||
return true;
|
||||
}
|
||||
//# sourceMappingURL=generateLaravelResource.js.map
|
||||
Reference in New Issue
Block a user