@graphql-eslint/eslint-plugin 4.3.0-alpha-20241205065956-601947f40654914ef0effc1cdf4fe269245fc7bc → 4.3.1-alpha-20241207204625-6a4230707a78900a6339b03afe904b9dd6c31561
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/cjs/cache.js +6 -2
- package/cjs/configs/operations-all.js +2 -2
- package/cjs/configs/schema-all.js +2 -2
- package/cjs/configs/schema-recommended.js +1 -1
- package/cjs/documents.js +13 -7
- package/cjs/estree-converter/converter.js +17 -8
- package/cjs/estree-converter/utils.js +22 -9
- package/cjs/graphql-config.js +13 -6
- package/cjs/index.d.cts +18 -4
- package/cjs/meta.js +1 -1
- package/cjs/parser.js +36 -9
- package/cjs/processor.js +48 -20
- package/cjs/rules/alphabetize/index.js +99 -47
- package/cjs/rules/description-style/index.js +10 -6
- package/cjs/rules/graphql-js-validation.js +142 -108
- package/cjs/rules/index.d.cts +18 -4
- package/cjs/rules/input-name/index.js +51 -38
- package/cjs/rules/lone-executable-definition/index.js +15 -6
- package/cjs/rules/match-document-filename/index.d.cts +4 -3
- package/cjs/rules/match-document-filename/index.js +63 -37
- package/cjs/rules/naming-convention/index.d.cts +6 -10
- package/cjs/rules/naming-convention/index.js +146 -57
- package/cjs/rules/no-anonymous-operations/index.js +8 -5
- package/cjs/rules/no-deprecated/index.js +27 -13
- package/cjs/rules/no-duplicate-fields/index.js +15 -8
- package/cjs/rules/no-hashtag-description/index.js +18 -10
- package/cjs/rules/no-one-place-fragments/index.js +17 -10
- package/cjs/rules/no-root-type/index.js +15 -8
- package/cjs/rules/no-scalar-result-type-on-mutation/index.js +20 -12
- package/cjs/rules/no-typename-prefix/index.js +25 -21
- package/cjs/rules/no-unreachable-types/index.js +34 -17
- package/cjs/rules/no-unused-fields/index.js +56 -30
- package/cjs/rules/relay-arguments/index.js +31 -13
- package/cjs/rules/relay-connection-types/index.js +31 -9
- package/cjs/rules/relay-edge-types/index.js +84 -41
- package/cjs/rules/relay-page-info/index.js +31 -14
- package/cjs/rules/require-deprecation-date/index.js +20 -9
- package/cjs/rules/require-deprecation-reason/index.js +8 -5
- package/cjs/rules/require-description/index.d.cts +79 -13
- package/cjs/rules/require-description/index.js +67 -49
- package/cjs/rules/require-field-of-type-query-in-mutation-result/index.js +21 -10
- package/cjs/rules/require-import-fragment/index.js +20 -11
- package/cjs/rules/require-nullable-fields-with-oneof/index.js +12 -5
- package/cjs/rules/require-nullable-result-in-root/index.js +32 -27
- package/cjs/rules/require-selections/index.js +88 -46
- package/cjs/rules/require-type-pattern-with-oneof/index.js +14 -10
- package/cjs/rules/selection-set-depth/index.js +19 -10
- package/cjs/rules/strict-id-in-types/index.js +32 -19
- package/cjs/rules/unique-enum-value-names/index.js +4 -3
- package/cjs/rules/unique-fragment-name/index.js +25 -18
- package/cjs/rules/unique-operation-name/index.js +5 -5
- package/cjs/schema.js +14 -8
- package/cjs/siblings.js +60 -32
- package/cjs/utils.js +23 -9
- package/esm/cache.js +6 -2
- package/esm/configs/operations-all.js +2 -2
- package/esm/configs/schema-all.js +2 -2
- package/esm/configs/schema-recommended.js +1 -1
- package/esm/documents.js +13 -7
- package/esm/estree-converter/converter.js +17 -8
- package/esm/estree-converter/utils.js +22 -9
- package/esm/graphql-config.js +13 -6
- package/esm/index.d.ts +19 -5
- package/esm/meta.js +1 -1
- package/esm/parser.js +36 -9
- package/esm/processor.js +48 -20
- package/esm/rules/alphabetize/index.js +99 -47
- package/esm/rules/description-style/index.js +10 -6
- package/esm/rules/graphql-js-validation.js +142 -108
- package/esm/rules/index.d.ts +19 -5
- package/esm/rules/input-name/index.js +51 -38
- package/esm/rules/lone-executable-definition/index.js +15 -6
- package/esm/rules/match-document-filename/index.d.ts +4 -3
- package/esm/rules/match-document-filename/index.js +63 -37
- package/esm/rules/naming-convention/index.d.ts +6 -10
- package/esm/rules/naming-convention/index.js +146 -57
- package/esm/rules/no-anonymous-operations/index.js +8 -5
- package/esm/rules/no-deprecated/index.js +27 -13
- package/esm/rules/no-duplicate-fields/index.js +15 -8
- package/esm/rules/no-hashtag-description/index.js +18 -10
- package/esm/rules/no-one-place-fragments/index.js +17 -10
- package/esm/rules/no-root-type/index.js +15 -8
- package/esm/rules/no-scalar-result-type-on-mutation/index.js +20 -12
- package/esm/rules/no-typename-prefix/index.js +25 -21
- package/esm/rules/no-unreachable-types/index.js +34 -17
- package/esm/rules/no-unused-fields/index.js +56 -30
- package/esm/rules/relay-arguments/index.js +31 -13
- package/esm/rules/relay-connection-types/index.js +31 -9
- package/esm/rules/relay-edge-types/index.js +84 -41
- package/esm/rules/relay-page-info/index.js +31 -14
- package/esm/rules/require-deprecation-date/index.js +20 -9
- package/esm/rules/require-deprecation-reason/index.js +8 -5
- package/esm/rules/require-description/index.d.ts +79 -13
- package/esm/rules/require-description/index.js +67 -49
- package/esm/rules/require-field-of-type-query-in-mutation-result/index.js +21 -10
- package/esm/rules/require-import-fragment/index.js +20 -11
- package/esm/rules/require-nullable-fields-with-oneof/index.js +12 -5
- package/esm/rules/require-nullable-result-in-root/index.js +32 -27
- package/esm/rules/require-selections/index.js +88 -46
- package/esm/rules/require-type-pattern-with-oneof/index.js +14 -10
- package/esm/rules/selection-set-depth/index.js +19 -10
- package/esm/rules/strict-id-in-types/index.js +32 -19
- package/esm/rules/unique-enum-value-names/index.js +4 -3
- package/esm/rules/unique-fragment-name/index.js +25 -18
- package/esm/rules/unique-operation-name/index.js +5 -5
- package/esm/schema.js +15 -8
- package/esm/siblings.js +60 -32
- package/esm/utils.js +23 -9
- package/index.browser.js +1838 -1135
- package/package.json +1 -1
@@ -24,26 +24,33 @@ const KindToDisplayName = {
|
|
24
24
|
[_graphql.Kind.OPERATION_DEFINITION]: "Operation",
|
25
25
|
[_graphql.Kind.FRAGMENT_DEFINITION]: "Fragment",
|
26
26
|
[_graphql.Kind.VARIABLE_DEFINITION]: "Variable"
|
27
|
-
}
|
27
|
+
};
|
28
|
+
const StyleToRegex = {
|
28
29
|
camelCase: /^[a-z][\dA-Za-z]*$/,
|
29
30
|
PascalCase: /^[A-Z][\dA-Za-z]*$/,
|
30
31
|
snake_case: /^[a-z][\d_a-z]*[\da-z]*$/,
|
31
32
|
UPPER_CASE: /^[A-Z][\dA-Z_]*[\dA-Z]*$/
|
32
|
-
}
|
33
|
+
};
|
34
|
+
const ALLOWED_KINDS = Object.keys(KindToDisplayName).sort();
|
35
|
+
const ALLOWED_STYLES = Object.keys(StyleToRegex);
|
36
|
+
const schemaOption = {
|
33
37
|
oneOf: [{ $ref: "#/definitions/asString" }, { $ref: "#/definitions/asObject" }]
|
34
|
-
}
|
38
|
+
};
|
39
|
+
const descriptionPrefixesSuffixes = (name, id) => `> [!WARNING]
|
35
40
|
>
|
36
|
-
> This option is deprecated and will be removed in the next major release. Use [\`${name}\`](#${
|
41
|
+
> This option is deprecated and will be removed in the next major release. Use [\`${name}\`](#${id}) instead.`;
|
42
|
+
const caseSchema = {
|
43
|
+
enum: ALLOWED_STYLES,
|
44
|
+
description: `One of: ${ALLOWED_STYLES.map((t) => `\`${t}\``).join(", ")}`
|
45
|
+
};
|
46
|
+
const schema = {
|
37
47
|
definitions: {
|
38
|
-
asString:
|
39
|
-
enum: ALLOWED_STYLES,
|
40
|
-
description: `One of: ${ALLOWED_STYLES.map((t) => `\`${t}\``).join(", ")}`
|
41
|
-
},
|
48
|
+
asString: caseSchema,
|
42
49
|
asObject: {
|
43
50
|
type: "object",
|
44
|
-
additionalProperties:
|
51
|
+
additionalProperties: false,
|
45
52
|
properties: {
|
46
|
-
style:
|
53
|
+
style: caseSchema,
|
47
54
|
prefix: { type: "string" },
|
48
55
|
suffix: { type: "string" },
|
49
56
|
forbiddenPatterns: {
|
@@ -53,28 +60,25 @@ const KindToDisplayName = {
|
|
53
60
|
},
|
54
61
|
description: "Should be of instance of `RegEx`"
|
55
62
|
},
|
56
|
-
|
57
|
-
|
58
|
-
items: {
|
59
|
-
type: "object"
|
60
|
-
},
|
63
|
+
requiredPattern: {
|
64
|
+
type: "object",
|
61
65
|
description: "Should be of instance of `RegEx`"
|
62
66
|
},
|
63
67
|
forbiddenPrefixes: {
|
64
68
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
65
|
-
description: descriptionPrefixesSuffixes("forbiddenPatterns")
|
69
|
+
description: descriptionPrefixesSuffixes("forbiddenPatterns", "forbiddenpatterns-array")
|
66
70
|
},
|
67
71
|
forbiddenSuffixes: {
|
68
72
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
69
|
-
description: descriptionPrefixesSuffixes("forbiddenPatterns")
|
73
|
+
description: descriptionPrefixesSuffixes("forbiddenPatterns", "forbiddenpatterns-array")
|
70
74
|
},
|
71
75
|
requiredPrefixes: {
|
72
76
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
73
|
-
description: descriptionPrefixesSuffixes("
|
77
|
+
description: descriptionPrefixesSuffixes("requiredPattern", "requiredpattern-object")
|
74
78
|
},
|
75
79
|
requiredSuffixes: {
|
76
80
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
77
|
-
description: descriptionPrefixesSuffixes("
|
81
|
+
description: descriptionPrefixesSuffixes("requiredPattern", "requiredpattern-object")
|
78
82
|
},
|
79
83
|
ignorePattern: {
|
80
84
|
type: "string",
|
@@ -87,13 +91,12 @@ const KindToDisplayName = {
|
|
87
91
|
maxItems: 1,
|
88
92
|
items: {
|
89
93
|
type: "object",
|
90
|
-
additionalProperties:
|
94
|
+
additionalProperties: false,
|
91
95
|
properties: {
|
92
96
|
types: {
|
93
97
|
...schemaOption,
|
94
98
|
description: `Includes:
|
95
|
-
${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
96
|
-
`)}`
|
99
|
+
${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join("\n")}`
|
97
100
|
},
|
98
101
|
...Object.fromEntries(
|
99
102
|
ALLOWED_KINDS.map((kind) => [
|
@@ -108,11 +111,11 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
108
111
|
),
|
109
112
|
allowLeadingUnderscore: {
|
110
113
|
type: "boolean",
|
111
|
-
default:
|
114
|
+
default: false
|
112
115
|
},
|
113
116
|
allowTrailingUnderscore: {
|
114
117
|
type: "boolean",
|
115
|
-
default:
|
118
|
+
default: false
|
116
119
|
}
|
117
120
|
},
|
118
121
|
patternProperties: {
|
@@ -124,16 +127,16 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
124
127
|
"> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.",
|
125
128
|
">",
|
126
129
|
"> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`."
|
127
|
-
].join(
|
128
|
-
`)
|
130
|
+
].join("\n")
|
129
131
|
}
|
130
|
-
}
|
132
|
+
};
|
133
|
+
const rule = {
|
131
134
|
meta: {
|
132
135
|
type: "suggestion",
|
133
136
|
docs: {
|
134
137
|
description: "Require names to follow specified conventions.",
|
135
138
|
category: ["Schema", "Operations"],
|
136
|
-
recommended:
|
139
|
+
recommended: true,
|
137
140
|
url: "https://the-guild.dev/graphql/eslint/rules/naming-convention",
|
138
141
|
examples: [
|
139
142
|
{
|
@@ -267,6 +270,30 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
267
270
|
}
|
268
271
|
`
|
269
272
|
)
|
273
|
+
},
|
274
|
+
{
|
275
|
+
title: "Correct (Relay fragment convention `<module_name>_<property_name>`)",
|
276
|
+
usage: [
|
277
|
+
{
|
278
|
+
FragmentDefinition: {
|
279
|
+
style: "PascalCase",
|
280
|
+
requiredPattern: /_(?<camelCase>.+?)$/
|
281
|
+
}
|
282
|
+
}
|
283
|
+
],
|
284
|
+
code: (
|
285
|
+
/* GraphQL */
|
286
|
+
`
|
287
|
+
# schema
|
288
|
+
type User {
|
289
|
+
# ...
|
290
|
+
}
|
291
|
+
# operations
|
292
|
+
fragment UserFields_data on User {
|
293
|
+
# ...
|
294
|
+
}
|
295
|
+
`
|
296
|
+
)
|
270
297
|
}
|
271
298
|
],
|
272
299
|
configOptions: {
|
@@ -325,14 +352,16 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
325
352
|
]
|
326
353
|
}
|
327
354
|
},
|
328
|
-
hasSuggestions:
|
355
|
+
hasSuggestions: true,
|
329
356
|
schema
|
330
357
|
},
|
331
358
|
create(context) {
|
332
|
-
const options = context.options[0] || {}
|
359
|
+
const options = context.options[0] || {};
|
360
|
+
const { allowLeadingUnderscore, allowTrailingUnderscore, types, ...restOptions } = options;
|
361
|
+
const ignoredNodes = /* @__PURE__ */ new Set();
|
333
362
|
function normalisePropertyOption(kind) {
|
334
363
|
const style = restOptions[kind] || types;
|
335
|
-
return typeof style
|
364
|
+
return typeof style === "object" ? style : { style };
|
336
365
|
}
|
337
366
|
function report(node, message, suggestedNames) {
|
338
367
|
context.report({
|
@@ -346,8 +375,9 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
346
375
|
}
|
347
376
|
const checkNode = (selector) => (n) => {
|
348
377
|
const { name: node } = n.kind === _graphql.Kind.VARIABLE_DEFINITION ? n.variable : n;
|
349
|
-
if (!node)
|
378
|
+
if (!node) {
|
350
379
|
return;
|
380
|
+
}
|
351
381
|
const {
|
352
382
|
prefix,
|
353
383
|
suffix,
|
@@ -358,12 +388,18 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
358
388
|
requiredPrefixes,
|
359
389
|
requiredSuffixes,
|
360
390
|
forbiddenPatterns,
|
361
|
-
|
362
|
-
} = normalisePropertyOption(selector)
|
391
|
+
requiredPattern
|
392
|
+
} = normalisePropertyOption(selector);
|
393
|
+
const nodeName = node.value;
|
394
|
+
const error = getError();
|
363
395
|
if (error) {
|
364
|
-
const { errorMessage, renameToNames } = error
|
396
|
+
const { errorMessage, renameToNames } = error;
|
397
|
+
const [leadingUnderscores] = nodeName.match(/^_*/);
|
398
|
+
const [trailingUnderscores] = nodeName.match(/_*$/);
|
399
|
+
const suggestedNames = renameToNames.map(
|
365
400
|
(renameToName) => leadingUnderscores + renameToName + trailingUnderscores
|
366
|
-
)
|
401
|
+
);
|
402
|
+
const name = _utilsjs.displayNodeName.call(void 0, n);
|
367
403
|
report(
|
368
404
|
node,
|
369
405
|
`${name[0].toUpperCase()}${name.slice(1)} should ${errorMessage}`,
|
@@ -371,80 +407,133 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
371
407
|
);
|
372
408
|
}
|
373
409
|
function getError() {
|
374
|
-
|
410
|
+
let name = nodeName;
|
411
|
+
if (allowLeadingUnderscore) name = name.replace(/^_+/, "");
|
412
|
+
if (allowTrailingUnderscore) name = name.replace(/_+$/, "");
|
375
413
|
if (ignorePattern && new RegExp(ignorePattern, "u").test(name)) {
|
376
|
-
"name" in n
|
414
|
+
if ("name" in n) {
|
415
|
+
ignoredNodes.add(n.name);
|
416
|
+
}
|
377
417
|
return;
|
378
418
|
}
|
379
|
-
if (prefix && !name.startsWith(prefix))
|
419
|
+
if (prefix && !name.startsWith(prefix)) {
|
380
420
|
return {
|
381
421
|
errorMessage: `have "${prefix}" prefix`,
|
382
422
|
renameToNames: [prefix + name]
|
383
423
|
};
|
384
|
-
|
424
|
+
}
|
425
|
+
if (suffix && !name.endsWith(suffix)) {
|
385
426
|
return {
|
386
427
|
errorMessage: `have "${suffix}" suffix`,
|
387
428
|
renameToNames: [name + suffix]
|
388
429
|
};
|
430
|
+
}
|
431
|
+
if (requiredPattern) {
|
432
|
+
if (requiredPattern.source.includes("(?<")) {
|
433
|
+
try {
|
434
|
+
name = name.replace(requiredPattern, (originalString, ...args) => {
|
435
|
+
const groups = args.at(-1);
|
436
|
+
for (const [styleName, value] of Object.entries(groups)) {
|
437
|
+
if (!(styleName in StyleToRegex)) {
|
438
|
+
throw new Error("Invalid case style in `requiredPatterns` option");
|
439
|
+
}
|
440
|
+
if (value === _utilsjs.convertCase.call(void 0, styleName, value)) {
|
441
|
+
return "";
|
442
|
+
}
|
443
|
+
throw new Error(`contain the required pattern: ${requiredPattern}`);
|
444
|
+
}
|
445
|
+
return originalString;
|
446
|
+
});
|
447
|
+
if (name === nodeName) {
|
448
|
+
throw new Error(`contain the required pattern: ${requiredPattern}`);
|
449
|
+
}
|
450
|
+
} catch (error2) {
|
451
|
+
return {
|
452
|
+
errorMessage: error2.message,
|
453
|
+
renameToNames: []
|
454
|
+
};
|
455
|
+
}
|
456
|
+
} else if (!requiredPattern.test(name)) {
|
457
|
+
return {
|
458
|
+
errorMessage: `contain the required pattern: ${requiredPattern}`,
|
459
|
+
renameToNames: []
|
460
|
+
};
|
461
|
+
}
|
462
|
+
}
|
389
463
|
const forbidden = _optionalChain([forbiddenPatterns, 'optionalAccess', _ => _.find, 'call', _2 => _2((pattern) => pattern.test(name))]);
|
390
|
-
if (forbidden)
|
464
|
+
if (forbidden) {
|
391
465
|
return {
|
392
466
|
errorMessage: `not contain the forbidden pattern "${forbidden}"`,
|
393
467
|
renameToNames: [name.replace(forbidden, "")]
|
394
468
|
};
|
395
|
-
|
396
|
-
return {
|
397
|
-
errorMessage: `contain the required pattern: ${_utilsjs.englishJoinWords.call(void 0, requiredPatterns.map((re) => re.source))}`,
|
398
|
-
renameToNames: []
|
399
|
-
};
|
469
|
+
}
|
400
470
|
const forbiddenPrefix = _optionalChain([forbiddenPrefixes, 'optionalAccess', _3 => _3.find, 'call', _4 => _4((prefix2) => name.startsWith(prefix2))]);
|
401
|
-
if (forbiddenPrefix)
|
471
|
+
if (forbiddenPrefix) {
|
402
472
|
return {
|
403
473
|
errorMessage: `not have "${forbiddenPrefix}" prefix`,
|
404
474
|
renameToNames: [name.replace(new RegExp(`^${forbiddenPrefix}`), "")]
|
405
475
|
};
|
476
|
+
}
|
406
477
|
const forbiddenSuffix = _optionalChain([forbiddenSuffixes, 'optionalAccess', _5 => _5.find, 'call', _6 => _6((suffix2) => name.endsWith(suffix2))]);
|
407
|
-
if (forbiddenSuffix)
|
478
|
+
if (forbiddenSuffix) {
|
408
479
|
return {
|
409
480
|
errorMessage: `not have "${forbiddenSuffix}" suffix`,
|
410
481
|
renameToNames: [name.replace(new RegExp(`${forbiddenSuffix}$`), "")]
|
411
482
|
};
|
412
|
-
|
483
|
+
}
|
484
|
+
if (requiredPrefixes && !requiredPrefixes.some((requiredPrefix) => name.startsWith(requiredPrefix))) {
|
413
485
|
return {
|
414
486
|
errorMessage: `have one of the following prefixes: ${_utilsjs.englishJoinWords.call(void 0,
|
415
487
|
requiredPrefixes
|
416
488
|
)}`,
|
417
489
|
renameToNames: style ? requiredPrefixes.map((prefix2) => _utilsjs.convertCase.call(void 0, style, `${prefix2} ${name}`)) : requiredPrefixes.map((prefix2) => `${prefix2}${name}`)
|
418
490
|
};
|
419
|
-
|
491
|
+
}
|
492
|
+
if (requiredSuffixes && !requiredSuffixes.some((requiredSuffix) => name.endsWith(requiredSuffix))) {
|
420
493
|
return {
|
421
494
|
errorMessage: `have one of the following suffixes: ${_utilsjs.englishJoinWords.call(void 0,
|
422
495
|
requiredSuffixes
|
423
496
|
)}`,
|
424
497
|
renameToNames: style ? requiredSuffixes.map((suffix2) => _utilsjs.convertCase.call(void 0, style, `${name} ${suffix2}`)) : requiredSuffixes.map((suffix2) => `${name}${suffix2}`)
|
425
498
|
};
|
426
|
-
|
499
|
+
}
|
500
|
+
if (!style) {
|
427
501
|
return;
|
428
|
-
|
502
|
+
}
|
503
|
+
const caseRegex = StyleToRegex[style];
|
504
|
+
if (!caseRegex.test(name)) {
|
429
505
|
return {
|
430
506
|
errorMessage: `be in ${style} format`,
|
431
507
|
renameToNames: [_utilsjs.convertCase.call(void 0, style, name)]
|
432
508
|
};
|
509
|
+
}
|
433
510
|
}
|
434
|
-
}
|
435
|
-
|
511
|
+
};
|
512
|
+
const checkUnderscore = (isLeading) => (node) => {
|
513
|
+
if (ignoredNodes.has(node)) {
|
436
514
|
return;
|
515
|
+
}
|
516
|
+
if (node.parent.kind === "Field" && node.parent.alias !== node) {
|
517
|
+
return;
|
518
|
+
}
|
437
519
|
const suggestedName = node.value.replace(isLeading ? /^_+/ : /_+$/, "");
|
438
520
|
report(node, `${isLeading ? "Leading" : "Trailing"} underscores are not allowed`, [
|
439
521
|
suggestedName
|
440
522
|
]);
|
441
|
-
}
|
442
|
-
|
523
|
+
};
|
524
|
+
const listeners = {};
|
525
|
+
if (!allowLeadingUnderscore) {
|
526
|
+
listeners["Name[value=/^_/]"] = checkUnderscore(true);
|
527
|
+
}
|
528
|
+
if (!allowTrailingUnderscore) {
|
529
|
+
listeners["Name[value=/_$/]"] = checkUnderscore(false);
|
530
|
+
}
|
443
531
|
const selectors = new Set(
|
444
532
|
[types && _utilsjs.TYPES_KINDS, Object.keys(restOptions)].filter((v) => !!v).flat()
|
445
533
|
);
|
446
|
-
for (const selector of selectors)
|
534
|
+
for (const selector of selectors) {
|
447
535
|
listeners[selector] = checkNode(selector);
|
536
|
+
}
|
448
537
|
return listeners;
|
449
538
|
}
|
450
539
|
};
|
@@ -1,13 +1,14 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _graphql = require('graphql');
|
2
2
|
var _utilsjs = require('../../utils.js');
|
3
|
-
const RULE_ID = "no-anonymous-operations"
|
3
|
+
const RULE_ID = "no-anonymous-operations";
|
4
|
+
const rule = {
|
4
5
|
meta: {
|
5
6
|
type: "suggestion",
|
6
|
-
hasSuggestions:
|
7
|
+
hasSuggestions: true,
|
7
8
|
docs: {
|
8
9
|
category: "Operations",
|
9
10
|
description: "Require name for your GraphQL operations. This is useful since most GraphQL client libraries are using the operation name for caching purposes.",
|
10
|
-
recommended:
|
11
|
+
recommended: true,
|
11
12
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
12
13
|
examples: [
|
13
14
|
{
|
@@ -42,7 +43,8 @@ const RULE_ID = "no-anonymous-operations", rule = exports.rule = {
|
|
42
43
|
create(context) {
|
43
44
|
return {
|
44
45
|
"OperationDefinition[name=undefined]"(node) {
|
45
|
-
const [firstSelection] = node.selectionSet.selections
|
46
|
+
const [firstSelection] = node.selectionSet.selections;
|
47
|
+
const suggestedName = firstSelection.kind === _graphql.Kind.FIELD ? (firstSelection.alias || firstSelection.name).value : node.operation;
|
46
48
|
context.report({
|
47
49
|
loc: _utilsjs.getLocation.call(void 0, node.loc.start, node.operation),
|
48
50
|
messageId: RULE_ID,
|
@@ -53,7 +55,8 @@ const RULE_ID = "no-anonymous-operations", rule = exports.rule = {
|
|
53
55
|
{
|
54
56
|
desc: `Rename to \`${suggestedName}\``,
|
55
57
|
fix(fixer) {
|
56
|
-
const
|
58
|
+
const sourceCode = context.getSourceCode();
|
59
|
+
const hasQueryKeyword = sourceCode.getText({ range: [node.range[0], node.range[0] + 1] }) !== "{";
|
57
60
|
return fixer.insertTextAfterRange(
|
58
61
|
[node.range[0], node.range[0] + (hasQueryKeyword ? node.operation.length : 0)],
|
59
62
|
`${hasQueryKeyword ? "" : "query"} ${suggestedName}${hasQueryKeyword ? "" : " "}`
|
@@ -1,13 +1,14 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _utilsjs = require('../../utils.js');
|
2
|
-
const RULE_ID = "no-deprecated"
|
2
|
+
const RULE_ID = "no-deprecated";
|
3
|
+
const rule = {
|
3
4
|
meta: {
|
4
5
|
type: "suggestion",
|
5
|
-
hasSuggestions:
|
6
|
+
hasSuggestions: true,
|
6
7
|
docs: {
|
7
8
|
category: "Operations",
|
8
9
|
description: "Enforce that deprecated fields or enum values are not in use by operations.",
|
9
10
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
10
|
-
requiresSchema:
|
11
|
+
requiresSchema: true,
|
11
12
|
examples: [
|
12
13
|
{
|
13
14
|
title: "Incorrect (field)",
|
@@ -79,7 +80,7 @@ const RULE_ID = "no-deprecated", rule = exports.rule = {
|
|
79
80
|
)
|
80
81
|
}
|
81
82
|
],
|
82
|
-
recommended:
|
83
|
+
recommended: true
|
83
84
|
},
|
84
85
|
messages: {
|
85
86
|
[RULE_ID]: "{{ type }} is marked as deprecated in your GraphQL schema (reason: {{ reason }})"
|
@@ -107,24 +108,37 @@ const RULE_ID = "no-deprecated", rule = exports.rule = {
|
|
107
108
|
}
|
108
109
|
return {
|
109
110
|
EnumValue(node) {
|
110
|
-
const
|
111
|
-
reason
|
111
|
+
const typeInfo = node.typeInfo();
|
112
|
+
const reason = _optionalChain([typeInfo, 'access', _ => _.enumValue, 'optionalAccess', _2 => _2.deprecationReason]);
|
113
|
+
if (reason) {
|
114
|
+
report(node, reason);
|
115
|
+
}
|
112
116
|
},
|
113
117
|
Field(node) {
|
114
|
-
const
|
115
|
-
reason
|
118
|
+
const typeInfo = node.typeInfo();
|
119
|
+
const reason = _optionalChain([typeInfo, 'access', _3 => _3.fieldDef, 'optionalAccess', _4 => _4.deprecationReason]);
|
120
|
+
if (reason) {
|
121
|
+
report(node, reason);
|
122
|
+
}
|
116
123
|
},
|
117
124
|
Argument(node) {
|
118
|
-
const
|
119
|
-
reason
|
125
|
+
const typeInfo = node.typeInfo();
|
126
|
+
const reason = _optionalChain([typeInfo, 'access', _5 => _5.argument, 'optionalAccess', _6 => _6.deprecationReason]);
|
127
|
+
if (reason) {
|
128
|
+
report(node, reason);
|
129
|
+
}
|
120
130
|
},
|
121
131
|
ObjectValue(node) {
|
122
132
|
const { inputType } = node.typeInfo();
|
123
|
-
if (inputType
|
133
|
+
if (!inputType) return;
|
134
|
+
if ("getFields" in inputType) {
|
124
135
|
const fields = inputType.getFields();
|
125
136
|
for (const field of node.fields) {
|
126
|
-
const fieldName = field.name.value
|
127
|
-
reason
|
137
|
+
const fieldName = field.name.value;
|
138
|
+
const reason = fields[fieldName].deprecationReason;
|
139
|
+
if (reason) {
|
140
|
+
report(field, reason);
|
141
|
+
}
|
128
142
|
}
|
129
143
|
}
|
130
144
|
}
|
@@ -1,13 +1,14 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _graphql = require('graphql');
|
2
|
-
const RULE_ID = "no-duplicate-fields"
|
2
|
+
const RULE_ID = "no-duplicate-fields";
|
3
|
+
const rule = {
|
3
4
|
meta: {
|
4
5
|
type: "suggestion",
|
5
|
-
hasSuggestions:
|
6
|
+
hasSuggestions: true,
|
6
7
|
docs: {
|
7
8
|
description: "Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.",
|
8
9
|
category: "Operations",
|
9
10
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
10
|
-
recommended:
|
11
|
+
recommended: true,
|
11
12
|
examples: [
|
12
13
|
{
|
13
14
|
title: "Incorrect",
|
@@ -88,24 +89,30 @@ const RULE_ID = "no-duplicate-fields", rule = exports.rule = {
|
|
88
89
|
}
|
89
90
|
]
|
90
91
|
});
|
91
|
-
} else
|
92
|
+
} else {
|
92
93
|
usedFields.add(fieldName);
|
94
|
+
}
|
93
95
|
}
|
94
96
|
return {
|
95
97
|
OperationDefinition(node) {
|
96
98
|
const set = /* @__PURE__ */ new Set();
|
97
|
-
for (const varDef of node.variableDefinitions || [])
|
99
|
+
for (const varDef of node.variableDefinitions || []) {
|
98
100
|
checkNode(set, varDef.variable.name);
|
101
|
+
}
|
99
102
|
},
|
100
103
|
Field(node) {
|
101
104
|
const set = /* @__PURE__ */ new Set();
|
102
|
-
for (const arg of node.arguments || [])
|
105
|
+
for (const arg of node.arguments || []) {
|
103
106
|
checkNode(set, arg.name);
|
107
|
+
}
|
104
108
|
},
|
105
109
|
SelectionSet(node) {
|
106
110
|
const set = /* @__PURE__ */ new Set();
|
107
|
-
for (const selection of node.selections)
|
108
|
-
selection.kind === _graphql.Kind.FIELD
|
111
|
+
for (const selection of node.selections) {
|
112
|
+
if (selection.kind === _graphql.Kind.FIELD) {
|
113
|
+
checkNode(set, selection.alias || selection.name);
|
114
|
+
}
|
115
|
+
}
|
109
116
|
}
|
110
117
|
};
|
111
118
|
}
|
@@ -1,9 +1,10 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _graphql = require('graphql');
|
2
2
|
var _utilsjs = require('../../utils.js');
|
3
|
-
const RULE_ID = "HASHTAG_COMMENT"
|
3
|
+
const RULE_ID = "HASHTAG_COMMENT";
|
4
|
+
const rule = {
|
4
5
|
meta: {
|
5
6
|
type: "suggestion",
|
6
|
-
hasSuggestions:
|
7
|
+
hasSuggestions: true,
|
7
8
|
schema: [],
|
8
9
|
messages: {
|
9
10
|
[RULE_ID]: 'Unexpected GraphQL descriptions as hashtag `#` for {{ nodeName }}.\nPrefer using `"""` for multiline, or `"` for a single line description.'
|
@@ -56,21 +57,27 @@ const RULE_ID = "HASHTAG_COMMENT", rule = exports.rule = {
|
|
56
57
|
)
|
57
58
|
}
|
58
59
|
],
|
59
|
-
recommended:
|
60
|
+
recommended: true
|
60
61
|
}
|
61
62
|
},
|
62
63
|
create(context) {
|
64
|
+
const selector = "Document[definitions.0.kind!=/^(OperationDefinition|FragmentDefinition)$/]";
|
63
65
|
return {
|
64
|
-
[
|
65
|
-
|
66
|
-
|
66
|
+
[selector](node) {
|
67
|
+
const rawNode = node.rawNode();
|
68
|
+
let token = rawNode.loc.startToken;
|
69
|
+
while (token) {
|
67
70
|
const { kind, prev, next, value, line, column } = token;
|
68
71
|
if (kind === _graphql.TokenKind.COMMENT && prev && next) {
|
69
|
-
const isEslintComment = value.trimStart().startsWith("eslint")
|
72
|
+
const isEslintComment = value.trimStart().startsWith("eslint");
|
73
|
+
const linesAfter = next.line - line;
|
70
74
|
if (!isEslintComment && line !== prev.line && next.kind === _graphql.TokenKind.NAME && linesAfter < 2) {
|
71
|
-
const sourceCode = context.getSourceCode()
|
75
|
+
const sourceCode = context.getSourceCode();
|
76
|
+
const { tokens } = sourceCode.ast;
|
77
|
+
const t = tokens.find(
|
72
78
|
(token2) => token2.loc.start.line === next.line && token2.loc.start.column === next.column - 1
|
73
|
-
)
|
79
|
+
);
|
80
|
+
const nextNode = sourceCode.getNodeByRangeIndex(t.range[1] + 1);
|
74
81
|
context.report({
|
75
82
|
messageId: RULE_ID,
|
76
83
|
data: {
|
@@ -92,8 +99,9 @@ const RULE_ID = "HASHTAG_COMMENT", rule = exports.rule = {
|
|
92
99
|
});
|
93
100
|
}
|
94
101
|
}
|
95
|
-
if (!next)
|
102
|
+
if (!next) {
|
96
103
|
break;
|
104
|
+
}
|
97
105
|
token = next;
|
98
106
|
}
|
99
107
|
}
|
@@ -1,7 +1,8 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _nodepath = require('node:path');
|
2
2
|
var _graphql = require('graphql');
|
3
3
|
var _utilsjs = require('../../utils.js');
|
4
|
-
const RULE_ID = "no-one-place-fragments"
|
4
|
+
const RULE_ID = "no-one-place-fragments";
|
5
|
+
const rule = {
|
5
6
|
meta: {
|
6
7
|
type: "suggestion",
|
7
8
|
docs: {
|
@@ -47,7 +48,7 @@ const RULE_ID = "no-one-place-fragments", rule = exports.rule = {
|
|
47
48
|
)
|
48
49
|
}
|
49
50
|
],
|
50
|
-
requiresSiblings:
|
51
|
+
requiresSiblings: true
|
51
52
|
},
|
52
53
|
messages: {
|
53
54
|
[RULE_ID]: 'Fragment `{{fragmentName}}` used only once. Inline him in "{{filePath}}".'
|
@@ -55,24 +56,30 @@ const RULE_ID = "no-one-place-fragments", rule = exports.rule = {
|
|
55
56
|
schema: []
|
56
57
|
},
|
57
58
|
create(context) {
|
58
|
-
const operations = _utilsjs.requireGraphQLOperations.call(void 0, RULE_ID, context)
|
59
|
+
const operations = _utilsjs.requireGraphQLOperations.call(void 0, RULE_ID, context);
|
60
|
+
const allDocuments = [...operations.getOperations(), ...operations.getFragments()];
|
61
|
+
const usedFragmentsMap = /* @__PURE__ */ Object.create(null);
|
59
62
|
for (const { document, filePath } of allDocuments) {
|
60
63
|
const relativeFilePath = _nodepath.relative.call(void 0, _utilsjs.CWD, filePath);
|
61
64
|
_graphql.visit.call(void 0, document, {
|
62
65
|
FragmentSpread({ name }) {
|
63
66
|
const spreadName = name.value;
|
64
|
-
usedFragmentsMap[spreadName] ||= []
|
67
|
+
usedFragmentsMap[spreadName] ||= [];
|
68
|
+
usedFragmentsMap[spreadName].push(relativeFilePath);
|
65
69
|
}
|
66
70
|
});
|
67
71
|
}
|
68
72
|
return {
|
69
73
|
"FragmentDefinition > Name"(node) {
|
70
|
-
const fragmentName = node.value
|
71
|
-
fragmentUsage
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
74
|
+
const fragmentName = node.value;
|
75
|
+
const fragmentUsage = usedFragmentsMap[fragmentName];
|
76
|
+
if (fragmentUsage.length === 1) {
|
77
|
+
context.report({
|
78
|
+
node,
|
79
|
+
messageId: RULE_ID,
|
80
|
+
data: { fragmentName, filePath: fragmentUsage[0] }
|
81
|
+
});
|
82
|
+
}
|
76
83
|
}
|
77
84
|
};
|
78
85
|
}
|