@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/docs/parser.md +1 -1
- package/docs/rules/alphabetize.md +2 -0
- package/estree-parser/estree-ast.d.ts +1 -2
- package/estree-parser/utils.d.ts +8 -5
- package/index.js +200 -158
- package/index.mjs +199 -157
- package/package.json +1 -1
- package/rules/alphabetize.d.ts +1 -1
- package/rules/index.d.ts +1 -7
- package/testkit.d.ts +0 -1
- package/utils.d.ts +4 -8
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 = ''
|
312
|
-
const {
|
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
|
321
|
-
column
|
278
|
+
line,
|
279
|
+
column,
|
322
280
|
},
|
323
281
|
end: {
|
324
|
-
line
|
325
|
-
column:
|
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:
|
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:
|
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
|
-
|
847
|
-
for (
|
848
|
-
const
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
1874
|
+
function checkNode(usedFields, type, node) {
|
1875
|
+
const fieldName = node.value;
|
1893
1876
|
if (usedFields.has(fieldName)) {
|
1894
1877
|
context.report({
|
1895
|
-
|
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,
|
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,
|
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,
|
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 =
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
2412
|
-
|
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
|
-
|
2422
|
-
|
2423
|
-
|
2424
|
-
|
2425
|
-
|
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
|
-
|
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
|
-
|
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) :
|
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
|
-
|
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
|
-
|
2872
|
-
|
2873
|
-
|
2874
|
-
|
2875
|
-
|
2876
|
-
|
2877
|
-
|
2878
|
-
|
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:
|
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
|
3218
|
+
reportError(error) {
|
3219
|
+
const { line, column } = error.locations[0];
|
3200
3220
|
context.report({
|
3201
|
-
loc:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
4017
|
-
|
4018
|
-
|
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,
|
4114
|
+
export { GraphQLRuleTester, configs, convertDescription, convertToESTree, convertToken, extractCommentsFromAst, extractTokens, getBaseType, isNodeWithDescription, parse, parseForESLint, processors, rules, valueFromNode };
|
package/package.json
CHANGED
package/rules/alphabetize.d.ts
CHANGED
@@ -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;
|