@comet/api-generator 8.0.0-beta.4 → 8.0.0-beta.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.
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  require("ts-node").register({
3
3
  require: ["tsconfig-paths/register"],
4
+ transpileOnly: true,
4
5
  });
5
6
  require("../lib/apiGenerator");
@@ -10,44 +10,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.generateCommand = void 0;
13
- const cli_1 = require("@mikro-orm/cli");
14
- const lazy_metadata_storage_1 = require("@nestjs/graphql/dist/schema-builder/storages/lazy-metadata.storage");
15
13
  const commander_1 = require("commander");
16
- const generate_crud_1 = require("./generateCrud/generate-crud");
17
- const generate_crud_single_1 = require("./generateCrudSingle/generate-crud-single");
18
- const write_generated_files_1 = require("./utils/write-generated-files");
19
- exports.generateCommand = new commander_1.Command("generate").action((options) => __awaiter(void 0, void 0, void 0, function* () {
20
- let orm = null;
21
- try {
22
- orm = yield cli_1.CLIHelper.getORM(undefined, undefined, { dbName: "generator" });
14
+ const generateFiles_1 = require("./generateFiles");
15
+ const watchMode_1 = require("./watchMode/watchMode");
16
+ exports.generateCommand = new commander_1.Command("generate")
17
+ .action((options) => __awaiter(void 0, void 0, void 0, function* () {
18
+ if (options.watch) {
19
+ yield (0, generateFiles_1.generateFiles)();
20
+ console.log("Watching for modified entities...");
21
+ yield (0, watchMode_1.watchMode)();
23
22
  }
24
- catch (e) {
25
- console.warn(e);
23
+ else {
24
+ (0, generateFiles_1.generateFiles)(options.file);
26
25
  }
27
- if (orm != null) {
28
- const entities = orm.em.getMetadata().getAll();
29
- lazy_metadata_storage_1.LazyMetadataStorage.load();
30
- for (const name in entities) {
31
- const entity = entities[name];
32
- if (!entity.class) {
33
- // Ignore e.g. relation entities that don't have a class
34
- continue;
35
- }
36
- {
37
- const generatorOptions = Reflect.getMetadata(`data:crudGeneratorOptions`, entity.class);
38
- if (generatorOptions) {
39
- const files = yield (0, generate_crud_1.generateCrud)(generatorOptions, entity);
40
- yield (0, write_generated_files_1.writeGeneratedFiles)(files, { targetDirectory: generatorOptions.targetDirectory });
41
- }
42
- }
43
- {
44
- const generatorOptions = Reflect.getMetadata(`data:crudSingleGeneratorOptions`, entity.class);
45
- if (generatorOptions) {
46
- const files = yield (0, generate_crud_single_1.generateCrudSingle)(generatorOptions, entity);
47
- yield (0, write_generated_files_1.writeGeneratedFiles)(files, { targetDirectory: generatorOptions.targetDirectory });
48
- }
49
- }
50
- }
51
- yield orm.close(true);
52
- }
53
- }));
26
+ }))
27
+ .option("-f, --file <file>", "path to entity file")
28
+ .option("-w, --watch", "Watch for changes");
@@ -106,7 +106,8 @@ function buildOptions(metadata, generatorOptions) {
106
106
  prop.type === "DateType" ||
107
107
  prop.type === "Date" ||
108
108
  prop.kind === "m:1" ||
109
- prop.type === "EnumArrayType"));
109
+ prop.type === "EnumArrayType" ||
110
+ prop.enum));
110
111
  const hasSortArg = crudSortProps.length > 0;
111
112
  const hasSlugProp = metadata.props.some((prop) => prop.name == "slug");
112
113
  const scopeProp = metadata.props.find((prop) => prop.name == "scope");
@@ -147,7 +148,7 @@ function buildOptions(metadata, generatorOptions) {
147
148
  function generateFilterDto({ generatorOptions, metadata }) {
148
149
  const { classNameSingular } = (0, build_name_variants_1.buildNameVariants)(metadata);
149
150
  const { crudFilterProps } = buildOptions(metadata, generatorOptions);
150
- let importsOut = "";
151
+ const imports = [];
151
152
  let enumFiltersOut = "";
152
153
  const generatedEnumNames = new Set();
153
154
  const generatedEnumsNames = new Set();
@@ -160,7 +161,7 @@ function generateFilterDto({ generatorOptions, metadata }) {
160
161
  enumFiltersOut += `@InputType()
161
162
  class ${enumName}EnumsFilter extends createEnumsFilter(${enumName}) {}
162
163
  `;
163
- importsOut += `import { ${enumName} } from "${importPath}";`;
164
+ imports.push({ name: enumName, importPath });
164
165
  }
165
166
  }
166
167
  else if (prop.enum) {
@@ -171,7 +172,7 @@ function generateFilterDto({ generatorOptions, metadata }) {
171
172
  enumFiltersOut += `@InputType()
172
173
  class ${enumName}EnumFilter extends createEnumFilter(${enumName}) {}
173
174
  `;
174
- importsOut += `import { ${enumName} } from "${importPath}";`;
175
+ imports.push({ name: enumName, importPath });
175
176
  }
176
177
  }
177
178
  });
@@ -179,7 +180,7 @@ function generateFilterDto({ generatorOptions, metadata }) {
179
180
  import { Field, InputType } from "@nestjs/graphql";
180
181
  import { Type } from "class-transformer";
181
182
  import { IsNumber, IsOptional, IsString, ValidateNested } from "class-validator";
182
- ${importsOut}
183
+ ${(0, generate_imports_code_1.generateImportsCode)(imports)}
183
184
 
184
185
  ${enumFiltersOut}
185
186
 
@@ -434,9 +435,7 @@ function generateService({ generatorOptions, metadata }) {
434
435
  })
435
436
  .join(",")} }`
436
437
  : false;
437
- const serviceOut = `import { FilterQuery } from "@mikro-orm/postgresql";
438
- import { InjectRepository } from "@mikro-orm/nestjs";
439
- import { EntityRepository, EntityManager, raw } from "@mikro-orm/postgresql";
438
+ const serviceOut = `import { EntityManager, FilterQuery, raw } from "@mikro-orm/postgresql";
440
439
  import { Injectable } from "@nestjs/common";
441
440
 
442
441
  ${(0, generate_imports_code_1.generateImportsCode)([generateEntityImport(metadata, generatorOptions.targetDirectory)])}
@@ -452,8 +451,7 @@ function generateService({ generatorOptions, metadata }) {
452
451
  export class ${classNamePlural}Service {
453
452
  ${hasPositionProp
454
453
  ? `constructor(
455
- private readonly entityManager: EntityManager,
456
- @InjectRepository(${metadata.className}) private readonly repository: EntityRepository<${metadata.className}>,
454
+ protected readonly entityManager: EntityManager,
457
455
  ) {}`
458
456
  : ""}
459
457
 
@@ -461,7 +459,7 @@ function generateService({ generatorOptions, metadata }) {
461
459
  ? `
462
460
  async incrementPositions(${positionGroupProps.length ? `group: ${positionGroupType},` : ``}lowestPosition: number, highestPosition?: number) {
463
461
  // Increment positions between newPosition (inclusive) and oldPosition (exclusive)
464
- await this.repository.nativeUpdate(
462
+ await this.entityManager.nativeUpdate(${metadata.className},
465
463
  ${positionGroupProps.length
466
464
  ? `{
467
465
  $and: [
@@ -476,7 +474,7 @@ function generateService({ generatorOptions, metadata }) {
476
474
 
477
475
  async decrementPositions(${positionGroupProps.length ? `group: ${positionGroupType},` : ``}lowestPosition: number, highestPosition?: number) {
478
476
  // Decrement positions between oldPosition (exclusive) and newPosition (inclusive)
479
- await this.repository.nativeUpdate(
477
+ await this.entityManager.nativeUpdate(${metadata.className},
480
478
  ${positionGroupProps.length
481
479
  ? `{
482
480
  $and: [
@@ -490,7 +488,7 @@ function generateService({ generatorOptions, metadata }) {
490
488
  }
491
489
 
492
490
  async getLastPosition(${positionGroupProps.length ? `group: ${positionGroupType}` : ``}) {
493
- return this.repository.count(${positionGroupProps.length ? `this.getPositionGroupCondition(group)` : `{}`});
491
+ return this.entityManager.count(${metadata.className}, ${positionGroupProps.length ? `this.getPositionGroupCondition(group)` : `{}`});
494
492
  }
495
493
 
496
494
  ${positionGroupProps.length
@@ -515,7 +513,6 @@ function generateEntityImport(targetMetadata, relativeTo) {
515
513
  function generateInputHandling(options, metadata, generatorOptions) {
516
514
  const { instanceNameSingular } = (0, build_name_variants_1.buildNameVariants)(metadata);
517
515
  const { blockProps, scopeProp, hasPositionProp, dedicatedResolverArgProps } = buildOptions(metadata, generatorOptions);
518
- const injectRepositories = [];
519
516
  const props = metadata.props.filter((prop) => !options.excludeFields || !options.excludeFields.includes(prop.name));
520
517
  const relationManyToOneProps = props.filter((prop) => prop.kind === "m:1");
521
518
  const relationOneToManyProps = props.filter((prop) => prop.kind === "1:m");
@@ -531,13 +528,11 @@ function generateInputHandling(options, metadata, generatorOptions) {
531
528
  const targetMeta = prop.targetMeta;
532
529
  if (!targetMeta)
533
530
  throw new Error("targetMeta is not set for relation");
534
- injectRepositories.push(targetMeta);
535
531
  return {
536
532
  name: prop.name,
537
533
  singularName: (0, pluralize_1.singular)(prop.name),
538
534
  nullable: prop.nullable,
539
535
  type: prop.type,
540
- repositoryName: `${(0, build_name_variants_1.classNameToInstanceName)(prop.type)}Repository`,
541
536
  };
542
537
  });
543
538
  const inputRelationOneToOneProps = relationOneToOneProps
@@ -546,13 +541,11 @@ function generateInputHandling(options, metadata, generatorOptions) {
546
541
  const targetMeta = prop.targetMeta;
547
542
  if (!targetMeta)
548
543
  throw new Error("targetMeta is not set for relation");
549
- injectRepositories.push(targetMeta);
550
544
  return {
551
545
  name: prop.name,
552
546
  singularName: (0, pluralize_1.singular)(prop.name),
553
547
  nullable: prop.nullable,
554
548
  type: prop.type,
555
- repositoryName: `${(0, build_name_variants_1.classNameToInstanceName)(prop.type)}Repository`,
556
549
  targetMeta,
557
550
  };
558
551
  });
@@ -562,20 +555,17 @@ function generateInputHandling(options, metadata, generatorOptions) {
562
555
  const targetMeta = prop.targetMeta;
563
556
  if (!targetMeta)
564
557
  throw new Error("targetMeta is not set for relation");
565
- injectRepositories.push(targetMeta);
566
558
  return {
567
559
  name: prop.name,
568
560
  singularName: (0, pluralize_1.singular)(prop.name),
569
561
  nullable: prop.nullable,
570
562
  type: prop.type,
571
- repositoryName: `${(0, build_name_variants_1.classNameToInstanceName)(prop.type)}Repository`,
572
563
  orphanRemoval: prop.orphanRemoval,
573
564
  targetMeta,
574
565
  };
575
566
  });
576
567
  function innerGenerateInputHandling(...args) {
577
568
  const ret = generateInputHandling(...args);
578
- injectRepositories.push(...ret.injectRepositories);
579
569
  return ret.code;
580
570
  }
581
571
  const noAssignProps = [...inputRelationToManyProps, ...inputRelationManyToOneProps, ...inputRelationOneToOneProps, ...blockProps];
@@ -589,13 +579,13 @@ function generateInputHandling(options, metadata, generatorOptions) {
589
579
  ${options.mode == "create"
590
580
  ? dedicatedResolverArgProps
591
581
  .map((dedicatedResolverArgProp) => {
592
- return `${dedicatedResolverArgProp.name}: Reference.create(await this.${(0, build_name_variants_1.classNameToInstanceName)(dedicatedResolverArgProp.type)}Repository.findOneOrFail(${dedicatedResolverArgProp.name})), `;
582
+ return `${dedicatedResolverArgProp.name}: Reference.create(await this.entityManager.findOneOrFail(${dedicatedResolverArgProp.type}, ${dedicatedResolverArgProp.name})), `;
593
583
  })
594
584
  .join("")
595
585
  : ""}
596
586
  ${options.mode == "create" || options.mode == "updateNested"
597
587
  ? inputRelationManyToOneProps
598
- .map((prop) => `${prop.name}: ${prop.nullable ? `${prop.name}Input ? ` : ""}Reference.create(await this.${prop.repositoryName}.findOneOrFail(${prop.name}Input))${prop.nullable ? ` : undefined` : ""}, `)
588
+ .map((prop) => `${prop.name}: ${prop.nullable ? `${prop.name}Input ? ` : ""}Reference.create(await this.entityManager.findOneOrFail(${prop.type}, ${prop.name}Input))${prop.nullable ? ` : undefined` : ""}, `)
599
589
  .join("")
600
590
  : ""}
601
591
  ${options.mode == "create" || options.mode == "updateNested"
@@ -608,8 +598,8 @@ ${inputRelationToManyProps
608
598
  const code = innerGenerateInputHandling({
609
599
  mode: "updateNested",
610
600
  inputName: `${prop.singularName}Input`,
611
- // alternative `return this.${prop.repositoryName}.create({` requires back relation to be set
612
- assignEntityCode: `return this.${prop.repositoryName}.assign(new ${prop.type}(), {`,
601
+ // alternative `return this.entityManager.create(${prop.type}, {` requires back relation to be set
602
+ assignEntityCode: `return this.entityManager.assign(new ${prop.type}(), {`,
613
603
  excludeFields: prop.targetMeta.props
614
604
  .filter((prop) => prop.kind == "m:1" && prop.targetMeta == metadata) //filter out referencing back to this entity
615
605
  .map((prop) => prop.name),
@@ -629,7 +619,7 @@ ${inputRelationToManyProps
629
619
  else {
630
620
  return `
631
621
  if (${prop.name}Input) {
632
- const ${prop.name} = await this.${prop.repositoryName}.find({ id: ${prop.name}Input });
622
+ const ${prop.name} = await this.entityManager.find(${prop.type}, { id: ${prop.name}Input });
633
623
  if (${prop.name}.length != ${prop.name}Input.length) throw new Error("Couldn't find all ${prop.name} that were passed as input");
634
624
  await ${instanceNameSingular}.${prop.name}.loadItems();
635
625
  ${instanceNameSingular}.${prop.name}.set(${prop.name}.map((${prop.singularName}) => Reference.create(${prop.singularName})));
@@ -647,7 +637,7 @@ ${inputRelationOneToOneProps
647
637
  ${innerGenerateInputHandling({
648
638
  mode: "updateNested",
649
639
  inputName: `${prop.name}Input`,
650
- assignEntityCode: `this.${prop.repositoryName}.assign(${prop.singularName}, {`,
640
+ assignEntityCode: `this.entityManager.assign(${prop.singularName}, {`,
651
641
  excludeFields: prop.targetMeta.props
652
642
  .filter((prop) => prop.kind == "1:1" && prop.targetMeta == metadata) //filter out referencing back to this entity
653
643
  .map((prop) => prop.name),
@@ -659,7 +649,7 @@ ${options.mode == "update"
659
649
  .map((prop) => `if (${prop.name}Input !== undefined) {
660
650
  ${instanceNameSingular}.${prop.name} =
661
651
  ${prop.nullable ? `${prop.name}Input ? ` : ""}
662
- Reference.create(await this.${prop.repositoryName}.findOneOrFail(${prop.name}Input))
652
+ Reference.create(await this.entityManager.findOneOrFail(${prop.type}, ${prop.name}Input))
663
653
  ${prop.nullable ? ` : undefined` : ""};
664
654
  }`)
665
655
  .join("")
@@ -673,7 +663,7 @@ ${options.mode == "update"
673
663
  .join("")
674
664
  : ""}
675
665
  `;
676
- return { code, injectRepositories };
666
+ return { code };
677
667
  }
678
668
  function generateNestedEntityResolver({ generatorOptions, metadata }) {
679
669
  const { classNameSingular } = (0, build_name_variants_1.buildNameVariants)(metadata);
@@ -692,7 +682,7 @@ function generateNestedEntityResolver({ generatorOptions, metadata }) {
692
682
  @Resolver(() => ${metadata.className})
693
683
  @RequiredPermission(${JSON.stringify(generatorOptions.requiredPermission)}${skipScopeCheck ? `, { skipScopeCheck: true }` : ""})
694
684
  export class ${classNameSingular}Resolver {
695
- ${needsBlocksTransformer ? `constructor(private readonly blocksTransformer: BlocksTransformerService) {}` : ""}
685
+ ${needsBlocksTransformer ? `constructor(protected readonly blocksTransformer: BlocksTransformerService) {}` : ""}
696
686
  ${code}
697
687
  }
698
688
  `;
@@ -800,16 +790,12 @@ function generateResolver({ generatorOptions, metadata }) {
800
790
  const outputRelationManyToManyProps = relationManyToManyProps.filter((prop) => (0, cms_api_1.hasCrudFieldFeature)(metadata.class, prop.name, "resolveField"));
801
791
  const outputRelationOneToOneProps = relationOneToOneProps.filter((prop) => (0, cms_api_1.hasCrudFieldFeature)(metadata.class, prop.name, "resolveField"));
802
792
  const imports = [];
803
- const injectRepositories = new Array();
804
- const { code: createInputHandlingCode, injectRepositories: createInputHandlingInjectRepositories } = generateInputHandling({ mode: "create", inputName: "input", assignEntityCode: `const ${instanceNameSingular} = this.repository.create({` }, metadata, generatorOptions);
805
- injectRepositories.push(...createInputHandlingInjectRepositories);
806
- const { code: updateInputHandlingCode, injectRepositories: updateInputHandlingInjectRepositories } = generateInputHandling({ mode: "update", inputName: "input", assignEntityCode: `${instanceNameSingular}.assign({` }, metadata, generatorOptions);
807
- injectRepositories.push(...updateInputHandlingInjectRepositories);
808
- injectRepositories.push(...dedicatedResolverArgProps.map((prop) => {
809
- if (!prop.targetMeta)
810
- throw new Error("targetMeta is not set for relation");
811
- return prop.targetMeta;
812
- }));
793
+ const { code: createInputHandlingCode } = generateInputHandling({
794
+ mode: "create",
795
+ inputName: "input",
796
+ assignEntityCode: `const ${instanceNameSingular} = this.entityManager.create(${metadata.className}, {`,
797
+ }, metadata, generatorOptions);
798
+ const { code: updateInputHandlingCode } = generateInputHandling({ mode: "update", inputName: "input", assignEntityCode: `${instanceNameSingular}.assign({` }, metadata, generatorOptions);
813
799
  const { imports: relationsFieldResolverImports, code: relationsFieldResolverCode, hasOutputRelations, needsBlocksTransformer, } = generateRelationsFieldResolver({
814
800
  generatorOptions,
815
801
  metadata,
@@ -819,7 +805,6 @@ function generateResolver({ generatorOptions, metadata }) {
819
805
  if (scopeProp && scopeProp.targetMeta) {
820
806
  imports.push(generateEntityImport(scopeProp.targetMeta, generatorOptions.targetDirectory));
821
807
  }
822
- imports.push(...injectRepositories.map((meta) => generateEntityImport(meta, generatorOptions.targetDirectory)));
823
808
  function generateIdArg(name, metadata) {
824
809
  if (constants_1.integerTypes.includes(metadata.properties[name].type)) {
825
810
  return `@Args("${name}", { type: () => ID }, { transform: (value) => parseInt(value) }) ${name}: number`;
@@ -836,9 +821,7 @@ function generateResolver({ generatorOptions, metadata }) {
836
821
  imports.push({ name: "RootBlockDataScalar", importPath: "@comet/cms-api" });
837
822
  imports.push({ name: "BlocksTransformerService", importPath: "@comet/cms-api" });
838
823
  imports.push({ name: "gqlArgsToMikroOrmQuery", importPath: "@comet/cms-api" });
839
- const resolverOut = `import { InjectRepository } from "@mikro-orm/nestjs";
840
- import { EntityRepository, EntityManager } from "@mikro-orm/postgresql";
841
- import { FindOptions, ObjectQuery, Reference } from "@mikro-orm/postgresql";
824
+ const resolverOut = `import { EntityManager, FindOptions, ObjectQuery, Reference } from "@mikro-orm/postgresql";
842
825
  import { Args, ID, Info, Mutation, Query, Resolver, ResolveField, Parent } from "@nestjs/graphql";
843
826
  import { GraphQLResolveInfo } from "graphql";
844
827
 
@@ -852,19 +835,20 @@ function generateResolver({ generatorOptions, metadata }) {
852
835
  @RequiredPermission(${JSON.stringify(generatorOptions.requiredPermission)}${skipScopeCheck ? `, { skipScopeCheck: true }` : ""})
853
836
  export class ${classNameSingular}Resolver {
854
837
  constructor(
855
- private readonly entityManager: EntityManager,${hasPositionProp ? `private readonly ${instanceNamePlural}Service: ${classNamePlural}Service,` : ``}
856
- @InjectRepository(${metadata.className}) private readonly repository: EntityRepository<${metadata.className}>,
857
- ${[...new Set(injectRepositories.map((meta) => meta.className))]
858
- .map((type) => `@InjectRepository(${type}) private readonly ${(0, build_name_variants_1.classNameToInstanceName)(type)}Repository: EntityRepository<${type}>,`)
859
- .join("")}${needsBlocksTransformer ? `private readonly blocksTransformer: BlocksTransformerService,` : ""}
838
+ protected readonly entityManager: EntityManager,${hasPositionProp ? `protected readonly ${instanceNamePlural}Service: ${classNamePlural}Service,` : ``}
839
+ ${needsBlocksTransformer ? `private readonly blocksTransformer: BlocksTransformerService,` : ""}
860
840
  ) {}
861
841
 
842
+ ${generatorOptions.single
843
+ ? `
862
844
  @Query(() => ${metadata.className})
863
845
  @AffectedEntity(${metadata.className})
864
846
  async ${instanceNameSingular}(${generateIdArg("id", metadata)}): Promise<${metadata.className}> {
865
- const ${instanceNameSingular} = await this.repository.findOneOrFail(id);
847
+ const ${instanceNameSingular} = await this.entityManager.findOneOrFail(${metadata.className}, id);
866
848
  return ${instanceNameSingular};
867
849
  }
850
+ `
851
+ : ""}
868
852
 
869
853
  ${hasSlugProp
870
854
  ? `
@@ -873,7 +857,7 @@ function generateResolver({ generatorOptions, metadata }) {
873
857
  @Args("slug") slug: string
874
858
  ${scopeProp ? `, @Args("scope", { type: () => ${scopeProp.type} }) scope: ${scopeProp.type}` : ""}
875
859
  ): Promise<${metadata.className} | null> {
876
- const ${instanceNameSingular} = await this.repository.findOne({ slug${scopeProp ? `, scope` : ""}});
860
+ const ${instanceNameSingular} = await this.entityManager.findOne(${metadata.className}, { slug${scopeProp ? `, scope` : ""}});
877
861
 
878
862
  return ${instanceNameSingular} ?? null;
879
863
  }
@@ -900,7 +884,7 @@ function generateResolver({ generatorOptions, metadata }) {
900
884
  ${hasOutputRelations ? `, @Info() info: GraphQLResolveInfo` : ""}
901
885
  ): Promise<Paginated${classNamePlural}> {
902
886
  const where${hasSearchArg || hasFilterArg
903
- ? ` = gqlArgsToMikroOrmQuery({ ${hasSearchArg ? `search, ` : ""}${hasFilterArg ? `filter, ` : ""} }, this.repository);`
887
+ ? ` = gqlArgsToMikroOrmQuery({ ${hasSearchArg ? `search, ` : ""}${hasFilterArg ? `filter, ` : ""} }, this.entityManager.getMetadata(${metadata.className}));`
904
888
  : `: ObjectQuery<${metadata.className}> = {}`}
905
889
  ${scopeProp ? `where.scope = scope;` : ""}
906
890
  ${dedicatedResolverArgProps
@@ -932,7 +916,7 @@ function generateResolver({ generatorOptions, metadata }) {
932
916
  }`
933
917
  : ""}
934
918
 
935
- const [entities, totalCount] = await this.repository.findAndCount(where, options);
919
+ const [entities, totalCount] = await this.entityManager.findAndCount(${metadata.className}, where, options);
936
920
  return new Paginated${classNamePlural}(entities, totalCount);
937
921
  }
938
922
  `
@@ -1003,7 +987,7 @@ function generateResolver({ generatorOptions, metadata }) {
1003
987
  ${generateIdArg("id", metadata)},
1004
988
  @Args("input", { type: () => ${classNameSingular}UpdateInput }) input: ${classNameSingular}UpdateInput
1005
989
  ): Promise<${metadata.className}> {
1006
- const ${instanceNameSingular} = await this.repository.findOneOrFail(id);
990
+ const ${instanceNameSingular} = await this.entityManager.findOneOrFail(${metadata.className}, id);
1007
991
 
1008
992
  ${hasPositionProp
1009
993
  ? `
@@ -1052,7 +1036,7 @@ function generateResolver({ generatorOptions, metadata }) {
1052
1036
  @Mutation(() => Boolean)
1053
1037
  @AffectedEntity(${metadata.className})
1054
1038
  async delete${metadata.className}(${generateIdArg("id", metadata)}): Promise<boolean> {
1055
- const ${instanceNameSingular} = await this.repository.findOneOrFail(id);
1039
+ const ${instanceNameSingular} = await this.entityManager.findOneOrFail(${metadata.className}, id);
1056
1040
  this.entityManager.remove(${instanceNameSingular});${hasPositionProp
1057
1041
  ? `await this.${instanceNamePlural}Service.decrementPositions(${positionGroupProps.length
1058
1042
  ? `{ ${positionGroupProps
@@ -1075,8 +1059,11 @@ function generateResolver({ generatorOptions, metadata }) {
1075
1059
  }
1076
1060
  function generateCrud(generatorOptionsParam, metadata) {
1077
1061
  return __awaiter(this, void 0, void 0, function* () {
1078
- var _a, _b, _c, _d;
1079
- const generatorOptions = Object.assign(Object.assign({}, generatorOptionsParam), { create: (_a = generatorOptionsParam.create) !== null && _a !== void 0 ? _a : true, update: (_b = generatorOptionsParam.update) !== null && _b !== void 0 ? _b : true, delete: (_c = generatorOptionsParam.delete) !== null && _c !== void 0 ? _c : true, list: (_d = generatorOptionsParam.list) !== null && _d !== void 0 ? _d : true });
1062
+ var _a, _b, _c, _d, _e;
1063
+ const generatorOptions = Object.assign(Object.assign({}, generatorOptionsParam), { create: (_a = generatorOptionsParam.create) !== null && _a !== void 0 ? _a : true, update: (_b = generatorOptionsParam.update) !== null && _b !== void 0 ? _b : true, delete: (_c = generatorOptionsParam.delete) !== null && _c !== void 0 ? _c : true, list: (_d = generatorOptionsParam.list) !== null && _d !== void 0 ? _d : true, single: (_e = generatorOptionsParam.single) !== null && _e !== void 0 ? _e : true });
1064
+ if (!generatorOptions.create && !generatorOptions.update && !generatorOptions.delete && !generatorOptions.list && !generatorOptions.single) {
1065
+ throw new Error("At least one of create, update, delete, list or single must be true");
1066
+ }
1080
1067
  const generatedFiles = [];
1081
1068
  const { fileNameSingular, fileNamePlural, instanceNamePlural } = (0, build_name_variants_1.buildNameVariants)(metadata);
1082
1069
  const { hasFilterArg, hasSortArg, argsFileName, hasPositionProp } = buildOptions(metadata, generatorOptions);
@@ -96,6 +96,7 @@ function generateCrudInput(generatorOptions_1, metadata_1) {
96
96
  const fieldOptions = tsCodeRecordToString({ nullable: "true", defaultValue });
97
97
  isOptional = true;
98
98
  decorators.push(`@IsOptional()`);
99
+ imports.push({ name: "Min", importPath: "class-validator" });
99
100
  decorators.push(`@Min(1)`);
100
101
  decorators.push("@IsInt()");
101
102
  decorators.push(`@Field(() => Int, ${fieldOptions})`);
@@ -395,6 +396,16 @@ function generateCrudInput(generatorOptions_1, metadata_1) {
395
396
  decorators.push("@IsUUID()");
396
397
  type = "string";
397
398
  }
399
+ else if ((0, ts_morph_helper_1.getFieldDecoratorClassName)(prop.name, metadata)) {
400
+ //for custom mikro-orm type
401
+ const className = (0, ts_morph_helper_1.getFieldDecoratorClassName)(prop.name, metadata);
402
+ const importPath = (0, ts_morph_helper_1.findInputClassImportPath)(className, `${generatorOptions.targetDirectory}/dto`, metadata);
403
+ imports.push({ name: className, importPath });
404
+ decorators.push(`@ValidateNested()`);
405
+ decorators.push(`@Type(() => ${className})`);
406
+ decorators.push(`@Field(() => ${className}${prop.nullable ? ", { nullable: true }" : ""})`);
407
+ type = className;
408
+ }
398
409
  else {
399
410
  console.warn(`${prop.name}: unsupported type ${type}`);
400
411
  continue;
@@ -434,7 +445,7 @@ function generateCrudInput(generatorOptions_1, metadata_1) {
434
445
  const className = (_m = options.className) !== null && _m !== void 0 ? _m : `${metadata.className}Input`;
435
446
  const inputOut = `import { Field, InputType, ID, Int } from "@nestjs/graphql";
436
447
  import { Transform, Type } from "class-transformer";
437
- import { IsString, IsNotEmpty, ValidateNested, IsNumber, IsBoolean, IsDate, IsOptional, IsEnum, IsUUID, IsArray, IsInt, Min } from "class-validator";
448
+ import { IsString, IsNotEmpty, ValidateNested, IsNumber, IsBoolean, IsDate, IsOptional, IsEnum, IsUUID, IsArray, IsInt } from "class-validator";
438
449
  import { GraphQLJSONObject } from "graphql-scalars";
439
450
  import { GraphQLDate } from "graphql-scalars";
440
451
  ${(0, generate_imports_code_1.generateImportsCode)(imports)}
@@ -67,8 +67,6 @@ function generateCrudSingle(generatorOptions, metadata) {
67
67
  return (0, cms_api_1.hasCrudFieldFeature)(metadata.class, prop.name, "input") && prop.type === "RootBlockType";
68
68
  });
69
69
  const serviceOut = `import { ObjectQuery } from "@mikro-orm/postgresql";
70
- import { InjectRepository } from "@mikro-orm/nestjs";
71
- import { EntityRepository } from "@mikro-orm/postgresql";
72
70
  import { Injectable } from "@nestjs/common";
73
71
  import { ${metadata.className} } from "${path.relative(generatorOptions.targetDirectory, metadata.path).replace(/\.ts$/, "")}";
74
72
 
@@ -78,9 +76,7 @@ function generateCrudSingle(generatorOptions, metadata) {
78
76
  }
79
77
  `;
80
78
  generatedFiles.push({ name: `${fileNamePlural}.service.ts`, content: serviceOut, type: "service" });
81
- const resolverOut = `import { InjectRepository } from "@mikro-orm/nestjs";
82
- import { EntityRepository, EntityManager } from "@mikro-orm/postgresql";
83
- import { FindOptions } from "@mikro-orm/postgresql";
79
+ const resolverOut = `import { FindOptions, EntityManager } from "@mikro-orm/postgresql";
84
80
  import { Args, ID, Mutation, Query, Resolver } from "@nestjs/graphql";
85
81
  import { RequiredPermission, SortDirection, validateNotModified } from "@comet/cms-api";
86
82
 
@@ -92,22 +88,20 @@ function generateCrudSingle(generatorOptions, metadata) {
92
88
  : ""}
93
89
  import { ${classNamePlural}Service } from "./${fileNamePlural}.service";
94
90
  import { ${classNameSingular}Input } from "./dto/${fileNameSingular}.input";
95
- import { Paginated${classNamePlural} } from "./dto/paginated-${fileNamePlural}";
96
91
 
97
92
  @Resolver(() => ${metadata.className})
98
93
  @RequiredPermission(${JSON.stringify(generatorOptions.requiredPermission)}${!scopeProp ? `, { skipScopeCheck: true }` : ""})
99
94
  export class ${classNameSingular}Resolver {
100
95
  constructor(
101
- private readonly entityManager: EntityManager,
102
- private readonly ${instanceNamePlural}Service: ${classNamePlural}Service,
103
- @InjectRepository(${metadata.className}) private readonly repository: EntityRepository<${metadata.className}>
96
+ protected readonly entityManager: EntityManager,
97
+ protected readonly ${instanceNamePlural}Service: ${classNamePlural}Service,
104
98
  ) {}
105
99
 
106
100
  @Query(() => ${metadata.className}, { nullable: true })
107
101
  async ${instanceNameSingular}(
108
102
  ${scopeProp ? `@Args("scope", { type: () => ${scopeProp.type} }) scope: ${scopeProp.type},` : ""}
109
103
  ): Promise<${metadata.className} | null> {
110
- const ${instanceNamePlural} = await this.repository.find({${scopeProp ? `scope` : ""}});
104
+ const ${instanceNamePlural} = await this.entityManager.find(${metadata.className}, {${scopeProp ? `scope` : ""}});
111
105
  if (${instanceNamePlural}.length > 1) {
112
106
  throw new Error("There must be only one ${instanceNameSingular}");
113
107
  }
@@ -120,10 +114,10 @@ function generateCrudSingle(generatorOptions, metadata) {
120
114
  ${scopeProp ? `@Args("scope", { type: () => ${scopeProp.type} }) scope: ${scopeProp.type},` : ""}
121
115
  @Args("input", { type: () => ${classNameSingular}Input }) input: ${classNameSingular}Input
122
116
  ): Promise<${metadata.className}> {
123
- let ${instanceNameSingular} = await this.repository.findOne({${scopeProp ? `scope` : ""}});
117
+ let ${instanceNameSingular} = await this.entityManager.findOne(${metadata.className}, {${scopeProp ? `scope` : ""}});
124
118
 
125
119
  if (!${instanceNameSingular}) {
126
- ${instanceNameSingular} = this.repository.create({
120
+ ${instanceNameSingular} = this.entityManager.create(${metadata.className}, {
127
121
  ...input,
128
122
  ${blockProps.length ? `${blockProps.map((prop) => `${prop.name}: input.${prop.name}.transformToBlockData()`).join(", ")}, ` : ""}
129
123
  ${scopeProp ? `scope,` : ""}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Generate mode for the generator.
3
+ *
4
+ * Generates CRUD files for all entities or a specific entity available at file path.
5
+ * @param file
6
+ */
7
+ export declare const generateFiles: (file?: string) => Promise<void>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.generateFiles = void 0;
16
+ const node_console_1 = __importDefault(require("node:console"));
17
+ const cli_1 = require("@mikro-orm/cli");
18
+ const lazy_metadata_storage_1 = require("@nestjs/graphql/dist/schema-builder/storages/lazy-metadata.storage");
19
+ const generate_crud_1 = require("./generateCrud/generate-crud");
20
+ const generate_crud_single_1 = require("./generateCrudSingle/generate-crud-single");
21
+ const write_generated_files_1 = require("./utils/write-generated-files");
22
+ /**
23
+ * Generate mode for the generator.
24
+ *
25
+ * Generates CRUD files for all entities or a specific entity available at file path.
26
+ * @param file
27
+ */
28
+ const generateFiles = (
29
+ /**
30
+ * File path to the entity for which to generate CRUD files.
31
+ *
32
+ * @default undefined -> generate all entities
33
+ */
34
+ file) => __awaiter(void 0, void 0, void 0, function* () {
35
+ let orm = null;
36
+ try {
37
+ orm = yield cli_1.CLIHelper.getORM(undefined, undefined, { dbName: "generator" });
38
+ }
39
+ catch (e) {
40
+ node_console_1.default.warn(e);
41
+ }
42
+ if (orm != null) {
43
+ const entities = orm.em.getMetadata().getAll();
44
+ lazy_metadata_storage_1.LazyMetadataStorage.load();
45
+ for (const name in entities) {
46
+ const entity = entities[name];
47
+ if (!entity.class) {
48
+ // Ignore e.g. relation entities that don't have a class
49
+ continue;
50
+ }
51
+ if (file == null || entity.path === `./${file}`) {
52
+ {
53
+ const generatorOptions = Reflect.getMetadata(`data:crudGeneratorOptions`, entity.class);
54
+ if (generatorOptions) {
55
+ node_console_1.default.log(`🚀 start generateCrud for Entity ${entity.path}`);
56
+ const files = yield (0, generate_crud_1.generateCrud)(generatorOptions, entity);
57
+ yield (0, write_generated_files_1.writeGeneratedFiles)(files, { targetDirectory: generatorOptions.targetDirectory });
58
+ }
59
+ }
60
+ {
61
+ const generatorOptions = Reflect.getMetadata(`data:crudSingleGeneratorOptions`, entity.class);
62
+ if (generatorOptions) {
63
+ node_console_1.default.log(`🚀 start generateCrudSingle for Entity ${entity.path}`);
64
+ const files = yield (0, generate_crud_single_1.generateCrudSingle)(generatorOptions, entity);
65
+ yield (0, write_generated_files_1.writeGeneratedFiles)(files, { targetDirectory: generatorOptions.targetDirectory });
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ });
72
+ exports.generateFiles = generateFiles;
@@ -1,5 +1,4 @@
1
1
  import { type EntityMetadata } from "@mikro-orm/postgresql";
2
- export declare function classNameToInstanceName(className: string): string;
3
2
  export declare function buildNameVariants(metadata: EntityMetadata<any>): {
4
3
  classNameSingular: string;
5
4
  classNamePlural: string;
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.classNameToInstanceName = classNameToInstanceName;
4
3
  exports.buildNameVariants = buildNameVariants;
5
4
  const pluralize_1 = require("pluralize");
6
5
  function classNameToInstanceName(className) {
@@ -8,3 +8,4 @@ export declare function findValidatorImportPath(validatorName: string, generator
8
8
  export declare function findBlockName(propertyName: string, metadata: EntityMetadata<any>): string;
9
9
  export declare function findBlockImportPath(blockName: string, targetDirectory: string, metadata: EntityMetadata<any>): string;
10
10
  export declare function findInputClassImportPath(className: string, targetDirectory: string, metadata: EntityMetadata<any>): string;
11
+ export declare function getFieldDecoratorClassName(propertyName: string, metadata: EntityMetadata<any>): string | false;
@@ -40,6 +40,7 @@ exports.findValidatorImportPath = findValidatorImportPath;
40
40
  exports.findBlockName = findBlockName;
41
41
  exports.findBlockImportPath = findBlockImportPath;
42
42
  exports.findInputClassImportPath = findInputClassImportPath;
43
+ exports.getFieldDecoratorClassName = getFieldDecoratorClassName;
43
44
  const path = __importStar(require("path"));
44
45
  const ts_morph_1 = require("ts-morph");
45
46
  /* eslint-disable @typescript-eslint/no-explicit-any */
@@ -61,8 +62,14 @@ function morphTsClass(metadata) {
61
62
  return tsClass;
62
63
  }
63
64
  function morphTsProperty(name, metadata) {
64
- const tsClass = morphTsClass(metadata);
65
- return tsClass.getPropertyOrThrow(name);
65
+ let currentClass = morphTsClass(metadata);
66
+ while (currentClass) {
67
+ const prop = currentClass.getProperty(name);
68
+ if (prop)
69
+ return prop;
70
+ currentClass = currentClass.getBaseClass();
71
+ }
72
+ throw new Error(`Property ${name} not found in ${metadata.className}`);
66
73
  }
67
74
  function findImportPath(importName, targetDirectory, metadata) {
68
75
  var _a;
@@ -189,3 +196,22 @@ function findInputClassImportPath(className, targetDirectory, metadata) {
189
196
  }
190
197
  return returnImportPath;
191
198
  }
199
+ function getFieldDecoratorClassName(propertyName, metadata) {
200
+ const definedDecorators = morphTsProperty(propertyName, metadata).getDecorators();
201
+ const fieldDecorator = definedDecorators.find((decorator) => decorator.getName() == "Field");
202
+ if (!fieldDecorator) {
203
+ return false;
204
+ }
205
+ const typeFnArg = fieldDecorator.getArguments()[0];
206
+ if (!typeFnArg)
207
+ throw new Error(`${propertyName}: @Field is missing argument`);
208
+ if (typeFnArg.getKind() != ts_morph_1.SyntaxKind.ArrowFunction)
209
+ throw new Error(`${propertyName}: @Field first argument must be an ArrowFunction`);
210
+ const typeReturningArrowFunction = typeFnArg;
211
+ const body = typeReturningArrowFunction.getBody();
212
+ if (body.getKind() != ts_morph_1.SyntaxKind.Identifier)
213
+ throw new Error(`${propertyName}: @Field first argument must be an ArrowFunction returning an Identifier`);
214
+ const identifier = body;
215
+ const className = identifier.getText();
216
+ return className;
217
+ }
@@ -1 +1,3 @@
1
+ import ts from "typescript";
1
2
  export declare function writeGeneratedFile(filePath: string, contents: string): Promise<void>;
3
+ export declare function removeUnusedImports(): ts.TransformerFactory<ts.SourceFile>;
@@ -41,29 +41,84 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
41
41
  step((generator = generator.apply(thisArg, _arguments || [])).next());
42
42
  });
43
43
  };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
44
47
  Object.defineProperty(exports, "__esModule", { value: true });
45
48
  exports.writeGeneratedFile = writeGeneratedFile;
46
- const eslint_1 = require("eslint");
49
+ exports.removeUnusedImports = removeUnusedImports;
47
50
  const fs_1 = require("fs");
48
51
  const path = __importStar(require("path"));
52
+ const typescript_1 = __importDefault(require("typescript"));
49
53
  function writeGeneratedFile(filePath, contents) {
50
54
  return __awaiter(this, void 0, void 0, function* () {
51
55
  const header = `// This file has been generated by comet api-generator.
52
56
  // You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment.
53
57
  `;
54
58
  yield fs_1.promises.mkdir(path.dirname(filePath), { recursive: true });
55
- yield fs_1.promises.writeFile(filePath, contents);
56
- const eslint = new eslint_1.ESLint({
57
- cwd: process.cwd(),
58
- fix: true,
59
- });
60
- const lintResult = yield eslint.lintText(header + contents, {
61
- filePath,
59
+ const sourceFile = typescript_1.default.createSourceFile(filePath, header + contents, typescript_1.default.ScriptTarget.ES2024, true, typescript_1.default.ScriptKind.TS);
60
+ const result = typescript_1.default.transform(sourceFile, [removeUnusedImports()]);
61
+ const printer = typescript_1.default.createPrinter({
62
+ newLine: typescript_1.default.NewLineKind.LineFeed,
63
+ removeComments: false,
62
64
  });
63
- const output = lintResult[0] && lintResult[0].output ? lintResult[0].output : lintResult[0].source;
64
- if (output) {
65
- yield fs_1.promises.writeFile(filePath, output);
66
- }
65
+ const prettyCode = printer.printFile(result.transformed[0]);
66
+ result.dispose();
67
+ yield fs_1.promises.writeFile(filePath, prettyCode);
67
68
  console.log(`generated ${filePath}`);
68
69
  });
69
70
  }
71
+ function removeUnusedImports() {
72
+ return (context) => {
73
+ function visit(sourceFile) {
74
+ const usedIdentifiers = new Set();
75
+ // Step 1: Collect all used identifiers
76
+ function collectUsage(node) {
77
+ if (typescript_1.default.isIdentifier(node)) {
78
+ usedIdentifiers.add(node.text);
79
+ }
80
+ else if (typescript_1.default.isImportDeclaration(node)) {
81
+ // Skip import declarations (those identifiers are not usages)
82
+ return;
83
+ }
84
+ typescript_1.default.forEachChild(node, collectUsage);
85
+ }
86
+ typescript_1.default.forEachChild(sourceFile, collectUsage);
87
+ // Step 2: Visit and update the import declarations
88
+ function visitor(node) {
89
+ if (typescript_1.default.isImportDeclaration(node) && node.importClause) {
90
+ const { name, namedBindings } = node.importClause;
91
+ const updatedBindings = [];
92
+ if (name && usedIdentifiers.has(name.text)) {
93
+ // default import is used
94
+ }
95
+ else if (name) {
96
+ // default import is unused
97
+ return undefined;
98
+ }
99
+ if (namedBindings && typescript_1.default.isNamedImports(namedBindings)) {
100
+ for (const specifier of namedBindings.elements) {
101
+ const importName = specifier.name.text;
102
+ if (usedIdentifiers.has(importName)) {
103
+ updatedBindings.push(specifier);
104
+ }
105
+ }
106
+ if (updatedBindings.length === 0 && !name) {
107
+ // nothing left in this import
108
+ return undefined;
109
+ }
110
+ return typescript_1.default.factory.updateImportDeclaration(node, node.modifiers, typescript_1.default.factory.updateImportClause(node.importClause, node.importClause.isTypeOnly, name, updatedBindings.length > 0 ? typescript_1.default.factory.updateNamedImports(namedBindings, updatedBindings) : undefined), node.moduleSpecifier, node.assertClause);
111
+ }
112
+ if (!namedBindings && !name) {
113
+ // empty import, probably a side-effect import
114
+ return node;
115
+ }
116
+ return node;
117
+ }
118
+ return typescript_1.default.visitEachChild(node, visitor, context);
119
+ }
120
+ return typescript_1.default.visitNode(sourceFile, visitor);
121
+ }
122
+ return (sourceFile) => visit(sourceFile);
123
+ };
124
+ }
@@ -0,0 +1,2 @@
1
+ import { type ChildProcessWithoutNullStreams } from "child_process";
2
+ export declare function handleChildProcess(child: ChildProcessWithoutNullStreams): Promise<string>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleChildProcess = handleChildProcess;
4
+ function handleChildProcess(child) {
5
+ return new Promise((resolve, reject) => {
6
+ let scriptOutput = "";
7
+ let errorOutput = "";
8
+ child.stdout.setEncoding("utf8");
9
+ child.stdout.on("data", (data) => {
10
+ scriptOutput += data;
11
+ console.log(data);
12
+ });
13
+ child.stderr.on("data", (data) => {
14
+ errorOutput += data;
15
+ console.error(data);
16
+ });
17
+ child.on("close", (code) => {
18
+ if (code !== 0) {
19
+ reject(errorOutput);
20
+ }
21
+ else {
22
+ resolve(scriptOutput);
23
+ }
24
+ });
25
+ });
26
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Watch mode for the generator.
3
+ *
4
+ * Watches the `src` directory for changes and triggers child processes of generator for the changed file, to process it.
5
+ */
6
+ export declare const watchMode: () => Promise<void>;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.watchMode = void 0;
16
+ const node_console_1 = __importDefault(require("node:console"));
17
+ const child_process_1 = require("child_process");
18
+ const chokidar_1 = require("chokidar");
19
+ const handleChildProcess_1 = require("./handleChildProcess");
20
+ const waitForExit = (proc) => {
21
+ return new Promise((resolve) => {
22
+ proc.on("exit", (code, signal) => {
23
+ resolve({ code, signal });
24
+ });
25
+ });
26
+ };
27
+ /**
28
+ * Watch mode for the generator.
29
+ *
30
+ * Watches the `src` directory for changes and triggers child processes of generator for the changed file, to process it.
31
+ */
32
+ const watchMode = () => __awaiter(void 0, void 0, void 0, function* () {
33
+ /**
34
+ * Collection of last ChildProcesses for each file
35
+ */
36
+ const childProcesses = {};
37
+ (0, chokidar_1.watch)("./src", {
38
+ awaitWriteFinish: {
39
+ stabilityThreshold: 300,
40
+ pollInterval: 200,
41
+ },
42
+ ignored: (path, stats) => {
43
+ if (stats === null || stats === void 0 ? void 0 : stats.isFile()) {
44
+ return !path.endsWith(".entity.ts");
45
+ }
46
+ return false;
47
+ },
48
+ })
49
+ .on("change", (path) => __awaiter(void 0, void 0, void 0, function* () {
50
+ node_console_1.default.log(`🚀 File changed: ${path}`);
51
+ // Kill running processes for the changed file
52
+ if (childProcesses[path]) {
53
+ if (childProcesses[path].exitCode == null) {
54
+ childProcesses[path].kill();
55
+ yield waitForExit(childProcesses[path]);
56
+ node_console_1.default.log("💀 Killed running process for file: ", path);
57
+ }
58
+ delete childProcesses[path];
59
+ }
60
+ // Generate changed file with child process.
61
+ //
62
+ // Triggering the generator with changed files in the same process has problems with
63
+ // reflection and new Classes / Decorator are not recognised correctly.
64
+ const childProcess = (0, child_process_1.spawn)(`node ${__dirname}/../../../../bin/api-generator.js generate -f ${path}`, {
65
+ shell: true,
66
+ });
67
+ childProcesses[path] = childProcess;
68
+ try {
69
+ (0, handleChildProcess_1.handleChildProcess)(childProcess);
70
+ }
71
+ catch (e) {
72
+ node_console_1.default.error(`❌ Error processing ${path} with error: ${e}`);
73
+ }
74
+ }))
75
+ .on("error", (error) => {
76
+ node_console_1.default.error(`Watcher error: ${error}`);
77
+ });
78
+ });
79
+ exports.watchMode = watchMode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comet/api-generator",
3
- "version": "8.0.0-beta.4",
3
+ "version": "8.0.0-beta.6",
4
4
  "repository": {
5
5
  "directory": "packages/api/api-generator",
6
6
  "type": "git",
@@ -17,11 +17,12 @@
17
17
  "lib/*"
18
18
  ],
19
19
  "dependencies": {
20
+ "chokidar": "^4.0.3",
20
21
  "commander": "^9.5.0",
21
22
  "pluralize": "^8.0.0",
22
23
  "ts-morph": "^25.0.1",
23
24
  "ts-node": "^10.9.2",
24
- "@comet/cms-api": "8.0.0-beta.4"
25
+ "@comet/cms-api": "8.0.0-beta.6"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@mikro-orm/cli": "^6.4.10",
@@ -29,9 +30,9 @@
29
30
  "@mikro-orm/postgresql": "^6.4.10",
30
31
  "@nestjs/graphql": "^13.1.0",
31
32
  "@types/jest": "^29.5.14",
32
- "@types/node": "^22.14.0",
33
+ "@types/node": "^22.15.34",
33
34
  "@types/pluralize": "^0.0.33",
34
- "class-validator": "^0.14.1",
35
+ "class-validator": "^0.14.2",
35
36
  "eslint": "^9.22.0",
36
37
  "jest": "^29.7.0",
37
38
  "npm-run-all2": "^5.0.2",
@@ -41,7 +42,7 @@
41
42
  "ts-jest": "^29.2.6",
42
43
  "typescript": "^5.7.3",
43
44
  "uuid": "^11.1.0",
44
- "@comet/eslint-config": "8.0.0-beta.4"
45
+ "@comet/eslint-config": "8.0.0-beta.6"
45
46
  },
46
47
  "peerDependencies": {
47
48
  "@mikro-orm/cli": "^6.0.0",