@graphql-eslint/eslint-plugin 3.7.0 → 3.7.1-alpha-d9a3c78.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/README.md +1 -1
- package/docs/parser.md +1 -1
- package/docs/rules/alphabetize.md +2 -0
- package/docs/rules/match-document-filename.md +2 -1
- package/docs/rules/require-description.md +18 -1
- package/estree-parser/estree-ast.d.ts +1 -2
- package/estree-parser/utils.d.ts +10 -5
- package/index.js +298 -193
- package/index.mjs +297 -192
- package/package.json +1 -1
- package/rules/alphabetize.d.ts +1 -1
- package/rules/index.d.ts +2 -21
- package/rules/match-document-filename.d.ts +2 -1
- package/rules/require-description.d.ts +2 -2
- package/testkit.d.ts +0 -1
- package/utils.d.ts +4 -8
package/index.js
CHANGED
@@ -208,43 +208,6 @@ function requireUsedFieldsFromContext(ruleName, context) {
|
|
208
208
|
const siblings = requireSiblingsOperations(ruleName, context);
|
209
209
|
return context.parserServices.usedFields(schema, siblings);
|
210
210
|
}
|
211
|
-
function getLexer(source) {
|
212
|
-
// GraphQL v14
|
213
|
-
const gqlLanguage = require('graphql/language');
|
214
|
-
if (gqlLanguage && gqlLanguage.createLexer) {
|
215
|
-
return gqlLanguage.createLexer(source, {});
|
216
|
-
}
|
217
|
-
// GraphQL v15
|
218
|
-
const { Lexer: LexerCls } = require('graphql');
|
219
|
-
if (LexerCls && typeof LexerCls === 'function') {
|
220
|
-
return new LexerCls(source);
|
221
|
-
}
|
222
|
-
throw new Error(`Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!`);
|
223
|
-
}
|
224
|
-
function extractTokens(source) {
|
225
|
-
const lexer = getLexer(source);
|
226
|
-
const tokens = [];
|
227
|
-
let token = lexer.advance();
|
228
|
-
while (token && token.kind !== '<EOF>') {
|
229
|
-
tokens.push({
|
230
|
-
type: token.kind,
|
231
|
-
loc: {
|
232
|
-
start: {
|
233
|
-
line: token.line,
|
234
|
-
column: token.column,
|
235
|
-
},
|
236
|
-
end: {
|
237
|
-
line: token.line,
|
238
|
-
column: token.column,
|
239
|
-
},
|
240
|
-
},
|
241
|
-
value: token.value,
|
242
|
-
range: [token.start, token.end],
|
243
|
-
});
|
244
|
-
token = lexer.advance();
|
245
|
-
}
|
246
|
-
return tokens;
|
247
|
-
}
|
248
211
|
const normalizePath = (path) => (path || '').replace(/\\/g, '/');
|
249
212
|
/**
|
250
213
|
* https://github.com/prettier/eslint-plugin-prettier/blob/76bd45ece6d56eb52f75db6b4a1efdd2efb56392/eslint-plugin-prettier.js#L71
|
@@ -314,21 +277,16 @@ const convertCase = (style, str) => {
|
|
314
277
|
return lowerCase(str).replace(/ /g, '-');
|
315
278
|
}
|
316
279
|
};
|
317
|
-
function getLocation(loc, fieldName = ''
|
318
|
-
const {
|
319
|
-
/*
|
320
|
-
* ESLint has 0-based column number
|
321
|
-
* https://eslint.org/docs/developer-guide/working-with-rules#contextreport
|
322
|
-
*/
|
323
|
-
const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
|
280
|
+
function getLocation(loc, fieldName = '') {
|
281
|
+
const { line, column } = loc.start;
|
324
282
|
return {
|
325
283
|
start: {
|
326
|
-
line
|
327
|
-
column
|
284
|
+
line,
|
285
|
+
column,
|
328
286
|
},
|
329
287
|
end: {
|
330
|
-
line
|
331
|
-
column:
|
288
|
+
line,
|
289
|
+
column: column + fieldName.length,
|
332
290
|
},
|
333
291
|
};
|
334
292
|
}
|
@@ -342,23 +300,16 @@ function validateDocument(context, schema = null, documentNode, rule) {
|
|
342
300
|
? graphql.validate(schema, documentNode, [rule])
|
343
301
|
: validate.validateSDL(documentNode, null, [rule]);
|
344
302
|
for (const error of validationErrors) {
|
345
|
-
/*
|
346
|
-
* TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
|
347
|
-
* Example: loc.end always equal loc.start
|
348
|
-
* {
|
349
|
-
* token: {
|
350
|
-
* type: 'Name',
|
351
|
-
* loc: { start: { line: 4, column: 13 }, end: { line: 4, column: 13 } },
|
352
|
-
* value: 'veryBad',
|
353
|
-
* range: [ 40, 47 ]
|
354
|
-
* }
|
355
|
-
* }
|
356
|
-
*/
|
357
303
|
const { line, column } = error.locations[0];
|
358
304
|
const ancestors = context.getAncestors();
|
359
|
-
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);
|
360
306
|
context.report({
|
361
|
-
loc:
|
307
|
+
loc: token
|
308
|
+
? token.loc
|
309
|
+
: {
|
310
|
+
line,
|
311
|
+
column: column - 1,
|
312
|
+
},
|
362
313
|
message: error.message,
|
363
314
|
});
|
364
315
|
}
|
@@ -688,9 +639,10 @@ const argumentsEnum = [
|
|
688
639
|
const rule = {
|
689
640
|
meta: {
|
690
641
|
type: 'suggestion',
|
642
|
+
fixable: 'code',
|
691
643
|
docs: {
|
692
644
|
category: ['Schema', 'Operations'],
|
693
|
-
description:
|
645
|
+
description: `Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.`,
|
694
646
|
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/alphabetize.md',
|
695
647
|
examples: [
|
696
648
|
{
|
@@ -848,24 +800,56 @@ const rule = {
|
|
848
800
|
},
|
849
801
|
create(context) {
|
850
802
|
var _a, _b, _c, _d, _e;
|
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]];
|
824
|
+
}
|
851
825
|
function checkNodes(nodes) {
|
852
|
-
|
853
|
-
for (
|
854
|
-
const
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
? {
|
862
|
-
currName: `$${currName}`,
|
863
|
-
prevName: `$${prevName}`,
|
864
|
-
}
|
865
|
-
: { currName, prevName },
|
866
|
-
});
|
826
|
+
// Starts from 1, ignore nodes.length <= 1
|
827
|
+
for (let i = 1; i < nodes.length; i += 1) {
|
828
|
+
const prevNode = nodes[i - 1];
|
829
|
+
const currNode = nodes[i];
|
830
|
+
const prevName = prevNode.name.value;
|
831
|
+
const currName = currNode.name.value;
|
832
|
+
// Compare with lexicographic order
|
833
|
+
if (prevName.localeCompare(currName) !== 1) {
|
834
|
+
continue;
|
867
835
|
}
|
868
|
-
|
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
|
+
});
|
869
853
|
}
|
870
854
|
}
|
871
855
|
const opts = context.options[0];
|
@@ -1062,7 +1046,7 @@ const rule$2 = {
|
|
1062
1046
|
if (shouldCheckType(node.parent.parent)) {
|
1063
1047
|
const name = node.name.value;
|
1064
1048
|
context.report({
|
1065
|
-
|
1049
|
+
node: node.name,
|
1066
1050
|
message: `Input "${name}" should be called "input"`,
|
1067
1051
|
});
|
1068
1052
|
}
|
@@ -1084,7 +1068,7 @@ const rule$2 = {
|
|
1084
1068
|
if ((options.caseSensitiveInputType && node.name.value !== mutationName) ||
|
1085
1069
|
name.toLowerCase() !== mutationName.toLowerCase()) {
|
1086
1070
|
context.report({
|
1087
|
-
|
1071
|
+
node: node.name,
|
1088
1072
|
message: `InputType "${name}" name should be "${mutationName}"`,
|
1089
1073
|
});
|
1090
1074
|
}
|
@@ -1098,7 +1082,7 @@ const rule$2 = {
|
|
1098
1082
|
const MATCH_EXTENSION = 'MATCH_EXTENSION';
|
1099
1083
|
const MATCH_STYLE = 'MATCH_STYLE';
|
1100
1084
|
const ACCEPTED_EXTENSIONS = ['.gql', '.graphql'];
|
1101
|
-
const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case'];
|
1085
|
+
const CASE_STYLES = ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE', 'kebab-case', 'matchDocumentStyle'];
|
1102
1086
|
const schemaOption = {
|
1103
1087
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1104
1088
|
};
|
@@ -1272,7 +1256,14 @@ const rule$3 = {
|
|
1272
1256
|
option = { style: option };
|
1273
1257
|
}
|
1274
1258
|
const expectedExtension = options.fileExtension || fileExtension;
|
1275
|
-
|
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;
|
1276
1267
|
const filenameWithExtension = filename + expectedExtension;
|
1277
1268
|
if (expectedFilename !== filenameWithExtension) {
|
1278
1269
|
context.report({
|
@@ -1529,7 +1520,7 @@ const rule$4 = {
|
|
1529
1520
|
const [trailingUnderscores] = nodeName.match(/_*$/);
|
1530
1521
|
const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
|
1531
1522
|
context.report({
|
1532
|
-
|
1523
|
+
node,
|
1533
1524
|
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
1534
1525
|
suggest: [
|
1535
1526
|
{
|
@@ -1587,7 +1578,7 @@ const rule$4 = {
|
|
1587
1578
|
const name = node.value;
|
1588
1579
|
const renameToName = name.replace(new RegExp(isLeading ? '^_+' : '_+$'), '');
|
1589
1580
|
context.report({
|
1590
|
-
|
1581
|
+
node,
|
1591
1582
|
message: `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`,
|
1592
1583
|
suggest: [
|
1593
1584
|
{
|
@@ -1703,7 +1694,7 @@ const rule$6 = {
|
|
1703
1694
|
for (const duplicate of duplicates) {
|
1704
1695
|
const enumName = duplicate.name.value;
|
1705
1696
|
context.report({
|
1706
|
-
|
1697
|
+
node: duplicate.name,
|
1707
1698
|
message: `Case-insensitive enum values duplicates are not allowed! Found: "${enumName}"`,
|
1708
1699
|
});
|
1709
1700
|
}
|
@@ -1797,9 +1788,8 @@ const rule$7 = {
|
|
1797
1788
|
const typeInfo = node.typeInfo();
|
1798
1789
|
if (typeInfo && typeInfo.enumValue) {
|
1799
1790
|
if (typeInfo.enumValue.deprecationReason) {
|
1800
|
-
const enumValueName = node.value;
|
1801
1791
|
context.report({
|
1802
|
-
|
1792
|
+
node,
|
1803
1793
|
messageId: NO_DEPRECATED,
|
1804
1794
|
data: {
|
1805
1795
|
type: 'enum value',
|
@@ -1814,9 +1804,8 @@ const rule$7 = {
|
|
1814
1804
|
const typeInfo = node.typeInfo();
|
1815
1805
|
if (typeInfo && typeInfo.fieldDef) {
|
1816
1806
|
if (typeInfo.fieldDef.deprecationReason) {
|
1817
|
-
const fieldName = node.name.value;
|
1818
1807
|
context.report({
|
1819
|
-
|
1808
|
+
node: node.name,
|
1820
1809
|
messageId: NO_DEPRECATED,
|
1821
1810
|
data: {
|
1822
1811
|
type: 'field',
|
@@ -1888,12 +1877,11 @@ const rule$8 = {
|
|
1888
1877
|
schema: [],
|
1889
1878
|
},
|
1890
1879
|
create(context) {
|
1891
|
-
function checkNode(usedFields,
|
1880
|
+
function checkNode(usedFields, type, node) {
|
1881
|
+
const fieldName = node.value;
|
1892
1882
|
if (usedFields.has(fieldName)) {
|
1893
1883
|
context.report({
|
1894
|
-
|
1895
|
-
offsetEnd: node.kind === graphql.Kind.VARIABLE_DEFINITION ? 0 : 1,
|
1896
|
-
}),
|
1884
|
+
node,
|
1897
1885
|
messageId: NO_DUPLICATE_FIELDS,
|
1898
1886
|
data: {
|
1899
1887
|
type,
|
@@ -1909,21 +1897,20 @@ const rule$8 = {
|
|
1909
1897
|
OperationDefinition(node) {
|
1910
1898
|
const set = new Set();
|
1911
1899
|
for (const varDef of node.variableDefinitions) {
|
1912
|
-
checkNode(set,
|
1900
|
+
checkNode(set, 'Operation variable', varDef.variable.name);
|
1913
1901
|
}
|
1914
1902
|
},
|
1915
1903
|
Field(node) {
|
1916
1904
|
const set = new Set();
|
1917
1905
|
for (const arg of node.arguments) {
|
1918
|
-
checkNode(set,
|
1906
|
+
checkNode(set, 'Field argument', arg.name);
|
1919
1907
|
}
|
1920
1908
|
},
|
1921
1909
|
SelectionSet(node) {
|
1922
|
-
var _a;
|
1923
1910
|
const set = new Set();
|
1924
1911
|
for (const selection of node.selections) {
|
1925
1912
|
if (selection.kind === graphql.Kind.FIELD) {
|
1926
|
-
checkNode(set,
|
1913
|
+
checkNode(set, 'Field', selection.alias || selection.name);
|
1927
1914
|
}
|
1928
1915
|
}
|
1929
1916
|
},
|
@@ -1982,7 +1969,7 @@ const rule$9 = {
|
|
1982
1969
|
schema: [],
|
1983
1970
|
},
|
1984
1971
|
create(context) {
|
1985
|
-
const selector =
|
1972
|
+
const selector = 'Document[definitions.0.kind!=/^(OperationDefinition|FragmentDefinition)$/]';
|
1986
1973
|
return {
|
1987
1974
|
[selector](node) {
|
1988
1975
|
const rawNode = node.rawNode();
|
@@ -1995,7 +1982,10 @@ const rule$9 = {
|
|
1995
1982
|
if (!isEslintComment && line !== prev.line && next.kind === graphql.TokenKind.NAME && linesAfter < 2) {
|
1996
1983
|
context.report({
|
1997
1984
|
messageId: HASHTAG_COMMENT,
|
1998
|
-
loc:
|
1985
|
+
loc: {
|
1986
|
+
line,
|
1987
|
+
column: column - 1,
|
1988
|
+
},
|
1999
1989
|
});
|
2000
1990
|
}
|
2001
1991
|
}
|
@@ -2078,7 +2068,7 @@ const rule$a = {
|
|
2078
2068
|
[selector](node) {
|
2079
2069
|
const typeName = node.value;
|
2080
2070
|
context.report({
|
2081
|
-
|
2071
|
+
node,
|
2082
2072
|
message: `Root type "${typeName}" is forbidden`,
|
2083
2073
|
});
|
2084
2074
|
},
|
@@ -2131,7 +2121,7 @@ const rule$b = {
|
|
2131
2121
|
const graphQLType = schema.getType(typeName);
|
2132
2122
|
if (graphql.isScalarType(graphQLType)) {
|
2133
2123
|
context.report({
|
2134
|
-
|
2124
|
+
node,
|
2135
2125
|
message: `Unexpected scalar result type "${typeName}"`,
|
2136
2126
|
});
|
2137
2127
|
}
|
@@ -2187,7 +2177,7 @@ const rule$c = {
|
|
2187
2177
|
typeName,
|
2188
2178
|
},
|
2189
2179
|
messageId: NO_TYPENAME_PREFIX,
|
2190
|
-
|
2180
|
+
node: field.name,
|
2191
2181
|
});
|
2192
2182
|
}
|
2193
2183
|
}
|
@@ -2265,7 +2255,7 @@ const rule$d = {
|
|
2265
2255
|
const typeName = node.name.value;
|
2266
2256
|
if (!reachableTypes.has(typeName)) {
|
2267
2257
|
context.report({
|
2268
|
-
|
2258
|
+
node: node.name,
|
2269
2259
|
messageId: UNREACHABLE_TYPE,
|
2270
2260
|
data: { typeName },
|
2271
2261
|
suggest: [
|
@@ -2354,7 +2344,7 @@ const rule$e = {
|
|
2354
2344
|
return;
|
2355
2345
|
}
|
2356
2346
|
context.report({
|
2357
|
-
|
2347
|
+
node: node.name,
|
2358
2348
|
messageId: UNUSED_FIELD,
|
2359
2349
|
data: { fieldName },
|
2360
2350
|
suggest: [
|
@@ -2407,8 +2397,51 @@ function getBaseType(type) {
|
|
2407
2397
|
}
|
2408
2398
|
return type;
|
2409
2399
|
}
|
2410
|
-
function
|
2411
|
-
|
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;
|
2412
2445
|
}
|
2413
2446
|
function extractCommentsFromAst(loc) {
|
2414
2447
|
if (!loc) {
|
@@ -2417,35 +2450,16 @@ function extractCommentsFromAst(loc) {
|
|
2417
2450
|
const comments = [];
|
2418
2451
|
let token = loc.startToken;
|
2419
2452
|
while (token !== null) {
|
2420
|
-
|
2421
|
-
|
2422
|
-
|
2423
|
-
|
2424
|
-
|
2425
|
-
loc: {
|
2426
|
-
start: { line, column },
|
2427
|
-
end: { line, column },
|
2428
|
-
},
|
2429
|
-
range: [start, end],
|
2430
|
-
});
|
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);
|
2431
2458
|
}
|
2432
|
-
token = next;
|
2459
|
+
token = token.next;
|
2433
2460
|
}
|
2434
2461
|
return comments;
|
2435
2462
|
}
|
2436
|
-
function convertLocation(gqlLocation) {
|
2437
|
-
return {
|
2438
|
-
start: {
|
2439
|
-
column: gqlLocation.startToken.column,
|
2440
|
-
line: gqlLocation.startToken.line,
|
2441
|
-
},
|
2442
|
-
end: {
|
2443
|
-
column: gqlLocation.endToken.column,
|
2444
|
-
line: gqlLocation.endToken.line,
|
2445
|
-
},
|
2446
|
-
source: gqlLocation.source.body,
|
2447
|
-
};
|
2448
|
-
}
|
2449
2463
|
function isNodeWithDescription(obj) {
|
2450
2464
|
var _a;
|
2451
2465
|
return (_a = obj) === null || _a === void 0 ? void 0 : _a.description;
|
@@ -2531,7 +2545,7 @@ const rule$f = {
|
|
2531
2545
|
const deletionDateNode = node.arguments.find(arg => arg.name.value === argName);
|
2532
2546
|
if (!deletionDateNode) {
|
2533
2547
|
context.report({
|
2534
|
-
|
2548
|
+
node: node.name,
|
2535
2549
|
messageId: MESSAGE_REQUIRE_DATE,
|
2536
2550
|
});
|
2537
2551
|
return;
|
@@ -2559,7 +2573,7 @@ const rule$f = {
|
|
2559
2573
|
const canRemove = Date.now() > deletionDateInMS;
|
2560
2574
|
if (canRemove) {
|
2561
2575
|
context.report({
|
2562
|
-
node,
|
2576
|
+
node: node.parent.name,
|
2563
2577
|
messageId: MESSAGE_CAN_BE_REMOVED,
|
2564
2578
|
data: {
|
2565
2579
|
nodeName: node.parent.name.value,
|
@@ -2616,7 +2630,7 @@ const rule$g = {
|
|
2616
2630
|
const value = reasonArg ? String(valueFromNode(reasonArg.value) || '').trim() : null;
|
2617
2631
|
if (!value) {
|
2618
2632
|
context.report({
|
2619
|
-
|
2633
|
+
node: node.name,
|
2620
2634
|
message: 'Directive "@deprecated" must have a reason!',
|
2621
2635
|
});
|
2622
2636
|
}
|
@@ -2625,20 +2639,49 @@ const rule$g = {
|
|
2625
2639
|
},
|
2626
2640
|
};
|
2627
2641
|
|
2628
|
-
const
|
2642
|
+
const RULE_ID$2 = 'require-description';
|
2629
2643
|
const ALLOWED_KINDS$1 = [
|
2630
2644
|
...TYPES_KINDS,
|
2645
|
+
graphql.Kind.DIRECTIVE_DEFINITION,
|
2631
2646
|
graphql.Kind.FIELD_DEFINITION,
|
2632
2647
|
graphql.Kind.INPUT_VALUE_DEFINITION,
|
2633
2648
|
graphql.Kind.ENUM_VALUE_DEFINITION,
|
2634
|
-
graphql.Kind.
|
2649
|
+
graphql.Kind.OPERATION_DEFINITION,
|
2635
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
|
+
}
|
2636
2679
|
const rule$h = {
|
2637
2680
|
meta: {
|
2638
2681
|
docs: {
|
2639
2682
|
category: 'Schema',
|
2640
|
-
description: 'Enforce descriptions in
|
2641
|
-
url:
|
2683
|
+
description: 'Enforce descriptions in type definitions and operations.',
|
2684
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
|
2642
2685
|
examples: [
|
2643
2686
|
{
|
2644
2687
|
title: 'Incorrect',
|
@@ -2662,6 +2705,16 @@ const rule$h = {
|
|
2662
2705
|
"""
|
2663
2706
|
name: String
|
2664
2707
|
}
|
2708
|
+
`,
|
2709
|
+
},
|
2710
|
+
{
|
2711
|
+
title: 'Correct',
|
2712
|
+
usage: [{ OperationDefinition: true }],
|
2713
|
+
code: /* GraphQL */ `
|
2714
|
+
# Create a new user
|
2715
|
+
mutation createUser {
|
2716
|
+
# ...
|
2717
|
+
}
|
2665
2718
|
`,
|
2666
2719
|
},
|
2667
2720
|
],
|
@@ -2675,7 +2728,7 @@ const rule$h = {
|
|
2675
2728
|
},
|
2676
2729
|
type: 'suggestion',
|
2677
2730
|
messages: {
|
2678
|
-
[
|
2731
|
+
[RULE_ID$2]: 'Description is required for `{{ nodeName }}`.',
|
2679
2732
|
},
|
2680
2733
|
schema: {
|
2681
2734
|
type: 'array',
|
@@ -2690,19 +2743,19 @@ const rule$h = {
|
|
2690
2743
|
type: 'boolean',
|
2691
2744
|
description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
|
2692
2745
|
},
|
2693
|
-
...Object.fromEntries([...ALLOWED_KINDS$1].sort().map(kind =>
|
2694
|
-
kind
|
2695
|
-
{
|
2696
|
-
|
2697
|
-
|
2698
|
-
}
|
2699
|
-
|
2746
|
+
...Object.fromEntries([...ALLOWED_KINDS$1].sort().map(kind => {
|
2747
|
+
let description = `Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`;
|
2748
|
+
if (kind === graphql.Kind.OPERATION_DEFINITION) {
|
2749
|
+
description += '\n\n> You must use only comment syntax `#` and not description syntax `"""` or `"`.';
|
2750
|
+
}
|
2751
|
+
return [kind, { type: 'boolean', description }];
|
2752
|
+
})),
|
2700
2753
|
},
|
2701
2754
|
},
|
2702
2755
|
},
|
2703
2756
|
},
|
2704
2757
|
create(context) {
|
2705
|
-
const { types, ...restOptions } = context.options[0];
|
2758
|
+
const { types, ...restOptions } = context.options[0] || {};
|
2706
2759
|
const kinds = new Set(types ? TYPES_KINDS : []);
|
2707
2760
|
for (const [kind, isEnabled] of Object.entries(restOptions)) {
|
2708
2761
|
if (isEnabled) {
|
@@ -2716,13 +2769,28 @@ const rule$h = {
|
|
2716
2769
|
return {
|
2717
2770
|
[selector](node) {
|
2718
2771
|
var _a;
|
2719
|
-
|
2720
|
-
|
2772
|
+
let description = '';
|
2773
|
+
const isOperation = node.kind === graphql.Kind.OPERATION_DEFINITION;
|
2774
|
+
if (isOperation) {
|
2775
|
+
const rawNode = node.rawNode();
|
2776
|
+
const { prev, line } = rawNode.loc.startToken;
|
2777
|
+
if (prev.kind === graphql.TokenKind.COMMENT) {
|
2778
|
+
const value = prev.value.trim();
|
2779
|
+
const linesBefore = line - prev.line;
|
2780
|
+
if (!value.startsWith('eslint') && linesBefore === 1) {
|
2781
|
+
description = value;
|
2782
|
+
}
|
2783
|
+
}
|
2784
|
+
}
|
2785
|
+
else {
|
2786
|
+
description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value.trim()) || '';
|
2787
|
+
}
|
2788
|
+
if (description.length === 0) {
|
2721
2789
|
context.report({
|
2722
|
-
loc: getLocation(node.
|
2723
|
-
messageId:
|
2790
|
+
loc: isOperation ? getLocation(node.loc, node.operation) : node.name.loc,
|
2791
|
+
messageId: RULE_ID$2,
|
2724
2792
|
data: {
|
2725
|
-
|
2793
|
+
nodeName: getNodeName(node),
|
2726
2794
|
},
|
2727
2795
|
});
|
2728
2796
|
}
|
@@ -2792,7 +2860,7 @@ const rule$i = {
|
|
2792
2860
|
const hasQueryType = fields.some(field => getTypeName(field) === queryType.name);
|
2793
2861
|
if (!hasQueryType) {
|
2794
2862
|
context.report({
|
2795
|
-
|
2863
|
+
node,
|
2796
2864
|
message: `Mutation result type "${graphQLType.name}" must contain field of type "${queryType.name}"`,
|
2797
2865
|
});
|
2798
2866
|
}
|
@@ -2812,16 +2880,30 @@ function convertToESTree(node, typeInfo) {
|
|
2812
2880
|
function hasTypeField(obj) {
|
2813
2881
|
return obj && !!obj.type;
|
2814
2882
|
}
|
2815
|
-
|
2816
|
-
|
2817
|
-
|
2818
|
-
|
2819
|
-
|
2820
|
-
|
2821
|
-
|
2822
|
-
|
2823
|
-
|
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,
|
2824
2902
|
};
|
2903
|
+
if (loc.start.column === loc.end.column) {
|
2904
|
+
loc.end.column += end - start;
|
2905
|
+
}
|
2906
|
+
return loc;
|
2825
2907
|
}
|
2826
2908
|
const convertNode = (typeInfo) => (node, key, parent) => {
|
2827
2909
|
const calculatedTypeInfo = typeInfo
|
@@ -2841,7 +2923,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2841
2923
|
typeInfo: () => calculatedTypeInfo,
|
2842
2924
|
leadingComments: convertDescription(node),
|
2843
2925
|
loc: convertLocation(node.loc),
|
2844
|
-
range:
|
2926
|
+
range: [node.loc.start, node.loc.end],
|
2845
2927
|
};
|
2846
2928
|
if (hasTypeField(node)) {
|
2847
2929
|
const { type: gqlType, loc: gqlLocation, ...rest } = node;
|
@@ -2866,7 +2948,6 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2866
2948
|
}
|
2867
2949
|
return parent[key];
|
2868
2950
|
},
|
2869
|
-
gqlLocation: stripTokens(gqlLocation),
|
2870
2951
|
};
|
2871
2952
|
return estreeNode;
|
2872
2953
|
}
|
@@ -2890,13 +2971,12 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2890
2971
|
}
|
2891
2972
|
return parent[key];
|
2892
2973
|
},
|
2893
|
-
gqlLocation: stripTokens(gqlLocation),
|
2894
2974
|
};
|
2895
2975
|
return estreeNode;
|
2896
2976
|
}
|
2897
2977
|
};
|
2898
2978
|
|
2899
|
-
const RULE_ID$
|
2979
|
+
const RULE_ID$3 = 'require-id-when-available';
|
2900
2980
|
const MESSAGE_ID = 'REQUIRE_ID_WHEN_AVAILABLE';
|
2901
2981
|
const DEFAULT_ID_FIELD_NAME = 'id';
|
2902
2982
|
const rule$j = {
|
@@ -2905,7 +2985,7 @@ const rule$j = {
|
|
2905
2985
|
docs: {
|
2906
2986
|
category: 'Operations',
|
2907
2987
|
description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
|
2908
|
-
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$
|
2988
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$3}.md`,
|
2909
2989
|
requiresSchema: true,
|
2910
2990
|
requiresSiblings: true,
|
2911
2991
|
examples: [
|
@@ -2979,8 +3059,8 @@ const rule$j = {
|
|
2979
3059
|
},
|
2980
3060
|
},
|
2981
3061
|
create(context) {
|
2982
|
-
requireGraphQLSchemaFromContext(RULE_ID$
|
2983
|
-
const siblings = requireSiblingsOperations(RULE_ID$
|
3062
|
+
requireGraphQLSchemaFromContext(RULE_ID$3, context);
|
3063
|
+
const siblings = requireSiblingsOperations(RULE_ID$3, context);
|
2984
3064
|
const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
|
2985
3065
|
const idNames = utils.asArray(fieldName);
|
2986
3066
|
const isFound = (s) => s.kind === graphql.Kind.FIELD && idNames.includes(s.name.value);
|
@@ -3042,13 +3122,13 @@ const rule$j = {
|
|
3042
3122
|
},
|
3043
3123
|
};
|
3044
3124
|
|
3045
|
-
const RULE_ID$
|
3125
|
+
const RULE_ID$4 = 'selection-set-depth';
|
3046
3126
|
const rule$k = {
|
3047
3127
|
meta: {
|
3048
3128
|
docs: {
|
3049
3129
|
category: 'Operations',
|
3050
3130
|
description: `Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://github.com/stems/graphql-depth-limit).`,
|
3051
|
-
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$
|
3131
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$4}.md`,
|
3052
3132
|
requiresSiblings: true,
|
3053
3133
|
examples: [
|
3054
3134
|
{
|
@@ -3122,10 +3202,10 @@ const rule$k = {
|
|
3122
3202
|
create(context) {
|
3123
3203
|
let siblings = null;
|
3124
3204
|
try {
|
3125
|
-
siblings = requireSiblingsOperations(RULE_ID$
|
3205
|
+
siblings = requireSiblingsOperations(RULE_ID$4, context);
|
3126
3206
|
}
|
3127
3207
|
catch (e) {
|
3128
|
-
logger.warn(`Rule "${RULE_ID$
|
3208
|
+
logger.warn(`Rule "${RULE_ID$4}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3129
3209
|
}
|
3130
3210
|
const { maxDepth } = context.options[0];
|
3131
3211
|
const ignore = context.options[0].ignore || [];
|
@@ -3141,23 +3221,27 @@ const rule$k = {
|
|
3141
3221
|
};
|
3142
3222
|
checkFn({
|
3143
3223
|
getDocument: () => document,
|
3144
|
-
reportError
|
3224
|
+
reportError(error) {
|
3225
|
+
const { line, column } = error.locations[0];
|
3145
3226
|
context.report({
|
3146
|
-
loc:
|
3227
|
+
loc: {
|
3228
|
+
line,
|
3229
|
+
column: column - 1,
|
3230
|
+
},
|
3147
3231
|
message: error.message,
|
3148
3232
|
});
|
3149
3233
|
},
|
3150
3234
|
});
|
3151
3235
|
}
|
3152
3236
|
catch (e) {
|
3153
|
-
logger.warn(`Rule "${RULE_ID$
|
3237
|
+
logger.warn(`Rule "${RULE_ID$4}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
|
3154
3238
|
}
|
3155
3239
|
},
|
3156
3240
|
};
|
3157
3241
|
},
|
3158
3242
|
};
|
3159
3243
|
|
3160
|
-
const RULE_ID$
|
3244
|
+
const RULE_ID$5 = 'strict-id-in-types';
|
3161
3245
|
const rule$l = {
|
3162
3246
|
meta: {
|
3163
3247
|
type: 'suggestion',
|
@@ -3165,7 +3249,7 @@ const rule$l = {
|
|
3165
3249
|
description: `Requires output types to have one unique identifier unless they do not have a logical one. Exceptions can be used to ignore output types that do not have unique identifiers.`,
|
3166
3250
|
category: 'Schema',
|
3167
3251
|
recommended: true,
|
3168
|
-
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$
|
3252
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$5}.md`,
|
3169
3253
|
requiresSchema: true,
|
3170
3254
|
examples: [
|
3171
3255
|
{
|
@@ -3279,7 +3363,7 @@ const rule$l = {
|
|
3279
3363
|
},
|
3280
3364
|
},
|
3281
3365
|
messages: {
|
3282
|
-
[RULE_ID$
|
3366
|
+
[RULE_ID$5]: `{{ typeName }} must have exactly one non-nullable unique identifier. Accepted name(s): {{ acceptedNamesString }}; Accepted type(s): {{ acceptedTypesString }}.`,
|
3283
3367
|
},
|
3284
3368
|
},
|
3285
3369
|
create(context) {
|
@@ -3289,7 +3373,7 @@ const rule$l = {
|
|
3289
3373
|
exceptions: {},
|
3290
3374
|
...context.options[0],
|
3291
3375
|
};
|
3292
|
-
const schema = requireGraphQLSchemaFromContext(RULE_ID$
|
3376
|
+
const schema = requireGraphQLSchemaFromContext(RULE_ID$5, context);
|
3293
3377
|
const rootTypeNames = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]
|
3294
3378
|
.filter(Boolean)
|
3295
3379
|
.map(type => type.name);
|
@@ -3318,8 +3402,8 @@ const rule$l = {
|
|
3318
3402
|
// we can extend this rule later.
|
3319
3403
|
if (validIds.length !== 1) {
|
3320
3404
|
context.report({
|
3321
|
-
|
3322
|
-
messageId: RULE_ID$
|
3405
|
+
node: node.name,
|
3406
|
+
messageId: RULE_ID$5,
|
3323
3407
|
data: {
|
3324
3408
|
typeName,
|
3325
3409
|
acceptedNamesString: options.acceptedIdNames.join(', '),
|
@@ -3354,7 +3438,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3354
3438
|
.map(f => `\t${path.relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
3355
3439
|
.join('\n'),
|
3356
3440
|
},
|
3357
|
-
|
3441
|
+
node: node.name,
|
3358
3442
|
});
|
3359
3443
|
}
|
3360
3444
|
};
|
@@ -3799,7 +3883,8 @@ function getReachableTypes(schema) {
|
|
3799
3883
|
graphql.visit(astNode, visitor);
|
3800
3884
|
}
|
3801
3885
|
}
|
3802
|
-
else {
|
3886
|
+
else if (type.astNode) {
|
3887
|
+
// astNode can be undefined for ID, String, Boolean
|
3803
3888
|
graphql.visit(type.astNode, visitor);
|
3804
3889
|
}
|
3805
3890
|
};
|
@@ -3818,7 +3903,8 @@ function getReachableTypes(schema) {
|
|
3818
3903
|
schema.getMutationType(),
|
3819
3904
|
schema.getSubscriptionType(),
|
3820
3905
|
]) {
|
3821
|
-
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) {
|
3822
3908
|
graphql.visit(type.astNode, visitor);
|
3823
3909
|
}
|
3824
3910
|
}
|
@@ -3908,6 +3994,15 @@ function parseForESLint(code, options = {}) {
|
|
3908
3994
|
}
|
3909
3995
|
}
|
3910
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
|
+
}
|
3911
4006
|
class GraphQLRuleTester extends eslint.RuleTester {
|
3912
4007
|
constructor(parserOptions = {}) {
|
3913
4008
|
const config = {
|
@@ -3949,19 +4044,29 @@ class GraphQLRuleTester extends eslint.RuleTester {
|
|
3949
4044
|
}
|
3950
4045
|
const linter = new eslint.Linter();
|
3951
4046
|
linter.defineRule(name, rule);
|
4047
|
+
const hasOnlyTest = tests.invalid.some(t => t.only);
|
3952
4048
|
for (const testCase of tests.invalid) {
|
4049
|
+
const { only, code, filename } = testCase;
|
4050
|
+
if (hasOnlyTest && !only) {
|
4051
|
+
continue;
|
4052
|
+
}
|
3953
4053
|
const verifyConfig = getVerifyConfig(name, this.config, testCase);
|
3954
4054
|
defineParser(linter, verifyConfig.parser);
|
3955
|
-
const { code, filename } = testCase;
|
3956
4055
|
const messages = linter.verify(code, verifyConfig, { filename });
|
3957
|
-
|
4056
|
+
const messageForSnapshot = [];
|
4057
|
+
for (const [index, message] of messages.entries()) {
|
3958
4058
|
if (message.fatal) {
|
3959
4059
|
throw new Error(message.message);
|
3960
4060
|
}
|
3961
|
-
|
3962
|
-
|
3963
|
-
|
4061
|
+
messageForSnapshot.push(`❌ Error ${index + 1}/${messages.length}`, visualizeEslintMessage(code, message));
|
4062
|
+
}
|
4063
|
+
if (rule.meta.fixable) {
|
4064
|
+
const { fixed, output } = linter.verifyAndFix(code, verifyConfig, { filename });
|
4065
|
+
if (fixed) {
|
4066
|
+
messageForSnapshot.push('🔧 Autofix output', indentCode(printCode(output), 2));
|
4067
|
+
}
|
3964
4068
|
}
|
4069
|
+
expect(messageForSnapshot.join('\n\n')).toMatchSnapshot();
|
3965
4070
|
}
|
3966
4071
|
}
|
3967
4072
|
}
|
@@ -4017,10 +4122,10 @@ function visualizeEslintMessage(text, result) {
|
|
4017
4122
|
exports.GraphQLRuleTester = GraphQLRuleTester;
|
4018
4123
|
exports.configs = configs;
|
4019
4124
|
exports.convertDescription = convertDescription;
|
4020
|
-
exports.convertLocation = convertLocation;
|
4021
|
-
exports.convertRange = convertRange;
|
4022
4125
|
exports.convertToESTree = convertToESTree;
|
4126
|
+
exports.convertToken = convertToken;
|
4023
4127
|
exports.extractCommentsFromAst = extractCommentsFromAst;
|
4128
|
+
exports.extractTokens = extractTokens;
|
4024
4129
|
exports.getBaseType = getBaseType;
|
4025
4130
|
exports.isNodeWithDescription = isNodeWithDescription;
|
4026
4131
|
exports.parse = parse;
|