@graphql-eslint/eslint-plugin 3.3.0-alpha-1c01242.0 → 3.4.0-alpha-d7fc26d.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/README.md CHANGED
@@ -42,7 +42,7 @@ npm install --save-dev @graphql-eslint/eslint-plugin
42
42
 
43
43
  > Make sure you have `graphql` dependency in your project.
44
44
 
45
- ### Configuration
45
+ ## Configuration
46
46
 
47
47
  To get started, define an override in your ESLint config to apply this plugin to `.graphql` files. Add the [rules](docs/README.md) you want applied.
48
48
 
@@ -65,7 +65,7 @@ To get started, define an override in your ESLint config to apply this plugin to
65
65
 
66
66
  If your GraphQL definitions are defined only in `.graphql` files, and you're only using rules that apply to individual files, you should be good to go 👍. If you would like use a remote schema or use rules that apply across the entire collection of definitions at once, see [here](#using-a-remote-schema-or-rules-with-constraints-that-span-the-entire-schema).
67
67
 
68
- #### Tell ESLint to apply this plugin to GraphQL definitions defined in code files
68
+ ### Apply this plugin to GraphQL definitions defined in code files
69
69
 
70
70
  If you are defining GraphQL schema or GraphQL operations in code files, you'll want to define an additional override to extend the functionality of this plugin to the schema and operations in those files.
71
71
 
@@ -90,7 +90,7 @@ If you are defining GraphQL schema or GraphQL operations in code files, you'll w
90
90
 
91
91
  Under the hood, specifying the `@graphql-eslint/graphql` processor for code files will cause `graphql-eslint/graphql` to extract the schema and operation definitions from these files into virtual GraphQL documents with `.graphql` extensions. This will allow the overrides you've defined for `.graphql` files, via `"files": ["*.graphql"]`, to get applied to the definitions defined in your code files.
92
92
 
93
- #### Using a remote schema or rules with constraints that span the entire schema
93
+ ### Extended linting rules with GraphQL Schema
94
94
 
95
95
  Some rules require an understanding of the entire schema at once. For example, [no-unreachable-types](https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-unreachable-types.md#no-unreachable-types) checks that all types are reachable by root-level fields.
96
96
 
@@ -120,7 +120,7 @@ The parser allows you to specify a json file / graphql files(s) / url / raw stri
120
120
 
121
121
  > Some rules require type information to operate, it's marked in the docs for each rule!
122
122
 
123
- #### Extended linting rules with siblings operations
123
+ ### Extended linting rules with siblings operations
124
124
 
125
125
  While implementing this tool, we had to find solutions for a better integration of the GraphQL ecosystem and ESLint core.
126
126
 
@@ -180,7 +180,7 @@ You can find a list of [ESLint directives here](https://eslint.org/docs/2.13.1/u
180
180
 
181
181
  You can find a complete list of [all available rules here](docs/README.md).
182
182
 
183
- ## Deprecated Rules
183
+ ### Deprecated Rules
184
184
 
185
185
  See [docs/deprecated-rules.md](docs/deprecated-rules.md).
186
186
 
@@ -201,7 +201,7 @@ See [docs/deprecated-rules.md](docs/deprecated-rules.md).
201
201
 
202
202
  > If you are in a monorepo project, you probably need both sets of rules.
203
203
 
204
- ## Config usage
204
+ ### Config usage
205
205
 
206
206
  For example, to enable the `schema-recommended` config, enable it in your `.eslintrc` file with the `extends` option:
207
207
 
@@ -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)
@@ -2,7 +2,7 @@
2
2
 
3
3
  - Category: `Schema`
4
4
  - Rule name: `@graphql-eslint/unique-enum-value-names`
5
- - Requires GraphQL Schema: `false` [ℹ️](../../README.md#extended-linting-rules-with-graphql-schema)
5
+ - Requires GraphQL Schema: `true` [ℹ️](../../README.md#extended-linting-rules-with-graphql-schema)
6
6
  - Requires GraphQL Operations: `false` [ℹ️](../../README.md#extended-linting-rules-with-siblings-operations)
7
7
 
8
8
  A GraphQL enum type is only valid if all its values are uniquely named.
@@ -4,7 +4,7 @@
4
4
 
5
5
  - Category: `Schema`
6
6
  - Rule name: `@graphql-eslint/unique-field-definition-names`
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
  A GraphQL complex type is only valid if all its fields are uniquely named.
package/index.js CHANGED
@@ -10,6 +10,7 @@ const fs = require('fs');
10
10
  const path = require('path');
11
11
  const utils = require('@graphql-tools/utils');
12
12
  const lowerCase = _interopDefault(require('lodash.lowercase'));
13
+ const chalk = _interopDefault(require('chalk'));
13
14
  const depthLimit = _interopDefault(require('graphql-depth-limit'));
14
15
  const graphqlTagPluck = require('@graphql-tools/graphql-tag-pluck');
15
16
  const graphqlConfig = require('graphql-config');
@@ -192,6 +193,12 @@ function requireGraphQLSchemaFromContext(ruleName, context) {
192
193
  }
193
194
  return context.parserServices.schema;
194
195
  }
196
+ const logger = {
197
+ // eslint-disable-next-line no-console
198
+ error: (...args) => console.error(chalk.red('error'), '[graphql-eslint]', chalk(...args)),
199
+ // eslint-disable-next-line no-console
200
+ warn: (...args) => console.warn(chalk.yellow('warning'), '[graphql-eslint]', chalk(...args)),
201
+ };
195
202
  function requireReachableTypesFromContext(ruleName, context) {
196
203
  const schema = requireGraphQLSchemaFromContext(ruleName, context);
197
204
  return context.parserServices.reachableTypes(schema);
@@ -326,12 +333,12 @@ function getLocation(loc, fieldName = '', offset) {
326
333
  };
327
334
  }
328
335
 
329
- function validateDocument(context, schema = null, documentNode, rule, isSchemaToExtend = false) {
336
+ function validateDocument(context, schema = null, documentNode, rule) {
330
337
  if (documentNode.definitions.length === 0) {
331
338
  return;
332
339
  }
333
340
  try {
334
- const validationErrors = schema && !isSchemaToExtend
341
+ const validationErrors = schema
335
342
  ? graphql.validate(schema, documentNode, [rule])
336
343
  : validate.validateSDL(documentNode, schema, [rule]);
337
344
  for (const error of validationErrors) {
@@ -442,17 +449,16 @@ const validationToRule = (ruleId, ruleName, docs, getDocumentNode) => {
442
449
  },
443
450
  create(context) {
444
451
  if (!ruleFn) {
445
- // eslint-disable-next-line no-console
446
- console.warn(`You rule "${ruleId}" depends on a GraphQL validation rule "${ruleName}" but it's not available in the "graphql-js" version you are using. Skipping...`);
452
+ logger.warn(`You rule "${ruleId}" depends on a GraphQL validation rule "${ruleName}" but it's not available in the "graphql-js" version you are using. Skipping...`);
447
453
  return {};
448
454
  }
449
- const schema = docs.requiresSchema ? requireGraphQLSchemaFromContext(ruleId, context) : null;
450
455
  return {
451
456
  Document(node) {
457
+ const schema = docs.requiresSchema ? requireGraphQLSchemaFromContext(ruleId, context) : null;
452
458
  const documentNode = getDocumentNode
453
459
  ? getDocumentNode({ ruleId, context, schema, node: node.rawNode() })
454
460
  : node.rawNode();
455
- validateDocument(context, schema, documentNode, ruleFn, docs.requiresSchemaToExtend);
461
+ validateDocument(context, schema, documentNode, ruleFn);
456
462
  },
457
463
  };
458
464
  },
@@ -604,7 +610,6 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
604
610
  description: `A type extension is only valid if the type is defined and has the same kind.`,
605
611
  recommended: false,
606
612
  requiresSchema: true,
607
- requiresSchemaToExtend: true,
608
613
  }), validationToRule('provided-required-arguments', 'ProvidedRequiredArguments', {
609
614
  category: ['Schema', 'Operations'],
610
615
  description: `A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.`,
@@ -1376,6 +1381,17 @@ const rule$4 = {
1376
1381
  type Query {
1377
1382
  users: [User!]!
1378
1383
  }
1384
+ `,
1385
+ },
1386
+ {
1387
+ title: 'Correct',
1388
+ usage: [{ FieldDefinition: { style: 'camelCase', ignorePattern: '^(EAN13|UPC|UK)' } }],
1389
+ code: /* GraphQL */ `
1390
+ type Product {
1391
+ EAN13: String
1392
+ UPC: String
1393
+ UKFlag: String
1394
+ }
1379
1395
  `,
1380
1396
  },
1381
1397
  ],
@@ -1445,6 +1461,10 @@ const rule$4 = {
1445
1461
  minItems: 1,
1446
1462
  items: { type: 'string' },
1447
1463
  },
1464
+ ignorePattern: {
1465
+ type: 'string',
1466
+ description: 'Option to skip validation of some words, e.g. acronyms',
1467
+ },
1448
1468
  },
1449
1469
  },
1450
1470
  },
@@ -1499,15 +1519,15 @@ const rule$4 = {
1499
1519
  if (!node) {
1500
1520
  return;
1501
1521
  }
1502
- const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
1522
+ const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern } = normalisePropertyOption(selector);
1503
1523
  const nodeType = KindToDisplayName[n.kind] || n.kind;
1504
1524
  const nodeName = node.value;
1505
1525
  const error = getError();
1506
1526
  if (error) {
1507
1527
  const { errorMessage, renameToName } = error;
1508
- const [leadingUnderscore] = nodeName.match(/^_*/);
1509
- const [trailingUnderscore] = nodeName.match(/_*$/);
1510
- const suggestedName = leadingUnderscore + renameToName + trailingUnderscore;
1528
+ const [leadingUnderscores] = nodeName.match(/^_*/);
1529
+ const [trailingUnderscores] = nodeName.match(/_*$/);
1530
+ const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
1511
1531
  context.report({
1512
1532
  loc: getLocation(node.loc, node.value),
1513
1533
  message: `${nodeType} "${nodeName}" should ${errorMessage}`,
@@ -1521,6 +1541,9 @@ const rule$4 = {
1521
1541
  }
1522
1542
  function getError() {
1523
1543
  const name = nodeName.replace(/(^_+)|(_+$)/g, '');
1544
+ if (ignorePattern && new RegExp(ignorePattern, 'u').test(name)) {
1545
+ return;
1546
+ }
1524
1547
  if (prefix && !name.startsWith(prefix)) {
1525
1548
  return {
1526
1549
  errorMessage: `have "${prefix}" prefix`,
@@ -1547,8 +1570,12 @@ const rule$4 = {
1547
1570
  renameToName: name.replace(new RegExp(`${forbiddenSuffix}$`), ''),
1548
1571
  };
1549
1572
  }
1573
+ // Style is optional
1574
+ if (!style) {
1575
+ return;
1576
+ }
1550
1577
  const caseRegex = StyleToRegex[style];
1551
- if (caseRegex && !caseRegex.test(name)) {
1578
+ if (!caseRegex.test(name)) {
1552
1579
  return {
1553
1580
  errorMessage: `be in ${style} format`,
1554
1581
  renameToName: convertCase(style, name),
@@ -3015,12 +3042,13 @@ const rule$j = {
3015
3042
  },
3016
3043
  };
3017
3044
 
3045
+ const RULE_ID$2 = 'selection-set-depth';
3018
3046
  const rule$k = {
3019
3047
  meta: {
3020
3048
  docs: {
3021
3049
  category: 'Operations',
3022
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).`,
3023
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/selection-set-depth.md',
3051
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
3024
3052
  requiresSiblings: true,
3025
3053
  examples: [
3026
3054
  {
@@ -3094,11 +3122,10 @@ const rule$k = {
3094
3122
  create(context) {
3095
3123
  let siblings = null;
3096
3124
  try {
3097
- siblings = requireSiblingsOperations('selection-set-depth', context);
3125
+ siblings = requireSiblingsOperations(RULE_ID$2, context);
3098
3126
  }
3099
3127
  catch (e) {
3100
- // eslint-disable-next-line no-console
3101
- console.warn(`Rule "selection-set-depth" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
3128
+ logger.warn(`Rule "${RULE_ID$2}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
3102
3129
  }
3103
3130
  const { maxDepth } = context.options[0];
3104
3131
  const ignore = context.options[0].ignore || [];
@@ -3123,8 +3150,7 @@ const rule$k = {
3123
3150
  });
3124
3151
  }
3125
3152
  catch (e) {
3126
- // eslint-disable-next-line no-console
3127
- console.warn(`Rule "selection-set-depth" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, 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);
3128
3154
  }
3129
3155
  },
3130
3156
  };
@@ -3506,8 +3532,7 @@ function createGraphqlProcessor() {
3506
3532
  }
3507
3533
  }
3508
3534
  catch (e) {
3509
- // eslint-disable-next-line no-console
3510
- console.warn(`[graphql-eslint/processor]: Unable to process file "${filename}": `, e);
3535
+ logger.warn(`Unable to process file "${filename}"`, e);
3511
3536
  }
3512
3537
  }
3513
3538
  return [text];
@@ -3547,20 +3572,25 @@ const schemaCache = new Map();
3547
3572
  function getSchema(options = {}, gqlConfig) {
3548
3573
  const realFilepath = options.filePath ? getOnDiskFilepath(options.filePath) : null;
3549
3574
  const projectForFile = realFilepath ? gqlConfig.getProjectForFile(realFilepath) : gqlConfig.getDefault();
3550
- const schemaKey = utils.asArray(projectForFile.schema)
3551
- .sort()
3552
- .join(',');
3575
+ const schemaKey = utils.asArray(projectForFile.schema).sort().join(',');
3553
3576
  if (!schemaKey) {
3554
3577
  return null;
3555
3578
  }
3556
- let schema = schemaCache.get(schemaKey);
3557
- if (!schema) {
3579
+ if (schemaCache.has(schemaKey)) {
3580
+ return schemaCache.get(schemaKey);
3581
+ }
3582
+ let schema;
3583
+ try {
3558
3584
  schema = projectForFile.loadSchemaSync(projectForFile.schema, 'GraphQLSchema', {
3559
3585
  cache: loaderCache,
3560
- ...options.schemaOptions
3586
+ ...options.schemaOptions,
3561
3587
  });
3562
- schemaCache.set(schemaKey, schema);
3563
3588
  }
3589
+ catch (e) {
3590
+ schema = null;
3591
+ logger.error('Error while loading schema\n', e);
3592
+ }
3593
+ schemaCache.set(schemaKey, schema);
3564
3594
  return schema;
3565
3595
  }
3566
3596
 
@@ -3573,10 +3603,10 @@ const handleVirtualPath = (documents) => {
3573
3603
  return source;
3574
3604
  }
3575
3605
  (_a = filepathMap[location]) !== null && _a !== void 0 ? _a : (filepathMap[location] = -1);
3576
- const index = filepathMap[location] += 1;
3606
+ const index = (filepathMap[location] += 1);
3577
3607
  return {
3578
3608
  ...source,
3579
- location: path.resolve(location, `${index}_document.graphql`)
3609
+ location: path.resolve(location, `${index}_document.graphql`),
3580
3610
  };
3581
3611
  });
3582
3612
  };
@@ -3585,9 +3615,7 @@ const siblingOperationsCache = new Map();
3585
3615
  const getSiblings = (filePath, gqlConfig) => {
3586
3616
  const realFilepath = filePath ? getOnDiskFilepath(filePath) : null;
3587
3617
  const projectForFile = realFilepath ? gqlConfig.getProjectForFile(realFilepath) : gqlConfig.getDefault();
3588
- const documentsKey = utils.asArray(projectForFile.documents)
3589
- .sort()
3590
- .join(',');
3618
+ const documentsKey = utils.asArray(projectForFile.documents).sort().join(',');
3591
3619
  if (!documentsKey) {
3592
3620
  return [];
3593
3621
  }
@@ -3595,7 +3623,7 @@ const getSiblings = (filePath, gqlConfig) => {
3595
3623
  if (!siblings) {
3596
3624
  const documents = projectForFile.loadDocumentsSync(projectForFile.documents, {
3597
3625
  skipGraphQLImport: true,
3598
- cache: loaderCache
3626
+ cache: loaderCache,
3599
3627
  });
3600
3628
  siblings = handleVirtualPath(documents);
3601
3629
  operationsCache.set(documentsKey, siblings);
@@ -3608,8 +3636,7 @@ function getSiblingOperations(options, gqlConfig) {
3608
3636
  let printed = false;
3609
3637
  const noopWarn = () => {
3610
3638
  if (!printed) {
3611
- // eslint-disable-next-line no-console
3612
- console.warn(`getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!`);
3639
+ logger.warn(`getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!`);
3613
3640
  printed = true;
3614
3641
  }
3615
3642
  return [];
@@ -3673,8 +3700,7 @@ function getSiblingOperations(options, gqlConfig) {
3673
3700
  const name = spread.name.value;
3674
3701
  const fragmentInfo = getFragment(name);
3675
3702
  if (fragmentInfo.length === 0) {
3676
- // eslint-disable-next-line no-console
3677
- console.warn(`Unable to locate fragment named "${name}", please make sure it's loaded using "parserOptions.operations"`);
3703
+ logger.warn(`Unable to locate fragment named "${name}", please make sure it's loaded using "parserOptions.operations"`);
3678
3704
  return;
3679
3705
  }
3680
3706
  const fragment = fragmentInfo[0];
@@ -3824,17 +3850,9 @@ function parse(code, options) {
3824
3850
  }
3825
3851
  function parseForESLint(code, options = {}) {
3826
3852
  const gqlConfig = loadGraphQLConfig(options);
3827
- let schema;
3828
- try {
3829
- schema = getSchema(options, gqlConfig);
3830
- }
3831
- catch (e) {
3832
- e.message = `[graphql-eslint] Error while loading schema: ${e.message}`;
3833
- // eslint-disable-next-line no-console
3834
- console.error(e);
3835
- }
3853
+ const schema = getSchema(options, gqlConfig);
3836
3854
  const parserServices = {
3837
- hasTypeInfo: Boolean(schema),
3855
+ hasTypeInfo: schema !== null,
3838
3856
  schema,
3839
3857
  siblingOperations: getSiblingOperations(options, gqlConfig),
3840
3858
  reachableTypes: getReachableTypes,
package/index.mjs CHANGED
@@ -4,6 +4,7 @@ import { statSync, existsSync, readFileSync } from 'fs';
4
4
  import { dirname, extname, basename, relative, resolve } from 'path';
5
5
  import { asArray, parseGraphQLSDL } from '@graphql-tools/utils';
6
6
  import lowerCase from 'lodash.lowercase';
7
+ import chalk from 'chalk';
7
8
  import depthLimit from 'graphql-depth-limit';
8
9
  import { parseCode } from '@graphql-tools/graphql-tag-pluck';
9
10
  import { loadConfigSync, GraphQLConfig } from 'graphql-config';
@@ -186,6 +187,12 @@ function requireGraphQLSchemaFromContext(ruleName, context) {
186
187
  }
187
188
  return context.parserServices.schema;
188
189
  }
190
+ const logger = {
191
+ // eslint-disable-next-line no-console
192
+ error: (...args) => console.error(chalk.red('error'), '[graphql-eslint]', chalk(...args)),
193
+ // eslint-disable-next-line no-console
194
+ warn: (...args) => console.warn(chalk.yellow('warning'), '[graphql-eslint]', chalk(...args)),
195
+ };
189
196
  function requireReachableTypesFromContext(ruleName, context) {
190
197
  const schema = requireGraphQLSchemaFromContext(ruleName, context);
191
198
  return context.parserServices.reachableTypes(schema);
@@ -320,12 +327,12 @@ function getLocation(loc, fieldName = '', offset) {
320
327
  };
321
328
  }
322
329
 
323
- function validateDocument(context, schema = null, documentNode, rule, isSchemaToExtend = false) {
330
+ function validateDocument(context, schema = null, documentNode, rule) {
324
331
  if (documentNode.definitions.length === 0) {
325
332
  return;
326
333
  }
327
334
  try {
328
- const validationErrors = schema && !isSchemaToExtend
335
+ const validationErrors = schema
329
336
  ? validate(schema, documentNode, [rule])
330
337
  : validateSDL(documentNode, schema, [rule]);
331
338
  for (const error of validationErrors) {
@@ -436,17 +443,16 @@ const validationToRule = (ruleId, ruleName, docs, getDocumentNode) => {
436
443
  },
437
444
  create(context) {
438
445
  if (!ruleFn) {
439
- // eslint-disable-next-line no-console
440
- console.warn(`You rule "${ruleId}" depends on a GraphQL validation rule "${ruleName}" but it's not available in the "graphql-js" version you are using. Skipping...`);
446
+ logger.warn(`You rule "${ruleId}" depends on a GraphQL validation rule "${ruleName}" but it's not available in the "graphql-js" version you are using. Skipping...`);
441
447
  return {};
442
448
  }
443
- const schema = docs.requiresSchema ? requireGraphQLSchemaFromContext(ruleId, context) : null;
444
449
  return {
445
450
  Document(node) {
451
+ const schema = docs.requiresSchema ? requireGraphQLSchemaFromContext(ruleId, context) : null;
446
452
  const documentNode = getDocumentNode
447
453
  ? getDocumentNode({ ruleId, context, schema, node: node.rawNode() })
448
454
  : node.rawNode();
449
- validateDocument(context, schema, documentNode, ruleFn, docs.requiresSchemaToExtend);
455
+ validateDocument(context, schema, documentNode, ruleFn);
450
456
  },
451
457
  };
452
458
  },
@@ -598,7 +604,6 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
598
604
  description: `A type extension is only valid if the type is defined and has the same kind.`,
599
605
  recommended: false,
600
606
  requiresSchema: true,
601
- requiresSchemaToExtend: true,
602
607
  }), validationToRule('provided-required-arguments', 'ProvidedRequiredArguments', {
603
608
  category: ['Schema', 'Operations'],
604
609
  description: `A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.`,
@@ -1370,6 +1375,17 @@ const rule$4 = {
1370
1375
  type Query {
1371
1376
  users: [User!]!
1372
1377
  }
1378
+ `,
1379
+ },
1380
+ {
1381
+ title: 'Correct',
1382
+ usage: [{ FieldDefinition: { style: 'camelCase', ignorePattern: '^(EAN13|UPC|UK)' } }],
1383
+ code: /* GraphQL */ `
1384
+ type Product {
1385
+ EAN13: String
1386
+ UPC: String
1387
+ UKFlag: String
1388
+ }
1373
1389
  `,
1374
1390
  },
1375
1391
  ],
@@ -1439,6 +1455,10 @@ const rule$4 = {
1439
1455
  minItems: 1,
1440
1456
  items: { type: 'string' },
1441
1457
  },
1458
+ ignorePattern: {
1459
+ type: 'string',
1460
+ description: 'Option to skip validation of some words, e.g. acronyms',
1461
+ },
1442
1462
  },
1443
1463
  },
1444
1464
  },
@@ -1493,15 +1513,15 @@ const rule$4 = {
1493
1513
  if (!node) {
1494
1514
  return;
1495
1515
  }
1496
- const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
1516
+ const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern } = normalisePropertyOption(selector);
1497
1517
  const nodeType = KindToDisplayName[n.kind] || n.kind;
1498
1518
  const nodeName = node.value;
1499
1519
  const error = getError();
1500
1520
  if (error) {
1501
1521
  const { errorMessage, renameToName } = error;
1502
- const [leadingUnderscore] = nodeName.match(/^_*/);
1503
- const [trailingUnderscore] = nodeName.match(/_*$/);
1504
- const suggestedName = leadingUnderscore + renameToName + trailingUnderscore;
1522
+ const [leadingUnderscores] = nodeName.match(/^_*/);
1523
+ const [trailingUnderscores] = nodeName.match(/_*$/);
1524
+ const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
1505
1525
  context.report({
1506
1526
  loc: getLocation(node.loc, node.value),
1507
1527
  message: `${nodeType} "${nodeName}" should ${errorMessage}`,
@@ -1515,6 +1535,9 @@ const rule$4 = {
1515
1535
  }
1516
1536
  function getError() {
1517
1537
  const name = nodeName.replace(/(^_+)|(_+$)/g, '');
1538
+ if (ignorePattern && new RegExp(ignorePattern, 'u').test(name)) {
1539
+ return;
1540
+ }
1518
1541
  if (prefix && !name.startsWith(prefix)) {
1519
1542
  return {
1520
1543
  errorMessage: `have "${prefix}" prefix`,
@@ -1541,8 +1564,12 @@ const rule$4 = {
1541
1564
  renameToName: name.replace(new RegExp(`${forbiddenSuffix}$`), ''),
1542
1565
  };
1543
1566
  }
1567
+ // Style is optional
1568
+ if (!style) {
1569
+ return;
1570
+ }
1544
1571
  const caseRegex = StyleToRegex[style];
1545
- if (caseRegex && !caseRegex.test(name)) {
1572
+ if (!caseRegex.test(name)) {
1546
1573
  return {
1547
1574
  errorMessage: `be in ${style} format`,
1548
1575
  renameToName: convertCase(style, name),
@@ -3009,12 +3036,13 @@ const rule$j = {
3009
3036
  },
3010
3037
  };
3011
3038
 
3039
+ const RULE_ID$2 = 'selection-set-depth';
3012
3040
  const rule$k = {
3013
3041
  meta: {
3014
3042
  docs: {
3015
3043
  category: 'Operations',
3016
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).`,
3017
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/selection-set-depth.md',
3045
+ url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
3018
3046
  requiresSiblings: true,
3019
3047
  examples: [
3020
3048
  {
@@ -3088,11 +3116,10 @@ const rule$k = {
3088
3116
  create(context) {
3089
3117
  let siblings = null;
3090
3118
  try {
3091
- siblings = requireSiblingsOperations('selection-set-depth', context);
3119
+ siblings = requireSiblingsOperations(RULE_ID$2, context);
3092
3120
  }
3093
3121
  catch (e) {
3094
- // eslint-disable-next-line no-console
3095
- console.warn(`Rule "selection-set-depth" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
3122
+ logger.warn(`Rule "${RULE_ID$2}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
3096
3123
  }
3097
3124
  const { maxDepth } = context.options[0];
3098
3125
  const ignore = context.options[0].ignore || [];
@@ -3117,8 +3144,7 @@ const rule$k = {
3117
3144
  });
3118
3145
  }
3119
3146
  catch (e) {
3120
- // eslint-disable-next-line no-console
3121
- console.warn(`Rule "selection-set-depth" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, 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);
3122
3148
  }
3123
3149
  },
3124
3150
  };
@@ -3500,8 +3526,7 @@ function createGraphqlProcessor() {
3500
3526
  }
3501
3527
  }
3502
3528
  catch (e) {
3503
- // eslint-disable-next-line no-console
3504
- console.warn(`[graphql-eslint/processor]: Unable to process file "${filename}": `, e);
3529
+ logger.warn(`Unable to process file "${filename}"`, e);
3505
3530
  }
3506
3531
  }
3507
3532
  return [text];
@@ -3541,20 +3566,25 @@ const schemaCache = new Map();
3541
3566
  function getSchema(options = {}, gqlConfig) {
3542
3567
  const realFilepath = options.filePath ? getOnDiskFilepath(options.filePath) : null;
3543
3568
  const projectForFile = realFilepath ? gqlConfig.getProjectForFile(realFilepath) : gqlConfig.getDefault();
3544
- const schemaKey = asArray(projectForFile.schema)
3545
- .sort()
3546
- .join(',');
3569
+ const schemaKey = asArray(projectForFile.schema).sort().join(',');
3547
3570
  if (!schemaKey) {
3548
3571
  return null;
3549
3572
  }
3550
- let schema = schemaCache.get(schemaKey);
3551
- if (!schema) {
3573
+ if (schemaCache.has(schemaKey)) {
3574
+ return schemaCache.get(schemaKey);
3575
+ }
3576
+ let schema;
3577
+ try {
3552
3578
  schema = projectForFile.loadSchemaSync(projectForFile.schema, 'GraphQLSchema', {
3553
3579
  cache: loaderCache,
3554
- ...options.schemaOptions
3580
+ ...options.schemaOptions,
3555
3581
  });
3556
- schemaCache.set(schemaKey, schema);
3557
3582
  }
3583
+ catch (e) {
3584
+ schema = null;
3585
+ logger.error('Error while loading schema\n', e);
3586
+ }
3587
+ schemaCache.set(schemaKey, schema);
3558
3588
  return schema;
3559
3589
  }
3560
3590
 
@@ -3567,10 +3597,10 @@ const handleVirtualPath = (documents) => {
3567
3597
  return source;
3568
3598
  }
3569
3599
  (_a = filepathMap[location]) !== null && _a !== void 0 ? _a : (filepathMap[location] = -1);
3570
- const index = filepathMap[location] += 1;
3600
+ const index = (filepathMap[location] += 1);
3571
3601
  return {
3572
3602
  ...source,
3573
- location: resolve(location, `${index}_document.graphql`)
3603
+ location: resolve(location, `${index}_document.graphql`),
3574
3604
  };
3575
3605
  });
3576
3606
  };
@@ -3579,9 +3609,7 @@ const siblingOperationsCache = new Map();
3579
3609
  const getSiblings = (filePath, gqlConfig) => {
3580
3610
  const realFilepath = filePath ? getOnDiskFilepath(filePath) : null;
3581
3611
  const projectForFile = realFilepath ? gqlConfig.getProjectForFile(realFilepath) : gqlConfig.getDefault();
3582
- const documentsKey = asArray(projectForFile.documents)
3583
- .sort()
3584
- .join(',');
3612
+ const documentsKey = asArray(projectForFile.documents).sort().join(',');
3585
3613
  if (!documentsKey) {
3586
3614
  return [];
3587
3615
  }
@@ -3589,7 +3617,7 @@ const getSiblings = (filePath, gqlConfig) => {
3589
3617
  if (!siblings) {
3590
3618
  const documents = projectForFile.loadDocumentsSync(projectForFile.documents, {
3591
3619
  skipGraphQLImport: true,
3592
- cache: loaderCache
3620
+ cache: loaderCache,
3593
3621
  });
3594
3622
  siblings = handleVirtualPath(documents);
3595
3623
  operationsCache.set(documentsKey, siblings);
@@ -3602,8 +3630,7 @@ function getSiblingOperations(options, gqlConfig) {
3602
3630
  let printed = false;
3603
3631
  const noopWarn = () => {
3604
3632
  if (!printed) {
3605
- // eslint-disable-next-line no-console
3606
- console.warn(`getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!`);
3633
+ logger.warn(`getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!`);
3607
3634
  printed = true;
3608
3635
  }
3609
3636
  return [];
@@ -3667,8 +3694,7 @@ function getSiblingOperations(options, gqlConfig) {
3667
3694
  const name = spread.name.value;
3668
3695
  const fragmentInfo = getFragment(name);
3669
3696
  if (fragmentInfo.length === 0) {
3670
- // eslint-disable-next-line no-console
3671
- console.warn(`Unable to locate fragment named "${name}", please make sure it's loaded using "parserOptions.operations"`);
3697
+ logger.warn(`Unable to locate fragment named "${name}", please make sure it's loaded using "parserOptions.operations"`);
3672
3698
  return;
3673
3699
  }
3674
3700
  const fragment = fragmentInfo[0];
@@ -3818,17 +3844,9 @@ function parse(code, options) {
3818
3844
  }
3819
3845
  function parseForESLint(code, options = {}) {
3820
3846
  const gqlConfig = loadGraphQLConfig(options);
3821
- let schema;
3822
- try {
3823
- schema = getSchema(options, gqlConfig);
3824
- }
3825
- catch (e) {
3826
- e.message = `[graphql-eslint] Error while loading schema: ${e.message}`;
3827
- // eslint-disable-next-line no-console
3828
- console.error(e);
3829
- }
3847
+ const schema = getSchema(options, gqlConfig);
3830
3848
  const parserServices = {
3831
- hasTypeInfo: Boolean(schema),
3849
+ hasTypeInfo: schema !== null,
3832
3850
  schema,
3833
3851
  siblingOperations: getSiblingOperations(options, gqlConfig),
3834
3852
  reachableTypes: getReachableTypes,
package/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@graphql-eslint/eslint-plugin",
3
- "version": "3.3.0-alpha-1c01242.0",
3
+ "version": "3.4.0-alpha-d7fc26d.0",
4
4
  "description": "GraphQL plugin for ESLint",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
7
  "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
8
8
  },
9
9
  "dependencies": {
10
- "@babel/code-frame": "7.16.0",
10
+ "@babel/code-frame": "7.16.7",
11
11
  "@graphql-tools/code-file-loader": "7.2.3",
12
12
  "@graphql-tools/graphql-tag-pluck": "7.1.4",
13
13
  "@graphql-tools/utils": "8.5.5",
14
+ "chalk": "4.1.2",
14
15
  "graphql-config": "4.1.0",
15
16
  "graphql-depth-limit": "1.1.0",
16
17
  "lodash.lowercase": "4.3.0"
@@ -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 = {
package/types.d.ts CHANGED
@@ -51,7 +51,6 @@ export declare type RuleDocsInfo<T> = {
51
51
  category: CategoryType | CategoryType[];
52
52
  requiresSchema?: true;
53
53
  requiresSiblings?: true;
54
- requiresSchemaToExtend?: true;
55
54
  examples?: {
56
55
  title: string;
57
56
  code: string;
package/utils.d.ts CHANGED
@@ -6,6 +6,10 @@ import { SiblingOperations } from './sibling-operations';
6
6
  import { UsedFields, ReachableTypes } from './graphql-ast';
7
7
  export declare function requireSiblingsOperations(ruleName: string, context: GraphQLESLintRuleContext): SiblingOperations | never;
8
8
  export declare function requireGraphQLSchemaFromContext(ruleName: string, context: GraphQLESLintRuleContext): GraphQLSchema | never;
9
+ export declare const logger: {
10
+ error: (...args: any[]) => void;
11
+ warn: (...args: any[]) => void;
12
+ };
9
13
  export declare function requireReachableTypesFromContext(ruleName: string, context: GraphQLESLintRuleContext): ReachableTypes | never;
10
14
  export declare function requireUsedFieldsFromContext(ruleName: string, context: GraphQLESLintRuleContext): UsedFields | never;
11
15
  export declare function extractTokens(source: Source): AST.Token[];