@k0lyan/nestjs-prisma-graphql-generator 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,862 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generateCodeGrouped = generateCodeGrouped;
7
- const ts_morph_1 = require("ts-morph");
8
- const types_1 = require("./dmmf/types");
9
- const transformer_1 = require("./dmmf/transformer");
10
- const pluralize_1 = __importDefault(require("pluralize"));
11
- /**
12
- * Generate code with files grouped by model
13
- *
14
- * Output structure:
15
- * - enums/ (shared enums)
16
- * - common/ (AffectedRows, etc.)
17
- * - {ModelName}/ (per-model folder)
18
- * - model.ts (ObjectType)
19
- * - inputs.ts (all inputs for this model)
20
- * - args.ts (all args for this model)
21
- * - resolver.ts (resolver)
22
- * - index.ts (re-exports)
23
- * - helpers.ts
24
- * - index.ts
25
- */
26
- async function generateCodeGrouped(dmmf, config) {
27
- const project = new ts_morph_1.Project({
28
- useInMemoryFileSystem: true,
29
- compilerOptions: {
30
- declaration: true,
31
- strict: true,
32
- experimentalDecorators: true,
33
- emitDecoratorMetadata: true,
34
- },
35
- });
36
- const allFiles = new Map();
37
- // Generate shared enums
38
- const enumFiles = generateEnumsGrouped(project, dmmf);
39
- for (const [path, file] of enumFiles) {
40
- allFiles.set(path, file);
41
- }
42
- // Generate common types
43
- const commonFiles = generateCommonTypesGrouped(project);
44
- for (const [path, file] of commonFiles) {
45
- allFiles.set(path, file);
46
- }
47
- // Generate helpers
48
- const helpersFile = generateHelpersGrouped(project, config);
49
- allFiles.set('helpers.ts', helpersFile);
50
- // Generate per-model files
51
- for (const model of dmmf.models) {
52
- const modelFiles = generateModelGrouped(project, model, dmmf, config);
53
- for (const [path, file] of modelFiles) {
54
- allFiles.set(path, file);
55
- }
56
- }
57
- // Generate root index
58
- const indexFile = project.createSourceFile('index.ts', '', { overwrite: true });
59
- generateRootIndexGrouped(indexFile, dmmf, config);
60
- allFiles.set('index.ts', indexFile);
61
- // Convert to GeneratedFile array
62
- const result = [];
63
- for (const [path, sourceFile] of allFiles) {
64
- result.push({
65
- path,
66
- content: sourceFile.getFullText(),
67
- });
68
- }
69
- return result;
70
- }
71
- /**
72
- * Generate all files for a single model
73
- */
74
- function generateModelGrouped(project, model, dmmf, config) {
75
- const files = new Map();
76
- const modelDir = model.name;
77
- // Get available input types for this model
78
- const inputTypeNames = new Set(dmmf.inputTypes.keys());
79
- const available = {
80
- hasWhereInput: inputTypeNames.has(`${model.name}WhereInput`),
81
- hasWhereUniqueInput: inputTypeNames.has(`${model.name}WhereUniqueInput`),
82
- hasOrderByInput: inputTypeNames.has(`${model.name}OrderByWithRelationInput`),
83
- hasCreateInput: inputTypeNames.has(`${model.name}CreateInput`),
84
- hasCreateManyInput: inputTypeNames.has(`${model.name}CreateManyInput`),
85
- hasUpdateInput: inputTypeNames.has(`${model.name}UpdateInput`),
86
- hasUpdateManyInput: inputTypeNames.has(`${model.name}UpdateManyMutationInput`),
87
- hasScalarWhereWithAggregates: inputTypeNames.has(`${model.name}ScalarWhereWithAggregatesInput`),
88
- };
89
- // Skip models with no query capability
90
- if (!available.hasWhereInput && !available.hasWhereUniqueInput) {
91
- return files;
92
- }
93
- // Generate model.ts (ObjectType)
94
- const modelFile = project.createSourceFile(`${modelDir}/model.ts`, '', { overwrite: true });
95
- generateModelObjectType(modelFile, model, dmmf, config);
96
- files.set(`${modelDir}/model.ts`, modelFile);
97
- // Generate inputs.ts (all inputs for this model)
98
- const modelInputTypes = getInputTypesForModel(model.name, dmmf);
99
- if (modelInputTypes.length > 0) {
100
- const inputsFile = project.createSourceFile(`${modelDir}/inputs.ts`, '', { overwrite: true });
101
- generateModelInputs(inputsFile, model, modelInputTypes, dmmf, config);
102
- files.set(`${modelDir}/inputs.ts`, inputsFile);
103
- }
104
- // Generate args.ts (all args for this model)
105
- const argsFile = project.createSourceFile(`${modelDir}/args.ts`, '', { overwrite: true });
106
- generateModelArgs(argsFile, model, dmmf, config, available);
107
- files.set(`${modelDir}/args.ts`, argsFile);
108
- // Generate resolver.ts
109
- if (config.generateResolvers) {
110
- const resolverFile = project.createSourceFile(`${modelDir}/resolver.ts`, '', { overwrite: true });
111
- generateModelResolver(resolverFile, model, dmmf, config, available);
112
- files.set(`${modelDir}/resolver.ts`, resolverFile);
113
- }
114
- // Generate index.ts for the model folder
115
- const indexFile = project.createSourceFile(`${modelDir}/index.ts`, '', { overwrite: true });
116
- generateModelIndex(indexFile, model, config, modelInputTypes.length > 0);
117
- files.set(`${modelDir}/index.ts`, indexFile);
118
- return files;
119
- }
120
- /**
121
- * Get all input types that belong to a specific model
122
- */
123
- function getInputTypesForModel(modelName, dmmf) {
124
- const result = [];
125
- for (const [name, inputType] of dmmf.inputTypes) {
126
- if (name.startsWith(modelName)) {
127
- result.push(inputType);
128
- }
129
- }
130
- return result;
131
- }
132
- /**
133
- * Generate the ObjectType class for a model
134
- */
135
- function generateModelObjectType(sourceFile, model, dmmf, config) {
136
- const nestjsImports = ['ObjectType', 'Field', 'ID', 'Int', 'Float'];
137
- const hasJson = model.fields.some(f => f.type === 'Json');
138
- const hasBigInt = model.fields.some(f => f.type === 'BigInt');
139
- const relationFields = model.fields.filter(f => (0, transformer_1.isRelationField)(f));
140
- const relatedModels = [...new Set(relationFields.map(f => f.type))].filter(m => m !== model.name);
141
- // Add imports
142
- sourceFile.addImportDeclaration({
143
- moduleSpecifier: '@nestjs/graphql',
144
- namedImports: nestjsImports,
145
- });
146
- if (hasJson) {
147
- sourceFile.addImportDeclaration({
148
- moduleSpecifier: 'graphql-type-json',
149
- namedImports: ['GraphQLJSON'],
150
- });
151
- }
152
- if (hasBigInt) {
153
- sourceFile.addImportDeclaration({
154
- moduleSpecifier: 'graphql-scalars',
155
- namedImports: ['GraphQLBigInt'],
156
- });
157
- }
158
- // Import related models
159
- if (relatedModels.length > 0) {
160
- sourceFile.addStatements(`// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport type { ${relatedModels.join(', ')} } from '../index';`);
161
- }
162
- // Import enums
163
- const enumFields = model.fields.filter(f => (0, transformer_1.isEnumField)(f));
164
- const enumTypes = [...new Set(enumFields.map(f => f.type))];
165
- if (enumTypes.length > 0) {
166
- sourceFile.addImportDeclaration({
167
- moduleSpecifier: '../enums',
168
- namedImports: enumTypes,
169
- });
170
- }
171
- // Create the class
172
- const classDecl = sourceFile.addClass({
173
- name: `${config.typePrefix ?? ''}${model.name}${config.typeSuffix ?? ''}`,
174
- isExported: true,
175
- decorators: [
176
- {
177
- name: 'ObjectType',
178
- arguments: [
179
- ts_morph_1.Writers.object({
180
- description: model.documentation
181
- ? `'${escapeDescription(model.documentation)}'`
182
- : undefined,
183
- }),
184
- ],
185
- },
186
- ],
187
- });
188
- // Add fields
189
- for (const field of model.fields) {
190
- addFieldToModelClass(classDecl, field, dmmf);
191
- }
192
- }
193
- /**
194
- * Add a field to the model class
195
- */
196
- function addFieldToModelClass(classDecl, field, _dmmf) {
197
- const { graphqlType, tsType } = getFieldTypes(field);
198
- const isRelation = (0, transformer_1.isRelationField)(field);
199
- const fieldDecoratorArgs = [];
200
- if (isRelation) {
201
- if (field.isList) {
202
- fieldDecoratorArgs.push(`() => [require('../${field.type}/model').${field.type}]`);
203
- }
204
- else {
205
- fieldDecoratorArgs.push(`() => require('../${field.type}/model').${field.type}`);
206
- }
207
- }
208
- else {
209
- if (field.isList) {
210
- fieldDecoratorArgs.push(`() => [${graphqlType}]`);
211
- }
212
- else {
213
- fieldDecoratorArgs.push(`() => ${graphqlType}`);
214
- }
215
- }
216
- const options = {};
217
- if (!field.isRequired && !field.isList) {
218
- options['nullable'] = 'true';
219
- }
220
- if (field.documentation) {
221
- options['description'] = `'${escapeDescription(field.documentation)}'`;
222
- }
223
- if (Object.keys(options).length > 0) {
224
- const optionsStr = Object.entries(options)
225
- .map(([k, v]) => `${k}: ${v}`)
226
- .join(', ');
227
- fieldDecoratorArgs.push(`{ ${optionsStr} }`);
228
- }
229
- let propertyType = tsType;
230
- if (field.isList) {
231
- propertyType = `${tsType}[]`;
232
- }
233
- if (!field.isRequired) {
234
- propertyType = `${propertyType} | null`;
235
- }
236
- classDecl.addProperty({
237
- name: field.name,
238
- type: propertyType,
239
- hasExclamationToken: field.isRequired || field.isList,
240
- hasQuestionToken: !field.isRequired && !field.isList,
241
- decorators: [{ name: 'Field', arguments: fieldDecoratorArgs }],
242
- });
243
- }
244
- /**
245
- * Get GraphQL and TypeScript types for a field
246
- */
247
- function getFieldTypes(field) {
248
- if (field.isId) {
249
- return { graphqlType: 'ID', tsType: 'string' };
250
- }
251
- if ((0, transformer_1.isScalarField)(field)) {
252
- const graphqlType = types_1.PRISMA_TO_GRAPHQL_SCALAR[field.type] ?? 'String';
253
- const tsType = types_1.PRISMA_TO_TS_TYPE[field.type] ?? 'string';
254
- if (field.type === 'Json') {
255
- return { graphqlType: 'GraphQLJSON', tsType: 'any' };
256
- }
257
- return { graphqlType, tsType };
258
- }
259
- if ((0, transformer_1.isEnumField)(field)) {
260
- return { graphqlType: field.type, tsType: field.type };
261
- }
262
- if ((0, transformer_1.isRelationField)(field)) {
263
- return { graphqlType: field.type, tsType: field.type };
264
- }
265
- return { graphqlType: 'String', tsType: 'string' };
266
- }
267
- /**
268
- * Generate all inputs for a model in one file
269
- */
270
- function generateModelInputs(sourceFile, model, inputTypes, dmmf, _config) {
271
- // Collect all needed imports
272
- const hasJson = inputTypes.some(it => it.fields.some(f => f.type === 'Json'));
273
- const hasBigInt = inputTypes.some(it => it.fields.some(f => f.type === 'BigInt'));
274
- // Add base imports
275
- sourceFile.addImportDeclaration({
276
- moduleSpecifier: '@nestjs/graphql',
277
- namedImports: ['InputType', 'Field', 'Int', 'Float'],
278
- });
279
- if (hasJson) {
280
- sourceFile.addImportDeclaration({
281
- moduleSpecifier: 'graphql-type-json',
282
- namedImports: ['GraphQLJSON'],
283
- });
284
- }
285
- if (hasBigInt) {
286
- sourceFile.addImportDeclaration({
287
- moduleSpecifier: 'graphql-scalars',
288
- namedImports: ['GraphQLBigInt'],
289
- });
290
- }
291
- // Collect enum imports
292
- const enumTypes = new Set();
293
- for (const inputType of inputTypes) {
294
- for (const field of inputType.fields) {
295
- if (dmmf.isEnum(field.type)) {
296
- enumTypes.add(field.type);
297
- }
298
- }
299
- }
300
- if (enumTypes.size > 0) {
301
- sourceFile.addImportDeclaration({
302
- moduleSpecifier: '../enums',
303
- namedImports: [...enumTypes],
304
- });
305
- }
306
- // Generate each input class
307
- for (const inputType of inputTypes) {
308
- generateInputClass(sourceFile, inputType, dmmf, model.name);
309
- }
310
- }
311
- /**
312
- * Generate a single input class
313
- */
314
- function generateInputClass(sourceFile, inputType, dmmf, modelName) {
315
- const classDecl = sourceFile.addClass({
316
- name: inputType.name,
317
- isExported: true,
318
- decorators: [{ name: 'InputType', arguments: [] }],
319
- });
320
- for (const field of inputType.fields) {
321
- addFieldToInputClass(classDecl, field, dmmf, inputType.name, modelName);
322
- }
323
- }
324
- /**
325
- * Add a field to an input class
326
- */
327
- function addFieldToInputClass(classDecl, field, dmmf, currentTypeName, modelName) {
328
- const { graphqlType, tsType, isInputObjectType } = getInputFieldTypes(field, dmmf);
329
- const fieldDecoratorArgs = [];
330
- // For input object types within the same model, use class reference; others use lazy require
331
- if (isInputObjectType && graphqlType !== currentTypeName) {
332
- if (graphqlType.startsWith(modelName)) {
333
- // Same model's input, just reference the class
334
- if (field.isList) {
335
- fieldDecoratorArgs.push(`() => [${graphqlType}]`);
336
- }
337
- else {
338
- fieldDecoratorArgs.push(`() => ${graphqlType}`);
339
- }
340
- }
341
- else {
342
- // Different model's input, use lazy require
343
- const otherModel = graphqlType.replace(/(?:Where|Create|Update|OrderBy|Scalar).*Input$/, '');
344
- if (field.isList) {
345
- fieldDecoratorArgs.push(`() => [require('../${otherModel}/inputs').${graphqlType}]`);
346
- }
347
- else {
348
- fieldDecoratorArgs.push(`() => require('../${otherModel}/inputs').${graphqlType}`);
349
- }
350
- }
351
- }
352
- else {
353
- if (field.isList) {
354
- fieldDecoratorArgs.push(`() => [${graphqlType}]`);
355
- }
356
- else {
357
- fieldDecoratorArgs.push(`() => ${graphqlType}`);
358
- }
359
- }
360
- if (!field.isRequired) {
361
- fieldDecoratorArgs.push('{ nullable: true }');
362
- }
363
- let propertyType = tsType;
364
- if (field.isList) {
365
- propertyType = `${tsType}[]`;
366
- }
367
- if (!field.isRequired) {
368
- propertyType = `${propertyType} | undefined`;
369
- }
370
- classDecl.addProperty({
371
- name: field.name,
372
- type: propertyType,
373
- hasQuestionToken: !field.isRequired,
374
- decorators: [{ name: 'Field', arguments: fieldDecoratorArgs }],
375
- });
376
- }
377
- /**
378
- * Get types for an input field
379
- */
380
- function getInputFieldTypes(field, dmmf) {
381
- const mainType = field.type;
382
- if (types_1.PRISMA_TO_GRAPHQL_SCALAR[mainType]) {
383
- const graphqlType = types_1.PRISMA_TO_GRAPHQL_SCALAR[mainType];
384
- const tsType = types_1.PRISMA_TO_TS_TYPE[mainType];
385
- if (mainType === 'Json') {
386
- return { graphqlType: 'GraphQLJSON', tsType: 'any', isInputObjectType: false };
387
- }
388
- return { graphqlType: graphqlType ?? 'String', tsType: tsType ?? 'string', isInputObjectType: false };
389
- }
390
- if (dmmf.isEnum(mainType)) {
391
- return { graphqlType: mainType, tsType: mainType, isInputObjectType: false };
392
- }
393
- return { graphqlType: mainType, tsType: 'any', isInputObjectType: true };
394
- }
395
- /**
396
- * Generate all args for a model in one file
397
- */
398
- function generateModelArgs(sourceFile, model, _dmmf, _config, available) {
399
- // Add imports
400
- sourceFile.addImportDeclaration({
401
- moduleSpecifier: '@nestjs/graphql',
402
- namedImports: ['ArgsType', 'Field', 'Int'],
403
- });
404
- // Import from local inputs.ts
405
- const inputImports = [];
406
- if (available.hasWhereInput)
407
- inputImports.push(`${model.name}WhereInput`);
408
- if (available.hasWhereUniqueInput)
409
- inputImports.push(`${model.name}WhereUniqueInput`);
410
- if (available.hasOrderByInput)
411
- inputImports.push(`${model.name}OrderByWithRelationInput`);
412
- if (available.hasCreateInput)
413
- inputImports.push(`${model.name}CreateInput`);
414
- if (available.hasCreateManyInput)
415
- inputImports.push(`${model.name}CreateManyInput`);
416
- if (available.hasUpdateInput)
417
- inputImports.push(`${model.name}UpdateInput`);
418
- if (available.hasUpdateManyInput)
419
- inputImports.push(`${model.name}UpdateManyMutationInput`);
420
- if (available.hasScalarWhereWithAggregates)
421
- inputImports.push(`${model.name}ScalarWhereWithAggregatesInput`);
422
- if (inputImports.length > 0) {
423
- sourceFile.addImportDeclaration({
424
- moduleSpecifier: './inputs',
425
- namedImports: inputImports,
426
- });
427
- }
428
- // Import ScalarFieldEnum
429
- sourceFile.addImportDeclaration({
430
- moduleSpecifier: '../enums',
431
- namedImports: [`${model.name}ScalarFieldEnum`],
432
- });
433
- // Generate args classes
434
- if (available.hasWhereInput) {
435
- generateFindManyArgsClass(sourceFile, model, available);
436
- generateFindFirstArgsClass(sourceFile, model, available);
437
- generateDeleteManyArgsClass(sourceFile, model);
438
- generateAggregateArgsClass(sourceFile, model, available);
439
- generateGroupByArgsClass(sourceFile, model, available);
440
- }
441
- if (available.hasWhereUniqueInput) {
442
- generateFindUniqueArgsClass(sourceFile, model);
443
- generateDeleteArgsClass(sourceFile, model);
444
- }
445
- if (available.hasCreateInput) {
446
- generateCreateArgsClass(sourceFile, model);
447
- }
448
- if (available.hasCreateManyInput) {
449
- generateCreateManyArgsClass(sourceFile, model);
450
- }
451
- if (available.hasUpdateInput && available.hasWhereUniqueInput) {
452
- generateUpdateArgsClass(sourceFile, model);
453
- }
454
- if (available.hasUpdateManyInput && available.hasWhereInput) {
455
- generateUpdateManyArgsClass(sourceFile, model);
456
- }
457
- if (available.hasCreateInput && available.hasUpdateInput && available.hasWhereUniqueInput) {
458
- generateUpsertArgsClass(sourceFile, model);
459
- }
460
- }
461
- // Individual args class generators (simplified versions in single file)
462
- function generateFindManyArgsClass(sourceFile, model, available) {
463
- const properties = [
464
- { name: 'where', type: `${model.name}WhereInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereInput`, '{ nullable: true }'] }] },
465
- ];
466
- if (available.hasOrderByInput) {
467
- properties.push({ name: 'orderBy', type: `${model.name}OrderByWithRelationInput[]`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => [${model.name}OrderByWithRelationInput]`, '{ nullable: true }'] }] });
468
- }
469
- if (available.hasWhereUniqueInput) {
470
- properties.push({ name: 'cursor', type: `${model.name}WhereUniqueInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereUniqueInput`, '{ nullable: true }'] }] });
471
- }
472
- properties.push({ name: 'take', type: 'number', hasQuestionToken: true, decorators: [{ name: 'Field', arguments: ['() => Int', '{ nullable: true }'] }] }, { name: 'skip', type: 'number', hasQuestionToken: true, decorators: [{ name: 'Field', arguments: ['() => Int', '{ nullable: true }'] }] }, { name: 'distinct', type: `${model.name}ScalarFieldEnum[]`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => [${model.name}ScalarFieldEnum]`, '{ nullable: true }'] }] });
473
- sourceFile.addClass({ name: `FindMany${model.name}Args`, isExported: true, decorators: [{ name: 'ArgsType', arguments: [] }], properties });
474
- }
475
- function generateFindFirstArgsClass(sourceFile, model, available) {
476
- const properties = [
477
- { name: 'where', type: `${model.name}WhereInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereInput`, '{ nullable: true }'] }] },
478
- ];
479
- if (available.hasOrderByInput) {
480
- properties.push({ name: 'orderBy', type: `${model.name}OrderByWithRelationInput[]`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => [${model.name}OrderByWithRelationInput]`, '{ nullable: true }'] }] });
481
- }
482
- if (available.hasWhereUniqueInput) {
483
- properties.push({ name: 'cursor', type: `${model.name}WhereUniqueInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereUniqueInput`, '{ nullable: true }'] }] });
484
- }
485
- properties.push({ name: 'take', type: 'number', hasQuestionToken: true, decorators: [{ name: 'Field', arguments: ['() => Int', '{ nullable: true }'] }] }, { name: 'skip', type: 'number', hasQuestionToken: true, decorators: [{ name: 'Field', arguments: ['() => Int', '{ nullable: true }'] }] }, { name: 'distinct', type: `${model.name}ScalarFieldEnum[]`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => [${model.name}ScalarFieldEnum]`, '{ nullable: true }'] }] });
486
- sourceFile.addClass({ name: `FindFirst${model.name}Args`, isExported: true, decorators: [{ name: 'ArgsType', arguments: [] }], properties });
487
- }
488
- function generateFindUniqueArgsClass(sourceFile, model) {
489
- sourceFile.addClass({
490
- name: `FindUnique${model.name}Args`,
491
- isExported: true,
492
- decorators: [{ name: 'ArgsType', arguments: [] }],
493
- properties: [{ name: 'where', type: `${model.name}WhereUniqueInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereUniqueInput`] }] }],
494
- });
495
- }
496
- function generateCreateArgsClass(sourceFile, model) {
497
- sourceFile.addClass({
498
- name: `Create${model.name}Args`,
499
- isExported: true,
500
- decorators: [{ name: 'ArgsType', arguments: [] }],
501
- properties: [{ name: 'data', type: `${model.name}CreateInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}CreateInput`] }] }],
502
- });
503
- }
504
- function generateCreateManyArgsClass(sourceFile, model) {
505
- sourceFile.addClass({
506
- name: `CreateMany${model.name}Args`,
507
- isExported: true,
508
- decorators: [{ name: 'ArgsType', arguments: [] }],
509
- properties: [{ name: 'data', type: `${model.name}CreateManyInput[]`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => [${model.name}CreateManyInput]`] }] }],
510
- });
511
- }
512
- function generateUpdateArgsClass(sourceFile, model) {
513
- sourceFile.addClass({
514
- name: `Update${model.name}Args`,
515
- isExported: true,
516
- decorators: [{ name: 'ArgsType', arguments: [] }],
517
- properties: [
518
- { name: 'data', type: `${model.name}UpdateInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}UpdateInput`] }] },
519
- { name: 'where', type: `${model.name}WhereUniqueInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereUniqueInput`] }] },
520
- ],
521
- });
522
- }
523
- function generateUpdateManyArgsClass(sourceFile, model) {
524
- sourceFile.addClass({
525
- name: `UpdateMany${model.name}Args`,
526
- isExported: true,
527
- decorators: [{ name: 'ArgsType', arguments: [] }],
528
- properties: [
529
- { name: 'data', type: `${model.name}UpdateManyMutationInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}UpdateManyMutationInput`] }] },
530
- { name: 'where', type: `${model.name}WhereInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereInput`, '{ nullable: true }'] }] },
531
- ],
532
- });
533
- }
534
- function generateUpsertArgsClass(sourceFile, model) {
535
- sourceFile.addClass({
536
- name: `Upsert${model.name}Args`,
537
- isExported: true,
538
- decorators: [{ name: 'ArgsType', arguments: [] }],
539
- properties: [
540
- { name: 'where', type: `${model.name}WhereUniqueInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereUniqueInput`] }] },
541
- { name: 'create', type: `${model.name}CreateInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}CreateInput`] }] },
542
- { name: 'update', type: `${model.name}UpdateInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}UpdateInput`] }] },
543
- ],
544
- });
545
- }
546
- function generateDeleteArgsClass(sourceFile, model) {
547
- sourceFile.addClass({
548
- name: `Delete${model.name}Args`,
549
- isExported: true,
550
- decorators: [{ name: 'ArgsType', arguments: [] }],
551
- properties: [{ name: 'where', type: `${model.name}WhereUniqueInput`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereUniqueInput`] }] }],
552
- });
553
- }
554
- function generateDeleteManyArgsClass(sourceFile, model) {
555
- sourceFile.addClass({
556
- name: `DeleteMany${model.name}Args`,
557
- isExported: true,
558
- decorators: [{ name: 'ArgsType', arguments: [] }],
559
- properties: [{ name: 'where', type: `${model.name}WhereInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereInput`, '{ nullable: true }'] }] }],
560
- });
561
- }
562
- function generateAggregateArgsClass(sourceFile, model, available) {
563
- const properties = [
564
- { name: 'where', type: `${model.name}WhereInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereInput`, '{ nullable: true }'] }] },
565
- ];
566
- if (available.hasOrderByInput) {
567
- properties.push({ name: 'orderBy', type: `${model.name}OrderByWithRelationInput[]`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => [${model.name}OrderByWithRelationInput]`, '{ nullable: true }'] }] });
568
- }
569
- if (available.hasWhereUniqueInput) {
570
- properties.push({ name: 'cursor', type: `${model.name}WhereUniqueInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereUniqueInput`, '{ nullable: true }'] }] });
571
- }
572
- properties.push({ name: 'take', type: 'number', hasQuestionToken: true, decorators: [{ name: 'Field', arguments: ['() => Int', '{ nullable: true }'] }] }, { name: 'skip', type: 'number', hasQuestionToken: true, decorators: [{ name: 'Field', arguments: ['() => Int', '{ nullable: true }'] }] });
573
- sourceFile.addClass({ name: `Aggregate${model.name}Args`, isExported: true, decorators: [{ name: 'ArgsType', arguments: [] }], properties });
574
- }
575
- function generateGroupByArgsClass(sourceFile, model, available) {
576
- const properties = [
577
- { name: 'where', type: `${model.name}WhereInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}WhereInput`, '{ nullable: true }'] }] },
578
- ];
579
- if (available.hasOrderByInput) {
580
- properties.push({ name: 'orderBy', type: `${model.name}OrderByWithRelationInput[]`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => [${model.name}OrderByWithRelationInput]`, '{ nullable: true }'] }] });
581
- }
582
- properties.push({ name: 'by', type: `${model.name}ScalarFieldEnum[]`, hasExclamationToken: true, decorators: [{ name: 'Field', arguments: [`() => [${model.name}ScalarFieldEnum]`] }] });
583
- if (available.hasScalarWhereWithAggregates) {
584
- properties.push({ name: 'having', type: `${model.name}ScalarWhereWithAggregatesInput`, hasQuestionToken: true, decorators: [{ name: 'Field', arguments: [`() => ${model.name}ScalarWhereWithAggregatesInput`, '{ nullable: true }'] }] });
585
- }
586
- properties.push({ name: 'take', type: 'number', hasQuestionToken: true, decorators: [{ name: 'Field', arguments: ['() => Int', '{ nullable: true }'] }] }, { name: 'skip', type: 'number', hasQuestionToken: true, decorators: [{ name: 'Field', arguments: ['() => Int', '{ nullable: true }'] }] });
587
- sourceFile.addClass({ name: `GroupBy${model.name}Args`, isExported: true, decorators: [{ name: 'ArgsType', arguments: [] }], properties });
588
- }
589
- /**
590
- * Generate resolver for a model
591
- */
592
- function generateModelResolver(sourceFile, model, _dmmf, _config, ops) {
593
- const modelName = model.name;
594
- const lowerModelName = (0, transformer_1.camelCase)(modelName);
595
- const pluralName = (0, pluralize_1.default)(lowerModelName);
596
- const isAlreadyPlural = pluralName === lowerModelName;
597
- const findManyMethodName = isAlreadyPlural ? `findMany${modelName}` : pluralName;
598
- const findUniqueMethodName = isAlreadyPlural ? `findUnique${modelName}` : lowerModelName;
599
- // Build imports
600
- const nestjsImports = ['Resolver', 'Query', 'Args', 'Info', 'Int'];
601
- if (ops.hasCreateInput || ops.hasUpdateInput) {
602
- nestjsImports.push('Mutation');
603
- }
604
- sourceFile.addImportDeclaration({
605
- moduleSpecifier: '@nestjs/graphql',
606
- namedImports: nestjsImports,
607
- });
608
- sourceFile.addImportDeclaration({
609
- moduleSpecifier: 'graphql',
610
- namedImports: ['GraphQLResolveInfo'],
611
- });
612
- sourceFile.addImportDeclaration({
613
- moduleSpecifier: './model',
614
- namedImports: [modelName],
615
- });
616
- sourceFile.addImportDeclaration({
617
- moduleSpecifier: '../common/AffectedRows',
618
- namedImports: ['AffectedRows'],
619
- });
620
- sourceFile.addImportDeclaration({
621
- moduleSpecifier: '../helpers',
622
- namedImports: ['transformInfoIntoPrismaArgs', 'getPrismaFromContext'],
623
- });
624
- // Import args
625
- const argsImports = [];
626
- if (ops.hasWhereInput) {
627
- argsImports.push(`FindMany${modelName}Args`, `FindFirst${modelName}Args`, `DeleteMany${modelName}Args`);
628
- }
629
- if (ops.hasWhereUniqueInput) {
630
- argsImports.push(`FindUnique${modelName}Args`, `Delete${modelName}Args`);
631
- }
632
- if (ops.hasCreateInput)
633
- argsImports.push(`Create${modelName}Args`);
634
- if (ops.hasCreateManyInput)
635
- argsImports.push(`CreateMany${modelName}Args`);
636
- if (ops.hasUpdateInput && ops.hasWhereUniqueInput)
637
- argsImports.push(`Update${modelName}Args`);
638
- if (ops.hasUpdateManyInput && ops.hasWhereInput)
639
- argsImports.push(`UpdateMany${modelName}Args`);
640
- if (ops.hasCreateInput && ops.hasUpdateInput && ops.hasWhereUniqueInput)
641
- argsImports.push(`Upsert${modelName}Args`);
642
- if (argsImports.length > 0) {
643
- sourceFile.addImportDeclaration({
644
- moduleSpecifier: './args',
645
- namedImports: argsImports,
646
- });
647
- }
648
- // Create resolver class
649
- const resolverClass = sourceFile.addClass({
650
- name: `${modelName}Resolver`,
651
- isExported: true,
652
- decorators: [{ name: 'Resolver', arguments: [`() => ${modelName}`] }],
653
- });
654
- // Add query methods
655
- if (ops.hasWhereInput) {
656
- addResolverMethod(resolverClass, 'Query', findManyMethodName, `FindMany${modelName}Args`, `Promise<${modelName}[]>`, `[${modelName}]`, lowerModelName, 'findMany');
657
- addResolverMethod(resolverClass, 'Query', `findFirst${modelName}`, `FindFirst${modelName}Args`, `Promise<${modelName} | null>`, modelName, lowerModelName, 'findFirst', true);
658
- }
659
- if (ops.hasWhereUniqueInput) {
660
- addResolverMethod(resolverClass, 'Query', findUniqueMethodName, `FindUnique${modelName}Args`, `Promise<${modelName} | null>`, modelName, lowerModelName, 'findUnique', true);
661
- }
662
- // Add mutation methods
663
- if (ops.hasCreateInput) {
664
- addResolverMethod(resolverClass, 'Mutation', `createOne${modelName}`, `Create${modelName}Args`, `Promise<${modelName}>`, modelName, lowerModelName, 'create');
665
- }
666
- if (ops.hasCreateManyInput) {
667
- addResolverMethod(resolverClass, 'Mutation', `createMany${modelName}`, `CreateMany${modelName}Args`, 'Promise<AffectedRows>', 'AffectedRows', lowerModelName, 'createMany');
668
- }
669
- if (ops.hasUpdateInput && ops.hasWhereUniqueInput) {
670
- addResolverMethod(resolverClass, 'Mutation', `updateOne${modelName}`, `Update${modelName}Args`, `Promise<${modelName} | null>`, modelName, lowerModelName, 'update', true);
671
- }
672
- if (ops.hasUpdateManyInput && ops.hasWhereInput) {
673
- addResolverMethod(resolverClass, 'Mutation', `updateMany${modelName}`, `UpdateMany${modelName}Args`, 'Promise<AffectedRows>', 'AffectedRows', lowerModelName, 'updateMany');
674
- }
675
- if (ops.hasCreateInput && ops.hasUpdateInput && ops.hasWhereUniqueInput) {
676
- addResolverMethod(resolverClass, 'Mutation', `upsertOne${modelName}`, `Upsert${modelName}Args`, `Promise<${modelName}>`, modelName, lowerModelName, 'upsert');
677
- }
678
- if (ops.hasWhereUniqueInput) {
679
- addResolverMethod(resolverClass, 'Mutation', `deleteOne${modelName}`, `Delete${modelName}Args`, `Promise<${modelName} | null>`, modelName, lowerModelName, 'delete', true);
680
- }
681
- if (ops.hasWhereInput) {
682
- addResolverMethod(resolverClass, 'Mutation', `deleteMany${modelName}`, `DeleteMany${modelName}Args`, 'Promise<AffectedRows>', 'AffectedRows', lowerModelName, 'deleteMany');
683
- }
684
- }
685
- function addResolverMethod(classDecl, decoratorType, methodName, argsType, returnType, graphqlReturnType, prismaModel, prismaMethod, nullable = false) {
686
- const returnTypeArg = nullable ? `{ nullable: true }` : '';
687
- classDecl.addMethod({
688
- name: methodName,
689
- isAsync: true,
690
- decorators: [
691
- {
692
- name: decoratorType,
693
- arguments: returnTypeArg
694
- ? [`() => ${graphqlReturnType}`, returnTypeArg]
695
- : [`() => ${graphqlReturnType}`],
696
- },
697
- ],
698
- parameters: [
699
- { name: 'args', type: argsType, decorators: [{ name: 'Args', arguments: [] }] },
700
- { name: 'info', type: 'GraphQLResolveInfo', decorators: [{ name: 'Info', arguments: [] }] },
701
- ],
702
- returnType,
703
- statements: [
704
- `const select = transformInfoIntoPrismaArgs(info);`,
705
- `const prisma = getPrismaFromContext(info);`,
706
- `return prisma.${prismaModel}.${prismaMethod}({ ...args, ...select });`,
707
- ],
708
- });
709
- }
710
- /**
711
- * Generate model folder index
712
- */
713
- function generateModelIndex(sourceFile, _model, config, hasInputs) {
714
- sourceFile.addExportDeclaration({ moduleSpecifier: './model' });
715
- if (hasInputs) {
716
- sourceFile.addExportDeclaration({ moduleSpecifier: './inputs' });
717
- }
718
- sourceFile.addExportDeclaration({ moduleSpecifier: './args' });
719
- if (config.generateResolvers) {
720
- sourceFile.addExportDeclaration({ moduleSpecifier: './resolver' });
721
- }
722
- }
723
- /**
724
- * Generate shared enums
725
- */
726
- function generateEnumsGrouped(project, dmmf) {
727
- const files = new Map();
728
- for (const enumDef of dmmf.enums) {
729
- const filePath = `enums/${enumDef.name}.ts`;
730
- const sourceFile = project.createSourceFile(filePath, '', { overwrite: true });
731
- sourceFile.addImportDeclaration({
732
- moduleSpecifier: '@nestjs/graphql',
733
- namedImports: ['registerEnumType'],
734
- });
735
- sourceFile.addEnum({
736
- name: enumDef.name,
737
- isExported: true,
738
- members: enumDef.values.map(v => ({ name: v.name, value: `'${v.name}'` })),
739
- });
740
- sourceFile.addStatements(`
741
- registerEnumType(${enumDef.name}, {
742
- name: '${enumDef.name}',
743
- description: ${enumDef.documentation ? `'${escapeDescription(enumDef.documentation)}'` : 'undefined'},
744
- });
745
- `);
746
- files.set(filePath, sourceFile);
747
- }
748
- // Generate index
749
- const indexFile = project.createSourceFile('enums/index.ts', '', { overwrite: true });
750
- for (const enumDef of dmmf.enums) {
751
- indexFile.addExportDeclaration({ moduleSpecifier: `./${enumDef.name}` });
752
- }
753
- files.set('enums/index.ts', indexFile);
754
- return files;
755
- }
756
- /**
757
- * Generate common types
758
- */
759
- function generateCommonTypesGrouped(project) {
760
- const files = new Map();
761
- const affectedRowsFile = project.createSourceFile('common/AffectedRows.ts', '', { overwrite: true });
762
- affectedRowsFile.addImportDeclaration({
763
- moduleSpecifier: '@nestjs/graphql',
764
- namedImports: ['ObjectType', 'Field', 'Int'],
765
- });
766
- affectedRowsFile.addClass({
767
- name: 'AffectedRows',
768
- isExported: true,
769
- decorators: [{ name: 'ObjectType', arguments: [] }],
770
- properties: [
771
- {
772
- name: 'count',
773
- type: 'number',
774
- hasExclamationToken: true,
775
- decorators: [{ name: 'Field', arguments: ['() => Int'] }],
776
- },
777
- ],
778
- });
779
- files.set('common/AffectedRows.ts', affectedRowsFile);
780
- const indexFile = project.createSourceFile('common/index.ts', '', { overwrite: true });
781
- indexFile.addExportDeclaration({ moduleSpecifier: './AffectedRows' });
782
- files.set('common/index.ts', indexFile);
783
- return files;
784
- }
785
- /**
786
- * Generate helpers file
787
- */
788
- function generateHelpersGrouped(project, _config) {
789
- const sourceFile = project.createSourceFile('helpers.ts', '', { overwrite: true });
790
- sourceFile.addStatements(`
791
- import { parseResolveInfo, ResolveTree, FieldsByTypeName } from 'graphql-parse-resolve-info';
792
- import type { GraphQLResolveInfo } from 'graphql';
793
-
794
- export function transformInfoIntoPrismaArgs(info: GraphQLResolveInfo): { select?: Record<string, any>; include?: Record<string, any> } {
795
- const parsedInfo = parseResolveInfo(info) as ResolveTree | null;
796
- if (!parsedInfo) return {};
797
-
798
- const select = buildPrismaSelect(parsedInfo.fieldsByTypeName);
799
- return Object.keys(select).length > 0 ? { select } : {};
800
- }
801
-
802
- function buildPrismaSelect(fieldsByTypeName: FieldsByTypeName): Record<string, any> {
803
- const result: Record<string, any> = {};
804
-
805
- for (const typeName in fieldsByTypeName) {
806
- const fields = fieldsByTypeName[typeName];
807
- for (const fieldName in fields) {
808
- if (fieldName.startsWith('__') || fieldName.startsWith('_count') || fieldName.startsWith('_avg') || fieldName.startsWith('_sum') || fieldName.startsWith('_min') || fieldName.startsWith('_max')) continue;
809
-
810
- const field = fields[fieldName];
811
- const nestedFields = field.fieldsByTypeName;
812
-
813
- if (Object.keys(nestedFields).length > 0) {
814
- const nestedSelect = buildPrismaSelect(nestedFields);
815
- result[fieldName] = Object.keys(nestedSelect).length > 0 ? { select: nestedSelect } : true;
816
- } else {
817
- result[fieldName] = true;
818
- }
819
- }
820
- }
821
-
822
- return result;
823
- }
824
-
825
- export function getPrismaFromContext(info: GraphQLResolveInfo): any {
826
- const context = (info.rootValue as any)?.context ?? info.rootValue;
827
- const prisma = context?.prisma ?? context?.db;
828
- if (!prisma) throw new Error('Prisma client not found in context. Please provide prisma in your GraphQL context.');
829
- return prisma;
830
- }
831
-
832
- export function mergePrismaSelects(...selects: Array<{ select?: Record<string, any>; include?: Record<string, any> }>): { select?: Record<string, any>; include?: Record<string, any> } {
833
- const result: { select?: Record<string, any>; include?: Record<string, any> } = {};
834
- for (const s of selects) {
835
- if (s.select) result.select = { ...(result.select ?? {}), ...s.select };
836
- if (s.include) result.include = { ...(result.include ?? {}), ...s.include };
837
- }
838
- return result;
839
- }
840
- `);
841
- return sourceFile;
842
- }
843
- /**
844
- * Generate root index
845
- */
846
- function generateRootIndexGrouped(sourceFile, dmmf, _config) {
847
- sourceFile.addExportDeclaration({ moduleSpecifier: './enums' });
848
- sourceFile.addExportDeclaration({ moduleSpecifier: './common' });
849
- sourceFile.addExportDeclaration({ moduleSpecifier: './helpers' });
850
- for (const model of dmmf.models) {
851
- const inputTypeNames = new Set(dmmf.inputTypes.keys());
852
- const hasWhereInput = inputTypeNames.has(`${model.name}WhereInput`);
853
- const hasWhereUniqueInput = inputTypeNames.has(`${model.name}WhereUniqueInput`);
854
- if (hasWhereInput || hasWhereUniqueInput) {
855
- sourceFile.addExportDeclaration({ moduleSpecifier: `./${model.name}` });
856
- }
857
- }
858
- }
859
- function escapeDescription(text) {
860
- return text.replace(/'/g, "\\'").replace(/\n/g, '\\n');
861
- }
862
- //# sourceMappingURL=generate-grouped.js.map