houdini 0.17.8 → 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.
Files changed (148) hide show
  1. package/README.md +33 -0
  2. package/build/cmd-cjs/index.js +124 -38
  3. package/build/cmd-esm/index.js +124 -38
  4. package/build/codegen-cjs/index.js +112 -36
  5. package/build/codegen-esm/index.js +112 -36
  6. package/build/lib/config.d.ts +3 -0
  7. package/build/lib-cjs/index.js +31 -12
  8. package/build/lib-esm/index.js +31 -12
  9. package/build/runtime/cache/cache.d.ts +1 -1
  10. package/build/runtime/cache/lists.d.ts +1 -1
  11. package/build/runtime/lib/config.d.ts +10 -2
  12. package/build/runtime/lib/types.d.ts +1 -0
  13. package/build/runtime-cjs/cache/cache.d.ts +1 -1
  14. package/build/runtime-cjs/cache/cache.js +6 -6
  15. package/build/runtime-cjs/cache/lists.d.ts +1 -1
  16. package/build/runtime-cjs/cache/lists.js +15 -6
  17. package/build/runtime-cjs/cache/tests/list.test.js +160 -70
  18. package/build/runtime-cjs/lib/config.d.ts +10 -2
  19. package/build/runtime-cjs/lib/types.d.ts +1 -0
  20. package/build/runtime-esm/cache/cache.d.ts +1 -1
  21. package/build/runtime-esm/cache/cache.js +6 -6
  22. package/build/runtime-esm/cache/lists.d.ts +1 -1
  23. package/build/runtime-esm/cache/lists.js +15 -6
  24. package/build/runtime-esm/cache/tests/list.test.js +160 -70
  25. package/build/runtime-esm/lib/config.d.ts +10 -2
  26. package/build/runtime-esm/lib/types.d.ts +1 -0
  27. package/build/test-cjs/index.js +122 -36
  28. package/build/test-esm/index.js +122 -36
  29. package/build/vite-cjs/index.js +122 -36
  30. package/build/vite-esm/index.js +122 -36
  31. package/package.json +16 -1
  32. package/.turbo/turbo-compile.log +0 -5
  33. package/.turbo/turbo-typedefs.log +0 -5
  34. package/CHANGELOG.md +0 -367
  35. package/src/cmd/generate.ts +0 -54
  36. package/src/cmd/index.ts +0 -60
  37. package/src/cmd/init.ts +0 -637
  38. package/src/cmd/pullSchema.ts +0 -40
  39. package/src/codegen/generators/artifacts/artifacts.test.ts +0 -2978
  40. package/src/codegen/generators/artifacts/fieldKey.ts +0 -60
  41. package/src/codegen/generators/artifacts/index.ts +0 -330
  42. package/src/codegen/generators/artifacts/indexFile.ts +0 -24
  43. package/src/codegen/generators/artifacts/inputs.ts +0 -81
  44. package/src/codegen/generators/artifacts/operations.ts +0 -263
  45. package/src/codegen/generators/artifacts/pagination.test.ts +0 -664
  46. package/src/codegen/generators/artifacts/policy.test.ts +0 -298
  47. package/src/codegen/generators/artifacts/selection.ts +0 -208
  48. package/src/codegen/generators/artifacts/utils.test.ts +0 -118
  49. package/src/codegen/generators/artifacts/utils.ts +0 -108
  50. package/src/codegen/generators/definitions/enums.test.ts +0 -61
  51. package/src/codegen/generators/definitions/enums.ts +0 -68
  52. package/src/codegen/generators/definitions/index.ts +0 -11
  53. package/src/codegen/generators/definitions/schema.test.ts +0 -227
  54. package/src/codegen/generators/index.ts +0 -6
  55. package/src/codegen/generators/indexFile/index.ts +0 -63
  56. package/src/codegen/generators/indexFile/indexFile.test.ts +0 -72
  57. package/src/codegen/generators/persistedQueries/index.ts +0 -55
  58. package/src/codegen/generators/persistedQueries/persistedQuery.test.ts +0 -26
  59. package/src/codegen/generators/runtime/index.test.ts +0 -74
  60. package/src/codegen/generators/runtime/index.ts +0 -64
  61. package/src/codegen/generators/runtime/runtime.test.ts +0 -25
  62. package/src/codegen/generators/typescript/addReferencedInputTypes.ts +0 -77
  63. package/src/codegen/generators/typescript/index.ts +0 -412
  64. package/src/codegen/generators/typescript/inlineType.ts +0 -409
  65. package/src/codegen/generators/typescript/typeReference.ts +0 -44
  66. package/src/codegen/generators/typescript/types.ts +0 -81
  67. package/src/codegen/generators/typescript/typescript.test.ts +0 -1434
  68. package/src/codegen/index.ts +0 -406
  69. package/src/codegen/transforms/addID.test.ts +0 -93
  70. package/src/codegen/transforms/addID.ts +0 -86
  71. package/src/codegen/transforms/composeQueries.test.ts +0 -50
  72. package/src/codegen/transforms/composeQueries.ts +0 -154
  73. package/src/codegen/transforms/fragmentVariables.test.ts +0 -636
  74. package/src/codegen/transforms/fragmentVariables.ts +0 -417
  75. package/src/codegen/transforms/index.ts +0 -7
  76. package/src/codegen/transforms/list.ts +0 -485
  77. package/src/codegen/transforms/lists.test.ts +0 -530
  78. package/src/codegen/transforms/paginate.test.ts +0 -1481
  79. package/src/codegen/transforms/paginate.ts +0 -750
  80. package/src/codegen/transforms/schema.test.ts +0 -136
  81. package/src/codegen/transforms/schema.ts +0 -104
  82. package/src/codegen/transforms/typename.test.ts +0 -125
  83. package/src/codegen/transforms/typename.ts +0 -55
  84. package/src/codegen/utils/commonjs.ts +0 -26
  85. package/src/codegen/utils/flattenSelections.ts +0 -179
  86. package/src/codegen/utils/graphql.test.ts +0 -35
  87. package/src/codegen/utils/graphql.ts +0 -79
  88. package/src/codegen/utils/index.ts +0 -5
  89. package/src/codegen/utils/moduleExport.ts +0 -27
  90. package/src/codegen/utils/murmur.ts +0 -79
  91. package/src/codegen/validators/index.ts +0 -4
  92. package/src/codegen/validators/noIDAlias.test.ts +0 -71
  93. package/src/codegen/validators/noIDAlias.ts +0 -39
  94. package/src/codegen/validators/plugins.ts +0 -25
  95. package/src/codegen/validators/typeCheck.test.ts +0 -904
  96. package/src/codegen/validators/typeCheck.ts +0 -1031
  97. package/src/codegen/validators/uniqueNames.test.ts +0 -59
  98. package/src/codegen/validators/uniqueNames.ts +0 -39
  99. package/src/lib/cleanupFiles.ts +0 -20
  100. package/src/lib/config.test.ts +0 -13
  101. package/src/lib/config.ts +0 -943
  102. package/src/lib/constants.ts +0 -11
  103. package/src/lib/error.ts +0 -24
  104. package/src/lib/fs.ts +0 -285
  105. package/src/lib/graphql.test.ts +0 -211
  106. package/src/lib/graphql.ts +0 -200
  107. package/src/lib/imports.ts +0 -82
  108. package/src/lib/index.ts +0 -17
  109. package/src/lib/introspection.ts +0 -39
  110. package/src/lib/parse.test.ts +0 -75
  111. package/src/lib/parse.ts +0 -23
  112. package/src/lib/path.ts +0 -49
  113. package/src/lib/pipeline.ts +0 -17
  114. package/src/lib/types.ts +0 -34
  115. package/src/lib/walk.ts +0 -104
  116. package/src/runtime/cache/cache.ts +0 -1023
  117. package/src/runtime/cache/gc.ts +0 -56
  118. package/src/runtime/cache/index.ts +0 -3
  119. package/src/runtime/cache/lists.ts +0 -502
  120. package/src/runtime/cache/storage.ts +0 -574
  121. package/src/runtime/cache/stuff.ts +0 -77
  122. package/src/runtime/cache/subscription.ts +0 -329
  123. package/src/runtime/cache/tests/availability.test.ts +0 -408
  124. package/src/runtime/cache/tests/gc.test.ts +0 -319
  125. package/src/runtime/cache/tests/keys.test.ts +0 -36
  126. package/src/runtime/cache/tests/list.test.ts +0 -3747
  127. package/src/runtime/cache/tests/readwrite.test.ts +0 -1201
  128. package/src/runtime/cache/tests/scalars.test.ts +0 -218
  129. package/src/runtime/cache/tests/storage.test.ts +0 -426
  130. package/src/runtime/cache/tests/subscriptions.test.ts +0 -1757
  131. package/src/runtime/index.ts +0 -29
  132. package/src/runtime/lib/config.ts +0 -201
  133. package/src/runtime/lib/constants.ts +0 -17
  134. package/src/runtime/lib/deepEquals.ts +0 -32
  135. package/src/runtime/lib/errors.ts +0 -8
  136. package/src/runtime/lib/index.ts +0 -8
  137. package/src/runtime/lib/log.ts +0 -69
  138. package/src/runtime/lib/network.ts +0 -303
  139. package/src/runtime/lib/networkUtils.ts +0 -151
  140. package/src/runtime/lib/scalars.test.ts +0 -877
  141. package/src/runtime/lib/scalars.ts +0 -195
  142. package/src/runtime/lib/types.ts +0 -194
  143. package/src/test/index.ts +0 -294
  144. package/src/vite/ast.ts +0 -107
  145. package/src/vite/houdini.ts +0 -113
  146. package/src/vite/imports.ts +0 -129
  147. package/src/vite/index.ts +0 -55
  148. 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
- )