@chimerai/cli 0.2.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +317 -0
- package/dist/commands/add.d.ts +11 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +2126 -0
- package/dist/commands/create.d.ts +12 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +1703 -0
- package/dist/commands/deploy.d.ts +11 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +219 -0
- package/dist/commands/dev.d.ts +17 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +206 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +728 -0
- package/dist/commands/generate.d.ts +19 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +429 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +269 -0
- package/dist/commands/list.d.ts +12 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +328 -0
- package/dist/commands/migrate.d.ts +14 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +197 -0
- package/dist/commands/plugin.d.ts +10 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +239 -0
- package/dist/commands/remove.d.ts +11 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +472 -0
- package/dist/commands/secret.d.ts +12 -0
- package/dist/commands/secret.d.ts.map +1 -0
- package/dist/commands/secret.js +102 -0
- package/dist/commands/setup.d.ts +9 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +788 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +211 -0
- package/dist/commands/use.d.ts +9 -0
- package/dist/commands/use.d.ts.map +1 -0
- package/dist/commands/use.js +51 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/license.d.ts +55 -0
- package/dist/license.d.ts.map +1 -0
- package/dist/license.js +258 -0
- package/dist/scanner.d.ts +31 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +113 -0
- package/dist/schema-manager.d.ts +26 -0
- package/dist/schema-manager.d.ts.map +1 -0
- package/dist/schema-manager.js +132 -0
- package/dist/templates/admin.d.ts +49 -0
- package/dist/templates/admin.d.ts.map +1 -0
- package/dist/templates/admin.js +1358 -0
- package/dist/templates/ai-routes.d.ts +17 -0
- package/dist/templates/ai-routes.d.ts.map +1 -0
- package/dist/templates/ai-routes.js +1130 -0
- package/dist/templates/ai-service-tools.d.ts +22 -0
- package/dist/templates/ai-service-tools.d.ts.map +1 -0
- package/dist/templates/ai-service-tools.js +1424 -0
- package/dist/templates/ai-service.d.ts +66 -0
- package/dist/templates/ai-service.d.ts.map +1 -0
- package/dist/templates/ai-service.js +2202 -0
- package/dist/templates/api-routes.d.ts +108 -0
- package/dist/templates/api-routes.d.ts.map +1 -0
- package/dist/templates/api-routes.js +1219 -0
- package/dist/templates/auth.d.ts +48 -0
- package/dist/templates/auth.d.ts.map +1 -0
- package/dist/templates/auth.js +381 -0
- package/dist/templates/billing.d.ts +44 -0
- package/dist/templates/billing.d.ts.map +1 -0
- package/dist/templates/billing.js +551 -0
- package/dist/templates/chat.d.ts +63 -0
- package/dist/templates/chat.d.ts.map +1 -0
- package/dist/templates/chat.js +1979 -0
- package/dist/templates/components.d.ts +22 -0
- package/dist/templates/components.d.ts.map +1 -0
- package/dist/templates/components.js +672 -0
- package/dist/templates/config.d.ts +6 -0
- package/dist/templates/config.d.ts.map +1 -0
- package/dist/templates/config.js +86 -0
- package/dist/templates/docker.d.ts +25 -0
- package/dist/templates/docker.d.ts.map +1 -0
- package/dist/templates/docker.js +165 -0
- package/dist/templates/gdpr.d.ts +16 -0
- package/dist/templates/gdpr.d.ts.map +1 -0
- package/dist/templates/gdpr.js +259 -0
- package/dist/templates/index.d.ts +77 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +339 -0
- package/dist/templates/layout.d.ts +67 -0
- package/dist/templates/layout.d.ts.map +1 -0
- package/dist/templates/layout.js +670 -0
- package/dist/templates/mfa.d.ts +23 -0
- package/dist/templates/mfa.d.ts.map +1 -0
- package/dist/templates/mfa.js +353 -0
- package/dist/templates/middleware.d.ts +12 -0
- package/dist/templates/middleware.d.ts.map +1 -0
- package/dist/templates/middleware.js +116 -0
- package/dist/templates/prisma.d.ts +35 -0
- package/dist/templates/prisma.d.ts.map +1 -0
- package/dist/templates/prisma.js +724 -0
- package/dist/templates/provider-routes.d.ts +21 -0
- package/dist/templates/provider-routes.d.ts.map +1 -0
- package/dist/templates/provider-routes.js +1203 -0
- package/dist/templates/rag.d.ts +48 -0
- package/dist/templates/rag.d.ts.map +1 -0
- package/dist/templates/rag.js +532 -0
- package/dist/templates/widget.d.ts +64 -0
- package/dist/templates/widget.d.ts.map +1 -0
- package/dist/templates/widget.js +1360 -0
- package/dist/utils/provider-db.d.ts +63 -0
- package/dist/utils/provider-db.d.ts.map +1 -0
- package/dist/utils/provider-db.js +300 -0
- package/dist/utils.d.ts +78 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +330 -0
- package/package.json +60 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Command - PHASE 3.4
|
|
3
|
+
* Code generation scaffolding for models, API routes, pages, components, and hooks
|
|
4
|
+
* Automatically generates Prisma models, CRUD API routes, and TypeScript scaffolding
|
|
5
|
+
*/
|
|
6
|
+
export interface GenerateOptions {
|
|
7
|
+
dir: string;
|
|
8
|
+
force: boolean;
|
|
9
|
+
}
|
|
10
|
+
type GeneratorType = 'model' | 'api' | 'page' | 'component' | 'hook';
|
|
11
|
+
/**
|
|
12
|
+
* Main generate command - routes to specific generators
|
|
13
|
+
* @param type - Generator type (model, api, page, component, hook)
|
|
14
|
+
* @param name - Name of what to generate (e.g., "user-profile", "post")
|
|
15
|
+
* @param options - Generate options
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateCommand(type: GeneratorType, name: string, options: GenerateOptions): Promise<void>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,KAAK,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;AAErE;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generate Command - PHASE 3.4
|
|
4
|
+
* Code generation scaffolding for models, API routes, pages, components, and hooks
|
|
5
|
+
* Automatically generates Prisma models, CRUD API routes, and TypeScript scaffolding
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.generateCommand = generateCommand;
|
|
12
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
15
|
+
const scanner_js_1 = require("../scanner.js");
|
|
16
|
+
const schema_manager_js_1 = require("../schema-manager.js");
|
|
17
|
+
const utils_js_1 = require("../utils.js");
|
|
18
|
+
/**
|
|
19
|
+
* Main generate command - routes to specific generators
|
|
20
|
+
* @param type - Generator type (model, api, page, component, hook)
|
|
21
|
+
* @param name - Name of what to generate (e.g., "user-profile", "post")
|
|
22
|
+
* @param options - Generate options
|
|
23
|
+
*/
|
|
24
|
+
async function generateCommand(type, name, options) {
|
|
25
|
+
const targetDir = (0, utils_js_1.resolveTargetDir)(options.dir || '.');
|
|
26
|
+
const project = await (0, scanner_js_1.scanProject)(targetDir);
|
|
27
|
+
if (!project.hasPackageJson) {
|
|
28
|
+
(0, utils_js_1.handleCliError)('No package.json found. Run: chimerai create <name>');
|
|
29
|
+
}
|
|
30
|
+
switch (type) {
|
|
31
|
+
case 'model':
|
|
32
|
+
await generateModel(name, targetDir, project, options.force);
|
|
33
|
+
break;
|
|
34
|
+
case 'api':
|
|
35
|
+
await generateApiRoute(name, targetDir, project, options.force);
|
|
36
|
+
break;
|
|
37
|
+
case 'page':
|
|
38
|
+
await generatePage(name, targetDir, project, options.force);
|
|
39
|
+
break;
|
|
40
|
+
case 'component':
|
|
41
|
+
await generateComponent(name, targetDir, project, options.force);
|
|
42
|
+
break;
|
|
43
|
+
case 'hook':
|
|
44
|
+
await generateHook(name, targetDir, project, options.force);
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
(0, utils_js_1.handleCliError)(`Unknown generator type: ${type}. Available types: model, api, page, component, hook`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Normalize names: "user-profile" → { pascal: "UserProfile", camel: "userProfile", kebab: "user-profile" }
|
|
52
|
+
*/
|
|
53
|
+
function normalizeName(name) {
|
|
54
|
+
const kebab = name
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.replace(/([A-Z])/g, '-$1')
|
|
57
|
+
.toLowerCase();
|
|
58
|
+
const pascal = kebab
|
|
59
|
+
.split('-')
|
|
60
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
61
|
+
.join('');
|
|
62
|
+
const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
63
|
+
const snake = kebab.replace(/-/g, '_');
|
|
64
|
+
return { pascal, camel, kebab, snake };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Extract hook name - removes 'use' prefix if present
|
|
68
|
+
* e.g., 'use-auth' -> 'Auth', 'useAuth' -> 'Auth', 'auth' -> 'Auth'
|
|
69
|
+
*/
|
|
70
|
+
function extractHookName(name) {
|
|
71
|
+
// Remove 'use' prefix if present (case-insensitive)
|
|
72
|
+
let cleaned = name.replace(/^use-?/i, '');
|
|
73
|
+
// Ensure it starts with uppercase
|
|
74
|
+
return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Generate Prisma model with CRUD API routes
|
|
78
|
+
*/
|
|
79
|
+
async function generateModel(name, targetDir, project, force) {
|
|
80
|
+
if (!project.hasPrisma) {
|
|
81
|
+
(0, utils_js_1.handleCliError)('Prisma not configured in this project. Run: chimerai init and select RBAC');
|
|
82
|
+
}
|
|
83
|
+
// Preserve original casing for model name (just ensure first char is uppercase)
|
|
84
|
+
const modelName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
85
|
+
const { kebab, camel } = normalizeName(name);
|
|
86
|
+
console.log(chalk_1.default.blue(`\n 📦 Generating model: ${chalk_1.default.bold(modelName)}\n`));
|
|
87
|
+
// 1. Add Prisma model
|
|
88
|
+
const schemaPath = path_1.default.join(targetDir, 'prisma', 'schema.prisma');
|
|
89
|
+
const prismaModel = `
|
|
90
|
+
model ${modelName} {
|
|
91
|
+
id String @id @default(cuid())
|
|
92
|
+
// TODO: Add your fields here
|
|
93
|
+
createdAt DateTime @default(now())
|
|
94
|
+
updatedAt DateTime @updatedAt
|
|
95
|
+
|
|
96
|
+
@@map("${kebab}")
|
|
97
|
+
}
|
|
98
|
+
`;
|
|
99
|
+
try {
|
|
100
|
+
await (0, schema_manager_js_1.extendPrismaSchema)(schemaPath, prismaModel);
|
|
101
|
+
console.log(chalk_1.default.green(` ✓ prisma/schema.prisma (${modelName} model)`));
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
(0, utils_js_1.handleCliError)(`Failed to add Prisma model: ${error.message}`);
|
|
105
|
+
}
|
|
106
|
+
// 2. Generate GET/POST API route
|
|
107
|
+
const apiRoute = generateCrudRoute(modelName, camel);
|
|
108
|
+
const apiPath = path_1.default.join(targetDir, 'app', 'api', kebab, 'route.ts');
|
|
109
|
+
if (await fs_extra_1.default.pathExists(apiPath)) {
|
|
110
|
+
if (!force) {
|
|
111
|
+
console.log(chalk_1.default.yellow(` ⊘ app/api/${kebab}/route.ts already exists (use --force to overwrite)`));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(apiPath));
|
|
116
|
+
await fs_extra_1.default.writeFile(apiPath, apiRoute);
|
|
117
|
+
console.log(chalk_1.default.green(` ✓ app/api/${kebab}/route.ts (GET, POST)`));
|
|
118
|
+
// 3. Generate GET/PUT/DELETE API route for [id]
|
|
119
|
+
const apiIdRoute = generateCrudByIdRoute(modelName, camel);
|
|
120
|
+
const apiIdPath = path_1.default.join(targetDir, 'app', 'api', kebab, '[id]', 'route.ts');
|
|
121
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(apiIdPath));
|
|
122
|
+
await fs_extra_1.default.writeFile(apiIdPath, apiIdRoute);
|
|
123
|
+
console.log(chalk_1.default.green(` ✓ app/api/${kebab}/[id]/route.ts (GET, PUT, DELETE)`));
|
|
124
|
+
// Summary
|
|
125
|
+
console.log(chalk_1.default.cyan('\n 📋 Next steps:'));
|
|
126
|
+
console.log(chalk_1.default.gray(` 1. Edit prisma/schema.prisma to add fields to ${modelName}`));
|
|
127
|
+
console.log(chalk_1.default.gray(` 2. Run: npx prisma db push`));
|
|
128
|
+
console.log(chalk_1.default.gray(` 3. Your API is ready at /api/${kebab}\n`));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Generate standalone API route
|
|
132
|
+
*/
|
|
133
|
+
async function generateApiRoute(name, targetDir, _project, force) {
|
|
134
|
+
const { kebab } = normalizeName(name);
|
|
135
|
+
console.log(chalk_1.default.blue(`\n 🔌 Generating API route: ${chalk_1.default.bold(`/api/${kebab}`)}\n`));
|
|
136
|
+
const apiRoute = `import { NextRequest, NextResponse } from 'next/server';
|
|
137
|
+
|
|
138
|
+
export async function GET(request: NextRequest) {
|
|
139
|
+
try {
|
|
140
|
+
// TODO: Implement GET logic
|
|
141
|
+
return NextResponse.json({ data: [] });
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error('GET error:', error);
|
|
144
|
+
return NextResponse.json(
|
|
145
|
+
{ error: { code: 'INTERNAL_ERROR', message: 'Internal server error' } },
|
|
146
|
+
{ status: 500 }
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function POST(request: NextRequest) {
|
|
152
|
+
try {
|
|
153
|
+
const body = await request.json();
|
|
154
|
+
// TODO: Implement POST logic
|
|
155
|
+
return NextResponse.json({ data: body }, { status: 201 });
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error('POST error:', error);
|
|
158
|
+
return NextResponse.json(
|
|
159
|
+
{ error: { code: 'INTERNAL_ERROR', message: 'Internal server error' } },
|
|
160
|
+
{ status: 500 }
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
`;
|
|
165
|
+
const apiPath = path_1.default.join(targetDir, 'app', 'api', kebab, 'route.ts');
|
|
166
|
+
if (await fs_extra_1.default.pathExists(apiPath)) {
|
|
167
|
+
if (!force) {
|
|
168
|
+
console.log(chalk_1.default.yellow(` ⊘ app/api/${kebab}/route.ts already exists (use --force to overwrite)`));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(apiPath));
|
|
173
|
+
await fs_extra_1.default.writeFile(apiPath, apiRoute);
|
|
174
|
+
console.log(chalk_1.default.green(` ✓ app/api/${kebab}/route.ts\n`));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Generate page component
|
|
178
|
+
*/
|
|
179
|
+
async function generatePage(name, targetDir, _project, force) {
|
|
180
|
+
const { pascal, kebab } = normalizeName(name);
|
|
181
|
+
console.log(chalk_1.default.blue(`\n 📄 Generating page: ${chalk_1.default.bold(`/${kebab}`)}\n`));
|
|
182
|
+
const page = `'use client';
|
|
183
|
+
|
|
184
|
+
import { useState, useEffect } from 'react';
|
|
185
|
+
|
|
186
|
+
export default function ${pascal}Page() {
|
|
187
|
+
const [data, setData] = useState(null);
|
|
188
|
+
const [loading, setLoading] = useState(true);
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
// TODO: Fetch data
|
|
192
|
+
setLoading(false);
|
|
193
|
+
}, []);
|
|
194
|
+
|
|
195
|
+
if (loading) return <div>Loading...</div>;
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<div className="p-6">
|
|
199
|
+
<h1 className="text-3xl font-bold">${pascal}</h1>
|
|
200
|
+
{/* TODO: Add content */}
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
const pagePath = path_1.default.join(targetDir, 'app', kebab, 'page.tsx');
|
|
206
|
+
if (await fs_extra_1.default.pathExists(pagePath)) {
|
|
207
|
+
if (!force) {
|
|
208
|
+
console.log(chalk_1.default.yellow(` ⊘ app/${kebab}/page.tsx already exists (use --force to overwrite)`));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(pagePath));
|
|
213
|
+
await fs_extra_1.default.writeFile(pagePath, page);
|
|
214
|
+
console.log(chalk_1.default.green(` ✓ app/${kebab}/page.tsx\n`));
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Generate React component
|
|
218
|
+
*/
|
|
219
|
+
async function generateComponent(name, targetDir, _project, force) {
|
|
220
|
+
const { pascal } = normalizeName(name);
|
|
221
|
+
console.log(chalk_1.default.blue(`\n ⚛️ Generating component: ${chalk_1.default.bold(pascal)}\n`));
|
|
222
|
+
const component = `import React from 'react';
|
|
223
|
+
|
|
224
|
+
interface ${pascal}Props {
|
|
225
|
+
// TODO: Define props
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function ${pascal}({ }: ${pascal}Props) {
|
|
229
|
+
return (
|
|
230
|
+
<div>
|
|
231
|
+
{/* TODO: Add JSX */}
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
const componentPath = path_1.default.join(targetDir, 'components', `${pascal}.tsx`);
|
|
237
|
+
if (await fs_extra_1.default.pathExists(componentPath)) {
|
|
238
|
+
if (!force) {
|
|
239
|
+
console.log(chalk_1.default.yellow(` ⊘ components/${pascal}.tsx already exists (use --force to overwrite)`));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(componentPath));
|
|
244
|
+
await fs_extra_1.default.writeFile(componentPath, component);
|
|
245
|
+
console.log(chalk_1.default.green(` ✓ components/${pascal}.tsx\n`));
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Generate custom hook
|
|
249
|
+
*/
|
|
250
|
+
async function generateHook(name, targetDir, _project, force) {
|
|
251
|
+
const hookName = extractHookName(name);
|
|
252
|
+
console.log(chalk_1.default.blue(`\n 🪝 Generating hook: ${chalk_1.default.bold(`use${hookName}`)}\n`));
|
|
253
|
+
const hook = `'use client';
|
|
254
|
+
|
|
255
|
+
import { useState, useEffect } from 'react';
|
|
256
|
+
|
|
257
|
+
export function use${hookName}() {
|
|
258
|
+
const [state, setState] = useState(null);
|
|
259
|
+
const [loading, setLoading] = useState(false);
|
|
260
|
+
const [error, setError] = useState<Error | null>(null);
|
|
261
|
+
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
// TODO: Implement hook logic
|
|
264
|
+
}, []);
|
|
265
|
+
|
|
266
|
+
return { state, loading, error };
|
|
267
|
+
}
|
|
268
|
+
`;
|
|
269
|
+
const hookPath = path_1.default.join(targetDir, 'hooks', `use${hookName}.ts`);
|
|
270
|
+
if (await fs_extra_1.default.pathExists(hookPath)) {
|
|
271
|
+
if (!force) {
|
|
272
|
+
console.log(chalk_1.default.yellow(` ⊘ hooks/use${hookName}.ts already exists (use --force to overwrite)`));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(hookPath));
|
|
277
|
+
await fs_extra_1.default.writeFile(hookPath, hook);
|
|
278
|
+
console.log(chalk_1.default.green(` ✓ hooks/use${hookName}.ts\n`));
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Generate CRUD API route (GET list, POST create)
|
|
282
|
+
*/
|
|
283
|
+
function generateCrudRoute(modelName, varName) {
|
|
284
|
+
return `import { NextRequest, NextResponse } from 'next/server';
|
|
285
|
+
import { prisma } from '@/lib/prisma';
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* GET /api/${varName.toLowerCase()}s
|
|
289
|
+
* List all ${varName} records with pagination
|
|
290
|
+
*/
|
|
291
|
+
export async function GET(request: NextRequest) {
|
|
292
|
+
try {
|
|
293
|
+
const { searchParams } = new URL(request.url);
|
|
294
|
+
const page = Math.max(1, parseInt(searchParams.get('page') || '1', 10));
|
|
295
|
+
const limit = Math.min(100, Math.max(1, parseInt(searchParams.get('limit') || '20', 10)));
|
|
296
|
+
const skip = (page - 1) * limit;
|
|
297
|
+
|
|
298
|
+
const [items, total] = await Promise.all([
|
|
299
|
+
prisma.${varName}.findMany({
|
|
300
|
+
skip,
|
|
301
|
+
take: limit,
|
|
302
|
+
orderBy: { createdAt: 'desc' },
|
|
303
|
+
}),
|
|
304
|
+
prisma.${varName}.count(),
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
return NextResponse.json({
|
|
308
|
+
data: items,
|
|
309
|
+
pagination: {
|
|
310
|
+
page,
|
|
311
|
+
limit,
|
|
312
|
+
total,
|
|
313
|
+
totalPages: Math.ceil(total / limit),
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.error('Failed to fetch ${varName} records:', error);
|
|
318
|
+
return NextResponse.json(
|
|
319
|
+
{ error: { code: 'INTERNAL_ERROR', message: 'Failed to fetch records' } },
|
|
320
|
+
{ status: 500 }
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* POST /api/${varName.toLowerCase()}s
|
|
327
|
+
* Create a new ${varName}
|
|
328
|
+
*/
|
|
329
|
+
export async function POST(request: NextRequest) {
|
|
330
|
+
try {
|
|
331
|
+
const body = await request.json();
|
|
332
|
+
|
|
333
|
+
const item = await prisma.${varName}.create({
|
|
334
|
+
data: body,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
return NextResponse.json({ data: item }, { status: 201 });
|
|
338
|
+
} catch (error) {
|
|
339
|
+
console.error('Failed to create ${varName}:', error);
|
|
340
|
+
return NextResponse.json(
|
|
341
|
+
{ error: { code: 'INTERNAL_ERROR', message: 'Failed to create record' } },
|
|
342
|
+
{ status: 500 }
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
`;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Generate CRUD API route for [id] (GET, PUT, DELETE)
|
|
350
|
+
*/
|
|
351
|
+
function generateCrudByIdRoute(modelName, varName) {
|
|
352
|
+
return `import { NextRequest, NextResponse } from 'next/server';
|
|
353
|
+
import { prisma } from '@/lib/prisma';
|
|
354
|
+
|
|
355
|
+
interface Params {
|
|
356
|
+
params: { id: string };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* GET /api/${varName.toLowerCase()}s/:id
|
|
361
|
+
* Get a single ${varName}
|
|
362
|
+
*/
|
|
363
|
+
export async function GET(request: NextRequest, { params }: Params) {
|
|
364
|
+
try {
|
|
365
|
+
const item = await prisma.${varName}.findUnique({
|
|
366
|
+
where: { id: params.id },
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
if (!item) {
|
|
370
|
+
return NextResponse.json(
|
|
371
|
+
{ error: { code: 'NOT_FOUND', message: '${modelName} not found' } },
|
|
372
|
+
{ status: 404 }
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return NextResponse.json({ data: item });
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error('Failed to fetch ${varName}:', error);
|
|
379
|
+
return NextResponse.json(
|
|
380
|
+
{ error: { code: 'INTERNAL_ERROR', message: 'Failed to fetch record' } },
|
|
381
|
+
{ status: 500 }
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* PUT /api/${varName.toLowerCase()}s/:id
|
|
388
|
+
* Update a ${varName}
|
|
389
|
+
*/
|
|
390
|
+
export async function PUT(request: NextRequest, { params }: Params) {
|
|
391
|
+
try {
|
|
392
|
+
const body = await request.json();
|
|
393
|
+
|
|
394
|
+
const item = await prisma.${varName}.update({
|
|
395
|
+
where: { id: params.id },
|
|
396
|
+
data: body,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
return NextResponse.json({ data: item });
|
|
400
|
+
} catch (error) {
|
|
401
|
+
console.error('Failed to update ${varName}:', error);
|
|
402
|
+
return NextResponse.json(
|
|
403
|
+
{ error: { code: 'INTERNAL_ERROR', message: 'Failed to update record' } },
|
|
404
|
+
{ status: 500 }
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* DELETE /api/${varName.toLowerCase()}s/:id
|
|
411
|
+
* Delete a ${varName}
|
|
412
|
+
*/
|
|
413
|
+
export async function DELETE(request: NextRequest, { params }: Params) {
|
|
414
|
+
try {
|
|
415
|
+
await prisma.${varName}.delete({
|
|
416
|
+
where: { id: params.id },
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
return NextResponse.json({ success: true });
|
|
420
|
+
} catch (error) {
|
|
421
|
+
console.error('Failed to delete ${varName}:', error);
|
|
422
|
+
return NextResponse.json(
|
|
423
|
+
{ error: { code: 'INTERNAL_ERROR', message: 'Failed to delete record' } },
|
|
424
|
+
{ status: 500 }
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
`;
|
|
429
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init Command - Initialize RBAC or complete project
|
|
3
|
+
* REFACTORED: Uses generic installFeature() instead of individual install functions (PHASE 2.1)
|
|
4
|
+
*/
|
|
5
|
+
interface InitOptions {
|
|
6
|
+
dir: string;
|
|
7
|
+
yes: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function initCommand(feature: string, options: InitOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,UAAU,WAAW;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,OAAO,CAAC;CACd;AAyND,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,iBAiDtE"}
|