@graphql-eslint/eslint-plugin 3.0.1 → 3.2.0-alpha-2e742a6.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 +57 -58
- package/docs/rules/known-fragment-names.md +9 -30
- package/docs/rules/no-undefined-variables.md +1 -1
- package/docs/rules/no-unreachable-types.md +0 -2
- package/docs/rules/no-unused-fields.md +0 -2
- package/docs/rules/no-unused-variables.md +1 -1
- package/index.js +185 -155
- package/index.mjs +187 -157
- package/package.json +1 -2
- package/rules/graphql-js-validation.d.ts +2 -5
package/index.js
CHANGED
@@ -6,7 +6,6 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau
|
|
6
6
|
|
7
7
|
const graphql = require('graphql');
|
8
8
|
const validate = require('graphql/validation/validate');
|
9
|
-
const _import = require('@graphql-tools/import');
|
10
9
|
const fs = require('fs');
|
11
10
|
const path = require('path');
|
12
11
|
const utils = require('@graphql-tools/utils');
|
@@ -335,34 +334,75 @@ function getLocation(loc, fieldName = '', offset) {
|
|
335
334
|
};
|
336
335
|
}
|
337
336
|
|
338
|
-
function
|
339
|
-
|
340
|
-
|
341
|
-
}
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
for (const error of validationErrors) {
|
348
|
-
const validateRuleName = ruleName || `[${extractRuleName(error.stack)}]`;
|
349
|
-
context.report({
|
350
|
-
loc: getLocation({ start: error.locations[0] }),
|
351
|
-
message: ruleName ? error.message : `${validateRuleName} ${error.message}`,
|
352
|
-
});
|
353
|
-
}
|
354
|
-
}
|
355
|
-
catch (e) {
|
337
|
+
function validateDoc(sourceNode, context, schema, documentNode, rule) {
|
338
|
+
if (documentNode.definitions.length === 0) {
|
339
|
+
return;
|
340
|
+
}
|
341
|
+
try {
|
342
|
+
const validationErrors = schema
|
343
|
+
? graphql.validate(schema, documentNode, [rule])
|
344
|
+
: validate.validateSDL(documentNode, null, [rule]);
|
345
|
+
for (const error of validationErrors) {
|
356
346
|
context.report({
|
357
|
-
|
358
|
-
message:
|
347
|
+
loc: getLocation({ start: error.locations[0] }),
|
348
|
+
message: error.message,
|
359
349
|
});
|
360
350
|
}
|
361
351
|
}
|
352
|
+
catch (e) {
|
353
|
+
context.report({
|
354
|
+
node: sourceNode,
|
355
|
+
message: e.message,
|
356
|
+
});
|
357
|
+
}
|
362
358
|
}
|
363
|
-
const
|
364
|
-
const
|
365
|
-
|
359
|
+
const getFragmentDefsAndFragmentSpreads = (schema, node) => {
|
360
|
+
const typeInfo = new graphql.TypeInfo(schema);
|
361
|
+
const fragmentDefs = new Set();
|
362
|
+
const fragmentSpreads = new Set();
|
363
|
+
const visitor = graphql.visitWithTypeInfo(typeInfo, {
|
364
|
+
FragmentDefinition(node) {
|
365
|
+
fragmentDefs.add(`${node.name.value}:${node.typeCondition.name.value}`);
|
366
|
+
},
|
367
|
+
FragmentSpread(node) {
|
368
|
+
const fieldDef = typeInfo.getFieldDef();
|
369
|
+
if (fieldDef) {
|
370
|
+
fragmentSpreads.add(`${node.name.value}:${typeInfo.getParentType().name}`);
|
371
|
+
}
|
372
|
+
},
|
373
|
+
});
|
374
|
+
graphql.visit(node, visitor);
|
375
|
+
return { fragmentDefs, fragmentSpreads };
|
376
|
+
};
|
377
|
+
const getMissingFragments = (schema, node) => {
|
378
|
+
const { fragmentDefs, fragmentSpreads } = getFragmentDefsAndFragmentSpreads(schema, node);
|
379
|
+
return [...fragmentSpreads].filter(name => !fragmentDefs.has(name));
|
380
|
+
};
|
381
|
+
const handleMissingFragments = ({ schema, node, ruleId, context }) => {
|
382
|
+
const missingFragments = getMissingFragments(schema, node);
|
383
|
+
if (missingFragments.length > 0) {
|
384
|
+
const siblings = requireSiblingsOperations(ruleId, context);
|
385
|
+
const fragmentsToAdd = [];
|
386
|
+
for (const missingFragment of missingFragments) {
|
387
|
+
const [fragmentName, fragmentTypeName] = missingFragment.split(':');
|
388
|
+
const fragments = siblings
|
389
|
+
.getFragment(fragmentName)
|
390
|
+
.map(source => source.document)
|
391
|
+
.filter(fragment => fragment.typeCondition.name.value === fragmentTypeName);
|
392
|
+
if (fragments.length > 1) {
|
393
|
+
// eslint-disable-next-line no-console
|
394
|
+
console.warn(`You have ${fragments.length} fragments that have same name ${fragmentName} and same type ${fragmentTypeName}. That can provoke unexpected result for "${ruleId}" rule.`);
|
395
|
+
}
|
396
|
+
fragmentsToAdd.push(fragments[0]);
|
397
|
+
}
|
398
|
+
if (fragmentsToAdd.length > 0) {
|
399
|
+
return {
|
400
|
+
kind: graphql.Kind.DOCUMENT,
|
401
|
+
definitions: [...node.definitions, ...fragmentsToAdd],
|
402
|
+
};
|
403
|
+
}
|
404
|
+
}
|
405
|
+
return node;
|
366
406
|
};
|
367
407
|
const validationToRule = (name, ruleName, docs, getDocumentNode) => {
|
368
408
|
var _a;
|
@@ -370,11 +410,11 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
|
|
370
410
|
try {
|
371
411
|
ruleFn = require(`graphql/validation/rules/${ruleName}Rule`)[`${ruleName}Rule`];
|
372
412
|
}
|
373
|
-
catch (
|
413
|
+
catch (_b) {
|
374
414
|
try {
|
375
415
|
ruleFn = require(`graphql/validation/rules/${ruleName}`)[`${ruleName}Rule`];
|
376
416
|
}
|
377
|
-
catch (
|
417
|
+
catch (_c) {
|
378
418
|
ruleFn = require('graphql/validation')[`${ruleName}Rule`];
|
379
419
|
}
|
380
420
|
}
|
@@ -396,30 +436,25 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
|
|
396
436
|
Document(node) {
|
397
437
|
if (!ruleFn) {
|
398
438
|
// eslint-disable-next-line no-console
|
399
|
-
console.warn(`You rule "${name}" depends on a GraphQL validation rule
|
439
|
+
console.warn(`You rule "${name}" depends on a GraphQL validation rule "${ruleName}" but it's not available in the "graphql-js" version you are using. Skipping...`);
|
400
440
|
return;
|
401
441
|
}
|
402
442
|
const schema = requiresSchema ? requireGraphQLSchemaFromContext(name, context) : null;
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
443
|
+
const documentNode = node.rawNode();
|
444
|
+
validateDoc(node, context, schema, getDocumentNode
|
445
|
+
? getDocumentNode({
|
446
|
+
schema,
|
447
|
+
node: documentNode,
|
448
|
+
ruleId: name,
|
449
|
+
context,
|
450
|
+
})
|
451
|
+
: documentNode, ruleFn);
|
409
452
|
},
|
410
453
|
};
|
411
454
|
},
|
412
455
|
},
|
413
456
|
};
|
414
457
|
};
|
415
|
-
const importFiles = (context) => {
|
416
|
-
const code = context.getSourceCode().text;
|
417
|
-
if (!isGraphQLImportFile(code)) {
|
418
|
-
return null;
|
419
|
-
}
|
420
|
-
// Import documents because file contains '#import' comments
|
421
|
-
return _import.processImport(context.getFilename());
|
422
|
-
};
|
423
458
|
const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-definitions', 'ExecutableDefinitions', {
|
424
459
|
category: 'Operations',
|
425
460
|
description: `A GraphQL document is only valid for execution if all definitions are either operation or fragment definitions.`,
|
@@ -438,14 +473,15 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
438
473
|
}), validationToRule('known-fragment-names', 'KnownFragmentNames', {
|
439
474
|
category: 'Operations',
|
440
475
|
description: `A GraphQL document is only valid if all \`...Fragment\` fragment spreads refer to fragments defined in the same document.`,
|
476
|
+
requiresSiblings: true,
|
441
477
|
examples: [
|
442
478
|
{
|
443
|
-
title: 'Incorrect
|
479
|
+
title: 'Incorrect',
|
444
480
|
code: /* GraphQL */ `
|
445
481
|
query {
|
446
482
|
user {
|
447
483
|
id
|
448
|
-
...UserFields
|
484
|
+
...UserFields # fragment not defined in the document
|
449
485
|
}
|
450
486
|
}
|
451
487
|
`,
|
@@ -467,42 +503,24 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
467
503
|
`,
|
468
504
|
},
|
469
505
|
{
|
470
|
-
title: 'Correct (
|
506
|
+
title: 'Correct (`UserFields` fragment located in a separate file)',
|
471
507
|
code: /* GraphQL */ `
|
472
|
-
#
|
473
|
-
|
508
|
+
# user.gql
|
474
509
|
query {
|
475
510
|
user {
|
476
511
|
id
|
477
512
|
...UserFields
|
478
513
|
}
|
479
514
|
}
|
480
|
-
`,
|
481
|
-
},
|
482
|
-
{
|
483
|
-
title: "False positive case\n\nFor extracting documents from code under the hood we use [graphql-tag-pluck](https://graphql-tools.com/docs/graphql-tag-pluck) that [don't support string interpolation](https://stackoverflow.com/questions/62749847/graphql-codegen-dynamic-fields-with-interpolation/62751311#62751311) for this moment.",
|
484
|
-
code: `
|
485
|
-
const USER_FIELDS = gql\`
|
486
|
-
fragment UserFields on User {
|
487
|
-
id
|
488
|
-
}
|
489
|
-
\`
|
490
|
-
|
491
|
-
const GET_USER = /* GraphQL */ \`
|
492
|
-
# eslint @graphql-eslint/known-fragment-names: 'error'
|
493
|
-
|
494
|
-
query User {
|
495
|
-
user {
|
496
|
-
...UserFields
|
497
|
-
}
|
498
|
-
}
|
499
515
|
|
500
|
-
|
501
|
-
|
502
|
-
|
516
|
+
# user-fields.gql
|
517
|
+
fragment UserFields on User {
|
518
|
+
id
|
519
|
+
}
|
520
|
+
`,
|
503
521
|
},
|
504
522
|
],
|
505
|
-
},
|
523
|
+
}, handleMissingFragments), validationToRule('known-type-names', 'KnownTypeNames', {
|
506
524
|
category: ['Schema', 'Operations'],
|
507
525
|
description: `A GraphQL document is only valid if referenced types (specifically variable definitions and fragment conditions) are defined by the type schema.`,
|
508
526
|
}), validationToRule('lone-anonymous-operation', 'LoneAnonymousOperation', {
|
@@ -518,40 +536,47 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
518
536
|
}), validationToRule('no-undefined-variables', 'NoUndefinedVariables', {
|
519
537
|
category: 'Operations',
|
520
538
|
description: `A GraphQL operation is only valid if all variables encountered, both directly and via fragment spreads, are defined by that operation.`,
|
521
|
-
|
539
|
+
requiresSiblings: true,
|
540
|
+
}, handleMissingFragments), validationToRule('no-unused-fragments', 'NoUnusedFragments', {
|
522
541
|
category: 'Operations',
|
523
542
|
description: `A GraphQL document is only valid if all fragment definitions are spread within operations, or spread within other fragments spread within operations.`,
|
524
543
|
requiresSiblings: true,
|
525
|
-
}, context => {
|
526
|
-
const siblings = requireSiblingsOperations(
|
527
|
-
const
|
528
|
-
|
529
|
-
|
530
|
-
filePath
|
531
|
-
|
532
|
-
}));
|
533
|
-
const getParentNode = (
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
.filter(isGraphQLImportFile)
|
538
|
-
.map(line => _import.parseImportLine(line.replace('#', '')))
|
539
|
-
.some(o => filePath === path.join(path.dirname(docFilePath), o.from));
|
540
|
-
if (!isFileImported) {
|
541
|
-
continue;
|
542
|
-
}
|
543
|
-
// Import first file that import this file
|
544
|
-
const document = _import.processImport(docFilePath);
|
545
|
-
// Import most top file that import this file
|
546
|
-
return getParentNode(docFilePath) || document;
|
544
|
+
}, ({ context, node, schema, ruleId }) => {
|
545
|
+
const siblings = requireSiblingsOperations(ruleId, context);
|
546
|
+
const filePathForDocumentsMap = [...siblings.getOperations(), ...siblings.getFragments()].reduce((map, { filePath, document }) => {
|
547
|
+
var _a;
|
548
|
+
(_a = map[filePath]) !== null && _a !== void 0 ? _a : (map[filePath] = []);
|
549
|
+
map[filePath].push(document);
|
550
|
+
return map;
|
551
|
+
}, Object.create(null));
|
552
|
+
const getParentNode = (currentFilePath, node) => {
|
553
|
+
const { fragmentDefs } = getFragmentDefsAndFragmentSpreads(schema, node);
|
554
|
+
if (fragmentDefs.size === 0) {
|
555
|
+
return node;
|
547
556
|
}
|
548
|
-
|
557
|
+
// skip iteration over documents for current filepath
|
558
|
+
delete filePathForDocumentsMap[currentFilePath];
|
559
|
+
for (const [filePath, documents] of Object.entries(filePathForDocumentsMap)) {
|
560
|
+
const missingFragments = getMissingFragments(schema, {
|
561
|
+
kind: graphql.Kind.DOCUMENT,
|
562
|
+
definitions: documents,
|
563
|
+
});
|
564
|
+
const isCurrentFileImportFragment = missingFragments.some(fragment => fragmentDefs.has(fragment));
|
565
|
+
if (isCurrentFileImportFragment) {
|
566
|
+
return getParentNode(filePath, {
|
567
|
+
kind: graphql.Kind.DOCUMENT,
|
568
|
+
definitions: [...node.definitions, ...documents],
|
569
|
+
});
|
570
|
+
}
|
571
|
+
}
|
572
|
+
return node;
|
549
573
|
};
|
550
|
-
return getParentNode(context.getFilename());
|
574
|
+
return getParentNode(context.getFilename(), node);
|
551
575
|
}), validationToRule('no-unused-variables', 'NoUnusedVariables', {
|
552
576
|
category: 'Operations',
|
553
577
|
description: `A GraphQL operation is only valid if all variables defined by an operation are used, either directly or within a spread fragment.`,
|
554
|
-
|
578
|
+
requiresSiblings: true,
|
579
|
+
}, handleMissingFragments), validationToRule('overlapping-fields-can-be-merged', 'OverlappingFieldsCanBeMerged', {
|
555
580
|
category: 'Operations',
|
556
581
|
description: `A selection set is only valid if all fields (including spreading any fragments) either correspond to distinct response names or can be merged without ambiguity.`,
|
557
582
|
}), validationToRule('possible-fragment-spread', 'PossibleFragmentSpreads', {
|
@@ -1845,7 +1870,7 @@ const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
|
|
1845
1870
|
const rule$9 = {
|
1846
1871
|
meta: {
|
1847
1872
|
messages: {
|
1848
|
-
[HASHTAG_COMMENT]:
|
1873
|
+
[HASHTAG_COMMENT]: `Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.`,
|
1849
1874
|
},
|
1850
1875
|
docs: {
|
1851
1876
|
description: 'Requires to use `"""` or `"` for adding a GraphQL description instead of `#`.\nAllows to use hashtag for comments, as long as it\'s not attached to an AST definition.',
|
@@ -1892,14 +1917,15 @@ const rule$9 = {
|
|
1892
1917
|
schema: [],
|
1893
1918
|
},
|
1894
1919
|
create(context) {
|
1920
|
+
const selector = `${graphql.Kind.DOCUMENT}[definitions.0.kind!=/^(${graphql.Kind.OPERATION_DEFINITION}|${graphql.Kind.FRAGMENT_DEFINITION})$/]`;
|
1895
1921
|
return {
|
1896
|
-
|
1922
|
+
[selector](node) {
|
1897
1923
|
const rawNode = node.rawNode();
|
1898
1924
|
let token = rawNode.loc.startToken;
|
1899
1925
|
while (token !== null) {
|
1900
1926
|
const { kind, prev, next, value, line, column } = token;
|
1901
1927
|
if (kind === graphql.TokenKind.COMMENT && prev && next) {
|
1902
|
-
const isEslintComment = value.
|
1928
|
+
const isEslintComment = value.trimStart().startsWith('eslint');
|
1903
1929
|
const linesAfter = next.line - line;
|
1904
1930
|
if (!isEslintComment && line !== prev.line && next.kind === graphql.TokenKind.NAME && linesAfter < 2) {
|
1905
1931
|
context.report({
|
@@ -2106,7 +2132,22 @@ const rule$c = {
|
|
2106
2132
|
};
|
2107
2133
|
|
2108
2134
|
const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
|
2109
|
-
const
|
2135
|
+
const RULE_ID = 'no-unreachable-types';
|
2136
|
+
const KINDS = [
|
2137
|
+
graphql.Kind.DIRECTIVE_DEFINITION,
|
2138
|
+
graphql.Kind.OBJECT_TYPE_DEFINITION,
|
2139
|
+
graphql.Kind.OBJECT_TYPE_EXTENSION,
|
2140
|
+
graphql.Kind.INTERFACE_TYPE_DEFINITION,
|
2141
|
+
graphql.Kind.INTERFACE_TYPE_EXTENSION,
|
2142
|
+
graphql.Kind.SCALAR_TYPE_DEFINITION,
|
2143
|
+
graphql.Kind.SCALAR_TYPE_EXTENSION,
|
2144
|
+
graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
2145
|
+
graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
2146
|
+
graphql.Kind.UNION_TYPE_DEFINITION,
|
2147
|
+
graphql.Kind.UNION_TYPE_EXTENSION,
|
2148
|
+
graphql.Kind.ENUM_TYPE_DEFINITION,
|
2149
|
+
graphql.Kind.ENUM_TYPE_EXTENSION,
|
2150
|
+
];
|
2110
2151
|
const rule$d = {
|
2111
2152
|
meta: {
|
2112
2153
|
messages: {
|
@@ -2115,7 +2156,7 @@ const rule$d = {
|
|
2115
2156
|
docs: {
|
2116
2157
|
description: `Requires all types to be reachable at some level by root level fields.`,
|
2117
2158
|
category: 'Schema',
|
2118
|
-
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${
|
2159
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
|
2119
2160
|
requiresSchema: true,
|
2120
2161
|
examples: [
|
2121
2162
|
{
|
@@ -2147,43 +2188,36 @@ const rule$d = {
|
|
2147
2188
|
],
|
2148
2189
|
recommended: true,
|
2149
2190
|
},
|
2150
|
-
fixable: 'code',
|
2151
2191
|
type: 'suggestion',
|
2152
2192
|
schema: [],
|
2193
|
+
hasSuggestions: true,
|
2153
2194
|
},
|
2154
2195
|
create(context) {
|
2155
|
-
const reachableTypes = requireReachableTypesFromContext(
|
2156
|
-
|
2157
|
-
const typeName = node.name.value;
|
2158
|
-
if (!reachableTypes.has(typeName)) {
|
2159
|
-
context.report({
|
2160
|
-
loc: getLocation(node.name.loc, typeName, { offsetStart: node.kind === graphql.Kind.DIRECTIVE_DEFINITION ? 2 : 1 }),
|
2161
|
-
messageId: UNREACHABLE_TYPE,
|
2162
|
-
data: { typeName },
|
2163
|
-
fix: fixer => fixer.remove(node),
|
2164
|
-
});
|
2165
|
-
}
|
2166
|
-
}
|
2196
|
+
const reachableTypes = requireReachableTypesFromContext(RULE_ID, context);
|
2197
|
+
const selector = KINDS.join(',');
|
2167
2198
|
return {
|
2168
|
-
|
2169
|
-
|
2170
|
-
|
2171
|
-
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
|
2177
|
-
|
2178
|
-
|
2179
|
-
|
2180
|
-
|
2199
|
+
[selector](node) {
|
2200
|
+
const typeName = node.name.value;
|
2201
|
+
if (!reachableTypes.has(typeName)) {
|
2202
|
+
context.report({
|
2203
|
+
loc: getLocation(node.name.loc, typeName, { offsetStart: node.kind === graphql.Kind.DIRECTIVE_DEFINITION ? 2 : 1 }),
|
2204
|
+
messageId: UNREACHABLE_TYPE,
|
2205
|
+
data: { typeName },
|
2206
|
+
suggest: [
|
2207
|
+
{
|
2208
|
+
desc: `Remove ${typeName}`,
|
2209
|
+
fix: fixer => fixer.remove(node),
|
2210
|
+
},
|
2211
|
+
],
|
2212
|
+
});
|
2213
|
+
}
|
2214
|
+
},
|
2181
2215
|
};
|
2182
2216
|
},
|
2183
2217
|
};
|
2184
2218
|
|
2185
2219
|
const UNUSED_FIELD = 'UNUSED_FIELD';
|
2186
|
-
const
|
2220
|
+
const RULE_ID$1 = 'no-unused-fields';
|
2187
2221
|
const rule$e = {
|
2188
2222
|
meta: {
|
2189
2223
|
messages: {
|
@@ -2192,7 +2226,7 @@ const rule$e = {
|
|
2192
2226
|
docs: {
|
2193
2227
|
description: `Requires all fields to be used at some level by siblings operations.`,
|
2194
2228
|
category: 'Schema',
|
2195
|
-
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${
|
2229
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$1}.md`,
|
2196
2230
|
requiresSiblings: true,
|
2197
2231
|
requiresSchema: true,
|
2198
2232
|
examples: [
|
@@ -2239,12 +2273,12 @@ const rule$e = {
|
|
2239
2273
|
},
|
2240
2274
|
],
|
2241
2275
|
},
|
2242
|
-
fixable: 'code',
|
2243
2276
|
type: 'suggestion',
|
2244
2277
|
schema: [],
|
2278
|
+
hasSuggestions: true,
|
2245
2279
|
},
|
2246
2280
|
create(context) {
|
2247
|
-
const usedFields = requireUsedFieldsFromContext(
|
2281
|
+
const usedFields = requireUsedFieldsFromContext(RULE_ID$1, context);
|
2248
2282
|
return {
|
2249
2283
|
FieldDefinition(node) {
|
2250
2284
|
var _a;
|
@@ -2258,22 +2292,18 @@ const rule$e = {
|
|
2258
2292
|
loc: getLocation(node.loc, fieldName),
|
2259
2293
|
messageId: UNUSED_FIELD,
|
2260
2294
|
data: { fieldName },
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2266
|
-
|
2267
|
-
|
2268
|
-
|
2269
|
-
|
2270
|
-
|
2271
|
-
|
2272
|
-
|
2273
|
-
}
|
2274
|
-
// Remove whitespace before token
|
2275
|
-
return fixer.removeRange([tokenBefore.range[1], node.range[1]]);
|
2276
|
-
},
|
2295
|
+
suggest: [
|
2296
|
+
{
|
2297
|
+
desc: `Remove "${fieldName}" field`,
|
2298
|
+
fix(fixer) {
|
2299
|
+
const sourceCode = context.getSourceCode();
|
2300
|
+
const tokenBefore = sourceCode.getTokenBefore(node);
|
2301
|
+
const tokenAfter = sourceCode.getTokenAfter(node);
|
2302
|
+
const isEmptyType = tokenBefore.type === '{' && tokenAfter.type === '}';
|
2303
|
+
return isEmptyType ? fixer.remove(node.parent) : fixer.remove(node);
|
2304
|
+
},
|
2305
|
+
},
|
2306
|
+
],
|
2277
2307
|
});
|
2278
2308
|
},
|
2279
2309
|
};
|
@@ -2636,14 +2666,14 @@ const rule$h = {
|
|
2636
2666
|
},
|
2637
2667
|
};
|
2638
2668
|
|
2639
|
-
const RULE_NAME
|
2669
|
+
const RULE_NAME = 'require-field-of-type-query-in-mutation-result';
|
2640
2670
|
const rule$i = {
|
2641
2671
|
meta: {
|
2642
2672
|
type: 'suggestion',
|
2643
2673
|
docs: {
|
2644
2674
|
category: 'Schema',
|
2645
2675
|
description: 'Allow the client in one round-trip not only to call mutation but also to get a wagon of data to update their application.\n> Currently, no errors are reported for result type `union`, `interface` and `scalar`.',
|
2646
|
-
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME
|
2676
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME}.md`,
|
2647
2677
|
requiresSchema: true,
|
2648
2678
|
examples: [
|
2649
2679
|
{
|
@@ -2678,7 +2708,7 @@ const rule$i = {
|
|
2678
2708
|
schema: [],
|
2679
2709
|
},
|
2680
2710
|
create(context) {
|
2681
|
-
const schema = requireGraphQLSchemaFromContext(RULE_NAME
|
2711
|
+
const schema = requireGraphQLSchemaFromContext(RULE_NAME, context);
|
2682
2712
|
const mutationType = schema.getMutationType();
|
2683
2713
|
const queryType = schema.getQueryType();
|
2684
2714
|
if (!mutationType || !queryType) {
|
@@ -3221,7 +3251,7 @@ const rule$l = {
|
|
3221
3251
|
},
|
3222
3252
|
};
|
3223
3253
|
|
3224
|
-
const RULE_NAME$
|
3254
|
+
const RULE_NAME$1 = 'unique-fragment-name';
|
3225
3255
|
const UNIQUE_FRAGMENT_NAME = 'UNIQUE_FRAGMENT_NAME';
|
3226
3256
|
const checkNode = (context, node, ruleName, messageId) => {
|
3227
3257
|
const documentName = node.name.value;
|
@@ -3253,7 +3283,7 @@ const rule$m = {
|
|
3253
3283
|
docs: {
|
3254
3284
|
category: 'Operations',
|
3255
3285
|
description: `Enforce unique fragment names across your project.`,
|
3256
|
-
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$
|
3286
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$1}.md`,
|
3257
3287
|
requiresSiblings: true,
|
3258
3288
|
examples: [
|
3259
3289
|
{
|
@@ -3298,13 +3328,13 @@ const rule$m = {
|
|
3298
3328
|
create(context) {
|
3299
3329
|
return {
|
3300
3330
|
FragmentDefinition(node) {
|
3301
|
-
checkNode(context, node, RULE_NAME$
|
3331
|
+
checkNode(context, node, RULE_NAME$1, UNIQUE_FRAGMENT_NAME);
|
3302
3332
|
},
|
3303
3333
|
};
|
3304
3334
|
},
|
3305
3335
|
};
|
3306
3336
|
|
3307
|
-
const RULE_NAME$
|
3337
|
+
const RULE_NAME$2 = 'unique-operation-name';
|
3308
3338
|
const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
|
3309
3339
|
const rule$n = {
|
3310
3340
|
meta: {
|
@@ -3312,7 +3342,7 @@ const rule$n = {
|
|
3312
3342
|
docs: {
|
3313
3343
|
category: 'Operations',
|
3314
3344
|
description: `Enforce unique operation names across your project.`,
|
3315
|
-
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$
|
3345
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$2}.md`,
|
3316
3346
|
requiresSiblings: true,
|
3317
3347
|
examples: [
|
3318
3348
|
{
|
@@ -3361,7 +3391,7 @@ const rule$n = {
|
|
3361
3391
|
create(context) {
|
3362
3392
|
return {
|
3363
3393
|
'OperationDefinition[name!=undefined]'(node) {
|
3364
|
-
checkNode(context, node, RULE_NAME$
|
3394
|
+
checkNode(context, node, RULE_NAME$2, UNIQUE_OPERATION_NAME);
|
3365
3395
|
},
|
3366
3396
|
};
|
3367
3397
|
},
|