@graphql-eslint/eslint-plugin 3.4.0-alpha-d977284.0 → 3.6.0-alpha-56305a5.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.
- package/docs/rules/require-id-when-available.md +2 -2
- package/docs/rules/strict-id-in-types.md +1 -1
- package/index.js +67 -58
- package/index.mjs +67 -58
- package/package.json +1 -1
- package/rules/index.d.ts +2 -8
- package/rules/require-id-when-available.d.ts +2 -2
- package/rules/strict-id-in-types.d.ts +5 -6
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
- Category: `Schema`
|
6
6
|
- Rule name: `@graphql-eslint/strict-id-in-types`
|
7
|
-
- Requires 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
@@ -340,7 +340,7 @@ function validateDocument(context, schema = null, documentNode, rule) {
|
|
340
340
|
try {
|
341
341
|
const validationErrors = schema
|
342
342
|
? graphql.validate(schema, documentNode, [rule])
|
343
|
-
: validate.validateSDL(documentNode,
|
343
|
+
: validate.validateSDL(documentNode, null, [rule]);
|
344
344
|
for (const error of validationErrors) {
|
345
345
|
/*
|
346
346
|
* TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
|
@@ -2896,7 +2896,8 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2896
2896
|
}
|
2897
2897
|
};
|
2898
2898
|
|
2899
|
-
const
|
2899
|
+
const RULE_ID$2 = 'require-id-when-available';
|
2900
|
+
const MESSAGE_ID = 'REQUIRE_ID_WHEN_AVAILABLE';
|
2900
2901
|
const DEFAULT_ID_FIELD_NAME = 'id';
|
2901
2902
|
const rule$j = {
|
2902
2903
|
meta: {
|
@@ -2904,7 +2905,7 @@ const rule$j = {
|
|
2904
2905
|
docs: {
|
2905
2906
|
category: 'Operations',
|
2906
2907
|
description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
|
2907
|
-
url:
|
2908
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
|
2908
2909
|
requiresSchema: true,
|
2909
2910
|
requiresSiblings: true,
|
2910
2911
|
examples: [
|
@@ -2918,7 +2919,7 @@ const rule$j = {
|
|
2918
2919
|
}
|
2919
2920
|
|
2920
2921
|
# Query
|
2921
|
-
query
|
2922
|
+
query {
|
2922
2923
|
user {
|
2923
2924
|
name
|
2924
2925
|
}
|
@@ -2935,7 +2936,7 @@ const rule$j = {
|
|
2935
2936
|
}
|
2936
2937
|
|
2937
2938
|
# Query
|
2938
|
-
query
|
2939
|
+
query {
|
2939
2940
|
user {
|
2940
2941
|
id
|
2941
2942
|
name
|
@@ -2947,7 +2948,7 @@ const rule$j = {
|
|
2947
2948
|
recommended: true,
|
2948
2949
|
},
|
2949
2950
|
messages: {
|
2950
|
-
[
|
2951
|
+
[MESSAGE_ID]: [
|
2951
2952
|
`Field {{ fieldName }} must be selected when it's available on a type. Please make sure to include it in your selection set!`,
|
2952
2953
|
`If you are using fragments, make sure that all used fragments {{ checkedFragments }}specifies the field {{ fieldName }}.`,
|
2953
2954
|
].join('\n'),
|
@@ -2978,14 +2979,16 @@ const rule$j = {
|
|
2978
2979
|
},
|
2979
2980
|
},
|
2980
2981
|
create(context) {
|
2981
|
-
requireGraphQLSchemaFromContext(
|
2982
|
-
const siblings = requireSiblingsOperations(
|
2982
|
+
requireGraphQLSchemaFromContext(RULE_ID$2, context);
|
2983
|
+
const siblings = requireSiblingsOperations(RULE_ID$2, context);
|
2983
2984
|
const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
|
2984
|
-
const idNames =
|
2985
|
+
const idNames = utils.asArray(fieldName);
|
2985
2986
|
const isFound = (s) => s.kind === graphql.Kind.FIELD && idNames.includes(s.name.value);
|
2987
|
+
// Skip check selections in FragmentDefinition
|
2988
|
+
const selector = 'OperationDefinition SelectionSet[parent.kind!=OperationDefinition]';
|
2986
2989
|
return {
|
2987
|
-
|
2988
|
-
var _a
|
2990
|
+
[selector](node) {
|
2991
|
+
var _a;
|
2989
2992
|
const typeInfo = node.typeInfo();
|
2990
2993
|
if (!typeInfo.gqlType) {
|
2991
2994
|
return;
|
@@ -3002,53 +3005,50 @@ const rule$j = {
|
|
3002
3005
|
return;
|
3003
3006
|
}
|
3004
3007
|
const checkedFragmentSpreads = new Set();
|
3005
|
-
let found = false;
|
3006
3008
|
for (const selection of node.selections) {
|
3007
3009
|
if (isFound(selection)) {
|
3008
|
-
|
3010
|
+
return;
|
3009
3011
|
}
|
3010
|
-
|
3011
|
-
|
3012
|
+
if (selection.kind === graphql.Kind.INLINE_FRAGMENT && selection.selectionSet.selections.some(isFound)) {
|
3013
|
+
return;
|
3012
3014
|
}
|
3013
|
-
|
3015
|
+
if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
|
3014
3016
|
const [foundSpread] = siblings.getFragment(selection.name.value);
|
3015
3017
|
if (foundSpread) {
|
3016
3018
|
checkedFragmentSpreads.add(foundSpread.document.name.value);
|
3017
|
-
|
3019
|
+
if (foundSpread.document.selectionSet.selections.some(isFound)) {
|
3020
|
+
return;
|
3021
|
+
}
|
3018
3022
|
}
|
3019
3023
|
}
|
3020
|
-
if (found) {
|
3021
|
-
break;
|
3022
|
-
}
|
3023
3024
|
}
|
3024
3025
|
const { parent } = node;
|
3025
|
-
const hasIdFieldInInterfaceSelectionSet = parent &&
|
3026
|
-
parent.kind === graphql.Kind.
|
3027
|
-
parent.parent
|
3028
|
-
|
3029
|
-
|
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
|
-
});
|
3026
|
+
const hasIdFieldInInterfaceSelectionSet = (parent === null || parent === void 0 ? void 0 : parent.kind) === graphql.Kind.INLINE_FRAGMENT &&
|
3027
|
+
((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.kind) === graphql.Kind.SELECTION_SET &&
|
3028
|
+
parent.parent.selections.some(isFound);
|
3029
|
+
if (hasIdFieldInInterfaceSelectionSet) {
|
3030
|
+
return;
|
3039
3031
|
}
|
3032
|
+
context.report({
|
3033
|
+
loc: getLocation(node.loc),
|
3034
|
+
messageId: MESSAGE_ID,
|
3035
|
+
data: {
|
3036
|
+
checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
|
3037
|
+
fieldName: idNames.map(name => `"${name}"`).join(' or '),
|
3038
|
+
},
|
3039
|
+
});
|
3040
3040
|
},
|
3041
3041
|
};
|
3042
3042
|
},
|
3043
3043
|
};
|
3044
3044
|
|
3045
|
-
const RULE_ID$
|
3045
|
+
const RULE_ID$3 = 'selection-set-depth';
|
3046
3046
|
const rule$k = {
|
3047
3047
|
meta: {
|
3048
3048
|
docs: {
|
3049
3049
|
category: 'Operations',
|
3050
3050
|
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$
|
3051
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$3}.md`,
|
3052
3052
|
requiresSiblings: true,
|
3053
3053
|
examples: [
|
3054
3054
|
{
|
@@ -3122,10 +3122,10 @@ const rule$k = {
|
|
3122
3122
|
create(context) {
|
3123
3123
|
let siblings = null;
|
3124
3124
|
try {
|
3125
|
-
siblings = requireSiblingsOperations(RULE_ID$
|
3125
|
+
siblings = requireSiblingsOperations(RULE_ID$3, context);
|
3126
3126
|
}
|
3127
3127
|
catch (e) {
|
3128
|
-
logger.warn(`Rule "${RULE_ID$
|
3128
|
+
logger.warn(`Rule "${RULE_ID$3}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3129
3129
|
}
|
3130
3130
|
const { maxDepth } = context.options[0];
|
3131
3131
|
const ignore = context.options[0].ignore || [];
|
@@ -3150,35 +3150,33 @@ const rule$k = {
|
|
3150
3150
|
});
|
3151
3151
|
}
|
3152
3152
|
catch (e) {
|
3153
|
-
logger.warn(`Rule "${RULE_ID$
|
3153
|
+
logger.warn(`Rule "${RULE_ID$3}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
|
3154
3154
|
}
|
3155
3155
|
},
|
3156
3156
|
};
|
3157
3157
|
},
|
3158
3158
|
};
|
3159
3159
|
|
3160
|
-
const
|
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
|
-
};
|
3160
|
+
const RULE_ID$4 = 'strict-id-in-types';
|
3170
3161
|
const rule$l = {
|
3171
3162
|
meta: {
|
3172
3163
|
type: 'suggestion',
|
3173
3164
|
docs: {
|
3174
|
-
description:
|
3165
|
+
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
3166
|
category: 'Schema',
|
3176
3167
|
recommended: true,
|
3177
|
-
url:
|
3168
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$4}.md`,
|
3169
|
+
requiresSchema: true,
|
3178
3170
|
examples: [
|
3179
3171
|
{
|
3180
3172
|
title: 'Incorrect',
|
3181
|
-
usage: [
|
3173
|
+
usage: [
|
3174
|
+
{
|
3175
|
+
acceptedIdNames: ['id', '_id'],
|
3176
|
+
acceptedIdTypes: ['ID'],
|
3177
|
+
exceptions: { suffixes: ['Payload'] },
|
3178
|
+
},
|
3179
|
+
],
|
3182
3180
|
code: /* GraphQL */ `
|
3183
3181
|
# Incorrect field name
|
3184
3182
|
type InvalidFieldName {
|
@@ -3280,6 +3278,9 @@ const rule$l = {
|
|
3280
3278
|
},
|
3281
3279
|
},
|
3282
3280
|
},
|
3281
|
+
messages: {
|
3282
|
+
[RULE_ID$4]: `{{ typeName }} must have exactly one non-nullable unique identifier. Accepted name(s): {{ acceptedNamesString }}; Accepted type(s): {{ acceptedTypesString }}.`,
|
3283
|
+
},
|
3283
3284
|
},
|
3284
3285
|
create(context) {
|
3285
3286
|
const options = {
|
@@ -3288,9 +3289,18 @@ const rule$l = {
|
|
3288
3289
|
exceptions: {},
|
3289
3290
|
...context.options[0],
|
3290
3291
|
};
|
3292
|
+
const schema = requireGraphQLSchemaFromContext(RULE_ID$4, context);
|
3293
|
+
const rootTypeNames = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]
|
3294
|
+
.filter(Boolean)
|
3295
|
+
.map(type => type.name);
|
3296
|
+
const selector = `ObjectTypeDefinition[name.value!=/^(${rootTypeNames.join('|')})$/]`;
|
3291
3297
|
return {
|
3292
|
-
|
3293
|
-
|
3298
|
+
[selector](node) {
|
3299
|
+
var _a, _b;
|
3300
|
+
const typeName = node.name.value;
|
3301
|
+
const shouldIgnoreNode = ((_a = options.exceptions.types) === null || _a === void 0 ? void 0 : _a.includes(typeName)) ||
|
3302
|
+
((_b = options.exceptions.suffixes) === null || _b === void 0 ? void 0 : _b.some(suffix => typeName.endsWith(suffix)));
|
3303
|
+
if (shouldIgnoreNode) {
|
3294
3304
|
return;
|
3295
3305
|
}
|
3296
3306
|
const validIds = node.fields.filter(field => {
|
@@ -3303,18 +3313,17 @@ const rule$l = {
|
|
3303
3313
|
}
|
3304
3314
|
return isValidIdName && isValidIdType;
|
3305
3315
|
});
|
3306
|
-
const typeName = node.name.value;
|
3307
3316
|
// Usually, there should be only one unique identifier field per type.
|
3308
3317
|
// Some clients allow multiple fields to be used. If more people need this,
|
3309
3318
|
// we can extend this rule later.
|
3310
3319
|
if (validIds.length !== 1) {
|
3311
3320
|
context.report({
|
3312
3321
|
loc: getLocation(node.name.loc, typeName),
|
3313
|
-
|
3322
|
+
messageId: RULE_ID$4,
|
3314
3323
|
data: {
|
3315
3324
|
typeName,
|
3316
|
-
acceptedNamesString: options.acceptedIdNames.join(','),
|
3317
|
-
acceptedTypesString: options.acceptedIdTypes.join(','),
|
3325
|
+
acceptedNamesString: options.acceptedIdNames.join(', '),
|
3326
|
+
acceptedTypesString: options.acceptedIdTypes.join(', '),
|
3318
3327
|
},
|
3319
3328
|
});
|
3320
3329
|
}
|
package/index.mjs
CHANGED
@@ -334,7 +334,7 @@ function validateDocument(context, schema = null, documentNode, rule) {
|
|
334
334
|
try {
|
335
335
|
const validationErrors = schema
|
336
336
|
? validate(schema, documentNode, [rule])
|
337
|
-
: validateSDL(documentNode,
|
337
|
+
: validateSDL(documentNode, null, [rule]);
|
338
338
|
for (const error of validationErrors) {
|
339
339
|
/*
|
340
340
|
* TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
|
@@ -2890,7 +2890,8 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2890
2890
|
}
|
2891
2891
|
};
|
2892
2892
|
|
2893
|
-
const
|
2893
|
+
const RULE_ID$2 = 'require-id-when-available';
|
2894
|
+
const MESSAGE_ID = 'REQUIRE_ID_WHEN_AVAILABLE';
|
2894
2895
|
const DEFAULT_ID_FIELD_NAME = 'id';
|
2895
2896
|
const rule$j = {
|
2896
2897
|
meta: {
|
@@ -2898,7 +2899,7 @@ const rule$j = {
|
|
2898
2899
|
docs: {
|
2899
2900
|
category: 'Operations',
|
2900
2901
|
description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
|
2901
|
-
url:
|
2902
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
|
2902
2903
|
requiresSchema: true,
|
2903
2904
|
requiresSiblings: true,
|
2904
2905
|
examples: [
|
@@ -2912,7 +2913,7 @@ const rule$j = {
|
|
2912
2913
|
}
|
2913
2914
|
|
2914
2915
|
# Query
|
2915
|
-
query
|
2916
|
+
query {
|
2916
2917
|
user {
|
2917
2918
|
name
|
2918
2919
|
}
|
@@ -2929,7 +2930,7 @@ const rule$j = {
|
|
2929
2930
|
}
|
2930
2931
|
|
2931
2932
|
# Query
|
2932
|
-
query
|
2933
|
+
query {
|
2933
2934
|
user {
|
2934
2935
|
id
|
2935
2936
|
name
|
@@ -2941,7 +2942,7 @@ const rule$j = {
|
|
2941
2942
|
recommended: true,
|
2942
2943
|
},
|
2943
2944
|
messages: {
|
2944
|
-
[
|
2945
|
+
[MESSAGE_ID]: [
|
2945
2946
|
`Field {{ fieldName }} must be selected when it's available on a type. Please make sure to include it in your selection set!`,
|
2946
2947
|
`If you are using fragments, make sure that all used fragments {{ checkedFragments }}specifies the field {{ fieldName }}.`,
|
2947
2948
|
].join('\n'),
|
@@ -2972,14 +2973,16 @@ const rule$j = {
|
|
2972
2973
|
},
|
2973
2974
|
},
|
2974
2975
|
create(context) {
|
2975
|
-
requireGraphQLSchemaFromContext(
|
2976
|
-
const siblings = requireSiblingsOperations(
|
2976
|
+
requireGraphQLSchemaFromContext(RULE_ID$2, context);
|
2977
|
+
const siblings = requireSiblingsOperations(RULE_ID$2, context);
|
2977
2978
|
const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
|
2978
|
-
const idNames =
|
2979
|
+
const idNames = asArray(fieldName);
|
2979
2980
|
const isFound = (s) => s.kind === Kind.FIELD && idNames.includes(s.name.value);
|
2981
|
+
// Skip check selections in FragmentDefinition
|
2982
|
+
const selector = 'OperationDefinition SelectionSet[parent.kind!=OperationDefinition]';
|
2980
2983
|
return {
|
2981
|
-
|
2982
|
-
var _a
|
2984
|
+
[selector](node) {
|
2985
|
+
var _a;
|
2983
2986
|
const typeInfo = node.typeInfo();
|
2984
2987
|
if (!typeInfo.gqlType) {
|
2985
2988
|
return;
|
@@ -2996,53 +2999,50 @@ const rule$j = {
|
|
2996
2999
|
return;
|
2997
3000
|
}
|
2998
3001
|
const checkedFragmentSpreads = new Set();
|
2999
|
-
let found = false;
|
3000
3002
|
for (const selection of node.selections) {
|
3001
3003
|
if (isFound(selection)) {
|
3002
|
-
|
3004
|
+
return;
|
3003
3005
|
}
|
3004
|
-
|
3005
|
-
|
3006
|
+
if (selection.kind === Kind.INLINE_FRAGMENT && selection.selectionSet.selections.some(isFound)) {
|
3007
|
+
return;
|
3006
3008
|
}
|
3007
|
-
|
3009
|
+
if (selection.kind === Kind.FRAGMENT_SPREAD) {
|
3008
3010
|
const [foundSpread] = siblings.getFragment(selection.name.value);
|
3009
3011
|
if (foundSpread) {
|
3010
3012
|
checkedFragmentSpreads.add(foundSpread.document.name.value);
|
3011
|
-
|
3013
|
+
if (foundSpread.document.selectionSet.selections.some(isFound)) {
|
3014
|
+
return;
|
3015
|
+
}
|
3012
3016
|
}
|
3013
3017
|
}
|
3014
|
-
if (found) {
|
3015
|
-
break;
|
3016
|
-
}
|
3017
3018
|
}
|
3018
3019
|
const { parent } = node;
|
3019
|
-
const hasIdFieldInInterfaceSelectionSet = parent &&
|
3020
|
-
parent.kind === Kind.
|
3021
|
-
parent.parent
|
3022
|
-
|
3023
|
-
|
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
|
-
});
|
3020
|
+
const hasIdFieldInInterfaceSelectionSet = (parent === null || parent === void 0 ? void 0 : parent.kind) === Kind.INLINE_FRAGMENT &&
|
3021
|
+
((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.kind) === Kind.SELECTION_SET &&
|
3022
|
+
parent.parent.selections.some(isFound);
|
3023
|
+
if (hasIdFieldInInterfaceSelectionSet) {
|
3024
|
+
return;
|
3033
3025
|
}
|
3026
|
+
context.report({
|
3027
|
+
loc: getLocation(node.loc),
|
3028
|
+
messageId: MESSAGE_ID,
|
3029
|
+
data: {
|
3030
|
+
checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
|
3031
|
+
fieldName: idNames.map(name => `"${name}"`).join(' or '),
|
3032
|
+
},
|
3033
|
+
});
|
3034
3034
|
},
|
3035
3035
|
};
|
3036
3036
|
},
|
3037
3037
|
};
|
3038
3038
|
|
3039
|
-
const RULE_ID$
|
3039
|
+
const RULE_ID$3 = 'selection-set-depth';
|
3040
3040
|
const rule$k = {
|
3041
3041
|
meta: {
|
3042
3042
|
docs: {
|
3043
3043
|
category: 'Operations',
|
3044
3044
|
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$
|
3045
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$3}.md`,
|
3046
3046
|
requiresSiblings: true,
|
3047
3047
|
examples: [
|
3048
3048
|
{
|
@@ -3116,10 +3116,10 @@ const rule$k = {
|
|
3116
3116
|
create(context) {
|
3117
3117
|
let siblings = null;
|
3118
3118
|
try {
|
3119
|
-
siblings = requireSiblingsOperations(RULE_ID$
|
3119
|
+
siblings = requireSiblingsOperations(RULE_ID$3, context);
|
3120
3120
|
}
|
3121
3121
|
catch (e) {
|
3122
|
-
logger.warn(`Rule "${RULE_ID$
|
3122
|
+
logger.warn(`Rule "${RULE_ID$3}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3123
3123
|
}
|
3124
3124
|
const { maxDepth } = context.options[0];
|
3125
3125
|
const ignore = context.options[0].ignore || [];
|
@@ -3144,35 +3144,33 @@ const rule$k = {
|
|
3144
3144
|
});
|
3145
3145
|
}
|
3146
3146
|
catch (e) {
|
3147
|
-
logger.warn(`Rule "${RULE_ID$
|
3147
|
+
logger.warn(`Rule "${RULE_ID$3}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
|
3148
3148
|
}
|
3149
3149
|
},
|
3150
3150
|
};
|
3151
3151
|
},
|
3152
3152
|
};
|
3153
3153
|
|
3154
|
-
const
|
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
|
-
};
|
3154
|
+
const RULE_ID$4 = 'strict-id-in-types';
|
3164
3155
|
const rule$l = {
|
3165
3156
|
meta: {
|
3166
3157
|
type: 'suggestion',
|
3167
3158
|
docs: {
|
3168
|
-
description:
|
3159
|
+
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
3160
|
category: 'Schema',
|
3170
3161
|
recommended: true,
|
3171
|
-
url:
|
3162
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$4}.md`,
|
3163
|
+
requiresSchema: true,
|
3172
3164
|
examples: [
|
3173
3165
|
{
|
3174
3166
|
title: 'Incorrect',
|
3175
|
-
usage: [
|
3167
|
+
usage: [
|
3168
|
+
{
|
3169
|
+
acceptedIdNames: ['id', '_id'],
|
3170
|
+
acceptedIdTypes: ['ID'],
|
3171
|
+
exceptions: { suffixes: ['Payload'] },
|
3172
|
+
},
|
3173
|
+
],
|
3176
3174
|
code: /* GraphQL */ `
|
3177
3175
|
# Incorrect field name
|
3178
3176
|
type InvalidFieldName {
|
@@ -3274,6 +3272,9 @@ const rule$l = {
|
|
3274
3272
|
},
|
3275
3273
|
},
|
3276
3274
|
},
|
3275
|
+
messages: {
|
3276
|
+
[RULE_ID$4]: `{{ typeName }} must have exactly one non-nullable unique identifier. Accepted name(s): {{ acceptedNamesString }}; Accepted type(s): {{ acceptedTypesString }}.`,
|
3277
|
+
},
|
3277
3278
|
},
|
3278
3279
|
create(context) {
|
3279
3280
|
const options = {
|
@@ -3282,9 +3283,18 @@ const rule$l = {
|
|
3282
3283
|
exceptions: {},
|
3283
3284
|
...context.options[0],
|
3284
3285
|
};
|
3286
|
+
const schema = requireGraphQLSchemaFromContext(RULE_ID$4, context);
|
3287
|
+
const rootTypeNames = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]
|
3288
|
+
.filter(Boolean)
|
3289
|
+
.map(type => type.name);
|
3290
|
+
const selector = `ObjectTypeDefinition[name.value!=/^(${rootTypeNames.join('|')})$/]`;
|
3285
3291
|
return {
|
3286
|
-
|
3287
|
-
|
3292
|
+
[selector](node) {
|
3293
|
+
var _a, _b;
|
3294
|
+
const typeName = node.name.value;
|
3295
|
+
const shouldIgnoreNode = ((_a = options.exceptions.types) === null || _a === void 0 ? void 0 : _a.includes(typeName)) ||
|
3296
|
+
((_b = options.exceptions.suffixes) === null || _b === void 0 ? void 0 : _b.some(suffix => typeName.endsWith(suffix)));
|
3297
|
+
if (shouldIgnoreNode) {
|
3288
3298
|
return;
|
3289
3299
|
}
|
3290
3300
|
const validIds = node.fields.filter(field => {
|
@@ -3297,18 +3307,17 @@ const rule$l = {
|
|
3297
3307
|
}
|
3298
3308
|
return isValidIdName && isValidIdType;
|
3299
3309
|
});
|
3300
|
-
const typeName = node.name.value;
|
3301
3310
|
// Usually, there should be only one unique identifier field per type.
|
3302
3311
|
// Some clients allow multiple fields to be used. If more people need this,
|
3303
3312
|
// we can extend this rule later.
|
3304
3313
|
if (validIds.length !== 1) {
|
3305
3314
|
context.report({
|
3306
3315
|
loc: getLocation(node.name.loc, typeName),
|
3307
|
-
|
3316
|
+
messageId: RULE_ID$4,
|
3308
3317
|
data: {
|
3309
3318
|
typeName,
|
3310
|
-
acceptedNamesString: options.acceptedIdNames.join(','),
|
3311
|
-
acceptedTypesString: options.acceptedIdTypes.join(','),
|
3319
|
+
acceptedNamesString: options.acceptedIdNames.join(', '),
|
3320
|
+
acceptedTypesString: options.acceptedIdTypes.join(', '),
|
3312
3321
|
},
|
3313
3322
|
});
|
3314
3323
|
}
|
package/package.json
CHANGED
package/rules/index.d.ts
CHANGED
@@ -48,18 +48,12 @@ export declare const rules: {
|
|
48
48
|
DirectiveDefinition?: boolean;
|
49
49
|
}], false>;
|
50
50
|
'require-field-of-type-query-in-mutation-result': import("..").GraphQLESLintRule<any[], false>;
|
51
|
-
'require-id-when-available': import("..").GraphQLESLintRule<[
|
52
|
-
fieldName: string;
|
53
|
-
}], true>;
|
51
|
+
'require-id-when-available': import("..").GraphQLESLintRule<[import("./require-id-when-available").RequireIdWhenAvailableRuleConfig], true>;
|
54
52
|
'selection-set-depth': import("..").GraphQLESLintRule<[{
|
55
53
|
maxDepth: number;
|
56
54
|
ignore?: string[];
|
57
55
|
}], false>;
|
58
|
-
'strict-id-in-types': import("..").GraphQLESLintRule<[
|
59
|
-
acceptedIdNames?: string[];
|
60
|
-
acceptedIdTypes?: string[];
|
61
|
-
exceptions?: import("./strict-id-in-types").ExceptionRule;
|
62
|
-
}], false>;
|
56
|
+
'strict-id-in-types': import("..").GraphQLESLintRule<[import("./strict-id-in-types").StrictIdInTypesRuleConfig], false>;
|
63
57
|
'unique-fragment-name': import("..").GraphQLESLintRule<any[], false>;
|
64
58
|
'unique-operation-name': import("..").GraphQLESLintRule<any[], false>;
|
65
59
|
};
|
@@ -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
|
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?:
|
5
|
+
exceptions?: {
|
6
|
+
types?: string[];
|
7
|
+
suffixes?: string[];
|
8
|
+
};
|
10
9
|
};
|
11
10
|
declare const rule: GraphQLESLintRule<[StrictIdInTypesRuleConfig]>;
|
12
11
|
export default rule;
|