stackkit 0.1.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 (121) hide show
  1. package/README.md +41 -0
  2. package/bin/stackkit.js +4 -0
  3. package/dist/cli/add.d.ts +8 -0
  4. package/dist/cli/add.js +313 -0
  5. package/dist/cli/create.d.ts +22 -0
  6. package/dist/cli/create.js +336 -0
  7. package/dist/cli/doctor.d.ts +7 -0
  8. package/dist/cli/doctor.js +569 -0
  9. package/dist/cli/list.d.ts +6 -0
  10. package/dist/cli/list.js +123 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +91 -0
  13. package/dist/lib/conversion/js-conversion.d.ts +1 -0
  14. package/dist/lib/conversion/js-conversion.js +244 -0
  15. package/dist/lib/database/database-config.d.ts +6 -0
  16. package/dist/lib/database/database-config.js +9 -0
  17. package/dist/lib/discovery/module-discovery.d.ts +62 -0
  18. package/dist/lib/discovery/module-discovery.js +188 -0
  19. package/dist/lib/env/env-editor.d.ts +9 -0
  20. package/dist/lib/env/env-editor.js +116 -0
  21. package/dist/lib/framework/framework-utils.d.ts +22 -0
  22. package/dist/lib/framework/framework-utils.js +74 -0
  23. package/dist/lib/fs/files.d.ts +14 -0
  24. package/dist/lib/fs/files.js +101 -0
  25. package/dist/lib/generation/code-generator.d.ts +83 -0
  26. package/dist/lib/generation/code-generator.js +681 -0
  27. package/dist/lib/git-utils.d.ts +1 -0
  28. package/dist/lib/git-utils.js +9 -0
  29. package/dist/lib/pm/package-manager.d.ts +5 -0
  30. package/dist/lib/pm/package-manager.js +69 -0
  31. package/dist/lib/project/detect.d.ts +4 -0
  32. package/dist/lib/project/detect.js +121 -0
  33. package/dist/lib/ui/logger.d.ts +16 -0
  34. package/dist/lib/ui/logger.js +59 -0
  35. package/dist/types/index.d.ts +92 -0
  36. package/dist/types/index.js +2 -0
  37. package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +6 -0
  38. package/modules/auth/authjs/files/lib/auth-client.ts +11 -0
  39. package/modules/auth/authjs/files/lib/auth.ts +36 -0
  40. package/modules/auth/authjs/files/schemas/prisma-schema.prisma +45 -0
  41. package/modules/auth/authjs/module.json +22 -0
  42. package/modules/auth/better-auth/files/api/auth/[...all]/route.ts +4 -0
  43. package/modules/auth/better-auth/files/lib/auth-client.ts +7 -0
  44. package/modules/auth/better-auth/files/lib/auth.ts +83 -0
  45. package/modules/auth/better-auth/files/lib/email-service.ts +34 -0
  46. package/modules/auth/better-auth/files/lib/email-templates.ts +89 -0
  47. package/modules/auth/better-auth/files/prisma/schema.prisma +63 -0
  48. package/modules/auth/better-auth/generator.json +78 -0
  49. package/modules/auth/better-auth/module.json +37 -0
  50. package/modules/database/mongoose/files/lib/db.ts +63 -0
  51. package/modules/database/mongoose/files/models/User.ts +34 -0
  52. package/modules/database/mongoose/generator.json +24 -0
  53. package/modules/database/mongoose/module.json +15 -0
  54. package/modules/database/prisma/files/lib/prisma.ts +45 -0
  55. package/modules/database/prisma/files/prisma/schema.prisma +8 -0
  56. package/modules/database/prisma/files/prisma.config.ts +12 -0
  57. package/modules/database/prisma/generator.json +43 -0
  58. package/modules/database/prisma/module.json +17 -0
  59. package/package.json +83 -0
  60. package/templates/express/.env.example +2 -0
  61. package/templates/express/eslint.config.cjs +42 -0
  62. package/templates/express/package.json +33 -0
  63. package/templates/express/src/app.ts +51 -0
  64. package/templates/express/src/config/env.ts +12 -0
  65. package/templates/express/src/features/health/health.controller.ts +18 -0
  66. package/templates/express/src/features/health/health.route.ts +9 -0
  67. package/templates/express/src/features/health/health.service.ts +6 -0
  68. package/templates/express/src/middlewares/error.middleware.ts +18 -0
  69. package/templates/express/src/server.ts +8 -0
  70. package/templates/express/template.json +27 -0
  71. package/templates/express/tsconfig.json +30 -0
  72. package/templates/nextjs/README.md +52 -0
  73. package/templates/nextjs/app/favicon.ico +0 -0
  74. package/templates/nextjs/app/globals.css +26 -0
  75. package/templates/nextjs/app/layout.tsx +30 -0
  76. package/templates/nextjs/app/page.tsx +57 -0
  77. package/templates/nextjs/eslint.config.mjs +18 -0
  78. package/templates/nextjs/lib/env.ts +8 -0
  79. package/templates/nextjs/next.config.ts +7 -0
  80. package/templates/nextjs/package.json +27 -0
  81. package/templates/nextjs/postcss.config.mjs +7 -0
  82. package/templates/nextjs/public/file.svg +1 -0
  83. package/templates/nextjs/public/globe.svg +1 -0
  84. package/templates/nextjs/public/next.svg +1 -0
  85. package/templates/nextjs/public/vercel.svg +1 -0
  86. package/templates/nextjs/public/window.svg +1 -0
  87. package/templates/nextjs/template.json +34 -0
  88. package/templates/nextjs/tsconfig.json +34 -0
  89. package/templates/react/.env.example +1 -0
  90. package/templates/react/.prettierignore +4 -0
  91. package/templates/react/.prettierrc +9 -0
  92. package/templates/react/README.md +56 -0
  93. package/templates/react/eslint.config.js +23 -0
  94. package/templates/react/index.html +14 -0
  95. package/templates/react/package.json +44 -0
  96. package/templates/react/public/vite.svg +1 -0
  97. package/templates/react/src/api/client.ts +47 -0
  98. package/templates/react/src/assets/react.svg +1 -0
  99. package/templates/react/src/components/ErrorBoundary.tsx +51 -0
  100. package/templates/react/src/components/Layout.tsx +13 -0
  101. package/templates/react/src/components/Loading.tsx +8 -0
  102. package/templates/react/src/components/SEO.tsx +49 -0
  103. package/templates/react/src/config/constants.ts +5 -0
  104. package/templates/react/src/hooks/index.ts +64 -0
  105. package/templates/react/src/index.css +1 -0
  106. package/templates/react/src/lib/queryClient.ts +12 -0
  107. package/templates/react/src/main.tsx +22 -0
  108. package/templates/react/src/pages/About.tsx +78 -0
  109. package/templates/react/src/pages/Home.tsx +49 -0
  110. package/templates/react/src/pages/NotFound.tsx +24 -0
  111. package/templates/react/src/router.tsx +21 -0
  112. package/templates/react/src/types/api.d.ts +20 -0
  113. package/templates/react/src/utils/helpers.ts +51 -0
  114. package/templates/react/src/utils/storage.ts +35 -0
  115. package/templates/react/src/vite-env.d.ts +11 -0
  116. package/templates/react/template.json +38 -0
  117. package/templates/react/tsconfig.app.json +28 -0
  118. package/templates/react/tsconfig.json +4 -0
  119. package/templates/react/tsconfig.node.json +26 -0
  120. package/templates/react/vite.config.ts +7 -0
  121. package/templates/react-vite/README.md +56 -0
@@ -0,0 +1,681 @@
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
+ // Also load module.json for additional metadata like postInstall
58
+ const modulePath = path.join(typePath, moduleName, 'module.json');
59
+ if (await fs.pathExists(modulePath)) {
60
+ try {
61
+ const moduleConfig = await fs.readJson(modulePath);
62
+ if (moduleConfig.postInstall && Array.isArray(moduleConfig.postInstall)) {
63
+ // Store postInstall commands with the generator for later use
64
+ config.postInstall = moduleConfig.postInstall;
65
+ }
66
+ }
67
+ catch {
68
+ // Silently skip if module.json is invalid
69
+ }
70
+ }
71
+ this.generators.set(`${type}:${moduleName}`, config);
72
+ }
73
+ catch {
74
+ // Silently skip invalid generator files
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ evaluateCondition(condition, context) {
82
+ if (!condition)
83
+ return true;
84
+ for (const [key, value] of Object.entries(condition)) {
85
+ if (key === 'features') {
86
+ const requiredFeatures = value;
87
+ const contextFeatures = context.features || [];
88
+ if (!requiredFeatures.every(feature => contextFeatures.includes(feature))) {
89
+ return false;
90
+ }
91
+ }
92
+ else {
93
+ if (Array.isArray(value)) {
94
+ if (!value.includes(context[key])) {
95
+ return false;
96
+ }
97
+ }
98
+ else {
99
+ if (context[key] !== value) {
100
+ return false;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ return true;
106
+ }
107
+ processTemplate(content, context) {
108
+ // Create a copy of context for template variables
109
+ const templateContext = { ...context };
110
+ // Handle variable definitions {{#var name = value}} at the top of the file
111
+ content = this.processVariableDefinitions(content, templateContext);
112
+ // Process the rest of the template with the extended context
113
+ content = this.processTemplateRecursive(content, templateContext);
114
+ // Remove leading newlines that might be left from {{#var}} removal
115
+ content = content.replace(/^\n+/, '');
116
+ // Reduce multiple consecutive newlines to maximum 2
117
+ content = content.replace(/\n{3,}/g, '\n\n');
118
+ return content;
119
+ }
120
+ processVariableDefinitions(content, context) {
121
+ let result = content;
122
+ let index = 0;
123
+ while (true) {
124
+ const varStart = result.indexOf('{{#var ', index);
125
+ if (varStart === -1)
126
+ break;
127
+ const equalsIndex = result.indexOf('=', varStart);
128
+ if (equalsIndex === -1)
129
+ break;
130
+ const varNameMatch = result.substring(varStart + 7, equalsIndex).trim();
131
+ if (!varNameMatch)
132
+ break;
133
+ // Find the end of the variable value by counting braces
134
+ // Start with braceCount = 1 because {{#var is already open
135
+ let braceCount = 1;
136
+ const valueStart = equalsIndex + 1;
137
+ let valueEnd = valueStart;
138
+ for (let i = valueStart; i < result.length; i++) {
139
+ if (result[i] === '{' && result[i + 1] === '{') {
140
+ braceCount++;
141
+ i++; // Skip next character
142
+ }
143
+ else if (result[i] === '}' && result[i + 1] === '}') {
144
+ braceCount--;
145
+ if (braceCount === 0) {
146
+ valueEnd = i;
147
+ break;
148
+ }
149
+ i++; // Skip next character
150
+ }
151
+ }
152
+ if (valueEnd === valueStart)
153
+ break;
154
+ const varValue = result.substring(valueStart, valueEnd).trim();
155
+ const fullMatch = result.substring(varStart, valueEnd + 2);
156
+ // Process the variable value with current context (allowing nested variables/conditionals)
157
+ const processedValue = this.processTemplateRecursive(varValue, context);
158
+ context[varNameMatch] = processedValue;
159
+ // Remove the variable definition
160
+ result = result.replace(fullMatch, '');
161
+ index = varStart;
162
+ }
163
+ return result;
164
+ }
165
+ processTemplateRecursive(content, context) {
166
+ content = content.replace(/\{\{#if\s+([^}\s]+)\s+([^}\s]+)\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, varName, operator, expectedValue, blockContent) => {
167
+ const actualVal = context[varName.trim()];
168
+ const cleanExpectedVal = expectedValue.trim().replace(/['"]/g, '');
169
+ let conditionMet = false;
170
+ switch (operator) {
171
+ case '==':
172
+ case '===':
173
+ conditionMet = actualVal === cleanExpectedVal;
174
+ break;
175
+ case '!=':
176
+ case '!==':
177
+ conditionMet = actualVal !== cleanExpectedVal;
178
+ break;
179
+ case 'includes':
180
+ conditionMet = Array.isArray(actualVal) && actualVal.includes(cleanExpectedVal);
181
+ break;
182
+ case 'startsWith':
183
+ conditionMet = typeof actualVal === 'string' && actualVal.startsWith(cleanExpectedVal);
184
+ break;
185
+ case 'endsWith':
186
+ conditionMet = typeof actualVal === 'string' && actualVal.endsWith(cleanExpectedVal);
187
+ break;
188
+ }
189
+ return conditionMet ? this.processTemplateRecursive(blockContent, context).replace(/^\n+/, '').replace(/\n+$/, '') : '';
190
+ });
191
+ // Handle simple conditional blocks {{#if condition}}...{{/if}} (backward compatibility)
192
+ content = content.replace(/\{\{#if\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, condition, blockContent) => {
193
+ const conditionParts = condition.split('==');
194
+ if (conditionParts.length === 2) {
195
+ const [varName, expectedValue] = conditionParts.map((s) => s.trim().replace(/['"]/g, ''));
196
+ if (context[varName] === expectedValue) {
197
+ return this.processTemplateRecursive(blockContent, context).replace(/^\n+/, '').replace(/\n+$/, '');
198
+ }
199
+ return '';
200
+ }
201
+ const conditionFunc = condition.split('.');
202
+ if (conditionFunc.length === 2 && conditionFunc[1] === 'includes') {
203
+ const [arrayName, item] = conditionFunc[0].split('(');
204
+ const itemValue = item.replace(')', '').replace(/['"]/g, '');
205
+ const array = context[arrayName] || [];
206
+ if (Array.isArray(array) && array.includes(itemValue)) {
207
+ return this.processTemplateRecursive(blockContent, context).replace(/^\n+/, '').replace(/\n+$/, '');
208
+ }
209
+ return '';
210
+ }
211
+ return '';
212
+ });
213
+ // Handle switch statements {{#switch variable}}...{{/switch}}
214
+ content = content.replace(/\{\{#switch\s+([^}]+)\}\}([\s\S]*?)\{\{\/switch\}\}/g, (match, varName, switchContent) => {
215
+ const actualVal = context[varName.trim()];
216
+ // Parse cases
217
+ const caseRegex = /\{\{#case\s+([^}]+)\}\}([\s\S]*?)(?=\{\{#case|\{\{\/switch\})/g;
218
+ let result = '';
219
+ let defaultCase = '';
220
+ let caseMatch;
221
+ while ((caseMatch = caseRegex.exec(switchContent)) !== null) {
222
+ const [, caseValue, caseContent] = caseMatch;
223
+ const cleanCaseValue = caseValue.trim().replace(/['"]/g, '');
224
+ if (cleanCaseValue === 'default') {
225
+ defaultCase = caseContent;
226
+ }
227
+ else if (actualVal === cleanCaseValue) {
228
+ result = caseContent;
229
+ break;
230
+ }
231
+ }
232
+ return result || defaultCase || '';
233
+ });
234
+ // Handle variable replacement with advanced expressions
235
+ content = content.replace(/\{\{([^}]+)\}\}/g, (match, varExpr) => {
236
+ const trimmedExpr = varExpr.trim();
237
+ // Handle ternary expressions like framework=='nextjs' ? '@/lib' : '.'
238
+ const ternaryMatch = trimmedExpr.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+?)$/);
239
+ if (ternaryMatch) {
240
+ const [, condition, trueVal, falseVal] = ternaryMatch;
241
+ const conditionMatch = condition.match(/^(.+?)==(.+)$/);
242
+ if (conditionMatch) {
243
+ const [, varName, expectedVal] = conditionMatch;
244
+ const cleanVarName = varName.trim();
245
+ const cleanExpectedVal = expectedVal.trim().replace(/['"]/g, '');
246
+ const actualVal = context[cleanVarName];
247
+ const result = actualVal === cleanExpectedVal ? trueVal.trim() : falseVal.trim();
248
+ return result.replace(/['"]/g, ''); // Remove quotes from result
249
+ }
250
+ }
251
+ // Handle switch expressions {{switch variable case1: value1, case2: value2, default: defaultValue}}
252
+ const switchMatch = trimmedExpr.match(/^switch\s+([^}\s]+)\s+(.+)$/);
253
+ if (switchMatch) {
254
+ const [, varName, casesStr] = switchMatch;
255
+ const actualVal = context[varName.trim()];
256
+ const cases = casesStr.split(',').map((c) => c.trim());
257
+ for (const caseStr of cases) {
258
+ const [caseVal, result] = caseStr.split(':').map((s) => s.trim());
259
+ const cleanCaseVal = caseVal.replace(/['"]/g, '');
260
+ if (cleanCaseVal === actualVal || cleanCaseVal === 'default') {
261
+ return result.replace(/['"]/g, '');
262
+ }
263
+ }
264
+ return '';
265
+ }
266
+ // Handle feature flags {{feature:name}}
267
+ if (trimmedExpr.startsWith('feature:')) {
268
+ const featureName = trimmedExpr.substring(8);
269
+ const features = context.features || [];
270
+ return features.includes(featureName) ? 'true' : 'false';
271
+ }
272
+ // Handle conditional expressions {{if condition then:value else:value}}
273
+ const conditionalMatch = trimmedExpr.match(/^if\s+(.+?)\s+then:([^,]+),\s*else:(.+)$/);
274
+ if (conditionalMatch) {
275
+ const [, condition, thenVal, elseVal] = conditionalMatch;
276
+ const conditionMatch2 = condition.match(/^(.+?)==(.+)$/);
277
+ if (conditionMatch2) {
278
+ const [, varName, expectedVal] = conditionMatch2;
279
+ const cleanVarName = varName.trim();
280
+ const cleanExpectedVal = expectedVal.trim().replace(/['"]/g, '');
281
+ const actualVal = context[cleanVarName];
282
+ const result = actualVal === cleanExpectedVal ? thenVal.trim() : elseVal.trim();
283
+ return result.replace(/['"]/g, '');
284
+ }
285
+ }
286
+ // Simple variable replacement
287
+ const value = context[trimmedExpr];
288
+ return value !== undefined ? String(value) : match;
289
+ });
290
+ return content;
291
+ }
292
+ async generate(selectedModules, features, outputPath) {
293
+ // First, copy the base template
294
+ await this.copyTemplate(selectedModules.framework, outputPath);
295
+ const context = {
296
+ ...selectedModules,
297
+ features,
298
+ };
299
+ // Set default prismaProvider if database is prisma but no provider specified
300
+ if (selectedModules.database === 'prisma' && !context.prismaProvider) {
301
+ context.prismaProvider = 'postgresql';
302
+ }
303
+ // Collect all applicable operations
304
+ const applicableOperations = [];
305
+ for (const [key, generator] of this.generators) {
306
+ const [genType, name] = key.split(':');
307
+ // Check if this generator is selected
308
+ if (genType === 'framework' && name === selectedModules.framework) {
309
+ // Framework is always included
310
+ }
311
+ else if (genType === 'database' && name === selectedModules.database) {
312
+ // Database is selected
313
+ }
314
+ else if (genType === 'auth' && name === selectedModules.auth) {
315
+ // Auth is selected
316
+ }
317
+ else {
318
+ continue; // Skip unselected generators
319
+ }
320
+ // Collect postInstall commands from selected generators
321
+ if (generator.postInstall && Array.isArray(generator.postInstall)) {
322
+ this.postInstallCommands.push(...generator.postInstall);
323
+ }
324
+ // Handle operations
325
+ const items = generator.operations || [];
326
+ for (const item of items) {
327
+ if (this.evaluateCondition(item.condition, context)) {
328
+ applicableOperations.push({
329
+ ...item,
330
+ generator: name,
331
+ generatorType: genType,
332
+ priority: generator.priority,
333
+ });
334
+ }
335
+ }
336
+ }
337
+ // Sort operations by priority
338
+ applicableOperations.sort((a, b) => {
339
+ const priorityA = a.priority || 0;
340
+ const priorityB = b.priority || 0;
341
+ return priorityA - priorityB;
342
+ });
343
+ // Execute operations
344
+ for (const operation of applicableOperations) {
345
+ await this.executeOperation(operation, context, outputPath);
346
+ }
347
+ // Generate package.json updates
348
+ await this.generatePackageJson(selectedModules, features, outputPath);
349
+ return this.postInstallCommands;
350
+ }
351
+ async executeOperation(operation, context, outputPath) {
352
+ // Process templates in operation content
353
+ const processedOperation = this.processOperationTemplates(operation, context);
354
+ switch (processedOperation.type) {
355
+ case 'create-file':
356
+ await this.executeCreateFile(processedOperation, context, outputPath);
357
+ break;
358
+ case 'patch-file':
359
+ await this.executePatchFile(processedOperation, context, outputPath);
360
+ break;
361
+ case 'add-dependency':
362
+ await this.executeAddDependency(processedOperation, context, outputPath);
363
+ break;
364
+ case 'add-script':
365
+ await this.executeAddScript(processedOperation, context, outputPath);
366
+ break;
367
+ case 'add-env':
368
+ await this.executeAddEnv(processedOperation, context, outputPath);
369
+ break;
370
+ case 'run-command':
371
+ this.executeRunCommand(processedOperation, context);
372
+ break;
373
+ default:
374
+ // Unknown operation type - skip silently
375
+ }
376
+ }
377
+ async copyTemplate(frameworkName, outputPath) {
378
+ const candidates = [
379
+ path.resolve(__dirname, '..', '..', 'templates'), // dist/templates (when bundled)
380
+ path.resolve(__dirname, '..', '..', '..', 'templates'), // package root templates
381
+ ];
382
+ let templateBase;
383
+ for (const c of candidates) {
384
+ const p = path.join(c, frameworkName);
385
+ if (await fs.pathExists(p)) {
386
+ templateBase = p;
387
+ break;
388
+ }
389
+ }
390
+ if (!templateBase)
391
+ return;
392
+ await fs.copy(templateBase, outputPath, {
393
+ filter: (src) => {
394
+ const relativePath = path.relative(templateBase, src);
395
+ return relativePath !== 'template.json' &&
396
+ relativePath !== 'node_modules' &&
397
+ !relativePath.startsWith('node_modules/');
398
+ }
399
+ });
400
+ }
401
+ processOperationTemplates(operation, context) {
402
+ const processed = { ...operation };
403
+ // Process templates in string fields
404
+ if (processed.source) {
405
+ processed.source = this.processTemplate(processed.source, context);
406
+ }
407
+ if (processed.destination) {
408
+ processed.destination = this.processTemplate(processed.destination, context);
409
+ }
410
+ if (processed.content) {
411
+ processed.content = this.processTemplate(processed.content, context);
412
+ }
413
+ if (processed.destination) {
414
+ processed.destination = this.processTemplate(processed.destination, context);
415
+ }
416
+ // Process templates in patch operations
417
+ if (processed.operations) {
418
+ processed.operations = processed.operations.map(op => {
419
+ const processedOp = { ...op };
420
+ if (processedOp.imports) {
421
+ processedOp.imports = processedOp.imports.map(imp => this.processTemplate(imp, context));
422
+ }
423
+ if (processedOp.code) {
424
+ processedOp.code = this.processTemplate(processedOp.code, context);
425
+ }
426
+ if (processedOp.after) {
427
+ processedOp.after = this.processTemplate(processedOp.after, context);
428
+ }
429
+ if (processedOp.before) {
430
+ processedOp.before = this.processTemplate(processedOp.before, context);
431
+ }
432
+ if (processedOp.replace) {
433
+ processedOp.replace = this.processTemplate(processedOp.replace, context);
434
+ }
435
+ if (processedOp.content) {
436
+ processedOp.content = this.processTemplate(processedOp.content, context);
437
+ }
438
+ if (processedOp.source) {
439
+ processedOp.source = this.processTemplate(processedOp.source, context);
440
+ }
441
+ return processedOp;
442
+ });
443
+ }
444
+ return processed;
445
+ }
446
+ async executeCreateFile(operation, context, outputPath) {
447
+ if (!operation.destination)
448
+ return;
449
+ const destinationPath = path.join(outputPath, this.processTemplate(operation.destination, context));
450
+ // Ensure directory exists
451
+ await fs.ensureDir(path.dirname(destinationPath));
452
+ let content;
453
+ if (operation.content) {
454
+ // Use content directly
455
+ content = this.processTemplate(operation.content, context);
456
+ }
457
+ else if (operation.source) {
458
+ // Find the source file path relative to the module/template directory
459
+ // Resolve modules/templates base paths (try both dist and package root)
460
+ const modulesCandidates = [
461
+ path.resolve(__dirname, '..', '..', 'modules'),
462
+ path.resolve(__dirname, '..', '..', '..', 'modules'),
463
+ ];
464
+ const templatesCandidates = [
465
+ path.resolve(__dirname, '..', '..', 'templates'),
466
+ path.resolve(__dirname, '..', '..', '..', 'templates'),
467
+ ];
468
+ const resolveExisting = async (cands) => {
469
+ for (const c of cands) {
470
+ if (await fs.pathExists(c))
471
+ return c;
472
+ }
473
+ return undefined;
474
+ };
475
+ const modulesPathResolved = await resolveExisting(modulesCandidates);
476
+ const templatesPathResolved = await resolveExisting(templatesCandidates);
477
+ const moduleBasePath = operation.generatorType === 'framework'
478
+ ? path.join(templatesPathResolved || templatesCandidates[0], operation.generator)
479
+ : path.join(modulesPathResolved || modulesCandidates[0], operation.generatorType, operation.generator);
480
+ const sourcePath = path.join(moduleBasePath, 'files', operation.source);
481
+ // Check if source file exists
482
+ if (await fs.pathExists(sourcePath)) {
483
+ // Read source file
484
+ content = await fs.readFile(sourcePath, 'utf-8');
485
+ // Process template content
486
+ content = this.processTemplate(content, context);
487
+ }
488
+ else {
489
+ throw new Error(`Source file not found: ${sourcePath}`);
490
+ }
491
+ }
492
+ else {
493
+ throw new Error(`Create file operation must have either 'content' or 'source' field`);
494
+ }
495
+ // Write destination file
496
+ await fs.writeFile(destinationPath, content, 'utf-8');
497
+ }
498
+ async executePatchFile(operation, context, outputPath) {
499
+ if (!operation.destination)
500
+ return;
501
+ const filePath = path.join(outputPath, this.processTemplate(operation.destination, context));
502
+ // Read existing file
503
+ let content = await fs.readFile(filePath, 'utf-8');
504
+ if (operation.content) {
505
+ content += this.processTemplate(operation.content, context).trim();
506
+ }
507
+ else if (operation.operations) {
508
+ // Execute patch operations
509
+ for (const patchOp of operation.operations) {
510
+ if (!this.evaluateCondition(patchOp.condition, context))
511
+ continue;
512
+ switch (patchOp.type) {
513
+ case 'add-import':
514
+ if (patchOp.imports) {
515
+ const imports = patchOp.imports.map(imp => this.processTemplate(imp, context)).join('\n');
516
+ // Add imports at the top, after existing imports
517
+ const lines = content.split('\n');
518
+ let insertIndex = 0;
519
+ for (let i = 0; i < lines.length; i++) {
520
+ if (lines[i].trim().startsWith('import') || lines[i].trim() === '') {
521
+ insertIndex = i + 1;
522
+ }
523
+ else {
524
+ break;
525
+ }
526
+ }
527
+ lines.splice(insertIndex, 0, imports);
528
+ content = lines.join('\n');
529
+ }
530
+ break;
531
+ case 'add-code':
532
+ if (patchOp.code && patchOp.after) {
533
+ const processedCode = this.processTemplate(patchOp.code, context);
534
+ const afterPattern = this.processTemplate(patchOp.after, context);
535
+ const index = content.indexOf(afterPattern);
536
+ if (index !== -1) {
537
+ content = content.slice(0, index + afterPattern.length) + processedCode + content.slice(index + afterPattern.length);
538
+ }
539
+ }
540
+ break;
541
+ case 'replace-code':
542
+ if (patchOp.code && patchOp.replace) {
543
+ const processedCode = this.processTemplate(patchOp.code, context);
544
+ const replacePattern = this.processTemplate(patchOp.replace, context);
545
+ content = content.replace(replacePattern, processedCode);
546
+ }
547
+ break;
548
+ case 'add-to-top': {
549
+ let processedContentTop = '';
550
+ if (patchOp.content) {
551
+ processedContentTop = this.processTemplate(patchOp.content, context).trim();
552
+ }
553
+ else if (patchOp.source) {
554
+ const modulesPath = path.join(__dirname, '..', '..', 'modules');
555
+ const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, 'files', patchOp.source);
556
+ if (await fs.pathExists(sourcePath)) {
557
+ processedContentTop = await fs.readFile(sourcePath, 'utf-8');
558
+ processedContentTop = this.processTemplate(processedContentTop, context).trim();
559
+ }
560
+ }
561
+ if (processedContentTop) {
562
+ content = processedContentTop + '\n' + content;
563
+ }
564
+ break;
565
+ }
566
+ case 'add-to-bottom': {
567
+ let processedContentBottom = '';
568
+ if (patchOp.content) {
569
+ processedContentBottom = this.processTemplate(patchOp.content, context).trim();
570
+ }
571
+ else if (patchOp.source) {
572
+ const modulesPath = path.join(__dirname, '..', '..', 'modules');
573
+ const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, 'files', patchOp.source);
574
+ if (await fs.pathExists(sourcePath)) {
575
+ processedContentBottom = await fs.readFile(sourcePath, 'utf-8');
576
+ processedContentBottom = this.processTemplate(processedContentBottom, context).trim();
577
+ }
578
+ }
579
+ if (processedContentBottom) {
580
+ content = content + '\n' + processedContentBottom;
581
+ }
582
+ break;
583
+ }
584
+ }
585
+ }
586
+ }
587
+ // Write back the modified content
588
+ await fs.writeFile(filePath, content, 'utf-8');
589
+ }
590
+ async executeAddDependency(operation, context, outputPath) {
591
+ const packageJsonPath = path.join(outputPath, 'package.json');
592
+ let packageJson = {};
593
+ if (await fs.pathExists(packageJsonPath)) {
594
+ packageJson = await fs.readJson(packageJsonPath);
595
+ }
596
+ if (operation.dependencies) {
597
+ packageJson.dependencies = { ...(packageJson.dependencies || {}), ...operation.dependencies };
598
+ }
599
+ if (operation.devDependencies) {
600
+ packageJson.devDependencies = { ...(packageJson.devDependencies || {}), ...operation.devDependencies };
601
+ }
602
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
603
+ }
604
+ async executeAddScript(operation, context, outputPath) {
605
+ const packageJsonPath = path.join(outputPath, 'package.json');
606
+ let packageJson = {};
607
+ if (await fs.pathExists(packageJsonPath)) {
608
+ packageJson = await fs.readJson(packageJsonPath);
609
+ }
610
+ if (operation.scripts) {
611
+ packageJson.scripts = { ...(packageJson.scripts || {}), ...operation.scripts };
612
+ }
613
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
614
+ }
615
+ async executeAddEnv(operation, context, outputPath) {
616
+ const envPath = path.join(outputPath, '.env');
617
+ let envContent = '';
618
+ if (await fs.pathExists(envPath)) {
619
+ envContent = await fs.readFile(envPath, 'utf-8');
620
+ }
621
+ if (operation.envVars) {
622
+ const envLines = Object.entries(operation.envVars).map(([key, value]) => `${key}=${value}`);
623
+ envContent += '\n' + envLines.join('\n');
624
+ }
625
+ await fs.writeFile(envPath, envContent.trim(), 'utf-8');
626
+ }
627
+ executeRunCommand(operation, context) {
628
+ if (operation.command) {
629
+ // Process template variables in the command
630
+ const processedCommand = this.processTemplate(operation.command, context);
631
+ this.postInstallCommands.push(processedCommand);
632
+ }
633
+ }
634
+ async generatePackageJson(selectedModules, features, outputPath) {
635
+ const packageJsonPath = path.join(outputPath, 'package.json');
636
+ let packageJson = {};
637
+ if (await fs.pathExists(packageJsonPath)) {
638
+ packageJson = await fs.readJson(packageJsonPath);
639
+ }
640
+ // Collect dependencies from selected generators
641
+ const allDeps = {};
642
+ const allDevDeps = {};
643
+ const allScripts = {};
644
+ for (const [key, generator] of this.generators) {
645
+ const [type, name] = key.split(':');
646
+ if ((type === 'framework' && name === selectedModules.framework) ||
647
+ (type === 'database' && name === selectedModules.database) ||
648
+ (type === 'auth' && name === selectedModules.auth)) {
649
+ Object.assign(allDeps, generator.dependencies);
650
+ Object.assign(allDevDeps, generator.devDependencies);
651
+ Object.assign(allScripts, generator.scripts);
652
+ }
653
+ }
654
+ // Update package.json
655
+ packageJson.dependencies = { ...(packageJson.dependencies || {}), ...allDeps };
656
+ packageJson.devDependencies = { ...(packageJson.devDependencies || {}), ...allDevDeps };
657
+ packageJson.scripts = { ...(packageJson.scripts || {}), ...allScripts };
658
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
659
+ }
660
+ getAvailableGenerators() {
661
+ const frameworks = [];
662
+ const databases = [];
663
+ const auths = [];
664
+ for (const [key] of this.generators) {
665
+ const [type, name] = key.split(':');
666
+ switch (type) {
667
+ case 'framework':
668
+ frameworks.push(name);
669
+ break;
670
+ case 'database':
671
+ databases.push(name);
672
+ break;
673
+ case 'auth':
674
+ auths.push(name);
675
+ break;
676
+ }
677
+ }
678
+ return { frameworks, databases, auths };
679
+ }
680
+ }
681
+ exports.AdvancedCodeGenerator = AdvancedCodeGenerator;
@@ -0,0 +1 @@
1
+ export declare function initGit(cwd: string): Promise<void>;