@ingenyus/swarm-wasp 0.1.0 → 0.2.1

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 (126) hide show
  1. package/README.md +229 -21
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/common/filesystem.d.ts +0 -14
  4. package/dist/common/filesystem.d.ts.map +1 -1
  5. package/dist/common/filesystem.js +123 -0
  6. package/dist/common/index.d.ts +0 -1
  7. package/dist/common/index.d.ts.map +1 -1
  8. package/dist/common/index.js +366 -0
  9. package/dist/common/prisma.js +140 -0
  10. package/dist/common/schemas.d.ts +8 -42
  11. package/dist/common/schemas.d.ts.map +1 -1
  12. package/dist/common/schemas.js +54 -0
  13. package/dist/common/templates.js +52 -0
  14. package/dist/generators/action/action-generator.d.ts +16 -29
  15. package/dist/generators/action/action-generator.d.ts.map +1 -1
  16. package/dist/generators/action/action-generator.js +1425 -0
  17. package/dist/generators/action/index.js +1425 -0
  18. package/dist/generators/action/schema.d.ts +11 -23
  19. package/dist/generators/action/schema.d.ts.map +1 -1
  20. package/dist/generators/action/schema.js +115 -0
  21. package/dist/generators/api/api-generator.d.ts +19 -26
  22. package/dist/generators/api/api-generator.d.ts.map +1 -1
  23. package/dist/generators/api/api-generator.js +1104 -0
  24. package/dist/generators/api/index.js +1104 -0
  25. package/dist/generators/api/schema.d.ts +13 -21
  26. package/dist/generators/api/schema.d.ts.map +1 -1
  27. package/dist/generators/api/schema.js +117 -0
  28. package/dist/generators/api-namespace/api-namespace-generator.d.ts +10 -17
  29. package/dist/generators/api-namespace/api-namespace-generator.d.ts.map +1 -1
  30. package/dist/generators/api-namespace/api-namespace-generator.js +1028 -0
  31. package/dist/generators/api-namespace/index.js +1028 -0
  32. package/dist/generators/api-namespace/schema.d.ts +4 -12
  33. package/dist/generators/api-namespace/schema.d.ts.map +1 -1
  34. package/dist/generators/api-namespace/schema.js +89 -0
  35. package/dist/generators/base/{entity-generator.base.d.ts → component-generator.base.d.ts} +9 -9
  36. package/dist/generators/base/component-generator.base.d.ts.map +1 -0
  37. package/dist/generators/base/component-generator.base.js +931 -0
  38. package/dist/generators/base/index.d.ts +1 -1
  39. package/dist/generators/base/index.d.ts.map +1 -1
  40. package/dist/generators/base/index.js +1330 -0
  41. package/dist/generators/base/operation-generator.base.d.ts +12 -3
  42. package/dist/generators/base/operation-generator.base.d.ts.map +1 -1
  43. package/dist/generators/base/operation-generator.base.js +1331 -0
  44. package/dist/generators/base/wasp-generator.base.d.ts +2 -1
  45. package/dist/generators/base/wasp-generator.base.d.ts.map +1 -1
  46. package/dist/generators/base/wasp-generator.base.js +706 -0
  47. package/dist/generators/config/config-generator.d.ts +7 -4
  48. package/dist/generators/config/config-generator.d.ts.map +1 -1
  49. package/dist/generators/config/config-generator.js +0 -0
  50. package/dist/generators/config/index.js +596 -0
  51. package/dist/generators/config/wasp-config-generator.d.ts +1 -1
  52. package/dist/generators/config/wasp-config-generator.d.ts.map +1 -1
  53. package/dist/generators/config/wasp-config-generator.js +596 -0
  54. package/dist/generators/crud/crud-generator.d.ts +34 -22
  55. package/dist/generators/crud/crud-generator.d.ts.map +1 -1
  56. package/dist/generators/crud/crud-generator.js +1550 -0
  57. package/dist/generators/crud/index.js +1550 -0
  58. package/dist/generators/crud/schema.d.ts +25 -18
  59. package/dist/generators/crud/schema.d.ts.map +1 -1
  60. package/dist/generators/crud/schema.js +133 -0
  61. package/dist/generators/feature/feature-generator.d.ts +20 -0
  62. package/dist/generators/feature/feature-generator.d.ts.map +1 -0
  63. package/dist/generators/feature/feature-generator.js +765 -0
  64. package/dist/generators/feature/index.d.ts +2 -0
  65. package/dist/generators/feature/index.d.ts.map +1 -0
  66. package/dist/generators/feature/index.js +765 -0
  67. package/dist/generators/feature/schema.d.ts +5 -0
  68. package/dist/generators/feature/schema.d.ts.map +1 -0
  69. package/dist/generators/feature/schema.js +86 -0
  70. package/dist/generators/index.d.ts +1 -1
  71. package/dist/generators/index.d.ts.map +1 -1
  72. package/dist/generators/index.js +2211 -0
  73. package/dist/generators/job/index.js +1099 -0
  74. package/dist/generators/job/job-generator.d.ts +12 -23
  75. package/dist/generators/job/job-generator.d.ts.map +1 -1
  76. package/dist/generators/job/job-generator.js +1099 -0
  77. package/dist/generators/job/schema.d.ts +6 -18
  78. package/dist/generators/job/schema.d.ts.map +1 -1
  79. package/dist/generators/job/schema.js +152 -0
  80. package/dist/generators/query/index.js +1425 -0
  81. package/dist/generators/query/query-generator.d.ts +16 -29
  82. package/dist/generators/query/query-generator.d.ts.map +1 -1
  83. package/dist/generators/query/query-generator.js +1425 -0
  84. package/dist/generators/query/schema.d.ts +11 -23
  85. package/dist/generators/query/schema.d.ts.map +1 -1
  86. package/dist/generators/query/schema.js +115 -0
  87. package/dist/generators/route/index.js +1038 -0
  88. package/dist/generators/route/route-generator.d.ts +11 -20
  89. package/dist/generators/route/route-generator.d.ts.map +1 -1
  90. package/dist/generators/route/route-generator.js +1038 -0
  91. package/dist/generators/route/schema.d.ts +5 -15
  92. package/dist/generators/route/schema.d.ts.map +1 -1
  93. package/dist/generators/route/schema.js +90 -0
  94. package/dist/index.d.ts +2 -10
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +1980 -2115
  97. package/dist/plugins/index.d.ts +2 -0
  98. package/dist/plugins/index.d.ts.map +1 -0
  99. package/dist/plugins/wasp.d.ts +3 -0
  100. package/dist/plugins/wasp.d.ts.map +1 -0
  101. package/dist/types/constants.d.ts +4 -22
  102. package/dist/types/constants.d.ts.map +1 -1
  103. package/dist/types/constants.js +8 -2
  104. package/dist/types/index.d.ts +2 -2
  105. package/dist/types/index.d.ts.map +1 -1
  106. package/dist/types/index.js +8 -2
  107. package/dist/wasp-config/app.d.ts +2 -1
  108. package/dist/wasp-config/app.d.ts.map +1 -1
  109. package/dist/wasp-config/app.js +357 -0
  110. package/dist/wasp-config/index.js +357 -0
  111. package/dist/wasp-config/stubs/index.js +48 -0
  112. package/package.json +5 -14
  113. package/dist/common/plugin.d.ts +0 -2
  114. package/dist/common/plugin.d.ts.map +0 -1
  115. package/dist/generators/args.types.d.ts +0 -85
  116. package/dist/generators/args.types.d.ts.map +0 -1
  117. package/dist/generators/base/entity-generator.base.d.ts.map +0 -1
  118. package/dist/generators/feature-directory/feature-directory-generator.d.ts +0 -18
  119. package/dist/generators/feature-directory/feature-directory-generator.d.ts.map +0 -1
  120. package/dist/generators/feature-directory/index.d.ts +0 -2
  121. package/dist/generators/feature-directory/index.d.ts.map +0 -1
  122. package/dist/generators/feature-directory/schema.d.ts +0 -8
  123. package/dist/generators/feature-directory/schema.d.ts.map +0 -1
  124. package/dist/plugin.d.ts +0 -6
  125. package/dist/plugin.d.ts.map +0 -1
  126. /package/dist/generators/{feature-directory → feature}/templates/feature.wasp.eta +0 -0
@@ -0,0 +1,596 @@
1
+ // src/generators/config/wasp-config-generator.ts
2
+ import {
3
+ handleFatalError,
4
+ parseHelperMethodDefinition,
5
+ logger as singletonLogger
6
+ } from "@ingenyus/swarm";
7
+ import path4 from "path";
8
+
9
+ // src/common/filesystem.ts
10
+ import { toPascalCase, validateFeaturePath } from "@ingenyus/swarm";
11
+ import fs from "fs";
12
+ import path from "path";
13
+ var realFileSystem = {
14
+ readFileSync: fs.readFileSync,
15
+ writeFileSync: fs.writeFileSync,
16
+ existsSync: fs.existsSync,
17
+ copyFileSync: fs.copyFileSync,
18
+ mkdirSync: fs.mkdirSync,
19
+ readdirSync: fs.readdirSync,
20
+ statSync: fs.statSync
21
+ };
22
+ function findWaspRoot(fileSystem, startDir = process.cwd()) {
23
+ const startDirPath = path.resolve(startDir);
24
+ let currentDirPath = startDirPath;
25
+ const root = path.parse(currentDirPath).root;
26
+ while (currentDirPath !== root) {
27
+ const waspRootPath = path.join(currentDirPath, ".wasproot");
28
+ if (fileSystem.existsSync(waspRootPath)) {
29
+ return currentDirPath;
30
+ }
31
+ currentDirPath = path.dirname(currentDirPath);
32
+ }
33
+ throw new Error(
34
+ `Couldn't find Wasp application root from ${startDirPath}. Make sure you are running this command from within a Wasp project directory.`
35
+ );
36
+ }
37
+ function normaliseFeaturePath(featurePath) {
38
+ const segments = validateFeaturePath(featurePath);
39
+ const normalisedSegments = [];
40
+ for (let i = 0; i < segments.length; i++) {
41
+ const segment = segments[i];
42
+ const previousSegment = normalisedSegments[normalisedSegments.length - 1];
43
+ if (previousSegment !== "features" && segment !== "features") {
44
+ normalisedSegments.push("features");
45
+ }
46
+ normalisedSegments.push(segment);
47
+ }
48
+ return normalisedSegments.join("/");
49
+ }
50
+ function getFeatureDir(fileSystem, featureName) {
51
+ const waspRoot = findWaspRoot(fileSystem);
52
+ const normalisedPath = normaliseFeaturePath(featureName);
53
+ return path.join(waspRoot, "src", normalisedPath);
54
+ }
55
+
56
+ // src/common/prisma.ts
57
+ import {
58
+ getSchema
59
+ } from "@mrleebo/prisma-ast";
60
+ import fs2 from "fs";
61
+ import path2 from "path";
62
+
63
+ // src/common/schemas.ts
64
+ import { commandRegistry } from "@ingenyus/swarm";
65
+ import { z } from "zod";
66
+ var commonSchemas = {
67
+ feature: z.string().min(1, "Feature is required").meta({
68
+ description: "The feature directory this component will be generated in"
69
+ }).register(commandRegistry, {
70
+ shortName: "f",
71
+ examples: ["root", "auth", "dashboard/users"],
72
+ helpText: "Can be nested as a logical or relative path, e.g. 'dashboard/users' or 'features/dashboard/features/users'"
73
+ }),
74
+ name: z.string().min(1, "Name is required").meta({ description: "The name of the generated component" }).register(commandRegistry, {
75
+ shortName: "n",
76
+ examples: ["users", "task"],
77
+ helpText: "Will be used for generated files and configuration entries"
78
+ }),
79
+ target: z.string().min(1, "Target directory is required").meta({ description: "The target path of the generated directory" }).register(commandRegistry, {
80
+ shortName: "t",
81
+ examples: ["dashboard/users", "features/dashboard/features/users"],
82
+ helpText: "A logical or relative path, e.g. 'dashboard/users' or 'features/dashboard/features/users'"
83
+ }),
84
+ path: z.string().min(1, "Path is required").meta({ description: "The path that this component will be accessible at" }).register(commandRegistry, {
85
+ shortName: "p",
86
+ examples: ["/api/users/:id", "/api/products"],
87
+ helpText: "Supports Express-style placeholders, e.g. '/api/users/:id'"
88
+ }),
89
+ dataType: z.string().min(1, "Data type is required").meta({ description: "The data type/model name for this operation" }).register(commandRegistry, {
90
+ shortName: "d",
91
+ examples: ["User", "Product", "Task"],
92
+ helpText: "The Wasp entity or model name this operation will interact with"
93
+ }),
94
+ entities: z.array(z.string()).optional().meta({
95
+ description: "The Wasp entities that will be available to this component (optional)"
96
+ }).register(commandRegistry, {
97
+ shortName: "e",
98
+ examples: ["User", "User Task"],
99
+ helpText: "An array of Wasp entity names"
100
+ }),
101
+ force: z.boolean().optional().meta({
102
+ description: "Force overwrite of existing files and configuration entries (optional)"
103
+ }).register(commandRegistry, {
104
+ shortName: "F",
105
+ helpText: "CAUTION: Will overwrite existing files and configuration entries with current parameters"
106
+ }),
107
+ auth: z.boolean().optional().meta({
108
+ description: "Require authentication for this component (optional)"
109
+ }).register(commandRegistry, {
110
+ shortName: "a",
111
+ helpText: "Will generate authentication checks"
112
+ })
113
+ };
114
+
115
+ // src/common/templates.ts
116
+ import { toKebabCase } from "@ingenyus/swarm";
117
+ import { Eta } from "eta";
118
+ import path3 from "path";
119
+ var TemplateUtility = class {
120
+ constructor(fileSystem) {
121
+ this.fileSystem = fileSystem;
122
+ }
123
+ processTemplate(templatePath, replacements) {
124
+ const declarations = Object.keys(replacements).map((key) => `${key}=it.${key}`).join(", ");
125
+ const functionHeader = declarations ? `const ${declarations};` : void 0;
126
+ const templateDir = path3.dirname(templatePath);
127
+ const eta = new Eta({
128
+ autoTrim: false,
129
+ autoEscape: false,
130
+ views: templateDir,
131
+ functionHeader
132
+ });
133
+ const templateName = path3.basename(templatePath).replace(/\.eta$/, "");
134
+ if (this.fileSystem.existsSync(templatePath)) {
135
+ return eta.render(templateName, replacements);
136
+ } else {
137
+ const template = this.fileSystem.readFileSync(templatePath, "utf8");
138
+ return eta.renderString(template, replacements);
139
+ }
140
+ }
141
+ /**
142
+ * Helper method to resolve template paths for concrete generators
143
+ * @param relativePath - The relative path to the template file
144
+ * @param generatorName - The name of the generator (e.g., 'api', 'job')
145
+ * @param currentFileUrl - The import.meta.url from the concrete generator class
146
+ * @returns The full path to the template file
147
+ */
148
+ resolveTemplatePath(relativePath, generatorName, currentFileUrl) {
149
+ const generatorDirName = toKebabCase(generatorName);
150
+ const currentFilePath = new URL(currentFileUrl).pathname;
151
+ const currentFileDir = path3.dirname(currentFilePath);
152
+ const currentFileName = path3.basename(currentFilePath);
153
+ const isInstalledPackage = currentFileDir.includes("node_modules") && currentFileDir.endsWith("/dist") && currentFileName === "index.js";
154
+ const startDir = isInstalledPackage ? currentFileDir : path3.dirname(path3.dirname(currentFileDir));
155
+ return path3.join(
156
+ startDir,
157
+ "generators",
158
+ generatorDirName,
159
+ "templates",
160
+ relativePath
161
+ );
162
+ }
163
+ };
164
+
165
+ // src/generators/config/wasp-config-generator.ts
166
+ var WaspConfigGenerator = class {
167
+ constructor(logger = singletonLogger, fileSystem = realFileSystem) {
168
+ this.logger = logger;
169
+ this.fileSystem = fileSystem;
170
+ this.templateUtility = new TemplateUtility(fileSystem);
171
+ }
172
+ path = path4;
173
+ templateUtility;
174
+ /**
175
+ * Gets the template path for feature config templates.
176
+ * Feature config templates are located in the feature generator's templates directory.
177
+ * @param templateName - The name of the template file (e.g., 'feature.wasp.eta')
178
+ * @returns The full path to the template file
179
+ */
180
+ getTemplatePath(templateName) {
181
+ return this.templateUtility.resolveTemplatePath(
182
+ templateName,
183
+ "feature",
184
+ import.meta.url
185
+ );
186
+ }
187
+ /**
188
+ * Generate a TypeScript Wasp config file in a feature directory
189
+ * @param featurePath - The feature directory path
190
+ */
191
+ generate(featurePath) {
192
+ const featureDir = getFeatureDir(this.fileSystem, featurePath);
193
+ if (!this.fileSystem.existsSync(featureDir)) {
194
+ this.fileSystem.mkdirSync(featureDir, { recursive: true });
195
+ }
196
+ const templatePath = this.getTemplatePath("feature.wasp.eta");
197
+ if (!this.fileSystem.existsSync(templatePath)) {
198
+ this.logger.error(`Template not found: ${templatePath}`);
199
+ return;
200
+ }
201
+ const configFilePath = path4.join(featureDir, `feature.wasp.ts`);
202
+ if (this.fileSystem.existsSync(configFilePath)) {
203
+ this.logger.warn(`Feature config already exists: ${configFilePath}`);
204
+ return;
205
+ }
206
+ this.fileSystem.copyFileSync(templatePath, configFilePath);
207
+ this.logger.success(`Generated feature config: ${configFilePath}`);
208
+ }
209
+ /**
210
+ * Updates or creates a feature configuration file with a pre-built declaration.
211
+ * @param featurePath - The path to the feature
212
+ * @param declaration - The pre-built declaration string to add or update
213
+ * @returns The updated feature configuration file
214
+ */
215
+ update(featurePath, declaration) {
216
+ const configDir = getFeatureDir(this.fileSystem, featurePath);
217
+ const configFilePath = path4.join(configDir, `feature.wasp.ts`);
218
+ if (!this.fileSystem.existsSync(configFilePath)) {
219
+ const templatePath = this.getTemplatePath("feature.wasp.eta");
220
+ if (!this.fileSystem.existsSync(templatePath)) {
221
+ handleFatalError(`Feature config template not found: ${templatePath}`);
222
+ }
223
+ this.fileSystem.copyFileSync(templatePath, configFilePath);
224
+ }
225
+ let content = this.fileSystem.readFileSync(configFilePath, "utf8");
226
+ content = this.normaliseSemicolons(content);
227
+ const parsed = parseHelperMethodDefinition(declaration);
228
+ if (!parsed) {
229
+ handleFatalError(`Could not parse definition: ${declaration}`);
230
+ return content;
231
+ }
232
+ const { methodName } = parsed;
233
+ const hadExistingDefinitions = this.hasExistingDefinitions(
234
+ content,
235
+ methodName
236
+ );
237
+ content = this.removeExistingDefinition(content, declaration);
238
+ const hasExistingDefinitions = this.hasExistingDefinitions(
239
+ content,
240
+ methodName
241
+ );
242
+ const lines = content.split("\n");
243
+ const configureFunctionStart = lines.findIndex(
244
+ (line) => line.trim().startsWith("export default function")
245
+ );
246
+ if (configureFunctionStart === -1) {
247
+ handleFatalError("Could not find configure function in feature config");
248
+ }
249
+ const appLineIndex = lines.findIndex(
250
+ (line, index) => index > configureFunctionStart && line.trim() === "app"
251
+ );
252
+ if (appLineIndex === -1) {
253
+ const insertIndex = configureFunctionStart + 1;
254
+ const itemsToInsert = [" app"];
255
+ const comment = this.getMethodComment(methodName);
256
+ itemsToInsert.push(` ${comment}`);
257
+ itemsToInsert.push(declaration.trimEnd());
258
+ lines.splice(insertIndex, 0, ...itemsToInsert);
259
+ } else {
260
+ const { insertIndex, addComment } = this.findGroupInsertionPoint(
261
+ lines,
262
+ methodName,
263
+ declaration,
264
+ hadExistingDefinitions || hasExistingDefinitions
265
+ );
266
+ const newLines = this.insertWithSpacing(
267
+ lines,
268
+ declaration,
269
+ insertIndex,
270
+ methodName,
271
+ addComment
272
+ );
273
+ const normalisedContent2 = this.normaliseSemicolons(newLines.join("\n"));
274
+ this.fileSystem.writeFileSync(configFilePath, normalisedContent2);
275
+ return configFilePath;
276
+ }
277
+ const normalisedContent = this.normaliseSemicolons(lines.join("\n"));
278
+ this.fileSystem.writeFileSync(configFilePath, normalisedContent);
279
+ return configFilePath;
280
+ }
281
+ /**
282
+ * Determines the insertion index for a method name based on alphabetical ordering
283
+ * of existing groups in the configuration file.
284
+ * @param groups - Object containing existing method groups
285
+ * @param methodName - The method name to find insertion index for
286
+ * @returns The insertion index for the method name
287
+ */
288
+ getInsertionIndexForMethod(groups, methodName) {
289
+ const existingMethods = Object.keys(groups).filter(
290
+ (method) => groups[method].length > 0
291
+ );
292
+ const allMethods = [...existingMethods, methodName].sort();
293
+ return allMethods.indexOf(methodName);
294
+ }
295
+ /**
296
+ * Gets the comment text for a method type.
297
+ * @param methodName The method name (e.g., 'addApi')
298
+ * @returns The comment text for the method type
299
+ */
300
+ getMethodComment(methodName) {
301
+ const entityName = methodName.startsWith("add") ? methodName.slice(3) : methodName;
302
+ return `// ${entityName} definitions`;
303
+ }
304
+ /**
305
+ * Finds the correct insertion point for a new configuration item.
306
+ * @param lines - Array of file lines
307
+ * @param methodName - The method name (e.g., 'addApi')
308
+ * @param definition - The definition string to parse for item name
309
+ * @returns Object with insertion index and whether to add a comment
310
+ */
311
+ findGroupInsertionPoint(lines, methodName, definition, hasExistingDefinitionsOfType) {
312
+ const appLineIndex = lines.findIndex((line) => line.trim() === "app");
313
+ if (appLineIndex === -1) {
314
+ return { insertIndex: appLineIndex + 1, addComment: false };
315
+ }
316
+ const methodCalls = [];
317
+ for (let i = appLineIndex + 1; i < lines.length; i++) {
318
+ const line = lines[i].trim();
319
+ if (line.startsWith(".") && line.includes("(")) {
320
+ let methodCallContent = line;
321
+ let j = i;
322
+ let closingParenCount = 0;
323
+ let foundClosingParen = false;
324
+ for (let k = 0; k < methodCallContent.length; k++) {
325
+ if (methodCallContent[k] === "(") closingParenCount++;
326
+ if (methodCallContent[k] === ")") closingParenCount--;
327
+ if (closingParenCount === 0 && methodCallContent[k] === ")") {
328
+ foundClosingParen = true;
329
+ break;
330
+ }
331
+ }
332
+ while (!foundClosingParen && j < lines.length - 1) {
333
+ j++;
334
+ methodCallContent += " " + lines[j].trim();
335
+ for (let k = 0; k < lines[j].length; k++) {
336
+ if (lines[j][k] === "(") closingParenCount++;
337
+ if (lines[j][k] === ")") closingParenCount--;
338
+ if (closingParenCount === 0 && lines[j][k] === ")") {
339
+ foundClosingParen = true;
340
+ break;
341
+ }
342
+ }
343
+ }
344
+ const match = methodCallContent.match(
345
+ /\.(\w+)\([^,]+,\s*['"`]([^'"`]+)['"`]/
346
+ );
347
+ if (match) {
348
+ methodCalls.push({
349
+ lineIndex: i,
350
+ endLineIndex: j,
351
+ methodName: match[1],
352
+ itemName: match[2]
353
+ });
354
+ }
355
+ }
356
+ }
357
+ const groups = {};
358
+ methodCalls.forEach((call) => {
359
+ if (!groups[call.methodName]) {
360
+ groups[call.methodName] = [];
361
+ }
362
+ groups[call.methodName].push({
363
+ lineIndex: call.lineIndex,
364
+ endLineIndex: call.endLineIndex,
365
+ itemName: call.itemName
366
+ });
367
+ });
368
+ const targetGroup = groups[methodName] || [];
369
+ if (targetGroup.length === 0) {
370
+ const targetGroupIndex = this.getInsertionIndexForMethod(
371
+ groups,
372
+ methodName
373
+ );
374
+ const existingMethods = Object.keys(groups).filter((method) => groups[method].length > 0).sort();
375
+ for (let i = targetGroupIndex; i < existingMethods.length; i++) {
376
+ const groupMethod = existingMethods[i];
377
+ if (groups[groupMethod] && groups[groupMethod].length > 0) {
378
+ const firstItem = groups[groupMethod][0];
379
+ let insertIndex = firstItem.lineIndex;
380
+ for (let j = firstItem.lineIndex - 1; j > appLineIndex; j--) {
381
+ const line = lines[j].trim();
382
+ if (line.startsWith("//") && line.includes("definitions")) {
383
+ insertIndex = j;
384
+ break;
385
+ } else if (line.startsWith(".") || line === "") {
386
+ continue;
387
+ } else {
388
+ break;
389
+ }
390
+ }
391
+ return { insertIndex, addComment: !hasExistingDefinitionsOfType };
392
+ }
393
+ }
394
+ for (let i = targetGroupIndex - 1; i >= 0; i--) {
395
+ const groupMethod = existingMethods[i];
396
+ if (groups[groupMethod] && groups[groupMethod].length > 0) {
397
+ const lastItem2 = groups[groupMethod][groups[groupMethod].length - 1];
398
+ return {
399
+ insertIndex: lastItem2.endLineIndex + 1,
400
+ addComment: !hasExistingDefinitionsOfType
401
+ };
402
+ }
403
+ }
404
+ return {
405
+ insertIndex: appLineIndex + 1,
406
+ addComment: !hasExistingDefinitionsOfType
407
+ };
408
+ }
409
+ const parsed = parseHelperMethodDefinition(definition);
410
+ if (!parsed) {
411
+ return { insertIndex: appLineIndex + 1, addComment: false };
412
+ }
413
+ const { firstParam: itemName } = parsed;
414
+ for (let i = 0; i < targetGroup.length; i++) {
415
+ if (itemName.localeCompare(targetGroup[i].itemName) < 0) {
416
+ return { insertIndex: targetGroup[i].lineIndex, addComment: false };
417
+ }
418
+ }
419
+ const lastItem = targetGroup[targetGroup.length - 1];
420
+ return { insertIndex: lastItem.endLineIndex + 1, addComment: false };
421
+ }
422
+ /**
423
+ * Inserts a definition with optional comment header.
424
+ * @param lines - Array of file lines
425
+ * @param declaration - The declaration to insert
426
+ * @param insertIndex - The index where to insert
427
+ * @param methodName - The method name for comment generation
428
+ * @param addComment - Whether to add a comment before the declaration
429
+ * @returns The modified lines array
430
+ */
431
+ insertWithSpacing(lines, declaration, insertIndex, methodName, addComment = false) {
432
+ const newLines = [...lines];
433
+ if (addComment) {
434
+ const comment = this.getMethodComment(methodName);
435
+ newLines.splice(insertIndex, 0, ` ${comment}`);
436
+ insertIndex += 1;
437
+ }
438
+ newLines.splice(insertIndex, 0, declaration.trimEnd());
439
+ return newLines;
440
+ }
441
+ /**
442
+ * Checks if there are any existing definitions of a specific type in the content.
443
+ * @param content - The file content to search
444
+ * @param methodName - The method name to check for (e.g., 'addJob', 'addApi')
445
+ * @returns true if there are existing definitions of this type, false otherwise
446
+ */
447
+ hasExistingDefinitions(content, methodName) {
448
+ const lines = content.split("\n");
449
+ for (const line of lines) {
450
+ if (line.trim().startsWith(`.${methodName}(`)) {
451
+ return true;
452
+ }
453
+ }
454
+ return false;
455
+ }
456
+ /**
457
+ * Removes an existing definition from the content by finding the helper method call
458
+ * and removing the entire method call block.
459
+ * @param content - The file content
460
+ * @param definition - The new definition to find the existing one from
461
+ * @returns The content with the existing definition removed
462
+ */
463
+ removeExistingDefinition(content, definition) {
464
+ const parsed = parseHelperMethodDefinition(definition);
465
+ if (!parsed) {
466
+ return content;
467
+ }
468
+ const { methodName, firstParam } = parsed;
469
+ let contentLines = content.split("\n");
470
+ let openingLineIndex = -1;
471
+ for (let i = 0; i < contentLines.length; i++) {
472
+ const line = contentLines[i];
473
+ if (line.trim().startsWith(`.${methodName}(`)) {
474
+ if (firstParam && line.includes(firstParam)) {
475
+ openingLineIndex = i;
476
+ break;
477
+ }
478
+ }
479
+ }
480
+ if (openingLineIndex === -1) {
481
+ return content;
482
+ }
483
+ let closingLineIndex = -1;
484
+ let parenCount = 0;
485
+ let braceCount = 0;
486
+ let foundOpening = false;
487
+ for (let i = openingLineIndex; i < contentLines.length; i++) {
488
+ const line = contentLines[i];
489
+ for (const char of line) {
490
+ if (char === "(") {
491
+ parenCount++;
492
+ foundOpening = true;
493
+ } else if (char === ")") {
494
+ parenCount--;
495
+ if (foundOpening && parenCount === 0 && braceCount === 0) {
496
+ closingLineIndex = i;
497
+ break;
498
+ }
499
+ } else if (char === "{") {
500
+ braceCount++;
501
+ } else if (char === "}") {
502
+ braceCount--;
503
+ }
504
+ }
505
+ if (closingLineIndex !== -1) {
506
+ break;
507
+ }
508
+ }
509
+ if (closingLineIndex === -1) {
510
+ this.logger.warn(
511
+ "Could not find closing parenthesis for existing definition"
512
+ );
513
+ return content;
514
+ }
515
+ contentLines.splice(
516
+ openingLineIndex,
517
+ closingLineIndex - openingLineIndex + 1
518
+ );
519
+ return contentLines.join("\n");
520
+ }
521
+ /**
522
+ * Adds a definition to the content by finding the appropriate place to insert it.
523
+ * @param content - The current file content
524
+ * @param definition - The definition to add
525
+ * @returns The updated content with the new definition
526
+ */
527
+ addDefinitionToContent(content, definition) {
528
+ const lines = content.split("\n");
529
+ const lastLineIndex = lines.length - 1;
530
+ let insertIndex = lastLineIndex;
531
+ for (let i = lastLineIndex; i >= 0; i--) {
532
+ const line = lines[i].trim();
533
+ if (line && !line.startsWith("}")) {
534
+ insertIndex = i;
535
+ break;
536
+ }
537
+ }
538
+ lines.splice(insertIndex + 1, 0, ` ${definition}`);
539
+ return lines.join("\n");
540
+ }
541
+ /**
542
+ * Normalises semicolons in the config file by removing them from method chain calls
543
+ * while preserving them in other contexts (imports, declarations, etc.).
544
+ * @param content - The file content to normalise
545
+ * @returns The normalised content
546
+ */
547
+ normaliseSemicolons(content) {
548
+ const lines = content.split("\n");
549
+ const configureFunctionStart = lines.findIndex(
550
+ (line) => line.trim().startsWith("export default function")
551
+ );
552
+ if (configureFunctionStart === -1) {
553
+ return content;
554
+ }
555
+ const appLineIndex = lines.findIndex(
556
+ (line, index) => index > configureFunctionStart && line.trim().startsWith("app")
557
+ );
558
+ if (appLineIndex === -1) {
559
+ return content;
560
+ }
561
+ let braceCount = 0;
562
+ let functionEndIndex = lines.length - 1;
563
+ for (let i = configureFunctionStart; i < lines.length; i++) {
564
+ const line = lines[i];
565
+ for (const char of line) {
566
+ if (char === "{") braceCount++;
567
+ if (char === "}") {
568
+ braceCount--;
569
+ if (braceCount === 0) {
570
+ functionEndIndex = i;
571
+ break;
572
+ }
573
+ }
574
+ }
575
+ if (braceCount === 0 && i > configureFunctionStart) {
576
+ break;
577
+ }
578
+ }
579
+ let lastMethodCallIndex = -1;
580
+ for (let i = appLineIndex + 1; i < functionEndIndex; i++) {
581
+ const line = lines[i];
582
+ const trimmed = line.trim();
583
+ if ((trimmed.endsWith(")") || trimmed.endsWith(");")) && !trimmed.startsWith("//")) {
584
+ lines[i] = line.replace(/;\s*$/, "");
585
+ lastMethodCallIndex = i;
586
+ }
587
+ }
588
+ if (lastMethodCallIndex !== -1 && !lines[lastMethodCallIndex].trim().endsWith(";")) {
589
+ lines[lastMethodCallIndex] = lines[lastMethodCallIndex] + ";";
590
+ }
591
+ return lines.join("\n");
592
+ }
593
+ };
594
+ export {
595
+ WaspConfigGenerator
596
+ };
@@ -1,31 +1,43 @@
1
- import { CrudFlags } from '../../generators/args.types';
1
+ import { Out } from '@ingenyus/swarm';
2
2
  import { CONFIG_TYPES } from '../../types';
3
3
  import { OperationGeneratorBase } from '../base';
4
- export declare class CrudGenerator extends OperationGeneratorBase<typeof CONFIG_TYPES.CRUD> {
5
- protected get entityType(): "Crud";
4
+ import { schema } from './schema';
5
+ export declare class CrudGenerator extends OperationGeneratorBase<typeof schema, typeof CONFIG_TYPES.CRUD> {
6
+ protected get componentType(): "Crud";
6
7
  description: string;
7
8
  schema: import("zod").ZodObject<{
8
- feature: import("zod").ZodString & {
9
- _metadata: import("@ingenyus/swarm").FieldMetadata;
10
- };
11
- name: import("zod").ZodString & {
12
- _metadata: import("@ingenyus/swarm").FieldMetadata;
13
- };
14
- public: import("zod").ZodPipe<import("zod").ZodOptional<import("zod").ZodString>, import("zod").ZodTransform<("create" | "update" | "delete" | "get" | "getAll")[] | undefined, string | undefined>> & {
15
- _metadata: import("@ingenyus/swarm").FieldMetadata;
16
- };
17
- override: import("zod").ZodPipe<import("zod").ZodOptional<import("zod").ZodString>, import("zod").ZodTransform<("create" | "update" | "delete" | "get" | "getAll")[] | undefined, string | undefined>> & {
18
- _metadata: import("@ingenyus/swarm").FieldMetadata;
19
- };
20
- exclude: import("zod").ZodPipe<import("zod").ZodOptional<import("zod").ZodString>, import("zod").ZodTransform<("create" | "update" | "delete" | "get" | "getAll")[] | undefined, string | undefined>> & {
21
- _metadata: import("@ingenyus/swarm").FieldMetadata;
22
- };
23
- force: import("zod").ZodOptional<import("zod").ZodBoolean> & {
24
- _metadata: import("@ingenyus/swarm").FieldMetadata;
25
- };
9
+ feature: import("zod").ZodString;
10
+ name: import("zod").ZodString;
11
+ dataType: import("zod").ZodString;
12
+ public: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodPipe<import("zod").ZodPipe<import("zod").ZodString, import("zod").ZodTransform<string, string>>, import("zod").ZodEnum<{
13
+ readonly CREATE: "create";
14
+ readonly GET: "get";
15
+ readonly GETALL: "getAll";
16
+ readonly UPDATE: "update";
17
+ readonly DELETE: "delete";
18
+ }>>>>;
19
+ override: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodPipe<import("zod").ZodPipe<import("zod").ZodString, import("zod").ZodTransform<string, string>>, import("zod").ZodEnum<{
20
+ readonly CREATE: "create";
21
+ readonly GET: "get";
22
+ readonly GETALL: "getAll";
23
+ readonly UPDATE: "update";
24
+ readonly DELETE: "delete";
25
+ }>>>>;
26
+ exclude: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodPipe<import("zod").ZodPipe<import("zod").ZodString, import("zod").ZodTransform<string, string>>, import("zod").ZodEnum<{
27
+ readonly CREATE: "create";
28
+ readonly GET: "get";
29
+ readonly GETALL: "getAll";
30
+ readonly UPDATE: "update";
31
+ readonly DELETE: "delete";
32
+ }>>>>;
33
+ force: import("zod").ZodOptional<import("zod").ZodBoolean>;
26
34
  }, import("zod/v4/core").$strip>;
27
- generate(flags: CrudFlags): Promise<void>;
35
+ generate(args: Out<typeof schema>): Promise<void>;
28
36
  private generateCrudFile;
37
+ /**
38
+ * Generates import statements for an operation.
39
+ */
40
+ private generateCrudImports;
29
41
  private updateConfigFile;
30
42
  private buildOperations;
31
43
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"crud-generator.d.ts","sourceRoot":"","sources":["../../../src/generators/crud/crud-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAWjD,qBAAa,aAAc,SAAQ,sBAAsB,CACvD,OAAO,YAAY,CAAC,IAAI,CACzB;IACC,SAAS,KAAK,UAAU,WAEvB;IAED,WAAW,SAAoD;IAC/D,MAAM;;;;;;;;;;;;;;;;;;;qCAAU;IAEV,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;YAwCjC,gBAAgB;YAyBhB,gBAAgB;IAoB9B,OAAO,CAAC,eAAe;IA+BvB;;OAEG;YACW,iBAAiB;IA0B/B;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,GAAG,MAAM;CAoB3E"}
1
+ {"version":3,"file":"crud-generator.d.ts","sourceRoot":"","sources":["../../../src/generators/crud/crud-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,GAAG,EAA6B,MAAM,iBAAiB,CAAC;AAE5E,OAAO,EAAE,YAAY,EAAiC,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAUlC,qBAAa,aAAc,SAAQ,sBAAsB,CACvD,OAAO,MAAM,EACb,OAAO,YAAY,CAAC,IAAI,CACzB;IACC,SAAS,KAAK,aAAa,WAE1B;IAED,WAAW,SAAqC;IAChD,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;qCAAU;IAEV,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAuCzC,gBAAgB;IA8B9B;;OAEG;IACH,OAAO,CAAC,mBAAmB;YAsBb,gBAAgB;IAoB9B,OAAO,CAAC,eAAe;IA+BvB;;OAEG;YACW,iBAAiB;IA4B/B;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,GAAG,MAAM;CAoB3E"}