init project
This commit is contained in:
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
import { z } from 'zod';
|
||||
import { config } from '../../config.js';
|
||||
import { detectProject } from '../../lib/frameworks/detect.js';
|
||||
import { resolveTarget } from '../../lib/frameworks/resolve.js';
|
||||
import { addDependencyCommandFor, detectJsPackageManager } from '../../lib/frameworks/packageManager.js';
|
||||
import { runCommand } from '../../lib/runCommand.js';
|
||||
import { errorResult, jsonResult, targetSchema, toErrorMessage } from '../shared.js';
|
||||
export function registerAddDependencyTool(server) {
|
||||
server.registerTool('add_dependency', {
|
||||
title: 'Add / Remove Dependency',
|
||||
description: 'Add or remove a dependency, auto-detecting npm/pnpm/yarn/bun (package.json) vs. Composer (composer.json) from the resolved project. Pass `ecosystem` explicitly to override the auto-detection.',
|
||||
inputSchema: {
|
||||
packages: z.array(z.string().min(1)).min(1).describe('Package names to add/remove, e.g. ["axios"] or ["laravel/sanctum"].'),
|
||||
ecosystem: z.enum(['npm', 'composer']).optional(),
|
||||
dev: z.boolean().optional().default(false).describe('Install as a dev dependency (ignored for composer remove).'),
|
||||
remove: z.boolean().optional().default(false),
|
||||
target: targetSchema,
|
||||
},
|
||||
}, async ({ packages, ecosystem, dev, remove, target }) => {
|
||||
try {
|
||||
const detected = detectProject(config.workspaceRoot);
|
||||
const { adapter, target: resolvedTarget } = resolveTarget(detected, target);
|
||||
const resolvedEcosystem = ecosystem ?? (adapter.packageManager === 'composer' ? 'composer' : 'npm');
|
||||
const { command, args } = resolvedEcosystem === 'composer'
|
||||
? {
|
||||
command: 'composer',
|
||||
args: remove
|
||||
? ['remove', ...packages]
|
||||
: ['require', ...(dev ? ['--dev'] : []), ...packages],
|
||||
}
|
||||
: addDependencyCommandFor(detectJsPackageManager(adapter.root), packages, { dev, remove });
|
||||
const result = await runCommand(command, args, {
|
||||
cwd: adapter.root,
|
||||
timeoutMs: config.scaffoldCommandTimeoutMs,
|
||||
});
|
||||
return jsonResult({ target: resolvedTarget, ecosystem: resolvedEcosystem, ...result });
|
||||
}
|
||||
catch (error) {
|
||||
return errorResult(toErrorMessage(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=addDependency.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"addDependency.js","sourceRoot":"","sources":["../../../src/tools/scaffold/addDependency.ts"],"names":[],"mappings":"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"}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { z } from 'zod';
|
||||
import { config } from '../../config.js';
|
||||
import { detectProject } from '../../lib/frameworks/detect.js';
|
||||
import { camelCase, kebabCase, pascalCase } from '../../lib/strings.js';
|
||||
import { errorResult, jsonResult, targetSchema, toErrorMessage } from '../shared.js';
|
||||
const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
||||
export function registerGenerateApiRouteTool(server) {
|
||||
server.registerTool('generate_api_route', {
|
||||
title: 'Generate API Route',
|
||||
description: 'Scaffold a backend route + handler for the detected framework: controller + routes/api.php entry for Laravel, controller + Routes.php entry for CodeIgniter, an app/pages API route file for Next.js, or a standalone Express router file otherwise.',
|
||||
inputSchema: {
|
||||
name: z.string().min(1).describe('Resource/route name, e.g. "posts" or "UserProfile".'),
|
||||
method: z.enum(METHODS).optional().default('GET'),
|
||||
target: targetSchema,
|
||||
},
|
||||
}, async ({ name, method, target }) => {
|
||||
try {
|
||||
const detected = detectProject(config.workspaceRoot);
|
||||
const adapter = target === 'frontend'
|
||||
? detected.frontend
|
||||
: target === 'backend'
|
||||
? detected.backend
|
||||
: (detected.backend ?? detected.frontend ?? detected.primary);
|
||||
if (!adapter)
|
||||
return errorResult(`target "${target}" was requested but no such project was detected.`);
|
||||
switch (adapter.kind) {
|
||||
case 'laravel':
|
||||
return jsonResult(generateLaravelRoute(adapter.root, name, method));
|
||||
case 'codeigniter':
|
||||
return jsonResult(generateCodeIgniterRoute(adapter.root, name, method));
|
||||
case 'react':
|
||||
return jsonResult(generateNextApiRoute(adapter.root, name, method));
|
||||
default:
|
||||
return jsonResult(generateExpressRoute(adapter.root, name, method));
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
return errorResult(toErrorMessage(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
function appendUnlessPresent(file, header, line) {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
if (!fs.existsSync(file)) {
|
||||
fs.writeFileSync(file, header);
|
||||
}
|
||||
const existing = fs.readFileSync(file, 'utf8');
|
||||
if (!existing.includes(line.trim())) {
|
||||
fs.appendFileSync(file, `${line}\n`);
|
||||
}
|
||||
}
|
||||
function writeIfMissing(file, contents) {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
if (fs.existsSync(file))
|
||||
return false;
|
||||
fs.writeFileSync(file, contents);
|
||||
return true;
|
||||
}
|
||||
function generateLaravelRoute(root, name, method) {
|
||||
const controllerName = `${pascalCase(name)}Controller`;
|
||||
const controllerFile = path.join(root, 'app', 'Http', 'Controllers', `${controllerName}.php`);
|
||||
const controllerCreated = writeIfMissing(controllerFile, `<?php
|
||||
|
||||
namespace App\\Http\\Controllers;
|
||||
|
||||
use Illuminate\\Http\\Request;
|
||||
|
||||
class ${controllerName}
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
return response()->json([]);
|
||||
}
|
||||
}
|
||||
`);
|
||||
const routesFile = path.join(root, 'routes', 'api.php');
|
||||
appendUnlessPresent(routesFile, `<?php
|
||||
|
||||
use Illuminate\\Support\\Facades\\Route;
|
||||
|
||||
`, `Route::${method.toLowerCase()}('/${kebabCase(name)}', \\App\\Http\\Controllers\\${controllerName}::class);`);
|
||||
return { kind: 'laravel', controllerFile, controllerCreated, routesFile, route: `${method} /api/${kebabCase(name)}` };
|
||||
}
|
||||
function generateCodeIgniterRoute(root, name, method) {
|
||||
const controllerName = pascalCase(name);
|
||||
const controllerFile = path.join(root, 'app', 'Controllers', `${controllerName}.php`);
|
||||
const controllerCreated = writeIfMissing(controllerFile, `<?php
|
||||
|
||||
namespace App\\Controllers;
|
||||
|
||||
class ${controllerName} extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return $this->response->setJSON([]);
|
||||
}
|
||||
}
|
||||
`);
|
||||
const routesFile = path.join(root, 'app', 'Config', 'Routes.php');
|
||||
appendUnlessPresent(routesFile, `<?php
|
||||
|
||||
use CodeIgniter\\Router\\RouteCollection;
|
||||
|
||||
/** @var RouteCollection $routes */
|
||||
|
||||
`, `$routes->${method.toLowerCase()}('${kebabCase(name)}', '${controllerName}::index');`);
|
||||
return { kind: 'codeigniter', controllerFile, controllerCreated, routesFile, route: `${method} /${kebabCase(name)}` };
|
||||
}
|
||||
function generateNextApiRoute(root, name, method) {
|
||||
const appDir = path.join(root, 'app');
|
||||
const srcAppDir = path.join(root, 'src', 'app');
|
||||
const pagesDir = path.join(root, 'pages');
|
||||
const srcPagesDir = path.join(root, 'src', 'pages');
|
||||
const appRoot = fs.existsSync(appDir) ? appDir : fs.existsSync(srcAppDir) ? srcAppDir : null;
|
||||
const pagesRoot = fs.existsSync(pagesDir) ? pagesDir : fs.existsSync(srcPagesDir) ? srcPagesDir : null;
|
||||
if (appRoot) {
|
||||
const file = path.join(appRoot, 'api', kebabCase(name), 'route.ts');
|
||||
const created = writeIfMissing(file, `import { NextResponse } from 'next/server';
|
||||
|
||||
export async function ${method}() {
|
||||
return NextResponse.json({});
|
||||
}
|
||||
`);
|
||||
return { kind: 'next-app-router', file, created, route: `${method} /api/${kebabCase(name)}` };
|
||||
}
|
||||
if (pagesRoot) {
|
||||
const file = path.join(pagesRoot, 'api', `${kebabCase(name)}.ts`);
|
||||
const created = writeIfMissing(file, `import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method !== '${method}') {
|
||||
res.status(405).end();
|
||||
return;
|
||||
}
|
||||
res.status(200).json({});
|
||||
}
|
||||
`);
|
||||
return { kind: 'next-pages-router', file, created, route: `${method} /api/${kebabCase(name)}` };
|
||||
}
|
||||
throw new Error('The detected React project has no "app" or "pages" directory, so it has no built-in API routes (this looks like a plain Vite React app, not Next.js). ' +
|
||||
'Scaffold a separate backend (Laravel/CodeIgniter/express-api) instead, or use target: "backend" if one already exists.');
|
||||
}
|
||||
function generateExpressRoute(root, name, method) {
|
||||
const file = path.join(root, 'routes', `${kebabCase(name)}.js`);
|
||||
const created = writeIfMissing(file, `import { Router } from 'express';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.${method.toLowerCase()}('/', (req, res) => {
|
||||
res.json({});
|
||||
});
|
||||
|
||||
export default router;
|
||||
`);
|
||||
const varName = `${camelCase(name)}Router`;
|
||||
return {
|
||||
kind: 'express',
|
||||
file,
|
||||
created,
|
||||
route: `${method} /api/${kebabCase(name)}`,
|
||||
wiring: `Not auto-mounted. Add to your app entry point: import ${varName} from './routes/${kebabCase(name)}.js'; app.use('/api/${kebabCase(name)}', ${varName});`,
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=generateApiRoute.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+162
@@ -0,0 +1,162 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { z } from 'zod';
|
||||
import { config } from '../../config.js';
|
||||
import { detectProject } from '../../lib/frameworks/detect.js';
|
||||
import { resolveWorkspacePath } from '../../lib/sandbox.js';
|
||||
import { errorResult, jsonResult, toErrorMessage } from '../shared.js';
|
||||
/**
|
||||
* UI component generator. Prefers React (TSX/JSX) or Vue/Nuxt SFCs when a
|
||||
* matching frontend is detected; otherwise writes a plain HTML/CSS/JS snippet.
|
||||
*/
|
||||
export function registerGenerateComponentTool(server) {
|
||||
server.registerTool('generate_component', {
|
||||
title: 'Generate Component',
|
||||
description: 'Scaffold a UI component. Generates a React function component (TSX/JSX) when React is detected, ' +
|
||||
'a Vue SFC (.vue) when Vue or Nuxt is detected, or a plain HTML/CSS/JS snippet otherwise.',
|
||||
inputSchema: {
|
||||
name: z.string().min(1).describe('Component name, e.g. "UserCard" (PascalCase recommended).'),
|
||||
directory: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Directory relative to the frontend project root. Defaults to "src/components".'),
|
||||
style: z.enum(['css-module', 'tailwind', 'none']).optional().default('css-module'),
|
||||
withTest: z.boolean().optional().default(false),
|
||||
clientComponent: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe('If true (Next.js App Router), prepends "use client" for Client Components. Ignored for Vue/Nuxt.'),
|
||||
overwrite: z.boolean().optional().default(false),
|
||||
},
|
||||
}, async ({ name, directory, style, withTest, clientComponent, overwrite }) => {
|
||||
try {
|
||||
const detected = detectProject(config.workspaceRoot);
|
||||
const frontend = detected.frontend;
|
||||
if (frontend?.kind === 'react') {
|
||||
return jsonResult(generateReactComponent(frontend.root, name, directory, style, withTest, clientComponent ?? false, overwrite));
|
||||
}
|
||||
if (frontend?.kind === 'vue' || frontend?.kind === 'nuxt') {
|
||||
return jsonResult(generateVueSfc(frontend.root, frontend.kind, name, directory, style, withTest, overwrite));
|
||||
}
|
||||
return jsonResult(generatePlainComponent(config.workspaceRoot, name, directory, overwrite));
|
||||
}
|
||||
catch (error) {
|
||||
return errorResult(toErrorMessage(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
function generateReactComponent(frontendRoot, name, directory, style, withTest, clientComponent, overwrite) {
|
||||
const typescript = fs.existsSync(path.join(frontendRoot, 'tsconfig.json'));
|
||||
const ext = typescript ? 'tsx' : 'jsx';
|
||||
const dir = resolveWorkspacePath(frontendRoot, directory ?? 'src/components');
|
||||
const componentFile = path.join(dir, `${name}.${ext}`);
|
||||
const files = [];
|
||||
assertNotExists(componentFile, overwrite);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
const useClient = clientComponent ? `'use client';\n\n` : '';
|
||||
const cssImport = style === 'css-module' ? `import styles from './${name}.module.css';\n` : '';
|
||||
const rootClass = style === 'css-module' ? ' className={styles.root}' : style === 'tailwind' ? ' className="p-4"' : '';
|
||||
fs.writeFileSync(componentFile, `${useClient}${cssImport}export function ${name}() {
|
||||
return (
|
||||
<div${rootClass}>
|
||||
<p>${name}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
`);
|
||||
files.push(componentFile);
|
||||
if (style === 'css-module') {
|
||||
const cssFile = path.join(dir, `${name}.module.css`);
|
||||
assertNotExists(cssFile, overwrite);
|
||||
fs.writeFileSync(cssFile, `.root {\n}\n`);
|
||||
files.push(cssFile);
|
||||
}
|
||||
if (withTest) {
|
||||
const testFile = path.join(dir, `${name}.test.${ext}`);
|
||||
assertNotExists(testFile, overwrite);
|
||||
fs.writeFileSync(testFile, `import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ${name} } from './${name}';
|
||||
|
||||
describe('${name}', () => {
|
||||
it('renders', () => {
|
||||
render(<${name} />);
|
||||
expect(screen.getByText('${name}')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
`);
|
||||
files.push(testFile);
|
||||
}
|
||||
return { kind: 'react', name, files, clientComponent };
|
||||
}
|
||||
function generateVueSfc(frontendRoot, kind, name, directory, style, withTest, overwrite) {
|
||||
const typescript = fs.existsSync(path.join(frontendRoot, 'tsconfig.json'));
|
||||
const dir = resolveWorkspacePath(frontendRoot, directory ?? 'src/components');
|
||||
const componentFile = path.join(dir, `${name}.vue`);
|
||||
const files = [];
|
||||
assertNotExists(componentFile, overwrite);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
const langAttr = typescript ? ' lang="ts"' : '';
|
||||
const rootClass = style === 'tailwind' ? ' class="p-4"' : style === 'none' ? '' : ' class="root"';
|
||||
const styleBlock = style === 'none'
|
||||
? ''
|
||||
: style === 'tailwind'
|
||||
? ''
|
||||
: `\n<style scoped>\n.root {\n}\n</style>\n`;
|
||||
fs.writeFileSync(componentFile, `<script setup${langAttr}>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div${rootClass}>
|
||||
<p>${name}</p>
|
||||
</div>
|
||||
</template>
|
||||
${styleBlock}`);
|
||||
files.push(componentFile);
|
||||
if (withTest) {
|
||||
const testExt = typescript ? 'ts' : 'js';
|
||||
const testFile = path.join(dir, `${name}.test.${testExt}`);
|
||||
assertNotExists(testFile, overwrite);
|
||||
fs.writeFileSync(testFile, `import { mount } from '@vue/test-utils';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import ${name} from './${name}.vue';
|
||||
|
||||
describe('${name}', () => {
|
||||
it('renders', () => {
|
||||
const wrapper = mount(${name});
|
||||
expect(wrapper.text()).toContain('${name}');
|
||||
});
|
||||
});
|
||||
`);
|
||||
files.push(testFile);
|
||||
}
|
||||
return { kind, name, files };
|
||||
}
|
||||
function generatePlainComponent(root, name, directory, overwrite) {
|
||||
const dir = resolveWorkspacePath(root, directory ?? path.join('components', name));
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
const htmlFile = path.join(dir, `${name}.html`);
|
||||
const cssFile = path.join(dir, `${name}.css`);
|
||||
const jsFile = path.join(dir, `${name}.js`);
|
||||
for (const file of [htmlFile, cssFile, jsFile])
|
||||
assertNotExists(file, overwrite);
|
||||
fs.writeFileSync(htmlFile, `<div class="${name.toLowerCase()}">
|
||||
<p>${name}</p>
|
||||
</div>
|
||||
`);
|
||||
fs.writeFileSync(cssFile, `.${name.toLowerCase()} {\n}\n`);
|
||||
fs.writeFileSync(jsFile, `// Behaviour for the ${name} component.\n`);
|
||||
return {
|
||||
kind: 'generic',
|
||||
name,
|
||||
files: [htmlFile, cssFile, jsFile],
|
||||
note: 'No React/Vue/Nuxt frontend was detected, so a plain HTML/CSS/JS snippet was generated instead.',
|
||||
};
|
||||
}
|
||||
function assertNotExists(file, overwrite) {
|
||||
if (!overwrite && fs.existsSync(file)) {
|
||||
throw new Error(`"${file}" already exists. Pass overwrite: true to replace it.`);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=generateComponent.js.map
|
||||
File diff suppressed because one or more lines are too long
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import { registerScaffoldProjectTool } from './scaffoldProject.js';
|
||||
import { registerGenerateComponentTool } from './generateComponent.js';
|
||||
import { registerGenerateApiRouteTool } from './generateApiRoute.js';
|
||||
import { registerAddDependencyTool } from './addDependency.js';
|
||||
export function registerScaffoldTools(server) {
|
||||
registerScaffoldProjectTool(server);
|
||||
registerGenerateComponentTool(server);
|
||||
registerGenerateApiRouteTool(server);
|
||||
registerAddDependencyTool(server);
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/scaffold/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACpC,6BAA6B,CAAC,MAAM,CAAC,CAAC;IACtC,4BAA4B,CAAC,MAAM,CAAC,CAAC;IACrC,yBAAyB,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC"}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { z } from 'zod';
|
||||
import { config } from '../../config.js';
|
||||
import { resolveWorkspacePath } from '../../lib/sandbox.js';
|
||||
import { runCommand } from '../../lib/runCommand.js';
|
||||
import { errorResult, jsonResult, toErrorMessage } from '../shared.js';
|
||||
import { writeStaticHtmlTemplate } from './templates/staticHtml.js';
|
||||
import { writeExpressApiTemplate } from './templates/expressApi.js';
|
||||
const TEMPLATES = [
|
||||
'vite-react',
|
||||
'vite-vue',
|
||||
'next',
|
||||
'express-api',
|
||||
'static-html',
|
||||
'laravel',
|
||||
'codeigniter',
|
||||
'laravel-react',
|
||||
'codeigniter-react',
|
||||
];
|
||||
export function registerScaffoldProjectTool(server) {
|
||||
server.registerTool('scaffold_project', {
|
||||
title: 'Scaffold Project',
|
||||
description: `Create a new project from a template. Supported templates: ${TEMPLATES.join(', ')}. "laravel-react" and "codeigniter-react" create a PHP backend in "<name>/backend" and a separate Vite+React frontend in "<name>/frontend".`,
|
||||
inputSchema: {
|
||||
template: z.enum(TEMPLATES),
|
||||
name: z.string().min(1).describe('Project/folder name to create.'),
|
||||
directory: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Parent directory (relative to the workspace root) to create the project in. Defaults to the workspace root.'),
|
||||
},
|
||||
}, async ({ template, name, directory }) => {
|
||||
try {
|
||||
const parentDir = resolveWorkspacePath(config.workspaceRoot, directory ?? '.');
|
||||
fs.mkdirSync(parentDir, { recursive: true });
|
||||
const targetDir = path.join(parentDir, name);
|
||||
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
|
||||
return errorResult(`"${path.relative(config.workspaceRoot, targetDir)}" already exists and is not empty.`);
|
||||
}
|
||||
const result = await runScaffold(template, name, parentDir);
|
||||
return jsonResult(result);
|
||||
}
|
||||
catch (error) {
|
||||
return errorResult(toErrorMessage(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
async function runScaffold(template, name, parentDir) {
|
||||
switch (template) {
|
||||
case 'vite-react':
|
||||
return scaffoldVite(name, parentDir, 'react-ts');
|
||||
case 'vite-vue':
|
||||
return scaffoldVite(name, parentDir, 'vue-ts');
|
||||
case 'next':
|
||||
return scaffoldNext(name, parentDir);
|
||||
case 'express-api':
|
||||
return { template, path: writeExpressApiTemplate(path.join(parentDir, name), name) };
|
||||
case 'static-html':
|
||||
return { template, path: writeStaticHtmlTemplate(path.join(parentDir, name), name) };
|
||||
case 'laravel':
|
||||
return scaffoldComposerProject('laravel/laravel', name, parentDir, template);
|
||||
case 'codeigniter':
|
||||
return scaffoldComposerProject('codeigniter4/appstarter', name, parentDir, template);
|
||||
case 'laravel-react':
|
||||
return scaffoldCombined('laravel/laravel', name, parentDir, template);
|
||||
case 'codeigniter-react':
|
||||
return scaffoldCombined('codeigniter4/appstarter', name, parentDir, template);
|
||||
default: {
|
||||
const exhaustive = template;
|
||||
throw new Error(`Unhandled template: ${exhaustive}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function scaffoldVite(name, parentDir, viteTemplate) {
|
||||
const result = await runCommand('npm', ['create', 'vite@latest', name, '--', '--template', viteTemplate], {
|
||||
cwd: parentDir,
|
||||
timeoutMs: config.scaffoldCommandTimeoutMs,
|
||||
});
|
||||
return { template: viteTemplate === 'react-ts' ? 'vite-react' : 'vite-vue', path: path.join(parentDir, name), ...result };
|
||||
}
|
||||
async function scaffoldNext(name, parentDir) {
|
||||
const result = await runCommand('npx', [
|
||||
'--yes',
|
||||
'create-next-app@latest',
|
||||
name,
|
||||
'--ts',
|
||||
'--eslint',
|
||||
'--app',
|
||||
'--src-dir',
|
||||
'--import-alias',
|
||||
'@/*',
|
||||
'--use-npm',
|
||||
'--no-tailwind',
|
||||
'--yes',
|
||||
], { cwd: parentDir, timeoutMs: config.scaffoldCommandTimeoutMs });
|
||||
return { template: 'next', path: path.join(parentDir, name), ...result };
|
||||
}
|
||||
async function scaffoldComposerProject(composerPackage, name, parentDir, template) {
|
||||
const result = await runCommand('composer', ['create-project', composerPackage, name], {
|
||||
cwd: parentDir,
|
||||
timeoutMs: config.scaffoldCommandTimeoutMs,
|
||||
});
|
||||
return { template, path: path.join(parentDir, name), ...result };
|
||||
}
|
||||
async function scaffoldCombined(composerPackage, name, parentDir, template) {
|
||||
const projectDir = path.join(parentDir, name);
|
||||
fs.mkdirSync(projectDir, { recursive: true });
|
||||
const backendResult = await runCommand('composer', ['create-project', composerPackage, 'backend'], {
|
||||
cwd: projectDir,
|
||||
timeoutMs: config.scaffoldCommandTimeoutMs,
|
||||
});
|
||||
const frontendResult = await runCommand('npm', ['create', 'vite@latest', 'frontend', '--', '--template', 'react-ts'], { cwd: projectDir, timeoutMs: config.scaffoldCommandTimeoutMs });
|
||||
return {
|
||||
template,
|
||||
path: projectDir,
|
||||
backend: { path: path.join(projectDir, 'backend'), ...backendResult },
|
||||
frontend: { path: path.join(projectDir, 'frontend'), ...frontendResult },
|
||||
note: 'Backend and frontend were scaffolded as independent siblings (not wired together via Inertia). Point the frontend at the backend API base URL as a next step.',
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=scaffoldProject.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"scaffoldProject.js","sourceRoot":"","sources":["../../../src/tools/scaffold/scaffoldProject.ts"],"names":[],"mappings":"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"}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
export function writeExpressApiTemplate(targetDir, name) {
|
||||
fs.mkdirSync(path.join(targetDir, 'routes'), { recursive: true });
|
||||
fs.writeFileSync(path.join(targetDir, 'package.json'), `${JSON.stringify({
|
||||
name,
|
||||
version: '0.1.0',
|
||||
private: true,
|
||||
type: 'module',
|
||||
scripts: {
|
||||
dev: 'node --watch index.js',
|
||||
start: 'node index.js',
|
||||
test: 'echo "no tests yet" && exit 0',
|
||||
},
|
||||
dependencies: {
|
||||
express: '^5.0.0',
|
||||
},
|
||||
}, null, 2)}\n`);
|
||||
fs.writeFileSync(path.join(targetDir, 'index.js'), `import express from 'express';
|
||||
import healthRouter from './routes/health.js';
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use('/api/health', healthRouter);
|
||||
|
||||
const port = process.env.PORT ?? 3000;
|
||||
app.listen(port, () => {
|
||||
console.log(\`${name} listening on http://localhost:\${port}\`);
|
||||
});
|
||||
`);
|
||||
fs.writeFileSync(path.join(targetDir, 'routes', 'health.js'), `import { Router } from 'express';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', (_req, res) => {
|
||||
res.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
export default router;
|
||||
`);
|
||||
fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules/\n');
|
||||
return targetDir;
|
||||
}
|
||||
//# sourceMappingURL=expressApi.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"expressApi.js","sourceRoot":"","sources":["../../../../src/tools/scaffold/templates/expressApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,uBAAuB,CAAC,SAAiB,EAAE,IAAY;IACrE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAElE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,EACpC,GAAG,IAAI,CAAC,SAAS,CACf;QACE,IAAI;QACJ,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE;YACP,GAAG,EAAE,uBAAuB;YAC5B,KAAK,EAAE,eAAe;YACtB,IAAI,EAAE,+BAA+B;SACtC;QACD,YAAY,EAAE;YACZ,OAAO,EAAE,QAAQ;SAClB;KACF,EACD,IAAI,EACJ,CAAC,CACF,IAAI,CACN,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAChC;;;;;;;;;kBASc,IAAI;;CAErB,CACE,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,EAC3C;;;;;;;;;CASH,CACE,CAAC;IAEF,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAExE,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
export function writeStaticHtmlTemplate(targetDir, name) {
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(targetDir, 'index.html'), `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>${name}</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>${name}</h1>
|
||||
<p>Edit <code>index.html</code>, <code>style.css</code>, and <code>script.js</code> to get started.</p>
|
||||
</main>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
fs.writeFileSync(path.join(targetDir, 'style.css'), `:root {
|
||||
color-scheme: light dark;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 40rem;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
`);
|
||||
fs.writeFileSync(path.join(targetDir, 'script.js'), `// Entry point for ${name}.
|
||||
console.log('${name} loaded');
|
||||
`);
|
||||
return targetDir;
|
||||
}
|
||||
//# sourceMappingURL=staticHtml.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"staticHtml.js","sourceRoot":"","sources":["../../../../src/tools/scaffold/templates/staticHtml.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,uBAAuB,CAAC,SAAiB,EAAE,IAAY;IACrE,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAClC;;;;;aAKS,IAAI;;;;;YAKL,IAAI;;;;;;CAMf,CACE,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EACjC;;;;;;;;;;;;;;;;;CAiBH,CACE,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EACjC,sBAAsB,IAAI;eACf,IAAI;CAClB,CACE,CAAC;IAEF,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
||||
Reference in New Issue
Block a user