@graphql-eslint/eslint-plugin 4.1.0-alpha-20241129085030-b075ada387ad44de7944517af78877c45bc8fd3b → 4.1.1-alpha-20241129095718-0cc689c0eb8b65d162f4c6d87030879fe63a105b
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/index.d.cts +2 -2
- package/cjs/meta.js +1 -1
- package/cjs/rules/index.d.cts +2 -2
- package/cjs/rules/naming-convention/index.d.cts +2 -2
- package/cjs/rules/naming-convention/index.js +19 -15
- package/cjs/rules/no-unused-fields/index.js +1 -3
- package/cjs/rules/require-description/index.d.cts +3 -2
- package/cjs/rules/require-description/index.js +60 -6
- package/cjs/utils.d.cts +2 -1
- package/cjs/utils.js +6 -1
- package/esm/index.d.ts +2 -2
- package/esm/meta.js +1 -1
- package/esm/rules/index.d.ts +2 -2
- package/esm/rules/naming-convention/index.d.ts +2 -2
- package/esm/rules/naming-convention/index.js +19 -15
- package/esm/rules/no-unused-fields/index.js +2 -4
- package/esm/rules/require-description/index.d.ts +3 -2
- package/esm/rules/require-description/index.js +61 -7
- package/esm/utils.d.ts +2 -1
- package/esm/utils.js +5 -0
- package/index.browser.js +78 -25
- package/package.json +1 -1
package/cjs/index.d.cts
CHANGED
@@ -98,10 +98,10 @@ declare const _default: {
|
|
98
98
|
style?: ("camelCase" | "PascalCase" | "snake_case" | "UPPER_CASE") | undefined;
|
99
99
|
suffix?: string | undefined;
|
100
100
|
prefix?: string | undefined;
|
101
|
-
|
101
|
+
forbiddenPatterns?: {
|
102
102
|
[x: string]: unknown;
|
103
103
|
}[] | undefined;
|
104
|
-
|
104
|
+
requiredPatterns?: {
|
105
105
|
[x: string]: unknown;
|
106
106
|
}[] | undefined;
|
107
107
|
forbiddenPrefixes?: string[] | undefined;
|
package/cjs/meta.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});const version = "4.1.
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});const version = "4.1.1-alpha-20241129095718-0cc689c0eb8b65d162f4c6d87030879fe63a105b";
|
2
2
|
|
3
3
|
|
4
4
|
exports.version = version;
|
package/cjs/rules/index.d.cts
CHANGED
@@ -61,10 +61,10 @@ declare const rules: {
|
|
61
61
|
style?: ("camelCase" | "PascalCase" | "snake_case" | "UPPER_CASE") | undefined;
|
62
62
|
suffix?: string | undefined;
|
63
63
|
prefix?: string | undefined;
|
64
|
-
|
64
|
+
forbiddenPatterns?: {
|
65
65
|
[x: string]: unknown;
|
66
66
|
}[] | undefined;
|
67
|
-
|
67
|
+
requiredPatterns?: {
|
68
68
|
[x: string]: unknown;
|
69
69
|
}[] | undefined;
|
70
70
|
forbiddenPrefixes?: string[] | undefined;
|
@@ -28,7 +28,7 @@ declare const schema: {
|
|
28
28
|
readonly suffix: {
|
29
29
|
readonly type: "string";
|
30
30
|
};
|
31
|
-
readonly
|
31
|
+
readonly forbiddenPatterns: {
|
32
32
|
readonly items: {
|
33
33
|
readonly type: "object";
|
34
34
|
};
|
@@ -37,7 +37,7 @@ declare const schema: {
|
|
37
37
|
readonly uniqueItems: true;
|
38
38
|
readonly minItems: 1;
|
39
39
|
};
|
40
|
-
readonly
|
40
|
+
readonly requiredPatterns: {
|
41
41
|
readonly items: {
|
42
42
|
readonly type: "object";
|
43
43
|
};
|
@@ -47,14 +47,14 @@ const KindToDisplayName = {
|
|
47
47
|
style: { enum: ALLOWED_STYLES },
|
48
48
|
prefix: { type: "string" },
|
49
49
|
suffix: { type: "string" },
|
50
|
-
|
50
|
+
forbiddenPatterns: {
|
51
51
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
52
52
|
items: {
|
53
53
|
type: "object"
|
54
54
|
},
|
55
55
|
description: "Should be of instance of `RegEx`"
|
56
56
|
},
|
57
|
-
|
57
|
+
requiredPatterns: {
|
58
58
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
59
59
|
items: {
|
60
60
|
type: "object"
|
@@ -63,19 +63,19 @@ const KindToDisplayName = {
|
|
63
63
|
},
|
64
64
|
forbiddenPrefixes: {
|
65
65
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
66
|
-
description: descriptionPrefixesSuffixes("
|
66
|
+
description: descriptionPrefixesSuffixes("forbiddenPatterns")
|
67
67
|
},
|
68
68
|
forbiddenSuffixes: {
|
69
69
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
70
|
-
description: descriptionPrefixesSuffixes("
|
70
|
+
description: descriptionPrefixesSuffixes("forbiddenPatterns")
|
71
71
|
},
|
72
72
|
requiredPrefixes: {
|
73
73
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
74
|
-
description: descriptionPrefixesSuffixes("
|
74
|
+
description: descriptionPrefixesSuffixes("requiredPatterns")
|
75
75
|
},
|
76
76
|
requiredSuffixes: {
|
77
77
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
78
|
-
description: descriptionPrefixesSuffixes("
|
78
|
+
description: descriptionPrefixesSuffixes("requiredPatterns")
|
79
79
|
},
|
80
80
|
ignorePattern: {
|
81
81
|
type: "string",
|
@@ -101,7 +101,9 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
101
101
|
kind,
|
102
102
|
{
|
103
103
|
...schemaOption,
|
104
|
-
description:
|
104
|
+
description: `> [!NOTE]
|
105
|
+
>
|
106
|
+
> Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`
|
105
107
|
}
|
106
108
|
])
|
107
109
|
),
|
@@ -328,7 +330,7 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
328
330
|
schema
|
329
331
|
},
|
330
332
|
create(context) {
|
331
|
-
const options = context.options[0] || {}, { allowLeadingUnderscore, allowTrailingUnderscore, types, ...restOptions } = options;
|
333
|
+
const options = context.options[0] || {}, { allowLeadingUnderscore, allowTrailingUnderscore, types, ...restOptions } = options, ignoredNodes = /* @__PURE__ */ new Set();
|
332
334
|
function normalisePropertyOption(kind) {
|
333
335
|
const style = restOptions[kind] || types;
|
334
336
|
return typeof style == "object" ? style : { style };
|
@@ -356,8 +358,8 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
356
358
|
ignorePattern,
|
357
359
|
requiredPrefixes,
|
358
360
|
requiredSuffixes,
|
359
|
-
|
360
|
-
|
361
|
+
forbiddenPatterns,
|
362
|
+
requiredPatterns
|
361
363
|
} = normalisePropertyOption(selector), nodeName = node.value, error = getError();
|
362
364
|
if (error) {
|
363
365
|
const { errorMessage, renameToNames } = error, [leadingUnderscores] = nodeName.match(/^_*/), [trailingUnderscores] = nodeName.match(/_*$/), suggestedNames = renameToNames.map(
|
@@ -371,8 +373,10 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
371
373
|
}
|
372
374
|
function getError() {
|
373
375
|
const name = nodeName.replace(/(^_+)|(_+$)/g, "");
|
374
|
-
if (ignorePattern && new RegExp(ignorePattern, "u").test(name))
|
376
|
+
if (ignorePattern && new RegExp(ignorePattern, "u").test(name)) {
|
377
|
+
"name" in n && ignoredNodes.add(n.name);
|
375
378
|
return;
|
379
|
+
}
|
376
380
|
if (prefix && !name.startsWith(prefix))
|
377
381
|
return {
|
378
382
|
errorMessage: `have "${prefix}" prefix`,
|
@@ -383,15 +387,15 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
383
387
|
errorMessage: `have "${suffix}" suffix`,
|
384
388
|
renameToNames: [name + suffix]
|
385
389
|
};
|
386
|
-
const forbidden = _optionalChain([
|
390
|
+
const forbidden = _optionalChain([forbiddenPatterns, 'optionalAccess', _ => _.find, 'call', _2 => _2((pattern) => pattern.test(name))]);
|
387
391
|
if (forbidden)
|
388
392
|
return {
|
389
393
|
errorMessage: `not contain the forbidden pattern "${forbidden}"`,
|
390
394
|
renameToNames: [name.replace(forbidden, "")]
|
391
395
|
};
|
392
|
-
if (
|
396
|
+
if (requiredPatterns && !requiredPatterns.some((pattern) => pattern.test(name)))
|
393
397
|
return {
|
394
|
-
errorMessage: `contain the required pattern: ${_utilsjs.englishJoinWords.call(void 0,
|
398
|
+
errorMessage: `contain the required pattern: ${_utilsjs.englishJoinWords.call(void 0, requiredPatterns.map((re) => re.source))}`,
|
395
399
|
renameToNames: []
|
396
400
|
};
|
397
401
|
const forbiddenPrefix = _optionalChain([forbiddenPrefixes, 'optionalAccess', _3 => _3.find, 'call', _4 => _4((prefix2) => name.startsWith(prefix2))]);
|
@@ -429,7 +433,7 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
429
433
|
};
|
430
434
|
}
|
431
435
|
}, checkUnderscore = (isLeading) => (node) => {
|
432
|
-
if (node.parent.kind === "Field" && node.parent.alias !== node)
|
436
|
+
if (ignoredNodes.has(node) || node.parent.kind === "Field" && node.parent.alias !== node)
|
433
437
|
return;
|
434
438
|
const suggestedName = node.value.replace(isLeading ? /^_+/ : /_+$/, "");
|
435
439
|
report(node, `${isLeading ? "Leading" : "Trailing"} underscores are not allowed`, [
|
@@ -82,9 +82,7 @@ const RULE_ID = "no-unused-fields", RELAY_SCHEMA = (
|
|
82
82
|
"```json",
|
83
83
|
JSON.stringify(RELAY_DEFAULT_IGNORED_FIELD_SELECTORS, null, 2),
|
84
84
|
"```",
|
85
|
-
|
86
|
-
"> These fields are defined by ESLint [`selectors`](https://eslint.org/docs/developer-guide/selectors).",
|
87
|
-
"> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector."
|
85
|
+
_utilsjs.eslintSelectorsTip
|
88
86
|
].join(`
|
89
87
|
`),
|
90
88
|
items: {
|
@@ -1,5 +1,12 @@
|
|
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 _utils = require('@graphql-tools/utils');
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
|
3
10
|
var _utilsjs = require('../../utils.js');
|
4
11
|
const RULE_ID = "require-description", ALLOWED_KINDS = [
|
5
12
|
..._utilsjs.TYPES_KINDS,
|
@@ -19,18 +26,34 @@ const RULE_ID = "require-description", ALLOWED_KINDS = [
|
|
19
26
|
properties: {
|
20
27
|
types: {
|
21
28
|
type: "boolean",
|
29
|
+
enum: [!0],
|
22
30
|
description: `Includes:
|
23
31
|
${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
24
32
|
`)}`
|
25
33
|
},
|
26
34
|
rootField: {
|
27
35
|
type: "boolean",
|
36
|
+
enum: [!0],
|
28
37
|
description: "Definitions within `Query`, `Mutation`, and `Subscription` root types."
|
29
38
|
},
|
39
|
+
ignoredSelectors: {
|
40
|
+
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
41
|
+
description: ["Ignore specific selectors", _utilsjs.eslintSelectorsTip].join(`
|
42
|
+
`)
|
43
|
+
},
|
30
44
|
...Object.fromEntries(
|
31
45
|
[...ALLOWED_KINDS].sort().map((kind) => {
|
32
|
-
let description =
|
33
|
-
|
46
|
+
let description = `> [!NOTE]
|
47
|
+
>
|
48
|
+
> Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`;
|
49
|
+
return kind === _graphql.Kind.OPERATION_DEFINITION && (description += [
|
50
|
+
"",
|
51
|
+
"",
|
52
|
+
"> [!WARNING]",
|
53
|
+
">",
|
54
|
+
'> You must use only comment syntax `#` and not description syntax `"""` or `"`.'
|
55
|
+
].join(`
|
56
|
+
`)), [kind, { type: "boolean", description }];
|
34
57
|
})
|
35
58
|
)
|
36
59
|
}
|
@@ -101,6 +124,36 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
101
124
|
}
|
102
125
|
`
|
103
126
|
)
|
127
|
+
},
|
128
|
+
{
|
129
|
+
title: "Correct",
|
130
|
+
usage: [
|
131
|
+
{
|
132
|
+
ignoredSelectors: [
|
133
|
+
"[type=ObjectTypeDefinition][name.value=PageInfo]",
|
134
|
+
"[type=ObjectTypeDefinition][name.value=/(Connection|Edge)$/]"
|
135
|
+
]
|
136
|
+
}
|
137
|
+
],
|
138
|
+
code: (
|
139
|
+
/* GraphQL */
|
140
|
+
`
|
141
|
+
type FriendConnection {
|
142
|
+
edges: [FriendEdge]
|
143
|
+
pageInfo: PageInfo!
|
144
|
+
}
|
145
|
+
type FriendEdge {
|
146
|
+
cursor: String!
|
147
|
+
node: Friend!
|
148
|
+
}
|
149
|
+
type PageInfo {
|
150
|
+
hasPreviousPage: Boolean!
|
151
|
+
hasNextPage: Boolean!
|
152
|
+
startCursor: String
|
153
|
+
endCursor: String
|
154
|
+
}
|
155
|
+
`
|
156
|
+
)
|
104
157
|
}
|
105
158
|
],
|
106
159
|
configOptions: [
|
@@ -119,7 +172,7 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
119
172
|
schema
|
120
173
|
},
|
121
174
|
create(context) {
|
122
|
-
const { types, rootField, ...restOptions } = context.options[0] || {}, kinds = new Set(types ? _utilsjs.TYPES_KINDS : []);
|
175
|
+
const { types, rootField, ignoredSelectors = [], ...restOptions } = context.options[0] || {}, kinds = new Set(types ? _utilsjs.TYPES_KINDS : []);
|
123
176
|
for (const [kind, isEnabled] of Object.entries(restOptions))
|
124
177
|
isEnabled ? kinds.add(kind) : kinds.delete(kind);
|
125
178
|
if (rootField) {
|
@@ -130,10 +183,11 @@ ${_utilsjs.TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
130
183
|
].join(",")})$/] > FieldDefinition`
|
131
184
|
);
|
132
185
|
}
|
133
|
-
|
134
|
-
|
186
|
+
let selector = `:matches(${[...kinds]})`;
|
187
|
+
for (const str of ignoredSelectors)
|
188
|
+
selector += `:not(${str})`;
|
135
189
|
return {
|
136
|
-
[
|
190
|
+
[selector](node) {
|
137
191
|
let description = "";
|
138
192
|
const isOperation = node.kind === _graphql.Kind.OPERATION_DEFINITION;
|
139
193
|
if (isOperation) {
|
package/cjs/utils.d.cts
CHANGED
@@ -41,5 +41,6 @@ type Truthy<T> = T extends '' | 0 | false | null | undefined ? never : T;
|
|
41
41
|
declare function truthy<T>(value: T): value is Truthy<T>;
|
42
42
|
declare function displayNodeName(node: GraphQLESTreeNode<ASTNode, boolean>): string;
|
43
43
|
declare function getNodeName(node: GraphQLESTreeNode<ASTNode>): string;
|
44
|
+
declare const eslintSelectorsTip = "> [!TIP]\n>\n> These fields are defined by ESLint [`selectors`](https://eslint.org/docs/developer-guide/selectors).\n> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.";
|
44
45
|
|
45
|
-
export { ARRAY_DEFAULT_OPTIONS, CWD, type CaseStyle, REPORT_ON_FIRST_CHARACTER, TYPES_KINDS, VIRTUAL_DOCUMENT_REGEX, camelCase, convertCase, displayNodeName, englishJoinWords, getLocation, getNodeName, getTypeName, logger, pascalCase, requireGraphQLOperations, requireGraphQLSchema, slash, truthy };
|
46
|
+
export { ARRAY_DEFAULT_OPTIONS, CWD, type CaseStyle, REPORT_ON_FIRST_CHARACTER, TYPES_KINDS, VIRTUAL_DOCUMENT_REGEX, camelCase, convertCase, displayNodeName, englishJoinWords, eslintSelectorsTip, getLocation, getNodeName, getTypeName, logger, pascalCase, requireGraphQLOperations, requireGraphQLSchema, slash, truthy };
|
package/cjs/utils.js
CHANGED
@@ -144,6 +144,10 @@ function getNodeName(node) {
|
|
144
144
|
}
|
145
145
|
return "";
|
146
146
|
}
|
147
|
+
const eslintSelectorsTip = `> [!TIP]
|
148
|
+
>
|
149
|
+
> These fields are defined by ESLint [\`selectors\`](https://eslint.org/docs/developer-guide/selectors).
|
150
|
+
> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.`;
|
147
151
|
|
148
152
|
|
149
153
|
|
@@ -163,4 +167,5 @@ function getNodeName(node) {
|
|
163
167
|
|
164
168
|
|
165
169
|
|
166
|
-
|
170
|
+
|
171
|
+
exports.ARRAY_DEFAULT_OPTIONS = ARRAY_DEFAULT_OPTIONS; exports.CWD = CWD; exports.REPORT_ON_FIRST_CHARACTER = REPORT_ON_FIRST_CHARACTER; exports.TYPES_KINDS = TYPES_KINDS; exports.VIRTUAL_DOCUMENT_REGEX = VIRTUAL_DOCUMENT_REGEX; exports.camelCase = camelCase; exports.convertCase = convertCase; exports.displayNodeName = displayNodeName; exports.englishJoinWords = englishJoinWords; exports.eslintSelectorsTip = eslintSelectorsTip; exports.getLocation = getLocation; exports.getNodeName = getNodeName; exports.getTypeName = getTypeName; exports.logger = logger; exports.pascalCase = pascalCase; exports.requireGraphQLOperations = requireGraphQLOperations; exports.requireGraphQLSchema = requireGraphQLSchema; exports.slash = slash; exports.truthy = truthy;
|
package/esm/index.d.ts
CHANGED
@@ -98,10 +98,10 @@ declare const _default: {
|
|
98
98
|
style?: ("camelCase" | "PascalCase" | "snake_case" | "UPPER_CASE") | undefined;
|
99
99
|
suffix?: string | undefined;
|
100
100
|
prefix?: string | undefined;
|
101
|
-
|
101
|
+
forbiddenPatterns?: {
|
102
102
|
[x: string]: unknown;
|
103
103
|
}[] | undefined;
|
104
|
-
|
104
|
+
requiredPatterns?: {
|
105
105
|
[x: string]: unknown;
|
106
106
|
}[] | undefined;
|
107
107
|
forbiddenPrefixes?: string[] | undefined;
|
package/esm/meta.js
CHANGED
package/esm/rules/index.d.ts
CHANGED
@@ -61,10 +61,10 @@ declare const rules: {
|
|
61
61
|
style?: ("camelCase" | "PascalCase" | "snake_case" | "UPPER_CASE") | undefined;
|
62
62
|
suffix?: string | undefined;
|
63
63
|
prefix?: string | undefined;
|
64
|
-
|
64
|
+
forbiddenPatterns?: {
|
65
65
|
[x: string]: unknown;
|
66
66
|
}[] | undefined;
|
67
|
-
|
67
|
+
requiredPatterns?: {
|
68
68
|
[x: string]: unknown;
|
69
69
|
}[] | undefined;
|
70
70
|
forbiddenPrefixes?: string[] | undefined;
|
@@ -28,7 +28,7 @@ declare const schema: {
|
|
28
28
|
readonly suffix: {
|
29
29
|
readonly type: "string";
|
30
30
|
};
|
31
|
-
readonly
|
31
|
+
readonly forbiddenPatterns: {
|
32
32
|
readonly items: {
|
33
33
|
readonly type: "object";
|
34
34
|
};
|
@@ -37,7 +37,7 @@ declare const schema: {
|
|
37
37
|
readonly uniqueItems: true;
|
38
38
|
readonly minItems: 1;
|
39
39
|
};
|
40
|
-
readonly
|
40
|
+
readonly requiredPatterns: {
|
41
41
|
readonly items: {
|
42
42
|
readonly type: "object";
|
43
43
|
};
|
@@ -47,14 +47,14 @@ const KindToDisplayName = {
|
|
47
47
|
style: { enum: ALLOWED_STYLES },
|
48
48
|
prefix: { type: "string" },
|
49
49
|
suffix: { type: "string" },
|
50
|
-
|
50
|
+
forbiddenPatterns: {
|
51
51
|
...ARRAY_DEFAULT_OPTIONS,
|
52
52
|
items: {
|
53
53
|
type: "object"
|
54
54
|
},
|
55
55
|
description: "Should be of instance of `RegEx`"
|
56
56
|
},
|
57
|
-
|
57
|
+
requiredPatterns: {
|
58
58
|
...ARRAY_DEFAULT_OPTIONS,
|
59
59
|
items: {
|
60
60
|
type: "object"
|
@@ -63,19 +63,19 @@ const KindToDisplayName = {
|
|
63
63
|
},
|
64
64
|
forbiddenPrefixes: {
|
65
65
|
...ARRAY_DEFAULT_OPTIONS,
|
66
|
-
description: descriptionPrefixesSuffixes("
|
66
|
+
description: descriptionPrefixesSuffixes("forbiddenPatterns")
|
67
67
|
},
|
68
68
|
forbiddenSuffixes: {
|
69
69
|
...ARRAY_DEFAULT_OPTIONS,
|
70
|
-
description: descriptionPrefixesSuffixes("
|
70
|
+
description: descriptionPrefixesSuffixes("forbiddenPatterns")
|
71
71
|
},
|
72
72
|
requiredPrefixes: {
|
73
73
|
...ARRAY_DEFAULT_OPTIONS,
|
74
|
-
description: descriptionPrefixesSuffixes("
|
74
|
+
description: descriptionPrefixesSuffixes("requiredPatterns")
|
75
75
|
},
|
76
76
|
requiredSuffixes: {
|
77
77
|
...ARRAY_DEFAULT_OPTIONS,
|
78
|
-
description: descriptionPrefixesSuffixes("
|
78
|
+
description: descriptionPrefixesSuffixes("requiredPatterns")
|
79
79
|
},
|
80
80
|
ignorePattern: {
|
81
81
|
type: "string",
|
@@ -101,7 +101,9 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
101
101
|
kind,
|
102
102
|
{
|
103
103
|
...schemaOption,
|
104
|
-
description:
|
104
|
+
description: `> [!NOTE]
|
105
|
+
>
|
106
|
+
> Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`
|
105
107
|
}
|
106
108
|
])
|
107
109
|
),
|
@@ -328,7 +330,7 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
328
330
|
schema
|
329
331
|
},
|
330
332
|
create(context) {
|
331
|
-
const options = context.options[0] || {}, { allowLeadingUnderscore, allowTrailingUnderscore, types, ...restOptions } = options;
|
333
|
+
const options = context.options[0] || {}, { allowLeadingUnderscore, allowTrailingUnderscore, types, ...restOptions } = options, ignoredNodes = /* @__PURE__ */ new Set();
|
332
334
|
function normalisePropertyOption(kind) {
|
333
335
|
const style = restOptions[kind] || types;
|
334
336
|
return typeof style == "object" ? style : { style };
|
@@ -356,8 +358,8 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
356
358
|
ignorePattern,
|
357
359
|
requiredPrefixes,
|
358
360
|
requiredSuffixes,
|
359
|
-
|
360
|
-
|
361
|
+
forbiddenPatterns,
|
362
|
+
requiredPatterns
|
361
363
|
} = normalisePropertyOption(selector), nodeName = node.value, error = getError();
|
362
364
|
if (error) {
|
363
365
|
const { errorMessage, renameToNames } = error, [leadingUnderscores] = nodeName.match(/^_*/), [trailingUnderscores] = nodeName.match(/_*$/), suggestedNames = renameToNames.map(
|
@@ -371,8 +373,10 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
371
373
|
}
|
372
374
|
function getError() {
|
373
375
|
const name = nodeName.replace(/(^_+)|(_+$)/g, "");
|
374
|
-
if (ignorePattern && new RegExp(ignorePattern, "u").test(name))
|
376
|
+
if (ignorePattern && new RegExp(ignorePattern, "u").test(name)) {
|
377
|
+
"name" in n && ignoredNodes.add(n.name);
|
375
378
|
return;
|
379
|
+
}
|
376
380
|
if (prefix && !name.startsWith(prefix))
|
377
381
|
return {
|
378
382
|
errorMessage: `have "${prefix}" prefix`,
|
@@ -383,15 +387,15 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
383
387
|
errorMessage: `have "${suffix}" suffix`,
|
384
388
|
renameToNames: [name + suffix]
|
385
389
|
};
|
386
|
-
const forbidden =
|
390
|
+
const forbidden = forbiddenPatterns?.find((pattern) => pattern.test(name));
|
387
391
|
if (forbidden)
|
388
392
|
return {
|
389
393
|
errorMessage: `not contain the forbidden pattern "${forbidden}"`,
|
390
394
|
renameToNames: [name.replace(forbidden, "")]
|
391
395
|
};
|
392
|
-
if (
|
396
|
+
if (requiredPatterns && !requiredPatterns.some((pattern) => pattern.test(name)))
|
393
397
|
return {
|
394
|
-
errorMessage: `contain the required pattern: ${englishJoinWords(
|
398
|
+
errorMessage: `contain the required pattern: ${englishJoinWords(requiredPatterns.map((re) => re.source))}`,
|
395
399
|
renameToNames: []
|
396
400
|
};
|
397
401
|
const forbiddenPrefix = forbiddenPrefixes?.find((prefix2) => name.startsWith(prefix2));
|
@@ -429,7 +433,7 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
429
433
|
};
|
430
434
|
}
|
431
435
|
}, checkUnderscore = (isLeading) => (node) => {
|
432
|
-
if (node.parent.kind === "Field" && node.parent.alias !== node)
|
436
|
+
if (ignoredNodes.has(node) || node.parent.kind === "Field" && node.parent.alias !== node)
|
433
437
|
return;
|
434
438
|
const suggestedName = node.value.replace(isLeading ? /^_+/ : /_+$/, "");
|
435
439
|
report(node, `${isLeading ? "Leading" : "Trailing"} underscores are not allowed`, [
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { TypeInfo, visit, visitWithTypeInfo } from "graphql";
|
2
2
|
import { ModuleCache } from "../../cache.js";
|
3
|
-
import { requireGraphQLOperations, requireGraphQLSchema } from "../../utils.js";
|
3
|
+
import { eslintSelectorsTip, requireGraphQLOperations, requireGraphQLSchema } from "../../utils.js";
|
4
4
|
const RULE_ID = "no-unused-fields", RELAY_SCHEMA = (
|
5
5
|
/* GraphQL */
|
6
6
|
`
|
@@ -82,9 +82,7 @@ const RULE_ID = "no-unused-fields", RELAY_SCHEMA = (
|
|
82
82
|
"```json",
|
83
83
|
JSON.stringify(RELAY_DEFAULT_IGNORED_FIELD_SELECTORS, null, 2),
|
84
84
|
"```",
|
85
|
-
|
86
|
-
"> These fields are defined by ESLint [`selectors`](https://eslint.org/docs/developer-guide/selectors).",
|
87
|
-
"> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector."
|
85
|
+
eslintSelectorsTip
|
88
86
|
].join(`
|
89
87
|
`),
|
90
88
|
items: {
|
@@ -1,6 +1,13 @@
|
|
1
1
|
import { Kind, TokenKind } from "graphql";
|
2
2
|
import { getRootTypeNames } from "@graphql-tools/utils";
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
ARRAY_DEFAULT_OPTIONS,
|
5
|
+
eslintSelectorsTip,
|
6
|
+
getLocation,
|
7
|
+
getNodeName,
|
8
|
+
requireGraphQLSchema,
|
9
|
+
TYPES_KINDS
|
10
|
+
} from "../../utils.js";
|
4
11
|
const RULE_ID = "require-description", ALLOWED_KINDS = [
|
5
12
|
...TYPES_KINDS,
|
6
13
|
Kind.DIRECTIVE_DEFINITION,
|
@@ -19,18 +26,34 @@ const RULE_ID = "require-description", ALLOWED_KINDS = [
|
|
19
26
|
properties: {
|
20
27
|
types: {
|
21
28
|
type: "boolean",
|
29
|
+
enum: [!0],
|
22
30
|
description: `Includes:
|
23
31
|
${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
24
32
|
`)}`
|
25
33
|
},
|
26
34
|
rootField: {
|
27
35
|
type: "boolean",
|
36
|
+
enum: [!0],
|
28
37
|
description: "Definitions within `Query`, `Mutation`, and `Subscription` root types."
|
29
38
|
},
|
39
|
+
ignoredSelectors: {
|
40
|
+
...ARRAY_DEFAULT_OPTIONS,
|
41
|
+
description: ["Ignore specific selectors", eslintSelectorsTip].join(`
|
42
|
+
`)
|
43
|
+
},
|
30
44
|
...Object.fromEntries(
|
31
45
|
[...ALLOWED_KINDS].sort().map((kind) => {
|
32
|
-
let description =
|
33
|
-
|
46
|
+
let description = `> [!NOTE]
|
47
|
+
>
|
48
|
+
> Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`;
|
49
|
+
return kind === Kind.OPERATION_DEFINITION && (description += [
|
50
|
+
"",
|
51
|
+
"",
|
52
|
+
"> [!WARNING]",
|
53
|
+
">",
|
54
|
+
'> You must use only comment syntax `#` and not description syntax `"""` or `"`.'
|
55
|
+
].join(`
|
56
|
+
`)), [kind, { type: "boolean", description }];
|
34
57
|
})
|
35
58
|
)
|
36
59
|
}
|
@@ -101,6 +124,36 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
101
124
|
}
|
102
125
|
`
|
103
126
|
)
|
127
|
+
},
|
128
|
+
{
|
129
|
+
title: "Correct",
|
130
|
+
usage: [
|
131
|
+
{
|
132
|
+
ignoredSelectors: [
|
133
|
+
"[type=ObjectTypeDefinition][name.value=PageInfo]",
|
134
|
+
"[type=ObjectTypeDefinition][name.value=/(Connection|Edge)$/]"
|
135
|
+
]
|
136
|
+
}
|
137
|
+
],
|
138
|
+
code: (
|
139
|
+
/* GraphQL */
|
140
|
+
`
|
141
|
+
type FriendConnection {
|
142
|
+
edges: [FriendEdge]
|
143
|
+
pageInfo: PageInfo!
|
144
|
+
}
|
145
|
+
type FriendEdge {
|
146
|
+
cursor: String!
|
147
|
+
node: Friend!
|
148
|
+
}
|
149
|
+
type PageInfo {
|
150
|
+
hasPreviousPage: Boolean!
|
151
|
+
hasNextPage: Boolean!
|
152
|
+
startCursor: String
|
153
|
+
endCursor: String
|
154
|
+
}
|
155
|
+
`
|
156
|
+
)
|
104
157
|
}
|
105
158
|
],
|
106
159
|
configOptions: [
|
@@ -119,7 +172,7 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
119
172
|
schema
|
120
173
|
},
|
121
174
|
create(context) {
|
122
|
-
const { types, rootField, ...restOptions } = context.options[0] || {}, kinds = new Set(types ? TYPES_KINDS : []);
|
175
|
+
const { types, rootField, ignoredSelectors = [], ...restOptions } = context.options[0] || {}, kinds = new Set(types ? TYPES_KINDS : []);
|
123
176
|
for (const [kind, isEnabled] of Object.entries(restOptions))
|
124
177
|
isEnabled ? kinds.add(kind) : kinds.delete(kind);
|
125
178
|
if (rootField) {
|
@@ -130,10 +183,11 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
130
183
|
].join(",")})$/] > FieldDefinition`
|
131
184
|
);
|
132
185
|
}
|
133
|
-
|
134
|
-
|
186
|
+
let selector = `:matches(${[...kinds]})`;
|
187
|
+
for (const str of ignoredSelectors)
|
188
|
+
selector += `:not(${str})`;
|
135
189
|
return {
|
136
|
-
[
|
190
|
+
[selector](node) {
|
137
191
|
let description = "";
|
138
192
|
const isOperation = node.kind === Kind.OPERATION_DEFINITION;
|
139
193
|
if (isOperation) {
|
package/esm/utils.d.ts
CHANGED
@@ -41,5 +41,6 @@ type Truthy<T> = T extends '' | 0 | false | null | undefined ? never : T;
|
|
41
41
|
declare function truthy<T>(value: T): value is Truthy<T>;
|
42
42
|
declare function displayNodeName(node: GraphQLESTreeNode<ASTNode, boolean>): string;
|
43
43
|
declare function getNodeName(node: GraphQLESTreeNode<ASTNode>): string;
|
44
|
+
declare const eslintSelectorsTip = "> [!TIP]\n>\n> These fields are defined by ESLint [`selectors`](https://eslint.org/docs/developer-guide/selectors).\n> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.";
|
44
45
|
|
45
|
-
export { ARRAY_DEFAULT_OPTIONS, CWD, type CaseStyle, REPORT_ON_FIRST_CHARACTER, TYPES_KINDS, VIRTUAL_DOCUMENT_REGEX, camelCase, convertCase, displayNodeName, englishJoinWords, getLocation, getNodeName, getTypeName, logger, pascalCase, requireGraphQLOperations, requireGraphQLSchema, slash, truthy };
|
46
|
+
export { ARRAY_DEFAULT_OPTIONS, CWD, type CaseStyle, REPORT_ON_FIRST_CHARACTER, TYPES_KINDS, VIRTUAL_DOCUMENT_REGEX, camelCase, convertCase, displayNodeName, englishJoinWords, eslintSelectorsTip, getLocation, getNodeName, getTypeName, logger, pascalCase, requireGraphQLOperations, requireGraphQLSchema, slash, truthy };
|
package/esm/utils.js
CHANGED
@@ -144,6 +144,10 @@ function getNodeName(node) {
|
|
144
144
|
}
|
145
145
|
return "";
|
146
146
|
}
|
147
|
+
const eslintSelectorsTip = `> [!TIP]
|
148
|
+
>
|
149
|
+
> These fields are defined by ESLint [\`selectors\`](https://eslint.org/docs/developer-guide/selectors).
|
150
|
+
> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.`;
|
147
151
|
export {
|
148
152
|
ARRAY_DEFAULT_OPTIONS,
|
149
153
|
CWD,
|
@@ -154,6 +158,7 @@ export {
|
|
154
158
|
convertCase,
|
155
159
|
displayNodeName,
|
156
160
|
englishJoinWords,
|
161
|
+
eslintSelectorsTip,
|
157
162
|
getLocation,
|
158
163
|
getNodeName,
|
159
164
|
getTypeName,
|
package/index.browser.js
CHANGED
@@ -150,7 +150,7 @@ function convertToESTree(node, schema16) {
|
|
150
150
|
}
|
151
151
|
|
152
152
|
// src/meta.ts
|
153
|
-
var version = "4.1.
|
153
|
+
var version = "4.1.1-alpha-20241129095718-0cc689c0eb8b65d162f4c6d87030879fe63a105b";
|
154
154
|
|
155
155
|
// src/siblings.ts
|
156
156
|
import {
|
@@ -305,6 +305,10 @@ function getNodeName(node) {
|
|
305
305
|
}
|
306
306
|
return "";
|
307
307
|
}
|
308
|
+
var eslintSelectorsTip = `> [!TIP]
|
309
|
+
>
|
310
|
+
> These fields are defined by ESLint [\`selectors\`](https://eslint.org/docs/developer-guide/selectors).
|
311
|
+
> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.`;
|
308
312
|
|
309
313
|
// src/siblings.ts
|
310
314
|
var siblingOperationsCache = /* @__PURE__ */ new Map();
|
@@ -1934,14 +1938,14 @@ var KindToDisplayName = {
|
|
1934
1938
|
style: { enum: ALLOWED_STYLES },
|
1935
1939
|
prefix: { type: "string" },
|
1936
1940
|
suffix: { type: "string" },
|
1937
|
-
|
1941
|
+
forbiddenPatterns: {
|
1938
1942
|
...ARRAY_DEFAULT_OPTIONS,
|
1939
1943
|
items: {
|
1940
1944
|
type: "object"
|
1941
1945
|
},
|
1942
1946
|
description: "Should be of instance of `RegEx`"
|
1943
1947
|
},
|
1944
|
-
|
1948
|
+
requiredPatterns: {
|
1945
1949
|
...ARRAY_DEFAULT_OPTIONS,
|
1946
1950
|
items: {
|
1947
1951
|
type: "object"
|
@@ -1950,19 +1954,19 @@ var KindToDisplayName = {
|
|
1950
1954
|
},
|
1951
1955
|
forbiddenPrefixes: {
|
1952
1956
|
...ARRAY_DEFAULT_OPTIONS,
|
1953
|
-
description: descriptionPrefixesSuffixes("
|
1957
|
+
description: descriptionPrefixesSuffixes("forbiddenPatterns")
|
1954
1958
|
},
|
1955
1959
|
forbiddenSuffixes: {
|
1956
1960
|
...ARRAY_DEFAULT_OPTIONS,
|
1957
|
-
description: descriptionPrefixesSuffixes("
|
1961
|
+
description: descriptionPrefixesSuffixes("forbiddenPatterns")
|
1958
1962
|
},
|
1959
1963
|
requiredPrefixes: {
|
1960
1964
|
...ARRAY_DEFAULT_OPTIONS,
|
1961
|
-
description: descriptionPrefixesSuffixes("
|
1965
|
+
description: descriptionPrefixesSuffixes("requiredPatterns")
|
1962
1966
|
},
|
1963
1967
|
requiredSuffixes: {
|
1964
1968
|
...ARRAY_DEFAULT_OPTIONS,
|
1965
|
-
description: descriptionPrefixesSuffixes("
|
1969
|
+
description: descriptionPrefixesSuffixes("requiredPatterns")
|
1966
1970
|
},
|
1967
1971
|
ignorePattern: {
|
1968
1972
|
type: "string",
|
@@ -1988,7 +1992,9 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
1988
1992
|
kind,
|
1989
1993
|
{
|
1990
1994
|
...schemaOption2,
|
1991
|
-
description:
|
1995
|
+
description: `> [!NOTE]
|
1996
|
+
>
|
1997
|
+
> Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`
|
1992
1998
|
}
|
1993
1999
|
])
|
1994
2000
|
),
|
@@ -2215,7 +2221,7 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
2215
2221
|
schema: schema6
|
2216
2222
|
},
|
2217
2223
|
create(context) {
|
2218
|
-
let options = context.options[0] || {}, { allowLeadingUnderscore, allowTrailingUnderscore, types, ...restOptions } = options;
|
2224
|
+
let options = context.options[0] || {}, { allowLeadingUnderscore, allowTrailingUnderscore, types, ...restOptions } = options, ignoredNodes = /* @__PURE__ */ new Set();
|
2219
2225
|
function normalisePropertyOption(kind) {
|
2220
2226
|
let style = restOptions[kind] || types;
|
2221
2227
|
return typeof style == "object" ? style : { style };
|
@@ -2243,8 +2249,8 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
2243
2249
|
ignorePattern,
|
2244
2250
|
requiredPrefixes,
|
2245
2251
|
requiredSuffixes,
|
2246
|
-
|
2247
|
-
|
2252
|
+
forbiddenPatterns,
|
2253
|
+
requiredPatterns
|
2248
2254
|
} = normalisePropertyOption(selector), nodeName = node.value, error = getError();
|
2249
2255
|
if (error) {
|
2250
2256
|
let { errorMessage, renameToNames } = error, [leadingUnderscores] = nodeName.match(/^_*/), [trailingUnderscores] = nodeName.match(/_*$/), suggestedNames = renameToNames.map(
|
@@ -2258,8 +2264,10 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
2258
2264
|
}
|
2259
2265
|
function getError() {
|
2260
2266
|
let name = nodeName.replace(/(^_+)|(_+$)/g, "");
|
2261
|
-
if (ignorePattern && new RegExp(ignorePattern, "u").test(name))
|
2267
|
+
if (ignorePattern && new RegExp(ignorePattern, "u").test(name)) {
|
2268
|
+
"name" in n && ignoredNodes.add(n.name);
|
2262
2269
|
return;
|
2270
|
+
}
|
2263
2271
|
if (prefix && !name.startsWith(prefix))
|
2264
2272
|
return {
|
2265
2273
|
errorMessage: `have "${prefix}" prefix`,
|
@@ -2270,15 +2278,15 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
2270
2278
|
errorMessage: `have "${suffix}" suffix`,
|
2271
2279
|
renameToNames: [name + suffix]
|
2272
2280
|
};
|
2273
|
-
let forbidden =
|
2281
|
+
let forbidden = forbiddenPatterns?.find((pattern) => pattern.test(name));
|
2274
2282
|
if (forbidden)
|
2275
2283
|
return {
|
2276
2284
|
errorMessage: `not contain the forbidden pattern "${forbidden}"`,
|
2277
2285
|
renameToNames: [name.replace(forbidden, "")]
|
2278
2286
|
};
|
2279
|
-
if (
|
2287
|
+
if (requiredPatterns && !requiredPatterns.some((pattern) => pattern.test(name)))
|
2280
2288
|
return {
|
2281
|
-
errorMessage: `contain the required pattern: ${englishJoinWords(
|
2289
|
+
errorMessage: `contain the required pattern: ${englishJoinWords(requiredPatterns.map((re) => re.source))}`,
|
2282
2290
|
renameToNames: []
|
2283
2291
|
};
|
2284
2292
|
let forbiddenPrefix = forbiddenPrefixes?.find((prefix2) => name.startsWith(prefix2));
|
@@ -2316,7 +2324,7 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
2316
2324
|
};
|
2317
2325
|
}
|
2318
2326
|
}, checkUnderscore = (isLeading) => (node) => {
|
2319
|
-
if (node.parent.kind === "Field" && node.parent.alias !== node)
|
2327
|
+
if (ignoredNodes.has(node) || node.parent.kind === "Field" && node.parent.alias !== node)
|
2320
2328
|
return;
|
2321
2329
|
let suggestedName = node.value.replace(isLeading ? /^_+/ : /_+$/, "");
|
2322
2330
|
report(node, `${isLeading ? "Leading" : "Trailing"} underscores are not allowed`, [
|
@@ -3281,9 +3289,7 @@ var RULE_ID10 = "no-unused-fields", RELAY_SCHEMA = (
|
|
3281
3289
|
"```json",
|
3282
3290
|
JSON.stringify(RELAY_DEFAULT_IGNORED_FIELD_SELECTORS, null, 2),
|
3283
3291
|
"```",
|
3284
|
-
|
3285
|
-
"> These fields are defined by ESLint [`selectors`](https://eslint.org/docs/developer-guide/selectors).",
|
3286
|
-
"> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector."
|
3292
|
+
eslintSelectorsTip
|
3287
3293
|
].join(`
|
3288
3294
|
`),
|
3289
3295
|
items: {
|
@@ -4033,18 +4039,34 @@ var RULE_ID14 = "require-description", ALLOWED_KINDS2 = [
|
|
4033
4039
|
properties: {
|
4034
4040
|
types: {
|
4035
4041
|
type: "boolean",
|
4042
|
+
enum: [!0],
|
4036
4043
|
description: `Includes:
|
4037
4044
|
${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
4038
4045
|
`)}`
|
4039
4046
|
},
|
4040
4047
|
rootField: {
|
4041
4048
|
type: "boolean",
|
4049
|
+
enum: [!0],
|
4042
4050
|
description: "Definitions within `Query`, `Mutation`, and `Subscription` root types."
|
4043
4051
|
},
|
4052
|
+
ignoredSelectors: {
|
4053
|
+
...ARRAY_DEFAULT_OPTIONS,
|
4054
|
+
description: ["Ignore specific selectors", eslintSelectorsTip].join(`
|
4055
|
+
`)
|
4056
|
+
},
|
4044
4057
|
...Object.fromEntries(
|
4045
4058
|
[...ALLOWED_KINDS2].sort().map((kind) => {
|
4046
|
-
let description =
|
4047
|
-
|
4059
|
+
let description = `> [!NOTE]
|
4060
|
+
>
|
4061
|
+
> Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`;
|
4062
|
+
return kind === Kind17.OPERATION_DEFINITION && (description += [
|
4063
|
+
"",
|
4064
|
+
"",
|
4065
|
+
"> [!WARNING]",
|
4066
|
+
">",
|
4067
|
+
'> You must use only comment syntax `#` and not description syntax `"""` or `"`.'
|
4068
|
+
].join(`
|
4069
|
+
`)), [kind, { type: "boolean", description }];
|
4048
4070
|
})
|
4049
4071
|
)
|
4050
4072
|
}
|
@@ -4115,6 +4137,36 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
4115
4137
|
}
|
4116
4138
|
`
|
4117
4139
|
)
|
4140
|
+
},
|
4141
|
+
{
|
4142
|
+
title: "Correct",
|
4143
|
+
usage: [
|
4144
|
+
{
|
4145
|
+
ignoredSelectors: [
|
4146
|
+
"[type=ObjectTypeDefinition][name.value=PageInfo]",
|
4147
|
+
"[type=ObjectTypeDefinition][name.value=/(Connection|Edge)$/]"
|
4148
|
+
]
|
4149
|
+
}
|
4150
|
+
],
|
4151
|
+
code: (
|
4152
|
+
/* GraphQL */
|
4153
|
+
`
|
4154
|
+
type FriendConnection {
|
4155
|
+
edges: [FriendEdge]
|
4156
|
+
pageInfo: PageInfo!
|
4157
|
+
}
|
4158
|
+
type FriendEdge {
|
4159
|
+
cursor: String!
|
4160
|
+
node: Friend!
|
4161
|
+
}
|
4162
|
+
type PageInfo {
|
4163
|
+
hasPreviousPage: Boolean!
|
4164
|
+
hasNextPage: Boolean!
|
4165
|
+
startCursor: String
|
4166
|
+
endCursor: String
|
4167
|
+
}
|
4168
|
+
`
|
4169
|
+
)
|
4118
4170
|
}
|
4119
4171
|
],
|
4120
4172
|
configOptions: [
|
@@ -4133,7 +4185,7 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
4133
4185
|
schema: schema12
|
4134
4186
|
},
|
4135
4187
|
create(context) {
|
4136
|
-
let { types, rootField, ...restOptions } = context.options[0] || {}, kinds = new Set(types ? TYPES_KINDS : []);
|
4188
|
+
let { types, rootField, ignoredSelectors = [], ...restOptions } = context.options[0] || {}, kinds = new Set(types ? TYPES_KINDS : []);
|
4137
4189
|
for (let [kind, isEnabled] of Object.entries(restOptions))
|
4138
4190
|
isEnabled ? kinds.add(kind) : kinds.delete(kind);
|
4139
4191
|
if (rootField) {
|
@@ -4144,10 +4196,11 @@ ${TYPES_KINDS.map((kind) => `- \`${kind}\``).join(`
|
|
4144
4196
|
].join(",")})$/] > FieldDefinition`
|
4145
4197
|
);
|
4146
4198
|
}
|
4147
|
-
|
4148
|
-
|
4199
|
+
let selector = `:matches(${[...kinds]})`;
|
4200
|
+
for (let str of ignoredSelectors)
|
4201
|
+
selector += `:not(${str})`;
|
4149
4202
|
return {
|
4150
|
-
[
|
4203
|
+
[selector](node) {
|
4151
4204
|
let description = "", isOperation = node.kind === Kind17.OPERATION_DEFINITION;
|
4152
4205
|
if (isOperation) {
|
4153
4206
|
let rawNode = node.rawNode(), { prev, line } = rawNode.loc.startToken;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@graphql-eslint/eslint-plugin",
|
3
|
-
"version": "4.1.
|
3
|
+
"version": "4.1.1-alpha-20241129095718-0cc689c0eb8b65d162f4c6d87030879fe63a105b",
|
4
4
|
"type": "module",
|
5
5
|
"description": "GraphQL plugin for ESLint",
|
6
6
|
"repository": "https://github.com/dimaMachina/graphql-eslint",
|