@graphql-eslint/eslint-plugin 3.0.1 → 3.1.0-alpha-878c908.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 +16 -1
- package/index.js +67 -59
- package/index.mjs +67 -59
- package/package.json +1 -1
@@ -54,10 +54,25 @@ query user {
|
|
54
54
|
|
55
55
|
The schema defines the following properties:
|
56
56
|
|
57
|
-
### `fieldName`
|
57
|
+
### `fieldName`
|
58
|
+
|
59
|
+
The object must be one of the following types:
|
60
|
+
|
61
|
+
* `asString`
|
62
|
+
* `asArray`
|
58
63
|
|
59
64
|
Default: `"id"`
|
60
65
|
|
66
|
+
---
|
67
|
+
|
68
|
+
# Sub Schemas
|
69
|
+
|
70
|
+
The schema defines the following additional types:
|
71
|
+
|
72
|
+
## `asString` (string)
|
73
|
+
|
74
|
+
## `asArray` (array)
|
75
|
+
|
61
76
|
## Resources
|
62
77
|
|
63
78
|
- [Rule source](../../packages/plugin/src/rules/require-id-when-available.ts)
|
package/index.js
CHANGED
@@ -2852,9 +2852,22 @@ const rule$j = {
|
|
2852
2852
|
recommended: true,
|
2853
2853
|
},
|
2854
2854
|
messages: {
|
2855
|
-
[REQUIRE_ID_WHEN_AVAILABLE]:
|
2855
|
+
[REQUIRE_ID_WHEN_AVAILABLE]: [
|
2856
|
+
`Field {{ fieldName }} must be selected when it's available on a type. Please make sure to include it in your selection set!`,
|
2857
|
+
`If you are using fragments, make sure that all used fragments {{ checkedFragments }}specifies the field {{ fieldName }}.`,
|
2858
|
+
].join('\n'),
|
2856
2859
|
},
|
2857
2860
|
schema: {
|
2861
|
+
definitions: {
|
2862
|
+
asString: {
|
2863
|
+
type: 'string',
|
2864
|
+
},
|
2865
|
+
asArray: {
|
2866
|
+
type: 'array',
|
2867
|
+
minItems: 1,
|
2868
|
+
uniqueItems: true,
|
2869
|
+
},
|
2870
|
+
},
|
2858
2871
|
type: 'array',
|
2859
2872
|
maxItems: 1,
|
2860
2873
|
items: {
|
@@ -2862,7 +2875,7 @@ const rule$j = {
|
|
2862
2875
|
additionalProperties: false,
|
2863
2876
|
properties: {
|
2864
2877
|
fieldName: {
|
2865
|
-
|
2878
|
+
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asArray' }],
|
2866
2879
|
default: DEFAULT_ID_FIELD_NAME,
|
2867
2880
|
},
|
2868
2881
|
},
|
@@ -2870,69 +2883,64 @@ const rule$j = {
|
|
2870
2883
|
},
|
2871
2884
|
},
|
2872
2885
|
create(context) {
|
2886
|
+
requireGraphQLSchemaFromContext('require-id-when-available', context);
|
2887
|
+
const siblings = requireSiblingsOperations('require-id-when-available', context);
|
2888
|
+
const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
|
2889
|
+
const idNames = Array.isArray(fieldName) ? fieldName : [fieldName];
|
2890
|
+
const isFound = (s) => s.kind === graphql.Kind.FIELD && idNames.includes(s.name.value);
|
2873
2891
|
return {
|
2874
2892
|
SelectionSet(node) {
|
2875
2893
|
var _a, _b;
|
2876
|
-
|
2877
|
-
|
2878
|
-
const fieldName = (context.options[0] || {}).fieldName || DEFAULT_ID_FIELD_NAME;
|
2879
|
-
if (!node.selections || node.selections.length === 0) {
|
2894
|
+
const typeInfo = node.typeInfo();
|
2895
|
+
if (!typeInfo.gqlType) {
|
2880
2896
|
return;
|
2881
2897
|
}
|
2882
|
-
const
|
2883
|
-
|
2884
|
-
|
2885
|
-
|
2886
|
-
|
2887
|
-
|
2888
|
-
|
2889
|
-
|
2890
|
-
|
2891
|
-
|
2892
|
-
|
2893
|
-
|
2894
|
-
|
2895
|
-
|
2896
|
-
|
2897
|
-
|
2898
|
-
|
2899
|
-
|
2900
|
-
|
2901
|
-
|
2902
|
-
|
2903
|
-
|
2904
|
-
|
2905
|
-
|
2906
|
-
|
2907
|
-
}
|
2908
|
-
}
|
2909
|
-
const { parent } = node;
|
2910
|
-
const hasIdFieldInInterfaceSelectionSet = parent &&
|
2911
|
-
parent.kind === 'InlineFragment' &&
|
2912
|
-
parent.parent &&
|
2913
|
-
parent.parent.kind === 'SelectionSet' &&
|
2914
|
-
parent.parent.selections.some(s => s.kind === 'Field' && s.name.value === fieldName);
|
2915
|
-
if (!found && !hasIdFieldInInterfaceSelectionSet) {
|
2916
|
-
context.report({
|
2917
|
-
loc: {
|
2918
|
-
start: {
|
2919
|
-
line: node.loc.start.line,
|
2920
|
-
column: node.loc.start.column - 1,
|
2921
|
-
},
|
2922
|
-
end: {
|
2923
|
-
line: node.loc.end.line,
|
2924
|
-
column: node.loc.end.column - 1,
|
2925
|
-
},
|
2926
|
-
},
|
2927
|
-
messageId: REQUIRE_ID_WHEN_AVAILABLE,
|
2928
|
-
data: {
|
2929
|
-
checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${Array.from(checkedFragmentSpreads).join(', ')})`,
|
2930
|
-
fieldName,
|
2931
|
-
},
|
2932
|
-
});
|
2933
|
-
}
|
2898
|
+
const rawType = getBaseType(typeInfo.gqlType);
|
2899
|
+
const isObjectType = rawType instanceof graphql.GraphQLObjectType;
|
2900
|
+
const isInterfaceType = rawType instanceof graphql.GraphQLInterfaceType;
|
2901
|
+
if (!isObjectType && !isInterfaceType) {
|
2902
|
+
return;
|
2903
|
+
}
|
2904
|
+
const fields = rawType.getFields();
|
2905
|
+
const hasIdFieldInType = idNames.some(name => fields[name]);
|
2906
|
+
if (!hasIdFieldInType) {
|
2907
|
+
return;
|
2908
|
+
}
|
2909
|
+
const checkedFragmentSpreads = new Set();
|
2910
|
+
let found = false;
|
2911
|
+
for (const selection of node.selections) {
|
2912
|
+
if (isFound(selection)) {
|
2913
|
+
found = true;
|
2914
|
+
}
|
2915
|
+
else if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
|
2916
|
+
found = (_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.selections.some(s => isFound(s));
|
2917
|
+
}
|
2918
|
+
else if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
|
2919
|
+
const [foundSpread] = siblings.getFragment(selection.name.value);
|
2920
|
+
if (foundSpread) {
|
2921
|
+
checkedFragmentSpreads.add(foundSpread.document.name.value);
|
2922
|
+
found = (_b = foundSpread.document.selectionSet) === null || _b === void 0 ? void 0 : _b.selections.some(s => isFound(s));
|
2934
2923
|
}
|
2935
2924
|
}
|
2925
|
+
if (found) {
|
2926
|
+
break;
|
2927
|
+
}
|
2928
|
+
}
|
2929
|
+
const { parent } = node;
|
2930
|
+
const hasIdFieldInInterfaceSelectionSet = parent &&
|
2931
|
+
parent.kind === graphql.Kind.INLINE_FRAGMENT &&
|
2932
|
+
parent.parent &&
|
2933
|
+
parent.parent.kind === graphql.Kind.SELECTION_SET &&
|
2934
|
+
parent.parent.selections.some(s => isFound(s));
|
2935
|
+
if (!found && !hasIdFieldInInterfaceSelectionSet) {
|
2936
|
+
context.report({
|
2937
|
+
loc: getLocation(node.loc),
|
2938
|
+
messageId: REQUIRE_ID_WHEN_AVAILABLE,
|
2939
|
+
data: {
|
2940
|
+
checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
|
2941
|
+
fieldName: idNames.map(name => `"${name}"`).join(' or '),
|
2942
|
+
},
|
2943
|
+
});
|
2936
2944
|
}
|
2937
2945
|
},
|
2938
2946
|
};
|
@@ -3024,7 +3032,7 @@ const rule$k = {
|
|
3024
3032
|
// eslint-disable-next-line no-console
|
3025
3033
|
console.warn(`Rule "selection-set-depth" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3026
3034
|
}
|
3027
|
-
const maxDepth = context.options[0]
|
3035
|
+
const { maxDepth } = context.options[0];
|
3028
3036
|
const ignore = context.options[0].ignore || [];
|
3029
3037
|
const checkFn = depthLimit(maxDepth, { ignore });
|
3030
3038
|
return {
|
package/index.mjs
CHANGED
@@ -2846,9 +2846,22 @@ const rule$j = {
|
|
2846
2846
|
recommended: true,
|
2847
2847
|
},
|
2848
2848
|
messages: {
|
2849
|
-
[REQUIRE_ID_WHEN_AVAILABLE]:
|
2849
|
+
[REQUIRE_ID_WHEN_AVAILABLE]: [
|
2850
|
+
`Field {{ fieldName }} must be selected when it's available on a type. Please make sure to include it in your selection set!`,
|
2851
|
+
`If you are using fragments, make sure that all used fragments {{ checkedFragments }}specifies the field {{ fieldName }}.`,
|
2852
|
+
].join('\n'),
|
2850
2853
|
},
|
2851
2854
|
schema: {
|
2855
|
+
definitions: {
|
2856
|
+
asString: {
|
2857
|
+
type: 'string',
|
2858
|
+
},
|
2859
|
+
asArray: {
|
2860
|
+
type: 'array',
|
2861
|
+
minItems: 1,
|
2862
|
+
uniqueItems: true,
|
2863
|
+
},
|
2864
|
+
},
|
2852
2865
|
type: 'array',
|
2853
2866
|
maxItems: 1,
|
2854
2867
|
items: {
|
@@ -2856,7 +2869,7 @@ const rule$j = {
|
|
2856
2869
|
additionalProperties: false,
|
2857
2870
|
properties: {
|
2858
2871
|
fieldName: {
|
2859
|
-
|
2872
|
+
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asArray' }],
|
2860
2873
|
default: DEFAULT_ID_FIELD_NAME,
|
2861
2874
|
},
|
2862
2875
|
},
|
@@ -2864,69 +2877,64 @@ const rule$j = {
|
|
2864
2877
|
},
|
2865
2878
|
},
|
2866
2879
|
create(context) {
|
2880
|
+
requireGraphQLSchemaFromContext('require-id-when-available', context);
|
2881
|
+
const siblings = requireSiblingsOperations('require-id-when-available', context);
|
2882
|
+
const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
|
2883
|
+
const idNames = Array.isArray(fieldName) ? fieldName : [fieldName];
|
2884
|
+
const isFound = (s) => s.kind === Kind.FIELD && idNames.includes(s.name.value);
|
2867
2885
|
return {
|
2868
2886
|
SelectionSet(node) {
|
2869
2887
|
var _a, _b;
|
2870
|
-
|
2871
|
-
|
2872
|
-
const fieldName = (context.options[0] || {}).fieldName || DEFAULT_ID_FIELD_NAME;
|
2873
|
-
if (!node.selections || node.selections.length === 0) {
|
2888
|
+
const typeInfo = node.typeInfo();
|
2889
|
+
if (!typeInfo.gqlType) {
|
2874
2890
|
return;
|
2875
2891
|
}
|
2876
|
-
const
|
2877
|
-
|
2878
|
-
|
2879
|
-
|
2880
|
-
|
2881
|
-
|
2882
|
-
|
2883
|
-
|
2884
|
-
|
2885
|
-
|
2886
|
-
|
2887
|
-
|
2888
|
-
|
2889
|
-
|
2890
|
-
|
2891
|
-
|
2892
|
-
|
2893
|
-
|
2894
|
-
|
2895
|
-
|
2896
|
-
|
2897
|
-
|
2898
|
-
|
2899
|
-
|
2900
|
-
|
2901
|
-
}
|
2902
|
-
}
|
2903
|
-
const { parent } = node;
|
2904
|
-
const hasIdFieldInInterfaceSelectionSet = parent &&
|
2905
|
-
parent.kind === 'InlineFragment' &&
|
2906
|
-
parent.parent &&
|
2907
|
-
parent.parent.kind === 'SelectionSet' &&
|
2908
|
-
parent.parent.selections.some(s => s.kind === 'Field' && s.name.value === fieldName);
|
2909
|
-
if (!found && !hasIdFieldInInterfaceSelectionSet) {
|
2910
|
-
context.report({
|
2911
|
-
loc: {
|
2912
|
-
start: {
|
2913
|
-
line: node.loc.start.line,
|
2914
|
-
column: node.loc.start.column - 1,
|
2915
|
-
},
|
2916
|
-
end: {
|
2917
|
-
line: node.loc.end.line,
|
2918
|
-
column: node.loc.end.column - 1,
|
2919
|
-
},
|
2920
|
-
},
|
2921
|
-
messageId: REQUIRE_ID_WHEN_AVAILABLE,
|
2922
|
-
data: {
|
2923
|
-
checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${Array.from(checkedFragmentSpreads).join(', ')})`,
|
2924
|
-
fieldName,
|
2925
|
-
},
|
2926
|
-
});
|
2927
|
-
}
|
2892
|
+
const rawType = getBaseType(typeInfo.gqlType);
|
2893
|
+
const isObjectType = rawType instanceof GraphQLObjectType;
|
2894
|
+
const isInterfaceType = rawType instanceof GraphQLInterfaceType;
|
2895
|
+
if (!isObjectType && !isInterfaceType) {
|
2896
|
+
return;
|
2897
|
+
}
|
2898
|
+
const fields = rawType.getFields();
|
2899
|
+
const hasIdFieldInType = idNames.some(name => fields[name]);
|
2900
|
+
if (!hasIdFieldInType) {
|
2901
|
+
return;
|
2902
|
+
}
|
2903
|
+
const checkedFragmentSpreads = new Set();
|
2904
|
+
let found = false;
|
2905
|
+
for (const selection of node.selections) {
|
2906
|
+
if (isFound(selection)) {
|
2907
|
+
found = true;
|
2908
|
+
}
|
2909
|
+
else if (selection.kind === Kind.INLINE_FRAGMENT) {
|
2910
|
+
found = (_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.selections.some(s => isFound(s));
|
2911
|
+
}
|
2912
|
+
else if (selection.kind === Kind.FRAGMENT_SPREAD) {
|
2913
|
+
const [foundSpread] = siblings.getFragment(selection.name.value);
|
2914
|
+
if (foundSpread) {
|
2915
|
+
checkedFragmentSpreads.add(foundSpread.document.name.value);
|
2916
|
+
found = (_b = foundSpread.document.selectionSet) === null || _b === void 0 ? void 0 : _b.selections.some(s => isFound(s));
|
2928
2917
|
}
|
2929
2918
|
}
|
2919
|
+
if (found) {
|
2920
|
+
break;
|
2921
|
+
}
|
2922
|
+
}
|
2923
|
+
const { parent } = node;
|
2924
|
+
const hasIdFieldInInterfaceSelectionSet = parent &&
|
2925
|
+
parent.kind === Kind.INLINE_FRAGMENT &&
|
2926
|
+
parent.parent &&
|
2927
|
+
parent.parent.kind === Kind.SELECTION_SET &&
|
2928
|
+
parent.parent.selections.some(s => isFound(s));
|
2929
|
+
if (!found && !hasIdFieldInInterfaceSelectionSet) {
|
2930
|
+
context.report({
|
2931
|
+
loc: getLocation(node.loc),
|
2932
|
+
messageId: REQUIRE_ID_WHEN_AVAILABLE,
|
2933
|
+
data: {
|
2934
|
+
checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
|
2935
|
+
fieldName: idNames.map(name => `"${name}"`).join(' or '),
|
2936
|
+
},
|
2937
|
+
});
|
2930
2938
|
}
|
2931
2939
|
},
|
2932
2940
|
};
|
@@ -3018,7 +3026,7 @@ const rule$k = {
|
|
3018
3026
|
// eslint-disable-next-line no-console
|
3019
3027
|
console.warn(`Rule "selection-set-depth" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3020
3028
|
}
|
3021
|
-
const maxDepth = context.options[0]
|
3029
|
+
const { maxDepth } = context.options[0];
|
3022
3030
|
const ignore = context.options[0].ignore || [];
|
3023
3031
|
const checkFn = depthLimit(maxDepth, { ignore });
|
3024
3032
|
return {
|
package/package.json
CHANGED