@graphql-eslint/eslint-plugin 4.3.0 → 4.3.1-alpha-20241209185034-de2d7397da8c26620a8930dd12b6dff42e43f537
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/{index.browser.js → browser.js} +1756 -1104
- 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/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/input-name/index.js +51 -38
- package/cjs/rules/lone-executable-definition/index.js +15 -6
- package/cjs/rules/match-document-filename/index.js +57 -32
- package/cjs/rules/naming-convention/index.js +76 -37
- 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.js +60 -42
- 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/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/input-name/index.js +51 -38
- package/esm/rules/lone-executable-definition/index.js +15 -6
- package/esm/rules/match-document-filename/index.js +57 -32
- package/esm/rules/naming-convention/index.js +76 -37
- 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.js +60 -42
- 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/package.json +11 -1
@@ -1,12 +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 _graphql = require('graphql');
|
2
2
|
var _utilsjs = require('../../utils.js');
|
3
|
-
const RULE_ID = "lone-executable-definition"
|
3
|
+
const RULE_ID = "lone-executable-definition";
|
4
|
+
const definitionTypes = ["fragment", ...Object.values(_graphql.OperationTypeNode)];
|
5
|
+
const schema = {
|
4
6
|
type: "array",
|
5
7
|
maxItems: 1,
|
6
8
|
items: {
|
7
9
|
type: "object",
|
8
10
|
minProperties: 1,
|
9
|
-
additionalProperties:
|
11
|
+
additionalProperties: false,
|
10
12
|
properties: {
|
11
13
|
ignore: {
|
12
14
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
@@ -19,7 +21,8 @@ const RULE_ID = "lone-executable-definition", definitionTypes = ["fragment", ...
|
|
19
21
|
}
|
20
22
|
}
|
21
23
|
}
|
22
|
-
}
|
24
|
+
};
|
25
|
+
const rule = {
|
23
26
|
meta: {
|
24
27
|
type: "suggestion",
|
25
28
|
docs: {
|
@@ -60,17 +63,23 @@ const RULE_ID = "lone-executable-definition", definitionTypes = ["fragment", ...
|
|
60
63
|
schema
|
61
64
|
},
|
62
65
|
create(context) {
|
63
|
-
const ignore = new Set(_optionalChain([context, 'access', _ => _.options, 'access', _2 => _2[0], 'optionalAccess', _3 => _3.ignore]) || [])
|
66
|
+
const ignore = new Set(_optionalChain([context, 'access', _ => _.options, 'access', _2 => _2[0], 'optionalAccess', _3 => _3.ignore]) || []);
|
67
|
+
const definitions = [];
|
64
68
|
return {
|
65
69
|
":matches(OperationDefinition, FragmentDefinition)"(node) {
|
66
70
|
const type = "operation" in node ? node.operation : "fragment";
|
67
|
-
ignore.has(type)
|
71
|
+
if (!ignore.has(type)) {
|
72
|
+
definitions.push({ type, node });
|
73
|
+
}
|
68
74
|
},
|
69
75
|
"Document:exit"() {
|
70
76
|
for (const { node, type } of definitions.slice(1)) {
|
71
77
|
let name = _utilsjs.pascalCase.call(void 0, type);
|
72
78
|
const definitionName = _optionalChain([node, 'access', _4 => _4.name, 'optionalAccess', _5 => _5.value]);
|
73
|
-
|
79
|
+
if (definitionName) {
|
80
|
+
name += ` "${definitionName}"`;
|
81
|
+
}
|
82
|
+
context.report({
|
74
83
|
loc: _optionalChain([node, 'access', _6 => _6.name, 'optionalAccess', _7 => _7.loc]) || _utilsjs.getLocation.call(void 0, node.loc.start, type),
|
75
84
|
messageId: RULE_ID,
|
76
85
|
data: { name }
|
@@ -5,16 +5,20 @@ var _graphql = require('graphql');
|
|
5
5
|
|
6
6
|
|
7
7
|
var _utilsjs = require('../../utils.js');
|
8
|
-
const MATCH_EXTENSION = "MATCH_EXTENSION"
|
8
|
+
const MATCH_EXTENSION = "MATCH_EXTENSION";
|
9
|
+
const MATCH_STYLE = "MATCH_STYLE";
|
10
|
+
const CASE_STYLES = [
|
9
11
|
"camelCase",
|
10
12
|
"PascalCase",
|
11
13
|
"snake_case",
|
12
14
|
"UPPER_CASE",
|
13
15
|
"kebab-case",
|
14
16
|
"matchDocumentStyle"
|
15
|
-
]
|
17
|
+
];
|
18
|
+
const schemaOption = {
|
16
19
|
oneOf: [{ $ref: "#/definitions/asString" }, { $ref: "#/definitions/asObject" }]
|
17
|
-
}
|
20
|
+
};
|
21
|
+
const schema = {
|
18
22
|
definitions: {
|
19
23
|
asString: {
|
20
24
|
enum: CASE_STYLES,
|
@@ -22,7 +26,7 @@ const MATCH_EXTENSION = "MATCH_EXTENSION", MATCH_STYLE = "MATCH_STYLE", CASE_STY
|
|
22
26
|
},
|
23
27
|
asObject: {
|
24
28
|
type: "object",
|
25
|
-
additionalProperties:
|
29
|
+
additionalProperties: false,
|
26
30
|
minProperties: 1,
|
27
31
|
properties: {
|
28
32
|
style: { enum: CASE_STYLES },
|
@@ -36,7 +40,7 @@ const MATCH_EXTENSION = "MATCH_EXTENSION", MATCH_STYLE = "MATCH_STYLE", CASE_STY
|
|
36
40
|
maxItems: 1,
|
37
41
|
items: {
|
38
42
|
type: "object",
|
39
|
-
additionalProperties:
|
43
|
+
additionalProperties: false,
|
40
44
|
minProperties: 1,
|
41
45
|
properties: {
|
42
46
|
fileExtension: { enum: [".gql", ".graphql"] },
|
@@ -46,7 +50,8 @@ const MATCH_EXTENSION = "MATCH_EXTENSION", MATCH_STYLE = "MATCH_STYLE", CASE_STY
|
|
46
50
|
fragment: schemaOption
|
47
51
|
}
|
48
52
|
}
|
49
|
-
}
|
53
|
+
};
|
54
|
+
const rule = {
|
50
55
|
meta: {
|
51
56
|
type: "suggestion",
|
52
57
|
docs: {
|
@@ -186,47 +191,67 @@ const MATCH_EXTENSION = "MATCH_EXTENSION", MATCH_STYLE = "MATCH_STYLE", CASE_STY
|
|
186
191
|
create(context) {
|
187
192
|
const options = context.options[0] || {
|
188
193
|
fileExtension: null
|
189
|
-
}
|
190
|
-
|
194
|
+
};
|
195
|
+
const filePath = context.filename;
|
196
|
+
const isVirtualFile = _utilsjs.VIRTUAL_DOCUMENT_REGEX.test(filePath);
|
197
|
+
if (process.env.NODE_ENV !== "test" && isVirtualFile) {
|
191
198
|
return {};
|
192
|
-
|
199
|
+
}
|
200
|
+
const fileExtension = _nodepath.extname.call(void 0, filePath);
|
201
|
+
const filename = _nodepath.basename.call(void 0, filePath, fileExtension);
|
193
202
|
return {
|
194
203
|
Document(documentNode) {
|
195
|
-
options.fileExtension && options.fileExtension !== fileExtension
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
204
|
+
if (options.fileExtension && options.fileExtension !== fileExtension) {
|
205
|
+
context.report({
|
206
|
+
loc: _utilsjs.REPORT_ON_FIRST_CHARACTER,
|
207
|
+
messageId: MATCH_EXTENSION,
|
208
|
+
data: {
|
209
|
+
fileExtension,
|
210
|
+
expectedFileExtension: options.fileExtension
|
211
|
+
}
|
212
|
+
});
|
213
|
+
}
|
203
214
|
const firstOperation = documentNode.definitions.find(
|
204
215
|
(n) => n.kind === _graphql.Kind.OPERATION_DEFINITION
|
205
|
-
)
|
216
|
+
);
|
217
|
+
const firstFragment = documentNode.definitions.find(
|
206
218
|
(n) => n.kind === _graphql.Kind.FRAGMENT_DEFINITION
|
207
|
-
)
|
208
|
-
|
219
|
+
);
|
220
|
+
const node = firstOperation || firstFragment;
|
221
|
+
if (!node) {
|
209
222
|
return;
|
223
|
+
}
|
210
224
|
const docName = _optionalChain([node, 'access', _ => _.name, 'optionalAccess', _2 => _2.value]);
|
211
|
-
if (!docName)
|
225
|
+
if (!docName) {
|
212
226
|
return;
|
227
|
+
}
|
213
228
|
const docType = "operation" in node ? node.operation : "fragment";
|
214
229
|
let option = options[docType];
|
215
|
-
if (!option)
|
230
|
+
if (!option) {
|
216
231
|
return;
|
217
|
-
|
232
|
+
}
|
233
|
+
if (typeof option === "string") {
|
234
|
+
option = { style: option };
|
235
|
+
}
|
218
236
|
const expectedExtension = options.fileExtension || fileExtension;
|
219
237
|
let expectedFilename = option.prefix || "";
|
220
|
-
|
238
|
+
if (option.style) {
|
239
|
+
expectedFilename += option.style === "matchDocumentStyle" ? docName : _utilsjs.convertCase.call(void 0, option.style, docName);
|
240
|
+
} else {
|
241
|
+
expectedFilename += filename;
|
242
|
+
}
|
243
|
+
expectedFilename += (option.suffix || "") + expectedExtension;
|
221
244
|
const filenameWithExtension = filename + expectedExtension;
|
222
|
-
expectedFilename !== filenameWithExtension
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
245
|
+
if (expectedFilename !== filenameWithExtension) {
|
246
|
+
context.report({
|
247
|
+
loc: _utilsjs.REPORT_ON_FIRST_CHARACTER,
|
248
|
+
messageId: MATCH_STYLE,
|
249
|
+
data: {
|
250
|
+
expectedFilename,
|
251
|
+
filename: filenameWithExtension
|
252
|
+
}
|
253
|
+
});
|
254
|
+
}
|
230
255
|
}
|
231
256
|
};
|
232
257
|
}
|
@@ -24,16 +24,22 @@ 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) => `> [!WARNING]
|
35
40
|
>
|
36
|
-
> This option is deprecated and will be removed in the next major release. Use [\`${name}\`](#${name.toLowerCase()}-array) instead
|
41
|
+
> This option is deprecated and will be removed in the next major release. Use [\`${name}\`](#${name.toLowerCase()}-array) instead.`;
|
42
|
+
const schema = {
|
37
43
|
definitions: {
|
38
44
|
asString: {
|
39
45
|
enum: ALLOWED_STYLES,
|
@@ -41,7 +47,7 @@ const KindToDisplayName = {
|
|
41
47
|
},
|
42
48
|
asObject: {
|
43
49
|
type: "object",
|
44
|
-
additionalProperties:
|
50
|
+
additionalProperties: false,
|
45
51
|
properties: {
|
46
52
|
style: { enum: ALLOWED_STYLES },
|
47
53
|
prefix: { type: "string" },
|
@@ -87,13 +93,12 @@ const KindToDisplayName = {
|
|
87
93
|
maxItems: 1,
|
88
94
|
items: {
|
89
95
|
type: "object",
|
90
|
-
additionalProperties:
|
96
|
+
additionalProperties: false,
|
91
97
|
properties: {
|
92
98
|
types: {
|
93
99
|
...schemaOption,
|
94
100
|
description: `Includes:
|
95
|
-
${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
96
|
-
`)}`
|
101
|
+
${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join("\n")}`
|
97
102
|
},
|
98
103
|
...Object.fromEntries(
|
99
104
|
ALLOWED_KINDS.map((kind) => [
|
@@ -108,11 +113,11 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
108
113
|
),
|
109
114
|
allowLeadingUnderscore: {
|
110
115
|
type: "boolean",
|
111
|
-
default:
|
116
|
+
default: false
|
112
117
|
},
|
113
118
|
allowTrailingUnderscore: {
|
114
119
|
type: "boolean",
|
115
|
-
default:
|
120
|
+
default: false
|
116
121
|
}
|
117
122
|
},
|
118
123
|
patternProperties: {
|
@@ -124,16 +129,16 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
124
129
|
"> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.",
|
125
130
|
">",
|
126
131
|
"> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`."
|
127
|
-
].join(
|
128
|
-
`)
|
132
|
+
].join("\n")
|
129
133
|
}
|
130
|
-
}
|
134
|
+
};
|
135
|
+
const rule = {
|
131
136
|
meta: {
|
132
137
|
type: "suggestion",
|
133
138
|
docs: {
|
134
139
|
description: "Require names to follow specified conventions.",
|
135
140
|
category: ["Schema", "Operations"],
|
136
|
-
recommended:
|
141
|
+
recommended: true,
|
137
142
|
url: "https://the-guild.dev/graphql/eslint/rules/naming-convention",
|
138
143
|
examples: [
|
139
144
|
{
|
@@ -325,14 +330,16 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
325
330
|
]
|
326
331
|
}
|
327
332
|
},
|
328
|
-
hasSuggestions:
|
333
|
+
hasSuggestions: true,
|
329
334
|
schema
|
330
335
|
},
|
331
336
|
create(context) {
|
332
|
-
const options = context.options[0] || {}
|
337
|
+
const options = context.options[0] || {};
|
338
|
+
const { allowLeadingUnderscore, allowTrailingUnderscore, types, ...restOptions } = options;
|
339
|
+
const ignoredNodes = /* @__PURE__ */ new Set();
|
333
340
|
function normalisePropertyOption(kind) {
|
334
341
|
const style = restOptions[kind] || types;
|
335
|
-
return typeof style
|
342
|
+
return typeof style === "object" ? style : { style };
|
336
343
|
}
|
337
344
|
function report(node, message, suggestedNames) {
|
338
345
|
context.report({
|
@@ -346,8 +353,9 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
346
353
|
}
|
347
354
|
const checkNode = (selector) => (n) => {
|
348
355
|
const { name: node } = n.kind === _graphql.Kind.VARIABLE_DEFINITION ? n.variable : n;
|
349
|
-
if (!node)
|
356
|
+
if (!node) {
|
350
357
|
return;
|
358
|
+
}
|
351
359
|
const {
|
352
360
|
prefix,
|
353
361
|
suffix,
|
@@ -359,11 +367,17 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
359
367
|
requiredSuffixes,
|
360
368
|
forbiddenPatterns,
|
361
369
|
requiredPatterns
|
362
|
-
} = normalisePropertyOption(selector)
|
370
|
+
} = normalisePropertyOption(selector);
|
371
|
+
const nodeName = node.value;
|
372
|
+
const error = getError();
|
363
373
|
if (error) {
|
364
|
-
const { errorMessage, renameToNames } = error
|
374
|
+
const { errorMessage, renameToNames } = error;
|
375
|
+
const [leadingUnderscores] = nodeName.match(/^_*/);
|
376
|
+
const [trailingUnderscores] = nodeName.match(/_*$/);
|
377
|
+
const suggestedNames = renameToNames.map(
|
365
378
|
(renameToName) => leadingUnderscores + renameToName + trailingUnderscores
|
366
|
-
)
|
379
|
+
);
|
380
|
+
const name = _utilsjs.displayNodeName.call(void 0, n);
|
367
381
|
report(
|
368
382
|
node,
|
369
383
|
`${name[0].toUpperCase()}${name.slice(1)} should ${errorMessage}`,
|
@@ -373,78 +387,103 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
373
387
|
function getError() {
|
374
388
|
const name = nodeName.replace(/(^_+)|(_+$)/g, "");
|
375
389
|
if (ignorePattern && new RegExp(ignorePattern, "u").test(name)) {
|
376
|
-
"name" in n
|
390
|
+
if ("name" in n) {
|
391
|
+
ignoredNodes.add(n.name);
|
392
|
+
}
|
377
393
|
return;
|
378
394
|
}
|
379
|
-
if (prefix && !name.startsWith(prefix))
|
395
|
+
if (prefix && !name.startsWith(prefix)) {
|
380
396
|
return {
|
381
397
|
errorMessage: `have "${prefix}" prefix`,
|
382
398
|
renameToNames: [prefix + name]
|
383
399
|
};
|
384
|
-
|
400
|
+
}
|
401
|
+
if (suffix && !name.endsWith(suffix)) {
|
385
402
|
return {
|
386
403
|
errorMessage: `have "${suffix}" suffix`,
|
387
404
|
renameToNames: [name + suffix]
|
388
405
|
};
|
406
|
+
}
|
389
407
|
const forbidden = _optionalChain([forbiddenPatterns, 'optionalAccess', _ => _.find, 'call', _2 => _2((pattern) => pattern.test(name))]);
|
390
|
-
if (forbidden)
|
408
|
+
if (forbidden) {
|
391
409
|
return {
|
392
410
|
errorMessage: `not contain the forbidden pattern "${forbidden}"`,
|
393
411
|
renameToNames: [name.replace(forbidden, "")]
|
394
412
|
};
|
395
|
-
|
413
|
+
}
|
414
|
+
if (requiredPatterns && !requiredPatterns.some((pattern) => pattern.test(name))) {
|
396
415
|
return {
|
397
416
|
errorMessage: `contain the required pattern: ${_utilsjs.englishJoinWords.call(void 0, requiredPatterns.map((re) => re.source))}`,
|
398
417
|
renameToNames: []
|
399
418
|
};
|
419
|
+
}
|
400
420
|
const forbiddenPrefix = _optionalChain([forbiddenPrefixes, 'optionalAccess', _3 => _3.find, 'call', _4 => _4((prefix2) => name.startsWith(prefix2))]);
|
401
|
-
if (forbiddenPrefix)
|
421
|
+
if (forbiddenPrefix) {
|
402
422
|
return {
|
403
423
|
errorMessage: `not have "${forbiddenPrefix}" prefix`,
|
404
424
|
renameToNames: [name.replace(new RegExp(`^${forbiddenPrefix}`), "")]
|
405
425
|
};
|
426
|
+
}
|
406
427
|
const forbiddenSuffix = _optionalChain([forbiddenSuffixes, 'optionalAccess', _5 => _5.find, 'call', _6 => _6((suffix2) => name.endsWith(suffix2))]);
|
407
|
-
if (forbiddenSuffix)
|
428
|
+
if (forbiddenSuffix) {
|
408
429
|
return {
|
409
430
|
errorMessage: `not have "${forbiddenSuffix}" suffix`,
|
410
431
|
renameToNames: [name.replace(new RegExp(`${forbiddenSuffix}$`), "")]
|
411
432
|
};
|
412
|
-
|
433
|
+
}
|
434
|
+
if (requiredPrefixes && !requiredPrefixes.some((requiredPrefix) => name.startsWith(requiredPrefix))) {
|
413
435
|
return {
|
414
436
|
errorMessage: `have one of the following prefixes: ${_utilsjs.englishJoinWords.call(void 0,
|
415
437
|
requiredPrefixes
|
416
438
|
)}`,
|
417
439
|
renameToNames: style ? requiredPrefixes.map((prefix2) => _utilsjs.convertCase.call(void 0, style, `${prefix2} ${name}`)) : requiredPrefixes.map((prefix2) => `${prefix2}${name}`)
|
418
440
|
};
|
419
|
-
|
441
|
+
}
|
442
|
+
if (requiredSuffixes && !requiredSuffixes.some((requiredSuffix) => name.endsWith(requiredSuffix))) {
|
420
443
|
return {
|
421
444
|
errorMessage: `have one of the following suffixes: ${_utilsjs.englishJoinWords.call(void 0,
|
422
445
|
requiredSuffixes
|
423
446
|
)}`,
|
424
447
|
renameToNames: style ? requiredSuffixes.map((suffix2) => _utilsjs.convertCase.call(void 0, style, `${name} ${suffix2}`)) : requiredSuffixes.map((suffix2) => `${name}${suffix2}`)
|
425
448
|
};
|
426
|
-
|
449
|
+
}
|
450
|
+
if (!style) {
|
427
451
|
return;
|
428
|
-
|
452
|
+
}
|
453
|
+
const caseRegex = StyleToRegex[style];
|
454
|
+
if (!caseRegex.test(name)) {
|
429
455
|
return {
|
430
456
|
errorMessage: `be in ${style} format`,
|
431
457
|
renameToNames: [_utilsjs.convertCase.call(void 0, style, name)]
|
432
458
|
};
|
459
|
+
}
|
433
460
|
}
|
434
|
-
}
|
435
|
-
|
461
|
+
};
|
462
|
+
const checkUnderscore = (isLeading) => (node) => {
|
463
|
+
if (ignoredNodes.has(node)) {
|
436
464
|
return;
|
465
|
+
}
|
466
|
+
if (node.parent.kind === "Field" && node.parent.alias !== node) {
|
467
|
+
return;
|
468
|
+
}
|
437
469
|
const suggestedName = node.value.replace(isLeading ? /^_+/ : /_+$/, "");
|
438
470
|
report(node, `${isLeading ? "Leading" : "Trailing"} underscores are not allowed`, [
|
439
471
|
suggestedName
|
440
472
|
]);
|
441
|
-
}
|
442
|
-
|
473
|
+
};
|
474
|
+
const listeners = {};
|
475
|
+
if (!allowLeadingUnderscore) {
|
476
|
+
listeners["Name[value=/^_/]"] = checkUnderscore(true);
|
477
|
+
}
|
478
|
+
if (!allowTrailingUnderscore) {
|
479
|
+
listeners["Name[value=/_$/]"] = checkUnderscore(false);
|
480
|
+
}
|
443
481
|
const selectors = new Set(
|
444
482
|
[types && _utilsjs.TYPES_KINDS, Object.keys(restOptions)].filter((v) => !!v).flat()
|
445
483
|
);
|
446
|
-
for (const selector of selectors)
|
484
|
+
for (const selector of selectors) {
|
447
485
|
listeners[selector] = checkNode(selector);
|
486
|
+
}
|
448
487
|
return listeners;
|
449
488
|
}
|
450
489
|
};
|
@@ -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
|
}
|