@graphql-eslint/eslint-plugin 3.8.0-alpha-db02c77.0 → 3.8.0-alpha-269d044.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 +0 -2
- 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 +219 -183
- package/index.mjs +218 -182
- 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
|
}
|
@@ -686,10 +636,7 @@ const rule = {
|
|
686
636
|
fixable: 'code',
|
687
637
|
docs: {
|
688
638
|
category: ['Schema', 'Operations'],
|
689
|
-
description:
|
690
|
-
'Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.',
|
691
|
-
'> Note: autofix will work only for fields without comments (between or around)',
|
692
|
-
].join('\n\n'),
|
639
|
+
description: `Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.`,
|
693
640
|
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/alphabetize.md',
|
694
641
|
examples: [
|
695
642
|
{
|
@@ -847,8 +794,27 @@ const rule = {
|
|
847
794
|
},
|
848
795
|
create(context) {
|
849
796
|
var _a, _b, _c, _d, _e;
|
850
|
-
|
851
|
-
|
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]];
|
852
818
|
}
|
853
819
|
function checkNodes(nodes) {
|
854
820
|
// Starts from 1, ignore nodes.length <= 1
|
@@ -857,39 +823,27 @@ const rule = {
|
|
857
823
|
const currNode = nodes[i];
|
858
824
|
const prevName = prevNode.name.value;
|
859
825
|
const currName = currNode.name.value;
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
loc: getLocation(currNode.loc, currName, { offsetEnd: isVariableNode ? 0 : 1 }),
|
864
|
-
messageId: ALPHABETIZE,
|
865
|
-
data: isVariableNode
|
866
|
-
? {
|
867
|
-
currName: `$${currName}`,
|
868
|
-
prevName: `$${prevName}`,
|
869
|
-
}
|
870
|
-
: { currName, prevName },
|
871
|
-
*fix(fixer) {
|
872
|
-
const prev = prevNode;
|
873
|
-
const curr = currNode;
|
874
|
-
const sourceCode = context.getSourceCode();
|
875
|
-
const beforeComments = sourceCode.getCommentsBefore(prev);
|
876
|
-
if (beforeComments.length > 0) {
|
877
|
-
const tokenBefore = sourceCode.getTokenBefore(prev);
|
878
|
-
const lastBeforeComment = beforeComments[beforeComments.length - 1];
|
879
|
-
if (!tokenBefore || !isOnSameLineNodeAndComment(tokenBefore, lastBeforeComment))
|
880
|
-
return;
|
881
|
-
}
|
882
|
-
const betweenComments = sourceCode.getCommentsBefore(curr);
|
883
|
-
if (betweenComments.length > 0)
|
884
|
-
return;
|
885
|
-
const [firstAfterComment] = sourceCode.getCommentsAfter(curr);
|
886
|
-
if (firstAfterComment && isOnSameLineNodeAndComment(curr, firstAfterComment))
|
887
|
-
return;
|
888
|
-
yield fixer.replaceText(prev, sourceCode.getText(curr));
|
889
|
-
yield fixer.replaceText(curr, sourceCode.getText(prev));
|
890
|
-
},
|
891
|
-
});
|
826
|
+
// Compare with lexicographic order
|
827
|
+
if (prevName.localeCompare(currName) !== 1) {
|
828
|
+
continue;
|
892
829
|
}
|
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
|
+
});
|
893
847
|
}
|
894
848
|
}
|
895
849
|
const opts = context.options[0];
|
@@ -1086,7 +1040,7 @@ const rule$2 = {
|
|
1086
1040
|
if (shouldCheckType(node.parent.parent)) {
|
1087
1041
|
const name = node.name.value;
|
1088
1042
|
context.report({
|
1089
|
-
|
1043
|
+
node: node.name,
|
1090
1044
|
message: `Input "${name}" should be called "input"`,
|
1091
1045
|
});
|
1092
1046
|
}
|
@@ -1108,7 +1062,7 @@ const rule$2 = {
|
|
1108
1062
|
if ((options.caseSensitiveInputType && node.name.value !== mutationName) ||
|
1109
1063
|
name.toLowerCase() !== mutationName.toLowerCase()) {
|
1110
1064
|
context.report({
|
1111
|
-
|
1065
|
+
node: node.name,
|
1112
1066
|
message: `InputType "${name}" name should be "${mutationName}"`,
|
1113
1067
|
});
|
1114
1068
|
}
|
@@ -1122,7 +1076,7 @@ const rule$2 = {
|
|
1122
1076
|
const MATCH_EXTENSION = 'MATCH_EXTENSION';
|
1123
1077
|
const MATCH_STYLE = 'MATCH_STYLE';
|
1124
1078
|
const ACCEPTED_EXTENSIONS = ['.gql', '.graphql'];
|
1125
|
-
const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case'];
|
1079
|
+
const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case', 'matchDocumentStyle'];
|
1126
1080
|
const schemaOption = {
|
1127
1081
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1128
1082
|
};
|
@@ -1296,7 +1250,14 @@ const rule$3 = {
|
|
1296
1250
|
option = { style: option };
|
1297
1251
|
}
|
1298
1252
|
const expectedExtension = options.fileExtension || fileExtension;
|
1299
|
-
|
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;
|
1300
1261
|
const filenameWithExtension = filename + expectedExtension;
|
1301
1262
|
if (expectedFilename !== filenameWithExtension) {
|
1302
1263
|
context.report({
|
@@ -1553,7 +1514,7 @@ const rule$4 = {
|
|
1553
1514
|
const [trailingUnderscores] = nodeName.match(/_*$/);
|
1554
1515
|
const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
|
1555
1516
|
context.report({
|
1556
|
-
|
1517
|
+
node,
|
1557
1518
|
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
1558
1519
|
suggest: [
|
1559
1520
|
{
|
@@ -1611,7 +1572,7 @@ const rule$4 = {
|
|
1611
1572
|
const name = node.value;
|
1612
1573
|
const renameToName = name.replace(new RegExp(isLeading ? '^_+' : '_+$'), '');
|
1613
1574
|
context.report({
|
1614
|
-
|
1575
|
+
node,
|
1615
1576
|
message: `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`,
|
1616
1577
|
suggest: [
|
1617
1578
|
{
|
@@ -1727,7 +1688,7 @@ const rule$6 = {
|
|
1727
1688
|
for (const duplicate of duplicates) {
|
1728
1689
|
const enumName = duplicate.name.value;
|
1729
1690
|
context.report({
|
1730
|
-
|
1691
|
+
node: duplicate.name,
|
1731
1692
|
message: `Case-insensitive enum values duplicates are not allowed! Found: "${enumName}"`,
|
1732
1693
|
});
|
1733
1694
|
}
|
@@ -1821,9 +1782,8 @@ const rule$7 = {
|
|
1821
1782
|
const typeInfo = node.typeInfo();
|
1822
1783
|
if (typeInfo && typeInfo.enumValue) {
|
1823
1784
|
if (typeInfo.enumValue.deprecationReason) {
|
1824
|
-
const enumValueName = node.value;
|
1825
1785
|
context.report({
|
1826
|
-
|
1786
|
+
node,
|
1827
1787
|
messageId: NO_DEPRECATED,
|
1828
1788
|
data: {
|
1829
1789
|
type: 'enum value',
|
@@ -1838,9 +1798,8 @@ const rule$7 = {
|
|
1838
1798
|
const typeInfo = node.typeInfo();
|
1839
1799
|
if (typeInfo && typeInfo.fieldDef) {
|
1840
1800
|
if (typeInfo.fieldDef.deprecationReason) {
|
1841
|
-
const fieldName = node.name.value;
|
1842
1801
|
context.report({
|
1843
|
-
|
1802
|
+
node: node.name,
|
1844
1803
|
messageId: NO_DEPRECATED,
|
1845
1804
|
data: {
|
1846
1805
|
type: 'field',
|
@@ -1912,12 +1871,11 @@ const rule$8 = {
|
|
1912
1871
|
schema: [],
|
1913
1872
|
},
|
1914
1873
|
create(context) {
|
1915
|
-
function checkNode(usedFields,
|
1874
|
+
function checkNode(usedFields, type, node) {
|
1875
|
+
const fieldName = node.value;
|
1916
1876
|
if (usedFields.has(fieldName)) {
|
1917
1877
|
context.report({
|
1918
|
-
|
1919
|
-
offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
|
1920
|
-
}),
|
1878
|
+
node,
|
1921
1879
|
messageId: NO_DUPLICATE_FIELDS,
|
1922
1880
|
data: {
|
1923
1881
|
type,
|
@@ -1933,21 +1891,20 @@ const rule$8 = {
|
|
1933
1891
|
OperationDefinition(node) {
|
1934
1892
|
const set = new Set();
|
1935
1893
|
for (const varDef of node.variableDefinitions) {
|
1936
|
-
checkNode(set,
|
1894
|
+
checkNode(set, 'Operation variable', varDef.variable.name);
|
1937
1895
|
}
|
1938
1896
|
},
|
1939
1897
|
Field(node) {
|
1940
1898
|
const set = new Set();
|
1941
1899
|
for (const arg of node.arguments) {
|
1942
|
-
checkNode(set,
|
1900
|
+
checkNode(set, 'Field argument', arg.name);
|
1943
1901
|
}
|
1944
1902
|
},
|
1945
1903
|
SelectionSet(node) {
|
1946
|
-
var _a;
|
1947
1904
|
const set = new Set();
|
1948
1905
|
for (const selection of node.selections) {
|
1949
1906
|
if (selection.kind === Kind.FIELD) {
|
1950
|
-
checkNode(set,
|
1907
|
+
checkNode(set, 'Field', selection.alias || selection.name);
|
1951
1908
|
}
|
1952
1909
|
}
|
1953
1910
|
},
|
@@ -2006,7 +1963,7 @@ const rule$9 = {
|
|
2006
1963
|
schema: [],
|
2007
1964
|
},
|
2008
1965
|
create(context) {
|
2009
|
-
const selector =
|
1966
|
+
const selector = 'Document[definitions.0.kind!=/^(OperationDefinition|FragmentDefinition)$/]';
|
2010
1967
|
return {
|
2011
1968
|
[selector](node) {
|
2012
1969
|
const rawNode = node.rawNode();
|
@@ -2019,7 +1976,10 @@ const rule$9 = {
|
|
2019
1976
|
if (!isEslintComment && line !== prev.line && next.kind === TokenKind.NAME && linesAfter < 2) {
|
2020
1977
|
context.report({
|
2021
1978
|
messageId: HASHTAG_COMMENT,
|
2022
|
-
loc:
|
1979
|
+
loc: {
|
1980
|
+
line,
|
1981
|
+
column: column - 1,
|
1982
|
+
},
|
2023
1983
|
});
|
2024
1984
|
}
|
2025
1985
|
}
|
@@ -2102,7 +2062,7 @@ const rule$a = {
|
|
2102
2062
|
[selector](node) {
|
2103
2063
|
const typeName = node.value;
|
2104
2064
|
context.report({
|
2105
|
-
|
2065
|
+
node,
|
2106
2066
|
message: `Root type "${typeName}" is forbidden`,
|
2107
2067
|
});
|
2108
2068
|
},
|
@@ -2155,7 +2115,7 @@ const rule$b = {
|
|
2155
2115
|
const graphQLType = schema.getType(typeName);
|
2156
2116
|
if (isScalarType(graphQLType)) {
|
2157
2117
|
context.report({
|
2158
|
-
|
2118
|
+
node,
|
2159
2119
|
message: `Unexpected scalar result type "${typeName}"`,
|
2160
2120
|
});
|
2161
2121
|
}
|
@@ -2211,7 +2171,7 @@ const rule$c = {
|
|
2211
2171
|
typeName,
|
2212
2172
|
},
|
2213
2173
|
messageId: NO_TYPENAME_PREFIX,
|
2214
|
-
|
2174
|
+
node: field.name,
|
2215
2175
|
});
|
2216
2176
|
}
|
2217
2177
|
}
|
@@ -2289,7 +2249,7 @@ const rule$d = {
|
|
2289
2249
|
const typeName = node.name.value;
|
2290
2250
|
if (!reachableTypes.has(typeName)) {
|
2291
2251
|
context.report({
|
2292
|
-
|
2252
|
+
node: node.name,
|
2293
2253
|
messageId: UNREACHABLE_TYPE,
|
2294
2254
|
data: { typeName },
|
2295
2255
|
suggest: [
|
@@ -2378,7 +2338,7 @@ const rule$e = {
|
|
2378
2338
|
return;
|
2379
2339
|
}
|
2380
2340
|
context.report({
|
2381
|
-
|
2341
|
+
node: node.name,
|
2382
2342
|
messageId: UNUSED_FIELD,
|
2383
2343
|
data: { fieldName },
|
2384
2344
|
suggest: [
|
@@ -2431,8 +2391,51 @@ function getBaseType(type) {
|
|
2431
2391
|
}
|
2432
2392
|
return type;
|
2433
2393
|
}
|
2434
|
-
function
|
2435
|
-
|
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;
|
2436
2439
|
}
|
2437
2440
|
function extractCommentsFromAst(loc) {
|
2438
2441
|
if (!loc) {
|
@@ -2441,35 +2444,16 @@ function extractCommentsFromAst(loc) {
|
|
2441
2444
|
const comments = [];
|
2442
2445
|
let token = loc.startToken;
|
2443
2446
|
while (token !== null) {
|
2444
|
-
|
2445
|
-
|
2446
|
-
|
2447
|
-
|
2448
|
-
|
2449
|
-
loc: {
|
2450
|
-
start: { line, column },
|
2451
|
-
end: { line, column },
|
2452
|
-
},
|
2453
|
-
range: [start, end],
|
2454
|
-
});
|
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);
|
2455
2452
|
}
|
2456
|
-
token = next;
|
2453
|
+
token = token.next;
|
2457
2454
|
}
|
2458
2455
|
return comments;
|
2459
2456
|
}
|
2460
|
-
function convertLocation(gqlLocation) {
|
2461
|
-
return {
|
2462
|
-
start: {
|
2463
|
-
column: gqlLocation.startToken.column,
|
2464
|
-
line: gqlLocation.startToken.line,
|
2465
|
-
},
|
2466
|
-
end: {
|
2467
|
-
column: gqlLocation.endToken.column,
|
2468
|
-
line: gqlLocation.endToken.line,
|
2469
|
-
},
|
2470
|
-
source: gqlLocation.source.body,
|
2471
|
-
};
|
2472
|
-
}
|
2473
2457
|
function isNodeWithDescription(obj) {
|
2474
2458
|
var _a;
|
2475
2459
|
return (_a = obj) === null || _a === void 0 ? void 0 : _a.description;
|
@@ -2555,7 +2539,7 @@ const rule$f = {
|
|
2555
2539
|
const deletionDateNode = node.arguments.find(arg => arg.name.value === argName);
|
2556
2540
|
if (!deletionDateNode) {
|
2557
2541
|
context.report({
|
2558
|
-
|
2542
|
+
node: node.name,
|
2559
2543
|
messageId: MESSAGE_REQUIRE_DATE,
|
2560
2544
|
});
|
2561
2545
|
return;
|
@@ -2583,7 +2567,7 @@ const rule$f = {
|
|
2583
2567
|
const canRemove = Date.now() > deletionDateInMS;
|
2584
2568
|
if (canRemove) {
|
2585
2569
|
context.report({
|
2586
|
-
node,
|
2570
|
+
node: node.parent.name,
|
2587
2571
|
messageId: MESSAGE_CAN_BE_REMOVED,
|
2588
2572
|
data: {
|
2589
2573
|
nodeName: node.parent.name.value,
|
@@ -2640,7 +2624,7 @@ const rule$g = {
|
|
2640
2624
|
const value = reasonArg ? String(valueFromNode(reasonArg.value) || '').trim() : null;
|
2641
2625
|
if (!value) {
|
2642
2626
|
context.report({
|
2643
|
-
|
2627
|
+
node: node.name,
|
2644
2628
|
message: 'Directive "@deprecated" must have a reason!',
|
2645
2629
|
});
|
2646
2630
|
}
|
@@ -2652,12 +2636,40 @@ const rule$g = {
|
|
2652
2636
|
const RULE_ID$2 = 'require-description';
|
2653
2637
|
const ALLOWED_KINDS$1 = [
|
2654
2638
|
...TYPES_KINDS,
|
2639
|
+
Kind.DIRECTIVE_DEFINITION,
|
2655
2640
|
Kind.FIELD_DEFINITION,
|
2656
2641
|
Kind.INPUT_VALUE_DEFINITION,
|
2657
2642
|
Kind.ENUM_VALUE_DEFINITION,
|
2658
|
-
Kind.DIRECTIVE_DEFINITION,
|
2659
2643
|
Kind.OPERATION_DEFINITION,
|
2660
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
|
+
}
|
2661
2673
|
const rule$h = {
|
2662
2674
|
meta: {
|
2663
2675
|
docs: {
|
@@ -2710,7 +2722,7 @@ const rule$h = {
|
|
2710
2722
|
},
|
2711
2723
|
type: 'suggestion',
|
2712
2724
|
messages: {
|
2713
|
-
[RULE_ID$2]: 'Description is required for
|
2725
|
+
[RULE_ID$2]: 'Description is required for `{{ nodeName }}`.',
|
2714
2726
|
},
|
2715
2727
|
schema: {
|
2716
2728
|
type: 'array',
|
@@ -2769,10 +2781,10 @@ const rule$h = {
|
|
2769
2781
|
}
|
2770
2782
|
if (description.length === 0) {
|
2771
2783
|
context.report({
|
2772
|
-
loc: isOperation ? getLocation(node.loc, node.operation) :
|
2784
|
+
loc: isOperation ? getLocation(node.loc, node.operation) : node.name.loc,
|
2773
2785
|
messageId: RULE_ID$2,
|
2774
2786
|
data: {
|
2775
|
-
|
2787
|
+
nodeName: getNodeName(node),
|
2776
2788
|
},
|
2777
2789
|
});
|
2778
2790
|
}
|
@@ -2842,7 +2854,7 @@ const rule$i = {
|
|
2842
2854
|
const hasQueryType = fields.some(field => getTypeName(field) === queryType.name);
|
2843
2855
|
if (!hasQueryType) {
|
2844
2856
|
context.report({
|
2845
|
-
|
2857
|
+
node,
|
2846
2858
|
message: `Mutation result type "${graphQLType.name}" must contain field of type "${queryType.name}"`,
|
2847
2859
|
});
|
2848
2860
|
}
|
@@ -2862,16 +2874,30 @@ function convertToESTree(node, typeInfo) {
|
|
2862
2874
|
function hasTypeField(obj) {
|
2863
2875
|
return obj && !!obj.type;
|
2864
2876
|
}
|
2865
|
-
|
2866
|
-
|
2867
|
-
|
2868
|
-
|
2869
|
-
|
2870
|
-
|
2871
|
-
|
2872
|
-
|
2873
|
-
|
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,
|
2874
2896
|
};
|
2897
|
+
if (loc.start.column === loc.end.column) {
|
2898
|
+
loc.end.column += end - start;
|
2899
|
+
}
|
2900
|
+
return loc;
|
2875
2901
|
}
|
2876
2902
|
const convertNode = (typeInfo) => (node, key, parent) => {
|
2877
2903
|
const calculatedTypeInfo = typeInfo
|
@@ -2891,7 +2917,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2891
2917
|
typeInfo: () => calculatedTypeInfo,
|
2892
2918
|
leadingComments: convertDescription(node),
|
2893
2919
|
loc: convertLocation(node.loc),
|
2894
|
-
range:
|
2920
|
+
range: [node.loc.start, node.loc.end],
|
2895
2921
|
};
|
2896
2922
|
if (hasTypeField(node)) {
|
2897
2923
|
const { type: gqlType, loc: gqlLocation, ...rest } = node;
|
@@ -2916,7 +2942,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2916
2942
|
}
|
2917
2943
|
return parent[key];
|
2918
2944
|
},
|
2919
|
-
gqlLocation: stripTokens(gqlLocation),
|
2920
2945
|
};
|
2921
2946
|
return estreeNode;
|
2922
2947
|
}
|
@@ -2940,7 +2965,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2940
2965
|
}
|
2941
2966
|
return parent[key];
|
2942
2967
|
},
|
2943
|
-
gqlLocation: stripTokens(gqlLocation),
|
2944
2968
|
};
|
2945
2969
|
return estreeNode;
|
2946
2970
|
}
|
@@ -3191,9 +3215,13 @@ const rule$k = {
|
|
3191
3215
|
};
|
3192
3216
|
checkFn({
|
3193
3217
|
getDocument: () => document,
|
3194
|
-
reportError
|
3218
|
+
reportError(error) {
|
3219
|
+
const { line, column } = error.locations[0];
|
3195
3220
|
context.report({
|
3196
|
-
loc:
|
3221
|
+
loc: {
|
3222
|
+
line,
|
3223
|
+
column: column - 1,
|
3224
|
+
},
|
3197
3225
|
message: error.message,
|
3198
3226
|
});
|
3199
3227
|
},
|
@@ -3368,7 +3396,7 @@ const rule$l = {
|
|
3368
3396
|
// we can extend this rule later.
|
3369
3397
|
if (validIds.length !== 1) {
|
3370
3398
|
context.report({
|
3371
|
-
|
3399
|
+
node: node.name,
|
3372
3400
|
messageId: RULE_ID$5,
|
3373
3401
|
data: {
|
3374
3402
|
typeName,
|
@@ -3404,7 +3432,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3404
3432
|
.map(f => `\t${relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
3405
3433
|
.join('\n'),
|
3406
3434
|
},
|
3407
|
-
|
3435
|
+
node: node.name,
|
3408
3436
|
});
|
3409
3437
|
}
|
3410
3438
|
};
|
@@ -3958,6 +3986,15 @@ function parseForESLint(code, options = {}) {
|
|
3958
3986
|
}
|
3959
3987
|
}
|
3960
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
|
+
}
|
3961
3998
|
class GraphQLRuleTester extends RuleTester {
|
3962
3999
|
constructor(parserOptions = {}) {
|
3963
4000
|
const config = {
|
@@ -4013,15 +4050,14 @@ class GraphQLRuleTester extends RuleTester {
|
|
4013
4050
|
if (message.fatal) {
|
4014
4051
|
throw new Error(message.message);
|
4015
4052
|
}
|
4016
|
-
messageForSnapshot.push(
|
4053
|
+
messageForSnapshot.push(`❌ Error ${index + 1}/${messages.length}`, visualizeEslintMessage(code, message));
|
4017
4054
|
}
|
4018
4055
|
if (rule.meta.fixable) {
|
4019
4056
|
const { fixed, output } = linter.verifyAndFix(code, verifyConfig, { filename });
|
4020
4057
|
if (fixed) {
|
4021
|
-
messageForSnapshot.push('Autofix output',
|
4058
|
+
messageForSnapshot.push('🔧 Autofix output', indentCode(printCode(output), 2));
|
4022
4059
|
}
|
4023
4060
|
}
|
4024
|
-
// eslint-disable-next-line no-undef
|
4025
4061
|
expect(messageForSnapshot.join('\n\n')).toMatchSnapshot();
|
4026
4062
|
}
|
4027
4063
|
}
|
@@ -4075,4 +4111,4 @@ function visualizeEslintMessage(text, result) {
|
|
4075
4111
|
});
|
4076
4112
|
}
|
4077
4113
|
|
4078
|
-
export { GraphQLRuleTester, configs, convertDescription,
|
4114
|
+
export { GraphQLRuleTester, configs, convertDescription, convertToESTree, convertToken, extractCommentsFromAst, extractTokens, getBaseType, isNodeWithDescription, parse, parseForESLint, processors, rules, valueFromNode };
|