@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.
- package/CHANGELOG.md +12 -0
- package/README.md +21 -0
- package/dts/global-types.d.ts +3 -0
- package/dts/global-types.d.ts.map +1 -1
- package/dts/prisma-field-builder.d.ts +25 -23
- package/dts/prisma-field-builder.d.ts.map +1 -1
- package/dts/util/map-query.d.ts +2 -1
- package/dts/util/map-query.d.ts.map +1 -1
- package/dts/util/usage.d.ts +4 -0
- package/dts/util/usage.d.ts.map +1 -0
- package/esm/connection-helpers.js +2 -1
- package/esm/connection-helpers.js.map +1 -1
- package/esm/field-builder.js +65 -23
- package/esm/field-builder.js.map +1 -1
- package/esm/generator.js +2 -2
- package/esm/generator.js.map +1 -1
- package/esm/global-types.d.ts +3 -0
- package/esm/global-types.d.ts.map +1 -1
- package/esm/global-types.js.map +1 -1
- package/esm/index.js.map +1 -1
- package/esm/model-loader.js +10 -12
- package/esm/model-loader.js.map +1 -1
- package/esm/node-ref.js.map +1 -1
- package/esm/object-ref.js +4 -4
- package/esm/object-ref.js.map +1 -1
- package/esm/prisma-field-builder.d.ts +85 -43
- package/esm/prisma-field-builder.d.ts.map +1 -1
- package/esm/prisma-field-builder.js +22 -20
- package/esm/prisma-field-builder.js.map +1 -1
- package/esm/schema-builder.js +1 -1
- package/esm/schema-builder.js.map +1 -1
- package/esm/util/cursors.js +2 -2
- package/esm/util/cursors.js.map +1 -1
- package/esm/util/datamodel.js.map +1 -1
- package/esm/util/deep-equal.js +2 -1
- package/esm/util/deep-equal.js.map +1 -1
- package/esm/util/description.js +4 -2
- package/esm/util/description.js.map +1 -1
- package/esm/util/get-client.js.map +1 -1
- package/esm/util/loader-map.js.map +1 -1
- package/esm/util/map-query.d.ts +2 -1
- package/esm/util/map-query.d.ts.map +1 -1
- package/esm/util/map-query.js +12 -7
- package/esm/util/map-query.js.map +1 -1
- package/esm/util/relation-map.js.map +1 -1
- package/esm/util/selections.js.map +1 -1
- package/esm/util/usage.d.ts +4 -0
- package/esm/util/usage.d.ts.map +1 -0
- package/esm/util/usage.js +29 -0
- package/esm/util/usage.js.map +1 -0
- package/lib/connection-helpers.js +13 -8
- package/lib/connection-helpers.js.map +1 -1
- package/lib/field-builder.js +76 -35
- package/lib/field-builder.js.map +1 -1
- package/lib/generator.js +11 -11
- package/lib/generator.js.map +1 -1
- package/lib/index.js +58 -34
- package/lib/index.js.map +1 -1
- package/lib/model-loader.js +14 -14
- package/lib/model-loader.js.map +1 -1
- package/lib/node-ref.js +5 -3
- package/lib/node-ref.js.map +1 -1
- package/lib/object-ref.js +10 -6
- package/lib/object-ref.js.map +1 -1
- package/lib/prisma-field-builder.js +34 -30
- package/lib/prisma-field-builder.js.map +1 -1
- package/lib/schema-builder.js +25 -25
- package/lib/schema-builder.js.map +1 -1
- package/lib/types.js +3 -1
- package/lib/types.js.map +1 -1
- package/lib/util/cursors.js +46 -18
- package/lib/util/cursors.js.map +1 -1
- package/lib/util/datamodel.js +28 -12
- package/lib/util/datamodel.js.map +1 -1
- package/lib/util/deep-equal.js +6 -3
- package/lib/util/deep-equal.js.map +1 -1
- package/lib/util/description.js +12 -6
- package/lib/util/description.js.map +1 -1
- package/lib/util/get-client.js +6 -2
- package/lib/util/get-client.js.map +1 -1
- package/lib/util/loader-map.js +9 -3
- package/lib/util/loader-map.js.map +1 -1
- package/lib/util/map-query.js +25 -14
- package/lib/util/map-query.js.map +1 -1
- package/lib/util/relation-map.js +7 -3
- package/lib/util/relation-map.js.map +1 -1
- package/lib/util/selections.js +24 -12
- package/lib/util/selections.js.map +1 -1
- package/lib/util/usage.js +51 -0
- package/lib/util/usage.js.map +1 -0
- package/package.json +8 -8
- package/src/field-builder.ts +90 -15
- package/src/generator.ts +2 -3
- package/src/global-types.ts +3 -2
- package/src/prisma-field-builder.ts +28 -18
- package/src/util/map-query.ts +6 -1
- package/src/util/usage.ts +33 -0
package/src/field-builder.ts
CHANGED
|
@@ -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({
|
|
38
|
+
const query = queryFromInfo({
|
|
39
|
+
context,
|
|
40
|
+
info,
|
|
41
|
+
withUsageCheck: !!this.builder.options.prisma?.onUnusedQuery,
|
|
42
|
+
});
|
|
36
43
|
|
|
37
|
-
return
|
|
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({
|
|
72
|
+
const query = queryFromInfo({
|
|
73
|
+
context,
|
|
74
|
+
info,
|
|
75
|
+
withUsageCheck: !!this.builder.options.prisma?.onUnusedQuery,
|
|
76
|
+
});
|
|
61
77
|
|
|
62
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
-
(
|
|
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
|
|
package/src/global-types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-empty-interface */
|
|
3
|
-
import {
|
|
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,
|
|
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,
|
|
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
|
-
> &
|
|
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
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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
|
}
|
package/src/util/map-query.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|