@graphql-eslint/eslint-plugin 3.14.0-alpha-20221222211346-788e7eb → 3.14.0-alpha-20221223011223-bd3e820

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. package/cjs/configs/index.js +10 -10
  2. package/cjs/documents.js +5 -5
  3. package/cjs/estree-converter/converter.js +2 -2
  4. package/cjs/estree-converter/index.js +3 -3
  5. package/cjs/estree-converter/utils.js +2 -2
  6. package/cjs/flat-configs.js +36 -0
  7. package/cjs/index.js +16 -14
  8. package/cjs/parser.js +13 -13
  9. package/cjs/processor.js +2 -2
  10. package/cjs/rules/alphabetize.js +7 -7
  11. package/cjs/rules/graphql-js-validation.js +9 -9
  12. package/cjs/rules/index.js +66 -66
  13. package/cjs/rules/lone-executable-definition.js +4 -4
  14. package/cjs/rules/match-document-filename.js +4 -4
  15. package/cjs/rules/naming-convention.js +6 -6
  16. package/cjs/rules/no-anonymous-operations.js +2 -2
  17. package/cjs/rules/no-deprecated.js +2 -2
  18. package/cjs/rules/no-one-place-fragments.js +3 -4
  19. package/cjs/rules/no-root-type.js +3 -3
  20. package/cjs/rules/no-scalar-result-type-on-mutation.js +2 -2
  21. package/cjs/rules/no-unreachable-types.js +3 -3
  22. package/cjs/rules/no-unused-fields.js +3 -3
  23. package/cjs/rules/relay-arguments.js +2 -2
  24. package/cjs/rules/relay-edge-types.js +6 -6
  25. package/cjs/rules/relay-page-info.js +5 -5
  26. package/cjs/rules/require-deprecation-date.js +2 -2
  27. package/cjs/rules/require-deprecation-reason.js +2 -2
  28. package/cjs/rules/require-description.js +8 -8
  29. package/cjs/rules/require-field-of-type-query-in-mutation-result.js +3 -3
  30. package/cjs/rules/require-id-when-available.js +8 -8
  31. package/cjs/rules/selection-set-depth.js +5 -5
  32. package/cjs/rules/strict-id-in-types.js +7 -7
  33. package/cjs/rules/unique-fragment-name.js +4 -4
  34. package/cjs/rules/unique-operation-name.js +2 -2
  35. package/cjs/schema.js +2 -2
  36. package/esm/cache.js +25 -0
  37. package/esm/configs/base.js +4 -0
  38. package/esm/configs/index.js +12 -0
  39. package/esm/configs/operations-all.js +29 -0
  40. package/esm/configs/operations-recommended.js +53 -0
  41. package/esm/configs/relay.js +9 -0
  42. package/esm/configs/schema-all.js +22 -0
  43. package/esm/configs/schema-recommended.js +49 -0
  44. package/esm/documents.js +144 -0
  45. package/esm/estree-converter/converter.js +58 -0
  46. package/esm/estree-converter/index.js +3 -0
  47. package/esm/estree-converter/types.js +1 -0
  48. package/esm/estree-converter/utils.js +102 -0
  49. package/esm/flat-configs.js +33 -0
  50. package/esm/graphql-config.js +49 -0
  51. package/esm/index.js +9 -0
  52. package/esm/package.json +1 -0
  53. package/esm/parser.js +56 -0
  54. package/esm/processor.js +75 -0
  55. package/esm/rules/alphabetize.js +344 -0
  56. package/esm/rules/description-style.js +75 -0
  57. package/esm/rules/graphql-js-validation.js +498 -0
  58. package/esm/rules/index.js +71 -0
  59. package/esm/rules/input-name.js +133 -0
  60. package/esm/rules/lone-executable-definition.js +85 -0
  61. package/esm/rules/match-document-filename.js +232 -0
  62. package/esm/rules/naming-convention.js +307 -0
  63. package/esm/rules/no-anonymous-operations.js +64 -0
  64. package/esm/rules/no-case-insensitive-enum-values-duplicates.js +58 -0
  65. package/esm/rules/no-deprecated.js +121 -0
  66. package/esm/rules/no-duplicate-fields.js +109 -0
  67. package/esm/rules/no-hashtag-description.js +86 -0
  68. package/esm/rules/no-one-place-fragments.js +80 -0
  69. package/esm/rules/no-root-type.js +83 -0
  70. package/esm/rules/no-scalar-result-type-on-mutation.js +63 -0
  71. package/esm/rules/no-typename-prefix.js +62 -0
  72. package/esm/rules/no-unreachable-types.js +154 -0
  73. package/esm/rules/no-unused-fields.js +127 -0
  74. package/esm/rules/relay-arguments.js +118 -0
  75. package/esm/rules/relay-connection-types.js +104 -0
  76. package/esm/rules/relay-edge-types.js +186 -0
  77. package/esm/rules/relay-page-info.js +97 -0
  78. package/esm/rules/require-deprecation-date.js +120 -0
  79. package/esm/rules/require-deprecation-reason.js +53 -0
  80. package/esm/rules/require-description.js +190 -0
  81. package/esm/rules/require-field-of-type-query-in-mutation-result.js +69 -0
  82. package/esm/rules/require-id-when-available.js +196 -0
  83. package/esm/rules/require-nullable-fields-with-oneof.js +58 -0
  84. package/esm/rules/require-type-pattern-with-oneof.js +57 -0
  85. package/esm/rules/selection-set-depth.js +131 -0
  86. package/esm/rules/strict-id-in-types.js +159 -0
  87. package/esm/rules/unique-fragment-name.js +86 -0
  88. package/esm/rules/unique-operation-name.js +62 -0
  89. package/esm/schema.js +37 -0
  90. package/esm/testkit.js +181 -0
  91. package/esm/types.js +1 -0
  92. package/esm/utils.js +83 -0
  93. package/package.json +10 -1
  94. package/typings/estree-converter/converter.d.cts +1 -1
  95. package/typings/estree-converter/converter.d.ts +1 -1
  96. package/typings/estree-converter/index.d.cts +3 -3
  97. package/typings/estree-converter/index.d.ts +3 -3
  98. package/typings/estree-converter/types.d.cts +3 -3
  99. package/typings/estree-converter/types.d.ts +3 -3
  100. package/typings/estree-converter/utils.d.cts +2 -2
  101. package/typings/estree-converter/utils.d.ts +2 -2
  102. package/typings/flat-configs.d.cts +248 -0
  103. package/typings/flat-configs.d.ts +248 -0
  104. package/typings/graphql-config.d.cts +1 -1
  105. package/typings/graphql-config.d.ts +1 -1
  106. package/typings/index.d.cts +8 -7
  107. package/typings/index.d.ts +8 -7
  108. package/typings/parser.d.cts +1 -1
  109. package/typings/parser.d.ts +1 -1
  110. package/typings/rules/alphabetize.d.cts +1 -1
  111. package/typings/rules/alphabetize.d.ts +1 -1
  112. package/typings/rules/description-style.d.cts +1 -1
  113. package/typings/rules/description-style.d.ts +1 -1
  114. package/typings/rules/graphql-js-validation.d.cts +1 -1
  115. package/typings/rules/graphql-js-validation.d.ts +1 -1
  116. package/typings/rules/index.d.cts +45 -45
  117. package/typings/rules/index.d.ts +45 -45
  118. package/typings/rules/input-name.d.cts +1 -1
  119. package/typings/rules/input-name.d.ts +1 -1
  120. package/typings/rules/lone-executable-definition.d.cts +1 -1
  121. package/typings/rules/lone-executable-definition.d.ts +1 -1
  122. package/typings/rules/match-document-filename.d.cts +2 -2
  123. package/typings/rules/match-document-filename.d.ts +2 -2
  124. package/typings/rules/naming-convention.d.cts +1 -1
  125. package/typings/rules/naming-convention.d.ts +1 -1
  126. package/typings/rules/no-anonymous-operations.d.cts +1 -1
  127. package/typings/rules/no-anonymous-operations.d.ts +1 -1
  128. package/typings/rules/no-case-insensitive-enum-values-duplicates.d.cts +1 -1
  129. package/typings/rules/no-case-insensitive-enum-values-duplicates.d.ts +1 -1
  130. package/typings/rules/no-deprecated.d.cts +1 -1
  131. package/typings/rules/no-deprecated.d.ts +1 -1
  132. package/typings/rules/no-duplicate-fields.d.cts +1 -1
  133. package/typings/rules/no-duplicate-fields.d.ts +1 -1
  134. package/typings/rules/no-hashtag-description.d.cts +1 -1
  135. package/typings/rules/no-hashtag-description.d.ts +1 -1
  136. package/typings/rules/no-one-place-fragments.d.cts +1 -1
  137. package/typings/rules/no-one-place-fragments.d.ts +1 -1
  138. package/typings/rules/no-root-type.d.cts +1 -1
  139. package/typings/rules/no-root-type.d.ts +1 -1
  140. package/typings/rules/no-scalar-result-type-on-mutation.d.cts +1 -1
  141. package/typings/rules/no-scalar-result-type-on-mutation.d.ts +1 -1
  142. package/typings/rules/no-typename-prefix.d.cts +1 -1
  143. package/typings/rules/no-typename-prefix.d.ts +1 -1
  144. package/typings/rules/no-unreachable-types.d.cts +1 -1
  145. package/typings/rules/no-unreachable-types.d.ts +1 -1
  146. package/typings/rules/no-unused-fields.d.cts +1 -1
  147. package/typings/rules/no-unused-fields.d.ts +1 -1
  148. package/typings/rules/relay-arguments.d.cts +1 -1
  149. package/typings/rules/relay-arguments.d.ts +1 -1
  150. package/typings/rules/relay-connection-types.d.cts +1 -1
  151. package/typings/rules/relay-connection-types.d.ts +1 -1
  152. package/typings/rules/relay-edge-types.d.cts +1 -1
  153. package/typings/rules/relay-edge-types.d.ts +1 -1
  154. package/typings/rules/relay-page-info.d.cts +1 -1
  155. package/typings/rules/relay-page-info.d.ts +1 -1
  156. package/typings/rules/require-deprecation-date.d.cts +1 -1
  157. package/typings/rules/require-deprecation-date.d.ts +1 -1
  158. package/typings/rules/require-deprecation-reason.d.cts +1 -1
  159. package/typings/rules/require-deprecation-reason.d.ts +1 -1
  160. package/typings/rules/require-description.d.cts +1 -1
  161. package/typings/rules/require-description.d.ts +1 -1
  162. package/typings/rules/require-field-of-type-query-in-mutation-result.d.cts +1 -1
  163. package/typings/rules/require-field-of-type-query-in-mutation-result.d.ts +1 -1
  164. package/typings/rules/require-id-when-available.d.cts +1 -1
  165. package/typings/rules/require-id-when-available.d.ts +1 -1
  166. package/typings/rules/require-nullable-fields-with-oneof.d.cts +1 -1
  167. package/typings/rules/require-nullable-fields-with-oneof.d.ts +1 -1
  168. package/typings/rules/require-type-pattern-with-oneof.d.cts +1 -1
  169. package/typings/rules/require-type-pattern-with-oneof.d.ts +1 -1
  170. package/typings/rules/selection-set-depth.d.cts +1 -1
  171. package/typings/rules/selection-set-depth.d.ts +1 -1
  172. package/typings/rules/strict-id-in-types.d.cts +1 -1
  173. package/typings/rules/strict-id-in-types.d.ts +1 -1
  174. package/typings/rules/unique-fragment-name.d.cts +2 -2
  175. package/typings/rules/unique-fragment-name.d.ts +2 -2
  176. package/typings/rules/unique-operation-name.d.cts +1 -1
  177. package/typings/rules/unique-operation-name.d.ts +1 -1
  178. package/typings/schema.d.cts +1 -1
  179. package/typings/schema.d.ts +1 -1
  180. package/typings/testkit.d.cts +3 -3
  181. package/typings/testkit.d.ts +3 -3
  182. package/typings/types.d.cts +2 -2
  183. package/typings/types.d.ts +2 -2
  184. package/typings/utils.d.cts +2 -2
  185. package/typings/utils.d.ts +2 -2
@@ -0,0 +1,29 @@
1
+ /*
2
+ * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
3
+ */
4
+ export default {
5
+ extends: ['./configs/base', './configs/operations-recommended'],
6
+ rules: {
7
+ '@graphql-eslint/alphabetize': [
8
+ 'error',
9
+ {
10
+ selections: ['OperationDefinition', 'FragmentDefinition'],
11
+ variables: ['OperationDefinition'],
12
+ arguments: ['Field', 'Directive'],
13
+ },
14
+ ],
15
+ '@graphql-eslint/lone-executable-definition': 'error',
16
+ '@graphql-eslint/match-document-filename': [
17
+ 'error',
18
+ {
19
+ query: 'kebab-case',
20
+ mutation: 'kebab-case',
21
+ subscription: 'kebab-case',
22
+ fragment: 'kebab-case',
23
+ },
24
+ ],
25
+ '@graphql-eslint/no-one-place-fragments': 'error',
26
+ '@graphql-eslint/unique-fragment-name': 'error',
27
+ '@graphql-eslint/unique-operation-name': 'error',
28
+ },
29
+ };
@@ -0,0 +1,53 @@
1
+ /*
2
+ * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
3
+ */
4
+ export default {
5
+ extends: './configs/base',
6
+ rules: {
7
+ '@graphql-eslint/executable-definitions': 'error',
8
+ '@graphql-eslint/fields-on-correct-type': 'error',
9
+ '@graphql-eslint/fragments-on-composite-type': 'error',
10
+ '@graphql-eslint/known-argument-names': 'error',
11
+ '@graphql-eslint/known-directives': 'error',
12
+ '@graphql-eslint/known-fragment-names': 'error',
13
+ '@graphql-eslint/known-type-names': 'error',
14
+ '@graphql-eslint/lone-anonymous-operation': 'error',
15
+ '@graphql-eslint/naming-convention': [
16
+ 'error',
17
+ {
18
+ VariableDefinition: 'camelCase',
19
+ OperationDefinition: {
20
+ style: 'PascalCase',
21
+ forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
22
+ forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
23
+ },
24
+ FragmentDefinition: {
25
+ style: 'PascalCase',
26
+ forbiddenPrefixes: ['Fragment'],
27
+ forbiddenSuffixes: ['Fragment'],
28
+ },
29
+ },
30
+ ],
31
+ '@graphql-eslint/no-anonymous-operations': 'error',
32
+ '@graphql-eslint/no-deprecated': 'error',
33
+ '@graphql-eslint/no-duplicate-fields': 'error',
34
+ '@graphql-eslint/no-fragment-cycles': 'error',
35
+ '@graphql-eslint/no-undefined-variables': 'error',
36
+ '@graphql-eslint/no-unused-fragments': 'error',
37
+ '@graphql-eslint/no-unused-variables': 'error',
38
+ '@graphql-eslint/one-field-subscriptions': 'error',
39
+ '@graphql-eslint/overlapping-fields-can-be-merged': 'error',
40
+ '@graphql-eslint/possible-fragment-spread': 'error',
41
+ '@graphql-eslint/provided-required-arguments': 'error',
42
+ '@graphql-eslint/require-id-when-available': 'error',
43
+ '@graphql-eslint/scalar-leafs': 'error',
44
+ '@graphql-eslint/selection-set-depth': ['error', { maxDepth: 7 }],
45
+ '@graphql-eslint/unique-argument-names': 'error',
46
+ '@graphql-eslint/unique-directive-names-per-location': 'error',
47
+ '@graphql-eslint/unique-input-field-names': 'error',
48
+ '@graphql-eslint/unique-variable-names': 'error',
49
+ '@graphql-eslint/value-literals-of-correct-type': 'error',
50
+ '@graphql-eslint/variables-are-input-types': 'error',
51
+ '@graphql-eslint/variables-in-allowed-position': 'error',
52
+ },
53
+ };
@@ -0,0 +1,9 @@
1
+ export default {
2
+ extends: './configs/base',
3
+ rules: {
4
+ '@graphql-eslint/relay-arguments': 'error',
5
+ '@graphql-eslint/relay-connection-types': 'error',
6
+ '@graphql-eslint/relay-edge-types': 'error',
7
+ '@graphql-eslint/relay-page-info': 'error',
8
+ },
9
+ };
@@ -0,0 +1,22 @@
1
+ /*
2
+ * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
3
+ */
4
+ export default {
5
+ extends: ['./configs/base', './configs/schema-recommended'],
6
+ rules: {
7
+ '@graphql-eslint/alphabetize': [
8
+ 'error',
9
+ {
10
+ fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
11
+ values: ['EnumTypeDefinition'],
12
+ arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
13
+ },
14
+ ],
15
+ '@graphql-eslint/input-name': 'error',
16
+ '@graphql-eslint/no-scalar-result-type-on-mutation': 'error',
17
+ '@graphql-eslint/require-deprecation-date': 'error',
18
+ '@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
19
+ '@graphql-eslint/require-nullable-fields-with-oneof': 'error',
20
+ '@graphql-eslint/require-type-pattern-with-oneof': 'error',
21
+ },
22
+ };
@@ -0,0 +1,49 @@
1
+ /*
2
+ * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
3
+ */
4
+ export default {
5
+ extends: './configs/base',
6
+ rules: {
7
+ '@graphql-eslint/description-style': 'error',
8
+ '@graphql-eslint/known-argument-names': 'error',
9
+ '@graphql-eslint/known-directives': 'error',
10
+ '@graphql-eslint/known-type-names': 'error',
11
+ '@graphql-eslint/lone-schema-definition': 'error',
12
+ '@graphql-eslint/naming-convention': [
13
+ 'error',
14
+ {
15
+ types: 'PascalCase',
16
+ FieldDefinition: 'camelCase',
17
+ InputValueDefinition: 'camelCase',
18
+ Argument: 'camelCase',
19
+ DirectiveDefinition: 'camelCase',
20
+ EnumValueDefinition: 'UPPER_CASE',
21
+ 'FieldDefinition[parent.name.value=Query]': {
22
+ forbiddenPrefixes: ['query', 'get'],
23
+ forbiddenSuffixes: ['Query'],
24
+ },
25
+ 'FieldDefinition[parent.name.value=Mutation]': {
26
+ forbiddenPrefixes: ['mutation'],
27
+ forbiddenSuffixes: ['Mutation'],
28
+ },
29
+ 'FieldDefinition[parent.name.value=Subscription]': {
30
+ forbiddenPrefixes: ['subscription'],
31
+ forbiddenSuffixes: ['Subscription'],
32
+ },
33
+ },
34
+ ],
35
+ '@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
36
+ '@graphql-eslint/no-hashtag-description': 'error',
37
+ '@graphql-eslint/no-typename-prefix': 'error',
38
+ '@graphql-eslint/no-unreachable-types': 'error',
39
+ '@graphql-eslint/provided-required-arguments': 'error',
40
+ '@graphql-eslint/require-deprecation-reason': 'error',
41
+ '@graphql-eslint/require-description': ['error', { types: true, DirectiveDefinition: true }],
42
+ '@graphql-eslint/strict-id-in-types': 'error',
43
+ '@graphql-eslint/unique-directive-names': 'error',
44
+ '@graphql-eslint/unique-directive-names-per-location': 'error',
45
+ '@graphql-eslint/unique-field-definition-names': 'error',
46
+ '@graphql-eslint/unique-operation-types': 'error',
47
+ '@graphql-eslint/unique-type-names': 'error',
48
+ },
49
+ };
@@ -0,0 +1,144 @@
1
+ import { resolve } from 'path';
2
+ import { Kind, visit, } from 'graphql';
3
+ import debugFactory from 'debug';
4
+ import fg from 'fast-glob';
5
+ import { logger } from './utils.js';
6
+ import { ModuleCache } from './cache.js';
7
+ const debug = debugFactory('graphql-eslint:operations');
8
+ const handleVirtualPath = (documents) => {
9
+ const filepathMap = Object.create(null);
10
+ return documents.map(source => {
11
+ var _a;
12
+ const { location } = source;
13
+ if (['.gql', '.graphql'].some(extension => location.endsWith(extension))) {
14
+ return source;
15
+ }
16
+ (_a = filepathMap[location]) !== null && _a !== void 0 ? _a : (filepathMap[location] = -1);
17
+ const index = (filepathMap[location] += 1);
18
+ return {
19
+ ...source,
20
+ location: resolve(location, `${index}_document.graphql`),
21
+ };
22
+ });
23
+ };
24
+ const operationsCache = new ModuleCache();
25
+ const siblingOperationsCache = new Map();
26
+ const getSiblings = (project) => {
27
+ const documentsKey = project.documents;
28
+ if (!documentsKey) {
29
+ return [];
30
+ }
31
+ let siblings = operationsCache.get(documentsKey);
32
+ if (!siblings) {
33
+ debug('Loading operations from %o', project.documents);
34
+ const documents = project.loadDocumentsSync(project.documents, {
35
+ skipGraphQLImport: true,
36
+ pluckConfig: project.extensions.pluckConfig,
37
+ });
38
+ if (debug.enabled) {
39
+ debug('Loaded %d operations', documents.length);
40
+ const operationsPaths = fg.sync(project.documents, { absolute: true });
41
+ debug('Operations pointers %O', operationsPaths);
42
+ }
43
+ siblings = handleVirtualPath(documents);
44
+ operationsCache.set(documentsKey, siblings);
45
+ }
46
+ return siblings;
47
+ };
48
+ export function getDocuments(project) {
49
+ const siblings = getSiblings(project);
50
+ if (siblings.length === 0) {
51
+ let printed = false;
52
+ const noopWarn = () => {
53
+ if (!printed) {
54
+ logger.warn('getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!');
55
+ printed = true;
56
+ }
57
+ return [];
58
+ };
59
+ return {
60
+ available: false,
61
+ getFragment: noopWarn,
62
+ getFragments: noopWarn,
63
+ getFragmentByType: noopWarn,
64
+ getFragmentsInUse: noopWarn,
65
+ getOperation: noopWarn,
66
+ getOperations: noopWarn,
67
+ getOperationByType: noopWarn,
68
+ };
69
+ }
70
+ // Since the siblings array is cached, we can use it as cache key.
71
+ // We should get the same array reference each time we get
72
+ // to this point for the same graphql project
73
+ if (siblingOperationsCache.has(siblings)) {
74
+ return siblingOperationsCache.get(siblings);
75
+ }
76
+ let fragmentsCache = null;
77
+ const getFragments = () => {
78
+ if (fragmentsCache === null) {
79
+ const result = [];
80
+ for (const source of siblings) {
81
+ for (const definition of source.document.definitions) {
82
+ if (definition.kind === Kind.FRAGMENT_DEFINITION) {
83
+ result.push({
84
+ filePath: source.location,
85
+ document: definition,
86
+ });
87
+ }
88
+ }
89
+ }
90
+ fragmentsCache = result;
91
+ }
92
+ return fragmentsCache;
93
+ };
94
+ let cachedOperations = null;
95
+ const getOperations = () => {
96
+ if (cachedOperations === null) {
97
+ const result = [];
98
+ for (const source of siblings) {
99
+ for (const definition of source.document.definitions) {
100
+ if (definition.kind === Kind.OPERATION_DEFINITION) {
101
+ result.push({
102
+ filePath: source.location,
103
+ document: definition,
104
+ });
105
+ }
106
+ }
107
+ }
108
+ cachedOperations = result;
109
+ }
110
+ return cachedOperations;
111
+ };
112
+ const getFragment = (name) => getFragments().filter(f => { var _a; return ((_a = f.document.name) === null || _a === void 0 ? void 0 : _a.value) === name; });
113
+ const collectFragments = (selectable, recursive, collected = new Map()) => {
114
+ visit(selectable, {
115
+ FragmentSpread(spread) {
116
+ const fragmentName = spread.name.value;
117
+ const [fragment] = getFragment(fragmentName);
118
+ if (!fragment) {
119
+ logger.warn(`Unable to locate fragment named "${fragmentName}", please make sure it's loaded using "parserOptions.operations"`);
120
+ return;
121
+ }
122
+ if (!collected.has(fragmentName)) {
123
+ collected.set(fragmentName, fragment.document);
124
+ if (recursive) {
125
+ collectFragments(fragment.document, recursive, collected);
126
+ }
127
+ }
128
+ },
129
+ });
130
+ return collected;
131
+ };
132
+ const siblingOperations = {
133
+ available: true,
134
+ getFragment,
135
+ getFragments,
136
+ getFragmentByType: typeName => getFragments().filter(f => { var _a, _b; return ((_b = (_a = f.document.typeCondition) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.value) === typeName; }),
137
+ getFragmentsInUse: (selectable, recursive = true) => Array.from(collectFragments(selectable, recursive).values()),
138
+ getOperation: name => getOperations().filter(o => { var _a; return ((_a = o.document.name) === null || _a === void 0 ? void 0 : _a.value) === name; }),
139
+ getOperations,
140
+ getOperationByType: type => getOperations().filter(o => o.document.operation === type),
141
+ };
142
+ siblingOperationsCache.set(siblings, siblingOperations);
143
+ return siblingOperations;
144
+ }
@@ -0,0 +1,58 @@
1
+ import { TypeInfo, visit, visitWithTypeInfo, Kind, } from 'graphql';
2
+ import { convertLocation } from './utils.js';
3
+ export function convertToESTree(node, schema) {
4
+ const typeInfo = schema ? new TypeInfo(schema) : null;
5
+ const visitor = {
6
+ leave(node, key, parent) {
7
+ const leadingComments = 'description' in node && node.description
8
+ ? [
9
+ {
10
+ type: node.description.block ? 'Block' : 'Line',
11
+ value: node.description.value,
12
+ },
13
+ ]
14
+ : [];
15
+ const calculatedTypeInfo = typeInfo
16
+ ? {
17
+ argument: typeInfo.getArgument(),
18
+ defaultValue: typeInfo.getDefaultValue(),
19
+ directive: typeInfo.getDirective(),
20
+ enumValue: typeInfo.getEnumValue(),
21
+ fieldDef: typeInfo.getFieldDef(),
22
+ inputType: typeInfo.getInputType(),
23
+ parentInputType: typeInfo.getParentInputType(),
24
+ parentType: typeInfo.getParentType(),
25
+ gqlType: typeInfo.getType(),
26
+ }
27
+ : {};
28
+ const rawNode = () => {
29
+ if (parent && key !== undefined) {
30
+ return parent[key];
31
+ }
32
+ return node.kind === Kind.DOCUMENT
33
+ ? {
34
+ ...node,
35
+ definitions: node.definitions.map(definition => definition.rawNode()),
36
+ }
37
+ : node;
38
+ };
39
+ const commonFields = {
40
+ ...node,
41
+ type: node.kind,
42
+ loc: convertLocation(node.loc),
43
+ range: [node.loc.start, node.loc.end],
44
+ leadingComments,
45
+ // Use function to prevent RangeError: Maximum call stack size exceeded
46
+ typeInfo: () => calculatedTypeInfo,
47
+ rawNode,
48
+ };
49
+ return 'type' in node
50
+ ? {
51
+ ...commonFields,
52
+ gqlType: node.type,
53
+ }
54
+ : commonFields;
55
+ },
56
+ };
57
+ return visit(node, typeInfo ? visitWithTypeInfo(typeInfo, visitor) : visitor);
58
+ }
@@ -0,0 +1,3 @@
1
+ export * from './converter.js';
2
+ export * from './types.js';
3
+ export * from './utils.js';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,102 @@
1
+ import { createRequire } from 'module';
2
+ const require = createRequire(import.meta.url);
3
+ import { TokenKind, isNonNullType, isListType, Source, } from 'graphql';
4
+ import { valueFromASTUntyped } from 'graphql/utilities/valueFromASTUntyped.js';
5
+ export const valueFromNode = (...args) => {
6
+ return valueFromASTUntyped(...args);
7
+ };
8
+ export function getBaseType(type) {
9
+ if (isNonNullType(type) || isListType(type)) {
10
+ return getBaseType(type.ofType);
11
+ }
12
+ return type;
13
+ }
14
+ export function convertToken(token, type) {
15
+ const { line, column, end, start, value } = token;
16
+ return {
17
+ type,
18
+ value,
19
+ /*
20
+ * ESLint has 0-based column number
21
+ * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
22
+ */
23
+ loc: {
24
+ start: {
25
+ line,
26
+ column: column - 1,
27
+ },
28
+ end: {
29
+ line,
30
+ column: column - 1 + (end - start),
31
+ },
32
+ },
33
+ range: [start, end],
34
+ };
35
+ }
36
+ function getLexer(source) {
37
+ // GraphQL v14
38
+ const gqlLanguage = require('graphql/language');
39
+ if (gqlLanguage && gqlLanguage.createLexer) {
40
+ return gqlLanguage.createLexer(source, {});
41
+ }
42
+ // GraphQL v15
43
+ const { Lexer: LexerCls } = require('graphql');
44
+ if (LexerCls && typeof LexerCls === 'function') {
45
+ return new LexerCls(source);
46
+ }
47
+ throw new Error('Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!');
48
+ }
49
+ export function extractTokens(filePath, code) {
50
+ const source = new Source(code, filePath);
51
+ const lexer = getLexer(source);
52
+ const tokens = [];
53
+ let token = lexer.advance();
54
+ while (token && token.kind !== TokenKind.EOF) {
55
+ const result = convertToken(token, token.kind);
56
+ tokens.push(result);
57
+ token = lexer.advance();
58
+ }
59
+ return tokens;
60
+ }
61
+ export function extractComments(loc) {
62
+ if (!loc) {
63
+ return [];
64
+ }
65
+ const comments = [];
66
+ let token = loc.startToken;
67
+ while (token) {
68
+ if (token.kind === TokenKind.COMMENT) {
69
+ const comment = convertToken(token,
70
+ // `eslint-disable` directive works only with `Block` type comment
71
+ token.value.trimStart().startsWith('eslint') ? 'Block' : 'Line');
72
+ comments.push(comment);
73
+ }
74
+ token = token.next;
75
+ }
76
+ return comments;
77
+ }
78
+ export function convertLocation(location) {
79
+ const { startToken, endToken, source, start, end } = location;
80
+ /*
81
+ * ESLint has 0-based column number
82
+ * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
83
+ */
84
+ const loc = {
85
+ start: {
86
+ /*
87
+ * Kind.Document has startToken: { line: 0, column: 0 }, we set line as 1 and column as 0
88
+ */
89
+ line: startToken.line === 0 ? 1 : startToken.line,
90
+ column: startToken.column === 0 ? 0 : startToken.column - 1,
91
+ },
92
+ end: {
93
+ line: endToken.line,
94
+ column: endToken.column - 1,
95
+ },
96
+ source: source.body,
97
+ };
98
+ if (loc.start.column === loc.end.column) {
99
+ loc.end.column += end - start;
100
+ }
101
+ return loc;
102
+ }
@@ -0,0 +1,33 @@
1
+ import { parseForESLint } from './parser.js';
2
+ import { configs } from './configs/index.js';
3
+ const languageOptions = {
4
+ parser: { parseForESLint },
5
+ };
6
+ export const flatConfigs = {
7
+ 'operations-all': {
8
+ languageOptions,
9
+ rules: {
10
+ ...configs['operations-recommended'].rules,
11
+ ...configs['operations-all'].rules,
12
+ },
13
+ },
14
+ 'operations-recommended': {
15
+ languageOptions,
16
+ rules: configs['operations-recommended'].rules,
17
+ },
18
+ relay: {
19
+ languageOptions,
20
+ rules: configs.relay.rules,
21
+ },
22
+ 'schema-all': {
23
+ languageOptions,
24
+ rules: {
25
+ ...configs['schema-recommended'].rules,
26
+ ...configs['schema-all'].rules,
27
+ },
28
+ },
29
+ 'schema-recommended': {
30
+ languageOptions,
31
+ rules: configs['schema-recommended'].rules,
32
+ },
33
+ };
@@ -0,0 +1,49 @@
1
+ import { dirname } from 'path';
2
+ import debugFactory from 'debug';
3
+ import { GraphQLConfig, loadConfigSync } from 'graphql-config';
4
+ import { CodeFileLoader } from '@graphql-tools/code-file-loader';
5
+ const debug = debugFactory('graphql-eslint:graphql-config');
6
+ let graphQLConfig;
7
+ export function loadOnDiskGraphQLConfig(filePath) {
8
+ return loadConfigSync({
9
+ // load config relative to the file being linted
10
+ rootDir: dirname(filePath),
11
+ throwOnEmpty: false,
12
+ throwOnMissing: false,
13
+ extensions: [codeFileLoaderExtension],
14
+ });
15
+ }
16
+ export function loadGraphQLConfig(options) {
17
+ // We don't want cache config on test environment
18
+ // Otherwise schema and documents will be same for all tests
19
+ if (process.env.NODE_ENV !== 'test' && graphQLConfig) {
20
+ return graphQLConfig;
21
+ }
22
+ const onDiskConfig = !options.skipGraphQLConfig && loadOnDiskGraphQLConfig(options.filePath);
23
+ debug('options.skipGraphQLConfig: %o', options.skipGraphQLConfig);
24
+ if (onDiskConfig) {
25
+ debug('Graphql-config path %o', onDiskConfig.filepath);
26
+ }
27
+ const configOptions = options.projects
28
+ ? { projects: options.projects }
29
+ : {
30
+ schema: (options.schema || ''),
31
+ documents: options.documents || options.operations,
32
+ extensions: options.extensions,
33
+ include: options.include,
34
+ exclude: options.exclude,
35
+ };
36
+ graphQLConfig =
37
+ onDiskConfig ||
38
+ new GraphQLConfig({
39
+ config: configOptions,
40
+ filepath: 'virtual-config',
41
+ }, [codeFileLoaderExtension]);
42
+ return graphQLConfig;
43
+ }
44
+ const codeFileLoaderExtension = api => {
45
+ const { schema, documents } = api.loaders;
46
+ schema.register(new CodeFileLoader());
47
+ documents.register(new CodeFileLoader());
48
+ return { name: 'graphql-eslint-loaders' };
49
+ };
package/esm/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { processor } from './processor.js';
2
+ export { rules } from './rules/index.js';
3
+ export { parseForESLint } from './parser.js';
4
+ export * from './testkit.js';
5
+ export * from './types.js';
6
+ export { requireGraphQLSchemaFromContext, requireSiblingsOperations } from './utils.js';
7
+ export const processors = { graphql: processor };
8
+ export { configs } from './configs/index.js';
9
+ export { flatConfigs } from './flat-configs.js';
@@ -0,0 +1 @@
1
+ { "type": "module" }
package/esm/parser.js ADDED
@@ -0,0 +1,56 @@
1
+ import { parseGraphQLSDL } from '@graphql-tools/utils';
2
+ import { GraphQLError, GraphQLSchema } from 'graphql';
3
+ import debugFactory from 'debug';
4
+ import { convertToESTree, extractComments, extractTokens } from './estree-converter/index.js';
5
+ import { getSchema } from './schema.js';
6
+ import { getDocuments } from './documents.js';
7
+ import { loadGraphQLConfig } from './graphql-config.js';
8
+ import { CWD, VIRTUAL_DOCUMENT_REGEX } from './utils.js';
9
+ const debug = debugFactory('graphql-eslint:parser');
10
+ debug('cwd %o', CWD);
11
+ export function parseForESLint(code, options) {
12
+ try {
13
+ const { filePath } = options;
14
+ // First parse code from file, in case of syntax error do not try load schema,
15
+ // documents or even graphql-config instance
16
+ const { document } = parseGraphQLSDL(filePath, code, {
17
+ ...options.graphQLParserOptions,
18
+ noLocation: false,
19
+ });
20
+ const gqlConfig = loadGraphQLConfig(options);
21
+ const realFilepath = filePath.replace(VIRTUAL_DOCUMENT_REGEX, '');
22
+ const project = gqlConfig.getProjectForFile(realFilepath);
23
+ const schema = getSchema(project, options.schemaOptions);
24
+ const rootTree = convertToESTree(document, schema instanceof GraphQLSchema ? schema : null);
25
+ return {
26
+ services: {
27
+ schema,
28
+ siblingOperations: getDocuments(project),
29
+ },
30
+ ast: {
31
+ comments: extractComments(document.loc),
32
+ tokens: extractTokens(filePath, code),
33
+ loc: rootTree.loc,
34
+ range: rootTree.range,
35
+ type: 'Program',
36
+ sourceType: 'script',
37
+ body: [rootTree],
38
+ },
39
+ };
40
+ }
41
+ catch (error) {
42
+ error.message = `[graphql-eslint] ${error.message}`;
43
+ // In case of GraphQL parser error, we report it to ESLint as a parser error that matches the requirements
44
+ // of ESLint. This will make sure to display it correctly in IDEs and lint results.
45
+ if (error instanceof GraphQLError) {
46
+ const eslintError = {
47
+ index: error.positions[0],
48
+ lineNumber: error.locations[0].line,
49
+ column: error.locations[0].column - 1,
50
+ message: error.message,
51
+ };
52
+ throw eslintError;
53
+ }
54
+ throw error;
55
+ }
56
+ }