@flusys/nestjs-core 1.0.0-beta → 1.0.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/README.md +507 -61
- package/cjs/config/env-config.service.js +1 -1
- package/cjs/docs/docs.config.js +77 -3
- package/cjs/docs/index.js +0 -1
- package/cjs/interfaces/base-entity.interface.js +5 -3
- package/cjs/interfaces/database.interface.js +1 -3
- package/cjs/migration/datasource.factory.js +1 -3
- package/cjs/migration/index.js +0 -12
- package/cjs/migration/migration.cli.js +1 -17
- package/cjs/migration/migration.runner.js +37 -65
- package/cjs/seeders/base-seeder.js +6 -25
- package/cjs/seeders/cli.js +65 -172
- package/cjs/seeders/data-generator.js +96 -142
- package/cjs/seeders/entity-reader.js +0 -17
- package/cjs/seeders/field-patterns.js +172 -0
- package/cjs/seeders/index.js +16 -8
- package/cjs/seeders/seed-config.js +9 -48
- package/cjs/seeders/seed-runner.js +8 -14
- package/cjs/utils/datasource-config.builder.js +2 -14
- package/docs/docs.config.d.ts +7 -0
- package/docs/index.d.ts +0 -1
- package/fesm/config/env-config.service.js +1 -1
- package/fesm/docs/docs.config.js +68 -0
- package/fesm/docs/index.js +0 -1
- package/fesm/interfaces/app-config.interfaces.js +1 -3
- package/fesm/interfaces/base-entity.interface.js +5 -5
- package/fesm/interfaces/database.interface.js +1 -5
- package/fesm/migration/cli.js +1 -20
- package/fesm/migration/datasource.factory.js +3 -20
- package/fesm/migration/index.js +0 -14
- package/fesm/migration/migration.cli.js +1 -17
- package/fesm/migration/migration.runner.js +43 -132
- package/fesm/seeders/base-seeder.js +7 -51
- package/fesm/seeders/cli.js +65 -182
- package/fesm/seeders/data-generator.js +96 -149
- package/fesm/seeders/entity-reader.js +0 -17
- package/fesm/seeders/field-patterns.js +143 -0
- package/fesm/seeders/index.js +3 -7
- package/fesm/seeders/seed-config.js +9 -59
- package/fesm/seeders/seed-runner.js +8 -14
- package/fesm/utils/datasource-config.builder.js +2 -13
- package/interfaces/base-entity.interface.d.ts +3 -0
- package/package.json +2 -2
- package/seeders/data-generator.d.ts +1 -1
- package/seeders/entity-reader.d.ts +0 -1
- package/seeders/field-patterns.d.ts +12 -0
- package/seeders/index.d.ts +3 -3
- package/seeders/seed-config.d.ts +1 -0
- package/seeders/seed-runner.d.ts +1 -0
- package/utils/datasource-config.builder.d.ts +0 -1
- package/cjs/docs/docs.setup.js +0 -14
- package/cjs/seeders/template-generator.js +0 -297
- package/docs/docs.setup.d.ts +0 -3
- package/fesm/docs/docs.setup.js +0 -4
- package/fesm/seeders/template-generator.js +0 -257
- package/seeders/template-generator.d.ts +0 -16
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
/**
|
|
4
|
-
* Template Generator Service
|
|
5
|
-
*
|
|
6
|
-
* Generates seeder template files for entities.
|
|
7
|
-
* Creates TypeScript class files with BaseSeeder extension.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* ```typescript
|
|
11
|
-
* const generator = new TemplateGenerator();
|
|
12
|
-
* generator.generateSeederFile(entityInfo, outputPath);
|
|
13
|
-
* ```
|
|
14
|
-
*/ export class TemplateGenerator {
|
|
15
|
-
/**
|
|
16
|
-
* Generate seeder file for an entity
|
|
17
|
-
* @param entityInfo Entity metadata
|
|
18
|
-
* @param outputDir Output directory for seeder files
|
|
19
|
-
*/ generateSeederFile(entityInfo, outputDir) {
|
|
20
|
-
const fileName = this.getSeederFileName(entityInfo.name);
|
|
21
|
-
const filePath = path.join(outputDir, fileName);
|
|
22
|
-
const content = this.generateSeederContent(entityInfo);
|
|
23
|
-
// Ensure directory exists
|
|
24
|
-
if (!fs.existsSync(outputDir)) {
|
|
25
|
-
fs.mkdirSync(outputDir, {
|
|
26
|
-
recursive: true
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
// Write file
|
|
30
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
31
|
-
return filePath;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Generate index file that exports all seeders
|
|
35
|
-
* @param entityInfos Array of entity metadata
|
|
36
|
-
* @param outputDir Output directory
|
|
37
|
-
*/ generateIndexFile(entityInfos, outputDir) {
|
|
38
|
-
const indexPath = path.join(outputDir, 'index.ts');
|
|
39
|
-
const exports = entityInfos.map((info)=>{
|
|
40
|
-
const className = this.getSeederClassName(info.name);
|
|
41
|
-
const fileName = this.getSeederFileName(info.name).replace('.ts', '');
|
|
42
|
-
return `export { ${className} } from './${fileName}';`;
|
|
43
|
-
}).join('\n');
|
|
44
|
-
fs.writeFileSync(indexPath, exports, 'utf-8');
|
|
45
|
-
return indexPath;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Generate seeder file content
|
|
49
|
-
*/ generateSeederContent(entityInfo) {
|
|
50
|
-
const className = this.getSeederClassName(entityInfo.name);
|
|
51
|
-
const imports = this.generateImports(entityInfo);
|
|
52
|
-
const classContent = this.generateClass(entityInfo, className);
|
|
53
|
-
return `${imports}\n\n${classContent}\n`;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Generate import statements
|
|
57
|
-
*/ generateImports(entityInfo) {
|
|
58
|
-
const imports = [
|
|
59
|
-
`import { DataSource } from 'typeorm';`,
|
|
60
|
-
`import { faker } from '@faker-js/faker';`,
|
|
61
|
-
`import { BaseSeeder } from '@flusys/nestjs-core/seeders/base-seeder';`
|
|
62
|
-
];
|
|
63
|
-
// Add entity import (adjust path as needed)
|
|
64
|
-
const entityImportPath = this.getEntityImportPath(entityInfo.name);
|
|
65
|
-
imports.push(`import { ${entityInfo.name} } from '${entityImportPath}';`);
|
|
66
|
-
// Add relation imports
|
|
67
|
-
const relationEntities = new Set(entityInfo.relations.map((r)=>r.targetEntity));
|
|
68
|
-
for (const relEntity of relationEntities){
|
|
69
|
-
const relPath = this.getEntityImportPath(relEntity);
|
|
70
|
-
imports.push(`import { ${relEntity} } from '${relPath}';`);
|
|
71
|
-
}
|
|
72
|
-
return imports.join('\n');
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Generate class content
|
|
76
|
-
*/ generateClass(entityInfo, className) {
|
|
77
|
-
const generateMethod = this.generateGenerateMethod(entityInfo);
|
|
78
|
-
const relationMethod = this.generateRelationMethod(entityInfo);
|
|
79
|
-
return `
|
|
80
|
-
/**
|
|
81
|
-
* ${className}
|
|
82
|
-
*
|
|
83
|
-
* Seeder for ${entityInfo.name} entity.
|
|
84
|
-
* Generates sample data for development/testing.
|
|
85
|
-
*/
|
|
86
|
-
export class ${className} extends BaseSeeder<${entityInfo.name}> {
|
|
87
|
-
constructor(dataSource: DataSource) {
|
|
88
|
-
super(dataSource, ${entityInfo.name});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
${generateMethod}${relationMethod ? '\n\n' + relationMethod : ''}
|
|
92
|
-
}`.trim();
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Generate the generate() method
|
|
96
|
-
*/ generateGenerateMethod(entityInfo) {
|
|
97
|
-
const fields = this.generateFieldAssignments(entityInfo.columns);
|
|
98
|
-
return ` /**
|
|
99
|
-
* Generate sample ${entityInfo.name} data
|
|
100
|
-
* @param count Number of records to generate
|
|
101
|
-
* @returns Array of created entities
|
|
102
|
-
*/
|
|
103
|
-
async generate(count: number = 10): Promise<${entityInfo.name}[]> {
|
|
104
|
-
const entities: ${entityInfo.name}[] = [];
|
|
105
|
-
|
|
106
|
-
for (let i = 0; i < count; i++) {
|
|
107
|
-
const entity = this.repository.create({
|
|
108
|
-
${fields}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
entities.push(entity);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return this.repository.save(entities);
|
|
115
|
-
}`;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Generate field assignments for entity creation
|
|
119
|
-
*/ generateFieldAssignments(columns) {
|
|
120
|
-
const assignments = [];
|
|
121
|
-
for (const column of columns){
|
|
122
|
-
// Skip system fields
|
|
123
|
-
if (this.isSystemField(column.propertyName)) {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
// Skip foreign keys (handled in relations method)
|
|
127
|
-
if (column.propertyName.endsWith('Id')) {
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
const fakerCall = this.getFakerCall(column);
|
|
131
|
-
const comment = column.isNullable ? ' // Optional' : ' // Required';
|
|
132
|
-
assignments.push(` ${column.propertyName}: ${fakerCall},${comment}`);
|
|
133
|
-
}
|
|
134
|
-
return assignments.join('\n');
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Generate relation handling method if entity has relations
|
|
138
|
-
*/ generateRelationMethod(entityInfo) {
|
|
139
|
-
const manyToOneRelations = entityInfo.relations.filter((r)=>r.type === 'many-to-one');
|
|
140
|
-
if (manyToOneRelations.length === 0) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
const relationParams = manyToOneRelations.map((r)=>`${r.propertyName}s: ${r.targetEntity}[]`).join(', ');
|
|
144
|
-
const relationAssignments = manyToOneRelations.map((r)=>{
|
|
145
|
-
return ` const random${r.targetEntity} = faker.helpers.arrayElement(${r.propertyName}s);
|
|
146
|
-
entity.${r.propertyName}Id = random${r.targetEntity}.id;`;
|
|
147
|
-
}).join('\n');
|
|
148
|
-
return ` /**
|
|
149
|
-
* Generate with related entities
|
|
150
|
-
* @param ${relationParams}
|
|
151
|
-
* @param count Number of records to generate
|
|
152
|
-
*/
|
|
153
|
-
async generateWithRelations(${relationParams}, count: number = 10): Promise<${entityInfo.name}[]> {
|
|
154
|
-
const entities = await this.generate(count);
|
|
155
|
-
|
|
156
|
-
for (const entity of entities) {
|
|
157
|
-
${relationAssignments}
|
|
158
|
-
await this.repository.save(entity);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return entities;
|
|
162
|
-
}`;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Get faker call for column based on its properties
|
|
166
|
-
*/ getFakerCall(column) {
|
|
167
|
-
const nameLower = column.propertyName.toLowerCase();
|
|
168
|
-
// Name fields
|
|
169
|
-
if (nameLower.includes('firstname')) return 'faker.person.firstName()';
|
|
170
|
-
if (nameLower.includes('lastname')) return 'faker.person.lastName()';
|
|
171
|
-
if (nameLower === 'name' || nameLower.includes('fullname')) {
|
|
172
|
-
return 'faker.person.fullName()';
|
|
173
|
-
}
|
|
174
|
-
// Contact
|
|
175
|
-
if (nameLower.includes('email')) return 'faker.internet.email().toLowerCase()';
|
|
176
|
-
if (nameLower.includes('phone')) return 'faker.phone.number()';
|
|
177
|
-
// Text
|
|
178
|
-
if (nameLower.includes('description')) return 'faker.lorem.paragraph()';
|
|
179
|
-
if (nameLower.includes('content')) return 'faker.lorem.paragraphs(2)';
|
|
180
|
-
if (nameLower.includes('title')) return 'faker.lorem.sentence()';
|
|
181
|
-
// URL/Web
|
|
182
|
-
if (nameLower.includes('slug')) {
|
|
183
|
-
return "faker.helpers.slugify(faker.lorem.words(3)).toLowerCase()";
|
|
184
|
-
}
|
|
185
|
-
if (nameLower.includes('url')) return 'faker.internet.url()';
|
|
186
|
-
// Status
|
|
187
|
-
if (nameLower.includes('isactive') || nameLower.includes('isenabled')) {
|
|
188
|
-
return 'true';
|
|
189
|
-
}
|
|
190
|
-
// Type-based
|
|
191
|
-
const type = column.type.toLowerCase();
|
|
192
|
-
if (type.includes('bool')) return 'faker.datatype.boolean()';
|
|
193
|
-
if (type.includes('int') || type.includes('serial')) {
|
|
194
|
-
return 'faker.number.int({ min: 1, max: 1000 })';
|
|
195
|
-
}
|
|
196
|
-
if (type.includes('decimal') || type.includes('float') || type.includes('numeric')) {
|
|
197
|
-
return 'faker.number.float({ min: 0, max: 1000, precision: 0.01 })';
|
|
198
|
-
}
|
|
199
|
-
if (type.includes('date') || type.includes('timestamp')) {
|
|
200
|
-
return 'faker.date.recent({ days: 30 })';
|
|
201
|
-
}
|
|
202
|
-
if (type.includes('uuid')) return 'faker.string.uuid()';
|
|
203
|
-
if (type.includes('json')) return '{ key: faker.lorem.word() }';
|
|
204
|
-
// Default string
|
|
205
|
-
const maxLength = typeof column.length === 'number' ? column.length : 255;
|
|
206
|
-
if (maxLength <= 50) {
|
|
207
|
-
return 'faker.lorem.word()';
|
|
208
|
-
} else if (maxLength <= 255) {
|
|
209
|
-
return 'faker.lorem.sentence()';
|
|
210
|
-
} else {
|
|
211
|
-
return 'faker.lorem.paragraph()';
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Check if field is a system field (should be skipped)
|
|
216
|
-
*/ isSystemField(fieldName) {
|
|
217
|
-
const systemFields = [
|
|
218
|
-
'id',
|
|
219
|
-
'createdAt',
|
|
220
|
-
'updatedAt',
|
|
221
|
-
'deletedAt',
|
|
222
|
-
'createdById',
|
|
223
|
-
'updatedById',
|
|
224
|
-
'deletedById'
|
|
225
|
-
];
|
|
226
|
-
return systemFields.includes(fieldName);
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Get seeder class name from entity name
|
|
230
|
-
*/ getSeederClassName(entityName) {
|
|
231
|
-
return `${entityName}Seeder`;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Get seeder file name from entity name
|
|
235
|
-
*/ getSeederFileName(entityName) {
|
|
236
|
-
const kebab = entityName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
237
|
-
return `${kebab}.seeder.ts`;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Get entity import path (adjust based on project structure)
|
|
241
|
-
*/ getEntityImportPath(entityName) {
|
|
242
|
-
// Try to determine module from entity name
|
|
243
|
-
// This is a simplified approach - adjust based on actual project structure
|
|
244
|
-
const nameLower = entityName.toLowerCase();
|
|
245
|
-
if (nameLower.includes('user') || nameLower.includes('company') || nameLower.includes('branch')) {
|
|
246
|
-
return `@flusys/nestjs-auth/entities`;
|
|
247
|
-
}
|
|
248
|
-
if (nameLower.includes('role') || nameLower.includes('permission') || nameLower.includes('action') || nameLower.includes('menu')) {
|
|
249
|
-
return `@flusys/nestjs-iam/entities`;
|
|
250
|
-
}
|
|
251
|
-
if (nameLower.includes('file') || nameLower.includes('folder') || nameLower.includes('storage')) {
|
|
252
|
-
return `@flusys/nestjs-storage/entities`;
|
|
253
|
-
}
|
|
254
|
-
// Default fallback
|
|
255
|
-
return `@flusys/nestjs-shared/entities`;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { IEntityInfo } from './entity-reader';
|
|
2
|
-
export declare class TemplateGenerator {
|
|
3
|
-
generateSeederFile(entityInfo: IEntityInfo, outputDir: string): string;
|
|
4
|
-
generateIndexFile(entityInfos: IEntityInfo[], outputDir: string): string;
|
|
5
|
-
private generateSeederContent;
|
|
6
|
-
private generateImports;
|
|
7
|
-
private generateClass;
|
|
8
|
-
private generateGenerateMethod;
|
|
9
|
-
private generateFieldAssignments;
|
|
10
|
-
private generateRelationMethod;
|
|
11
|
-
private getFakerCall;
|
|
12
|
-
private isSystemField;
|
|
13
|
-
private getSeederClassName;
|
|
14
|
-
private getSeederFileName;
|
|
15
|
-
private getEntityImportPath;
|
|
16
|
-
}
|