@khanacademy/graphql-flow 0.0.2 → 0.2.1

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 (52) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/Readme.md +64 -65
  3. package/dist/cli/config.js +73 -0
  4. package/dist/cli/config.js.flow +104 -0
  5. package/dist/cli/config.js.map +1 -0
  6. package/dist/cli/run.js +159 -0
  7. package/dist/cli/run.js.flow +165 -0
  8. package/dist/cli/run.js.map +1 -0
  9. package/dist/enums.js +2 -1
  10. package/dist/enums.js.map +1 -0
  11. package/dist/generateResponseType.js +170 -60
  12. package/dist/generateResponseType.js.flow +248 -82
  13. package/dist/generateResponseType.js.map +1 -0
  14. package/dist/generateTypeFiles.js +141 -0
  15. package/dist/generateTypeFiles.js.flow +167 -0
  16. package/dist/generateTypeFiles.js.map +1 -0
  17. package/dist/generateVariablesType.js +2 -1
  18. package/dist/generateVariablesType.js.map +1 -0
  19. package/dist/index.js +45 -4
  20. package/dist/index.js.flow +54 -4
  21. package/dist/index.js.map +1 -0
  22. package/dist/jest-mock-graphql-tag.js +22 -107
  23. package/dist/jest-mock-graphql-tag.js.flow +30 -138
  24. package/dist/jest-mock-graphql-tag.js.map +1 -0
  25. package/dist/parser/parse.js +349 -0
  26. package/dist/parser/parse.js.flow +403 -0
  27. package/dist/parser/parse.js.map +1 -0
  28. package/dist/parser/resolve.js +111 -0
  29. package/dist/parser/resolve.js.flow +117 -0
  30. package/dist/parser/resolve.js.map +1 -0
  31. package/dist/schemaFromIntrospectionData.js +2 -1
  32. package/dist/schemaFromIntrospectionData.js.map +1 -0
  33. package/dist/types.js +2 -1
  34. package/dist/types.js.flow +6 -0
  35. package/dist/types.js.map +1 -0
  36. package/dist/utils.js +2 -1
  37. package/dist/utils.js.map +1 -0
  38. package/package.json +9 -5
  39. package/src/__test__/example-schema.graphql +1 -1
  40. package/src/__test__/generateTypeFileContents.test.js +61 -0
  41. package/src/__test__/graphql-flow.test.js +309 -54
  42. package/src/__test__/{jest-mock-graphql-tag.test.js → processPragmas.test.js} +13 -1
  43. package/src/cli/config.js +104 -0
  44. package/src/cli/run.js +165 -0
  45. package/src/generateResponseType.js +248 -82
  46. package/src/generateTypeFiles.js +167 -0
  47. package/src/index.js +54 -4
  48. package/src/jest-mock-graphql-tag.js +30 -138
  49. package/src/parser/__test__/parse.test.js +247 -0
  50. package/src/parser/parse.js +403 -0
  51. package/src/parser/resolve.js +117 -0
  52. package/src/types.js +6 -0
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+ // @flow
3
+ /* eslint-disable no-console */
4
+ import {generateTypeFiles, processPragmas} from '../generateTypeFiles';
5
+ import {processFiles} from '../parser/parse';
6
+ import {resolveDocuments} from '../parser/resolve';
7
+ import {getSchemas, loadConfigFile} from './config';
8
+
9
+ import {addTypenameToDocument} from 'apollo-utilities'; // eslint-disable-line flowtype-errors/uncovered
10
+
11
+ import {execSync} from 'child_process';
12
+ import {readFileSync} from 'fs';
13
+ import {type DocumentNode} from 'graphql';
14
+ import {print} from 'graphql/language/printer';
15
+ import {validate} from 'graphql/validation';
16
+ import path from 'path';
17
+
18
+ /**
19
+ * This CLI tool executes the following steps:
20
+ * 1) process options
21
+ * 2) crawl files to find all operations and fragments, with
22
+ * tagged template literals and expressions.
23
+ * 3) resolve the found operations, passing the literals and
24
+ * fragments into the `graphql-tag` function to produce
25
+ * the DocumentNodes.
26
+ * 4) generate types for all resolved Queries & Mutations
27
+ */
28
+
29
+ /** Step (1) */
30
+
31
+ const findGraphqlTagReferences = (root: string): Array<string> => {
32
+ const response = execSync(
33
+ "git grep -I --word-regexp --name-only --fixed-strings 'graphql-tag' -- '*.js' '*.jsx'",
34
+ {
35
+ encoding: 'utf8',
36
+ cwd: root,
37
+ },
38
+ );
39
+ return response
40
+ .trim()
41
+ .split('\n')
42
+ .map((relative) => path.join(root, relative));
43
+ };
44
+
45
+ const [_, __, configFile, ...cliFiles] = process.argv;
46
+
47
+ if (
48
+ configFile === '-h' ||
49
+ configFile === '--help' ||
50
+ configFile === 'help' ||
51
+ !configFile
52
+ ) {
53
+ console.log(`graphql-flow
54
+
55
+ Usage: graphql-flow [configFile.json] [filesToCrawl...]`);
56
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
57
+ }
58
+
59
+ const config = loadConfigFile(configFile);
60
+
61
+ const [schemaForValidation, schemaForTypeGeneration] = getSchemas(
62
+ config.schemaFilePath,
63
+ );
64
+
65
+ const inputFiles = cliFiles.length
66
+ ? cliFiles
67
+ : findGraphqlTagReferences(process.cwd());
68
+
69
+ /** Step (2) */
70
+
71
+ const files = processFiles(inputFiles, (f) => readFileSync(f, 'utf8'));
72
+
73
+ let filesHadErrors = false;
74
+ Object.keys(files).forEach((key) => {
75
+ const file = files[key];
76
+ if (file.errors.length) {
77
+ filesHadErrors = true;
78
+ console.error(`Errors in ${file.path}`);
79
+ file.errors.forEach((error) => {
80
+ console.error(` - ${error.message}`);
81
+ });
82
+ }
83
+ });
84
+
85
+ if (filesHadErrors) {
86
+ console.error('Aborting');
87
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
88
+ }
89
+
90
+ /** Step (3) */
91
+
92
+ const {resolved, errors} = resolveDocuments(files);
93
+ if (errors.length) {
94
+ errors.forEach((error) => {
95
+ console.error(`Resolution error ${error.message} in ${error.loc.path}`);
96
+ });
97
+ console.error('Aborting');
98
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
99
+ }
100
+
101
+ console.log(Object.keys(resolved).length, 'resolved queries');
102
+
103
+ /** Step (4) */
104
+
105
+ let validationFailures: number = 0;
106
+
107
+ Object.keys(resolved).forEach((k) => {
108
+ const {document, raw} = resolved[k];
109
+ if (config.excludes.some((rx) => rx.test(raw.loc.path))) {
110
+ return; // skip
111
+ }
112
+ const hasNonFragments = document.definitions.some(
113
+ ({kind}) => kind !== 'FragmentDefinition',
114
+ );
115
+ const rawSource: string = raw.literals[0];
116
+ const processedOptions = processPragmas(config.options, rawSource);
117
+ if (!processedOptions) {
118
+ return;
119
+ }
120
+
121
+ // eslint-disable-next-line flowtype-errors/uncovered
122
+ const withTypeNames: DocumentNode = addTypenameToDocument(document);
123
+ const printed = print(withTypeNames);
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 */
140
+ }
141
+
142
+ try {
143
+ generateTypeFiles(
144
+ raw.loc.path,
145
+ schemaForTypeGeneration,
146
+ withTypeNames,
147
+ processedOptions,
148
+ );
149
+ // eslint-disable-next-line flowtype-errors/uncovered
150
+ } catch (err) {
151
+ console.error(`Error while generating operation from ${raw.loc.path}`);
152
+ console.error(printed);
153
+ // eslint-disable-next-line flowtype-errors/uncovered
154
+ console.error(err);
155
+ validationFailures++;
156
+ }
157
+ });
158
+
159
+ if (validationFailures) {
160
+ console.error(
161
+ `Encountered ${validationFailures} validation failures while printing types.`,
162
+ );
163
+ // eslint-disable-next-line flowtype-errors/uncovered
164
+ process.exit(1);
165
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/run.js"],"names":["findGraphqlTagReferences","root","response","encoding","cwd","trim","split","map","relative","path","join","_","__","configFile","cliFiles","process","argv","console","log","exit","config","schemaForValidation","schemaForTypeGeneration","schemaFilePath","inputFiles","length","files","f","filesHadErrors","Object","keys","forEach","key","file","errors","error","message","resolved","loc","validationFailures","k","document","raw","excludes","some","rx","test","hasNonFragments","definitions","kind","rawSource","literals","processedOptions","options","withTypeNames","printed","err"],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n// @flow\n/* eslint-disable no-console */\nimport {generateTypeFiles, processPragmas} from '../generateTypeFiles';\nimport {processFiles} from '../parser/parse';\nimport {resolveDocuments} from '../parser/resolve';\nimport {getSchemas, loadConfigFile} from './config';\n\nimport {addTypenameToDocument} from 'apollo-utilities'; // eslint-disable-line flowtype-errors/uncovered\n\nimport {execSync} from 'child_process';\nimport {readFileSync} from 'fs';\nimport {type DocumentNode} from 'graphql';\nimport {print} from 'graphql/language/printer';\nimport {validate} from 'graphql/validation';\nimport path from 'path';\n\n/**\n * This CLI tool executes the following steps:\n * 1) process options\n * 2) crawl files to find all operations and fragments, with\n * tagged template literals and expressions.\n * 3) resolve the found operations, passing the literals and\n * fragments into the `graphql-tag` function to produce\n * the DocumentNodes.\n * 4) generate types for all resolved Queries & Mutations\n */\n\n/** Step (1) */\n\nconst findGraphqlTagReferences = (root: string): Array<string> => {\n const response = execSync(\n \"git grep -I --word-regexp --name-only --fixed-strings 'graphql-tag' -- '*.js' '*.jsx'\",\n {\n encoding: 'utf8',\n cwd: root,\n },\n );\n return response\n .trim()\n .split('\\n')\n .map((relative) => path.join(root, relative));\n};\n\nconst [_, __, configFile, ...cliFiles] = process.argv;\n\nif (\n configFile === '-h' ||\n configFile === '--help' ||\n configFile === 'help' ||\n !configFile\n) {\n console.log(`graphql-flow\n\nUsage: graphql-flow [configFile.json] [filesToCrawl...]`);\n process.exit(1); // eslint-disable-line flowtype-errors/uncovered\n}\n\nconst config = loadConfigFile(configFile);\n\nconst [schemaForValidation, schemaForTypeGeneration] = getSchemas(\n config.schemaFilePath,\n);\n\nconst inputFiles = cliFiles.length\n ? cliFiles\n : findGraphqlTagReferences(process.cwd());\n\n/** Step (2) */\n\nconst files = processFiles(inputFiles, (f) => readFileSync(f, 'utf8'));\n\nlet filesHadErrors = false;\nObject.keys(files).forEach((key) => {\n const file = files[key];\n if (file.errors.length) {\n filesHadErrors = true;\n console.error(`Errors in ${file.path}`);\n file.errors.forEach((error) => {\n console.error(` - ${error.message}`);\n });\n }\n});\n\nif (filesHadErrors) {\n console.error('Aborting');\n process.exit(1); // eslint-disable-line flowtype-errors/uncovered\n}\n\n/** Step (3) */\n\nconst {resolved, errors} = resolveDocuments(files);\nif (errors.length) {\n errors.forEach((error) => {\n console.error(`Resolution error ${error.message} in ${error.loc.path}`);\n });\n console.error('Aborting');\n process.exit(1); // eslint-disable-line flowtype-errors/uncovered\n}\n\nconsole.log(Object.keys(resolved).length, 'resolved queries');\n\n/** Step (4) */\n\nlet validationFailures: number = 0;\n\nObject.keys(resolved).forEach((k) => {\n const {document, raw} = resolved[k];\n if (config.excludes.some((rx) => rx.test(raw.loc.path))) {\n return; // skip\n }\n const hasNonFragments = document.definitions.some(\n ({kind}) => kind !== 'FragmentDefinition',\n );\n const rawSource: string = raw.literals[0];\n const processedOptions = processPragmas(config.options, rawSource);\n if (!processedOptions) {\n return;\n }\n\n // eslint-disable-next-line flowtype-errors/uncovered\n const withTypeNames: DocumentNode = addTypenameToDocument(document);\n const printed = print(withTypeNames);\n\n if (hasNonFragments) {\n /* eslint-disable flowtype-errors/uncovered */\n const errors = validate(schemaForValidation, withTypeNames);\n /* eslint-disable flowtype-errors/uncovered */\n if (errors.length) {\n errors.forEach((error) => {\n console.error(\n `Schema validation found errors for ${raw.loc.path}!`,\n );\n console.error(printed);\n console.error(error);\n validationFailures++;\n });\n }\n /* eslint-enable flowtype-errors/uncovered */\n }\n\n try {\n generateTypeFiles(\n raw.loc.path,\n schemaForTypeGeneration,\n withTypeNames,\n processedOptions,\n );\n // eslint-disable-next-line flowtype-errors/uncovered\n } catch (err) {\n console.error(`Error while generating operation from ${raw.loc.path}`);\n console.error(printed);\n // eslint-disable-next-line flowtype-errors/uncovered\n console.error(err);\n validationFailures++;\n }\n});\n\nif (validationFailures) {\n console.error(\n `Encountered ${validationFailures} validation failures while printing types.`,\n );\n // eslint-disable-next-line flowtype-errors/uncovered\n process.exit(1);\n}\n"],"file":"run.js"}
package/dist/enums.js CHANGED
@@ -54,4 +54,5 @@ const scalarTypeToFlow = (config, name) => {
54
54
  return babelTypes.genericTypeAnnotation(babelTypes.identifier(`UNKNOWN_SCALAR["${name}"]`));
55
55
  };
56
56
 
57
- exports.scalarTypeToFlow = scalarTypeToFlow;
57
+ exports.scalarTypeToFlow = scalarTypeToFlow;
58
+ //# sourceMappingURL=enums.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/enums.js"],"names":["enumTypeToFlow","config","name","enumConfig","schema","enumsByName","combinedDescription","enumValues","map","n","description","replace","join","babelTypes","unionTypeAnnotation","stringLiteralTypeAnnotation","builtinScalars","Boolean","String","DateTime","Date","ID","Int","Float","scalarTypeToFlow","genericTypeAnnotation","identifier","underlyingType","scalars","errors","push"],"mappings":";;;;;;;AAIA;;AAGA;;;;;;AANA;AACA;AACA;AAMO,MAAMA,cAAc,GAAG,CAC1BC,MAD0B,EAE1BC,IAF0B,KAGN;AACpB,QAAMC,UAAU,GAAGF,MAAM,CAACG,MAAP,CAAcC,WAAd,CAA0BH,IAA1B,CAAnB;AACA,MAAII,mBAAmB,GAAGH,UAAU,CAACI,UAAX,CACrBC,GADqB,CAEjBC,CAAD,IACK,KAAIA,CAAC,CAACP,IAAK,EAAZ,IACCO,CAAC,CAACC,WAAF,GACK,eAAeD,CAAC,CAACC,WAAF,CAAcC,OAAd,CAAsB,KAAtB,EAA6B,UAA7B,CADpB,GAEK,EAHN,CAHc,EAQrBC,IARqB,CAQhB,IARgB,CAA1B;;AASA,MAAIT,UAAU,CAACO,WAAf,EAA4B;AACxBJ,IAAAA,mBAAmB,GACfH,UAAU,CAACO,WAAX,GAAyB,MAAzB,GAAkCJ,mBADtC;AAEH;;AACD,SAAO,uCACHA,mBADG,EAEHO,UAAU,CAACC,mBAAX,CACIX,UAAU,CAACI,UAAX,CAAsBC,GAAtB,CAA2BC,CAAD,IACtBI,UAAU,CAACE,2BAAX,CAAuCN,CAAC,CAACP,IAAzC,CADJ,CADJ,CAFG,CAAP;AAQH,CA1BM;;;AA4BA,MAAMc,cAAuC,GAAG;AACnDC,EAAAA,OAAO,EAAE,SAD0C;AAEnDC,EAAAA,MAAM,EAAE,QAF2C;AAGnDC,EAAAA,QAAQ,EAAE,QAHyC;AAInDC,EAAAA,IAAI,EAAE,QAJ6C;AAKnDC,EAAAA,EAAE,EAAE,QAL+C;AAMnDC,EAAAA,GAAG,EAAE,QAN8C;AAOnDC,EAAAA,KAAK,EAAE;AAP4C,CAAhD;;;AAUA,MAAMC,gBAAgB,GAAG,CAC5BvB,MAD4B,EAE5BC,IAF4B,KAGR;AACpB,MAAIc,cAAc,CAACd,IAAD,CAAlB,EAA0B;AACtB,WAAOW,UAAU,CAACY,qBAAX,CACHZ,UAAU,CAACa,UAAX,CAAsBV,cAAc,CAACd,IAAD,CAApC,CADG,CAAP;AAGH;;AACD,QAAMyB,cAAc,GAAG1B,MAAM,CAAC2B,OAAP,CAAe1B,IAAf,CAAvB;;AACA,MAAIyB,cAAc,IAAI,IAAtB,EAA4B;AACxB,WAAOd,UAAU,CAACY,qBAAX,CACHZ,UAAU,CAACa,UAAX,CAAsBC,cAAtB,CADG,CAAP;AAGH;;AACD1B,EAAAA,MAAM,CAAC4B,MAAP,CAAcC,IAAd,CACK,sBAAqB5B,IAAK,sFAD/B;AAGA,SAAOW,UAAU,CAACY,qBAAX,CACHZ,UAAU,CAACa,UAAX,CAAuB,mBAAkBxB,IAAK,IAA9C,CADG,CAAP;AAGH,CArBM","sourcesContent":["// @flow\n/**\n * Both input & output types can have enums & scalars.\n */\nimport * as babelTypes from '@babel/types';\nimport {type BabelNodeFlowType} from '@babel/types';\nimport type {Config} from './types';\nimport {maybeAddDescriptionComment} from './utils';\n\nexport const enumTypeToFlow = (\n config: Config,\n name: string,\n): BabelNodeFlowType => {\n const enumConfig = config.schema.enumsByName[name];\n let combinedDescription = enumConfig.enumValues\n .map(\n (n) =>\n `- ${n.name}` +\n (n.description\n ? '\\n\\n ' + n.description.replace(/\\n/g, '\\n ')\n : ''),\n )\n .join('\\n');\n if (enumConfig.description) {\n combinedDescription =\n enumConfig.description + '\\n\\n' + combinedDescription;\n }\n return maybeAddDescriptionComment(\n combinedDescription,\n babelTypes.unionTypeAnnotation(\n enumConfig.enumValues.map((n) =>\n babelTypes.stringLiteralTypeAnnotation(n.name),\n ),\n ),\n );\n};\n\nexport const builtinScalars: {[key: string]: string} = {\n Boolean: 'boolean',\n String: 'string',\n DateTime: 'string',\n Date: 'string',\n ID: 'string',\n Int: 'number',\n Float: 'number',\n};\n\nexport const scalarTypeToFlow = (\n config: Config,\n name: string,\n): BabelNodeFlowType => {\n if (builtinScalars[name]) {\n return babelTypes.genericTypeAnnotation(\n babelTypes.identifier(builtinScalars[name]),\n );\n }\n const underlyingType = config.scalars[name];\n if (underlyingType != null) {\n return babelTypes.genericTypeAnnotation(\n babelTypes.identifier(underlyingType),\n );\n }\n config.errors.push(\n `Unexpected scalar '${name}'! Please add it to the \"scalars\" argument at the callsite of 'generateFlowTypes()'.`,\n );\n return babelTypes.genericTypeAnnotation(\n babelTypes.identifier(`UNKNOWN_SCALAR[\"${name}\"]`),\n );\n};\n"],"file":"enums.js"}
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.unionOrInterfaceToFlow = exports.typeToFlow = exports.objectPropertiesToFlow = exports.generateResponseType = void 0;
6
+ exports.unionOrInterfaceToFlow = exports.typeToFlow = exports.objectPropertiesToFlow = exports.generateResponseType = exports.generateFragmentType = void 0;
7
7
 
8
8
  var _generator = _interopRequireDefault(require("@babel/generator"));
9
9
 
@@ -29,6 +29,55 @@ const generateResponseType = (schema, query, config) => {
29
29
 
30
30
  exports.generateResponseType = generateResponseType;
31
31
 
32
+ const sortedObjectTypeAnnotation = (config, properties) => {
33
+ const obj = babelTypes.objectTypeAnnotation(properties.sort((a, b) => {
34
+ if (a.type === 'ObjectTypeProperty' && b.type === 'ObjectTypeProperty') {
35
+ const aName = a.key.type === 'Identifier' ? a.key.name : '';
36
+ const bName = b.key.type === 'Identifier' ? b.key.name : '';
37
+ return aName < bName ? -1 : 1;
38
+ }
39
+
40
+ return 0;
41
+ }), undefined
42
+ /* indexers */
43
+ , undefined
44
+ /* callProperties */
45
+ , undefined
46
+ /* internalSlots */
47
+ , true
48
+ /* exact */
49
+ );
50
+ const name = config.path.join('_');
51
+ const isTopLevelType = config.path.length <= 1;
52
+
53
+ if (config.allObjectTypes != null && !isTopLevelType) {
54
+ config.allObjectTypes[name] = obj;
55
+ return babelTypes.genericTypeAnnotation(babelTypes.identifier(name));
56
+ } else {
57
+ return obj;
58
+ }
59
+ };
60
+
61
+ const generateFragmentType = (schema, fragment, config) => {
62
+ const onType = fragment.typeCondition.name.value;
63
+ let ast;
64
+
65
+ if (schema.typesByName[onType]) {
66
+ ast = sortedObjectTypeAnnotation(config, objectPropertiesToFlow(config, schema.typesByName[onType], onType, fragment.selectionSet.selections));
67
+ } else if (schema.interfacesByName[onType]) {
68
+ ast = unionOrInterfaceToFlow(config, config.schema.interfacesByName[onType], fragment.selectionSet.selections);
69
+ } else if (schema.unionsByName[onType]) {
70
+ ast = unionOrInterfaceToFlow(config, config.schema.unionsByName[onType], fragment.selectionSet.selections);
71
+ } else {
72
+ throw new Error(`Unknown ${onType}`);
73
+ } // eslint-disable-next-line flowtype-errors/uncovered
74
+
75
+
76
+ return (0, _generator.default)(ast).code;
77
+ };
78
+
79
+ exports.generateFragmentType = generateFragmentType;
80
+
32
81
  const _typeToFlow = (config, type, selection) => {
33
82
  if (type.kind === 'SCALAR') {
34
83
  return (0, _enums.scalarTypeToFlow)(config, type.name);
@@ -103,35 +152,50 @@ const typeToFlow = (config, type, selection) => {
103
152
 
104
153
  exports.typeToFlow = typeToFlow;
105
154
 
106
- const querySelectionToObjectType = (config, selections, type, typeName) => {
155
+ const ensureOnlyOneTypenameProperty = properties => {
107
156
  let seenTypeName = false;
108
- return babelTypes.objectTypeAnnotation(objectPropertiesToFlow(config, type, typeName, selections).filter(type => {
157
+ return properties.filter(type => {
109
158
  // The apollo-utilities "addTypeName" utility will add it
110
159
  // even if it's already specified :( so we have to filter out
111
160
  // the extra one here.
112
161
  if (type.type === 'ObjectTypeProperty' && type.key.name === '__typename') {
162
+ const name = type.value.type === 'StringLiteralTypeAnnotation' ? type.value.value : 'INVALID';
163
+
113
164
  if (seenTypeName) {
165
+ if (name !== seenTypeName) {
166
+ throw new Error(`Got two different type names ${name}, ${seenTypeName}`);
167
+ }
168
+
114
169
  return false;
115
170
  }
116
171
 
117
- seenTypeName = true;
172
+ seenTypeName = name;
118
173
  }
119
174
 
120
175
  return true;
121
- }), undefined
122
- /* indexers */
123
- , undefined
124
- /* callProperties */
125
- , undefined
126
- /* internalSlots */
127
- , true
128
- /* exact */
129
- );
176
+ });
177
+ };
178
+
179
+ const querySelectionToObjectType = (config, selections, type, typeName) => {
180
+ return sortedObjectTypeAnnotation(config, ensureOnlyOneTypenameProperty(objectPropertiesToFlow(config, type, typeName, selections)));
130
181
  };
131
182
 
132
183
  const objectPropertiesToFlow = (config, type, typeName, selections) => {
133
184
  return [].concat(...selections.map(selection => {
134
185
  switch (selection.kind) {
186
+ case 'InlineFragment':
187
+ {
188
+ var _selection$typeCondit;
189
+
190
+ const newTypeName = ((_selection$typeCondit = selection.typeCondition) === null || _selection$typeCondit === void 0 ? void 0 : _selection$typeCondit.name.value) ?? typeName;
191
+
192
+ if (newTypeName !== typeName) {
193
+ return [];
194
+ }
195
+
196
+ return objectPropertiesToFlow(config, config.schema.typesByName[newTypeName], newTypeName, selection.selectionSet.selections);
197
+ }
198
+
135
199
  case 'FragmentSpread':
136
200
  if (!config.fragments[selection.name.value]) {
137
201
  config.errors.push(`No fragment named '${selection.name.value}'. Did you forget to include it in the template literal?`);
@@ -154,10 +218,13 @@ const objectPropertiesToFlow = (config, type, typeName, selections) => {
154
218
  }
155
219
 
156
220
  const typeField = type.fieldsByName[name];
157
- return [(0, _utils.maybeAddDescriptionComment)(typeField.description, (0, _utils.liftLeadingPropertyComments)(babelTypes.objectTypeProperty(babelTypes.identifier(alias), typeToFlow(config, typeField.type, selection))))];
221
+ return [(0, _utils.maybeAddDescriptionComment)(typeField.description, (0, _utils.liftLeadingPropertyComments)(babelTypes.objectTypeProperty(babelTypes.identifier(alias), typeToFlow({ ...config,
222
+ path: config.path.concat([alias])
223
+ }, typeField.type, selection))))];
158
224
 
159
225
  default:
160
- config.errors.push(`Unsupported selection kind '${selection.kind}'`);
226
+ config.errors.push( // eslint-disable-next-line flowtype-errors/uncovered
227
+ `Unsupported selection kind '${selection.kind}'`);
161
228
  return [];
162
229
  }
163
230
  }));
@@ -166,46 +233,80 @@ const objectPropertiesToFlow = (config, type, typeName, selections) => {
166
233
  exports.objectPropertiesToFlow = objectPropertiesToFlow;
167
234
 
168
235
  const unionOrInterfaceToFlow = (config, type, selections) => {
169
- const selectedAttributes = type.possibleTypes.map(possible => {
170
- let seenTypeName = false;
171
- return selections.map(selection => unionOrInterfaceSelection(config, type, possible, selection)).flat().filter(type => {
172
- // The apollo-utilities "addTypeName" utility will add it
173
- // even if it's already specified :( so we have to filter out
174
- // the extra one here.
175
- if (type.type === 'ObjectTypeProperty' && type.key.name === '__typename') {
176
- if (seenTypeName) {
177
- return false;
178
- }
179
-
180
- seenTypeName = true;
181
- }
182
-
183
- return true;
184
- });
185
- });
186
236
  const allFields = selections.every(selection => selection.kind === 'Field');
237
+ const selectedAttributes = type.possibleTypes.slice().sort((a, b) => {
238
+ return a.name < b.name ? -1 : 1;
239
+ }).map(possible => {
240
+ const configWithUpdatedPath = { ...config,
241
+ path: allFields ? config.path : config.path.concat([possible.name])
242
+ };
243
+ return {
244
+ typeName: possible.name,
245
+ attributes: ensureOnlyOneTypenameProperty(selections.map(selection => unionOrInterfaceSelection(configWithUpdatedPath, type, possible, selection)).flat())
246
+ };
247
+ }); // If they're all fields, the only selection that could be different is __typename
248
+
249
+ if (allFields) {
250
+ const sharedAttributes = selectedAttributes[0].attributes.slice();
251
+ const typeNameIndex = selectedAttributes[0].attributes.findIndex(x => x.type === 'ObjectTypeProperty' && x.key.type === 'Identifier' && x.key.name === '__typename');
252
+
253
+ if (typeNameIndex !== -1) {
254
+ sharedAttributes[typeNameIndex] = babelTypes.objectTypeProperty(babelTypes.identifier('__typename'), babelTypes.unionTypeAnnotation(selectedAttributes.map(attrs => // eslint-disable-next-line flowtype-errors/uncovered
255
+ attrs.attributes[typeNameIndex].value)));
256
+ }
187
257
 
188
- if (selectedAttributes.length === 1 || allFields) {
189
- return babelTypes.objectTypeAnnotation(selectedAttributes[0], undefined
190
- /* indexers */
191
- , undefined
192
- /* callProperties */
193
- , undefined
194
- /* internalSlots */
195
- , true
196
- /* exact */
197
- );
258
+ return sortedObjectTypeAnnotation(config, sharedAttributes);
198
259
  }
199
260
 
200
- return babelTypes.unionTypeAnnotation(selectedAttributes.map(properties => babelTypes.objectTypeAnnotation(properties, undefined
201
- /* indexers */
202
- , undefined
203
- /* callProperties */
204
- , undefined
205
- /* internalSlots */
206
- , true
207
- /* exact */
208
- )));
261
+ if (selectedAttributes.length === 1) {
262
+ return sortedObjectTypeAnnotation(config, selectedAttributes[0].attributes);
263
+ }
264
+ /**
265
+ * When generating the objects for the sub-options of a union, the path needs
266
+ * to include the name of the object type.
267
+ * ```
268
+ * query getFriend {
269
+ * friend {
270
+ * ... on Human { id }
271
+ * ... on Droid { arms }
272
+ * }
273
+ * }
274
+ * ```
275
+ * produces
276
+ * ```
277
+ * type getFriend = {friend: getFriend_friend_Human | getFriend_friend_Droid }
278
+ * type getFriend_friend_Human = {id: string}
279
+ * type getFriend_friend_Droid = {arms: number}
280
+ * ```
281
+ * Note that this is different from when an attribute has a plain object type.
282
+ * ```
283
+ * query getHuman {
284
+ * me: human(id: "me") { id }
285
+ * }
286
+ * ```
287
+ * produces
288
+ * ```
289
+ * type getHuman = {me: getHuman_me}
290
+ * type getHuman_me = {id: string}
291
+ * ```
292
+ * instead of e.g. `getHuman_me_Human`.
293
+ */
294
+
295
+
296
+ const result = babelTypes.unionTypeAnnotation(selectedAttributes.map(({
297
+ typeName,
298
+ attributes
299
+ }) => sortedObjectTypeAnnotation({ ...config,
300
+ path: config.path.concat([typeName])
301
+ }, attributes)));
302
+ const name = config.path.join('_');
303
+
304
+ if (config.allObjectTypes && config.path.length > 1) {
305
+ config.allObjectTypes[name] = result;
306
+ return babelTypes.genericTypeAnnotation(babelTypes.identifier(name));
307
+ }
308
+
309
+ return result;
209
310
  };
210
311
 
211
312
  exports.unionOrInterfaceToFlow = unionOrInterfaceToFlow;
@@ -227,11 +328,18 @@ const unionOrInterfaceSelection = (config, type, possible, selection) => {
227
328
  }
228
329
 
229
330
  const typeField = type.fieldsByName[name];
230
- return [(0, _utils.liftLeadingPropertyComments)(babelTypes.objectTypeProperty(babelTypes.identifier(alias), typeToFlow(config, typeField.type, selection)))];
331
+ return [(0, _utils.liftLeadingPropertyComments)(babelTypes.objectTypeProperty(babelTypes.identifier(alias), typeToFlow({ ...config,
332
+ path: config.path.concat([name])
333
+ }, typeField.type, selection)))];
231
334
  }
232
335
 
233
336
  if (selection.kind === 'FragmentSpread') {
234
337
  const fragment = config.fragments[selection.name.value];
338
+
339
+ if (!fragment) {
340
+ throw new Error(`Unknown fragment ${selection.name.value}`);
341
+ }
342
+
235
343
  const typeName = fragment.typeCondition.name.value;
236
344
 
237
345
  if (config.schema.interfacesByName[typeName] && config.schema.interfacesByName[typeName].possibleTypesByName[possible.name] || typeName === possible.name) {
@@ -253,15 +361,17 @@ Try using an inline fragment "... on SomeType {}".`);
253
361
  return [];
254
362
  }
255
363
 
256
- if (!selection.typeCondition) {
257
- throw new Error('Expected selection to have a typeCondition');
258
- }
364
+ if (selection.typeCondition) {
365
+ var _config$schema$interf;
259
366
 
260
- const typeName = selection.typeCondition.name.value;
367
+ const typeName = selection.typeCondition.name.value;
368
+ const indirectMatch = (_config$schema$interf = config.schema.interfacesByName[typeName]) === null || _config$schema$interf === void 0 ? void 0 : _config$schema$interf.possibleTypesByName[possible.name];
261
369
 
262
- if (config.schema.interfacesByName[typeName] && config.schema.interfacesByName[typeName].possibleTypesByName[possible.name] || typeName === possible.name) {
263
- return objectPropertiesToFlow(config, config.schema.typesByName[possible.name], possible.name, selection.selectionSet.selections);
370
+ if (typeName !== possible.name && !indirectMatch) {
371
+ return [];
372
+ }
264
373
  }
265
374
 
266
- return [];
267
- };
375
+ return objectPropertiesToFlow(config, config.schema.typesByName[possible.name], possible.name, selection.selectionSet.selections);
376
+ };
377
+ //# sourceMappingURL=generateResponseType.js.map