@comet/api-generator 8.0.0-beta.3 → 8.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/api-generator.js +1 -0
- package/lib/commands/generate/generate-command.js +13 -38
- package/lib/commands/generate/generateCrud/generate-crud.d.ts +0 -3
- package/lib/commands/generate/generateCrud/generate-crud.js +57 -126
- package/lib/commands/generate/generateCrudInput/generate-crud-input.js +12 -1
- package/lib/commands/generate/generateCrudSingle/generate-crud-single.js +5 -11
- package/lib/commands/generate/generateFiles.d.ts +7 -0
- package/lib/commands/generate/generateFiles.js +72 -0
- package/lib/commands/generate/utils/build-name-variants.d.ts +0 -1
- package/lib/commands/generate/utils/build-name-variants.js +0 -1
- package/lib/commands/generate/utils/ts-morph-helper.d.ts +1 -0
- package/lib/commands/generate/utils/ts-morph-helper.js +28 -2
- package/lib/commands/generate/utils/write-generated-file.d.ts +2 -0
- package/lib/commands/generate/utils/write-generated-file.js +67 -12
- package/lib/commands/generate/watchMode/handleChildProcess.d.ts +2 -0
- package/lib/commands/generate/watchMode/handleChildProcess.js +26 -0
- package/lib/commands/generate/watchMode/watchMode.d.ts +6 -0
- package/lib/commands/generate/watchMode/watchMode.js +79 -0
- package/package.json +7 -6
package/bin/api-generator.js
CHANGED
|
@@ -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
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
25
|
-
|
|
23
|
+
else {
|
|
24
|
+
(0, generateFiles_1.generateFiles)(options.file);
|
|
26
25
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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");
|
|
@@ -11,9 +11,6 @@ export declare function buildOptions(metadata: EntityMetadata<any>, generatorOpt
|
|
|
11
11
|
hasSlugProp: boolean;
|
|
12
12
|
hasPositionProp: boolean;
|
|
13
13
|
positionGroupProps: import("@mikro-orm/postgresql").EntityProperty<any, any>[];
|
|
14
|
-
statusProp: import("@mikro-orm/postgresql").EntityProperty<any, any> | undefined;
|
|
15
|
-
statusActiveItems: (string | number)[] | undefined;
|
|
16
|
-
hasStatusFilter: boolean | undefined;
|
|
17
14
|
scopeProp: import("@mikro-orm/postgresql").EntityProperty<any, any> | undefined;
|
|
18
15
|
skipScopeCheck: boolean;
|
|
19
16
|
argsClassName: string;
|
|
@@ -56,7 +56,7 @@ const generate_imports_code_1 = require("../utils/generate-imports-code");
|
|
|
56
56
|
const ts_morph_helper_1 = require("../utils/ts-morph-helper");
|
|
57
57
|
// TODO move into own file
|
|
58
58
|
function buildOptions(metadata, generatorOptions) {
|
|
59
|
-
var _a, _b
|
|
59
|
+
var _a, _b;
|
|
60
60
|
const { classNameSingular, classNamePlural, fileNameSingular, fileNamePlural } = (0, build_name_variants_1.buildNameVariants)(metadata);
|
|
61
61
|
const dedicatedResolverArgProps = metadata.props.filter((prop) => {
|
|
62
62
|
if ((0, cms_api_1.hasCrudFieldFeature)(metadata.class, prop.name, "dedicatedResolverArg")) {
|
|
@@ -72,34 +72,6 @@ function buildOptions(metadata, generatorOptions) {
|
|
|
72
72
|
});
|
|
73
73
|
const crudSearchPropNames = (0, cms_api_1.getCrudSearchFieldsFromMetadata)(metadata);
|
|
74
74
|
const hasSearchArg = crudSearchPropNames.length > 0;
|
|
75
|
-
let statusProp = metadata.props.find((prop) => prop.name == "status");
|
|
76
|
-
if (statusProp) {
|
|
77
|
-
if (!statusProp.enum) {
|
|
78
|
-
console.warn(`${metadata.className} status prop must be an enum to be supported by crud generator`);
|
|
79
|
-
statusProp = undefined;
|
|
80
|
-
}
|
|
81
|
-
else if (statusProp.nullable) {
|
|
82
|
-
console.warn(`${metadata.className} status prop must not be nullable to be supported by crud generator`);
|
|
83
|
-
statusProp = undefined;
|
|
84
|
-
}
|
|
85
|
-
else if (((_a = (0, ts_morph_helper_1.morphTsProperty)(statusProp.name, metadata).getInitializer()) === null || _a === void 0 ? void 0 : _a.getText()) == "") {
|
|
86
|
-
console.warn(`${metadata.className} status prop must have a default value to be supported by crud generator`);
|
|
87
|
-
statusProp = undefined;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
let statusActiveItems = undefined;
|
|
91
|
-
if (statusProp) {
|
|
92
|
-
if (!statusProp.items)
|
|
93
|
-
throw new Error("Status enum prop has not items");
|
|
94
|
-
statusActiveItems = statusProp.items.filter((item) => {
|
|
95
|
-
if (typeof item == "number") {
|
|
96
|
-
console.warn(`${metadata.className} status prop must not have numeric items to be supported by crud generator`);
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
return ["Active", "Visible", "Invisible", "Published", "Unpublished"].includes(item);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
const hasStatusFilter = statusProp && statusActiveItems && statusActiveItems.length != ((_b = statusProp.items) === null || _b === void 0 ? void 0 : _b.length); //if all items are active ones, no need for status filter
|
|
103
75
|
const crudFilterProps = metadata.props.filter((prop) => (0, cms_api_1.hasCrudFieldFeature)(metadata.class, prop.name, "filter") &&
|
|
104
76
|
!prop.name.startsWith("scope_") &&
|
|
105
77
|
prop.name != "position" &&
|
|
@@ -117,7 +89,8 @@ function buildOptions(metadata, generatorOptions) {
|
|
|
117
89
|
prop.kind === "m:1" ||
|
|
118
90
|
prop.kind === "1:m" ||
|
|
119
91
|
prop.kind === "m:n" ||
|
|
120
|
-
prop.type === "EnumArrayType"
|
|
92
|
+
prop.type === "EnumArrayType" ||
|
|
93
|
+
prop.type === "uuid") &&
|
|
121
94
|
!dedicatedResolverArgProps.some((dedicatedResolverArgProp) => dedicatedResolverArgProp.name == prop.name));
|
|
122
95
|
const hasFilterArg = crudFilterProps.length > 0;
|
|
123
96
|
const crudSortProps = metadata.props.filter((prop) => (0, cms_api_1.hasCrudFieldFeature)(metadata.class, prop.name, "sort") &&
|
|
@@ -133,7 +106,8 @@ function buildOptions(metadata, generatorOptions) {
|
|
|
133
106
|
prop.type === "DateType" ||
|
|
134
107
|
prop.type === "Date" ||
|
|
135
108
|
prop.kind === "m:1" ||
|
|
136
|
-
prop.type === "EnumArrayType"
|
|
109
|
+
prop.type === "EnumArrayType" ||
|
|
110
|
+
prop.enum));
|
|
137
111
|
const hasSortArg = crudSortProps.length > 0;
|
|
138
112
|
const hasSlugProp = metadata.props.some((prop) => prop.name == "slug");
|
|
139
113
|
const scopeProp = metadata.props.find((prop) => prop.name == "scope");
|
|
@@ -141,7 +115,7 @@ function buildOptions(metadata, generatorOptions) {
|
|
|
141
115
|
throw new Error("Scope prop has no targetMeta");
|
|
142
116
|
const hasPositionProp = metadata.props.some((prop) => prop.name == "position");
|
|
143
117
|
const positionGroupPropNames = hasPositionProp
|
|
144
|
-
? ((
|
|
118
|
+
? ((_b = (_a = generatorOptions.position) === null || _a === void 0 ? void 0 : _a.groupByFields) !== null && _b !== void 0 ? _b : [
|
|
145
119
|
...(scopeProp ? [scopeProp.name] : []), // if there is a scope prop it's effecting position-group, if not groupByFields should be used
|
|
146
120
|
])
|
|
147
121
|
: [];
|
|
@@ -163,9 +137,6 @@ function buildOptions(metadata, generatorOptions) {
|
|
|
163
137
|
hasSlugProp,
|
|
164
138
|
hasPositionProp,
|
|
165
139
|
positionGroupProps,
|
|
166
|
-
statusProp,
|
|
167
|
-
statusActiveItems,
|
|
168
|
-
hasStatusFilter,
|
|
169
140
|
scopeProp,
|
|
170
141
|
skipScopeCheck,
|
|
171
142
|
argsClassName,
|
|
@@ -177,7 +148,7 @@ function buildOptions(metadata, generatorOptions) {
|
|
|
177
148
|
function generateFilterDto({ generatorOptions, metadata }) {
|
|
178
149
|
const { classNameSingular } = (0, build_name_variants_1.buildNameVariants)(metadata);
|
|
179
150
|
const { crudFilterProps } = buildOptions(metadata, generatorOptions);
|
|
180
|
-
|
|
151
|
+
const imports = [];
|
|
181
152
|
let enumFiltersOut = "";
|
|
182
153
|
const generatedEnumNames = new Set();
|
|
183
154
|
const generatedEnumsNames = new Set();
|
|
@@ -190,7 +161,7 @@ function generateFilterDto({ generatorOptions, metadata }) {
|
|
|
190
161
|
enumFiltersOut += `@InputType()
|
|
191
162
|
class ${enumName}EnumsFilter extends createEnumsFilter(${enumName}) {}
|
|
192
163
|
`;
|
|
193
|
-
|
|
164
|
+
imports.push({ name: enumName, importPath });
|
|
194
165
|
}
|
|
195
166
|
}
|
|
196
167
|
else if (prop.enum) {
|
|
@@ -201,15 +172,15 @@ function generateFilterDto({ generatorOptions, metadata }) {
|
|
|
201
172
|
enumFiltersOut += `@InputType()
|
|
202
173
|
class ${enumName}EnumFilter extends createEnumFilter(${enumName}) {}
|
|
203
174
|
`;
|
|
204
|
-
|
|
175
|
+
imports.push({ name: enumName, importPath });
|
|
205
176
|
}
|
|
206
177
|
}
|
|
207
178
|
});
|
|
208
|
-
const filterOut = `import { StringFilter, NumberFilter, BooleanFilter, DateFilter, DateTimeFilter, ManyToOneFilter, OneToManyFilter, ManyToManyFilter, createEnumFilter, createEnumsFilter } from "@comet/cms-api";
|
|
179
|
+
const filterOut = `import { StringFilter, NumberFilter, BooleanFilter, DateFilter, DateTimeFilter, ManyToOneFilter, OneToManyFilter, ManyToManyFilter, IdFilter, createEnumFilter, createEnumsFilter } from "@comet/cms-api";
|
|
209
180
|
import { Field, InputType } from "@nestjs/graphql";
|
|
210
181
|
import { Type } from "class-transformer";
|
|
211
182
|
import { IsNumber, IsOptional, IsString, ValidateNested } from "class-validator";
|
|
212
|
-
${
|
|
183
|
+
${(0, generate_imports_code_1.generateImportsCode)(imports)}
|
|
213
184
|
|
|
214
185
|
${enumFiltersOut}
|
|
215
186
|
|
|
@@ -301,6 +272,14 @@ function generateFilterDto({ generatorOptions, metadata }) {
|
|
|
301
272
|
${prop.name}?: ManyToManyFilter;
|
|
302
273
|
`;
|
|
303
274
|
}
|
|
275
|
+
else if (prop.type == "uuid") {
|
|
276
|
+
return `@Field(() => IdFilter, { nullable: true })
|
|
277
|
+
@ValidateNested()
|
|
278
|
+
@IsOptional()
|
|
279
|
+
@Type(() => IdFilter)
|
|
280
|
+
${prop.name}?: IdFilter;
|
|
281
|
+
`;
|
|
282
|
+
}
|
|
304
283
|
else {
|
|
305
284
|
//unsupported type TODO support more
|
|
306
285
|
}
|
|
@@ -368,29 +347,12 @@ function generatePaginatedDto({ generatorOptions, metadata }) {
|
|
|
368
347
|
return paginatedOut;
|
|
369
348
|
}
|
|
370
349
|
function generateArgsDto({ generatorOptions, metadata }) {
|
|
371
|
-
var _a;
|
|
372
350
|
const { classNameSingular, fileNameSingular } = (0, build_name_variants_1.buildNameVariants)(metadata);
|
|
373
|
-
const { scopeProp, argsClassName, hasSearchArg, hasSortArg, hasFilterArg,
|
|
351
|
+
const { scopeProp, argsClassName, hasSearchArg, hasSortArg, hasFilterArg, dedicatedResolverArgProps } = buildOptions(metadata, generatorOptions);
|
|
374
352
|
const imports = [];
|
|
375
353
|
if (scopeProp && scopeProp.targetMeta) {
|
|
376
354
|
imports.push(generateEntityImport(scopeProp.targetMeta, `${generatorOptions.targetDirectory}/dto`));
|
|
377
355
|
}
|
|
378
|
-
let statusFilterClassName = undefined;
|
|
379
|
-
let statusFilterDefaultValue;
|
|
380
|
-
if (hasStatusFilter && statusProp) {
|
|
381
|
-
statusFilterClassName = (0, ts_morph_helper_1.findEnumName)(statusProp.name, metadata);
|
|
382
|
-
const importPath = (0, ts_morph_helper_1.findEnumImportPath)(statusFilterClassName, `${generatorOptions.targetDirectory}/dto`, metadata);
|
|
383
|
-
imports.push({
|
|
384
|
-
name: statusFilterClassName,
|
|
385
|
-
importPath,
|
|
386
|
-
});
|
|
387
|
-
if (statusActiveItems && statusActiveItems.length > 1) {
|
|
388
|
-
statusFilterDefaultValue = `[${statusActiveItems.map((i) => `${statusFilterClassName}.${i}`).join(", ")}]`;
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
statusFilterDefaultValue = `[${(_a = (0, ts_morph_helper_1.morphTsProperty)(statusProp.name, metadata).getInitializer()) === null || _a === void 0 ? void 0 : _a.getText()}]`;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
356
|
const argsOut = `import { ArgsType, Field, IntersectionType, registerEnumType, ID } from "@nestjs/graphql";
|
|
395
357
|
import { Type } from "class-transformer";
|
|
396
358
|
import { IsOptional, IsString, ValidateNested, IsEnum, IsUUID } from "class-validator";
|
|
@@ -427,14 +389,6 @@ function generateArgsDto({ generatorOptions, metadata }) {
|
|
|
427
389
|
})
|
|
428
390
|
.join("")}
|
|
429
391
|
|
|
430
|
-
${hasStatusFilter
|
|
431
|
-
? `
|
|
432
|
-
@Field(() => [${statusFilterClassName}], { defaultValue: ${statusFilterDefaultValue} })
|
|
433
|
-
@IsEnum(${statusFilterClassName}, { each: true })
|
|
434
|
-
status: ${statusFilterClassName}[];
|
|
435
|
-
`
|
|
436
|
-
: ""}
|
|
437
|
-
|
|
438
392
|
${hasSearchArg
|
|
439
393
|
? `
|
|
440
394
|
@Field({ nullable: true })
|
|
@@ -481,9 +435,7 @@ function generateService({ generatorOptions, metadata }) {
|
|
|
481
435
|
})
|
|
482
436
|
.join(",")} }`
|
|
483
437
|
: false;
|
|
484
|
-
const serviceOut = `import { FilterQuery } from "@mikro-orm/postgresql";
|
|
485
|
-
import { InjectRepository } from "@mikro-orm/nestjs";
|
|
486
|
-
import { EntityRepository, EntityManager, raw } from "@mikro-orm/postgresql";
|
|
438
|
+
const serviceOut = `import { EntityManager, FilterQuery, raw } from "@mikro-orm/postgresql";
|
|
487
439
|
import { Injectable } from "@nestjs/common";
|
|
488
440
|
|
|
489
441
|
${(0, generate_imports_code_1.generateImportsCode)([generateEntityImport(metadata, generatorOptions.targetDirectory)])}
|
|
@@ -500,7 +452,6 @@ function generateService({ generatorOptions, metadata }) {
|
|
|
500
452
|
${hasPositionProp
|
|
501
453
|
? `constructor(
|
|
502
454
|
private readonly entityManager: EntityManager,
|
|
503
|
-
@InjectRepository(${metadata.className}) private readonly repository: EntityRepository<${metadata.className}>,
|
|
504
455
|
) {}`
|
|
505
456
|
: ""}
|
|
506
457
|
|
|
@@ -508,7 +459,7 @@ function generateService({ generatorOptions, metadata }) {
|
|
|
508
459
|
? `
|
|
509
460
|
async incrementPositions(${positionGroupProps.length ? `group: ${positionGroupType},` : ``}lowestPosition: number, highestPosition?: number) {
|
|
510
461
|
// Increment positions between newPosition (inclusive) and oldPosition (exclusive)
|
|
511
|
-
await this.
|
|
462
|
+
await this.entityManager.nativeUpdate(${metadata.className},
|
|
512
463
|
${positionGroupProps.length
|
|
513
464
|
? `{
|
|
514
465
|
$and: [
|
|
@@ -523,7 +474,7 @@ function generateService({ generatorOptions, metadata }) {
|
|
|
523
474
|
|
|
524
475
|
async decrementPositions(${positionGroupProps.length ? `group: ${positionGroupType},` : ``}lowestPosition: number, highestPosition?: number) {
|
|
525
476
|
// Decrement positions between oldPosition (exclusive) and newPosition (inclusive)
|
|
526
|
-
await this.
|
|
477
|
+
await this.entityManager.nativeUpdate(${metadata.className},
|
|
527
478
|
${positionGroupProps.length
|
|
528
479
|
? `{
|
|
529
480
|
$and: [
|
|
@@ -537,7 +488,7 @@ function generateService({ generatorOptions, metadata }) {
|
|
|
537
488
|
}
|
|
538
489
|
|
|
539
490
|
async getLastPosition(${positionGroupProps.length ? `group: ${positionGroupType}` : ``}) {
|
|
540
|
-
return this.
|
|
491
|
+
return this.entityManager.count(${metadata.className}, ${positionGroupProps.length ? `this.getPositionGroupCondition(group)` : `{}`});
|
|
541
492
|
}
|
|
542
493
|
|
|
543
494
|
${positionGroupProps.length
|
|
@@ -562,7 +513,6 @@ function generateEntityImport(targetMetadata, relativeTo) {
|
|
|
562
513
|
function generateInputHandling(options, metadata, generatorOptions) {
|
|
563
514
|
const { instanceNameSingular } = (0, build_name_variants_1.buildNameVariants)(metadata);
|
|
564
515
|
const { blockProps, scopeProp, hasPositionProp, dedicatedResolverArgProps } = buildOptions(metadata, generatorOptions);
|
|
565
|
-
const injectRepositories = [];
|
|
566
516
|
const props = metadata.props.filter((prop) => !options.excludeFields || !options.excludeFields.includes(prop.name));
|
|
567
517
|
const relationManyToOneProps = props.filter((prop) => prop.kind === "m:1");
|
|
568
518
|
const relationOneToManyProps = props.filter((prop) => prop.kind === "1:m");
|
|
@@ -578,13 +528,11 @@ function generateInputHandling(options, metadata, generatorOptions) {
|
|
|
578
528
|
const targetMeta = prop.targetMeta;
|
|
579
529
|
if (!targetMeta)
|
|
580
530
|
throw new Error("targetMeta is not set for relation");
|
|
581
|
-
injectRepositories.push(targetMeta);
|
|
582
531
|
return {
|
|
583
532
|
name: prop.name,
|
|
584
533
|
singularName: (0, pluralize_1.singular)(prop.name),
|
|
585
534
|
nullable: prop.nullable,
|
|
586
535
|
type: prop.type,
|
|
587
|
-
repositoryName: `${(0, build_name_variants_1.classNameToInstanceName)(prop.type)}Repository`,
|
|
588
536
|
};
|
|
589
537
|
});
|
|
590
538
|
const inputRelationOneToOneProps = relationOneToOneProps
|
|
@@ -593,13 +541,11 @@ function generateInputHandling(options, metadata, generatorOptions) {
|
|
|
593
541
|
const targetMeta = prop.targetMeta;
|
|
594
542
|
if (!targetMeta)
|
|
595
543
|
throw new Error("targetMeta is not set for relation");
|
|
596
|
-
injectRepositories.push(targetMeta);
|
|
597
544
|
return {
|
|
598
545
|
name: prop.name,
|
|
599
546
|
singularName: (0, pluralize_1.singular)(prop.name),
|
|
600
547
|
nullable: prop.nullable,
|
|
601
548
|
type: prop.type,
|
|
602
|
-
repositoryName: `${(0, build_name_variants_1.classNameToInstanceName)(prop.type)}Repository`,
|
|
603
549
|
targetMeta,
|
|
604
550
|
};
|
|
605
551
|
});
|
|
@@ -609,20 +555,17 @@ function generateInputHandling(options, metadata, generatorOptions) {
|
|
|
609
555
|
const targetMeta = prop.targetMeta;
|
|
610
556
|
if (!targetMeta)
|
|
611
557
|
throw new Error("targetMeta is not set for relation");
|
|
612
|
-
injectRepositories.push(targetMeta);
|
|
613
558
|
return {
|
|
614
559
|
name: prop.name,
|
|
615
560
|
singularName: (0, pluralize_1.singular)(prop.name),
|
|
616
561
|
nullable: prop.nullable,
|
|
617
562
|
type: prop.type,
|
|
618
|
-
repositoryName: `${(0, build_name_variants_1.classNameToInstanceName)(prop.type)}Repository`,
|
|
619
563
|
orphanRemoval: prop.orphanRemoval,
|
|
620
564
|
targetMeta,
|
|
621
565
|
};
|
|
622
566
|
});
|
|
623
567
|
function innerGenerateInputHandling(...args) {
|
|
624
568
|
const ret = generateInputHandling(...args);
|
|
625
|
-
injectRepositories.push(...ret.injectRepositories);
|
|
626
569
|
return ret.code;
|
|
627
570
|
}
|
|
628
571
|
const noAssignProps = [...inputRelationToManyProps, ...inputRelationManyToOneProps, ...inputRelationOneToOneProps, ...blockProps];
|
|
@@ -636,13 +579,13 @@ function generateInputHandling(options, metadata, generatorOptions) {
|
|
|
636
579
|
${options.mode == "create"
|
|
637
580
|
? dedicatedResolverArgProps
|
|
638
581
|
.map((dedicatedResolverArgProp) => {
|
|
639
|
-
return `${dedicatedResolverArgProp.name}: Reference.create(await this
|
|
582
|
+
return `${dedicatedResolverArgProp.name}: Reference.create(await this.entityManager.findOneOrFail(${dedicatedResolverArgProp.type}, ${dedicatedResolverArgProp.name})), `;
|
|
640
583
|
})
|
|
641
584
|
.join("")
|
|
642
585
|
: ""}
|
|
643
586
|
${options.mode == "create" || options.mode == "updateNested"
|
|
644
587
|
? inputRelationManyToOneProps
|
|
645
|
-
.map((prop) => `${prop.name}: ${prop.nullable ? `${prop.name}Input ? ` : ""}Reference.create(await this
|
|
588
|
+
.map((prop) => `${prop.name}: ${prop.nullable ? `${prop.name}Input ? ` : ""}Reference.create(await this.entityManager.findOneOrFail(${prop.type}, ${prop.name}Input))${prop.nullable ? ` : undefined` : ""}, `)
|
|
646
589
|
.join("")
|
|
647
590
|
: ""}
|
|
648
591
|
${options.mode == "create" || options.mode == "updateNested"
|
|
@@ -655,8 +598,8 @@ ${inputRelationToManyProps
|
|
|
655
598
|
const code = innerGenerateInputHandling({
|
|
656
599
|
mode: "updateNested",
|
|
657
600
|
inputName: `${prop.singularName}Input`,
|
|
658
|
-
// alternative `return this
|
|
659
|
-
assignEntityCode: `return this
|
|
601
|
+
// alternative `return this.entityManager.create(${prop.type}, {` requires back relation to be set
|
|
602
|
+
assignEntityCode: `return this.entityManager.assign(new ${prop.type}(), {`,
|
|
660
603
|
excludeFields: prop.targetMeta.props
|
|
661
604
|
.filter((prop) => prop.kind == "m:1" && prop.targetMeta == metadata) //filter out referencing back to this entity
|
|
662
605
|
.map((prop) => prop.name),
|
|
@@ -676,7 +619,7 @@ ${inputRelationToManyProps
|
|
|
676
619
|
else {
|
|
677
620
|
return `
|
|
678
621
|
if (${prop.name}Input) {
|
|
679
|
-
const ${prop.name} = await this
|
|
622
|
+
const ${prop.name} = await this.entityManager.find(${prop.type}, { id: ${prop.name}Input });
|
|
680
623
|
if (${prop.name}.length != ${prop.name}Input.length) throw new Error("Couldn't find all ${prop.name} that were passed as input");
|
|
681
624
|
await ${instanceNameSingular}.${prop.name}.loadItems();
|
|
682
625
|
${instanceNameSingular}.${prop.name}.set(${prop.name}.map((${prop.singularName}) => Reference.create(${prop.singularName})));
|
|
@@ -694,7 +637,7 @@ ${inputRelationOneToOneProps
|
|
|
694
637
|
${innerGenerateInputHandling({
|
|
695
638
|
mode: "updateNested",
|
|
696
639
|
inputName: `${prop.name}Input`,
|
|
697
|
-
assignEntityCode: `this
|
|
640
|
+
assignEntityCode: `this.entityManager.assign(${prop.singularName}, {`,
|
|
698
641
|
excludeFields: prop.targetMeta.props
|
|
699
642
|
.filter((prop) => prop.kind == "1:1" && prop.targetMeta == metadata) //filter out referencing back to this entity
|
|
700
643
|
.map((prop) => prop.name),
|
|
@@ -706,7 +649,7 @@ ${options.mode == "update"
|
|
|
706
649
|
.map((prop) => `if (${prop.name}Input !== undefined) {
|
|
707
650
|
${instanceNameSingular}.${prop.name} =
|
|
708
651
|
${prop.nullable ? `${prop.name}Input ? ` : ""}
|
|
709
|
-
Reference.create(await this
|
|
652
|
+
Reference.create(await this.entityManager.findOneOrFail(${prop.type}, ${prop.name}Input))
|
|
710
653
|
${prop.nullable ? ` : undefined` : ""};
|
|
711
654
|
}`)
|
|
712
655
|
.join("")
|
|
@@ -720,7 +663,7 @@ ${options.mode == "update"
|
|
|
720
663
|
.join("")
|
|
721
664
|
: ""}
|
|
722
665
|
`;
|
|
723
|
-
return { code
|
|
666
|
+
return { code };
|
|
724
667
|
}
|
|
725
668
|
function generateNestedEntityResolver({ generatorOptions, metadata }) {
|
|
726
669
|
const { classNameSingular } = (0, build_name_variants_1.buildNameVariants)(metadata);
|
|
@@ -837,7 +780,7 @@ function generateRelationsFieldResolver({ generatorOptions, metadata }) {
|
|
|
837
780
|
}
|
|
838
781
|
function generateResolver({ generatorOptions, metadata }) {
|
|
839
782
|
const { classNameSingular, fileNameSingular, instanceNameSingular, classNamePlural, fileNamePlural, instanceNamePlural } = (0, build_name_variants_1.buildNameVariants)(metadata);
|
|
840
|
-
const { scopeProp, skipScopeCheck, argsClassName, argsFileName, hasSlugProp, hasSearchArg, hasSortArg, hasFilterArg, hasPositionProp, positionGroupProps,
|
|
783
|
+
const { scopeProp, skipScopeCheck, argsClassName, argsFileName, hasSlugProp, hasSearchArg, hasSortArg, hasFilterArg, hasPositionProp, positionGroupProps, dedicatedResolverArgProps, } = buildOptions(metadata, generatorOptions);
|
|
841
784
|
const relationManyToOneProps = metadata.props.filter((prop) => prop.kind === "m:1");
|
|
842
785
|
const relationOneToManyProps = metadata.props.filter((prop) => prop.kind === "1:m");
|
|
843
786
|
const relationManyToManyProps = metadata.props.filter((prop) => prop.kind === "m:n");
|
|
@@ -847,16 +790,12 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
847
790
|
const outputRelationManyToManyProps = relationManyToManyProps.filter((prop) => (0, cms_api_1.hasCrudFieldFeature)(metadata.class, prop.name, "resolveField"));
|
|
848
791
|
const outputRelationOneToOneProps = relationOneToOneProps.filter((prop) => (0, cms_api_1.hasCrudFieldFeature)(metadata.class, prop.name, "resolveField"));
|
|
849
792
|
const imports = [];
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
if (!prop.targetMeta)
|
|
857
|
-
throw new Error("targetMeta is not set for relation");
|
|
858
|
-
return prop.targetMeta;
|
|
859
|
-
}));
|
|
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);
|
|
860
799
|
const { imports: relationsFieldResolverImports, code: relationsFieldResolverCode, hasOutputRelations, needsBlocksTransformer, } = generateRelationsFieldResolver({
|
|
861
800
|
generatorOptions,
|
|
862
801
|
metadata,
|
|
@@ -866,15 +805,6 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
866
805
|
if (scopeProp && scopeProp.targetMeta) {
|
|
867
806
|
imports.push(generateEntityImport(scopeProp.targetMeta, generatorOptions.targetDirectory));
|
|
868
807
|
}
|
|
869
|
-
imports.push(...injectRepositories.map((meta) => generateEntityImport(meta, generatorOptions.targetDirectory)));
|
|
870
|
-
if (statusProp) {
|
|
871
|
-
const enumName = (0, ts_morph_helper_1.findEnumName)(statusProp.name, metadata);
|
|
872
|
-
const importPath = (0, ts_morph_helper_1.findEnumImportPath)(enumName, generatorOptions.targetDirectory, metadata);
|
|
873
|
-
imports.push({
|
|
874
|
-
name: enumName,
|
|
875
|
-
importPath,
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
808
|
function generateIdArg(name, metadata) {
|
|
879
809
|
if (constants_1.integerTypes.includes(metadata.properties[name].type)) {
|
|
880
810
|
return `@Args("${name}", { type: () => ID }, { transform: (value) => parseInt(value) }) ${name}: number`;
|
|
@@ -891,9 +821,7 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
891
821
|
imports.push({ name: "RootBlockDataScalar", importPath: "@comet/cms-api" });
|
|
892
822
|
imports.push({ name: "BlocksTransformerService", importPath: "@comet/cms-api" });
|
|
893
823
|
imports.push({ name: "gqlArgsToMikroOrmQuery", importPath: "@comet/cms-api" });
|
|
894
|
-
const resolverOut = `import {
|
|
895
|
-
import { EntityRepository, EntityManager } from "@mikro-orm/postgresql";
|
|
896
|
-
import { FindOptions, ObjectQuery, Reference } from "@mikro-orm/postgresql";
|
|
824
|
+
const resolverOut = `import { EntityManager, FindOptions, ObjectQuery, Reference } from "@mikro-orm/postgresql";
|
|
897
825
|
import { Args, ID, Info, Mutation, Query, Resolver, ResolveField, Parent } from "@nestjs/graphql";
|
|
898
826
|
import { GraphQLResolveInfo } from "graphql";
|
|
899
827
|
|
|
@@ -908,18 +836,19 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
908
836
|
export class ${classNameSingular}Resolver {
|
|
909
837
|
constructor(
|
|
910
838
|
private readonly entityManager: EntityManager,${hasPositionProp ? `private readonly ${instanceNamePlural}Service: ${classNamePlural}Service,` : ``}
|
|
911
|
-
|
|
912
|
-
${[...new Set(injectRepositories.map((meta) => meta.className))]
|
|
913
|
-
.map((type) => `@InjectRepository(${type}) private readonly ${(0, build_name_variants_1.classNameToInstanceName)(type)}Repository: EntityRepository<${type}>,`)
|
|
914
|
-
.join("")}${needsBlocksTransformer ? `private readonly blocksTransformer: BlocksTransformerService,` : ""}
|
|
839
|
+
${needsBlocksTransformer ? `private readonly blocksTransformer: BlocksTransformerService,` : ""}
|
|
915
840
|
) {}
|
|
916
841
|
|
|
842
|
+
${generatorOptions.single
|
|
843
|
+
? `
|
|
917
844
|
@Query(() => ${metadata.className})
|
|
918
845
|
@AffectedEntity(${metadata.className})
|
|
919
846
|
async ${instanceNameSingular}(${generateIdArg("id", metadata)}): Promise<${metadata.className}> {
|
|
920
|
-
const ${instanceNameSingular} = await this.
|
|
847
|
+
const ${instanceNameSingular} = await this.entityManager.findOneOrFail(${metadata.className}, id);
|
|
921
848
|
return ${instanceNameSingular};
|
|
922
849
|
}
|
|
850
|
+
`
|
|
851
|
+
: ""}
|
|
923
852
|
|
|
924
853
|
${hasSlugProp
|
|
925
854
|
? `
|
|
@@ -928,7 +857,7 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
928
857
|
@Args("slug") slug: string
|
|
929
858
|
${scopeProp ? `, @Args("scope", { type: () => ${scopeProp.type} }) scope: ${scopeProp.type}` : ""}
|
|
930
859
|
): Promise<${metadata.className} | null> {
|
|
931
|
-
const ${instanceNameSingular} = await this.
|
|
860
|
+
const ${instanceNameSingular} = await this.entityManager.findOne(${metadata.className}, { slug${scopeProp ? `, scope` : ""}});
|
|
932
861
|
|
|
933
862
|
return ${instanceNameSingular} ?? null;
|
|
934
863
|
}
|
|
@@ -948,16 +877,15 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
948
877
|
@Args() {${Object.entries(Object.assign(Object.assign({ scope: !!scopeProp }, dedicatedResolverArgProps.reduce((acc, dedicatedResolverArgProp) => {
|
|
949
878
|
acc[dedicatedResolverArgProp.name] = true;
|
|
950
879
|
return acc;
|
|
951
|
-
}, {})), {
|
|
880
|
+
}, {})), { search: !!hasSearchArg, filter: !!hasFilterArg, sort: !!hasSortArg, offset: true, limit: true }))
|
|
952
881
|
.filter(([key, use]) => use)
|
|
953
882
|
.map(([key]) => key)
|
|
954
883
|
.join(", ")}}: ${argsClassName}
|
|
955
884
|
${hasOutputRelations ? `, @Info() info: GraphQLResolveInfo` : ""}
|
|
956
885
|
): Promise<Paginated${classNamePlural}> {
|
|
957
886
|
const where${hasSearchArg || hasFilterArg
|
|
958
|
-
? ` = gqlArgsToMikroOrmQuery({ ${hasSearchArg ? `search, ` : ""}${hasFilterArg ? `filter, ` : ""} }, this.
|
|
887
|
+
? ` = gqlArgsToMikroOrmQuery({ ${hasSearchArg ? `search, ` : ""}${hasFilterArg ? `filter, ` : ""} }, this.entityManager.getMetadata(${metadata.className}));`
|
|
959
888
|
: `: ObjectQuery<${metadata.className}> = {}`}
|
|
960
|
-
${hasStatusFilter ? `where.status = { $in: status };` : ""}
|
|
961
889
|
${scopeProp ? `where.scope = scope;` : ""}
|
|
962
890
|
${dedicatedResolverArgProps
|
|
963
891
|
.map((dedicatedResolverArgProp) => {
|
|
@@ -988,7 +916,7 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
988
916
|
}`
|
|
989
917
|
: ""}
|
|
990
918
|
|
|
991
|
-
const [entities, totalCount] = await this.
|
|
919
|
+
const [entities, totalCount] = await this.entityManager.findAndCount(${metadata.className}, where, options);
|
|
992
920
|
return new Paginated${classNamePlural}(entities, totalCount);
|
|
993
921
|
}
|
|
994
922
|
`
|
|
@@ -1059,7 +987,7 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
1059
987
|
${generateIdArg("id", metadata)},
|
|
1060
988
|
@Args("input", { type: () => ${classNameSingular}UpdateInput }) input: ${classNameSingular}UpdateInput
|
|
1061
989
|
): Promise<${metadata.className}> {
|
|
1062
|
-
const ${instanceNameSingular} = await this.
|
|
990
|
+
const ${instanceNameSingular} = await this.entityManager.findOneOrFail(${metadata.className}, id);
|
|
1063
991
|
|
|
1064
992
|
${hasPositionProp
|
|
1065
993
|
? `
|
|
@@ -1108,7 +1036,7 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
1108
1036
|
@Mutation(() => Boolean)
|
|
1109
1037
|
@AffectedEntity(${metadata.className})
|
|
1110
1038
|
async delete${metadata.className}(${generateIdArg("id", metadata)}): Promise<boolean> {
|
|
1111
|
-
const ${instanceNameSingular} = await this.
|
|
1039
|
+
const ${instanceNameSingular} = await this.entityManager.findOneOrFail(${metadata.className}, id);
|
|
1112
1040
|
this.entityManager.remove(${instanceNameSingular});${hasPositionProp
|
|
1113
1041
|
? `await this.${instanceNamePlural}Service.decrementPositions(${positionGroupProps.length
|
|
1114
1042
|
? `{ ${positionGroupProps
|
|
@@ -1131,8 +1059,11 @@ function generateResolver({ generatorOptions, metadata }) {
|
|
|
1131
1059
|
}
|
|
1132
1060
|
function generateCrud(generatorOptionsParam, metadata) {
|
|
1133
1061
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1134
|
-
var _a, _b, _c, _d;
|
|
1135
|
-
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
|
+
}
|
|
1136
1067
|
const generatedFiles = [];
|
|
1137
1068
|
const { fileNameSingular, fileNamePlural, instanceNamePlural } = (0, build_name_variants_1.buildNameVariants)(metadata);
|
|
1138
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
|
|
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 {
|
|
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
96
|
private readonly entityManager: EntityManager,
|
|
102
|
-
private readonly ${instanceNamePlural}Service: ${classNamePlural}Service
|
|
103
|
-
@InjectRepository(${metadata.className}) private readonly repository: EntityRepository<${metadata.className}>
|
|
97
|
+
private 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.
|
|
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.
|
|
117
|
+
let ${instanceNameSingular} = await this.entityManager.findOne(${metadata.className}, {${scopeProp ? `scope` : ""}});
|
|
124
118
|
|
|
125
119
|
if (!${instanceNameSingular}) {
|
|
126
|
-
${instanceNameSingular} = this.
|
|
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,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
|
-
|
|
65
|
-
|
|
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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
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,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,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.
|
|
3
|
+
"version": "8.0.0-beta.5",
|
|
4
4
|
"repository": {
|
|
5
5
|
"directory": "packages/api/api-generator",
|
|
6
6
|
"type": "git",
|
|
@@ -17,19 +17,20 @@
|
|
|
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.
|
|
25
|
+
"@comet/cms-api": "8.0.0-beta.5"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@mikro-orm/cli": "^6.4.10",
|
|
28
29
|
"@mikro-orm/core": "^6.4.10",
|
|
29
30
|
"@mikro-orm/postgresql": "^6.4.10",
|
|
30
|
-
"@nestjs/graphql": "^13.0
|
|
31
|
+
"@nestjs/graphql": "^13.1.0",
|
|
31
32
|
"@types/jest": "^29.5.14",
|
|
32
|
-
"@types/node": "^22.
|
|
33
|
+
"@types/node": "^22.15.21",
|
|
33
34
|
"@types/pluralize": "^0.0.33",
|
|
34
35
|
"class-validator": "^0.14.1",
|
|
35
36
|
"eslint": "^9.22.0",
|
|
@@ -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.
|
|
45
|
+
"@comet/eslint-config": "8.0.0-beta.5"
|
|
45
46
|
},
|
|
46
47
|
"peerDependencies": {
|
|
47
48
|
"@mikro-orm/cli": "^6.0.0",
|
|
@@ -64,6 +65,6 @@
|
|
|
64
65
|
"lint:prettier": "npx prettier --check './**/*.{js,json,md,yml,yaml}'",
|
|
65
66
|
"lint:tsc": "tsc",
|
|
66
67
|
"test": "NODE_OPTIONS=--experimental-vm-modules npx jest",
|
|
67
|
-
"test:watch": "jest --watch"
|
|
68
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
68
69
|
}
|
|
69
70
|
}
|