@graphql-eslint/eslint-plugin 2.3.0-alpha-f7157af.0 → 2.3.2-alpha-2901045.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 (65) hide show
  1. package/README.md +1 -1
  2. package/docs/README.md +1 -1
  3. package/docs/custom-rules.md +3 -3
  4. package/docs/rules/alphabetize.md +6 -1
  5. package/docs/rules/avoid-duplicate-fields.md +6 -1
  6. package/docs/rules/avoid-operation-name-prefix.md +5 -1
  7. package/docs/rules/avoid-scalar-result-type-on-mutation.md +6 -1
  8. package/docs/rules/avoid-typename-prefix.md +6 -1
  9. package/docs/rules/description-style.md +6 -1
  10. package/docs/rules/executable-definitions.md +6 -1
  11. package/docs/rules/fields-on-correct-type.md +6 -1
  12. package/docs/rules/fragments-on-composite-type.md +6 -1
  13. package/docs/rules/input-name.md +6 -1
  14. package/docs/rules/known-argument-names.md +6 -1
  15. package/docs/rules/known-directives.md +6 -1
  16. package/docs/rules/known-fragment-names.md +6 -1
  17. package/docs/rules/known-type-names.md +6 -1
  18. package/docs/rules/lone-anonymous-operation.md +6 -1
  19. package/docs/rules/lone-schema-definition.md +6 -1
  20. package/docs/rules/match-document-filename.md +6 -1
  21. package/docs/rules/naming-convention.md +6 -1
  22. package/docs/rules/no-anonymous-operations.md +6 -1
  23. package/docs/rules/no-case-insensitive-enum-values-duplicates.md +5 -1
  24. package/docs/rules/no-deprecated.md +6 -1
  25. package/docs/rules/no-fragment-cycles.md +6 -1
  26. package/docs/rules/no-hashtag-description.md +6 -1
  27. package/docs/rules/no-operation-name-suffix.md +5 -1
  28. package/docs/rules/no-undefined-variables.md +6 -1
  29. package/docs/rules/no-unreachable-types.md +6 -1
  30. package/docs/rules/no-unused-fields.md +6 -1
  31. package/docs/rules/no-unused-fragments.md +6 -1
  32. package/docs/rules/no-unused-variables.md +6 -1
  33. package/docs/rules/one-field-subscriptions.md +6 -1
  34. package/docs/rules/overlapping-fields-can-be-merged.md +6 -1
  35. package/docs/rules/possible-fragment-spread.md +6 -1
  36. package/docs/rules/possible-type-extension.md +6 -1
  37. package/docs/rules/provided-required-arguments.md +6 -1
  38. package/docs/rules/require-deprecation-date.md +6 -1
  39. package/docs/rules/require-deprecation-reason.md +6 -1
  40. package/docs/rules/require-description.md +6 -1
  41. package/docs/rules/require-field-of-type-query-in-mutation-result.md +6 -1
  42. package/docs/rules/require-id-when-available.md +6 -1
  43. package/docs/rules/scalar-leafs.md +6 -1
  44. package/docs/rules/selection-set-depth.md +6 -1
  45. package/docs/rules/strict-id-in-types.md +6 -1
  46. package/docs/rules/unique-argument-names.md +6 -1
  47. package/docs/rules/unique-directive-names-per-location.md +6 -1
  48. package/docs/rules/unique-directive-names.md +6 -1
  49. package/docs/rules/unique-enum-value-names.md +6 -1
  50. package/docs/rules/unique-field-definition-names.md +6 -1
  51. package/docs/rules/unique-fragment-name.md +6 -1
  52. package/docs/rules/unique-input-field-names.md +6 -1
  53. package/docs/rules/unique-operation-name.md +6 -1
  54. package/docs/rules/unique-operation-types.md +6 -1
  55. package/docs/rules/unique-type-names.md +6 -1
  56. package/docs/rules/unique-variable-names.md +6 -1
  57. package/docs/rules/value-literals-of-correct-type.md +6 -1
  58. package/docs/rules/variables-are-input-types.md +6 -1
  59. package/docs/rules/variables-in-allowed-position.md +6 -1
  60. package/index.js +167 -55
  61. package/index.mjs +167 -55
  62. package/package.json +1 -1
  63. package/testkit.d.ts +5 -3
  64. package/types.d.ts +2 -0
  65. package/utils.d.ts +4 -0
@@ -43,4 +43,9 @@ fragment AllUserFields on User {
43
43
  fragment UserFields on User {
44
44
  id
45
45
  }
46
- ```
46
+ ```
47
+
48
+ ## Resources
49
+
50
+ - [Rule source](../../packages/plugin/src/rules/unique-fragment-name.ts)
51
+ - [Test source](../../packages/plugin/tests/unique-fragment-name.spec.ts)
@@ -9,4 +9,9 @@
9
9
 
10
10
  A GraphQL input object value is only valid if all supplied fields are uniquely named.
11
11
 
12
- > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueInputFieldNamesRule.ts).
12
+ > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueInputFieldNamesRule.ts).
13
+
14
+ ## Resources
15
+
16
+ - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueInputFieldNamesRule.ts)
17
+ - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts)
@@ -47,4 +47,9 @@ query me {
47
47
  id
48
48
  }
49
49
  }
50
- ```
50
+ ```
51
+
52
+ ## Resources
53
+
54
+ - [Rule source](../../packages/plugin/src/rules/unique-operation-name.ts)
55
+ - [Test source](../../packages/plugin/tests/unique-operation-name.spec.ts)
@@ -9,4 +9,9 @@
9
9
 
10
10
  A GraphQL document is only valid if it has only one type per operation.
11
11
 
12
- > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueOperationTypesRule.ts).
12
+ > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueOperationTypesRule.ts).
13
+
14
+ ## Resources
15
+
16
+ - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueOperationTypesRule.ts)
17
+ - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueOperationTypesRule-test.ts)
@@ -9,4 +9,9 @@
9
9
 
10
10
  A GraphQL document is only valid if all defined types have unique names.
11
11
 
12
- > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueTypeNamesRule.ts).
12
+ > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueTypeNamesRule.ts).
13
+
14
+ ## Resources
15
+
16
+ - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueTypeNamesRule.ts)
17
+ - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueTypeNamesRule-test.ts)
@@ -9,4 +9,9 @@
9
9
 
10
10
  A GraphQL operation is only valid if all its variables are uniquely named.
11
11
 
12
- > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueVariableNamesRule.ts).
12
+ > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueVariableNamesRule.ts).
13
+
14
+ ## Resources
15
+
16
+ - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueVariableNamesRule.ts)
17
+ - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueVariableNamesRule-test.ts)
@@ -9,4 +9,9 @@
9
9
 
10
10
  A GraphQL document is only valid if all value literals are of the type expected at their position.
11
11
 
12
- > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ValuesOfCorrectTypeRule.ts).
12
+ > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ValuesOfCorrectTypeRule.ts).
13
+
14
+ ## Resources
15
+
16
+ - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ValuesOfCorrectTypeRule.ts)
17
+ - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/ValuesOfCorrectTypeRule-test.ts)
@@ -9,4 +9,9 @@
9
9
 
10
10
  A GraphQL operation is only valid if all the variables it defines are of input types (scalar, enum, or input object).
11
11
 
12
- > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesAreInputTypesRule.ts).
12
+ > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesAreInputTypesRule.ts).
13
+
14
+ ## Resources
15
+
16
+ - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesAreInputTypesRule.ts)
17
+ - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/VariablesAreInputTypesRule-test.ts)
@@ -9,4 +9,9 @@
9
9
 
10
10
  Variables passed to field arguments conform to type.
11
11
 
12
- > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesInAllowedPositionRule.ts).
12
+ > This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesInAllowedPositionRule.ts).
13
+
14
+ ## Resources
15
+
16
+ - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesInAllowedPositionRule.ts)
17
+ - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts)
package/index.js CHANGED
@@ -15,6 +15,8 @@ const depthLimit = _interopDefault(require('graphql-depth-limit'));
15
15
  const graphqlTagPluck = require('@graphql-tools/graphql-tag-pluck');
16
16
  const graphqlConfig$1 = require('graphql-config');
17
17
  const codeFileLoader = require('@graphql-tools/code-file-loader');
18
+ const eslint = require('eslint');
19
+ const codeFrame = require('@babel/code-frame');
18
20
 
19
21
  /*
20
22
  * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
@@ -242,6 +244,24 @@ const convertCase = (style, str) => {
242
244
  return lowerCase(str).replace(/ /g, '-');
243
245
  }
244
246
  };
247
+ function getLocation(loc, fieldName = '', offset) {
248
+ const { start } = loc;
249
+ /*
250
+ * ESLint has 0-based column number
251
+ * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
252
+ */
253
+ const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
254
+ return {
255
+ start: {
256
+ line: start.line,
257
+ column: start.column - offsetStart,
258
+ },
259
+ end: {
260
+ line: start.line,
261
+ column: start.column - offsetEnd + fieldName.length,
262
+ },
263
+ };
264
+ }
245
265
 
246
266
  function extractRuleName(stack) {
247
267
  const match = (stack || '').match(/validation[/\\\\]rules[/\\\\](.*?)\.js:/) || [];
@@ -292,6 +312,7 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
292
312
  meta: {
293
313
  docs: {
294
314
  ...docs,
315
+ graphQLJSRuleName: ruleName,
295
316
  category: 'Validation',
296
317
  recommended: true,
297
318
  requiresSchema,
@@ -591,7 +612,7 @@ const rule = {
591
612
  ],
592
613
  },
593
614
  messages: {
594
- [ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}".',
615
+ [ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}"',
595
616
  },
596
617
  schema: {
597
618
  type: 'array',
@@ -648,19 +669,9 @@ const rule = {
648
669
  for (const node of nodes) {
649
670
  const currName = node.name.value;
650
671
  if (prevName && prevName > currName) {
651
- const { start, end } = node.name.loc;
652
672
  const isVariableNode = node.kind === graphql.Kind.VARIABLE;
653
673
  context.report({
654
- loc: {
655
- start: {
656
- line: start.line,
657
- column: start.column - (isVariableNode ? 2 : 1),
658
- },
659
- end: {
660
- line: end.line,
661
- column: end.column,
662
- },
663
- },
674
+ loc: getLocation(node.loc, node.name.value, { offsetEnd: isVariableNode ? 0 : 1 }),
664
675
  messageId: ALPHABETIZE,
665
676
  data: isVariableNode
666
677
  ? {
@@ -790,6 +801,7 @@ const rule$1 = {
790
801
  messages: {
791
802
  [AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times.`,
792
803
  },
804
+ schema: [],
793
805
  },
794
806
  create(context) {
795
807
  return {
@@ -909,15 +921,16 @@ const rule$2 = {
909
921
  const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
910
922
  const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
911
923
  if (testName.startsWith(testKeyword)) {
924
+ const { start } = node.name.loc;
912
925
  context.report({
913
926
  loc: {
914
927
  start: {
915
- line: node.name.loc.start.line,
916
- column: node.name.loc.start.column - 1,
928
+ line: start.line,
929
+ column: start.column - 1,
917
930
  },
918
931
  end: {
919
- line: node.name.loc.start.line,
920
- column: node.name.loc.start.column + testKeyword.length - 1,
932
+ line: start.line,
933
+ column: start.column - 1 + testKeyword.length,
921
934
  },
922
935
  },
923
936
  data: {
@@ -960,6 +973,7 @@ const rule$3 = {
960
973
  },
961
974
  ],
962
975
  },
976
+ schema: [],
963
977
  },
964
978
  create(context) {
965
979
  const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
@@ -1015,22 +1029,33 @@ const rule$4 = {
1015
1029
  messages: {
1016
1030
  [AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
1017
1031
  },
1032
+ schema: [],
1018
1033
  },
1019
1034
  create(context) {
1020
1035
  return {
1021
1036
  'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
1022
1037
  const typeName = node.name.value;
1023
- const lowerTypeName = (typeName || '').toLowerCase();
1038
+ const lowerTypeName = typeName.toLowerCase();
1024
1039
  for (const field of node.fields) {
1025
- const fieldName = field.name.value || '';
1026
- if (fieldName && lowerTypeName && fieldName.toLowerCase().startsWith(lowerTypeName)) {
1040
+ const fieldName = field.name.value;
1041
+ if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
1042
+ const { start } = field.loc;
1027
1043
  context.report({
1028
- node: field.name,
1029
1044
  data: {
1030
1045
  fieldName,
1031
1046
  typeName,
1032
1047
  },
1033
1048
  messageId: AVOID_TYPENAME_PREFIX,
1049
+ loc: {
1050
+ start: {
1051
+ line: start.line,
1052
+ column: start.column - 1,
1053
+ },
1054
+ end: {
1055
+ line: start.line,
1056
+ column: start.column - 1 + lowerTypeName.length,
1057
+ },
1058
+ },
1034
1059
  });
1035
1060
  }
1036
1061
  }
@@ -1358,7 +1383,8 @@ const rule$7 = {
1358
1383
  var _a;
1359
1384
  if (options.fileExtension && options.fileExtension !== fileExtension) {
1360
1385
  context.report({
1361
- node: documentNode,
1386
+ // Report on first character
1387
+ loc: { column: 0, line: 1 },
1362
1388
  messageId: MATCH_EXTENSION,
1363
1389
  data: {
1364
1390
  fileExtension,
@@ -1390,7 +1416,8 @@ const rule$7 = {
1390
1416
  const filenameWithExtension = filename + expectedExtension;
1391
1417
  if (expectedFilename !== filenameWithExtension) {
1392
1418
  context.report({
1393
- node: documentNode,
1419
+ // Report on first character
1420
+ loc: { column: 0, line: 1 },
1394
1421
  messageId: MATCH_STYLE,
1395
1422
  data: {
1396
1423
  expectedFilename,
@@ -1728,20 +1755,24 @@ const rule$9 = {
1728
1755
  messages: {
1729
1756
  [NO_ANONYMOUS_OPERATIONS]: `Anonymous GraphQL operations are forbidden. Please make sure to name your {{ operation }}!`,
1730
1757
  },
1758
+ schema: [],
1731
1759
  },
1732
1760
  create(context) {
1733
1761
  return {
1734
1762
  OperationDefinition(node) {
1735
- if (node && (!node.name || node.name.value === '')) {
1763
+ var _a;
1764
+ const isAnonymous = (((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '').length === 0;
1765
+ if (isAnonymous) {
1766
+ const { start } = node.loc;
1736
1767
  context.report({
1737
1768
  loc: {
1738
1769
  start: {
1739
- column: node.loc.start.column - 1,
1740
- line: node.loc.start.line,
1770
+ column: start.column - 1,
1771
+ line: start.line,
1741
1772
  },
1742
1773
  end: {
1743
- column: node.loc.start.column + node.operation.length,
1744
- line: node.loc.start.line,
1774
+ column: start.column - 1 + node.operation.length,
1775
+ line: start.line,
1745
1776
  },
1746
1777
  },
1747
1778
  data: {
@@ -1791,6 +1822,7 @@ const rule$a = {
1791
1822
  messages: {
1792
1823
  [ERROR_MESSAGE_ID]: `Case-insensitive enum values duplicates are not allowed! Found: "{{ found }}"`,
1793
1824
  },
1825
+ schema: [],
1794
1826
  },
1795
1827
  create(context) {
1796
1828
  return {
@@ -1885,6 +1917,7 @@ const rule$b = {
1885
1917
  messages: {
1886
1918
  [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1887
1919
  },
1920
+ schema: [],
1888
1921
  },
1889
1922
  create(context) {
1890
1923
  return {
@@ -1971,6 +2004,7 @@ const rule$c = {
1971
2004
  ],
1972
2005
  },
1973
2006
  type: 'suggestion',
2007
+ schema: [],
1974
2008
  },
1975
2009
  create(context) {
1976
2010
  return {
@@ -2031,15 +2065,28 @@ const rule$d = {
2031
2065
  messages: {
2032
2066
  [NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
2033
2067
  },
2068
+ schema: [],
2034
2069
  },
2035
2070
  create(context) {
2036
2071
  return {
2037
2072
  'OperationDefinition, FragmentDefinition'(node) {
2038
- if (node && node.name && node.name.value !== '') {
2039
- const invalidSuffix = (node.type === 'OperationDefinition' ? node.operation : 'fragment').toLowerCase();
2040
- if (node.name.value.toLowerCase().endsWith(invalidSuffix)) {
2073
+ var _a;
2074
+ const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
2075
+ if (name.length > 0) {
2076
+ const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
2077
+ if (name.toLowerCase().endsWith(invalidSuffix)) {
2078
+ const { start, end } = node.name.loc;
2041
2079
  context.report({
2042
- node: node.name,
2080
+ loc: {
2081
+ start: {
2082
+ column: start.column - 1 + name.length - invalidSuffix.length,
2083
+ line: start.line,
2084
+ },
2085
+ end: {
2086
+ column: end.column - 1 + name.length,
2087
+ line: end.line,
2088
+ },
2089
+ },
2043
2090
  data: {
2044
2091
  invalidSuffix,
2045
2092
  },
@@ -2096,6 +2143,7 @@ const rule$e = {
2096
2143
  },
2097
2144
  fixable: 'code',
2098
2145
  type: 'suggestion',
2146
+ schema: [],
2099
2147
  },
2100
2148
  create(context) {
2101
2149
  const reachableTypes = requireReachableTypesFromContext(RULE_NAME, context);
@@ -2187,6 +2235,7 @@ const rule$f = {
2187
2235
  },
2188
2236
  fixable: 'code',
2189
2237
  type: 'suggestion',
2238
+ schema: [],
2190
2239
  },
2191
2240
  create(context) {
2192
2241
  const usedFields = requireUsedFieldsFromContext(RULE_NAME$1, context);
@@ -2452,6 +2501,7 @@ const rule$h = {
2452
2501
  ],
2453
2502
  },
2454
2503
  type: 'suggestion',
2504
+ schema: [],
2455
2505
  },
2456
2506
  create(context) {
2457
2507
  return {
@@ -2488,15 +2538,18 @@ const DESCRIBABLE_NODES = [
2488
2538
  function verifyRule(context, node) {
2489
2539
  if (node) {
2490
2540
  if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
2541
+ const { start, end } = ('name' in node ? node.name : node).loc;
2491
2542
  context.report({
2492
2543
  loc: {
2493
2544
  start: {
2494
- line: node.loc.start.line,
2495
- column: node.loc.start.column - 1,
2545
+ line: start.line,
2546
+ column: start.column - 1,
2496
2547
  },
2497
2548
  end: {
2498
- line: node.loc.end.line,
2499
- column: node.loc.end.column,
2549
+ line: end.line,
2550
+ column:
2551
+ // node.name don't exist on SchemaDefinition
2552
+ 'name' in node ? end.column - 1 + node.name.value.length : end.column,
2500
2553
  },
2501
2554
  },
2502
2555
  messageId: REQUIRE_DESCRIPTION_ERROR,
@@ -2610,6 +2663,7 @@ const rule$j = {
2610
2663
  },
2611
2664
  ],
2612
2665
  },
2666
+ schema: [],
2613
2667
  },
2614
2668
  create(context) {
2615
2669
  const schema = requireGraphQLSchemaFromContext(RULE_NAME$2, context);
@@ -3146,11 +3200,7 @@ const rule$m = {
3146
3200
  const RULE_NAME$3 = 'unique-fragment-name';
3147
3201
  const UNIQUE_FRAGMENT_NAME = 'UNIQUE_FRAGMENT_NAME';
3148
3202
  const checkNode = (context, node, ruleName, messageId) => {
3149
- var _a;
3150
- const documentName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.value;
3151
- if (!documentName) {
3152
- return;
3153
- }
3203
+ const documentName = node.name.value;
3154
3204
  const siblings = requireSiblingsOperations(ruleName, context);
3155
3205
  const siblingDocuments = node.kind === graphql.Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
3156
3206
  const filepath = context.getFilename();
@@ -3161,7 +3211,6 @@ const checkNode = (context, node, ruleName, messageId) => {
3161
3211
  return isSameName && !isSamePath;
3162
3212
  });
3163
3213
  if (conflictingDocuments.length > 0) {
3164
- const { start, end } = node.name.loc;
3165
3214
  context.report({
3166
3215
  messageId,
3167
3216
  data: {
@@ -3170,16 +3219,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3170
3219
  .map(f => `\t${path.relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
3171
3220
  .join('\n'),
3172
3221
  },
3173
- loc: {
3174
- start: {
3175
- line: start.line,
3176
- column: start.column - 1,
3177
- },
3178
- end: {
3179
- line: end.line,
3180
- column: end.column - 1,
3181
- },
3182
- },
3222
+ loc: getLocation(node.name.loc, documentName),
3183
3223
  });
3184
3224
  }
3185
3225
  };
@@ -3229,6 +3269,7 @@ const rule$n = {
3229
3269
  messages: {
3230
3270
  [UNIQUE_FRAGMENT_NAME]: 'Fragment named "{{ documentName }}" already defined in:\n{{ summary }}',
3231
3271
  },
3272
+ schema: [],
3232
3273
  },
3233
3274
  create(context) {
3234
3275
  return {
@@ -3291,10 +3332,11 @@ const rule$o = {
3291
3332
  messages: {
3292
3333
  [UNIQUE_OPERATION_NAME]: 'Operation named "{{ documentName }}" already defined in:\n{{ summary }}',
3293
3334
  },
3335
+ schema: [],
3294
3336
  },
3295
3337
  create(context) {
3296
3338
  return {
3297
- OperationDefinition(node) {
3339
+ 'OperationDefinition[name!=undefined]'(node) {
3298
3340
  checkNode(context, node, RULE_NAME$4, UNIQUE_OPERATION_NAME);
3299
3341
  },
3300
3342
  };
@@ -3724,23 +3766,93 @@ function parseForESLint(code, options = {}) {
3724
3766
  }
3725
3767
  }
3726
3768
 
3727
- class GraphQLRuleTester extends require('eslint').RuleTester {
3769
+ class GraphQLRuleTester extends eslint.RuleTester {
3728
3770
  constructor(parserOptions = {}) {
3729
- super({
3771
+ const config = {
3730
3772
  parser: require.resolve('@graphql-eslint/eslint-plugin'),
3731
3773
  parserOptions: {
3732
3774
  ...parserOptions,
3733
3775
  skipGraphQLConfig: true,
3734
3776
  },
3735
- });
3777
+ };
3778
+ super(config);
3779
+ this.config = config;
3736
3780
  }
3737
3781
  fromMockFile(path$1) {
3738
3782
  return fs.readFileSync(path.resolve(__dirname, `../tests/mocks/${path$1}`), 'utf-8');
3739
3783
  }
3740
3784
  runGraphQLTests(name, rule, tests) {
3741
3785
  super.run(name, rule, tests);
3786
+ // Skip snapshot testing if `expect` variable is not defined
3787
+ if (typeof expect === 'undefined') {
3788
+ return;
3789
+ }
3790
+ const linter = new eslint.Linter();
3791
+ linter.defineRule(name, rule);
3792
+ for (const testCase of tests.invalid) {
3793
+ const verifyConfig = getVerifyConfig(name, this.config, testCase);
3794
+ defineParser(linter, verifyConfig.parser);
3795
+ const { code, filename } = testCase;
3796
+ const messages = linter.verify(code, verifyConfig, { filename });
3797
+ for (const message of messages) {
3798
+ if (message.fatal) {
3799
+ throw new Error(message.message);
3800
+ }
3801
+ const messageForSnapshot = visualizeEslintMessage(code, message);
3802
+ // eslint-disable-next-line no-undef
3803
+ expect(messageForSnapshot).toMatchSnapshot();
3804
+ }
3805
+ }
3806
+ }
3807
+ }
3808
+ function getVerifyConfig(ruleId, testerConfig, testCase) {
3809
+ const { options, parserOptions, parser = testerConfig.parser } = testCase;
3810
+ return {
3811
+ ...testerConfig,
3812
+ parser,
3813
+ parserOptions: {
3814
+ ...testerConfig.parserOptions,
3815
+ ...parserOptions,
3816
+ },
3817
+ rules: {
3818
+ [ruleId]: ['error', ...(Array.isArray(options) ? options : [])],
3819
+ },
3820
+ };
3821
+ }
3822
+ const parsers = new WeakMap();
3823
+ function defineParser(linter, parser) {
3824
+ if (!parser) {
3825
+ return;
3826
+ }
3827
+ if (!parsers.has(linter)) {
3828
+ parsers.set(linter, new Set());
3829
+ }
3830
+ const defined = parsers.get(linter);
3831
+ if (!defined.has(parser)) {
3832
+ defined.add(parser);
3833
+ linter.defineParser(parser, require(parser));
3742
3834
  }
3743
3835
  }
3836
+ function visualizeEslintMessage(text, result) {
3837
+ const { line, column, endLine, endColumn, message } = result;
3838
+ const location = {
3839
+ start: {
3840
+ line,
3841
+ column,
3842
+ },
3843
+ };
3844
+ if (typeof endLine === 'number' && typeof endColumn === 'number') {
3845
+ location.end = {
3846
+ line: endLine,
3847
+ column: endColumn,
3848
+ };
3849
+ }
3850
+ return codeFrame.codeFrameColumns(text, location, {
3851
+ linesAbove: Number.POSITIVE_INFINITY,
3852
+ linesBelow: Number.POSITIVE_INFINITY,
3853
+ message,
3854
+ });
3855
+ }
3744
3856
 
3745
3857
  exports.GraphQLRuleTester = GraphQLRuleTester;
3746
3858
  exports.configs = configs;