@khanacademy/graphql-flow 0.2.3 → 0.3.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 (40) hide show
  1. package/.flowconfig +1 -0
  2. package/.github/workflows/changeset-release.yml +3 -17
  3. package/.github/workflows/pr-checks.yml +15 -10
  4. package/CHANGELOG.md +19 -0
  5. package/Readme.md +36 -1
  6. package/dist/cli/config.js +102 -15
  7. package/dist/cli/config.js.flow +131 -29
  8. package/dist/cli/config.js.map +1 -1
  9. package/dist/cli/run.js +45 -11
  10. package/dist/cli/run.js.flow +45 -7
  11. package/dist/cli/run.js.map +1 -1
  12. package/dist/cli/utils.js +21 -0
  13. package/dist/cli/utils.js.flow +14 -0
  14. package/dist/cli/utils.js.map +1 -0
  15. package/dist/enums.js +15 -2
  16. package/dist/enums.js.flow +38 -9
  17. package/dist/enums.js.map +1 -1
  18. package/dist/generateTypeFiles.js +30 -7
  19. package/dist/generateTypeFiles.js.flow +68 -38
  20. package/dist/generateTypeFiles.js.map +1 -1
  21. package/dist/index.js +18 -12
  22. package/dist/index.js.flow +27 -11
  23. package/dist/index.js.map +1 -1
  24. package/dist/types.js.flow +3 -0
  25. package/flow-typed/npm/@babel/types_vx.x.x.js +17 -3
  26. package/package.json +1 -1
  27. package/src/__test__/generateTypeFileContents.test.js +52 -0
  28. package/src/cli/__test__/config.test.js +94 -0
  29. package/src/cli/__test__/utils.test.js +19 -0
  30. package/src/cli/config.js +131 -29
  31. package/src/cli/run.js +45 -7
  32. package/src/cli/utils.js +14 -0
  33. package/src/enums.js +38 -9
  34. package/src/generateTypeFiles.js +68 -38
  35. package/src/index.js +27 -11
  36. package/src/types.js +3 -0
  37. package/.github/actions/filter-files/action.yml +0 -37
  38. package/.github/actions/full-or-limited/action.yml +0 -27
  39. package/.github/actions/json-args/action.yml +0 -32
  40. package/.github/actions/setup/action.yml +0 -28
package/.flowconfig CHANGED
@@ -9,5 +9,6 @@ flow-typed/
9
9
  [lints]
10
10
 
11
11
  [options]
12
+ enums=true
12
13
 
13
14
  [strict]
@@ -34,7 +34,7 @@ jobs:
34
34
  - uses: actions/checkout@v2
35
35
  with:
36
36
  fetch-depth: 0
37
- - uses: ./.github/actions/setup
37
+ - uses: Khan/actions@shared-node-cache-v0.0.2
38
38
  with:
39
39
  node-version: 12.x
40
40
 
@@ -55,8 +55,8 @@ jobs:
55
55
  if: steps.changesets.outputs.published == 'true'
56
56
  uses: rtCamp/action-slack-notify@v2
57
57
  env:
58
- SLACK_WEBHOOK: ${{ secrets.SLACK_FEIWEB_WEBHOOK }}
59
- SLACK_CHANNEL: frontend-infra-web
58
+ SLACK_WEBHOOK: ${{ secrets.SLACK_FEI_FIREHOSE }}
59
+ SLACK_CHANNEL: fei-firehose
60
60
  SLACK_MSG_AUTHOR: ${{ github.event.pull_request.user.login }}
61
61
  SLACK_USERNAME: GithubGoose
62
62
  SLACK_ICON_EMOJI: ":goose:"
@@ -64,17 +64,3 @@ jobs:
64
64
  SLACK_TITLE: "New Graphql-Flow release!"
65
65
  SLACK_FOOTER: Graphql-Flow Slack Notification
66
66
  MSG_MINIMAL: true
67
-
68
- - name: Send a Slack notification for mobile if a publish happens
69
- if: steps.changesets.outputs.published == 'true'
70
- uses: rtCamp/action-slack-notify@v2
71
- env:
72
- SLACK_WEBHOOK: ${{ secrets.SLACK_FEIMOBILE_WEBHOOK }}
73
- SLACK_CHANNEL: frontend-infra-mobile
74
- SLACK_MSG_AUTHOR: ${{ github.event.pull_request.user.login }}
75
- SLACK_USERNAME: GithubGoose
76
- SLACK_ICON_EMOJI: ":goose:"
77
- SLACK_MESSAGE: "A new version of ${{ github.event.repository.name }} was published! 🎉 \nRelease notes → https://github.com/Khan/${{ github.event.repository.name }}/releases/"
78
- SLACK_TITLE: "New Graphql-Flow release!"
79
- SLACK_FOOTER: Graphql-Flow Slack Notification
80
- MSG_MINIMAL: true
@@ -17,16 +17,21 @@ jobs:
17
17
  node-version: [16.x]
18
18
  steps:
19
19
  - uses: actions/checkout@v2
20
- - uses: ./.github/actions/setup
21
- id: setup
20
+ - uses: Khan/actions@shared-node-cache-v0
22
21
  with:
23
22
  node-version: ${{ matrix.node-version }}
24
23
 
24
+ - name: Get All Changed Files
25
+ uses: jaredly/get-changed-files@absolute
26
+ id: changed
27
+ with:
28
+ format: 'json'
29
+
25
30
  - id: js-files
26
31
  name: Find .js changed files
27
- uses: ./.github/actions/filter-files
32
+ uses: Khan/actions@filter-files-v0
28
33
  with:
29
- changed-files: ${{ steps.setup.outputs.changed_files }}
34
+ changed-files: ${{ steps.changed.outputs.added_modified }}
30
35
  extensions: '.js'
31
36
 
32
37
  - name: Run Flow
@@ -34,14 +39,14 @@ jobs:
34
39
  run: yarn flow
35
40
 
36
41
  - id: eslint-reset
37
- uses: ./.github/actions/filter-files
42
+ uses: Khan/actions@filter-files-v0
38
43
  name: Files that would trigger a full eslint run
39
44
  with:
40
- changed-files: ${{ steps.setup.outputs.changed_files }}
45
+ changed-files: ${{ steps.changed.outputs.added_modified }}
41
46
  files: '.eslintrc.js,package.json,.eslintignore'
42
47
 
43
48
  - name: Eslint
44
- uses: ./.github/actions/full-or-limited
49
+ uses: Khan/actions@full-or-limited-v0
45
50
  with:
46
51
  full-trigger: ${{ steps.eslint-reset.outputs.filtered }}
47
52
  full: yarn eslint
@@ -49,14 +54,14 @@ jobs:
49
54
  limited: yarn eslint {}
50
55
 
51
56
  - id: jest-reset
52
- uses: ./.github/actions/filter-files
57
+ uses: Khan/actions@filter-files-v0
53
58
  name: Files that would trigger a full jest run
54
59
  with:
55
- changed-files: ${{ steps.setup.outputs.changed_files }}
60
+ changed-files: ${{ steps.changed.outputs.added_modified }}
56
61
  files: 'package.json,package-lock.json'
57
62
 
58
63
  - name: Jest
59
- uses: ./.github/actions/full-or-limited
64
+ uses: Khan/actions@full-or-limited-v0
60
65
  with:
61
66
  full-trigger: ${{ steps.jest-reset.outputs.filtered }}
62
67
  full: yarn jest
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @khanacademy/graphql-flow
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 093fa5f: Enable `experimentalEnums` in a config or subconfig file in to enable the export of flow enum types, which replace the default string union literals. The type currently comes with eslint decorators to skirt a bug in eslint and flow.
8
+ - 5078624: Users can add files with the name ending in `graphql-flow.config.js` with a subset of the config fields (`options`, `excludes`) in order to have more granular control of the behavior. Another field, `extends`, takes the path of another config file to use as a base and extends/overrides fields. If no `extends` is provided, the file completely overwrites any other config files (as far as `options` and `excludes`).
9
+
10
+ ## 0.2.5
11
+
12
+ ### Patch Changes
13
+
14
+ - 0df32ac: Allow generatedDir to be an absolute path
15
+
16
+ ## 0.2.4
17
+
18
+ ### Patch Changes
19
+
20
+ - 843839c: Add 'dumpOperations' config option, to enable safelisting
21
+
3
22
  ## 0.2.3
4
23
 
5
24
  ### Patch Changes
package/Readme.md CHANGED
@@ -23,6 +23,26 @@ Write a config file, with the following options:
23
23
  }
24
24
  ```
25
25
 
26
+ Optionally add subconfig files to subdirectories for granular control of behavior, with the following options:
27
+
28
+ ```json
29
+ {
30
+ // Note that this file must be named, or end with, "graphql-flow.config.json"
31
+ // I.e., "my-service.graphql.config.json" would also work.
32
+ // These files will affect the directory in which they are located and all subdirectories, unless overridden by a deeper subconfig.
33
+
34
+ // Optionally add the path of another config file. Can be the root config (provided when running the script) or any other subconfig to merge options.
35
+ // If a chain of extends are provided, will resolve in order. Be sure not to extend in a circle-- currently, this will just cause a stack overflow error.
36
+ // Cannot currently override `schemaFilePath`.
37
+ "extends": "./another/config/from/root.config.json",
38
+ // Can extend or override `excludes` and `options`.
39
+ "excludes": ["\\bsome-thing", "_test.jsx?$"],
40
+ "options": {
41
+ ...
42
+ }
43
+ }
44
+ ```
45
+
26
46
  Then run from the CLI, like so:
27
47
 
28
48
  ```bash
@@ -42,7 +62,17 @@ type Options = {
42
62
  readOnlyArray: boolean = true,
43
63
  scalars: {[key: string]: 'string' | 'boolean' | 'number'}
44
64
 
45
- // Specify the name of the generated types directory
65
+ // Specify the name of the generated types directory. If this
66
+ // is a relative path, then this is used to suffix the output
67
+ // directory; if it's an absolute path it's used to prefix the
68
+ // output directory. For instance, if a gql directive is
69
+ // found in /foo/bar/baz/query.js and you run the cli (or
70
+ // jest) from directory /foo, then:
71
+ // * if `generatedDirectory` is "__generated__", the output will
72
+ // be in /foo/bar/baz/__generated__/index.js and sibling files
73
+ // * if `generatedDirectory` is "/tmp/__generated__", the output
74
+ // will be in /tmp/__generated__/bar/baz/index.js and sibling
75
+ // files.
46
76
  generatedDirectory: string = '__generated__',
47
77
 
48
78
  // The default generated type contains both the types of the response
@@ -74,6 +104,11 @@ type Options = {
74
104
  // A template for the name of generated files
75
105
  // default: [operationName].js
76
106
  typeFileName?: string,
107
+
108
+ // Generate flow enums to replace literal unions in generated types. Exports
109
+ // each set of enums from each file regardless of other options. Designated
110
+ // "experimental" because of bug in eslint that requires config comments.
111
+ experimentalEnums?: boolean,
77
112
  }
78
113
  ```
79
114
 
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.loadConfigFile = exports.getSchemas = void 0;
6
+ exports.loadSubConfigFile = exports.loadDirConfigFiles = exports.loadConfigFile = exports.getSchemas = void 0;
7
7
 
8
8
  var _schemaFromIntrospectionData = require("../schemaFromIntrospectionData");
9
9
 
@@ -16,38 +16,114 @@ var _path = _interopRequireDefault(require("path"));
16
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
17
 
18
18
  const loadConfigFile = configFile => {
19
- var _data$options, _data$excludes$map, _data$excludes;
19
+ var _data$options, _data$options2, _data$excludes$map, _data$excludes;
20
20
 
21
21
  // eslint-disable-next-line flowtype-errors/uncovered
22
22
  const data = JSON.parse(_fs.default.readFileSync(configFile, 'utf8'));
23
- const toplevelKeys = ['excludes', 'schemaFilePath', 'options'];
23
+ const toplevelKeys = ['excludes', 'schemaFilePath', 'options', 'dumpOperations'];
24
24
  Object.keys(data).forEach(k => {
25
25
  if (!toplevelKeys.includes(k)) {
26
26
  throw new Error(`Invalid attribute in config file ${configFile}: ${k}. Allowed attributes: ${toplevelKeys.join(', ')}`);
27
27
  }
28
28
  });
29
+ validateOptions(configFile, (_data$options = data.options) !== null && _data$options !== void 0 ? _data$options : {});
30
+ return {
31
+ options: (_data$options2 = data.options) !== null && _data$options2 !== void 0 ? _data$options2 : {},
32
+ 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 : [],
33
+ schemaFilePath: _path.default.isAbsolute(data.schemaFilePath) ? data.schemaFilePath : _path.default.join(_path.default.dirname(configFile), data.schemaFilePath),
34
+ dumpOperations: data.dumpOperations
35
+ };
36
+ };
37
+ /**
38
+ * Subdirectory config to extend or overwrite higher-level config.
39
+ * @param {string} extends - Path from root; optional field for a config file in a subdirectory. If left blank, config file will overwrite root for directory.
40
+ */
29
41
 
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
- });
37
- }
38
42
 
43
+ exports.loadConfigFile = loadConfigFile;
44
+
45
+ const loadSubConfigFile = configFile => {
46
+ var _data$options3, _data$excludes$map2, _data$excludes2, _data$options4, _data$extends;
47
+
48
+ const jsonData = _fs.default.readFileSync(configFile, 'utf8'); // eslint-disable-next-line flowtype-errors/uncovered
49
+
50
+
51
+ const data = JSON.parse(jsonData);
52
+ const toplevelKeys = ['excludes', 'options', 'extends'];
53
+ Object.keys(data).forEach(k => {
54
+ if (!toplevelKeys.includes(k)) {
55
+ throw new Error(`Invalid attribute in non-root config file ${configFile}: ${k}. Allowed attributes: ${toplevelKeys.join(', ')}`);
56
+ }
57
+ });
58
+ validateOptions(configFile, (_data$options3 = data.options) !== null && _data$options3 !== void 0 ? _data$options3 : {});
39
59
  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.join(_path.default.dirname(configFile), data.schemaFilePath)
60
+ excludes: (_data$excludes$map2 = (_data$excludes2 = data.excludes) === null || _data$excludes2 === void 0 ? void 0 : _data$excludes2.map(string => new RegExp(string))) !== null && _data$excludes$map2 !== void 0 ? _data$excludes$map2 : [],
61
+ options: (_data$options4 = data.options) !== null && _data$options4 !== void 0 ? _data$options4 : {},
62
+ extends: (_data$extends = data.extends) !== null && _data$extends !== void 0 ? _data$extends : ''
63
+ };
64
+ };
65
+
66
+ exports.loadSubConfigFile = loadSubConfigFile;
67
+
68
+ const loadDirConfigFiles = (filesResponse, rootConfig) => {
69
+ const dirConfigMap = {}; // TODO: circular extends will cause infinite loop... consider instrumenting code to monitor for loops in the future?
70
+
71
+ const loadExtendedConfig = configPath => {
72
+ let dirConfig = loadSubConfigFile(configPath);
73
+
74
+ if (dirConfig.extends) {
75
+ const isRootConfig = dirConfig.extends === rootConfig.path;
76
+ const {
77
+ options,
78
+ excludes
79
+ } = isRootConfig ? rootConfig.config : addConfig(dirConfig.extends);
80
+ dirConfig = extendConfig({
81
+ options,
82
+ excludes
83
+ }, dirConfig);
84
+ }
85
+
86
+ return dirConfig;
87
+ };
88
+
89
+ const addConfig = configPath => {
90
+ const {
91
+ dir
92
+ } = _path.default.parse(configPath);
93
+
94
+ if (dirConfigMap[dir]) {
95
+ return dirConfigMap[dir];
96
+ }
97
+
98
+ dirConfigMap[dir] = loadExtendedConfig(configPath);
99
+ return dirConfigMap[dir];
43
100
  };
101
+
102
+ const extendConfig = (toExtend, current) => ({
103
+ // $FlowFixMe[exponential-spread]
104
+ options: { ...toExtend.options,
105
+ ...current.options
106
+ },
107
+ excludes: Array.from(new Set([...toExtend.excludes, ...current.excludes]))
108
+ });
109
+
110
+ filesResponse.trim().split('\n').forEach(configPath => {
111
+ const {
112
+ dir
113
+ } = _path.default.parse(configPath);
114
+
115
+ if (dir && !dirConfigMap[dir]) {
116
+ dirConfigMap[dir] = loadExtendedConfig(configPath);
117
+ }
118
+ });
119
+ return dirConfigMap;
44
120
  };
45
121
  /**
46
122
  * Loads a .json 'introspection query response', or a .graphql schema definition.
47
123
  */
48
124
 
49
125
 
50
- exports.loadConfigFile = loadConfigFile;
126
+ exports.loadDirConfigFiles = loadDirConfigFiles;
51
127
 
52
128
  const getSchemas = schemaFilePath => {
53
129
  const raw = _fs.default.readFileSync(schemaFilePath, 'utf8');
@@ -70,4 +146,15 @@ const getSchemas = schemaFilePath => {
70
146
  };
71
147
 
72
148
  exports.getSchemas = getSchemas;
149
+
150
+ const validateOptions = (configFile, options) => {
151
+ if (options) {
152
+ const externalOptionsKeys = ['pragma', 'loosePragma', 'ignorePragma', 'scalars', 'strictNullability', 'regenerateCommand', 'readOnlyArray', 'splitTypes', 'generatedDirectory', 'exportAllObjectTypes', 'typeFileName', 'experimentalEnums'];
153
+ Object.keys(options).forEach(k => {
154
+ if (!externalOptionsKeys.includes(k)) {
155
+ throw new Error(`Invalid option in config file ${configFile}: ${k}. Allowed options: ${externalOptionsKeys.join(', ')}`);
156
+ }
157
+ });
158
+ }
159
+ };
73
160
  //# sourceMappingURL=config.js.map
@@ -18,6 +18,7 @@ import path from 'path';
18
18
  export type CliConfig = {
19
19
  excludes: Array<RegExp>,
20
20
  schemaFilePath: string,
21
+ dumpOperations?: string,
21
22
  options: ExternalOptions,
22
23
  };
23
24
 
@@ -29,12 +30,18 @@ type JSONConfig = {
29
30
  excludes?: Array<string>,
30
31
  schemaFilePath: string,
31
32
  options?: ExternalOptions,
33
+ dumpOperations?: string,
32
34
  };
33
35
 
34
36
  export const loadConfigFile = (configFile: string): CliConfig => {
35
37
  // eslint-disable-next-line flowtype-errors/uncovered
36
38
  const data: JSONConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));
37
- const toplevelKeys = ['excludes', 'schemaFilePath', 'options'];
39
+ const toplevelKeys = [
40
+ 'excludes',
41
+ 'schemaFilePath',
42
+ 'options',
43
+ 'dumpOperations',
44
+ ];
38
45
  Object.keys(data).forEach((k) => {
39
46
  if (!toplevelKeys.includes(k)) {
40
47
  throw new Error(
@@ -44,40 +51,104 @@ export const loadConfigFile = (configFile: string): CliConfig => {
44
51
  );
45
52
  }
46
53
  });
47
- if (data.options) {
48
- const externalOptionsKeys = [
49
- 'pragma',
50
- 'loosePragma',
51
- 'ignorePragma',
52
- 'scalars',
53
- 'strictNullability',
54
- 'regenerateCommand',
55
- 'readOnlyArray',
56
- 'splitTypes',
57
- 'generatedDirectory',
58
- 'exportAllObjectTypes',
59
- 'typeFileName',
60
- ];
61
- Object.keys(data.options).forEach((k) => {
62
- if (!externalOptionsKeys.includes(k)) {
63
- throw new Error(
64
- `Invalid option in config file ${configFile}: ${k}. Allowed options: ${externalOptionsKeys.join(
65
- ', ',
66
- )}`,
67
- );
68
- }
69
- });
70
- }
54
+ validateOptions(configFile, data.options ?? {});
71
55
  return {
72
56
  options: data.options ?? {},
73
57
  excludes: data.excludes?.map((string) => new RegExp(string)) ?? [],
74
- schemaFilePath: path.join(
75
- path.dirname(configFile),
76
- data.schemaFilePath,
77
- ),
58
+ schemaFilePath: path.isAbsolute(data.schemaFilePath)
59
+ ? data.schemaFilePath
60
+ : path.join(path.dirname(configFile), data.schemaFilePath),
61
+ dumpOperations: data.dumpOperations,
78
62
  };
79
63
  };
80
64
 
65
+ /**
66
+ * Subdirectory config to extend or overwrite higher-level config.
67
+ * @param {string} extends - Path from root; optional field for a config file in a subdirectory. If left blank, config file will overwrite root for directory.
68
+ */
69
+ type JSONSubConfig = {
70
+ excludes?: Array<string>,
71
+ options?: ExternalOptions,
72
+ extends?: string,
73
+ };
74
+
75
+ type SubConfig = {
76
+ excludes: Array<RegExp>,
77
+ options: ExternalOptions,
78
+ extends?: string,
79
+ };
80
+
81
+ export const loadSubConfigFile = (configFile: string): SubConfig => {
82
+ const jsonData = fs.readFileSync(configFile, 'utf8');
83
+ // eslint-disable-next-line flowtype-errors/uncovered
84
+ const data: JSONSubConfig = JSON.parse(jsonData);
85
+ const toplevelKeys = ['excludes', 'options', 'extends'];
86
+ Object.keys(data).forEach((k) => {
87
+ if (!toplevelKeys.includes(k)) {
88
+ throw new Error(
89
+ `Invalid attribute in non-root config file ${configFile}: ${k}. Allowed attributes: ${toplevelKeys.join(
90
+ ', ',
91
+ )}`,
92
+ );
93
+ }
94
+ });
95
+ validateOptions(configFile, data.options ?? {});
96
+ return {
97
+ excludes: data.excludes?.map((string) => new RegExp(string)) ?? [],
98
+ options: data.options ?? {},
99
+ extends: data.extends ?? '',
100
+ };
101
+ };
102
+
103
+ export const loadDirConfigFiles = (
104
+ filesResponse: string,
105
+ rootConfig: {path: string, config: CliConfig},
106
+ ): {[dir: string]: SubConfig} => {
107
+ const dirConfigMap: {[key: string]: SubConfig} = {};
108
+
109
+ // TODO: circular extends will cause infinite loop... consider instrumenting code to monitor for loops in the future?
110
+ const loadExtendedConfig = (configPath: string): SubConfig => {
111
+ let dirConfig = loadSubConfigFile(configPath);
112
+ if (dirConfig.extends) {
113
+ const isRootConfig = dirConfig.extends === rootConfig.path;
114
+ const {options, excludes} = isRootConfig
115
+ ? rootConfig.config
116
+ : addConfig(dirConfig.extends);
117
+ dirConfig = extendConfig({options, excludes}, dirConfig);
118
+ }
119
+ return dirConfig;
120
+ };
121
+ const addConfig = (configPath) => {
122
+ const {dir} = path.parse(configPath);
123
+ if (dirConfigMap[dir]) {
124
+ return dirConfigMap[dir];
125
+ }
126
+ dirConfigMap[dir] = loadExtendedConfig(configPath);
127
+ return dirConfigMap[dir];
128
+ };
129
+ const extendConfig = (
130
+ toExtend: SubConfig,
131
+ current: SubConfig,
132
+ ): SubConfig => ({
133
+ // $FlowFixMe[exponential-spread]
134
+ options: {...toExtend.options, ...current.options},
135
+ excludes: Array.from(
136
+ new Set([...toExtend.excludes, ...current.excludes]),
137
+ ),
138
+ });
139
+
140
+ filesResponse
141
+ .trim()
142
+ .split('\n')
143
+ .forEach((configPath) => {
144
+ const {dir} = path.parse(configPath);
145
+ if (dir && !dirConfigMap[dir]) {
146
+ dirConfigMap[dir] = loadExtendedConfig(configPath);
147
+ }
148
+ });
149
+ return dirConfigMap;
150
+ };
151
+
81
152
  /**
82
153
  * Loads a .json 'introspection query response', or a .graphql schema definition.
83
154
  */
@@ -103,3 +174,34 @@ export const getSchemas = (schemaFilePath: string): [GraphQLSchema, Schema] => {
103
174
  return [schemaForValidation, schemaForTypeGeneration];
104
175
  }
105
176
  };
177
+
178
+ const validateOptions = (
179
+ configFile: string,
180
+ options: ExternalOptions,
181
+ ): void => {
182
+ if (options) {
183
+ const externalOptionsKeys = [
184
+ 'pragma',
185
+ 'loosePragma',
186
+ 'ignorePragma',
187
+ 'scalars',
188
+ 'strictNullability',
189
+ 'regenerateCommand',
190
+ 'readOnlyArray',
191
+ 'splitTypes',
192
+ 'generatedDirectory',
193
+ 'exportAllObjectTypes',
194
+ 'typeFileName',
195
+ 'experimentalEnums',
196
+ ];
197
+ Object.keys(options).forEach((k) => {
198
+ if (!externalOptionsKeys.includes(k)) {
199
+ throw new Error(
200
+ `Invalid option in config file ${configFile}: ${k}. Allowed options: ${externalOptionsKeys.join(
201
+ ', ',
202
+ )}`,
203
+ );
204
+ }
205
+ });
206
+ }
207
+ };
@@ -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","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,EAQxB,YARwB,EASxB,oBATwB,EAUxB,sBAVwB,EAWxB,cAXwB,CAA5B;AAaAR,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,mBAAEb,IAAI,CAACa,OAAP,yDAAkB,EADtB;AAEHE,IAAAA,QAAQ,0CAAEf,IAAI,CAACe,QAAP,mDAAE,eAAeC,GAAf,CAAoBC,MAAD,IAAY,IAAIC,MAAJ,CAAWD,MAAX,CAA/B,CAAF,mEAAwD,EAF7D;AAGHE,IAAAA,cAAc,EAAEC,cAAKR,IAAL,CACZQ,cAAKC,OAAL,CAAatB,UAAb,CADY,EAEZC,IAAI,CAACmB,cAFO;AAHb,GAAP;AAQH,CA7CM;AA+CP;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 '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.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"}
1
+ {"version":3,"sources":["../../src/cli/config.js"],"names":["loadConfigFile","configFile","data","JSON","parse","fs","readFileSync","toplevelKeys","Object","keys","forEach","k","includes","Error","join","validateOptions","options","excludes","map","string","RegExp","schemaFilePath","path","isAbsolute","dirname","dumpOperations","loadSubConfigFile","jsonData","extends","loadDirConfigFiles","filesResponse","rootConfig","dirConfigMap","loadExtendedConfig","configPath","dirConfig","isRootConfig","config","addConfig","extendConfig","dir","toExtend","current","Array","from","Set","trim","split","getSchemas","raw","endsWith","schemaForValidation","queryResponse","descriptions","schemaForTypeGeneration","introspectionData","externalOptionsKeys"],"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 validateOptions(configFile, data.options ?? {});\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 * Subdirectory config to extend or overwrite higher-level config.\n * @param {string} extends - Path from root; optional field for a config file in a subdirectory. If left blank, config file will overwrite root for directory.\n */\ntype JSONSubConfig = {\n excludes?: Array<string>,\n options?: ExternalOptions,\n extends?: string,\n};\n\ntype SubConfig = {\n excludes: Array<RegExp>,\n options: ExternalOptions,\n extends?: string,\n};\n\nexport const loadSubConfigFile = (configFile: string): SubConfig => {\n const jsonData = fs.readFileSync(configFile, 'utf8');\n // eslint-disable-next-line flowtype-errors/uncovered\n const data: JSONSubConfig = JSON.parse(jsonData);\n const toplevelKeys = ['excludes', 'options', 'extends'];\n Object.keys(data).forEach((k) => {\n if (!toplevelKeys.includes(k)) {\n throw new Error(\n `Invalid attribute in non-root config file ${configFile}: ${k}. Allowed attributes: ${toplevelKeys.join(\n ', ',\n )}`,\n );\n }\n });\n validateOptions(configFile, data.options ?? {});\n return {\n excludes: data.excludes?.map((string) => new RegExp(string)) ?? [],\n options: data.options ?? {},\n extends: data.extends ?? '',\n };\n};\n\nexport const loadDirConfigFiles = (\n filesResponse: string,\n rootConfig: {path: string, config: CliConfig},\n): {[dir: string]: SubConfig} => {\n const dirConfigMap: {[key: string]: SubConfig} = {};\n\n // TODO: circular extends will cause infinite loop... consider instrumenting code to monitor for loops in the future?\n const loadExtendedConfig = (configPath: string): SubConfig => {\n let dirConfig = loadSubConfigFile(configPath);\n if (dirConfig.extends) {\n const isRootConfig = dirConfig.extends === rootConfig.path;\n const {options, excludes} = isRootConfig\n ? rootConfig.config\n : addConfig(dirConfig.extends);\n dirConfig = extendConfig({options, excludes}, dirConfig);\n }\n return dirConfig;\n };\n const addConfig = (configPath) => {\n const {dir} = path.parse(configPath);\n if (dirConfigMap[dir]) {\n return dirConfigMap[dir];\n }\n dirConfigMap[dir] = loadExtendedConfig(configPath);\n return dirConfigMap[dir];\n };\n const extendConfig = (\n toExtend: SubConfig,\n current: SubConfig,\n ): SubConfig => ({\n // $FlowFixMe[exponential-spread]\n options: {...toExtend.options, ...current.options},\n excludes: Array.from(\n new Set([...toExtend.excludes, ...current.excludes]),\n ),\n });\n\n filesResponse\n .trim()\n .split('\\n')\n .forEach((configPath) => {\n const {dir} = path.parse(configPath);\n if (dir && !dirConfigMap[dir]) {\n dirConfigMap[dir] = loadExtendedConfig(configPath);\n }\n });\n return dirConfigMap;\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\nconst validateOptions = (\n configFile: string,\n options: ExternalOptions,\n): void => {\n if (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 'experimentalEnums',\n ];\n Object.keys(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};\n"],"file":"config.js"}