create-stackkit-app 0.4.5 → 0.4.6

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/dist/index.js CHANGED
@@ -47,7 +47,7 @@ Usage:
47
47
 
48
48
  Options:
49
49
  -f, --framework <framework> Framework: nextjs, express, react-vite
50
- -d, --database <database> Database: prisma-postgresql, prisma-mongodb, prisma-mysql, prisma-sqlite, mongoose-mongodb, none
50
+ -d, --database <database> Database: prisma, mongoose, none
51
51
  -a, --auth <auth> Auth: better-auth, authjs, none
52
52
  -l, --language <language> Language: typescript, javascript
53
53
  -p, --package-manager <pm> Package manager: pnpm, npm, yarn, bun
@@ -58,7 +58,6 @@ Options:
58
58
 
59
59
  Examples:
60
60
  create-stackkit-app my-app --framework nextjs --database prisma-postgresql --auth better-auth
61
- create-stackkit-app --help
62
61
  `);
63
62
  }
64
63
  async function main() {
@@ -0,0 +1,80 @@
1
+ import { FrameworkConfig } from './framework-utils';
2
+ export interface GenerationContext {
3
+ framework: string;
4
+ database?: string;
5
+ auth?: string;
6
+ features?: string[];
7
+ [key: string]: unknown;
8
+ }
9
+ export interface TemplateCondition {
10
+ framework?: string;
11
+ database?: string;
12
+ auth?: string;
13
+ features?: string[];
14
+ }
15
+ export interface Operation {
16
+ type: 'create-file' | 'patch-file' | 'add-dependency' | 'add-script' | 'add-env' | 'run-command';
17
+ description?: string;
18
+ condition?: TemplateCondition;
19
+ priority?: number;
20
+ source?: string;
21
+ destination?: string;
22
+ content?: string;
23
+ file?: string;
24
+ operations?: PatchOperation[];
25
+ dependencies?: Record<string, string>;
26
+ devDependencies?: Record<string, string>;
27
+ scripts?: Record<string, string>;
28
+ envVars?: Record<string, string>;
29
+ command?: string;
30
+ }
31
+ export interface PatchOperation {
32
+ type: 'add-import' | 'add-code' | 'replace-code' | 'add-to-top' | 'add-to-bottom';
33
+ condition?: TemplateCondition;
34
+ imports?: string[];
35
+ code?: string;
36
+ after?: string;
37
+ before?: string;
38
+ replace?: string;
39
+ content?: string;
40
+ }
41
+ export interface GeneratorConfig {
42
+ name: string;
43
+ type: 'framework' | 'database' | 'auth';
44
+ priority: number;
45
+ operations?: Operation[];
46
+ dependencies?: Record<string, string>;
47
+ devDependencies?: Record<string, string>;
48
+ scripts?: Record<string, string>;
49
+ envVars?: Record<string, string>;
50
+ }
51
+ export declare class AdvancedCodeGenerator {
52
+ private generators;
53
+ private frameworkConfig;
54
+ private postInstallCommands;
55
+ constructor(frameworkConfig: FrameworkConfig);
56
+ loadGenerators(modulesPath: string): Promise<void>;
57
+ private evaluateCondition;
58
+ private processTemplate;
59
+ generate(selectedModules: {
60
+ framework: string;
61
+ database?: string;
62
+ auth?: string;
63
+ prismaProvider?: string;
64
+ }, features: string[], outputPath: string): Promise<string[]>;
65
+ private executeOperation;
66
+ private copyTemplate;
67
+ private processOperationTemplates;
68
+ private executeCreateFile;
69
+ private executePatchFile;
70
+ private executeAddDependency;
71
+ private executeAddScript;
72
+ private executeAddEnv;
73
+ private executeRunCommand;
74
+ private generatePackageJson;
75
+ getAvailableGenerators(): {
76
+ frameworks: string[];
77
+ databases: string[];
78
+ auths: string[];
79
+ };
80
+ }
@@ -0,0 +1,541 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AdvancedCodeGenerator = void 0;
37
+ const fs = __importStar(require("fs-extra"));
38
+ const path = __importStar(require("path"));
39
+ class AdvancedCodeGenerator {
40
+ constructor(frameworkConfig) {
41
+ this.generators = new Map();
42
+ this.postInstallCommands = [];
43
+ this.frameworkConfig = frameworkConfig;
44
+ }
45
+ async loadGenerators(modulesPath) {
46
+ const moduleTypes = ['auth', 'database'];
47
+ // Load module generators
48
+ for (const type of moduleTypes) {
49
+ const typePath = path.join(modulesPath, type);
50
+ if (await fs.pathExists(typePath)) {
51
+ const modules = await fs.readdir(typePath);
52
+ for (const moduleName of modules) {
53
+ const generatorPath = path.join(typePath, moduleName, 'generator.json');
54
+ if (await fs.pathExists(generatorPath)) {
55
+ try {
56
+ const config = await fs.readJson(generatorPath);
57
+ this.generators.set(`${type}:${moduleName}`, config);
58
+ }
59
+ catch {
60
+ // Silently skip invalid generator files
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ evaluateCondition(condition, context) {
68
+ if (!condition)
69
+ return true;
70
+ for (const [key, value] of Object.entries(condition)) {
71
+ if (key === 'features') {
72
+ const requiredFeatures = value;
73
+ const contextFeatures = context.features || [];
74
+ if (!requiredFeatures.every(feature => contextFeatures.includes(feature))) {
75
+ return false;
76
+ }
77
+ }
78
+ else {
79
+ if (context[key] !== value) {
80
+ return false;
81
+ }
82
+ }
83
+ }
84
+ return true;
85
+ }
86
+ processTemplate(content, context) {
87
+ // Handle advanced conditional blocks {{#if condition operator value}}...{{/if}}
88
+ content = content.replace(/\{\{#if\s+([^}\s]+)\s+([^}\s]+)\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, varName, operator, expectedValue, blockContent) => {
89
+ const actualVal = context[varName.trim()];
90
+ const cleanExpectedVal = expectedValue.trim().replace(/['"]/g, '');
91
+ let conditionMet = false;
92
+ switch (operator) {
93
+ case '==':
94
+ case '===':
95
+ conditionMet = actualVal === cleanExpectedVal;
96
+ break;
97
+ case '!=':
98
+ case '!==':
99
+ conditionMet = actualVal !== cleanExpectedVal;
100
+ break;
101
+ case 'includes':
102
+ conditionMet = Array.isArray(actualVal) && actualVal.includes(cleanExpectedVal);
103
+ break;
104
+ case 'startsWith':
105
+ conditionMet = typeof actualVal === 'string' && actualVal.startsWith(cleanExpectedVal);
106
+ break;
107
+ case 'endsWith':
108
+ conditionMet = typeof actualVal === 'string' && actualVal.endsWith(cleanExpectedVal);
109
+ break;
110
+ }
111
+ return conditionMet ? this.processTemplate(blockContent, context) : '';
112
+ });
113
+ // Handle simple conditional blocks {{#if condition}}...{{/if}} (backward compatibility)
114
+ content = content.replace(/\{\{#if\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, condition, blockContent) => {
115
+ const conditionParts = condition.split('==');
116
+ if (conditionParts.length === 2) {
117
+ const [varName, expectedValue] = conditionParts.map((s) => s.trim().replace(/['"]/g, ''));
118
+ if (context[varName] === expectedValue) {
119
+ return this.processTemplate(blockContent, context);
120
+ }
121
+ return '';
122
+ }
123
+ const conditionFunc = condition.split('.');
124
+ if (conditionFunc.length === 2 && conditionFunc[1] === 'includes') {
125
+ const [arrayName, item] = conditionFunc[0].split('(');
126
+ const itemValue = item.replace(')', '').replace(/['"]/g, '');
127
+ const array = context[arrayName] || [];
128
+ if (Array.isArray(array) && array.includes(itemValue)) {
129
+ return this.processTemplate(blockContent, context);
130
+ }
131
+ return '';
132
+ }
133
+ return '';
134
+ });
135
+ // Handle switch statements {{#switch variable}}...{{/switch}}
136
+ content = content.replace(/\{\{#switch\s+([^}]+)\}\}([\s\S]*?)\{\{\/switch\}\}/g, (match, varName, switchContent) => {
137
+ const actualVal = context[varName.trim()];
138
+ // Parse cases
139
+ const caseRegex = /\{\{#case\s+([^}]+)\}\}([\s\S]*?)(?=\{\{#case|\{\{\/switch\})/g;
140
+ let result = '';
141
+ let defaultCase = '';
142
+ let caseMatch;
143
+ while ((caseMatch = caseRegex.exec(switchContent)) !== null) {
144
+ const [, caseValue, caseContent] = caseMatch;
145
+ const cleanCaseValue = caseValue.trim().replace(/['"]/g, '');
146
+ if (cleanCaseValue === 'default') {
147
+ defaultCase = caseContent;
148
+ }
149
+ else if (actualVal === cleanCaseValue) {
150
+ result = caseContent;
151
+ break;
152
+ }
153
+ }
154
+ return result || defaultCase || '';
155
+ });
156
+ // Handle variable replacement with advanced expressions
157
+ content = content.replace(/\{\{([^}]+)\}\}/g, (match, varExpr) => {
158
+ const trimmedExpr = varExpr.trim();
159
+ // Handle ternary expressions like framework=='nextjs' ? '@/lib' : '.'
160
+ const ternaryMatch = trimmedExpr.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+?)$/);
161
+ if (ternaryMatch) {
162
+ const [, condition, trueVal, falseVal] = ternaryMatch;
163
+ const conditionMatch = condition.match(/^(.+?)==(.+)$/);
164
+ if (conditionMatch) {
165
+ const [, varName, expectedVal] = conditionMatch;
166
+ const cleanVarName = varName.trim();
167
+ const cleanExpectedVal = expectedVal.trim().replace(/['"]/g, '');
168
+ const actualVal = context[cleanVarName];
169
+ const result = actualVal === cleanExpectedVal ? trueVal.trim() : falseVal.trim();
170
+ return result.replace(/['"]/g, ''); // Remove quotes from result
171
+ }
172
+ }
173
+ // Handle switch expressions {{switch variable case1: value1, case2: value2, default: defaultValue}}
174
+ const switchMatch = trimmedExpr.match(/^switch\s+([^}\s]+)\s+(.+)$/);
175
+ if (switchMatch) {
176
+ const [, varName, casesStr] = switchMatch;
177
+ const actualVal = context[varName.trim()];
178
+ const cases = casesStr.split(',').map((c) => c.trim());
179
+ for (const caseStr of cases) {
180
+ const [caseVal, result] = caseStr.split(':').map((s) => s.trim());
181
+ const cleanCaseVal = caseVal.replace(/['"]/g, '');
182
+ if (cleanCaseVal === actualVal || cleanCaseVal === 'default') {
183
+ return result.replace(/['"]/g, '');
184
+ }
185
+ }
186
+ return '';
187
+ }
188
+ // Handle feature flags {{feature:name}}
189
+ if (trimmedExpr.startsWith('feature:')) {
190
+ const featureName = trimmedExpr.substring(8);
191
+ const features = context.features || [];
192
+ return features.includes(featureName) ? 'true' : 'false';
193
+ }
194
+ // Handle conditional expressions {{if condition then:value else:value}}
195
+ const conditionalMatch = trimmedExpr.match(/^if\s+(.+?)\s+then:([^,]+),\s*else:(.+)$/);
196
+ if (conditionalMatch) {
197
+ const [, condition, thenVal, elseVal] = conditionalMatch;
198
+ const conditionMatch2 = condition.match(/^(.+?)==(.+)$/);
199
+ if (conditionMatch2) {
200
+ const [, varName, expectedVal] = conditionMatch2;
201
+ const cleanVarName = varName.trim();
202
+ const cleanExpectedVal = expectedVal.trim().replace(/['"]/g, '');
203
+ const actualVal = context[cleanVarName];
204
+ const result = actualVal === cleanExpectedVal ? thenVal.trim() : elseVal.trim();
205
+ return result.replace(/['"]/g, '');
206
+ }
207
+ }
208
+ // Simple variable replacement
209
+ const value = context[trimmedExpr];
210
+ return value !== undefined ? String(value) : match;
211
+ });
212
+ return content;
213
+ }
214
+ async generate(selectedModules, features, outputPath) {
215
+ // First, copy the base template
216
+ await this.copyTemplate(selectedModules.framework, outputPath);
217
+ const context = {
218
+ ...selectedModules,
219
+ features,
220
+ };
221
+ // Add prismaProvider if prismaProvider is specified
222
+ if (selectedModules.prismaProvider) {
223
+ context.prismaProvider = selectedModules.prismaProvider;
224
+ }
225
+ // Collect all applicable operations
226
+ const applicableOperations = [];
227
+ for (const [key, generator] of this.generators) {
228
+ const [genType, name] = key.split(':');
229
+ // Check if this generator is selected
230
+ if (genType === 'framework' && name === selectedModules.framework) {
231
+ // Framework is always included
232
+ }
233
+ else if (genType === 'database' && name === selectedModules.database) {
234
+ // Database is selected
235
+ }
236
+ else if (genType === 'auth' && name === selectedModules.auth) {
237
+ // Auth is selected
238
+ }
239
+ else {
240
+ continue; // Skip unselected generators
241
+ }
242
+ // Handle operations
243
+ const items = generator.operations || [];
244
+ for (const item of items) {
245
+ if (this.evaluateCondition(item.condition, context)) {
246
+ applicableOperations.push({
247
+ ...item,
248
+ generator: name,
249
+ generatorType: genType,
250
+ });
251
+ }
252
+ }
253
+ }
254
+ // Sort operations by priority
255
+ applicableOperations.sort((a, b) => {
256
+ const priorityA = a.priority || 0;
257
+ const priorityB = b.priority || 0;
258
+ return priorityA - priorityB;
259
+ });
260
+ // Execute operations
261
+ for (const operation of applicableOperations) {
262
+ await this.executeOperation(operation, context, outputPath);
263
+ }
264
+ // Generate package.json updates
265
+ await this.generatePackageJson(selectedModules, features, outputPath);
266
+ return this.postInstallCommands;
267
+ }
268
+ async executeOperation(operation, context, outputPath) {
269
+ // Process templates in operation content
270
+ const processedOperation = this.processOperationTemplates(operation, context);
271
+ switch (processedOperation.type) {
272
+ case 'create-file':
273
+ await this.executeCreateFile(processedOperation, context, outputPath);
274
+ break;
275
+ case 'patch-file':
276
+ await this.executePatchFile(processedOperation, context, outputPath);
277
+ break;
278
+ case 'add-dependency':
279
+ await this.executeAddDependency(processedOperation, context, outputPath);
280
+ break;
281
+ case 'add-script':
282
+ await this.executeAddScript(processedOperation, context, outputPath);
283
+ break;
284
+ case 'add-env':
285
+ await this.executeAddEnv(processedOperation, context, outputPath);
286
+ break;
287
+ case 'run-command':
288
+ this.executeRunCommand(processedOperation, context);
289
+ break;
290
+ default:
291
+ // Unknown operation type - skip silently
292
+ }
293
+ }
294
+ async copyTemplate(frameworkName, outputPath) {
295
+ const templatesPath = path.resolve(__dirname, '..', '..', 'templates');
296
+ const templatePath = path.join(templatesPath, frameworkName);
297
+ if (await fs.pathExists(templatePath)) {
298
+ // Copy all files except template.json and node_modules
299
+ await fs.copy(templatePath, outputPath, {
300
+ filter: (src) => {
301
+ const relativePath = path.relative(templatePath, src);
302
+ return relativePath !== 'template.json' &&
303
+ relativePath !== 'node_modules' &&
304
+ !relativePath.startsWith('node_modules/');
305
+ }
306
+ });
307
+ }
308
+ }
309
+ processOperationTemplates(operation, context) {
310
+ const processed = { ...operation };
311
+ // Process templates in string fields
312
+ if (processed.source) {
313
+ processed.source = this.processTemplate(processed.source, context);
314
+ }
315
+ if (processed.destination) {
316
+ processed.destination = this.processTemplate(processed.destination, context);
317
+ }
318
+ if (processed.content) {
319
+ processed.content = this.processTemplate(processed.content, context);
320
+ }
321
+ if (processed.file) {
322
+ processed.file = this.processTemplate(processed.file, context);
323
+ }
324
+ // Process templates in patch operations
325
+ if (processed.operations) {
326
+ processed.operations = processed.operations.map(op => {
327
+ const processedOp = { ...op };
328
+ if (processedOp.imports) {
329
+ processedOp.imports = processedOp.imports.map(imp => this.processTemplate(imp, context));
330
+ }
331
+ if (processedOp.code) {
332
+ processedOp.code = this.processTemplate(processedOp.code, context);
333
+ }
334
+ if (processedOp.after) {
335
+ processedOp.after = this.processTemplate(processedOp.after, context);
336
+ }
337
+ if (processedOp.before) {
338
+ processedOp.before = this.processTemplate(processedOp.before, context);
339
+ }
340
+ if (processedOp.replace) {
341
+ processedOp.replace = this.processTemplate(processedOp.replace, context);
342
+ }
343
+ if (processedOp.content) {
344
+ processedOp.content = this.processTemplate(processedOp.content, context);
345
+ }
346
+ return processedOp;
347
+ });
348
+ }
349
+ return processed;
350
+ }
351
+ async executeCreateFile(operation, context, outputPath) {
352
+ if (!operation.destination)
353
+ return;
354
+ const destinationPath = path.join(outputPath, this.processTemplate(operation.destination, context));
355
+ // Ensure directory exists
356
+ await fs.ensureDir(path.dirname(destinationPath));
357
+ let content;
358
+ if (operation.content) {
359
+ // Use content directly
360
+ content = this.processTemplate(operation.content, context);
361
+ }
362
+ else if (operation.source) {
363
+ // Find the source file path relative to the module/template directory
364
+ const modulesPath = path.join(__dirname, '..', '..', 'modules');
365
+ const templatesPath = path.join(__dirname, '..', '..', 'templates');
366
+ const moduleBasePath = operation.generatorType === 'framework'
367
+ ? path.join(templatesPath, operation.generator)
368
+ : path.join(modulesPath, operation.generatorType, operation.generator);
369
+ const sourcePath = path.join(moduleBasePath, 'files', operation.source);
370
+ // Check if source file exists
371
+ if (await fs.pathExists(sourcePath)) {
372
+ // Read source file
373
+ content = await fs.readFile(sourcePath, 'utf-8');
374
+ // Process template content
375
+ content = this.processTemplate(content, context);
376
+ }
377
+ else {
378
+ throw new Error(`Source file not found: ${sourcePath}`);
379
+ }
380
+ }
381
+ else {
382
+ throw new Error(`Create file operation must have either 'content' or 'source' field`);
383
+ }
384
+ // Write destination file
385
+ await fs.writeFile(destinationPath, content, 'utf-8');
386
+ }
387
+ async executePatchFile(operation, context, outputPath) {
388
+ if (!operation.file || !operation.operations)
389
+ return;
390
+ const filePath = path.join(outputPath, this.processTemplate(operation.file, context));
391
+ // Read existing file
392
+ let content = await fs.readFile(filePath, 'utf-8');
393
+ // Execute patch operations
394
+ for (const patchOp of operation.operations) {
395
+ if (!this.evaluateCondition(patchOp.condition, context))
396
+ continue;
397
+ switch (patchOp.type) {
398
+ case 'add-import':
399
+ if (patchOp.imports) {
400
+ const imports = patchOp.imports.map(imp => this.processTemplate(imp, context)).join('\n');
401
+ // Add imports at the top, after existing imports
402
+ const lines = content.split('\n');
403
+ let insertIndex = 0;
404
+ for (let i = 0; i < lines.length; i++) {
405
+ if (lines[i].trim().startsWith('import') || lines[i].trim() === '') {
406
+ insertIndex = i + 1;
407
+ }
408
+ else {
409
+ break;
410
+ }
411
+ }
412
+ lines.splice(insertIndex, 0, imports);
413
+ content = lines.join('\n');
414
+ }
415
+ break;
416
+ case 'add-code':
417
+ if (patchOp.code && patchOp.after) {
418
+ const processedCode = this.processTemplate(patchOp.code, context);
419
+ const afterPattern = this.processTemplate(patchOp.after, context);
420
+ const index = content.indexOf(afterPattern);
421
+ if (index !== -1) {
422
+ content = content.slice(0, index + afterPattern.length) + processedCode + content.slice(index + afterPattern.length);
423
+ }
424
+ }
425
+ break;
426
+ case 'replace-code':
427
+ if (patchOp.code && patchOp.replace) {
428
+ const processedCode = this.processTemplate(patchOp.code, context);
429
+ const replacePattern = this.processTemplate(patchOp.replace, context);
430
+ content = content.replace(replacePattern, processedCode);
431
+ }
432
+ break;
433
+ case 'add-to-top':
434
+ if (patchOp.content) {
435
+ const processedContent = this.processTemplate(patchOp.content, context);
436
+ content = processedContent + '\n' + content;
437
+ }
438
+ break;
439
+ case 'add-to-bottom':
440
+ if (patchOp.content) {
441
+ const processedContent = this.processTemplate(patchOp.content, context);
442
+ content = content + '\n' + processedContent;
443
+ }
444
+ break;
445
+ }
446
+ }
447
+ // Write back the modified content
448
+ await fs.writeFile(filePath, content, 'utf-8');
449
+ }
450
+ async executeAddDependency(operation, context, outputPath) {
451
+ const packageJsonPath = path.join(outputPath, 'package.json');
452
+ let packageJson = {};
453
+ if (await fs.pathExists(packageJsonPath)) {
454
+ packageJson = await fs.readJson(packageJsonPath);
455
+ }
456
+ if (operation.dependencies) {
457
+ packageJson.dependencies = { ...(packageJson.dependencies || {}), ...operation.dependencies };
458
+ }
459
+ if (operation.devDependencies) {
460
+ packageJson.devDependencies = { ...(packageJson.devDependencies || {}), ...operation.devDependencies };
461
+ }
462
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
463
+ }
464
+ async executeAddScript(operation, context, outputPath) {
465
+ const packageJsonPath = path.join(outputPath, 'package.json');
466
+ let packageJson = {};
467
+ if (await fs.pathExists(packageJsonPath)) {
468
+ packageJson = await fs.readJson(packageJsonPath);
469
+ }
470
+ if (operation.scripts) {
471
+ packageJson.scripts = { ...(packageJson.scripts || {}), ...operation.scripts };
472
+ }
473
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
474
+ }
475
+ async executeAddEnv(operation, context, outputPath) {
476
+ const envPath = path.join(outputPath, '.env');
477
+ let envContent = '';
478
+ if (await fs.pathExists(envPath)) {
479
+ envContent = await fs.readFile(envPath, 'utf-8');
480
+ }
481
+ if (operation.envVars) {
482
+ const envLines = Object.entries(operation.envVars).map(([key, value]) => `${key}=${value}`);
483
+ envContent += '\n' + envLines.join('\n');
484
+ }
485
+ await fs.writeFile(envPath, envContent.trim(), 'utf-8');
486
+ }
487
+ executeRunCommand(operation, context) {
488
+ if (operation.command) {
489
+ // Process template variables in the command
490
+ const processedCommand = this.processTemplate(operation.command, context);
491
+ this.postInstallCommands.push(processedCommand);
492
+ }
493
+ }
494
+ async generatePackageJson(selectedModules, features, outputPath) {
495
+ const packageJsonPath = path.join(outputPath, 'package.json');
496
+ let packageJson = {};
497
+ if (await fs.pathExists(packageJsonPath)) {
498
+ packageJson = await fs.readJson(packageJsonPath);
499
+ }
500
+ // Collect dependencies from selected generators
501
+ const allDeps = {};
502
+ const allDevDeps = {};
503
+ const allScripts = {};
504
+ for (const [key, generator] of this.generators) {
505
+ const [type, name] = key.split(':');
506
+ if ((type === 'framework' && name === selectedModules.framework) ||
507
+ (type === 'database' && name === selectedModules.database) ||
508
+ (type === 'auth' && name === selectedModules.auth)) {
509
+ Object.assign(allDeps, generator.dependencies);
510
+ Object.assign(allDevDeps, generator.devDependencies);
511
+ Object.assign(allScripts, generator.scripts);
512
+ }
513
+ }
514
+ // Update package.json
515
+ packageJson.dependencies = { ...(packageJson.dependencies || {}), ...allDeps };
516
+ packageJson.devDependencies = { ...(packageJson.devDependencies || {}), ...allDevDeps };
517
+ packageJson.scripts = { ...(packageJson.scripts || {}), ...allScripts };
518
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
519
+ }
520
+ getAvailableGenerators() {
521
+ const frameworks = [];
522
+ const databases = [];
523
+ const auths = [];
524
+ for (const [key] of this.generators) {
525
+ const [type, name] = key.split(':');
526
+ switch (type) {
527
+ case 'framework':
528
+ frameworks.push(name);
529
+ break;
530
+ case 'database':
531
+ databases.push(name);
532
+ break;
533
+ case 'auth':
534
+ auths.push(name);
535
+ break;
536
+ }
537
+ }
538
+ return { frameworks, databases, auths };
539
+ }
540
+ }
541
+ exports.AdvancedCodeGenerator = AdvancedCodeGenerator;