@graphql-eslint/eslint-plugin 3.8.0-alpha-fb12b01.0 → 3.8.0-alpha-8ddf2a4.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
@@ -202,43 +202,6 @@ function requireUsedFieldsFromContext(ruleName, context) {
202
202
  const siblings = requireSiblingsOperations(ruleName, context);
203
203
  return context.parserServices.usedFields(schema, siblings);
204
204
  }
205
- function getLexer(source) {
206
- // GraphQL v14
207
- const gqlLanguage = require('graphql/language');
208
- if (gqlLanguage && gqlLanguage.createLexer) {
209
- return gqlLanguage.createLexer(source, {});
210
- }
211
- // GraphQL v15
212
- const { Lexer: LexerCls } = require('graphql');
213
- if (LexerCls && typeof LexerCls === 'function') {
214
- return new LexerCls(source);
215
- }
216
- throw new Error(`Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!`);
217
- }
218
- function extractTokens(source) {
219
- const lexer = getLexer(source);
220
- const tokens = [];
221
- let token = lexer.advance();
222
- while (token && token.kind !== '<EOF>') {
223
- tokens.push({
224
- type: token.kind,
225
- loc: {
226
- start: {
227
- line: token.line,
228
- column: token.column,
229
- },
230
- end: {
231
- line: token.line,
232
- column: token.column,
233
- },
234
- },
235
- value: token.value,
236
- range: [token.start, token.end],
237
- });
238
- token = lexer.advance();
239
- }
240
- return tokens;
241
- }
242
205
  const normalizePath = (path) => (path || '').replace(/\\/g, '/');
243
206
  /**
244
207
  * https://github.com/prettier/eslint-plugin-prettier/blob/76bd45ece6d56eb52f75db6b4a1efdd2efb56392/eslint-plugin-prettier.js#L71
@@ -308,21 +271,16 @@ const convertCase = (style, str) => {
308
271
  return lowerCase(str).replace(/ /g, '-');
309
272
  }
310
273
  };
311
- function getLocation(loc, fieldName = '', offset) {
312
- const { start } = loc;
313
- /*
314
- * ESLint has 0-based column number
315
- * https://eslint.org/docs/developer-guide/working-with-rules#contextreport
316
- */
317
- const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
274
+ function getLocation(loc, fieldName = '') {
275
+ const { line, column } = loc.start;
318
276
  return {
319
277
  start: {
320
- line: start.line,
321
- column: start.column - offsetStart,
278
+ line,
279
+ column,
322
280
  },
323
281
  end: {
324
- line: start.line,
325
- column: start.column - offsetEnd + fieldName.length,
282
+ line,
283
+ column: column + fieldName.length,
326
284
  },
327
285
  };
328
286
  }
@@ -336,23 +294,16 @@ function validateDocument(context, schema = null, documentNode, rule) {
336
294
  ? validate(schema, documentNode, [rule])
337
295
  : validateSDL(documentNode, null, [rule]);
338
296
  for (const error of validationErrors) {
339
- /*
340
- * TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
341
- * Example: loc.end always equal loc.start
342
- * {
343
- * token: {
344
- * type: 'Name',
345
- * loc: { start: { line: 4, column: 13 }, end: { line: 4, column: 13 } },
346
- * value: 'veryBad',
347
- * range: [ 40, 47 ]
348
- * }
349
- * }
350
- */
351
297
  const { line, column } = error.locations[0];
352
298
  const ancestors = context.getAncestors();
353
- 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);
354
300
  context.report({
355
- 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
+ },
356
307
  message: error.message,
357
308
  });
358
309
  }
@@ -682,9 +633,10 @@ const argumentsEnum = [
682
633
  const rule = {
683
634
  meta: {
684
635
  type: 'suggestion',
636
+ fixable: 'code',
685
637
  docs: {
686
638
  category: ['Schema', 'Operations'],
687
- description: 'Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.',
639
+ description: `Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.`,
688
640
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/alphabetize.md',
689
641
  examples: [
690
642
  {
@@ -842,24 +794,56 @@ const rule = {
842
794
  },
843
795
  create(context) {
844
796
  var _a, _b, _c, _d, _e;
797
+ const sourceCode = context.getSourceCode();
798
+ function isNodeAndCommentOnSameLine(node, comment) {
799
+ return node.loc.end.line === comment.loc.start.line;
800
+ }
801
+ function getBeforeComments(node) {
802
+ const commentsBefore = sourceCode.getCommentsBefore(node);
803
+ if (commentsBefore.length === 0) {
804
+ return [];
805
+ }
806
+ const tokenBefore = sourceCode.getTokenBefore(node);
807
+ if (tokenBefore) {
808
+ return commentsBefore.filter(comment => !isNodeAndCommentOnSameLine(tokenBefore, comment));
809
+ }
810
+ return commentsBefore;
811
+ }
812
+ function getRangeWithComments(node) {
813
+ const [firstBeforeComment] = getBeforeComments(node);
814
+ const [firstAfterComment] = sourceCode.getCommentsAfter(node);
815
+ const from = firstBeforeComment || node;
816
+ const to = firstAfterComment && isNodeAndCommentOnSameLine(node, firstAfterComment) ? firstAfterComment : node;
817
+ return [from.range[0], to.range[1]];
818
+ }
845
819
  function checkNodes(nodes) {
846
- let prevName = null;
847
- for (const node of nodes) {
848
- const currName = node.name.value;
849
- if (prevName && prevName > currName) {
850
- const isVariableNode = node.kind === Kind.VARIABLE;
851
- context.report({
852
- loc: getLocation(node.loc, node.name.value, { offsetEnd: isVariableNode ? 0 : 1 }),
853
- messageId: ALPHABETIZE,
854
- data: isVariableNode
855
- ? {
856
- currName: `$${currName}`,
857
- prevName: `$${prevName}`,
858
- }
859
- : { currName, prevName },
860
- });
820
+ // Starts from 1, ignore nodes.length <= 1
821
+ for (let i = 1; i < nodes.length; i += 1) {
822
+ const prevNode = nodes[i - 1];
823
+ const currNode = nodes[i];
824
+ const prevName = prevNode.name.value;
825
+ const currName = currNode.name.value;
826
+ // Compare with lexicographic order
827
+ if (prevName.localeCompare(currName) !== 1) {
828
+ continue;
861
829
  }
862
- prevName = currName;
830
+ const isVariableNode = currNode.kind === Kind.VARIABLE;
831
+ context.report({
832
+ node: currNode.name,
833
+ messageId: ALPHABETIZE,
834
+ data: isVariableNode
835
+ ? {
836
+ currName: `$${currName}`,
837
+ prevName: `$${prevName}`,
838
+ }
839
+ : { currName, prevName },
840
+ *fix(fixer) {
841
+ const prevRange = getRangeWithComments(prevNode);
842
+ const currRange = getRangeWithComments(currNode);
843
+ yield fixer.replaceTextRange(prevRange, sourceCode.getText({ range: currRange }));
844
+ yield fixer.replaceTextRange(currRange, sourceCode.getText({ range: prevRange }));
845
+ },
846
+ });
863
847
  }
864
848
  }
865
849
  const opts = context.options[0];
@@ -1056,7 +1040,7 @@ const rule$2 = {
1056
1040
  if (shouldCheckType(node.parent.parent)) {
1057
1041
  const name = node.name.value;
1058
1042
  context.report({
1059
- loc: getLocation(node.loc, name),
1043
+ node: node.name,
1060
1044
  message: `Input "${name}" should be called "input"`,
1061
1045
  });
1062
1046
  }
@@ -1078,7 +1062,7 @@ const rule$2 = {
1078
1062
  if ((options.caseSensitiveInputType && node.name.value !== mutationName) ||
1079
1063
  name.toLowerCase() !== mutationName.toLowerCase()) {
1080
1064
  context.report({
1081
- loc: getLocation(node.loc, name),
1065
+ node: node.name,
1082
1066
  message: `InputType "${name}" name should be "${mutationName}"`,
1083
1067
  });
1084
1068
  }
@@ -1530,7 +1514,7 @@ const rule$4 = {
1530
1514
  const [trailingUnderscores] = nodeName.match(/_*$/);
1531
1515
  const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
1532
1516
  context.report({
1533
- loc: getLocation(node.loc, node.value),
1517
+ node,
1534
1518
  message: `${nodeType} "${nodeName}" should ${errorMessage}`,
1535
1519
  suggest: [
1536
1520
  {
@@ -1588,7 +1572,7 @@ const rule$4 = {
1588
1572
  const name = node.value;
1589
1573
  const renameToName = name.replace(new RegExp(isLeading ? '^_+' : '_+$'), '');
1590
1574
  context.report({
1591
- loc: getLocation(node.loc, name),
1575
+ node,
1592
1576
  message: `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`,
1593
1577
  suggest: [
1594
1578
  {
@@ -1704,7 +1688,7 @@ const rule$6 = {
1704
1688
  for (const duplicate of duplicates) {
1705
1689
  const enumName = duplicate.name.value;
1706
1690
  context.report({
1707
- loc: getLocation(duplicate.loc, enumName),
1691
+ node: duplicate.name,
1708
1692
  message: `Case-insensitive enum values duplicates are not allowed! Found: "${enumName}"`,
1709
1693
  });
1710
1694
  }
@@ -1798,9 +1782,8 @@ const rule$7 = {
1798
1782
  const typeInfo = node.typeInfo();
1799
1783
  if (typeInfo && typeInfo.enumValue) {
1800
1784
  if (typeInfo.enumValue.deprecationReason) {
1801
- const enumValueName = node.value;
1802
1785
  context.report({
1803
- loc: getLocation(node.loc, enumValueName),
1786
+ node,
1804
1787
  messageId: NO_DEPRECATED,
1805
1788
  data: {
1806
1789
  type: 'enum value',
@@ -1815,9 +1798,8 @@ const rule$7 = {
1815
1798
  const typeInfo = node.typeInfo();
1816
1799
  if (typeInfo && typeInfo.fieldDef) {
1817
1800
  if (typeInfo.fieldDef.deprecationReason) {
1818
- const fieldName = node.name.value;
1819
1801
  context.report({
1820
- loc: getLocation(node.loc, fieldName),
1802
+ node: node.name,
1821
1803
  messageId: NO_DEPRECATED,
1822
1804
  data: {
1823
1805
  type: 'field',
@@ -1889,12 +1871,11 @@ const rule$8 = {
1889
1871
  schema: [],
1890
1872
  },
1891
1873
  create(context) {
1892
- function checkNode(usedFields, fieldName, type, node) {
1874
+ function checkNode(usedFields, type, node) {
1875
+ const fieldName = node.value;
1893
1876
  if (usedFields.has(fieldName)) {
1894
1877
  context.report({
1895
- loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
1896
- offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
1897
- }),
1878
+ node,
1898
1879
  messageId: NO_DUPLICATE_FIELDS,
1899
1880
  data: {
1900
1881
  type,
@@ -1910,21 +1891,20 @@ const rule$8 = {
1910
1891
  OperationDefinition(node) {
1911
1892
  const set = new Set();
1912
1893
  for (const varDef of node.variableDefinitions) {
1913
- checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
1894
+ checkNode(set, 'Operation variable', varDef.variable.name);
1914
1895
  }
1915
1896
  },
1916
1897
  Field(node) {
1917
1898
  const set = new Set();
1918
1899
  for (const arg of node.arguments) {
1919
- checkNode(set, arg.name.value, 'Field argument', arg);
1900
+ checkNode(set, 'Field argument', arg.name);
1920
1901
  }
1921
1902
  },
1922
1903
  SelectionSet(node) {
1923
- var _a;
1924
1904
  const set = new Set();
1925
1905
  for (const selection of node.selections) {
1926
1906
  if (selection.kind === Kind.FIELD) {
1927
- 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);
1928
1908
  }
1929
1909
  }
1930
1910
  },
@@ -1983,7 +1963,7 @@ const rule$9 = {
1983
1963
  schema: [],
1984
1964
  },
1985
1965
  create(context) {
1986
- const selector = `${Kind.DOCUMENT}[definitions.0.kind!=/^(${Kind.OPERATION_DEFINITION}|${Kind.FRAGMENT_DEFINITION})$/]`;
1966
+ const selector = 'Document[definitions.0.kind!=/^(OperationDefinition|FragmentDefinition)$/]';
1987
1967
  return {
1988
1968
  [selector](node) {
1989
1969
  const rawNode = node.rawNode();
@@ -1996,7 +1976,10 @@ const rule$9 = {
1996
1976
  if (!isEslintComment && line !== prev.line && next.kind === TokenKind.NAME && linesAfter < 2) {
1997
1977
  context.report({
1998
1978
  messageId: HASHTAG_COMMENT,
1999
- loc: getLocation({ start: { line, column } }),
1979
+ loc: {
1980
+ line,
1981
+ column: column - 1,
1982
+ },
2000
1983
  });
2001
1984
  }
2002
1985
  }
@@ -2079,7 +2062,7 @@ const rule$a = {
2079
2062
  [selector](node) {
2080
2063
  const typeName = node.value;
2081
2064
  context.report({
2082
- loc: getLocation(node.loc, typeName),
2065
+ node,
2083
2066
  message: `Root type "${typeName}" is forbidden`,
2084
2067
  });
2085
2068
  },
@@ -2132,7 +2115,7 @@ const rule$b = {
2132
2115
  const graphQLType = schema.getType(typeName);
2133
2116
  if (isScalarType(graphQLType)) {
2134
2117
  context.report({
2135
- loc: getLocation(node.loc, typeName),
2118
+ node,
2136
2119
  message: `Unexpected scalar result type "${typeName}"`,
2137
2120
  });
2138
2121
  }
@@ -2188,7 +2171,7 @@ const rule$c = {
2188
2171
  typeName,
2189
2172
  },
2190
2173
  messageId: NO_TYPENAME_PREFIX,
2191
- loc: getLocation(field.loc, lowerTypeName),
2174
+ node: field.name,
2192
2175
  });
2193
2176
  }
2194
2177
  }
@@ -2266,7 +2249,7 @@ const rule$d = {
2266
2249
  const typeName = node.name.value;
2267
2250
  if (!reachableTypes.has(typeName)) {
2268
2251
  context.report({
2269
- loc: getLocation(node.name.loc, typeName, { offsetStart: node.kind === Kind.DIRECTIVE_DEFINITION ? 2 : 1 }),
2252
+ node: node.name,
2270
2253
  messageId: UNREACHABLE_TYPE,
2271
2254
  data: { typeName },
2272
2255
  suggest: [
@@ -2355,7 +2338,7 @@ const rule$e = {
2355
2338
  return;
2356
2339
  }
2357
2340
  context.report({
2358
- loc: getLocation(node.loc, fieldName),
2341
+ node: node.name,
2359
2342
  messageId: UNUSED_FIELD,
2360
2343
  data: { fieldName },
2361
2344
  suggest: [
@@ -2408,8 +2391,51 @@ function getBaseType(type) {
2408
2391
  }
2409
2392
  return type;
2410
2393
  }
2411
- function convertRange(gqlLocation) {
2412
- 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;
2413
2439
  }
2414
2440
  function extractCommentsFromAst(loc) {
2415
2441
  if (!loc) {
@@ -2418,35 +2444,16 @@ function extractCommentsFromAst(loc) {
2418
2444
  const comments = [];
2419
2445
  let token = loc.startToken;
2420
2446
  while (token !== null) {
2421
- const { kind, value, line, column, start, end, next } = token;
2422
- if (kind === TokenKind.COMMENT) {
2423
- comments.push({
2424
- type: 'Block',
2425
- value,
2426
- loc: {
2427
- start: { line, column },
2428
- end: { line, column },
2429
- },
2430
- range: [start, end],
2431
- });
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);
2432
2452
  }
2433
- token = next;
2453
+ token = token.next;
2434
2454
  }
2435
2455
  return comments;
2436
2456
  }
2437
- function convertLocation(gqlLocation) {
2438
- return {
2439
- start: {
2440
- column: gqlLocation.startToken.column,
2441
- line: gqlLocation.startToken.line,
2442
- },
2443
- end: {
2444
- column: gqlLocation.endToken.column,
2445
- line: gqlLocation.endToken.line,
2446
- },
2447
- source: gqlLocation.source.body,
2448
- };
2449
- }
2450
2457
  function isNodeWithDescription(obj) {
2451
2458
  var _a;
2452
2459
  return (_a = obj) === null || _a === void 0 ? void 0 : _a.description;
@@ -2532,7 +2539,7 @@ const rule$f = {
2532
2539
  const deletionDateNode = node.arguments.find(arg => arg.name.value === argName);
2533
2540
  if (!deletionDateNode) {
2534
2541
  context.report({
2535
- loc: getLocation(node.loc, node.name.value, { offsetEnd: 0 }),
2542
+ node: node.name,
2536
2543
  messageId: MESSAGE_REQUIRE_DATE,
2537
2544
  });
2538
2545
  return;
@@ -2560,7 +2567,7 @@ const rule$f = {
2560
2567
  const canRemove = Date.now() > deletionDateInMS;
2561
2568
  if (canRemove) {
2562
2569
  context.report({
2563
- node,
2570
+ node: node.parent.name,
2564
2571
  messageId: MESSAGE_CAN_BE_REMOVED,
2565
2572
  data: {
2566
2573
  nodeName: node.parent.name.value,
@@ -2617,7 +2624,7 @@ const rule$g = {
2617
2624
  const value = reasonArg ? String(valueFromNode(reasonArg.value) || '').trim() : null;
2618
2625
  if (!value) {
2619
2626
  context.report({
2620
- loc: getLocation(node.loc, node.name.value, { offsetEnd: 0 }),
2627
+ node: node.name,
2621
2628
  message: 'Directive "@deprecated" must have a reason!',
2622
2629
  });
2623
2630
  }
@@ -2774,7 +2781,7 @@ const rule$h = {
2774
2781
  }
2775
2782
  if (description.length === 0) {
2776
2783
  context.report({
2777
- 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,
2778
2785
  messageId: RULE_ID$2,
2779
2786
  data: {
2780
2787
  nodeName: getNodeName(node),
@@ -2847,7 +2854,7 @@ const rule$i = {
2847
2854
  const hasQueryType = fields.some(field => getTypeName(field) === queryType.name);
2848
2855
  if (!hasQueryType) {
2849
2856
  context.report({
2850
- loc: getLocation(node.loc, typeName),
2857
+ node,
2851
2858
  message: `Mutation result type "${graphQLType.name}" must contain field of type "${queryType.name}"`,
2852
2859
  });
2853
2860
  }
@@ -2867,16 +2874,30 @@ function convertToESTree(node, typeInfo) {
2867
2874
  function hasTypeField(obj) {
2868
2875
  return obj && !!obj.type;
2869
2876
  }
2870
- /**
2871
- * Strips tokens information from `location` object - this is needed since it's created as linked list in GraphQL-JS,
2872
- * causing eslint to fail on circular JSON
2873
- * @param location
2874
- */
2875
- function stripTokens(location) {
2876
- return {
2877
- end: location.end,
2878
- 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,
2879
2896
  };
2897
+ if (loc.start.column === loc.end.column) {
2898
+ loc.end.column += end - start;
2899
+ }
2900
+ return loc;
2880
2901
  }
2881
2902
  const convertNode = (typeInfo) => (node, key, parent) => {
2882
2903
  const calculatedTypeInfo = typeInfo
@@ -2896,7 +2917,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2896
2917
  typeInfo: () => calculatedTypeInfo,
2897
2918
  leadingComments: convertDescription(node),
2898
2919
  loc: convertLocation(node.loc),
2899
- range: convertRange(node.loc),
2920
+ range: [node.loc.start, node.loc.end],
2900
2921
  };
2901
2922
  if (hasTypeField(node)) {
2902
2923
  const { type: gqlType, loc: gqlLocation, ...rest } = node;
@@ -2921,7 +2942,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2921
2942
  }
2922
2943
  return parent[key];
2923
2944
  },
2924
- gqlLocation: stripTokens(gqlLocation),
2925
2945
  };
2926
2946
  return estreeNode;
2927
2947
  }
@@ -2945,7 +2965,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2945
2965
  }
2946
2966
  return parent[key];
2947
2967
  },
2948
- gqlLocation: stripTokens(gqlLocation),
2949
2968
  };
2950
2969
  return estreeNode;
2951
2970
  }
@@ -3196,9 +3215,13 @@ const rule$k = {
3196
3215
  };
3197
3216
  checkFn({
3198
3217
  getDocument: () => document,
3199
- reportError: (error) => {
3218
+ reportError(error) {
3219
+ const { line, column } = error.locations[0];
3200
3220
  context.report({
3201
- loc: getLocation({ start: error.locations[0] }),
3221
+ loc: {
3222
+ line,
3223
+ column: column - 1,
3224
+ },
3202
3225
  message: error.message,
3203
3226
  });
3204
3227
  },
@@ -3373,7 +3396,7 @@ const rule$l = {
3373
3396
  // we can extend this rule later.
3374
3397
  if (validIds.length !== 1) {
3375
3398
  context.report({
3376
- loc: getLocation(node.name.loc, typeName),
3399
+ node: node.name,
3377
3400
  messageId: RULE_ID$5,
3378
3401
  data: {
3379
3402
  typeName,
@@ -3409,7 +3432,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3409
3432
  .map(f => `\t${relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
3410
3433
  .join('\n'),
3411
3434
  },
3412
- loc: getLocation(node.name.loc, documentName),
3435
+ node: node.name,
3413
3436
  });
3414
3437
  }
3415
3438
  };
@@ -3963,6 +3986,15 @@ function parseForESLint(code, options = {}) {
3963
3986
  }
3964
3987
  }
3965
3988
 
3989
+ function indentCode(code, indent = 4) {
3990
+ return code.replace(/^/gm, ' '.repeat(indent));
3991
+ }
3992
+ function printCode(code) {
3993
+ return codeFrameColumns(code, { start: { line: 0, column: 0 } }, {
3994
+ linesAbove: Number.POSITIVE_INFINITY,
3995
+ linesBelow: Number.POSITIVE_INFINITY,
3996
+ });
3997
+ }
3966
3998
  class GraphQLRuleTester extends RuleTester {
3967
3999
  constructor(parserOptions = {}) {
3968
4000
  const config = {
@@ -4004,19 +4036,29 @@ class GraphQLRuleTester extends RuleTester {
4004
4036
  }
4005
4037
  const linter = new Linter();
4006
4038
  linter.defineRule(name, rule);
4039
+ const hasOnlyTest = tests.invalid.some(t => t.only);
4007
4040
  for (const testCase of tests.invalid) {
4041
+ const { only, code, filename } = testCase;
4042
+ if (hasOnlyTest && !only) {
4043
+ continue;
4044
+ }
4008
4045
  const verifyConfig = getVerifyConfig(name, this.config, testCase);
4009
4046
  defineParser(linter, verifyConfig.parser);
4010
- const { code, filename } = testCase;
4011
4047
  const messages = linter.verify(code, verifyConfig, { filename });
4012
- for (const message of messages) {
4048
+ const messageForSnapshot = [];
4049
+ for (const [index, message] of messages.entries()) {
4013
4050
  if (message.fatal) {
4014
4051
  throw new Error(message.message);
4015
4052
  }
4016
- const messageForSnapshot = visualizeEslintMessage(code, message);
4017
- // eslint-disable-next-line no-undef
4018
- expect(messageForSnapshot).toMatchSnapshot();
4053
+ messageForSnapshot.push(`❌ Error ${index + 1}/${messages.length}`, visualizeEslintMessage(code, message));
4054
+ }
4055
+ if (rule.meta.fixable) {
4056
+ const { fixed, output } = linter.verifyAndFix(code, verifyConfig, { filename });
4057
+ if (fixed) {
4058
+ messageForSnapshot.push('🔧 Autofix output', indentCode(printCode(output), 2));
4059
+ }
4019
4060
  }
4061
+ expect(messageForSnapshot.join('\n\n')).toMatchSnapshot();
4020
4062
  }
4021
4063
  }
4022
4064
  }
@@ -4069,4 +4111,4 @@ function visualizeEslintMessage(text, result) {
4069
4111
  });
4070
4112
  }
4071
4113
 
4072
- export { GraphQLRuleTester, configs, convertDescription, convertLocation, convertRange, convertToESTree, extractCommentsFromAst, getBaseType, isNodeWithDescription, parse, parseForESLint, processors, rules, valueFromNode };
4114
+ 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-fb12b01.0",
3
+ "version": "3.8.0-alpha-8ddf2a4.0",
4
4
  "description": "GraphQL plugin for ESLint",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
@@ -4,7 +4,7 @@ declare const valuesEnum: ['EnumTypeDefinition'];
4
4
  declare const selectionsEnum: ('OperationDefinition' | 'FragmentDefinition')[];
5
5
  declare const variablesEnum: ['OperationDefinition'];
6
6
  declare const argumentsEnum: ('FieldDefinition' | 'Field' | 'DirectiveDefinition' | 'Directive')[];
7
- declare type AlphabetizeConfig = {
7
+ export declare type AlphabetizeConfig = {
8
8
  fields?: typeof fieldsEnum;
9
9
  values?: typeof valuesEnum;
10
10
  selections?: typeof selectionsEnum;