prisma-nestjs-graphql 21.2.0 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2352 @@
1
+ import { ok } from 'node:assert';
2
+ import awaitEventEmitterModule from 'await-event-emitter';
3
+ import { StructureKind, Project, QuoteKind } from 'ts-morph';
4
+ import lodash from 'lodash';
5
+ import { ok as ok$1 } from 'assert';
6
+ import JSON5 from 'json5';
7
+ import pupa from 'pupa';
8
+ import path from 'node:path';
9
+ import getRelativePath from 'get-relative-path';
10
+ import outmatch from 'outmatch';
11
+ import fs from 'graceful-fs';
12
+ import filenamify from 'filenamify';
13
+ import { unflatten } from 'flat';
14
+ import pluralize from 'pluralize';
15
+
16
+ function isManyAndReturnOutputType(name) {
17
+ const lowerName = name.toLowerCase();
18
+ if ((lowerName.startsWith('createmany') || lowerName.startsWith('updatemany')) && (lowerName.endsWith('andreturnoutputtype') || lowerName.endsWith('andreturn'))) {
19
+ return true;
20
+ }
21
+ return false;
22
+ }
23
+
24
+ const {
25
+ camelCase,
26
+ castArray,
27
+ cloneDeep,
28
+ countBy,
29
+ first,
30
+ isEqual,
31
+ isObject,
32
+ kebabCase,
33
+ keyBy,
34
+ last,
35
+ mapKeys,
36
+ memoize,
37
+ merge,
38
+ omit,
39
+ partition,
40
+ remove,
41
+ startCase,
42
+ trim,
43
+ uniq,
44
+ uniqWith
45
+ } = lodash;
46
+ function pascalCase(string) {
47
+ return startCase(camelCase(string)).replaceAll(' ', '');
48
+ }
49
+
50
+ /**
51
+ * See https://github.com/prisma/prisma/blob/master/src/packages/client/src/generation/TSClient/Model.ts@getAggregationTypes
52
+ * Subcribes on: 'ArgsType'
53
+ */
54
+ function argsType(field, args) {
55
+ if (['queryRaw', 'executeRaw'].includes(field.name)) {
56
+ return;
57
+ }
58
+ if (isManyAndReturnOutputType(field.name)) return;
59
+ const {
60
+ eventEmitter,
61
+ getModelName,
62
+ typeNames
63
+ } = args;
64
+ let className = pascalCase(`${field.name}Args`);
65
+ const modelName = getModelName(className) || '';
66
+ switch (className) {
67
+ case `Aggregate${modelName}Args`:
68
+ {
69
+ className = `${modelName}AggregateArgs`;
70
+ break;
71
+ }
72
+ case `GroupBy${modelName}Args`:
73
+ {
74
+ className = `${modelName}GroupByArgs`;
75
+ break;
76
+ }
77
+ }
78
+ const inputType = {
79
+ constraints: {
80
+ maxNumFields: null,
81
+ minNumFields: null
82
+ },
83
+ fields: [...field.args],
84
+ name: className
85
+ };
86
+ if (!field.args.some(x => x.name === '_count') && [`${modelName}AggregateArgs`, `${modelName}GroupByArgs`].includes(className)) {
87
+ const names = ['Count', 'Avg', 'Sum', 'Min', 'Max'];
88
+ if (`${modelName}GroupByArgs` === inputType.name) {
89
+ // Make `by` property array only, noEnumerable
90
+ const byField = inputType.fields.find(f => f.name === 'by');
91
+ if (byField?.inputTypes) {
92
+ byField.inputTypes = byField.inputTypes.filter(inputType => inputType.isList);
93
+ }
94
+ }
95
+ for (const name of names) {
96
+ if (!typeNames.has(`${modelName}${name}AggregateInput`)) {
97
+ continue;
98
+ }
99
+ inputType.fields.push({
100
+ inputTypes: [{
101
+ isList: false,
102
+ location: 'inputObjectTypes',
103
+ type: `${modelName}${name}AggregateInput`
104
+ }],
105
+ isNullable: true,
106
+ isParameterizable: false,
107
+ // ?
108
+ isRequired: false,
109
+ name: `_${name.toLowerCase()}`
110
+ });
111
+ }
112
+ }
113
+ eventEmitter.emitSync('InputType', {
114
+ ...args,
115
+ classDecoratorName: 'ArgsType',
116
+ fileType: 'args',
117
+ inputType
118
+ });
119
+ }
120
+
121
+ const BeforeGenerateField = 'BeforeGenerateField';
122
+
123
+ /**
124
+ * Subscribes on 'BeforeInputType'
125
+ */
126
+ function combineScalarFilters(eventEmitter) {
127
+ eventEmitter.on('BeforeInputType', beforeInputType$2);
128
+ eventEmitter.on(BeforeGenerateField, beforeGenerateField);
129
+ eventEmitter.on('PostBegin', postBegin);
130
+ }
131
+ function beforeInputType$2(args) {
132
+ const {
133
+ inputType,
134
+ removeTypes
135
+ } = args;
136
+ if (isContainBogus(inputType.name) && isScalarFilter(inputType)) {
137
+ removeTypes.add(inputType.name);
138
+ inputType.name = replaceBogus(inputType.name);
139
+ }
140
+ }
141
+ function beforeGenerateField(field) {
142
+ for (const fieldInput of field.inputTypes) {
143
+ if (fieldInput.location !== 'inputObjectTypes') {
144
+ continue;
145
+ }
146
+ const fieldInputType = String(fieldInput.type);
147
+ if (isContainBogus(fieldInputType)) {
148
+ fieldInput.type = replaceBogus(fieldInputType);
149
+ }
150
+ }
151
+ }
152
+ function replaceBogus(name) {
153
+ return name.replaceAll(/Nullable|Nested/g, '');
154
+ }
155
+ function isContainBogus(name) {
156
+ return name.startsWith('Nested') || name.includes('Nullable') && name.endsWith('Filter') || name.endsWith('NullableFilter');
157
+ }
158
+ function isScalarFilter(inputType) {
159
+ if (!inputType.name.endsWith('Filter')) {
160
+ return false;
161
+ }
162
+ let result = false;
163
+ const equals = inputType.fields.find(f => f.name === 'equals');
164
+ if (equals) {
165
+ result = equals.inputTypes.every(x => {
166
+ return ['enumTypes', 'scalar'].includes(x.location);
167
+ });
168
+ }
169
+ return result;
170
+ }
171
+ function postBegin(args) {
172
+ const {
173
+ modelNames,
174
+ schema
175
+ } = args;
176
+ const inputTypes = schema.inputObjectTypes.prisma ?? [];
177
+ const enumTypes = schema.enumTypes.model || [];
178
+ const types = ['Bool', 'Int', 'String', 'DateTime', 'Decimal', 'Float', 'Json', 'Bytes', 'BigInt'];
179
+ for (const enumType of enumTypes) {
180
+ const {
181
+ name
182
+ } = enumType;
183
+ types.push(`Enum${name}`);
184
+ }
185
+ const inputTypeByName = keyBy(inputTypes, inputType => inputType.name);
186
+ const replaceBogusFilters = (filterName, filterNameCandidates) => {
187
+ for (const filterNameCandidate of filterNameCandidates) {
188
+ const candidate = inputTypeByName[filterNameCandidate];
189
+ if (candidate) {
190
+ const inputType = cloneDeep({
191
+ ...candidate,
192
+ name: filterName
193
+ });
194
+ inputTypes.push(inputType);
195
+ inputTypeByName[filterName] = inputType;
196
+ break;
197
+ }
198
+ }
199
+ };
200
+ for (const type of types) {
201
+ // Scalar filters
202
+ replaceBogusFilters(`${type}Filter`, [`${type}NullableFilter`, `Nested${type}NullableFilter`]);
203
+ replaceBogusFilters(`${type}WithAggregatesFilter`, [`${type}NullableWithAggregatesFilter`, `Nested${type}NullableWithAggregatesFilter`]);
204
+ replaceBogusFilters(`${type}ListFilter`, [`${type}NullableListFilter`, `Nested${type}NullableListFilter`]);
205
+ }
206
+ for (const modelName of modelNames) {
207
+ replaceBogusFilters(`${modelName}RelationFilter`, [`${modelName}NullableRelationFilter`]);
208
+ }
209
+ for (const modelName of modelNames) {
210
+ replaceBogusFilters(`${modelName}ScalarRelationFilter`, [`${modelName}NullableScalarRelationFilter`]);
211
+ }
212
+ remove(inputTypes, inputType => {
213
+ return isContainBogus(inputType.name);
214
+ });
215
+ }
216
+
217
+ /**
218
+ * Create aggregate inputs from aggregate outputs.
219
+ * See client/src/generation/TSClient.ts @ getAggregationTypes
220
+ * Subcribes on: 'AggregateOutput'
221
+ */
222
+ function createAggregateInput(args) {
223
+ const {
224
+ eventEmitter,
225
+ outputType
226
+ } = args;
227
+ const className = `${outputType.name}Input`;
228
+
229
+ // console.dir({ outputType, className, __filename }, { depth: 5 });
230
+
231
+ const inputType = {
232
+ constraints: {
233
+ maxNumFields: null,
234
+ minNumFields: null
235
+ },
236
+ fields: outputType.fields.map(x => ({
237
+ inputTypes: [{
238
+ isList: false,
239
+ location: 'scalar',
240
+ type: 'true'
241
+ }],
242
+ isNullable: x.isNullable ?? true,
243
+ isParameterizable: false,
244
+ // ?
245
+ isRequired: false,
246
+ name: x.name
247
+ })),
248
+ name: className
249
+ };
250
+ eventEmitter.emitSync('InputType', {
251
+ ...args,
252
+ classDecoratorName: 'InputType',
253
+ fileType: 'input',
254
+ inputType
255
+ });
256
+ }
257
+
258
+ class ImportDeclarationMap extends Map {
259
+ add(name, value) {
260
+ if (!this.has(name)) {
261
+ if (typeof value === 'string') {
262
+ this.set(name, {
263
+ moduleSpecifier: value,
264
+ namedImports: [{
265
+ name
266
+ }]
267
+ });
268
+ } else {
269
+ this.set(name, value);
270
+ }
271
+ }
272
+ }
273
+ create(args) {
274
+ const {
275
+ config,
276
+ propertySettings,
277
+ propertyType
278
+ } = args;
279
+ if (propertySettings) {
280
+ return this.createFrom({
281
+ ...propertySettings
282
+ });
283
+ }
284
+ if (/\bIdentity</.test(propertyType)) {
285
+ this.add('Identity', {
286
+ isTypeOnly: true,
287
+ moduleSpecifier: 'identity-type',
288
+ namedImports: [{
289
+ name: 'Identity'
290
+ }]
291
+ });
292
+ }
293
+ if ([/\bDecimal\b/, /\bArray<Decimal>\b/].some(re => re.test(propertyType))) {
294
+ // TODO: Deprecated and should be removed
295
+ this.add('Decimal', '@prisma/client-runtime-utils');
296
+ }
297
+ if (/\bPrisma\./.test(propertyType)) {
298
+ this.add('Prisma', config.prismaClientImport);
299
+ }
300
+ }
301
+ createFrom(args) {
302
+ const {
303
+ defaultImport,
304
+ from,
305
+ namedImport,
306
+ namespaceImport
307
+ } = args;
308
+ let name = args.name;
309
+ const value = {
310
+ defaultImport: undefined,
311
+ moduleSpecifier: from,
312
+ namedImports: [],
313
+ namespaceImport: undefined
314
+ };
315
+ if (namedImport === true && namespaceImport) {
316
+ value.namedImports = [{
317
+ name: namespaceImport
318
+ }];
319
+ name = namespaceImport;
320
+ } else if (defaultImport) {
321
+ value.defaultImport = defaultImport === true ? name : defaultImport;
322
+ name = value.defaultImport;
323
+ } else if (namespaceImport) {
324
+ value.namespaceImport = namespaceImport;
325
+ name = namespaceImport;
326
+ } else {
327
+ value.namedImports = [{
328
+ name
329
+ }];
330
+ }
331
+ this.add(name, value);
332
+ }
333
+ *toStatements() {
334
+ const iterator = this.values();
335
+ let result = iterator.next();
336
+ while (result.value) {
337
+ yield {
338
+ ...result.value,
339
+ kind: StructureKind.ImportDeclaration
340
+ };
341
+ result = iterator.next();
342
+ }
343
+ }
344
+ }
345
+
346
+ async function generateFiles(args) {
347
+ const {
348
+ config,
349
+ eventEmitter,
350
+ output,
351
+ project
352
+ } = args;
353
+ if (config.emitSingle) {
354
+ combineToSingle({
355
+ config,
356
+ output,
357
+ project
358
+ });
359
+ }
360
+ if (config.emitCompiled) {
361
+ project.compilerOptions.set({
362
+ declaration: true,
363
+ declarationDir: output,
364
+ emitDecoratorMetadata: false,
365
+ outDir: output,
366
+ rootDir: output,
367
+ skipLibCheck: true
368
+ });
369
+ const emitResult = await project.emit();
370
+ const errors = emitResult.getDiagnostics().map(d => String(d.getMessageText()));
371
+ if (errors.length > 0) {
372
+ eventEmitter.emitSync('Warning', errors);
373
+ }
374
+ } else {
375
+ await project.save();
376
+ }
377
+ }
378
+ function combineToSingle(args) {
379
+ const {
380
+ config,
381
+ output,
382
+ project
383
+ } = args;
384
+ const rootDirectory = project.getDirectory(output) || project.createDirectory(output);
385
+ const sourceFile = rootDirectory.getSourceFile('index.ts') || rootDirectory.createSourceFile('index.ts', undefined, {
386
+ overwrite: true
387
+ });
388
+ const statements = project.getSourceFiles().flatMap(s => {
389
+ if (s === sourceFile) {
390
+ return [];
391
+ }
392
+ const classDeclaration = s.getClass(() => true);
393
+ const statements = s.getStructure().statements;
394
+ // Reget decorator full name
395
+ // TODO: Check possible bug of ts-morph
396
+ if (Array.isArray(statements)) {
397
+ for (const statement of statements) {
398
+ if (!(typeof statement === 'object' && statement.kind === StructureKind.Class)) {
399
+ continue;
400
+ }
401
+ for (const property of statement.properties || []) {
402
+ for (const decorator of property.decorators || []) {
403
+ const fullName = classDeclaration?.getProperty(property.name)?.getDecorator(decorator.name)?.getFullName();
404
+ ok(fullName, `Cannot get full name of decorator of class ${statement.name}`);
405
+ decorator.name = fullName;
406
+ }
407
+ }
408
+ }
409
+ }
410
+ project.removeSourceFile(s);
411
+ return statements;
412
+ });
413
+ const imports = new ImportDeclarationMap();
414
+ const enums = [];
415
+ const classes = [];
416
+ for (const statement of statements) {
417
+ if (typeof statement === 'string') {
418
+ if (statement.startsWith('registerEnumType')) {
419
+ enums.push(statement);
420
+ }
421
+ continue;
422
+ }
423
+ switch (statement.kind) {
424
+ case StructureKind.ImportDeclaration:
425
+ {
426
+ if (statement.moduleSpecifier.startsWith('.')) {
427
+ continue;
428
+ }
429
+ for (const namedImport of statement.namedImports) {
430
+ const name = namedImport.alias || namedImport.name;
431
+ if (statement.moduleSpecifier === 'identity-type') {
432
+ imports.add(name, statement);
433
+ continue;
434
+ }
435
+ imports.add(name, statement.moduleSpecifier);
436
+ }
437
+ if (statement.defaultImport) {
438
+ imports.createFrom({
439
+ defaultImport: statement.defaultImport,
440
+ from: statement.moduleSpecifier,
441
+ name: statement.defaultImport
442
+ });
443
+ }
444
+ if (statement.namespaceImport) {
445
+ imports.createFrom({
446
+ from: statement.moduleSpecifier,
447
+ name: statement.namespaceImport,
448
+ namespaceImport: statement.namespaceImport
449
+ });
450
+ }
451
+ break;
452
+ }
453
+ case StructureKind.Enum:
454
+ {
455
+ enums.unshift(statement);
456
+ break;
457
+ }
458
+ case StructureKind.Class:
459
+ {
460
+ classes.push(statement);
461
+ break;
462
+ }
463
+ }
464
+ }
465
+ for (const customImport of config.customImport) {
466
+ imports.createFrom(customImport);
467
+ }
468
+ sourceFile.set({
469
+ kind: StructureKind.SourceFile,
470
+ statements: [...imports.toStatements(), ...enums, ...classes]
471
+ });
472
+ }
473
+
474
+ const extensions = new Set(['.js', '.mjs', '.ts', '.mts']);
475
+ function adjustModuleSpecifier(moduleSpecifier, importExtension) {
476
+ if (moduleSpecifier.startsWith('.')) {
477
+ let specifierWithoutExtension = moduleSpecifier;
478
+ const extension = path.extname(moduleSpecifier);
479
+ if (extensions.has(extension)) {
480
+ specifierWithoutExtension = moduleSpecifier.slice(0, -extension.length);
481
+ }
482
+ if (!importExtension) return specifierWithoutExtension;
483
+ if (extension !== `.${importExtension}`) {
484
+ return `${specifierWithoutExtension}.${importExtension}`;
485
+ }
486
+ }
487
+ return moduleSpecifier;
488
+ }
489
+
490
+ function fileTypeByLocation(fieldLocation) {
491
+ switch (fieldLocation) {
492
+ case 'inputObjectTypes':
493
+ {
494
+ return 'input';
495
+ }
496
+ case 'outputObjectTypes':
497
+ {
498
+ return 'output';
499
+ }
500
+ case 'enumTypes':
501
+ {
502
+ return 'enum';
503
+ }
504
+ }
505
+ return 'object';
506
+ }
507
+
508
+ function relativePath(from, to) {
509
+ if (!from.startsWith('/')) {
510
+ from = `/${from}`;
511
+ }
512
+ if (!to.startsWith('/')) {
513
+ to = `/${to}`;
514
+ }
515
+ let result = getRelativePath(from, to);
516
+ if (!result.startsWith('.')) {
517
+ result = `./${result}`;
518
+ }
519
+ return result;
520
+ }
521
+
522
+ function getGraphqlImport(args) {
523
+ const {
524
+ config,
525
+ fileType,
526
+ getSourceFile,
527
+ isId,
528
+ location,
529
+ noTypeId,
530
+ sourceFile,
531
+ typeName
532
+ } = args;
533
+ const {
534
+ graphqlScalars,
535
+ importExtension
536
+ } = config;
537
+ if (location === 'scalar') {
538
+ if (isId && !noTypeId) {
539
+ return {
540
+ name: 'ID',
541
+ specifier: '@nestjs/graphql'
542
+ };
543
+ }
544
+ const graphqlType = graphqlScalars[typeName];
545
+ if (graphqlType) {
546
+ return {
547
+ name: graphqlType.name,
548
+ specifier: graphqlType.specifier
549
+ };
550
+ }
551
+ switch (typeName) {
552
+ case 'Float':
553
+ case 'Int':
554
+ {
555
+ return {
556
+ name: typeName,
557
+ specifier: '@nestjs/graphql'
558
+ };
559
+ }
560
+ case 'DateTime':
561
+ {
562
+ return {
563
+ name: 'Date',
564
+ specifier: undefined
565
+ };
566
+ }
567
+ case 'true':
568
+ case 'Boolean':
569
+ {
570
+ return {
571
+ name: 'Boolean',
572
+ specifier: undefined
573
+ };
574
+ }
575
+ case 'Decimal':
576
+ {
577
+ return {
578
+ name: 'GraphQLDecimal',
579
+ specifier: 'prisma-graphql-type-decimal'
580
+ };
581
+ }
582
+ case 'Json':
583
+ {
584
+ return {
585
+ name: 'GraphQLJSON',
586
+ specifier: 'graphql-type-json'
587
+ };
588
+ }
589
+ }
590
+ return {
591
+ name: 'String',
592
+ specifier: undefined
593
+ };
594
+ }
595
+ let sourceFileType = fileTypeByLocation(location);
596
+ if (sourceFileType === 'output' && fileType === 'model') {
597
+ sourceFileType = 'model';
598
+ }
599
+ const specifier = adjustModuleSpecifier(relativePath(sourceFile.getFilePath(), getSourceFile({
600
+ name: typeName,
601
+ type: sourceFileType
602
+ }).getFilePath()), importExtension);
603
+ return {
604
+ name: typeName,
605
+ specifier
606
+ };
607
+ }
608
+
609
+ /**
610
+ * Find input type for graphql field decorator.
611
+ */
612
+ function getGraphqlInputType(inputTypes, pattern) {
613
+ let result;
614
+ inputTypes = inputTypes.filter(t => !['null', 'Null'].includes(String(t.type)));
615
+ inputTypes = uniqWith(inputTypes, isEqual);
616
+ if (inputTypes.length === 1) {
617
+ return inputTypes[0];
618
+ }
619
+ const countTypes = countBy(inputTypes, x => x.location);
620
+ const isOneType = Object.keys(countTypes).length === 1;
621
+ if (isOneType) {
622
+ result = inputTypes.find(x => x.isList);
623
+ if (result) {
624
+ return result;
625
+ }
626
+ }
627
+ if (pattern) {
628
+ if (pattern.startsWith('matcher:') || pattern.startsWith('match:')) {
629
+ const {
630
+ 1: patternValue
631
+ } = pattern.split(':', 2);
632
+ const isMatch = outmatch(patternValue, {
633
+ separator: false
634
+ });
635
+ result = inputTypes.find(x => isMatch(String(x.type)));
636
+ if (result) {
637
+ return result;
638
+ }
639
+ }
640
+ result = inputTypes.find(x => String(x.type).includes(pattern));
641
+ if (result) {
642
+ return result;
643
+ }
644
+ }
645
+ result = inputTypes.find(x => x.location === 'inputObjectTypes');
646
+ if (result) {
647
+ return result;
648
+ }
649
+ if (countTypes.enumTypes && countTypes.scalar && inputTypes.some(x => x.type === 'Json' && x.location === 'scalar')) {
650
+ result = inputTypes.find(x => x.type === 'Json' && x.location === 'scalar');
651
+ if (result) {
652
+ return result;
653
+ }
654
+ }
655
+ if ((countTypes.scalar >= 1 || countTypes.enumTypes >= 1) && countTypes.fieldRefTypes === 1) {
656
+ result = inputTypes.find(x => (x.location === 'scalar' || x.location === 'enumTypes') && x.isList);
657
+ if (result) {
658
+ return result;
659
+ }
660
+ result = inputTypes.find(x => x.location === 'scalar' || x.location === 'enumTypes');
661
+ if (result) {
662
+ return result;
663
+ }
664
+ }
665
+ throw new TypeError(`Cannot get matching input type from ${inputTypes.map(x => x.type).join(', ') || 'zero length inputTypes'}`);
666
+ }
667
+
668
+ /**
669
+ * Returns typescript property type.
670
+ */
671
+ function getPropertyType(args) {
672
+ const {
673
+ location,
674
+ type
675
+ } = args;
676
+ switch (type) {
677
+ case 'Float':
678
+ case 'Int':
679
+ {
680
+ return ['number'];
681
+ }
682
+ case 'String':
683
+ {
684
+ return ['string'];
685
+ }
686
+ case 'Boolean':
687
+ {
688
+ return ['boolean'];
689
+ }
690
+ case 'DateTime':
691
+ {
692
+ return ['Date', 'string'];
693
+ }
694
+ case 'Decimal':
695
+ {
696
+ return ['Decimal']; // TODO: Use Prisma.Decimal
697
+ }
698
+ case 'Json':
699
+ {
700
+ return ['any'];
701
+ }
702
+ case 'Null':
703
+ {
704
+ return ['null'];
705
+ }
706
+ case 'Bytes':
707
+ {
708
+ return ['Prisma.Bytes'];
709
+ }
710
+ case 'BigInt':
711
+ {
712
+ return ['bigint', 'number'];
713
+ }
714
+ }
715
+ if (['inputObjectTypes', 'outputObjectTypes'].includes(location)) {
716
+ return [type];
717
+ }
718
+ if (location === 'enumTypes') {
719
+ const enumType = '`${' + type + '}`';
720
+ return [enumType];
721
+ }
722
+ if (location === 'scalar') {
723
+ return [type];
724
+ }
725
+ return ['unknown'];
726
+ }
727
+
728
+ function getWhereUniqueAtLeastKeys(model) {
729
+ const names = model.fields.filter(field => field.isUnique || field.isId).map(field => field.name);
730
+ if (model.primaryKey) {
731
+ names.push(createFieldName(model.primaryKey));
732
+ }
733
+ for (const uniqueIndex of model.uniqueIndexes) {
734
+ names.push(createFieldName(uniqueIndex));
735
+ }
736
+ return names;
737
+ }
738
+ function createFieldName(args) {
739
+ const {
740
+ fields,
741
+ name
742
+ } = args;
743
+ return name || fields.join('_');
744
+ }
745
+
746
+ function isWhereUniqueInputType(name) {
747
+ return name.endsWith('WhereUniqueInput');
748
+ }
749
+
750
+ /**
751
+ * Get property structure (field) for class.
752
+ */
753
+ function propertyStructure(args) {
754
+ const {
755
+ hasExclamationToken,
756
+ hasQuestionToken,
757
+ isList,
758
+ isNullable,
759
+ location,
760
+ name,
761
+ propertyType
762
+ } = args;
763
+ const type = createProperyType({
764
+ isList,
765
+ location,
766
+ propertyType
767
+ });
768
+ return {
769
+ decorators: [],
770
+ hasExclamationToken: hasExclamationToken ?? !isNullable,
771
+ hasQuestionToken: hasQuestionToken ?? isNullable,
772
+ kind: StructureKind.Property,
773
+ leadingTrivia: '\n',
774
+ name,
775
+ type
776
+ };
777
+ }
778
+ function createProperyType(args) {
779
+ const {
780
+ isList,
781
+ location,
782
+ propertyType
783
+ } = args;
784
+ return propertyType.map(type => {
785
+ if (isList) return `Array<${type}>`;
786
+ if (type.startsWith('Prisma.')) return type;
787
+ if (type === 'null') return type;
788
+ if (['inputObjectTypes', 'outputObjectTypes'].includes(location)) {
789
+ return `Identity<${type}>`;
790
+ }
791
+ return type;
792
+ }).join(' | ');
793
+ }
794
+
795
+ function inputType(args) {
796
+ const {
797
+ classDecoratorName,
798
+ classTransformerTypeModels,
799
+ config,
800
+ eventEmitter,
801
+ fieldSettings,
802
+ fileType,
803
+ getModelName,
804
+ getSourceFile,
805
+ inputType,
806
+ models,
807
+ removeTypes,
808
+ typeNames
809
+ } = args;
810
+ typeNames.add(inputType.name);
811
+ const importDeclarations = new ImportDeclarationMap();
812
+ const sourceFile = getSourceFile({
813
+ name: inputType.name,
814
+ type: fileType
815
+ });
816
+ const classStructure = {
817
+ decorators: [{
818
+ arguments: [],
819
+ name: classDecoratorName
820
+ }],
821
+ isExported: true,
822
+ kind: StructureKind.Class,
823
+ name: inputType.name,
824
+ properties: []
825
+ };
826
+ const modelName = getModelName(inputType.name) || '';
827
+ const model = models.get(modelName);
828
+ const modelFieldSettings = model && fieldSettings.get(model.name);
829
+ const moduleSpecifier = '@nestjs/graphql';
830
+ importDeclarations.set('Field', {
831
+ moduleSpecifier,
832
+ namedImports: [{
833
+ name: 'Field'
834
+ }]
835
+ }).set(classDecoratorName, {
836
+ moduleSpecifier,
837
+ namedImports: [{
838
+ name: classDecoratorName
839
+ }]
840
+ });
841
+ const useInputType = config.useInputType.find(x => inputType.name.includes(x.typeName));
842
+ const isWhereUnique = isWhereUniqueInputType(inputType.name);
843
+
844
+ // if (inputType.name.includes('DecimalNullableFilter')) {
845
+ // console.log({ importDeclarations, location, property, propertyType });
846
+ // }
847
+
848
+ for (const field of inputType.fields) {
849
+ field.inputTypes = field.inputTypes.filter(t => !removeTypes.has(String(t.type)));
850
+ eventEmitter.emitSync(BeforeGenerateField, field, args);
851
+ const {
852
+ inputTypes,
853
+ isRequired,
854
+ name
855
+ } = field;
856
+ if (inputTypes.length === 0) {
857
+ // No types
858
+ continue;
859
+ }
860
+ const usePattern = useInputType?.ALL || useInputType?.[name];
861
+ const graphqlInputType = getGraphqlInputType(inputTypes, usePattern);
862
+ const {
863
+ isList,
864
+ location,
865
+ type
866
+ } = graphqlInputType;
867
+ const typeName = String(type);
868
+ const settings = modelFieldSettings?.get(name);
869
+ const propertySettings = settings?.getPropertyType({
870
+ input: true,
871
+ name: inputType.name
872
+ });
873
+ const modelField = model?.fields.find(f => f.name === name);
874
+ const isCustomsApplicable = typeName === modelField?.type;
875
+ const atLeastKeys = model && getWhereUniqueAtLeastKeys(model);
876
+ const whereUniqueInputType = isWhereUniqueInputType(typeName) && atLeastKeys && `Prisma.AtLeast<${typeName}, ${atLeastKeys.map(name => `'${name}'`).join(' | ')}>`;
877
+ const propertyType = castArray(propertySettings?.name || whereUniqueInputType || getPropertyType({
878
+ location,
879
+ type: typeName
880
+ }));
881
+ const hasExclamationToken = Boolean(isWhereUnique && config.unsafeCompatibleWhereUniqueInput && atLeastKeys?.includes(name));
882
+ const property = propertyStructure({
883
+ hasExclamationToken: hasExclamationToken || undefined,
884
+ hasQuestionToken: hasExclamationToken ? false : undefined,
885
+ isList,
886
+ isNullable: !isRequired,
887
+ location,
888
+ name,
889
+ propertyType
890
+ });
891
+ classStructure.properties.push(property);
892
+ importDeclarations.create({
893
+ config,
894
+ propertySettings,
895
+ propertyType: property.type
896
+ });
897
+
898
+ // Get graphql type
899
+ let graphqlType;
900
+ const shouldHideField = settings?.shouldHideField({
901
+ input: true,
902
+ name: inputType.name
903
+ }) || config.decorate.some(d => d.name === 'HideField' && d.from === moduleSpecifier && d.isMatchField(name) && d.isMatchType(inputType.name));
904
+ const fieldType = settings?.getFieldType({
905
+ input: true,
906
+ name: inputType.name
907
+ });
908
+ if (fieldType && isCustomsApplicable && !shouldHideField) {
909
+ graphqlType = fieldType.name;
910
+ importDeclarations.createFrom({
911
+ ...fieldType
912
+ });
913
+ } else {
914
+ // Import property type class
915
+ const graphqlImport = getGraphqlImport({
916
+ config,
917
+ getSourceFile,
918
+ location,
919
+ sourceFile,
920
+ typeName
921
+ });
922
+ graphqlType = graphqlImport.name;
923
+ let referenceName = propertyType[0];
924
+ if (location === 'enumTypes') {
925
+ referenceName = last(referenceName.split(' '));
926
+ }
927
+ if (graphqlImport.specifier && !importDeclarations.has(graphqlImport.name) && graphqlImport.name !== inputType.name
928
+ // ((graphqlImport.name !== inputType.name && !shouldHideField) ||
929
+ // (shouldHideField && referenceName === graphqlImport.name))
930
+ ) {
931
+ importDeclarations.set(graphqlImport.name, {
932
+ moduleSpecifier: graphqlImport.specifier,
933
+ namedImports: [{
934
+ name: graphqlImport.name
935
+ }]
936
+ });
937
+ }
938
+ }
939
+ ok$1(property.decorators, 'property.decorators is undefined');
940
+ if (shouldHideField) {
941
+ importDeclarations.add('HideField', moduleSpecifier);
942
+ property.decorators.push({
943
+ arguments: [],
944
+ name: 'HideField'
945
+ });
946
+ } else {
947
+ // Generate `@Field()` decorator
948
+ property.decorators.push({
949
+ arguments: [isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`, JSON5.stringify({
950
+ ...settings?.fieldArguments(),
951
+ nullable: !isRequired
952
+ })],
953
+ name: 'Field'
954
+ });
955
+ if (graphqlType === 'GraphQLDecimal') {
956
+ importDeclarations.add('transformToDecimal', 'prisma-graphql-type-decimal');
957
+ importDeclarations.add('Transform', 'class-transformer');
958
+ importDeclarations.add('Type', 'class-transformer');
959
+ property.decorators.push({
960
+ arguments: ['() => Object'],
961
+ name: 'Type'
962
+ }, {
963
+ arguments: ['transformToDecimal'],
964
+ name: 'Transform'
965
+ });
966
+ } else if (location === 'inputObjectTypes' && (modelField?.type === 'Decimal' || ['connect', 'connectOrCreate', 'create', 'createMany', 'data', 'delete', 'deleteMany', 'disconnect', 'set', 'update', 'updateMany', 'upsert', 'where'].includes(name) || classTransformerTypeModels.has(getModelName(graphqlType) || '') || modelField?.kind === 'object' && models.get(modelField.type) && models.get(modelField.type)?.fields.some(field => field.kind === 'object' && classTransformerTypeModels.has(field.type)))) {
967
+ importDeclarations.add('Type', 'class-transformer');
968
+ property.decorators.push({
969
+ arguments: [`() => ${graphqlType}`],
970
+ name: 'Type'
971
+ });
972
+ }
973
+ if (isCustomsApplicable) {
974
+ for (const options of settings || []) {
975
+ if ((options.kind === 'Decorator' && options.input && options.match?.(name)) ?? true) {
976
+ property.decorators.push({
977
+ arguments: options.arguments,
978
+ name: options.name
979
+ });
980
+ ok$1(options.from, "Missed 'from' part in configuration or field setting");
981
+ importDeclarations.createFrom(options);
982
+ }
983
+ }
984
+ }
985
+ for (const decorate of config.decorate) {
986
+ if (decorate.isMatchField(name) && decorate.isMatchType(inputType.name)) {
987
+ property.decorators.push({
988
+ arguments: decorate.arguments?.map(x => pupa(x, {
989
+ propertyType
990
+ })),
991
+ name: decorate.name
992
+ });
993
+ importDeclarations.createFrom(decorate);
994
+ }
995
+ }
996
+ }
997
+ eventEmitter.emitSync('ClassProperty', property, {
998
+ isList,
999
+ location,
1000
+ propertyType
1001
+ });
1002
+ }
1003
+ sourceFile.set({
1004
+ statements: [...importDeclarations.toStatements(), classStructure]
1005
+ });
1006
+ }
1007
+
1008
+ class ObjectSettings extends Array {
1009
+ shouldHideField({
1010
+ input = false,
1011
+ name,
1012
+ output = false
1013
+ }) {
1014
+ const hideField = this.find(s => s.name === 'HideField');
1015
+ return Boolean(hideField?.input && input || hideField?.output && output || hideField?.match?.(name));
1016
+ }
1017
+ getFieldType({
1018
+ input,
1019
+ name,
1020
+ output
1021
+ }) {
1022
+ const fieldType = this.find(s => s.kind === 'FieldType');
1023
+ if (!fieldType) {
1024
+ return undefined;
1025
+ }
1026
+ if (fieldType.match) {
1027
+ return fieldType.match(name) ? fieldType : undefined;
1028
+ }
1029
+ if (input && !fieldType.input) {
1030
+ return undefined;
1031
+ }
1032
+ if (output && !fieldType.output) {
1033
+ return undefined;
1034
+ }
1035
+ return fieldType;
1036
+ }
1037
+ getPropertyType({
1038
+ input,
1039
+ name,
1040
+ output
1041
+ }) {
1042
+ const propertyType = this.find(s => s.kind === 'PropertyType');
1043
+ if (!propertyType) {
1044
+ return undefined;
1045
+ }
1046
+ if (propertyType.match) {
1047
+ return propertyType.match(name) ? propertyType : undefined;
1048
+ }
1049
+ if (input && !propertyType.input) {
1050
+ return undefined;
1051
+ }
1052
+ if (output && !propertyType.output) {
1053
+ return undefined;
1054
+ }
1055
+ return propertyType;
1056
+ }
1057
+ getObjectTypeArguments(options) {
1058
+ const objectTypeOptions = merge({}, options);
1059
+ const resultArguments = [objectTypeOptions];
1060
+ const objectType = this.find(s => s.kind === 'ObjectType');
1061
+ if (objectType && isObject(objectType.arguments)) {
1062
+ const name = objectType.arguments.name;
1063
+ merge(objectTypeOptions, omit(objectType.arguments, 'name'));
1064
+ if (name) {
1065
+ resultArguments.unshift(name);
1066
+ }
1067
+ }
1068
+ return resultArguments.map(x => JSON5.stringify(x));
1069
+ }
1070
+ fieldArguments() {
1071
+ const item = this.find(item => item.kind === 'Field');
1072
+ if (item) {
1073
+ return item.arguments;
1074
+ }
1075
+ }
1076
+ }
1077
+ function createObjectSettings(args) {
1078
+ const {
1079
+ config,
1080
+ text
1081
+ } = args;
1082
+ const result = new ObjectSettings();
1083
+ const textLines = text.split('\n');
1084
+ const documentationLines = [];
1085
+ let fieldElement = result.find(item => item.kind === 'Field');
1086
+ if (!fieldElement) {
1087
+ fieldElement = {
1088
+ arguments: {},
1089
+ kind: 'Field',
1090
+ name: ''
1091
+ };
1092
+ }
1093
+ for (const line of textLines) {
1094
+ const match = /^@(?<name>\w+(\.(\w+))?)\((?<args>.*)\)/.exec(line);
1095
+ const {
1096
+ documentLine,
1097
+ element
1098
+ } = createSettingElement({
1099
+ config,
1100
+ fieldElement,
1101
+ line,
1102
+ match
1103
+ });
1104
+ if (element) {
1105
+ result.push(element);
1106
+ }
1107
+ if (documentLine) {
1108
+ documentationLines.push(line);
1109
+ }
1110
+ }
1111
+ return {
1112
+ documentation: documentationLines.filter(Boolean).join('\n') || undefined,
1113
+ settings: result
1114
+ };
1115
+ }
1116
+ function createSettingElement({
1117
+ config,
1118
+ fieldElement,
1119
+ line,
1120
+ match
1121
+ }) {
1122
+ const result = {
1123
+ documentLine: '',
1124
+ element: undefined
1125
+ };
1126
+ if (line.startsWith('@deprecated')) {
1127
+ fieldElement.arguments['deprecationReason'] = trim(line.slice(11));
1128
+ result.element = fieldElement;
1129
+ return result;
1130
+ }
1131
+ if (line.startsWith('@complexity')) {
1132
+ let n = Number.parseInt(trim(line.slice(11)));
1133
+ if (n !== n || n < 1) n = 1;
1134
+ fieldElement.arguments['complexity'] = n;
1135
+ result.element = fieldElement;
1136
+ return result;
1137
+ }
1138
+ const name = match?.groups?.name;
1139
+ if (!(match && name)) {
1140
+ result.documentLine = line;
1141
+ return result;
1142
+ }
1143
+ const element = {
1144
+ arguments: [],
1145
+ from: '',
1146
+ input: false,
1147
+ kind: 'Decorator',
1148
+ model: false,
1149
+ name: '',
1150
+ output: false
1151
+ };
1152
+ result.element = element;
1153
+ if (name === 'TypeGraphQL.omit' || name === 'HideField') {
1154
+ Object.assign(element, hideFieldDecorator(match));
1155
+ return result;
1156
+ }
1157
+ if (['FieldType', 'PropertyType'].includes(name) && match.groups?.args) {
1158
+ const options = customType(match.groups.args);
1159
+ merge(element, options.namespace && config.fields[options.namespace], options, {
1160
+ kind: name
1161
+ });
1162
+ return result;
1163
+ }
1164
+ if (name === 'ObjectType' && match.groups?.args) {
1165
+ element.kind = 'ObjectType';
1166
+ const options = customType(match.groups.args);
1167
+ if (typeof options[0] === 'string' && options[0]) {
1168
+ options.name = options[0];
1169
+ }
1170
+ if (isObject(options[1])) {
1171
+ merge(options, options[1]);
1172
+ }
1173
+ element.arguments = {
1174
+ isAbstract: options.isAbstract,
1175
+ name: options.name
1176
+ };
1177
+ return result;
1178
+ }
1179
+ if (name === 'Directive' && match.groups?.args) {
1180
+ const options = customType(match.groups.args);
1181
+ merge(element, {
1182
+ from: '@nestjs/graphql',
1183
+ model: true
1184
+ }, options, {
1185
+ arguments: Array.isArray(options.arguments) ? options.arguments.map(s => JSON5.stringify(s)) : options.arguments,
1186
+ kind: 'Decorator',
1187
+ name,
1188
+ namespace: false
1189
+ });
1190
+ return result;
1191
+ }
1192
+ const namespace = getNamespace(name);
1193
+ element.namespaceImport = namespace;
1194
+ const options = {
1195
+ arguments: (match.groups?.args || '').split(',').map(s => trim(s)).filter(Boolean),
1196
+ name
1197
+ };
1198
+ merge(element, namespace && config.fields[namespace], options);
1199
+ return result;
1200
+ }
1201
+ function customType(args) {
1202
+ const result = {};
1203
+ let options = parseArgs(args);
1204
+ if (typeof options === 'string') {
1205
+ options = {
1206
+ name: options
1207
+ };
1208
+ }
1209
+ Object.assign(result, options);
1210
+ const namespace = getNamespace(options.name);
1211
+ result.namespace = namespace;
1212
+ if (options.name?.includes('.')) {
1213
+ result.namespaceImport = namespace;
1214
+ }
1215
+ if (typeof options.match === 'string' || Array.isArray(options.match)) {
1216
+ result.match = outmatch(options.match, {
1217
+ separator: false
1218
+ });
1219
+ }
1220
+ return result;
1221
+ }
1222
+ function hideFieldDecorator(match) {
1223
+ const result = {
1224
+ arguments: [],
1225
+ defaultImport: undefined,
1226
+ from: '@nestjs/graphql',
1227
+ match: undefined,
1228
+ name: 'HideField',
1229
+ namespaceImport: undefined
1230
+ };
1231
+ if (!match.groups?.args) {
1232
+ result.output = true;
1233
+ return result;
1234
+ }
1235
+ if (match.groups.args.includes('{') && match.groups.args.includes('}')) {
1236
+ const options = parseArgs(match.groups.args);
1237
+ result.output = Boolean(options.output);
1238
+ result.input = Boolean(options.input);
1239
+ if (typeof options.match === 'string' || Array.isArray(options.match)) {
1240
+ result.match = outmatch(options.match, {
1241
+ separator: false
1242
+ });
1243
+ }
1244
+ } else {
1245
+ if (/output:\s*true/.test(match.groups.args)) {
1246
+ result.output = true;
1247
+ }
1248
+ if (/input:\s*true/.test(match.groups.args)) {
1249
+ result.input = true;
1250
+ }
1251
+ }
1252
+ return result;
1253
+ }
1254
+ function parseArgs(string) {
1255
+ try {
1256
+ return JSON5.parse(string);
1257
+ } catch {
1258
+ try {
1259
+ return JSON5.parse(`[${string}]`);
1260
+ } catch {
1261
+ throw new Error(`Failed to parse: ${string}`);
1262
+ }
1263
+ }
1264
+ }
1265
+ function getNamespace(name) {
1266
+ if (name === undefined) {
1267
+ return undefined;
1268
+ }
1269
+ let result = String(name);
1270
+ if (result.includes('.')) {
1271
+ [result] = result.split('.');
1272
+ }
1273
+ return result;
1274
+ }
1275
+
1276
+ function modelData(model, args) {
1277
+ const {
1278
+ classTransformerTypeModels,
1279
+ config,
1280
+ fieldSettings,
1281
+ modelFields,
1282
+ modelNames,
1283
+ models
1284
+ } = args;
1285
+ modelNames.push(model.name);
1286
+ models.set(model.name, model);
1287
+ const modelFieldsValue = new Map();
1288
+ modelFields.set(model.name, modelFieldsValue);
1289
+ const fieldSettingsValue = new Map();
1290
+ fieldSettings.set(model.name, fieldSettingsValue);
1291
+ for (const field of model.fields) {
1292
+ if (field.documentation) {
1293
+ const {
1294
+ documentation,
1295
+ settings
1296
+ } = createObjectSettings({
1297
+ config,
1298
+ text: field.documentation
1299
+ });
1300
+ field.documentation = documentation;
1301
+ fieldSettingsValue.set(field.name, settings);
1302
+ }
1303
+ modelFieldsValue.set(field.name, field);
1304
+ }
1305
+ if (model.fields.some(field => field.type === 'Decimal')) {
1306
+ classTransformerTypeModels.add(model.name);
1307
+ }
1308
+ }
1309
+
1310
+ function createComment(documentation, settings) {
1311
+ const documentationLines = documentation.split('\n');
1312
+ const commentLines = ['/**'];
1313
+ for (const line of documentationLines) {
1314
+ commentLines.push(` * ${line}`);
1315
+ }
1316
+ const deprecationReason = settings?.fieldArguments()?.deprecationReason;
1317
+ if (deprecationReason) {
1318
+ commentLines.push(` * @deprecated ${deprecationReason}`);
1319
+ }
1320
+ commentLines.push(' */\n');
1321
+ return commentLines.join('\n');
1322
+ }
1323
+
1324
+ function getOutputTypeName(name) {
1325
+ return name.replace(/(?:OutputType|Output)$/, '');
1326
+ }
1327
+
1328
+ const nestjsGraphql$1 = '@nestjs/graphql';
1329
+ function modelOutputType(outputType, args) {
1330
+ const {
1331
+ config,
1332
+ eventEmitter,
1333
+ fieldSettings,
1334
+ getSourceFile,
1335
+ modelFields,
1336
+ models
1337
+ } = args;
1338
+ if (isManyAndReturnOutputType(outputType.name)) return;
1339
+ const model = models.get(outputType.name);
1340
+ ok$1(model, `Cannot find model by name ${outputType.name}`);
1341
+ const sourceFile = getSourceFile({
1342
+ name: outputType.name,
1343
+ type: 'model'
1344
+ });
1345
+ const sourceFileStructure = sourceFile.getStructure();
1346
+ const exportDeclaration = getExportDeclaration$1(model.name, sourceFileStructure.statements);
1347
+ const importDeclarations = new ImportDeclarationMap();
1348
+ const classStructure = {
1349
+ decorators: [{
1350
+ arguments: [],
1351
+ name: 'ObjectType'
1352
+ }],
1353
+ isExported: true,
1354
+ kind: StructureKind.Class,
1355
+ name: outputType.name,
1356
+ properties: []
1357
+ };
1358
+ sourceFileStructure.statements.push(classStructure);
1359
+ ok$1(classStructure.decorators, 'classStructure.decorators is undefined');
1360
+ const decorator = classStructure.decorators.find(d => d.name === 'ObjectType');
1361
+ ok$1(decorator, 'ObjectType decorator not found');
1362
+ let modelSettings;
1363
+ // Get model settings from documentation
1364
+ if (model.documentation) {
1365
+ const objectTypeOptions = {};
1366
+ const {
1367
+ documentation,
1368
+ settings
1369
+ } = createObjectSettings({
1370
+ config,
1371
+ text: model.documentation
1372
+ });
1373
+ if (documentation) {
1374
+ if (!classStructure.leadingTrivia) {
1375
+ classStructure.leadingTrivia = createComment(documentation);
1376
+ }
1377
+ objectTypeOptions.description = documentation;
1378
+ }
1379
+ decorator.arguments = settings.getObjectTypeArguments(objectTypeOptions);
1380
+ modelSettings = settings;
1381
+ }
1382
+ importDeclarations.add('Field', nestjsGraphql$1);
1383
+ importDeclarations.add('ObjectType', nestjsGraphql$1);
1384
+ for (const field of outputType.fields) {
1385
+ if (config.omitModelsCount && field.name === '_count') continue;
1386
+ let fileType = 'model';
1387
+ const {
1388
+ isList,
1389
+ location,
1390
+ namespace,
1391
+ type
1392
+ } = field.outputType;
1393
+ let outputTypeName = String(type);
1394
+ if (namespace !== 'model') {
1395
+ fileType = 'output';
1396
+ outputTypeName = getOutputTypeName(outputTypeName);
1397
+ }
1398
+ const modelField = modelFields.get(model.name)?.get(field.name);
1399
+ const settings = fieldSettings.get(model.name)?.get(field.name);
1400
+ const fieldType = settings?.getFieldType({
1401
+ name: outputType.name,
1402
+ output: true
1403
+ });
1404
+ const propertySettings = settings?.getPropertyType({
1405
+ name: outputType.name,
1406
+ output: true
1407
+ });
1408
+ const propertyType = castArray(propertySettings?.name || getPropertyType({
1409
+ location,
1410
+ type: outputTypeName
1411
+ }));
1412
+
1413
+ // For model we keep only one type
1414
+ propertyType.splice(1);
1415
+ if (field.isNullable && !isList) {
1416
+ propertyType.push('null');
1417
+ }
1418
+ let graphqlType;
1419
+ if (fieldType) {
1420
+ graphqlType = fieldType.name;
1421
+ importDeclarations.createFrom({
1422
+ ...fieldType
1423
+ });
1424
+ } else {
1425
+ const graphqlImport = getGraphqlImport({
1426
+ config,
1427
+ fileType,
1428
+ getSourceFile,
1429
+ isId: modelField?.isId,
1430
+ location,
1431
+ noTypeId: config.noTypeId,
1432
+ sourceFile,
1433
+ typeName: outputTypeName
1434
+ });
1435
+ graphqlType = graphqlImport.name;
1436
+ if (graphqlImport.name !== outputType.name && graphqlImport.specifier) {
1437
+ importDeclarations.add(graphqlImport.name, graphqlImport.specifier);
1438
+ }
1439
+ }
1440
+ const property = propertyStructure({
1441
+ hasExclamationToken: true,
1442
+ hasQuestionToken: location === 'outputObjectTypes',
1443
+ isList,
1444
+ isNullable: field.isNullable,
1445
+ location,
1446
+ name: field.name,
1447
+ propertyType
1448
+ });
1449
+ if (typeof property.leadingTrivia === 'string' && modelField?.documentation) {
1450
+ property.leadingTrivia += createComment(modelField.documentation, settings);
1451
+ }
1452
+ classStructure.properties?.push(property);
1453
+ importDeclarations.create({
1454
+ config,
1455
+ propertySettings,
1456
+ propertyType: property.type
1457
+ });
1458
+ ok$1(property.decorators, 'property.decorators is undefined');
1459
+ const shouldHideField = settings?.shouldHideField({
1460
+ name: outputType.name,
1461
+ output: true
1462
+ }) || config.decorate.some(d => d.name === 'HideField' && d.from === '@nestjs/graphql' && d.isMatchField(field.name) && d.isMatchType(outputTypeName));
1463
+ if (shouldHideField) {
1464
+ importDeclarations.add('HideField', nestjsGraphql$1);
1465
+ property.decorators.push({
1466
+ arguments: [],
1467
+ name: 'HideField'
1468
+ });
1469
+ } else {
1470
+ // Generate `@Field()` decorator
1471
+ property.decorators.push({
1472
+ arguments: [isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`, JSON5.stringify({
1473
+ ...settings?.fieldArguments(),
1474
+ defaultValue: ['number', 'string', 'boolean'].includes(typeof modelField?.default) ? modelField?.default : undefined,
1475
+ description: modelField?.documentation,
1476
+ nullable: Boolean(field.isNullable)
1477
+ })],
1478
+ name: 'Field'
1479
+ });
1480
+ for (const setting of settings || []) {
1481
+ if (shouldBeDecorated(setting) && (setting.match?.(field.name) ?? true)) {
1482
+ property.decorators.push({
1483
+ arguments: setting.arguments,
1484
+ name: setting.name
1485
+ });
1486
+ ok$1(setting.from, "Missed 'from' part in configuration or field setting");
1487
+ importDeclarations.createFrom(setting);
1488
+ }
1489
+ }
1490
+ for (const decorate of config.decorate) {
1491
+ if (decorate.isMatchField(field.name) && decorate.isMatchType(outputTypeName)) {
1492
+ property.decorators.push({
1493
+ arguments: decorate.arguments?.map(x => pupa(x, {
1494
+ propertyType
1495
+ })),
1496
+ name: decorate.name
1497
+ });
1498
+ importDeclarations.createFrom(decorate);
1499
+ }
1500
+ }
1501
+ }
1502
+ eventEmitter.emitSync('ClassProperty', property, {
1503
+ isList,
1504
+ location,
1505
+ propertyType
1506
+ });
1507
+ }
1508
+
1509
+ // Generate class decorators from model settings
1510
+ for (const setting of modelSettings || []) {
1511
+ if (shouldBeDecorated(setting)) {
1512
+ classStructure.decorators.push({
1513
+ arguments: setting.arguments,
1514
+ name: setting.name
1515
+ });
1516
+ importDeclarations.createFrom(setting);
1517
+ }
1518
+ }
1519
+ if (exportDeclaration) {
1520
+ sourceFile.set({
1521
+ statements: [exportDeclaration, '\n', classStructure]
1522
+ });
1523
+ const classDeclaration = sourceFile.getClassOrThrow(model.name);
1524
+ const commentedText = classDeclaration.getText().split('\n').map(x => `// ${x}`);
1525
+ classDeclaration.remove();
1526
+ sourceFile.addStatements(['\n', ...commentedText]);
1527
+ } else {
1528
+ sourceFile.set({
1529
+ statements: [...importDeclarations.toStatements(), classStructure]
1530
+ });
1531
+ }
1532
+ }
1533
+ function shouldBeDecorated(setting) {
1534
+ return setting.kind === 'Decorator' && (setting.output || setting.model) && !(setting.output && setting.model);
1535
+ }
1536
+ function getExportDeclaration$1(name, statements) {
1537
+ return statements.find(structure => {
1538
+ return structure.kind === StructureKind.ExportDeclaration && structure.namedExports.some(o => (o.alias || o.name) === name);
1539
+ });
1540
+ }
1541
+
1542
+ function noAtomicOperations(eventEmitter) {
1543
+ eventEmitter.on('BeforeInputType', beforeInputType$1);
1544
+ eventEmitter.on('BeforeGenerateFiles', beforeGenerateFiles$1);
1545
+ }
1546
+ function beforeInputType$1(args) {
1547
+ const {
1548
+ getModelName,
1549
+ inputType
1550
+ } = args;
1551
+ for (const field of inputType.fields) {
1552
+ const fieldName = field.name;
1553
+ field.inputTypes = field.inputTypes.filter(inputType => {
1554
+ const inputTypeName = String(inputType.type);
1555
+ const modelName = getModelName(inputTypeName);
1556
+ if (isAtomicOperation(inputTypeName) || modelName && isListInput(inputTypeName, modelName, fieldName)) {
1557
+ return false;
1558
+ }
1559
+ return true;
1560
+ });
1561
+ }
1562
+ }
1563
+ function beforeGenerateFiles$1(args) {
1564
+ const {
1565
+ project
1566
+ } = args;
1567
+ for (const sourceFile of project.getSourceFiles()) {
1568
+ const className = sourceFile.getClass(() => true)?.getName();
1569
+ if (className && isAtomicOperation(className)) {
1570
+ project.removeSourceFile(sourceFile);
1571
+ }
1572
+ }
1573
+ }
1574
+ function isAtomicOperation(typeName) {
1575
+ if (typeName.endsWith('FieldUpdateOperationsInput')) {
1576
+ return true;
1577
+ }
1578
+ return false;
1579
+ }
1580
+ function isListInput(typeName, model, field) {
1581
+ return typeName === `${model}Create${field}Input` || typeName === `${model}Update${field}Input`;
1582
+ }
1583
+
1584
+ function getEnumName(referenceName) {
1585
+ // `${Role}`
1586
+ return referenceName.slice(3, -2);
1587
+ }
1588
+
1589
+ const nestjsGraphql = '@nestjs/graphql';
1590
+ function outputType(outputType, args) {
1591
+ const {
1592
+ config,
1593
+ eventEmitter,
1594
+ fieldSettings,
1595
+ getModelName,
1596
+ getSourceFile,
1597
+ models
1598
+ } = args;
1599
+ const importDeclarations = new ImportDeclarationMap();
1600
+ const fileType = 'output';
1601
+ const modelName = getModelName(outputType.name) || '';
1602
+ const model = models.get(modelName);
1603
+ const isAggregateOutput = model && /(?:Count|Avg|Sum|Min|Max)AggregateOutputType$/.test(outputType.name) && String(outputType.name).startsWith(model.name);
1604
+ const isCountOutput = model?.name && outputType.name === `${model.name}CountOutputType`;
1605
+ if (!config.emitBlocks.outputs && !isCountOutput) return;
1606
+
1607
+ // Get rid of bogus suffixes
1608
+ outputType.name = getOutputTypeName(outputType.name);
1609
+ if (isAggregateOutput) {
1610
+ eventEmitter.emitSync('AggregateOutput', {
1611
+ ...args,
1612
+ outputType
1613
+ });
1614
+ }
1615
+ const sourceFile = getSourceFile({
1616
+ name: outputType.name,
1617
+ type: fileType
1618
+ });
1619
+ const classStructure = {
1620
+ decorators: [{
1621
+ arguments: [],
1622
+ name: 'ObjectType'
1623
+ }],
1624
+ isExported: true,
1625
+ kind: StructureKind.Class,
1626
+ name: outputType.name,
1627
+ properties: []
1628
+ };
1629
+ importDeclarations.add('Field', nestjsGraphql);
1630
+ importDeclarations.add('ObjectType', nestjsGraphql);
1631
+ for (const field of outputType.fields) {
1632
+ const {
1633
+ isList,
1634
+ location,
1635
+ type
1636
+ } = field.outputType;
1637
+ const outputTypeName = getOutputTypeName(String(type));
1638
+ const settings = isCountOutput ? undefined : model && fieldSettings.get(model.name)?.get(field.name);
1639
+ const propertySettings = settings?.getPropertyType({
1640
+ name: outputType.name,
1641
+ output: true
1642
+ });
1643
+ const isCustomsApplicable = outputTypeName === model?.fields.find(f => f.name === field.name)?.type;
1644
+ field.outputType.type = outputTypeName;
1645
+ const propertyType = castArray(propertySettings?.name || getPropertyType({
1646
+ location,
1647
+ type: outputTypeName
1648
+ }));
1649
+ const property = propertyStructure({
1650
+ hasQuestionToken: isCountOutput ? true : undefined,
1651
+ isList,
1652
+ isNullable: field.isNullable,
1653
+ location,
1654
+ name: field.name,
1655
+ propertyType
1656
+ });
1657
+ classStructure.properties?.push(property);
1658
+ importDeclarations.create({
1659
+ config,
1660
+ propertySettings,
1661
+ propertyType: property.type
1662
+ });
1663
+
1664
+ // Get graphql type
1665
+ let graphqlType;
1666
+ const shouldHideField = settings?.shouldHideField({
1667
+ name: outputType.name,
1668
+ output: true
1669
+ }) || config.decorate.some(d => d.name === 'HideField' && d.from === '@nestjs/graphql' && d.isMatchField(field.name) && d.isMatchType(outputTypeName));
1670
+ const fieldType = settings?.getFieldType({
1671
+ name: outputType.name,
1672
+ output: true
1673
+ });
1674
+ if (fieldType && isCustomsApplicable && !shouldHideField) {
1675
+ graphqlType = fieldType.name;
1676
+ importDeclarations.createFrom({
1677
+ ...fieldType
1678
+ });
1679
+ } else {
1680
+ const graphqlImport = getGraphqlImport({
1681
+ config,
1682
+ fileType,
1683
+ getSourceFile,
1684
+ isId: false,
1685
+ location,
1686
+ sourceFile,
1687
+ typeName: outputTypeName
1688
+ });
1689
+ const referenceName = location === 'enumTypes' ? getEnumName(propertyType[0]) : propertyType[0];
1690
+ graphqlType = graphqlImport.name;
1691
+ if (graphqlImport.specifier && !importDeclarations.has(graphqlImport.name) && (graphqlImport.name !== outputType.name && !shouldHideField || shouldHideField && referenceName === graphqlImport.name)) {
1692
+ importDeclarations.set(graphqlImport.name, {
1693
+ moduleSpecifier: graphqlImport.specifier,
1694
+ namedImports: [{
1695
+ name: graphqlImport.name
1696
+ }]
1697
+ });
1698
+ }
1699
+ }
1700
+ ok$1(property.decorators, 'property.decorators is undefined');
1701
+ if (shouldHideField) {
1702
+ importDeclarations.add('HideField', nestjsGraphql);
1703
+ property.decorators.push({
1704
+ arguments: [],
1705
+ name: 'HideField'
1706
+ });
1707
+ } else {
1708
+ // Generate `@Field()` decorator
1709
+ property.decorators.push({
1710
+ arguments: [isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`, JSON5.stringify({
1711
+ ...settings?.fieldArguments(),
1712
+ nullable: Boolean(field.isNullable)
1713
+ })],
1714
+ name: 'Field'
1715
+ });
1716
+ if (isCustomsApplicable) {
1717
+ for (const options of settings || []) {
1718
+ if ((options.kind === 'Decorator' && options.output && options.match?.(field.name)) ?? true) {
1719
+ property.decorators.push({
1720
+ arguments: options.arguments,
1721
+ name: options.name
1722
+ });
1723
+ ok$1(options.from, "Missed 'from' part in configuration or field setting");
1724
+ importDeclarations.createFrom(options);
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1729
+ eventEmitter.emitSync('ClassProperty', property, {
1730
+ isList,
1731
+ location,
1732
+ propertyType
1733
+ });
1734
+ }
1735
+ sourceFile.set({
1736
+ statements: [...importDeclarations.toStatements(), classStructure]
1737
+ });
1738
+ }
1739
+
1740
+ function purgeOutput(emitter) {
1741
+ emitter.on('Begin', begin);
1742
+ emitter.on('End', end);
1743
+ }
1744
+ function begin({
1745
+ output,
1746
+ project
1747
+ }) {
1748
+ const sourceFiles = project.getDirectory(output)?.getDescendantSourceFiles();
1749
+ if (sourceFiles) {
1750
+ for (const sourceFile of sourceFiles) {
1751
+ sourceFile.delete();
1752
+ }
1753
+ }
1754
+ }
1755
+ function end({
1756
+ output,
1757
+ project
1758
+ }) {
1759
+ const directories = project.getDirectory(output)?.getDescendantDirectories().filter(directory => directory.getSourceFiles().length === 0).map(directory => directory.getPath());
1760
+ for (const directory of directories || []) {
1761
+ try {
1762
+ fs.rmdirSync(directory);
1763
+ // eslint-disable-next-line no-empty
1764
+ } catch {}
1765
+ }
1766
+ }
1767
+
1768
+ const ReExport = {
1769
+ All: 'All',
1770
+ Directories: 'Directories',
1771
+ None: 'None',
1772
+ Single: 'Single'
1773
+ };
1774
+ function reExport(emitter) {
1775
+ emitter.on('BeforeGenerateFiles', beforeGenerateFiles);
1776
+ }
1777
+ function beforeGenerateFiles(args) {
1778
+ const {
1779
+ config,
1780
+ output,
1781
+ project
1782
+ } = args;
1783
+ const rootDirectory = project.getDirectoryOrThrow(output);
1784
+ const {
1785
+ importExtension,
1786
+ reExport
1787
+ } = config;
1788
+ if ([ReExport.Directories, ReExport.All].includes(reExport)) {
1789
+ for (const directory of rootDirectory.getDescendantDirectories()) {
1790
+ let indexSourceFile;
1791
+ const exportDeclarations = directory.getSourceFiles().filter(sourceFile => {
1792
+ return sourceFile.getBaseName() !== 'index.ts';
1793
+ }).map(sourcesFile => getExportDeclaration(directory, sourcesFile, importExtension));
1794
+ if (exportDeclarations.length > 0) {
1795
+ indexSourceFile = directory.createSourceFile('index.ts', {
1796
+ statements: exportDeclarations
1797
+ }, {
1798
+ overwrite: true
1799
+ });
1800
+ }
1801
+ if (indexSourceFile) {
1802
+ continue;
1803
+ }
1804
+ const namespaceExportDeclarations = directory.getDirectories().map(sourceDirectory => getNamespaceExportDeclaration(directory, sourceDirectory, importExtension));
1805
+ project.createSourceFile(`${directory.getPath()}/index.ts`, {
1806
+ statements: namespaceExportDeclarations
1807
+ }, {
1808
+ overwrite: true
1809
+ });
1810
+ }
1811
+ }
1812
+ if (reExport === ReExport.Single) {
1813
+ const exportDeclarations = project.getSourceFiles().filter(sourceFile => {
1814
+ return sourceFile.getBaseName() !== 'index.ts';
1815
+ }).map(sourceFile => getExportDeclaration(rootDirectory, sourceFile, importExtension));
1816
+ rootDirectory.createSourceFile('index.ts', {
1817
+ statements: exportDeclarations
1818
+ }, {
1819
+ overwrite: true
1820
+ });
1821
+ }
1822
+ if (reExport === ReExport.All) {
1823
+ const exportDeclarations = [];
1824
+ for (const directory of rootDirectory.getDirectories()) {
1825
+ if (directory.getBaseName() === 'node_modules') continue;
1826
+ const sourceFile = directory.getSourceFileOrThrow('index.ts');
1827
+ exportDeclarations.push(getExportDeclaration(rootDirectory, sourceFile, importExtension));
1828
+ }
1829
+ rootDirectory.createSourceFile('index.ts', {
1830
+ statements: exportDeclarations
1831
+ }, {
1832
+ overwrite: true
1833
+ });
1834
+ }
1835
+ }
1836
+ function getExportDeclaration(directory, sourceFile, importExtension) {
1837
+ const moduleSpecifier = adjustModuleSpecifier(directory.getRelativePathAsModuleSpecifierTo(sourceFile), importExtension);
1838
+ return {
1839
+ kind: StructureKind.ExportDeclaration,
1840
+ moduleSpecifier,
1841
+ namedExports: sourceFile.getExportSymbols().map(s => ({
1842
+ name: s.getName()
1843
+ }))
1844
+ };
1845
+ }
1846
+ function getNamespaceExportDeclaration(directory, sourceDirectory, importExtension) {
1847
+ const moduleSpecifier = adjustModuleSpecifier(directory.getRelativePathAsModuleSpecifierTo(sourceDirectory), importExtension);
1848
+ return {
1849
+ kind: StructureKind.ExportDeclaration,
1850
+ moduleSpecifier
1851
+ };
1852
+ }
1853
+
1854
+ // utils/prisma-enum-doc.ts
1855
+
1856
+ function extractEnumValueDocs(values) {
1857
+ return Object.fromEntries(values.map(value => {
1858
+ const {
1859
+ name
1860
+ } = value;
1861
+ const documentation = value.documentation;
1862
+ if (typeof documentation !== 'string') return null;
1863
+ if (documentation.startsWith('@deprecated')) {
1864
+ return [name, {
1865
+ deprecationReason: documentation.slice(11).trim()
1866
+ }];
1867
+ }
1868
+ return [name, {
1869
+ description: documentation
1870
+ }];
1871
+ }).filter(entry => entry !== null));
1872
+ }
1873
+
1874
+ function registerEnum(enumType, args) {
1875
+ const {
1876
+ config,
1877
+ enums,
1878
+ getSourceFile
1879
+ } = args;
1880
+ if (!config.emitBlocks.prismaEnums && !enums[enumType.name]) return;
1881
+ const dataModelEnum = enums[enumType.name];
1882
+ const enumTypesData = dataModelEnum?.values || [];
1883
+ const sourceFile = getSourceFile({
1884
+ name: enumType.name,
1885
+ type: 'enum'
1886
+ });
1887
+ const importDeclarations = new ImportDeclarationMap();
1888
+ importDeclarations.set('registerEnumType', {
1889
+ moduleSpecifier: '@nestjs/graphql',
1890
+ namedImports: [{
1891
+ name: 'registerEnumType'
1892
+ }]
1893
+ });
1894
+
1895
+ // Extract valuesMap from enum documentation
1896
+ const valuesMap = extractEnumValueDocs(enumTypesData);
1897
+
1898
+ // Remove entries with no description or deprecationReason
1899
+ const filteredValuesMap = Object.fromEntries(Object.entries(valuesMap).filter(([_, v]) => Object.keys(v).length > 0));
1900
+
1901
+ // Format only if needed
1902
+ const hasValuesMap = Object.keys(filteredValuesMap).length > 0;
1903
+ const formattedValuesMap = hasValuesMap ? JSON.stringify(filteredValuesMap, null, 2).replaceAll(/"([^"]+)":/g, '$1:') : '';
1904
+ const valuesMapEntry = hasValuesMap ? `, valuesMap: ${formattedValuesMap}` : '';
1905
+ const enumStructure = {
1906
+ isExported: true,
1907
+ kind: StructureKind.Enum,
1908
+ members: enumType.values.map(v => ({
1909
+ initializer: JSON.stringify(v),
1910
+ name: v
1911
+ })),
1912
+ name: enumType.name
1913
+ };
1914
+ sourceFile.set({
1915
+ statements: [...importDeclarations.toStatements(), enumStructure, '\n', `registerEnumType(${enumType.name}, { name: '${enumType.name}', description: ${JSON.stringify(dataModelEnum?.documentation)}${valuesMapEntry} })`]
1916
+ });
1917
+ }
1918
+
1919
+ function requireSingleFieldsInWhereUniqueInput(eventEmitter) {
1920
+ eventEmitter.on('BeforeInputType', beforeInputType);
1921
+ }
1922
+ function beforeInputType(args) {
1923
+ const {
1924
+ inputType
1925
+ } = args;
1926
+ if (!isWhereUniqueInputType(inputType.name) || inputType.fields.length !== 1) {
1927
+ return;
1928
+ }
1929
+ for (const field of inputType.fields) {
1930
+ field.isRequired = true;
1931
+ field.isNullable = false;
1932
+ }
1933
+ }
1934
+
1935
+ function warning(message) {
1936
+ if (Array.isArray(message)) {
1937
+ console.log('prisma-nestjs-graphql:');
1938
+ console.log(message.join('\n'));
1939
+ } else {
1940
+ console.log('prisma-nestjs-graphql:', message);
1941
+ }
1942
+ }
1943
+
1944
+ const allEmmittedBlocks = ['prismaEnums', 'schemaEnums', 'models', 'inputs', 'args', 'outputs'];
1945
+ const blocksDependencyMap = {
1946
+ args: ['args', 'inputs', 'prismaEnums'],
1947
+ enums: ['schemaEnums', 'prismaEnums'],
1948
+ inputs: ['inputs', 'prismaEnums'],
1949
+ models: ['models', 'schemaEnums'],
1950
+ outputs: ['outputs']
1951
+ };
1952
+ function createEmitBlocks(data) {
1953
+ if (!data) {
1954
+ return Object.fromEntries(allEmmittedBlocks.map(block => [block, true]));
1955
+ }
1956
+ let blocksToEmit = {};
1957
+ for (const block of data) {
1958
+ if (!Object.keys(blocksDependencyMap).includes(block)) continue;
1959
+ blocksToEmit = {
1960
+ ...blocksToEmit,
1961
+ ...Object.fromEntries(blocksDependencyMap[block].map(block => [block, true]))
1962
+ };
1963
+ }
1964
+ return blocksToEmit;
1965
+ }
1966
+
1967
+ function createConfig(data) {
1968
+ const config = merge({}, unflatten(data, {
1969
+ delimiter: '_'
1970
+ }));
1971
+ const $warnings = [];
1972
+ const configOutputFilePattern = String(config.outputFilePattern || `{model}/{name}.{type}.ts`);
1973
+ const outputFilePattern = configOutputFilePattern.replaceAll('\\', '/').split('/').map(path => filenamify(path, {
1974
+ replacement: ''
1975
+ })).filter(Boolean).join('/');
1976
+ if (outputFilePattern !== configOutputFilePattern) {
1977
+ $warnings.push(`Due to invalid filepath 'outputFilePattern' changed to '${outputFilePattern}'`);
1978
+ }
1979
+ const fields = Object.fromEntries(Object.entries(config.fields ?? {}).filter(({
1980
+ 1: value
1981
+ }) => typeof value === 'object').map(([name, value]) => {
1982
+ const fieldSetting = {
1983
+ arguments: [],
1984
+ defaultImport: toBoolean(value.defaultImport) ? true : value.defaultImport,
1985
+ from: value.from,
1986
+ input: toBoolean(value.input),
1987
+ model: toBoolean(value.model),
1988
+ namespaceImport: value.namespaceImport,
1989
+ output: toBoolean(value.output)
1990
+ };
1991
+ return [name, fieldSetting];
1992
+ }));
1993
+ const decorate = [];
1994
+ const configDecorate = Object.values(config.decorate || {});
1995
+ for (const element of configDecorate) {
1996
+ if (!element) continue;
1997
+ ok$1(element.from && element.name, `Missed 'from' or 'name' part in configuration for decorate`);
1998
+ decorate.push({
1999
+ arguments: element.arguments ? JSON5.parse(element.arguments) : undefined,
2000
+ defaultImport: toBoolean(element.defaultImport) ? true : element.defaultImport,
2001
+ from: element.from,
2002
+ isMatchField: outmatch(element.field, {
2003
+ separator: false
2004
+ }),
2005
+ isMatchType: outmatch(element.type, {
2006
+ separator: false
2007
+ }),
2008
+ name: element.name,
2009
+ namedImport: toBoolean(element.namedImport),
2010
+ namespaceImport: element.namespaceImport
2011
+ });
2012
+ }
2013
+ const customImport = [];
2014
+ const configCustomImport = Object.values(config.customImport || {});
2015
+ for (const element of configCustomImport) {
2016
+ if (!element) continue;
2017
+ ok$1(element.from && element.name, `Missed 'from' or 'name' part in configuration for customImport`);
2018
+ customImport.push({
2019
+ defaultImport: toBoolean(element.defaultImport) ? true : element.defaultImport,
2020
+ from: element.from,
2021
+ name: element.name,
2022
+ namedImport: toBoolean(element.namedImport),
2023
+ namespaceImport: element.namespaceImport
2024
+ });
2025
+ }
2026
+ return {
2027
+ $warnings,
2028
+ combineScalarFilters: toBoolean(config.combineScalarFilters),
2029
+ customImport,
2030
+ decorate,
2031
+ emitBlocks: createEmitBlocks(config.emitBlocks),
2032
+ emitCompiled: toBoolean(config.emitCompiled),
2033
+ emitSingle: toBoolean(config.emitSingle),
2034
+ fields,
2035
+ graphqlScalars: config.graphqlScalars || {},
2036
+ importExtension: String(config.importExtension ?? ''),
2037
+ noAtomicOperations: toBoolean(config.noAtomicOperations),
2038
+ noTypeId: toBoolean(config.noTypeId),
2039
+ omitModelsCount: toBoolean(config.omitModelsCount),
2040
+ outputFilePattern,
2041
+ prismaClientImport: createPrismaImport(config.prismaClientImport),
2042
+ purgeOutput: toBoolean(config.purgeOutput),
2043
+ reExport: ReExport[String(config.reExport)] || ReExport.None,
2044
+ requireSingleFieldsInWhereUniqueInput: toBoolean(config.requireSingleFieldsInWhereUniqueInput),
2045
+ tsConfigFilePath: createTsConfigFilePathValue(config.tsConfigFilePath),
2046
+ unsafeCompatibleWhereUniqueInput: toBoolean(config.unsafeCompatibleWhereUniqueInput),
2047
+ useInputType: createUseInputType(config.useInputType)
2048
+ };
2049
+ }
2050
+ const tsConfigFileExists = memoize(filePath => {
2051
+ return fs.existsSync(filePath);
2052
+ });
2053
+ function createTsConfigFilePathValue(value) {
2054
+ if (typeof value === 'string') return value;
2055
+ if (tsConfigFileExists('tsconfig.json')) return 'tsconfig.json';
2056
+ }
2057
+ function createPrismaImport(value) {
2058
+ if (typeof value === 'string') return value;
2059
+ return '@prisma/client';
2060
+ }
2061
+ function createUseInputType(data) {
2062
+ if (!data) {
2063
+ return [];
2064
+ }
2065
+ const result = [];
2066
+ for (const [typeName, useInputs] of Object.entries(data)) {
2067
+ const entry = {
2068
+ ALL: undefined,
2069
+ typeName
2070
+ };
2071
+ if (useInputs.ALL) {
2072
+ entry.ALL = useInputs.ALL;
2073
+ delete useInputs.ALL;
2074
+ }
2075
+ for (const [propertyName, pattern] of Object.entries(useInputs)) {
2076
+ entry[propertyName] = pattern;
2077
+ }
2078
+ result.push(entry);
2079
+ }
2080
+ return result;
2081
+ }
2082
+ function toBoolean(value) {
2083
+ return ['true', '1', 'on'].includes(String(value));
2084
+ }
2085
+
2086
+ function generateFileName(args) {
2087
+ const {
2088
+ getModelName,
2089
+ name,
2090
+ template,
2091
+ type
2092
+ } = args;
2093
+ return pupa(template, {
2094
+ get model() {
2095
+ const result = getModelName(name) || 'prisma';
2096
+ return kebabCase(result);
2097
+ },
2098
+ get name() {
2099
+ let result = kebabCase(name);
2100
+ for (const suffix of ['input', 'args', 'enum']) {
2101
+ const ending = `-${suffix}`;
2102
+ if (type === suffix && result.endsWith(ending)) {
2103
+ result = result.slice(0, -ending.length);
2104
+ }
2105
+ }
2106
+ return result;
2107
+ },
2108
+ plural: {
2109
+ get type() {
2110
+ return pluralize(type);
2111
+ }
2112
+ },
2113
+ type
2114
+ });
2115
+ }
2116
+
2117
+ function factoryGetSourceFile(args) {
2118
+ const {
2119
+ getModelName,
2120
+ output,
2121
+ outputFilePattern,
2122
+ project
2123
+ } = args;
2124
+ return function getSourceFile(args) {
2125
+ const {
2126
+ name,
2127
+ type
2128
+ } = args;
2129
+ let filePath = generateFileName({
2130
+ getModelName,
2131
+ name,
2132
+ template: outputFilePattern,
2133
+ type
2134
+ });
2135
+ filePath = `${output}/${filePath}`;
2136
+ return project.getSourceFile(filePath) || project.createSourceFile(filePath, undefined, {
2137
+ overwrite: true
2138
+ });
2139
+ };
2140
+ }
2141
+
2142
+ function createGetModelName(modelNames) {
2143
+ return memoize(tryGetName);
2144
+ function tryGetName(name) {
2145
+ return getModelName({
2146
+ modelNames,
2147
+ name
2148
+ });
2149
+ }
2150
+ }
2151
+ function getModelName(args) {
2152
+ const {
2153
+ modelNames,
2154
+ name
2155
+ } = args;
2156
+ for (const keyword of splitKeywords) {
2157
+ const [test] = name.split(keyword, 1);
2158
+ if (modelNames.includes(test)) {
2159
+ return test;
2160
+ }
2161
+ }
2162
+ for (const keyword of endsWithKeywords) {
2163
+ const [test] = name.split(keyword).slice(-1);
2164
+ if (modelNames.includes(test)) {
2165
+ return test;
2166
+ }
2167
+ }
2168
+ for (const [start, end] of middleKeywords) {
2169
+ let test = name.slice(start.length).slice(0, -end.length);
2170
+ if (modelNames.includes(test) && name.startsWith(start) && name.endsWith(end)) {
2171
+ return test;
2172
+ }
2173
+ test = name.slice(0, -(start + end).length);
2174
+ if (modelNames.includes(test) && name.endsWith(start + end)) {
2175
+ return test;
2176
+ }
2177
+ }
2178
+
2179
+ // test for {Model}{UniqueName}CompoundUniqueInput
2180
+ if (name.slice(-19) === 'CompoundUniqueInput') {
2181
+ const test = name.slice(0, -19);
2182
+ const models = modelNames.filter(x => test.startsWith(x)).sort((a, b) => b.length - a.length);
2183
+ return first(models);
2184
+ }
2185
+
2186
+ // test for {Model}Count
2187
+ if (name.slice(-5) === 'Count') {
2188
+ const test = name.slice(0, -5);
2189
+ if (modelNames.includes(test)) {
2190
+ return test;
2191
+ }
2192
+ }
2193
+ }
2194
+ const splitKeywords = ['CreateInput', 'CreateMany', 'CreateNested', 'CreateOneWithout', 'CreateOrConnect', 'CreateWithout', 'DistinctField', 'Filter', 'ManyWithout', 'OrderByInput', 'RelationFilter', 'NullableRelationFilter', 'ListRelationFilter', 'ScalarWhereInput', 'UpdateInput', 'UpdateMany', 'UpdateOneRequiredWithout', 'UpdateOneWithout', 'UpdateWith', 'UpsertWith', 'UpsertWithout', 'WhereInput', 'WhereUniqueInput', 'AvgAggregate', 'SumAggregate', 'MinAggregate', 'MaxAggregate', 'CountAggregate', 'ScalarField', 'GroupBy', 'OrderBy', 'UncheckedUpdate', 'UncheckedCreate', 'ScalarWhere', 'CountOutputType', 'CountOrderBy', 'SumOrderBy', 'MinOrderBy', 'MaxOrderBy', 'AvgOrderBy', 'Create', 'Update', 'ScalarRelationFilter', 'NullableScalarRelationFilter'].sort((a, b) => b.length - a.length);
2195
+ const endsWithKeywords = ['Aggregate', 'GroupBy', 'CreateOne', 'CreateMany', 'DeleteMany', 'DeleteOne', 'FindMany', 'FindOne', 'FindUnique', 'UpdateMany', 'UpdateOne', 'UpsertOne'];
2196
+ const middleKeywords = [['FindFirst', 'OrThrowArgs'], ['FindUnique', 'OrThrowArgs'], ['Aggregate', 'Args'], ['CreateOne', 'Args'], ['CreateMany', 'Args'], ['DeleteMany', 'Args'], ['DeleteOne', 'Args'], ['FindMany', 'Args'], ['FindFirst', 'Args'], ['FindOne', 'Args'], ['FindUnique', 'Args'], ['UpdateMany', 'Args'], ['UpdateMany', 'AndReturnOutputType'], ['UpdateOne', 'Args'], ['UpsertOne', 'Args'], ['GroupBy', 'Args'], ['OrderBy', 'Args']];
2197
+
2198
+ function resolveAwaitEventEmitter() {
2199
+ if (typeof awaitEventEmitterModule === 'function') return awaitEventEmitterModule;
2200
+ if (typeof awaitEventEmitterModule.default === 'function') return awaitEventEmitterModule.default;
2201
+ }
2202
+ async function generate(args) {
2203
+ const {
2204
+ connectCallback,
2205
+ dmmf,
2206
+ generator,
2207
+ skipAddOutputSourceFiles
2208
+ } = args;
2209
+ const generatorOutputValue = generator.output?.value;
2210
+ ok(generatorOutputValue, 'Missing generator configuration: output');
2211
+ const config = createConfig(generator.config);
2212
+ const AwaitEventEmitter = resolveAwaitEventEmitter();
2213
+ const eventEmitter = new AwaitEventEmitter();
2214
+ eventEmitter.on('Warning', warning);
2215
+ config.emitBlocks.models && eventEmitter.on('Model', modelData);
2216
+ if (config.emitBlocks.prismaEnums || config.emitBlocks.schemaEnums) {
2217
+ eventEmitter.on('EnumType', registerEnum);
2218
+ }
2219
+ if (config.emitBlocks.outputs || config.emitBlocks.models && !config.omitModelsCount) {
2220
+ eventEmitter.on('OutputType', outputType);
2221
+ }
2222
+ config.emitBlocks.models && eventEmitter.on('ModelOutputType', modelOutputType);
2223
+ config.emitBlocks.outputs && eventEmitter.on('AggregateOutput', createAggregateInput);
2224
+ config.emitBlocks.inputs && eventEmitter.on('InputType', inputType);
2225
+ config.emitBlocks.args && eventEmitter.on('ArgsType', argsType);
2226
+ eventEmitter.on('GenerateFiles', generateFiles);
2227
+ for (const message of config.$warnings) {
2228
+ eventEmitter.emitSync('Warning', message);
2229
+ }
2230
+ const project = new Project({
2231
+ manipulationSettings: {
2232
+ quoteKind: QuoteKind.Single
2233
+ },
2234
+ skipAddingFilesFromTsConfig: true,
2235
+ skipLoadingLibFiles: !config.emitCompiled,
2236
+ tsConfigFilePath: config.tsConfigFilePath
2237
+ });
2238
+ if (!skipAddOutputSourceFiles) {
2239
+ project.addSourceFilesAtPaths([`${generatorOutputValue}/**/*.ts`, `!${generatorOutputValue}/**/*.d.ts`]);
2240
+ }
2241
+ config.combineScalarFilters && combineScalarFilters(eventEmitter);
2242
+ config.noAtomicOperations && noAtomicOperations(eventEmitter);
2243
+ config.reExport !== ReExport.None && reExport(eventEmitter);
2244
+ config.purgeOutput && purgeOutput(eventEmitter);
2245
+ config.requireSingleFieldsInWhereUniqueInput && requireSingleFieldsInWhereUniqueInput(eventEmitter);
2246
+ const models = new Map();
2247
+ const modelNames = [];
2248
+ const modelFields = new Map();
2249
+ const fieldSettings = new Map();
2250
+ const getModelName = createGetModelName(modelNames);
2251
+ const getSourceFile = factoryGetSourceFile({
2252
+ eventEmitter,
2253
+ getModelName,
2254
+ output: generatorOutputValue,
2255
+ outputFilePattern: config.outputFilePattern,
2256
+ project
2257
+ });
2258
+ const {
2259
+ datamodel,
2260
+ schema
2261
+ } = structuredClone(dmmf);
2262
+ const removeTypes = new Set();
2263
+ const eventArguments = {
2264
+ classTransformerTypeModels: new Set(),
2265
+ config,
2266
+ enums: mapKeys(datamodel.enums, x => x.name),
2267
+ eventEmitter,
2268
+ fieldSettings,
2269
+ getModelName,
2270
+ getSourceFile,
2271
+ modelFields,
2272
+ modelNames,
2273
+ models,
2274
+ output: generatorOutputValue,
2275
+ project,
2276
+ removeTypes,
2277
+ schema,
2278
+ typeNames: new Set()
2279
+ };
2280
+ if (connectCallback) {
2281
+ await connectCallback(eventEmitter, eventArguments);
2282
+ }
2283
+ await eventEmitter.emit('Begin', eventArguments);
2284
+ for (const model of datamodel.models) {
2285
+ await eventEmitter.emit('Model', model, eventArguments);
2286
+ }
2287
+
2288
+ // Types behaves like model
2289
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
2290
+ for (const model of datamodel.types || []) {
2291
+ await eventEmitter.emit('Model', model, eventArguments);
2292
+ }
2293
+ const {
2294
+ enumTypes,
2295
+ inputObjectTypes,
2296
+ outputObjectTypes
2297
+ } = schema;
2298
+ await eventEmitter.emit('PostBegin', eventArguments);
2299
+ for (const enumType of enumTypes.prisma.concat(enumTypes.model || [])) {
2300
+ await eventEmitter.emit('EnumType', enumType, eventArguments);
2301
+ }
2302
+ for (const outputType of outputObjectTypes.model) {
2303
+ await eventEmitter.emit('ModelOutputType', outputType, eventArguments);
2304
+ }
2305
+ const queryOutputTypes = [];
2306
+ for (const outputType of outputObjectTypes.prisma) {
2307
+ if (['Query', 'Mutation'].includes(outputType.name)) {
2308
+ queryOutputTypes.push(outputType);
2309
+ continue;
2310
+ }
2311
+ await eventEmitter.emit('OutputType', outputType, eventArguments);
2312
+ }
2313
+ const inputTypes = inputObjectTypes.prisma?.concat(inputObjectTypes.model ?? []) ?? [];
2314
+ for (const inputType of inputTypes) {
2315
+ const event = {
2316
+ ...eventArguments,
2317
+ classDecoratorName: 'InputType',
2318
+ fileType: 'input',
2319
+ inputType
2320
+ };
2321
+ if (inputType.fields.length === 0) {
2322
+ removeTypes.add(inputType.name);
2323
+ continue;
2324
+ }
2325
+ await eventEmitter.emit('BeforeInputType', event);
2326
+ await eventEmitter.emit('InputType', event);
2327
+ }
2328
+ for (const outputType of queryOutputTypes) {
2329
+ for (const field of outputType.fields) {
2330
+ await eventEmitter.emit('ArgsType', field, eventArguments);
2331
+ }
2332
+ }
2333
+ await eventEmitter.emit('BeforeGenerateFiles', eventArguments);
2334
+ await eventEmitter.emit('GenerateFiles', eventArguments);
2335
+ await eventEmitter.emit('End', eventArguments);
2336
+ for (const name of Object.keys(eventEmitter._events)) {
2337
+ eventEmitter.off(name);
2338
+ }
2339
+ }
2340
+ const generatorHandlerConfig = {
2341
+ async onGenerate(options) {
2342
+ await generate(options);
2343
+ },
2344
+ onManifest() {
2345
+ return {
2346
+ defaultOutput: '.',
2347
+ prettyName: 'Prisma NestJS/GraphQL'
2348
+ };
2349
+ }
2350
+ };
2351
+
2352
+ export { generate, generatorHandlerConfig };