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.
Files changed (121) hide show
  1. package/README.md +33 -0
  2. package/build/cmd-cjs/index.js +2 -2
  3. package/build/cmd-esm/index.js +2 -2
  4. package/package.json +16 -1
  5. package/.turbo/turbo-compile.log +0 -5
  6. package/.turbo/turbo-typedefs.log +0 -5
  7. package/CHANGELOG.md +0 -377
  8. package/src/cmd/generate.ts +0 -54
  9. package/src/cmd/index.ts +0 -60
  10. package/src/cmd/init.ts +0 -637
  11. package/src/cmd/pullSchema.ts +0 -40
  12. package/src/codegen/generators/artifacts/artifacts.test.ts +0 -3246
  13. package/src/codegen/generators/artifacts/fieldKey.ts +0 -60
  14. package/src/codegen/generators/artifacts/index.ts +0 -330
  15. package/src/codegen/generators/artifacts/indexFile.ts +0 -24
  16. package/src/codegen/generators/artifacts/inputs.ts +0 -81
  17. package/src/codegen/generators/artifacts/operations.ts +0 -281
  18. package/src/codegen/generators/artifacts/pagination.test.ts +0 -664
  19. package/src/codegen/generators/artifacts/policy.test.ts +0 -298
  20. package/src/codegen/generators/artifacts/selection.ts +0 -208
  21. package/src/codegen/generators/artifacts/utils.test.ts +0 -118
  22. package/src/codegen/generators/artifacts/utils.ts +0 -108
  23. package/src/codegen/generators/definitions/enums.test.ts +0 -61
  24. package/src/codegen/generators/definitions/enums.ts +0 -68
  25. package/src/codegen/generators/definitions/index.ts +0 -11
  26. package/src/codegen/generators/definitions/schema.test.ts +0 -236
  27. package/src/codegen/generators/index.ts +0 -6
  28. package/src/codegen/generators/indexFile/index.ts +0 -63
  29. package/src/codegen/generators/indexFile/indexFile.test.ts +0 -72
  30. package/src/codegen/generators/persistedQueries/index.ts +0 -55
  31. package/src/codegen/generators/persistedQueries/persistedQuery.test.ts +0 -26
  32. package/src/codegen/generators/runtime/index.test.ts +0 -74
  33. package/src/codegen/generators/runtime/index.ts +0 -64
  34. package/src/codegen/generators/runtime/runtime.test.ts +0 -25
  35. package/src/codegen/generators/typescript/addReferencedInputTypes.ts +0 -77
  36. package/src/codegen/generators/typescript/index.ts +0 -412
  37. package/src/codegen/generators/typescript/inlineType.ts +0 -409
  38. package/src/codegen/generators/typescript/typeReference.ts +0 -44
  39. package/src/codegen/generators/typescript/types.ts +0 -81
  40. package/src/codegen/generators/typescript/typescript.test.ts +0 -1434
  41. package/src/codegen/index.ts +0 -406
  42. package/src/codegen/transforms/addID.test.ts +0 -93
  43. package/src/codegen/transforms/addID.ts +0 -86
  44. package/src/codegen/transforms/composeQueries.test.ts +0 -50
  45. package/src/codegen/transforms/composeQueries.ts +0 -154
  46. package/src/codegen/transforms/fragmentVariables.test.ts +0 -636
  47. package/src/codegen/transforms/fragmentVariables.ts +0 -417
  48. package/src/codegen/transforms/index.ts +0 -7
  49. package/src/codegen/transforms/list.ts +0 -484
  50. package/src/codegen/transforms/lists.test.ts +0 -530
  51. package/src/codegen/transforms/paginate.test.ts +0 -1528
  52. package/src/codegen/transforms/paginate.ts +0 -770
  53. package/src/codegen/transforms/schema.test.ts +0 -136
  54. package/src/codegen/transforms/schema.ts +0 -109
  55. package/src/codegen/transforms/typename.test.ts +0 -125
  56. package/src/codegen/transforms/typename.ts +0 -55
  57. package/src/codegen/utils/commonjs.ts +0 -26
  58. package/src/codegen/utils/flattenSelections.ts +0 -179
  59. package/src/codegen/utils/graphql.test.ts +0 -35
  60. package/src/codegen/utils/graphql.ts +0 -79
  61. package/src/codegen/utils/index.ts +0 -5
  62. package/src/codegen/utils/moduleExport.ts +0 -27
  63. package/src/codegen/utils/murmur.ts +0 -79
  64. package/src/codegen/validators/index.ts +0 -4
  65. package/src/codegen/validators/noIDAlias.test.ts +0 -71
  66. package/src/codegen/validators/noIDAlias.ts +0 -39
  67. package/src/codegen/validators/plugins.ts +0 -25
  68. package/src/codegen/validators/typeCheck.test.ts +0 -960
  69. package/src/codegen/validators/typeCheck.ts +0 -1086
  70. package/src/codegen/validators/uniqueNames.test.ts +0 -59
  71. package/src/codegen/validators/uniqueNames.ts +0 -39
  72. package/src/lib/cleanupFiles.ts +0 -20
  73. package/src/lib/config.test.ts +0 -13
  74. package/src/lib/config.ts +0 -954
  75. package/src/lib/constants.ts +0 -11
  76. package/src/lib/error.ts +0 -24
  77. package/src/lib/fs.ts +0 -285
  78. package/src/lib/graphql.test.ts +0 -211
  79. package/src/lib/graphql.ts +0 -200
  80. package/src/lib/imports.ts +0 -82
  81. package/src/lib/index.ts +0 -17
  82. package/src/lib/introspection.ts +0 -39
  83. package/src/lib/parse.test.ts +0 -75
  84. package/src/lib/parse.ts +0 -23
  85. package/src/lib/path.ts +0 -49
  86. package/src/lib/pipeline.ts +0 -17
  87. package/src/lib/types.ts +0 -34
  88. package/src/lib/walk.ts +0 -104
  89. package/src/runtime/cache/cache.ts +0 -1026
  90. package/src/runtime/cache/gc.ts +0 -56
  91. package/src/runtime/cache/index.ts +0 -3
  92. package/src/runtime/cache/lists.ts +0 -516
  93. package/src/runtime/cache/storage.ts +0 -574
  94. package/src/runtime/cache/stuff.ts +0 -77
  95. package/src/runtime/cache/subscription.ts +0 -329
  96. package/src/runtime/cache/tests/availability.test.ts +0 -408
  97. package/src/runtime/cache/tests/gc.test.ts +0 -319
  98. package/src/runtime/cache/tests/keys.test.ts +0 -36
  99. package/src/runtime/cache/tests/list.test.ts +0 -3854
  100. package/src/runtime/cache/tests/readwrite.test.ts +0 -1201
  101. package/src/runtime/cache/tests/scalars.test.ts +0 -218
  102. package/src/runtime/cache/tests/storage.test.ts +0 -426
  103. package/src/runtime/cache/tests/subscriptions.test.ts +0 -1757
  104. package/src/runtime/index.ts +0 -29
  105. package/src/runtime/lib/config.ts +0 -211
  106. package/src/runtime/lib/constants.ts +0 -17
  107. package/src/runtime/lib/deepEquals.ts +0 -32
  108. package/src/runtime/lib/errors.ts +0 -8
  109. package/src/runtime/lib/index.ts +0 -8
  110. package/src/runtime/lib/log.ts +0 -69
  111. package/src/runtime/lib/network.ts +0 -303
  112. package/src/runtime/lib/networkUtils.ts +0 -151
  113. package/src/runtime/lib/scalars.test.ts +0 -877
  114. package/src/runtime/lib/scalars.ts +0 -195
  115. package/src/runtime/lib/types.ts +0 -195
  116. package/src/test/index.ts +0 -294
  117. package/src/vite/ast.ts +0 -107
  118. package/src/vite/houdini.ts +0 -113
  119. package/src/vite/imports.ts +0 -129
  120. package/src/vite/index.ts +0 -55
  121. package/src/vite/schema.ts +0 -80
@@ -1,406 +0,0 @@
1
- import * as graphql from 'graphql'
2
-
3
- import {
4
- Config,
5
- runPipeline as run,
6
- LogLevel,
7
- find_graphql,
8
- parseJS,
9
- HoudiniError,
10
- Plugin,
11
- fs,
12
- CollectedGraphQLDocument,
13
- path,
14
- } from '../lib'
15
- import { ArtifactKind } from '../runtime/lib/types'
16
- import * as generators from './generators'
17
- import * as transforms from './transforms'
18
- import * as validators from './validators'
19
-
20
- // the main entry point of the compile script
21
- export default async function compile(config: Config) {
22
- // grab the graphql documents
23
- const documents = await collectDocuments(config)
24
-
25
- // push the documents through the pipeline
26
- await runPipeline(config, documents)
27
- }
28
-
29
- // the compiler's job can be broken down into a few different tasks after the documents have been collected:
30
- // - validate their structure
31
- // - perform a series of transformations
32
- // - write the corresponding artifacts to disk
33
- export async function runPipeline(config: Config, docs: CollectedGraphQLDocument[]) {
34
- // we need to create the runtime folder structure
35
- config.createDirectories()
36
-
37
- // reset the newSchema accumulator
38
- config.newSchema = ''
39
-
40
- // reset the newDocuments accumulator
41
- config.newDocuments = ''
42
-
43
- // we need to hold onto some stats for the generated artifacts
44
- const artifactStats = {
45
- total: [],
46
- changed: [],
47
- new: [],
48
- deleted: [],
49
- }
50
-
51
- // collect any plugins that need to do something after generating
52
- const generatePlugins = config.plugins.filter((plugin) => plugin.generate)
53
-
54
- // run the generate command before we print "🎩 Generating runtime..." because we don't know upfront artifactStats.
55
- let error: Error | null = null
56
- try {
57
- await run(
58
- config,
59
- [
60
- // validators
61
- validators.typeCheck,
62
- validators.uniqueNames,
63
- validators.noIDAlias,
64
- validators.plugins,
65
-
66
- // transforms
67
- transforms.internalSchema,
68
- transforms.addID,
69
- transforms.typename,
70
- // list transform must go before fragment variables
71
- // so that the mutation fragments are defined before they get mixed in
72
- transforms.list,
73
- // paginate transform needs to go before fragmentVariables
74
- // so that the variable definitions get hashed
75
- transforms.paginate,
76
- transforms.fragmentVariables,
77
- transforms.composeQueries,
78
-
79
- // generators
80
-
81
- // the runtime is a static thing most of the time. It only needs to be regenerated if
82
- // the user is upgrading versions or the client path changed
83
- generators.runtime,
84
- generators.indexFile,
85
- generators.artifacts(artifactStats),
86
- generators.typescript,
87
- generators.persistOutput,
88
- generators.definitions,
89
-
90
- // these have to go after the artifacts so that plugins can import them
91
- ...generatePlugins.map(
92
- (plugin) => async (config: Config, docs: CollectedGraphQLDocument[]) =>
93
- await plugin.generate!({
94
- config,
95
- documents: docs,
96
- plugin_root: config.pluginDirectory(plugin.name),
97
- })
98
- ),
99
- ],
100
- docs
101
- )
102
- } catch (e) {
103
- error = e as Error
104
- }
105
-
106
- /// Summary
107
-
108
- // count the number of unchanged
109
- const unchanged =
110
- artifactStats.total.length -
111
- artifactStats.changed.length -
112
- artifactStats.new.length -
113
- artifactStats.deleted.length
114
-
115
- // If triggered from the plugin, we show logs ONLY if there are changes.
116
- const printMessage = !config.pluginMode || unchanged !== artifactStats.total.length
117
- if (!printMessage || config.logLevel === LogLevel.Quiet) {
118
- if (error) {
119
- throw error
120
- }
121
- return
122
- }
123
-
124
- if (!config.pluginMode) {
125
- console.log('🎩 Generating runtime...')
126
- }
127
-
128
- if (error) {
129
- throw error
130
- }
131
-
132
- // print a line showing that the process is finished (wo document)
133
- if (artifactStats.total.length === 0) {
134
- console.log(`💡 No operation found. If that's unexpected, please check your config.`)
135
- }
136
- // print summaries of the changes
137
- else if ([LogLevel.Summary, LogLevel.ShortSummary].includes(config.logLevel)) {
138
- // if we have any unchanged artifacts
139
- if (unchanged > 0 && printMessage && !config.pluginMode) {
140
- console.log(`📃 Unchanged: ${unchanged}`)
141
- }
142
-
143
- logStyled('CREATED', artifactStats.new, config.logLevel, config.pluginMode)
144
- logStyled('UPDATED', artifactStats.changed, config.logLevel, config.pluginMode)
145
- logStyled('DELETED', artifactStats.deleted, config.logLevel, config.pluginMode)
146
- }
147
- // print the status of every file
148
- else if (config.logLevel === LogLevel.Full) {
149
- for (const artifact of artifactStats.total) {
150
- // figure out the emoji to use
151
- let emoji = '📃'
152
- if (artifactStats.changed.includes(artifact)) {
153
- emoji = '✏️'
154
- } else if (artifactStats.new.includes(artifact)) {
155
- emoji = '✨'
156
- } else if (artifactStats.deleted.includes(artifact)) {
157
- emoji = '🧹'
158
- }
159
-
160
- // log the name
161
- console.log(`${emoji} ${artifact}`)
162
- }
163
- }
164
- }
165
-
166
- async function collectDocuments(config: Config): Promise<CollectedGraphQLDocument[]> {
167
- // the first step we have to do is grab a list of every file in the source tree
168
- let sourceFiles = await config.sourceFiles()
169
-
170
- // the list of documents we found
171
- const documents: DiscoveredDoc[] = []
172
-
173
- const extractors: Record<string, Plugin['extract_documents'][]> = {
174
- '.graphql': [],
175
- '.gql': [],
176
- '.js': [],
177
- '.ts': [],
178
- }
179
-
180
- // a config's set of plugins defines a priority list of ways to extract a document from a file
181
- // build up a mapping from extension to a list functions that extract documents
182
- for (const plugin of config.plugins) {
183
- if (plugin.extensions && plugin.extract_documents) {
184
- for (const extension of plugin.extensions) {
185
- extractors[extension] = [...(extractors[extension] || []), plugin.extract_documents]
186
- }
187
- }
188
- }
189
-
190
- // add the default extractors at the end of the appropriate lists
191
- const graphql_extractor = (filepath: string, content: string) => [content]
192
- const javascript_extractor = (filepath: string, content: string) =>
193
- processJSFile(config, content)
194
- extractors['.ts'].push(javascript_extractor)
195
- extractors['.js'].push(javascript_extractor)
196
- extractors['.graphql'].push(graphql_extractor)
197
- extractors['.gql'].push(graphql_extractor)
198
-
199
- // wait for every file to be processed
200
- await Promise.all(
201
- sourceFiles.map(async (filepath) => {
202
- // read the file
203
- const contents = await fs.readFile(filepath)
204
- if (!contents) {
205
- return
206
- }
207
-
208
- // look for extractors for the given extension
209
- const extension = path.extname(filepath)
210
-
211
- // if we don't recognize the extension but were told to include it as a
212
- // source file, there is likely something wrong. maybe a missing plugin?
213
- if (!extractors[extension]) {
214
- throw new HoudiniError({
215
- filepath,
216
- message:
217
- 'Encountered a file extension that could not be processed: ' + extension,
218
- description: 'Please verify you are not missing a plugin.',
219
- })
220
- }
221
-
222
- // make sure any errors include the filepath
223
- try {
224
- // if the file ends with .svelte, we need to look for graphql template tags
225
- for (const extractor of extractors[extension]) {
226
- if (!extractor) {
227
- continue
228
- }
229
-
230
- const found = await extractor(filepath, contents)
231
- if (found.length > 0) {
232
- documents.push(...found.map((document) => ({ filepath, document })))
233
- }
234
- }
235
- } catch (err) {
236
- throw new HoudiniError({ ...(err as HoudiniError), filepath })
237
- }
238
- })
239
- )
240
-
241
- return await Promise.all(
242
- documents.map(async ({ document, filepath }) => {
243
- try {
244
- return await processGraphQLDocument(config, filepath, document)
245
- } catch (e) {
246
- throw new HoudiniError({ filepath, message: (e as unknown as Error).message })
247
- }
248
- })
249
- )
250
- }
251
-
252
- export type DiscoveredDoc = {
253
- filepath: string
254
- document: string
255
- }
256
-
257
- async function processJSFile(config: Config, contents: string): Promise<string[]> {
258
- const documents: string[] = []
259
-
260
- // parse the contents as js
261
- try {
262
- var program = (await parseJS(contents))!.script
263
- } catch (e) {
264
- console.log(contents)
265
- throw e
266
- }
267
-
268
- // look for a graphql template tag
269
- await find_graphql(config, program, {
270
- tag({ tagContent }) {
271
- documents.push(tagContent)
272
- },
273
- })
274
-
275
- // we found every document in the file
276
- return documents
277
- }
278
-
279
- async function processGraphQLDocument(
280
- config: Config,
281
- filepath: string,
282
- document: string
283
- ): Promise<CollectedGraphQLDocument> {
284
- const parsedDoc = graphql.parse(document)
285
-
286
- // look for the operation
287
- const operations = parsedDoc.definitions.filter(
288
- ({ kind }) => kind === graphql.Kind.OPERATION_DEFINITION
289
- )
290
- // there are no operations, so its a fragment
291
- const fragments = parsedDoc.definitions.filter(
292
- ({ kind }) => kind === graphql.Kind.FRAGMENT_DEFINITION
293
- )
294
- // if there is more than one operation, throw an error
295
- if (operations.length > 1) {
296
- throw new HoudiniError({
297
- filepath,
298
- message: 'Operation documents can only have one operation',
299
- })
300
- }
301
- // we are looking at a fragment document
302
- else {
303
- // if there is more than one fragment, throw an error
304
- if (fragments.length > 1) {
305
- throw new HoudiniError({
306
- filepath,
307
- message: 'Fragment documents can only have one fragment',
308
- })
309
- }
310
- }
311
-
312
- // figure out the document kind
313
- let kind = ArtifactKind.Fragment
314
- if (operations.length === 1) {
315
- // the document kind depends on the artifact
316
-
317
- // query
318
- if (operations[0].kind === 'OperationDefinition' && operations[0].operation === 'query') {
319
- kind = ArtifactKind.Query
320
- }
321
- // mutation
322
- else if (
323
- operations[0].kind === 'OperationDefinition' &&
324
- operations[0].operation === 'mutation'
325
- ) {
326
- kind = ArtifactKind.Mutation
327
- }
328
- // subscription
329
- else if (
330
- operations[0].kind === 'OperationDefinition' &&
331
- operations[0].operation === 'subscription'
332
- ) {
333
- kind = ArtifactKind.Subscription
334
- }
335
- }
336
-
337
- // add it to the list
338
- return {
339
- name: config.documentName(parsedDoc),
340
- kind,
341
- document: parsedDoc,
342
- filename: filepath,
343
- originalDocument: parsedDoc,
344
- generateArtifact: true,
345
- generateStore: true,
346
- originalString: document,
347
- }
348
- }
349
-
350
- function logStyled(
351
- kind: 'CREATED' | 'UPDATED' | 'DELETED',
352
- stat: string[],
353
- logLevel: LogLevel,
354
- plugin: boolean
355
- ) {
356
- if (stat.length > 0) {
357
- // Let's prepare the one liner in the plugin mode, of a bit more in other lol level.
358
- const msg: string[] = []
359
-
360
- // in plugin mode, it will be very short, let's put a hat first.
361
- if (plugin) {
362
- msg.push(`🎩 `)
363
- }
364
-
365
- if (kind === 'CREATED') {
366
- msg.push(`✨ `)
367
- if (!plugin) {
368
- msg.push(`New: ${stat.length}`)
369
- }
370
- } else if (kind === 'UPDATED') {
371
- msg.push(`✏️ `)
372
- if (!plugin) {
373
- msg.push(`Changed: ${stat.length}`)
374
- }
375
- } else if (kind === 'DELETED') {
376
- msg.push(`🧹 `)
377
- if (!plugin) {
378
- msg.push(`Deleted: ${stat.length}`)
379
- }
380
- }
381
-
382
- // let's do a summary for x elements
383
- const nbToDisplay = 5
384
-
385
- // format for plugin
386
- if (plugin) {
387
- msg.push(`${stat.slice(0, nbToDisplay).join(', ')}`)
388
- if (stat.length > 5) {
389
- msg.push(`, ... ${stat.length - nbToDisplay} more`)
390
- }
391
- }
392
-
393
- console.log(msg.join(''))
394
-
395
- // Format for not plugin & Summary mode
396
- if (!plugin && logLevel === LogLevel.Summary) {
397
- for (const artifact of stat.slice(0, nbToDisplay)) {
398
- console.log(` ${artifact}`)
399
- }
400
- // if there are more than 5 just tell them how many
401
- if (stat.length > nbToDisplay) {
402
- console.log(` ... ${stat.length - nbToDisplay} more`)
403
- }
404
- }
405
- }
406
- }
@@ -1,93 +0,0 @@
1
- import { test, expect } from 'vitest'
2
-
3
- import { runPipeline } from '../../codegen'
4
- import { testConfig, mockCollectedDoc } from '../../test'
5
-
6
- test('adds ids to selection sets of objects with them', async function () {
7
- const docs = [
8
- mockCollectedDoc(
9
- `
10
- query Friends {
11
- user {
12
- firstName
13
- }
14
- }
15
- `
16
- ),
17
- ]
18
-
19
- // run the pipeline
20
- const config = testConfig()
21
- await runPipeline(config, docs)
22
-
23
- expect(docs[0].document).toMatchInlineSnapshot(`
24
- query Friends {
25
- user {
26
- firstName
27
- id
28
- }
29
- }
30
-
31
- `)
32
- })
33
-
34
- test("doesn't add id if there isn't one", async function () {
35
- const docs = [
36
- mockCollectedDoc(
37
- `
38
- query Friends {
39
- ghost {
40
- legends {
41
- name
42
- }
43
- }
44
- }
45
- `
46
- ),
47
- ]
48
-
49
- // run the pipeline
50
- const config = testConfig()
51
- await runPipeline(config, docs)
52
-
53
- expect(docs[0].document).toMatchInlineSnapshot(`
54
- query Friends {
55
- ghost {
56
- legends {
57
- name
58
- }
59
- name
60
- aka
61
- }
62
- }
63
-
64
- `)
65
- })
66
-
67
- test('adds custom id fields to selection sets of objects with them', async function () {
68
- const docs = [
69
- mockCollectedDoc(
70
- `
71
- query Friends {
72
- ghost {
73
- name
74
- }
75
- }
76
- `
77
- ),
78
- ]
79
-
80
- // run the pipeline
81
- const config = testConfig()
82
- await runPipeline(config, docs)
83
-
84
- expect(docs[0].document).toMatchInlineSnapshot(`
85
- query Friends {
86
- ghost {
87
- name
88
- aka
89
- }
90
- }
91
-
92
- `)
93
- })
@@ -1,86 +0,0 @@
1
- import * as graphql from 'graphql'
2
-
3
- import { Config, parentTypeFromAncestors, CollectedGraphQLDocument } from '../../lib'
4
- import { unwrapType } from '../utils'
5
-
6
- // typename adds __typename to the selection set of any unions or interfaces
7
- export default async function addID(
8
- config: Config,
9
- documents: CollectedGraphQLDocument[]
10
- ): Promise<void> {
11
- // visit every document
12
- for (const doc of documents) {
13
- // update the document (graphql.visit is pure)
14
- doc.document = graphql.visit(doc.document, {
15
- Field(node, key, parent, path, ancestors): graphql.ASTNode | undefined {
16
- // if we are looking at a leaf type
17
- if (!node.selectionSet) {
18
- return
19
- }
20
-
21
- // figure out the parent type
22
- const type = parentTypeFromAncestors(
23
- config.schema,
24
- doc.filename,
25
- ancestors.slice(0, -1)
26
- )
27
- // look up the field definition in the parent type
28
- const field = type.getFields()[node.name.value]
29
-
30
- // look up the field in the parent
31
- const fieldType = unwrapType(config, field.type).type
32
-
33
- // if there is no selection set, don't worry about it
34
- if (node.selectionSet?.selections.length > 0) {
35
- // if the type does not have an id field ignore it
36
- if (!graphql.isObjectType(fieldType) && !graphql.isInterfaceType(fieldType)) {
37
- return
38
- }
39
-
40
- // look up the key fields for a given type
41
- const keyFields = config.keyFieldsForType(fieldType.name)
42
-
43
- // if there is no id field of the type
44
- if (keyFields.find((key) => !fieldType.getFields()[key])) {
45
- return
46
- }
47
-
48
- // add the id fields for the given type
49
- const selections = [...node.selectionSet.selections]
50
-
51
- for (const keyField of keyFields) {
52
- // if there is already a selection for id, ignore it
53
- if (
54
- node.selectionSet.selections.find(
55
- (selection) =>
56
- selection.kind === 'Field' &&
57
- !selection.alias &&
58
- selection.name.value === keyField
59
- )
60
- ) {
61
- continue
62
- }
63
-
64
- // add a selection for the field to the selection set
65
- selections.push({
66
- kind: graphql.Kind.FIELD,
67
- name: {
68
- kind: graphql.Kind.NAME,
69
- value: keyField,
70
- },
71
- })
72
- }
73
-
74
- // add the __typename selection to the field's selection set
75
- return {
76
- ...node,
77
- selectionSet: {
78
- ...node.selectionSet,
79
- selections,
80
- },
81
- }
82
- }
83
- },
84
- })
85
- }
86
- }
@@ -1,50 +0,0 @@
1
- import * as graphql from 'graphql'
2
- import { expect, test } from 'vitest'
3
-
4
- import { CollectedGraphQLDocument } from '../../lib'
5
- import { pipelineTest, testConfig } from '../../test'
6
-
7
- const start = [
8
- `
9
- query Foo {
10
- version
11
- ...A
12
- }
13
- `,
14
- `
15
- fragment A on User {
16
- firstName
17
- ...B
18
- }
19
- `,
20
- `
21
- fragment B on User {
22
- firstName
23
- }
24
- `,
25
- ]
26
-
27
- test(
28
- 'include fragment definitions',
29
- pipelineTest(testConfig(), start, true, function (docs: CollectedGraphQLDocument[]) {
30
- // we only care about the Foo document
31
- const fooDoc = docs.find((doc) => doc.name === 'Foo')!
32
-
33
- // make sure there are at least three definitions
34
- expect(fooDoc.document.definitions).toHaveLength(3)
35
-
36
- // make sure that there is one for each fragment
37
- const fragmentADef = fooDoc.document.definitions.find(
38
- (definition) =>
39
- definition.kind === graphql.Kind.FRAGMENT_DEFINITION &&
40
- definition.name.value === 'A'
41
- )
42
- const fragmentBDef = fooDoc.document.definitions.find(
43
- (definition) =>
44
- definition.kind === graphql.Kind.FRAGMENT_DEFINITION &&
45
- definition.name.value === 'B'
46
- )
47
- expect(fragmentADef).toBeDefined()
48
- expect(fragmentBDef).toBeDefined()
49
- })
50
- )