@graphql-eslint/eslint-plugin 3.8.0-alpha-2d2b247.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -17,7 +17,6 @@ const graphqlConfig = require('graphql-config');
17
17
  const codeFileLoader = require('@graphql-tools/code-file-loader');
18
18
  const eslint = require('eslint');
19
19
  const codeFrame = require('@babel/code-frame');
20
- const dedent = _interopDefault(require('dedent'));
21
20
 
22
21
  const base = {
23
22
  parser: '@graphql-eslint/eslint-plugin',
@@ -209,43 +208,6 @@ function requireUsedFieldsFromContext(ruleName, context) {
209
208
  const siblings = requireSiblingsOperations(ruleName, context);
210
209
  return context.parserServices.usedFields(schema, siblings);
211
210
  }
212
- function getLexer(source) {
213
- // GraphQL v14
214
- const gqlLanguage = require('graphql/language');
215
- if (gqlLanguage && gqlLanguage.createLexer) {
216
- return gqlLanguage.createLexer(source, {});
217
- }
218
- // GraphQL v15
219
- const { Lexer: LexerCls } = require('graphql');
220
- if (LexerCls && typeof LexerCls === 'function') {
221
- return new LexerCls(source);
222
- }
223
- throw new Error(`Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!`);
224
- }
225
- function extractTokens(source) {
226
- const lexer = getLexer(source);
227
- const tokens = [];
228
- let token = lexer.advance();
229
- while (token && token.kind !== '<EOF>') {
230
- tokens.push({
231
- type: token.kind,
232
- loc: {
233
- start: {
234
- line: token.line,
235
- column: token.column,
236
- },
237
- end: {
238
- line: token.line,
239
- column: token.column,
240
- },
241
- },
242
- value: token.value,
243
- range: [token.start, token.end],
244
- });
245
- token = lexer.advance();
246
- }
247
- return tokens;
248
- }
249
211
  const normalizePath = (path) => (path || '').replace(/\\/g, '/');
250
212
  /**
251
213
  * https://github.com/prettier/eslint-plugin-prettier/blob/76bd45ece6d56eb52f75db6b4a1efdd2efb56392/eslint-plugin-prettier.js#L71
@@ -315,21 +277,16 @@ const convertCase = (style, str) => {
315
277
  return lowerCase(str).replace(/ /g, '-');
316
278
  }
317
279
  };
318
- function getLocation(loc, fieldName = '', offset) {
319
- const { start } = loc;
320
- /*
321
- * ESLint has 0-based column number
322
- * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
323
- */
324
- const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
280
+ function getLocation(loc, fieldName = '') {
281
+ const { line, column } = loc.start;
325
282
  return {
326
283
  start: {
327
- line: start.line,
328
- column: start.column - offsetStart,
284
+ line,
285
+ column,
329
286
  },
330
287
  end: {
331
- line: start.line,
332
- column: start.column - offsetEnd + fieldName.length,
288
+ line,
289
+ column: column + fieldName.length,
333
290
  },
334
291
  };
335
292
  }
@@ -343,23 +300,16 @@ function validateDocument(context, schema = null, documentNode, rule) {
343
300
  ? graphql.validate(schema, documentNode, [rule])
344
301
  : validate.validateSDL(documentNode, null, [rule]);
345
302
  for (const error of validationErrors) {
346
- /*
347
- * TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
348
- * Example: loc.end always equal loc.start
349
- * {
350
- * token: {
351
- * type: 'Name',
352
- * loc: { start: { line: 4, column: 13 }, end: { line: 4, column: 13 } },
353
- * value: 'veryBad',
354
- * range: [ 40, 47 ]
355
- * }
356
- * }
357
- */
358
303
  const { line, column } = error.locations[0];
359
304
  const ancestors = context.getAncestors();
360
- const token = ancestors[0].tokens.find(token => token.loc.start.line === line && token.loc.start.column === column);
305
+ const token = ancestors[0].tokens.find(token => token.loc.start.line === line && token.loc.start.column === column - 1);
361
306
  context.report({
362
- loc: getLocation({ start: error.locations[0] }, token === null || token === void 0 ? void 0 : token.value),
307
+ loc: token
308
+ ? token.loc
309
+ : {
310
+ line,
311
+ column: column - 1,
312
+ },
363
313
  message: error.message,
364
314
  });
365
315
  }
@@ -885,7 +835,7 @@ const rule = {
885
835
  }
886
836
  const isVariableNode = currNode.kind === graphql.Kind.VARIABLE;
887
837
  context.report({
888
- loc: getLocation(currNode.name.loc, currName, { offsetStart: isVariableNode ? 2 : 1 }),
838
+ node: currNode.name,
889
839
  messageId: ALPHABETIZE,
890
840
  data: isVariableNode
891
841
  ? {
@@ -1096,7 +1046,7 @@ const rule$2 = {
1096
1046
  if (shouldCheckType(node.parent.parent)) {
1097
1047
  const name = node.name.value;
1098
1048
  context.report({
1099
- loc: getLocation(node.loc, name),
1049
+ node: node.name,
1100
1050
  message: `Input "${name}" should be called "input"`,
1101
1051
  });
1102
1052
  }
@@ -1118,7 +1068,7 @@ const rule$2 = {
1118
1068
  if ((options.caseSensitiveInputType && node.name.value !== mutationName) ||
1119
1069
  name.toLowerCase() !== mutationName.toLowerCase()) {
1120
1070
  context.report({
1121
- loc: getLocation(node.loc, name),
1071
+ node: node.name,
1122
1072
  message: `InputType "${name}" name should be "${mutationName}"`,
1123
1073
  });
1124
1074
  }
@@ -1132,7 +1082,7 @@ const rule$2 = {
1132
1082
  const MATCH_EXTENSION = 'MATCH_EXTENSION';
1133
1083
  const MATCH_STYLE = 'MATCH_STYLE';
1134
1084
  const ACCEPTED_EXTENSIONS = ['.gql', '.graphql'];
1135
- const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case'];
1085
+ const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case', 'matchDocumentStyle'];
1136
1086
  const schemaOption = {
1137
1087
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1138
1088
  };
@@ -1306,7 +1256,14 @@ const rule$3 = {
1306
1256
  option = { style: option };
1307
1257
  }
1308
1258
  const expectedExtension = options.fileExtension || fileExtension;
1309
- const expectedFilename = (option.style ? convertCase(option.style, docName) : filename) + (option.suffix || '') + expectedExtension;
1259
+ let expectedFilename;
1260
+ if (option.style) {
1261
+ expectedFilename = option.style === 'matchDocumentStyle' ? docName : convertCase(option.style, docName);
1262
+ }
1263
+ else {
1264
+ expectedFilename = filename;
1265
+ }
1266
+ expectedFilename += (option.suffix || '') + expectedExtension;
1310
1267
  const filenameWithExtension = filename + expectedExtension;
1311
1268
  if (expectedFilename !== filenameWithExtension) {
1312
1269
  context.report({
@@ -1563,7 +1520,7 @@ const rule$4 = {
1563
1520
  const [trailingUnderscores] = nodeName.match(/_*$/);
1564
1521
  const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
1565
1522
  context.report({
1566
- loc: getLocation(node.loc, node.value),
1523
+ node,
1567
1524
  message: `${nodeType} "${nodeName}" should ${errorMessage}`,
1568
1525
  suggest: [
1569
1526
  {
@@ -1621,7 +1578,7 @@ const rule$4 = {
1621
1578
  const name = node.value;
1622
1579
  const renameToName = name.replace(new RegExp(isLeading ? '^_+' : '_+$'), '');
1623
1580
  context.report({
1624
- loc: getLocation(node.loc, name),
1581
+ node,
1625
1582
  message: `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`,
1626
1583
  suggest: [
1627
1584
  {
@@ -1737,7 +1694,7 @@ const rule$6 = {
1737
1694
  for (const duplicate of duplicates) {
1738
1695
  const enumName = duplicate.name.value;
1739
1696
  context.report({
1740
- loc: getLocation(duplicate.loc, enumName),
1697
+ node: duplicate.name,
1741
1698
  message: `Case-insensitive enum values duplicates are not allowed! Found: "${enumName}"`,
1742
1699
  });
1743
1700
  }
@@ -1831,9 +1788,8 @@ const rule$7 = {
1831
1788
  const typeInfo = node.typeInfo();
1832
1789
  if (typeInfo && typeInfo.enumValue) {
1833
1790
  if (typeInfo.enumValue.deprecationReason) {
1834
- const enumValueName = node.value;
1835
1791
  context.report({
1836
- loc: getLocation(node.loc, enumValueName),
1792
+ node,
1837
1793
  messageId: NO_DEPRECATED,
1838
1794
  data: {
1839
1795
  type: 'enum value',
@@ -1848,9 +1804,8 @@ const rule$7 = {
1848
1804
  const typeInfo = node.typeInfo();
1849
1805
  if (typeInfo && typeInfo.fieldDef) {
1850
1806
  if (typeInfo.fieldDef.deprecationReason) {
1851
- const fieldName = node.name.value;
1852
1807
  context.report({
1853
- loc: getLocation(node.loc, fieldName),
1808
+ node: node.name,
1854
1809
  messageId: NO_DEPRECATED,
1855
1810
  data: {
1856
1811
  type: 'field',
@@ -1922,12 +1877,11 @@ const rule$8 = {
1922
1877
  schema: [],
1923
1878
  },
1924
1879
  create(context) {
1925
- function checkNode(usedFields, fieldName, type, node) {
1880
+ function checkNode(usedFields, type, node) {
1881
+ const fieldName = node.value;
1926
1882
  if (usedFields.has(fieldName)) {
1927
1883
  context.report({
1928
- loc: getLocation((node.kind === graphql.Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
1929
- offsetEnd: node.kind === graphql.Kind.VARIABLE_DEFINITION ? 0 : 1,
1930
- }),
1884
+ node,
1931
1885
  messageId: NO_DUPLICATE_FIELDS,
1932
1886
  data: {
1933
1887
  type,
@@ -1943,21 +1897,20 @@ const rule$8 = {
1943
1897
  OperationDefinition(node) {
1944
1898
  const set = new Set();
1945
1899
  for (const varDef of node.variableDefinitions) {
1946
- checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
1900
+ checkNode(set, 'Operation variable', varDef.variable.name);
1947
1901
  }
1948
1902
  },
1949
1903
  Field(node) {
1950
1904
  const set = new Set();
1951
1905
  for (const arg of node.arguments) {
1952
- checkNode(set, arg.name.value, 'Field argument', arg);
1906
+ checkNode(set, 'Field argument', arg.name);
1953
1907
  }
1954
1908
  },
1955
1909
  SelectionSet(node) {
1956
- var _a;
1957
1910
  const set = new Set();
1958
1911
  for (const selection of node.selections) {
1959
1912
  if (selection.kind === graphql.Kind.FIELD) {
1960
- checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
1913
+ checkNode(set, 'Field', selection.alias || selection.name);
1961
1914
  }
1962
1915
  }
1963
1916
  },
@@ -2016,7 +1969,7 @@ const rule$9 = {
2016
1969
  schema: [],
2017
1970
  },
2018
1971
  create(context) {
2019
- const selector = `${graphql.Kind.DOCUMENT}[definitions.0.kind!=/^(${graphql.Kind.OPERATION_DEFINITION}|${graphql.Kind.FRAGMENT_DEFINITION})$/]`;
1972
+ const selector = 'Document[definitions.0.kind!=/^(OperationDefinition|FragmentDefinition)$/]';
2020
1973
  return {
2021
1974
  [selector](node) {
2022
1975
  const rawNode = node.rawNode();
@@ -2029,7 +1982,10 @@ const rule$9 = {
2029
1982
  if (!isEslintComment && line !== prev.line && next.kind === graphql.TokenKind.NAME && linesAfter < 2) {
2030
1983
  context.report({
2031
1984
  messageId: HASHTAG_COMMENT,
2032
- loc: getLocation({ start: { line, column } }),
1985
+ loc: {
1986
+ line,
1987
+ column: column - 1,
1988
+ },
2033
1989
  });
2034
1990
  }
2035
1991
  }
@@ -2112,7 +2068,7 @@ const rule$a = {
2112
2068
  [selector](node) {
2113
2069
  const typeName = node.value;
2114
2070
  context.report({
2115
- loc: getLocation(node.loc, typeName),
2071
+ node,
2116
2072
  message: `Root type "${typeName}" is forbidden`,
2117
2073
  });
2118
2074
  },
@@ -2165,7 +2121,7 @@ const rule$b = {
2165
2121
  const graphQLType = schema.getType(typeName);
2166
2122
  if (graphql.isScalarType(graphQLType)) {
2167
2123
  context.report({
2168
- loc: getLocation(node.loc, typeName),
2124
+ node,
2169
2125
  message: `Unexpected scalar result type "${typeName}"`,
2170
2126
  });
2171
2127
  }
@@ -2221,7 +2177,7 @@ const rule$c = {
2221
2177
  typeName,
2222
2178
  },
2223
2179
  messageId: NO_TYPENAME_PREFIX,
2224
- loc: getLocation(field.loc, lowerTypeName),
2180
+ node: field.name,
2225
2181
  });
2226
2182
  }
2227
2183
  }
@@ -2299,7 +2255,7 @@ const rule$d = {
2299
2255
  const typeName = node.name.value;
2300
2256
  if (!reachableTypes.has(typeName)) {
2301
2257
  context.report({
2302
- loc: getLocation(node.name.loc, typeName, { offsetStart: node.kind === graphql.Kind.DIRECTIVE_DEFINITION ? 2 : 1 }),
2258
+ node: node.name,
2303
2259
  messageId: UNREACHABLE_TYPE,
2304
2260
  data: { typeName },
2305
2261
  suggest: [
@@ -2388,7 +2344,7 @@ const rule$e = {
2388
2344
  return;
2389
2345
  }
2390
2346
  context.report({
2391
- loc: getLocation(node.loc, fieldName),
2347
+ node: node.name,
2392
2348
  messageId: UNUSED_FIELD,
2393
2349
  data: { fieldName },
2394
2350
  suggest: [
@@ -2441,8 +2397,51 @@ function getBaseType(type) {
2441
2397
  }
2442
2398
  return type;
2443
2399
  }
2444
- function convertRange(gqlLocation) {
2445
- return [gqlLocation.start, gqlLocation.end];
2400
+ function convertToken(token, type) {
2401
+ const { line, column, end, start, value } = token;
2402
+ return {
2403
+ type,
2404
+ value,
2405
+ /*
2406
+ * ESLint has 0-based column number
2407
+ * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
2408
+ */
2409
+ loc: {
2410
+ start: {
2411
+ line,
2412
+ column: column - 1,
2413
+ },
2414
+ end: {
2415
+ line,
2416
+ column: column - 1 + (end - start),
2417
+ },
2418
+ },
2419
+ range: [start, end],
2420
+ };
2421
+ }
2422
+ function getLexer(source) {
2423
+ // GraphQL v14
2424
+ const gqlLanguage = require('graphql/language');
2425
+ if (gqlLanguage && gqlLanguage.createLexer) {
2426
+ return gqlLanguage.createLexer(source, {});
2427
+ }
2428
+ // GraphQL v15
2429
+ const { Lexer: LexerCls } = require('graphql');
2430
+ if (LexerCls && typeof LexerCls === 'function') {
2431
+ return new LexerCls(source);
2432
+ }
2433
+ throw new Error('Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!');
2434
+ }
2435
+ function extractTokens(source) {
2436
+ const lexer = getLexer(source);
2437
+ const tokens = [];
2438
+ let token = lexer.advance();
2439
+ while (token && token.kind !== graphql.TokenKind.EOF) {
2440
+ const result = convertToken(token, token.kind);
2441
+ tokens.push(result);
2442
+ token = lexer.advance();
2443
+ }
2444
+ return tokens;
2446
2445
  }
2447
2446
  function extractCommentsFromAst(loc) {
2448
2447
  if (!loc) {
@@ -2451,35 +2450,16 @@ function extractCommentsFromAst(loc) {
2451
2450
  const comments = [];
2452
2451
  let token = loc.startToken;
2453
2452
  while (token !== null) {
2454
- const { kind, value, line, column, start, end, next } = token;
2455
- if (kind === graphql.TokenKind.COMMENT) {
2456
- comments.push({
2457
- type: 'Block',
2458
- value,
2459
- loc: {
2460
- start: { line, column },
2461
- end: { line, column },
2462
- },
2463
- range: [start, end],
2464
- });
2453
+ if (token.kind === graphql.TokenKind.COMMENT) {
2454
+ const comment = convertToken(token,
2455
+ // `eslint-disable` directive works only with `Block` type comment
2456
+ token.value.trimStart().startsWith('eslint') ? 'Block' : 'Line');
2457
+ comments.push(comment);
2465
2458
  }
2466
- token = next;
2459
+ token = token.next;
2467
2460
  }
2468
2461
  return comments;
2469
2462
  }
2470
- function convertLocation(gqlLocation) {
2471
- return {
2472
- start: {
2473
- column: gqlLocation.startToken.column,
2474
- line: gqlLocation.startToken.line,
2475
- },
2476
- end: {
2477
- column: gqlLocation.endToken.column,
2478
- line: gqlLocation.endToken.line,
2479
- },
2480
- source: gqlLocation.source.body,
2481
- };
2482
- }
2483
2463
  function isNodeWithDescription(obj) {
2484
2464
  var _a;
2485
2465
  return (_a = obj) === null || _a === void 0 ? void 0 : _a.description;
@@ -2565,7 +2545,7 @@ const rule$f = {
2565
2545
  const deletionDateNode = node.arguments.find(arg => arg.name.value === argName);
2566
2546
  if (!deletionDateNode) {
2567
2547
  context.report({
2568
- loc: getLocation(node.loc, node.name.value, { offsetEnd: 0 }),
2548
+ node: node.name,
2569
2549
  messageId: MESSAGE_REQUIRE_DATE,
2570
2550
  });
2571
2551
  return;
@@ -2593,7 +2573,7 @@ const rule$f = {
2593
2573
  const canRemove = Date.now() > deletionDateInMS;
2594
2574
  if (canRemove) {
2595
2575
  context.report({
2596
- node,
2576
+ node: node.parent.name,
2597
2577
  messageId: MESSAGE_CAN_BE_REMOVED,
2598
2578
  data: {
2599
2579
  nodeName: node.parent.name.value,
@@ -2650,7 +2630,7 @@ const rule$g = {
2650
2630
  const value = reasonArg ? String(valueFromNode(reasonArg.value) || '').trim() : null;
2651
2631
  if (!value) {
2652
2632
  context.report({
2653
- loc: getLocation(node.loc, node.name.value, { offsetEnd: 0 }),
2633
+ node: node.name,
2654
2634
  message: 'Directive "@deprecated" must have a reason!',
2655
2635
  });
2656
2636
  }
@@ -2662,12 +2642,40 @@ const rule$g = {
2662
2642
  const RULE_ID$2 = 'require-description';
2663
2643
  const ALLOWED_KINDS$1 = [
2664
2644
  ...TYPES_KINDS,
2645
+ graphql.Kind.DIRECTIVE_DEFINITION,
2665
2646
  graphql.Kind.FIELD_DEFINITION,
2666
2647
  graphql.Kind.INPUT_VALUE_DEFINITION,
2667
2648
  graphql.Kind.ENUM_VALUE_DEFINITION,
2668
- graphql.Kind.DIRECTIVE_DEFINITION,
2669
2649
  graphql.Kind.OPERATION_DEFINITION,
2670
2650
  ];
2651
+ function getNodeName(node) {
2652
+ const DisplayNodeNameMap = {
2653
+ [graphql.Kind.OBJECT_TYPE_DEFINITION]: 'type',
2654
+ [graphql.Kind.INTERFACE_TYPE_DEFINITION]: 'interface',
2655
+ [graphql.Kind.ENUM_TYPE_DEFINITION]: 'enum',
2656
+ [graphql.Kind.SCALAR_TYPE_DEFINITION]: 'scalar',
2657
+ [graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
2658
+ [graphql.Kind.UNION_TYPE_DEFINITION]: 'union',
2659
+ [graphql.Kind.DIRECTIVE_DEFINITION]: 'directive',
2660
+ };
2661
+ switch (node.kind) {
2662
+ case graphql.Kind.OBJECT_TYPE_DEFINITION:
2663
+ case graphql.Kind.INTERFACE_TYPE_DEFINITION:
2664
+ case graphql.Kind.ENUM_TYPE_DEFINITION:
2665
+ case graphql.Kind.SCALAR_TYPE_DEFINITION:
2666
+ case graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION:
2667
+ case graphql.Kind.UNION_TYPE_DEFINITION:
2668
+ return `${DisplayNodeNameMap[node.kind]} ${node.name.value}`;
2669
+ case graphql.Kind.DIRECTIVE_DEFINITION:
2670
+ return `${DisplayNodeNameMap[node.kind]} @${node.name.value}`;
2671
+ case graphql.Kind.FIELD_DEFINITION:
2672
+ case graphql.Kind.INPUT_VALUE_DEFINITION:
2673
+ case graphql.Kind.ENUM_VALUE_DEFINITION:
2674
+ return `${node.parent.name.value}.${node.name.value}`;
2675
+ case graphql.Kind.OPERATION_DEFINITION:
2676
+ return node.name ? `${node.operation} ${node.name.value}` : node.operation;
2677
+ }
2678
+ }
2671
2679
  const rule$h = {
2672
2680
  meta: {
2673
2681
  docs: {
@@ -2720,7 +2728,7 @@ const rule$h = {
2720
2728
  },
2721
2729
  type: 'suggestion',
2722
2730
  messages: {
2723
- [RULE_ID$2]: 'Description is required for nodes of type "{{ nodeType }}"',
2731
+ [RULE_ID$2]: 'Description is required for `{{ nodeName }}`.',
2724
2732
  },
2725
2733
  schema: {
2726
2734
  type: 'array',
@@ -2779,10 +2787,10 @@ const rule$h = {
2779
2787
  }
2780
2788
  if (description.length === 0) {
2781
2789
  context.report({
2782
- loc: isOperation ? getLocation(node.loc, node.operation) : getLocation(node.name.loc, node.name.value),
2790
+ loc: isOperation ? getLocation(node.loc, node.operation) : node.name.loc,
2783
2791
  messageId: RULE_ID$2,
2784
2792
  data: {
2785
- nodeType: node.kind,
2793
+ nodeName: getNodeName(node),
2786
2794
  },
2787
2795
  });
2788
2796
  }
@@ -2852,7 +2860,7 @@ const rule$i = {
2852
2860
  const hasQueryType = fields.some(field => getTypeName(field) === queryType.name);
2853
2861
  if (!hasQueryType) {
2854
2862
  context.report({
2855
- loc: getLocation(node.loc, typeName),
2863
+ node,
2856
2864
  message: `Mutation result type "${graphQLType.name}" must contain field of type "${queryType.name}"`,
2857
2865
  });
2858
2866
  }
@@ -2872,16 +2880,30 @@ function convertToESTree(node, typeInfo) {
2872
2880
  function hasTypeField(obj) {
2873
2881
  return obj && !!obj.type;
2874
2882
  }
2875
- /**
2876
- * Strips tokens information from `location` object - this is needed since it's created as linked list in GraphQL-JS,
2877
- * causing eslint to fail on circular JSON
2878
- * @param location
2879
- */
2880
- function stripTokens(location) {
2881
- return {
2882
- end: location.end,
2883
- start: location.start,
2883
+ function convertLocation(location) {
2884
+ const { startToken, endToken, source, start, end } = location;
2885
+ /*
2886
+ * ESLint has 0-based column number
2887
+ * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
2888
+ */
2889
+ const loc = {
2890
+ start: {
2891
+ /*
2892
+ * Kind.Document has startToken: { line: 0, column: 0 }, we set line as 1 and column as 0
2893
+ */
2894
+ line: startToken.line === 0 ? 1 : startToken.line,
2895
+ column: startToken.column === 0 ? 0 : startToken.column - 1,
2896
+ },
2897
+ end: {
2898
+ line: endToken.line,
2899
+ column: endToken.column - 1,
2900
+ },
2901
+ source: source.body,
2884
2902
  };
2903
+ if (loc.start.column === loc.end.column) {
2904
+ loc.end.column += end - start;
2905
+ }
2906
+ return loc;
2885
2907
  }
2886
2908
  const convertNode = (typeInfo) => (node, key, parent) => {
2887
2909
  const calculatedTypeInfo = typeInfo
@@ -2901,7 +2923,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2901
2923
  typeInfo: () => calculatedTypeInfo,
2902
2924
  leadingComments: convertDescription(node),
2903
2925
  loc: convertLocation(node.loc),
2904
- range: convertRange(node.loc),
2926
+ range: [node.loc.start, node.loc.end],
2905
2927
  };
2906
2928
  if (hasTypeField(node)) {
2907
2929
  const { type: gqlType, loc: gqlLocation, ...rest } = node;
@@ -2926,7 +2948,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2926
2948
  }
2927
2949
  return parent[key];
2928
2950
  },
2929
- gqlLocation: stripTokens(gqlLocation),
2930
2951
  };
2931
2952
  return estreeNode;
2932
2953
  }
@@ -2950,7 +2971,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2950
2971
  }
2951
2972
  return parent[key];
2952
2973
  },
2953
- gqlLocation: stripTokens(gqlLocation),
2954
2974
  };
2955
2975
  return estreeNode;
2956
2976
  }
@@ -3201,9 +3221,13 @@ const rule$k = {
3201
3221
  };
3202
3222
  checkFn({
3203
3223
  getDocument: () => document,
3204
- reportError: (error) => {
3224
+ reportError(error) {
3225
+ const { line, column } = error.locations[0];
3205
3226
  context.report({
3206
- loc: getLocation({ start: error.locations[0] }),
3227
+ loc: {
3228
+ line,
3229
+ column: column - 1,
3230
+ },
3207
3231
  message: error.message,
3208
3232
  });
3209
3233
  },
@@ -3378,7 +3402,7 @@ const rule$l = {
3378
3402
  // we can extend this rule later.
3379
3403
  if (validIds.length !== 1) {
3380
3404
  context.report({
3381
- loc: getLocation(node.name.loc, typeName),
3405
+ node: node.name,
3382
3406
  messageId: RULE_ID$5,
3383
3407
  data: {
3384
3408
  typeName,
@@ -3414,7 +3438,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3414
3438
  .map(f => `\t${path.relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
3415
3439
  .join('\n'),
3416
3440
  },
3417
- loc: getLocation(node.name.loc, documentName),
3441
+ node: node.name,
3418
3442
  });
3419
3443
  }
3420
3444
  };
@@ -3859,7 +3883,8 @@ function getReachableTypes(schema) {
3859
3883
  graphql.visit(astNode, visitor);
3860
3884
  }
3861
3885
  }
3862
- else {
3886
+ else if (type.astNode) {
3887
+ // astNode can be undefined for ID, String, Boolean
3863
3888
  graphql.visit(type.astNode, visitor);
3864
3889
  }
3865
3890
  };
@@ -3878,7 +3903,8 @@ function getReachableTypes(schema) {
3878
3903
  schema.getMutationType(),
3879
3904
  schema.getSubscriptionType(),
3880
3905
  ]) {
3881
- if (type) {
3906
+ // if schema don't have Query type, schema.astNode will be undefined
3907
+ if (type === null || type === void 0 ? void 0 : type.astNode) {
3882
3908
  graphql.visit(type.astNode, visitor);
3883
3909
  }
3884
3910
  }
@@ -3968,6 +3994,15 @@ function parseForESLint(code, options = {}) {
3968
3994
  }
3969
3995
  }
3970
3996
 
3997
+ function indentCode(code, indent = 4) {
3998
+ return code.replace(/^/gm, ' '.repeat(indent));
3999
+ }
4000
+ function printCode(code) {
4001
+ return codeFrame.codeFrameColumns(code, { start: { line: 0, column: 0 } }, {
4002
+ linesAbove: Number.POSITIVE_INFINITY,
4003
+ linesBelow: Number.POSITIVE_INFINITY,
4004
+ });
4005
+ }
3971
4006
  class GraphQLRuleTester extends eslint.RuleTester {
3972
4007
  constructor(parserOptions = {}) {
3973
4008
  const config = {
@@ -4023,15 +4058,14 @@ class GraphQLRuleTester extends eslint.RuleTester {
4023
4058
  if (message.fatal) {
4024
4059
  throw new Error(message.message);
4025
4060
  }
4026
- messageForSnapshot.push(`Error ${index + 1}/${messages.length}`, visualizeEslintMessage(code, message));
4061
+ messageForSnapshot.push(`❌ Error ${index + 1}/${messages.length}`, visualizeEslintMessage(code, message));
4027
4062
  }
4028
4063
  if (rule.meta.fixable) {
4029
4064
  const { fixed, output } = linter.verifyAndFix(code, verifyConfig, { filename });
4030
4065
  if (fixed) {
4031
- messageForSnapshot.push('Autofix output', dedent(output));
4066
+ messageForSnapshot.push('🔧 Autofix output', indentCode(printCode(output), 2));
4032
4067
  }
4033
4068
  }
4034
- // eslint-disable-next-line no-undef
4035
4069
  expect(messageForSnapshot.join('\n\n')).toMatchSnapshot();
4036
4070
  }
4037
4071
  }
@@ -4088,10 +4122,10 @@ function visualizeEslintMessage(text, result) {
4088
4122
  exports.GraphQLRuleTester = GraphQLRuleTester;
4089
4123
  exports.configs = configs;
4090
4124
  exports.convertDescription = convertDescription;
4091
- exports.convertLocation = convertLocation;
4092
- exports.convertRange = convertRange;
4093
4125
  exports.convertToESTree = convertToESTree;
4126
+ exports.convertToken = convertToken;
4094
4127
  exports.extractCommentsFromAst = extractCommentsFromAst;
4128
+ exports.extractTokens = extractTokens;
4095
4129
  exports.getBaseType = getBaseType;
4096
4130
  exports.isNodeWithDescription = isNodeWithDescription;
4097
4131
  exports.parse = parse;