@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/docs/parser.md +1 -1
- package/docs/rules/match-document-filename.md +2 -1
- package/estree-parser/estree-ast.d.ts +1 -2
- package/estree-parser/utils.d.ts +10 -5
- package/index.js +182 -148
- package/index.mjs +181 -147
- package/package.json +1 -2
- package/rules/match-document-filename.d.ts +2 -1
- package/rules/require-description.d.ts +1 -1
- package/utils.d.ts +4 -8
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 = ''
|
319
|
-
const {
|
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
|
328
|
-
column
|
284
|
+
line,
|
285
|
+
column,
|
329
286
|
},
|
330
287
|
end: {
|
331
|
-
line
|
332
|
-
column:
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
1880
|
+
function checkNode(usedFields, type, node) {
|
1881
|
+
const fieldName = node.value;
|
1926
1882
|
if (usedFields.has(fieldName)) {
|
1927
1883
|
context.report({
|
1928
|
-
|
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,
|
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,
|
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,
|
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 =
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
2445
|
-
|
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
|
-
|
2455
|
-
|
2456
|
-
|
2457
|
-
|
2458
|
-
|
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
|
-
|
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
|
-
|
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
|
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) :
|
2790
|
+
loc: isOperation ? getLocation(node.loc, node.operation) : node.name.loc,
|
2783
2791
|
messageId: RULE_ID$2,
|
2784
2792
|
data: {
|
2785
|
-
|
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
|
-
|
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
|
-
|
2877
|
-
|
2878
|
-
|
2879
|
-
|
2880
|
-
|
2881
|
-
|
2882
|
-
|
2883
|
-
|
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:
|
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
|
3224
|
+
reportError(error) {
|
3225
|
+
const { line, column } = error.locations[0];
|
3205
3226
|
context.report({
|
3206
|
-
loc:
|
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
|
-
|
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
|
-
|
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
|
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(
|
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',
|
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;
|