@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.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 = ''
|
313
|
-
const {
|
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
|
322
|
-
column
|
278
|
+
line,
|
279
|
+
column,
|
323
280
|
},
|
324
281
|
end: {
|
325
|
-
line
|
326
|
-
column:
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
1874
|
+
function checkNode(usedFields, type, node) {
|
1875
|
+
const fieldName = node.value;
|
1920
1876
|
if (usedFields.has(fieldName)) {
|
1921
1877
|
context.report({
|
1922
|
-
|
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,
|
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,
|
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,
|
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 =
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
2439
|
-
|
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
|
-
|
2449
|
-
|
2450
|
-
|
2451
|
-
|
2452
|
-
|
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
|
-
|
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
|
-
|
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
|
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) :
|
2784
|
+
loc: isOperation ? getLocation(node.loc, node.operation) : node.name.loc,
|
2777
2785
|
messageId: RULE_ID$2,
|
2778
2786
|
data: {
|
2779
|
-
|
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
|
-
|
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
|
-
|
2871
|
-
|
2872
|
-
|
2873
|
-
|
2874
|
-
|
2875
|
-
|
2876
|
-
|
2877
|
-
|
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:
|
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
|
3218
|
+
reportError(error) {
|
3219
|
+
const { line, column } = error.locations[0];
|
3199
3220
|
context.report({
|
3200
|
-
loc:
|
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
|
-
|
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
|
-
|
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
|
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(
|
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',
|
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,
|
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
|
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"
|