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,1434 +0,0 @@
1
- import * as recast from 'recast'
2
- import * as typeScriptParser from 'recast/parsers/typescript'
3
- import { test, expect, describe } from 'vitest'
4
-
5
- import { runPipeline } from '../..'
6
- import { fs } from '../../../lib'
7
- import { testConfig, mockCollectedDoc } from '../../../test'
8
-
9
- // the config to use in tests
10
- const config = testConfig({
11
- schema: `
12
- enum MyEnum {
13
- Hello
14
- }
15
-
16
- type Query {
17
- user(id: ID, filter: UserFilter, filterList: [UserFilter!], enumArg: MyEnum): User
18
- users: [User]
19
- nodes: [Node!]!
20
- entities: [Entity]
21
- entity: Entity!
22
- listOfLists: [[User]]!
23
- node(id: ID!): Node
24
- }
25
-
26
- type Mutation {
27
- doThing(
28
- filter: UserFilter,
29
- list: [UserFilter!]!,
30
- id: ID!
31
- firstName: String!
32
- admin: Boolean
33
- age: Int
34
- weight: Float
35
- ): User
36
- }
37
-
38
- input UserFilter {
39
- middle: NestedUserFilter
40
- listRequired: [String!]!
41
- nullList: [String]
42
- recursive: UserFilter
43
- enum: MyEnum
44
- }
45
-
46
- input NestedUserFilter {
47
- id: ID!
48
- firstName: String!
49
- admin: Boolean
50
- age: Int
51
- weight: Float
52
- }
53
-
54
- interface Node {
55
- id: ID!
56
- }
57
-
58
- type Cat implements Node & Animal {
59
- id: ID!
60
- kitty: Boolean!
61
- isAnimal: Boolean!
62
- }
63
-
64
-
65
- interface Animal {
66
- isAnimal: Boolean!
67
- }
68
-
69
- union Entity = User | Cat
70
-
71
- union AnotherEntity = User | Ghost
72
-
73
- type Ghost implements Node {
74
- id: ID!
75
- aka: String!
76
- }
77
-
78
- type User implements Node {
79
- id: ID!
80
-
81
- firstName: String!
82
- nickname: String
83
- parent: User
84
- friends: [User]
85
- enumValue: MyEnum
86
-
87
- admin: Boolean
88
- age: Int
89
- weight: Float
90
- }
91
- `,
92
- })
93
-
94
- describe('typescript', function () {
95
- test('fragment types', async function () {
96
- // the document to test
97
- const doc = mockCollectedDoc(
98
- `fragment TestFragment on User { firstName nickname enumValue }`
99
- )
100
-
101
- // execute the generator
102
- await runPipeline(config, [doc])
103
-
104
- // look up the files in the artifact directory
105
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
106
-
107
- // make sure they match what we expect
108
- expect(
109
- recast.parse(fileContents!, {
110
- parser: typeScriptParser,
111
- })
112
- ).toMatchInlineSnapshot(`
113
- import { MyEnum } from "$houdini/graphql/enums";
114
-
115
- export type TestFragment = {
116
- readonly "shape"?: TestFragment$data
117
- readonly "$fragments": {
118
- "TestFragment": true
119
- }
120
- };
121
-
122
- export type TestFragment$data = {
123
- readonly firstName: string
124
- readonly nickname: string | null
125
- readonly enumValue: MyEnum | null
126
- };
127
- `)
128
- })
129
-
130
- test('nested types', async function () {
131
- const fragment = `fragment TestFragment on User { firstName parent { firstName } }`
132
-
133
- // the document to test
134
- const doc = mockCollectedDoc(fragment)
135
-
136
- // execute the generator
137
- await runPipeline(config, [doc])
138
-
139
- // look up the files in the artifact directory
140
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
141
-
142
- // make sure they match what we expect
143
- expect(
144
- recast.parse(fileContents!, {
145
- parser: typeScriptParser,
146
- })
147
- ).toMatchInlineSnapshot(`
148
- export type TestFragment = {
149
- readonly "shape"?: TestFragment$data
150
- readonly "$fragments": {
151
- "TestFragment": true
152
- }
153
- };
154
-
155
- export type TestFragment$data = {
156
- readonly firstName: string
157
- readonly parent: {
158
- readonly firstName: string
159
- } | null
160
- };
161
- `)
162
- })
163
-
164
- test('scalars', async function () {
165
- // the document to test
166
- const doc = mockCollectedDoc(
167
- `fragment TestFragment on User { firstName admin age id weight }`
168
- )
169
-
170
- // execute the generator
171
- await runPipeline(config, [doc])
172
-
173
- // look up the files in the artifact directory
174
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
175
-
176
- // make sure they match what we expect
177
- expect(
178
- recast.parse(fileContents!, {
179
- parser: typeScriptParser,
180
- })
181
- ).toMatchInlineSnapshot(`
182
- export type TestFragment = {
183
- readonly "shape"?: TestFragment$data
184
- readonly "$fragments": {
185
- "TestFragment": true
186
- }
187
- };
188
-
189
- export type TestFragment$data = {
190
- readonly firstName: string
191
- readonly admin: boolean | null
192
- readonly age: number | null
193
- readonly id: string
194
- readonly weight: number | null
195
- };
196
- `)
197
- })
198
-
199
- test('list types', async function () {
200
- // the document to test
201
- const doc = mockCollectedDoc(
202
- `fragment TestFragment on User { firstName friends { firstName } }`
203
- )
204
-
205
- // execute the generator
206
- await runPipeline(config, [doc])
207
-
208
- // look up the files in the artifact directory
209
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
210
-
211
- // make sure they match what we expect
212
- expect(
213
- recast.parse(fileContents!, {
214
- parser: typeScriptParser,
215
- })
216
- ).toMatchInlineSnapshot(`
217
- export type TestFragment = {
218
- readonly "shape"?: TestFragment$data
219
- readonly "$fragments": {
220
- "TestFragment": true
221
- }
222
- };
223
-
224
- export type TestFragment$data = {
225
- readonly firstName: string
226
- readonly friends: ({
227
- readonly firstName: string
228
- } | null)[] | null
229
- };
230
- `)
231
- })
232
-
233
- test('query with no input', async function () {
234
- // the document to test
235
- const doc = mockCollectedDoc(`query MyQuery { user { firstName } }`)
236
-
237
- // execute the generator
238
- await runPipeline(config, [doc])
239
-
240
- // look up the files in the artifact directory
241
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
242
-
243
- // make sure they match what we expect
244
- expect(
245
- recast.parse(fileContents!, {
246
- parser: typeScriptParser,
247
- })
248
- ).toMatchInlineSnapshot(`
249
- export type MyQuery = {
250
- readonly "input": MyQuery$input
251
- readonly "result": MyQuery$result | undefined
252
- };
253
-
254
- export type MyQuery$result = {
255
- readonly user: {
256
- readonly firstName: string
257
- } | null
258
- };
259
-
260
- export type MyQuery$input = null;
261
- `)
262
- })
263
-
264
- test('query with root list', async function () {
265
- // the document with the query
266
- const queryDoc = mockCollectedDoc(`
267
- query MyQuery {
268
- users {
269
- firstName,
270
- }
271
- }
272
- `)
273
- // execute the generator
274
- await runPipeline(config, [queryDoc])
275
-
276
- // look up the files in the artifact directory
277
- const fileContents = await fs.readFile(config.artifactTypePath(queryDoc.document))
278
-
279
- // make sure they match what we expect
280
- expect(
281
- recast.parse(fileContents!, {
282
- parser: typeScriptParser,
283
- })
284
- ).toMatchInlineSnapshot(`
285
- export type MyQuery = {
286
- readonly "input": MyQuery$input
287
- readonly "result": MyQuery$result | undefined
288
- };
289
-
290
- export type MyQuery$result = {
291
- readonly users: ({
292
- readonly firstName: string
293
- } | null)[] | null
294
- };
295
-
296
- export type MyQuery$input = null;
297
- `)
298
- })
299
-
300
- test('query with input', async function () {
301
- // the document to test
302
- const doc = mockCollectedDoc(
303
- `query MyQuery($id: ID!, $enum: MyEnum) { user(id: $id, enumArg: $enum ) { firstName } }`
304
- )
305
-
306
- // execute the generator
307
- await runPipeline(config, [doc])
308
-
309
- // look up the files in the artifact directory
310
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
311
-
312
- // make sure they match what we expect
313
- expect(
314
- recast.parse(fileContents!, {
315
- parser: typeScriptParser,
316
- })
317
- ).toMatchInlineSnapshot(`
318
- import type { MyEnum } from "$houdini/graphql/enums";
319
-
320
- export type MyQuery = {
321
- readonly "input": MyQuery$input
322
- readonly "result": MyQuery$result | undefined
323
- };
324
-
325
- export type MyQuery$result = {
326
- readonly user: {
327
- readonly firstName: string
328
- } | null
329
- };
330
-
331
- export type MyQuery$input = {
332
- id: string
333
- enum?: MyEnum | null | undefined
334
- };
335
- `)
336
- })
337
-
338
- test('interface on interface', async function () {
339
- // the document to test
340
- const doc = mockCollectedDoc(
341
- `query MyTestQuery {
342
- entity {
343
- ... on Node {
344
- id
345
- }
346
- }
347
- }`
348
- )
349
-
350
- // execute the generator
351
- await runPipeline(config, [doc])
352
-
353
- // look up the files in the artifact directory
354
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
355
-
356
- // make sure they match what we expect
357
- expect(
358
- recast.parse(fileContents!, {
359
- parser: typeScriptParser,
360
- })
361
- ).toMatchInlineSnapshot(`
362
- export type MyTestQuery = {
363
- readonly "input": MyTestQuery$input
364
- readonly "result": MyTestQuery$result | undefined
365
- };
366
-
367
- export type MyTestQuery$result = {
368
- readonly entity: {} & (({
369
- readonly id: string
370
- readonly __typename: "Cat"
371
- }) | ({
372
- readonly id: string
373
- readonly __typename: "User"
374
- }))
375
- };
376
-
377
- export type MyTestQuery$input = null;
378
- `)
379
- })
380
-
381
- test('mutation with input list', async function () {
382
- // the document to test
383
- const doc = mockCollectedDoc(
384
- `mutation MyMutation(
385
- $filter: UserFilter,
386
- $filterList: [UserFilter!]!,
387
- $id: ID!
388
- $firstName: String!
389
- $admin: Boolean
390
- $age: Int
391
- $weight: Float
392
- ) { doThing(
393
- filter: $filter,
394
- list: $filterList,
395
- id:$id
396
- firstName:$firstName
397
- admin:$admin
398
- age:$age
399
- weight:$weight
400
- ) {
401
- firstName
402
- }
403
- }`
404
- )
405
-
406
- // execute the generator
407
- await runPipeline(config, [doc])
408
-
409
- // look up the files in the artifact directory
410
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
411
-
412
- // make sure they match what we expect
413
- expect(
414
- recast.parse(fileContents!, {
415
- parser: typeScriptParser,
416
- })
417
- ).toMatchInlineSnapshot(`
418
- import type { MyEnum } from "$houdini/graphql/enums";
419
-
420
- export type MyMutation = {
421
- readonly "input": MyMutation$input
422
- readonly "result": MyMutation$result
423
- };
424
-
425
- export type MyMutation$result = {
426
- readonly doThing: {
427
- readonly firstName: string
428
- } | null
429
- };
430
-
431
- type NestedUserFilter = {
432
- id: string
433
- firstName: string
434
- admin?: boolean | null | undefined
435
- age?: number | null | undefined
436
- weight?: number | null | undefined
437
- };
438
-
439
- type UserFilter = {
440
- middle?: NestedUserFilter | null | undefined
441
- listRequired: (string)[]
442
- nullList?: (string | null | undefined)[] | null | undefined
443
- recursive?: UserFilter | null | undefined
444
- enum?: MyEnum | null | undefined
445
- };
446
-
447
- export type MyMutation$input = {
448
- filter?: UserFilter | null | undefined
449
- filterList: (UserFilter)[]
450
- id: string
451
- firstName: string
452
- admin?: boolean | null | undefined
453
- age?: number | null | undefined
454
- weight?: number | null | undefined
455
- };
456
-
457
- export type MyMutation$optimistic = {
458
- readonly doThing?: {
459
- readonly firstName?: string
460
- } | null
461
- };
462
- `)
463
- })
464
-
465
- test("mutation optimistic response type doesn't include fragments", async function () {
466
- // the document to test
467
- const docs = [
468
- mockCollectedDoc(
469
- `mutation MyMutation {
470
- doThing(
471
- list: [],
472
- id: "1"
473
- firstName: "hello"
474
- ) {
475
- firstName
476
- ...TestFragment,
477
- }
478
- }`
479
- ),
480
- mockCollectedDoc(
481
- `fragment TestFragment on User {
482
- firstName
483
- }`
484
- ),
485
- ]
486
-
487
- // execute the generator
488
- await runPipeline(config, docs)
489
-
490
- // look up the files in the artifact directory
491
- const fileContents = await fs.readFile(config.artifactTypePath(docs[0].document))
492
-
493
- // make sure they match what we expect
494
- expect(
495
- recast.parse(fileContents!, {
496
- parser: typeScriptParser,
497
- })
498
- ).toMatchInlineSnapshot(`
499
- export type MyMutation = {
500
- readonly "input": MyMutation$input
501
- readonly "result": MyMutation$result
502
- };
503
-
504
- export type MyMutation$result = {
505
- readonly doThing: {
506
- readonly firstName: string
507
- readonly $fragments: {
508
- TestFragment: true
509
- }
510
- } | null
511
- };
512
-
513
- export type MyMutation$input = null;
514
-
515
- export type MyMutation$optimistic = {
516
- readonly doThing?: {
517
- readonly firstName?: string
518
- } | null
519
- };
520
- `)
521
- })
522
-
523
- test('nested input objects', async function () {
524
- // the document to test
525
- const doc = mockCollectedDoc(
526
- `query MyQuery($filter: UserFilter!) { user(filter: $filter) { firstName } }`
527
- )
528
-
529
- // execute the generator
530
- await runPipeline(config, [doc])
531
-
532
- // look up the files in the artifact directory
533
- const fileContents = await fs.readFile(config.artifactTypePath(doc.document))
534
-
535
- // make sure they match what we expect
536
- expect(
537
- recast.parse(fileContents!, {
538
- parser: typeScriptParser,
539
- })
540
- ).toMatchInlineSnapshot(`
541
- import type { MyEnum } from "$houdini/graphql/enums";
542
-
543
- export type MyQuery = {
544
- readonly "input": MyQuery$input
545
- readonly "result": MyQuery$result | undefined
546
- };
547
-
548
- export type MyQuery$result = {
549
- readonly user: {
550
- readonly firstName: string
551
- } | null
552
- };
553
-
554
- type NestedUserFilter = {
555
- id: string
556
- firstName: string
557
- admin?: boolean | null | undefined
558
- age?: number | null | undefined
559
- weight?: number | null | undefined
560
- };
561
-
562
- type UserFilter = {
563
- middle?: NestedUserFilter | null | undefined
564
- listRequired: (string)[]
565
- nullList?: (string | null | undefined)[] | null | undefined
566
- recursive?: UserFilter | null | undefined
567
- enum?: MyEnum | null | undefined
568
- };
569
-
570
- export type MyQuery$input = {
571
- filter: UserFilter
572
- };
573
- `)
574
- })
575
-
576
- test('generates index file', async function () {
577
- // the document to test
578
- const doc = mockCollectedDoc(
579
- `query MyQuery($filter: UserFilter!) { user(filter: $filter) { firstName } }`
580
- )
581
-
582
- // execute the generator
583
- await runPipeline(config, [doc])
584
-
585
- // read the type index file
586
- const fileContents = await fs.readFile(config.typeIndexPath)
587
-
588
- expect(
589
- recast.parse(fileContents!, {
590
- parser: typeScriptParser,
591
- })
592
- ).toMatchInlineSnapshot(`
593
- export * from "./artifacts/MyQuery";
594
- export * from "./runtime";
595
- export * from "./graphql";
596
- `)
597
- })
598
-
599
- test('fragment spreads', async function () {
600
- // the document with the fragment
601
- const fragment = mockCollectedDoc(`fragment Foo on User { firstName }`)
602
-
603
- // the document to test
604
- const query = mockCollectedDoc(`query MyQuery { user { ...Foo } }`)
605
-
606
- // execute the generator
607
- await runPipeline(config, [query, fragment])
608
-
609
- // look up the files in the artifact directory
610
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
611
-
612
- // make sure they match what we expect
613
- expect(
614
- recast.parse(fileContents!, {
615
- parser: typeScriptParser,
616
- })
617
- ).toMatchInlineSnapshot(`
618
- export type MyQuery = {
619
- readonly "input": MyQuery$input
620
- readonly "result": MyQuery$result | undefined
621
- };
622
-
623
- export type MyQuery$result = {
624
- readonly user: {
625
- readonly $fragments: {
626
- Foo: true
627
- }
628
- } | null
629
- };
630
-
631
- export type MyQuery$input = null;
632
- `)
633
- })
634
-
635
- test('fragment spreads no masking', async function () {
636
- const withoutMasking = testConfig({ disableMasking: true })
637
-
638
- // the document with the fragment
639
- const fragment = mockCollectedDoc(`fragment Foo on User { firstName }`)
640
-
641
- // the document to test
642
- const query = mockCollectedDoc(`query MyQuery { user { ...Foo } }`)
643
-
644
- // execute the generator
645
- await runPipeline(withoutMasking, [query, fragment])
646
-
647
- // look up the files in the artifact directory
648
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
649
-
650
- // make sure they match what we expect
651
- expect(
652
- recast.parse(fileContents!, {
653
- parser: typeScriptParser,
654
- })
655
- ).toMatchInlineSnapshot(`
656
- export type MyQuery = {
657
- readonly "input": MyQuery$input
658
- readonly "result": MyQuery$result | undefined
659
- };
660
-
661
- export type MyQuery$result = {
662
- readonly user: {
663
- readonly firstName: string
664
- readonly $fragments: {
665
- Foo: true
666
- }
667
- }
668
- };
669
-
670
- export type MyQuery$input = null;
671
- `)
672
- })
673
-
674
- test('interfaces', async function () {
675
- // the document to test
676
- const query = mockCollectedDoc(
677
- `
678
- query MyQuery {
679
- nodes {
680
- ... on User {
681
- id
682
- }
683
- ... on Cat {
684
- id
685
- }
686
- }
687
- }
688
- `
689
- )
690
-
691
- // execute the generator
692
- await runPipeline(config, [query])
693
-
694
- // look up the files in the artifact directory
695
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
696
-
697
- // make sure they match what we expect
698
- expect(
699
- recast.parse(fileContents!, {
700
- parser: typeScriptParser,
701
- })
702
- ).toMatchInlineSnapshot(`
703
- export type MyQuery = {
704
- readonly "input": MyQuery$input
705
- readonly "result": MyQuery$result | undefined
706
- };
707
-
708
- export type MyQuery$result = {
709
- readonly nodes: ({} & (({
710
- readonly id: string
711
- readonly __typename: "User"
712
- }) | ({
713
- readonly id: string
714
- readonly __typename: "Cat"
715
- })))[]
716
- };
717
-
718
- export type MyQuery$input = null;
719
- `)
720
- })
721
-
722
- test('unions', async function () {
723
- // the document to test
724
- const query = mockCollectedDoc(
725
- `
726
- query MyQuery {
727
- entities {
728
- ... on User {
729
- id
730
- }
731
- ... on Cat {
732
- id
733
- }
734
- }
735
- }
736
- `
737
- )
738
-
739
- // execute the generator
740
- await runPipeline(config, [query])
741
-
742
- // look up the files in the artifact directory
743
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
744
-
745
- // make sure they match what we expect
746
- expect(
747
- recast.parse(fileContents!, {
748
- parser: typeScriptParser,
749
- })
750
- ).toMatchInlineSnapshot(`
751
- export type MyQuery = {
752
- readonly "input": MyQuery$input
753
- readonly "result": MyQuery$result | undefined
754
- };
755
-
756
- export type MyQuery$result = {
757
- readonly entities: ({} & (({
758
- readonly id: string
759
- readonly __typename: "User"
760
- }) | ({
761
- readonly id: string
762
- readonly __typename: "Cat"
763
- })) | null)[] | null
764
- };
765
-
766
- export type MyQuery$input = null;
767
- `)
768
- })
769
-
770
- test('discriminated interface', async function () {
771
- // the document to test
772
- const query = mockCollectedDoc(
773
- `
774
- query MyQuery {
775
- nodes {
776
- id
777
- ... on User {
778
- firstName
779
- }
780
- ... on Cat {
781
- kitty
782
- }
783
- }
784
- }
785
- `
786
- )
787
-
788
- // execute the generator
789
- await runPipeline(config, [query])
790
-
791
- // look up the files in the artifact directory
792
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
793
-
794
- // make sure they match what we expect
795
- expect(
796
- recast.parse(fileContents!, {
797
- parser: typeScriptParser,
798
- })
799
- ).toMatchInlineSnapshot(`
800
- export type MyQuery = {
801
- readonly "input": MyQuery$input
802
- readonly "result": MyQuery$result | undefined
803
- };
804
-
805
- export type MyQuery$result = {
806
- readonly nodes: ({
807
- readonly id: string
808
- } & (({
809
- readonly firstName: string
810
- readonly __typename: "User"
811
- }) | ({
812
- readonly kitty: boolean
813
- readonly __typename: "Cat"
814
- })))[]
815
- };
816
-
817
- export type MyQuery$input = null;
818
- `)
819
- })
820
-
821
- test('intersecting interface', async function () {
822
- // the document to test
823
- const query = mockCollectedDoc(
824
- `
825
- query MyQuery {
826
- entities {
827
- ... on Animal {
828
- isAnimal
829
- }
830
- ... on User {
831
- firstName
832
- }
833
- ... on Cat {
834
- kitty
835
- }
836
- }
837
- }
838
- `
839
- )
840
-
841
- // execute the generator
842
- await runPipeline(config, [query])
843
-
844
- // look up the files in the artifact directory
845
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
846
-
847
- // make sure they match what we expect
848
- expect(
849
- recast.parse(fileContents!, {
850
- parser: typeScriptParser,
851
- })
852
- ).toMatchInlineSnapshot(`
853
- export type MyQuery = {
854
- readonly "input": MyQuery$input
855
- readonly "result": MyQuery$result | undefined
856
- };
857
-
858
- export type MyQuery$result = {
859
- readonly entities: ({} & (({
860
- readonly isAnimal: boolean
861
- readonly kitty: boolean
862
- readonly __typename: "Cat"
863
- }) | ({
864
- readonly firstName: string
865
- readonly __typename: "User"
866
- })) | null)[] | null
867
- };
868
-
869
- export type MyQuery$input = null;
870
- `)
871
- })
872
-
873
- test('fragment with custom scalars', async function () {
874
- // define a config with a custom scalar
875
- const localConfig = testConfig({
876
- schema: `
877
- scalar DateTime
878
-
879
- type TodoItem {
880
- text: String!
881
- createdAt: DateTime!
882
- }
883
-
884
- type Query {
885
- allItems: [TodoItem!]!
886
- }
887
- `,
888
- scalars: {
889
- DateTime: {
890
- type: 'Date',
891
- unmarshal(val: number): Date {
892
- return new Date(val)
893
- },
894
- marshal(date: Date): number {
895
- return date.getTime()
896
- },
897
- },
898
- },
899
- })
900
-
901
- // the document to test
902
- const query = mockCollectedDoc(`query MyQuery { allItems { createdAt } }`)
903
-
904
- // execute the generator
905
- await runPipeline(localConfig, [query])
906
-
907
- // look up the files in the artifact directory
908
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
909
-
910
- // make sure they match what we expect
911
- expect(
912
- recast.parse(fileContents!, {
913
- parser: typeScriptParser,
914
- })
915
- ).toMatchInlineSnapshot(`
916
- export type MyQuery = {
917
- readonly "input": MyQuery$input
918
- readonly "result": MyQuery$result | undefined
919
- };
920
-
921
- export type MyQuery$result = {
922
- readonly allItems: ({
923
- readonly createdAt: Date
924
- })[]
925
- };
926
-
927
- export type MyQuery$input = null;
928
- `)
929
- })
930
-
931
- test('input with custom scalars', async function () {
932
- // define a config with a custom scalar
933
- const localConfig = testConfig({
934
- schema: `
935
- scalar DateTime
936
-
937
- type TodoItem {
938
- text: String!
939
- createdAt: DateTime!
940
- }
941
-
942
- type Query {
943
- allItems(createdAt: DateTime): [TodoItem!]!
944
- }
945
- `,
946
- scalars: {
947
- DateTime: {
948
- type: 'Date',
949
- unmarshal(val: number): Date {
950
- return new Date(val)
951
- },
952
- marshal(date: Date): number {
953
- return date.getTime()
954
- },
955
- },
956
- },
957
- })
958
-
959
- // the document to test
960
- const query = mockCollectedDoc(
961
- `query MyQuery($date: DateTime!) { allItems(createdAt: $date) { createdAt } }`
962
- )
963
-
964
- // execute the generator
965
- await runPipeline(localConfig, [query])
966
-
967
- // look up the files in the artifact directory
968
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
969
-
970
- // make sure they match what we expect
971
- expect(
972
- recast.parse(fileContents!, {
973
- parser: typeScriptParser,
974
- })
975
- ).toMatchInlineSnapshot(`
976
- export type MyQuery = {
977
- readonly "input": MyQuery$input
978
- readonly "result": MyQuery$result | undefined
979
- };
980
-
981
- export type MyQuery$result = {
982
- readonly allItems: ({
983
- readonly createdAt: Date
984
- })[]
985
- };
986
-
987
- export type MyQuery$input = {
988
- date: Date
989
- };
990
- `)
991
- })
992
-
993
- test('can generate types for list of lists', async function () {
994
- // the document to test
995
- const query = mockCollectedDoc(
996
- `
997
- query MyQuery {
998
- listOfLists {
999
- firstName
1000
- nickname
1001
- }
1002
- }
1003
- `
1004
- )
1005
-
1006
- // execute the generator
1007
- await runPipeline(config, [query])
1008
-
1009
- // look up the files in the artifact directory
1010
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
1011
-
1012
- // make sure they match what we expect
1013
- expect(
1014
- recast.parse(fileContents!, {
1015
- parser: typeScriptParser,
1016
- })
1017
- ).toMatchInlineSnapshot(`
1018
- export type MyQuery = {
1019
- readonly "input": MyQuery$input
1020
- readonly "result": MyQuery$result | undefined
1021
- };
1022
-
1023
- export type MyQuery$result = {
1024
- readonly listOfLists: (({
1025
- readonly firstName: string
1026
- readonly nickname: string | null
1027
- } | null)[] | null)[]
1028
- };
1029
-
1030
- export type MyQuery$input = null;
1031
- `)
1032
- })
1033
-
1034
- test('duplicate fields', async function () {
1035
- // the document to test
1036
- const query = mockCollectedDoc(`query MyQuery {
1037
- user {
1038
- parent {
1039
- firstName
1040
- firstName
1041
- }
1042
- parent {
1043
- nickname
1044
- }
1045
- }
1046
- }`)
1047
-
1048
- // execute the generator
1049
- await runPipeline(config, [query])
1050
-
1051
- // look up the files in the artifact directory
1052
- const fileContents = await fs.readFile(config.artifactTypePath(query.document))
1053
-
1054
- // make sure they match what we expect
1055
- expect(
1056
- recast.parse(fileContents!, {
1057
- parser: typeScriptParser,
1058
- })
1059
- ).toMatchInlineSnapshot(`
1060
- export type MyQuery = {
1061
- readonly "input": MyQuery$input
1062
- readonly "result": MyQuery$result | undefined
1063
- };
1064
-
1065
- export type MyQuery$result = {
1066
- readonly user: {
1067
- readonly parent: {
1068
- readonly firstName: string
1069
- readonly nickname: string | null
1070
- } | null
1071
- } | null
1072
- };
1073
-
1074
- export type MyQuery$input = null;
1075
- `)
1076
- })
1077
-
1078
- test('can reference list fragments', async function () {
1079
- const unmaskedConfig = testConfig({ disableMasking: true, schema: config.schema })
1080
-
1081
- // the document to test
1082
- const docs = [
1083
- mockCollectedDoc(`
1084
- query MyQuery {
1085
- users @list(name:"My_Users") {
1086
- id
1087
- }
1088
- }
1089
- `),
1090
- mockCollectedDoc(`
1091
- mutation MyMutation(
1092
- $filter: UserFilter,
1093
- $filterList: [UserFilter!]!,
1094
- $id: ID!
1095
- $firstName: String!
1096
- $admin: Boolean
1097
- $age: Int
1098
- $weight: Float
1099
- ) {
1100
- doThing(
1101
- filter: $filter,
1102
- list: $filterList,
1103
- id:$id
1104
- firstName:$firstName
1105
- admin:$admin
1106
- age:$age
1107
- weight:$weight
1108
- ) {
1109
- ...My_Users_remove
1110
- ...My_Users_insert
1111
- }
1112
- }
1113
- `),
1114
- ]
1115
-
1116
- // execute the generator
1117
- await runPipeline(unmaskedConfig, docs)
1118
-
1119
- // look up the files in the artifact directory
1120
- const fileContents = await fs.readFile(config.artifactTypePath(docs[1].document))
1121
-
1122
- // make sure they match what we expect
1123
- expect(
1124
- recast.parse(fileContents!, {
1125
- parser: typeScriptParser,
1126
- })
1127
- ).toMatchInlineSnapshot(`
1128
- import type { MyEnum } from "$houdini/graphql/enums";
1129
-
1130
- export type MyMutation = {
1131
- readonly "input": MyMutation$input
1132
- readonly "result": MyMutation$result
1133
- };
1134
-
1135
- export type MyMutation$result = {
1136
- readonly doThing: {
1137
- readonly id: string
1138
- readonly $fragments: {
1139
- My_Users_remove: true
1140
- My_Users_insert: true
1141
- }
1142
- } | null
1143
- };
1144
-
1145
- type NestedUserFilter = {
1146
- id: string
1147
- firstName: string
1148
- admin?: boolean | null | undefined
1149
- age?: number | null | undefined
1150
- weight?: number | null | undefined
1151
- };
1152
-
1153
- type UserFilter = {
1154
- middle?: NestedUserFilter | null | undefined
1155
- listRequired: (string)[]
1156
- nullList?: (string | null | undefined)[] | null | undefined
1157
- recursive?: UserFilter | null | undefined
1158
- enum?: MyEnum | null | undefined
1159
- };
1160
-
1161
- export type MyMutation$input = {
1162
- filter?: UserFilter | null | undefined
1163
- filterList: (UserFilter)[]
1164
- id: string
1165
- firstName: string
1166
- admin?: boolean | null | undefined
1167
- age?: number | null | undefined
1168
- weight?: number | null | undefined
1169
- };
1170
-
1171
- export type MyMutation$optimistic = {
1172
- readonly doThing?: {
1173
- readonly id?: string
1174
- } | null
1175
- };
1176
- `)
1177
- })
1178
-
1179
- test('disable default fragment masking', async function () {
1180
- const configWithoutMasking = testConfig({
1181
- disableMasking: true,
1182
- schema: config.schema,
1183
- })
1184
-
1185
- const docs = [
1186
- mockCollectedDoc(`
1187
- query MyQuery {
1188
- user {
1189
- ...UserBase
1190
- ...UserMore
1191
- }
1192
- }
1193
- `),
1194
- mockCollectedDoc(`
1195
- fragment UserBase on User {
1196
- id
1197
- firstName
1198
- }
1199
- `),
1200
- mockCollectedDoc(`
1201
- fragment UserMore on User {
1202
- friends {
1203
- ...UserBase
1204
- }
1205
- }
1206
- `),
1207
- ]
1208
-
1209
- // execute the generator
1210
- await runPipeline(configWithoutMasking, docs)
1211
-
1212
- // look up the files in the artifact directory
1213
- const fileContents = await fs.readFile(
1214
- configWithoutMasking.artifactTypePath(docs[0].document)
1215
- )
1216
-
1217
- // make sure they match what we expect
1218
- expect(
1219
- recast.parse(fileContents!, {
1220
- parser: typeScriptParser,
1221
- })
1222
- ).toMatchInlineSnapshot(`
1223
- export type MyQuery = {
1224
- readonly "input": MyQuery$input
1225
- readonly "result": MyQuery$result | undefined
1226
- };
1227
-
1228
- export type MyQuery$result = {
1229
- readonly user: {
1230
- readonly id: string
1231
- readonly firstName: string
1232
- readonly friends: ({
1233
- readonly id: string
1234
- readonly firstName: string
1235
- readonly $fragments: {
1236
- UserBase: true
1237
- }
1238
- } | null)[] | null
1239
- readonly $fragments: {
1240
- UserBase: true
1241
- UserMore: true
1242
- }
1243
- } | null
1244
- };
1245
-
1246
- export type MyQuery$input = null;
1247
- `)
1248
- })
1249
-
1250
- test('disable individual fragment masking', async function () {
1251
- const configWithMasking = testConfig({
1252
- disableMasking: false,
1253
- schema: config.schema,
1254
- })
1255
-
1256
- const docs = [
1257
- mockCollectedDoc(`
1258
- query MyQuery {
1259
- user {
1260
- ...UserBase @houdini(mask: false)
1261
- ...UserMore
1262
- }
1263
- }
1264
- `),
1265
- mockCollectedDoc(`
1266
- fragment UserBase on User {
1267
- id
1268
- firstName
1269
- }
1270
- `),
1271
- mockCollectedDoc(`
1272
- fragment UserMore on User {
1273
- friends {
1274
- ...UserBase
1275
- }
1276
- }
1277
- `),
1278
- ]
1279
-
1280
- // execute the generator
1281
- await runPipeline(configWithMasking, docs)
1282
-
1283
- // look up the files in the artifact directory
1284
- const [queryFileContents, fragmentFileContents] = await Promise.all([
1285
- fs.readFile(configWithMasking.artifactTypePath(docs[0].document)),
1286
- fs.readFile(configWithMasking.artifactTypePath(docs[2].document)),
1287
- ])
1288
-
1289
- // make sure they match what we expect
1290
- expect(
1291
- recast.parse(queryFileContents!, {
1292
- parser: typeScriptParser,
1293
- })
1294
- ).toMatchInlineSnapshot(`
1295
- export type MyQuery = {
1296
- readonly "input": MyQuery$input
1297
- readonly "result": MyQuery$result | undefined
1298
- };
1299
-
1300
- export type MyQuery$result = {
1301
- readonly user: {
1302
- readonly id: string
1303
- readonly firstName: string
1304
- readonly $fragments: {
1305
- UserBase: true
1306
- UserMore: true
1307
- }
1308
- } | null
1309
- };
1310
-
1311
- export type MyQuery$input = null;
1312
- `)
1313
-
1314
- expect(
1315
- recast.parse(fragmentFileContents!, {
1316
- parser: typeScriptParser,
1317
- })
1318
- ).toMatchInlineSnapshot(`
1319
- export type UserMore = {
1320
- readonly "shape"?: UserMore$data
1321
- readonly "$fragments": {
1322
- "UserMore": true
1323
- }
1324
- };
1325
-
1326
- export type UserMore$data = {
1327
- readonly friends: ({
1328
- readonly $fragments: {
1329
- UserBase: true
1330
- }
1331
- } | null)[] | null
1332
- };
1333
- `)
1334
- })
1335
-
1336
- test('enable individual fragment masking', async function () {
1337
- const configWithoutMasking = testConfig({
1338
- disableMasking: true,
1339
- schema: config.schema,
1340
- })
1341
-
1342
- const docs = [
1343
- mockCollectedDoc(`
1344
- query MyQuery {
1345
- user {
1346
- ...UserBase @houdini(mask: true)
1347
- ...UserMore
1348
- }
1349
- }
1350
- `),
1351
- mockCollectedDoc(`
1352
- fragment UserBase on User {
1353
- id
1354
- firstName
1355
- }
1356
- `),
1357
- mockCollectedDoc(`
1358
- fragment UserMore on User {
1359
- friends {
1360
- ...UserBase
1361
- }
1362
- }
1363
- `),
1364
- ]
1365
-
1366
- // execute the generator
1367
- await runPipeline(configWithoutMasking, docs)
1368
-
1369
- // look up the files in the artifact directory
1370
- const [queryFileContents, fragmentFileContents] = await Promise.all([
1371
- fs.readFile(configWithoutMasking.artifactTypePath(docs[0].document)),
1372
- fs.readFile(configWithoutMasking.artifactTypePath(docs[2].document)),
1373
- ])
1374
-
1375
- // make sure they match what we expect
1376
- expect(
1377
- recast.parse(queryFileContents!, {
1378
- parser: typeScriptParser,
1379
- })
1380
- ).toMatchInlineSnapshot(`
1381
- export type MyQuery = {
1382
- readonly "input": MyQuery$input
1383
- readonly "result": MyQuery$result | undefined
1384
- };
1385
-
1386
- export type MyQuery$result = {
1387
- readonly user: {
1388
- readonly friends: ({
1389
- readonly id: string
1390
- readonly firstName: string
1391
- readonly $fragments: {
1392
- UserBase: true
1393
- }
1394
- } | null)[] | null
1395
- readonly $fragments: {
1396
- UserBase: true
1397
- UserMore: true
1398
- }
1399
- } | null
1400
- };
1401
-
1402
- export type MyQuery$input = null;
1403
- `)
1404
-
1405
- expect(
1406
- recast.parse(fragmentFileContents!, {
1407
- parser: typeScriptParser,
1408
- })
1409
- ).toMatchInlineSnapshot(`
1410
- export type UserMore = {
1411
- readonly "shape"?: UserMore$data
1412
- readonly "$fragments": {
1413
- "UserMore": true
1414
- }
1415
- };
1416
-
1417
- export type UserMore$data = {
1418
- readonly friends: ({
1419
- readonly id: string
1420
- readonly firstName: string
1421
- readonly $fragments: {
1422
- UserBase: true
1423
- }
1424
- } | null)[] | null
1425
- };
1426
- `)
1427
- })
1428
-
1429
- test.todo('fragments on interfaces')
1430
-
1431
- test.todo('intersections with __typename in subselection')
1432
-
1433
- test.todo('inline fragments')
1434
- })