@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.
- package/CHANGELOG.md +12 -0
- package/Readme.md +49 -65
- package/dist/cli/config.js +73 -0
- package/dist/cli/config.js.flow +101 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/run.js +155 -0
- package/dist/cli/run.js.flow +161 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/enums.js +2 -1
- package/dist/enums.js.map +1 -0
- package/dist/generateResponseType.js +20 -9
- package/dist/generateResponseType.js.flow +25 -17
- package/dist/generateResponseType.js.map +1 -0
- package/dist/generateTypeFiles.js +102 -0
- package/dist/generateTypeFiles.js.flow +127 -0
- package/dist/generateTypeFiles.js.map +1 -0
- package/dist/generateVariablesType.js +2 -1
- package/dist/generateVariablesType.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -0
- package/dist/jest-mock-graphql-tag.js +22 -107
- package/dist/jest-mock-graphql-tag.js.flow +30 -138
- package/dist/jest-mock-graphql-tag.js.map +1 -0
- package/dist/parser/parse.js +349 -0
- package/dist/parser/parse.js.flow +403 -0
- package/dist/parser/parse.js.map +1 -0
- package/dist/parser/resolve.js +111 -0
- package/dist/parser/resolve.js.flow +117 -0
- package/dist/parser/resolve.js.map +1 -0
- package/dist/schemaFromIntrospectionData.js +2 -1
- package/dist/schemaFromIntrospectionData.js.map +1 -0
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -0
- package/dist/utils.js +2 -1
- package/dist/utils.js.map +1 -0
- package/package.json +9 -5
- package/src/__test__/graphql-flow.test.js +68 -24
- package/src/__test__/{jest-mock-graphql-tag.test.js → processPragmas.test.js} +13 -1
- package/src/cli/config.js +101 -0
- package/src/cli/run.js +161 -0
- package/src/generateResponseType.js +25 -17
- package/src/generateTypeFiles.js +127 -0
- package/src/jest-mock-graphql-tag.js +30 -138
- package/src/parser/__test__/parse.test.js +247 -0
- package/src/parser/parse.js +403 -0
- 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
|
-
|
|
5
|
+
## Using as a CLI tool
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Write a config file, with the following options:
|
|
8
8
|
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
32
|
+
Files will be discovered relative to the current working directory.
|
|
45
33
|
|
|
46
|
-
|
|
34
|
+
To specify what file should be checked, pass them in as subsequent cli arguments.
|
|
47
35
|
|
|
48
|
-
## Options for
|
|
36
|
+
## Options (for the cli 'options' config item, or when running from jest):
|
|
49
37
|
|
|
50
|
-
```
|
|
51
|
-
{
|
|
52
|
-
//
|
|
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
|
|
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"}
|
package/dist/cli/run.js
ADDED
|
@@ -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
|
+
}
|