@hazeljs/cli 0.2.0-beta.1
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/README.md +61 -0
- package/@template/package.json +48 -0
- package/@template/src/app.module.ts +7 -0
- package/@template/src/hello.controller.ts +9 -0
- package/@template/src/index.ts +10 -0
- package/README.md +675 -0
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.js +90 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.js +52 -0
- package/dist/commands/generate-agent.d.ts +2 -0
- package/dist/commands/generate-agent.js +56 -0
- package/dist/commands/generate-ai-service.d.ts +2 -0
- package/dist/commands/generate-ai-service.js +49 -0
- package/dist/commands/generate-app.d.ts +2 -0
- package/dist/commands/generate-app.js +246 -0
- package/dist/commands/generate-controller.d.ts +2 -0
- package/dist/commands/generate-controller.js +59 -0
- package/dist/commands/generate-crud.d.ts +2 -0
- package/dist/commands/generate-crud.js +203 -0
- package/dist/commands/generate-dto.d.ts +2 -0
- package/dist/commands/generate-dto.js +56 -0
- package/dist/commands/generate-exception-filter.d.ts +2 -0
- package/dist/commands/generate-exception-filter.js +50 -0
- package/dist/commands/generate-guard.d.ts +2 -0
- package/dist/commands/generate-guard.js +35 -0
- package/dist/commands/generate-interceptor.d.ts +2 -0
- package/dist/commands/generate-interceptor.js +37 -0
- package/dist/commands/generate-middleware.d.ts +2 -0
- package/dist/commands/generate-middleware.js +79 -0
- package/dist/commands/generate-module.d.ts +2 -0
- package/dist/commands/generate-module.js +96 -0
- package/dist/commands/generate-pipe.d.ts +2 -0
- package/dist/commands/generate-pipe.js +33 -0
- package/dist/commands/generate-repository.d.ts +2 -0
- package/dist/commands/generate-repository.js +38 -0
- package/dist/commands/generate-serverless-handler.d.ts +2 -0
- package/dist/commands/generate-serverless-handler.js +46 -0
- package/dist/commands/generate-service.d.ts +2 -0
- package/dist/commands/generate-service.js +51 -0
- package/dist/commands/generate-websocket-gateway.d.ts +2 -0
- package/dist/commands/generate-websocket-gateway.js +47 -0
- package/dist/commands/info.d.ts +2 -0
- package/dist/commands/info.js +91 -0
- package/dist/commands/start.d.ts +2 -0
- package/dist/commands/start.js +61 -0
- package/dist/commands/test.d.ts +2 -0
- package/dist/commands/test.js +63 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +61 -0
- package/dist/utils/generator.d.ts +14 -0
- package/dist/utils/generator.js +79 -0
- package/package.json +67 -0
- package/src/commands/add.ts +101 -0
- package/src/commands/build.ts +56 -0
- package/src/commands/generate-agent.ts +58 -0
- package/src/commands/generate-ai-service.ts +52 -0
- package/src/commands/generate-app.ts +270 -0
- package/src/commands/generate-controller.ts +61 -0
- package/src/commands/generate-crud.ts +214 -0
- package/src/commands/generate-dto.ts +61 -0
- package/src/commands/generate-exception-filter.ts +53 -0
- package/src/commands/generate-guard.ts +37 -0
- package/src/commands/generate-interceptor.ts +39 -0
- package/src/commands/generate-middleware.ts +86 -0
- package/src/commands/generate-module.ts +102 -0
- package/src/commands/generate-pipe.ts +36 -0
- package/src/commands/generate-repository.ts +41 -0
- package/src/commands/generate-serverless-handler.ts +53 -0
- package/src/commands/generate-service.ts +53 -0
- package/src/commands/generate-websocket-gateway.ts +50 -0
- package/src/commands/info.ts +106 -0
- package/src/commands/start.ts +61 -0
- package/src/commands/test.ts +70 -0
- package/src/index.ts +68 -0
- package/src/utils/generator.ts +93 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const controllerTemplate = `import { Controller, Get, Post, Put, Delete, Body, Param } from '@hazeljs/core';
|
|
7
|
+
import { {{className}}Service } from './{{fileName}}.service';
|
|
8
|
+
import { Create{{className}}Dto, Update{{className}}Dto } from './dto/{{fileName}}.dto';
|
|
9
|
+
|
|
10
|
+
@Controller('/{{routePath}}')
|
|
11
|
+
export class {{className}}Controller {
|
|
12
|
+
constructor(private {{camelName}}Service: {{className}}Service) {}
|
|
13
|
+
|
|
14
|
+
@Get()
|
|
15
|
+
findAll() {
|
|
16
|
+
return this.{{camelName}}Service.findAll();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Get('/:id')
|
|
20
|
+
findOne(@Param('id') id: string) {
|
|
21
|
+
return this.{{camelName}}Service.findOne(id);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Post()
|
|
25
|
+
create(@Body() createDto: Create{{className}}Dto) {
|
|
26
|
+
return this.{{camelName}}Service.create(createDto);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Put('/:id')
|
|
30
|
+
update(@Param('id') id: string, @Body() updateDto: Update{{className}}Dto) {
|
|
31
|
+
return this.{{camelName}}Service.update(id, updateDto);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Delete('/:id')
|
|
35
|
+
delete(@Param('id') id: string) {
|
|
36
|
+
return this.{{camelName}}Service.delete(id);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const serviceTemplate = `import { Injectable } from '@hazeljs/core';
|
|
42
|
+
import { Create{{className}}Dto, Update{{className}}Dto } from './dto/{{fileName}}.dto';
|
|
43
|
+
|
|
44
|
+
@Injectable()
|
|
45
|
+
export class {{className}}Service {
|
|
46
|
+
private {{camelName}}s: any[] = [];
|
|
47
|
+
|
|
48
|
+
findAll() {
|
|
49
|
+
return this.{{camelName}}s;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
findOne(id: string) {
|
|
53
|
+
const {{camelName}} = this.{{camelName}}s.find(item => item.id === id);
|
|
54
|
+
if (!{{camelName}}) {
|
|
55
|
+
throw new Error('{{className}} not found');
|
|
56
|
+
}
|
|
57
|
+
return {{camelName}};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
create(createDto: Create{{className}}Dto) {
|
|
61
|
+
const {{camelName}} = {
|
|
62
|
+
id: Date.now().toString(),
|
|
63
|
+
...createDto,
|
|
64
|
+
createdAt: new Date(),
|
|
65
|
+
updatedAt: new Date(),
|
|
66
|
+
};
|
|
67
|
+
this.{{camelName}}s.push({{camelName}});
|
|
68
|
+
return {{camelName}};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
update(id: string, updateDto: Update{{className}}Dto) {
|
|
72
|
+
const index = this.{{camelName}}s.findIndex(item => item.id === id);
|
|
73
|
+
if (index === -1) {
|
|
74
|
+
throw new Error('{{className}} not found');
|
|
75
|
+
}
|
|
76
|
+
this.{{camelName}}s[index] = {
|
|
77
|
+
...this.{{camelName}}s[index],
|
|
78
|
+
...updateDto,
|
|
79
|
+
updatedAt: new Date(),
|
|
80
|
+
};
|
|
81
|
+
return this.{{camelName}}s[index];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
delete(id: string) {
|
|
85
|
+
const index = this.{{camelName}}s.findIndex(item => item.id === id);
|
|
86
|
+
if (index === -1) {
|
|
87
|
+
throw new Error('{{className}} not found');
|
|
88
|
+
}
|
|
89
|
+
const deleted = this.{{camelName}}s.splice(index, 1);
|
|
90
|
+
return deleted[0];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const dtoTemplate = `import { IsString, IsOptional, IsNotEmpty } from 'class-validator';
|
|
96
|
+
|
|
97
|
+
export class Create{{className}}Dto {
|
|
98
|
+
@IsString()
|
|
99
|
+
@IsNotEmpty()
|
|
100
|
+
name: string;
|
|
101
|
+
|
|
102
|
+
@IsString()
|
|
103
|
+
@IsOptional()
|
|
104
|
+
description?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export class Update{{className}}Dto {
|
|
108
|
+
@IsString()
|
|
109
|
+
@IsOptional()
|
|
110
|
+
name?: string;
|
|
111
|
+
|
|
112
|
+
@IsString()
|
|
113
|
+
@IsOptional()
|
|
114
|
+
description?: string;
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
const moduleTemplate = `import { HazelModule } from '@hazeljs/core';
|
|
119
|
+
import { {{className}}Controller } from './{{fileName}}.controller';
|
|
120
|
+
import { {{className}}Service } from './{{fileName}}.service';
|
|
121
|
+
|
|
122
|
+
@HazelModule({
|
|
123
|
+
controllers: [{{className}}Controller],
|
|
124
|
+
providers: [{{className}}Service],
|
|
125
|
+
})
|
|
126
|
+
export class {{className}}Module {}
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
function toPascalCase(str: string): string {
|
|
130
|
+
return str
|
|
131
|
+
.split(/[-_]/)
|
|
132
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
133
|
+
.join('');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function toKebabCase(str: string): string {
|
|
137
|
+
return str
|
|
138
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
139
|
+
.replace(/[\s_]+/g, '-')
|
|
140
|
+
.toLowerCase();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function toCamelCase(str: string): string {
|
|
144
|
+
const pascal = toPascalCase(str);
|
|
145
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function renderTemplate(template: string, data: Record<string, string>): string {
|
|
149
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] || '');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function generateCrud(command: Command) {
|
|
153
|
+
command
|
|
154
|
+
.command('crud <name>')
|
|
155
|
+
.description('Generate a complete CRUD resource (controller, service, module, DTOs)')
|
|
156
|
+
.option('-p, --path <path>', 'Specify the path', 'src')
|
|
157
|
+
.option('-r, --route <route>', 'Specify the route path')
|
|
158
|
+
.action((name: string, options: { path?: string; route?: string }) => {
|
|
159
|
+
const className = toPascalCase(name);
|
|
160
|
+
const fileName = toKebabCase(name);
|
|
161
|
+
const camelName = toCamelCase(name);
|
|
162
|
+
const routePath = options.route || fileName;
|
|
163
|
+
const basePath = path.join(process.cwd(), options.path || 'src', fileName);
|
|
164
|
+
|
|
165
|
+
const data = {
|
|
166
|
+
className,
|
|
167
|
+
fileName,
|
|
168
|
+
camelName,
|
|
169
|
+
routePath,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
// Create directory
|
|
174
|
+
if (!fs.existsSync(basePath)) {
|
|
175
|
+
fs.mkdirSync(basePath, { recursive: true });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Create DTO directory
|
|
179
|
+
const dtoPath = path.join(basePath, 'dto');
|
|
180
|
+
if (!fs.existsSync(dtoPath)) {
|
|
181
|
+
fs.mkdirSync(dtoPath, { recursive: true });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Generate controller
|
|
185
|
+
const controllerPath = path.join(basePath, `${fileName}.controller.ts`);
|
|
186
|
+
fs.writeFileSync(controllerPath, renderTemplate(controllerTemplate, data));
|
|
187
|
+
console.log(chalk.green(`✓ Generated ${controllerPath}`));
|
|
188
|
+
|
|
189
|
+
// Generate service
|
|
190
|
+
const servicePath = path.join(basePath, `${fileName}.service.ts`);
|
|
191
|
+
fs.writeFileSync(servicePath, renderTemplate(serviceTemplate, data));
|
|
192
|
+
console.log(chalk.green(`✓ Generated ${servicePath}`));
|
|
193
|
+
|
|
194
|
+
// Generate DTOs
|
|
195
|
+
const dtoFilePath = path.join(dtoPath, `${fileName}.dto.ts`);
|
|
196
|
+
fs.writeFileSync(dtoFilePath, renderTemplate(dtoTemplate, data));
|
|
197
|
+
console.log(chalk.green(`✓ Generated ${dtoFilePath}`));
|
|
198
|
+
|
|
199
|
+
// Generate module
|
|
200
|
+
const modulePath = path.join(basePath, `${fileName}.module.ts`);
|
|
201
|
+
fs.writeFileSync(modulePath, renderTemplate(moduleTemplate, data));
|
|
202
|
+
console.log(chalk.green(`✓ Generated ${modulePath}`));
|
|
203
|
+
|
|
204
|
+
console.log(chalk.blue('\n📦 CRUD resource generated successfully!'));
|
|
205
|
+
console.log(chalk.gray(`\nNext steps:`));
|
|
206
|
+
console.log(chalk.gray(`1. Import ${className}Module in your app module`));
|
|
207
|
+
console.log(chalk.gray(`2. Customize the DTOs in ${dtoFilePath}`));
|
|
208
|
+
console.log(chalk.gray(`3. Implement your business logic in ${servicePath}`));
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(chalk.red('Error generating CRUD resource:'), error);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { Generator, GeneratorOptions } from '../utils/generator';
|
|
3
|
+
|
|
4
|
+
const CREATE_DTO_TEMPLATE = `import { IsString, IsOptional } from 'class-validator';
|
|
5
|
+
|
|
6
|
+
export class Create{{className}}Dto {
|
|
7
|
+
@IsString()
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@IsString()
|
|
11
|
+
@IsOptional()
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const UPDATE_DTO_TEMPLATE = `import { PartialType } from '@hazeljs/core';
|
|
17
|
+
import { Create{{className}}Dto } from './create-{{fileName}}.dto';
|
|
18
|
+
|
|
19
|
+
export class Update{{className}}Dto extends PartialType(Create{{className}}Dto) {}
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
class DtoGenerator extends Generator {
|
|
23
|
+
protected getDefaultTemplate(): string {
|
|
24
|
+
return CREATE_DTO_TEMPLATE;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async generate(options: GeneratorOptions): Promise<void> {
|
|
28
|
+
// Generate create DTO
|
|
29
|
+
const createDtoOptions = {
|
|
30
|
+
...options,
|
|
31
|
+
template: CREATE_DTO_TEMPLATE,
|
|
32
|
+
path: options.path ? `${options.path}/dto` : 'src/dto',
|
|
33
|
+
};
|
|
34
|
+
await super.generate(createDtoOptions);
|
|
35
|
+
|
|
36
|
+
// Generate update DTO
|
|
37
|
+
const updateDtoOptions = {
|
|
38
|
+
...options,
|
|
39
|
+
template: UPDATE_DTO_TEMPLATE,
|
|
40
|
+
path: options.path ? `${options.path}/dto` : 'src/dto',
|
|
41
|
+
};
|
|
42
|
+
await super.generate(updateDtoOptions);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function generateDto(program: Command): void {
|
|
47
|
+
program
|
|
48
|
+
.command('dto <name>')
|
|
49
|
+
.description('Generate create and update DTOs')
|
|
50
|
+
.option('-p, --path <path>', 'Path where the DTOs should be generated')
|
|
51
|
+
.action(async (name: string, options: { path?: string }) => {
|
|
52
|
+
const generator = new DtoGenerator();
|
|
53
|
+
const generatorOptions: Partial<GeneratorOptions> = {
|
|
54
|
+
name,
|
|
55
|
+
path: options.path,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const finalOptions = await generator.promptForOptions(generatorOptions);
|
|
59
|
+
await generator.generate(finalOptions);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { Generator, GeneratorOptions } from '../utils/generator';
|
|
3
|
+
|
|
4
|
+
const EXCEPTION_FILTER_TEMPLATE = `import { Catch, ExceptionFilter, ArgumentsHost, HttpError } from '@hazeljs/core';
|
|
5
|
+
import logger from '@hazeljs/core';
|
|
6
|
+
|
|
7
|
+
@Catch(HttpError)
|
|
8
|
+
export class {{className}}ExceptionFilter implements ExceptionFilter<HttpError> {
|
|
9
|
+
catch(exception: HttpError, host: ArgumentsHost): void {
|
|
10
|
+
const ctx = host.switchToHttp();
|
|
11
|
+
const response = ctx.getResponse();
|
|
12
|
+
const request = ctx.getRequest();
|
|
13
|
+
|
|
14
|
+
const status = exception.statusCode || 500;
|
|
15
|
+
const message = exception.message || 'Internal server error';
|
|
16
|
+
|
|
17
|
+
logger.error(\`[\${request.method}] \${request.url} - \${message} (\${status})\`);
|
|
18
|
+
|
|
19
|
+
response.status(status).json({
|
|
20
|
+
statusCode: status,
|
|
21
|
+
message,
|
|
22
|
+
timestamp: new Date().toISOString(),
|
|
23
|
+
path: request.url,
|
|
24
|
+
...(exception.errors && { errors: exception.errors }),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
class ExceptionFilterGenerator extends Generator {
|
|
31
|
+
protected getDefaultTemplate(): string {
|
|
32
|
+
return EXCEPTION_FILTER_TEMPLATE;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function generateExceptionFilter(program: Command): void {
|
|
37
|
+
program
|
|
38
|
+
.command('filter <name>')
|
|
39
|
+
.description('Generate a new exception filter')
|
|
40
|
+
.alias('f')
|
|
41
|
+
.option('-p, --path <path>', 'Path where the filter should be generated')
|
|
42
|
+
.action(async (name: string, options: { path?: string }) => {
|
|
43
|
+
const generator = new ExceptionFilterGenerator();
|
|
44
|
+
const generatorOptions: Partial<GeneratorOptions> = {
|
|
45
|
+
name,
|
|
46
|
+
path: options.path,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const finalOptions = await generator.promptForOptions(generatorOptions);
|
|
50
|
+
await generator.generate(finalOptions);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { Generator, GeneratorOptions } from '../utils/generator';
|
|
3
|
+
|
|
4
|
+
const GUARD_TEMPLATE = `import { Injectable, CanActivate, ExecutionContext } from '@hazeljs/core';
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class {{className}}Guard implements CanActivate {
|
|
8
|
+
canActivate(context: ExecutionContext): boolean {
|
|
9
|
+
const request = context.switchToHttp().getRequest();
|
|
10
|
+
// Add your guard logic here
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
class GuardGenerator extends Generator {
|
|
17
|
+
protected getDefaultTemplate(): string {
|
|
18
|
+
return GUARD_TEMPLATE;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function generateGuard(program: Command): void {
|
|
23
|
+
program
|
|
24
|
+
.command('guard <name>')
|
|
25
|
+
.description('Generate a new guard')
|
|
26
|
+
.option('-p, --path <path>', 'Path where the guard should be generated')
|
|
27
|
+
.action(async (name: string, options: { path?: string }) => {
|
|
28
|
+
const generator = new GuardGenerator();
|
|
29
|
+
const generatorOptions: Partial<GeneratorOptions> = {
|
|
30
|
+
name,
|
|
31
|
+
path: options.path,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const finalOptions = await generator.promptForOptions(generatorOptions);
|
|
35
|
+
await generator.generate(finalOptions);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { Generator, GeneratorOptions } from '../utils/generator';
|
|
3
|
+
|
|
4
|
+
const INTERCEPTOR_TEMPLATE = `import { Injectable, Interceptor, ExecutionContext } from '@hazeljs/core';
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class {{className}}Interceptor implements Interceptor {
|
|
8
|
+
async intercept(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown> {
|
|
9
|
+
// Pre-processing logic here (before handler execution)
|
|
10
|
+
const result = await next();
|
|
11
|
+
// Post-processing logic here (after handler execution)
|
|
12
|
+
// Transform the response data here
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
class InterceptorGenerator extends Generator {
|
|
19
|
+
protected getDefaultTemplate(): string {
|
|
20
|
+
return INTERCEPTOR_TEMPLATE;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function generateInterceptor(program: Command): void {
|
|
25
|
+
program
|
|
26
|
+
.command('interceptor <name>')
|
|
27
|
+
.description('Generate a new interceptor')
|
|
28
|
+
.option('-p, --path <path>', 'Path where the interceptor should be generated')
|
|
29
|
+
.action(async (name: string, options: { path?: string }) => {
|
|
30
|
+
const generator = new InterceptorGenerator();
|
|
31
|
+
const generatorOptions: Partial<GeneratorOptions> = {
|
|
32
|
+
name,
|
|
33
|
+
path: options.path,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const finalOptions = await generator.promptForOptions(generatorOptions);
|
|
37
|
+
await generator.generate(finalOptions);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const middlewareTemplate = `import { Request, Response, NextFunction } from 'express';
|
|
7
|
+
import { Injectable } from '@hazeljs/core';
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class {{className}}Middleware {
|
|
11
|
+
use(req: Request, res: Response, next: NextFunction) {
|
|
12
|
+
// Add your middleware logic here
|
|
13
|
+
console.log(\`[{{className}}Middleware] \${req.method} \${req.path}\`);
|
|
14
|
+
|
|
15
|
+
// Example: Add custom header
|
|
16
|
+
res.setHeader('X-{{className}}', 'true');
|
|
17
|
+
|
|
18
|
+
// Continue to next middleware
|
|
19
|
+
next();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
function toPascalCase(str: string): string {
|
|
25
|
+
return str
|
|
26
|
+
.split(/[-_]/)
|
|
27
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
28
|
+
.join('');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function toKebabCase(str: string): string {
|
|
32
|
+
return str
|
|
33
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
34
|
+
.replace(/[\s_]+/g, '-')
|
|
35
|
+
.toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderTemplate(template: string, data: Record<string, string>): string {
|
|
39
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] || '');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function generateMiddleware(command: Command) {
|
|
43
|
+
command
|
|
44
|
+
.command('middleware <name>')
|
|
45
|
+
.alias('mw')
|
|
46
|
+
.description('Generate a middleware')
|
|
47
|
+
.option('-p, --path <path>', 'Specify the path', 'src/middleware')
|
|
48
|
+
.action((name: string, options: { path?: string }) => {
|
|
49
|
+
const className = toPascalCase(name);
|
|
50
|
+
const fileName = toKebabCase(name);
|
|
51
|
+
const filePath = path.join(
|
|
52
|
+
process.cwd(),
|
|
53
|
+
options.path || 'src/middleware',
|
|
54
|
+
`${fileName}.middleware.ts`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const data = {
|
|
58
|
+
className,
|
|
59
|
+
fileName,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Create directory if it doesn't exist
|
|
64
|
+
const dir = path.dirname(filePath);
|
|
65
|
+
if (!fs.existsSync(dir)) {
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Generate middleware
|
|
70
|
+
fs.writeFileSync(filePath, renderTemplate(middlewareTemplate, data));
|
|
71
|
+
console.log(chalk.green(`✓ Generated ${filePath}`));
|
|
72
|
+
|
|
73
|
+
console.log(chalk.gray('\nUsage:'));
|
|
74
|
+
console.log(chalk.gray(`import { ${className}Middleware } from './${options.path?.replace('src/', '') || 'middleware'}/${fileName}.middleware';`));
|
|
75
|
+
console.log(chalk.gray(`\n// In your module:`));
|
|
76
|
+
console.log(chalk.gray(`@HazelModule({`));
|
|
77
|
+
console.log(chalk.gray(` providers: [${className}Middleware],`));
|
|
78
|
+
console.log(chalk.gray(`})`));
|
|
79
|
+
console.log(chalk.gray(`\n// Or apply globally in main.ts:`));
|
|
80
|
+
console.log(chalk.gray(`app.use(new ${className}Middleware().use);`));
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error(chalk.red('Error generating middleware:'), error);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { Generator, GeneratorOptions } from '../utils/generator';
|
|
5
|
+
|
|
6
|
+
const MODULE_TEMPLATE = `import { HazelModule } from '@hazeljs/core';
|
|
7
|
+
import { {{className}}Controller } from './{{fileName}}.controller';
|
|
8
|
+
import { {{className}}Service } from './{{fileName}}.service';
|
|
9
|
+
|
|
10
|
+
@HazelModule({
|
|
11
|
+
controllers: [{{className}}Controller],
|
|
12
|
+
providers: [{{className}}Service],
|
|
13
|
+
})
|
|
14
|
+
export class {{className}}Module {}
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const CONTROLLER_TEMPLATE = `import { Controller, Get, Post, Body } from '../../core/decorators';
|
|
18
|
+
import { {{className}}Service } from './{{fileName}}.service';
|
|
19
|
+
import { Create{{className}}Dto } from './dto/create-{{fileName}}.dto';
|
|
20
|
+
|
|
21
|
+
@Controller('/{{fileName}}')
|
|
22
|
+
export class {{className}}Controller {
|
|
23
|
+
constructor(private readonly {{fileName}}Service: {{className}}Service) {}
|
|
24
|
+
|
|
25
|
+
@Get()
|
|
26
|
+
findAll() {
|
|
27
|
+
return this.{{fileName}}Service.findAll();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Post()
|
|
31
|
+
create(@Body() createDto: Create{{className}}Dto) {
|
|
32
|
+
return this.{{fileName}}Service.create(createDto);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const SERVICE_TEMPLATE = `import { Injectable } from '../../core/decorators';
|
|
38
|
+
import { Create{{className}}Dto } from './dto/create-{{fileName}}.dto';
|
|
39
|
+
|
|
40
|
+
@Injectable()
|
|
41
|
+
export class {{className}}Service {
|
|
42
|
+
private items = [];
|
|
43
|
+
|
|
44
|
+
findAll() {
|
|
45
|
+
return this.items;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
create(dto: Create{{className}}Dto) {
|
|
49
|
+
this.items.push(dto);
|
|
50
|
+
return dto;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
const CREATE_DTO_TEMPLATE = `export class Create{{className}}Dto {
|
|
56
|
+
// Add your properties here
|
|
57
|
+
name: string;
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const UPDATE_DTO_TEMPLATE = `export class Update{{className}}Dto {
|
|
62
|
+
// Add your properties here
|
|
63
|
+
name?: string;
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
class FullModuleGenerator extends Generator {
|
|
68
|
+
async generate(options: GeneratorOptions): Promise<void> {
|
|
69
|
+
const { name, path: customPath } = options;
|
|
70
|
+
const className = this.toPascalCase(name);
|
|
71
|
+
const fileName = this.toKebabCase(name);
|
|
72
|
+
const baseDir = path.join(process.cwd(), customPath || 'src', fileName);
|
|
73
|
+
|
|
74
|
+
// Create module directory and dto subdirectory
|
|
75
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
76
|
+
fs.mkdirSync(path.join(baseDir, 'dto'), { recursive: true });
|
|
77
|
+
|
|
78
|
+
// Generate files
|
|
79
|
+
fs.writeFileSync(path.join(baseDir, `${fileName}.module.ts`), this.render(MODULE_TEMPLATE, { className, fileName }));
|
|
80
|
+
fs.writeFileSync(path.join(baseDir, `${fileName}.controller.ts`), this.render(CONTROLLER_TEMPLATE, { className, fileName }));
|
|
81
|
+
fs.writeFileSync(path.join(baseDir, `${fileName}.service.ts`), this.render(SERVICE_TEMPLATE, { className, fileName }));
|
|
82
|
+
fs.writeFileSync(path.join(baseDir, 'dto', `create-${fileName}.dto.ts`), this.render(CREATE_DTO_TEMPLATE, { className, fileName }));
|
|
83
|
+
fs.writeFileSync(path.join(baseDir, 'dto', `update-${fileName}.dto.ts`), this.render(UPDATE_DTO_TEMPLATE, { className, fileName }));
|
|
84
|
+
|
|
85
|
+
console.log(`✓ Generated module in ${baseDir}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
render(template: string, data: Record<string, string>) {
|
|
89
|
+
return template.replace(/{{(\w+)}}/g, (_, key) => data[key] || '');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function generateModule(program: Command): void {
|
|
94
|
+
program
|
|
95
|
+
.command('module <name>')
|
|
96
|
+
.description('Generate a new module (with controller, service, DTOs)')
|
|
97
|
+
.option('-p, --path <path>', 'Path where the module should be generated')
|
|
98
|
+
.action(async (name: string, options: { path?: string }) => {
|
|
99
|
+
const generator = new FullModuleGenerator();
|
|
100
|
+
await generator.generate({ name, path: options.path });
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { Generator, GeneratorOptions } from '../utils/generator';
|
|
3
|
+
|
|
4
|
+
const PIPE_TEMPLATE = `import { PipeTransform, RequestContext } from '@hazeljs/core';
|
|
5
|
+
|
|
6
|
+
export class {{className}}Pipe implements PipeTransform {
|
|
7
|
+
transform(value: unknown, context: RequestContext): unknown {
|
|
8
|
+
// Transform logic here
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
class PipeGenerator extends Generator {
|
|
15
|
+
protected getDefaultTemplate(): string {
|
|
16
|
+
return PIPE_TEMPLATE;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function generatePipe(program: Command): void {
|
|
21
|
+
program
|
|
22
|
+
.command('pipe <name>')
|
|
23
|
+
.description('Generate a new pipe')
|
|
24
|
+
.option('-p, --path <path>', 'Path where the pipe should be generated')
|
|
25
|
+
.action(async (name: string, options: { path?: string }) => {
|
|
26
|
+
const generator = new PipeGenerator();
|
|
27
|
+
const generatorOptions: Partial<GeneratorOptions> = {
|
|
28
|
+
name,
|
|
29
|
+
path: options.path,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const finalOptions = await generator.promptForOptions(generatorOptions);
|
|
33
|
+
await generator.generate(finalOptions);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { Generator, GeneratorOptions } from '../utils/generator';
|
|
3
|
+
|
|
4
|
+
const REPOSITORY_TEMPLATE = `import { Injectable } from '@hazeljs/core';
|
|
5
|
+
import { BaseRepository, PrismaService, PrismaModel } from '@hazeljs/prisma';
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class {{className}}Repository extends BaseRepository<PrismaModel> {
|
|
9
|
+
constructor(prisma: PrismaService) {
|
|
10
|
+
// Replace 'modelName' with your actual Prisma model name
|
|
11
|
+
super(prisma.modelName as unknown as { findMany: () => Promise<PrismaModel[]>; findUnique: (args: { where: { id?: number } }) => Promise<PrismaModel | null>; create: (args: { data: unknown }) => Promise<PrismaModel>; update: (args: { where: { id?: number }; data: unknown }) => Promise<PrismaModel>; delete: (args: { where: { id?: number } }) => Promise<PrismaModel>; count: (args?: unknown) => Promise<number> });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Add custom repository methods here
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
class RepositoryGenerator extends Generator {
|
|
19
|
+
protected getDefaultTemplate(): string {
|
|
20
|
+
return REPOSITORY_TEMPLATE;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function generateRepository(program: Command): void {
|
|
25
|
+
program
|
|
26
|
+
.command('repository <name>')
|
|
27
|
+
.description('Generate a new Prisma repository')
|
|
28
|
+
.alias('repo')
|
|
29
|
+
.option('-p, --path <path>', 'Path where the repository should be generated')
|
|
30
|
+
.action(async (name: string, options: { path?: string }) => {
|
|
31
|
+
const generator = new RepositoryGenerator();
|
|
32
|
+
const generatorOptions: Partial<GeneratorOptions> = {
|
|
33
|
+
name,
|
|
34
|
+
path: options.path,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const finalOptions = await generator.promptForOptions(generatorOptions);
|
|
38
|
+
await generator.generate(finalOptions);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|