@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.mjs CHANGED
@@ -11,7 +11,6 @@ import { loadConfigSync, GraphQLConfig } from 'graphql-config';
11
11
  import { CodeFileLoader } from '@graphql-tools/code-file-loader';
12
12
  import { RuleTester, Linter } from 'eslint';
13
13
  import { codeFrameColumns } from '@babel/code-frame';
14
- import dedent from 'dedent';
15
14
 
16
15
  const base = {
17
16
  parser: '@graphql-eslint/eslint-plugin',
@@ -203,43 +202,6 @@ function requireUsedFieldsFromContext(ruleName, context) {
203
202
  const siblings = requireSiblingsOperations(ruleName, context);
204
203
  return context.parserServices.usedFields(schema, siblings);
205
204
  }
206
- function getLexer(source) {
207
- // GraphQL v14
208
- const gqlLanguage = require('graphql/language');
209
- if (gqlLanguage && gqlLanguage.createLexer) {
210
- return gqlLanguage.createLexer(source, {});
211
- }
212
- // GraphQL v15
213
- const { Lexer: LexerCls } = require('graphql');
214
- if (LexerCls && typeof LexerCls === 'function') {
215
- return new LexerCls(source);
216
- }
217
- throw new Error(`Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!`);
218
- }
219
- function extractTokens(source) {
220
- const lexer = getLexer(source);
221
- const tokens = [];
222
- let token = lexer.advance();
223
- while (token && token.kind !== '<EOF>') {
224
- tokens.push({
225
- type: token.kind,
226
- loc: {
227
- start: {
228
- line: token.line,
229
- column: token.column,
230
- },
231
- end: {
232
- line: token.line,
233
- column: token.column,
234
- },
235
- },
236
- value: token.value,
237
- range: [token.start, token.end],
238
- });
239
- token = lexer.advance();
240
- }
241
- return tokens;
242
- }
243
205
  const normalizePath = (path) => (path || '').replace(/\\/g, '/');
244
206
  /**
245
207
  * https://github.com/prettier/eslint-plugin-prettier/blob/76bd45ece6d56eb52f75db6b4a1efdd2efb56392/eslint-plugin-prettier.js#L71
@@ -309,21 +271,16 @@ const convertCase = (style, str) => {
309
271
  return lowerCase(str).replace(/ /g, '-');
310
272
  }
311
273
  };
312
- function getLocation(loc, fieldName = '', offset) {
313
- const { start } = loc;
314
- /*
315
- * ESLint has 0-based column number
316
- * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
317
- */
318
- const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
274
+ function getLocation(loc, fieldName = '') {
275
+ const { line, column } = loc.start;
319
276
  return {
320
277
  start: {
321
- line: start.line,
322
- column: start.column - offsetStart,
278
+ line,
279
+ column,
323
280
  },
324
281
  end: {
325
- line: start.line,
326
- column: start.column - offsetEnd + fieldName.length,
282
+ line,
283
+ column: column + fieldName.length,
327
284
  },
328
285
  };
329
286
  }
@@ -337,23 +294,16 @@ function validateDocument(context, schema = null, documentNode, rule) {
337
294
  ? validate(schema, documentNode, [rule])
338
295
  : validateSDL(documentNode, null, [rule]);
339
296
  for (const error of validationErrors) {
340
- /*
341
- * TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
342
- * Example: loc.end always equal loc.start
343
- * {
344
- * token: {
345
- * type: 'Name',
346
- * loc: { start: { line: 4, column: 13 }, end: { line: 4, column: 13 } },
347
- * value: 'veryBad',
348
- * range: [ 40, 47 ]
349
- * }
350
- * }
351
- */
352
297
  const { line, column } = error.locations[0];
353
298
  const ancestors = context.getAncestors();
354
- const token = ancestors[0].tokens.find(token => token.loc.start.line === line && token.loc.start.column === column);
299
+ const token = ancestors[0].tokens.find(token => token.loc.start.line === line && token.loc.start.column === column - 1);
355
300
  context.report({
356
- loc: getLocation({ start: error.locations[0] }, token === null || token === void 0 ? void 0 : token.value),
301
+ loc: token
302
+ ? token.loc
303
+ : {
304
+ line,
305
+ column: column - 1,
306
+ },
357
307
  message: error.message,
358
308
  });
359
309
  }
@@ -879,7 +829,7 @@ const rule = {
879
829
  }
880
830
  const isVariableNode = currNode.kind === Kind.VARIABLE;
881
831
  context.report({
882
- loc: getLocation(currNode.name.loc, currName, { offsetStart: isVariableNode ? 2 : 1 }),
832
+ node: currNode.name,
883
833
  messageId: ALPHABETIZE,
884
834
  data: isVariableNode
885
835
  ? {
@@ -1090,7 +1040,7 @@ const rule$2 = {
1090
1040
  if (shouldCheckType(node.parent.parent)) {
1091
1041
  const name = node.name.value;
1092
1042
  context.report({
1093
- loc: getLocation(node.loc, name),
1043
+ node: node.name,
1094
1044
  message: `Input "${name}" should be called "input"`,
1095
1045
  });
1096
1046
  }
@@ -1112,7 +1062,7 @@ const rule$2 = {
1112
1062
  if ((options.caseSensitiveInputType && node.name.value !== mutationName) ||
1113
1063
  name.toLowerCase() !== mutationName.toLowerCase()) {
1114
1064
  context.report({
1115
- loc: getLocation(node.loc, name),
1065
+ node: node.name,
1116
1066
  message: `InputType "${name}" name should be "${mutationName}"`,
1117
1067
  });
1118
1068
  }
@@ -1126,7 +1076,7 @@ const rule$2 = {
1126
1076
  const MATCH_EXTENSION = 'MATCH_EXTENSION';
1127
1077
  const MATCH_STYLE = 'MATCH_STYLE';
1128
1078
  const ACCEPTED_EXTENSIONS = ['.gql', '.graphql'];
1129
- const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case'];
1079
+ const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case', 'matchDocumentStyle'];
1130
1080
  const schemaOption = {
1131
1081
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1132
1082
  };
@@ -1300,7 +1250,14 @@ const rule$3 = {
1300
1250
  option = { style: option };
1301
1251
  }
1302
1252
  const expectedExtension = options.fileExtension || fileExtension;
1303
- const expectedFilename = (option.style ? convertCase(option.style, docName) : filename) + (option.suffix || '') + expectedExtension;
1253
+ let expectedFilename;
1254
+ if (option.style) {
1255
+ expectedFilename = option.style === 'matchDocumentStyle' ? docName : convertCase(option.style, docName);
1256
+ }
1257
+ else {
1258
+ expectedFilename = filename;
1259
+ }
1260
+ expectedFilename += (option.suffix || '') + expectedExtension;
1304
1261
  const filenameWithExtension = filename + expectedExtension;
1305
1262
  if (expectedFilename !== filenameWithExtension) {
1306
1263
  context.report({
@@ -1557,7 +1514,7 @@ const rule$4 = {
1557
1514
  const [trailingUnderscores] = nodeName.match(/_*$/);
1558
1515
  const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
1559
1516
  context.report({
1560
- loc: getLocation(node.loc, node.value),
1517
+ node,
1561
1518
  message: `${nodeType} "${nodeName}" should ${errorMessage}`,
1562
1519
  suggest: [
1563
1520
  {
@@ -1615,7 +1572,7 @@ const rule$4 = {
1615
1572
  const name = node.value;
1616
1573
  const renameToName = name.replace(new RegExp(isLeading ? '^_+' : '_+$'), '');
1617
1574
  context.report({
1618
- loc: getLocation(node.loc, name),
1575
+ node,
1619
1576
  message: `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`,
1620
1577
  suggest: [
1621
1578
  {
@@ -1731,7 +1688,7 @@ const rule$6 = {
1731
1688
  for (const duplicate of duplicates) {
1732
1689
  const enumName = duplicate.name.value;
1733
1690
  context.report({
1734
- loc: getLocation(duplicate.loc, enumName),
1691
+ node: duplicate.name,
1735
1692
  message: `Case-insensitive enum values duplicates are not allowed! Found: "${enumName}"`,
1736
1693
  });
1737
1694
  }
@@ -1825,9 +1782,8 @@ const rule$7 = {
1825
1782
  const typeInfo = node.typeInfo();
1826
1783
  if (typeInfo && typeInfo.enumValue) {
1827
1784
  if (typeInfo.enumValue.deprecationReason) {
1828
- const enumValueName = node.value;
1829
1785
  context.report({
1830
- loc: getLocation(node.loc, enumValueName),
1786
+ node,
1831
1787
  messageId: NO_DEPRECATED,
1832
1788
  data: {
1833
1789
  type: 'enum value',
@@ -1842,9 +1798,8 @@ const rule$7 = {
1842
1798
  const typeInfo = node.typeInfo();
1843
1799
  if (typeInfo && typeInfo.fieldDef) {
1844
1800
  if (typeInfo.fieldDef.deprecationReason) {
1845
- const fieldName = node.name.value;
1846
1801
  context.report({
1847
- loc: getLocation(node.loc, fieldName),
1802
+ node: node.name,
1848
1803
  messageId: NO_DEPRECATED,
1849
1804
  data: {
1850
1805
  type: 'field',
@@ -1916,12 +1871,11 @@ const rule$8 = {
1916
1871
  schema: [],
1917
1872
  },
1918
1873
  create(context) {
1919
- function checkNode(usedFields, fieldName, type, node) {
1874
+ function checkNode(usedFields, type, node) {
1875
+ const fieldName = node.value;
1920
1876
  if (usedFields.has(fieldName)) {
1921
1877
  context.report({
1922
- loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
1923
- offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
1924
- }),
1878
+ node,
1925
1879
  messageId: NO_DUPLICATE_FIELDS,
1926
1880
  data: {
1927
1881
  type,
@@ -1937,21 +1891,20 @@ const rule$8 = {
1937
1891
  OperationDefinition(node) {
1938
1892
  const set = new Set();
1939
1893
  for (const varDef of node.variableDefinitions) {
1940
- checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
1894
+ checkNode(set, 'Operation variable', varDef.variable.name);
1941
1895
  }
1942
1896
  },
1943
1897
  Field(node) {
1944
1898
  const set = new Set();
1945
1899
  for (const arg of node.arguments) {
1946
- checkNode(set, arg.name.value, 'Field argument', arg);
1900
+ checkNode(set, 'Field argument', arg.name);
1947
1901
  }
1948
1902
  },
1949
1903
  SelectionSet(node) {
1950
- var _a;
1951
1904
  const set = new Set();
1952
1905
  for (const selection of node.selections) {
1953
1906
  if (selection.kind === Kind.FIELD) {
1954
- checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
1907
+ checkNode(set, 'Field', selection.alias || selection.name);
1955
1908
  }
1956
1909
  }
1957
1910
  },
@@ -2010,7 +1963,7 @@ const rule$9 = {
2010
1963
  schema: [],
2011
1964
  },
2012
1965
  create(context) {
2013
- const selector = `${Kind.DOCUMENT}[definitions.0.kind!=/^(${Kind.OPERATION_DEFINITION}|${Kind.FRAGMENT_DEFINITION})$/]`;
1966
+ const selector = 'Document[definitions.0.kind!=/^(OperationDefinition|FragmentDefinition)$/]';
2014
1967
  return {
2015
1968
  [selector](node) {
2016
1969
  const rawNode = node.rawNode();
@@ -2023,7 +1976,10 @@ const rule$9 = {
2023
1976
  if (!isEslintComment && line !== prev.line && next.kind === TokenKind.NAME && linesAfter < 2) {
2024
1977
  context.report({
2025
1978
  messageId: HASHTAG_COMMENT,
2026
- loc: getLocation({ start: { line, column } }),
1979
+ loc: {
1980
+ line,
1981
+ column: column - 1,
1982
+ },
2027
1983
  });
2028
1984
  }
2029
1985
  }
@@ -2106,7 +2062,7 @@ const rule$a = {
2106
2062
  [selector](node) {
2107
2063
  const typeName = node.value;
2108
2064
  context.report({
2109
- loc: getLocation(node.loc, typeName),
2065
+ node,
2110
2066
  message: `Root type "${typeName}" is forbidden`,
2111
2067
  });
2112
2068
  },
@@ -2159,7 +2115,7 @@ const rule$b = {
2159
2115
  const graphQLType = schema.getType(typeName);
2160
2116
  if (isScalarType(graphQLType)) {
2161
2117
  context.report({
2162
- loc: getLocation(node.loc, typeName),
2118
+ node,
2163
2119
  message: `Unexpected scalar result type "${typeName}"`,
2164
2120
  });
2165
2121
  }
@@ -2215,7 +2171,7 @@ const rule$c = {
2215
2171
  typeName,
2216
2172
  },
2217
2173
  messageId: NO_TYPENAME_PREFIX,
2218
- loc: getLocation(field.loc, lowerTypeName),
2174
+ node: field.name,
2219
2175
  });
2220
2176
  }
2221
2177
  }
@@ -2293,7 +2249,7 @@ const rule$d = {
2293
2249
  const typeName = node.name.value;
2294
2250
  if (!reachableTypes.has(typeName)) {
2295
2251
  context.report({
2296
- loc: getLocation(node.name.loc, typeName, { offsetStart: node.kind === Kind.DIRECTIVE_DEFINITION ? 2 : 1 }),
2252
+ node: node.name,
2297
2253
  messageId: UNREACHABLE_TYPE,
2298
2254
  data: { typeName },
2299
2255
  suggest: [
@@ -2382,7 +2338,7 @@ const rule$e = {
2382
2338
  return;
2383
2339
  }
2384
2340
  context.report({
2385
- loc: getLocation(node.loc, fieldName),
2341
+ node: node.name,
2386
2342
  messageId: UNUSED_FIELD,
2387
2343
  data: { fieldName },
2388
2344
  suggest: [
@@ -2435,8 +2391,51 @@ function getBaseType(type) {
2435
2391
  }
2436
2392
  return type;
2437
2393
  }
2438
- function convertRange(gqlLocation) {
2439
- return [gqlLocation.start, gqlLocation.end];
2394
+ function convertToken(token, type) {
2395
+ const { line, column, end, start, value } = token;
2396
+ return {
2397
+ type,
2398
+ value,
2399
+ /*
2400
+ * ESLint has 0-based column number
2401
+ * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
2402
+ */
2403
+ loc: {
2404
+ start: {
2405
+ line,
2406
+ column: column - 1,
2407
+ },
2408
+ end: {
2409
+ line,
2410
+ column: column - 1 + (end - start),
2411
+ },
2412
+ },
2413
+ range: [start, end],
2414
+ };
2415
+ }
2416
+ function getLexer(source) {
2417
+ // GraphQL v14
2418
+ const gqlLanguage = require('graphql/language');
2419
+ if (gqlLanguage && gqlLanguage.createLexer) {
2420
+ return gqlLanguage.createLexer(source, {});
2421
+ }
2422
+ // GraphQL v15
2423
+ const { Lexer: LexerCls } = require('graphql');
2424
+ if (LexerCls && typeof LexerCls === 'function') {
2425
+ return new LexerCls(source);
2426
+ }
2427
+ throw new Error('Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!');
2428
+ }
2429
+ function extractTokens(source) {
2430
+ const lexer = getLexer(source);
2431
+ const tokens = [];
2432
+ let token = lexer.advance();
2433
+ while (token && token.kind !== TokenKind.EOF) {
2434
+ const result = convertToken(token, token.kind);
2435
+ tokens.push(result);
2436
+ token = lexer.advance();
2437
+ }
2438
+ return tokens;
2440
2439
  }
2441
2440
  function extractCommentsFromAst(loc) {
2442
2441
  if (!loc) {
@@ -2445,35 +2444,16 @@ function extractCommentsFromAst(loc) {
2445
2444
  const comments = [];
2446
2445
  let token = loc.startToken;
2447
2446
  while (token !== null) {
2448
- const { kind, value, line, column, start, end, next } = token;
2449
- if (kind === TokenKind.COMMENT) {
2450
- comments.push({
2451
- type: 'Block',
2452
- value,
2453
- loc: {
2454
- start: { line, column },
2455
- end: { line, column },
2456
- },
2457
- range: [start, end],
2458
- });
2447
+ if (token.kind === TokenKind.COMMENT) {
2448
+ const comment = convertToken(token,
2449
+ // `eslint-disable` directive works only with `Block` type comment
2450
+ token.value.trimStart().startsWith('eslint') ? 'Block' : 'Line');
2451
+ comments.push(comment);
2459
2452
  }
2460
- token = next;
2453
+ token = token.next;
2461
2454
  }
2462
2455
  return comments;
2463
2456
  }
2464
- function convertLocation(gqlLocation) {
2465
- return {
2466
- start: {
2467
- column: gqlLocation.startToken.column,
2468
- line: gqlLocation.startToken.line,
2469
- },
2470
- end: {
2471
- column: gqlLocation.endToken.column,
2472
- line: gqlLocation.endToken.line,
2473
- },
2474
- source: gqlLocation.source.body,
2475
- };
2476
- }
2477
2457
  function isNodeWithDescription(obj) {
2478
2458
  var _a;
2479
2459
  return (_a = obj) === null || _a === void 0 ? void 0 : _a.description;
@@ -2559,7 +2539,7 @@ const rule$f = {
2559
2539
  const deletionDateNode = node.arguments.find(arg => arg.name.value === argName);
2560
2540
  if (!deletionDateNode) {
2561
2541
  context.report({
2562
- loc: getLocation(node.loc, node.name.value, { offsetEnd: 0 }),
2542
+ node: node.name,
2563
2543
  messageId: MESSAGE_REQUIRE_DATE,
2564
2544
  });
2565
2545
  return;
@@ -2587,7 +2567,7 @@ const rule$f = {
2587
2567
  const canRemove = Date.now() > deletionDateInMS;
2588
2568
  if (canRemove) {
2589
2569
  context.report({
2590
- node,
2570
+ node: node.parent.name,
2591
2571
  messageId: MESSAGE_CAN_BE_REMOVED,
2592
2572
  data: {
2593
2573
  nodeName: node.parent.name.value,
@@ -2644,7 +2624,7 @@ const rule$g = {
2644
2624
  const value = reasonArg ? String(valueFromNode(reasonArg.value) || '').trim() : null;
2645
2625
  if (!value) {
2646
2626
  context.report({
2647
- loc: getLocation(node.loc, node.name.value, { offsetEnd: 0 }),
2627
+ node: node.name,
2648
2628
  message: 'Directive "@deprecated" must have a reason!',
2649
2629
  });
2650
2630
  }
@@ -2656,12 +2636,40 @@ const rule$g = {
2656
2636
  const RULE_ID$2 = 'require-description';
2657
2637
  const ALLOWED_KINDS$1 = [
2658
2638
  ...TYPES_KINDS,
2639
+ Kind.DIRECTIVE_DEFINITION,
2659
2640
  Kind.FIELD_DEFINITION,
2660
2641
  Kind.INPUT_VALUE_DEFINITION,
2661
2642
  Kind.ENUM_VALUE_DEFINITION,
2662
- Kind.DIRECTIVE_DEFINITION,
2663
2643
  Kind.OPERATION_DEFINITION,
2664
2644
  ];
2645
+ function getNodeName(node) {
2646
+ const DisplayNodeNameMap = {
2647
+ [Kind.OBJECT_TYPE_DEFINITION]: 'type',
2648
+ [Kind.INTERFACE_TYPE_DEFINITION]: 'interface',
2649
+ [Kind.ENUM_TYPE_DEFINITION]: 'enum',
2650
+ [Kind.SCALAR_TYPE_DEFINITION]: 'scalar',
2651
+ [Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
2652
+ [Kind.UNION_TYPE_DEFINITION]: 'union',
2653
+ [Kind.DIRECTIVE_DEFINITION]: 'directive',
2654
+ };
2655
+ switch (node.kind) {
2656
+ case Kind.OBJECT_TYPE_DEFINITION:
2657
+ case Kind.INTERFACE_TYPE_DEFINITION:
2658
+ case Kind.ENUM_TYPE_DEFINITION:
2659
+ case Kind.SCALAR_TYPE_DEFINITION:
2660
+ case Kind.INPUT_OBJECT_TYPE_DEFINITION:
2661
+ case Kind.UNION_TYPE_DEFINITION:
2662
+ return `${DisplayNodeNameMap[node.kind]} ${node.name.value}`;
2663
+ case Kind.DIRECTIVE_DEFINITION:
2664
+ return `${DisplayNodeNameMap[node.kind]} @${node.name.value}`;
2665
+ case Kind.FIELD_DEFINITION:
2666
+ case Kind.INPUT_VALUE_DEFINITION:
2667
+ case Kind.ENUM_VALUE_DEFINITION:
2668
+ return `${node.parent.name.value}.${node.name.value}`;
2669
+ case Kind.OPERATION_DEFINITION:
2670
+ return node.name ? `${node.operation} ${node.name.value}` : node.operation;
2671
+ }
2672
+ }
2665
2673
  const rule$h = {
2666
2674
  meta: {
2667
2675
  docs: {
@@ -2714,7 +2722,7 @@ const rule$h = {
2714
2722
  },
2715
2723
  type: 'suggestion',
2716
2724
  messages: {
2717
- [RULE_ID$2]: 'Description is required for nodes of type "{{ nodeType }}"',
2725
+ [RULE_ID$2]: 'Description is required for `{{ nodeName }}`.',
2718
2726
  },
2719
2727
  schema: {
2720
2728
  type: 'array',
@@ -2773,10 +2781,10 @@ const rule$h = {
2773
2781
  }
2774
2782
  if (description.length === 0) {
2775
2783
  context.report({
2776
- loc: isOperation ? getLocation(node.loc, node.operation) : getLocation(node.name.loc, node.name.value),
2784
+ loc: isOperation ? getLocation(node.loc, node.operation) : node.name.loc,
2777
2785
  messageId: RULE_ID$2,
2778
2786
  data: {
2779
- nodeType: node.kind,
2787
+ nodeName: getNodeName(node),
2780
2788
  },
2781
2789
  });
2782
2790
  }
@@ -2846,7 +2854,7 @@ const rule$i = {
2846
2854
  const hasQueryType = fields.some(field => getTypeName(field) === queryType.name);
2847
2855
  if (!hasQueryType) {
2848
2856
  context.report({
2849
- loc: getLocation(node.loc, typeName),
2857
+ node,
2850
2858
  message: `Mutation result type "${graphQLType.name}" must contain field of type "${queryType.name}"`,
2851
2859
  });
2852
2860
  }
@@ -2866,16 +2874,30 @@ function convertToESTree(node, typeInfo) {
2866
2874
  function hasTypeField(obj) {
2867
2875
  return obj && !!obj.type;
2868
2876
  }
2869
- /**
2870
- * Strips tokens information from `location` object - this is needed since it's created as linked list in GraphQL-JS,
2871
- * causing eslint to fail on circular JSON
2872
- * @param location
2873
- */
2874
- function stripTokens(location) {
2875
- return {
2876
- end: location.end,
2877
- start: location.start,
2877
+ function convertLocation(location) {
2878
+ const { startToken, endToken, source, start, end } = location;
2879
+ /*
2880
+ * ESLint has 0-based column number
2881
+ * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
2882
+ */
2883
+ const loc = {
2884
+ start: {
2885
+ /*
2886
+ * Kind.Document has startToken: { line: 0, column: 0 }, we set line as 1 and column as 0
2887
+ */
2888
+ line: startToken.line === 0 ? 1 : startToken.line,
2889
+ column: startToken.column === 0 ? 0 : startToken.column - 1,
2890
+ },
2891
+ end: {
2892
+ line: endToken.line,
2893
+ column: endToken.column - 1,
2894
+ },
2895
+ source: source.body,
2878
2896
  };
2897
+ if (loc.start.column === loc.end.column) {
2898
+ loc.end.column += end - start;
2899
+ }
2900
+ return loc;
2879
2901
  }
2880
2902
  const convertNode = (typeInfo) => (node, key, parent) => {
2881
2903
  const calculatedTypeInfo = typeInfo
@@ -2895,7 +2917,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2895
2917
  typeInfo: () => calculatedTypeInfo,
2896
2918
  leadingComments: convertDescription(node),
2897
2919
  loc: convertLocation(node.loc),
2898
- range: convertRange(node.loc),
2920
+ range: [node.loc.start, node.loc.end],
2899
2921
  };
2900
2922
  if (hasTypeField(node)) {
2901
2923
  const { type: gqlType, loc: gqlLocation, ...rest } = node;
@@ -2920,7 +2942,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2920
2942
  }
2921
2943
  return parent[key];
2922
2944
  },
2923
- gqlLocation: stripTokens(gqlLocation),
2924
2945
  };
2925
2946
  return estreeNode;
2926
2947
  }
@@ -2944,7 +2965,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2944
2965
  }
2945
2966
  return parent[key];
2946
2967
  },
2947
- gqlLocation: stripTokens(gqlLocation),
2948
2968
  };
2949
2969
  return estreeNode;
2950
2970
  }
@@ -3195,9 +3215,13 @@ const rule$k = {
3195
3215
  };
3196
3216
  checkFn({
3197
3217
  getDocument: () => document,
3198
- reportError: (error) => {
3218
+ reportError(error) {
3219
+ const { line, column } = error.locations[0];
3199
3220
  context.report({
3200
- loc: getLocation({ start: error.locations[0] }),
3221
+ loc: {
3222
+ line,
3223
+ column: column - 1,
3224
+ },
3201
3225
  message: error.message,
3202
3226
  });
3203
3227
  },
@@ -3372,7 +3396,7 @@ const rule$l = {
3372
3396
  // we can extend this rule later.
3373
3397
  if (validIds.length !== 1) {
3374
3398
  context.report({
3375
- loc: getLocation(node.name.loc, typeName),
3399
+ node: node.name,
3376
3400
  messageId: RULE_ID$5,
3377
3401
  data: {
3378
3402
  typeName,
@@ -3408,7 +3432,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3408
3432
  .map(f => `\t${relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
3409
3433
  .join('\n'),
3410
3434
  },
3411
- loc: getLocation(node.name.loc, documentName),
3435
+ node: node.name,
3412
3436
  });
3413
3437
  }
3414
3438
  };
@@ -3853,7 +3877,8 @@ function getReachableTypes(schema) {
3853
3877
  visit(astNode, visitor);
3854
3878
  }
3855
3879
  }
3856
- else {
3880
+ else if (type.astNode) {
3881
+ // astNode can be undefined for ID, String, Boolean
3857
3882
  visit(type.astNode, visitor);
3858
3883
  }
3859
3884
  };
@@ -3872,7 +3897,8 @@ function getReachableTypes(schema) {
3872
3897
  schema.getMutationType(),
3873
3898
  schema.getSubscriptionType(),
3874
3899
  ]) {
3875
- if (type) {
3900
+ // if schema don't have Query type, schema.astNode will be undefined
3901
+ if (type === null || type === void 0 ? void 0 : type.astNode) {
3876
3902
  visit(type.astNode, visitor);
3877
3903
  }
3878
3904
  }
@@ -3962,6 +3988,15 @@ function parseForESLint(code, options = {}) {
3962
3988
  }
3963
3989
  }
3964
3990
 
3991
+ function indentCode(code, indent = 4) {
3992
+ return code.replace(/^/gm, ' '.repeat(indent));
3993
+ }
3994
+ function printCode(code) {
3995
+ return codeFrameColumns(code, { start: { line: 0, column: 0 } }, {
3996
+ linesAbove: Number.POSITIVE_INFINITY,
3997
+ linesBelow: Number.POSITIVE_INFINITY,
3998
+ });
3999
+ }
3965
4000
  class GraphQLRuleTester extends RuleTester {
3966
4001
  constructor(parserOptions = {}) {
3967
4002
  const config = {
@@ -4017,15 +4052,14 @@ class GraphQLRuleTester extends RuleTester {
4017
4052
  if (message.fatal) {
4018
4053
  throw new Error(message.message);
4019
4054
  }
4020
- messageForSnapshot.push(`Error ${index + 1}/${messages.length}`, visualizeEslintMessage(code, message));
4055
+ messageForSnapshot.push(`❌ Error ${index + 1}/${messages.length}`, visualizeEslintMessage(code, message));
4021
4056
  }
4022
4057
  if (rule.meta.fixable) {
4023
4058
  const { fixed, output } = linter.verifyAndFix(code, verifyConfig, { filename });
4024
4059
  if (fixed) {
4025
- messageForSnapshot.push('Autofix output', dedent(output));
4060
+ messageForSnapshot.push('🔧 Autofix output', indentCode(printCode(output), 2));
4026
4061
  }
4027
4062
  }
4028
- // eslint-disable-next-line no-undef
4029
4063
  expect(messageForSnapshot.join('\n\n')).toMatchSnapshot();
4030
4064
  }
4031
4065
  }
@@ -4079,4 +4113,4 @@ function visualizeEslintMessage(text, result) {
4079
4113
  });
4080
4114
  }
4081
4115
 
4082
- export { GraphQLRuleTester, configs, convertDescription, convertLocation, convertRange, convertToESTree, extractCommentsFromAst, getBaseType, isNodeWithDescription, parse, parseForESLint, processors, rules, valueFromNode };
4116
+ export { GraphQLRuleTester, configs, convertDescription, convertToESTree, convertToken, extractCommentsFromAst, extractTokens, getBaseType, isNodeWithDescription, parse, parseForESLint, processors, rules, valueFromNode };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-eslint/eslint-plugin",
3
- "version": "3.8.0-alpha-2d2b247.0",
3
+ "version": "3.8.0",
4
4
  "description": "GraphQL plugin for ESLint",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
@@ -12,7 +12,6 @@
12
12
  "@graphql-tools/graphql-tag-pluck": "7.1.5",
13
13
  "@graphql-tools/utils": "8.6.1",
14
14
  "chalk": "4.1.2",
15
- "dedent": "0.7.0",
16
15
  "graphql-config": "4.1.0",
17
16
  "graphql-depth-limit": "1.1.0",
18
17
  "lodash.lowercase": "4.3.0"