@khanacademy/graphql-flow 0.2.5 → 1.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 (61) hide show
  1. package/.flowconfig +1 -0
  2. package/.github/workflows/changeset-release.yml +1 -1
  3. package/.github/workflows/pr-checks.yml +13 -10
  4. package/CHANGELOG.md +35 -0
  5. package/Readme.md +41 -142
  6. package/dist/__test__/example-schema.graphql +67 -0
  7. package/dist/__test__/generateTypeFileContents.test.js +157 -0
  8. package/dist/__test__/graphql-flow.test.js +639 -0
  9. package/dist/__test__/processPragmas.test.js +76 -0
  10. package/dist/cli/__test__/config.test.js +120 -0
  11. package/dist/cli/config.js +51 -26
  12. package/dist/cli/config.js.flow +40 -67
  13. package/dist/cli/config.js.map +1 -1
  14. package/dist/cli/run.js +42 -20
  15. package/dist/cli/run.js.flow +59 -23
  16. package/dist/cli/run.js.map +1 -1
  17. package/dist/cli/schema.json +94 -0
  18. package/dist/enums.js +20 -7
  19. package/dist/enums.js.flow +44 -15
  20. package/dist/enums.js.map +1 -1
  21. package/dist/generateResponseType.js +47 -47
  22. package/dist/generateResponseType.js.flow +55 -57
  23. package/dist/generateResponseType.js.map +1 -1
  24. package/dist/generateTypeFiles.js +42 -22
  25. package/dist/generateTypeFiles.js.flow +84 -77
  26. package/dist/generateTypeFiles.js.map +1 -1
  27. package/dist/generateVariablesType.js +24 -24
  28. package/dist/generateVariablesType.js.flow +25 -28
  29. package/dist/generateVariablesType.js.map +1 -1
  30. package/dist/index.js +21 -22
  31. package/dist/index.js.flow +32 -16
  32. package/dist/index.js.map +1 -1
  33. package/dist/parser/__test__/parse.test.js +247 -0
  34. package/dist/parser/parse.js +1 -1
  35. package/dist/parser/parse.js.flow +3 -1
  36. package/dist/parser/parse.js.map +1 -1
  37. package/dist/types.js.flow +30 -5
  38. package/flow-typed/npm/@babel/types_vx.x.x.js +17 -3
  39. package/package.json +4 -2
  40. package/src/__test__/generateTypeFileContents.test.js +55 -1
  41. package/src/__test__/graphql-flow.test.js +7 -7
  42. package/src/__test__/processPragmas.test.js +28 -15
  43. package/src/cli/__test__/config.test.js +120 -0
  44. package/src/cli/config.js +40 -67
  45. package/src/cli/run.js +59 -23
  46. package/src/cli/schema.json +94 -0
  47. package/src/enums.js +44 -15
  48. package/src/generateResponseType.js +55 -57
  49. package/src/generateTypeFiles.js +84 -77
  50. package/src/generateVariablesType.js +25 -28
  51. package/src/index.js +32 -16
  52. package/src/parser/parse.js +3 -1
  53. package/src/types.js +30 -5
  54. package/.github/actions/filter-files/action.yml +0 -37
  55. package/.github/actions/full-or-limited/action.yml +0 -27
  56. package/.github/actions/json-args/action.yml +0 -32
  57. package/.github/actions/setup/action.yml +0 -28
  58. package/dist/jest-mock-graphql-tag.js +0 -88
  59. package/dist/jest-mock-graphql-tag.js.flow +0 -96
  60. package/dist/jest-mock-graphql-tag.js.map +0 -1
  61. package/src/jest-mock-graphql-tag.js +0 -96
@@ -0,0 +1,120 @@
1
+ // @flow
2
+ import type {Config} from '../../types';
3
+
4
+ import {findApplicableConfig, validateOrThrow} from '../config';
5
+ import configSchema from '../schema.json'; // eslint-disable-line flowtype-errors/uncovered
6
+
7
+ describe('findApplicableConfig', () => {
8
+ it('should work with one that matches', () => {
9
+ const config = {
10
+ schemaFilePath: 'ok.graphql',
11
+ };
12
+ expect(findApplicableConfig('/hello', config)).toBe(config);
13
+ });
14
+
15
+ it('should be falsy if nothing matches', () => {
16
+ const config = {
17
+ schemaFilePath: 'ok.graphql',
18
+ exclude: [/hello$/],
19
+ };
20
+ expect(findApplicableConfig('/hello', config)).toBeUndefined();
21
+ });
22
+
23
+ it('should match & exclude with multiple configs', () => {
24
+ const configs = [
25
+ {schemaFilePath: 'one', match: [/\.jsx$/], exclude: [/^test/]},
26
+ {schemaFilePath: 'two', exclude: [/^hello/]},
27
+ {schemaFilePath: 'three'},
28
+ ];
29
+ expect(findApplicableConfig('hello.js', configs)).toBe(configs[2]);
30
+ expect(findApplicableConfig('goodbye.js', configs)).toBe(configs[1]);
31
+ expect(findApplicableConfig('hello.jsx', configs)).toBe(configs[0]);
32
+ expect(findApplicableConfig('test.jsx', configs)).toBe(configs[1]);
33
+ });
34
+ });
35
+
36
+ describe('jsonschema validation', () => {
37
+ it('should accept valid schema', () => {
38
+ const config: Config = {
39
+ crawl: {
40
+ root: '/here/we/crawl',
41
+ },
42
+ generate: {
43
+ match: [/\.fixture\.js$/],
44
+ exclude: [
45
+ '_test\\.js$',
46
+ '\\bcourse-editor-package\\b',
47
+ '\\.fixture\\.js$',
48
+ '\\b__flowtests__\\b',
49
+ '\\bcourse-editor\\b',
50
+ ],
51
+ readOnlyArray: false,
52
+ regenerateCommand: 'make gqlflow',
53
+ scalars: {
54
+ JSONString: 'string',
55
+ KALocale: 'string',
56
+ NaiveDateTime: 'string',
57
+ },
58
+ splitTypes: true,
59
+ generatedDirectory: '__graphql-types__',
60
+ exportAllObjectTypes: true,
61
+ schemaFilePath: './composed_schema.graphql',
62
+ },
63
+ };
64
+ validateOrThrow(
65
+ config,
66
+ configSchema, // eslint-disable-line flowtype-errors/uncovered
67
+ );
68
+ });
69
+
70
+ it('should accept a schema with multiple generate configs', () => {
71
+ const generate = {
72
+ match: [/\.fixture\.js$/],
73
+ exclude: [
74
+ '_test\\.js$',
75
+ '\\bcourse-editor-package\\b',
76
+ '\\.fixture\\.js$',
77
+ '\\b__flowtests__\\b',
78
+ '\\bcourse-editor\\b',
79
+ ],
80
+ readOnlyArray: false,
81
+ regenerateCommand: 'make gqlflow',
82
+ scalars: {
83
+ JSONString: 'string',
84
+ KALocale: 'string',
85
+ NaiveDateTime: 'string',
86
+ },
87
+ splitTypes: true,
88
+ generatedDirectory: '__graphql-types__',
89
+ exportAllObjectTypes: true,
90
+ schemaFilePath: './composed_schema.graphql',
91
+ };
92
+ const config: Config = {
93
+ crawl: {
94
+ root: '/here/we/crawl',
95
+ },
96
+ generate: [
97
+ {...generate, match: [/^static/], exportAllObjectTypes: false},
98
+ generate,
99
+ ],
100
+ };
101
+ validateOrThrow(
102
+ config,
103
+ configSchema, // eslint-disable-line flowtype-errors/uncovered
104
+ );
105
+ });
106
+
107
+ it('should reject invalid schema', () => {
108
+ expect(() =>
109
+ validateOrThrow(
110
+ {schemaFilePath: 10, options: {extraOption: 'hello'}},
111
+ configSchema, // eslint-disable-line flowtype-errors/uncovered
112
+ ),
113
+ ).toThrowErrorMatchingInlineSnapshot(`
114
+ "instance is not allowed to have the additional property \\"schemaFilePath\\"
115
+ instance is not allowed to have the additional property \\"options\\"
116
+ instance requires property \\"crawl\\"
117
+ instance requires property \\"generate\\""
118
+ `);
119
+ });
120
+ });
@@ -3,45 +3,42 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.loadConfigFile = exports.getSchemas = void 0;
6
+ exports.validateOrThrow = exports.loadConfigFile = exports.getSchemas = exports.findApplicableConfig = void 0;
7
7
 
8
8
  var _schemaFromIntrospectionData = require("../schemaFromIntrospectionData");
9
9
 
10
+ var _schema = _interopRequireDefault(require("./schema.json"));
11
+
10
12
  var _fs = _interopRequireDefault(require("fs"));
11
13
 
12
14
  var _graphql = require("graphql");
13
15
 
14
- var _path = _interopRequireDefault(require("path"));
16
+ var _jsonschema = require("jsonschema");
15
17
 
16
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
19
 
18
- const loadConfigFile = configFile => {
19
- var _data$options, _data$excludes$map, _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', 'dumpOperations'];
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
- });
20
+ // eslint-disable-line flowtype-errors/uncovered
21
+ // eslint-disable-line flowtype-errors/uncovered
22
+ const validateOrThrow = (value, jsonSchema) => {
23
+ /* eslint-disable flowtype-errors/uncovered */
24
+ const result = (0, _jsonschema.validate)(value, jsonSchema);
29
25
 
30
- if (data.options) {
31
- const externalOptionsKeys = ['pragma', 'loosePragma', 'ignorePragma', 'scalars', 'strictNullability', 'regenerateCommand', 'readOnlyArray', 'splitTypes', 'generatedDirectory', 'exportAllObjectTypes', 'typeFileName'];
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
- });
26
+ if (!result.valid) {
27
+ throw new Error(result.errors.map(error => error.toString()).join('\n'));
37
28
  }
29
+ /* eslint-enable flowtype-errors/uncovered */
30
+
31
+ };
38
32
 
39
- return {
40
- options: (_data$options = data.options) !== null && _data$options !== void 0 ? _data$options : {},
41
- excludes: (_data$excludes$map = (_data$excludes = data.excludes) === null || _data$excludes === void 0 ? void 0 : _data$excludes.map(string => new RegExp(string))) !== null && _data$excludes$map !== void 0 ? _data$excludes$map : [],
42
- schemaFilePath: _path.default.isAbsolute(data.schemaFilePath) ? data.schemaFilePath : _path.default.join(_path.default.dirname(configFile), data.schemaFilePath),
43
- dumpOperations: data.dumpOperations
44
- };
33
+ exports.validateOrThrow = validateOrThrow;
34
+
35
+ const loadConfigFile = configFile => {
36
+ // $FlowIgnore // eslint-disable-next-line flowtype-errors/uncovered
37
+ const data = require(configFile); // eslint-disable-next-line flowtype-errors/uncovered
38
+
39
+
40
+ validateOrThrow(data, _schema.default);
41
+ return data;
45
42
  };
46
43
  /**
47
44
  * Loads a .json 'introspection query response', or a .graphql schema definition.
@@ -69,6 +66,34 @@ const getSchemas = schemaFilePath => {
69
66
  return [schemaForValidation, schemaForTypeGeneration];
70
67
  }
71
68
  };
69
+ /**
70
+ * Find the first item of the `config.generate` array where both:
71
+ * - no item of `exclude` matches
72
+ * - at least one item of `match` matches
73
+ */
74
+
72
75
 
73
76
  exports.getSchemas = getSchemas;
77
+
78
+ const findApplicableConfig = (path, configs) => {
79
+ if (!Array.isArray(configs)) {
80
+ configs = [configs];
81
+ }
82
+
83
+ return configs.find(config => {
84
+ var _config$exclude;
85
+
86
+ if ((_config$exclude = config.exclude) !== null && _config$exclude !== void 0 && _config$exclude.some(exclude => new RegExp(exclude).test(path))) {
87
+ return false;
88
+ }
89
+
90
+ if (!config.match) {
91
+ return true;
92
+ }
93
+
94
+ return config.match.some(matcher => new RegExp(matcher).test(path));
95
+ });
96
+ };
97
+
98
+ exports.findApplicableConfig = findApplicableConfig;
74
99
  //# sourceMappingURL=config.js.map
@@ -1,9 +1,9 @@
1
1
  // @flow
2
- import type {ExternalOptions} from '../generateTypeFiles';
3
2
  import type {Schema} from '../types';
4
3
  import type {GraphQLSchema} from 'graphql/type/schema';
5
4
 
6
5
  import {schemaFromIntrospectionData} from '../schemaFromIntrospectionData';
6
+ import configSchema from './schema.json'; // eslint-disable-line flowtype-errors/uncovered
7
7
 
8
8
  import fs from 'fs';
9
9
  import {
@@ -13,76 +13,26 @@ import {
13
13
  graphqlSync,
14
14
  type IntrospectionQuery,
15
15
  } from 'graphql';
16
- import path from 'path';
16
+ import type {Config, GenerateConfig} from '../types';
17
+ import {validate} from 'jsonschema'; // eslint-disable-line flowtype-errors/uncovered
17
18
 
18
- export type CliConfig = {
19
- excludes: Array<RegExp>,
20
- schemaFilePath: string,
21
- dumpOperations?: string,
22
- options: ExternalOptions,
23
- };
24
-
25
- /**
26
- * This is the json-compatible form of the config
27
- * object.
28
- */
29
- type JSONConfig = {
30
- excludes?: Array<string>,
31
- schemaFilePath: string,
32
- options?: ExternalOptions,
33
- dumpOperations?: string,
19
+ export const validateOrThrow = (value: mixed, jsonSchema: mixed) => {
20
+ /* eslint-disable flowtype-errors/uncovered */
21
+ const result = validate(value, jsonSchema);
22
+ if (!result.valid) {
23
+ throw new Error(
24
+ result.errors.map((error) => error.toString()).join('\n'),
25
+ );
26
+ }
27
+ /* eslint-enable flowtype-errors/uncovered */
34
28
  };
35
29
 
36
- export const loadConfigFile = (configFile: string): CliConfig => {
30
+ export const loadConfigFile = (configFile: string): Config => {
31
+ // $FlowIgnore // eslint-disable-next-line flowtype-errors/uncovered
32
+ const data: Config = require(configFile);
37
33
  // eslint-disable-next-line flowtype-errors/uncovered
38
- const data: JSONConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));
39
- const toplevelKeys = [
40
- 'excludes',
41
- 'schemaFilePath',
42
- 'options',
43
- 'dumpOperations',
44
- ];
45
- Object.keys(data).forEach((k) => {
46
- if (!toplevelKeys.includes(k)) {
47
- throw new Error(
48
- `Invalid attribute in config file ${configFile}: ${k}. Allowed attributes: ${toplevelKeys.join(
49
- ', ',
50
- )}`,
51
- );
52
- }
53
- });
54
- if (data.options) {
55
- const externalOptionsKeys = [
56
- 'pragma',
57
- 'loosePragma',
58
- 'ignorePragma',
59
- 'scalars',
60
- 'strictNullability',
61
- 'regenerateCommand',
62
- 'readOnlyArray',
63
- 'splitTypes',
64
- 'generatedDirectory',
65
- 'exportAllObjectTypes',
66
- 'typeFileName',
67
- ];
68
- Object.keys(data.options).forEach((k) => {
69
- if (!externalOptionsKeys.includes(k)) {
70
- throw new Error(
71
- `Invalid option in config file ${configFile}: ${k}. Allowed options: ${externalOptionsKeys.join(
72
- ', ',
73
- )}`,
74
- );
75
- }
76
- });
77
- }
78
- return {
79
- options: data.options ?? {},
80
- excludes: data.excludes?.map((string) => new RegExp(string)) ?? [],
81
- schemaFilePath: path.isAbsolute(data.schemaFilePath)
82
- ? data.schemaFilePath
83
- : path.join(path.dirname(configFile), data.schemaFilePath),
84
- dumpOperations: data.dumpOperations,
85
- };
34
+ validateOrThrow(data, configSchema);
35
+ return data;
86
36
  };
87
37
 
88
38
  /**
@@ -110,3 +60,26 @@ export const getSchemas = (schemaFilePath: string): [GraphQLSchema, Schema] => {
110
60
  return [schemaForValidation, schemaForTypeGeneration];
111
61
  }
112
62
  };
63
+
64
+ /**
65
+ * Find the first item of the `config.generate` array where both:
66
+ * - no item of `exclude` matches
67
+ * - at least one item of `match` matches
68
+ */
69
+ export const findApplicableConfig = (
70
+ path: string,
71
+ configs: Array<GenerateConfig> | GenerateConfig,
72
+ ): ?GenerateConfig => {
73
+ if (!Array.isArray(configs)) {
74
+ configs = [configs];
75
+ }
76
+ return configs.find((config) => {
77
+ if (config.exclude?.some((exclude) => new RegExp(exclude).test(path))) {
78
+ return false;
79
+ }
80
+ if (!config.match) {
81
+ return true;
82
+ }
83
+ return config.match.some((matcher) => new RegExp(matcher).test(path));
84
+ });
85
+ };
@@ -1 +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","isAbsolute","dirname","dumpOperations","getSchemas","raw","endsWith","schemaForValidation","queryResponse","descriptions","schemaForTypeGeneration","introspectionData"],"mappings":"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","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 dumpOperations?: 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 dumpOperations?: string,\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 = [\n 'excludes',\n 'schemaFilePath',\n 'options',\n 'dumpOperations',\n ];\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 'splitTypes',\n 'generatedDirectory',\n 'exportAllObjectTypes',\n 'typeFileName',\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.isAbsolute(data.schemaFilePath)\n ? data.schemaFilePath\n : path.join(path.dirname(configFile), data.schemaFilePath),\n dumpOperations: data.dumpOperations,\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"}
1
+ {"version":3,"sources":["../../src/cli/config.js"],"names":["validateOrThrow","value","jsonSchema","result","valid","Error","errors","map","error","toString","join","loadConfigFile","configFile","data","require","configSchema","getSchemas","schemaFilePath","raw","fs","readFileSync","endsWith","schemaForValidation","queryResponse","descriptions","schemaForTypeGeneration","introspectionData","JSON","parse","findApplicableConfig","path","configs","Array","isArray","find","config","exclude","some","RegExp","test","match","matcher"],"mappings":";;;;;;;AAIA;;AACA;;AAEA;;AACA;;AAQA;;;;AAX0C;AAWL;AAE9B,MAAMA,eAAe,GAAG,CAACC,KAAD,EAAeC,UAAf,KAAqC;AAChE;AACA,QAAMC,MAAM,GAAG,0BAASF,KAAT,EAAgBC,UAAhB,CAAf;;AACA,MAAI,CAACC,MAAM,CAACC,KAAZ,EAAmB;AACf,UAAM,IAAIC,KAAJ,CACFF,MAAM,CAACG,MAAP,CAAcC,GAAd,CAAmBC,KAAD,IAAWA,KAAK,CAACC,QAAN,EAA7B,EAA+CC,IAA/C,CAAoD,IAApD,CADE,CAAN;AAGH;AACD;;AACH,CATM;;;;AAWA,MAAMC,cAAc,GAAIC,UAAD,IAAgC;AAC1D;AACA,QAAMC,IAAY,GAAGC,OAAO,CAACF,UAAD,CAA5B,CAF0D,CAG1D;;;AACAZ,EAAAA,eAAe,CAACa,IAAD,EAAOE,eAAP,CAAf;AACA,SAAOF,IAAP;AACH,CANM;AAQP;AACA;AACA;;;;;AACO,MAAMG,UAAU,GAAIC,cAAD,IAAqD;AAC3E,QAAMC,GAAG,GAAGC,YAAGC,YAAH,CAAgBH,cAAhB,EAAgC,MAAhC,CAAZ;;AACA,MAAIA,cAAc,CAACI,QAAf,CAAwB,UAAxB,CAAJ,EAAyC;AACrC,UAAMC,mBAAmB,GAAG,0BAAYJ,GAAZ,CAA5B;AACA,UAAMK,aAAa,GAAG,0BAClBD,mBADkB,EAElB,oCAAsB;AAACE,MAAAA,YAAY,EAAE;AAAf,KAAtB,CAFkB,CAAtB;AAIA,UAAMC,uBAAuB,GAAG,+DAC5B;AACEF,IAAAA,aAAa,CAACV,IAFY,CAAhC;AAIA,WAAO,CAACS,mBAAD,EAAsBG,uBAAtB,CAAP;AACH,GAXD,MAWO;AACH;AACA,UAAMC,iBAAqC,GAAGC,IAAI,CAACC,KAAL,CAAWV,GAAX,CAA9C;AACA,UAAMI,mBAAmB,GAAG,gCAAkBI,iBAAlB,CAA5B;AACA,UAAMD,uBAAuB,GACzB,8DAA4BC,iBAA5B,CADJ;AAEA,WAAO,CAACJ,mBAAD,EAAsBG,uBAAtB,CAAP;AACH;AACJ,CArBM;AAuBP;AACA;AACA;AACA;AACA;;;;;AACO,MAAMI,oBAAoB,GAAG,CAChCC,IADgC,EAEhCC,OAFgC,KAGd;AAClB,MAAI,CAACC,KAAK,CAACC,OAAN,CAAcF,OAAd,CAAL,EAA6B;AACzBA,IAAAA,OAAO,GAAG,CAACA,OAAD,CAAV;AACH;;AACD,SAAOA,OAAO,CAACG,IAAR,CAAcC,MAAD,IAAY;AAAA;;AAC5B,2BAAIA,MAAM,CAACC,OAAX,4CAAI,gBAAgBC,IAAhB,CAAsBD,OAAD,IAAa,IAAIE,MAAJ,CAAWF,OAAX,EAAoBG,IAApB,CAAyBT,IAAzB,CAAlC,CAAJ,EAAuE;AACnE,aAAO,KAAP;AACH;;AACD,QAAI,CAACK,MAAM,CAACK,KAAZ,EAAmB;AACf,aAAO,IAAP;AACH;;AACD,WAAOL,MAAM,CAACK,KAAP,CAAaH,IAAb,CAAmBI,OAAD,IAAa,IAAIH,MAAJ,CAAWG,OAAX,EAAoBF,IAApB,CAAyBT,IAAzB,CAA/B,CAAP;AACH,GARM,CAAP;AASH,CAhBM","sourcesContent":["// @flow\nimport type {Schema} from '../types';\nimport type {GraphQLSchema} from 'graphql/type/schema';\n\nimport {schemaFromIntrospectionData} from '../schemaFromIntrospectionData';\nimport configSchema from './schema.json'; // eslint-disable-line flowtype-errors/uncovered\n\nimport fs from 'fs';\nimport {\n buildClientSchema,\n buildSchema,\n getIntrospectionQuery,\n graphqlSync,\n type IntrospectionQuery,\n} from 'graphql';\nimport type {Config, GenerateConfig} from '../types';\nimport {validate} from 'jsonschema'; // eslint-disable-line flowtype-errors/uncovered\n\nexport const validateOrThrow = (value: mixed, jsonSchema: mixed) => {\n /* eslint-disable flowtype-errors/uncovered */\n const result = validate(value, jsonSchema);\n if (!result.valid) {\n throw new Error(\n result.errors.map((error) => error.toString()).join('\\n'),\n );\n }\n /* eslint-enable flowtype-errors/uncovered */\n};\n\nexport const loadConfigFile = (configFile: string): Config => {\n // $FlowIgnore // eslint-disable-next-line flowtype-errors/uncovered\n const data: Config = require(configFile);\n // eslint-disable-next-line flowtype-errors/uncovered\n validateOrThrow(data, configSchema);\n return data;\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\n/**\n * Find the first item of the `config.generate` array where both:\n * - no item of `exclude` matches\n * - at least one item of `match` matches\n */\nexport const findApplicableConfig = (\n path: string,\n configs: Array<GenerateConfig> | GenerateConfig,\n): ?GenerateConfig => {\n if (!Array.isArray(configs)) {\n configs = [configs];\n }\n return configs.find((config) => {\n if (config.exclude?.some((exclude) => new RegExp(exclude).test(path))) {\n return false;\n }\n if (!config.match) {\n return true;\n }\n return config.match.some((matcher) => new RegExp(matcher).test(path));\n });\n};\n"],"file":"config.js"}
package/dist/cli/run.js CHANGED
@@ -31,7 +31,7 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
31
31
 
32
32
  /**
33
33
  * This CLI tool executes the following steps:
34
- * 1) process options
34
+ * 1) parse & validate config file
35
35
  * 2) crawl files to find all operations and fragments, with
36
36
  * tagged template literals and expressions.
37
37
  * 3) resolve the found operations, passing the literals and
@@ -42,25 +42,29 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
42
42
 
43
43
  /** Step (1) */
44
44
  const findGraphqlTagReferences = root => {
45
- const response = (0, _child_process.execSync)("git grep -I --word-regexp --name-only --fixed-strings 'graphql-tag' -- '*.js' '*.jsx'", {
45
+ const response = (0, _child_process.execSync)("git grep -I --word-regexp --name-only --fixed-strings 'graphql-tag' -- '*.js' '*.jsx' '*.ts' '*.tsx'", {
46
46
  encoding: 'utf8',
47
47
  cwd: root
48
48
  });
49
49
  return response.trim().split('\n').map(relative => _path.default.join(root, relative));
50
50
  };
51
51
 
52
- const [_, __, configFile, ...cliFiles] = process.argv;
52
+ const [_, __, configFilePath, ...cliFiles] = process.argv;
53
53
 
54
- if (configFile === '-h' || configFile === '--help' || configFile === 'help' || !configFile) {
54
+ if (configFilePath === '-h' || configFilePath === '--help' || configFilePath === 'help' || !configFilePath) {
55
55
  console.log(`graphql-flow
56
56
 
57
57
  Usage: graphql-flow [configFile.json] [filesToCrawl...]`);
58
58
  process.exit(1); // eslint-disable-line flowtype-errors/uncovered
59
59
  }
60
60
 
61
- const config = (0, _config.loadConfigFile)(configFile);
62
- const [schemaForValidation, schemaForTypeGeneration] = (0, _config.getSchemas)(config.schemaFilePath);
63
- const inputFiles = cliFiles.length ? cliFiles : findGraphqlTagReferences(process.cwd());
61
+ const makeAbsPath = (maybeRelativePath, basePath) => {
62
+ return _path.default.isAbsolute(maybeRelativePath) ? maybeRelativePath : _path.default.join(basePath, maybeRelativePath);
63
+ };
64
+
65
+ const absConfigPath = makeAbsPath(configFilePath, process.cwd());
66
+ const config = (0, _config.loadConfigFile)(absConfigPath);
67
+ const inputFiles = cliFiles.length ? cliFiles : findGraphqlTagReferences(makeAbsPath(config.crawl.root, _path.default.dirname(absConfigPath)));
64
68
  /** Step (2) */
65
69
 
66
70
  const files = (0, _parse.processFiles)(inputFiles, f => {
@@ -110,22 +114,34 @@ if (errors.length) {
110
114
  console.log(Object.keys(resolved).length, 'resolved queries');
111
115
  /** Step (4) */
112
116
 
117
+ const schemaCache = {};
118
+
119
+ const getCachedSchemas = schemaFilePath => {
120
+ if (!schemaCache[schemaFilePath]) {
121
+ schemaCache[schemaFilePath] = (0, _config.getSchemas)(makeAbsPath(schemaFilePath, _path.default.dirname(absConfigPath)));
122
+ }
123
+
124
+ return schemaCache[schemaFilePath];
125
+ };
126
+
113
127
  let validationFailures = 0;
114
128
  const printedOperations = [];
115
- Object.keys(resolved).forEach(k => {
129
+ Object.keys(resolved).forEach(filePathAndLine => {
116
130
  const {
117
131
  document,
118
132
  raw
119
- } = resolved[k];
120
-
121
- if (config.excludes.some(rx => rx.test(raw.loc.path))) {
122
- return; // skip
123
- }
124
-
133
+ } = resolved[filePathAndLine];
125
134
  const hasNonFragments = document.definitions.some(({
126
135
  kind
127
136
  }) => kind !== 'FragmentDefinition');
128
- const rawSource = raw.literals[0]; // eslint-disable-next-line flowtype-errors/uncovered
137
+ const rawSource = raw.literals[0];
138
+ const generateConfig = (0, _config.findApplicableConfig)( // strip off the trailing line number, e.g. `:23`
139
+ filePathAndLine.split(':')[0], config.generate);
140
+
141
+ if (!generateConfig) {
142
+ return; // no generate config matches, bail
143
+ } // eslint-disable-next-line flowtype-errors/uncovered
144
+
129
145
 
130
146
  const withTypeNames = (0, _apolloUtilities.addTypenameToDocument)(document);
131
147
  const printed = (0, _printer.print)(withTypeNames);
@@ -134,12 +150,18 @@ Object.keys(resolved).forEach(k => {
134
150
  printedOperations.push(printed);
135
151
  }
136
152
 
137
- const processedOptions = (0, _generateTypeFiles.processPragmas)(config.options, rawSource);
153
+ const pragmaResult = (0, _generateTypeFiles.processPragmas)(generateConfig, config.crawl, rawSource);
138
154
 
139
- if (!processedOptions) {
155
+ if (!pragmaResult.generate) {
140
156
  return;
141
157
  }
142
158
 
159
+ if (pragmaResult.strict != null) {
160
+ generateConfig.strictNullability = pragmaResult.strict;
161
+ }
162
+
163
+ const [schemaForValidation, schemaForTypeGeneration] = getCachedSchemas(generateConfig.schemaFilePath);
164
+
143
165
  if (hasNonFragments) {
144
166
  /* eslint-disable flowtype-errors/uncovered */
145
167
  const errors = (0, _validation.validate)(schemaForValidation, withTypeNames);
@@ -158,7 +180,7 @@ Object.keys(resolved).forEach(k => {
158
180
  }
159
181
 
160
182
  try {
161
- (0, _generateTypeFiles.generateTypeFiles)(raw.loc.path, schemaForTypeGeneration, withTypeNames, processedOptions); // eslint-disable-next-line flowtype-errors/uncovered
183
+ (0, _generateTypeFiles.generateTypeFiles)(raw.loc.path, schemaForTypeGeneration, withTypeNames, generateConfig); // eslint-disable-next-line flowtype-errors/uncovered
162
184
  } catch (err) {
163
185
  console.error(`Error while generating operation from ${raw.loc.path}`);
164
186
  console.error(printed); // eslint-disable-next-line flowtype-errors/uncovered
@@ -174,8 +196,8 @@ if (validationFailures) {
174
196
  process.exit(1);
175
197
  }
176
198
 
177
- if (config.dumpOperations) {
178
- const dumpOperations = config.dumpOperations;
199
+ if (config.crawl.dumpOperations) {
200
+ const dumpOperations = config.crawl.dumpOperations;
179
201
  const parent = (0, _path.dirname)(dumpOperations);
180
202
  (0, _fs.mkdirSync)(parent, {
181
203
  recursive: true