@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.
Files changed (110) hide show
  1. package/cjs/cache.js +6 -2
  2. package/cjs/configs/operations-all.js +2 -2
  3. package/cjs/configs/schema-all.js +2 -2
  4. package/cjs/configs/schema-recommended.js +1 -1
  5. package/cjs/documents.js +13 -7
  6. package/cjs/estree-converter/converter.js +17 -8
  7. package/cjs/estree-converter/utils.js +22 -9
  8. package/cjs/graphql-config.js +13 -6
  9. package/cjs/index.d.cts +18 -4
  10. package/cjs/meta.js +1 -1
  11. package/cjs/parser.js +36 -9
  12. package/cjs/processor.js +48 -20
  13. package/cjs/rules/alphabetize/index.js +99 -47
  14. package/cjs/rules/description-style/index.js +10 -6
  15. package/cjs/rules/graphql-js-validation.js +142 -108
  16. package/cjs/rules/index.d.cts +18 -4
  17. package/cjs/rules/input-name/index.js +51 -38
  18. package/cjs/rules/lone-executable-definition/index.js +15 -6
  19. package/cjs/rules/match-document-filename/index.d.cts +4 -3
  20. package/cjs/rules/match-document-filename/index.js +63 -37
  21. package/cjs/rules/naming-convention/index.d.cts +6 -10
  22. package/cjs/rules/naming-convention/index.js +179 -82
  23. package/cjs/rules/no-anonymous-operations/index.js +8 -5
  24. package/cjs/rules/no-deprecated/index.js +27 -13
  25. package/cjs/rules/no-duplicate-fields/index.js +15 -8
  26. package/cjs/rules/no-hashtag-description/index.js +18 -10
  27. package/cjs/rules/no-one-place-fragments/index.js +17 -10
  28. package/cjs/rules/no-root-type/index.js +15 -8
  29. package/cjs/rules/no-scalar-result-type-on-mutation/index.js +20 -12
  30. package/cjs/rules/no-typename-prefix/index.js +25 -21
  31. package/cjs/rules/no-unreachable-types/index.js +34 -17
  32. package/cjs/rules/no-unused-fields/index.js +56 -30
  33. package/cjs/rules/relay-arguments/index.js +31 -13
  34. package/cjs/rules/relay-connection-types/index.js +31 -9
  35. package/cjs/rules/relay-edge-types/index.js +84 -41
  36. package/cjs/rules/relay-page-info/index.js +31 -14
  37. package/cjs/rules/require-deprecation-date/index.js +20 -9
  38. package/cjs/rules/require-deprecation-reason/index.js +8 -5
  39. package/cjs/rules/require-description/index.d.cts +79 -13
  40. package/cjs/rules/require-description/index.js +67 -49
  41. package/cjs/rules/require-field-of-type-query-in-mutation-result/index.js +21 -10
  42. package/cjs/rules/require-import-fragment/index.js +20 -11
  43. package/cjs/rules/require-nullable-fields-with-oneof/index.js +12 -5
  44. package/cjs/rules/require-nullable-result-in-root/index.js +32 -27
  45. package/cjs/rules/require-selections/index.js +88 -46
  46. package/cjs/rules/require-type-pattern-with-oneof/index.js +14 -10
  47. package/cjs/rules/selection-set-depth/index.js +19 -10
  48. package/cjs/rules/strict-id-in-types/index.js +32 -19
  49. package/cjs/rules/unique-enum-value-names/index.js +4 -3
  50. package/cjs/rules/unique-fragment-name/index.js +25 -18
  51. package/cjs/rules/unique-operation-name/index.js +5 -5
  52. package/cjs/schema.js +14 -8
  53. package/cjs/siblings.js +60 -32
  54. package/cjs/utils.js +23 -9
  55. package/esm/cache.js +6 -2
  56. package/esm/configs/operations-all.js +2 -2
  57. package/esm/configs/schema-all.js +2 -2
  58. package/esm/configs/schema-recommended.js +1 -1
  59. package/esm/documents.js +13 -7
  60. package/esm/estree-converter/converter.js +17 -8
  61. package/esm/estree-converter/utils.js +22 -9
  62. package/esm/graphql-config.js +13 -6
  63. package/esm/index.d.ts +18 -4
  64. package/esm/meta.js +1 -1
  65. package/esm/parser.js +36 -9
  66. package/esm/processor.js +48 -20
  67. package/esm/rules/alphabetize/index.js +99 -47
  68. package/esm/rules/description-style/index.js +10 -6
  69. package/esm/rules/graphql-js-validation.js +142 -108
  70. package/esm/rules/index.d.ts +18 -4
  71. package/esm/rules/input-name/index.js +51 -38
  72. package/esm/rules/lone-executable-definition/index.js +15 -6
  73. package/esm/rules/match-document-filename/index.d.ts +4 -3
  74. package/esm/rules/match-document-filename/index.js +63 -37
  75. package/esm/rules/naming-convention/index.d.ts +6 -10
  76. package/esm/rules/naming-convention/index.js +179 -82
  77. package/esm/rules/no-anonymous-operations/index.js +8 -5
  78. package/esm/rules/no-deprecated/index.js +27 -13
  79. package/esm/rules/no-duplicate-fields/index.js +15 -8
  80. package/esm/rules/no-hashtag-description/index.js +18 -10
  81. package/esm/rules/no-one-place-fragments/index.js +17 -10
  82. package/esm/rules/no-root-type/index.js +15 -8
  83. package/esm/rules/no-scalar-result-type-on-mutation/index.js +20 -12
  84. package/esm/rules/no-typename-prefix/index.js +25 -21
  85. package/esm/rules/no-unreachable-types/index.js +34 -17
  86. package/esm/rules/no-unused-fields/index.js +56 -30
  87. package/esm/rules/relay-arguments/index.js +31 -13
  88. package/esm/rules/relay-connection-types/index.js +31 -9
  89. package/esm/rules/relay-edge-types/index.js +84 -41
  90. package/esm/rules/relay-page-info/index.js +31 -14
  91. package/esm/rules/require-deprecation-date/index.js +20 -9
  92. package/esm/rules/require-deprecation-reason/index.js +8 -5
  93. package/esm/rules/require-description/index.d.ts +79 -13
  94. package/esm/rules/require-description/index.js +67 -49
  95. package/esm/rules/require-field-of-type-query-in-mutation-result/index.js +21 -10
  96. package/esm/rules/require-import-fragment/index.js +20 -11
  97. package/esm/rules/require-nullable-fields-with-oneof/index.js +12 -5
  98. package/esm/rules/require-nullable-result-in-root/index.js +32 -27
  99. package/esm/rules/require-selections/index.js +88 -46
  100. package/esm/rules/require-type-pattern-with-oneof/index.js +14 -10
  101. package/esm/rules/selection-set-depth/index.js +19 -10
  102. package/esm/rules/strict-id-in-types/index.js +32 -19
  103. package/esm/rules/unique-enum-value-names/index.js +4 -3
  104. package/esm/rules/unique-fragment-name/index.js +25 -18
  105. package/esm/rules/unique-operation-name/index.js +5 -5
  106. package/esm/schema.js +15 -8
  107. package/esm/siblings.js +60 -32
  108. package/esm/utils.js +23 -9
  109. package/index.browser.js +1871 -1160
  110. package/package.json +1 -1
@@ -15,7 +15,9 @@ import {
15
15
  requireGraphQLOperations,
16
16
  requireGraphQLSchema
17
17
  } from "../../utils.js";
18
- const RULE_ID = "require-selections", DEFAULT_ID_FIELD_NAME = "id", schema = {
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: !1,
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
- }, rule = {
43
+ };
44
+ const rule = {
42
45
  meta: {
43
46
  type: "suggestion",
44
- hasSuggestions: !0,
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: !0,
50
- requiresSiblings: !0,
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: !0,
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]: `Field{{ pluralSuffix }} {{ fieldName }} must be selected when it's available on a type.
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 = requireGraphQLSchema(RULE_ID, context), siblings = requireGraphQLOperations(RULE_ID, context), { fieldName = DEFAULT_ID_FIELD_NAME, requireAllFields } = context.options[0] || {}, idNames = asArray(fieldName), selector = "SelectionSet[parent.kind!=/(^OperationDefinition|InlineFragment)$/]", typeInfo = new TypeInfo(schema2);
113
+ const schema2 = requireGraphQLSchema(RULE_ID, context);
114
+ const siblings = requireGraphQLOperations(RULE_ID, context);
115
+ const { fieldName = DEFAULT_ID_FIELD_NAME, requireAllFields } = context.options[0] || {};
116
+ const idNames = asArray(fieldName);
117
+ const selector = "SelectionSet[parent.kind!=/(^OperationDefinition|InlineFragment)$/]";
118
+ const typeInfo = new TypeInfo(schema2);
112
119
  function checkFragments(node) {
113
120
  for (const selection of node.selections) {
114
- if (selection.kind !== Kind.FRAGMENT_SPREAD)
121
+ if (selection.kind !== 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
- const checkedFragmentSpreads = /* @__PURE__ */ new Set(), visitor = visitWithTypeInfo(typeInfo, {
127
+ }
128
+ const checkedFragmentSpreads = /* @__PURE__ */ new Set();
129
+ const visitor = visitWithTypeInfo(typeInfo, {
120
130
  SelectionSet(node2, key, _parent) {
121
131
  const parent = _parent;
122
- parent.kind === Kind.FRAGMENT_DEFINITION ? checkedFragmentSpreads.add(parent.name.value) : parent.kind !== Kind.INLINE_FRAGMENT && checkSelections(
123
- node2,
124
- typeInfo.getType(),
125
- selection.loc.start,
126
- parent,
127
- checkedFragmentSpreads
128
- );
132
+ if (parent.kind === Kind.FRAGMENT_DEFINITION) {
133
+ checkedFragmentSpreads.add(parent.name.value);
134
+ } else if (parent.kind !== 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
  visit(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 = getBaseType(type);
136
- if (rawType instanceof GraphQLObjectType || rawType instanceof GraphQLInterfaceType)
150
+ if (rawType instanceof GraphQLObjectType || rawType instanceof GraphQLInterfaceType) {
137
151
  checkFields(rawType);
138
- else if (rawType instanceof GraphQLUnionType)
152
+ } else if (rawType instanceof GraphQLUnionType) {
139
153
  for (const selection of node.selections) {
140
154
  const types = rawType.getTypes();
141
155
  if (selection.kind === Kind.INLINE_FRAGMENT) {
142
156
  const t = types.find((t2) => t2.name === selection.typeCondition.name.value);
143
- t && checkFields(t);
157
+ if (t) {
158
+ checkFields(t);
159
+ }
144
160
  } else if (selection.kind === Kind.FRAGMENT_SPREAD) {
145
161
  const [foundSpread] = siblings.getFragment(selection.name.value);
146
162
  if (!foundSpread) return;
147
- const fragmentSpread = foundSpread.document, t = fragmentSpread.typeCondition.name.value === rawType.name ? rawType : types.find((t2) => t2.name === fragmentSpread.typeCondition.name.value);
148
- checkedFragmentSpreads.add(fragmentSpread.name.value), checkSelections(fragmentSpread.selectionSet, t, loc, parent, checkedFragmentSpreads);
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
- if (idNames.some((name) => fields[name]))
154
- if (checkFragments(node), requireAllFields)
155
- for (const idName of idNames)
156
- report([idName]);
157
- else
158
- report(idNames);
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 === Kind.FIELD)
164
- return selection.alias && idNames2.includes(selection.alias.value) ? !0 : idNames2.includes(selection.name.value);
165
- if (selection.kind === Kind.INLINE_FRAGMENT)
188
+ if (selection.kind === 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 === Kind.INLINE_FRAGMENT) {
166
195
  return hasIdField(selection.selectionSet);
196
+ }
167
197
  if (selection.kind === Kind.FRAGMENT_SPREAD) {
168
198
  const [foundSpread] = siblings.getFragment(selection.name.value);
169
199
  if (foundSpread) {
170
200
  const fragmentSpread = foundSpread.document;
171
- return checkedFragmentSpreads.add(fragmentSpread.name.value), hasIdField(fragmentSpread.selectionSet);
201
+ checkedFragmentSpreads.add(fragmentSpread.name.value);
202
+ return hasIdField(fragmentSpread.selectionSet);
172
203
  }
173
204
  }
174
- return !1;
205
+ return false;
175
206
  });
176
207
  }
177
- if (hasIdField(node))
208
+ const hasId = hasIdField(node);
209
+ if (hasId) {
178
210
  return;
211
+ }
179
212
  const fieldName2 = englishJoinWords(
180
213
  idNames2.map((name) => `\`${(parent.alias || parent.name).value}.${name}\``)
181
- ), pluralSuffix = idNames2.length > 1 ? "s" : "", addition = checkedFragmentSpreads.size === 0 ? "" : ` or add to used fragment${checkedFragmentSpreads.size > 1 ? "s" : ""} ${englishJoinWords([...checkedFragmentSpreads].map((name) => `\`${name}\``))}`, problem = {
214
+ );
215
+ const pluralSuffix = idNames2.length > 1 ? "s" : "";
216
+ const addition = checkedFragmentSpreads.size === 0 ? "" : ` or add to used fragment${checkedFragmentSpreads.size > 1 ? "s" : ""} ${englishJoinWords([...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 && (problem.suggest = idNames2.map((idName) => ({
191
- desc: `Add \`${idName}\` selection`,
192
- fix: (fixer) => {
193
- let insertNode = node.selections[0];
194
- return insertNode = insertNode.kind === Kind.INLINE_FRAGMENT ? insertNode.selectionSet.selections[0] : insertNode, fixer.insertTextBefore(insertNode, `${idName} `);
195
- }
196
- }))), context.report(problem);
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 === 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
- typeInfo2.gqlType && checkSelections(node, typeInfo2.gqlType, node.loc.start, node.parent);
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
  import { displayNodeName } from "../../utils.js";
2
- const RULE_ID = "require-type-pattern-with-oneof", rule = {
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 = {
44
45
  parent
45
46
  }) {
46
47
  const requiredFields = ["error", "ok"];
47
- for (const fieldName of requiredFields)
48
- parent.fields?.some((field) => field.name.value === fieldName) || context.report({
49
- node: parent.name,
50
- messageId: RULE_ID,
51
- data: {
52
- nodeName: displayNodeName(parent),
53
- fieldName
54
- }
55
- });
48
+ for (const fieldName of requiredFields) {
49
+ if (!parent.fields?.some((field) => field.name.value === fieldName)) {
50
+ context.report({
51
+ node: parent.name,
52
+ messageId: RULE_ID,
53
+ data: {
54
+ nodeName: displayNodeName(parent),
55
+ fieldName
56
+ }
57
+ });
58
+ }
59
+ }
56
60
  }
57
61
  };
58
62
  }
@@ -1,13 +1,14 @@
1
1
  import { Kind } from "graphql";
2
2
  import depthLimit from "graphql-depth-limit";
3
3
  import { ARRAY_DEFAULT_OPTIONS, logger, requireGraphQLOperations } from "../../utils.js";
4
- const RULE_ID = "selection-set-depth", schema = {
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: !1,
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: ARRAY_DEFAULT_OPTIONS
17
18
  }
18
19
  }
19
- }, rule = {
20
+ };
21
+ const rule = {
20
22
  meta: {
21
23
  type: "suggestion",
22
- hasSuggestions: !0,
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: !0,
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: !0,
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], checkFn = depthLimit(maxDepth, { ignore });
85
+ const { maxDepth, ignore = [] } = context.options[0];
86
+ const checkFn = depthLimit(maxDepth, { ignore });
84
87
  return {
85
88
  "OperationDefinition, FragmentDefinition"(node) {
86
89
  try {
87
- const rawNode = node.rawNode(), fragmentsInUse = siblings ? siblings.getFragmentsInUse(rawNode) : [], document = {
90
+ const rawNode = node.rawNode();
91
+ const fragmentsInUse = siblings ? siblings.getFragmentsInUse(rawNode) : [];
92
+ const document = {
88
93
  kind: 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], token = context.sourceCode.getAncestors(node)[0].tokens.find(
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 foundNode = context.getSourceCode().getNodeByRangeIndex(token.range[0]), parentNode = foundNode.parent.parent;
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 @@ import {
5
5
  englishJoinWords,
6
6
  requireGraphQLSchema
7
7
  } from "../../utils.js";
8
- const RULE_ID = "strict-id-in-types", schema = {
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: !1,
14
+ additionalProperties: false,
14
15
  properties: {
15
16
  acceptedIdNames: {
16
17
  ...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: !1,
26
+ additionalProperties: false,
26
27
  properties: {
27
28
  types: {
28
29
  ...ARRAY_DEFAULT_OPTIONS,
@@ -36,15 +37,16 @@ const RULE_ID = "strict-id-in-types", schema = {
36
37
  }
37
38
  }
38
39
  }
39
- }, rule = {
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: !0,
47
+ recommended: true,
46
48
  url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
47
- requiresSchema: !0,
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
- }, schema2 = requireGraphQLSchema(RULE_ID, context);
126
+ };
127
+ const schema2 = requireGraphQLSchema(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
- [`ObjectTypeDefinition[name.value!=/^(${[
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
- if (options.exceptions.types?.includes(typeName) || options.exceptions.suffixes?.some((suffix) => typeName.endsWith(suffix)))
137
+ const shouldIgnoreNode = options.exceptions.types?.includes(typeName) || options.exceptions.suffixes?.some((suffix) => typeName.endsWith(suffix));
138
+ if (shouldIgnoreNode) {
133
139
  return;
134
- if (node.fields?.filter((field) => {
135
- const fieldNode = field.rawNode(), isValidIdName = options.acceptedIdNames.includes(fieldNode.name.value);
136
- let isValidIdType = !1;
137
- return fieldNode.type.kind === Kind.NON_NULL_TYPE && fieldNode.type.type.kind === Kind.NAMED_TYPE && (isValidIdType = options.acceptedIdTypes.includes(fieldNode.type.type.name.value)), isValidIdName && isValidIdType;
138
- })?.length !== 1) {
139
- const pluralNamesSuffix = options.acceptedIdNames.length > 1 ? "s" : "", pluralTypesSuffix = options.acceptedIdTypes.length > 1 ? "s" : "";
140
+ }
141
+ const validIds = node.fields?.filter((field) => {
142
+ const fieldNode = field.rawNode();
143
+ const isValidIdName = options.acceptedIdNames.includes(fieldNode.name.value);
144
+ let isValidIdType = false;
145
+ if (fieldNode.type.kind === Kind.NON_NULL_TYPE && fieldNode.type.type.kind === Kind.NAMED_TYPE) {
146
+ isValidIdType = options.acceptedIdTypes.includes(fieldNode.type.type.name.value);
147
+ }
148
+ return isValidIdName && isValidIdType;
149
+ });
150
+ if (validIds?.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: `${displayNodeName(node)} must have exactly one non-nullable unique identifier.
@@ -3,11 +3,11 @@ import { getNodeName } from "../../utils.js";
3
3
  const rule = {
4
4
  meta: {
5
5
  type: "suggestion",
6
- hasSuggestions: !0,
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: !0,
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 = [Kind.ENUM_TYPE_DEFINITION, Kind.ENUM_TYPE_EXTENSION].join(",");
45
46
  return {
46
- [[Kind.ENUM_TYPE_DEFINITION, Kind.ENUM_TYPE_EXTENSION].join(",")](node) {
47
+ [selector](node) {
47
48
  const duplicates = node.values?.filter(
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
  import { relative } from "node:path";
2
2
  import { Kind } from "graphql";
3
3
  import { CWD, requireGraphQLOperations, slash, VIRTUAL_DOCUMENT_REGEX } from "../../utils.js";
4
- const RULE_ID = "unique-fragment-name", checkNode = (context, node, ruleId) => {
5
- const documentName = node.name.value, siblings = requireGraphQLOperations(ruleId, context), siblingDocuments = node.kind === Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName), filepath = context.filename, conflictingDocuments = siblingDocuments.filter((f) => {
6
- const isSameName = f.document.name?.value === documentName, isSamePath = slash(f.filePath) === slash(filepath);
4
+ const RULE_ID = "unique-fragment-name";
5
+ const checkNode = (context, node, ruleId) => {
6
+ const documentName = node.name.value;
7
+ const siblings = requireGraphQLOperations(ruleId, context);
8
+ const siblingDocuments = node.kind === Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
9
+ const filepath = context.filename;
10
+ const conflictingDocuments = siblingDocuments.filter((f) => {
11
+ const isSameName = f.document.name?.value === documentName;
12
+ const isSamePath = slash(f.filePath) === slash(filepath);
7
13
  return isSameName && !isSamePath;
8
14
  });
9
- conflictingDocuments.length > 0 && context.report({
10
- messageId: ruleId,
11
- data: {
12
- documentName,
13
- summary: conflictingDocuments.map((f) => ` ${relative(CWD, f.filePath.replace(VIRTUAL_DOCUMENT_REGEX, ""))}`).join(`
14
- `)
15
- },
16
- // @ts-expect-error name will exist
17
- node: node.name
18
- });
19
- }, rule = {
15
+ if (conflictingDocuments.length > 0) {
16
+ context.report({
17
+ messageId: ruleId,
18
+ data: {
19
+ documentName,
20
+ summary: conflictingDocuments.map((f) => ` ${relative(CWD, f.filePath.replace(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: !0,
27
- recommended: !0,
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 = (context, node, ruleId) => {
67
75
  ]
68
76
  },
69
77
  messages: {
70
- [RULE_ID]: `Fragment named "{{ documentName }}" already defined in:
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
  import { checkNode } from "../unique-fragment-name/index.js";
2
- const RULE_ID = "unique-operation-name", rule = {
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: !0,
10
- recommended: !0,
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 = {
54
55
  ]
55
56
  },
56
57
  messages: {
57
- [RULE_ID]: `Operation named "{{ documentName }}" already defined in:
58
- {{ summary }}`
58
+ [RULE_ID]: 'Operation named "{{ documentName }}" already defined in:\n{{ summary }}'
59
59
  },
60
60
  schema: []
61
61
  },
package/esm/schema.js CHANGED
@@ -3,19 +3,24 @@ import debugFactory from "debug";
3
3
  import fg from "fast-glob";
4
4
  import { BREAK, GraphQLSchema, visit } from "graphql";
5
5
  import { ModuleCache } from "./cache.js";
6
- const schemaCache = new ModuleCache(), debug = debugFactory("graphql-eslint:schema"), require2 = createRequire(import.meta.url);
6
+ const schemaCache = new ModuleCache();
7
+ const debug = debugFactory("graphql-eslint:schema");
8
+ const require2 = createRequire(import.meta.url);
7
9
  function getSchema(project) {
8
10
  const schemaKey = project.schema;
9
- if (!schemaKey)
11
+ if (!schemaKey) {
10
12
  return null;
13
+ }
11
14
  const cache = schemaCache.get(schemaKey);
12
- if (cache)
15
+ if (cache) {
13
16
  return cache;
17
+ }
14
18
  debug("Loading schema from %o", project.schema);
15
19
  const opts = {
16
20
  pluckConfig: project.extensions.pluckConfig
17
- }, typeDefs = project.loadSchemaSync(project.schema, "DocumentNode", opts);
18
- let isFederation = !1;
21
+ };
22
+ const typeDefs = project.loadSchemaSync(project.schema, "DocumentNode", opts);
23
+ let isFederation = false;
19
24
  visit(typeDefs, {
20
25
  SchemaExtension(node) {
21
26
  const linkDirective = node.directives?.find((d) => d.name.value === "link");
@@ -30,14 +35,16 @@ function getSchema(project) {
30
35
  if (isFederation) {
31
36
  const { buildSubgraphSchema } = require2("@apollo/subgraph");
32
37
  schema = buildSubgraphSchema({ typeDefs });
33
- } else
38
+ } else {
34
39
  schema = project.loadSchemaSync(project.schema, "GraphQLSchema", opts);
40
+ }
35
41
  if (debug.enabled) {
36
42
  debug("Schema loaded: %o", schema instanceof GraphQLSchema);
37
- const schemaPaths = fg.sync(project.schema, { absolute: !0 });
43
+ const schemaPaths = fg.sync(project.schema, { absolute: true });
38
44
  debug("Schema pointers %O", schemaPaths);
39
45
  }
40
- return schemaCache.set(schemaKey, schema), schema;
46
+ schemaCache.set(schemaKey, schema);
47
+ return schema;
41
48
  }
42
49
  export {
43
50
  getSchema