@graphql-eslint/eslint-plugin 3.3.0-alpha-0df1b98.0 → 3.3.0-alpha-d255c2b.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.
@@ -71,6 +71,18 @@ type Query {
71
71
  }
72
72
  ```
73
73
 
74
+ ### Correct
75
+
76
+ ```graphql
77
+ # eslint @graphql-eslint/naming-convention: ['error', { FieldDefinition: { style: 'camelCase', ignorePattern: '^(EAN13|UPC|UK)' } }]
78
+
79
+ type Product {
80
+ EAN13: String
81
+ UPC: String
82
+ UKFlag: String
83
+ }
84
+ ```
85
+
74
86
  ## Config Schema
75
87
 
76
88
  > It's possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with allowed `ASTNode` names which are described below.
@@ -276,6 +288,10 @@ Additional restrictions:
276
288
  * Minimum items: `1`
277
289
  * Unique items: `true`
278
290
 
291
+ ### `ignorePattern` (string)
292
+
293
+ Option to skip validation of some words, e.g. acronyms
294
+
279
295
  ## Resources
280
296
 
281
297
  - [Rule source](../../packages/plugin/src/rules/naming-convention.ts)
@@ -23,7 +23,7 @@ type User {
23
23
  }
24
24
 
25
25
  # Query
26
- query {
26
+ query user {
27
27
  user {
28
28
  name
29
29
  }
@@ -42,7 +42,7 @@ type User {
42
42
  }
43
43
 
44
44
  # Query
45
- query {
45
+ query user {
46
46
  user {
47
47
  id
48
48
  name
package/index.js CHANGED
@@ -1373,6 +1373,17 @@ const rule$4 = {
1373
1373
  type Query {
1374
1374
  users: [User!]!
1375
1375
  }
1376
+ `,
1377
+ },
1378
+ {
1379
+ title: 'Correct',
1380
+ usage: [{ FieldDefinition: { style: 'camelCase', ignorePattern: '^(EAN13|UPC|UK)' } }],
1381
+ code: /* GraphQL */ `
1382
+ type Product {
1383
+ EAN13: String
1384
+ UPC: String
1385
+ UKFlag: String
1386
+ }
1376
1387
  `,
1377
1388
  },
1378
1389
  ],
@@ -1442,6 +1453,10 @@ const rule$4 = {
1442
1453
  minItems: 1,
1443
1454
  items: { type: 'string' },
1444
1455
  },
1456
+ ignorePattern: {
1457
+ type: 'string',
1458
+ description: 'Option to skip validation of some words, e.g. acronyms',
1459
+ },
1445
1460
  },
1446
1461
  },
1447
1462
  },
@@ -1496,15 +1511,15 @@ const rule$4 = {
1496
1511
  if (!node) {
1497
1512
  return;
1498
1513
  }
1499
- const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
1514
+ const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern } = normalisePropertyOption(selector);
1500
1515
  const nodeType = KindToDisplayName[n.kind] || n.kind;
1501
1516
  const nodeName = node.value;
1502
1517
  const error = getError();
1503
1518
  if (error) {
1504
1519
  const { errorMessage, renameToName } = error;
1505
- const [leadingUnderscore] = nodeName.match(/^_*/);
1506
- const [trailingUnderscore] = nodeName.match(/_*$/);
1507
- const suggestedName = leadingUnderscore + renameToName + trailingUnderscore;
1520
+ const [leadingUnderscores] = nodeName.match(/^_*/);
1521
+ const [trailingUnderscores] = nodeName.match(/_*$/);
1522
+ const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
1508
1523
  context.report({
1509
1524
  loc: getLocation(node.loc, node.value),
1510
1525
  message: `${nodeType} "${nodeName}" should ${errorMessage}`,
@@ -1518,6 +1533,9 @@ const rule$4 = {
1518
1533
  }
1519
1534
  function getError() {
1520
1535
  const name = nodeName.replace(/(^_+)|(_+$)/g, '');
1536
+ if (ignorePattern && new RegExp(ignorePattern, 'u').test(name)) {
1537
+ return;
1538
+ }
1521
1539
  if (prefix && !name.startsWith(prefix)) {
1522
1540
  return {
1523
1541
  errorMessage: `have "${prefix}" prefix`,
@@ -1544,8 +1562,12 @@ const rule$4 = {
1544
1562
  renameToName: name.replace(new RegExp(`${forbiddenSuffix}$`), ''),
1545
1563
  };
1546
1564
  }
1565
+ // Style is optional
1566
+ if (!style) {
1567
+ return;
1568
+ }
1547
1569
  const caseRegex = StyleToRegex[style];
1548
- if (caseRegex && !caseRegex.test(name)) {
1570
+ if (!caseRegex.test(name)) {
1549
1571
  return {
1550
1572
  errorMessage: `be in ${style} format`,
1551
1573
  renameToName: convertCase(style, name),
@@ -2866,8 +2888,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2866
2888
  }
2867
2889
  };
2868
2890
 
2869
- const RULE_ID$2 = 'require-id-when-available';
2870
- const MESSAGE_ID = 'REQUIRE_ID_WHEN_AVAILABLE';
2891
+ const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2871
2892
  const DEFAULT_ID_FIELD_NAME = 'id';
2872
2893
  const rule$j = {
2873
2894
  meta: {
@@ -2875,7 +2896,7 @@ const rule$j = {
2875
2896
  docs: {
2876
2897
  category: 'Operations',
2877
2898
  description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
2878
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
2899
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-id-when-available.md',
2879
2900
  requiresSchema: true,
2880
2901
  requiresSiblings: true,
2881
2902
  examples: [
@@ -2889,7 +2910,7 @@ const rule$j = {
2889
2910
  }
2890
2911
 
2891
2912
  # Query
2892
- query {
2913
+ query user {
2893
2914
  user {
2894
2915
  name
2895
2916
  }
@@ -2906,7 +2927,7 @@ const rule$j = {
2906
2927
  }
2907
2928
 
2908
2929
  # Query
2909
- query {
2930
+ query user {
2910
2931
  user {
2911
2932
  id
2912
2933
  name
@@ -2918,7 +2939,7 @@ const rule$j = {
2918
2939
  recommended: true,
2919
2940
  },
2920
2941
  messages: {
2921
- [MESSAGE_ID]: [
2942
+ [REQUIRE_ID_WHEN_AVAILABLE]: [
2922
2943
  `Field {{ fieldName }} must be selected when it's available on a type. Please make sure to include it in your selection set!`,
2923
2944
  `If you are using fragments, make sure that all used fragments {{ checkedFragments }}specifies the field {{ fieldName }}.`,
2924
2945
  ].join('\n'),
@@ -2949,16 +2970,14 @@ const rule$j = {
2949
2970
  },
2950
2971
  },
2951
2972
  create(context) {
2952
- requireGraphQLSchemaFromContext(RULE_ID$2, context);
2953
- const siblings = requireSiblingsOperations(RULE_ID$2, context);
2973
+ requireGraphQLSchemaFromContext('require-id-when-available', context);
2974
+ const siblings = requireSiblingsOperations('require-id-when-available', context);
2954
2975
  const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
2955
- const idNames = utils.asArray(fieldName);
2976
+ const idNames = Array.isArray(fieldName) ? fieldName : [fieldName];
2956
2977
  const isFound = (s) => s.kind === graphql.Kind.FIELD && idNames.includes(s.name.value);
2957
- // Skip check selections in FragmentDefinition
2958
- const selector = 'OperationDefinition SelectionSet[parent.kind!=OperationDefinition]';
2959
2978
  return {
2960
- [selector](node) {
2961
- var _a;
2979
+ SelectionSet(node) {
2980
+ var _a, _b;
2962
2981
  const typeInfo = node.typeInfo();
2963
2982
  if (!typeInfo.gqlType) {
2964
2983
  return;
@@ -2975,38 +2994,41 @@ const rule$j = {
2975
2994
  return;
2976
2995
  }
2977
2996
  const checkedFragmentSpreads = new Set();
2997
+ let found = false;
2978
2998
  for (const selection of node.selections) {
2979
2999
  if (isFound(selection)) {
2980
- return;
3000
+ found = true;
2981
3001
  }
2982
- if (selection.kind === graphql.Kind.INLINE_FRAGMENT && selection.selectionSet.selections.some(isFound)) {
2983
- return;
3002
+ else if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
3003
+ found = (_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.selections.some(s => isFound(s));
2984
3004
  }
2985
- if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
3005
+ else if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
2986
3006
  const [foundSpread] = siblings.getFragment(selection.name.value);
2987
3007
  if (foundSpread) {
2988
3008
  checkedFragmentSpreads.add(foundSpread.document.name.value);
2989
- if (foundSpread.document.selectionSet.selections.some(isFound)) {
2990
- return;
2991
- }
3009
+ found = (_b = foundSpread.document.selectionSet) === null || _b === void 0 ? void 0 : _b.selections.some(s => isFound(s));
2992
3010
  }
2993
3011
  }
3012
+ if (found) {
3013
+ break;
3014
+ }
2994
3015
  }
2995
3016
  const { parent } = node;
2996
- const hasIdFieldInInterfaceSelectionSet = (parent === null || parent === void 0 ? void 0 : parent.kind) === graphql.Kind.INLINE_FRAGMENT &&
2997
- ((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.kind) === graphql.Kind.SELECTION_SET &&
2998
- parent.parent.selections.some(isFound);
2999
- if (hasIdFieldInInterfaceSelectionSet) {
3000
- return;
3017
+ const hasIdFieldInInterfaceSelectionSet = parent &&
3018
+ parent.kind === graphql.Kind.INLINE_FRAGMENT &&
3019
+ parent.parent &&
3020
+ parent.parent.kind === graphql.Kind.SELECTION_SET &&
3021
+ parent.parent.selections.some(s => isFound(s));
3022
+ if (!found && !hasIdFieldInInterfaceSelectionSet) {
3023
+ context.report({
3024
+ loc: getLocation(node.loc),
3025
+ messageId: REQUIRE_ID_WHEN_AVAILABLE,
3026
+ data: {
3027
+ checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
3028
+ fieldName: idNames.map(name => `"${name}"`).join(' or '),
3029
+ },
3030
+ });
3001
3031
  }
3002
- context.report({
3003
- loc: getLocation(node.loc),
3004
- messageId: MESSAGE_ID,
3005
- data: {
3006
- checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
3007
- fieldName: idNames.map(name => `"${name}"`).join(' or '),
3008
- },
3009
- });
3010
3032
  },
3011
3033
  };
3012
3034
  },
package/index.mjs CHANGED
@@ -1367,6 +1367,17 @@ const rule$4 = {
1367
1367
  type Query {
1368
1368
  users: [User!]!
1369
1369
  }
1370
+ `,
1371
+ },
1372
+ {
1373
+ title: 'Correct',
1374
+ usage: [{ FieldDefinition: { style: 'camelCase', ignorePattern: '^(EAN13|UPC|UK)' } }],
1375
+ code: /* GraphQL */ `
1376
+ type Product {
1377
+ EAN13: String
1378
+ UPC: String
1379
+ UKFlag: String
1380
+ }
1370
1381
  `,
1371
1382
  },
1372
1383
  ],
@@ -1436,6 +1447,10 @@ const rule$4 = {
1436
1447
  minItems: 1,
1437
1448
  items: { type: 'string' },
1438
1449
  },
1450
+ ignorePattern: {
1451
+ type: 'string',
1452
+ description: 'Option to skip validation of some words, e.g. acronyms',
1453
+ },
1439
1454
  },
1440
1455
  },
1441
1456
  },
@@ -1490,15 +1505,15 @@ const rule$4 = {
1490
1505
  if (!node) {
1491
1506
  return;
1492
1507
  }
1493
- const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
1508
+ const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern } = normalisePropertyOption(selector);
1494
1509
  const nodeType = KindToDisplayName[n.kind] || n.kind;
1495
1510
  const nodeName = node.value;
1496
1511
  const error = getError();
1497
1512
  if (error) {
1498
1513
  const { errorMessage, renameToName } = error;
1499
- const [leadingUnderscore] = nodeName.match(/^_*/);
1500
- const [trailingUnderscore] = nodeName.match(/_*$/);
1501
- const suggestedName = leadingUnderscore + renameToName + trailingUnderscore;
1514
+ const [leadingUnderscores] = nodeName.match(/^_*/);
1515
+ const [trailingUnderscores] = nodeName.match(/_*$/);
1516
+ const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
1502
1517
  context.report({
1503
1518
  loc: getLocation(node.loc, node.value),
1504
1519
  message: `${nodeType} "${nodeName}" should ${errorMessage}`,
@@ -1512,6 +1527,9 @@ const rule$4 = {
1512
1527
  }
1513
1528
  function getError() {
1514
1529
  const name = nodeName.replace(/(^_+)|(_+$)/g, '');
1530
+ if (ignorePattern && new RegExp(ignorePattern, 'u').test(name)) {
1531
+ return;
1532
+ }
1515
1533
  if (prefix && !name.startsWith(prefix)) {
1516
1534
  return {
1517
1535
  errorMessage: `have "${prefix}" prefix`,
@@ -1538,8 +1556,12 @@ const rule$4 = {
1538
1556
  renameToName: name.replace(new RegExp(`${forbiddenSuffix}$`), ''),
1539
1557
  };
1540
1558
  }
1559
+ // Style is optional
1560
+ if (!style) {
1561
+ return;
1562
+ }
1541
1563
  const caseRegex = StyleToRegex[style];
1542
- if (caseRegex && !caseRegex.test(name)) {
1564
+ if (!caseRegex.test(name)) {
1543
1565
  return {
1544
1566
  errorMessage: `be in ${style} format`,
1545
1567
  renameToName: convertCase(style, name),
@@ -2860,8 +2882,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2860
2882
  }
2861
2883
  };
2862
2884
 
2863
- const RULE_ID$2 = 'require-id-when-available';
2864
- const MESSAGE_ID = 'REQUIRE_ID_WHEN_AVAILABLE';
2885
+ const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2865
2886
  const DEFAULT_ID_FIELD_NAME = 'id';
2866
2887
  const rule$j = {
2867
2888
  meta: {
@@ -2869,7 +2890,7 @@ const rule$j = {
2869
2890
  docs: {
2870
2891
  category: 'Operations',
2871
2892
  description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
2872
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
2893
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-id-when-available.md',
2873
2894
  requiresSchema: true,
2874
2895
  requiresSiblings: true,
2875
2896
  examples: [
@@ -2883,7 +2904,7 @@ const rule$j = {
2883
2904
  }
2884
2905
 
2885
2906
  # Query
2886
- query {
2907
+ query user {
2887
2908
  user {
2888
2909
  name
2889
2910
  }
@@ -2900,7 +2921,7 @@ const rule$j = {
2900
2921
  }
2901
2922
 
2902
2923
  # Query
2903
- query {
2924
+ query user {
2904
2925
  user {
2905
2926
  id
2906
2927
  name
@@ -2912,7 +2933,7 @@ const rule$j = {
2912
2933
  recommended: true,
2913
2934
  },
2914
2935
  messages: {
2915
- [MESSAGE_ID]: [
2936
+ [REQUIRE_ID_WHEN_AVAILABLE]: [
2916
2937
  `Field {{ fieldName }} must be selected when it's available on a type. Please make sure to include it in your selection set!`,
2917
2938
  `If you are using fragments, make sure that all used fragments {{ checkedFragments }}specifies the field {{ fieldName }}.`,
2918
2939
  ].join('\n'),
@@ -2943,16 +2964,14 @@ const rule$j = {
2943
2964
  },
2944
2965
  },
2945
2966
  create(context) {
2946
- requireGraphQLSchemaFromContext(RULE_ID$2, context);
2947
- const siblings = requireSiblingsOperations(RULE_ID$2, context);
2967
+ requireGraphQLSchemaFromContext('require-id-when-available', context);
2968
+ const siblings = requireSiblingsOperations('require-id-when-available', context);
2948
2969
  const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
2949
- const idNames = asArray(fieldName);
2970
+ const idNames = Array.isArray(fieldName) ? fieldName : [fieldName];
2950
2971
  const isFound = (s) => s.kind === Kind.FIELD && idNames.includes(s.name.value);
2951
- // Skip check selections in FragmentDefinition
2952
- const selector = 'OperationDefinition SelectionSet[parent.kind!=OperationDefinition]';
2953
2972
  return {
2954
- [selector](node) {
2955
- var _a;
2973
+ SelectionSet(node) {
2974
+ var _a, _b;
2956
2975
  const typeInfo = node.typeInfo();
2957
2976
  if (!typeInfo.gqlType) {
2958
2977
  return;
@@ -2969,38 +2988,41 @@ const rule$j = {
2969
2988
  return;
2970
2989
  }
2971
2990
  const checkedFragmentSpreads = new Set();
2991
+ let found = false;
2972
2992
  for (const selection of node.selections) {
2973
2993
  if (isFound(selection)) {
2974
- return;
2994
+ found = true;
2975
2995
  }
2976
- if (selection.kind === Kind.INLINE_FRAGMENT && selection.selectionSet.selections.some(isFound)) {
2977
- return;
2996
+ else if (selection.kind === Kind.INLINE_FRAGMENT) {
2997
+ found = (_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.selections.some(s => isFound(s));
2978
2998
  }
2979
- if (selection.kind === Kind.FRAGMENT_SPREAD) {
2999
+ else if (selection.kind === Kind.FRAGMENT_SPREAD) {
2980
3000
  const [foundSpread] = siblings.getFragment(selection.name.value);
2981
3001
  if (foundSpread) {
2982
3002
  checkedFragmentSpreads.add(foundSpread.document.name.value);
2983
- if (foundSpread.document.selectionSet.selections.some(isFound)) {
2984
- return;
2985
- }
3003
+ found = (_b = foundSpread.document.selectionSet) === null || _b === void 0 ? void 0 : _b.selections.some(s => isFound(s));
2986
3004
  }
2987
3005
  }
3006
+ if (found) {
3007
+ break;
3008
+ }
2988
3009
  }
2989
3010
  const { parent } = node;
2990
- const hasIdFieldInInterfaceSelectionSet = (parent === null || parent === void 0 ? void 0 : parent.kind) === Kind.INLINE_FRAGMENT &&
2991
- ((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.kind) === Kind.SELECTION_SET &&
2992
- parent.parent.selections.some(isFound);
2993
- if (hasIdFieldInInterfaceSelectionSet) {
2994
- return;
3011
+ const hasIdFieldInInterfaceSelectionSet = parent &&
3012
+ parent.kind === Kind.INLINE_FRAGMENT &&
3013
+ parent.parent &&
3014
+ parent.parent.kind === Kind.SELECTION_SET &&
3015
+ parent.parent.selections.some(s => isFound(s));
3016
+ if (!found && !hasIdFieldInInterfaceSelectionSet) {
3017
+ context.report({
3018
+ loc: getLocation(node.loc),
3019
+ messageId: REQUIRE_ID_WHEN_AVAILABLE,
3020
+ data: {
3021
+ checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
3022
+ fieldName: idNames.map(name => `"${name}"`).join(' or '),
3023
+ },
3024
+ });
2995
3025
  }
2996
- context.report({
2997
- loc: getLocation(node.loc),
2998
- messageId: MESSAGE_ID,
2999
- data: {
3000
- checkedFragments: checkedFragmentSpreads.size === 0 ? '' : `(${[...checkedFragmentSpreads].join(', ')}) `,
3001
- fieldName: idNames.map(name => `"${name}"`).join(' or '),
3002
- },
3003
- });
3004
3026
  },
3005
3027
  };
3006
3028
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-eslint/eslint-plugin",
3
- "version": "3.3.0-alpha-0df1b98.0",
3
+ "version": "3.3.0-alpha-d255c2b.0",
4
4
  "description": "GraphQL plugin for ESLint",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
package/rules/index.d.ts CHANGED
@@ -48,7 +48,9 @@ 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<[import("./require-id-when-available").RequireIdWhenAvailableRuleConfig], true>;
51
+ 'require-id-when-available': import("..").GraphQLESLintRule<[{
52
+ fieldName: string;
53
+ }], true>;
52
54
  'selection-set-depth': import("..").GraphQLESLintRule<[{
53
55
  maxDepth: number;
54
56
  ignore?: string[];
@@ -23,6 +23,7 @@ declare type PropertySchema = {
23
23
  prefix?: string;
24
24
  forbiddenPrefixes?: string[];
25
25
  forbiddenSuffixes?: string[];
26
+ ignorePattern?: string;
26
27
  };
27
28
  declare type Options = AllowedStyle | PropertySchema;
28
29
  export declare type NamingConventionRuleConfig = {
@@ -1,6 +1,6 @@
1
1
  import { GraphQLESLintRule } from '../types';
2
- export declare type RequireIdWhenAvailableRuleConfig = {
3
- fieldName: string | string[];
2
+ declare type RequireIdWhenAvailableRuleConfig = {
3
+ fieldName: string;
4
4
  };
5
5
  declare const rule: GraphQLESLintRule<[RequireIdWhenAvailableRuleConfig], true>;
6
6
  export default rule;