@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.
@@ -7,6 +7,7 @@ import type {
7
7
  FieldNode,
8
8
  IntrospectionOutputTypeRef,
9
9
  OperationDefinitionNode,
10
+ FragmentDefinitionNode,
10
11
  } from 'graphql';
11
12
  import type {Config, Schema, Selections} from './types';
12
13
  import {
@@ -21,7 +22,7 @@ import type {
21
22
  IntrospectionObjectType,
22
23
  IntrospectionUnionType,
23
24
  } from 'graphql/utilities/introspectionQuery';
24
- import type {
25
+ import {
25
26
  BabelNodeObjectTypeProperty,
26
27
  BabelNodeObjectTypeSpreadProperty,
27
28
  } from '@babel/types';
@@ -43,6 +44,77 @@ export const generateResponseType = (
43
44
  return generate(ast).code;
44
45
  };
45
46
 
47
+ const sortedObjectTypeAnnotation = (
48
+ config: Config,
49
+ properties: Array<
50
+ BabelNodeObjectTypeProperty | BabelNodeObjectTypeSpreadProperty,
51
+ >,
52
+ ) => {
53
+ const obj = babelTypes.objectTypeAnnotation(
54
+ properties.sort((a, b) => {
55
+ if (
56
+ a.type === 'ObjectTypeProperty' &&
57
+ b.type === 'ObjectTypeProperty'
58
+ ) {
59
+ const aName = a.key.type === 'Identifier' ? a.key.name : '';
60
+ const bName = b.key.type === 'Identifier' ? b.key.name : '';
61
+ return aName < bName ? -1 : 1;
62
+ }
63
+ return 0;
64
+ }),
65
+ undefined /* indexers */,
66
+ undefined /* callProperties */,
67
+ undefined /* internalSlots */,
68
+ true /* exact */,
69
+ );
70
+ const name = config.path.join('_');
71
+ const isTopLevelType = config.path.length <= 1;
72
+ if (config.allObjectTypes != null && !isTopLevelType) {
73
+ config.allObjectTypes[name] = obj;
74
+ return babelTypes.genericTypeAnnotation(babelTypes.identifier(name));
75
+ } else {
76
+ return obj;
77
+ }
78
+ };
79
+
80
+ export const generateFragmentType = (
81
+ schema: Schema,
82
+ fragment: FragmentDefinitionNode,
83
+ config: Config,
84
+ ): string => {
85
+ const onType = fragment.typeCondition.name.value;
86
+ let ast;
87
+
88
+ if (schema.typesByName[onType]) {
89
+ ast = sortedObjectTypeAnnotation(
90
+ config,
91
+ objectPropertiesToFlow(
92
+ config,
93
+ schema.typesByName[onType],
94
+ onType,
95
+ fragment.selectionSet.selections,
96
+ ),
97
+ );
98
+ } else if (schema.interfacesByName[onType]) {
99
+ ast = unionOrInterfaceToFlow(
100
+ config,
101
+ config.schema.interfacesByName[onType],
102
+ fragment.selectionSet.selections,
103
+ );
104
+ } else if (schema.unionsByName[onType]) {
105
+ ast = unionOrInterfaceToFlow(
106
+ config,
107
+ config.schema.unionsByName[onType],
108
+ fragment.selectionSet.selections,
109
+ );
110
+ } else {
111
+ throw new Error(`Unknown ${onType}`);
112
+ }
113
+
114
+ // eslint-disable-next-line flowtype-errors/uncovered
115
+ return generate(ast).code;
116
+ };
117
+
46
118
  const _typeToFlow = (
47
119
  config: Config,
48
120
  type,
@@ -132,35 +204,45 @@ export const typeToFlow = (
132
204
  return transferLeadingComments(inner, result);
133
205
  };
134
206
 
207
+ const ensureOnlyOneTypenameProperty = (properties) => {
208
+ let seenTypeName: false | string = false;
209
+ return properties.filter((type) => {
210
+ // The apollo-utilities "addTypeName" utility will add it
211
+ // even if it's already specified :( so we have to filter out
212
+ // the extra one here.
213
+ if (
214
+ type.type === 'ObjectTypeProperty' &&
215
+ type.key.name === '__typename'
216
+ ) {
217
+ const name =
218
+ type.value.type === 'StringLiteralTypeAnnotation'
219
+ ? type.value.value
220
+ : 'INVALID';
221
+ if (seenTypeName) {
222
+ if (name !== seenTypeName) {
223
+ throw new Error(
224
+ `Got two different type names ${name}, ${seenTypeName}`,
225
+ );
226
+ }
227
+ return false;
228
+ }
229
+ seenTypeName = name;
230
+ }
231
+ return true;
232
+ });
233
+ };
234
+
135
235
  const querySelectionToObjectType = (
136
236
  config: Config,
137
237
  selections,
138
238
  type,
139
239
  typeName: string,
140
240
  ): BabelNodeFlowType => {
141
- let seenTypeName = false;
142
- return babelTypes.objectTypeAnnotation(
143
- objectPropertiesToFlow(config, type, typeName, selections).filter(
144
- (type) => {
145
- // The apollo-utilities "addTypeName" utility will add it
146
- // even if it's already specified :( so we have to filter out
147
- // the extra one here.
148
- if (
149
- type.type === 'ObjectTypeProperty' &&
150
- type.key.name === '__typename'
151
- ) {
152
- if (seenTypeName) {
153
- return false;
154
- }
155
- seenTypeName = true;
156
- }
157
- return true;
158
- },
241
+ return sortedObjectTypeAnnotation(
242
+ config,
243
+ ensureOnlyOneTypenameProperty(
244
+ objectPropertiesToFlow(config, type, typeName, selections),
159
245
  ),
160
- undefined /* indexers */,
161
- undefined /* callProperties */,
162
- undefined /* internalSlots */,
163
- true /* exact */,
164
246
  );
165
247
  };
166
248
 
@@ -178,6 +260,9 @@ export const objectPropertiesToFlow = (
178
260
  case 'InlineFragment': {
179
261
  const newTypeName =
180
262
  selection.typeCondition?.name.value ?? typeName;
263
+ if (newTypeName !== typeName) {
264
+ return [];
265
+ }
181
266
  return objectPropertiesToFlow(
182
267
  config,
183
268
  config.schema.typesByName[newTypeName],
@@ -245,7 +330,10 @@ export const objectPropertiesToFlow = (
245
330
  babelTypes.objectTypeProperty(
246
331
  babelTypes.identifier(alias),
247
332
  typeToFlow(
248
- config,
333
+ {
334
+ ...config,
335
+ path: config.path.concat([alias]),
336
+ },
249
337
  typeField.type,
250
338
  selection,
251
339
  ),
@@ -274,54 +362,117 @@ export const unionOrInterfaceToFlow = (
274
362
  }),
275
363
  selections: Selections,
276
364
  ): BabelNodeFlowType => {
277
- const selectedAttributes: Array<
278
- Array<BabelNodeObjectTypeProperty | BabelNodeObjectTypeSpreadProperty>,
279
- > = type.possibleTypes.map((possible) => {
280
- let seenTypeName = false;
281
- return selections
282
- .map((selection) =>
283
- unionOrInterfaceSelection(config, type, possible, selection),
284
- )
285
- .flat()
286
- .filter((type) => {
287
- // The apollo-utilities "addTypeName" utility will add it
288
- // even if it's already specified :( so we have to filter out
289
- // the extra one here.
290
- if (
291
- type.type === 'ObjectTypeProperty' &&
292
- type.key.name === '__typename'
293
- ) {
294
- if (seenTypeName) {
295
- return false;
296
- }
297
- seenTypeName = true;
298
- }
299
- return true;
300
- });
301
- });
302
365
  const allFields = selections.every(
303
366
  (selection) => selection.kind === 'Field',
304
367
  );
305
- if (selectedAttributes.length === 1 || allFields) {
306
- return babelTypes.objectTypeAnnotation(
307
- selectedAttributes[0],
308
- undefined /* indexers */,
309
- undefined /* callProperties */,
310
- undefined /* internalSlots */,
311
- true /* exact */,
368
+ const selectedAttributes: Array<{
369
+ attributes: Array<
370
+ BabelNodeObjectTypeProperty | BabelNodeObjectTypeSpreadProperty,
371
+ >,
372
+ typeName: string,
373
+ }> = type.possibleTypes
374
+ .slice()
375
+ .sort((a, b) => {
376
+ return a.name < b.name ? -1 : 1;
377
+ })
378
+ .map((possible) => {
379
+ const configWithUpdatedPath = {
380
+ ...config,
381
+ path: allFields
382
+ ? config.path
383
+ : config.path.concat([possible.name]),
384
+ };
385
+ return {
386
+ typeName: possible.name,
387
+ attributes: ensureOnlyOneTypenameProperty(
388
+ selections
389
+ .map((selection) =>
390
+ unionOrInterfaceSelection(
391
+ configWithUpdatedPath,
392
+ type,
393
+ possible,
394
+ selection,
395
+ ),
396
+ )
397
+ .flat(),
398
+ ),
399
+ };
400
+ });
401
+ // If they're all fields, the only selection that could be different is __typename
402
+ if (allFields) {
403
+ const sharedAttributes = selectedAttributes[0].attributes.slice();
404
+ const typeNameIndex = selectedAttributes[0].attributes.findIndex(
405
+ (x) =>
406
+ x.type === 'ObjectTypeProperty' &&
407
+ x.key.type === 'Identifier' &&
408
+ x.key.name === '__typename',
312
409
  );
410
+ if (typeNameIndex !== -1) {
411
+ sharedAttributes[typeNameIndex] = babelTypes.objectTypeProperty(
412
+ babelTypes.identifier('__typename'),
413
+ babelTypes.unionTypeAnnotation(
414
+ selectedAttributes.map(
415
+ (attrs) =>
416
+ // eslint-disable-next-line flowtype-errors/uncovered
417
+ ((attrs.attributes[
418
+ typeNameIndex
419
+ ]: any): BabelNodeObjectTypeProperty).value,
420
+ ),
421
+ ),
422
+ );
423
+ }
424
+ return sortedObjectTypeAnnotation(config, sharedAttributes);
313
425
  }
314
- return babelTypes.unionTypeAnnotation(
315
- selectedAttributes.map((properties) =>
316
- babelTypes.objectTypeAnnotation(
317
- properties,
318
- undefined /* indexers */,
319
- undefined /* callProperties */,
320
- undefined /* internalSlots */,
321
- true /* exact */,
426
+ if (selectedAttributes.length === 1) {
427
+ return sortedObjectTypeAnnotation(
428
+ config,
429
+ selectedAttributes[0].attributes,
430
+ );
431
+ }
432
+ /**
433
+ * When generating the objects for the sub-options of a union, the path needs
434
+ * to include the name of the object type.
435
+ * ```
436
+ * query getFriend {
437
+ * friend {
438
+ * ... on Human { id }
439
+ * ... on Droid { arms }
440
+ * }
441
+ * }
442
+ * ```
443
+ * produces
444
+ * ```
445
+ * type getFriend = {friend: getFriend_friend_Human | getFriend_friend_Droid }
446
+ * type getFriend_friend_Human = {id: string}
447
+ * type getFriend_friend_Droid = {arms: number}
448
+ * ```
449
+ * Note that this is different from when an attribute has a plain object type.
450
+ * ```
451
+ * query getHuman {
452
+ * me: human(id: "me") { id }
453
+ * }
454
+ * ```
455
+ * produces
456
+ * ```
457
+ * type getHuman = {me: getHuman_me}
458
+ * type getHuman_me = {id: string}
459
+ * ```
460
+ * instead of e.g. `getHuman_me_Human`.
461
+ */
462
+ const result = babelTypes.unionTypeAnnotation(
463
+ selectedAttributes.map(({typeName, attributes}) =>
464
+ sortedObjectTypeAnnotation(
465
+ {...config, path: config.path.concat([typeName])},
466
+ attributes,
322
467
  ),
323
468
  ),
324
469
  );
470
+ const name = config.path.join('_');
471
+ if (config.allObjectTypes && config.path.length > 1) {
472
+ config.allObjectTypes[name] = result;
473
+ return babelTypes.genericTypeAnnotation(babelTypes.identifier(name));
474
+ }
475
+ return result;
325
476
  };
326
477
  const unionOrInterfaceSelection = (
327
478
  config,
@@ -367,13 +518,20 @@ const unionOrInterfaceSelection = (
367
518
  liftLeadingPropertyComments(
368
519
  babelTypes.objectTypeProperty(
369
520
  babelTypes.identifier(alias),
370
- typeToFlow(config, typeField.type, selection),
521
+ typeToFlow(
522
+ {...config, path: config.path.concat([name])},
523
+ typeField.type,
524
+ selection,
525
+ ),
371
526
  ),
372
527
  ),
373
528
  ];
374
529
  }
375
530
  if (selection.kind === 'FragmentSpread') {
376
531
  const fragment = config.fragments[selection.name.value];
532
+ if (!fragment) {
533
+ throw new Error(`Unknown fragment ${selection.name.value}`);
534
+ }
377
535
  const typeName = fragment.typeCondition.name.value;
378
536
  if (
379
537
  (config.schema.interfacesByName[typeName] &&
@@ -2,6 +2,9 @@
2
2
  // Import this in your jest setup, to mock out graphql-tag!
3
3
  import type {DocumentNode} from 'graphql';
4
4
  import type {Options, Schema, Scalars} from './types';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import {documentToFlowTypes} from '.';
5
8
 
6
9
  export type ExternalOptions = {
7
10
  pragma?: string,
@@ -14,9 +17,12 @@ export type ExternalOptions = {
14
17
  */
15
18
  regenerateCommand?: string,
16
19
  readOnlyArray?: boolean,
20
+ splitTypes?: boolean,
21
+ generatedDirectory?: string,
22
+ exportAllObjectTypes?: boolean,
17
23
  };
18
24
 
19
- const indexPrelude = (regenerateCommand?: string) => `// @flow
25
+ export const indexPrelude = (regenerateCommand?: string): string => `// @flow
20
26
  //
21
27
  // AUTOGENERATED
22
28
  // NOTE: New response types are added to this file automatically.
@@ -26,61 +32,42 @@ const indexPrelude = (regenerateCommand?: string) => `// @flow
26
32
 
27
33
  `;
28
34
 
29
- export const generateTypeFiles = (
35
+ export const generateTypeFileContents = (
30
36
  fileName: string,
31
37
  schema: Schema,
32
38
  document: DocumentNode,
33
39
  options: Options,
34
- ) => {
35
- const {documentToFlowTypes} = require('.');
36
- const path = require('path');
37
- const fs = require('fs');
38
-
39
- const indexFile = (generatedDir) => path.join(generatedDir, 'index.js');
40
-
41
- const maybeCreateGeneratedDir = (generatedDir) => {
42
- if (!fs.existsSync(generatedDir)) {
43
- fs.mkdirSync(generatedDir, {recursive: true});
44
-
45
- // Now write an index.js for each __generated__ dir.
46
- fs.writeFileSync(
47
- indexFile(generatedDir),
48
- indexPrelude(options.regenerateCommand),
49
- );
50
- }
51
- };
40
+ generatedDir: string,
41
+ indexContents: string,
42
+ ): {indexContents: string, files: {[key: string]: string}} => {
43
+ const files = {};
52
44
 
53
45
  /// Write export for __generated__/index.js if it doesn't exist
54
- const writeToIndex = (filePath, typeName) => {
55
- const index = indexFile(path.dirname(filePath));
56
- const indexContents = fs.readFileSync(index, 'utf8');
46
+ const addToIndex = (filePath, typeName) => {
57
47
  const newLine = `export type {${typeName}} from './${path.basename(
58
48
  filePath,
59
49
  )}';`;
60
- if (indexContents.indexOf(path.basename(filePath)) === -1) {
61
- fs.appendFileSync(index, newLine + '\n');
50
+ if (indexContents.indexOf('./' + path.basename(filePath)) === -1) {
51
+ indexContents += newLine + '\n';
62
52
  } else {
63
53
  const lines = indexContents.split('\n').map((line) => {
64
- if (line.includes(path.basename(filePath))) {
54
+ if (line.includes('./' + path.basename(filePath))) {
65
55
  return newLine;
66
56
  }
67
57
  return line;
68
58
  });
69
- fs.writeFileSync(index, lines.join('\n'));
59
+ indexContents = lines.join('\n');
70
60
  }
71
61
  };
72
62
 
73
63
  const generated = documentToFlowTypes(document, schema, options);
74
- generated.forEach(({name, typeName, code}) => {
64
+ generated.forEach(({name, typeName, code, isFragment, extraTypes}) => {
75
65
  // We write all generated files to a `__generated__` subdir to keep
76
66
  // things tidy.
77
- const targetFileName = `${typeName}.js`;
78
- const generatedDir = path.join(path.dirname(fileName), '__generated__');
67
+ const targetFileName = `${name}.js`;
79
68
  const targetPath = path.join(generatedDir, targetFileName);
80
69
 
81
- maybeCreateGeneratedDir(generatedDir);
82
-
83
- const fileContents =
70
+ let fileContents =
84
71
  '// @' +
85
72
  `flow\n// AUTOGENERATED -- DO NOT EDIT\n` +
86
73
  `// Generated for operation '${name}' in file '../${path.basename(
@@ -90,10 +77,60 @@ export const generateTypeFiles = (
90
77
  ? `// To regenerate, run '${options.regenerateCommand}'.\n`
91
78
  : '') +
92
79
  code;
93
- fs.writeFileSync(targetPath, fileContents);
80
+ if (options.splitTypes && !isFragment) {
81
+ fileContents +=
82
+ `\nexport type ${name} = ${typeName}['response'];\n` +
83
+ `export type ${name}Variables = ${typeName}['variables'];\n`;
84
+ }
85
+ Object.keys(extraTypes).forEach((name) => {
86
+ fileContents += `\n\nexport type ${name} = ${extraTypes[name]};`;
87
+ });
94
88
 
95
- writeToIndex(targetPath, typeName);
89
+ addToIndex(targetPath, typeName);
90
+ files[targetPath] =
91
+ fileContents
92
+ // Remove whitespace from the ends of lines; babel's generate sometimes
93
+ // leaves them hanging around.
94
+ .replace(/\s+$/gm, '') + '\n';
96
95
  });
96
+
97
+ return {files, indexContents};
98
+ };
99
+
100
+ export const generateTypeFiles = (
101
+ fileName: string,
102
+ schema: Schema,
103
+ document: DocumentNode,
104
+ options: Options,
105
+ ) => {
106
+ const generatedDir = path.join(
107
+ path.dirname(fileName),
108
+ options.generatedDirectory ?? '__generated__',
109
+ );
110
+ const indexFile = path.join(generatedDir, 'index.js');
111
+
112
+ if (!fs.existsSync(generatedDir)) {
113
+ fs.mkdirSync(generatedDir, {recursive: true});
114
+ }
115
+ if (!fs.existsSync(indexFile)) {
116
+ fs.writeFileSync(indexFile, indexPrelude(options.regenerateCommand));
117
+ }
118
+
119
+ const {indexContents, files} = generateTypeFileContents(
120
+ fileName,
121
+ schema,
122
+ document,
123
+ options,
124
+ generatedDir,
125
+ fs.readFileSync(indexFile, 'utf8'),
126
+ );
127
+
128
+ fs.writeFileSync(indexFile, indexContents);
129
+ Object.keys(files).forEach((key) => {
130
+ fs.writeFileSync(key, files[key]);
131
+ });
132
+
133
+ fs.writeFileSync(indexFile, indexContents);
97
134
  };
98
135
 
99
136
  export const processPragmas = (
@@ -120,6 +157,9 @@ export const processPragmas = (
120
157
  : autogenStrict || !autogen,
121
158
  readOnlyArray: options.readOnlyArray,
122
159
  scalars: options.scalars,
160
+ splitTypes: options.splitTypes,
161
+ generatedDirectory: options.generatedDirectory,
162
+ exportAllObjectTypes: options.exportAllObjectTypes,
123
163
  };
124
164
  } else {
125
165
  return null;
package/src/index.js CHANGED
@@ -9,7 +9,11 @@
9
9
  */
10
10
  import type {DefinitionNode, DocumentNode} from 'graphql';
11
11
 
12
- import {generateResponseType} from './generateResponseType';
12
+ import generate from '@babel/generator'; // eslint-disable-line flowtype-errors/uncovered
13
+ import {
14
+ generateFragmentType,
15
+ generateResponseType,
16
+ } from './generateResponseType';
13
17
  import {generateVariablesType} from './generateVariablesType';
14
18
  export {spyOnGraphqlTagToCollectQueries} from './jest-mock-graphql-tag';
15
19
 
@@ -36,6 +40,8 @@ const optionsToConfig = (
36
40
  fragments,
37
41
  schema,
38
42
  errors,
43
+ allObjectTypes: null,
44
+ path: [],
39
45
  ...internalOptions,
40
46
  };
41
47
 
@@ -58,6 +64,8 @@ export const documentToFlowTypes = (
58
64
  name: string,
59
65
  typeName: string,
60
66
  code: string,
67
+ isFragment?: boolean,
68
+ extraTypes: {[key: string]: string},
61
69
  }> => {
62
70
  const errors: Array<string> = [];
63
71
  const config = optionsToConfig(
@@ -68,21 +76,63 @@ export const documentToFlowTypes = (
68
76
  );
69
77
  const result = document.definitions
70
78
  .map((item) => {
79
+ if (item.kind === 'FragmentDefinition') {
80
+ const name = item.name.value;
81
+ const types = {};
82
+ const code = `export type ${name} = ${generateFragmentType(
83
+ schema,
84
+ item,
85
+ {
86
+ ...config,
87
+ path: [name],
88
+ allObjectTypes: options?.exportAllObjectTypes
89
+ ? types
90
+ : null,
91
+ },
92
+ )};`;
93
+ const extraTypes: {[key: string]: string} = {};
94
+ Object.keys(types).forEach((k) => {
95
+ // eslint-disable-next-line flowtype-errors/uncovered
96
+ extraTypes[k] = generate(types[k]).code;
97
+ });
98
+ return {
99
+ name,
100
+ typeName: name,
101
+ code,
102
+ isFragment: true,
103
+ extraTypes,
104
+ };
105
+ }
71
106
  if (
72
107
  item.kind === 'OperationDefinition' &&
73
108
  (item.operation === 'query' || item.operation === 'mutation') &&
74
109
  item.name
75
110
  ) {
111
+ const types = {};
76
112
  const name = item.name.value;
77
- const response = generateResponseType(schema, item, config);
78
- const variables = generateVariablesType(schema, item, config);
113
+ const response = generateResponseType(schema, item, {
114
+ ...config,
115
+ path: [name],
116
+ allObjectTypes: options?.exportAllObjectTypes
117
+ ? types
118
+ : null,
119
+ });
120
+ const variables = generateVariablesType(schema, item, {
121
+ ...config,
122
+ path: [name],
123
+ });
79
124
 
80
125
  const typeName = `${name}Type`;
81
126
  // TODO(jared): Maybe make this template configurable?
82
127
  // We'll see what's required to get webapp on board.
83
128
  const code = `export type ${typeName} = {|\n variables: ${variables},\n response: ${response}\n|};`;
84
129
 
85
- return {name, typeName, code};
130
+ const extraTypes: {[key: string]: string} = {};
131
+ Object.keys(types).forEach((k) => {
132
+ // eslint-disable-next-line flowtype-errors/uncovered
133
+ extraTypes[k] = generate(types[k]).code;
134
+ });
135
+ return {name, typeName, code, extraTypes};
86
136
  }
87
137
  })
88
138
  .filter(Boolean);
package/src/types.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // @flow
2
2
 
3
+ import type {BabelNode} from '@babel/types';
3
4
  import type {
4
5
  FragmentDefinitionNode,
5
6
  IntrospectionEnumType,
@@ -18,6 +19,9 @@ export type Options = {|
18
19
  strictNullability?: boolean, // default true
19
20
  readOnlyArray?: boolean, // default true
20
21
  scalars?: Scalars,
22
+ splitTypes?: boolean,
23
+ generatedDirectory?: string,
24
+ exportAllObjectTypes?: boolean,
21
25
  |};
22
26
 
23
27
  export type Schema = {
@@ -44,6 +48,7 @@ export type Schema = {
44
48
  };
45
49
 
46
50
  export type Config = {
51
+ path: Array<string>,
47
52
  strictNullability: boolean,
48
53
  readOnlyArray: boolean,
49
54
  fragments: {[key: string]: FragmentDefinitionNode},
@@ -51,5 +56,6 @@ export type Config = {
51
56
  schema: Schema,
52
57
  scalars: Scalars,
53
58
  errors: Array<string>,
59
+ allObjectTypes: null | {[key: string]: BabelNode},
54
60
  };
55
61
  export type Scalars = {[key: string]: 'string' | 'number' | 'boolean'};