houdini 0.17.9 → 0.17.11
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,770 +0,0 @@
|
|
|
1
|
-
import * as graphql from 'graphql'
|
|
2
|
-
|
|
3
|
-
import { Config, HoudiniError, parentTypeFromAncestors, CollectedGraphQLDocument } from '../../lib'
|
|
4
|
-
import { ArtifactKind, RefetchUpdateMode } from '../../runtime/lib/types'
|
|
5
|
-
import { unwrapType, wrapType } from '../utils'
|
|
6
|
-
|
|
7
|
-
// the paginate transform is responsible for preparing a fragment marked for pagination
|
|
8
|
-
// to be embedded in the query that will be used to fetch additional data. That means it
|
|
9
|
-
// is responsible for adding additional arguments to the paginated field and hoisting
|
|
10
|
-
// all of the pagination args to arguments of the fragment itself. It then generates
|
|
11
|
-
// a query that threads query variables to the updated fragment and lets the fragment
|
|
12
|
-
// argument transform do the rest. This whole process happens in a few steps:
|
|
13
|
-
|
|
14
|
-
// - walk through the document and look for a field marked for pagination. if one is found,
|
|
15
|
-
// add the necessary arguments to the field, referencing variables that will be injected
|
|
16
|
-
// and compute what kind of pagination (toggling an object of flags)
|
|
17
|
-
// - if the @paginate directive was found, add the @arguments directive to the fragment
|
|
18
|
-
// definition to pass new pagination arguments and use any fields that were previously
|
|
19
|
-
// set as the default value. That will cause the fragment arguments directive to inline
|
|
20
|
-
// the default values if one isn't given, preserving the original definition for the first query
|
|
21
|
-
// - generate the query with the fragment embedded using @with to pass query variables through
|
|
22
|
-
|
|
23
|
-
type PaginationFlags = {
|
|
24
|
-
[fieldName: string]: {
|
|
25
|
-
enabled: boolean
|
|
26
|
-
type: string
|
|
27
|
-
defaultValue?: any
|
|
28
|
-
variableName?: string
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// paginate transform adds the necessary fields for a paginated field
|
|
33
|
-
export default async function paginate(
|
|
34
|
-
config: Config,
|
|
35
|
-
documents: CollectedGraphQLDocument[]
|
|
36
|
-
): Promise<void> {
|
|
37
|
-
// we're going to have to add documents to the list so collect them here and we'll add them when we're done
|
|
38
|
-
const newDocs: CollectedGraphQLDocument[] = []
|
|
39
|
-
|
|
40
|
-
// visit every document
|
|
41
|
-
for (const doc of documents) {
|
|
42
|
-
// remember if we ran into a paginate argument
|
|
43
|
-
let paginated = false
|
|
44
|
-
|
|
45
|
-
// store the pagination state to coordinate what we define as args to the field and the argument definitions of
|
|
46
|
-
// the fragment and operation. we'll fill in the enabled state and default values once we encounter @paginate
|
|
47
|
-
const flags: PaginationFlags = {
|
|
48
|
-
first: {
|
|
49
|
-
enabled: false,
|
|
50
|
-
type: 'Int',
|
|
51
|
-
},
|
|
52
|
-
after: {
|
|
53
|
-
enabled: false,
|
|
54
|
-
type: 'String',
|
|
55
|
-
},
|
|
56
|
-
last: {
|
|
57
|
-
enabled: false,
|
|
58
|
-
type: 'Int',
|
|
59
|
-
},
|
|
60
|
-
before: {
|
|
61
|
-
enabled: false,
|
|
62
|
-
type: 'String',
|
|
63
|
-
},
|
|
64
|
-
limit: {
|
|
65
|
-
enabled: false,
|
|
66
|
-
type: 'Int',
|
|
67
|
-
},
|
|
68
|
-
offset: {
|
|
69
|
-
enabled: false,
|
|
70
|
-
type: 'Int',
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let cursorType = 'String'
|
|
75
|
-
|
|
76
|
-
// we need to know the path where the paginate directive shows up so we can distinguish updated
|
|
77
|
-
// values from data that needs to be added to the list
|
|
78
|
-
let paginationPath: string[] = []
|
|
79
|
-
|
|
80
|
-
// we need to add page info to the selection
|
|
81
|
-
doc.document = graphql.visit(doc.document, {
|
|
82
|
-
Field(node, _, __, ___, ancestors) {
|
|
83
|
-
// if there's no paginate directive, ignore the field
|
|
84
|
-
const paginateDirective = node.directives?.find(
|
|
85
|
-
(directive) => directive.name.value === config.paginateDirective
|
|
86
|
-
)
|
|
87
|
-
if (!paginateDirective || !node.selectionSet) {
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// remember we saw this directive
|
|
92
|
-
paginated = true
|
|
93
|
-
|
|
94
|
-
// loop over the args of the field once so we can check their existence
|
|
95
|
-
const fieldTypeFields = (
|
|
96
|
-
parentTypeFromAncestors(config.schema, doc.filename, ancestors) as
|
|
97
|
-
| graphql.GraphQLObjectType
|
|
98
|
-
| graphql.GraphQLInterfaceType
|
|
99
|
-
).getFields()[node.name.value]
|
|
100
|
-
const args = new Set(fieldTypeFields.args.map((arg) => arg.name))
|
|
101
|
-
|
|
102
|
-
// also look to see if the user wants to do forward pagination
|
|
103
|
-
const passedArgs = new Set(node.arguments?.map((arg) => arg.name.value))
|
|
104
|
-
const specifiedForwards = passedArgs.has('first')
|
|
105
|
-
const specifiedBackwards = passedArgs.has('last')
|
|
106
|
-
|
|
107
|
-
cursorType =
|
|
108
|
-
(
|
|
109
|
-
fieldTypeFields.args?.find((arg) => ['before', 'after'].includes(arg.name))
|
|
110
|
-
?.type as graphql.GraphQLNamedType
|
|
111
|
-
)?.name || 'String'
|
|
112
|
-
flags.after.type = cursorType
|
|
113
|
-
flags.before.type = cursorType
|
|
114
|
-
|
|
115
|
-
// figure out what kind of pagination the field supports
|
|
116
|
-
const forwardPagination =
|
|
117
|
-
!specifiedBackwards && args.has('first') && args.has('after')
|
|
118
|
-
const backwardsPagination =
|
|
119
|
-
!specifiedForwards && args.has('last') && args.has('before')
|
|
120
|
-
const offsetPagination =
|
|
121
|
-
!forwardPagination &&
|
|
122
|
-
!backwardsPagination &&
|
|
123
|
-
args.has('offset') &&
|
|
124
|
-
args.has('limit')
|
|
125
|
-
|
|
126
|
-
// update the flags based on what the tagged field supports
|
|
127
|
-
flags.first.enabled = forwardPagination
|
|
128
|
-
flags.after.enabled = forwardPagination
|
|
129
|
-
flags.last.enabled = backwardsPagination
|
|
130
|
-
flags.before.enabled = backwardsPagination
|
|
131
|
-
flags.offset.enabled = offsetPagination
|
|
132
|
-
flags.limit.enabled = offsetPagination
|
|
133
|
-
|
|
134
|
-
paginationPath = (
|
|
135
|
-
ancestors
|
|
136
|
-
.filter(
|
|
137
|
-
(ancestor) =>
|
|
138
|
-
// @ts-ignore
|
|
139
|
-
!Array.isArray(ancestor) && ancestor.kind === graphql.Kind.FIELD
|
|
140
|
-
)
|
|
141
|
-
.concat(node) as graphql.FieldNode[]
|
|
142
|
-
).map((field) => field.alias?.value || field.name.value)
|
|
143
|
-
|
|
144
|
-
// if the field supports cursor based pagination we need to make sure we have the
|
|
145
|
-
// page info field
|
|
146
|
-
return {
|
|
147
|
-
...node,
|
|
148
|
-
// any pagination arguments we run into will need to be replaced with variables
|
|
149
|
-
// since they will be hoisted into the arguments for the fragment or query
|
|
150
|
-
arguments: replaceArgumentsWithVariables(node.arguments, flags),
|
|
151
|
-
selectionSet: offsetPagination
|
|
152
|
-
? // no need to add any fields to the selection if we're dealing with offset pagination
|
|
153
|
-
node.selectionSet
|
|
154
|
-
: // add the page info if we are dealing with cursor-based pagination
|
|
155
|
-
{
|
|
156
|
-
...node.selectionSet,
|
|
157
|
-
selections: [...node.selectionSet.selections, ...pageInfoSelection],
|
|
158
|
-
},
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
// if we saw the paginate directive we need to add arguments to the fragment or query that contain the
|
|
164
|
-
// field that is marked for pagination
|
|
165
|
-
if (paginated) {
|
|
166
|
-
let fragmentName = ''
|
|
167
|
-
let refetchQueryName = ''
|
|
168
|
-
// check if we have to embed the fragment in Node
|
|
169
|
-
let nodeQuery = false
|
|
170
|
-
|
|
171
|
-
// figure out the right refetch
|
|
172
|
-
let refetchUpdate = RefetchUpdateMode.append
|
|
173
|
-
if (flags.last.enabled) {
|
|
174
|
-
refetchUpdate = RefetchUpdateMode.prepend
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// remember if we found a fragment or operation
|
|
178
|
-
let fragment = ''
|
|
179
|
-
|
|
180
|
-
doc.document = graphql.visit(doc.document, {
|
|
181
|
-
// if we are dealing with a query, we'll need to add the variables to the definition
|
|
182
|
-
OperationDefinition(node) {
|
|
183
|
-
// make sure its a query
|
|
184
|
-
if (node.operation !== 'query') {
|
|
185
|
-
throw new HoudiniError({
|
|
186
|
-
filepath: doc.filename,
|
|
187
|
-
message: `@${config.paginateDirective} can only show up in a query or fragment document`,
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
refetchQueryName = node.name?.value || ''
|
|
192
|
-
|
|
193
|
-
// build a map from existing variables to their value so we can compare with the ones we need to inject
|
|
194
|
-
const operationVariables: Record<string, graphql.VariableDefinitionNode> =
|
|
195
|
-
node.variableDefinitions?.reduce(
|
|
196
|
-
(vars, definition) => ({
|
|
197
|
-
...vars,
|
|
198
|
-
[definition.variable.name.value]: definition,
|
|
199
|
-
}),
|
|
200
|
-
{}
|
|
201
|
-
) || {}
|
|
202
|
-
|
|
203
|
-
// figure out the variables we want on the query
|
|
204
|
-
let newVariables: Record<string, graphql.VariableDefinitionNode> =
|
|
205
|
-
Object.fromEntries(
|
|
206
|
-
Object.entries(flags)
|
|
207
|
-
.filter(
|
|
208
|
-
([, spec]) =>
|
|
209
|
-
// let's tale the spec enabled AND where we don't have a dedicated variable for it
|
|
210
|
-
spec.enabled && spec.variableName === undefined
|
|
211
|
-
)
|
|
212
|
-
.map(([fieldName, spec]) => [
|
|
213
|
-
fieldName,
|
|
214
|
-
staticVariableDefinition(
|
|
215
|
-
fieldName,
|
|
216
|
-
spec.type,
|
|
217
|
-
spec.defaultValue,
|
|
218
|
-
spec.variableName
|
|
219
|
-
),
|
|
220
|
-
])
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
// the full list of variables comes from both source
|
|
224
|
-
const variableNames = new Set<string>(
|
|
225
|
-
Object.keys(operationVariables).concat(Object.keys(newVariables))
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
// we need to build a unique set of variable definitions
|
|
229
|
-
const finalVariables = [...variableNames].map(
|
|
230
|
-
(name) => operationVariables[name] || newVariables[name]
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
return {
|
|
234
|
-
...node,
|
|
235
|
-
variableDefinitions: finalVariables,
|
|
236
|
-
} as graphql.OperationDefinitionNode
|
|
237
|
-
},
|
|
238
|
-
// if we are dealing with a fragment definition we'll need to add the arguments directive if it doesn't exist
|
|
239
|
-
FragmentDefinition(node) {
|
|
240
|
-
fragment = node.typeCondition.name.value
|
|
241
|
-
|
|
242
|
-
fragmentName = node.name.value
|
|
243
|
-
refetchQueryName = config.paginationQueryName(fragmentName)
|
|
244
|
-
|
|
245
|
-
// a fragment has to be embedded in Node if its not on the query type
|
|
246
|
-
nodeQuery = node.typeCondition.name.value !== config.schema.getQueryType()?.name
|
|
247
|
-
|
|
248
|
-
// look at the fragment definition for an arguments directive
|
|
249
|
-
const argDirective = node.directives?.find(
|
|
250
|
-
(directive) => directive.name.value === config.argumentsDirective
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
// if there isn't an arguments directive, add it and we'll add arguments to it when
|
|
254
|
-
// we run into it again
|
|
255
|
-
if (!argDirective) {
|
|
256
|
-
return {
|
|
257
|
-
...node,
|
|
258
|
-
directives: [
|
|
259
|
-
...(node.directives || []),
|
|
260
|
-
{
|
|
261
|
-
kind: graphql.Kind.DIRECTIVE,
|
|
262
|
-
name: {
|
|
263
|
-
kind: graphql.Kind.NAME,
|
|
264
|
-
value: config.argumentsDirective,
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
] as graphql.DirectiveNode[],
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
},
|
|
271
|
-
Directive(node) {
|
|
272
|
-
// if we are not looking at the arguments directive, ignore it
|
|
273
|
-
if (node.name.value !== config.argumentsDirective) {
|
|
274
|
-
return
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// turn the set of enabled pagination args into arg definitions for the directive
|
|
278
|
-
let newArgs = [
|
|
279
|
-
...Object.entries(flags)
|
|
280
|
-
.filter(([, spec]) => spec.enabled)
|
|
281
|
-
.map(([key, spec]) =>
|
|
282
|
-
argumentNode(key, [spec.type, spec.defaultValue])
|
|
283
|
-
),
|
|
284
|
-
]
|
|
285
|
-
|
|
286
|
-
// add non-null versions of the arguments we'll use to paginate
|
|
287
|
-
return {
|
|
288
|
-
...node,
|
|
289
|
-
arguments: [...(node.arguments || []), ...newArgs],
|
|
290
|
-
} as graphql.DirectiveNode
|
|
291
|
-
},
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
// now that we've mutated the document to be flexible for @paginate's needs
|
|
295
|
-
// we need to add a document to perform the query if we are paginating on a
|
|
296
|
-
// fragment
|
|
297
|
-
|
|
298
|
-
// figure out the 'target' type of the refetch
|
|
299
|
-
let targetType = config.schema.getQueryType()?.name || ''
|
|
300
|
-
if (fragment) {
|
|
301
|
-
const nodeInterface = config.schema.getType('Node') as graphql.GraphQLInterfaceType
|
|
302
|
-
if (nodeInterface) {
|
|
303
|
-
const { objects, interfaces } = config.schema.getImplementations(nodeInterface)
|
|
304
|
-
|
|
305
|
-
if (
|
|
306
|
-
objects.find((obj) => obj.name === fragment) ||
|
|
307
|
-
interfaces.find((int) => int.name === fragment)
|
|
308
|
-
) {
|
|
309
|
-
targetType = 'Node'
|
|
310
|
-
} else {
|
|
311
|
-
targetType = fragment
|
|
312
|
-
}
|
|
313
|
-
} else {
|
|
314
|
-
targetType = fragment
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// add the paginate info to the collected document
|
|
319
|
-
doc.refetch = {
|
|
320
|
-
update: refetchUpdate,
|
|
321
|
-
path: paginationPath,
|
|
322
|
-
method: flags.first.enabled || flags.last.enabled ? 'cursor' : 'offset',
|
|
323
|
-
pageSize: 0,
|
|
324
|
-
embedded: nodeQuery,
|
|
325
|
-
targetType,
|
|
326
|
-
paginated: true,
|
|
327
|
-
direction: flags.last.enabled ? 'backwards' : 'forward',
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// add the correct default page size
|
|
331
|
-
if (flags.first.enabled) {
|
|
332
|
-
doc.refetch.pageSize = flags.first.defaultValue
|
|
333
|
-
doc.refetch.start = flags.after.defaultValue
|
|
334
|
-
} else if (flags.last.enabled) {
|
|
335
|
-
doc.refetch.pageSize = flags.last.defaultValue
|
|
336
|
-
doc.refetch.start = flags.before.defaultValue
|
|
337
|
-
} else if (flags.limit.enabled) {
|
|
338
|
-
doc.refetch.pageSize = flags.limit.defaultValue
|
|
339
|
-
doc.refetch.start = flags.offset.defaultValue
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// if we're not paginating a fragment, there's nothing more to do. we mutated
|
|
343
|
-
// the query's definition to contain the arguments we need to get more data
|
|
344
|
-
// and we can just use it for refetches
|
|
345
|
-
if (!fragment) {
|
|
346
|
-
continue
|
|
347
|
-
}
|
|
348
|
-
// grab the enabled fields to create the list of arguments for the directive
|
|
349
|
-
const paginationArgs = Object.entries(flags)
|
|
350
|
-
.filter(([_, { enabled }]) => enabled)
|
|
351
|
-
.map(([key, value]) => ({ name: key, ...value }))
|
|
352
|
-
|
|
353
|
-
const fragmentSpreadSelection = [
|
|
354
|
-
{
|
|
355
|
-
kind: graphql.Kind.FRAGMENT_SPREAD,
|
|
356
|
-
name: {
|
|
357
|
-
kind: graphql.Kind.NAME,
|
|
358
|
-
value: fragmentName,
|
|
359
|
-
},
|
|
360
|
-
directives: [
|
|
361
|
-
{
|
|
362
|
-
kind: graphql.Kind.DIRECTIVE,
|
|
363
|
-
name: {
|
|
364
|
-
kind: graphql.Kind.NAME,
|
|
365
|
-
value: config.withDirective,
|
|
366
|
-
},
|
|
367
|
-
['arguments']: paginationArgs.map(({ name }) =>
|
|
368
|
-
variableAsArgument(name)
|
|
369
|
-
),
|
|
370
|
-
},
|
|
371
|
-
],
|
|
372
|
-
},
|
|
373
|
-
] as graphql.SelectionNode[]
|
|
374
|
-
|
|
375
|
-
// we are going to add arguments for every key the type is configured with
|
|
376
|
-
const keys = config
|
|
377
|
-
.keyFieldsForType(!nodeQuery ? config.schema.getQueryType()?.name || '' : fragment)
|
|
378
|
-
.flatMap((key) => {
|
|
379
|
-
// if we are looking at the query, don't add anything
|
|
380
|
-
if (fragment === config.schema.getQueryType()?.name) {
|
|
381
|
-
return []
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// look up the type for each key
|
|
385
|
-
const fragmentType = config.schema.getType(fragment) as
|
|
386
|
-
| graphql.GraphQLObjectType
|
|
387
|
-
| graphql.GraphQLInterfaceType
|
|
388
|
-
|
|
389
|
-
const { type, wrappers } = unwrapType(
|
|
390
|
-
config,
|
|
391
|
-
fragmentType.getFields()[key].type
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
return [
|
|
395
|
-
{
|
|
396
|
-
name: key,
|
|
397
|
-
type: wrapType({ type, wrappers }),
|
|
398
|
-
},
|
|
399
|
-
]
|
|
400
|
-
})
|
|
401
|
-
|
|
402
|
-
const typeConfig = config.typeConfig?.[fragment]
|
|
403
|
-
|
|
404
|
-
const queryDoc: graphql.DocumentNode = {
|
|
405
|
-
kind: graphql.Kind.DOCUMENT,
|
|
406
|
-
definitions: [
|
|
407
|
-
{
|
|
408
|
-
kind: graphql.Kind.OPERATION_DEFINITION,
|
|
409
|
-
name: {
|
|
410
|
-
kind: graphql.Kind.NAME,
|
|
411
|
-
value: refetchQueryName,
|
|
412
|
-
},
|
|
413
|
-
operation: 'query',
|
|
414
|
-
variableDefinitions: paginationArgs
|
|
415
|
-
.map(
|
|
416
|
-
(arg) =>
|
|
417
|
-
({
|
|
418
|
-
kind: graphql.Kind.VARIABLE_DEFINITION,
|
|
419
|
-
type: {
|
|
420
|
-
kind: graphql.Kind.NAMED_TYPE,
|
|
421
|
-
name: {
|
|
422
|
-
kind: graphql.Kind.NAME,
|
|
423
|
-
value: arg.type,
|
|
424
|
-
},
|
|
425
|
-
},
|
|
426
|
-
variable: {
|
|
427
|
-
kind: graphql.Kind.VARIABLE,
|
|
428
|
-
name: {
|
|
429
|
-
kind: graphql.Kind.NAME,
|
|
430
|
-
value: arg.name,
|
|
431
|
-
},
|
|
432
|
-
},
|
|
433
|
-
defaultValue: !flags[arg.name].defaultValue
|
|
434
|
-
? undefined
|
|
435
|
-
: {
|
|
436
|
-
kind: (arg.type + 'Value') as
|
|
437
|
-
| 'IntValue'
|
|
438
|
-
| 'StringValue',
|
|
439
|
-
value: flags[arg.name].defaultValue,
|
|
440
|
-
},
|
|
441
|
-
} as graphql.VariableDefinitionNode)
|
|
442
|
-
)
|
|
443
|
-
.concat(
|
|
444
|
-
!nodeQuery
|
|
445
|
-
? []
|
|
446
|
-
: keys.map(
|
|
447
|
-
(key) =>
|
|
448
|
-
({
|
|
449
|
-
kind: graphql.Kind.VARIABLE_DEFINITION,
|
|
450
|
-
type: key.type,
|
|
451
|
-
variable: {
|
|
452
|
-
kind: graphql.Kind.VARIABLE,
|
|
453
|
-
name: {
|
|
454
|
-
kind: graphql.Kind.NAME,
|
|
455
|
-
value: key.name,
|
|
456
|
-
},
|
|
457
|
-
},
|
|
458
|
-
} as graphql.VariableDefinitionNode)
|
|
459
|
-
)
|
|
460
|
-
),
|
|
461
|
-
selectionSet: {
|
|
462
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
463
|
-
selections: !nodeQuery
|
|
464
|
-
? fragmentSpreadSelection
|
|
465
|
-
: [
|
|
466
|
-
{
|
|
467
|
-
kind: graphql.Kind.FIELD,
|
|
468
|
-
name: {
|
|
469
|
-
kind: graphql.Kind.NAME,
|
|
470
|
-
value: typeConfig?.resolve?.queryField || 'node',
|
|
471
|
-
},
|
|
472
|
-
['arguments']: keys.map((key) => ({
|
|
473
|
-
kind: graphql.Kind.ARGUMENT,
|
|
474
|
-
name: {
|
|
475
|
-
kind: graphql.Kind.NAME,
|
|
476
|
-
value: key.name,
|
|
477
|
-
},
|
|
478
|
-
value: {
|
|
479
|
-
kind: graphql.Kind.VARIABLE,
|
|
480
|
-
name: {
|
|
481
|
-
kind: graphql.Kind.NAME,
|
|
482
|
-
value: key.name,
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
})),
|
|
486
|
-
selectionSet: {
|
|
487
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
488
|
-
selections: [
|
|
489
|
-
// make sure we look up the type of the result
|
|
490
|
-
{
|
|
491
|
-
kind: graphql.Kind.FIELD,
|
|
492
|
-
name: {
|
|
493
|
-
kind: graphql.Kind.NAME,
|
|
494
|
-
value: '__typename',
|
|
495
|
-
},
|
|
496
|
-
},
|
|
497
|
-
// make sure every key field is present
|
|
498
|
-
...(typeConfig?.keys || ['id']).map((key) => ({
|
|
499
|
-
kind: graphql.Kind.FIELD,
|
|
500
|
-
name: {
|
|
501
|
-
kind: graphql.Kind.NAME,
|
|
502
|
-
value: key,
|
|
503
|
-
},
|
|
504
|
-
})),
|
|
505
|
-
...fragmentSpreadSelection,
|
|
506
|
-
] as graphql.SelectionNode[],
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
],
|
|
510
|
-
},
|
|
511
|
-
},
|
|
512
|
-
],
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
// add a document to the list
|
|
516
|
-
newDocs.push({
|
|
517
|
-
kind: ArtifactKind.Query,
|
|
518
|
-
filename: doc.filename,
|
|
519
|
-
name: refetchQueryName,
|
|
520
|
-
document: queryDoc,
|
|
521
|
-
originalDocument: queryDoc,
|
|
522
|
-
generateArtifact: true,
|
|
523
|
-
generateStore: false,
|
|
524
|
-
refetch: doc.refetch,
|
|
525
|
-
originalString: '',
|
|
526
|
-
})
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// add every new doc we generated to the list
|
|
531
|
-
documents.push(...newDocs)
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function replaceArgumentsWithVariables(
|
|
535
|
-
args: readonly graphql.ArgumentNode[] | undefined,
|
|
536
|
-
flags: PaginationFlags
|
|
537
|
-
): graphql.ArgumentNode[] {
|
|
538
|
-
const seenArgs: Record<string, boolean> = {}
|
|
539
|
-
|
|
540
|
-
const newArgs = (args || []).map((arg) => {
|
|
541
|
-
// the specification for this variable
|
|
542
|
-
const spec = flags[arg.name.value]
|
|
543
|
-
// if the arg is not something we care about or is disabled we need to leave it alone
|
|
544
|
-
if (!spec || !spec.enabled) {
|
|
545
|
-
return arg
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// if the argument isn't being passed a variable, we will need to set a default value
|
|
549
|
-
if (arg.value.kind !== 'Variable') {
|
|
550
|
-
const oldValue = (arg.value as graphql.StringValueNode).value
|
|
551
|
-
|
|
552
|
-
// transform the value if we have to and save the default value
|
|
553
|
-
flags[arg.name.value].defaultValue = spec.type === 'Int' ? parseInt(oldValue) : oldValue
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// if we have a variable
|
|
557
|
-
if (arg.value.kind === 'Variable') {
|
|
558
|
-
flags[arg.name.value].variableName = arg.value.name.value
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
seenArgs[arg.name.value] = true
|
|
562
|
-
|
|
563
|
-
// turn the field into a variable
|
|
564
|
-
return variableAsArgument(arg.name.value, flags[arg.name.value].variableName)
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
// any fields that are enabled but don't have values need to have variable references add
|
|
568
|
-
for (const name of Object.keys(flags)) {
|
|
569
|
-
// the specification for this variable
|
|
570
|
-
const spec = flags[name]
|
|
571
|
-
|
|
572
|
-
// if we have a value or its disabled, ignore it
|
|
573
|
-
if (flags[name].defaultValue || !spec.enabled || seenArgs[name]) {
|
|
574
|
-
continue
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// if we are looking at forward pagination args when backwards is enabled ignore it
|
|
578
|
-
if (['first', 'after'].includes(name) && flags['before'].enabled) {
|
|
579
|
-
continue
|
|
580
|
-
}
|
|
581
|
-
// same but opposite for backwards pagination
|
|
582
|
-
if (['last', 'before'].includes(name) && flags['first'].enabled) {
|
|
583
|
-
continue
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// we need to add a variable referencing the argument
|
|
587
|
-
newArgs.push(variableAsArgument(name))
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
return newArgs
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
function variableAsArgument(name: string, variable?: string): graphql.ArgumentNode {
|
|
594
|
-
return {
|
|
595
|
-
kind: graphql.Kind.ARGUMENT,
|
|
596
|
-
name: {
|
|
597
|
-
kind: graphql.Kind.NAME,
|
|
598
|
-
value: name,
|
|
599
|
-
},
|
|
600
|
-
value: {
|
|
601
|
-
kind: graphql.Kind.VARIABLE,
|
|
602
|
-
name: {
|
|
603
|
-
kind: graphql.Kind.NAME,
|
|
604
|
-
value: variable ?? name,
|
|
605
|
-
},
|
|
606
|
-
},
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
function staticVariableDefinition(
|
|
611
|
-
name: string,
|
|
612
|
-
type: string,
|
|
613
|
-
defaultValue?: string,
|
|
614
|
-
variableName?: string
|
|
615
|
-
) {
|
|
616
|
-
return {
|
|
617
|
-
kind: graphql.Kind.VARIABLE_DEFINITION,
|
|
618
|
-
type: {
|
|
619
|
-
kind: graphql.Kind.NAMED_TYPE,
|
|
620
|
-
name: {
|
|
621
|
-
kind: graphql.Kind.NAME,
|
|
622
|
-
value: type,
|
|
623
|
-
},
|
|
624
|
-
},
|
|
625
|
-
variable: {
|
|
626
|
-
kind: graphql.Kind.VARIABLE,
|
|
627
|
-
name: {
|
|
628
|
-
kind: graphql.Kind.NAME,
|
|
629
|
-
value: variableName ?? name,
|
|
630
|
-
},
|
|
631
|
-
},
|
|
632
|
-
defaultValue: !defaultValue
|
|
633
|
-
? undefined
|
|
634
|
-
: {
|
|
635
|
-
kind: (type + 'Value') as 'IntValue' | 'StringValue',
|
|
636
|
-
value: defaultValue,
|
|
637
|
-
},
|
|
638
|
-
} as graphql.VariableDefinitionNode
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
function argumentNode(
|
|
642
|
-
name: string,
|
|
643
|
-
value: [string, number | string | undefined]
|
|
644
|
-
): graphql.ArgumentNode {
|
|
645
|
-
return {
|
|
646
|
-
kind: graphql.Kind.ARGUMENT,
|
|
647
|
-
name: {
|
|
648
|
-
kind: graphql.Kind.NAME,
|
|
649
|
-
value: name,
|
|
650
|
-
},
|
|
651
|
-
value: objectNode(value),
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
function objectNode([type, defaultValue]: [
|
|
656
|
-
string,
|
|
657
|
-
number | string | undefined
|
|
658
|
-
]): graphql.ObjectValueNode {
|
|
659
|
-
const node = {
|
|
660
|
-
kind: graphql.Kind.OBJECT,
|
|
661
|
-
fields: [
|
|
662
|
-
{
|
|
663
|
-
kind: graphql.Kind.OBJECT_FIELD,
|
|
664
|
-
name: {
|
|
665
|
-
kind: graphql.Kind.NAME,
|
|
666
|
-
value: 'type',
|
|
667
|
-
},
|
|
668
|
-
value: {
|
|
669
|
-
kind: graphql.Kind.STRING,
|
|
670
|
-
value: type,
|
|
671
|
-
},
|
|
672
|
-
},
|
|
673
|
-
] as graphql.ObjectFieldNode[],
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
// if there's a default value, add it
|
|
677
|
-
if (defaultValue) {
|
|
678
|
-
node.fields.push({
|
|
679
|
-
kind: graphql.Kind.OBJECT_FIELD,
|
|
680
|
-
name: { kind: graphql.Kind.NAME, value: 'default' } as graphql.NameNode,
|
|
681
|
-
value: {
|
|
682
|
-
kind: typeof defaultValue === 'number' ? 'IntValue' : 'StringValue',
|
|
683
|
-
value: defaultValue.toString(),
|
|
684
|
-
},
|
|
685
|
-
} as graphql.ObjectFieldNode)
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
return node
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
export const pageInfoSelection = [
|
|
692
|
-
{
|
|
693
|
-
kind: graphql.Kind.FIELD,
|
|
694
|
-
name: {
|
|
695
|
-
kind: graphql.Kind.NAME,
|
|
696
|
-
value: 'edges',
|
|
697
|
-
},
|
|
698
|
-
selectionSet: {
|
|
699
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
700
|
-
selections: [
|
|
701
|
-
{
|
|
702
|
-
kind: graphql.Kind.FIELD,
|
|
703
|
-
name: {
|
|
704
|
-
kind: graphql.Kind.NAME,
|
|
705
|
-
value: 'cursor',
|
|
706
|
-
},
|
|
707
|
-
},
|
|
708
|
-
{
|
|
709
|
-
kind: graphql.Kind.FIELD,
|
|
710
|
-
name: {
|
|
711
|
-
kind: graphql.Kind.NAME,
|
|
712
|
-
value: 'node',
|
|
713
|
-
},
|
|
714
|
-
selectionSet: {
|
|
715
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
716
|
-
selections: [
|
|
717
|
-
{
|
|
718
|
-
kind: graphql.Kind.FIELD,
|
|
719
|
-
name: {
|
|
720
|
-
kind: graphql.Kind.NAME,
|
|
721
|
-
value: '__typename',
|
|
722
|
-
},
|
|
723
|
-
},
|
|
724
|
-
],
|
|
725
|
-
},
|
|
726
|
-
},
|
|
727
|
-
],
|
|
728
|
-
},
|
|
729
|
-
},
|
|
730
|
-
{
|
|
731
|
-
kind: graphql.Kind.FIELD,
|
|
732
|
-
name: {
|
|
733
|
-
kind: graphql.Kind.NAME,
|
|
734
|
-
value: 'pageInfo',
|
|
735
|
-
},
|
|
736
|
-
selectionSet: {
|
|
737
|
-
kind: graphql.Kind.SELECTION_SET,
|
|
738
|
-
selections: [
|
|
739
|
-
{
|
|
740
|
-
kind: graphql.Kind.FIELD,
|
|
741
|
-
name: {
|
|
742
|
-
kind: graphql.Kind.NAME,
|
|
743
|
-
value: 'hasPreviousPage',
|
|
744
|
-
},
|
|
745
|
-
},
|
|
746
|
-
{
|
|
747
|
-
kind: graphql.Kind.FIELD,
|
|
748
|
-
name: {
|
|
749
|
-
kind: graphql.Kind.NAME,
|
|
750
|
-
value: 'hasNextPage',
|
|
751
|
-
},
|
|
752
|
-
},
|
|
753
|
-
{
|
|
754
|
-
kind: graphql.Kind.FIELD,
|
|
755
|
-
name: {
|
|
756
|
-
kind: graphql.Kind.NAME,
|
|
757
|
-
value: 'startCursor',
|
|
758
|
-
},
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
kind: graphql.Kind.FIELD,
|
|
762
|
-
name: {
|
|
763
|
-
kind: graphql.Kind.NAME,
|
|
764
|
-
value: 'endCursor',
|
|
765
|
-
},
|
|
766
|
-
},
|
|
767
|
-
],
|
|
768
|
-
},
|
|
769
|
-
},
|
|
770
|
-
]
|