stackkit 0.1.4 → 0.1.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/README.md +1 -1
- package/dist/cli/add.js +146 -105
- package/dist/cli/create.d.ts +2 -2
- package/dist/cli/create.js +94 -27
- package/dist/cli/doctor.js +42 -17
- package/dist/cli/list.js +14 -2
- package/dist/index.js +16 -16
- package/dist/lib/conversion/js-conversion.js +1 -1
- package/dist/lib/discovery/module-discovery.js +35 -35
- package/dist/lib/env/env-editor.js +1 -1
- package/dist/lib/framework/framework-utils.d.ts +1 -1
- package/dist/lib/framework/framework-utils.js +3 -3
- package/dist/lib/generation/code-generator.d.ts +5 -5
- package/dist/lib/generation/code-generator.js +166 -132
- package/dist/lib/generation/generator-utils.d.ts +2 -2
- package/dist/lib/generation/generator-utils.js +28 -15
- package/dist/lib/utils/package-root.js +2 -2
- package/modules/auth/authjs/generator.json +1 -2
- package/modules/auth/better-auth/files/lib/auth.ts +37 -30
- package/modules/auth/better-auth/files/prisma/schema.prisma +3 -3
- package/modules/auth/better-auth/generator.json +46 -16
- package/modules/database/mongoose/generator.json +23 -4
- package/modules/database/prisma/files/lib/prisma.ts +26 -21
- package/modules/database/prisma/files/prisma.config.ts +17 -0
- package/modules/database/prisma/generator.json +50 -11
- package/package.json +3 -1
- package/templates/express/src/server.ts +9 -3
- package/templates/express/tsconfig.json +2 -2
- package/templates/nextjs/lib/env.ts +1 -1
|
@@ -45,13 +45,13 @@ class AdvancedCodeGenerator {
|
|
|
45
45
|
this.frameworkConfig = frameworkConfig;
|
|
46
46
|
}
|
|
47
47
|
async loadGenerators(modulesPath) {
|
|
48
|
-
const moduleTypes = [
|
|
48
|
+
const moduleTypes = ["auth", "database"];
|
|
49
49
|
for (const type of moduleTypes) {
|
|
50
50
|
const typePath = path.join(modulesPath, type);
|
|
51
51
|
if (await fs.pathExists(typePath)) {
|
|
52
52
|
const modules = await fs.readdir(typePath);
|
|
53
53
|
for (const moduleName of modules) {
|
|
54
|
-
const generatorPath = path.join(typePath, moduleName,
|
|
54
|
+
const generatorPath = path.join(typePath, moduleName, "generator.json");
|
|
55
55
|
if (await fs.pathExists(generatorPath)) {
|
|
56
56
|
try {
|
|
57
57
|
const config = await fs.readJson(generatorPath);
|
|
@@ -71,10 +71,10 @@ class AdvancedCodeGenerator {
|
|
|
71
71
|
if (!condition)
|
|
72
72
|
return true;
|
|
73
73
|
for (const [key, value] of Object.entries(condition)) {
|
|
74
|
-
if (key ===
|
|
74
|
+
if (key === "features") {
|
|
75
75
|
const requiredFeatures = value;
|
|
76
76
|
const contextFeatures = context.features || [];
|
|
77
|
-
if (!requiredFeatures.every(feature => contextFeatures.includes(feature))) {
|
|
77
|
+
if (!requiredFeatures.every((feature) => contextFeatures.includes(feature))) {
|
|
78
78
|
return false;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -101,19 +101,19 @@ class AdvancedCodeGenerator {
|
|
|
101
101
|
// Process the rest of the template with the extended context
|
|
102
102
|
content = this.processTemplateRecursive(content, templateContext);
|
|
103
103
|
// Remove leading newlines that might be left from {{#var}} removal
|
|
104
|
-
content = content.replace(/^\n+/,
|
|
104
|
+
content = content.replace(/^\n+/, "");
|
|
105
105
|
// Reduce multiple consecutive newlines to maximum 2
|
|
106
|
-
content = content.replace(/\n{3,}/g,
|
|
106
|
+
content = content.replace(/\n{3,}/g, "\n\n");
|
|
107
107
|
return content;
|
|
108
108
|
}
|
|
109
109
|
processVariableDefinitions(content, context) {
|
|
110
110
|
let result = content;
|
|
111
111
|
let index = 0;
|
|
112
112
|
while (true) {
|
|
113
|
-
const varStart = result.indexOf(
|
|
113
|
+
const varStart = result.indexOf("{{#var ", index);
|
|
114
114
|
if (varStart === -1)
|
|
115
115
|
break;
|
|
116
|
-
const equalsIndex = result.indexOf(
|
|
116
|
+
const equalsIndex = result.indexOf("=", varStart);
|
|
117
117
|
if (equalsIndex === -1)
|
|
118
118
|
break;
|
|
119
119
|
const varNameMatch = result.substring(varStart + 7, equalsIndex).trim();
|
|
@@ -125,11 +125,11 @@ class AdvancedCodeGenerator {
|
|
|
125
125
|
const valueStart = equalsIndex + 1;
|
|
126
126
|
let valueEnd = valueStart;
|
|
127
127
|
for (let i = valueStart; i < result.length; i++) {
|
|
128
|
-
if (result[i] ===
|
|
128
|
+
if (result[i] === "{" && result[i + 1] === "{") {
|
|
129
129
|
braceCount++;
|
|
130
130
|
i++; // Skip next character
|
|
131
131
|
}
|
|
132
|
-
else if (result[i] ===
|
|
132
|
+
else if (result[i] === "}" && result[i + 1] === "}") {
|
|
133
133
|
braceCount--;
|
|
134
134
|
if (braceCount === 0) {
|
|
135
135
|
valueEnd = i;
|
|
@@ -146,71 +146,74 @@ class AdvancedCodeGenerator {
|
|
|
146
146
|
const processedValue = this.processTemplateRecursive(varValue, context);
|
|
147
147
|
context[varNameMatch] = processedValue;
|
|
148
148
|
// Remove the variable definition
|
|
149
|
-
result = result.replace(fullMatch,
|
|
149
|
+
result = result.replace(fullMatch, "");
|
|
150
150
|
index = varStart;
|
|
151
151
|
}
|
|
152
152
|
return result;
|
|
153
153
|
}
|
|
154
154
|
processTemplateRecursive(content, context) {
|
|
155
|
-
content = content.replace(/\{\{#if\s+([^}\s]+)\s+([^}\s]+)\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, varName, operator, expectedValue, blockContent) => {
|
|
155
|
+
content = content.replace(/\{\{#if\s+([^}\s]+)\s+([^}\s]+)\s+([^}]+)\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}/g, (match, varName, operator, expectedValue, blockContent, elseContent) => {
|
|
156
156
|
const actualVal = context[varName.trim()];
|
|
157
|
-
const cleanExpectedVal = expectedValue.trim().replace(/['"]/g,
|
|
157
|
+
const cleanExpectedVal = expectedValue.trim().replace(/['"]/g, "");
|
|
158
158
|
let conditionMet = false;
|
|
159
159
|
switch (operator) {
|
|
160
|
-
case
|
|
161
|
-
case
|
|
160
|
+
case "==":
|
|
161
|
+
case "===":
|
|
162
162
|
conditionMet = actualVal === cleanExpectedVal;
|
|
163
163
|
break;
|
|
164
|
-
case
|
|
165
|
-
case
|
|
164
|
+
case "!=":
|
|
165
|
+
case "!==":
|
|
166
166
|
conditionMet = actualVal !== cleanExpectedVal;
|
|
167
167
|
break;
|
|
168
|
-
case
|
|
168
|
+
case "includes":
|
|
169
169
|
conditionMet = Array.isArray(actualVal) && actualVal.includes(cleanExpectedVal);
|
|
170
170
|
break;
|
|
171
|
-
case
|
|
172
|
-
conditionMet = typeof actualVal ===
|
|
171
|
+
case "startsWith":
|
|
172
|
+
conditionMet = typeof actualVal === "string" && actualVal.startsWith(cleanExpectedVal);
|
|
173
173
|
break;
|
|
174
|
-
case
|
|
175
|
-
conditionMet = typeof actualVal ===
|
|
174
|
+
case "endsWith":
|
|
175
|
+
conditionMet = typeof actualVal === "string" && actualVal.endsWith(cleanExpectedVal);
|
|
176
176
|
break;
|
|
177
177
|
}
|
|
178
|
-
|
|
178
|
+
const contentToProcess = conditionMet ? blockContent : (elseContent || "");
|
|
179
|
+
return this.processTemplateRecursive(contentToProcess, context)
|
|
180
|
+
.replace(/^\n+/, "")
|
|
181
|
+
.replace(/\n+$/, "");
|
|
179
182
|
});
|
|
180
183
|
// Handle simple conditional blocks {{#if condition}}...{{/if}} (backward compatibility)
|
|
181
|
-
content = content.replace(/\{\{#if\s+([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, condition, blockContent) => {
|
|
182
|
-
const conditionParts = condition.split(
|
|
184
|
+
content = content.replace(/\{\{#if\s+([^}]+)\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}/g, (match, condition, blockContent, elseContent) => {
|
|
185
|
+
const conditionParts = condition.split("==");
|
|
183
186
|
if (conditionParts.length === 2) {
|
|
184
|
-
const [varName, expectedValue] = conditionParts.map((s) => s.trim().replace(/['"]/g,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
const [varName, expectedValue] = conditionParts.map((s) => s.trim().replace(/['"]/g, ""));
|
|
188
|
+
const contentToProcess = context[varName] === expectedValue ? blockContent : (elseContent || "");
|
|
189
|
+
return this.processTemplateRecursive(contentToProcess, context)
|
|
190
|
+
.replace(/^\n+/, "")
|
|
191
|
+
.replace(/\n+$/, "");
|
|
189
192
|
}
|
|
190
|
-
const conditionFunc = condition.split(
|
|
191
|
-
if (conditionFunc.length === 2 && conditionFunc[1] ===
|
|
192
|
-
const [arrayName, item] = conditionFunc[0].split(
|
|
193
|
-
const itemValue = item.replace(
|
|
193
|
+
const conditionFunc = condition.split(".");
|
|
194
|
+
if (conditionFunc.length === 2 && conditionFunc[1] === "includes") {
|
|
195
|
+
const [arrayName, item] = conditionFunc[0].split("(");
|
|
196
|
+
const itemValue = item.replace(")", "").replace(/['"]/g, "");
|
|
194
197
|
const array = context[arrayName] || [];
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
const contentToProcess = Array.isArray(array) && array.includes(itemValue) ? blockContent : (elseContent || "");
|
|
199
|
+
return this.processTemplateRecursive(contentToProcess, context)
|
|
200
|
+
.replace(/^\n+/, "")
|
|
201
|
+
.replace(/\n+$/, "");
|
|
199
202
|
}
|
|
200
|
-
return
|
|
203
|
+
return "";
|
|
201
204
|
});
|
|
202
205
|
// Handle switch statements {{#switch variable}}...{{/switch}}
|
|
203
206
|
content = content.replace(/\{\{#switch\s+([^}]+)\}\}([\s\S]*?)\{\{\/switch\}\}/g, (match, varName, switchContent) => {
|
|
204
207
|
const actualVal = context[varName.trim()];
|
|
205
208
|
// Parse cases
|
|
206
|
-
const caseRegex = /\{\{#case\s+([^}]+)\}\}([\s\S]*?)(?=\{\{#case|\{\{\/switch\})/g;
|
|
207
|
-
let result =
|
|
208
|
-
let defaultCase =
|
|
209
|
+
const caseRegex = /\{\{#case\s+([^}]+)\}\}([\s\S]*?)(?=\{\{#case|\{\{\/case\}|\{\{\/switch\})/g;
|
|
210
|
+
let result = "";
|
|
211
|
+
let defaultCase = "";
|
|
209
212
|
let caseMatch;
|
|
210
213
|
while ((caseMatch = caseRegex.exec(switchContent)) !== null) {
|
|
211
214
|
const [, caseValue, caseContent] = caseMatch;
|
|
212
|
-
const cleanCaseValue = caseValue.trim().replace(/['"]/g,
|
|
213
|
-
if (cleanCaseValue ===
|
|
215
|
+
const cleanCaseValue = caseValue.trim().replace(/['"]/g, "");
|
|
216
|
+
if (cleanCaseValue === "default") {
|
|
214
217
|
defaultCase = caseContent;
|
|
215
218
|
}
|
|
216
219
|
else if (actualVal === cleanCaseValue) {
|
|
@@ -218,7 +221,7 @@ class AdvancedCodeGenerator {
|
|
|
218
221
|
break;
|
|
219
222
|
}
|
|
220
223
|
}
|
|
221
|
-
return result || defaultCase ||
|
|
224
|
+
return result || defaultCase || "";
|
|
222
225
|
});
|
|
223
226
|
// Handle variable replacement with advanced expressions
|
|
224
227
|
content = content.replace(/\{\{([^}]+)\}\}/g, (match, varExpr) => {
|
|
@@ -231,10 +234,10 @@ class AdvancedCodeGenerator {
|
|
|
231
234
|
if (conditionMatch) {
|
|
232
235
|
const [, varName, expectedVal] = conditionMatch;
|
|
233
236
|
const cleanVarName = varName.trim();
|
|
234
|
-
const cleanExpectedVal = expectedVal.trim().replace(/['"]/g,
|
|
237
|
+
const cleanExpectedVal = expectedVal.trim().replace(/['"]/g, "");
|
|
235
238
|
const actualVal = context[cleanVarName];
|
|
236
239
|
const result = actualVal === cleanExpectedVal ? trueVal.trim() : falseVal.trim();
|
|
237
|
-
return result.replace(/['"]/g,
|
|
240
|
+
return result.replace(/['"]/g, ""); // Remove quotes from result
|
|
238
241
|
}
|
|
239
242
|
}
|
|
240
243
|
// Handle switch expressions {{switch variable case1: value1, case2: value2, default: defaultValue}}
|
|
@@ -242,21 +245,21 @@ class AdvancedCodeGenerator {
|
|
|
242
245
|
if (switchMatch) {
|
|
243
246
|
const [, varName, casesStr] = switchMatch;
|
|
244
247
|
const actualVal = context[varName.trim()];
|
|
245
|
-
const cases = casesStr.split(
|
|
248
|
+
const cases = casesStr.split(",").map((c) => c.trim());
|
|
246
249
|
for (const caseStr of cases) {
|
|
247
|
-
const [caseVal, result] = caseStr.split(
|
|
248
|
-
const cleanCaseVal = caseVal.replace(/['"]/g,
|
|
249
|
-
if (cleanCaseVal === actualVal || cleanCaseVal ===
|
|
250
|
-
return result.replace(/['"]/g,
|
|
250
|
+
const [caseVal, result] = caseStr.split(":").map((s) => s.trim());
|
|
251
|
+
const cleanCaseVal = caseVal.replace(/['"]/g, "");
|
|
252
|
+
if (cleanCaseVal === actualVal || cleanCaseVal === "default") {
|
|
253
|
+
return result.replace(/['"]/g, "");
|
|
251
254
|
}
|
|
252
255
|
}
|
|
253
|
-
return
|
|
256
|
+
return "";
|
|
254
257
|
}
|
|
255
258
|
// Handle feature flags {{feature:name}}
|
|
256
|
-
if (trimmedExpr.startsWith(
|
|
259
|
+
if (trimmedExpr.startsWith("feature:")) {
|
|
257
260
|
const featureName = trimmedExpr.substring(8);
|
|
258
261
|
const features = context.features || [];
|
|
259
|
-
return features.includes(featureName) ?
|
|
262
|
+
return features.includes(featureName) ? "true" : "false";
|
|
260
263
|
}
|
|
261
264
|
// Handle conditional expressions {{if condition then:value else:value}}
|
|
262
265
|
const conditionalMatch = trimmedExpr.match(/^if\s+(.+?)\s+then:([^,]+),\s*else:(.+)$/);
|
|
@@ -266,10 +269,10 @@ class AdvancedCodeGenerator {
|
|
|
266
269
|
if (conditionMatch2) {
|
|
267
270
|
const [, varName, expectedVal] = conditionMatch2;
|
|
268
271
|
const cleanVarName = varName.trim();
|
|
269
|
-
const cleanExpectedVal = expectedVal.trim().replace(/['"]/g,
|
|
272
|
+
const cleanExpectedVal = expectedVal.trim().replace(/['"]/g, "");
|
|
270
273
|
const actualVal = context[cleanVarName];
|
|
271
274
|
const result = actualVal === cleanExpectedVal ? thenVal.trim() : elseVal.trim();
|
|
272
|
-
return result.replace(/['"]/g,
|
|
275
|
+
return result.replace(/['"]/g, "");
|
|
273
276
|
}
|
|
274
277
|
}
|
|
275
278
|
// Simple variable replacement
|
|
@@ -286,21 +289,21 @@ class AdvancedCodeGenerator {
|
|
|
286
289
|
features,
|
|
287
290
|
};
|
|
288
291
|
// Set default prismaProvider if database is prisma but no provider specified
|
|
289
|
-
if (selectedModules.database ===
|
|
290
|
-
context.prismaProvider =
|
|
292
|
+
if (selectedModules.database === "prisma" && !context.prismaProvider) {
|
|
293
|
+
context.prismaProvider = "postgresql";
|
|
291
294
|
}
|
|
292
295
|
// Collect all applicable operations
|
|
293
296
|
const applicableOperations = [];
|
|
294
297
|
for (const [key, generator] of this.generators) {
|
|
295
|
-
const [genType, name] = key.split(
|
|
298
|
+
const [genType, name] = key.split(":");
|
|
296
299
|
// Check if this generator is selected
|
|
297
|
-
if (genType ===
|
|
300
|
+
if (genType === "framework" && name === selectedModules.framework) {
|
|
298
301
|
// Framework is always included
|
|
299
302
|
}
|
|
300
|
-
else if (genType ===
|
|
303
|
+
else if (genType === "database" && name === selectedModules.database) {
|
|
301
304
|
// Database is selected
|
|
302
305
|
}
|
|
303
|
-
else if (genType ===
|
|
306
|
+
else if (genType === "auth" && name === selectedModules.auth) {
|
|
304
307
|
// Auth is selected
|
|
305
308
|
}
|
|
306
309
|
else {
|
|
@@ -341,22 +344,22 @@ class AdvancedCodeGenerator {
|
|
|
341
344
|
// Process templates in operation content
|
|
342
345
|
const processedOperation = this.processOperationTemplates(operation, context);
|
|
343
346
|
switch (processedOperation.type) {
|
|
344
|
-
case
|
|
347
|
+
case "create-file":
|
|
345
348
|
await this.executeCreateFile(processedOperation, context, outputPath);
|
|
346
349
|
break;
|
|
347
|
-
case
|
|
350
|
+
case "patch-file":
|
|
348
351
|
await this.executePatchFile(processedOperation, context, outputPath);
|
|
349
352
|
break;
|
|
350
|
-
case
|
|
353
|
+
case "add-dependency":
|
|
351
354
|
await this.executeAddDependency(processedOperation, context, outputPath);
|
|
352
355
|
break;
|
|
353
|
-
case
|
|
356
|
+
case "add-script":
|
|
354
357
|
await this.executeAddScript(processedOperation, context, outputPath);
|
|
355
358
|
break;
|
|
356
|
-
case
|
|
359
|
+
case "add-env":
|
|
357
360
|
await this.executeAddEnv(processedOperation, context, outputPath);
|
|
358
361
|
break;
|
|
359
|
-
case
|
|
362
|
+
case "run-command":
|
|
360
363
|
this.executeRunCommand(processedOperation, context);
|
|
361
364
|
break;
|
|
362
365
|
default:
|
|
@@ -365,15 +368,15 @@ class AdvancedCodeGenerator {
|
|
|
365
368
|
}
|
|
366
369
|
async copyTemplate(frameworkName, outputPath) {
|
|
367
370
|
const packageRoot = (0, package_root_1.getPackageRoot)();
|
|
368
|
-
const templatePath = path.join(packageRoot,
|
|
371
|
+
const templatePath = path.join(packageRoot, "templates", frameworkName);
|
|
369
372
|
if (await fs.pathExists(templatePath)) {
|
|
370
373
|
await fs.copy(templatePath, outputPath, {
|
|
371
374
|
filter: (src) => {
|
|
372
375
|
const relativePath = path.relative(templatePath, src);
|
|
373
|
-
return relativePath !==
|
|
374
|
-
relativePath !==
|
|
375
|
-
!relativePath.startsWith(
|
|
376
|
-
}
|
|
376
|
+
return (relativePath !== "template.json" &&
|
|
377
|
+
relativePath !== "node_modules" &&
|
|
378
|
+
!relativePath.startsWith("node_modules/"));
|
|
379
|
+
},
|
|
377
380
|
});
|
|
378
381
|
}
|
|
379
382
|
}
|
|
@@ -394,10 +397,10 @@ class AdvancedCodeGenerator {
|
|
|
394
397
|
}
|
|
395
398
|
// Process templates in patch operations
|
|
396
399
|
if (processed.operations) {
|
|
397
|
-
processed.operations = processed.operations.map(op => {
|
|
400
|
+
processed.operations = processed.operations.map((op) => {
|
|
398
401
|
const processedOp = { ...op };
|
|
399
402
|
if (processedOp.imports) {
|
|
400
|
-
processedOp.imports = processedOp.imports.map(imp => this.processTemplate(imp, context));
|
|
403
|
+
processedOp.imports = processedOp.imports.map((imp) => this.processTemplate(imp, context));
|
|
401
404
|
}
|
|
402
405
|
if (processedOp.code) {
|
|
403
406
|
processedOp.code = this.processTemplate(processedOp.code, context);
|
|
@@ -434,9 +437,9 @@ class AdvancedCodeGenerator {
|
|
|
434
437
|
content = this.processTemplate(operation.content, context);
|
|
435
438
|
}
|
|
436
439
|
else if (operation.source) {
|
|
437
|
-
const sourcePath = (0, generator_utils_1.locateOperationSource)(operation.generatorType, operation.generator, operation.source ||
|
|
438
|
-
if (sourcePath && await fs.pathExists(sourcePath)) {
|
|
439
|
-
content = await fs.readFile(sourcePath,
|
|
440
|
+
const sourcePath = (0, generator_utils_1.locateOperationSource)(operation.generatorType, operation.generator, operation.source || "");
|
|
441
|
+
if (sourcePath && (await fs.pathExists(sourcePath))) {
|
|
442
|
+
content = await fs.readFile(sourcePath, "utf-8");
|
|
440
443
|
content = this.processTemplate(content, context);
|
|
441
444
|
}
|
|
442
445
|
else {
|
|
@@ -447,14 +450,14 @@ class AdvancedCodeGenerator {
|
|
|
447
450
|
throw new Error(`Create file operation must have either 'content' or 'source' field`);
|
|
448
451
|
}
|
|
449
452
|
// Write destination file
|
|
450
|
-
await fs.writeFile(destinationPath, content,
|
|
453
|
+
await fs.writeFile(destinationPath, content, "utf-8");
|
|
451
454
|
}
|
|
452
455
|
async executePatchFile(operation, context, outputPath) {
|
|
453
456
|
if (!operation.destination)
|
|
454
457
|
return;
|
|
455
458
|
const filePath = path.join(outputPath, this.processTemplate(operation.destination, context));
|
|
456
459
|
// Read existing file
|
|
457
|
-
let content = await fs.readFile(filePath,
|
|
460
|
+
let content = await fs.readFile(filePath, "utf-8");
|
|
458
461
|
if (operation.content) {
|
|
459
462
|
content += this.processTemplate(operation.content, context).trim();
|
|
460
463
|
}
|
|
@@ -464,14 +467,16 @@ class AdvancedCodeGenerator {
|
|
|
464
467
|
if (!this.evaluateCondition(patchOp.condition, context))
|
|
465
468
|
continue;
|
|
466
469
|
switch (patchOp.type) {
|
|
467
|
-
case
|
|
470
|
+
case "add-import":
|
|
468
471
|
if (patchOp.imports) {
|
|
469
|
-
const imports = patchOp.imports
|
|
472
|
+
const imports = patchOp.imports
|
|
473
|
+
.map((imp) => this.processTemplate(imp, context))
|
|
474
|
+
.join("\n");
|
|
470
475
|
// Add imports at the top, after existing imports
|
|
471
|
-
const lines = content.split(
|
|
476
|
+
const lines = content.split("\n");
|
|
472
477
|
let insertIndex = 0;
|
|
473
478
|
for (let i = 0; i < lines.length; i++) {
|
|
474
|
-
if (lines[i].trim().startsWith(
|
|
479
|
+
if (lines[i].trim().startsWith("import") || lines[i].trim() === "") {
|
|
475
480
|
insertIndex = i + 1;
|
|
476
481
|
}
|
|
477
482
|
else {
|
|
@@ -479,59 +484,62 @@ class AdvancedCodeGenerator {
|
|
|
479
484
|
}
|
|
480
485
|
}
|
|
481
486
|
lines.splice(insertIndex, 0, imports);
|
|
482
|
-
content = lines.join(
|
|
487
|
+
content = lines.join("\n");
|
|
483
488
|
}
|
|
484
489
|
break;
|
|
485
|
-
case
|
|
490
|
+
case "add-code":
|
|
486
491
|
if (patchOp.code && patchOp.after) {
|
|
487
492
|
const processedCode = this.processTemplate(patchOp.code, context);
|
|
488
493
|
const afterPattern = this.processTemplate(patchOp.after, context);
|
|
489
494
|
const index = content.indexOf(afterPattern);
|
|
490
495
|
if (index !== -1) {
|
|
491
|
-
content =
|
|
496
|
+
content =
|
|
497
|
+
content.slice(0, index + afterPattern.length) +
|
|
498
|
+
processedCode +
|
|
499
|
+
content.slice(index + afterPattern.length);
|
|
492
500
|
}
|
|
493
501
|
}
|
|
494
502
|
break;
|
|
495
|
-
case
|
|
503
|
+
case "replace-code":
|
|
496
504
|
if (patchOp.code && patchOp.replace) {
|
|
497
505
|
const processedCode = this.processTemplate(patchOp.code, context);
|
|
498
506
|
const replacePattern = this.processTemplate(patchOp.replace, context);
|
|
499
507
|
content = content.replace(replacePattern, processedCode);
|
|
500
508
|
}
|
|
501
509
|
break;
|
|
502
|
-
case
|
|
503
|
-
let processedContentTop =
|
|
510
|
+
case "add-to-top": {
|
|
511
|
+
let processedContentTop = "";
|
|
504
512
|
if (patchOp.content) {
|
|
505
513
|
processedContentTop = this.processTemplate(patchOp.content, context).trim();
|
|
506
514
|
}
|
|
507
515
|
else if (patchOp.source) {
|
|
508
|
-
const modulesPath = path.join((0, package_root_1.getPackageRoot)(),
|
|
509
|
-
const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator,
|
|
516
|
+
const modulesPath = path.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
517
|
+
const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, "files", patchOp.source);
|
|
510
518
|
if (await fs.pathExists(sourcePath)) {
|
|
511
|
-
processedContentTop = await fs.readFile(sourcePath,
|
|
519
|
+
processedContentTop = await fs.readFile(sourcePath, "utf-8");
|
|
512
520
|
processedContentTop = this.processTemplate(processedContentTop, context).trim();
|
|
513
521
|
}
|
|
514
522
|
}
|
|
515
523
|
if (processedContentTop) {
|
|
516
|
-
content = processedContentTop +
|
|
524
|
+
content = processedContentTop + "\n" + content;
|
|
517
525
|
}
|
|
518
526
|
break;
|
|
519
527
|
}
|
|
520
|
-
case
|
|
521
|
-
let processedContentBottom =
|
|
528
|
+
case "add-to-bottom": {
|
|
529
|
+
let processedContentBottom = "";
|
|
522
530
|
if (patchOp.content) {
|
|
523
531
|
processedContentBottom = this.processTemplate(patchOp.content, context).trim();
|
|
524
532
|
}
|
|
525
533
|
else if (patchOp.source) {
|
|
526
|
-
const modulesPath = path.join((0, package_root_1.getPackageRoot)(),
|
|
527
|
-
const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator,
|
|
534
|
+
const modulesPath = path.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
535
|
+
const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, "files", patchOp.source);
|
|
528
536
|
if (await fs.pathExists(sourcePath)) {
|
|
529
|
-
processedContentBottom = await fs.readFile(sourcePath,
|
|
537
|
+
processedContentBottom = await fs.readFile(sourcePath, "utf-8");
|
|
530
538
|
processedContentBottom = this.processTemplate(processedContentBottom, context).trim();
|
|
531
539
|
}
|
|
532
540
|
}
|
|
533
541
|
if (processedContentBottom) {
|
|
534
|
-
content = content +
|
|
542
|
+
content = content + "\n" + processedContentBottom;
|
|
535
543
|
}
|
|
536
544
|
break;
|
|
537
545
|
}
|
|
@@ -539,44 +547,56 @@ class AdvancedCodeGenerator {
|
|
|
539
547
|
}
|
|
540
548
|
}
|
|
541
549
|
// Write back the modified content
|
|
542
|
-
await fs.writeFile(filePath, content,
|
|
550
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
543
551
|
}
|
|
544
552
|
async executeAddDependency(operation, context, outputPath) {
|
|
545
|
-
const packageJsonPath = path.join(outputPath,
|
|
553
|
+
const packageJsonPath = path.join(outputPath, "package.json");
|
|
546
554
|
let packageJson = {};
|
|
547
555
|
if (await fs.pathExists(packageJsonPath)) {
|
|
548
556
|
packageJson = await fs.readJson(packageJsonPath);
|
|
549
557
|
}
|
|
550
558
|
if (operation.dependencies) {
|
|
551
|
-
packageJson.dependencies = {
|
|
559
|
+
packageJson.dependencies = {
|
|
560
|
+
...(packageJson.dependencies || {}),
|
|
561
|
+
...operation.dependencies,
|
|
562
|
+
};
|
|
552
563
|
}
|
|
553
564
|
if (operation.devDependencies) {
|
|
554
|
-
packageJson.devDependencies = {
|
|
565
|
+
packageJson.devDependencies = {
|
|
566
|
+
...(packageJson.devDependencies || {}),
|
|
567
|
+
...operation.devDependencies,
|
|
568
|
+
};
|
|
555
569
|
}
|
|
556
570
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
557
571
|
}
|
|
558
572
|
async executeAddScript(operation, context, outputPath) {
|
|
559
|
-
const packageJsonPath = path.join(outputPath,
|
|
573
|
+
const packageJsonPath = path.join(outputPath, "package.json");
|
|
560
574
|
let packageJson = {};
|
|
561
575
|
if (await fs.pathExists(packageJsonPath)) {
|
|
562
576
|
packageJson = await fs.readJson(packageJsonPath);
|
|
563
577
|
}
|
|
564
578
|
if (operation.scripts) {
|
|
565
|
-
packageJson.scripts = {
|
|
579
|
+
packageJson.scripts = {
|
|
580
|
+
...(packageJson.scripts || {}),
|
|
581
|
+
...operation.scripts,
|
|
582
|
+
};
|
|
566
583
|
}
|
|
567
584
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
568
585
|
}
|
|
569
586
|
async executeAddEnv(operation, context, outputPath) {
|
|
570
|
-
const envPath = path.join(outputPath,
|
|
571
|
-
let envContent =
|
|
587
|
+
const envPath = path.join(outputPath, ".env");
|
|
588
|
+
let envContent = "";
|
|
572
589
|
if (await fs.pathExists(envPath)) {
|
|
573
|
-
envContent = await fs.readFile(envPath,
|
|
590
|
+
envContent = await fs.readFile(envPath, "utf-8");
|
|
574
591
|
}
|
|
575
592
|
if (operation.envVars) {
|
|
576
|
-
const envLines = Object.entries(operation.envVars).map(([key, value]) =>
|
|
577
|
-
|
|
593
|
+
const envLines = Object.entries(operation.envVars).map(([key, value]) => {
|
|
594
|
+
const processedValue = this.processTemplate(value, context);
|
|
595
|
+
return `${key}=${processedValue}`;
|
|
596
|
+
});
|
|
597
|
+
envContent += "\n" + envLines.join("\n");
|
|
578
598
|
}
|
|
579
|
-
await fs.writeFile(envPath, envContent.trim(),
|
|
599
|
+
await fs.writeFile(envPath, envContent.trim(), "utf-8");
|
|
580
600
|
}
|
|
581
601
|
executeRunCommand(operation, context) {
|
|
582
602
|
if (operation.command) {
|
|
@@ -586,7 +606,7 @@ class AdvancedCodeGenerator {
|
|
|
586
606
|
}
|
|
587
607
|
}
|
|
588
608
|
async generatePackageJson(selectedModules, features, outputPath) {
|
|
589
|
-
const packageJsonPath = path.join(outputPath,
|
|
609
|
+
const packageJsonPath = path.join(outputPath, "package.json");
|
|
590
610
|
let packageJson = {};
|
|
591
611
|
if (await fs.pathExists(packageJsonPath)) {
|
|
592
612
|
packageJson = await fs.readJson(packageJsonPath);
|
|
@@ -596,19 +616,28 @@ class AdvancedCodeGenerator {
|
|
|
596
616
|
const allDevDeps = {};
|
|
597
617
|
const allScripts = {};
|
|
598
618
|
for (const [key, generator] of this.generators) {
|
|
599
|
-
const [type, name] = key.split(
|
|
600
|
-
if ((type ===
|
|
601
|
-
(type ===
|
|
602
|
-
(type ===
|
|
619
|
+
const [type, name] = key.split(":");
|
|
620
|
+
if ((type === "framework" && name === selectedModules.framework) ||
|
|
621
|
+
(type === "database" && name === selectedModules.database) ||
|
|
622
|
+
(type === "auth" && name === selectedModules.auth)) {
|
|
603
623
|
Object.assign(allDeps, generator.dependencies);
|
|
604
624
|
Object.assign(allDevDeps, generator.devDependencies);
|
|
605
625
|
Object.assign(allScripts, generator.scripts);
|
|
606
626
|
}
|
|
607
627
|
}
|
|
608
628
|
// Update package.json
|
|
609
|
-
packageJson.dependencies = {
|
|
610
|
-
|
|
611
|
-
|
|
629
|
+
packageJson.dependencies = {
|
|
630
|
+
...(packageJson.dependencies || {}),
|
|
631
|
+
...allDeps,
|
|
632
|
+
};
|
|
633
|
+
packageJson.devDependencies = {
|
|
634
|
+
...(packageJson.devDependencies || {}),
|
|
635
|
+
...allDevDeps,
|
|
636
|
+
};
|
|
637
|
+
packageJson.scripts = {
|
|
638
|
+
...(packageJson.scripts || {}),
|
|
639
|
+
...allScripts,
|
|
640
|
+
};
|
|
612
641
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
613
642
|
}
|
|
614
643
|
/**
|
|
@@ -620,19 +649,19 @@ class AdvancedCodeGenerator {
|
|
|
620
649
|
...selectedModules,
|
|
621
650
|
features,
|
|
622
651
|
};
|
|
623
|
-
if (selectedModules.database ===
|
|
624
|
-
context.prismaProvider =
|
|
652
|
+
if (selectedModules.database === "prisma" && !context.prismaProvider) {
|
|
653
|
+
context.prismaProvider = "postgresql";
|
|
625
654
|
}
|
|
626
655
|
const applicableOperations = [];
|
|
627
656
|
for (const [key, generator] of this.generators) {
|
|
628
|
-
const [genType, name] = key.split(
|
|
629
|
-
if (genType ===
|
|
657
|
+
const [genType, name] = key.split(":");
|
|
658
|
+
if (genType === "framework" && name === selectedModules.framework) {
|
|
630
659
|
// framework
|
|
631
660
|
}
|
|
632
|
-
else if (genType ===
|
|
661
|
+
else if (genType === "database" && name === selectedModules.database) {
|
|
633
662
|
// database
|
|
634
663
|
}
|
|
635
|
-
else if (genType ===
|
|
664
|
+
else if (genType === "auth" && name === selectedModules.auth) {
|
|
636
665
|
// auth
|
|
637
666
|
}
|
|
638
667
|
else {
|
|
@@ -644,7 +673,12 @@ class AdvancedCodeGenerator {
|
|
|
644
673
|
const items = generator.operations || [];
|
|
645
674
|
for (const item of items) {
|
|
646
675
|
if (this.evaluateCondition(item.condition, context)) {
|
|
647
|
-
applicableOperations.push({
|
|
676
|
+
applicableOperations.push({
|
|
677
|
+
...item,
|
|
678
|
+
generator: name,
|
|
679
|
+
generatorType: genType,
|
|
680
|
+
priority: generator.priority,
|
|
681
|
+
});
|
|
648
682
|
}
|
|
649
683
|
}
|
|
650
684
|
}
|
|
@@ -660,15 +694,15 @@ class AdvancedCodeGenerator {
|
|
|
660
694
|
const databases = [];
|
|
661
695
|
const auths = [];
|
|
662
696
|
for (const [key] of this.generators) {
|
|
663
|
-
const [type, name] = key.split(
|
|
697
|
+
const [type, name] = key.split(":");
|
|
664
698
|
switch (type) {
|
|
665
|
-
case
|
|
699
|
+
case "framework":
|
|
666
700
|
frameworks.push(name);
|
|
667
701
|
break;
|
|
668
|
-
case
|
|
702
|
+
case "database":
|
|
669
703
|
databases.push(name);
|
|
670
704
|
break;
|
|
671
|
-
case
|
|
705
|
+
case "auth":
|
|
672
706
|
auths.push(name);
|
|
673
707
|
break;
|
|
674
708
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { GeneratorConfig } from
|
|
2
|
-
import type { ModuleMetadata } from
|
|
1
|
+
import type { GeneratorConfig } from "./code-generator";
|
|
2
|
+
import type { ModuleMetadata } from "../../types";
|
|
3
3
|
export declare function mergeModuleIntoGeneratorConfig(config: GeneratorConfig, modulePath: string): Promise<GeneratorConfig>;
|
|
4
4
|
export declare function mergeGeneratorIntoModuleMetadata(metadata: ModuleMetadata, modulePath: string): Promise<ModuleMetadata>;
|
|
5
5
|
export declare function locateOperationSource(generatorType: string, generatorName: string, sourceRel: string): string | null;
|