stackkit 0.2.9 → 0.3.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.
Files changed (31) hide show
  1. package/bin/stackkit.js +8 -5
  2. package/dist/cli/add.js +4 -9
  3. package/dist/cli/create.js +4 -9
  4. package/dist/cli/doctor.js +11 -32
  5. package/dist/lib/constants.js +3 -4
  6. package/dist/lib/conversion/js-conversion.js +20 -16
  7. package/dist/lib/discovery/installed-detection.js +28 -38
  8. package/dist/lib/discovery/module-discovery.d.ts +0 -15
  9. package/dist/lib/discovery/module-discovery.js +15 -50
  10. package/dist/lib/framework/framework-utils.d.ts +4 -5
  11. package/dist/lib/framework/framework-utils.js +38 -49
  12. package/dist/lib/generation/code-generator.d.ts +11 -12
  13. package/dist/lib/generation/code-generator.js +54 -134
  14. package/dist/lib/generation/generator-utils.js +3 -15
  15. package/dist/lib/project/detect.js +11 -19
  16. package/modules/auth/better-auth/files/express/middlewares/authorize.ts +66 -8
  17. package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +7 -0
  18. package/modules/auth/better-auth/files/express/modules/auth/auth.route.ts +5 -0
  19. package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +145 -11
  20. package/modules/auth/better-auth/files/express/modules/auth/{auth.interface.ts → auth.type.ts} +16 -6
  21. package/modules/auth/better-auth/files/shared/config/env.ts +8 -4
  22. package/modules/auth/better-auth/files/shared/lib/auth.ts +24 -25
  23. package/modules/auth/better-auth/files/shared/mongoose/auth/constants.ts +11 -0
  24. package/modules/auth/better-auth/files/shared/mongoose/auth/helper.ts +51 -0
  25. package/modules/auth/better-auth/files/shared/utils/email.ts +0 -1
  26. package/modules/auth/better-auth/generator.json +24 -20
  27. package/modules/database/mongoose/files/lib/mongoose.ts +28 -3
  28. package/modules/database/mongoose/generator.json +1 -1
  29. package/package.json +2 -2
  30. package/templates/express/env.example +1 -0
  31. package/templates/express/src/config/env.ts +4 -2
@@ -46,6 +46,25 @@ class AdvancedCodeGenerator {
46
46
  this.createdFiles = [];
47
47
  this.frameworkConfig = frameworkConfig;
48
48
  }
49
+ initializeContext(selectedModules, features) {
50
+ const context = {
51
+ ...selectedModules,
52
+ features,
53
+ combo: `${selectedModules.database || ""}:${selectedModules.framework || ""}`,
54
+ };
55
+ if (selectedModules.database === "prisma" && !context.prismaProvider) {
56
+ const providers = (0, shared_1.getPrismaProvidersFromGenerator)((0, package_root_1.getPackageRoot)());
57
+ if (providers.length > 0) {
58
+ context.prismaProvider = providers[0];
59
+ }
60
+ }
61
+ return context;
62
+ }
63
+ isGeneratorSelected(genType, name, selectedModules) {
64
+ return ((genType === "framework" && name === selectedModules.framework) ||
65
+ (genType === "database" && name === selectedModules.database) ||
66
+ (genType === "auth" && name === selectedModules.auth));
67
+ }
49
68
  async loadGenerators(modulesPath) {
50
69
  const moduleTypes = ["auth", "database"];
51
70
  for (const type of moduleTypes) {
@@ -62,7 +81,7 @@ class AdvancedCodeGenerator {
62
81
  this.generators.set(`${type}:${moduleName}`, config);
63
82
  }
64
83
  catch {
65
- // ignore invalid generator files
84
+ continue;
66
85
  }
67
86
  }
68
87
  }
@@ -96,15 +115,10 @@ class AdvancedCodeGenerator {
96
115
  return true;
97
116
  }
98
117
  processTemplate(content, context) {
99
- // Create a copy of context for template variables
100
118
  const templateContext = { ...context };
101
- // Handle variable definitions {{#var name = value}} at the top of the file
102
119
  content = this.processVariableDefinitions(content, templateContext);
103
- // Process the rest of the template with the extended context
104
120
  content = this.processTemplateRecursive(content, templateContext);
105
- // Remove leading newlines that might be left from {{#var}} removal
106
121
  content = content.replace(/^\n+/, "");
107
- // Reduce multiple consecutive newlines to maximum 2
108
122
  content = content.replace(/\n{3,}/g, "\n\n");
109
123
  return content;
110
124
  }
@@ -128,14 +142,13 @@ class AdvancedCodeGenerator {
128
142
  const varNameMatch = result.substring(varStart + 7, equalsIndex).trim();
129
143
  if (!varNameMatch)
130
144
  break;
131
- // Find the closing }} for the variable definition, accounting for nested {{#var}}...{{/var}}
132
145
  let braceCount = 1;
133
146
  const valueStart = equalsIndex + 1;
134
147
  let valueEnd = valueStart;
135
148
  for (let i = valueStart; i < result.length; i++) {
136
149
  if (result[i] === "{" && result[i + 1] === "{") {
137
150
  braceCount++;
138
- i++; // Skip next character
151
+ i++;
139
152
  }
140
153
  else if (result[i] === "}" && result[i + 1] === "}") {
141
154
  braceCount--;
@@ -143,17 +156,15 @@ class AdvancedCodeGenerator {
143
156
  valueEnd = i;
144
157
  break;
145
158
  }
146
- i++; // Skip next character
159
+ i++;
147
160
  }
148
161
  }
149
162
  if (valueEnd === valueStart)
150
163
  break;
151
164
  const varValue = result.substring(valueStart, valueEnd).trim();
152
165
  const fullMatch = result.substring(varStart, valueEnd + 2);
153
- // Process the variable value with current context (allowing nested variables/conditionals)
154
166
  const processedValue = this.processTemplateRecursive(varValue, context);
155
167
  context[varNameMatch] = processedValue;
156
- // Remove the variable definition
157
168
  result = result.replace(fullMatch, "");
158
169
  index = varStart;
159
170
  }
@@ -188,7 +199,6 @@ class AdvancedCodeGenerator {
188
199
  .replace(/^\n+/, "")
189
200
  .replace(/\n+$/, "");
190
201
  });
191
- // Handle simple conditional blocks {{#if condition}}...{{/if}} (backward compatibility)
192
202
  content = content.replace(/\{\{#if\s+([^}]+)\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}/g, (match, condition, blockContent, elseContent) => {
193
203
  const conditionParts = condition.split("==");
194
204
  if (conditionParts.length === 2) {
@@ -210,10 +220,8 @@ class AdvancedCodeGenerator {
210
220
  }
211
221
  return "";
212
222
  });
213
- // Handle switch statements {{#switch variable}}...{{/switch}}
214
223
  content = content.replace(/\{\{#switch\s+([^}]+)\}\}([\s\S]*?)\{\{\/switch\}\}/g, (match, varName, switchContent) => {
215
224
  const actualVal = context[varName.trim()];
216
- // Parse cases
217
225
  const caseRegex = /\{\{#case\s+([^}]+)\}\}([\s\S]*?)(?=\{\{#case|\{\{\/case\}|\{\{\/switch\})/g;
218
226
  let result = "";
219
227
  let defaultCase = "";
@@ -232,10 +240,8 @@ class AdvancedCodeGenerator {
232
240
  const chosen = (result || defaultCase || "").trim();
233
241
  return this.processTemplateRecursive(chosen, context);
234
242
  });
235
- // Handle variable replacement with advanced expressions
236
243
  content = content.replace(/\{\{([^}]+)\}\}/g, (match, varExpr) => {
237
244
  const trimmedExpr = varExpr.trim();
238
- // Handle ternary expressions like framework=='nextjs' ? '@/lib' : '.'
239
245
  const ternaryMatch = trimmedExpr.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+?)$/);
240
246
  if (ternaryMatch) {
241
247
  const [, condition, trueVal, falseVal] = ternaryMatch;
@@ -246,10 +252,9 @@ class AdvancedCodeGenerator {
246
252
  const cleanExpectedVal = expectedVal.trim().replace(/['"]/g, "");
247
253
  const actualVal = context[cleanVarName];
248
254
  const result = actualVal === cleanExpectedVal ? trueVal.trim() : falseVal.trim();
249
- return result.replace(/['"]/g, ""); // Remove quotes from result
255
+ return result.replace(/['"]/g, "");
250
256
  }
251
257
  }
252
- // Handle switch expressions {{switch variable case1: value1, case2: value2, default: defaultValue}}
253
258
  const switchMatch = trimmedExpr.match(/^switch\s+([^}\s]+)\s+(.+)$/);
254
259
  if (switchMatch) {
255
260
  const [, varName, casesStr] = switchMatch;
@@ -264,17 +269,14 @@ class AdvancedCodeGenerator {
264
269
  }
265
270
  return "";
266
271
  }
267
- // Handle heading helper {{heading:depthVar}} -> '#', '##', ... up to '######'
268
272
  if (trimmedExpr.startsWith("heading:")) {
269
273
  return this.renderHeadingFromExpr(trimmedExpr, context);
270
274
  }
271
- // Handle feature flags {{feature:name}}
272
275
  if (trimmedExpr.startsWith("feature:")) {
273
276
  const featureName = trimmedExpr.substring(8).trim();
274
277
  const features = Array.isArray(context.features) ? context.features : [];
275
278
  return features.includes(featureName) ? "true" : "false";
276
279
  }
277
- // Handle conditional expressions {{if condition then:value else:value}}
278
280
  const conditionalMatch = trimmedExpr.match(/^if\s+(.+?)\s+then:([^,]+),\s*else:(.+)$/);
279
281
  if (conditionalMatch) {
280
282
  const [, condition, thenVal, elseVal] = conditionalMatch;
@@ -288,50 +290,23 @@ class AdvancedCodeGenerator {
288
290
  return result.replace(/['"]/g, "");
289
291
  }
290
292
  }
291
- // Simple variable replacement
292
293
  const value = context[trimmedExpr];
293
294
  return value !== undefined ? String(value) : match;
294
295
  });
295
296
  return content;
296
297
  }
297
298
  async generate(selectedModules, features, outputPath) {
298
- // First, copy the base template
299
299
  await this.copyTemplate(selectedModules.framework, outputPath);
300
- const context = {
301
- ...selectedModules,
302
- features,
303
- };
304
- // Derived combined key to simplify template conditionals (e.g. "prisma:express")
305
- context.combo = `${context.database || ""}:${context.framework || ""}`;
306
- // Set default prismaProvider if database is prisma but no provider specified
307
- if (selectedModules.database === "prisma" && !context.prismaProvider) {
308
- const providers = (0, shared_1.getPrismaProvidersFromGenerator)((0, package_root_1.getPackageRoot)());
309
- if (providers && providers.length > 0) {
310
- context.prismaProvider = providers[0];
311
- }
312
- }
313
- // Collect all applicable operations
300
+ const context = this.initializeContext(selectedModules, features);
314
301
  const applicableOperations = [];
315
302
  for (const [key, generator] of this.generators) {
316
303
  const [genType, name] = key.split(":");
317
- // Check if this generator is selected
318
- if (genType === "framework" && name === selectedModules.framework) {
319
- // Framework is always included
320
- }
321
- else if (genType === "database" && name === selectedModules.database) {
322
- // Database is selected
323
- }
324
- else if (genType === "auth" && name === selectedModules.auth) {
325
- // Auth is selected
326
- }
327
- else {
328
- continue; // Skip unselected generators
304
+ if (!this.isGeneratorSelected(genType, name, selectedModules)) {
305
+ continue;
329
306
  }
330
- // Collect postInstall commands from selected generators
331
307
  if (generator.postInstall && Array.isArray(generator.postInstall)) {
332
308
  this.postInstallCommands.push(...generator.postInstall);
333
309
  }
334
- // Handle operations
335
310
  const items = generator.operations || [];
336
311
  for (const item of items) {
337
312
  if (this.evaluateCondition(item.condition, context)) {
@@ -344,25 +319,21 @@ class AdvancedCodeGenerator {
344
319
  }
345
320
  }
346
321
  }
347
- // Sort operations by priority
348
322
  applicableOperations.sort((a, b) => {
349
323
  const priorityA = a.priority || 0;
350
324
  const priorityB = b.priority || 0;
351
325
  return priorityA - priorityB;
352
326
  });
353
- // Execute operations
354
327
  for (const operation of applicableOperations) {
355
328
  await this.executeOperation(operation, context, outputPath);
356
329
  }
357
- // Generate package.json updates
358
- await this.generatePackageJson(selectedModules, features, outputPath);
330
+ await this.generatePackageJson(selectedModules, outputPath);
359
331
  return this.postInstallCommands;
360
332
  }
361
333
  getCreatedFiles() {
362
334
  return this.createdFiles.slice();
363
335
  }
364
336
  async executeOperation(operation, context, outputPath) {
365
- // Process templates in operation content
366
337
  const processedOperation = this.processOperationTemplates(operation, context);
367
338
  switch (processedOperation.type) {
368
339
  case "create-file":
@@ -372,10 +343,10 @@ class AdvancedCodeGenerator {
372
343
  await this.executePatchFile(processedOperation, context, outputPath);
373
344
  break;
374
345
  case "add-dependency":
375
- await this.executeAddDependency(processedOperation, context, outputPath);
346
+ await this.executeAddDependency(processedOperation, outputPath);
376
347
  break;
377
348
  case "add-script":
378
- await this.executeAddScript(processedOperation, context, outputPath);
349
+ await this.executeAddScript(processedOperation, outputPath);
379
350
  break;
380
351
  case "add-env":
381
352
  await this.executeAddEnv(processedOperation, context, outputPath);
@@ -384,7 +355,7 @@ class AdvancedCodeGenerator {
384
355
  this.executeRunCommand(processedOperation, context);
385
356
  break;
386
357
  default:
387
- // Unknown operation type - skip silently
358
+ return;
388
359
  }
389
360
  }
390
361
  async copyTemplate(frameworkName, outputPath) {
@@ -412,8 +383,8 @@ class AdvancedCodeGenerator {
412
383
  }
413
384
  }
414
385
  }
415
- catch {
416
- // ignore
386
+ catch (error) {
387
+ void error;
417
388
  }
418
389
  try {
419
390
  const templateJsonPath = path.join(templatePath, "template.json");
@@ -424,8 +395,7 @@ class AdvancedCodeGenerator {
424
395
  if (typeof f === "string" && f.startsWith(".")) {
425
396
  const targetDest = path.join(outputPath, f);
426
397
  if (await fs.pathExists(targetDest))
427
- continue; // already present
428
- // Special-case: allow creating .gitignore from non-dot fallbacks
398
+ continue;
429
399
  if (f === ".gitignore") {
430
400
  const nameWithoutDot = f.slice(1);
431
401
  const candidates = [f, nameWithoutDot, `_${nameWithoutDot}`];
@@ -438,7 +408,6 @@ class AdvancedCodeGenerator {
438
408
  }
439
409
  continue;
440
410
  }
441
- // For other dotfiles, only copy if the exact dotfile exists in the template to avoid unintended fallbacks
442
411
  const srcDot = path.join(templatePath, f);
443
412
  if (await fs.pathExists(srcDot)) {
444
413
  await fs.copy(srcDot, targetDest);
@@ -448,8 +417,8 @@ class AdvancedCodeGenerator {
448
417
  }
449
418
  }
450
419
  }
451
- catch {
452
- // ignore
420
+ catch (error) {
421
+ void error;
453
422
  }
454
423
  try {
455
424
  const templateJsonPath2 = path.join(templatePath, "template.json");
@@ -462,7 +431,6 @@ class AdvancedCodeGenerator {
462
431
  const nameWithoutDot = f.slice(1);
463
432
  const nonDot = path.join(outputPath, nameWithoutDot);
464
433
  const underscore = path.join(outputPath, `_${nameWithoutDot}`);
465
- // If dot already exists, remove non-dot fallbacks
466
434
  if (await fs.pathExists(dotDest)) {
467
435
  if (await fs.pathExists(nonDot)) {
468
436
  await fs.remove(nonDot);
@@ -472,7 +440,6 @@ class AdvancedCodeGenerator {
472
440
  }
473
441
  continue;
474
442
  }
475
- // If dot doesn't exist but a non-dot fallback was copied, rename it
476
443
  if (await fs.pathExists(nonDot)) {
477
444
  await fs.move(nonDot, dotDest, { overwrite: true });
478
445
  }
@@ -484,10 +451,9 @@ class AdvancedCodeGenerator {
484
451
  }
485
452
  }
486
453
  }
487
- catch {
488
- // ignore
454
+ catch (error) {
455
+ void error;
489
456
  }
490
- // Handle .env.example -> .env copying if .env doesn't already exist
491
457
  try {
492
458
  const envExampleDest = path.join(outputPath, ".env.example");
493
459
  const envDest = path.join(outputPath, ".env");
@@ -495,14 +461,13 @@ class AdvancedCodeGenerator {
495
461
  await fs.copy(envExampleDest, envDest);
496
462
  }
497
463
  }
498
- catch {
499
- // ignore (not critical)
464
+ catch (error) {
465
+ void error;
500
466
  }
501
467
  }
502
468
  }
503
469
  processOperationTemplates(operation, context) {
504
470
  const processed = { ...operation };
505
- // Process templates in string fields
506
471
  if (processed.source) {
507
472
  processed.source = this.processTemplate(processed.source, context);
508
473
  }
@@ -512,7 +477,6 @@ class AdvancedCodeGenerator {
512
477
  if (processed.content) {
513
478
  processed.content = this.processTemplate(processed.content, context);
514
479
  }
515
- // Process templates in patch operations
516
480
  if (processed.operations) {
517
481
  processed.operations = processed.operations.map((op) => {
518
482
  const processedOp = { ...op };
@@ -564,8 +528,8 @@ class AdvancedCodeGenerator {
564
528
  if (rel && !this.createdFiles.includes(rel))
565
529
  this.createdFiles.push(rel);
566
530
  }
567
- catch {
568
- // ignore logging failures
531
+ catch (error) {
532
+ void error;
569
533
  }
570
534
  return;
571
535
  }
@@ -622,8 +586,8 @@ class AdvancedCodeGenerator {
622
586
  if (rel && !this.createdFiles.includes(rel))
623
587
  this.createdFiles.push(rel);
624
588
  }
625
- catch {
626
- // ignore logging failures
589
+ catch (error) {
590
+ void error;
627
591
  }
628
592
  }
629
593
  return;
@@ -640,8 +604,8 @@ class AdvancedCodeGenerator {
640
604
  if (rel && !this.createdFiles.includes(rel))
641
605
  this.createdFiles.push(rel);
642
606
  }
643
- catch {
644
- // ignore logging failures
607
+ catch (error) {
608
+ void error;
645
609
  }
646
610
  }
647
611
  parsePathPattern(inputPath) {
@@ -673,13 +637,11 @@ class AdvancedCodeGenerator {
673
637
  if (!operation.destination)
674
638
  return;
675
639
  const filePath = path.join(outputPath, this.processTemplate(operation.destination, context));
676
- // Read existing file
677
640
  let content = await fs.readFile(filePath, "utf-8");
678
641
  if (operation.content) {
679
642
  content += this.processTemplate(operation.content, context).trim();
680
643
  }
681
644
  else if (operation.operations) {
682
- // Execute patch operations
683
645
  for (const patchOp of operation.operations) {
684
646
  if (!this.evaluateCondition(patchOp.condition, context))
685
647
  continue;
@@ -691,31 +653,25 @@ class AdvancedCodeGenerator {
691
653
  .join("\n")
692
654
  .replace(/^\n+/, "")
693
655
  .replace(/\n+$/, "");
694
- // Split content into lines for easier manipulation
695
656
  const lines = content.split("\n");
696
- // Find the last import line index
697
657
  let lastImportIndex = -1;
698
658
  for (let i = 0; i < lines.length; i++) {
699
659
  if (lines[i].trim().startsWith("import"))
700
660
  lastImportIndex = i;
701
661
  }
702
- // Determine where to insert new imports: after the last existing import, or at the top if no imports exist
703
662
  const insertIndex = lastImportIndex === -1 ? 0 : lastImportIndex + 1;
704
- // Only add imports that don't already exist in the file
705
663
  const importLines = imports
706
664
  .split("\n")
707
665
  .map((l) => l.trim())
708
666
  .filter(Boolean);
709
667
  const newImportLines = importLines.filter((imp) => !lines.some((ln) => ln.trim() === imp));
710
668
  if (newImportLines.length > 0) {
711
- // Insert imports
712
669
  if (insertIndex < lines.length && lines[insertIndex].trim() === "") {
713
670
  lines.splice(insertIndex, 1, ...newImportLines);
714
671
  }
715
672
  else {
716
673
  lines.splice(insertIndex, 0, ...newImportLines);
717
674
  }
718
- // After adding imports, ensure there is exactly one blank line after the last import statement (if there are any imports)
719
675
  let lastIdx = -1;
720
676
  for (let i = 0; i < lines.length; i++) {
721
677
  if (lines[i].trim().startsWith("import"))
@@ -727,7 +683,6 @@ class AdvancedCodeGenerator {
727
683
  while (j < lines.length && lines[j].trim() === "") {
728
684
  j++;
729
685
  }
730
- // Ensure exactly one blank line after imports unless imports end at EOF
731
686
  if (nextIdx < lines.length) {
732
687
  lines.splice(nextIdx, j - nextIdx, "");
733
688
  }
@@ -742,21 +697,17 @@ class AdvancedCodeGenerator {
742
697
  if (Array.isArray(codeValue))
743
698
  codeValue = codeValue.join("\n");
744
699
  const processedCode = this.processTemplate(codeValue, context);
745
- // Skip insertion if the exact code already exists in the file
746
700
  const codeTrimmed = processedCode.trim();
747
701
  if (codeTrimmed && content.includes(codeTrimmed)) {
748
702
  break;
749
703
  }
750
- // Insert after pattern if provided
751
704
  if (patchOp.after) {
752
705
  const afterPattern = this.processTemplate(patchOp.after, context);
753
706
  const index = content.indexOf(afterPattern);
754
707
  if (index !== -1) {
755
708
  const left = content.slice(0, index + afterPattern.length);
756
709
  const right = content.slice(index + afterPattern.length);
757
- // Normalize code: trim surrounding newlines and ensure single trailing newline
758
710
  let codeNormalized = processedCode.replace(/^\n+|\n+$/g, "") + "\n";
759
- // If right already starts with a newline, avoid double-blank by
760
711
  const rightStartsWithNewline = right.startsWith("\n");
761
712
  if (rightStartsWithNewline && codeNormalized.endsWith("\n")) {
762
713
  codeNormalized = codeNormalized.replace(/\n+$/, "");
@@ -765,16 +716,13 @@ class AdvancedCodeGenerator {
765
716
  content = left + (leftNeedsNewline ? "\n" : "") + codeNormalized + right;
766
717
  }
767
718
  }
768
- // Insert before pattern if provided
769
719
  if (patchOp.before) {
770
720
  const beforePattern = this.processTemplate(patchOp.before, context);
771
721
  const index = content.indexOf(beforePattern);
772
722
  if (index !== -1) {
773
723
  const left = content.slice(0, index);
774
724
  const right = content.slice(index);
775
- // Normalize code: trim surrounding newlines and ensure single trailing newline
776
725
  let codeNormalized = processedCode.replace(/^\n+|\n+$/g, "") + "\n";
777
- // If right already starts with a newline, avoid double-blank by
778
726
  const rightStartsWithNewline = right.startsWith("\n");
779
727
  if (rightStartsWithNewline && codeNormalized.endsWith("\n")) {
780
728
  codeNormalized = codeNormalized.replace(/\n+$/, "");
@@ -834,12 +782,10 @@ class AdvancedCodeGenerator {
834
782
  }
835
783
  }
836
784
  }
837
- // Normalize excessive blank lines introduced during patching
838
785
  content = content.replace(/\n{3,}/g, "\n\n");
839
- // Write back the modified content
840
786
  await fs.writeFile(filePath, content, "utf-8");
841
787
  }
842
- async executeAddDependency(operation, context, outputPath) {
788
+ async executeAddDependency(operation, outputPath) {
843
789
  const packageJsonPath = path.join(outputPath, "package.json");
844
790
  let packageJson = {};
845
791
  if (await fs.pathExists(packageJsonPath)) {
@@ -859,7 +805,7 @@ class AdvancedCodeGenerator {
859
805
  }
860
806
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
861
807
  }
862
- async executeAddScript(operation, context, outputPath) {
808
+ async executeAddScript(operation, outputPath) {
863
809
  const packageJsonPath = path.join(outputPath, "package.json");
864
810
  let packageJson = {};
865
811
  if (await fs.pathExists(packageJsonPath)) {
@@ -890,31 +836,27 @@ class AdvancedCodeGenerator {
890
836
  }
891
837
  executeRunCommand(operation, context) {
892
838
  if (operation.command) {
893
- // Process template variables in the command
894
839
  const processedCommand = this.processTemplate(operation.command, context);
895
840
  this.postInstallCommands.push(processedCommand);
896
841
  }
897
842
  }
898
- async generatePackageJson(selectedModules, features, outputPath) {
843
+ async generatePackageJson(selectedModules, outputPath) {
899
844
  const packageJsonPath = path.join(outputPath, "package.json");
900
845
  let packageJson = {};
901
846
  if (await fs.pathExists(packageJsonPath)) {
902
847
  packageJson = await fs.readJson(packageJsonPath);
903
848
  }
904
- // Collect dependencies from selected generators
905
849
  const allDeps = {};
906
850
  const allDevDeps = {};
907
851
  const allScripts = {};
908
852
  for (const [key, generator] of this.generators) {
909
853
  const [type, name] = key.split(":");
910
- if ((type === "framework" && name === selectedModules.framework) ||
911
- (type === "database" && name === selectedModules.database) ||
912
- (type === "auth" && name === selectedModules.auth)) {
913
- // Merge dependencies, devDependencies, and scripts from the generator into the cumulative objects
854
+ if (this.isGeneratorSelected(type, name, selectedModules)) {
855
+ Object.assign(allDeps, generator.dependencies);
856
+ Object.assign(allDevDeps, generator.devDependencies);
914
857
  Object.assign(allScripts, generator.scripts);
915
858
  }
916
859
  }
917
- // Update package.json
918
860
  packageJson.dependencies = {
919
861
  ...(packageJson.dependencies || {}),
920
862
  ...allDeps,
@@ -929,33 +871,12 @@ class AdvancedCodeGenerator {
929
871
  };
930
872
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
931
873
  }
932
- // Public method to apply generators to an existing project (used for "Add to existing project" flow)
933
874
  async applyToProject(selectedModules, features, projectPath) {
934
- const context = {
935
- ...selectedModules,
936
- features,
937
- };
938
- // Derived combined key to simplify template conditionals (e.g. "prisma:express")
939
- context.combo = `${context.database || ""}:${context.framework || ""}`;
940
- if (selectedModules.database === "prisma" && !context.prismaProvider) {
941
- const providers = (0, shared_1.getPrismaProvidersFromGenerator)((0, package_root_1.getPackageRoot)());
942
- if (providers && providers.length > 0) {
943
- context.prismaProvider = providers[0];
944
- }
945
- }
875
+ const context = this.initializeContext(selectedModules, features);
946
876
  const applicableOperations = [];
947
877
  for (const [key, generator] of this.generators) {
948
878
  const [genType, name] = key.split(":");
949
- if (genType === "framework" && name === selectedModules.framework) {
950
- // framework
951
- }
952
- else if (genType === "database" && name === selectedModules.database) {
953
- // database
954
- }
955
- else if (genType === "auth" && name === selectedModules.auth) {
956
- // auth
957
- }
958
- else {
879
+ if (!this.isGeneratorSelected(genType, name, selectedModules)) {
959
880
  continue;
960
881
  }
961
882
  if (generator.postInstall && Array.isArray(generator.postInstall)) {
@@ -977,7 +898,7 @@ class AdvancedCodeGenerator {
977
898
  for (const operation of applicableOperations) {
978
899
  await this.executeOperation(operation, context, projectPath);
979
900
  }
980
- await this.generatePackageJson(selectedModules, features, projectPath);
901
+ await this.generatePackageJson(selectedModules, projectPath);
981
902
  return this.postInstallCommands;
982
903
  }
983
904
  getAvailableGenerators() {
@@ -1000,7 +921,6 @@ class AdvancedCodeGenerator {
1000
921
  }
1001
922
  return { frameworks, databases, auths };
1002
923
  }
1003
- // Method to register a generator configuration
1004
924
  registerGenerator(type, name, config) {
1005
925
  this.generators.set(`${type}:${name}`, config);
1006
926
  }
@@ -64,7 +64,7 @@ async function mergeModuleIntoGeneratorConfig(config, modulePath) {
64
64
  }
65
65
  }
66
66
  catch {
67
- // ignore invalid module.json
67
+ return config;
68
68
  }
69
69
  }
70
70
  return config;
@@ -74,7 +74,6 @@ async function mergeGeneratorIntoModuleMetadata(metadata, modulePath) {
74
74
  if (await fs.pathExists(generatorPath)) {
75
75
  try {
76
76
  const generator = await fs.readJson(generatorPath);
77
- // Process add-env operations to extract envVars
78
77
  if (generator.operations && Array.isArray(generator.operations)) {
79
78
  for (const operation of generator.operations) {
80
79
  if (operation.type === "add-env" && operation.envVars) {
@@ -90,11 +89,6 @@ async function mergeGeneratorIntoModuleMetadata(metadata, modulePath) {
90
89
  });
91
90
  }
92
91
  }
93
- }
94
- }
95
- // Collect dependencies/devDependencies from add-dependency operations
96
- if (generator.operations && Array.isArray(generator.operations)) {
97
- for (const operation of generator.operations) {
98
92
  if (operation.type === "add-dependency") {
99
93
  if (operation.dependencies) {
100
94
  metadata.dependencies = {
@@ -117,7 +111,7 @@ async function mergeGeneratorIntoModuleMetadata(metadata, modulePath) {
117
111
  }
118
112
  }
119
113
  catch {
120
- // ignore invalid generator.json
114
+ return metadata;
121
115
  }
122
116
  }
123
117
  return metadata;
@@ -129,13 +123,7 @@ function locateOperationSource(generatorType, generatorName, sourceRel) {
129
123
  const moduleBasePath = generatorType === "framework"
130
124
  ? path.join(templatesPath, generatorName)
131
125
  : path.join(modulesPath, generatorType, generatorName);
132
- const sourcePath = path.join(moduleBasePath, "files", sourceRel);
133
- try {
134
- return sourcePath;
135
- }
136
- catch {
137
- return null;
138
- }
126
+ return path.join(moduleBasePath, "files", sourceRel);
139
127
  }
140
128
  exports.default = {
141
129
  mergeModuleIntoGeneratorConfig,