archetype-engine 2.0.1 → 2.2.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 +30 -9
- package/dist/src/cli.js +6 -0
- package/dist/src/init/templates.d.ts +2 -0
- package/dist/src/init/templates.d.ts.map +1 -1
- package/dist/src/init/templates.js +418 -0
- package/dist/src/mcp-server.d.ts +15 -0
- package/dist/src/mcp-server.d.ts.map +1 -0
- package/dist/src/mcp-server.js +271 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.d.ts +3 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.d.ts.map +1 -1
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.js +7 -1
- package/dist/src/templates/nextjs-drizzle-trpc/generators/openapi.d.ts +26 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/openapi.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/openapi.js +690 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/seed.d.ts +27 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/seed.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/seed.js +407 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/test.d.ts +27 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/test.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/test.js +520 -0
- package/dist/src/templates/nextjs-drizzle-trpc/index.d.ts +5 -1
- package/dist/src/templates/nextjs-drizzle-trpc/index.d.ts.map +1 -1
- package/dist/src/templates/nextjs-drizzle-trpc/index.js +11 -1
- package/package.json +3 -2
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Data Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates realistic seed data for development and testing.
|
|
5
|
+
* Creates seed functions for each entity with smart mock data based on field types.
|
|
6
|
+
*
|
|
7
|
+
* Generated files:
|
|
8
|
+
* - seeds/{entity}.ts - Individual entity seed functions
|
|
9
|
+
* - seeds/index.ts - Main orchestrator handling dependencies
|
|
10
|
+
* - seeds/run.ts - CLI script to run seeds
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Smart field-to-data mapping (email → valid emails, name → person names)
|
|
14
|
+
* - Respects validation constraints (min/max, enum values)
|
|
15
|
+
* - Handles entity dependencies (creates in correct order)
|
|
16
|
+
* - Configurable quantities
|
|
17
|
+
* - Optional faker.js integration for realistic data
|
|
18
|
+
* - Database reset utilities
|
|
19
|
+
*
|
|
20
|
+
* @module generators/seed
|
|
21
|
+
*/
|
|
22
|
+
import type { Generator } from '../../../template/types';
|
|
23
|
+
/**
|
|
24
|
+
* Seed data generator
|
|
25
|
+
*/
|
|
26
|
+
export declare const seedGenerator: Generator;
|
|
27
|
+
//# sourceMappingURL=seed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,yBAAyB,CAAA;AAuWvE;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,SAwC3B,CAAA"}
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Seed Data Generator
|
|
4
|
+
*
|
|
5
|
+
* Generates realistic seed data for development and testing.
|
|
6
|
+
* Creates seed functions for each entity with smart mock data based on field types.
|
|
7
|
+
*
|
|
8
|
+
* Generated files:
|
|
9
|
+
* - seeds/{entity}.ts - Individual entity seed functions
|
|
10
|
+
* - seeds/index.ts - Main orchestrator handling dependencies
|
|
11
|
+
* - seeds/run.ts - CLI script to run seeds
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Smart field-to-data mapping (email → valid emails, name → person names)
|
|
15
|
+
* - Respects validation constraints (min/max, enum values)
|
|
16
|
+
* - Handles entity dependencies (creates in correct order)
|
|
17
|
+
* - Configurable quantities
|
|
18
|
+
* - Optional faker.js integration for realistic data
|
|
19
|
+
* - Database reset utilities
|
|
20
|
+
*
|
|
21
|
+
* @module generators/seed
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.seedGenerator = void 0;
|
|
25
|
+
const utils_1 = require("../../../core/utils");
|
|
26
|
+
/**
|
|
27
|
+
* Generate mock value for a field
|
|
28
|
+
*/
|
|
29
|
+
function generateMockValue(fieldName, field, index) {
|
|
30
|
+
const lowerName = fieldName.toLowerCase();
|
|
31
|
+
switch (field.type) {
|
|
32
|
+
case 'text':
|
|
33
|
+
// Email fields
|
|
34
|
+
if (field.validations.some(v => v.type === 'email')) {
|
|
35
|
+
return `faker ? faker.internet.email() : \`user\${i}@example.com\``;
|
|
36
|
+
}
|
|
37
|
+
// URL fields
|
|
38
|
+
if (field.validations.some(v => v.type === 'url')) {
|
|
39
|
+
return `faker ? faker.internet.url() : \`https://example.com/\${i}\``;
|
|
40
|
+
}
|
|
41
|
+
// Name fields
|
|
42
|
+
if (lowerName.includes('name') || lowerName === 'title') {
|
|
43
|
+
if (lowerName.includes('first')) {
|
|
44
|
+
return `faker ? faker.person.firstName() : \`FirstName\${i}\``;
|
|
45
|
+
}
|
|
46
|
+
if (lowerName.includes('last')) {
|
|
47
|
+
return `faker ? faker.person.lastName() : \`LastName\${i}\``;
|
|
48
|
+
}
|
|
49
|
+
if (lowerName.includes('full') || lowerName === 'name') {
|
|
50
|
+
return `faker ? faker.person.fullName() : \`Sample Name \${i}\``;
|
|
51
|
+
}
|
|
52
|
+
if (lowerName === 'title') {
|
|
53
|
+
return `faker ? faker.lorem.sentence(5) : \`Sample Title \${i}\``;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Content/description fields
|
|
57
|
+
if (lowerName.includes('content') || lowerName.includes('description') || lowerName.includes('bio')) {
|
|
58
|
+
return `faker ? faker.lorem.paragraphs(2) : \`Sample ${fieldName} content for record \${i}\``;
|
|
59
|
+
}
|
|
60
|
+
// Address fields
|
|
61
|
+
if (lowerName.includes('address')) {
|
|
62
|
+
return `faker ? faker.location.streetAddress() : \`\${i} Main Street\``;
|
|
63
|
+
}
|
|
64
|
+
if (lowerName.includes('city')) {
|
|
65
|
+
return `faker ? faker.location.city() : \`City\${i}\``;
|
|
66
|
+
}
|
|
67
|
+
if (lowerName.includes('country')) {
|
|
68
|
+
return `faker ? faker.location.country() : \`Country\${i}\``;
|
|
69
|
+
}
|
|
70
|
+
// Phone fields
|
|
71
|
+
if (lowerName.includes('phone')) {
|
|
72
|
+
return `faker ? faker.phone.number() : \`+1-555-\${1000 + i}\``;
|
|
73
|
+
}
|
|
74
|
+
// Slug fields
|
|
75
|
+
if (lowerName.includes('slug')) {
|
|
76
|
+
return `faker ? faker.helpers.slugify(faker.lorem.words(3)).toLowerCase() : \`slug-\${i}\``;
|
|
77
|
+
}
|
|
78
|
+
// Enum fields
|
|
79
|
+
if (field.enumValues) {
|
|
80
|
+
const values = field.enumValues;
|
|
81
|
+
return `faker ? faker.helpers.arrayElement(${JSON.stringify(values)}) : ${JSON.stringify(values)}[i % ${values.length}]`;
|
|
82
|
+
}
|
|
83
|
+
// Generic text with min length
|
|
84
|
+
const minLength = field.validations.find(v => v.type === 'minLength')?.value;
|
|
85
|
+
if (minLength) {
|
|
86
|
+
return `faker ? faker.lorem.words(${Math.ceil(minLength / 5)}) : \`${'x'.repeat(minLength)}\${i}\``;
|
|
87
|
+
}
|
|
88
|
+
// Default text
|
|
89
|
+
return `faker ? faker.lorem.words(3) : \`Sample ${fieldName} \${i}\``;
|
|
90
|
+
case 'number':
|
|
91
|
+
const min = field.validations.find(v => v.type === 'min')?.value ?? 0;
|
|
92
|
+
const max = field.validations.find(v => v.type === 'max')?.value ?? 1000;
|
|
93
|
+
const isInteger = field.validations.some(v => v.type === 'integer');
|
|
94
|
+
if (lowerName.includes('age')) {
|
|
95
|
+
return `faker ? faker.number.int({ min: ${min || 18}, max: ${max || 100} }) : ${min || 18} + (i % ${(max || 100) - (min || 18)})`;
|
|
96
|
+
}
|
|
97
|
+
if (lowerName.includes('price') || lowerName.includes('amount')) {
|
|
98
|
+
return `faker ? faker.number.float({ min: ${min}, max: ${max}, precision: 0.01 }) : ${min} + (i * 10)`;
|
|
99
|
+
}
|
|
100
|
+
if (lowerName.includes('count') || lowerName.includes('quantity')) {
|
|
101
|
+
return `faker ? faker.number.int({ min: ${min}, max: ${max} }) : i % ${max || 100}`;
|
|
102
|
+
}
|
|
103
|
+
if (isInteger) {
|
|
104
|
+
return `faker ? faker.number.int({ min: ${min}, max: ${max} }) : ${min} + (i % ${max - min})`;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return `faker ? faker.number.float({ min: ${min}, max: ${max} }) : ${min} + (i * 0.5)`;
|
|
108
|
+
}
|
|
109
|
+
case 'boolean':
|
|
110
|
+
if (lowerName.includes('active') || lowerName.includes('enabled')) {
|
|
111
|
+
return `i % 3 !== 0`; // 66% true
|
|
112
|
+
}
|
|
113
|
+
if (lowerName.includes('published')) {
|
|
114
|
+
return `i % 2 === 0`; // 50% true
|
|
115
|
+
}
|
|
116
|
+
return `faker ? faker.datatype.boolean() : i % 2 === 0`;
|
|
117
|
+
case 'date':
|
|
118
|
+
if (lowerName.includes('birth')) {
|
|
119
|
+
return `faker ? faker.date.birthdate() : new Date(1990 + (i % 30), i % 12, 1 + (i % 28))`;
|
|
120
|
+
}
|
|
121
|
+
return `faker ? faker.date.recent() : new Date()`;
|
|
122
|
+
case 'enum':
|
|
123
|
+
const enumValues = field.enumValues || [];
|
|
124
|
+
return `faker ? faker.helpers.arrayElement(${JSON.stringify(enumValues)}) : ${JSON.stringify(enumValues)}[i % ${enumValues.length}]`;
|
|
125
|
+
default:
|
|
126
|
+
return `\`value-\${i}\``;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Generate seed function for an entity
|
|
131
|
+
*/
|
|
132
|
+
function generateEntitySeedFunction(entity) {
|
|
133
|
+
const entityName = entity.name;
|
|
134
|
+
const routerName = (0, utils_1.toCamelCase)(entityName);
|
|
135
|
+
const tableName = (0, utils_1.toSnakeCase)((0, utils_1.pluralize)(entityName));
|
|
136
|
+
const lines = [];
|
|
137
|
+
// Get non-computed, non-relation fields
|
|
138
|
+
const seedableFields = Object.entries(entity.fields).filter(([_, field]) => field.type !== 'computed');
|
|
139
|
+
// Check for relations
|
|
140
|
+
const hasRelations = Object.keys(entity.relations).length > 0;
|
|
141
|
+
const foreignKeyFields = Object.entries(entity.relations)
|
|
142
|
+
.filter(([_, rel]) => rel.type === 'hasOne')
|
|
143
|
+
.map(([name, _]) => `${name}Id`);
|
|
144
|
+
// Function signature
|
|
145
|
+
const params = hasRelations
|
|
146
|
+
? `count = 10, options: { ${foreignKeyFields.map(fk => `${fk}s?: string[]`).join(', ')} } = {}`
|
|
147
|
+
: `count = 10`;
|
|
148
|
+
lines.push(`/**`);
|
|
149
|
+
lines.push(` * Seed ${entityName} records`);
|
|
150
|
+
if (hasRelations) {
|
|
151
|
+
lines.push(` * @param count - Number of records to create`);
|
|
152
|
+
lines.push(` * @param options - Related entity IDs for foreign keys`);
|
|
153
|
+
}
|
|
154
|
+
lines.push(` */`);
|
|
155
|
+
lines.push(`export async function seed${(0, utils_1.pluralize)(entityName)}(${params}) {`);
|
|
156
|
+
lines.push(` let faker: any`);
|
|
157
|
+
lines.push(` try {`);
|
|
158
|
+
lines.push(` faker = (await import('@faker-js/faker')).faker`);
|
|
159
|
+
lines.push(` } catch {`);
|
|
160
|
+
lines.push(` faker = null`);
|
|
161
|
+
lines.push(` }`);
|
|
162
|
+
lines.push(``);
|
|
163
|
+
lines.push(` const data = Array.from({ length: count }, (_, i) => ({`);
|
|
164
|
+
// Generate field values
|
|
165
|
+
for (const [fieldName, field] of seedableFields) {
|
|
166
|
+
const value = generateMockValue(fieldName, field, 0);
|
|
167
|
+
lines.push(` ${fieldName}: ${value},`);
|
|
168
|
+
}
|
|
169
|
+
// Add foreign keys from relations
|
|
170
|
+
for (const fkField of foreignKeyFields) {
|
|
171
|
+
const baseName = fkField.replace(/Id$/, '');
|
|
172
|
+
lines.push(` ${fkField}: options.${fkField}s?.[i % (options.${fkField}s?.length || 1)],`);
|
|
173
|
+
}
|
|
174
|
+
// Add timestamps if enabled
|
|
175
|
+
if (entity.behaviors.timestamps) {
|
|
176
|
+
lines.push(` createdAt: faker ? faker.date.recent({ days: 30 }) : new Date(),`);
|
|
177
|
+
lines.push(` updatedAt: faker ? faker.date.recent({ days: 7 }) : new Date(),`);
|
|
178
|
+
}
|
|
179
|
+
lines.push(` }))`);
|
|
180
|
+
lines.push(``);
|
|
181
|
+
lines.push(` const created = await db.insert(${tableName}).values(data).returning()`);
|
|
182
|
+
lines.push(` console.log(\`✓ Created \${created.length} ${(0, utils_1.pluralize)(entityName).toLowerCase()}\`)`);
|
|
183
|
+
lines.push(` return created`);
|
|
184
|
+
lines.push(`}`);
|
|
185
|
+
return lines.join('\n');
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Analyze entity dependencies to determine seed order
|
|
189
|
+
*/
|
|
190
|
+
function analyzeEntityDependencies(entities) {
|
|
191
|
+
const graph = new Map();
|
|
192
|
+
const entityMap = new Map();
|
|
193
|
+
// Build dependency graph
|
|
194
|
+
for (const entity of entities) {
|
|
195
|
+
entityMap.set(entity.name, entity);
|
|
196
|
+
graph.set(entity.name, new Set());
|
|
197
|
+
// Check for hasOne relations (foreign keys)
|
|
198
|
+
for (const [_, relation] of Object.entries(entity.relations)) {
|
|
199
|
+
if (relation.type === 'hasOne') {
|
|
200
|
+
graph.get(entity.name).add(relation.entity);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Topological sort
|
|
205
|
+
const sorted = [];
|
|
206
|
+
const visited = new Set();
|
|
207
|
+
const visiting = new Set();
|
|
208
|
+
function visit(entityName) {
|
|
209
|
+
if (visited.has(entityName))
|
|
210
|
+
return;
|
|
211
|
+
if (visiting.has(entityName)) {
|
|
212
|
+
// Circular dependency - just add it
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
visiting.add(entityName);
|
|
216
|
+
const deps = graph.get(entityName) || new Set();
|
|
217
|
+
for (const dep of deps) {
|
|
218
|
+
visit(dep);
|
|
219
|
+
}
|
|
220
|
+
visiting.delete(entityName);
|
|
221
|
+
visited.add(entityName);
|
|
222
|
+
sorted.push(entityMap.get(entityName));
|
|
223
|
+
}
|
|
224
|
+
for (const entity of entities) {
|
|
225
|
+
visit(entity.name);
|
|
226
|
+
}
|
|
227
|
+
return sorted;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Generate main seed orchestrator
|
|
231
|
+
*/
|
|
232
|
+
function generateSeedOrchestrator(manifest) {
|
|
233
|
+
const lines = [];
|
|
234
|
+
const orderedEntities = analyzeEntityDependencies(manifest.entities);
|
|
235
|
+
lines.push(`/**`);
|
|
236
|
+
lines.push(` * Main seed orchestrator`);
|
|
237
|
+
lines.push(` * `);
|
|
238
|
+
lines.push(` * Seeds all entities in correct dependency order.`);
|
|
239
|
+
lines.push(` */`);
|
|
240
|
+
lines.push(``);
|
|
241
|
+
lines.push(`import { db } from '@/server/db'`);
|
|
242
|
+
// Import schemas for reset
|
|
243
|
+
const tableNames = orderedEntities.map(e => (0, utils_1.toSnakeCase)((0, utils_1.pluralize)(e.name)));
|
|
244
|
+
lines.push(`import { ${tableNames.join(', ')} } from '@/generated/db/schema'`);
|
|
245
|
+
lines.push(``);
|
|
246
|
+
// Import seed functions
|
|
247
|
+
for (const entity of orderedEntities) {
|
|
248
|
+
lines.push(`import { seed${(0, utils_1.pluralize)(entity.name)} } from './${(0, utils_1.toCamelCase)(entity.name)}'`);
|
|
249
|
+
}
|
|
250
|
+
lines.push(``);
|
|
251
|
+
// Reset function
|
|
252
|
+
lines.push(`/**`);
|
|
253
|
+
lines.push(` * Clear all data from database (in reverse dependency order)`);
|
|
254
|
+
lines.push(` */`);
|
|
255
|
+
lines.push(`export async function resetDatabase() {`);
|
|
256
|
+
lines.push(` console.log('🗑️ Clearing database...\\n')`);
|
|
257
|
+
lines.push(``);
|
|
258
|
+
// Delete in reverse order
|
|
259
|
+
for (const entity of [...orderedEntities].reverse()) {
|
|
260
|
+
const tableName = (0, utils_1.toSnakeCase)((0, utils_1.pluralize)(entity.name));
|
|
261
|
+
lines.push(` await db.delete(${tableName})`);
|
|
262
|
+
lines.push(` console.log(\`✓ Cleared ${(0, utils_1.pluralize)(entity.name).toLowerCase()}\`)`);
|
|
263
|
+
}
|
|
264
|
+
lines.push(``);
|
|
265
|
+
lines.push(` console.log('')`);
|
|
266
|
+
lines.push(`}`);
|
|
267
|
+
lines.push(``);
|
|
268
|
+
// Main seed function
|
|
269
|
+
lines.push(`/**`);
|
|
270
|
+
lines.push(` * Seed all entities`);
|
|
271
|
+
lines.push(` */`);
|
|
272
|
+
lines.push(`export async function seedAll(options: { reset?: boolean } = {}) {`);
|
|
273
|
+
lines.push(` console.log('🌱 Seeding database...\\n')`);
|
|
274
|
+
lines.push(``);
|
|
275
|
+
lines.push(` if (options.reset) {`);
|
|
276
|
+
lines.push(` await resetDatabase()`);
|
|
277
|
+
lines.push(` }`);
|
|
278
|
+
lines.push(``);
|
|
279
|
+
// Seed in dependency order
|
|
280
|
+
for (const entity of orderedEntities) {
|
|
281
|
+
const varName = (0, utils_1.toCamelCase)((0, utils_1.pluralize)(entity.name));
|
|
282
|
+
const hasOneRelations = Object.entries(entity.relations)
|
|
283
|
+
.filter(([_, rel]) => rel.type === 'hasOne');
|
|
284
|
+
if (hasOneRelations.length > 0) {
|
|
285
|
+
// Pass related IDs
|
|
286
|
+
const relatedIds = hasOneRelations
|
|
287
|
+
.map(([name, _]) => `${name}Ids: ${(0, utils_1.toCamelCase)((0, utils_1.pluralize)(name))}.map(x => x.id)`)
|
|
288
|
+
.join(', ');
|
|
289
|
+
lines.push(` const ${varName} = await seed${(0, utils_1.pluralize)(entity.name)}(10, { ${relatedIds} })`);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
lines.push(` const ${varName} = await seed${(0, utils_1.pluralize)(entity.name)}(10)`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
lines.push(``);
|
|
296
|
+
lines.push(` console.log('\\n✅ Seeding complete!')`);
|
|
297
|
+
lines.push(`}`);
|
|
298
|
+
return lines.join('\n');
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Generate CLI runner script
|
|
302
|
+
*/
|
|
303
|
+
function generateRunnerScript() {
|
|
304
|
+
return `#!/usr/bin/env tsx
|
|
305
|
+
/**
|
|
306
|
+
* Seed Database Script
|
|
307
|
+
*
|
|
308
|
+
* Usage:
|
|
309
|
+
* npm run seed # Seed with sample data
|
|
310
|
+
* npm run seed --reset # Reset database and seed
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
import { seedAll } from './index'
|
|
314
|
+
|
|
315
|
+
const shouldReset = process.argv.includes('--reset')
|
|
316
|
+
|
|
317
|
+
seedAll({ reset: shouldReset })
|
|
318
|
+
.then(() => {
|
|
319
|
+
console.log('\\n👋 Done!')
|
|
320
|
+
process.exit(0)
|
|
321
|
+
})
|
|
322
|
+
.catch((error) => {
|
|
323
|
+
console.error('\\n❌ Seeding failed:', error)
|
|
324
|
+
process.exit(1)
|
|
325
|
+
})
|
|
326
|
+
`;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Seed data generator
|
|
330
|
+
*/
|
|
331
|
+
exports.seedGenerator = {
|
|
332
|
+
name: 'seed-data',
|
|
333
|
+
description: 'Generates realistic seed data for development and testing',
|
|
334
|
+
generate(manifest, ctx) {
|
|
335
|
+
const files = [];
|
|
336
|
+
// Generate seed function for each entity
|
|
337
|
+
for (const entity of manifest.entities) {
|
|
338
|
+
const imports = [
|
|
339
|
+
`import { db } from '@/server/db'`,
|
|
340
|
+
`import { ${(0, utils_1.toSnakeCase)((0, utils_1.pluralize)(entity.name))} } from '@/generated/db/schema'`,
|
|
341
|
+
];
|
|
342
|
+
files.push({
|
|
343
|
+
path: `seeds/${(0, utils_1.toCamelCase)(entity.name)}.ts`,
|
|
344
|
+
content: imports.join('\n') + '\n\n' + generateEntitySeedFunction(entity),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
// Generate orchestrator
|
|
348
|
+
files.push({
|
|
349
|
+
path: 'seeds/index.ts',
|
|
350
|
+
content: generateSeedOrchestrator(manifest),
|
|
351
|
+
});
|
|
352
|
+
// Generate runner script
|
|
353
|
+
files.push({
|
|
354
|
+
path: 'seeds/run.ts',
|
|
355
|
+
content: generateRunnerScript(),
|
|
356
|
+
});
|
|
357
|
+
// Generate README
|
|
358
|
+
files.push({
|
|
359
|
+
path: 'seeds/README.md',
|
|
360
|
+
content: generateSeedReadme(manifest),
|
|
361
|
+
});
|
|
362
|
+
return files;
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* Generate README for seed data
|
|
367
|
+
*/
|
|
368
|
+
function generateSeedReadme(manifest) {
|
|
369
|
+
const lines = [];
|
|
370
|
+
lines.push('# Seed Data');
|
|
371
|
+
lines.push('');
|
|
372
|
+
lines.push('Auto-generated seed data for development and testing.');
|
|
373
|
+
lines.push('');
|
|
374
|
+
lines.push('## Usage');
|
|
375
|
+
lines.push('');
|
|
376
|
+
lines.push('```bash');
|
|
377
|
+
lines.push('# Seed database with sample data');
|
|
378
|
+
lines.push('npm run seed');
|
|
379
|
+
lines.push('');
|
|
380
|
+
lines.push('# Reset database and seed');
|
|
381
|
+
lines.push('npm run seed --reset');
|
|
382
|
+
lines.push('```');
|
|
383
|
+
lines.push('');
|
|
384
|
+
lines.push('## What Gets Seeded');
|
|
385
|
+
lines.push('');
|
|
386
|
+
const orderedEntities = analyzeEntityDependencies(manifest.entities);
|
|
387
|
+
for (const entity of orderedEntities) {
|
|
388
|
+
lines.push(`- **${(0, utils_1.pluralize)(entity.name)}**: 10 records`);
|
|
389
|
+
}
|
|
390
|
+
lines.push('');
|
|
391
|
+
lines.push('## Optional: Faker.js');
|
|
392
|
+
lines.push('');
|
|
393
|
+
lines.push('For more realistic data, install faker.js:');
|
|
394
|
+
lines.push('');
|
|
395
|
+
lines.push('```bash');
|
|
396
|
+
lines.push('npm install --save-dev @faker-js/faker');
|
|
397
|
+
lines.push('```');
|
|
398
|
+
lines.push('');
|
|
399
|
+
lines.push('If installed, faker will generate realistic:');
|
|
400
|
+
lines.push('- Names, emails, addresses');
|
|
401
|
+
lines.push('- Dates, numbers, booleans');
|
|
402
|
+
lines.push('- Lorem ipsum content');
|
|
403
|
+
lines.push('- URLs, phone numbers, etc.');
|
|
404
|
+
lines.push('');
|
|
405
|
+
lines.push('Without faker, simple but valid data is generated.');
|
|
406
|
+
return lines.join('\n');
|
|
407
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates comprehensive Vitest test suites for each entity's tRPC router.
|
|
5
|
+
* Tests are generated based on entity configuration (fields, validations, relations, protection).
|
|
6
|
+
*
|
|
7
|
+
* Generated files:
|
|
8
|
+
* - tests/{entity}.test.ts - Full CRUD test suite with validation, auth, relations, filters
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - CRUD operation tests (create, list, get, update, remove)
|
|
12
|
+
* - Validation tests (required fields, field constraints, type checking)
|
|
13
|
+
* - Authentication tests (protected operations require auth)
|
|
14
|
+
* - Relation tests (hasMany, belongsToMany associations)
|
|
15
|
+
* - Filter/search/pagination tests
|
|
16
|
+
* - Batch operation tests (createMany, updateMany, removeMany)
|
|
17
|
+
* - Soft delete tests (if enabled)
|
|
18
|
+
* - Computed field tests (if present)
|
|
19
|
+
*
|
|
20
|
+
* @module generators/test
|
|
21
|
+
*/
|
|
22
|
+
import type { Generator } from '../../../template/types';
|
|
23
|
+
/**
|
|
24
|
+
* Test generator - creates Vitest test files for tRPC routers
|
|
25
|
+
*/
|
|
26
|
+
export declare const testGenerator: Generator;
|
|
27
|
+
//# sourceMappingURL=test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,yBAAyB,CAAA;AAmgBvE;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,SAuB3B,CAAA"}
|