@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.
@@ -30,7 +30,14 @@ const rawQueryToFlowTypes = (query: string, options?: Options): string => {
30
30
  scalars: {PositiveNumber: 'number'},
31
31
  ...options,
32
32
  })
33
- .map(({code}) => code)
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: "Human",
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: ?string,
178
+ primaryFunction: string,
143
179
  |} | {|
144
- __typename: "Animal"
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: "Human",
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 (!hasNonFragments || !processedOptions) {
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
- /* eslint-disable flowtype-errors/uncovered */
124
- const errors = validate(schemaForValidation, withTypeNames);
125
- /* eslint-disable flowtype-errors/uncovered */
126
- if (errors.length) {
127
- errors.forEach((error) => {
128
- console.error(
129
- `Schema validation found errors for ${raw.loc.path}!`,
130
- );
131
- console.error(printed);
132
- console.error(error);
133
- validationFailures++;
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(