@pothos/plugin-prisma 3.52.0 → 3.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +21 -0
  3. package/dts/global-types.d.ts +3 -0
  4. package/dts/global-types.d.ts.map +1 -1
  5. package/dts/prisma-field-builder.d.ts +25 -23
  6. package/dts/prisma-field-builder.d.ts.map +1 -1
  7. package/dts/util/map-query.d.ts +2 -1
  8. package/dts/util/map-query.d.ts.map +1 -1
  9. package/dts/util/usage.d.ts +4 -0
  10. package/dts/util/usage.d.ts.map +1 -0
  11. package/esm/connection-helpers.js +2 -1
  12. package/esm/connection-helpers.js.map +1 -1
  13. package/esm/field-builder.js +65 -23
  14. package/esm/field-builder.js.map +1 -1
  15. package/esm/generator.js +2 -2
  16. package/esm/generator.js.map +1 -1
  17. package/esm/global-types.d.ts +3 -0
  18. package/esm/global-types.d.ts.map +1 -1
  19. package/esm/global-types.js.map +1 -1
  20. package/esm/index.js.map +1 -1
  21. package/esm/model-loader.js +10 -12
  22. package/esm/model-loader.js.map +1 -1
  23. package/esm/node-ref.js.map +1 -1
  24. package/esm/object-ref.js +4 -4
  25. package/esm/object-ref.js.map +1 -1
  26. package/esm/prisma-field-builder.d.ts +85 -43
  27. package/esm/prisma-field-builder.d.ts.map +1 -1
  28. package/esm/prisma-field-builder.js +22 -20
  29. package/esm/prisma-field-builder.js.map +1 -1
  30. package/esm/schema-builder.js +1 -1
  31. package/esm/schema-builder.js.map +1 -1
  32. package/esm/util/cursors.js +2 -2
  33. package/esm/util/cursors.js.map +1 -1
  34. package/esm/util/datamodel.js.map +1 -1
  35. package/esm/util/deep-equal.js +2 -1
  36. package/esm/util/deep-equal.js.map +1 -1
  37. package/esm/util/description.js +4 -2
  38. package/esm/util/description.js.map +1 -1
  39. package/esm/util/get-client.js.map +1 -1
  40. package/esm/util/loader-map.js.map +1 -1
  41. package/esm/util/map-query.d.ts +2 -1
  42. package/esm/util/map-query.d.ts.map +1 -1
  43. package/esm/util/map-query.js +12 -7
  44. package/esm/util/map-query.js.map +1 -1
  45. package/esm/util/relation-map.js.map +1 -1
  46. package/esm/util/selections.js.map +1 -1
  47. package/esm/util/usage.d.ts +4 -0
  48. package/esm/util/usage.d.ts.map +1 -0
  49. package/esm/util/usage.js +29 -0
  50. package/esm/util/usage.js.map +1 -0
  51. package/lib/connection-helpers.js +13 -8
  52. package/lib/connection-helpers.js.map +1 -1
  53. package/lib/field-builder.js +76 -35
  54. package/lib/field-builder.js.map +1 -1
  55. package/lib/generator.js +11 -11
  56. package/lib/generator.js.map +1 -1
  57. package/lib/index.js +58 -34
  58. package/lib/index.js.map +1 -1
  59. package/lib/model-loader.js +14 -14
  60. package/lib/model-loader.js.map +1 -1
  61. package/lib/node-ref.js +5 -3
  62. package/lib/node-ref.js.map +1 -1
  63. package/lib/object-ref.js +10 -6
  64. package/lib/object-ref.js.map +1 -1
  65. package/lib/prisma-field-builder.js +34 -30
  66. package/lib/prisma-field-builder.js.map +1 -1
  67. package/lib/schema-builder.js +25 -25
  68. package/lib/schema-builder.js.map +1 -1
  69. package/lib/types.js +3 -1
  70. package/lib/types.js.map +1 -1
  71. package/lib/util/cursors.js +46 -18
  72. package/lib/util/cursors.js.map +1 -1
  73. package/lib/util/datamodel.js +28 -12
  74. package/lib/util/datamodel.js.map +1 -1
  75. package/lib/util/deep-equal.js +6 -3
  76. package/lib/util/deep-equal.js.map +1 -1
  77. package/lib/util/description.js +12 -6
  78. package/lib/util/description.js.map +1 -1
  79. package/lib/util/get-client.js +6 -2
  80. package/lib/util/get-client.js.map +1 -1
  81. package/lib/util/loader-map.js +9 -3
  82. package/lib/util/loader-map.js.map +1 -1
  83. package/lib/util/map-query.js +25 -14
  84. package/lib/util/map-query.js.map +1 -1
  85. package/lib/util/relation-map.js +7 -3
  86. package/lib/util/relation-map.js.map +1 -1
  87. package/lib/util/selections.js +24 -12
  88. package/lib/util/selections.js.map +1 -1
  89. package/lib/util/usage.js +51 -0
  90. package/lib/util/usage.js.map +1 -0
  91. package/package.json +8 -8
  92. package/src/field-builder.ts +90 -15
  93. package/src/generator.ts +2 -3
  94. package/src/global-types.ts +3 -2
  95. package/src/prisma-field-builder.ts +28 -18
  96. package/src/util/map-query.ts +6 -1
  97. package/src/util/usage.ts +33 -0
@@ -3,8 +3,10 @@ import {
3
3
  FieldKind,
4
4
  FieldRef,
5
5
  InputFieldMap,
6
+ isThenable,
6
7
  MaybePromise,
7
8
  ObjectRef,
9
+ PothosError,
8
10
  RootFieldBuilder,
9
11
  SchemaTypes,
10
12
  } from '@pothos/core';
@@ -13,6 +15,7 @@ import { PrismaConnectionFieldOptions, PrismaModelTypes } from './types';
13
15
  import { getCursorFormatter, getCursorParser, resolvePrismaCursorConnection } from './util/cursors';
14
16
  import { getRefFromModel } from './util/datamodel';
15
17
  import { queryFromInfo } from './util/map-query';
18
+ import { isUsed } from './util/usage';
16
19
 
17
20
  const fieldBuilderProto = RootFieldBuilder.prototype as PothosSchemaTypes.RootFieldBuilder<
18
21
  SchemaTypes,
@@ -32,9 +35,18 @@ fieldBuilderProto.prismaField = function prismaField({ type, resolve, ...options
32
35
  ...(options as {}),
33
36
  type: typeParam,
34
37
  resolve: (parent: never, args: unknown, context: {}, info: GraphQLResolveInfo) => {
35
- const query = queryFromInfo({ context, info });
38
+ const query = queryFromInfo({
39
+ context,
40
+ info,
41
+ withUsageCheck: !!this.builder.options.prisma?.onUnusedQuery,
42
+ });
36
43
 
37
- return resolve(query, parent, args as never, context, info) as never;
44
+ return checkIfQueryIsUsed(
45
+ this.builder,
46
+ query,
47
+ info,
48
+ resolve(query, parent, args as never, context, info) as never,
49
+ );
38
50
  },
39
51
  }) as never;
40
52
  };
@@ -57,9 +69,18 @@ fieldBuilderProto.prismaFieldWithInput = function prismaFieldWithInput({
57
69
  ...(options as {}),
58
70
  type: typeParam,
59
71
  resolve: (parent: never, args: unknown, context: {}, info: GraphQLResolveInfo) => {
60
- const query = queryFromInfo({ context, info });
72
+ const query = queryFromInfo({
73
+ context,
74
+ info,
75
+ withUsageCheck: !!this.builder.options.prisma?.onUnusedQuery,
76
+ });
61
77
 
62
- return resolve(query, parent, args as never, context, info) as never;
78
+ return checkIfQueryIsUsed(
79
+ this.builder,
80
+ query,
81
+ info,
82
+ resolve(query, parent, args as never, context, info) as never,
83
+ );
63
84
  },
64
85
  }) as never;
65
86
  };
@@ -114,17 +135,20 @@ fieldBuilderProto.prismaConnection = function prismaConnection<
114
135
  args: PothosSchemaTypes.DefaultConnectionArguments,
115
136
  context: {},
116
137
  info: GraphQLResolveInfo,
117
- ) =>
118
- resolvePrismaCursorConnection(
138
+ ) => {
139
+ const query = queryFromInfo({
140
+ context,
141
+ info,
142
+ select: cursorSelection as {},
143
+ paths: [['nodes'], ['edges', 'node']],
144
+ typeName,
145
+ withUsageCheck: !!this.builder.options.prisma?.onUnusedQuery,
146
+ });
147
+
148
+ return resolvePrismaCursorConnection(
119
149
  {
120
150
  parent,
121
- query: queryFromInfo({
122
- context,
123
- info,
124
- select: cursorSelection as {},
125
- paths: [['nodes'], ['edges', 'node']],
126
- typeName,
127
- }),
151
+ query,
128
152
  ctx: context,
129
153
  parseCursor,
130
154
  maxSize,
@@ -133,8 +157,15 @@ fieldBuilderProto.prismaConnection = function prismaConnection<
133
157
  totalCount: totalCount && (() => totalCount(parent, args as never, context, info)),
134
158
  },
135
159
  formatCursor,
136
- (query) => resolve(query as never, parent, args as never, context, info) as never,
137
- ),
160
+ (q) =>
161
+ checkIfQueryIsUsed(
162
+ this.builder,
163
+ query,
164
+ info,
165
+ resolve(q as never, parent, args as never, context, info) as never,
166
+ ),
167
+ );
168
+ },
138
169
  },
139
170
  connectionOptions instanceof ObjectRef
140
171
  ? connectionOptions
@@ -163,3 +194,47 @@ fieldBuilderProto.prismaConnection = function prismaConnection<
163
194
 
164
195
  return fieldRef;
165
196
  } as never;
197
+
198
+ function checkIfQueryIsUsed<Types extends SchemaTypes, T>(
199
+ builder: PothosSchemaTypes.SchemaBuilder<Types>,
200
+ query: object,
201
+ info: GraphQLResolveInfo,
202
+ result: T,
203
+ ): T {
204
+ const { onUnusedQuery } = builder.options.prisma || {};
205
+ if (!onUnusedQuery) {
206
+ return result;
207
+ }
208
+
209
+ if (isThenable(result)) {
210
+ return result.then((resolved) => {
211
+ if (!isUsed(query)) {
212
+ onUnused();
213
+ }
214
+
215
+ return resolved;
216
+ }) as T;
217
+ }
218
+
219
+ if (!isUsed(query)) {
220
+ onUnused();
221
+ }
222
+
223
+ return result;
224
+
225
+ function onUnused() {
226
+ if (typeof onUnusedQuery === 'function') {
227
+ onUnusedQuery(info);
228
+ return;
229
+ }
230
+
231
+ const message = `Prisma query was unused in resolver for ${info.parentType.name}.${info.fieldName}`;
232
+
233
+ if (onUnusedQuery === 'error') {
234
+ throw new PothosError(message);
235
+ } else if (onUnusedQuery === 'warn') {
236
+ // eslint-disable-next-line no-console
237
+ console.warn(message);
238
+ }
239
+ }
240
+ }
package/src/generator.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  /* eslint-disable unicorn/prefer-module */
2
- /* eslint-disable node/prefer-promises/fs */
3
2
  /* eslint-disable no-magic-numbers */
4
3
  /* eslint-disable no-nested-ternary */
5
- import { mkdir, writeFile } from 'fs';
6
- import { dirname, join, resolve as resolvePath } from 'path';
4
+ import { mkdir, writeFile } from 'node:fs';
5
+ import { dirname, join, resolve as resolvePath } from 'node:path';
7
6
  import ts, { ListFormat, ScriptKind, ScriptTarget, SyntaxKind, version } from 'typescript';
8
7
  import { DMMF, generatorHandler } from '@prisma/generator-helper';
9
8
 
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  /* eslint-disable @typescript-eslint/no-empty-interface */
3
- import { A } from 'vitest/dist/types-fafda418';
3
+ import { GraphQLResolveInfo } from 'graphql';
4
4
  import {
5
5
  FieldKind,
6
6
  FieldMap,
@@ -8,7 +8,6 @@ import {
8
8
  FieldRef,
9
9
  InputFieldMap,
10
10
  InputFieldRef,
11
- InputShapeFromFields,
12
11
  InterfaceParam,
13
12
  NormalizeArgs,
14
13
  OutputType,
@@ -54,6 +53,7 @@ declare global {
54
53
  models?: boolean;
55
54
  fields?: boolean;
56
55
  };
56
+ onUnusedQuery?: null | 'warn' | 'error' | ((info: GraphQLResolveInfo) => void);
57
57
  }
58
58
  | {
59
59
  filterConnectionTotalCount?: boolean;
@@ -65,6 +65,7 @@ declare global {
65
65
  models?: boolean;
66
66
  fields?: boolean;
67
67
  };
68
+ onUnusedQuery?: null | 'warn' | 'error' | ((info: GraphQLResolveInfo) => void);
68
69
  };
69
70
  }
70
71
 
@@ -3,6 +3,7 @@
3
3
  import { FieldNode, GraphQLResolveInfo } from 'graphql';
4
4
  import {
5
5
  CompatibleTypes,
6
+ ExposeNullability,
6
7
  FieldKind,
7
8
  FieldRef,
8
9
  InputFieldMap,
@@ -525,7 +526,7 @@ export class PrismaObjectFieldBuilder<
525
526
  Type extends TypeParam<Types>,
526
527
  Nullable extends boolean,
527
528
  ResolveReturnShape,
528
- Name extends CompatibleTypes<Types, Model['Shape'], Type, Nullable>,
529
+ Name extends CompatibleTypes<Types, Model['Shape'], Type, true>,
529
530
  >(
530
531
  ...args: NormalizeArgs<
531
532
  [
@@ -539,8 +540,11 @@ export class PrismaObjectFieldBuilder<
539
540
  {},
540
541
  ResolveReturnShape
541
542
  >,
542
- 'resolve' | 'select'
543
- >,
543
+ 'resolve' | 'select' | 'description' | 'nullable'
544
+ > &
545
+ ExposeNullability<Types, Type, Model['Shape'], Name, Nullable> & {
546
+ description?: string | false;
547
+ },
544
548
  ]
545
549
  >
546
550
  ) {
@@ -549,13 +553,19 @@ export class PrismaObjectFieldBuilder<
549
553
  const typeConfig = this.builder.configStore.getTypeConfig(this.typename, 'Object');
550
554
  const usingSelect = !!typeConfig.extensions?.pothosPrismaSelect;
551
555
 
552
- return this.exposeField(name as never, {
556
+ return this.exposeField<Type, Nullable, never>(name as never, {
553
557
  ...options,
558
+ description: getFieldDescription(
559
+ this.model,
560
+ this.builder,
561
+ name as string,
562
+ options.description,
563
+ ) as never,
554
564
  extensions: {
555
565
  ...options.extensions,
556
566
  pothosPrismaVariant: name,
557
567
  pothosPrismaSelect: usingSelect && {
558
- [name]: true,
568
+ [name as string]: true,
559
569
  },
560
570
  },
561
571
  });
@@ -565,7 +575,7 @@ export class PrismaObjectFieldBuilder<
565
575
  return <
566
576
  Nullable extends boolean,
567
577
  ResolveReturnShape,
568
- Name extends CompatibleTypes<Types, Model['Shape'], Type, Nullable>,
578
+ Name extends CompatibleTypes<Types, Model['Shape'], Type, true>,
569
579
  >(
570
580
  ...args: NormalizeArgs<
571
581
  [
@@ -579,23 +589,23 @@ export class PrismaObjectFieldBuilder<
579
589
  {},
580
590
  ResolveReturnShape
581
591
  >,
582
- 'resolve' | 'type' | 'select' | 'description'
583
- > & { description?: string | false },
592
+ 'resolve' | 'type' | 'select' | 'description' | 'nullable'
593
+ > &
594
+ ExposeNullability<Types, Type, Model['Shape'], Name, Nullable> & {
595
+ description?: string | false;
596
+ },
584
597
  ]
585
598
  >
586
599
  ): FieldRef<ShapeFromTypeParam<Types, Type, Nullable>, 'PrismaObject'> => {
587
600
  const [name, { description, ...options } = {} as never] = args;
588
601
 
589
- return this.expose(name as never, {
590
- ...options,
591
- description: getFieldDescription(
592
- this.model,
593
- this.builder,
594
- name as string,
595
- description,
596
- ) as never,
597
- type,
598
- });
602
+ return this.expose<Type, Nullable, ResolveReturnShape, never>(
603
+ name as never,
604
+ {
605
+ ...options,
606
+ type,
607
+ } as never,
608
+ );
599
609
  };
600
610
  }
601
611
  }
@@ -31,6 +31,7 @@ import {
31
31
  SelectionState,
32
32
  selectionToQuery,
33
33
  } from './selections';
34
+ import { wrapWithUsageCheck } from './usage';
34
35
 
35
36
  function addTypeSelectionsForField(
36
37
  type: GraphQLNamedType,
@@ -370,6 +371,7 @@ export function queryFromInfo<T extends SelectionMap['select'] | undefined = und
370
371
  select,
371
372
  path = [],
372
373
  paths = [],
374
+ withUsageCheck = false,
373
375
  }: {
374
376
  context: object;
375
377
  info: GraphQLResolveInfo;
@@ -377,6 +379,7 @@ export function queryFromInfo<T extends SelectionMap['select'] | undefined = und
377
379
  select?: T;
378
380
  path?: string[];
379
381
  paths?: string[][];
382
+ withUsageCheck?: boolean;
380
383
  }): { select: T } | { include?: {} } {
381
384
  const returnType = getNamedType(info.returnType);
382
385
  const type = typeName ? info.schema.getTypeMap()[typeName] : returnType;
@@ -437,7 +440,9 @@ export function queryFromInfo<T extends SelectionMap['select'] | undefined = und
437
440
 
438
441
  setLoaderMappings(context, info, state.mappings);
439
442
 
440
- return selectionToQuery(state) as { select: T };
443
+ const query = selectionToQuery(state) as { select: T };
444
+
445
+ return withUsageCheck ? wrapWithUsageCheck(query) : query;
441
446
  }
442
447
 
443
448
  export function selectionStateFromInfo(
@@ -0,0 +1,33 @@
1
+ export const usageSymbol = Symbol.for('Pothos.isUsed');
2
+
3
+ export function wrapWithUsageCheck<T extends Object>(obj: T): T {
4
+ const result = {};
5
+ let used = true;
6
+
7
+ Object.defineProperty(result, usageSymbol, {
8
+ get() {
9
+ return used;
10
+ },
11
+ enumerable: false,
12
+ });
13
+
14
+ for (const key of Object.keys(obj)) {
15
+ // only set to false if the object has keys
16
+ used = false;
17
+ Object.defineProperty(result, key, {
18
+ enumerable: true,
19
+ configurable: true,
20
+ // eslint-disable-next-line @typescript-eslint/no-loop-func
21
+ get() {
22
+ used = true;
23
+ return obj[key as keyof T];
24
+ },
25
+ });
26
+ }
27
+
28
+ return result as T;
29
+ }
30
+
31
+ export function isUsed(obj: object): boolean {
32
+ return !(usageSymbol in obj) || (obj as { [usageSymbol]: boolean })[usageSymbol];
33
+ }