@hazeljs/cli 0.6.0 → 0.7.0
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/@template-ai-native/package.json +1 -1
- package/README.md +22 -1
- package/cli-manifest.json +940 -0
- package/dist/commands/add.d.ts +9 -0
- package/dist/commands/add.js +9 -0
- package/dist/commands/generate-auth.d.ts +12 -0
- package/dist/commands/generate-auth.js +12 -0
- package/dist/commands/generate-crud.d.ts +12 -0
- package/dist/commands/generate-crud.js +12 -0
- package/dist/commands/generate-dto.d.ts +12 -0
- package/dist/commands/generate-dto.js +12 -0
- package/dist/commands/generate-module.d.ts +12 -0
- package/dist/commands/generate-module.js +12 -0
- package/dist/commands/generate-simple.d.ts +53 -6
- package/dist/commands/generate-simple.js +42 -386
- package/dist/commands/info.d.ts +12 -0
- package/dist/commands/info.js +12 -0
- package/dist/commands/templates.d.ts +34 -0
- package/dist/commands/templates.js +387 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +45 -1
- package/dist/utils/generator-registry.d.ts +5 -0
- package/dist/utils/generator-registry.js +5 -0
- package/dist/utils/packages-registry.d.ts +11 -2
- package/dist/utils/packages-registry.js +11 -2
- package/package.json +6 -4
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Mustache templates for all simple (single-file) generators.
|
|
4
|
+
*
|
|
5
|
+
* Each template uses three standard variables provided by the Generator base class:
|
|
6
|
+
* - {{className}} — PascalCase name (e.g. "Users")
|
|
7
|
+
* - {{fileName}} — kebab-case name (e.g. "users")
|
|
8
|
+
* - {{camelName}} — camelCase name (e.g. "users")
|
|
9
|
+
*
|
|
10
|
+
* Some templates accept additional data via `extraData` in SimpleGeneratorConfig:
|
|
11
|
+
* - AGENT_TEMPLATE — {{description}}
|
|
12
|
+
* - REPOSITORY_TEMPLATE — {{modelName}} (defaults to fileName)
|
|
13
|
+
*
|
|
14
|
+
* Templates are imported by generate-simple.ts and referenced in the
|
|
15
|
+
* SIMPLE_GENERATORS config array. To add a new generator, create a template
|
|
16
|
+
* here and add a config entry in generate-simple.ts.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.SERVERLESS_CLOUD_FUNCTION_TEMPLATE = exports.SERVERLESS_LAMBDA_TEMPLATE = exports.CONFIG_TEMPLATE = exports.DISCOVERY_TEMPLATE = exports.RAG_SERVICE_TEMPLATE = exports.CRON_SERVICE_TEMPLATE = exports.CACHE_SERVICE_TEMPLATE = exports.AGENT_TEMPLATE = exports.AI_SERVICE_TEMPLATE = exports.WEBSOCKET_GATEWAY_TEMPLATE = exports.REPOSITORY_TEMPLATE = exports.EXCEPTION_FILTER_TEMPLATE = exports.PIPE_TEMPLATE = exports.MIDDLEWARE_TEMPLATE = exports.INTERCEPTOR_TEMPLATE = exports.GUARD_TEMPLATE = exports.SERVICE_TEMPLATE = exports.CONTROLLER_TEMPLATE = void 0;
|
|
20
|
+
// ── Core framework generators ────────────────────────────────────────────────
|
|
21
|
+
exports.CONTROLLER_TEMPLATE = `import { Controller, Get, Post, Body, Param, Delete, Put } from '@hazeljs/core';
|
|
22
|
+
import { {{className}}Service } from './{{fileName}}.service';
|
|
23
|
+
import { Create{{className}}Dto } from './dto/create-{{fileName}}.dto';
|
|
24
|
+
import { Update{{className}}Dto } from './dto/update-{{fileName}}.dto';
|
|
25
|
+
|
|
26
|
+
@Controller('{{fileName}}')
|
|
27
|
+
export class {{className}}Controller {
|
|
28
|
+
constructor(private readonly {{camelName}}Service: {{className}}Service) {}
|
|
29
|
+
|
|
30
|
+
@Get()
|
|
31
|
+
findAll() {
|
|
32
|
+
return this.{{camelName}}Service.findAll();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Get(':id')
|
|
36
|
+
findOne(@Param('id') id: string) {
|
|
37
|
+
return this.{{camelName}}Service.findOne(id);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Post()
|
|
41
|
+
create(@Body(Create{{className}}Dto) createDto: Create{{className}}Dto) {
|
|
42
|
+
return this.{{camelName}}Service.create(createDto);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Put(':id')
|
|
46
|
+
update(@Param('id') id: string, @Body(Update{{className}}Dto) updateDto: Update{{className}}Dto) {
|
|
47
|
+
return this.{{camelName}}Service.update(id, updateDto);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Delete(':id')
|
|
51
|
+
remove(@Param('id') id: string) {
|
|
52
|
+
return this.{{camelName}}Service.remove(id);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
exports.SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
|
|
57
|
+
|
|
58
|
+
@Service()
|
|
59
|
+
export class {{className}}Service {
|
|
60
|
+
constructor() {}
|
|
61
|
+
|
|
62
|
+
async findAll() {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async findOne(id: string) {
|
|
67
|
+
return { id };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async create(createDto: any) {
|
|
71
|
+
return createDto;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async update(id: string, updateDto: any) {
|
|
75
|
+
return { id, ...updateDto };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async remove(id: string) {
|
|
79
|
+
return { id };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
exports.GUARD_TEMPLATE = `import { Injectable, type CanActivate, type ExecutionContext } from '@hazeljs/core';
|
|
84
|
+
|
|
85
|
+
@Injectable()
|
|
86
|
+
export class {{className}}Guard implements CanActivate {
|
|
87
|
+
canActivate(context: ExecutionContext): boolean {
|
|
88
|
+
const request = context.switchToHttp().getRequest();
|
|
89
|
+
// Add your guard logic here
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
exports.INTERCEPTOR_TEMPLATE = `import { Injectable, Interceptor, type ExecutionContext } from '@hazeljs/core';
|
|
95
|
+
|
|
96
|
+
@Injectable()
|
|
97
|
+
export class {{className}}Interceptor implements Interceptor {
|
|
98
|
+
async intercept(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown> {
|
|
99
|
+
// Pre-processing logic here (before handler execution)
|
|
100
|
+
const result = await next();
|
|
101
|
+
// Post-processing logic here (after handler execution)
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
exports.MIDDLEWARE_TEMPLATE = `import { Injectable, type MiddlewareHandler, type Request, type Response, type NextFunction } from '@hazeljs/core';
|
|
107
|
+
|
|
108
|
+
@Injectable()
|
|
109
|
+
export class {{className}}Middleware implements MiddlewareHandler {
|
|
110
|
+
use(req: Request, res: Response, next: NextFunction) {
|
|
111
|
+
// Add your middleware logic here
|
|
112
|
+
console.log(\`[{{className}}Middleware] \${req.method} \${req.url}\`);
|
|
113
|
+
|
|
114
|
+
// Continue to next middleware
|
|
115
|
+
next();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
`;
|
|
119
|
+
exports.PIPE_TEMPLATE = `import { type PipeTransform, type RequestContext } from '@hazeljs/core';
|
|
120
|
+
|
|
121
|
+
export class {{className}}Pipe implements PipeTransform {
|
|
122
|
+
transform(value: unknown, context: RequestContext): unknown {
|
|
123
|
+
// Transform logic here
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
128
|
+
exports.EXCEPTION_FILTER_TEMPLATE = `import { Catch, type ExceptionFilter, type ArgumentsHost, HttpError, logger } from '@hazeljs/core';
|
|
129
|
+
|
|
130
|
+
@Catch(HttpError)
|
|
131
|
+
export class {{className}}ExceptionFilter implements ExceptionFilter<HttpError> {
|
|
132
|
+
catch(exception: HttpError, host: ArgumentsHost): void {
|
|
133
|
+
const ctx = host.switchToHttp();
|
|
134
|
+
const response = ctx.getResponse();
|
|
135
|
+
const request = ctx.getRequest();
|
|
136
|
+
|
|
137
|
+
const status = exception.statusCode || 500;
|
|
138
|
+
const message = exception.message || 'Internal server error';
|
|
139
|
+
|
|
140
|
+
logger.error(\`[\${request.method}] \${request.url} - \${message} (\${status})\`);
|
|
141
|
+
|
|
142
|
+
response.status(status).json({
|
|
143
|
+
statusCode: status,
|
|
144
|
+
message,
|
|
145
|
+
timestamp: new Date().toISOString(),
|
|
146
|
+
path: request.url,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
`;
|
|
151
|
+
// ── Package-specific generators ──────────────────────────────────────────────
|
|
152
|
+
exports.REPOSITORY_TEMPLATE = `import { Repository, BaseRepository, PrismaService } from '@hazeljs/prisma';
|
|
153
|
+
|
|
154
|
+
// @Repository implies @Injectable() — no need for both decorators
|
|
155
|
+
@Repository({ model: '{{modelName}}' })
|
|
156
|
+
export class {{className}}Repository extends BaseRepository<any> {
|
|
157
|
+
constructor(prisma: PrismaService) {
|
|
158
|
+
super(prisma, '{{modelName}}');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Add custom repository methods here
|
|
162
|
+
async findByName(name: string) {
|
|
163
|
+
return this.findMany({ where: { name } });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
`;
|
|
167
|
+
exports.WEBSOCKET_GATEWAY_TEMPLATE = `import { Realtime, OnConnect, OnDisconnect, OnMessage, Subscribe, Client, Data, WebSocketClient } from '@hazeljs/websocket';
|
|
168
|
+
|
|
169
|
+
@Realtime('/{{fileName}}')
|
|
170
|
+
export class {{className}}Gateway {
|
|
171
|
+
@OnConnect()
|
|
172
|
+
handleConnection(@Client() client: WebSocketClient) {
|
|
173
|
+
console.log('Client connected:', client.id);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@OnDisconnect()
|
|
177
|
+
handleDisconnect(@Client() client: WebSocketClient) {
|
|
178
|
+
console.log('Client disconnected:', client.id);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@Subscribe('message')
|
|
182
|
+
@OnMessage('message')
|
|
183
|
+
handleMessage(@Client() client: WebSocketClient, @Data() data: unknown) {
|
|
184
|
+
console.log('Message received from', client.id, ':', data);
|
|
185
|
+
// Handle message logic here
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
`;
|
|
189
|
+
exports.AI_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
|
|
190
|
+
import { AIService, AIFunction, AIPrompt } from '@hazeljs/ai';
|
|
191
|
+
|
|
192
|
+
@Service()
|
|
193
|
+
export class {{className}}AIService {
|
|
194
|
+
constructor(private readonly aiService: AIService) {}
|
|
195
|
+
|
|
196
|
+
@AIFunction({
|
|
197
|
+
provider: 'openai',
|
|
198
|
+
model: 'gpt-4',
|
|
199
|
+
streaming: false,
|
|
200
|
+
})
|
|
201
|
+
async {{camelName}}Task(@AIPrompt() prompt: string): Promise<unknown> {
|
|
202
|
+
const result = await this.aiService.complete({
|
|
203
|
+
provider: 'openai',
|
|
204
|
+
model: 'gpt-4',
|
|
205
|
+
messages: [{ role: 'user', content: prompt }],
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
exports.AGENT_TEMPLATE = `import { Agent, Tool } from '@hazeljs/agent';
|
|
213
|
+
|
|
214
|
+
@Agent({
|
|
215
|
+
name: '{{fileName}}',
|
|
216
|
+
description: '{{description}}',
|
|
217
|
+
systemPrompt: 'You are a helpful {{className}} agent.',
|
|
218
|
+
enableMemory: true,
|
|
219
|
+
enableRAG: true,
|
|
220
|
+
})
|
|
221
|
+
export class {{className}}Agent {
|
|
222
|
+
@Tool({
|
|
223
|
+
description: 'Example tool for {{fileName}}',
|
|
224
|
+
parameters: [
|
|
225
|
+
{
|
|
226
|
+
name: 'input',
|
|
227
|
+
type: 'string',
|
|
228
|
+
description: 'Input parameter',
|
|
229
|
+
required: true,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
})
|
|
233
|
+
async exampleTool(input: { input: string }): Promise<{ result: string }> {
|
|
234
|
+
// Implement your tool logic here
|
|
235
|
+
return {
|
|
236
|
+
result: \`Processed: \${input.input}\`,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
`;
|
|
241
|
+
exports.CACHE_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
|
|
242
|
+
import { CacheService, Cacheable, CacheEvict } from '@hazeljs/cache';
|
|
243
|
+
|
|
244
|
+
@Service()
|
|
245
|
+
export class {{className}}CacheService {
|
|
246
|
+
constructor(private readonly cacheService: CacheService) {}
|
|
247
|
+
|
|
248
|
+
@Cacheable({ key: '{{fileName}}:all', ttl: 60 })
|
|
249
|
+
async findAll() {
|
|
250
|
+
// This result will be cached for 60 seconds
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
@Cacheable({ key: '{{fileName}}:{{=<% %>=}}#{id}<%={{ }}=%>', ttl: 300 })
|
|
255
|
+
async findOne(id: string) {
|
|
256
|
+
// This result will be cached for 5 minutes
|
|
257
|
+
return { id };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@CacheEvict({ key: '{{fileName}}:all' })
|
|
261
|
+
async create(data: any) {
|
|
262
|
+
// Creating a new item evicts the list cache
|
|
263
|
+
return data;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async clearAll() {
|
|
267
|
+
await this.cacheService.clear();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
`;
|
|
271
|
+
exports.CRON_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
|
|
272
|
+
import { Cron, CronExpression } from '@hazeljs/cron';
|
|
273
|
+
|
|
274
|
+
@Service()
|
|
275
|
+
export class {{className}}CronService {
|
|
276
|
+
@Cron(CronExpression.EVERY_MINUTE)
|
|
277
|
+
handleEveryMinute() {
|
|
278
|
+
console.log('[{{className}}Cron] Running every minute...');
|
|
279
|
+
// Add your cron job logic here
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@Cron('0 0 * * *') // Every day at midnight
|
|
283
|
+
handleDaily() {
|
|
284
|
+
console.log('[{{className}}Cron] Running daily task...');
|
|
285
|
+
// Add your daily task logic here
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
@Cron(CronExpression.EVERY_HOUR)
|
|
289
|
+
handleHourly() {
|
|
290
|
+
console.log('[{{className}}Cron] Running hourly cleanup...');
|
|
291
|
+
// Add your hourly task logic here
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
`;
|
|
295
|
+
exports.RAG_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
|
|
296
|
+
import { RAGPipeline, MemoryVectorStore } from '@hazeljs/rag';
|
|
297
|
+
|
|
298
|
+
@Service()
|
|
299
|
+
export class {{className}}RagService {
|
|
300
|
+
private pipeline: RAGPipeline;
|
|
301
|
+
|
|
302
|
+
constructor() {
|
|
303
|
+
// Initialize with a memory vector store (swap for Pinecone, Qdrant, etc. in production)
|
|
304
|
+
const vectorStore = new MemoryVectorStore();
|
|
305
|
+
|
|
306
|
+
this.pipeline = new RAGPipeline({
|
|
307
|
+
vectorStore,
|
|
308
|
+
topK: 5,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async addDocument(content: string, metadata?: Record<string, unknown>) {
|
|
313
|
+
// Add a document to the vector store for retrieval
|
|
314
|
+
await this.pipeline.addDocument({
|
|
315
|
+
content,
|
|
316
|
+
metadata: metadata || {},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async query(question: string) {
|
|
321
|
+
// Retrieve relevant documents and generate a response
|
|
322
|
+
const results = await this.pipeline.query(question);
|
|
323
|
+
return results;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
`;
|
|
327
|
+
exports.DISCOVERY_TEMPLATE = `import { Service } from '@hazeljs/core';
|
|
328
|
+
import { ServiceRegistry, DiscoveryClient } from '@hazeljs/discovery';
|
|
329
|
+
|
|
330
|
+
@Service()
|
|
331
|
+
export class {{className}}DiscoveryService {
|
|
332
|
+
constructor(
|
|
333
|
+
private readonly registry: ServiceRegistry,
|
|
334
|
+
private readonly client: DiscoveryClient,
|
|
335
|
+
) {}
|
|
336
|
+
|
|
337
|
+
async registerService() {
|
|
338
|
+
await this.registry.register({
|
|
339
|
+
name: '{{fileName}}-service',
|
|
340
|
+
host: 'localhost',
|
|
341
|
+
port: 3000,
|
|
342
|
+
metadata: {
|
|
343
|
+
version: '1.0.0',
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async discoverService(serviceName: string) {
|
|
349
|
+
const instances = await this.client.getInstances(serviceName);
|
|
350
|
+
return instances;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
`;
|
|
354
|
+
exports.CONFIG_TEMPLATE = `import { HazelModule } from '@hazeljs/core';
|
|
355
|
+
import { ConfigModule, ConfigService } from '@hazeljs/config';
|
|
356
|
+
|
|
357
|
+
// Import ConfigModule.forRoot() in your app module:
|
|
358
|
+
//
|
|
359
|
+
// @HazelModule({
|
|
360
|
+
// imports: [
|
|
361
|
+
// ConfigModule.forRoot({
|
|
362
|
+
// envFilePath: '.env',
|
|
363
|
+
// }),
|
|
364
|
+
// ],
|
|
365
|
+
// })
|
|
366
|
+
//
|
|
367
|
+
// Then inject ConfigService wherever you need it:
|
|
368
|
+
//
|
|
369
|
+
// constructor(private readonly config: ConfigService) {}
|
|
370
|
+
//
|
|
371
|
+
// Usage:
|
|
372
|
+
// this.config.get('DATABASE_URL');
|
|
373
|
+
// this.config.get('PORT', '3000'); // with default value
|
|
374
|
+
|
|
375
|
+
export { ConfigModule, ConfigService };
|
|
376
|
+
`;
|
|
377
|
+
// ── Serverless generators ────────────────────────────────────────────────────
|
|
378
|
+
exports.SERVERLESS_LAMBDA_TEMPLATE = `import { createLambdaHandler } from '@hazeljs/serverless';
|
|
379
|
+
import { AppModule } from './app.module';
|
|
380
|
+
|
|
381
|
+
export const handler = createLambdaHandler(AppModule);
|
|
382
|
+
`;
|
|
383
|
+
exports.SERVERLESS_CLOUD_FUNCTION_TEMPLATE = `import { createCloudFunctionHandler } from '@hazeljs/serverless';
|
|
384
|
+
import { AppModule } from './app.module';
|
|
385
|
+
|
|
386
|
+
export const handler = createCloudFunctionHandler(AppModule);
|
|
387
|
+
`;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* HazelJS CLI — Entry point and command registration
|
|
4
|
+
*
|
|
5
|
+
* Architecture Overview:
|
|
6
|
+
* =====================
|
|
7
|
+
*
|
|
8
|
+
* index.ts — Entry point, registers all commands with Commander
|
|
9
|
+
* ├─ commands/
|
|
10
|
+
* │ ├─ generate-app.ts — `hazel new` (full scaffolding) and `hazel g app` (skeleton)
|
|
11
|
+
* │ ├─ add.ts — `hazel add [pkg]` with --setup flag (replaces generate-setup)
|
|
12
|
+
* │ ├─ info.ts — `hazel info` (project diagnostics)
|
|
13
|
+
* │ ├─ generate-simple.ts — Config-driven single-file generators (18 types)
|
|
14
|
+
* │ ├─ generate-module.ts — Multi-file module generator
|
|
15
|
+
* │ ├─ generate-dto.ts — DTO pair generator
|
|
16
|
+
* │ ├─ generate-crud.ts — Full CRUD resource generator
|
|
17
|
+
* │ ├─ generate-auth.ts — Auth module with JWT guard
|
|
18
|
+
* │ └─ templates.ts — Mustache templates for all simple generators
|
|
19
|
+
* └─ utils/
|
|
20
|
+
* ├─ generator.ts — Base Generator class, shared types, string utils
|
|
21
|
+
* ├─ generator-registry.ts — Unified registry + runGenerator dispatcher
|
|
22
|
+
* └─ packages-registry.ts — All HazelJS package metadata (single source of truth)
|
|
23
|
+
*
|
|
24
|
+
* Command Structure:
|
|
25
|
+
* =================
|
|
26
|
+
*
|
|
27
|
+
* hazel new <app> — Full interactive scaffolding (packages, git, install)
|
|
28
|
+
* hazel g app <name> — Minimal skeleton app (no install/git)
|
|
29
|
+
* hazel g <type> <name> — Unified generator for 23+ types (see --list)
|
|
30
|
+
* hazel add <pkg> [--setup] — Install HazelJS packages + optional setup file
|
|
31
|
+
* hazel info — Project diagnostics
|
|
32
|
+
*
|
|
33
|
+
* Design Principles:
|
|
34
|
+
* ===================
|
|
35
|
+
*
|
|
36
|
+
* 1. Config-driven generators: SIMPLE_GENERATORS array defines 18+ single-file generators
|
|
37
|
+
* 2. Centralized package registry: HAZEL_PACKAGES drives `hazel add` and `hazel new -i`
|
|
38
|
+
* 3. Machine-readable output: --json and --list --list-json for LLM tool-use
|
|
39
|
+
* 4. Consistent CLI options: --path, --dry-run, --json available everywhere
|
|
40
|
+
* 5. Separation of concerns: Templates live separately from logic; registry is data-driven
|
|
41
|
+
*/
|
|
2
42
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* HazelJS CLI — Entry point and command registration
|
|
5
|
+
*
|
|
6
|
+
* Architecture Overview:
|
|
7
|
+
* =====================
|
|
8
|
+
*
|
|
9
|
+
* index.ts — Entry point, registers all commands with Commander
|
|
10
|
+
* ├─ commands/
|
|
11
|
+
* │ ├─ generate-app.ts — `hazel new` (full scaffolding) and `hazel g app` (skeleton)
|
|
12
|
+
* │ ├─ add.ts — `hazel add [pkg]` with --setup flag (replaces generate-setup)
|
|
13
|
+
* │ ├─ info.ts — `hazel info` (project diagnostics)
|
|
14
|
+
* │ ├─ generate-simple.ts — Config-driven single-file generators (18 types)
|
|
15
|
+
* │ ├─ generate-module.ts — Multi-file module generator
|
|
16
|
+
* │ ├─ generate-dto.ts — DTO pair generator
|
|
17
|
+
* │ ├─ generate-crud.ts — Full CRUD resource generator
|
|
18
|
+
* │ ├─ generate-auth.ts — Auth module with JWT guard
|
|
19
|
+
* │ └─ templates.ts — Mustache templates for all simple generators
|
|
20
|
+
* └─ utils/
|
|
21
|
+
* ├─ generator.ts — Base Generator class, shared types, string utils
|
|
22
|
+
* ├─ generator-registry.ts — Unified registry + runGenerator dispatcher
|
|
23
|
+
* └─ packages-registry.ts — All HazelJS package metadata (single source of truth)
|
|
24
|
+
*
|
|
25
|
+
* Command Structure:
|
|
26
|
+
* =================
|
|
27
|
+
*
|
|
28
|
+
* hazel new <app> — Full interactive scaffolding (packages, git, install)
|
|
29
|
+
* hazel g app <name> — Minimal skeleton app (no install/git)
|
|
30
|
+
* hazel g <type> <name> — Unified generator for 23+ types (see --list)
|
|
31
|
+
* hazel add <pkg> [--setup] — Install HazelJS packages + optional setup file
|
|
32
|
+
* hazel info — Project diagnostics
|
|
33
|
+
*
|
|
34
|
+
* Design Principles:
|
|
35
|
+
* ===================
|
|
36
|
+
*
|
|
37
|
+
* 1. Config-driven generators: SIMPLE_GENERATORS array defines 18+ single-file generators
|
|
38
|
+
* 2. Centralized package registry: HAZEL_PACKAGES drives `hazel add` and `hazel new -i`
|
|
39
|
+
* 3. Machine-readable output: --json and --list --list-json for LLM tool-use
|
|
40
|
+
* 4. Consistent CLI options: --path, --dry-run, --json available everywhere
|
|
41
|
+
* 5. Separation of concerns: Templates live separately from logic; registry is data-driven
|
|
42
|
+
*/
|
|
3
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
44
|
const commander_1 = require("commander");
|
|
45
|
+
const fs_1 = require("fs");
|
|
46
|
+
const path_1 = require("path");
|
|
5
47
|
const generate_app_1 = require("./commands/generate-app");
|
|
6
48
|
const generate_module_1 = require("./commands/generate-module");
|
|
7
49
|
const generate_dto_1 = require("./commands/generate-dto");
|
|
@@ -11,11 +53,13 @@ const generate_simple_1 = require("./commands/generate-simple");
|
|
|
11
53
|
const generator_registry_1 = require("./utils/generator-registry");
|
|
12
54
|
const info_1 = require("./commands/info");
|
|
13
55
|
const add_1 = require("./commands/add");
|
|
56
|
+
// Read version from package.json to ensure consistency
|
|
57
|
+
const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../package.json'), 'utf8'));
|
|
14
58
|
const program = new commander_1.Command();
|
|
15
59
|
program
|
|
16
60
|
.name('hazel')
|
|
17
61
|
.description('CLI for generating HazelJS components and applications')
|
|
18
|
-
.version(
|
|
62
|
+
.version(packageJson.version);
|
|
19
63
|
// New app command
|
|
20
64
|
(0, generate_app_1.generateApp)(program);
|
|
21
65
|
// Utility commands
|
|
@@ -12,4 +12,9 @@ export declare const GENERATOR_LIST: GeneratorMeta[];
|
|
|
12
12
|
* For types that don't require a name (auth, config), pass a placeholder (e.g. 'auth').
|
|
13
13
|
*/
|
|
14
14
|
export declare function runGenerator(type: string, name: string, options: GenerateCLIOptions): Promise<GenerateResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Get an array of all available generator type names.
|
|
17
|
+
*
|
|
18
|
+
* @returns Array of generator types (e.g. ['controller', 'service', 'module', ...])
|
|
19
|
+
*/
|
|
15
20
|
export declare function getGeneratorTypes(): string[];
|
|
@@ -56,6 +56,11 @@ async function runGenerator(type, name, options) {
|
|
|
56
56
|
error: `Unknown generator type: "${type}". Use "hazel generate --list" to see available types.`,
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Get an array of all available generator type names.
|
|
61
|
+
*
|
|
62
|
+
* @returns Array of generator types (e.g. ['controller', 'service', 'module', ...])
|
|
63
|
+
*/
|
|
59
64
|
function getGeneratorTypes() {
|
|
60
65
|
return exports.GENERATOR_LIST.map((g) => g.type);
|
|
61
66
|
}
|
|
@@ -19,7 +19,16 @@ export interface HazelPackageMeta {
|
|
|
19
19
|
setupTemplate: string | null;
|
|
20
20
|
}
|
|
21
21
|
export declare const HAZEL_PACKAGES: HazelPackageMeta[];
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Look up a HazelJS package by its short CLI name or full npm package name.
|
|
24
|
+
*
|
|
25
|
+
* @param nameOrNpm - Either the short name ('ai', 'auth') or full npm name ('@hazeljs/ai')
|
|
26
|
+
* @returns The matching package metadata, or undefined if not found
|
|
27
|
+
*/
|
|
23
28
|
export declare function findPackage(nameOrNpm: string): HazelPackageMeta | undefined;
|
|
24
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Map of short CLI name → HazelPackageMeta for O(1) lookups.
|
|
31
|
+
*
|
|
32
|
+
* Example: PACKAGES_BY_NAME['ai'] returns the @hazeljs/ai package metadata.
|
|
33
|
+
*/
|
|
25
34
|
export declare const PACKAGES_BY_NAME: Record<string, HazelPackageMeta>;
|
|
@@ -363,9 +363,18 @@ SwaggerModule.setRootModule(AppModule);
|
|
|
363
363
|
setupTemplate: null,
|
|
364
364
|
},
|
|
365
365
|
];
|
|
366
|
-
/**
|
|
366
|
+
/**
|
|
367
|
+
* Look up a HazelJS package by its short CLI name or full npm package name.
|
|
368
|
+
*
|
|
369
|
+
* @param nameOrNpm - Either the short name ('ai', 'auth') or full npm name ('@hazeljs/ai')
|
|
370
|
+
* @returns The matching package metadata, or undefined if not found
|
|
371
|
+
*/
|
|
367
372
|
function findPackage(nameOrNpm) {
|
|
368
373
|
return exports.HAZEL_PACKAGES.find((p) => p.shortName === nameOrNpm || p.npm === nameOrNpm);
|
|
369
374
|
}
|
|
370
|
-
/**
|
|
375
|
+
/**
|
|
376
|
+
* Map of short CLI name → HazelPackageMeta for O(1) lookups.
|
|
377
|
+
*
|
|
378
|
+
* Example: PACKAGES_BY_NAME['ai'] returns the @hazeljs/ai package metadata.
|
|
379
|
+
*/
|
|
371
380
|
exports.PACKAGES_BY_NAME = Object.fromEntries(exports.HAZEL_PACKAGES.map((p) => [p.shortName, p]));
|
package/package.json
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hazeljs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Command-line interface for scaffolding and generating HazelJS applications and components",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"@template",
|
|
10
|
-
"@template-ai-native"
|
|
10
|
+
"@template-ai-native",
|
|
11
|
+
"cli-manifest.json"
|
|
11
12
|
],
|
|
12
13
|
"bin": {
|
|
13
14
|
"hazel": "./dist/index.js"
|
|
14
15
|
},
|
|
15
16
|
"scripts": {
|
|
16
|
-
"build": "tsc",
|
|
17
|
+
"build": "tsc && node scripts/generate-manifest.js",
|
|
18
|
+
"build:types": "tsc",
|
|
17
19
|
"test": "jest",
|
|
18
20
|
"lint": "eslint src --ext .ts",
|
|
19
21
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
@@ -68,5 +70,5 @@
|
|
|
68
70
|
"type": "opencollective",
|
|
69
71
|
"url": "https://opencollective.com/hazeljs"
|
|
70
72
|
},
|
|
71
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "6d79ac53e32ef59de371ba363f4215beaba89371"
|
|
72
74
|
}
|