@ingenyus/swarm-wasp 1.0.3 → 1.2.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 (51) hide show
  1. package/README.md +14 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/common/index.d.ts +1 -0
  4. package/dist/common/index.d.ts.map +1 -1
  5. package/dist/common/index.js +81 -0
  6. package/dist/common/wasp-compatibility.d.ts +10 -0
  7. package/dist/common/wasp-compatibility.d.ts.map +1 -0
  8. package/dist/common/wasp-compatibility.js +82 -0
  9. package/dist/generators/action/action-generator.d.ts.map +1 -1
  10. package/dist/generators/action/action-generator.js +102 -13
  11. package/dist/generators/action/index.js +102 -13
  12. package/dist/generators/action/schema.js +7 -0
  13. package/dist/generators/api/api-generator.d.ts.map +1 -1
  14. package/dist/generators/api/api-generator.js +104 -15
  15. package/dist/generators/api/index.js +104 -15
  16. package/dist/generators/api/schema.js +7 -0
  17. package/dist/generators/api-namespace/api-namespace-generator.d.ts.map +1 -1
  18. package/dist/generators/api-namespace/api-namespace-generator.js +104 -15
  19. package/dist/generators/api-namespace/index.js +104 -15
  20. package/dist/generators/api-namespace/schema.js +7 -0
  21. package/dist/generators/base/component-generator.base.js +101 -13
  22. package/dist/generators/base/index.js +101 -13
  23. package/dist/generators/base/operation-generator.base.js +101 -13
  24. package/dist/generators/base/wasp-generator.base.d.ts +5 -0
  25. package/dist/generators/base/wasp-generator.base.d.ts.map +1 -1
  26. package/dist/generators/base/wasp-generator.base.js +93 -6
  27. package/dist/generators/config/index.js +11 -4
  28. package/dist/generators/config/wasp-config-generator.js +11 -4
  29. package/dist/generators/crud/crud-generator.d.ts.map +1 -1
  30. package/dist/generators/crud/crud-generator.js +102 -13
  31. package/dist/generators/crud/index.js +102 -13
  32. package/dist/generators/crud/schema.js +7 -0
  33. package/dist/generators/feature/feature-generator.d.ts.map +1 -1
  34. package/dist/generators/feature/feature-generator.js +98 -10
  35. package/dist/generators/feature/index.js +98 -10
  36. package/dist/generators/feature/schema.js +7 -0
  37. package/dist/generators/index.js +112 -17
  38. package/dist/generators/job/index.js +102 -13
  39. package/dist/generators/job/job-generator.d.ts.map +1 -1
  40. package/dist/generators/job/job-generator.js +102 -13
  41. package/dist/generators/job/schema.js +7 -0
  42. package/dist/generators/query/index.js +102 -13
  43. package/dist/generators/query/query-generator.d.ts.map +1 -1
  44. package/dist/generators/query/query-generator.js +102 -13
  45. package/dist/generators/query/schema.js +7 -0
  46. package/dist/generators/route/index.js +102 -13
  47. package/dist/generators/route/route-generator.d.ts.map +1 -1
  48. package/dist/generators/route/route-generator.js +102 -13
  49. package/dist/generators/route/schema.js +7 -0
  50. package/dist/index.js +120 -25
  51. package/package.json +11 -6
@@ -381,22 +381,102 @@ var TemplateUtility = class {
381
381
  }
382
382
  };
383
383
 
384
+ // src/common/wasp-compatibility.ts
385
+ import { findPackageJson, getVersion } from "@ingenyus/swarm";
386
+ import { execSync } from "child_process";
387
+ import path4 from "path";
388
+ import { fileURLToPath } from "url";
389
+ import * as semver from "semver";
390
+ var cachedSupportedRange = null;
391
+ function getWaspSupportedRange() {
392
+ if (cachedSupportedRange) {
393
+ return cachedSupportedRange;
394
+ }
395
+ const currentFile = fileURLToPath(import.meta.url);
396
+ const currentDir = path4.dirname(currentFile);
397
+ const result = findPackageJson(currentDir, {
398
+ packageName: "@ingenyus/swarm-wasp"
399
+ });
400
+ if (!result) {
401
+ throw new Error(
402
+ "Unable to find package.json for @ingenyus/swarm-wasp. Wasp supported version range must be specified in swarm.wasp field."
403
+ );
404
+ }
405
+ const swarm = result.packageJson.swarm;
406
+ const waspRange = swarm?.wasp;
407
+ if (!waspRange) {
408
+ throw new Error(
409
+ `Wasp supported version range not found in package.json. Please specify swarm.wasp field in @ingenyus/swarm-wasp package.json (e.g., "swarm": { "wasp": ">=0.18.0 <0.20.0" }).`
410
+ );
411
+ }
412
+ cachedSupportedRange = waspRange;
413
+ return cachedSupportedRange;
414
+ }
415
+ function getInstalledWaspVersion(logger) {
416
+ try {
417
+ const output = execSync("wasp version", {
418
+ encoding: "utf8",
419
+ stdio: "pipe"
420
+ });
421
+ const firstLine = output.split("\n")[0]?.trim();
422
+ if (!firstLine) {
423
+ logger.error(
424
+ "Unable to parse Wasp version from command output. Expected version number on first line."
425
+ );
426
+ throw new Error("Unable to parse Wasp version from command output");
427
+ }
428
+ if (!semver.valid(firstLine)) {
429
+ logger.error(
430
+ `Invalid Wasp version format: "${firstLine}". Expected a valid semver version (e.g., "0.18.2").`
431
+ );
432
+ throw new Error("Invalid Wasp version format");
433
+ }
434
+ return firstLine;
435
+ } catch (error) {
436
+ if (error.code === "ENOENT" || error.message?.includes("wasp")) {
437
+ logger.error(
438
+ "Wasp CLI not found. Install using: curl -sSL https://get.wasp.sh/installer.sh | sh -s"
439
+ );
440
+ throw new Error("Wasp CLI not found");
441
+ }
442
+ throw new Error("Unable to determine installed Wasp version");
443
+ }
444
+ }
445
+ function isTestEnvironment() {
446
+ return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || typeof process.env.VITEST !== "undefined";
447
+ }
448
+ function assertWaspCompatible(logger) {
449
+ if (isTestEnvironment()) {
450
+ return;
451
+ }
452
+ const version = getInstalledWaspVersion(logger);
453
+ const supportedRange = getWaspSupportedRange();
454
+ if (!semver.satisfies(version, supportedRange)) {
455
+ const startDir = path4.dirname(fileURLToPath(import.meta.url));
456
+ const packageVersion = getVersion("@ingenyus/swarm-wasp", startDir);
457
+ logger.error(
458
+ `Incompatible Wasp version detected: ${version}. @ingenyus/swarm-wasp@${packageVersion} supports Wasp ${supportedRange}.`
459
+ );
460
+ throw new Error("Incompatible Wasp version");
461
+ }
462
+ }
463
+
384
464
  // src/generators/base/component-generator.base.ts
385
465
  import {
386
466
  hasHelperMethodCall,
387
467
  toKebabCase as toKebabCase2
388
468
  } from "@ingenyus/swarm";
389
- import path6 from "path";
469
+ import path7 from "path";
390
470
 
391
471
  // src/generators/feature/feature-generator.ts
392
472
  import { handleFatalError as handleFatalError2 } from "@ingenyus/swarm";
393
- import path5 from "path";
473
+ import path6 from "path";
394
474
 
395
475
  // src/generators/base/wasp-generator.base.ts
396
476
  import {
397
477
  GeneratorBase,
398
- getConfigManager,
399
- TemplateResolver
478
+ TemplateResolver,
479
+ getConfigManager
400
480
  } from "@ingenyus/swarm";
401
481
 
402
482
  // src/generators/config/wasp-config-generator.ts
@@ -405,14 +485,14 @@ import {
405
485
  handleFatalError,
406
486
  parseHelperMethodDefinition
407
487
  } from "@ingenyus/swarm";
408
- import path4 from "path";
488
+ import path5 from "path";
409
489
  var WaspConfigGenerator = class {
410
490
  constructor(logger = getCLILogger(), fileSystem = realFileSystem) {
411
491
  this.logger = logger;
412
492
  this.fileSystem = fileSystem;
413
493
  this.templateUtility = new TemplateUtility(fileSystem);
414
494
  }
415
- path = path4;
495
+ path = path5;
416
496
  templateUtility;
417
497
  /**
418
498
  * Gets the template path for feature config templates.
@@ -441,7 +521,7 @@ var WaspConfigGenerator = class {
441
521
  this.logger.error(`Template not found: ${templatePath}`);
442
522
  return;
443
523
  }
444
- const configFilePath = path4.join(featureDir, `feature.wasp.ts`);
524
+ const configFilePath = path5.join(featureDir, `feature.wasp.ts`);
445
525
  if (this.fileSystem.existsSync(configFilePath)) {
446
526
  this.logger.warn(`Feature config already exists: ${configFilePath}`);
447
527
  return;
@@ -457,7 +537,7 @@ var WaspConfigGenerator = class {
457
537
  */
458
538
  update(featurePath, declaration) {
459
539
  const configDir = getFeatureDir(this.fileSystem, featurePath);
460
- const configFilePath = path4.join(configDir, `feature.wasp.ts`);
540
+ const configFilePath = path5.join(configDir, `feature.wasp.ts`);
461
541
  if (!this.fileSystem.existsSync(configFilePath)) {
462
542
  const templatePath = this.getTemplatePath("feature.wasp.eta");
463
543
  if (!this.fileSystem.existsSync(templatePath)) {
@@ -853,6 +933,13 @@ var WaspGeneratorBase = class extends GeneratorBase {
853
933
  this.templateUtility = new TemplateUtility(this.fileSystem);
854
934
  this.templateResolver = new TemplateResolver(this.fileSystem);
855
935
  }
936
+ /**
937
+ * Ensures that the installed Wasp version is compatible with this package.
938
+ * Should be called at the start of generator execution.
939
+ */
940
+ ensureWaspCompatible() {
941
+ assertWaspCompatible(this.logger);
942
+ }
856
943
  async loadConfig() {
857
944
  if (this.configLoaded) return;
858
945
  const configManager = getConfigManager();
@@ -969,21 +1056,22 @@ var FeatureGenerator = class extends WaspGeneratorBase {
969
1056
  * @param target - The target path of the generated directory
970
1057
  */
971
1058
  async generate(args) {
1059
+ this.ensureWaspCompatible();
972
1060
  const { target } = args;
973
1061
  const segments = validateFeaturePath(target);
974
1062
  const normalisedPath = normaliseFeaturePath(target);
975
- const sourceRoot = path5.join(findWaspRoot(this.fileSystem), "src");
1063
+ const sourceRoot = path6.join(findWaspRoot(this.fileSystem), "src");
976
1064
  if (segments.length > 1) {
977
1065
  const parentPath = segments.slice(0, -1).join("/");
978
1066
  const parentNormalisedPath = normaliseFeaturePath(parentPath);
979
- const parentFeatureDir = path5.join(sourceRoot, parentNormalisedPath);
1067
+ const parentFeatureDir = path6.join(sourceRoot, parentNormalisedPath);
980
1068
  if (!this.fileSystem.existsSync(parentFeatureDir)) {
981
1069
  handleFatalError2(
982
1070
  `Parent feature '${parentPath}' does not exist. Please create it first.`
983
1071
  );
984
1072
  }
985
1073
  }
986
- const featureDir = path5.join(sourceRoot, normalisedPath);
1074
+ const featureDir = path6.join(sourceRoot, normalisedPath);
987
1075
  this.fileSystem.mkdirSync(featureDir, { recursive: true });
988
1076
  this.configGenerator.generate(normalisedPath);
989
1077
  this.logger.success(`Generated feature: ${normalisedPath}`);
@@ -1018,7 +1106,7 @@ var ComponentGeneratorBase = class extends WaspGeneratorBase {
1018
1106
  const currentPath = pathSegments.join("/");
1019
1107
  const featureName = pathSegments[pathSegments.length - 1];
1020
1108
  const featureDir = getFeatureDir(this.fileSystem, currentPath);
1021
- const configPath = path6.join(featureDir, `feature.wasp.ts`);
1109
+ const configPath = path7.join(featureDir, `feature.wasp.ts`);
1022
1110
  if (this.fileSystem.existsSync(configPath)) {
1023
1111
  return configPath;
1024
1112
  }
@@ -1090,7 +1178,7 @@ var ComponentGeneratorBase = class extends WaspGeneratorBase {
1090
1178
  const featureDir = getFeatureDir(this.fileSystem, normalisedPath);
1091
1179
  const typeKey = type.toLowerCase();
1092
1180
  const typeDirectory = TYPE_DIRECTORIES[typeKey];
1093
- const targetDirectory = path6.join(featureDir, typeDirectory);
1181
+ const targetDirectory = path7.join(featureDir, typeDirectory);
1094
1182
  const importDirectory = `@src/${normalisedPath}/${typeDirectory}`;
1095
1183
  return { targetDirectory, importDirectory };
1096
1184
  }
@@ -1472,6 +1560,7 @@ var CrudGenerator = class extends OperationGeneratorBase {
1472
1560
  const crudName = name || toCamelCase(getPlural2(dataType));
1473
1561
  const crudType = toPascalCase3(crudName);
1474
1562
  return this.handleGeneratorError(this.componentType, crudName, async () => {
1563
+ this.ensureWaspCompatible();
1475
1564
  const configPath = this.validateFeatureConfig(feature);
1476
1565
  const { targetDirectory } = this.ensureTargetDirectory(
1477
1566
  feature,
@@ -113,6 +113,13 @@ var commonFieldMetadata = {
113
113
  import { toKebabCase } from "@ingenyus/swarm";
114
114
  import { Eta } from "eta";
115
115
 
116
+ // src/common/wasp-compatibility.ts
117
+ import { findPackageJson, getVersion } from "@ingenyus/swarm";
118
+ import { execSync } from "child_process";
119
+ import path2 from "path";
120
+ import { fileURLToPath } from "url";
121
+ import * as semver from "semver";
122
+
116
123
  // src/generators/crud/schema.ts
117
124
  var validCrudOperations = Object.values(CRUD_OPERATIONS);
118
125
  var publicOperations = getCrudOperationsArray();
@@ -1 +1 @@
1
- {"version":3,"file":"feature-generator.d.ts","sourceRoot":"","sources":["../../../src/generators/feature/feature-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAoB,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAO3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,qBAAa,gBAAiB,SAAQ,iBAAiB,CAAC,OAAO,MAAM,CAAC;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM;;qCAAU;gBAEJ,QAAQ,EAAE,iBAAiB;IAOvC,SAAS,CAAC,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAQ9D;;;OAGG;IACG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAyBxD"}
1
+ {"version":3,"file":"feature-generator.d.ts","sourceRoot":"","sources":["../../../src/generators/feature/feature-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAoB,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAO3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,qBAAa,gBAAiB,SAAQ,iBAAiB,CAAC,OAAO,MAAM,CAAC;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM;;qCAAU;gBAEJ,QAAQ,EAAE,iBAAiB;IAOvC,SAAS,CAAC,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAQ9D;;;OAGG;IACG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CA2BxD"}
@@ -1,6 +1,6 @@
1
1
  // src/generators/feature/feature-generator.ts
2
2
  import { handleFatalError as handleFatalError2 } from "@ingenyus/swarm";
3
- import path5 from "path";
3
+ import path6 from "path";
4
4
 
5
5
  // src/common/constants.ts
6
6
  var PLUGIN_NAME = "wasp";
@@ -194,11 +194,91 @@ var TemplateUtility = class {
194
194
  }
195
195
  };
196
196
 
197
+ // src/common/wasp-compatibility.ts
198
+ import { findPackageJson, getVersion } from "@ingenyus/swarm";
199
+ import { execSync } from "child_process";
200
+ import path4 from "path";
201
+ import { fileURLToPath } from "url";
202
+ import * as semver from "semver";
203
+ var cachedSupportedRange = null;
204
+ function getWaspSupportedRange() {
205
+ if (cachedSupportedRange) {
206
+ return cachedSupportedRange;
207
+ }
208
+ const currentFile = fileURLToPath(import.meta.url);
209
+ const currentDir = path4.dirname(currentFile);
210
+ const result = findPackageJson(currentDir, {
211
+ packageName: "@ingenyus/swarm-wasp"
212
+ });
213
+ if (!result) {
214
+ throw new Error(
215
+ "Unable to find package.json for @ingenyus/swarm-wasp. Wasp supported version range must be specified in swarm.wasp field."
216
+ );
217
+ }
218
+ const swarm = result.packageJson.swarm;
219
+ const waspRange = swarm?.wasp;
220
+ if (!waspRange) {
221
+ throw new Error(
222
+ `Wasp supported version range not found in package.json. Please specify swarm.wasp field in @ingenyus/swarm-wasp package.json (e.g., "swarm": { "wasp": ">=0.18.0 <0.20.0" }).`
223
+ );
224
+ }
225
+ cachedSupportedRange = waspRange;
226
+ return cachedSupportedRange;
227
+ }
228
+ function getInstalledWaspVersion(logger) {
229
+ try {
230
+ const output = execSync("wasp version", {
231
+ encoding: "utf8",
232
+ stdio: "pipe"
233
+ });
234
+ const firstLine = output.split("\n")[0]?.trim();
235
+ if (!firstLine) {
236
+ logger.error(
237
+ "Unable to parse Wasp version from command output. Expected version number on first line."
238
+ );
239
+ throw new Error("Unable to parse Wasp version from command output");
240
+ }
241
+ if (!semver.valid(firstLine)) {
242
+ logger.error(
243
+ `Invalid Wasp version format: "${firstLine}". Expected a valid semver version (e.g., "0.18.2").`
244
+ );
245
+ throw new Error("Invalid Wasp version format");
246
+ }
247
+ return firstLine;
248
+ } catch (error) {
249
+ if (error.code === "ENOENT" || error.message?.includes("wasp")) {
250
+ logger.error(
251
+ "Wasp CLI not found. Install using: curl -sSL https://get.wasp.sh/installer.sh | sh -s"
252
+ );
253
+ throw new Error("Wasp CLI not found");
254
+ }
255
+ throw new Error("Unable to determine installed Wasp version");
256
+ }
257
+ }
258
+ function isTestEnvironment() {
259
+ return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || typeof process.env.VITEST !== "undefined";
260
+ }
261
+ function assertWaspCompatible(logger) {
262
+ if (isTestEnvironment()) {
263
+ return;
264
+ }
265
+ const version = getInstalledWaspVersion(logger);
266
+ const supportedRange = getWaspSupportedRange();
267
+ if (!semver.satisfies(version, supportedRange)) {
268
+ const startDir = path4.dirname(fileURLToPath(import.meta.url));
269
+ const packageVersion = getVersion("@ingenyus/swarm-wasp", startDir);
270
+ logger.error(
271
+ `Incompatible Wasp version detected: ${version}. @ingenyus/swarm-wasp@${packageVersion} supports Wasp ${supportedRange}.`
272
+ );
273
+ throw new Error("Incompatible Wasp version");
274
+ }
275
+ }
276
+
197
277
  // src/generators/base/wasp-generator.base.ts
198
278
  import {
199
279
  GeneratorBase,
200
- getConfigManager,
201
- TemplateResolver
280
+ TemplateResolver,
281
+ getConfigManager
202
282
  } from "@ingenyus/swarm";
203
283
 
204
284
  // src/generators/config/wasp-config-generator.ts
@@ -207,14 +287,14 @@ import {
207
287
  handleFatalError,
208
288
  parseHelperMethodDefinition
209
289
  } from "@ingenyus/swarm";
210
- import path4 from "path";
290
+ import path5 from "path";
211
291
  var WaspConfigGenerator = class {
212
292
  constructor(logger = getCLILogger(), fileSystem = realFileSystem) {
213
293
  this.logger = logger;
214
294
  this.fileSystem = fileSystem;
215
295
  this.templateUtility = new TemplateUtility(fileSystem);
216
296
  }
217
- path = path4;
297
+ path = path5;
218
298
  templateUtility;
219
299
  /**
220
300
  * Gets the template path for feature config templates.
@@ -243,7 +323,7 @@ var WaspConfigGenerator = class {
243
323
  this.logger.error(`Template not found: ${templatePath}`);
244
324
  return;
245
325
  }
246
- const configFilePath = path4.join(featureDir, `feature.wasp.ts`);
326
+ const configFilePath = path5.join(featureDir, `feature.wasp.ts`);
247
327
  if (this.fileSystem.existsSync(configFilePath)) {
248
328
  this.logger.warn(`Feature config already exists: ${configFilePath}`);
249
329
  return;
@@ -259,7 +339,7 @@ var WaspConfigGenerator = class {
259
339
  */
260
340
  update(featurePath, declaration) {
261
341
  const configDir = getFeatureDir(this.fileSystem, featurePath);
262
- const configFilePath = path4.join(configDir, `feature.wasp.ts`);
342
+ const configFilePath = path5.join(configDir, `feature.wasp.ts`);
263
343
  if (!this.fileSystem.existsSync(configFilePath)) {
264
344
  const templatePath = this.getTemplatePath("feature.wasp.eta");
265
345
  if (!this.fileSystem.existsSync(templatePath)) {
@@ -655,6 +735,13 @@ var WaspGeneratorBase = class extends GeneratorBase {
655
735
  this.templateUtility = new TemplateUtility(this.fileSystem);
656
736
  this.templateResolver = new TemplateResolver(this.fileSystem);
657
737
  }
738
+ /**
739
+ * Ensures that the installed Wasp version is compatible with this package.
740
+ * Should be called at the start of generator execution.
741
+ */
742
+ ensureWaspCompatible() {
743
+ assertWaspCompatible(this.logger);
744
+ }
658
745
  async loadConfig() {
659
746
  if (this.configLoaded) return;
660
747
  const configManager = getConfigManager();
@@ -771,21 +858,22 @@ var FeatureGenerator = class extends WaspGeneratorBase {
771
858
  * @param target - The target path of the generated directory
772
859
  */
773
860
  async generate(args) {
861
+ this.ensureWaspCompatible();
774
862
  const { target } = args;
775
863
  const segments = validateFeaturePath(target);
776
864
  const normalisedPath = normaliseFeaturePath(target);
777
- const sourceRoot = path5.join(findWaspRoot(this.fileSystem), "src");
865
+ const sourceRoot = path6.join(findWaspRoot(this.fileSystem), "src");
778
866
  if (segments.length > 1) {
779
867
  const parentPath = segments.slice(0, -1).join("/");
780
868
  const parentNormalisedPath = normaliseFeaturePath(parentPath);
781
- const parentFeatureDir = path5.join(sourceRoot, parentNormalisedPath);
869
+ const parentFeatureDir = path6.join(sourceRoot, parentNormalisedPath);
782
870
  if (!this.fileSystem.existsSync(parentFeatureDir)) {
783
871
  handleFatalError2(
784
872
  `Parent feature '${parentPath}' does not exist. Please create it first.`
785
873
  );
786
874
  }
787
875
  }
788
- const featureDir = path5.join(sourceRoot, normalisedPath);
876
+ const featureDir = path6.join(sourceRoot, normalisedPath);
789
877
  this.fileSystem.mkdirSync(featureDir, { recursive: true });
790
878
  this.configGenerator.generate(normalisedPath);
791
879
  this.logger.success(`Generated feature: ${normalisedPath}`);
@@ -1,6 +1,6 @@
1
1
  // src/generators/feature/feature-generator.ts
2
2
  import { handleFatalError as handleFatalError2 } from "@ingenyus/swarm";
3
- import path5 from "path";
3
+ import path6 from "path";
4
4
 
5
5
  // src/common/constants.ts
6
6
  var PLUGIN_NAME = "wasp";
@@ -194,11 +194,91 @@ var TemplateUtility = class {
194
194
  }
195
195
  };
196
196
 
197
+ // src/common/wasp-compatibility.ts
198
+ import { findPackageJson, getVersion } from "@ingenyus/swarm";
199
+ import { execSync } from "child_process";
200
+ import path4 from "path";
201
+ import { fileURLToPath } from "url";
202
+ import * as semver from "semver";
203
+ var cachedSupportedRange = null;
204
+ function getWaspSupportedRange() {
205
+ if (cachedSupportedRange) {
206
+ return cachedSupportedRange;
207
+ }
208
+ const currentFile = fileURLToPath(import.meta.url);
209
+ const currentDir = path4.dirname(currentFile);
210
+ const result = findPackageJson(currentDir, {
211
+ packageName: "@ingenyus/swarm-wasp"
212
+ });
213
+ if (!result) {
214
+ throw new Error(
215
+ "Unable to find package.json for @ingenyus/swarm-wasp. Wasp supported version range must be specified in swarm.wasp field."
216
+ );
217
+ }
218
+ const swarm = result.packageJson.swarm;
219
+ const waspRange = swarm?.wasp;
220
+ if (!waspRange) {
221
+ throw new Error(
222
+ `Wasp supported version range not found in package.json. Please specify swarm.wasp field in @ingenyus/swarm-wasp package.json (e.g., "swarm": { "wasp": ">=0.18.0 <0.20.0" }).`
223
+ );
224
+ }
225
+ cachedSupportedRange = waspRange;
226
+ return cachedSupportedRange;
227
+ }
228
+ function getInstalledWaspVersion(logger) {
229
+ try {
230
+ const output = execSync("wasp version", {
231
+ encoding: "utf8",
232
+ stdio: "pipe"
233
+ });
234
+ const firstLine = output.split("\n")[0]?.trim();
235
+ if (!firstLine) {
236
+ logger.error(
237
+ "Unable to parse Wasp version from command output. Expected version number on first line."
238
+ );
239
+ throw new Error("Unable to parse Wasp version from command output");
240
+ }
241
+ if (!semver.valid(firstLine)) {
242
+ logger.error(
243
+ `Invalid Wasp version format: "${firstLine}". Expected a valid semver version (e.g., "0.18.2").`
244
+ );
245
+ throw new Error("Invalid Wasp version format");
246
+ }
247
+ return firstLine;
248
+ } catch (error) {
249
+ if (error.code === "ENOENT" || error.message?.includes("wasp")) {
250
+ logger.error(
251
+ "Wasp CLI not found. Install using: curl -sSL https://get.wasp.sh/installer.sh | sh -s"
252
+ );
253
+ throw new Error("Wasp CLI not found");
254
+ }
255
+ throw new Error("Unable to determine installed Wasp version");
256
+ }
257
+ }
258
+ function isTestEnvironment() {
259
+ return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || typeof process.env.VITEST !== "undefined";
260
+ }
261
+ function assertWaspCompatible(logger) {
262
+ if (isTestEnvironment()) {
263
+ return;
264
+ }
265
+ const version = getInstalledWaspVersion(logger);
266
+ const supportedRange = getWaspSupportedRange();
267
+ if (!semver.satisfies(version, supportedRange)) {
268
+ const startDir = path4.dirname(fileURLToPath(import.meta.url));
269
+ const packageVersion = getVersion("@ingenyus/swarm-wasp", startDir);
270
+ logger.error(
271
+ `Incompatible Wasp version detected: ${version}. @ingenyus/swarm-wasp@${packageVersion} supports Wasp ${supportedRange}.`
272
+ );
273
+ throw new Error("Incompatible Wasp version");
274
+ }
275
+ }
276
+
197
277
  // src/generators/base/wasp-generator.base.ts
198
278
  import {
199
279
  GeneratorBase,
200
- getConfigManager,
201
- TemplateResolver
280
+ TemplateResolver,
281
+ getConfigManager
202
282
  } from "@ingenyus/swarm";
203
283
 
204
284
  // src/generators/config/wasp-config-generator.ts
@@ -207,14 +287,14 @@ import {
207
287
  handleFatalError,
208
288
  parseHelperMethodDefinition
209
289
  } from "@ingenyus/swarm";
210
- import path4 from "path";
290
+ import path5 from "path";
211
291
  var WaspConfigGenerator = class {
212
292
  constructor(logger = getCLILogger(), fileSystem = realFileSystem) {
213
293
  this.logger = logger;
214
294
  this.fileSystem = fileSystem;
215
295
  this.templateUtility = new TemplateUtility(fileSystem);
216
296
  }
217
- path = path4;
297
+ path = path5;
218
298
  templateUtility;
219
299
  /**
220
300
  * Gets the template path for feature config templates.
@@ -243,7 +323,7 @@ var WaspConfigGenerator = class {
243
323
  this.logger.error(`Template not found: ${templatePath}`);
244
324
  return;
245
325
  }
246
- const configFilePath = path4.join(featureDir, `feature.wasp.ts`);
326
+ const configFilePath = path5.join(featureDir, `feature.wasp.ts`);
247
327
  if (this.fileSystem.existsSync(configFilePath)) {
248
328
  this.logger.warn(`Feature config already exists: ${configFilePath}`);
249
329
  return;
@@ -259,7 +339,7 @@ var WaspConfigGenerator = class {
259
339
  */
260
340
  update(featurePath, declaration) {
261
341
  const configDir = getFeatureDir(this.fileSystem, featurePath);
262
- const configFilePath = path4.join(configDir, `feature.wasp.ts`);
342
+ const configFilePath = path5.join(configDir, `feature.wasp.ts`);
263
343
  if (!this.fileSystem.existsSync(configFilePath)) {
264
344
  const templatePath = this.getTemplatePath("feature.wasp.eta");
265
345
  if (!this.fileSystem.existsSync(templatePath)) {
@@ -655,6 +735,13 @@ var WaspGeneratorBase = class extends GeneratorBase {
655
735
  this.templateUtility = new TemplateUtility(this.fileSystem);
656
736
  this.templateResolver = new TemplateResolver(this.fileSystem);
657
737
  }
738
+ /**
739
+ * Ensures that the installed Wasp version is compatible with this package.
740
+ * Should be called at the start of generator execution.
741
+ */
742
+ ensureWaspCompatible() {
743
+ assertWaspCompatible(this.logger);
744
+ }
658
745
  async loadConfig() {
659
746
  if (this.configLoaded) return;
660
747
  const configManager = getConfigManager();
@@ -771,21 +858,22 @@ var FeatureGenerator = class extends WaspGeneratorBase {
771
858
  * @param target - The target path of the generated directory
772
859
  */
773
860
  async generate(args) {
861
+ this.ensureWaspCompatible();
774
862
  const { target } = args;
775
863
  const segments = validateFeaturePath(target);
776
864
  const normalisedPath = normaliseFeaturePath(target);
777
- const sourceRoot = path5.join(findWaspRoot(this.fileSystem), "src");
865
+ const sourceRoot = path6.join(findWaspRoot(this.fileSystem), "src");
778
866
  if (segments.length > 1) {
779
867
  const parentPath = segments.slice(0, -1).join("/");
780
868
  const parentNormalisedPath = normaliseFeaturePath(parentPath);
781
- const parentFeatureDir = path5.join(sourceRoot, parentNormalisedPath);
869
+ const parentFeatureDir = path6.join(sourceRoot, parentNormalisedPath);
782
870
  if (!this.fileSystem.existsSync(parentFeatureDir)) {
783
871
  handleFatalError2(
784
872
  `Parent feature '${parentPath}' does not exist. Please create it first.`
785
873
  );
786
874
  }
787
875
  }
788
- const featureDir = path5.join(sourceRoot, normalisedPath);
876
+ const featureDir = path6.join(sourceRoot, normalisedPath);
789
877
  this.fileSystem.mkdirSync(featureDir, { recursive: true });
790
878
  this.configGenerator.generate(normalisedPath);
791
879
  this.logger.success(`Generated feature: ${normalisedPath}`);
@@ -104,6 +104,13 @@ var commonFieldMetadata = {
104
104
  import { toKebabCase } from "@ingenyus/swarm";
105
105
  import { Eta } from "eta";
106
106
 
107
+ // src/common/wasp-compatibility.ts
108
+ import { findPackageJson, getVersion } from "@ingenyus/swarm";
109
+ import { execSync } from "child_process";
110
+ import path2 from "path";
111
+ import { fileURLToPath } from "url";
112
+ import * as semver from "semver";
113
+
107
114
  // src/generators/feature/schema.ts
108
115
  var baseSchema = z2.object({
109
116
  target: commonSchemas.target