@graphql-eslint/eslint-plugin 3.4.0 → 3.7.0-alpha-a9b0ecf.0

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.
@@ -37,6 +37,17 @@ type someTypeName {
37
37
  }
38
38
  ```
39
39
 
40
+ ### Correct
41
+
42
+ ```graphql
43
+ # eslint @graphql-eslint/require-description: ['error', { OperationDefinition: true }]
44
+
45
+ # Enforce description on operations
46
+ query {
47
+ foo
48
+ }
49
+ ```
50
+
40
51
  ## Config Schema
41
52
 
42
53
  The schema defines the following properties:
@@ -84,6 +95,10 @@ Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October
84
95
 
85
96
  Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#ObjectTypeDefinition).
86
97
 
98
+ ### `OperationDefinition` (boolean)
99
+
100
+ Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#OperationDefinition).
101
+
87
102
  ### `ScalarTypeDefinition` (boolean)
88
103
 
89
104
  Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#ScalarTypeDefinition).
@@ -23,7 +23,7 @@ type User {
23
23
  }
24
24
 
25
25
  # Query
26
- query user {
26
+ query {
27
27
  user {
28
28
  name
29
29
  }
@@ -42,7 +42,7 @@ type User {
42
42
  }
43
43
 
44
44
  # Query
45
- query user {
45
+ query {
46
46
  user {
47
47
  id
48
48
  name
@@ -4,7 +4,7 @@
4
4
 
5
5
  - Category: `Schema`
6
6
  - Rule name: `@graphql-eslint/strict-id-in-types`
7
- - Requires GraphQL Schema: `false` [ℹ️](../../README.md#extended-linting-rules-with-graphql-schema)
7
+ - Requires GraphQL Schema: `true` [ℹ️](../../README.md#extended-linting-rules-with-graphql-schema)
8
8
  - Requires GraphQL Operations: `false` [ℹ️](../../README.md#extended-linting-rules-with-siblings-operations)
9
9
 
10
10
  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.
package/index.js CHANGED
@@ -2625,20 +2625,21 @@ const rule$g = {
2625
2625
  },
2626
2626
  };
2627
2627
 
2628
- const REQUIRE_DESCRIPTION_ERROR = 'REQUIRE_DESCRIPTION_ERROR';
2628
+ const RULE_ID$2 = 'require-description';
2629
2629
  const ALLOWED_KINDS$1 = [
2630
2630
  ...TYPES_KINDS,
2631
2631
  graphql.Kind.FIELD_DEFINITION,
2632
2632
  graphql.Kind.INPUT_VALUE_DEFINITION,
2633
2633
  graphql.Kind.ENUM_VALUE_DEFINITION,
2634
2634
  graphql.Kind.DIRECTIVE_DEFINITION,
2635
+ graphql.Kind.OPERATION_DEFINITION,
2635
2636
  ];
2636
2637
  const rule$h = {
2637
2638
  meta: {
2638
2639
  docs: {
2639
2640
  category: 'Schema',
2640
2641
  description: 'Enforce descriptions in your type definitions.',
2641
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md',
2642
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
2642
2643
  examples: [
2643
2644
  {
2644
2645
  title: 'Incorrect',
@@ -2662,6 +2663,16 @@ const rule$h = {
2662
2663
  """
2663
2664
  name: String
2664
2665
  }
2666
+ `,
2667
+ },
2668
+ {
2669
+ title: 'Correct',
2670
+ usage: [{ OperationDefinition: true }],
2671
+ code: /* GraphQL */ `
2672
+ # Enforce description on operations
2673
+ query {
2674
+ foo
2675
+ }
2665
2676
  `,
2666
2677
  },
2667
2678
  ],
@@ -2675,7 +2686,7 @@ const rule$h = {
2675
2686
  },
2676
2687
  type: 'suggestion',
2677
2688
  messages: {
2678
- [REQUIRE_DESCRIPTION_ERROR]: 'Description is required for nodes of type "{{ nodeType }}"',
2689
+ [RULE_ID$2]: 'Description is required for nodes of type "{{ nodeType }}"',
2679
2690
  },
2680
2691
  schema: {
2681
2692
  type: 'array',
@@ -2702,7 +2713,7 @@ const rule$h = {
2702
2713
  },
2703
2714
  },
2704
2715
  create(context) {
2705
- const { types, ...restOptions } = context.options[0];
2716
+ const { types, ...restOptions } = context.options[0] || {};
2706
2717
  const kinds = new Set(types ? TYPES_KINDS : []);
2707
2718
  for (const [kind, isEnabled] of Object.entries(restOptions)) {
2708
2719
  if (isEnabled) {
@@ -2716,11 +2727,26 @@ const rule$h = {
2716
2727
  return {
2717
2728
  [selector](node) {
2718
2729
  var _a;
2719
- const description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value) || '';
2720
- if (description.trim().length === 0) {
2730
+ let description = '';
2731
+ const isOperation = node.kind === graphql.Kind.OPERATION_DEFINITION;
2732
+ if (isOperation) {
2733
+ const rawNode = node.rawNode();
2734
+ const { prev, line } = rawNode.loc.startToken;
2735
+ if (prev.kind === graphql.TokenKind.COMMENT) {
2736
+ const value = prev.value.trim();
2737
+ const linesBefore = line - prev.line;
2738
+ if (!value.startsWith('eslint') && linesBefore === 1) {
2739
+ description = value;
2740
+ }
2741
+ }
2742
+ }
2743
+ else {
2744
+ description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value.trim()) || '';
2745
+ }
2746
+ if (description.length === 0) {
2721
2747
  context.report({
2722
- loc: getLocation(node.name.loc, node.name.value),
2723
- messageId: REQUIRE_DESCRIPTION_ERROR,
2748
+ loc: isOperation ? getLocation(node.loc, node.operation) : getLocation(node.name.loc, node.name.value),
2749
+ messageId: RULE_ID$2,
2724
2750
  data: {
2725
2751
  nodeType: node.kind,
2726
2752
  },
@@ -2896,7 +2922,8 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2896
2922
  }
2897
2923
  };
2898
2924
 
2899
- const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2925
+ const RULE_ID$3 = 'require-id-when-available';
2926
+ const MESSAGE_ID = 'REQUIRE_ID_WHEN_AVAILABLE';
2900
2927
  const DEFAULT_ID_FIELD_NAME = 'id';
2901
2928
  const rule$j = {
2902
2929
  meta: {
@@ -2904,7 +2931,7 @@ const rule$j = {
2904
2931
  docs: {
2905
2932
  category: 'Operations',
2906
2933
  description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
2907
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-id-when-available.md',
2934
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$3}.md`,
2908
2935
  requiresSchema: true,
2909
2936
  requiresSiblings: true,
2910
2937
  examples: [
@@ -2918,7 +2945,7 @@ const rule$j = {
2918
2945
  }
2919
2946
 
2920
2947
  # Query
2921
- query user {
2948
+ query {
2922
2949
  user {
2923
2950
  name
2924
2951
  }
@@ -2935,7 +2962,7 @@ const rule$j = {
2935
2962
  }
2936
2963
 
2937
2964
  # Query
2938
- query user {
2965
+ query {
2939
2966
  user {
2940
2967
  id
2941
2968
  name
@@ -2947,7 +2974,7 @@ const rule$j = {
2947
2974
  recommended: true,
2948
2975
  },
2949
2976
  messages: {
2950
- [REQUIRE_ID_WHEN_AVAILABLE]: [
2977
+ [MESSAGE_ID]: [
2951
2978
  `Field {{ fieldName }} must be selected when it's available on a type. Please make sure to include it in your selection set!`,
2952
2979
  `If you are using fragments, make sure that all used fragments {{ checkedFragments }}specifies the field {{ fieldName }}.`,
2953
2980
  ].join('\n'),
@@ -2978,14 +3005,16 @@ const rule$j = {
2978
3005
  },
2979
3006
  },
2980
3007
  create(context) {
2981
- requireGraphQLSchemaFromContext('require-id-when-available', context);
2982
- const siblings = requireSiblingsOperations('require-id-when-available', context);
3008
+ requireGraphQLSchemaFromContext(RULE_ID$3, context);
3009
+ const siblings = requireSiblingsOperations(RULE_ID$3, context);
2983
3010
  const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
2984
- const idNames = Array.isArray(fieldName) ? fieldName : [fieldName];
3011
+ const idNames = utils.asArray(fieldName);
2985
3012
  const isFound = (s) => s.kind === graphql.Kind.FIELD && idNames.includes(s.name.value);
3013
+ // Skip check selections in FragmentDefinition
3014
+ const selector = 'OperationDefinition SelectionSet[parent.kind!=OperationDefinition]';
2986
3015
  return {
2987
- SelectionSet(node) {
2988
- var _a, _b;
3016
+ [selector](node) {
3017
+ var _a;
2989
3018
  const typeInfo = node.typeInfo();
2990
3019
  if (!typeInfo.gqlType) {
2991
3020
  return;
@@ -3002,53 +3031,50 @@ const rule$j = {
3002
3031
  return;
3003
3032
  }
3004
3033
  const checkedFragmentSpreads = new Set();
3005
- let found = false;
3006
3034
  for (const selection of node.selections) {
3007
3035
  if (isFound(selection)) {
3008
- found = true;
3036
+ return;
3009
3037
  }
3010
- else if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
3011
- found = (_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.selections.some(s => isFound(s));
3038
+ if (selection.kind === graphql.Kind.INLINE_FRAGMENT && selection.selectionSet.selections.some(isFound)) {
3039
+ return;
3012
3040
  }
3013
- else if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
3041
+ if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
3014
3042
  const [foundSpread] = siblings.getFragment(selection.name.value);
3015
3043
  if (foundSpread) {
3016
3044
  checkedFragmentSpreads.add(foundSpread.document.name.value);
3017
- found = (_b = foundSpread.document.selectionSet) === null || _b === void 0 ? void 0 : _b.selections.some(s => isFound(s));
3045
+ if (foundSpread.document.selectionSet.selections.some(isFound)) {
3046
+ return;
3047
+ }
3018
3048
  }
3019
3049
  }
3020
- if (found) {
3021
- break;
3022
- }
3023
3050
  }
3024
3051
  const { parent } = node;
3025
- const hasIdFieldInInterfaceSelectionSet = parent &&
3026
- parent.kind === graphql.Kind.INLINE_FRAGMENT &&
3027
- parent.parent &&
3028
- parent.parent.kind === graphql.Kind.SELECTION_SET &&
3029
- parent.parent.selections.some(s => isFound(s));
3030
- if (!found && !hasIdFieldInInterfaceSelectionSet) {
3031
- context.report({
3032
- loc: getLocation(node.loc),
3033
- messageId: REQUIRE_ID_WHEN_AVAILABLE,
3034
- data: {
3035
- checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
3036
- fieldName: idNames.map(name => `"${name}"`).join(' or '),
3037
- },
3038
- });
3052
+ const hasIdFieldInInterfaceSelectionSet = (parent === null || parent === void 0 ? void 0 : parent.kind) === graphql.Kind.INLINE_FRAGMENT &&
3053
+ ((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.kind) === graphql.Kind.SELECTION_SET &&
3054
+ parent.parent.selections.some(isFound);
3055
+ if (hasIdFieldInInterfaceSelectionSet) {
3056
+ return;
3039
3057
  }
3058
+ context.report({
3059
+ loc: getLocation(node.loc),
3060
+ messageId: MESSAGE_ID,
3061
+ data: {
3062
+ checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
3063
+ fieldName: idNames.map(name => `"${name}"`).join(' or '),
3064
+ },
3065
+ });
3040
3066
  },
3041
3067
  };
3042
3068
  },
3043
3069
  };
3044
3070
 
3045
- const RULE_ID$2 = 'selection-set-depth';
3071
+ const RULE_ID$4 = 'selection-set-depth';
3046
3072
  const rule$k = {
3047
3073
  meta: {
3048
3074
  docs: {
3049
3075
  category: 'Operations',
3050
3076
  description: `Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://github.com/stems/graphql-depth-limit).`,
3051
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
3077
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$4}.md`,
3052
3078
  requiresSiblings: true,
3053
3079
  examples: [
3054
3080
  {
@@ -3122,10 +3148,10 @@ const rule$k = {
3122
3148
  create(context) {
3123
3149
  let siblings = null;
3124
3150
  try {
3125
- siblings = requireSiblingsOperations(RULE_ID$2, context);
3151
+ siblings = requireSiblingsOperations(RULE_ID$4, context);
3126
3152
  }
3127
3153
  catch (e) {
3128
- logger.warn(`Rule "${RULE_ID$2}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
3154
+ logger.warn(`Rule "${RULE_ID$4}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
3129
3155
  }
3130
3156
  const { maxDepth } = context.options[0];
3131
3157
  const ignore = context.options[0].ignore || [];
@@ -3150,35 +3176,33 @@ const rule$k = {
3150
3176
  });
3151
3177
  }
3152
3178
  catch (e) {
3153
- logger.warn(`Rule "${RULE_ID$2}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
3179
+ logger.warn(`Rule "${RULE_ID$4}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
3154
3180
  }
3155
3181
  },
3156
3182
  };
3157
3183
  },
3158
3184
  };
3159
3185
 
3160
- const shouldIgnoreNode = ({ node, exceptions }) => {
3161
- const rawNode = node.rawNode();
3162
- if (exceptions.types && exceptions.types.includes(rawNode.name.value)) {
3163
- return true;
3164
- }
3165
- if (exceptions.suffixes && exceptions.suffixes.some(suffix => rawNode.name.value.endsWith(suffix))) {
3166
- return true;
3167
- }
3168
- return false;
3169
- };
3186
+ const RULE_ID$5 = 'strict-id-in-types';
3170
3187
  const rule$l = {
3171
3188
  meta: {
3172
3189
  type: 'suggestion',
3173
3190
  docs: {
3174
- 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.',
3191
+ 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.`,
3175
3192
  category: 'Schema',
3176
3193
  recommended: true,
3177
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/strict-id-in-types.md',
3194
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$5}.md`,
3195
+ requiresSchema: true,
3178
3196
  examples: [
3179
3197
  {
3180
3198
  title: 'Incorrect',
3181
- usage: [{ acceptedIdNames: ['id', '_id'], acceptedIdTypes: ['ID'], exceptions: { suffixes: ['Payload'] } }],
3199
+ usage: [
3200
+ {
3201
+ acceptedIdNames: ['id', '_id'],
3202
+ acceptedIdTypes: ['ID'],
3203
+ exceptions: { suffixes: ['Payload'] },
3204
+ },
3205
+ ],
3182
3206
  code: /* GraphQL */ `
3183
3207
  # Incorrect field name
3184
3208
  type InvalidFieldName {
@@ -3280,6 +3304,9 @@ const rule$l = {
3280
3304
  },
3281
3305
  },
3282
3306
  },
3307
+ messages: {
3308
+ [RULE_ID$5]: `{{ typeName }} must have exactly one non-nullable unique identifier. Accepted name(s): {{ acceptedNamesString }}; Accepted type(s): {{ acceptedTypesString }}.`,
3309
+ },
3283
3310
  },
3284
3311
  create(context) {
3285
3312
  const options = {
@@ -3288,9 +3315,18 @@ const rule$l = {
3288
3315
  exceptions: {},
3289
3316
  ...context.options[0],
3290
3317
  };
3318
+ const schema = requireGraphQLSchemaFromContext(RULE_ID$5, context);
3319
+ const rootTypeNames = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]
3320
+ .filter(Boolean)
3321
+ .map(type => type.name);
3322
+ const selector = `ObjectTypeDefinition[name.value!=/^(${rootTypeNames.join('|')})$/]`;
3291
3323
  return {
3292
- ObjectTypeDefinition(node) {
3293
- if (shouldIgnoreNode({ node, exceptions: options.exceptions })) {
3324
+ [selector](node) {
3325
+ var _a, _b;
3326
+ const typeName = node.name.value;
3327
+ const shouldIgnoreNode = ((_a = options.exceptions.types) === null || _a === void 0 ? void 0 : _a.includes(typeName)) ||
3328
+ ((_b = options.exceptions.suffixes) === null || _b === void 0 ? void 0 : _b.some(suffix => typeName.endsWith(suffix)));
3329
+ if (shouldIgnoreNode) {
3294
3330
  return;
3295
3331
  }
3296
3332
  const validIds = node.fields.filter(field => {
@@ -3303,18 +3339,17 @@ const rule$l = {
3303
3339
  }
3304
3340
  return isValidIdName && isValidIdType;
3305
3341
  });
3306
- const typeName = node.name.value;
3307
3342
  // Usually, there should be only one unique identifier field per type.
3308
3343
  // Some clients allow multiple fields to be used. If more people need this,
3309
3344
  // we can extend this rule later.
3310
3345
  if (validIds.length !== 1) {
3311
3346
  context.report({
3312
3347
  loc: getLocation(node.name.loc, typeName),
3313
- message: `{{ typeName }} must have exactly one non-nullable unique identifier. Accepted name(s): {{ acceptedNamesString }} ; Accepted type(s): {{ acceptedTypesString }}`,
3348
+ messageId: RULE_ID$5,
3314
3349
  data: {
3315
3350
  typeName,
3316
- acceptedNamesString: options.acceptedIdNames.join(','),
3317
- acceptedTypesString: options.acceptedIdTypes.join(','),
3351
+ acceptedNamesString: options.acceptedIdNames.join(', '),
3352
+ acceptedTypesString: options.acceptedIdTypes.join(', '),
3318
3353
  },
3319
3354
  });
3320
3355
  }
package/index.mjs CHANGED
@@ -2619,20 +2619,21 @@ const rule$g = {
2619
2619
  },
2620
2620
  };
2621
2621
 
2622
- const REQUIRE_DESCRIPTION_ERROR = 'REQUIRE_DESCRIPTION_ERROR';
2622
+ const RULE_ID$2 = 'require-description';
2623
2623
  const ALLOWED_KINDS$1 = [
2624
2624
  ...TYPES_KINDS,
2625
2625
  Kind.FIELD_DEFINITION,
2626
2626
  Kind.INPUT_VALUE_DEFINITION,
2627
2627
  Kind.ENUM_VALUE_DEFINITION,
2628
2628
  Kind.DIRECTIVE_DEFINITION,
2629
+ Kind.OPERATION_DEFINITION,
2629
2630
  ];
2630
2631
  const rule$h = {
2631
2632
  meta: {
2632
2633
  docs: {
2633
2634
  category: 'Schema',
2634
2635
  description: 'Enforce descriptions in your type definitions.',
2635
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md',
2636
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
2636
2637
  examples: [
2637
2638
  {
2638
2639
  title: 'Incorrect',
@@ -2656,6 +2657,16 @@ const rule$h = {
2656
2657
  """
2657
2658
  name: String
2658
2659
  }
2660
+ `,
2661
+ },
2662
+ {
2663
+ title: 'Correct',
2664
+ usage: [{ OperationDefinition: true }],
2665
+ code: /* GraphQL */ `
2666
+ # Enforce description on operations
2667
+ query {
2668
+ foo
2669
+ }
2659
2670
  `,
2660
2671
  },
2661
2672
  ],
@@ -2669,7 +2680,7 @@ const rule$h = {
2669
2680
  },
2670
2681
  type: 'suggestion',
2671
2682
  messages: {
2672
- [REQUIRE_DESCRIPTION_ERROR]: 'Description is required for nodes of type "{{ nodeType }}"',
2683
+ [RULE_ID$2]: 'Description is required for nodes of type "{{ nodeType }}"',
2673
2684
  },
2674
2685
  schema: {
2675
2686
  type: 'array',
@@ -2696,7 +2707,7 @@ const rule$h = {
2696
2707
  },
2697
2708
  },
2698
2709
  create(context) {
2699
- const { types, ...restOptions } = context.options[0];
2710
+ const { types, ...restOptions } = context.options[0] || {};
2700
2711
  const kinds = new Set(types ? TYPES_KINDS : []);
2701
2712
  for (const [kind, isEnabled] of Object.entries(restOptions)) {
2702
2713
  if (isEnabled) {
@@ -2710,11 +2721,26 @@ const rule$h = {
2710
2721
  return {
2711
2722
  [selector](node) {
2712
2723
  var _a;
2713
- const description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value) || '';
2714
- if (description.trim().length === 0) {
2724
+ let description = '';
2725
+ const isOperation = node.kind === Kind.OPERATION_DEFINITION;
2726
+ if (isOperation) {
2727
+ const rawNode = node.rawNode();
2728
+ const { prev, line } = rawNode.loc.startToken;
2729
+ if (prev.kind === TokenKind.COMMENT) {
2730
+ const value = prev.value.trim();
2731
+ const linesBefore = line - prev.line;
2732
+ if (!value.startsWith('eslint') && linesBefore === 1) {
2733
+ description = value;
2734
+ }
2735
+ }
2736
+ }
2737
+ else {
2738
+ description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value.trim()) || '';
2739
+ }
2740
+ if (description.length === 0) {
2715
2741
  context.report({
2716
- loc: getLocation(node.name.loc, node.name.value),
2717
- messageId: REQUIRE_DESCRIPTION_ERROR,
2742
+ loc: isOperation ? getLocation(node.loc, node.operation) : getLocation(node.name.loc, node.name.value),
2743
+ messageId: RULE_ID$2,
2718
2744
  data: {
2719
2745
  nodeType: node.kind,
2720
2746
  },
@@ -2890,7 +2916,8 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2890
2916
  }
2891
2917
  };
2892
2918
 
2893
- const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2919
+ const RULE_ID$3 = 'require-id-when-available';
2920
+ const MESSAGE_ID = 'REQUIRE_ID_WHEN_AVAILABLE';
2894
2921
  const DEFAULT_ID_FIELD_NAME = 'id';
2895
2922
  const rule$j = {
2896
2923
  meta: {
@@ -2898,7 +2925,7 @@ const rule$j = {
2898
2925
  docs: {
2899
2926
  category: 'Operations',
2900
2927
  description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
2901
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-id-when-available.md',
2928
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$3}.md`,
2902
2929
  requiresSchema: true,
2903
2930
  requiresSiblings: true,
2904
2931
  examples: [
@@ -2912,7 +2939,7 @@ const rule$j = {
2912
2939
  }
2913
2940
 
2914
2941
  # Query
2915
- query user {
2942
+ query {
2916
2943
  user {
2917
2944
  name
2918
2945
  }
@@ -2929,7 +2956,7 @@ const rule$j = {
2929
2956
  }
2930
2957
 
2931
2958
  # Query
2932
- query user {
2959
+ query {
2933
2960
  user {
2934
2961
  id
2935
2962
  name
@@ -2941,7 +2968,7 @@ const rule$j = {
2941
2968
  recommended: true,
2942
2969
  },
2943
2970
  messages: {
2944
- [REQUIRE_ID_WHEN_AVAILABLE]: [
2971
+ [MESSAGE_ID]: [
2945
2972
  `Field {{ fieldName }} must be selected when it's available on a type. Please make sure to include it in your selection set!`,
2946
2973
  `If you are using fragments, make sure that all used fragments {{ checkedFragments }}specifies the field {{ fieldName }}.`,
2947
2974
  ].join('\n'),
@@ -2972,14 +2999,16 @@ const rule$j = {
2972
2999
  },
2973
3000
  },
2974
3001
  create(context) {
2975
- requireGraphQLSchemaFromContext('require-id-when-available', context);
2976
- const siblings = requireSiblingsOperations('require-id-when-available', context);
3002
+ requireGraphQLSchemaFromContext(RULE_ID$3, context);
3003
+ const siblings = requireSiblingsOperations(RULE_ID$3, context);
2977
3004
  const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
2978
- const idNames = Array.isArray(fieldName) ? fieldName : [fieldName];
3005
+ const idNames = asArray(fieldName);
2979
3006
  const isFound = (s) => s.kind === Kind.FIELD && idNames.includes(s.name.value);
3007
+ // Skip check selections in FragmentDefinition
3008
+ const selector = 'OperationDefinition SelectionSet[parent.kind!=OperationDefinition]';
2980
3009
  return {
2981
- SelectionSet(node) {
2982
- var _a, _b;
3010
+ [selector](node) {
3011
+ var _a;
2983
3012
  const typeInfo = node.typeInfo();
2984
3013
  if (!typeInfo.gqlType) {
2985
3014
  return;
@@ -2996,53 +3025,50 @@ const rule$j = {
2996
3025
  return;
2997
3026
  }
2998
3027
  const checkedFragmentSpreads = new Set();
2999
- let found = false;
3000
3028
  for (const selection of node.selections) {
3001
3029
  if (isFound(selection)) {
3002
- found = true;
3030
+ return;
3003
3031
  }
3004
- else if (selection.kind === Kind.INLINE_FRAGMENT) {
3005
- found = (_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.selections.some(s => isFound(s));
3032
+ if (selection.kind === Kind.INLINE_FRAGMENT && selection.selectionSet.selections.some(isFound)) {
3033
+ return;
3006
3034
  }
3007
- else if (selection.kind === Kind.FRAGMENT_SPREAD) {
3035
+ if (selection.kind === Kind.FRAGMENT_SPREAD) {
3008
3036
  const [foundSpread] = siblings.getFragment(selection.name.value);
3009
3037
  if (foundSpread) {
3010
3038
  checkedFragmentSpreads.add(foundSpread.document.name.value);
3011
- found = (_b = foundSpread.document.selectionSet) === null || _b === void 0 ? void 0 : _b.selections.some(s => isFound(s));
3039
+ if (foundSpread.document.selectionSet.selections.some(isFound)) {
3040
+ return;
3041
+ }
3012
3042
  }
3013
3043
  }
3014
- if (found) {
3015
- break;
3016
- }
3017
3044
  }
3018
3045
  const { parent } = node;
3019
- const hasIdFieldInInterfaceSelectionSet = parent &&
3020
- parent.kind === Kind.INLINE_FRAGMENT &&
3021
- parent.parent &&
3022
- parent.parent.kind === Kind.SELECTION_SET &&
3023
- parent.parent.selections.some(s => isFound(s));
3024
- if (!found && !hasIdFieldInInterfaceSelectionSet) {
3025
- context.report({
3026
- loc: getLocation(node.loc),
3027
- messageId: REQUIRE_ID_WHEN_AVAILABLE,
3028
- data: {
3029
- checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
3030
- fieldName: idNames.map(name => `"${name}"`).join(' or '),
3031
- },
3032
- });
3046
+ const hasIdFieldInInterfaceSelectionSet = (parent === null || parent === void 0 ? void 0 : parent.kind) === Kind.INLINE_FRAGMENT &&
3047
+ ((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.kind) === Kind.SELECTION_SET &&
3048
+ parent.parent.selections.some(isFound);
3049
+ if (hasIdFieldInInterfaceSelectionSet) {
3050
+ return;
3033
3051
  }
3052
+ context.report({
3053
+ loc: getLocation(node.loc),
3054
+ messageId: MESSAGE_ID,
3055
+ data: {
3056
+ checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
3057
+ fieldName: idNames.map(name => `"${name}"`).join(' or '),
3058
+ },
3059
+ });
3034
3060
  },
3035
3061
  };
3036
3062
  },
3037
3063
  };
3038
3064
 
3039
- const RULE_ID$2 = 'selection-set-depth';
3065
+ const RULE_ID$4 = 'selection-set-depth';
3040
3066
  const rule$k = {
3041
3067
  meta: {
3042
3068
  docs: {
3043
3069
  category: 'Operations',
3044
3070
  description: `Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://github.com/stems/graphql-depth-limit).`,
3045
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
3071
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$4}.md`,
3046
3072
  requiresSiblings: true,
3047
3073
  examples: [
3048
3074
  {
@@ -3116,10 +3142,10 @@ const rule$k = {
3116
3142
  create(context) {
3117
3143
  let siblings = null;
3118
3144
  try {
3119
- siblings = requireSiblingsOperations(RULE_ID$2, context);
3145
+ siblings = requireSiblingsOperations(RULE_ID$4, context);
3120
3146
  }
3121
3147
  catch (e) {
3122
- logger.warn(`Rule "${RULE_ID$2}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
3148
+ logger.warn(`Rule "${RULE_ID$4}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
3123
3149
  }
3124
3150
  const { maxDepth } = context.options[0];
3125
3151
  const ignore = context.options[0].ignore || [];
@@ -3144,35 +3170,33 @@ const rule$k = {
3144
3170
  });
3145
3171
  }
3146
3172
  catch (e) {
3147
- logger.warn(`Rule "${RULE_ID$2}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
3173
+ logger.warn(`Rule "${RULE_ID$4}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
3148
3174
  }
3149
3175
  },
3150
3176
  };
3151
3177
  },
3152
3178
  };
3153
3179
 
3154
- const shouldIgnoreNode = ({ node, exceptions }) => {
3155
- const rawNode = node.rawNode();
3156
- if (exceptions.types && exceptions.types.includes(rawNode.name.value)) {
3157
- return true;
3158
- }
3159
- if (exceptions.suffixes && exceptions.suffixes.some(suffix => rawNode.name.value.endsWith(suffix))) {
3160
- return true;
3161
- }
3162
- return false;
3163
- };
3180
+ const RULE_ID$5 = 'strict-id-in-types';
3164
3181
  const rule$l = {
3165
3182
  meta: {
3166
3183
  type: 'suggestion',
3167
3184
  docs: {
3168
- 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.',
3185
+ 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.`,
3169
3186
  category: 'Schema',
3170
3187
  recommended: true,
3171
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/strict-id-in-types.md',
3188
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$5}.md`,
3189
+ requiresSchema: true,
3172
3190
  examples: [
3173
3191
  {
3174
3192
  title: 'Incorrect',
3175
- usage: [{ acceptedIdNames: ['id', '_id'], acceptedIdTypes: ['ID'], exceptions: { suffixes: ['Payload'] } }],
3193
+ usage: [
3194
+ {
3195
+ acceptedIdNames: ['id', '_id'],
3196
+ acceptedIdTypes: ['ID'],
3197
+ exceptions: { suffixes: ['Payload'] },
3198
+ },
3199
+ ],
3176
3200
  code: /* GraphQL */ `
3177
3201
  # Incorrect field name
3178
3202
  type InvalidFieldName {
@@ -3274,6 +3298,9 @@ const rule$l = {
3274
3298
  },
3275
3299
  },
3276
3300
  },
3301
+ messages: {
3302
+ [RULE_ID$5]: `{{ typeName }} must have exactly one non-nullable unique identifier. Accepted name(s): {{ acceptedNamesString }}; Accepted type(s): {{ acceptedTypesString }}.`,
3303
+ },
3277
3304
  },
3278
3305
  create(context) {
3279
3306
  const options = {
@@ -3282,9 +3309,18 @@ const rule$l = {
3282
3309
  exceptions: {},
3283
3310
  ...context.options[0],
3284
3311
  };
3312
+ const schema = requireGraphQLSchemaFromContext(RULE_ID$5, context);
3313
+ const rootTypeNames = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]
3314
+ .filter(Boolean)
3315
+ .map(type => type.name);
3316
+ const selector = `ObjectTypeDefinition[name.value!=/^(${rootTypeNames.join('|')})$/]`;
3285
3317
  return {
3286
- ObjectTypeDefinition(node) {
3287
- if (shouldIgnoreNode({ node, exceptions: options.exceptions })) {
3318
+ [selector](node) {
3319
+ var _a, _b;
3320
+ const typeName = node.name.value;
3321
+ const shouldIgnoreNode = ((_a = options.exceptions.types) === null || _a === void 0 ? void 0 : _a.includes(typeName)) ||
3322
+ ((_b = options.exceptions.suffixes) === null || _b === void 0 ? void 0 : _b.some(suffix => typeName.endsWith(suffix)));
3323
+ if (shouldIgnoreNode) {
3288
3324
  return;
3289
3325
  }
3290
3326
  const validIds = node.fields.filter(field => {
@@ -3297,18 +3333,17 @@ const rule$l = {
3297
3333
  }
3298
3334
  return isValidIdName && isValidIdType;
3299
3335
  });
3300
- const typeName = node.name.value;
3301
3336
  // Usually, there should be only one unique identifier field per type.
3302
3337
  // Some clients allow multiple fields to be used. If more people need this,
3303
3338
  // we can extend this rule later.
3304
3339
  if (validIds.length !== 1) {
3305
3340
  context.report({
3306
3341
  loc: getLocation(node.name.loc, typeName),
3307
- message: `{{ typeName }} must have exactly one non-nullable unique identifier. Accepted name(s): {{ acceptedNamesString }} ; Accepted type(s): {{ acceptedTypesString }}`,
3342
+ messageId: RULE_ID$5,
3308
3343
  data: {
3309
3344
  typeName,
3310
- acceptedNamesString: options.acceptedIdNames.join(','),
3311
- acceptedTypesString: options.acceptedIdTypes.join(','),
3345
+ acceptedNamesString: options.acceptedIdNames.join(', '),
3346
+ acceptedTypesString: options.acceptedIdTypes.join(', '),
3312
3347
  },
3313
3348
  });
3314
3349
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-eslint/eslint-plugin",
3
- "version": "3.4.0",
3
+ "version": "3.7.0-alpha-a9b0ecf.0",
4
4
  "description": "GraphQL plugin for ESLint",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
@@ -9,8 +9,8 @@
9
9
  "dependencies": {
10
10
  "@babel/code-frame": "7.16.7",
11
11
  "@graphql-tools/code-file-loader": "7.2.3",
12
- "@graphql-tools/graphql-tag-pluck": "7.1.4",
13
- "@graphql-tools/utils": "8.5.5",
12
+ "@graphql-tools/graphql-tag-pluck": "7.1.5",
13
+ "@graphql-tools/utils": "8.6.1",
14
14
  "chalk": "4.1.2",
15
15
  "graphql-config": "4.1.0",
16
16
  "graphql-depth-limit": "1.1.0",
package/rules/index.d.ts CHANGED
@@ -33,33 +33,14 @@ export declare const rules: {
33
33
  argumentName?: string;
34
34
  }], false>;
35
35
  'require-deprecation-reason': import("..").GraphQLESLintRule<any[], false>;
36
- 'require-description': import("..").GraphQLESLintRule<[{
37
- types?: boolean;
38
- } & {
39
- ScalarTypeDefinition?: boolean;
40
- ObjectTypeDefinition?: boolean;
41
- FieldDefinition?: boolean;
42
- InputValueDefinition?: boolean;
43
- InterfaceTypeDefinition?: boolean;
44
- UnionTypeDefinition?: boolean;
45
- EnumTypeDefinition?: boolean;
46
- EnumValueDefinition?: boolean;
47
- InputObjectTypeDefinition?: boolean;
48
- DirectiveDefinition?: boolean;
49
- }], false>;
36
+ 'require-description': import("..").GraphQLESLintRule<[import("./require-description").RequireDescriptionRuleConfig], false>;
50
37
  'require-field-of-type-query-in-mutation-result': import("..").GraphQLESLintRule<any[], false>;
51
- 'require-id-when-available': import("..").GraphQLESLintRule<[{
52
- fieldName: string;
53
- }], true>;
38
+ 'require-id-when-available': import("..").GraphQLESLintRule<[import("./require-id-when-available").RequireIdWhenAvailableRuleConfig], true>;
54
39
  'selection-set-depth': import("..").GraphQLESLintRule<[{
55
40
  maxDepth: number;
56
41
  ignore?: string[];
57
42
  }], false>;
58
- 'strict-id-in-types': import("..").GraphQLESLintRule<[{
59
- acceptedIdNames?: string[];
60
- acceptedIdTypes?: string[];
61
- exceptions?: import("./strict-id-in-types").ExceptionRule;
62
- }], false>;
43
+ 'strict-id-in-types': import("..").GraphQLESLintRule<[import("./strict-id-in-types").StrictIdInTypesRuleConfig], false>;
63
44
  'unique-fragment-name': import("..").GraphQLESLintRule<any[], false>;
64
45
  'unique-operation-name': import("..").GraphQLESLintRule<any[], false>;
65
46
  };
@@ -1,8 +1,8 @@
1
1
  import { Kind } from 'graphql';
2
2
  import { GraphQLESLintRule } from '../types';
3
- declare const ALLOWED_KINDS: readonly [Kind.OBJECT_TYPE_DEFINITION, Kind.INTERFACE_TYPE_DEFINITION, Kind.ENUM_TYPE_DEFINITION, Kind.SCALAR_TYPE_DEFINITION, Kind.INPUT_OBJECT_TYPE_DEFINITION, Kind.UNION_TYPE_DEFINITION, Kind.FIELD_DEFINITION, Kind.INPUT_VALUE_DEFINITION, Kind.ENUM_VALUE_DEFINITION, Kind.DIRECTIVE_DEFINITION];
3
+ declare const ALLOWED_KINDS: readonly [Kind.OBJECT_TYPE_DEFINITION, Kind.INTERFACE_TYPE_DEFINITION, Kind.ENUM_TYPE_DEFINITION, Kind.SCALAR_TYPE_DEFINITION, Kind.INPUT_OBJECT_TYPE_DEFINITION, Kind.UNION_TYPE_DEFINITION, Kind.FIELD_DEFINITION, Kind.INPUT_VALUE_DEFINITION, Kind.ENUM_VALUE_DEFINITION, Kind.DIRECTIVE_DEFINITION, Kind.OPERATION_DEFINITION];
4
4
  declare type AllowedKind = typeof ALLOWED_KINDS[number];
5
- declare type RequireDescriptionRuleConfig = {
5
+ export declare type RequireDescriptionRuleConfig = {
6
6
  types?: boolean;
7
7
  } & {
8
8
  [key in AllowedKind]?: boolean;
@@ -1,6 +1,6 @@
1
1
  import { GraphQLESLintRule } from '../types';
2
- declare type RequireIdWhenAvailableRuleConfig = {
3
- fieldName: string;
2
+ export declare type RequireIdWhenAvailableRuleConfig = {
3
+ fieldName: string | string[];
4
4
  };
5
5
  declare const rule: GraphQLESLintRule<[RequireIdWhenAvailableRuleConfig], true>;
6
6
  export default rule;
@@ -1,12 +1,11 @@
1
1
  import { GraphQLESLintRule } from '../types';
2
- export interface ExceptionRule {
3
- types?: string[];
4
- suffixes?: string[];
5
- }
6
- declare type StrictIdInTypesRuleConfig = {
2
+ export declare type StrictIdInTypesRuleConfig = {
7
3
  acceptedIdNames?: string[];
8
4
  acceptedIdTypes?: string[];
9
- exceptions?: ExceptionRule;
5
+ exceptions?: {
6
+ types?: string[];
7
+ suffixes?: string[];
8
+ };
10
9
  };
11
10
  declare const rule: GraphQLESLintRule<[StrictIdInTypesRuleConfig]>;
12
11
  export default rule;