@khanacademy/graphql-flow 0.1.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +1 -1
- package/CHANGELOG.md +23 -0
- package/Readme.md +15 -0
- package/dist/cli/config.js +4 -4
- package/dist/cli/config.js.flow +3 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/run.js +18 -14
- package/dist/cli/run.js.flow +18 -14
- package/dist/cli/run.js.map +1 -1
- package/dist/generateResponseType.js +152 -53
- package/dist/generateResponseType.js.flow +223 -65
- package/dist/generateResponseType.js.map +1 -1
- package/dist/generateTypeFiles.js +80 -39
- package/dist/generateTypeFiles.js.flow +75 -35
- package/dist/generateTypeFiles.js.map +1 -1
- package/dist/index.js +48 -6
- package/dist/index.js.flow +54 -4
- package/dist/index.js.map +1 -1
- package/dist/parser/parse.js +27 -17
- package/dist/parser/parse.js.map +1 -1
- package/dist/types.js.flow +6 -0
- package/package.json +1 -1
- package/src/__test__/example-schema.graphql +1 -1
- package/src/__test__/generateTypeFileContents.test.js +61 -0
- package/src/__test__/graphql-flow.test.js +243 -32
- package/src/cli/config.js +3 -0
- package/src/cli/run.js +18 -14
- package/src/generateResponseType.js +223 -65
- package/src/generateTypeFiles.js +75 -35
- package/src/index.js +54 -4
- package/src/types.js +6 -0
|
@@ -30,7 +30,14 @@ const rawQueryToFlowTypes = (query: string, options?: Options): string => {
|
|
|
30
30
|
scalars: {PositiveNumber: 'number'},
|
|
31
31
|
...options,
|
|
32
32
|
})
|
|
33
|
-
.map(
|
|
33
|
+
.map(
|
|
34
|
+
({typeName, code, extraTypes}) =>
|
|
35
|
+
`// ${typeName}.js\n${code}` +
|
|
36
|
+
Object.keys(extraTypes)
|
|
37
|
+
.sort()
|
|
38
|
+
.map((k) => `\nexport type ${k} = ${extraTypes[k]};`)
|
|
39
|
+
.join(''),
|
|
40
|
+
)
|
|
34
41
|
.join('\n\n');
|
|
35
42
|
};
|
|
36
43
|
|
|
@@ -43,6 +50,7 @@ describe('graphql-flow generation', () => {
|
|
|
43
50
|
`);
|
|
44
51
|
|
|
45
52
|
expect(result).toMatchInlineSnapshot(`
|
|
53
|
+
// SomeQueryType.js
|
|
46
54
|
export type SomeQueryType = {|
|
|
47
55
|
variables: {|
|
|
48
56
|
candies: number
|
|
@@ -54,6 +62,33 @@ describe('graphql-flow generation', () => {
|
|
|
54
62
|
`);
|
|
55
63
|
});
|
|
56
64
|
|
|
65
|
+
it('should split types', () => {
|
|
66
|
+
const result = rawQueryToFlowTypes(
|
|
67
|
+
`
|
|
68
|
+
query SomeQuery($id: String!) {
|
|
69
|
+
human(id: $id) { id }
|
|
70
|
+
}
|
|
71
|
+
`,
|
|
72
|
+
{splitTypes: true},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
expect(result).toMatchInlineSnapshot(`
|
|
76
|
+
// SomeQueryType.js
|
|
77
|
+
export type SomeQueryType = {|
|
|
78
|
+
variables: {|
|
|
79
|
+
id: string
|
|
80
|
+
|},
|
|
81
|
+
response: {|
|
|
82
|
+
|
|
83
|
+
/** A human character*/
|
|
84
|
+
human: ?{|
|
|
85
|
+
id: string
|
|
86
|
+
|}
|
|
87
|
+
|}
|
|
88
|
+
|};
|
|
89
|
+
`);
|
|
90
|
+
});
|
|
91
|
+
|
|
57
92
|
it('should work with a basic query', () => {
|
|
58
93
|
const result = rawQueryToFlowTypes(`
|
|
59
94
|
query SomeQuery {
|
|
@@ -69,20 +104,21 @@ describe('graphql-flow generation', () => {
|
|
|
69
104
|
`);
|
|
70
105
|
|
|
71
106
|
expect(result).toMatchInlineSnapshot(`
|
|
107
|
+
// SomeQueryType.js
|
|
72
108
|
export type SomeQueryType = {|
|
|
73
109
|
variables: {||},
|
|
74
110
|
response: {|
|
|
75
111
|
|
|
76
112
|
/** A human character*/
|
|
77
113
|
human: ?{|
|
|
114
|
+
friends: ?$ReadOnlyArray<?{|
|
|
115
|
+
name: ?string
|
|
116
|
+
|}>,
|
|
117
|
+
homePlanet: ?string,
|
|
78
118
|
id: string,
|
|
79
119
|
|
|
80
120
|
/** The person's name*/
|
|
81
121
|
name: ?string,
|
|
82
|
-
homePlanet: ?string,
|
|
83
|
-
friends: ?$ReadOnlyArray<?{|
|
|
84
|
-
name: ?string
|
|
85
|
-
|}>,
|
|
86
122
|
|}
|
|
87
123
|
|}
|
|
88
124
|
|};
|
|
@@ -99,6 +135,7 @@ describe('graphql-flow generation', () => {
|
|
|
99
135
|
`);
|
|
100
136
|
|
|
101
137
|
expect(result).toMatchInlineSnapshot(`
|
|
138
|
+
// SomeQueryType.js
|
|
102
139
|
export type SomeQueryType = {|
|
|
103
140
|
variables: {||},
|
|
104
141
|
response: {|
|
|
@@ -128,20 +165,21 @@ describe('graphql-flow generation', () => {
|
|
|
128
165
|
}
|
|
129
166
|
`);
|
|
130
167
|
expect(result).toMatchInlineSnapshot(`
|
|
168
|
+
// SomeQueryType.js
|
|
131
169
|
export type SomeQueryType = {|
|
|
132
170
|
variables: {||},
|
|
133
171
|
response: {|
|
|
134
172
|
friend: ?({|
|
|
135
|
-
__typename: "
|
|
136
|
-
id: string,
|
|
137
|
-
hands: ?number,
|
|
173
|
+
__typename: "Animal"
|
|
138
174
|
|} | {|
|
|
139
175
|
__typename: "Droid",
|
|
140
176
|
|
|
141
177
|
/** The robot's primary function*/
|
|
142
|
-
primaryFunction:
|
|
178
|
+
primaryFunction: string,
|
|
143
179
|
|} | {|
|
|
144
|
-
__typename: "
|
|
180
|
+
__typename: "Human",
|
|
181
|
+
hands: ?number,
|
|
182
|
+
id: string,
|
|
145
183
|
|})
|
|
146
184
|
|}
|
|
147
185
|
|};
|
|
@@ -178,48 +216,64 @@ describe('graphql-flow generation', () => {
|
|
|
178
216
|
`);
|
|
179
217
|
|
|
180
218
|
expect(result).toMatchInlineSnapshot(`
|
|
219
|
+
// SomeQueryType.js
|
|
181
220
|
export type SomeQueryType = {|
|
|
182
221
|
variables: {||},
|
|
183
222
|
response: {|
|
|
184
223
|
|
|
185
224
|
/** A human character*/
|
|
186
225
|
human: ?{|
|
|
187
|
-
id: string,
|
|
188
|
-
|
|
189
|
-
/** The person's name*/
|
|
190
|
-
name: ?string,
|
|
191
|
-
homePlanet: ?string,
|
|
192
|
-
hands: ?number,
|
|
193
226
|
alive: ?boolean,
|
|
194
227
|
friends: ?$ReadOnlyArray<?({|
|
|
195
|
-
__typename: "
|
|
196
|
-
id: string,
|
|
197
|
-
name: ?string,
|
|
198
|
-
friends: ?$ReadOnlyArray<?{|
|
|
199
|
-
id: string
|
|
200
|
-
|}>,
|
|
228
|
+
__typename: "Droid",
|
|
201
229
|
appearsIn: ?$ReadOnlyArray<
|
|
202
230
|
/** - NEW_HOPE
|
|
203
231
|
- EMPIRE
|
|
204
232
|
- JEDI*/
|
|
205
233
|
?("NEW_HOPE" | "EMPIRE" | "JEDI")>,
|
|
206
|
-
hands: ?number,
|
|
207
|
-
|} | {|
|
|
208
|
-
__typename: "Droid",
|
|
209
|
-
id: string,
|
|
210
|
-
name: ?string,
|
|
211
234
|
friends: ?$ReadOnlyArray<?{|
|
|
212
235
|
id: string
|
|
213
236
|
|}>,
|
|
237
|
+
id: string,
|
|
238
|
+
name: ?string,
|
|
239
|
+
|} | {|
|
|
240
|
+
__typename: "Human",
|
|
214
241
|
appearsIn: ?$ReadOnlyArray<
|
|
215
242
|
/** - NEW_HOPE
|
|
216
243
|
- EMPIRE
|
|
217
244
|
- JEDI*/
|
|
218
245
|
?("NEW_HOPE" | "EMPIRE" | "JEDI")>,
|
|
246
|
+
friends: ?$ReadOnlyArray<?{|
|
|
247
|
+
id: string
|
|
248
|
+
|}>,
|
|
249
|
+
hands: ?number,
|
|
250
|
+
id: string,
|
|
251
|
+
name: ?string,
|
|
219
252
|
|})>,
|
|
253
|
+
hands: ?number,
|
|
254
|
+
homePlanet: ?string,
|
|
255
|
+
id: string,
|
|
256
|
+
|
|
257
|
+
/** The person's name*/
|
|
258
|
+
name: ?string,
|
|
220
259
|
|}
|
|
221
260
|
|}
|
|
222
261
|
|};
|
|
262
|
+
|
|
263
|
+
// Profile.js
|
|
264
|
+
export type Profile = {|
|
|
265
|
+
__typename: "Droid" | "Human",
|
|
266
|
+
appearsIn: ?$ReadOnlyArray<
|
|
267
|
+
/** - NEW_HOPE
|
|
268
|
+
- EMPIRE
|
|
269
|
+
- JEDI*/
|
|
270
|
+
?("NEW_HOPE" | "EMPIRE" | "JEDI")>,
|
|
271
|
+
friends: ?$ReadOnlyArray<?{|
|
|
272
|
+
id: string
|
|
273
|
+
|}>,
|
|
274
|
+
id: string,
|
|
275
|
+
name: ?string,
|
|
276
|
+
|};
|
|
223
277
|
`);
|
|
224
278
|
});
|
|
225
279
|
|
|
@@ -238,6 +292,7 @@ describe('graphql-flow generation', () => {
|
|
|
238
292
|
);
|
|
239
293
|
|
|
240
294
|
expect(result).toMatchInlineSnapshot(`
|
|
295
|
+
// SomeQueryType.js
|
|
241
296
|
export type SomeQueryType = {|
|
|
242
297
|
variables: {||},
|
|
243
298
|
response: {|
|
|
@@ -283,6 +338,158 @@ describe('graphql-flow generation', () => {
|
|
|
283
338
|
});
|
|
284
339
|
});
|
|
285
340
|
|
|
341
|
+
describe('Fragments', () => {
|
|
342
|
+
it('should resolve correctly, and produce a type file for the fragment', () => {
|
|
343
|
+
const result = rawQueryToFlowTypes(
|
|
344
|
+
`query Hello {
|
|
345
|
+
hero(episode: JEDI) {
|
|
346
|
+
...onChar
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
fragment onChar on Character {
|
|
351
|
+
__typename
|
|
352
|
+
... on Droid {
|
|
353
|
+
primaryFunction
|
|
354
|
+
}
|
|
355
|
+
}`,
|
|
356
|
+
);
|
|
357
|
+
expect(result).toMatchInlineSnapshot(`
|
|
358
|
+
// HelloType.js
|
|
359
|
+
export type HelloType = {|
|
|
360
|
+
variables: {||},
|
|
361
|
+
response: {|
|
|
362
|
+
hero: ?({|
|
|
363
|
+
__typename: "Droid",
|
|
364
|
+
|
|
365
|
+
/** The robot's primary function*/
|
|
366
|
+
primaryFunction: string,
|
|
367
|
+
|} | {|
|
|
368
|
+
__typename: "Human"
|
|
369
|
+
|})
|
|
370
|
+
|}
|
|
371
|
+
|};
|
|
372
|
+
|
|
373
|
+
// onChar.js
|
|
374
|
+
export type onChar = {|
|
|
375
|
+
__typename: "Droid",
|
|
376
|
+
|
|
377
|
+
/** The robot's primary function*/
|
|
378
|
+
primaryFunction: string,
|
|
379
|
+
|} | {|
|
|
380
|
+
__typename: "Human"
|
|
381
|
+
|};
|
|
382
|
+
`);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('Should specialize the fragment type correctly', () => {
|
|
386
|
+
const result = rawQueryToFlowTypes(
|
|
387
|
+
`query Deps {
|
|
388
|
+
droid(id: "hello") {
|
|
389
|
+
...Hello
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
fragment Hello on Character {
|
|
394
|
+
__typename
|
|
395
|
+
name
|
|
396
|
+
... on Droid {
|
|
397
|
+
primaryFunction
|
|
398
|
+
}
|
|
399
|
+
... on Human {
|
|
400
|
+
homePlanet
|
|
401
|
+
}
|
|
402
|
+
}`,
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
// Note how `homePlanet` is ommitted in
|
|
406
|
+
// `DepsType.response.droid`
|
|
407
|
+
expect(result).toMatchInlineSnapshot(`
|
|
408
|
+
// DepsType.js
|
|
409
|
+
export type DepsType = {|
|
|
410
|
+
variables: {||},
|
|
411
|
+
response: {|
|
|
412
|
+
|
|
413
|
+
/** A robot character*/
|
|
414
|
+
droid: ?{|
|
|
415
|
+
__typename: "Droid",
|
|
416
|
+
name: ?string,
|
|
417
|
+
|
|
418
|
+
/** The robot's primary function*/
|
|
419
|
+
primaryFunction: string,
|
|
420
|
+
|}
|
|
421
|
+
|}
|
|
422
|
+
|};
|
|
423
|
+
|
|
424
|
+
// Hello.js
|
|
425
|
+
export type Hello = {|
|
|
426
|
+
__typename: "Droid",
|
|
427
|
+
name: ?string,
|
|
428
|
+
|
|
429
|
+
/** The robot's primary function*/
|
|
430
|
+
primaryFunction: string,
|
|
431
|
+
|} | {|
|
|
432
|
+
__typename: "Human",
|
|
433
|
+
homePlanet: ?string,
|
|
434
|
+
name: ?string,
|
|
435
|
+
|};
|
|
436
|
+
`);
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('should generate all types when exportAllObjectTypes is set', () => {
|
|
441
|
+
const result = rawQueryToFlowTypes(
|
|
442
|
+
`
|
|
443
|
+
query SomeQuery {
|
|
444
|
+
human(id: "Han Solo") {
|
|
445
|
+
id
|
|
446
|
+
name
|
|
447
|
+
homePlanet
|
|
448
|
+
hands
|
|
449
|
+
alive
|
|
450
|
+
friends {
|
|
451
|
+
__typename
|
|
452
|
+
... on Human {
|
|
453
|
+
hands
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
`,
|
|
459
|
+
{exportAllObjectTypes: true},
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
expect(result).toMatchInlineSnapshot(`
|
|
463
|
+
// SomeQueryType.js
|
|
464
|
+
export type SomeQueryType = {|
|
|
465
|
+
variables: {||},
|
|
466
|
+
response: {|
|
|
467
|
+
|
|
468
|
+
/** A human character*/
|
|
469
|
+
human: ?SomeQuery_human
|
|
470
|
+
|}
|
|
471
|
+
|};
|
|
472
|
+
export type SomeQuery_human = {|
|
|
473
|
+
alive: ?boolean,
|
|
474
|
+
friends: ?$ReadOnlyArray<?SomeQuery_human_friends>,
|
|
475
|
+
hands: ?number,
|
|
476
|
+
homePlanet: ?string,
|
|
477
|
+
id: string,
|
|
478
|
+
|
|
479
|
+
/** The person's name*/
|
|
480
|
+
name: ?string,
|
|
481
|
+
|};
|
|
482
|
+
export type SomeQuery_human_friends = SomeQuery_human_friends_Droid | SomeQuery_human_friends_Human;
|
|
483
|
+
export type SomeQuery_human_friends_Droid = {|
|
|
484
|
+
__typename: "Droid"
|
|
485
|
+
|};
|
|
486
|
+
export type SomeQuery_human_friends_Human = {|
|
|
487
|
+
__typename: "Human",
|
|
488
|
+
hands: ?number,
|
|
489
|
+
|};
|
|
490
|
+
`);
|
|
491
|
+
});
|
|
492
|
+
|
|
286
493
|
describe('Input variables', () => {
|
|
287
494
|
it('should generate a variables type', () => {
|
|
288
495
|
const result = rawQueryToFlowTypes(
|
|
@@ -300,6 +507,7 @@ describe('graphql-flow generation', () => {
|
|
|
300
507
|
);
|
|
301
508
|
|
|
302
509
|
expect(result).toMatchInlineSnapshot(`
|
|
510
|
+
// SomeQueryType.js
|
|
303
511
|
export type SomeQueryType = {|
|
|
304
512
|
variables: {|
|
|
305
513
|
id: string,
|
|
@@ -310,6 +518,9 @@ describe('graphql-flow generation', () => {
|
|
|
310
518
|
episode?: ?("NEW_HOPE" | "EMPIRE" | "JEDI"),
|
|
311
519
|
|},
|
|
312
520
|
response: {|
|
|
521
|
+
hero: ?{|
|
|
522
|
+
name: ?string
|
|
523
|
+
|},
|
|
313
524
|
|
|
314
525
|
/** A human character*/
|
|
315
526
|
human: ?{|
|
|
@@ -317,9 +528,6 @@ describe('graphql-flow generation', () => {
|
|
|
317
528
|
name: ?string
|
|
318
529
|
|}>
|
|
319
530
|
|},
|
|
320
|
-
hero: ?{|
|
|
321
|
-
name: ?string
|
|
322
|
-
|},
|
|
323
531
|
|}
|
|
324
532
|
|};
|
|
325
533
|
`);
|
|
@@ -339,16 +547,17 @@ describe('graphql-flow generation', () => {
|
|
|
339
547
|
{readOnlyArray: false},
|
|
340
548
|
);
|
|
341
549
|
expect(result).toMatchInlineSnapshot(`
|
|
550
|
+
// SomeQueryType.js
|
|
342
551
|
export type SomeQueryType = {|
|
|
343
552
|
variables: {||},
|
|
344
553
|
response: {|
|
|
345
554
|
hero: ?({|
|
|
346
555
|
id: string,
|
|
347
|
-
|
|
348
|
-
/** The person's name*/
|
|
349
556
|
name: ?string,
|
|
350
557
|
|} | {|
|
|
351
558
|
id: string,
|
|
559
|
+
|
|
560
|
+
/** The person's name*/
|
|
352
561
|
name: ?string,
|
|
353
562
|
|})
|
|
354
563
|
|}
|
|
@@ -370,6 +579,7 @@ describe('graphql-flow generation', () => {
|
|
|
370
579
|
{readOnlyArray: false},
|
|
371
580
|
);
|
|
372
581
|
expect(result).toMatchInlineSnapshot(`
|
|
582
|
+
// SomeQueryType.js
|
|
373
583
|
export type SomeQueryType = {|
|
|
374
584
|
variables: {||},
|
|
375
585
|
response: {|
|
|
@@ -397,6 +607,7 @@ describe('graphql-flow generation', () => {
|
|
|
397
607
|
);
|
|
398
608
|
|
|
399
609
|
expect(result).toMatchInlineSnapshot(`
|
|
610
|
+
// addCharacterType.js
|
|
400
611
|
export type addCharacterType = {|
|
|
401
612
|
variables: {|
|
|
402
613
|
|
package/src/cli/config.js
CHANGED
|
@@ -53,6 +53,9 @@ export const loadConfigFile = (configFile: string): CliConfig => {
|
|
|
53
53
|
'strictNullability',
|
|
54
54
|
'regenerateCommand',
|
|
55
55
|
'readOnlyArray',
|
|
56
|
+
'splitTypes',
|
|
57
|
+
'generatedDirectory',
|
|
58
|
+
'exportAllObjectTypes',
|
|
56
59
|
];
|
|
57
60
|
Object.keys(data.options).forEach((k) => {
|
|
58
61
|
if (!externalOptionsKeys.includes(k)) {
|
package/src/cli/run.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
// @flow
|
|
2
3
|
/* eslint-disable no-console */
|
|
3
4
|
import {generateTypeFiles, processPragmas} from '../generateTypeFiles';
|
|
@@ -113,27 +114,30 @@ Object.keys(resolved).forEach((k) => {
|
|
|
113
114
|
);
|
|
114
115
|
const rawSource: string = raw.literals[0];
|
|
115
116
|
const processedOptions = processPragmas(config.options, rawSource);
|
|
116
|
-
if (!
|
|
117
|
+
if (!processedOptions) {
|
|
117
118
|
return;
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
// eslint-disable-next-line flowtype-errors/uncovered
|
|
121
122
|
const withTypeNames: DocumentNode = addTypenameToDocument(document);
|
|
122
123
|
const printed = print(withTypeNames);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
errors
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
124
|
+
|
|
125
|
+
if (hasNonFragments) {
|
|
126
|
+
/* eslint-disable flowtype-errors/uncovered */
|
|
127
|
+
const errors = validate(schemaForValidation, withTypeNames);
|
|
128
|
+
/* eslint-disable flowtype-errors/uncovered */
|
|
129
|
+
if (errors.length) {
|
|
130
|
+
errors.forEach((error) => {
|
|
131
|
+
console.error(
|
|
132
|
+
`Schema validation found errors for ${raw.loc.path}!`,
|
|
133
|
+
);
|
|
134
|
+
console.error(printed);
|
|
135
|
+
console.error(error);
|
|
136
|
+
validationFailures++;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/* eslint-enable flowtype-errors/uncovered */
|
|
135
140
|
}
|
|
136
|
-
/* eslint-enable flowtype-errors/uncovered */
|
|
137
141
|
|
|
138
142
|
try {
|
|
139
143
|
generateTypeFiles(
|