speedrun-cli 2.6.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/CHANGELOG.md +96 -0
- package/LICENSE +21 -0
- package/README.md +620 -0
- package/bin/cli.js +224 -0
- package/index.js +12 -0
- package/package.json +74 -0
- package/src/constants.js +65 -0
- package/src/generator.js +271 -0
- package/src/index.js +13 -0
- package/src/moduleGenerator.js +586 -0
- package/src/postSetup.js +365 -0
- package/src/prompts.js +189 -0
- package/src/utils.js +112 -0
- package/templates/README.md +81 -0
- package/templates/base/eslint.config.mjs +34 -0
- package/templates/base/gitignore +78 -0
- package/templates/base/nest-cli.json +8 -0
- package/templates/base/src/common/constants/cookie.config.ts +20 -0
- package/templates/base/src/common/decorators/get-user.decorator.ts +13 -0
- package/templates/base/src/common/decorators/public.decorator.ts +4 -0
- package/templates/base/src/common/decorators/roles.decorator.ts +4 -0
- package/templates/base/src/common/dtos/pagination.dto.ts +29 -0
- package/templates/base/src/common/filters/http-exception.filter.ts +108 -0
- package/templates/base/src/common/guards/auth.guard.ts +51 -0
- package/templates/base/src/common/guards/refresh-token.guard.ts +39 -0
- package/templates/base/src/common/guards/roles.guard.ts +45 -0
- package/templates/base/src/common/interceptors/response.interceptor.ts +55 -0
- package/templates/base/src/common/interfaces/api-response.interface.ts +26 -0
- package/templates/base/src/common/middleware/correlation-id.middleware.ts +20 -0
- package/templates/base/src/common/validators/password.validator.ts +37 -0
- package/templates/base/src/config/config.module.ts +14 -0
- package/templates/base/src/config/logger.config.ts +109 -0
- package/templates/base/src/main.ts +84 -0
- package/templates/base/src/modules/auth/auth.controller.ts +133 -0
- package/templates/base/src/modules/auth/dtos/login.dto.ts +11 -0
- package/templates/base/src/modules/auth/dtos/signup.dto.ts +8 -0
- package/templates/base/src/modules/health/health.controller.ts +20 -0
- package/templates/base/src/modules/health/health.module.ts +7 -0
- package/templates/base/src/modules/users/dtos/update-profile.dto.ts +12 -0
- package/templates/base/src/modules/users/dtos/update-user.dto.ts +13 -0
- package/templates/base/src/modules/users/users.controller.ts +65 -0
- package/templates/base/test/app.e2e-spec.ts +24 -0
- package/templates/base/test/jest-e2e.json +9 -0
- package/templates/base/tsconfig.build.json +4 -0
- package/templates/base/tsconfig.json +24 -0
- package/templates/base-crud/CRUD_README.md +385 -0
- package/templates/base-crud/src/common/base/base.controller.ts +321 -0
- package/templates/base-crud/src/common/base/base.service.ts +192 -0
- package/templates/base-crud/src/common/base/index.ts +20 -0
- package/templates/base-crud/src/common/base/swagger/api-response.dto.ts +82 -0
- package/templates/base-crud/src/common/base/swagger/paginated.dto.ts +102 -0
- package/templates/base-crud/src/modules/products/dto/create-product.dto.ts +44 -0
- package/templates/base-crud/src/modules/products/dto/product.dto.ts +38 -0
- package/templates/base-crud/src/modules/products/dto/update-product.dto.ts +12 -0
- package/templates/base-crud/src/modules/products/products.controller.ts +94 -0
- package/templates/base-crud-drizzle/src/modules/products/products.controller.ts +79 -0
- package/templates/base-crud-drizzle/src/modules/products/products.module.ts +24 -0
- package/templates/base-crud-drizzle/src/modules/products/products.service.ts +140 -0
- package/templates/base-crud-drizzle/src/modules/products/schema/products.schema.ts +39 -0
- package/templates/base-crud-mongoose/src/modules/products/products.controller.ts +79 -0
- package/templates/base-crud-mongoose/src/modules/products/products.module.ts +26 -0
- package/templates/base-crud-mongoose/src/modules/products/products.service.ts +133 -0
- package/templates/base-crud-mongoose/src/modules/products/schemas/product.schema.ts +67 -0
- package/templates/base-crud-prisma/src/modules/products/products.module.ts +12 -0
- package/templates/base-crud-prisma/src/modules/products/products.service.ts +100 -0
- package/templates/base-crud-typeorm/src/modules/products/entities/product.entity.ts +58 -0
- package/templates/base-crud-typeorm/src/modules/products/products.controller.ts +79 -0
- package/templates/base-crud-typeorm/src/modules/products/products.module.ts +25 -0
- package/templates/base-crud-typeorm/src/modules/products/products.service.ts +102 -0
- package/templates/database/mongodb/.env.example +22 -0
- package/templates/database/mysql/.env.example +24 -0
- package/templates/database/mysql/drizzle.config.ts +13 -0
- package/templates/database/mysql/package.json +5 -0
- package/templates/database/mysql/prisma/schema.prisma +55 -0
- package/templates/database/mysql/src/database/drizzle.ts +13 -0
- package/templates/database/mysql/src/database/schema.ts +58 -0
- package/templates/database/postgres/.env.example +24 -0
- package/templates/database/postgres/drizzle.config.ts +13 -0
- package/templates/database/postgres/package.json +8 -0
- package/templates/database/postgres/prisma/schema.prisma +55 -0
- package/templates/database/sqlite/.env.example +24 -0
- package/templates/database/sqlite/drizzle.config.ts +13 -0
- package/templates/database/sqlite/package.json +8 -0
- package/templates/database/sqlite/prisma/schema.prisma +48 -0
- package/templates/database/sqlite/src/database/drizzle.ts +11 -0
- package/templates/database/sqlite/src/database/schema.ts +52 -0
- package/templates/orm/drizzle/drizzle.config.ts +13 -0
- package/templates/orm/drizzle/package.json +90 -0
- package/templates/orm/drizzle/src/app.module.ts +73 -0
- package/templates/orm/drizzle/src/config/env.validation.ts +55 -0
- package/templates/orm/drizzle/src/database/database.module.ts +25 -0
- package/templates/orm/drizzle/src/database/drizzle.ts +13 -0
- package/templates/orm/drizzle/src/database/schema.ts +60 -0
- package/templates/orm/drizzle/src/database/seed.ts +87 -0
- package/templates/orm/drizzle/src/modules/auth/auth.module.ts +12 -0
- package/templates/orm/drizzle/src/modules/auth/auth.service.ts +298 -0
- package/templates/orm/drizzle/src/modules/health/health.controller.ts +34 -0
- package/templates/orm/drizzle/src/modules/health/health.module.ts +7 -0
- package/templates/orm/drizzle/src/modules/users/users.module.ts +10 -0
- package/templates/orm/drizzle/src/modules/users/users.service.ts +152 -0
- package/templates/orm/mongoose/.env.example +22 -0
- package/templates/orm/mongoose/package.json +86 -0
- package/templates/orm/mongoose/src/app.module.ts +73 -0
- package/templates/orm/mongoose/src/config/env.validation.ts +55 -0
- package/templates/orm/mongoose/src/database/database.module.ts +19 -0
- package/templates/orm/mongoose/src/database/seed.ts +107 -0
- package/templates/orm/mongoose/src/modules/auth/auth.module.ts +21 -0
- package/templates/orm/mongoose/src/modules/auth/auth.service.ts +272 -0
- package/templates/orm/mongoose/src/modules/auth/dtos/login.dto.ts +10 -0
- package/templates/orm/mongoose/src/modules/auth/dtos/signup.dto.ts +8 -0
- package/templates/orm/mongoose/src/modules/health/health.controller.ts +26 -0
- package/templates/orm/mongoose/src/modules/health/health.module.ts +7 -0
- package/templates/orm/mongoose/src/modules/users/dtos/update-profile.dto.ts +24 -0
- package/templates/orm/mongoose/src/modules/users/dtos/update-user.dto.ts +13 -0
- package/templates/orm/mongoose/src/modules/users/users.module.ts +19 -0
- package/templates/orm/mongoose/src/modules/users/users.service.ts +179 -0
- package/templates/orm/mongoose/src/schemas/index.ts +2 -0
- package/templates/orm/mongoose/src/schemas/refresh-token.schema.ts +55 -0
- package/templates/orm/mongoose/src/schemas/user.schema.ts +53 -0
- package/templates/orm/prisma/package.json +102 -0
- package/templates/orm/prisma/prisma/schema.prisma +60 -0
- package/templates/orm/prisma/prisma/seed.ts +69 -0
- package/templates/orm/prisma/src/app.module.ts +55 -0
- package/templates/orm/prisma/src/config/env.validation.ts +56 -0
- package/templates/orm/prisma/src/modules/auth/auth.module.ts +17 -0
- package/templates/orm/prisma/src/modules/auth/auth.service.ts +308 -0
- package/templates/orm/prisma/src/modules/health/health.controller.ts +26 -0
- package/templates/orm/prisma/src/modules/health/health.module.ts +10 -0
- package/templates/orm/prisma/src/modules/users/users.module.ts +11 -0
- package/templates/orm/prisma/src/modules/users/users.service.ts +105 -0
- package/templates/orm/prisma/src/prisma/prisma.module.ts +9 -0
- package/templates/orm/prisma/src/prisma/prisma.service.ts +16 -0
- package/templates/orm/typeorm/package.json +100 -0
- package/templates/orm/typeorm/src/app.module.ts +55 -0
- package/templates/orm/typeorm/src/config/env.validation.ts +58 -0
- package/templates/orm/typeorm/src/database/data-source.ts +19 -0
- package/templates/orm/typeorm/src/database/database.module.ts +27 -0
- package/templates/orm/typeorm/src/database/seed.ts +76 -0
- package/templates/orm/typeorm/src/entities/index.ts +2 -0
- package/templates/orm/typeorm/src/entities/refresh-token.entity.ts +45 -0
- package/templates/orm/typeorm/src/entities/user.entity.ts +51 -0
- package/templates/orm/typeorm/src/modules/auth/auth.module.ts +19 -0
- package/templates/orm/typeorm/src/modules/auth/auth.service.ts +286 -0
- package/templates/orm/typeorm/src/modules/health/health.controller.ts +24 -0
- package/templates/orm/typeorm/src/modules/health/health.module.ts +9 -0
- package/templates/orm/typeorm/src/modules/users/users.module.ts +13 -0
- package/templates/orm/typeorm/src/modules/users/users.service.ts +108 -0
- package/templates/swagger/src/common/dtos/pagination.dto.ts +43 -0
- package/templates/swagger/src/main.ts +116 -0
- package/templates/swagger/src/modules/auth/auth.controller.ts +235 -0
- package/templates/swagger/src/modules/auth/dtos/login.dto.ts +20 -0
- package/templates/swagger/src/modules/auth/dtos/signup.dto.ts +14 -0
- package/templates/swagger/src/modules/users/dtos/update-profile.dto.ts +19 -0
- package/templates/swagger/src/modules/users/dtos/update-user.dto.ts +23 -0
- package/templates/swagger/src/modules/users/users.controller.ts +214 -0
- package/templates/swagger-mongoose/src/modules/auth/dtos/login.dto.ts +20 -0
- package/templates/swagger-mongoose/src/modules/auth/dtos/signup.dto.ts +14 -0
- package/templates/swagger-mongoose/src/modules/users/dtos/update-profile.dto.ts +40 -0
- package/templates/swagger-mongoose/src/modules/users/dtos/update-user.dto.ts +23 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Generator for create-nestjs-auth
|
|
3
|
+
* @module moduleGenerator
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
|
|
11
|
+
function toPascalCase(str) {
|
|
12
|
+
return str
|
|
13
|
+
.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
|
|
14
|
+
.map(x => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase())
|
|
15
|
+
.join('');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function toCamelCase(str) {
|
|
19
|
+
const pascal = toPascalCase(str);
|
|
20
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function toKebabCase(str) {
|
|
24
|
+
return str
|
|
25
|
+
.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
|
|
26
|
+
.map(x => x.toLowerCase())
|
|
27
|
+
.join('-');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function detectOrm(targetDir) {
|
|
31
|
+
try {
|
|
32
|
+
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
33
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
34
|
+
const packageJson = await fs.readJSON(packageJsonPath);
|
|
35
|
+
const deps = { ...(packageJson.dependencies || {}), ...(packageJson.devDependencies || {}) };
|
|
36
|
+
|
|
37
|
+
if (deps['prisma']) return 'prisma';
|
|
38
|
+
if (deps['typeorm']) return 'typeorm';
|
|
39
|
+
if (deps['mongoose']) return 'mongoose';
|
|
40
|
+
if (deps['drizzle-orm']) return 'drizzle';
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// Ignore error and fallback to default
|
|
44
|
+
}
|
|
45
|
+
return 'prisma';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function promptForModuleOptions(moduleName) {
|
|
49
|
+
const questions = [];
|
|
50
|
+
|
|
51
|
+
if (!moduleName) {
|
|
52
|
+
questions.push({
|
|
53
|
+
type: 'input',
|
|
54
|
+
name: 'moduleName',
|
|
55
|
+
message: 'What module do you want to generate? (e.g., orders, products)',
|
|
56
|
+
validate: (input) => input.trim() ? true : 'Module name is required',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
questions.push({
|
|
61
|
+
type: 'list',
|
|
62
|
+
name: 'crudMode',
|
|
63
|
+
message: 'Which CRUD mode do you want to use?',
|
|
64
|
+
choices: [
|
|
65
|
+
{ name: 'Full CRUD (Create, Read All, Read One, Update, Delete)', value: 'full' },
|
|
66
|
+
{ name: 'Custom Selection...', value: 'custom' }
|
|
67
|
+
],
|
|
68
|
+
default: 'full'
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const answers = await inquirer.prompt(questions);
|
|
72
|
+
|
|
73
|
+
let selectedOperations = ['create', 'findAll', 'findOne', 'update', 'remove'];
|
|
74
|
+
|
|
75
|
+
if (answers.crudMode === 'custom') {
|
|
76
|
+
const { customOps } = await inquirer.prompt([{
|
|
77
|
+
type: 'checkbox',
|
|
78
|
+
name: 'customOps',
|
|
79
|
+
message: 'Select the operations you want to generate:',
|
|
80
|
+
choices: [
|
|
81
|
+
{ name: 'Create (POST)', value: 'create', checked: true },
|
|
82
|
+
{ name: 'Read All / findAll (GET)', value: 'findAll', checked: true },
|
|
83
|
+
{ name: 'Read One / findOne (GET /:id)', value: 'findOne', checked: false },
|
|
84
|
+
{ name: 'Update (PUT /:id)', value: 'update', checked: true },
|
|
85
|
+
{ name: 'Delete / remove (DELETE /:id)', value: 'remove', checked: false }
|
|
86
|
+
]
|
|
87
|
+
}]);
|
|
88
|
+
selectedOperations = customOps;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
moduleName: moduleName || answers.moduleName,
|
|
93
|
+
operations: selectedOperations
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getServiceContent(orm, pascalName, camelName, kebabName, createDtoName, updateDtoName, ops) {
|
|
98
|
+
if (orm === 'typeorm') {
|
|
99
|
+
return `import { Injectable, NotFoundException } from '@nestjs/common';
|
|
100
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
101
|
+
import { Repository, IsNull } from 'typeorm';
|
|
102
|
+
import { BaseService, IBaseRepository, PaginationQueryDto } from '../../common/base';
|
|
103
|
+
${ops.create || ops.update ? `import { ${createDtoName} } from './dto/create-${kebabName}.dto';\nimport { ${updateDtoName} } from './dto/update-${kebabName}.dto';` : ''}
|
|
104
|
+
|
|
105
|
+
// Placeholder for TypeORM Entity. Ensure you create entities/${kebabName}.entity.ts
|
|
106
|
+
type ${pascalName}Entity = any;
|
|
107
|
+
${!ops.create && !ops.update ? `\ntype ${createDtoName} = any;\ntype ${updateDtoName} = any;` : ''}
|
|
108
|
+
|
|
109
|
+
class TypeOrm${pascalName}Repository implements IBaseRepository<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
110
|
+
constructor(private readonly repo: Repository<${pascalName}Entity>) {}
|
|
111
|
+
|
|
112
|
+
${ops.create ? `async create(dto: ${createDtoName}): Promise<${pascalName}Entity> {
|
|
113
|
+
const entity = this.repo.create(dto);
|
|
114
|
+
return this.repo.save(entity);
|
|
115
|
+
}` : `async create(dto: ${createDtoName}): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
116
|
+
|
|
117
|
+
${ops.findAll ? `async findAll(pagination: PaginationQueryDto): Promise<{ data: ${pascalName}Entity[]; total: number }> {
|
|
118
|
+
const { page = 1, limit = 10 } = pagination;
|
|
119
|
+
const skip = (page - 1) * limit;
|
|
120
|
+
const [data, total] = await this.repo.findAndCount({ where: { deletedAt: IsNull() }, skip, take: limit, order: { createdAt: 'DESC' } as any });
|
|
121
|
+
return { data, total };
|
|
122
|
+
}` : `async findAll(p: PaginationQueryDto): Promise<{ data: ${pascalName}Entity[]; total: number }> { throw new Error('Not implemented'); }`}
|
|
123
|
+
|
|
124
|
+
${ops.findOne ? `async findOne(id: string): Promise<${pascalName}Entity | null> {
|
|
125
|
+
return this.repo.findOne({ where: { id, deletedAt: IsNull() } as any });
|
|
126
|
+
}` : `async findOne(id: string): Promise<${pascalName}Entity | null> { throw new Error('Not implemented'); }`}
|
|
127
|
+
|
|
128
|
+
${ops.update ? `async update(id: string, dto: ${updateDtoName}): Promise<${pascalName}Entity> {
|
|
129
|
+
await this.repo.update(id, dto as any);
|
|
130
|
+
return this.repo.findOneOrFail({ where: { id } as any });
|
|
131
|
+
}` : `async update(id: string, dto: ${updateDtoName}): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
132
|
+
|
|
133
|
+
${ops.remove ? `async remove(id: string): Promise<${pascalName}Entity> {
|
|
134
|
+
const entity = await this.repo.findOneOrFail({ where: { id } as any });
|
|
135
|
+
entity.deletedAt = new Date();
|
|
136
|
+
return this.repo.save(entity);
|
|
137
|
+
}` : `async remove(id: string): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Injectable()
|
|
141
|
+
export class ${pascalName}Service extends BaseService<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
142
|
+
private readonly repository: TypeOrm${pascalName}Repository;
|
|
143
|
+
|
|
144
|
+
constructor(@InjectRepository(Object) private readonly repo: Repository<${pascalName}Entity>) {
|
|
145
|
+
super();
|
|
146
|
+
this.repository = new TypeOrm${pascalName}Repository(this.repo);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected getRepository(): IBaseRepository<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
150
|
+
return this.repository;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
} else if (orm === 'mongoose') {
|
|
155
|
+
return `import { Injectable, NotFoundException } from '@nestjs/common';
|
|
156
|
+
import { InjectModel } from '@nestjs/mongoose';
|
|
157
|
+
import { Model } from 'mongoose';
|
|
158
|
+
import { BaseService, IBaseRepository, PaginationQueryDto } from '../../common/base';
|
|
159
|
+
${ops.create || ops.update ? `import { ${createDtoName} } from './dto/create-${kebabName}.dto';\nimport { ${updateDtoName} } from './dto/update-${kebabName}.dto';` : ''}
|
|
160
|
+
|
|
161
|
+
// Placeholder for Mongoose Document. Ensure you create schemas/${kebabName}.schema.ts
|
|
162
|
+
type ${pascalName}Document = any;
|
|
163
|
+
${!ops.create && !ops.update ? `\ntype ${createDtoName} = any;\ntype ${updateDtoName} = any;` : ''}
|
|
164
|
+
|
|
165
|
+
class Mongoose${pascalName}Repository implements IBaseRepository<${pascalName}Document, ${createDtoName}, ${updateDtoName}> {
|
|
166
|
+
constructor(private readonly model: Model<${pascalName}Document>) {}
|
|
167
|
+
|
|
168
|
+
${ops.create ? `async create(dto: ${createDtoName}): Promise<${pascalName}Document> {
|
|
169
|
+
const created = new this.model(dto);
|
|
170
|
+
return created.save();
|
|
171
|
+
}` : `async create(dto: ${createDtoName}): Promise<${pascalName}Document> { throw new Error('Not implemented'); }`}
|
|
172
|
+
|
|
173
|
+
${ops.findAll ? `async findAll(pagination: PaginationQueryDto): Promise<{ data: ${pascalName}Document[]; total: number }> {
|
|
174
|
+
const { page = 1, limit = 10 } = pagination;
|
|
175
|
+
const skip = (page - 1) * limit;
|
|
176
|
+
const [data, total] = await Promise.all([
|
|
177
|
+
this.model.find({ deletedAt: null }).skip(skip).limit(limit).sort({ createdAt: -1 }).exec(),
|
|
178
|
+
this.model.countDocuments({ deletedAt: null }).exec()
|
|
179
|
+
]);
|
|
180
|
+
return { data, total };
|
|
181
|
+
}` : `async findAll(p: PaginationQueryDto): Promise<{ data: ${pascalName}Document[]; total: number }> { throw new Error('Not implemented'); }`}
|
|
182
|
+
|
|
183
|
+
${ops.findOne ? `async findOne(id: string): Promise<${pascalName}Document | null> {
|
|
184
|
+
return this.model.findOne({ _id: id, deletedAt: null }).exec();
|
|
185
|
+
}` : `async findOne(id: string): Promise<${pascalName}Document | null> { throw new Error('Not implemented'); }`}
|
|
186
|
+
|
|
187
|
+
${ops.update ? `async update(id: string, dto: ${updateDtoName}): Promise<${pascalName}Document> {
|
|
188
|
+
return this.model.findByIdAndUpdate(id, dto as any, { new: true }).exec() as Promise<${pascalName}Document>;
|
|
189
|
+
}` : `async update(id: string, dto: ${updateDtoName}): Promise<${pascalName}Document> { throw new Error('Not implemented'); }`}
|
|
190
|
+
|
|
191
|
+
${ops.remove ? `async remove(id: string): Promise<${pascalName}Document> {
|
|
192
|
+
return this.model.findByIdAndUpdate(id, { deletedAt: new Date() }, { new: true }).exec() as Promise<${pascalName}Document>;
|
|
193
|
+
}` : `async remove(id: string): Promise<${pascalName}Document> { throw new Error('Not implemented'); }`}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@Injectable()
|
|
197
|
+
export class ${pascalName}Service extends BaseService<${pascalName}Document, ${createDtoName}, ${updateDtoName}> {
|
|
198
|
+
private readonly repository: Mongoose${pascalName}Repository;
|
|
199
|
+
|
|
200
|
+
constructor(@InjectModel('${pascalName}') private readonly model: Model<${pascalName}Document>) {
|
|
201
|
+
super();
|
|
202
|
+
this.repository = new Mongoose${pascalName}Repository(this.model);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
protected getRepository(): IBaseRepository<${pascalName}Document, ${createDtoName}, ${updateDtoName}> {
|
|
206
|
+
return this.repository;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
} else if (orm === 'drizzle') {
|
|
211
|
+
return `import { Injectable, Inject, NotFoundException } from '@nestjs/common';
|
|
212
|
+
import { eq, isNull, desc } from 'drizzle-orm';
|
|
213
|
+
import { BaseService, IBaseRepository, PaginationQueryDto } from '../../common/base';
|
|
214
|
+
${ops.create || ops.update ? `import { ${createDtoName} } from './dto/create-${kebabName}.dto';\nimport { ${updateDtoName} } from './dto/update-${kebabName}.dto';` : ''}
|
|
215
|
+
|
|
216
|
+
// Placeholder for Drizzle schema
|
|
217
|
+
const ${camelName}Schema = {} as any;
|
|
218
|
+
type ${pascalName}Entity = any;
|
|
219
|
+
${!ops.create && !ops.update ? `\ntype ${createDtoName} = any;\ntype ${updateDtoName} = any;` : ''}
|
|
220
|
+
|
|
221
|
+
class Drizzle${pascalName}Repository implements IBaseRepository<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
222
|
+
constructor(private readonly db: any) {}
|
|
223
|
+
|
|
224
|
+
${ops.create ? `async create(dto: ${createDtoName}): Promise<${pascalName}Entity> {
|
|
225
|
+
const [result] = await this.db.insert(${camelName}Schema).values(dto).returning();
|
|
226
|
+
return result;
|
|
227
|
+
}` : `async create(dto: ${createDtoName}): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
228
|
+
|
|
229
|
+
${ops.findAll ? `async findAll(pagination: PaginationQueryDto): Promise<{ data: ${pascalName}Entity[]; total: number }> {
|
|
230
|
+
const { page = 1, limit = 10 } = pagination;
|
|
231
|
+
const skip = (page - 1) * limit;
|
|
232
|
+
|
|
233
|
+
const data = await this.db.select().from(${camelName}Schema)
|
|
234
|
+
.where(isNull(${camelName}Schema.deletedAt))
|
|
235
|
+
.limit(limit).offset(skip)
|
|
236
|
+
.orderBy(desc(${camelName}Schema.createdAt));
|
|
237
|
+
|
|
238
|
+
return { data, total: 0 };
|
|
239
|
+
}` : `async findAll(p: PaginationQueryDto): Promise<{ data: ${pascalName}Entity[]; total: number }> { throw new Error('Not implemented'); }`}
|
|
240
|
+
|
|
241
|
+
${ops.findOne ? `async findOne(id: string): Promise<${pascalName}Entity | null> {
|
|
242
|
+
const [result] = await this.db.select().from(${camelName}Schema)
|
|
243
|
+
.where(eq(${camelName}Schema.id, id));
|
|
244
|
+
return result || null;
|
|
245
|
+
}` : `async findOne(id: string): Promise<${pascalName}Entity | null> { throw new Error('Not implemented'); }`}
|
|
246
|
+
|
|
247
|
+
${ops.update ? `async update(id: string, dto: ${updateDtoName}): Promise<${pascalName}Entity> {
|
|
248
|
+
const [result] = await this.db.update(${camelName}Schema).set(dto).where(eq(${camelName}Schema.id, id)).returning();
|
|
249
|
+
return result;
|
|
250
|
+
}` : `async update(id: string, dto: ${updateDtoName}): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
251
|
+
|
|
252
|
+
${ops.remove ? `async remove(id: string): Promise<${pascalName}Entity> {
|
|
253
|
+
const [result] = await this.db.update(${camelName}Schema).set({ deletedAt: new Date() }).where(eq(${camelName}Schema.id, id)).returning();
|
|
254
|
+
return result;
|
|
255
|
+
}` : `async remove(id: string): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@Injectable()
|
|
259
|
+
export class ${pascalName}Service extends BaseService<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
260
|
+
private readonly repository: Drizzle${pascalName}Repository;
|
|
261
|
+
|
|
262
|
+
constructor(@Inject('DB_CONNECTION') private readonly db: any) {
|
|
263
|
+
super();
|
|
264
|
+
this.repository = new Drizzle${pascalName}Repository(this.db);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
protected getRepository(): IBaseRepository<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
268
|
+
return this.repository;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// DEFAULT (PRISMA)
|
|
275
|
+
return `import { Injectable } from '@nestjs/common';
|
|
276
|
+
import { PrismaService } from '../../prisma/prisma.service';
|
|
277
|
+
import { BaseService, IBaseRepository, PaginationQueryDto } from '../../common/base';
|
|
278
|
+
${ops.create || ops.update ? `import { ${createDtoName} } from './dto/create-${kebabName}.dto';\nimport { ${updateDtoName} } from './dto/update-${kebabName}.dto';` : ''}
|
|
279
|
+
|
|
280
|
+
// Placeholder for Prisma Model type.
|
|
281
|
+
// Assuming a model named \`${pascalName}\` exists in schema.prisma
|
|
282
|
+
type ${pascalName}Entity = any;
|
|
283
|
+
${!ops.create && !ops.update ? `\ntype ${createDtoName} = any;\ntype ${updateDtoName} = any;` : ''}
|
|
284
|
+
|
|
285
|
+
class Prisma${pascalName}Repository implements IBaseRepository<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
286
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
287
|
+
|
|
288
|
+
${ops.create ? `async create(dto: ${createDtoName}): Promise<${pascalName}Entity> {
|
|
289
|
+
return (this.prisma as any).${camelName}.create({ data: dto });
|
|
290
|
+
}` : `async create(dto: ${createDtoName}): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
291
|
+
|
|
292
|
+
${ops.findAll ? `async findAll(pagination: PaginationQueryDto): Promise<{ data: ${pascalName}Entity[]; total: number }> {
|
|
293
|
+
const { page = 1, limit = 10 } = pagination;
|
|
294
|
+
const skip = (page - 1) * limit;
|
|
295
|
+
const [data, total] = await this.prisma.$transaction([
|
|
296
|
+
(this.prisma as any).${camelName}.findMany({ where: { deletedAt: null }, skip, take: limit, orderBy: { createdAt: 'desc' } }),
|
|
297
|
+
(this.prisma as any).${camelName}.count({ where: { deletedAt: null } }),
|
|
298
|
+
]);
|
|
299
|
+
return { data, total };
|
|
300
|
+
}` : `async findAll(p: PaginationQueryDto): Promise<{ data: ${pascalName}Entity[]; total: number }> { throw new Error('Not implemented'); }`}
|
|
301
|
+
|
|
302
|
+
${ops.findOne ? `async findOne(id: string): Promise<${pascalName}Entity | null> {
|
|
303
|
+
return (this.prisma as any).${camelName}.findFirst({ where: { id, deletedAt: null } });
|
|
304
|
+
}` : `async findOne(id: string): Promise<${pascalName}Entity | null> { throw new Error('Not implemented'); }`}
|
|
305
|
+
|
|
306
|
+
${ops.update ? `async update(id: string, dto: ${updateDtoName}): Promise<${pascalName}Entity> {
|
|
307
|
+
return (this.prisma as any).${camelName}.update({ where: { id }, data: dto });
|
|
308
|
+
}` : `async update(id: string, dto: ${updateDtoName}): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
309
|
+
|
|
310
|
+
${ops.remove ? `async remove(id: string): Promise<${pascalName}Entity> {
|
|
311
|
+
return (this.prisma as any).${camelName}.update({ where: { id }, data: { deletedAt: new Date() } });
|
|
312
|
+
}` : `async remove(id: string): Promise<${pascalName}Entity> { throw new Error('Not implemented'); }`}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
@Injectable()
|
|
316
|
+
export class ${pascalName}Service extends BaseService<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
317
|
+
private readonly repository: Prisma${pascalName}Repository;
|
|
318
|
+
|
|
319
|
+
constructor(private readonly prisma: PrismaService) {
|
|
320
|
+
super();
|
|
321
|
+
this.repository = new Prisma${pascalName}Repository(this.prisma);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
protected getRepository(): IBaseRepository<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
325
|
+
return this.repository;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
`;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function getModuleContent(orm, pascalName, kebabName) {
|
|
332
|
+
if (orm === 'typeorm') {
|
|
333
|
+
return `import { Module } from '@nestjs/common';
|
|
334
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
335
|
+
import { ${pascalName}Controller } from './${kebabName}.controller';
|
|
336
|
+
import { ${pascalName}Service } from './${kebabName}.service';
|
|
337
|
+
|
|
338
|
+
@Module({
|
|
339
|
+
imports: [TypeOrmModule.forFeature([/* ${pascalName}Entity */])],
|
|
340
|
+
controllers: [${pascalName}Controller],
|
|
341
|
+
providers: [${pascalName}Service],
|
|
342
|
+
exports: [${pascalName}Service],
|
|
343
|
+
})
|
|
344
|
+
export class ${pascalName}Module {}`;
|
|
345
|
+
} else if (orm === 'mongoose') {
|
|
346
|
+
return `import { Module } from '@nestjs/common';
|
|
347
|
+
import { MongooseModule } from '@nestjs/mongoose';
|
|
348
|
+
import { ${pascalName}Controller } from './${kebabName}.controller';
|
|
349
|
+
import { ${pascalName}Service } from './${kebabName}.service';
|
|
350
|
+
|
|
351
|
+
@Module({
|
|
352
|
+
imports: [MongooseModule.forFeature([{ name: '${pascalName}', schema: null /* ${pascalName}Schema */ }])],
|
|
353
|
+
controllers: [${pascalName}Controller],
|
|
354
|
+
providers: [${pascalName}Service],
|
|
355
|
+
exports: [${pascalName}Service],
|
|
356
|
+
})
|
|
357
|
+
export class ${pascalName}Module {}`;
|
|
358
|
+
} else if (orm === 'drizzle') {
|
|
359
|
+
return `import { Module } from '@nestjs/common';
|
|
360
|
+
import { ${pascalName}Controller } from './${kebabName}.controller';
|
|
361
|
+
import { ${pascalName}Service } from './${kebabName}.service';
|
|
362
|
+
import { DatabaseModule } from '../../database/database.module';
|
|
363
|
+
|
|
364
|
+
@Module({
|
|
365
|
+
imports: [DatabaseModule],
|
|
366
|
+
controllers: [${pascalName}Controller],
|
|
367
|
+
providers: [${pascalName}Service],
|
|
368
|
+
exports: [${pascalName}Service],
|
|
369
|
+
})
|
|
370
|
+
export class ${pascalName}Module {}`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// DEFAULT (PRISMA)
|
|
374
|
+
return `import { Module } from '@nestjs/common';
|
|
375
|
+
import { ${pascalName}Controller } from './${kebabName}.controller';
|
|
376
|
+
import { ${pascalName}Service } from './${kebabName}.service';
|
|
377
|
+
import { PrismaModule } from '../../prisma/prisma.module';
|
|
378
|
+
|
|
379
|
+
@Module({
|
|
380
|
+
imports: [PrismaModule],
|
|
381
|
+
controllers: [${pascalName}Controller],
|
|
382
|
+
providers: [${pascalName}Service],
|
|
383
|
+
exports: [${pascalName}Service],
|
|
384
|
+
})
|
|
385
|
+
export class ${pascalName}Module {}
|
|
386
|
+
`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function generateModule(providedModuleName, targetDir = process.cwd(), specifiedOrm = null) {
|
|
390
|
+
try {
|
|
391
|
+
const options = await promptForModuleOptions(providedModuleName);
|
|
392
|
+
const kebabName = toKebabCase(options.moduleName);
|
|
393
|
+
const pascalName = toPascalCase(options.moduleName);
|
|
394
|
+
const camelName = toCamelCase(options.moduleName);
|
|
395
|
+
|
|
396
|
+
// Detect ORM
|
|
397
|
+
const orm = specifiedOrm || await detectOrm(targetDir);
|
|
398
|
+
|
|
399
|
+
// Ensure we are inside a NestJS project structure
|
|
400
|
+
const srcDir = path.join(targetDir, 'src');
|
|
401
|
+
if (!(await fs.pathExists(srcDir))) {
|
|
402
|
+
console.warn(chalk.yellow('⚠️ Could not find "src" directory. Generating at current directory.'));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const moduleDir = (await fs.pathExists(srcDir)) ? path.join(srcDir, 'modules', kebabName) : path.join(targetDir, 'modules', kebabName);
|
|
406
|
+
const dtoDir = path.join(moduleDir, 'dto');
|
|
407
|
+
|
|
408
|
+
await fs.ensureDir(moduleDir);
|
|
409
|
+
await fs.ensureDir(dtoDir);
|
|
410
|
+
|
|
411
|
+
const ops = {
|
|
412
|
+
create: options.operations.includes('create'),
|
|
413
|
+
findAll: options.operations.includes('findAll'),
|
|
414
|
+
findOne: options.operations.includes('findOne'),
|
|
415
|
+
update: options.operations.includes('update'),
|
|
416
|
+
remove: options.operations.includes('remove')
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// 1. Generate DTOs
|
|
420
|
+
const createDtoName = `Create${pascalName}Dto`;
|
|
421
|
+
const updateDtoName = `Update${pascalName}Dto`;
|
|
422
|
+
const responseDtoName = `${pascalName}Dto`;
|
|
423
|
+
|
|
424
|
+
if (ops.create || ops.update) {
|
|
425
|
+
await fs.writeFile(
|
|
426
|
+
path.join(dtoDir, `create-${kebabName}.dto.ts`),
|
|
427
|
+
`import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
428
|
+
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
|
|
429
|
+
|
|
430
|
+
export class ${createDtoName} {
|
|
431
|
+
@ApiProperty({ description: 'Example property', example: 'Example value' })
|
|
432
|
+
@IsString()
|
|
433
|
+
@IsNotEmpty()
|
|
434
|
+
name: string;
|
|
435
|
+
|
|
436
|
+
@ApiPropertyOptional({ description: 'Optional property', example: 'Optional value' })
|
|
437
|
+
@IsString()
|
|
438
|
+
@IsOptional()
|
|
439
|
+
description?: string;
|
|
440
|
+
}
|
|
441
|
+
`
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
await fs.writeFile(
|
|
445
|
+
path.join(dtoDir, `update-${kebabName}.dto.ts`),
|
|
446
|
+
`import { PartialType } from '@nestjs/swagger';
|
|
447
|
+
import { ${createDtoName} } from './create-${kebabName}.dto';
|
|
448
|
+
|
|
449
|
+
export class ${updateDtoName} extends PartialType(${createDtoName}) {}
|
|
450
|
+
`
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
await fs.writeFile(
|
|
455
|
+
path.join(dtoDir, `${kebabName}.dto.ts`),
|
|
456
|
+
`import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
457
|
+
|
|
458
|
+
export class ${responseDtoName} {
|
|
459
|
+
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', format: 'uuid' })
|
|
460
|
+
id: string;
|
|
461
|
+
|
|
462
|
+
@ApiProperty({ example: 'Example value' })
|
|
463
|
+
name: string;
|
|
464
|
+
|
|
465
|
+
@ApiPropertyOptional({ example: 'Optional value' })
|
|
466
|
+
description?: string;
|
|
467
|
+
|
|
468
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
469
|
+
createdAt: Date;
|
|
470
|
+
|
|
471
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
472
|
+
updatedAt: Date;
|
|
473
|
+
|
|
474
|
+
@ApiPropertyOptional({ nullable: true, example: null })
|
|
475
|
+
deletedAt?: Date | null;
|
|
476
|
+
}
|
|
477
|
+
`
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
// 2. Generate Service
|
|
481
|
+
const serviceContent = getServiceContent(orm, pascalName, camelName, kebabName, createDtoName, updateDtoName, ops);
|
|
482
|
+
await fs.writeFile(path.join(moduleDir, `${kebabName}.service.ts`), serviceContent);
|
|
483
|
+
|
|
484
|
+
// 3. Generate Controller
|
|
485
|
+
const controllerContent = `import { Controller${ops.findAll ? ', Query' : ''}${ops.findOne || ops.update || ops.remove ? ', Param, ParseUUIDPipe, HttpStatus' : ''}${ops.create ? ', Post, Body' : ''}${ops.findAll || ops.findOne ? ', Get' : ''}${ops.update ? ', Put' : ''}${ops.remove ? ', Delete' : ''}, Type } from '@nestjs/common';
|
|
486
|
+
import { ApiTags, ApiBearerAuth, ApiExtraModels, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
|
487
|
+
import { BaseController } from '../../common/base/base.controller';
|
|
488
|
+
import { ApiResponseDto, ApiResponseSchema, PaginatedResponseDto, PaginatedResponseSchema, PaginationQueryDto } from '../../common/base';
|
|
489
|
+
import { ${pascalName}Service } from './${kebabName}.service';
|
|
490
|
+
${ops.create || ops.update ? `import { ${createDtoName} } from './dto/create-${kebabName}.dto';\nimport { ${updateDtoName} } from './dto/update-${kebabName}.dto';` : `type ${createDtoName} = any;\ntype ${updateDtoName} = any;`}
|
|
491
|
+
import { ${responseDtoName} } from './dto/${kebabName}.dto';
|
|
492
|
+
|
|
493
|
+
type ${pascalName}Entity = any;
|
|
494
|
+
|
|
495
|
+
@ApiTags('${pascalName}')
|
|
496
|
+
@ApiBearerAuth('bearer')
|
|
497
|
+
@ApiExtraModels(ApiResponseDto, PaginatedResponseDto, ${responseDtoName})
|
|
498
|
+
@Controller('${kebabName}')
|
|
499
|
+
export class ${pascalName}Controller extends BaseController<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
|
|
500
|
+
constructor(private readonly service: ${pascalName}Service) {
|
|
501
|
+
super(service);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
protected getDtoClass(): Type<${pascalName}Entity> {
|
|
505
|
+
return ${responseDtoName} as unknown as Type<${pascalName}Entity>;
|
|
506
|
+
}
|
|
507
|
+
${ops.create ? `
|
|
508
|
+
@Post()
|
|
509
|
+
@ApiOperation({ summary: 'Create a new ${kebabName}' })
|
|
510
|
+
@ApiResponse({ status: 201, description: '${pascalName} successfully created', schema: ApiResponseSchema(${responseDtoName}) })
|
|
511
|
+
override async create(@Body() dto: ${createDtoName}): Promise<ApiResponseDto<${pascalName}Entity>> {
|
|
512
|
+
return super.create(dto);
|
|
513
|
+
}
|
|
514
|
+
` : ''}${ops.findAll ? `
|
|
515
|
+
@Get()
|
|
516
|
+
@ApiOperation({ summary: 'Get all ${kebabName} (paginated)' })
|
|
517
|
+
@ApiResponse({ status: 200, description: 'Paginated ${kebabName}', schema: PaginatedResponseSchema(${responseDtoName}) })
|
|
518
|
+
override async findAll(@Query() pagination: PaginationQueryDto): Promise<PaginatedResponseDto<${pascalName}Entity>> {
|
|
519
|
+
return super.findAll(pagination);
|
|
520
|
+
}
|
|
521
|
+
` : ''}${ops.findOne ? `
|
|
522
|
+
@Get(':id')
|
|
523
|
+
@ApiOperation({ summary: 'Get ${kebabName} by ID' })
|
|
524
|
+
@ApiParam({ name: 'id', format: 'uuid' })
|
|
525
|
+
@ApiResponse({ status: 200, description: '${pascalName} found', schema: ApiResponseSchema(${responseDtoName}) })
|
|
526
|
+
@ApiResponse({ status: 404, description: '${pascalName} not found' })
|
|
527
|
+
override async findOne(@Param('id', new ParseUUIDPipe({ version: '4', errorHttpStatusCode: HttpStatus.BAD_REQUEST })) id: string): Promise<ApiResponseDto<${pascalName}Entity>> {
|
|
528
|
+
return super.findOne(id);
|
|
529
|
+
}
|
|
530
|
+
` : ''}${ops.update ? `
|
|
531
|
+
@Put(':id')
|
|
532
|
+
@ApiOperation({ summary: 'Update ${kebabName} by ID' })
|
|
533
|
+
@ApiParam({ name: 'id', format: 'uuid' })
|
|
534
|
+
@ApiResponse({ status: 200, description: '${pascalName} successfully updated', schema: ApiResponseSchema(${responseDtoName}) })
|
|
535
|
+
@ApiResponse({ status: 404, description: '${pascalName} not found' })
|
|
536
|
+
override async update(
|
|
537
|
+
@Param('id', new ParseUUIDPipe({ version: '4', errorHttpStatusCode: HttpStatus.BAD_REQUEST })) id: string,
|
|
538
|
+
@Body() dto: ${updateDtoName}
|
|
539
|
+
): Promise<ApiResponseDto<${pascalName}Entity>> {
|
|
540
|
+
return super.update(id, dto);
|
|
541
|
+
}
|
|
542
|
+
` : ''}${ops.remove ? `
|
|
543
|
+
@Delete(':id')
|
|
544
|
+
@ApiOperation({ summary: 'Delete ${kebabName} by ID' })
|
|
545
|
+
@ApiParam({ name: 'id', format: 'uuid' })
|
|
546
|
+
@ApiResponse({ status: 200, description: '${pascalName} successfully deleted', schema: ApiResponseSchema(${responseDtoName}) })
|
|
547
|
+
@ApiResponse({ status: 404, description: '${pascalName} not found' })
|
|
548
|
+
override async remove(@Param('id', new ParseUUIDPipe({ version: '4', errorHttpStatusCode: HttpStatus.BAD_REQUEST })) id: string): Promise<ApiResponseDto<${pascalName}Entity>> {
|
|
549
|
+
return super.remove(id);
|
|
550
|
+
}
|
|
551
|
+
` : ''}
|
|
552
|
+
}
|
|
553
|
+
`;
|
|
554
|
+
await fs.writeFile(path.join(moduleDir, `${kebabName}.controller.ts`), controllerContent);
|
|
555
|
+
|
|
556
|
+
// 4. Generate Module
|
|
557
|
+
const moduleContent = getModuleContent(orm, pascalName, kebabName);
|
|
558
|
+
await fs.writeFile(path.join(moduleDir, `${kebabName}.module.ts`), moduleContent);
|
|
559
|
+
|
|
560
|
+
console.log(chalk.green(`\n✅ Module "${kebabName}" successfully generated in ${path.relative(process.cwd(), moduleDir)}`));
|
|
561
|
+
console.log(chalk.gray(` Detected ORM: ${orm}`));
|
|
562
|
+
console.log(chalk.yellow(`\n⚠️ Don't forget to register ${pascalName}Module in src/app.module.ts:`));
|
|
563
|
+
console.log(chalk.cyan(`
|
|
564
|
+
import { ${pascalName}Module } from './modules/${kebabName}/${kebabName}.module';
|
|
565
|
+
|
|
566
|
+
@Module({
|
|
567
|
+
imports: [
|
|
568
|
+
...
|
|
569
|
+
${pascalName}Module,
|
|
570
|
+
],
|
|
571
|
+
})
|
|
572
|
+
export class AppModule {}
|
|
573
|
+
`));
|
|
574
|
+
|
|
575
|
+
return true;
|
|
576
|
+
} catch (error) {
|
|
577
|
+
console.error(chalk.red('\n❌ Module generation failed:'), error.message);
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
module.exports = {
|
|
583
|
+
generateModule,
|
|
584
|
+
promptForModuleOptions,
|
|
585
|
+
detectOrm
|
|
586
|
+
};
|