@khanacademy/graphql-flow 0.0.2 → 0.1.0

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 (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/Readme.md +49 -65
  3. package/dist/cli/config.js +73 -0
  4. package/dist/cli/config.js.flow +101 -0
  5. package/dist/cli/config.js.map +1 -0
  6. package/dist/cli/run.js +155 -0
  7. package/dist/cli/run.js.flow +161 -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 +20 -9
  12. package/dist/generateResponseType.js.flow +25 -17
  13. package/dist/generateResponseType.js.map +1 -0
  14. package/dist/generateTypeFiles.js +102 -0
  15. package/dist/generateTypeFiles.js.flow +127 -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 +2 -1
  20. package/dist/index.js.map +1 -0
  21. package/dist/jest-mock-graphql-tag.js +22 -107
  22. package/dist/jest-mock-graphql-tag.js.flow +30 -138
  23. package/dist/jest-mock-graphql-tag.js.map +1 -0
  24. package/dist/parser/parse.js +349 -0
  25. package/dist/parser/parse.js.flow +403 -0
  26. package/dist/parser/parse.js.map +1 -0
  27. package/dist/parser/resolve.js +111 -0
  28. package/dist/parser/resolve.js.flow +117 -0
  29. package/dist/parser/resolve.js.map +1 -0
  30. package/dist/schemaFromIntrospectionData.js +2 -1
  31. package/dist/schemaFromIntrospectionData.js.map +1 -0
  32. package/dist/types.js +2 -1
  33. package/dist/types.js.map +1 -0
  34. package/dist/utils.js +2 -1
  35. package/dist/utils.js.map +1 -0
  36. package/package.json +9 -5
  37. package/src/__test__/graphql-flow.test.js +68 -24
  38. package/src/__test__/{jest-mock-graphql-tag.test.js → processPragmas.test.js} +13 -1
  39. package/src/cli/config.js +101 -0
  40. package/src/cli/run.js +161 -0
  41. package/src/generateResponseType.js +25 -17
  42. package/src/generateTypeFiles.js +127 -0
  43. package/src/jest-mock-graphql-tag.js +30 -138
  44. package/src/parser/__test__/parse.test.js +247 -0
  45. package/src/parser/parse.js +403 -0
  46. package/src/parser/resolve.js +117 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @khanacademy/graphql-flow
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b08ed1b: Build out a cli tool that does our own parsing of javascript files, dramatically speeding things up!
8
+
9
+ ### Patch Changes
10
+
11
+ - 8cdcdc2: Support inline fragments on objects
12
+ - fd5c6b7: Allow schema to be provided as a .graphql file, not just .json
13
+ - 6869203: Add 'ignorePragma' option, to allow skipping of documents
14
+
3
15
  ## 0.0.2
4
16
 
5
17
  ### Patch Changes
package/Readme.md CHANGED
@@ -2,64 +2,69 @@
2
2
 
3
3
  This is a tool for generating flow types from graphql queries in javascript frontends.
4
4
 
5
- The core of this tool is the `documentToFlowTypes` function, which takes a `DocumentNode` (such as is returned by the `graphql-tag` package) as well as your backend's graphql schema (see below for instructions on how to produce this), and produces a list of stringified flow types, one for each query and mutation defined in that graphql block.
5
+ ## Using as a CLI tool
6
6
 
7
- It looks something like this:
7
+ Write a config file, with the following options:
8
8
 
9
- ```js
10
- import gql from 'graphql-tag';
11
- import {documentToFlowTypes, schemaFromIntrospectionData} from 'graphql-flow';
12
- import myIntrospectionData from './server-introspection-response.json';
13
-
14
- const schema = schemaFromIntrospectionData(myIntrospectionData);
15
-
16
- const MyQuery = gql`
17
- query SomeQuery {
18
- human(id: "Han Solo") {
19
- id
20
- name
21
- homePlanet
22
- friends {
23
- name
24
- }
9
+ ```json
10
+ {
11
+ // Response to the "introspection query" (see below), or a .graphql schema file.
12
+ // The file extension indicates the format, .json or .graphql (default .json).
13
+ // This path is resolved relative to the config file location.
14
+ "schemaFilePath": "../some/schema-file.json",
15
+ // List of regexes
16
+ "excludes": ["\\bsome-thing", "_test.jsx?$"],
17
+ // Options for type generation (see below)
18
+ "options": {
19
+ "scalars": {
20
+ "JSONString": "string"
25
21
  }
26
22
  }
27
- `;
28
-
29
- console.log(documentToFlowTypes(MyQuery, schema))
30
- /*
31
- export type SomeQueryResponseType = {|
32
- human: ?{|
33
- id: string,
34
- name: ?string,
35
- homePlanet: ?string,
36
- friends: ?$ReadOnlyArray<?{|
37
- name: ?string
38
- |}>,
39
- |}
40
- |};
41
- */
23
+ }
24
+ ```
25
+
26
+ Then run from the CLI, like so:
27
+
28
+ ```bash
29
+ $ graphql-flow path/to/config.json
42
30
  ```
43
31
 
44
- If you already have a setup whereby you collect all of your graphql literals, that may be all you need!
32
+ Files will be discovered relative to the current working directory.
45
33
 
46
- Otherwise, we provide a way to hook into jest to automatically collect your queries and generate the types.
34
+ To specify what file should be checked, pass them in as subsequent cli arguments.
47
35
 
48
- ## Options for `documentToFlowTypes`
36
+ ## Options (for the cli 'options' config item, or when running from jest):
49
37
 
50
- ```js
51
- {
52
- // Use nullable types where the graphql type is nullable. Included for legacy compatability,
53
- // will probably remove once the mobile repo no longer needs it.
38
+ ```ts
39
+ type Options = {
40
+ // These are from the `documentToFlowTypes` options object above
54
41
  strictNullability: boolean = true,
55
- // Output `$ReadOnlyArray<>` instead of `Array<>`, for stricter flow typing. On by default.
56
42
  readOnlyArray: boolean = true,
57
- // A mapping of custom scalar names to the underlying json representation.
58
43
  scalars: {[key: string]: 'string' | 'boolean' | 'number'}
44
+
45
+ // Specify an opt-in pragma that must be present in a graphql string source
46
+ // in order for it to be picked up and processed
47
+ // e.g. set this to `"# @autogen\n"` to only generate types for queries that
48
+ // have the comment `# @autogen` in them.
49
+ pragma?: string,
50
+ // Specify a pragma that will turn off `strictNullability` for that
51
+ // source file. e.g. `"# @autogen-loose\n"`.
52
+ loosePragma?: string,
53
+ // If neither pragma nor loosePragma are specified, all graphql documents
54
+ // that contain a query or mutation will be processed.
55
+
56
+ // Any graphql operations containing ignorePragma will be skipped
57
+ ignorePragma?: string,
59
58
  }
60
59
  ```
61
60
 
62
- ## Using jest to do the heavy lifting:
61
+ ## Using from jest
62
+
63
+ You can also use jest to do the heavy lifting, running all of your code and collecting queries
64
+ by mocking out the `graphql-tag` function itself. This requires that all graphql operations are
65
+ defined at the top level (no queries defined in functions or components, for example), but does
66
+ support complicated things like returning a fragment from a function (which is probably
67
+ not a great idea code-style-wise anyway).
63
68
 
64
69
  ### jest-setup.js
65
70
 
@@ -74,6 +79,7 @@ if (process.env.GRAPHQL_FLOW) {
74
79
 
75
80
  return jest.requireActual('../tools/graphql-flow/jest-mock-graphql-tag.js')(
76
81
  introspectionData,
82
+ // See "Options" type above
77
83
  {
78
84
  pragma: '# @autogen\n',
79
85
  loosePragma: '# @autogen-loose\n',
@@ -126,28 +132,6 @@ And then `yarn generate-types` or `npm run generate-types` gets your types gener
126
132
 
127
133
  🚀
128
134
 
129
- ### Options for the `jest-mock-graphql-tag.js` helper:
130
-
131
- ```js
132
- {
133
- // These are from the `documentToFlowTypes` options object above
134
- strictNullability: boolean = true,
135
- readOnlyArray: boolean = true,
136
- scalars: {[key: string]: 'string' | 'boolean' | 'number'}
137
-
138
- // Specify an opt-in pragma that must be present in a graphql string source
139
- // in order for it to be picked up and processed
140
- // e.g. set this to `"# @autogen\n"` to only generate types for queries that
141
- // have the comment `# @autogen` in them.
142
- pragma?: string,
143
- // Specify a pragma that will turn off `strictNullability` for that
144
- // source file. e.g. `"# @autogen-loose\n"`.
145
- loosePragma?: string,
146
- // If neither pragma nor loosePragma are specified, all graphql documents
147
- // that contain a query or mutation will be processed.
148
- }
149
- ```
150
-
151
135
  ## Introspecting your backend's graphql schema
152
136
  Here's how to get your backend's schema in the way that this tool expects, using the builtin 'graphql introspection query':
153
137
 
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadConfigFile = exports.getSchemas = void 0;
7
+
8
+ var _schemaFromIntrospectionData = require("../schemaFromIntrospectionData");
9
+
10
+ var _fs = _interopRequireDefault(require("fs"));
11
+
12
+ var _graphql = require("graphql");
13
+
14
+ var _path = _interopRequireDefault(require("path"));
15
+
16
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
+
18
+ const loadConfigFile = configFile => {
19
+ var _data$excludes;
20
+
21
+ // eslint-disable-next-line flowtype-errors/uncovered
22
+ const data = JSON.parse(_fs.default.readFileSync(configFile, 'utf8'));
23
+ const toplevelKeys = ['excludes', 'schemaFilePath', 'options'];
24
+ Object.keys(data).forEach(k => {
25
+ if (!toplevelKeys.includes(k)) {
26
+ throw new Error(`Invalid attribute in config file ${configFile}: ${k}. Allowed attributes: ${toplevelKeys.join(', ')}`);
27
+ }
28
+ });
29
+
30
+ if (data.options) {
31
+ const externalOptionsKeys = ['pragma', 'loosePragma', 'ignorePragma', 'scalars', 'strictNullability', 'regenerateCommand', 'readOnlyArray'];
32
+ Object.keys(data.options).forEach(k => {
33
+ if (!externalOptionsKeys.includes(k)) {
34
+ throw new Error(`Invalid option in config file ${configFile}: ${k}. Allowed options: ${externalOptionsKeys.join(', ')}`);
35
+ }
36
+ });
37
+ }
38
+
39
+ return {
40
+ options: data.options ?? {},
41
+ excludes: ((_data$excludes = data.excludes) === null || _data$excludes === void 0 ? void 0 : _data$excludes.map(string => new RegExp(string))) ?? [],
42
+ schemaFilePath: _path.default.join(_path.default.dirname(configFile), data.schemaFilePath)
43
+ };
44
+ };
45
+ /**
46
+ * Loads a .json 'introspection query response', or a .graphql schema definition.
47
+ */
48
+
49
+
50
+ exports.loadConfigFile = loadConfigFile;
51
+
52
+ const getSchemas = schemaFilePath => {
53
+ const raw = _fs.default.readFileSync(schemaFilePath, 'utf8');
54
+
55
+ if (schemaFilePath.endsWith('.graphql')) {
56
+ const schemaForValidation = (0, _graphql.buildSchema)(raw);
57
+ const queryResponse = (0, _graphql.graphqlSync)(schemaForValidation, (0, _graphql.getIntrospectionQuery)({
58
+ descriptions: true
59
+ }));
60
+ const schemaForTypeGeneration = (0, _schemaFromIntrospectionData.schemaFromIntrospectionData)( // eslint-disable-next-line flowtype-errors/uncovered
61
+ queryResponse.data);
62
+ return [schemaForValidation, schemaForTypeGeneration];
63
+ } else {
64
+ // eslint-disable-next-line flowtype-errors/uncovered
65
+ const introspectionData = JSON.parse(raw);
66
+ const schemaForValidation = (0, _graphql.buildClientSchema)(introspectionData);
67
+ const schemaForTypeGeneration = (0, _schemaFromIntrospectionData.schemaFromIntrospectionData)(introspectionData);
68
+ return [schemaForValidation, schemaForTypeGeneration];
69
+ }
70
+ };
71
+
72
+ exports.getSchemas = getSchemas;
73
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,101 @@
1
+ // @flow
2
+ import type {ExternalOptions} from '../generateTypeFiles';
3
+ import type {Schema} from '../types';
4
+ import type {GraphQLSchema} from 'graphql/type/schema';
5
+
6
+ import {schemaFromIntrospectionData} from '../schemaFromIntrospectionData';
7
+
8
+ import fs from 'fs';
9
+ import {
10
+ buildClientSchema,
11
+ buildSchema,
12
+ getIntrospectionQuery,
13
+ graphqlSync,
14
+ type IntrospectionQuery,
15
+ } from 'graphql';
16
+ import path from 'path';
17
+
18
+ export type CliConfig = {
19
+ excludes: Array<RegExp>,
20
+ schemaFilePath: string,
21
+ options: ExternalOptions,
22
+ };
23
+
24
+ /**
25
+ * This is the json-compatible form of the config
26
+ * object.
27
+ */
28
+ type JSONConfig = {
29
+ excludes?: Array<string>,
30
+ schemaFilePath: string,
31
+ options?: ExternalOptions,
32
+ };
33
+
34
+ export const loadConfigFile = (configFile: string): CliConfig => {
35
+ // eslint-disable-next-line flowtype-errors/uncovered
36
+ const data: JSONConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));
37
+ const toplevelKeys = ['excludes', 'schemaFilePath', 'options'];
38
+ Object.keys(data).forEach((k) => {
39
+ if (!toplevelKeys.includes(k)) {
40
+ throw new Error(
41
+ `Invalid attribute in config file ${configFile}: ${k}. Allowed attributes: ${toplevelKeys.join(
42
+ ', ',
43
+ )}`,
44
+ );
45
+ }
46
+ });
47
+ if (data.options) {
48
+ const externalOptionsKeys = [
49
+ 'pragma',
50
+ 'loosePragma',
51
+ 'ignorePragma',
52
+ 'scalars',
53
+ 'strictNullability',
54
+ 'regenerateCommand',
55
+ 'readOnlyArray',
56
+ ];
57
+ Object.keys(data.options).forEach((k) => {
58
+ if (!externalOptionsKeys.includes(k)) {
59
+ throw new Error(
60
+ `Invalid option in config file ${configFile}: ${k}. Allowed options: ${externalOptionsKeys.join(
61
+ ', ',
62
+ )}`,
63
+ );
64
+ }
65
+ });
66
+ }
67
+ return {
68
+ options: data.options ?? {},
69
+ excludes: data.excludes?.map((string) => new RegExp(string)) ?? [],
70
+ schemaFilePath: path.join(
71
+ path.dirname(configFile),
72
+ data.schemaFilePath,
73
+ ),
74
+ };
75
+ };
76
+
77
+ /**
78
+ * Loads a .json 'introspection query response', or a .graphql schema definition.
79
+ */
80
+ export const getSchemas = (schemaFilePath: string): [GraphQLSchema, Schema] => {
81
+ const raw = fs.readFileSync(schemaFilePath, 'utf8');
82
+ if (schemaFilePath.endsWith('.graphql')) {
83
+ const schemaForValidation = buildSchema(raw);
84
+ const queryResponse = graphqlSync(
85
+ schemaForValidation,
86
+ getIntrospectionQuery({descriptions: true}),
87
+ );
88
+ const schemaForTypeGeneration = schemaFromIntrospectionData(
89
+ // eslint-disable-next-line flowtype-errors/uncovered
90
+ ((queryResponse.data: any): IntrospectionQuery),
91
+ );
92
+ return [schemaForValidation, schemaForTypeGeneration];
93
+ } else {
94
+ // eslint-disable-next-line flowtype-errors/uncovered
95
+ const introspectionData: IntrospectionQuery = JSON.parse(raw);
96
+ const schemaForValidation = buildClientSchema(introspectionData);
97
+ const schemaForTypeGeneration =
98
+ schemaFromIntrospectionData(introspectionData);
99
+ return [schemaForValidation, schemaForTypeGeneration];
100
+ }
101
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/config.js"],"names":["loadConfigFile","configFile","data","JSON","parse","fs","readFileSync","toplevelKeys","Object","keys","forEach","k","includes","Error","join","options","externalOptionsKeys","excludes","map","string","RegExp","schemaFilePath","path","dirname","getSchemas","raw","endsWith","schemaForValidation","queryResponse","descriptions","schemaForTypeGeneration","introspectionData"],"mappings":";;;;;;;AAKA;;AAEA;;AACA;;AAOA;;;;AAkBO,MAAMA,cAAc,GAAIC,UAAD,IAAmC;AAAA;;AAC7D;AACA,QAAMC,IAAgB,GAAGC,IAAI,CAACC,KAAL,CAAWC,YAAGC,YAAH,CAAgBL,UAAhB,EAA4B,MAA5B,CAAX,CAAzB;AACA,QAAMM,YAAY,GAAG,CAAC,UAAD,EAAa,gBAAb,EAA+B,SAA/B,CAArB;AACAC,EAAAA,MAAM,CAACC,IAAP,CAAYP,IAAZ,EAAkBQ,OAAlB,CAA2BC,CAAD,IAAO;AAC7B,QAAI,CAACJ,YAAY,CAACK,QAAb,CAAsBD,CAAtB,CAAL,EAA+B;AAC3B,YAAM,IAAIE,KAAJ,CACD,oCAAmCZ,UAAW,KAAIU,CAAE,yBAAwBJ,YAAY,CAACO,IAAb,CACzE,IADyE,CAE3E,EAHA,CAAN;AAKH;AACJ,GARD;;AASA,MAAIZ,IAAI,CAACa,OAAT,EAAkB;AACd,UAAMC,mBAAmB,GAAG,CACxB,QADwB,EAExB,aAFwB,EAGxB,cAHwB,EAIxB,SAJwB,EAKxB,mBALwB,EAMxB,mBANwB,EAOxB,eAPwB,CAA5B;AASAR,IAAAA,MAAM,CAACC,IAAP,CAAYP,IAAI,CAACa,OAAjB,EAA0BL,OAA1B,CAAmCC,CAAD,IAAO;AACrC,UAAI,CAACK,mBAAmB,CAACJ,QAApB,CAA6BD,CAA7B,CAAL,EAAsC;AAClC,cAAM,IAAIE,KAAJ,CACD,iCAAgCZ,UAAW,KAAIU,CAAE,sBAAqBK,mBAAmB,CAACF,IAApB,CACnE,IADmE,CAErE,EAHA,CAAN;AAKH;AACJ,KARD;AASH;;AACD,SAAO;AACHC,IAAAA,OAAO,EAAEb,IAAI,CAACa,OAAL,IAAgB,EADtB;AAEHE,IAAAA,QAAQ,EAAE,mBAAAf,IAAI,CAACe,QAAL,kEAAeC,GAAf,CAAoBC,MAAD,IAAY,IAAIC,MAAJ,CAAWD,MAAX,CAA/B,MAAsD,EAF7D;AAGHE,IAAAA,cAAc,EAAEC,cAAKR,IAAL,CACZQ,cAAKC,OAAL,CAAatB,UAAb,CADY,EAEZC,IAAI,CAACmB,cAFO;AAHb,GAAP;AAQH,CAzCM;AA2CP;AACA;AACA;;;;;AACO,MAAMG,UAAU,GAAIH,cAAD,IAAqD;AAC3E,QAAMI,GAAG,GAAGpB,YAAGC,YAAH,CAAgBe,cAAhB,EAAgC,MAAhC,CAAZ;;AACA,MAAIA,cAAc,CAACK,QAAf,CAAwB,UAAxB,CAAJ,EAAyC;AACrC,UAAMC,mBAAmB,GAAG,0BAAYF,GAAZ,CAA5B;AACA,UAAMG,aAAa,GAAG,0BAClBD,mBADkB,EAElB,oCAAsB;AAACE,MAAAA,YAAY,EAAE;AAAf,KAAtB,CAFkB,CAAtB;AAIA,UAAMC,uBAAuB,GAAG,+DAC5B;AACEF,IAAAA,aAAa,CAAC1B,IAFY,CAAhC;AAIA,WAAO,CAACyB,mBAAD,EAAsBG,uBAAtB,CAAP;AACH,GAXD,MAWO;AACH;AACA,UAAMC,iBAAqC,GAAG5B,IAAI,CAACC,KAAL,CAAWqB,GAAX,CAA9C;AACA,UAAME,mBAAmB,GAAG,gCAAkBI,iBAAlB,CAA5B;AACA,UAAMD,uBAAuB,GACzB,8DAA4BC,iBAA5B,CADJ;AAEA,WAAO,CAACJ,mBAAD,EAAsBG,uBAAtB,CAAP;AACH;AACJ,CArBM","sourcesContent":["// @flow\nimport type {ExternalOptions} from '../generateTypeFiles';\nimport type {Schema} from '../types';\nimport type {GraphQLSchema} from 'graphql/type/schema';\n\nimport {schemaFromIntrospectionData} from '../schemaFromIntrospectionData';\n\nimport fs from 'fs';\nimport {\n buildClientSchema,\n buildSchema,\n getIntrospectionQuery,\n graphqlSync,\n type IntrospectionQuery,\n} from 'graphql';\nimport path from 'path';\n\nexport type CliConfig = {\n excludes: Array<RegExp>,\n schemaFilePath: string,\n options: ExternalOptions,\n};\n\n/**\n * This is the json-compatible form of the config\n * object.\n */\ntype JSONConfig = {\n excludes?: Array<string>,\n schemaFilePath: string,\n options?: ExternalOptions,\n};\n\nexport const loadConfigFile = (configFile: string): CliConfig => {\n // eslint-disable-next-line flowtype-errors/uncovered\n const data: JSONConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));\n const toplevelKeys = ['excludes', 'schemaFilePath', 'options'];\n Object.keys(data).forEach((k) => {\n if (!toplevelKeys.includes(k)) {\n throw new Error(\n `Invalid attribute in config file ${configFile}: ${k}. Allowed attributes: ${toplevelKeys.join(\n ', ',\n )}`,\n );\n }\n });\n if (data.options) {\n const externalOptionsKeys = [\n 'pragma',\n 'loosePragma',\n 'ignorePragma',\n 'scalars',\n 'strictNullability',\n 'regenerateCommand',\n 'readOnlyArray',\n ];\n Object.keys(data.options).forEach((k) => {\n if (!externalOptionsKeys.includes(k)) {\n throw new Error(\n `Invalid option in config file ${configFile}: ${k}. Allowed options: ${externalOptionsKeys.join(\n ', ',\n )}`,\n );\n }\n });\n }\n return {\n options: data.options ?? {},\n excludes: data.excludes?.map((string) => new RegExp(string)) ?? [],\n schemaFilePath: path.join(\n path.dirname(configFile),\n data.schemaFilePath,\n ),\n };\n};\n\n/**\n * Loads a .json 'introspection query response', or a .graphql schema definition.\n */\nexport const getSchemas = (schemaFilePath: string): [GraphQLSchema, Schema] => {\n const raw = fs.readFileSync(schemaFilePath, 'utf8');\n if (schemaFilePath.endsWith('.graphql')) {\n const schemaForValidation = buildSchema(raw);\n const queryResponse = graphqlSync(\n schemaForValidation,\n getIntrospectionQuery({descriptions: true}),\n );\n const schemaForTypeGeneration = schemaFromIntrospectionData(\n // eslint-disable-next-line flowtype-errors/uncovered\n ((queryResponse.data: any): IntrospectionQuery),\n );\n return [schemaForValidation, schemaForTypeGeneration];\n } else {\n // eslint-disable-next-line flowtype-errors/uncovered\n const introspectionData: IntrospectionQuery = JSON.parse(raw);\n const schemaForValidation = buildClientSchema(introspectionData);\n const schemaForTypeGeneration =\n schemaFromIntrospectionData(introspectionData);\n return [schemaForValidation, schemaForTypeGeneration];\n }\n};\n"],"file":"config.js"}
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+
3
+ var _generateTypeFiles = require("../generateTypeFiles");
4
+
5
+ var _parse = require("../parser/parse");
6
+
7
+ var _resolve = require("../parser/resolve");
8
+
9
+ var _config = require("./config");
10
+
11
+ var _apolloUtilities = require("apollo-utilities");
12
+
13
+ var _child_process = require("child_process");
14
+
15
+ var _fs = require("fs");
16
+
17
+ var _printer = require("graphql/language/printer");
18
+
19
+ var _validation = require("graphql/validation");
20
+
21
+ var _path = _interopRequireDefault(require("path"));
22
+
23
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
24
+
25
+ /* eslint-disable no-console */
26
+ // eslint-disable-line flowtype-errors/uncovered
27
+
28
+ /**
29
+ * This CLI tool executes the following steps:
30
+ * 1) process options
31
+ * 2) crawl files to find all operations and fragments, with
32
+ * tagged template literals and expressions.
33
+ * 3) resolve the found operations, passing the literals and
34
+ * fragments into the `graphql-tag` function to produce
35
+ * the DocumentNodes.
36
+ * 4) generate types for all resolved Queries & Mutations
37
+ */
38
+
39
+ /** Step (1) */
40
+ const findGraphqlTagReferences = root => {
41
+ const response = (0, _child_process.execSync)("git grep -I --word-regexp --name-only --fixed-strings 'graphql-tag' -- '*.js' '*.jsx'", {
42
+ encoding: 'utf8',
43
+ cwd: root
44
+ });
45
+ return response.trim().split('\n').map(relative => _path.default.join(root, relative));
46
+ };
47
+
48
+ const [_, __, configFile, ...cliFiles] = process.argv;
49
+
50
+ if (configFile === '-h' || configFile === '--help' || configFile === 'help' || !configFile) {
51
+ console.log(`graphql-flow
52
+
53
+ Usage: graphql-flow [configFile.json] [filesToCrawl...]`);
54
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
55
+ }
56
+
57
+ const config = (0, _config.loadConfigFile)(configFile);
58
+ const [schemaForValidation, schemaForTypeGeneration] = (0, _config.getSchemas)(config.schemaFilePath);
59
+ const inputFiles = cliFiles.length ? cliFiles : findGraphqlTagReferences(process.cwd());
60
+ /** Step (2) */
61
+
62
+ const files = (0, _parse.processFiles)(inputFiles, f => (0, _fs.readFileSync)(f, 'utf8'));
63
+ let filesHadErrors = false;
64
+ Object.keys(files).forEach(key => {
65
+ const file = files[key];
66
+
67
+ if (file.errors.length) {
68
+ filesHadErrors = true;
69
+ console.error(`Errors in ${file.path}`);
70
+ file.errors.forEach(error => {
71
+ console.error(` - ${error.message}`);
72
+ });
73
+ }
74
+ });
75
+
76
+ if (filesHadErrors) {
77
+ console.error('Aborting');
78
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
79
+ }
80
+ /** Step (3) */
81
+
82
+
83
+ const {
84
+ resolved,
85
+ errors
86
+ } = (0, _resolve.resolveDocuments)(files);
87
+
88
+ if (errors.length) {
89
+ errors.forEach(error => {
90
+ console.error(`Resolution error ${error.message} in ${error.loc.path}`);
91
+ });
92
+ console.error('Aborting');
93
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
94
+ }
95
+
96
+ console.log(Object.keys(resolved).length, 'resolved queries');
97
+ /** Step (4) */
98
+
99
+ let validationFailures = 0;
100
+ Object.keys(resolved).forEach(k => {
101
+ const {
102
+ document,
103
+ raw
104
+ } = resolved[k];
105
+
106
+ if (config.excludes.some(rx => rx.test(raw.loc.path))) {
107
+ return; // skip
108
+ }
109
+
110
+ const hasNonFragments = document.definitions.some(({
111
+ kind
112
+ }) => kind !== 'FragmentDefinition');
113
+ const rawSource = raw.literals[0];
114
+ const processedOptions = (0, _generateTypeFiles.processPragmas)(config.options, rawSource);
115
+
116
+ if (!hasNonFragments || !processedOptions) {
117
+ return;
118
+ } // eslint-disable-next-line flowtype-errors/uncovered
119
+
120
+
121
+ const withTypeNames = (0, _apolloUtilities.addTypenameToDocument)(document);
122
+ const printed = (0, _printer.print)(withTypeNames);
123
+ /* eslint-disable flowtype-errors/uncovered */
124
+
125
+ const errors = (0, _validation.validate)(schemaForValidation, withTypeNames);
126
+ /* eslint-disable flowtype-errors/uncovered */
127
+
128
+ if (errors.length) {
129
+ errors.forEach(error => {
130
+ console.error(`Schema validation found errors for ${raw.loc.path}!`);
131
+ console.error(printed);
132
+ console.error(error);
133
+ validationFailures++;
134
+ });
135
+ }
136
+ /* eslint-enable flowtype-errors/uncovered */
137
+
138
+
139
+ try {
140
+ (0, _generateTypeFiles.generateTypeFiles)(raw.loc.path, schemaForTypeGeneration, withTypeNames, processedOptions); // eslint-disable-next-line flowtype-errors/uncovered
141
+ } catch (err) {
142
+ console.error(`Error while generating operation from ${raw.loc.path}`);
143
+ console.error(printed); // eslint-disable-next-line flowtype-errors/uncovered
144
+
145
+ console.error(err);
146
+ validationFailures++;
147
+ }
148
+ });
149
+
150
+ if (validationFailures) {
151
+ console.error(`Encountered ${validationFailures} validation failures while printing types.`); // eslint-disable-next-line flowtype-errors/uncovered
152
+
153
+ process.exit(1);
154
+ }
155
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1,161 @@
1
+ // @flow
2
+ /* eslint-disable no-console */
3
+ import {generateTypeFiles, processPragmas} from '../generateTypeFiles';
4
+ import {processFiles} from '../parser/parse';
5
+ import {resolveDocuments} from '../parser/resolve';
6
+ import {getSchemas, loadConfigFile} from './config';
7
+
8
+ import {addTypenameToDocument} from 'apollo-utilities'; // eslint-disable-line flowtype-errors/uncovered
9
+
10
+ import {execSync} from 'child_process';
11
+ import {readFileSync} from 'fs';
12
+ import {type DocumentNode} from 'graphql';
13
+ import {print} from 'graphql/language/printer';
14
+ import {validate} from 'graphql/validation';
15
+ import path from 'path';
16
+
17
+ /**
18
+ * This CLI tool executes the following steps:
19
+ * 1) process options
20
+ * 2) crawl files to find all operations and fragments, with
21
+ * tagged template literals and expressions.
22
+ * 3) resolve the found operations, passing the literals and
23
+ * fragments into the `graphql-tag` function to produce
24
+ * the DocumentNodes.
25
+ * 4) generate types for all resolved Queries & Mutations
26
+ */
27
+
28
+ /** Step (1) */
29
+
30
+ const findGraphqlTagReferences = (root: string): Array<string> => {
31
+ const response = execSync(
32
+ "git grep -I --word-regexp --name-only --fixed-strings 'graphql-tag' -- '*.js' '*.jsx'",
33
+ {
34
+ encoding: 'utf8',
35
+ cwd: root,
36
+ },
37
+ );
38
+ return response
39
+ .trim()
40
+ .split('\n')
41
+ .map((relative) => path.join(root, relative));
42
+ };
43
+
44
+ const [_, __, configFile, ...cliFiles] = process.argv;
45
+
46
+ if (
47
+ configFile === '-h' ||
48
+ configFile === '--help' ||
49
+ configFile === 'help' ||
50
+ !configFile
51
+ ) {
52
+ console.log(`graphql-flow
53
+
54
+ Usage: graphql-flow [configFile.json] [filesToCrawl...]`);
55
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
56
+ }
57
+
58
+ const config = loadConfigFile(configFile);
59
+
60
+ const [schemaForValidation, schemaForTypeGeneration] = getSchemas(
61
+ config.schemaFilePath,
62
+ );
63
+
64
+ const inputFiles = cliFiles.length
65
+ ? cliFiles
66
+ : findGraphqlTagReferences(process.cwd());
67
+
68
+ /** Step (2) */
69
+
70
+ const files = processFiles(inputFiles, (f) => readFileSync(f, 'utf8'));
71
+
72
+ let filesHadErrors = false;
73
+ Object.keys(files).forEach((key) => {
74
+ const file = files[key];
75
+ if (file.errors.length) {
76
+ filesHadErrors = true;
77
+ console.error(`Errors in ${file.path}`);
78
+ file.errors.forEach((error) => {
79
+ console.error(` - ${error.message}`);
80
+ });
81
+ }
82
+ });
83
+
84
+ if (filesHadErrors) {
85
+ console.error('Aborting');
86
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
87
+ }
88
+
89
+ /** Step (3) */
90
+
91
+ const {resolved, errors} = resolveDocuments(files);
92
+ if (errors.length) {
93
+ errors.forEach((error) => {
94
+ console.error(`Resolution error ${error.message} in ${error.loc.path}`);
95
+ });
96
+ console.error('Aborting');
97
+ process.exit(1); // eslint-disable-line flowtype-errors/uncovered
98
+ }
99
+
100
+ console.log(Object.keys(resolved).length, 'resolved queries');
101
+
102
+ /** Step (4) */
103
+
104
+ let validationFailures: number = 0;
105
+
106
+ Object.keys(resolved).forEach((k) => {
107
+ const {document, raw} = resolved[k];
108
+ if (config.excludes.some((rx) => rx.test(raw.loc.path))) {
109
+ return; // skip
110
+ }
111
+ const hasNonFragments = document.definitions.some(
112
+ ({kind}) => kind !== 'FragmentDefinition',
113
+ );
114
+ const rawSource: string = raw.literals[0];
115
+ const processedOptions = processPragmas(config.options, rawSource);
116
+ if (!hasNonFragments || !processedOptions) {
117
+ return;
118
+ }
119
+
120
+ // eslint-disable-next-line flowtype-errors/uncovered
121
+ const withTypeNames: DocumentNode = addTypenameToDocument(document);
122
+ 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
+ });
135
+ }
136
+ /* eslint-enable flowtype-errors/uncovered */
137
+
138
+ try {
139
+ generateTypeFiles(
140
+ raw.loc.path,
141
+ schemaForTypeGeneration,
142
+ withTypeNames,
143
+ processedOptions,
144
+ );
145
+ // eslint-disable-next-line flowtype-errors/uncovered
146
+ } catch (err) {
147
+ console.error(`Error while generating operation from ${raw.loc.path}`);
148
+ console.error(printed);
149
+ // eslint-disable-next-line flowtype-errors/uncovered
150
+ console.error(err);
151
+ validationFailures++;
152
+ }
153
+ });
154
+
155
+ if (validationFailures) {
156
+ console.error(
157
+ `Encountered ${validationFailures} validation failures while printing types.`,
158
+ );
159
+ // eslint-disable-next-line flowtype-errors/uncovered
160
+ process.exit(1);
161
+ }