houdini 0.17.9 → 0.17.10
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/README.md +33 -0
- package/build/cmd-cjs/index.js +2 -2
- package/build/cmd-esm/index.js +2 -2
- package/package.json +16 -1
- package/.turbo/turbo-compile.log +0 -5
- package/.turbo/turbo-typedefs.log +0 -5
- package/CHANGELOG.md +0 -377
- package/src/cmd/generate.ts +0 -54
- package/src/cmd/index.ts +0 -60
- package/src/cmd/init.ts +0 -637
- package/src/cmd/pullSchema.ts +0 -40
- package/src/codegen/generators/artifacts/artifacts.test.ts +0 -3246
- package/src/codegen/generators/artifacts/fieldKey.ts +0 -60
- package/src/codegen/generators/artifacts/index.ts +0 -330
- package/src/codegen/generators/artifacts/indexFile.ts +0 -24
- package/src/codegen/generators/artifacts/inputs.ts +0 -81
- package/src/codegen/generators/artifacts/operations.ts +0 -281
- package/src/codegen/generators/artifacts/pagination.test.ts +0 -664
- package/src/codegen/generators/artifacts/policy.test.ts +0 -298
- package/src/codegen/generators/artifacts/selection.ts +0 -208
- package/src/codegen/generators/artifacts/utils.test.ts +0 -118
- package/src/codegen/generators/artifacts/utils.ts +0 -108
- package/src/codegen/generators/definitions/enums.test.ts +0 -61
- package/src/codegen/generators/definitions/enums.ts +0 -68
- package/src/codegen/generators/definitions/index.ts +0 -11
- package/src/codegen/generators/definitions/schema.test.ts +0 -236
- package/src/codegen/generators/index.ts +0 -6
- package/src/codegen/generators/indexFile/index.ts +0 -63
- package/src/codegen/generators/indexFile/indexFile.test.ts +0 -72
- package/src/codegen/generators/persistedQueries/index.ts +0 -55
- package/src/codegen/generators/persistedQueries/persistedQuery.test.ts +0 -26
- package/src/codegen/generators/runtime/index.test.ts +0 -74
- package/src/codegen/generators/runtime/index.ts +0 -64
- package/src/codegen/generators/runtime/runtime.test.ts +0 -25
- package/src/codegen/generators/typescript/addReferencedInputTypes.ts +0 -77
- package/src/codegen/generators/typescript/index.ts +0 -412
- package/src/codegen/generators/typescript/inlineType.ts +0 -409
- package/src/codegen/generators/typescript/typeReference.ts +0 -44
- package/src/codegen/generators/typescript/types.ts +0 -81
- package/src/codegen/generators/typescript/typescript.test.ts +0 -1434
- package/src/codegen/index.ts +0 -406
- package/src/codegen/transforms/addID.test.ts +0 -93
- package/src/codegen/transforms/addID.ts +0 -86
- package/src/codegen/transforms/composeQueries.test.ts +0 -50
- package/src/codegen/transforms/composeQueries.ts +0 -154
- package/src/codegen/transforms/fragmentVariables.test.ts +0 -636
- package/src/codegen/transforms/fragmentVariables.ts +0 -417
- package/src/codegen/transforms/index.ts +0 -7
- package/src/codegen/transforms/list.ts +0 -484
- package/src/codegen/transforms/lists.test.ts +0 -530
- package/src/codegen/transforms/paginate.test.ts +0 -1528
- package/src/codegen/transforms/paginate.ts +0 -770
- package/src/codegen/transforms/schema.test.ts +0 -136
- package/src/codegen/transforms/schema.ts +0 -109
- package/src/codegen/transforms/typename.test.ts +0 -125
- package/src/codegen/transforms/typename.ts +0 -55
- package/src/codegen/utils/commonjs.ts +0 -26
- package/src/codegen/utils/flattenSelections.ts +0 -179
- package/src/codegen/utils/graphql.test.ts +0 -35
- package/src/codegen/utils/graphql.ts +0 -79
- package/src/codegen/utils/index.ts +0 -5
- package/src/codegen/utils/moduleExport.ts +0 -27
- package/src/codegen/utils/murmur.ts +0 -79
- package/src/codegen/validators/index.ts +0 -4
- package/src/codegen/validators/noIDAlias.test.ts +0 -71
- package/src/codegen/validators/noIDAlias.ts +0 -39
- package/src/codegen/validators/plugins.ts +0 -25
- package/src/codegen/validators/typeCheck.test.ts +0 -960
- package/src/codegen/validators/typeCheck.ts +0 -1086
- package/src/codegen/validators/uniqueNames.test.ts +0 -59
- package/src/codegen/validators/uniqueNames.ts +0 -39
- package/src/lib/cleanupFiles.ts +0 -20
- package/src/lib/config.test.ts +0 -13
- package/src/lib/config.ts +0 -954
- package/src/lib/constants.ts +0 -11
- package/src/lib/error.ts +0 -24
- package/src/lib/fs.ts +0 -285
- package/src/lib/graphql.test.ts +0 -211
- package/src/lib/graphql.ts +0 -200
- package/src/lib/imports.ts +0 -82
- package/src/lib/index.ts +0 -17
- package/src/lib/introspection.ts +0 -39
- package/src/lib/parse.test.ts +0 -75
- package/src/lib/parse.ts +0 -23
- package/src/lib/path.ts +0 -49
- package/src/lib/pipeline.ts +0 -17
- package/src/lib/types.ts +0 -34
- package/src/lib/walk.ts +0 -104
- package/src/runtime/cache/cache.ts +0 -1026
- package/src/runtime/cache/gc.ts +0 -56
- package/src/runtime/cache/index.ts +0 -3
- package/src/runtime/cache/lists.ts +0 -516
- package/src/runtime/cache/storage.ts +0 -574
- package/src/runtime/cache/stuff.ts +0 -77
- package/src/runtime/cache/subscription.ts +0 -329
- package/src/runtime/cache/tests/availability.test.ts +0 -408
- package/src/runtime/cache/tests/gc.test.ts +0 -319
- package/src/runtime/cache/tests/keys.test.ts +0 -36
- package/src/runtime/cache/tests/list.test.ts +0 -3854
- package/src/runtime/cache/tests/readwrite.test.ts +0 -1201
- package/src/runtime/cache/tests/scalars.test.ts +0 -218
- package/src/runtime/cache/tests/storage.test.ts +0 -426
- package/src/runtime/cache/tests/subscriptions.test.ts +0 -1757
- package/src/runtime/index.ts +0 -29
- package/src/runtime/lib/config.ts +0 -211
- package/src/runtime/lib/constants.ts +0 -17
- package/src/runtime/lib/deepEquals.ts +0 -32
- package/src/runtime/lib/errors.ts +0 -8
- package/src/runtime/lib/index.ts +0 -8
- package/src/runtime/lib/log.ts +0 -69
- package/src/runtime/lib/network.ts +0 -303
- package/src/runtime/lib/networkUtils.ts +0 -151
- package/src/runtime/lib/scalars.test.ts +0 -877
- package/src/runtime/lib/scalars.ts +0 -195
- package/src/runtime/lib/types.ts +0 -195
- package/src/test/index.ts +0 -294
- package/src/vite/ast.ts +0 -107
- package/src/vite/houdini.ts +0 -113
- package/src/vite/imports.ts +0 -129
- package/src/vite/index.ts +0 -55
- package/src/vite/schema.ts +0 -80
|
@@ -1,484 +0,0 @@
|
|
|
1
|
-
import { logGreen, logYellow } from '@kitql/helper'
|
|
2
|
-
import * as graphql from 'graphql'
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
Config,
|
|
6
|
-
parentTypeFromAncestors,
|
|
7
|
-
HoudiniError,
|
|
8
|
-
CollectedGraphQLDocument,
|
|
9
|
-
siteURL,
|
|
10
|
-
} from '../../lib'
|
|
11
|
-
import { ArtifactKind } from '../../runtime/lib/types'
|
|
12
|
-
import { TypeWrapper, unwrapType } from '../utils'
|
|
13
|
-
import { pageInfoSelection } from './paginate'
|
|
14
|
-
|
|
15
|
-
// addListFragments adds fragments for the fields tagged with @list
|
|
16
|
-
export default async function addListFragments(
|
|
17
|
-
config: Config,
|
|
18
|
-
documents: CollectedGraphQLDocument[]
|
|
19
|
-
): Promise<void> {
|
|
20
|
-
// collect all of the fields that have the list applied
|
|
21
|
-
const lists: {
|
|
22
|
-
[name: string]: {
|
|
23
|
-
selection: graphql.SelectionSetNode | undefined
|
|
24
|
-
type: graphql.GraphQLNamedType
|
|
25
|
-
filename: string
|
|
26
|
-
}
|
|
27
|
-
} = {}
|
|
28
|
-
|
|
29
|
-
const errors: Error[] = []
|
|
30
|
-
// look at every document
|
|
31
|
-
for (const doc of documents) {
|
|
32
|
-
doc.document = graphql.visit(doc.document, {
|
|
33
|
-
Directive(node, key, parent, path, ancestors) {
|
|
34
|
-
// if we found a @list applied (or a @paginate which implies a @list )
|
|
35
|
-
if ([config.listDirective, config.paginateDirective].includes(node.name.value)) {
|
|
36
|
-
// look up the name passed to the directive
|
|
37
|
-
const nameArg = node.arguments?.find((arg) => arg.name.value === 'name')
|
|
38
|
-
|
|
39
|
-
// if we need to use an error relative to this node
|
|
40
|
-
let error = {
|
|
41
|
-
...new graphql.GraphQLError(
|
|
42
|
-
'',
|
|
43
|
-
node,
|
|
44
|
-
new graphql.Source(''),
|
|
45
|
-
node.loc ? [node.loc.start, node.loc.end] : null,
|
|
46
|
-
path
|
|
47
|
-
),
|
|
48
|
-
filepath: doc.filename,
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// if there is no name argument
|
|
52
|
-
if (!nameArg) {
|
|
53
|
-
// if we are looking at a @list we need a name argument
|
|
54
|
-
if (node.name.value === config.listDirective) {
|
|
55
|
-
error.message = `@${node.name.value} must have a name argument`
|
|
56
|
-
errors.push(error)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// regardless, we don't need to process this node any more
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// make sure it was a string
|
|
64
|
-
if (nameArg.value.kind !== 'StringValue') {
|
|
65
|
-
error.message = `@${node.name.value} name must be a string`
|
|
66
|
-
errors.push(error)
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// if we've already seen this list
|
|
71
|
-
if (lists[nameArg.value.value]) {
|
|
72
|
-
error.message = `@${node.name.value} name must be unique`
|
|
73
|
-
errors.push(error)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// look up the parent's type
|
|
77
|
-
const parentType = parentTypeFromAncestors(
|
|
78
|
-
config.schema,
|
|
79
|
-
doc.filename,
|
|
80
|
-
ancestors.slice(0, -1)
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
// a non-connection list can just use the selection set of the tagged field
|
|
84
|
-
// but if this is a connection tagged with list we need to use the selection
|
|
85
|
-
// of the edges.node field
|
|
86
|
-
const targetField = ancestors[ancestors.length - 1] as graphql.FieldNode
|
|
87
|
-
const targetFieldDefinition = parentType.getFields()[
|
|
88
|
-
targetField.name.value
|
|
89
|
-
] as graphql.GraphQLField<any, any>
|
|
90
|
-
|
|
91
|
-
const { selection, type, connection } = connectionSelection(
|
|
92
|
-
config,
|
|
93
|
-
targetFieldDefinition,
|
|
94
|
-
parentTypeFromAncestors(
|
|
95
|
-
config.schema,
|
|
96
|
-
doc.filename,
|
|
97
|
-
ancestors
|
|
98
|
-
) as graphql.GraphQLObjectType,
|
|
99
|
-
(ancestors[ancestors.length - 1] as graphql.FieldNode).selectionSet
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
// add the target of the directive to the list
|
|
103
|
-
lists[nameArg.value.value] = {
|
|
104
|
-
selection,
|
|
105
|
-
type,
|
|
106
|
-
filename: doc.filename,
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// if the list is marking a connection we need to add the flag in a place we can track when
|
|
110
|
-
// generating the artifact
|
|
111
|
-
if (connection) {
|
|
112
|
-
return {
|
|
113
|
-
...node,
|
|
114
|
-
arguments: [
|
|
115
|
-
...node.arguments!,
|
|
116
|
-
{
|
|
117
|
-
kind: 'Argument',
|
|
118
|
-
name: {
|
|
119
|
-
kind: graphql.Kind.NAME,
|
|
120
|
-
value: 'connection',
|
|
121
|
-
},
|
|
122
|
-
value: {
|
|
123
|
-
kind: 'BooleanValue',
|
|
124
|
-
value: true,
|
|
125
|
-
},
|
|
126
|
-
} as graphql.ArgumentNode,
|
|
127
|
-
],
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
Field(node, key, parent, path, ancestors) {
|
|
133
|
-
// if the is marked with @list and is a connection, we need to make sure that we ask for
|
|
134
|
-
// the cursor fields
|
|
135
|
-
if (
|
|
136
|
-
!node.directives?.find(
|
|
137
|
-
(directive) => directive.name.value === config.listDirective
|
|
138
|
-
)
|
|
139
|
-
) {
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// the field is a list, is it a connection?
|
|
144
|
-
|
|
145
|
-
// look up the parent's type
|
|
146
|
-
const parentType = parentTypeFromAncestors(config.schema, doc.filename, ancestors)
|
|
147
|
-
// a non-connection list can just use the selection set of the tagged field
|
|
148
|
-
// but if this is a connection tagged with list we need to use the selection
|
|
149
|
-
// of the edges.node field
|
|
150
|
-
const targetField = node
|
|
151
|
-
const targetFieldDefinition = parentType.getFields()[
|
|
152
|
-
targetField.name.value
|
|
153
|
-
] as graphql.GraphQLField<any, any>
|
|
154
|
-
|
|
155
|
-
const { connection } = connectionSelection(
|
|
156
|
-
config,
|
|
157
|
-
targetFieldDefinition,
|
|
158
|
-
parentTypeFromAncestors(
|
|
159
|
-
config.schema,
|
|
160
|
-
doc.filename,
|
|
161
|
-
ancestors
|
|
162
|
-
) as graphql.GraphQLObjectType,
|
|
163
|
-
node.selectionSet
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
// if the field is a connection, add the cursor
|
|
167
|
-
if (connection) {
|
|
168
|
-
return {
|
|
169
|
-
...node,
|
|
170
|
-
selectionSet: {
|
|
171
|
-
...node.selectionSet,
|
|
172
|
-
selections: [...node.selectionSet!.selections, ...pageInfoSelection],
|
|
173
|
-
},
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// if we ran into any errors
|
|
181
|
-
if (errors.length > 0) {
|
|
182
|
-
throw errors
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// we need to add a delete directive for every type that is the target of a list
|
|
186
|
-
const listTargets = [
|
|
187
|
-
...new Set(
|
|
188
|
-
Object.values(lists).map(({ type }) => {
|
|
189
|
-
// only consider object types
|
|
190
|
-
if (!(type instanceof graphql.GraphQLObjectType)) {
|
|
191
|
-
return ''
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return type.name
|
|
195
|
-
})
|
|
196
|
-
).values(),
|
|
197
|
-
].filter(Boolean)
|
|
198
|
-
|
|
199
|
-
// if there are no documents, we don't have anything to do
|
|
200
|
-
if (Object.keys(lists).length === 0) {
|
|
201
|
-
return
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// we need to add the fragment definitions __somewhere__ where they will be picked up
|
|
205
|
-
// so we're going to add them to the list of documents, one each
|
|
206
|
-
const generatedDoc: graphql.DocumentNode = {
|
|
207
|
-
kind: graphql.Kind.DOCUMENT,
|
|
208
|
-
definitions: (
|
|
209
|
-
Object.entries(lists).flatMap<graphql.FragmentDefinitionNode>(
|
|
210
|
-
([name, { selection, type }]) => {
|
|
211
|
-
// look up the type
|
|
212
|
-
const schemaType = config.schema.getType(type.name) as graphql.GraphQLObjectType
|
|
213
|
-
|
|
214
|
-
// if there is no selection set
|
|
215
|
-
if (!selection) {
|
|
216
|
-
throw new HoudiniError({ message: 'Lists must have a selection' })
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// we need a copy of the field's selection set that we can mutate
|
|
220
|
-
const fragmentSelection: graphql.SelectionSetNode = {
|
|
221
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
222
|
-
selections: [...selection.selections],
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// is there no id selection
|
|
226
|
-
if (
|
|
227
|
-
schemaType &&
|
|
228
|
-
fragmentSelection &&
|
|
229
|
-
!fragmentSelection?.selections.find(
|
|
230
|
-
(field) => field.kind === 'Field' && field.name.value === 'id'
|
|
231
|
-
)
|
|
232
|
-
) {
|
|
233
|
-
// add the id field to the selection
|
|
234
|
-
fragmentSelection.selections = [
|
|
235
|
-
...fragmentSelection.selections,
|
|
236
|
-
{
|
|
237
|
-
kind: graphql.Kind.FIELD,
|
|
238
|
-
name: {
|
|
239
|
-
kind: graphql.Kind.NAME,
|
|
240
|
-
value: 'id',
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
]
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// we at least want to create fragment to indicate inserts in lists
|
|
247
|
-
return [
|
|
248
|
-
// a fragment to insert items into this list
|
|
249
|
-
{
|
|
250
|
-
name: {
|
|
251
|
-
value: config.listInsertFragment(name),
|
|
252
|
-
kind: graphql.Kind.NAME,
|
|
253
|
-
},
|
|
254
|
-
kind: graphql.Kind.FRAGMENT_DEFINITION,
|
|
255
|
-
// in order to insert an item into this list, it must
|
|
256
|
-
// have the same selection as the field
|
|
257
|
-
selectionSet: fragmentSelection,
|
|
258
|
-
typeCondition: {
|
|
259
|
-
kind: graphql.Kind.NAMED_TYPE,
|
|
260
|
-
name: {
|
|
261
|
-
kind: graphql.Kind.NAME,
|
|
262
|
-
value: type.name,
|
|
263
|
-
},
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
// a fragment to insert or remove an item into the list
|
|
267
|
-
{
|
|
268
|
-
name: {
|
|
269
|
-
value: config.listToggleFragment(name),
|
|
270
|
-
kind: graphql.Kind.NAME,
|
|
271
|
-
},
|
|
272
|
-
kind: graphql.Kind.FRAGMENT_DEFINITION,
|
|
273
|
-
// in order to insert an item into this list, it must
|
|
274
|
-
// have the same selection as the field
|
|
275
|
-
selectionSet: {
|
|
276
|
-
...fragmentSelection,
|
|
277
|
-
selections: [
|
|
278
|
-
...fragmentSelection.selections,
|
|
279
|
-
{
|
|
280
|
-
kind: graphql.Kind.FIELD,
|
|
281
|
-
name: {
|
|
282
|
-
kind: graphql.Kind.NAME,
|
|
283
|
-
value: 'id',
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
],
|
|
287
|
-
},
|
|
288
|
-
typeCondition: {
|
|
289
|
-
kind: graphql.Kind.NAMED_TYPE,
|
|
290
|
-
name: {
|
|
291
|
-
kind: graphql.Kind.NAME,
|
|
292
|
-
value: type.name,
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
// add a fragment to remove from the specific list
|
|
297
|
-
{
|
|
298
|
-
kind: graphql.Kind.FRAGMENT_DEFINITION,
|
|
299
|
-
name: {
|
|
300
|
-
value: config.listRemoveFragment(name),
|
|
301
|
-
kind: graphql.Kind.NAME,
|
|
302
|
-
},
|
|
303
|
-
// deleting an entity just takes its id and the parent
|
|
304
|
-
selectionSet: {
|
|
305
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
306
|
-
selections: [
|
|
307
|
-
{
|
|
308
|
-
kind: graphql.Kind.FIELD,
|
|
309
|
-
name: {
|
|
310
|
-
kind: graphql.Kind.NAME,
|
|
311
|
-
value: 'id',
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
],
|
|
315
|
-
},
|
|
316
|
-
typeCondition: {
|
|
317
|
-
kind: graphql.Kind.NAMED_TYPE,
|
|
318
|
-
name: {
|
|
319
|
-
kind: graphql.Kind.NAME,
|
|
320
|
-
value: type.name,
|
|
321
|
-
},
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
]
|
|
325
|
-
}
|
|
326
|
-
) as graphql.DefinitionNode[]
|
|
327
|
-
).concat(
|
|
328
|
-
...listTargets.map<graphql.DirectiveDefinitionNode>((typeName) => ({
|
|
329
|
-
kind: graphql.Kind.DIRECTIVE_DEFINITION,
|
|
330
|
-
name: {
|
|
331
|
-
kind: graphql.Kind.NAME,
|
|
332
|
-
value: config.listDeleteDirective(typeName),
|
|
333
|
-
},
|
|
334
|
-
locations: [
|
|
335
|
-
// the delete directive must be applied to a field in the response
|
|
336
|
-
// corresponding to the id
|
|
337
|
-
{
|
|
338
|
-
kind: graphql.Kind.NAME,
|
|
339
|
-
value: 'FIELD',
|
|
340
|
-
},
|
|
341
|
-
],
|
|
342
|
-
repeatable: true,
|
|
343
|
-
}))
|
|
344
|
-
),
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
config.newSchema +=
|
|
348
|
-
'\n' +
|
|
349
|
-
generatedDoc.definitions
|
|
350
|
-
.filter((c) => c.kind !== 'FragmentDefinition')
|
|
351
|
-
.map(graphql.print)
|
|
352
|
-
.join('\n\n')
|
|
353
|
-
config.newDocuments +=
|
|
354
|
-
'\n' +
|
|
355
|
-
generatedDoc.definitions
|
|
356
|
-
.filter((c) => c.kind === 'FragmentDefinition')
|
|
357
|
-
.map(graphql.print)
|
|
358
|
-
.join('\n\n')
|
|
359
|
-
|
|
360
|
-
documents.push({
|
|
361
|
-
name: 'generated::lists',
|
|
362
|
-
kind: ArtifactKind.Fragment,
|
|
363
|
-
generateArtifact: false,
|
|
364
|
-
generateStore: false,
|
|
365
|
-
document: generatedDoc,
|
|
366
|
-
originalDocument: generatedDoc,
|
|
367
|
-
filename: 'generated::lists',
|
|
368
|
-
originalString: '',
|
|
369
|
-
})
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// a field is considered a connection if it has one of the required connection arguments
|
|
373
|
-
// as well as an edges > node selection
|
|
374
|
-
export function connectionSelection(
|
|
375
|
-
config: Config,
|
|
376
|
-
field: graphql.GraphQLField<any, any>,
|
|
377
|
-
type: graphql.GraphQLObjectType,
|
|
378
|
-
selection: graphql.SelectionSetNode | undefined
|
|
379
|
-
): {
|
|
380
|
-
selection: graphql.SelectionSetNode | undefined
|
|
381
|
-
type: graphql.GraphQLObjectType
|
|
382
|
-
connection: boolean
|
|
383
|
-
error: string | null
|
|
384
|
-
} {
|
|
385
|
-
// make sure the field has the fields for either forward or backwards pagination
|
|
386
|
-
const fieldArgs = field.args.reduce<Record<string, string>>(
|
|
387
|
-
(args, arg) => ({
|
|
388
|
-
...args,
|
|
389
|
-
[arg.name]: unwrapType(config, arg.type).type.name,
|
|
390
|
-
}),
|
|
391
|
-
{}
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
// if the field has an argument for limit, we're good to go
|
|
395
|
-
if (fieldArgs['limit']) {
|
|
396
|
-
return { selection, type, connection: false, error: null }
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const forwardPagination =
|
|
400
|
-
fieldArgs['first'] === 'Int' && ['Cursor', 'String'].includes(fieldArgs['after'])
|
|
401
|
-
const backwardsPagination =
|
|
402
|
-
fieldArgs['last'] === 'Int' && ['Cursor', 'String'].includes(fieldArgs['before'])
|
|
403
|
-
if (!forwardPagination && !backwardsPagination) {
|
|
404
|
-
return { selection, type, connection: false, error: missingPaginationArgMessage(config) }
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// we need to make sure that there is an edges field
|
|
408
|
-
const edgesField = selection?.selections.find(
|
|
409
|
-
(selection) => selection.kind === 'Field' && selection.name.value === 'edges'
|
|
410
|
-
) as graphql.FieldNode
|
|
411
|
-
if (!edgesField) {
|
|
412
|
-
return { selection, type, connection: false, error: missingEdgeSelectionMessage(config) }
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const nodeSelection = edgesField.selectionSet?.selections.find(
|
|
416
|
-
(selection) => selection.kind === 'Field' && selection.name.value === 'node'
|
|
417
|
-
) as graphql.FieldNode
|
|
418
|
-
if (!nodeSelection.selectionSet) {
|
|
419
|
-
return { selection, type, connection: false, error: missingNodeSelectionMessage(config) }
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// now that we have the correct selection, we have to lookup node type
|
|
423
|
-
// we need to make sure that there is an edges field
|
|
424
|
-
const edgeField = (
|
|
425
|
-
unwrapType(config, field.type).type as graphql.GraphQLObjectType
|
|
426
|
-
).getFields()['edges']
|
|
427
|
-
const { wrappers, type: edgeFieldType } = unwrapType(config, edgeField.type)
|
|
428
|
-
// wrappers are in reverse order (last one is the top level, and there's a nullable entry)
|
|
429
|
-
// so a nullable list of non-null elements looks like [NonNull, List, Nullable].
|
|
430
|
-
// this means we just have to look at the second to last element and check if its a list
|
|
431
|
-
const list = wrappers[wrappers.length - 2] === TypeWrapper.List
|
|
432
|
-
if (!list) {
|
|
433
|
-
return { selection, type, connection: false, error: edgeInvalidTypeMessage(config) }
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
const nodeField = (edgeFieldType as graphql.GraphQLObjectType).getFields()['node']
|
|
437
|
-
if (!nodeField) {
|
|
438
|
-
return { selection, type, connection: false, error: nodeNotDefinedMessage(config) }
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return {
|
|
442
|
-
selection: nodeSelection.selectionSet,
|
|
443
|
-
type: unwrapType(config, nodeField.type).type as graphql.GraphQLObjectType,
|
|
444
|
-
connection: true,
|
|
445
|
-
error: null,
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
const missingPaginationArgMessage = (
|
|
450
|
-
config: Config
|
|
451
|
-
) => `Looks like you are trying to use the ${logGreen(
|
|
452
|
-
`@${config.paginateDirective}`
|
|
453
|
-
)} directive on a field but have not provided a ${logYellow('first')}, ${logYellow(
|
|
454
|
-
'last'
|
|
455
|
-
)}, or ${logYellow('limit')} argument. Please add one and try again.
|
|
456
|
-
For more information, visit this link: ${siteURL}/guides/pagination`
|
|
457
|
-
|
|
458
|
-
const missingEdgeSelectionMessage = (
|
|
459
|
-
config: Config
|
|
460
|
-
) => `Looks like you are trying to use the ${logGreen(
|
|
461
|
-
`@${config.paginateDirective}`
|
|
462
|
-
)} directive on a field but your selection does not contain an ${logYellow(
|
|
463
|
-
'edges'
|
|
464
|
-
)} field. Please add one and try again.
|
|
465
|
-
For more information, visit this link: ${siteURL}/guides/pagination`
|
|
466
|
-
|
|
467
|
-
const missingNodeSelectionMessage = (
|
|
468
|
-
config: Config
|
|
469
|
-
) => `Looks like you are trying to use the ${logGreen(
|
|
470
|
-
`@${config.paginateDirective}`
|
|
471
|
-
)} directive on a field but your selection does not contain a ${logYellow(
|
|
472
|
-
'node'
|
|
473
|
-
)} field. Please add one and try again.
|
|
474
|
-
For more information, visit this link: ${siteURL}/guides/pagination`
|
|
475
|
-
|
|
476
|
-
const edgeInvalidTypeMessage = (config: Config) => `Looks like you are trying to use the ${logGreen(
|
|
477
|
-
`@${config.paginateDirective}`
|
|
478
|
-
)} directive on a field but your field does not conform to the connection spec: your edges field seems strange.
|
|
479
|
-
For more information, visit this link: ${siteURL}/guides/pagination`
|
|
480
|
-
|
|
481
|
-
const nodeNotDefinedMessage = (config: Config) => `Looks like you are trying to use the ${logGreen(
|
|
482
|
-
`@${config.paginateDirective}`
|
|
483
|
-
)} directive on a field but your field does not conform to the connection spec: your edge type does not have node as a field.
|
|
484
|
-
For more information, visit this link: ${siteURL}/guides/pagination`
|