@graphql-eslint/eslint-plugin 3.8.0-alpha-a8fcc7b.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 +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 +8 -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.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
|
}
|
@@ -692,10 +642,7 @@ const rule = {
|
|
692
642
|
fixable: 'code',
|
693
643
|
docs: {
|
694
644
|
category: ['Schema', 'Operations'],
|
695
|
-
description:
|
696
|
-
'Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.',
|
697
|
-
'> Note: autofix will work only for fields without comments (between or around)',
|
698
|
-
].join('\n\n'),
|
645
|
+
description: `Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.`,
|
699
646
|
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/alphabetize.md',
|
700
647
|
examples: [
|
701
648
|
{
|
@@ -853,8 +800,27 @@ const rule = {
|
|
853
800
|
},
|
854
801
|
create(context) {
|
855
802
|
var _a, _b, _c, _d, _e;
|
856
|
-
|
857
|
-
|
803
|
+
const sourceCode = context.getSourceCode();
|
804
|
+
function isNodeAndCommentOnSameLine(node, comment) {
|
805
|
+
return node.loc.end.line === comment.loc.start.line;
|
806
|
+
}
|
807
|
+
function getBeforeComments(node) {
|
808
|
+
const commentsBefore = sourceCode.getCommentsBefore(node);
|
809
|
+
if (commentsBefore.length === 0) {
|
810
|
+
return [];
|
811
|
+
}
|
812
|
+
const tokenBefore = sourceCode.getTokenBefore(node);
|
813
|
+
if (tokenBefore) {
|
814
|
+
return commentsBefore.filter(comment => !isNodeAndCommentOnSameLine(tokenBefore, comment));
|
815
|
+
}
|
816
|
+
return commentsBefore;
|
817
|
+
}
|
818
|
+
function getRangeWithComments(node) {
|
819
|
+
const [firstBeforeComment] = getBeforeComments(node);
|
820
|
+
const [firstAfterComment] = sourceCode.getCommentsAfter(node);
|
821
|
+
const from = firstBeforeComment || node;
|
822
|
+
const to = firstAfterComment && isNodeAndCommentOnSameLine(node, firstAfterComment) ? firstAfterComment : node;
|
823
|
+
return [from.range[0], to.range[1]];
|
858
824
|
}
|
859
825
|
function checkNodes(nodes) {
|
860
826
|
// Starts from 1, ignore nodes.length <= 1
|
@@ -863,39 +829,27 @@ const rule = {
|
|
863
829
|
const currNode = nodes[i];
|
864
830
|
const prevName = prevNode.name.value;
|
865
831
|
const currName = currNode.name.value;
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
loc: getLocation(currNode.loc, currName, { offsetEnd: isVariableNode ? 0 : 1 }),
|
870
|
-
messageId: ALPHABETIZE,
|
871
|
-
data: isVariableNode
|
872
|
-
? {
|
873
|
-
currName: `$${currName}`,
|
874
|
-
prevName: `$${prevName}`,
|
875
|
-
}
|
876
|
-
: { currName, prevName },
|
877
|
-
*fix(fixer) {
|
878
|
-
const prev = prevNode;
|
879
|
-
const curr = currNode;
|
880
|
-
const sourceCode = context.getSourceCode();
|
881
|
-
const beforeComments = sourceCode.getCommentsBefore(prev);
|
882
|
-
if (beforeComments.length > 0) {
|
883
|
-
const tokenBefore = sourceCode.getTokenBefore(prev);
|
884
|
-
const lastBeforeComment = beforeComments.at(-1);
|
885
|
-
if (!tokenBefore || !isOnSameLineNodeAndComment(tokenBefore, lastBeforeComment))
|
886
|
-
return;
|
887
|
-
}
|
888
|
-
const betweenComments = sourceCode.getCommentsBefore(curr);
|
889
|
-
if (betweenComments.length > 0)
|
890
|
-
return;
|
891
|
-
const [firstAfterComment] = sourceCode.getCommentsAfter(curr);
|
892
|
-
if (firstAfterComment && isOnSameLineNodeAndComment(curr, firstAfterComment))
|
893
|
-
return;
|
894
|
-
yield fixer.replaceText(prev, sourceCode.getText(curr));
|
895
|
-
yield fixer.replaceText(curr, sourceCode.getText(prev));
|
896
|
-
},
|
897
|
-
});
|
832
|
+
// Compare with lexicographic order
|
833
|
+
if (prevName.localeCompare(currName) !== 1) {
|
834
|
+
continue;
|
898
835
|
}
|
836
|
+
const isVariableNode = currNode.kind === graphql.Kind.VARIABLE;
|
837
|
+
context.report({
|
838
|
+
node: currNode.name,
|
839
|
+
messageId: ALPHABETIZE,
|
840
|
+
data: isVariableNode
|
841
|
+
? {
|
842
|
+
currName: `$${currName}`,
|
843
|
+
prevName: `$${prevName}`,
|
844
|
+
}
|
845
|
+
: { currName, prevName },
|
846
|
+
*fix(fixer) {
|
847
|
+
const prevRange = getRangeWithComments(prevNode);
|
848
|
+
const currRange = getRangeWithComments(currNode);
|
849
|
+
yield fixer.replaceTextRange(prevRange, sourceCode.getText({ range: currRange }));
|
850
|
+
yield fixer.replaceTextRange(currRange, sourceCode.getText({ range: prevRange }));
|
851
|
+
},
|
852
|
+
});
|
899
853
|
}
|
900
854
|
}
|
901
855
|
const opts = context.options[0];
|
@@ -1092,7 +1046,7 @@ const rule$2 = {
|
|
1092
1046
|
if (shouldCheckType(node.parent.parent)) {
|
1093
1047
|
const name = node.name.value;
|
1094
1048
|
context.report({
|
1095
|
-
|
1049
|
+
node: node.name,
|
1096
1050
|
message: `Input "${name}" should be called "input"`,
|
1097
1051
|
});
|
1098
1052
|
}
|
@@ -1114,7 +1068,7 @@ const rule$2 = {
|
|
1114
1068
|
if ((options.caseSensitiveInputType && node.name.value !== mutationName) ||
|
1115
1069
|
name.toLowerCase() !== mutationName.toLowerCase()) {
|
1116
1070
|
context.report({
|
1117
|
-
|
1071
|
+
node: node.name,
|
1118
1072
|
message: `InputType "${name}" name should be "${mutationName}"`,
|
1119
1073
|
});
|
1120
1074
|
}
|
@@ -1128,7 +1082,7 @@ const rule$2 = {
|
|
1128
1082
|
const MATCH_EXTENSION = 'MATCH_EXTENSION';
|
1129
1083
|
const MATCH_STYLE = 'MATCH_STYLE';
|
1130
1084
|
const ACCEPTED_EXTENSIONS = ['.gql', '.graphql'];
|
1131
|
-
const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case'];
|
1085
|
+
const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case', 'matchDocumentStyle'];
|
1132
1086
|
const schemaOption = {
|
1133
1087
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1134
1088
|
};
|
@@ -1302,7 +1256,14 @@ const rule$3 = {
|
|
1302
1256
|
option = { style: option };
|
1303
1257
|
}
|
1304
1258
|
const expectedExtension = options.fileExtension || fileExtension;
|
1305
|
-
|
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;
|
1306
1267
|
const filenameWithExtension = filename + expectedExtension;
|
1307
1268
|
if (expectedFilename !== filenameWithExtension) {
|
1308
1269
|
context.report({
|
@@ -1559,7 +1520,7 @@ const rule$4 = {
|
|
1559
1520
|
const [trailingUnderscores] = nodeName.match(/_*$/);
|
1560
1521
|
const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
|
1561
1522
|
context.report({
|
1562
|
-
|
1523
|
+
node,
|
1563
1524
|
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
1564
1525
|
suggest: [
|
1565
1526
|
{
|
@@ -1617,7 +1578,7 @@ const rule$4 = {
|
|
1617
1578
|
const name = node.value;
|
1618
1579
|
const renameToName = name.replace(new RegExp(isLeading ? '^_+' : '_+$'), '');
|
1619
1580
|
context.report({
|
1620
|
-
|
1581
|
+
node,
|
1621
1582
|
message: `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`,
|
1622
1583
|
suggest: [
|
1623
1584
|
{
|
@@ -1733,7 +1694,7 @@ const rule$6 = {
|
|
1733
1694
|
for (const duplicate of duplicates) {
|
1734
1695
|
const enumName = duplicate.name.value;
|
1735
1696
|
context.report({
|
1736
|
-
|
1697
|
+
node: duplicate.name,
|
1737
1698
|
message: `Case-insensitive enum values duplicates are not allowed! Found: "${enumName}"`,
|
1738
1699
|
});
|
1739
1700
|
}
|
@@ -1827,9 +1788,8 @@ const rule$7 = {
|
|
1827
1788
|
const typeInfo = node.typeInfo();
|
1828
1789
|
if (typeInfo && typeInfo.enumValue) {
|
1829
1790
|
if (typeInfo.enumValue.deprecationReason) {
|
1830
|
-
const enumValueName = node.value;
|
1831
1791
|
context.report({
|
1832
|
-
|
1792
|
+
node,
|
1833
1793
|
messageId: NO_DEPRECATED,
|
1834
1794
|
data: {
|
1835
1795
|
type: 'enum value',
|
@@ -1844,9 +1804,8 @@ const rule$7 = {
|
|
1844
1804
|
const typeInfo = node.typeInfo();
|
1845
1805
|
if (typeInfo && typeInfo.fieldDef) {
|
1846
1806
|
if (typeInfo.fieldDef.deprecationReason) {
|
1847
|
-
const fieldName = node.name.value;
|
1848
1807
|
context.report({
|
1849
|
-
|
1808
|
+
node: node.name,
|
1850
1809
|
messageId: NO_DEPRECATED,
|
1851
1810
|
data: {
|
1852
1811
|
type: 'field',
|
@@ -1918,12 +1877,11 @@ const rule$8 = {
|
|
1918
1877
|
schema: [],
|
1919
1878
|
},
|
1920
1879
|
create(context) {
|
1921
|
-
function checkNode(usedFields,
|
1880
|
+
function checkNode(usedFields, type, node) {
|
1881
|
+
const fieldName = node.value;
|
1922
1882
|
if (usedFields.has(fieldName)) {
|
1923
1883
|
context.report({
|
1924
|
-
|
1925
|
-
offsetEnd: node.kind === graphql.Kind.VARIABLE_DEFINITION ? 0 : 1,
|
1926
|
-
}),
|
1884
|
+
node,
|
1927
1885
|
messageId: NO_DUPLICATE_FIELDS,
|
1928
1886
|
data: {
|
1929
1887
|
type,
|
@@ -1939,21 +1897,20 @@ const rule$8 = {
|
|
1939
1897
|
OperationDefinition(node) {
|
1940
1898
|
const set = new Set();
|
1941
1899
|
for (const varDef of node.variableDefinitions) {
|
1942
|
-
checkNode(set,
|
1900
|
+
checkNode(set, 'Operation variable', varDef.variable.name);
|
1943
1901
|
}
|
1944
1902
|
},
|
1945
1903
|
Field(node) {
|
1946
1904
|
const set = new Set();
|
1947
1905
|
for (const arg of node.arguments) {
|
1948
|
-
checkNode(set,
|
1906
|
+
checkNode(set, 'Field argument', arg.name);
|
1949
1907
|
}
|
1950
1908
|
},
|
1951
1909
|
SelectionSet(node) {
|
1952
|
-
var _a;
|
1953
1910
|
const set = new Set();
|
1954
1911
|
for (const selection of node.selections) {
|
1955
1912
|
if (selection.kind === graphql.Kind.FIELD) {
|
1956
|
-
checkNode(set,
|
1913
|
+
checkNode(set, 'Field', selection.alias || selection.name);
|
1957
1914
|
}
|
1958
1915
|
}
|
1959
1916
|
},
|
@@ -2012,7 +1969,7 @@ const rule$9 = {
|
|
2012
1969
|
schema: [],
|
2013
1970
|
},
|
2014
1971
|
create(context) {
|
2015
|
-
const selector =
|
1972
|
+
const selector = 'Document[definitions.0.kind!=/^(OperationDefinition|FragmentDefinition)$/]';
|
2016
1973
|
return {
|
2017
1974
|
[selector](node) {
|
2018
1975
|
const rawNode = node.rawNode();
|
@@ -2025,7 +1982,10 @@ const rule$9 = {
|
|
2025
1982
|
if (!isEslintComment && line !== prev.line && next.kind === graphql.TokenKind.NAME && linesAfter < 2) {
|
2026
1983
|
context.report({
|
2027
1984
|
messageId: HASHTAG_COMMENT,
|
2028
|
-
loc:
|
1985
|
+
loc: {
|
1986
|
+
line,
|
1987
|
+
column: column - 1,
|
1988
|
+
},
|
2029
1989
|
});
|
2030
1990
|
}
|
2031
1991
|
}
|
@@ -2108,7 +2068,7 @@ const rule$a = {
|
|
2108
2068
|
[selector](node) {
|
2109
2069
|
const typeName = node.value;
|
2110
2070
|
context.report({
|
2111
|
-
|
2071
|
+
node,
|
2112
2072
|
message: `Root type "${typeName}" is forbidden`,
|
2113
2073
|
});
|
2114
2074
|
},
|
@@ -2161,7 +2121,7 @@ const rule$b = {
|
|
2161
2121
|
const graphQLType = schema.getType(typeName);
|
2162
2122
|
if (graphql.isScalarType(graphQLType)) {
|
2163
2123
|
context.report({
|
2164
|
-
|
2124
|
+
node,
|
2165
2125
|
message: `Unexpected scalar result type "${typeName}"`,
|
2166
2126
|
});
|
2167
2127
|
}
|
@@ -2217,7 +2177,7 @@ const rule$c = {
|
|
2217
2177
|
typeName,
|
2218
2178
|
},
|
2219
2179
|
messageId: NO_TYPENAME_PREFIX,
|
2220
|
-
|
2180
|
+
node: field.name,
|
2221
2181
|
});
|
2222
2182
|
}
|
2223
2183
|
}
|
@@ -2295,7 +2255,7 @@ const rule$d = {
|
|
2295
2255
|
const typeName = node.name.value;
|
2296
2256
|
if (!reachableTypes.has(typeName)) {
|
2297
2257
|
context.report({
|
2298
|
-
|
2258
|
+
node: node.name,
|
2299
2259
|
messageId: UNREACHABLE_TYPE,
|
2300
2260
|
data: { typeName },
|
2301
2261
|
suggest: [
|
@@ -2384,7 +2344,7 @@ const rule$e = {
|
|
2384
2344
|
return;
|
2385
2345
|
}
|
2386
2346
|
context.report({
|
2387
|
-
|
2347
|
+
node: node.name,
|
2388
2348
|
messageId: UNUSED_FIELD,
|
2389
2349
|
data: { fieldName },
|
2390
2350
|
suggest: [
|
@@ -2437,8 +2397,51 @@ function getBaseType(type) {
|
|
2437
2397
|
}
|
2438
2398
|
return type;
|
2439
2399
|
}
|
2440
|
-
function
|
2441
|
-
|
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;
|
2442
2445
|
}
|
2443
2446
|
function extractCommentsFromAst(loc) {
|
2444
2447
|
if (!loc) {
|
@@ -2447,35 +2450,16 @@ function extractCommentsFromAst(loc) {
|
|
2447
2450
|
const comments = [];
|
2448
2451
|
let token = loc.startToken;
|
2449
2452
|
while (token !== null) {
|
2450
|
-
|
2451
|
-
|
2452
|
-
|
2453
|
-
|
2454
|
-
|
2455
|
-
loc: {
|
2456
|
-
start: { line, column },
|
2457
|
-
end: { line, column },
|
2458
|
-
},
|
2459
|
-
range: [start, end],
|
2460
|
-
});
|
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);
|
2461
2458
|
}
|
2462
|
-
token = next;
|
2459
|
+
token = token.next;
|
2463
2460
|
}
|
2464
2461
|
return comments;
|
2465
2462
|
}
|
2466
|
-
function convertLocation(gqlLocation) {
|
2467
|
-
return {
|
2468
|
-
start: {
|
2469
|
-
column: gqlLocation.startToken.column,
|
2470
|
-
line: gqlLocation.startToken.line,
|
2471
|
-
},
|
2472
|
-
end: {
|
2473
|
-
column: gqlLocation.endToken.column,
|
2474
|
-
line: gqlLocation.endToken.line,
|
2475
|
-
},
|
2476
|
-
source: gqlLocation.source.body,
|
2477
|
-
};
|
2478
|
-
}
|
2479
2463
|
function isNodeWithDescription(obj) {
|
2480
2464
|
var _a;
|
2481
2465
|
return (_a = obj) === null || _a === void 0 ? void 0 : _a.description;
|
@@ -2561,7 +2545,7 @@ const rule$f = {
|
|
2561
2545
|
const deletionDateNode = node.arguments.find(arg => arg.name.value === argName);
|
2562
2546
|
if (!deletionDateNode) {
|
2563
2547
|
context.report({
|
2564
|
-
|
2548
|
+
node: node.name,
|
2565
2549
|
messageId: MESSAGE_REQUIRE_DATE,
|
2566
2550
|
});
|
2567
2551
|
return;
|
@@ -2589,7 +2573,7 @@ const rule$f = {
|
|
2589
2573
|
const canRemove = Date.now() > deletionDateInMS;
|
2590
2574
|
if (canRemove) {
|
2591
2575
|
context.report({
|
2592
|
-
node,
|
2576
|
+
node: node.parent.name,
|
2593
2577
|
messageId: MESSAGE_CAN_BE_REMOVED,
|
2594
2578
|
data: {
|
2595
2579
|
nodeName: node.parent.name.value,
|
@@ -2646,7 +2630,7 @@ const rule$g = {
|
|
2646
2630
|
const value = reasonArg ? String(valueFromNode(reasonArg.value) || '').trim() : null;
|
2647
2631
|
if (!value) {
|
2648
2632
|
context.report({
|
2649
|
-
|
2633
|
+
node: node.name,
|
2650
2634
|
message: 'Directive "@deprecated" must have a reason!',
|
2651
2635
|
});
|
2652
2636
|
}
|
@@ -2658,12 +2642,40 @@ const rule$g = {
|
|
2658
2642
|
const RULE_ID$2 = 'require-description';
|
2659
2643
|
const ALLOWED_KINDS$1 = [
|
2660
2644
|
...TYPES_KINDS,
|
2645
|
+
graphql.Kind.DIRECTIVE_DEFINITION,
|
2661
2646
|
graphql.Kind.FIELD_DEFINITION,
|
2662
2647
|
graphql.Kind.INPUT_VALUE_DEFINITION,
|
2663
2648
|
graphql.Kind.ENUM_VALUE_DEFINITION,
|
2664
|
-
graphql.Kind.DIRECTIVE_DEFINITION,
|
2665
2649
|
graphql.Kind.OPERATION_DEFINITION,
|
2666
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
|
+
}
|
2667
2679
|
const rule$h = {
|
2668
2680
|
meta: {
|
2669
2681
|
docs: {
|
@@ -2716,7 +2728,7 @@ const rule$h = {
|
|
2716
2728
|
},
|
2717
2729
|
type: 'suggestion',
|
2718
2730
|
messages: {
|
2719
|
-
[RULE_ID$2]: 'Description is required for
|
2731
|
+
[RULE_ID$2]: 'Description is required for `{{ nodeName }}`.',
|
2720
2732
|
},
|
2721
2733
|
schema: {
|
2722
2734
|
type: 'array',
|
@@ -2775,10 +2787,10 @@ const rule$h = {
|
|
2775
2787
|
}
|
2776
2788
|
if (description.length === 0) {
|
2777
2789
|
context.report({
|
2778
|
-
loc: isOperation ? getLocation(node.loc, node.operation) :
|
2790
|
+
loc: isOperation ? getLocation(node.loc, node.operation) : node.name.loc,
|
2779
2791
|
messageId: RULE_ID$2,
|
2780
2792
|
data: {
|
2781
|
-
|
2793
|
+
nodeName: getNodeName(node),
|
2782
2794
|
},
|
2783
2795
|
});
|
2784
2796
|
}
|
@@ -2848,7 +2860,7 @@ const rule$i = {
|
|
2848
2860
|
const hasQueryType = fields.some(field => getTypeName(field) === queryType.name);
|
2849
2861
|
if (!hasQueryType) {
|
2850
2862
|
context.report({
|
2851
|
-
|
2863
|
+
node,
|
2852
2864
|
message: `Mutation result type "${graphQLType.name}" must contain field of type "${queryType.name}"`,
|
2853
2865
|
});
|
2854
2866
|
}
|
@@ -2868,16 +2880,30 @@ function convertToESTree(node, typeInfo) {
|
|
2868
2880
|
function hasTypeField(obj) {
|
2869
2881
|
return obj && !!obj.type;
|
2870
2882
|
}
|
2871
|
-
|
2872
|
-
|
2873
|
-
|
2874
|
-
|
2875
|
-
|
2876
|
-
|
2877
|
-
|
2878
|
-
|
2879
|
-
|
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,
|
2880
2902
|
};
|
2903
|
+
if (loc.start.column === loc.end.column) {
|
2904
|
+
loc.end.column += end - start;
|
2905
|
+
}
|
2906
|
+
return loc;
|
2881
2907
|
}
|
2882
2908
|
const convertNode = (typeInfo) => (node, key, parent) => {
|
2883
2909
|
const calculatedTypeInfo = typeInfo
|
@@ -2897,7 +2923,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2897
2923
|
typeInfo: () => calculatedTypeInfo,
|
2898
2924
|
leadingComments: convertDescription(node),
|
2899
2925
|
loc: convertLocation(node.loc),
|
2900
|
-
range:
|
2926
|
+
range: [node.loc.start, node.loc.end],
|
2901
2927
|
};
|
2902
2928
|
if (hasTypeField(node)) {
|
2903
2929
|
const { type: gqlType, loc: gqlLocation, ...rest } = node;
|
@@ -2922,7 +2948,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2922
2948
|
}
|
2923
2949
|
return parent[key];
|
2924
2950
|
},
|
2925
|
-
gqlLocation: stripTokens(gqlLocation),
|
2926
2951
|
};
|
2927
2952
|
return estreeNode;
|
2928
2953
|
}
|
@@ -2946,7 +2971,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2946
2971
|
}
|
2947
2972
|
return parent[key];
|
2948
2973
|
},
|
2949
|
-
gqlLocation: stripTokens(gqlLocation),
|
2950
2974
|
};
|
2951
2975
|
return estreeNode;
|
2952
2976
|
}
|
@@ -3197,9 +3221,13 @@ const rule$k = {
|
|
3197
3221
|
};
|
3198
3222
|
checkFn({
|
3199
3223
|
getDocument: () => document,
|
3200
|
-
reportError
|
3224
|
+
reportError(error) {
|
3225
|
+
const { line, column } = error.locations[0];
|
3201
3226
|
context.report({
|
3202
|
-
loc:
|
3227
|
+
loc: {
|
3228
|
+
line,
|
3229
|
+
column: column - 1,
|
3230
|
+
},
|
3203
3231
|
message: error.message,
|
3204
3232
|
});
|
3205
3233
|
},
|
@@ -3374,7 +3402,7 @@ const rule$l = {
|
|
3374
3402
|
// we can extend this rule later.
|
3375
3403
|
if (validIds.length !== 1) {
|
3376
3404
|
context.report({
|
3377
|
-
|
3405
|
+
node: node.name,
|
3378
3406
|
messageId: RULE_ID$5,
|
3379
3407
|
data: {
|
3380
3408
|
typeName,
|
@@ -3410,7 +3438,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3410
3438
|
.map(f => `\t${path.relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
3411
3439
|
.join('\n'),
|
3412
3440
|
},
|
3413
|
-
|
3441
|
+
node: node.name,
|
3414
3442
|
});
|
3415
3443
|
}
|
3416
3444
|
};
|
@@ -3964,6 +3992,15 @@ function parseForESLint(code, options = {}) {
|
|
3964
3992
|
}
|
3965
3993
|
}
|
3966
3994
|
|
3995
|
+
function indentCode(code, indent = 4) {
|
3996
|
+
return code.replace(/^/gm, ' '.repeat(indent));
|
3997
|
+
}
|
3998
|
+
function printCode(code) {
|
3999
|
+
return codeFrame.codeFrameColumns(code, { start: { line: 0, column: 0 } }, {
|
4000
|
+
linesAbove: Number.POSITIVE_INFINITY,
|
4001
|
+
linesBelow: Number.POSITIVE_INFINITY,
|
4002
|
+
});
|
4003
|
+
}
|
3967
4004
|
class GraphQLRuleTester extends eslint.RuleTester {
|
3968
4005
|
constructor(parserOptions = {}) {
|
3969
4006
|
const config = {
|
@@ -4019,15 +4056,14 @@ class GraphQLRuleTester extends eslint.RuleTester {
|
|
4019
4056
|
if (message.fatal) {
|
4020
4057
|
throw new Error(message.message);
|
4021
4058
|
}
|
4022
|
-
messageForSnapshot.push(
|
4059
|
+
messageForSnapshot.push(`❌ Error ${index + 1}/${messages.length}`, visualizeEslintMessage(code, message));
|
4023
4060
|
}
|
4024
4061
|
if (rule.meta.fixable) {
|
4025
4062
|
const { fixed, output } = linter.verifyAndFix(code, verifyConfig, { filename });
|
4026
4063
|
if (fixed) {
|
4027
|
-
messageForSnapshot.push('Autofix output',
|
4064
|
+
messageForSnapshot.push('🔧 Autofix output', indentCode(printCode(output), 2));
|
4028
4065
|
}
|
4029
4066
|
}
|
4030
|
-
// eslint-disable-next-line no-undef
|
4031
4067
|
expect(messageForSnapshot.join('\n\n')).toMatchSnapshot();
|
4032
4068
|
}
|
4033
4069
|
}
|
@@ -4084,10 +4120,10 @@ function visualizeEslintMessage(text, result) {
|
|
4084
4120
|
exports.GraphQLRuleTester = GraphQLRuleTester;
|
4085
4121
|
exports.configs = configs;
|
4086
4122
|
exports.convertDescription = convertDescription;
|
4087
|
-
exports.convertLocation = convertLocation;
|
4088
|
-
exports.convertRange = convertRange;
|
4089
4123
|
exports.convertToESTree = convertToESTree;
|
4124
|
+
exports.convertToken = convertToken;
|
4090
4125
|
exports.extractCommentsFromAst = extractCommentsFromAst;
|
4126
|
+
exports.extractTokens = extractTokens;
|
4091
4127
|
exports.getBaseType = getBaseType;
|
4092
4128
|
exports.isNodeWithDescription = isNodeWithDescription;
|
4093
4129
|
exports.parse = parse;
|