@graphql-eslint/eslint-plugin 4.3.0 → 4.4.0-alpha-20241207210859-41eb4549764dc0314b5bd4f257ea6667b178540e
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 +179 -82
- 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 +18 -4
- 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 +18 -4
- 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 +179 -82
- 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 +1871 -1160
- package/package.json +1 -1
@@ -15,7 +15,9 @@ var _indexjs = require('../../estree-converter/index.js');
|
|
15
15
|
|
16
16
|
|
17
17
|
var _utilsjs = require('../../utils.js');
|
18
|
-
const RULE_ID = "require-selections"
|
18
|
+
const RULE_ID = "require-selections";
|
19
|
+
const DEFAULT_ID_FIELD_NAME = "id";
|
20
|
+
const schema = {
|
19
21
|
definitions: {
|
20
22
|
asString: {
|
21
23
|
type: "string"
|
@@ -26,7 +28,7 @@ const RULE_ID = "require-selections", DEFAULT_ID_FIELD_NAME = "id", schema = {
|
|
26
28
|
maxItems: 1,
|
27
29
|
items: {
|
28
30
|
type: "object",
|
29
|
-
additionalProperties:
|
31
|
+
additionalProperties: false,
|
30
32
|
properties: {
|
31
33
|
fieldName: {
|
32
34
|
oneOf: [{ $ref: "#/definitions/asString" }, { $ref: "#/definitions/asArray" }],
|
@@ -38,16 +40,17 @@ const RULE_ID = "require-selections", DEFAULT_ID_FIELD_NAME = "id", schema = {
|
|
38
40
|
}
|
39
41
|
}
|
40
42
|
}
|
41
|
-
}
|
43
|
+
};
|
44
|
+
const rule = {
|
42
45
|
meta: {
|
43
46
|
type: "suggestion",
|
44
|
-
hasSuggestions:
|
47
|
+
hasSuggestions: true,
|
45
48
|
docs: {
|
46
49
|
category: "Operations",
|
47
50
|
description: "Enforce selecting specific fields when they are available on the GraphQL type.",
|
48
51
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
49
|
-
requiresSchema:
|
50
|
-
requiresSiblings:
|
52
|
+
requiresSchema: true,
|
53
|
+
requiresSiblings: true,
|
51
54
|
examples: [
|
52
55
|
{
|
53
56
|
title: "Incorrect",
|
@@ -98,34 +101,45 @@ const RULE_ID = "require-selections", DEFAULT_ID_FIELD_NAME = "id", schema = {
|
|
98
101
|
)
|
99
102
|
}
|
100
103
|
],
|
101
|
-
recommended:
|
104
|
+
recommended: true,
|
102
105
|
whenNotToUseIt: "Relay Compiler automatically adds an `id` field to any type that has an `id` field, even if it hasn't been explicitly requested. Requesting a field that is not used directly in the code can conflict with another Relay rule: `relay/unused-fields`."
|
103
106
|
},
|
104
107
|
messages: {
|
105
|
-
[RULE_ID]:
|
106
|
-
Include it in your selection set{{ addition }}.`
|
108
|
+
[RULE_ID]: "Field{{ pluralSuffix }} {{ fieldName }} must be selected when it's available on a type.\nInclude it in your selection set{{ addition }}."
|
107
109
|
},
|
108
110
|
schema
|
109
111
|
},
|
110
112
|
create(context) {
|
111
|
-
const schema2 = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context)
|
113
|
+
const schema2 = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context);
|
114
|
+
const siblings = _utilsjs.requireGraphQLOperations.call(void 0, RULE_ID, context);
|
115
|
+
const { fieldName = DEFAULT_ID_FIELD_NAME, requireAllFields } = context.options[0] || {};
|
116
|
+
const idNames = _utils.asArray.call(void 0, fieldName);
|
117
|
+
const selector = "SelectionSet[parent.kind!=/(^OperationDefinition|InlineFragment)$/]";
|
118
|
+
const typeInfo = new (0, _graphql.TypeInfo)(schema2);
|
112
119
|
function checkFragments(node) {
|
113
120
|
for (const selection of node.selections) {
|
114
|
-
if (selection.kind !== _graphql.Kind.FRAGMENT_SPREAD)
|
121
|
+
if (selection.kind !== _graphql.Kind.FRAGMENT_SPREAD) {
|
115
122
|
continue;
|
123
|
+
}
|
116
124
|
const [foundSpread] = siblings.getFragment(selection.name.value);
|
117
|
-
if (!foundSpread)
|
125
|
+
if (!foundSpread) {
|
118
126
|
continue;
|
119
|
-
|
127
|
+
}
|
128
|
+
const checkedFragmentSpreads = /* @__PURE__ */ new Set();
|
129
|
+
const visitor = _graphql.visitWithTypeInfo.call(void 0, typeInfo, {
|
120
130
|
SelectionSet(node2, key, _parent) {
|
121
131
|
const parent = _parent;
|
122
|
-
parent.kind === _graphql.Kind.FRAGMENT_DEFINITION
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
132
|
+
if (parent.kind === _graphql.Kind.FRAGMENT_DEFINITION) {
|
133
|
+
checkedFragmentSpreads.add(parent.name.value);
|
134
|
+
} else if (parent.kind !== _graphql.Kind.INLINE_FRAGMENT) {
|
135
|
+
checkSelections(
|
136
|
+
node2,
|
137
|
+
typeInfo.getType(),
|
138
|
+
selection.loc.start,
|
139
|
+
parent,
|
140
|
+
checkedFragmentSpreads
|
141
|
+
);
|
142
|
+
}
|
129
143
|
}
|
130
144
|
});
|
131
145
|
_graphql.visit.call(void 0, foundSpread.document, visitor);
|
@@ -133,52 +147,74 @@ Include it in your selection set{{ addition }}.`
|
|
133
147
|
}
|
134
148
|
function checkSelections(node, type, loc, parent, checkedFragmentSpreads = /* @__PURE__ */ new Set()) {
|
135
149
|
const rawType = _indexjs.getBaseType.call(void 0, type);
|
136
|
-
if (rawType instanceof _graphql.GraphQLObjectType || rawType instanceof _graphql.GraphQLInterfaceType)
|
150
|
+
if (rawType instanceof _graphql.GraphQLObjectType || rawType instanceof _graphql.GraphQLInterfaceType) {
|
137
151
|
checkFields(rawType);
|
138
|
-
else if (rawType instanceof _graphql.GraphQLUnionType)
|
152
|
+
} else if (rawType instanceof _graphql.GraphQLUnionType) {
|
139
153
|
for (const selection of node.selections) {
|
140
154
|
const types = rawType.getTypes();
|
141
155
|
if (selection.kind === _graphql.Kind.INLINE_FRAGMENT) {
|
142
156
|
const t = types.find((t2) => t2.name === selection.typeCondition.name.value);
|
143
|
-
|
157
|
+
if (t) {
|
158
|
+
checkFields(t);
|
159
|
+
}
|
144
160
|
} else if (selection.kind === _graphql.Kind.FRAGMENT_SPREAD) {
|
145
161
|
const [foundSpread] = siblings.getFragment(selection.name.value);
|
146
162
|
if (!foundSpread) return;
|
147
|
-
const fragmentSpread = foundSpread.document
|
148
|
-
|
163
|
+
const fragmentSpread = foundSpread.document;
|
164
|
+
const t = fragmentSpread.typeCondition.name.value === rawType.name ? rawType : types.find((t2) => t2.name === fragmentSpread.typeCondition.name.value);
|
165
|
+
checkedFragmentSpreads.add(fragmentSpread.name.value);
|
166
|
+
checkSelections(fragmentSpread.selectionSet, t, loc, parent, checkedFragmentSpreads);
|
149
167
|
}
|
150
168
|
}
|
169
|
+
}
|
151
170
|
function checkFields(rawType2) {
|
152
171
|
const fields = rawType2.getFields();
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
172
|
+
const hasIdFieldInType = idNames.some((name) => fields[name]);
|
173
|
+
if (!hasIdFieldInType) {
|
174
|
+
return;
|
175
|
+
}
|
176
|
+
checkFragments(node);
|
177
|
+
if (requireAllFields) {
|
178
|
+
for (const idName of idNames) {
|
179
|
+
report([idName]);
|
180
|
+
}
|
181
|
+
} else {
|
182
|
+
report(idNames);
|
183
|
+
}
|
159
184
|
}
|
160
185
|
function report(idNames2) {
|
161
186
|
function hasIdField({ selections }) {
|
162
187
|
return selections.some((selection) => {
|
163
|
-
if (selection.kind === _graphql.Kind.FIELD)
|
164
|
-
|
165
|
-
|
188
|
+
if (selection.kind === _graphql.Kind.FIELD) {
|
189
|
+
if (selection.alias && idNames2.includes(selection.alias.value)) {
|
190
|
+
return true;
|
191
|
+
}
|
192
|
+
return idNames2.includes(selection.name.value);
|
193
|
+
}
|
194
|
+
if (selection.kind === _graphql.Kind.INLINE_FRAGMENT) {
|
166
195
|
return hasIdField(selection.selectionSet);
|
196
|
+
}
|
167
197
|
if (selection.kind === _graphql.Kind.FRAGMENT_SPREAD) {
|
168
198
|
const [foundSpread] = siblings.getFragment(selection.name.value);
|
169
199
|
if (foundSpread) {
|
170
200
|
const fragmentSpread = foundSpread.document;
|
171
|
-
|
201
|
+
checkedFragmentSpreads.add(fragmentSpread.name.value);
|
202
|
+
return hasIdField(fragmentSpread.selectionSet);
|
172
203
|
}
|
173
204
|
}
|
174
|
-
return
|
205
|
+
return false;
|
175
206
|
});
|
176
207
|
}
|
177
|
-
|
208
|
+
const hasId = hasIdField(node);
|
209
|
+
if (hasId) {
|
178
210
|
return;
|
211
|
+
}
|
179
212
|
const fieldName2 = _utilsjs.englishJoinWords.call(void 0,
|
180
213
|
idNames2.map((name) => `\`${(parent.alias || parent.name).value}.${name}\``)
|
181
|
-
)
|
214
|
+
);
|
215
|
+
const pluralSuffix = idNames2.length > 1 ? "s" : "";
|
216
|
+
const addition = checkedFragmentSpreads.size === 0 ? "" : ` or add to used fragment${checkedFragmentSpreads.size > 1 ? "s" : ""} ${_utilsjs.englishJoinWords.call(void 0, [...checkedFragmentSpreads].map((name) => `\`${name}\``))}`;
|
217
|
+
const problem = {
|
182
218
|
loc,
|
183
219
|
messageId: RULE_ID,
|
184
220
|
data: {
|
@@ -187,19 +223,25 @@ Include it in your selection set{{ addition }}.`
|
|
187
223
|
addition
|
188
224
|
}
|
189
225
|
};
|
190
|
-
"type" in node
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
226
|
+
if ("type" in node) {
|
227
|
+
problem.suggest = idNames2.map((idName) => ({
|
228
|
+
desc: `Add \`${idName}\` selection`,
|
229
|
+
fix: (fixer) => {
|
230
|
+
let insertNode = node.selections[0];
|
231
|
+
insertNode = insertNode.kind === _graphql.Kind.INLINE_FRAGMENT ? insertNode.selectionSet.selections[0] : insertNode;
|
232
|
+
return fixer.insertTextBefore(insertNode, `${idName} `);
|
233
|
+
}
|
234
|
+
}));
|
235
|
+
}
|
236
|
+
context.report(problem);
|
197
237
|
}
|
198
238
|
}
|
199
239
|
return {
|
200
240
|
[selector](node) {
|
201
241
|
const typeInfo2 = node.typeInfo();
|
202
|
-
|
242
|
+
if (typeInfo2.gqlType) {
|
243
|
+
checkSelections(node, typeInfo2.gqlType, node.loc.start, node.parent);
|
244
|
+
}
|
203
245
|
}
|
204
246
|
};
|
205
247
|
}
|
@@ -1,5 +1,6 @@
|
|
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 = "require-type-pattern-with-oneof"
|
2
|
+
const RULE_ID = "require-type-pattern-with-oneof";
|
3
|
+
const rule = {
|
3
4
|
meta: {
|
4
5
|
type: "suggestion",
|
5
6
|
docs: {
|
@@ -44,15 +45,18 @@ const RULE_ID = "require-type-pattern-with-oneof", rule = exports.rule = {
|
|
44
45
|
parent
|
45
46
|
}) {
|
46
47
|
const requiredFields = ["error", "ok"];
|
47
|
-
for (const fieldName of requiredFields)
|
48
|
-
_optionalChain([parent, 'access', _ => _.fields, 'optionalAccess', _2 => _2.some, 'call', _3 => _3((field) => field.name.value === fieldName)])
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
48
|
+
for (const fieldName of requiredFields) {
|
49
|
+
if (!_optionalChain([parent, 'access', _ => _.fields, 'optionalAccess', _2 => _2.some, 'call', _3 => _3((field) => field.name.value === fieldName)])) {
|
50
|
+
context.report({
|
51
|
+
node: parent.name,
|
52
|
+
messageId: RULE_ID,
|
53
|
+
data: {
|
54
|
+
nodeName: _utilsjs.displayNodeName.call(void 0, parent),
|
55
|
+
fieldName
|
56
|
+
}
|
57
|
+
});
|
58
|
+
}
|
59
|
+
}
|
56
60
|
}
|
57
61
|
};
|
58
62
|
}
|
@@ -1,13 +1,14 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _graphql = require('graphql');
|
2
2
|
var _graphqldepthlimit = require('graphql-depth-limit'); var _graphqldepthlimit2 = _interopRequireDefault(_graphqldepthlimit);
|
3
3
|
var _utilsjs = require('../../utils.js');
|
4
|
-
const RULE_ID = "selection-set-depth"
|
4
|
+
const RULE_ID = "selection-set-depth";
|
5
|
+
const schema = {
|
5
6
|
type: "array",
|
6
7
|
minItems: 1,
|
7
8
|
maxItems: 1,
|
8
9
|
items: {
|
9
10
|
type: "object",
|
10
|
-
additionalProperties:
|
11
|
+
additionalProperties: false,
|
11
12
|
required: ["maxDepth"],
|
12
13
|
properties: {
|
13
14
|
maxDepth: {
|
@@ -16,15 +17,16 @@ const RULE_ID = "selection-set-depth", schema = {
|
|
16
17
|
ignore: _utilsjs.ARRAY_DEFAULT_OPTIONS
|
17
18
|
}
|
18
19
|
}
|
19
|
-
}
|
20
|
+
};
|
21
|
+
const rule = {
|
20
22
|
meta: {
|
21
23
|
type: "suggestion",
|
22
|
-
hasSuggestions:
|
24
|
+
hasSuggestions: true,
|
23
25
|
docs: {
|
24
26
|
category: "Operations",
|
25
27
|
description: "Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://npmjs.com/package/graphql-depth-limit).",
|
26
28
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
27
|
-
requiresSiblings:
|
29
|
+
requiresSiblings: true,
|
28
30
|
examples: [
|
29
31
|
{
|
30
32
|
title: "Incorrect",
|
@@ -66,7 +68,7 @@ const RULE_ID = "selection-set-depth", schema = {
|
|
66
68
|
`
|
67
69
|
}
|
68
70
|
],
|
69
|
-
recommended:
|
71
|
+
recommended: true,
|
70
72
|
configOptions: [{ maxDepth: 7 }]
|
71
73
|
},
|
72
74
|
schema
|
@@ -80,18 +82,23 @@ const RULE_ID = "selection-set-depth", schema = {
|
|
80
82
|
`Rule "${RULE_ID}" works best with siblings operations loaded. See https://the-guild.dev/graphql/eslint/docs/usage#providing-operations for more info`
|
81
83
|
);
|
82
84
|
}
|
83
|
-
const { maxDepth, ignore = [] } = context.options[0]
|
85
|
+
const { maxDepth, ignore = [] } = context.options[0];
|
86
|
+
const checkFn = _graphqldepthlimit2.default.call(void 0, maxDepth, { ignore });
|
84
87
|
return {
|
85
88
|
"OperationDefinition, FragmentDefinition"(node) {
|
86
89
|
try {
|
87
|
-
const rawNode = node.rawNode()
|
90
|
+
const rawNode = node.rawNode();
|
91
|
+
const fragmentsInUse = siblings ? siblings.getFragmentsInUse(rawNode) : [];
|
92
|
+
const document = {
|
88
93
|
kind: _graphql.Kind.DOCUMENT,
|
89
94
|
definitions: [rawNode, ...fragmentsInUse]
|
90
95
|
};
|
91
96
|
checkFn({
|
92
97
|
getDocument: () => document,
|
93
98
|
reportError(error) {
|
94
|
-
const { line, column } = error.locations[0]
|
99
|
+
const { line, column } = error.locations[0];
|
100
|
+
const ancestors = context.sourceCode.getAncestors(node);
|
101
|
+
const token = ancestors[0].tokens.find(
|
95
102
|
(token2) => token2.loc.start.line === line && token2.loc.start.column === column - 1
|
96
103
|
);
|
97
104
|
context.report({
|
@@ -106,7 +113,9 @@ const RULE_ID = "selection-set-depth", schema = {
|
|
106
113
|
{
|
107
114
|
desc: "Remove selections",
|
108
115
|
fix(fixer) {
|
109
|
-
const
|
116
|
+
const sourceCode = context.getSourceCode();
|
117
|
+
const foundNode = sourceCode.getNodeByRangeIndex(token.range[0]);
|
118
|
+
const parentNode = foundNode.parent.parent;
|
110
119
|
return fixer.remove(
|
111
120
|
foundNode.kind === "Name" ? parentNode.parent : parentNode
|
112
121
|
);
|
@@ -5,12 +5,13 @@
|
|
5
5
|
|
6
6
|
|
7
7
|
var _utilsjs = require('../../utils.js');
|
8
|
-
const RULE_ID = "strict-id-in-types"
|
8
|
+
const RULE_ID = "strict-id-in-types";
|
9
|
+
const schema = {
|
9
10
|
type: "array",
|
10
11
|
maxItems: 1,
|
11
12
|
items: {
|
12
13
|
type: "object",
|
13
|
-
additionalProperties:
|
14
|
+
additionalProperties: false,
|
14
15
|
properties: {
|
15
16
|
acceptedIdNames: {
|
16
17
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
@@ -22,7 +23,7 @@ const RULE_ID = "strict-id-in-types", schema = {
|
|
22
23
|
},
|
23
24
|
exceptions: {
|
24
25
|
type: "object",
|
25
|
-
additionalProperties:
|
26
|
+
additionalProperties: false,
|
26
27
|
properties: {
|
27
28
|
types: {
|
28
29
|
..._utilsjs.ARRAY_DEFAULT_OPTIONS,
|
@@ -36,15 +37,16 @@ const RULE_ID = "strict-id-in-types", schema = {
|
|
36
37
|
}
|
37
38
|
}
|
38
39
|
}
|
39
|
-
}
|
40
|
+
};
|
41
|
+
const rule = {
|
40
42
|
meta: {
|
41
43
|
type: "suggestion",
|
42
44
|
docs: {
|
43
45
|
description: "Requires output types to have one unique identifier unless they do not have a logical one. Exceptions can be used to ignore output types that do not have unique identifiers.",
|
44
46
|
category: "Schema",
|
45
|
-
recommended:
|
47
|
+
recommended: true,
|
46
48
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
47
|
-
requiresSchema:
|
49
|
+
requiresSchema: true,
|
48
50
|
examples: [
|
49
51
|
{
|
50
52
|
title: "Incorrect",
|
@@ -121,22 +123,33 @@ const RULE_ID = "strict-id-in-types", schema = {
|
|
121
123
|
acceptedIdTypes: ["ID"],
|
122
124
|
exceptions: {},
|
123
125
|
...context.options[0]
|
124
|
-
}
|
126
|
+
};
|
127
|
+
const schema2 = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context);
|
128
|
+
const rootTypeNames = [
|
129
|
+
schema2.getQueryType(),
|
130
|
+
schema2.getMutationType(),
|
131
|
+
schema2.getSubscriptionType()
|
132
|
+
].filter((v) => !!v).map((type) => type.name);
|
133
|
+
const selector = `ObjectTypeDefinition[name.value!=/^(${rootTypeNames.join("|")})$/]`;
|
125
134
|
return {
|
126
|
-
[
|
127
|
-
schema2.getQueryType(),
|
128
|
-
schema2.getMutationType(),
|
129
|
-
schema2.getSubscriptionType()
|
130
|
-
].filter((v) => !!v).map((type) => type.name).join("|")})$/]`](node) {
|
135
|
+
[selector](node) {
|
131
136
|
const typeName = node.name.value;
|
132
|
-
|
137
|
+
const shouldIgnoreNode = _optionalChain([options, 'access', _ => _.exceptions, 'access', _2 => _2.types, 'optionalAccess', _3 => _3.includes, 'call', _4 => _4(typeName)]) || _optionalChain([options, 'access', _5 => _5.exceptions, 'access', _6 => _6.suffixes, 'optionalAccess', _7 => _7.some, 'call', _8 => _8((suffix) => typeName.endsWith(suffix))]);
|
138
|
+
if (shouldIgnoreNode) {
|
133
139
|
return;
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
+
}
|
141
|
+
const validIds = _optionalChain([node, 'access', _9 => _9.fields, 'optionalAccess', _10 => _10.filter, 'call', _11 => _11((field) => {
|
142
|
+
const fieldNode = field.rawNode();
|
143
|
+
const isValidIdName = options.acceptedIdNames.includes(fieldNode.name.value);
|
144
|
+
let isValidIdType = false;
|
145
|
+
if (fieldNode.type.kind === _graphql.Kind.NON_NULL_TYPE && fieldNode.type.type.kind === _graphql.Kind.NAMED_TYPE) {
|
146
|
+
isValidIdType = options.acceptedIdTypes.includes(fieldNode.type.type.name.value);
|
147
|
+
}
|
148
|
+
return isValidIdName && isValidIdType;
|
149
|
+
})]);
|
150
|
+
if (_optionalChain([validIds, 'optionalAccess', _12 => _12.length]) !== 1) {
|
151
|
+
const pluralNamesSuffix = options.acceptedIdNames.length > 1 ? "s" : "";
|
152
|
+
const pluralTypesSuffix = options.acceptedIdTypes.length > 1 ? "s" : "";
|
140
153
|
context.report({
|
141
154
|
node: node.name,
|
142
155
|
message: `${_utilsjs.displayNodeName.call(void 0, node)} must have exactly one non-nullable unique identifier.
|
@@ -3,11 +3,11 @@ var _utilsjs = require('../../utils.js');
|
|
3
3
|
const rule = {
|
4
4
|
meta: {
|
5
5
|
type: "suggestion",
|
6
|
-
hasSuggestions:
|
6
|
+
hasSuggestions: true,
|
7
7
|
docs: {
|
8
8
|
url: "https://the-guild.dev/graphql/eslint/rules/unique-enum-value-names",
|
9
9
|
category: "Schema",
|
10
|
-
recommended:
|
10
|
+
recommended: true,
|
11
11
|
description: `A GraphQL enum type is only valid if all its values are uniquely named.
|
12
12
|
> This rule disallows case-insensitive enum values duplicates too.`,
|
13
13
|
examples: [
|
@@ -42,8 +42,9 @@ const rule = {
|
|
42
42
|
schema: []
|
43
43
|
},
|
44
44
|
create(context) {
|
45
|
+
const selector = [_graphql.Kind.ENUM_TYPE_DEFINITION, _graphql.Kind.ENUM_TYPE_EXTENSION].join(",");
|
45
46
|
return {
|
46
|
-
[
|
47
|
+
[selector](node) {
|
47
48
|
const duplicates = _optionalChain([node, 'access', _ => _.values, 'optionalAccess', _2 => _2.filter, 'call', _3 => _3(
|
48
49
|
(item, index, array) => array.findIndex((v) => v.name.value.toLowerCase() === item.name.value.toLowerCase()) !== index
|
49
50
|
)]);
|
@@ -1,30 +1,38 @@
|
|
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 _nodepath = require('node:path');
|
2
2
|
var _graphql = require('graphql');
|
3
3
|
var _utilsjs = require('../../utils.js');
|
4
|
-
const RULE_ID = "unique-fragment-name"
|
5
|
-
|
6
|
-
|
4
|
+
const RULE_ID = "unique-fragment-name";
|
5
|
+
const checkNode = (context, node, ruleId) => {
|
6
|
+
const documentName = node.name.value;
|
7
|
+
const siblings = _utilsjs.requireGraphQLOperations.call(void 0, ruleId, context);
|
8
|
+
const siblingDocuments = node.kind === _graphql.Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
|
9
|
+
const filepath = context.filename;
|
10
|
+
const conflictingDocuments = siblingDocuments.filter((f) => {
|
11
|
+
const isSameName = _optionalChain([f, 'access', _ => _.document, 'access', _2 => _2.name, 'optionalAccess', _3 => _3.value]) === documentName;
|
12
|
+
const isSamePath = _utilsjs.slash.call(void 0, f.filePath) === _utilsjs.slash.call(void 0, filepath);
|
7
13
|
return isSameName && !isSamePath;
|
8
14
|
});
|
9
|
-
conflictingDocuments.length > 0
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
`)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
}
|
15
|
+
if (conflictingDocuments.length > 0) {
|
16
|
+
context.report({
|
17
|
+
messageId: ruleId,
|
18
|
+
data: {
|
19
|
+
documentName,
|
20
|
+
summary: conflictingDocuments.map((f) => ` ${_nodepath.relative.call(void 0, _utilsjs.CWD, f.filePath.replace(_utilsjs.VIRTUAL_DOCUMENT_REGEX, ""))}`).join("\n")
|
21
|
+
},
|
22
|
+
// @ts-expect-error name will exist
|
23
|
+
node: node.name
|
24
|
+
});
|
25
|
+
}
|
26
|
+
};
|
27
|
+
const rule = {
|
20
28
|
meta: {
|
21
29
|
type: "suggestion",
|
22
30
|
docs: {
|
23
31
|
category: "Operations",
|
24
32
|
description: "Enforce unique fragment names across your project.",
|
25
33
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
26
|
-
requiresSiblings:
|
27
|
-
recommended:
|
34
|
+
requiresSiblings: true,
|
35
|
+
recommended: true,
|
28
36
|
examples: [
|
29
37
|
{
|
30
38
|
title: "Incorrect",
|
@@ -67,8 +75,7 @@ const RULE_ID = "unique-fragment-name", checkNode = exports.checkNode = (context
|
|
67
75
|
]
|
68
76
|
},
|
69
77
|
messages: {
|
70
|
-
[RULE_ID]:
|
71
|
-
{{ summary }}`
|
78
|
+
[RULE_ID]: 'Fragment named "{{ documentName }}" already defined in:\n{{ summary }}'
|
72
79
|
},
|
73
80
|
schema: []
|
74
81
|
},
|
@@ -1,13 +1,14 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _indexjs = require('../unique-fragment-name/index.js');
|
2
|
-
const RULE_ID = "unique-operation-name"
|
2
|
+
const RULE_ID = "unique-operation-name";
|
3
|
+
const rule = {
|
3
4
|
meta: {
|
4
5
|
type: "suggestion",
|
5
6
|
docs: {
|
6
7
|
category: "Operations",
|
7
8
|
description: "Enforce unique operation names across your project.",
|
8
9
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
9
|
-
requiresSiblings:
|
10
|
-
recommended:
|
10
|
+
requiresSiblings: true,
|
11
|
+
recommended: true,
|
11
12
|
examples: [
|
12
13
|
{
|
13
14
|
title: "Incorrect",
|
@@ -54,8 +55,7 @@ const RULE_ID = "unique-operation-name", rule = exports.rule = {
|
|
54
55
|
]
|
55
56
|
},
|
56
57
|
messages: {
|
57
|
-
[RULE_ID]:
|
58
|
-
{{ summary }}`
|
58
|
+
[RULE_ID]: 'Operation named "{{ documentName }}" already defined in:\n{{ summary }}'
|
59
59
|
},
|
60
60
|
schema: []
|
61
61
|
},
|
package/cjs/schema.js
CHANGED
@@ -2,19 +2,23 @@
|
|
2
2
|
var _fastglob = require('fast-glob'); var _fastglob2 = _interopRequireDefault(_fastglob);
|
3
3
|
var _graphql = require('graphql');
|
4
4
|
var _cachejs = require('./cache.js');
|
5
|
-
const schemaCache = new (0, _cachejs.ModuleCache)()
|
5
|
+
const schemaCache = new (0, _cachejs.ModuleCache)();
|
6
|
+
const debug = _debug2.default.call(void 0, "graphql-eslint:schema");
|
6
7
|
function getSchema(project) {
|
7
8
|
const schemaKey = project.schema;
|
8
|
-
if (!schemaKey)
|
9
|
+
if (!schemaKey) {
|
9
10
|
return null;
|
11
|
+
}
|
10
12
|
const cache = schemaCache.get(schemaKey);
|
11
|
-
if (cache)
|
13
|
+
if (cache) {
|
12
14
|
return cache;
|
15
|
+
}
|
13
16
|
debug("Loading schema from %o", project.schema);
|
14
17
|
const opts = {
|
15
18
|
pluckConfig: project.extensions.pluckConfig
|
16
|
-
}
|
17
|
-
|
19
|
+
};
|
20
|
+
const typeDefs = project.loadSchemaSync(project.schema, "DocumentNode", opts);
|
21
|
+
let isFederation = false;
|
18
22
|
_graphql.visit.call(void 0, typeDefs, {
|
19
23
|
SchemaExtension(node) {
|
20
24
|
const linkDirective = _optionalChain([node, 'access', _ => _.directives, 'optionalAccess', _2 => _2.find, 'call', _3 => _3((d) => d.name.value === "link")]);
|
@@ -29,14 +33,16 @@ function getSchema(project) {
|
|
29
33
|
if (isFederation) {
|
30
34
|
const { buildSubgraphSchema } = require("@apollo/subgraph");
|
31
35
|
schema = buildSubgraphSchema({ typeDefs });
|
32
|
-
} else
|
36
|
+
} else {
|
33
37
|
schema = project.loadSchemaSync(project.schema, "GraphQLSchema", opts);
|
38
|
+
}
|
34
39
|
if (debug.enabled) {
|
35
40
|
debug("Schema loaded: %o", schema instanceof _graphql.GraphQLSchema);
|
36
|
-
const schemaPaths = _fastglob2.default.sync(project.schema, { absolute:
|
41
|
+
const schemaPaths = _fastglob2.default.sync(project.schema, { absolute: true });
|
37
42
|
debug("Schema pointers %O", schemaPaths);
|
38
43
|
}
|
39
|
-
|
44
|
+
schemaCache.set(schemaKey, schema);
|
45
|
+
return schema;
|
40
46
|
}
|
41
47
|
|
42
48
|
|