@graphql-eslint/eslint-plugin 3.3.0-alpha-b07557f.0 → 3.3.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/naming-convention.md +16 -0
- package/graphql-config.d.ts +1 -2
- package/index.js +77 -93
- package/index.mjs +77 -93
- package/package.json +1 -1
- package/rules/naming-convention.d.ts +1 -0
- package/types.d.ts +0 -1
- package/utils.d.ts +0 -1
@@ -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)
|
package/graphql-config.d.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1
1
|
import { GraphQLConfig } from 'graphql-config';
|
2
2
|
import { ParserOptions } from './types';
|
3
|
-
export declare function
|
4
|
-
export declare function loadGraphQLConfig(options: ParserOptions): GraphQLConfig;
|
3
|
+
export declare function loadGraphqlConfig(options: ParserOptions): GraphQLConfig;
|
package/index.js
CHANGED
@@ -10,10 +10,10 @@ 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 graphqlConfig = require('graphql-config');
|
14
|
-
const codeFileLoader = require('@graphql-tools/code-file-loader');
|
15
13
|
const depthLimit = _interopDefault(require('graphql-depth-limit'));
|
16
14
|
const graphqlTagPluck = require('@graphql-tools/graphql-tag-pluck');
|
15
|
+
const graphqlConfig$1 = require('graphql-config');
|
16
|
+
const codeFileLoader = require('@graphql-tools/code-file-loader');
|
17
17
|
const eslint = require('eslint');
|
18
18
|
const codeFrame = require('@babel/code-frame');
|
19
19
|
|
@@ -174,47 +174,6 @@ const configs = {
|
|
174
174
|
'operations-all': operationsAllConfig,
|
175
175
|
};
|
176
176
|
|
177
|
-
let graphQLConfig;
|
178
|
-
function loadCachedGraphQLConfig(options) {
|
179
|
-
// We don't want cache config on test environment
|
180
|
-
// Otherwise schema and documents will be same for all tests
|
181
|
-
if (process.env.NODE_ENV !== 'test' && graphQLConfig) {
|
182
|
-
return graphQLConfig;
|
183
|
-
}
|
184
|
-
graphQLConfig = loadGraphQLConfig(options);
|
185
|
-
return graphQLConfig;
|
186
|
-
}
|
187
|
-
function loadGraphQLConfig(options) {
|
188
|
-
const onDiskConfig = options.skipGraphQLConfig
|
189
|
-
? null
|
190
|
-
: graphqlConfig.loadConfigSync({
|
191
|
-
throwOnEmpty: false,
|
192
|
-
throwOnMissing: false,
|
193
|
-
extensions: [addCodeFileLoaderExtension],
|
194
|
-
});
|
195
|
-
const configOptions = options.projects
|
196
|
-
? { projects: options.projects }
|
197
|
-
: {
|
198
|
-
schema: (options.schema || ''),
|
199
|
-
documents: options.documents || options.operations,
|
200
|
-
extensions: options.extensions,
|
201
|
-
include: options.include,
|
202
|
-
exclude: options.exclude,
|
203
|
-
};
|
204
|
-
graphQLConfig =
|
205
|
-
onDiskConfig ||
|
206
|
-
new graphqlConfig.GraphQLConfig({
|
207
|
-
config: configOptions,
|
208
|
-
filepath: 'virtual-config',
|
209
|
-
}, [addCodeFileLoaderExtension]);
|
210
|
-
return graphQLConfig;
|
211
|
-
}
|
212
|
-
const addCodeFileLoaderExtension = api => {
|
213
|
-
api.loaders.schema.register(new codeFileLoader.CodeFileLoader());
|
214
|
-
api.loaders.documents.register(new codeFileLoader.CodeFileLoader());
|
215
|
-
return { name: 'graphql-eslint-loaders' };
|
216
|
-
};
|
217
|
-
|
218
177
|
function requireSiblingsOperations(ruleName, context) {
|
219
178
|
if (!context.parserServices) {
|
220
179
|
throw new Error(`Rule '${ruleName}' requires 'parserOptions.operations' to be set and loaded. See http://bit.ly/graphql-eslint-operations for more info`);
|
@@ -233,32 +192,6 @@ function requireGraphQLSchemaFromContext(ruleName, context) {
|
|
233
192
|
}
|
234
193
|
return context.parserServices.schema;
|
235
194
|
}
|
236
|
-
const schemaToExtendCache = new Map();
|
237
|
-
function getGraphQLSchemaToExtend(context) {
|
238
|
-
// If parserOptions.schema not set or not loaded, there is no reason to make partial schema aka schemaToExtend
|
239
|
-
if (!context.parserServices.hasTypeInfo) {
|
240
|
-
return null;
|
241
|
-
}
|
242
|
-
const filename = context.getPhysicalFilename();
|
243
|
-
if (!schemaToExtendCache.has(filename)) {
|
244
|
-
const { schema, schemaOptions } = context.parserOptions;
|
245
|
-
const gqlConfig = loadGraphQLConfig({ schema });
|
246
|
-
const projectForFile = gqlConfig.getProjectForFile(filename);
|
247
|
-
let schemaToExtend;
|
248
|
-
try {
|
249
|
-
schemaToExtend = projectForFile.loadSchemaSync(projectForFile.schema, 'GraphQLSchema', {
|
250
|
-
...schemaOptions,
|
251
|
-
ignore: filename,
|
252
|
-
});
|
253
|
-
}
|
254
|
-
catch (_a) {
|
255
|
-
// If error throws just ignore it because maybe schema is located in 1 file
|
256
|
-
schemaToExtend = null;
|
257
|
-
}
|
258
|
-
schemaToExtendCache.set(filename, schemaToExtend);
|
259
|
-
}
|
260
|
-
return schemaToExtendCache.get(filename);
|
261
|
-
}
|
262
195
|
function requireReachableTypesFromContext(ruleName, context) {
|
263
196
|
const schema = requireGraphQLSchemaFromContext(ruleName, context);
|
264
197
|
return context.parserServices.reachableTypes(schema);
|
@@ -393,14 +326,14 @@ function getLocation(loc, fieldName = '', offset) {
|
|
393
326
|
};
|
394
327
|
}
|
395
328
|
|
396
|
-
function validateDocument(sourceNode, context, schema
|
329
|
+
function validateDocument(sourceNode, context, schema, documentNode, rule) {
|
397
330
|
if (documentNode.definitions.length === 0) {
|
398
331
|
return;
|
399
332
|
}
|
400
333
|
try {
|
401
|
-
const validationErrors = schema
|
334
|
+
const validationErrors = schema
|
402
335
|
? graphql.validate(schema, documentNode, [rule])
|
403
|
-
: validate.validateSDL(documentNode,
|
336
|
+
: validate.validateSDL(documentNode, null, [rule]);
|
404
337
|
for (const error of validationErrors) {
|
405
338
|
/*
|
406
339
|
* TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
|
@@ -507,24 +440,18 @@ const validationToRule = (ruleId, ruleName, docs, getDocumentNode) => {
|
|
507
440
|
},
|
508
441
|
},
|
509
442
|
create(context) {
|
510
|
-
if (!ruleFn) {
|
511
|
-
// eslint-disable-next-line no-console
|
512
|
-
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...`);
|
513
|
-
return {};
|
514
|
-
}
|
515
|
-
let schema;
|
516
|
-
if (docs.requiresSchemaToExtend) {
|
517
|
-
schema = getGraphQLSchemaToExtend(context);
|
518
|
-
}
|
519
|
-
if (docs.requiresSchema) {
|
520
|
-
schema = requireGraphQLSchemaFromContext(ruleId, context);
|
521
|
-
}
|
522
443
|
return {
|
523
444
|
Document(node) {
|
445
|
+
if (!ruleFn) {
|
446
|
+
// eslint-disable-next-line no-console
|
447
|
+
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...`);
|
448
|
+
return;
|
449
|
+
}
|
450
|
+
const schema = docs.requiresSchema ? requireGraphQLSchemaFromContext(ruleId, context) : null;
|
524
451
|
const documentNode = getDocumentNode
|
525
452
|
? getDocumentNode({ ruleId, context, schema, node: node.rawNode() })
|
526
453
|
: node.rawNode();
|
527
|
-
validateDocument(node, context, schema, documentNode, ruleFn
|
454
|
+
validateDocument(node, context, schema, documentNode, ruleFn);
|
528
455
|
},
|
529
456
|
};
|
530
457
|
},
|
@@ -674,8 +601,7 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
674
601
|
}), validationToRule('possible-type-extension', 'PossibleTypeExtensions', {
|
675
602
|
category: 'Schema',
|
676
603
|
description: `A type extension is only valid if the type is defined and has the same kind.`,
|
677
|
-
recommended: false,
|
678
|
-
requiresSchemaToExtend: true,
|
604
|
+
recommended: false, // TODO: enable after https://github.com/dotansimha/graphql-eslint/issues/787 will be fixed
|
679
605
|
}), validationToRule('provided-required-arguments', 'ProvidedRequiredArguments', {
|
680
606
|
category: ['Schema', 'Operations'],
|
681
607
|
description: `A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.`,
|
@@ -1447,6 +1373,17 @@ const rule$4 = {
|
|
1447
1373
|
type Query {
|
1448
1374
|
users: [User!]!
|
1449
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
|
+
}
|
1450
1387
|
`,
|
1451
1388
|
},
|
1452
1389
|
],
|
@@ -1516,6 +1453,10 @@ const rule$4 = {
|
|
1516
1453
|
minItems: 1,
|
1517
1454
|
items: { type: 'string' },
|
1518
1455
|
},
|
1456
|
+
ignorePattern: {
|
1457
|
+
type: 'string',
|
1458
|
+
description: 'Option to skip validation of some words, e.g. acronyms',
|
1459
|
+
},
|
1519
1460
|
},
|
1520
1461
|
},
|
1521
1462
|
},
|
@@ -1570,15 +1511,15 @@ const rule$4 = {
|
|
1570
1511
|
if (!node) {
|
1571
1512
|
return;
|
1572
1513
|
}
|
1573
|
-
const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
|
1514
|
+
const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern } = normalisePropertyOption(selector);
|
1574
1515
|
const nodeType = KindToDisplayName[n.kind] || n.kind;
|
1575
1516
|
const nodeName = node.value;
|
1576
1517
|
const error = getError();
|
1577
1518
|
if (error) {
|
1578
1519
|
const { errorMessage, renameToName } = error;
|
1579
|
-
const [
|
1580
|
-
const [
|
1581
|
-
const suggestedName =
|
1520
|
+
const [leadingUnderscores] = nodeName.match(/^_*/);
|
1521
|
+
const [trailingUnderscores] = nodeName.match(/_*$/);
|
1522
|
+
const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
|
1582
1523
|
context.report({
|
1583
1524
|
loc: getLocation(node.loc, node.value),
|
1584
1525
|
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
@@ -1592,6 +1533,9 @@ const rule$4 = {
|
|
1592
1533
|
}
|
1593
1534
|
function getError() {
|
1594
1535
|
const name = nodeName.replace(/(^_+)|(_+$)/g, '');
|
1536
|
+
if (ignorePattern && new RegExp(ignorePattern, 'u').test(name)) {
|
1537
|
+
return;
|
1538
|
+
}
|
1595
1539
|
if (prefix && !name.startsWith(prefix)) {
|
1596
1540
|
return {
|
1597
1541
|
errorMessage: `have "${prefix}" prefix`,
|
@@ -1618,8 +1562,12 @@ const rule$4 = {
|
|
1618
1562
|
renameToName: name.replace(new RegExp(`${forbiddenSuffix}$`), ''),
|
1619
1563
|
};
|
1620
1564
|
}
|
1565
|
+
// Style is optional
|
1566
|
+
if (!style) {
|
1567
|
+
return;
|
1568
|
+
}
|
1621
1569
|
const caseRegex = StyleToRegex[style];
|
1622
|
-
if (
|
1570
|
+
if (!caseRegex.test(name)) {
|
1623
1571
|
return {
|
1624
1572
|
errorMessage: `be in ${style} format`,
|
1625
1573
|
renameToName: convertCase(style, name),
|
@@ -3775,6 +3723,42 @@ function getSiblingOperations(options, gqlConfig) {
|
|
3775
3723
|
return siblingOperations;
|
3776
3724
|
}
|
3777
3725
|
|
3726
|
+
let graphqlConfig;
|
3727
|
+
function loadGraphqlConfig(options) {
|
3728
|
+
// We don't want cache config on test environment
|
3729
|
+
// Otherwise schema and documents will be same for all tests
|
3730
|
+
if (process.env.NODE_ENV !== 'test' && graphqlConfig) {
|
3731
|
+
return graphqlConfig;
|
3732
|
+
}
|
3733
|
+
const onDiskConfig = options.skipGraphQLConfig
|
3734
|
+
? null
|
3735
|
+
: graphqlConfig$1.loadConfigSync({
|
3736
|
+
throwOnEmpty: false,
|
3737
|
+
throwOnMissing: false,
|
3738
|
+
extensions: [addCodeFileLoaderExtension],
|
3739
|
+
});
|
3740
|
+
graphqlConfig =
|
3741
|
+
onDiskConfig ||
|
3742
|
+
new graphqlConfig$1.GraphQLConfig({
|
3743
|
+
config: options.projects
|
3744
|
+
? { projects: options.projects }
|
3745
|
+
: {
|
3746
|
+
schema: (options.schema || ''),
|
3747
|
+
documents: options.documents || options.operations,
|
3748
|
+
extensions: options.extensions,
|
3749
|
+
include: options.include,
|
3750
|
+
exclude: options.exclude,
|
3751
|
+
},
|
3752
|
+
filepath: 'virtual-config',
|
3753
|
+
}, [addCodeFileLoaderExtension]);
|
3754
|
+
return graphqlConfig;
|
3755
|
+
}
|
3756
|
+
const addCodeFileLoaderExtension = api => {
|
3757
|
+
api.loaders.schema.register(new codeFileLoader.CodeFileLoader());
|
3758
|
+
api.loaders.documents.register(new codeFileLoader.CodeFileLoader());
|
3759
|
+
return { name: 'graphql-eslint-loaders' };
|
3760
|
+
};
|
3761
|
+
|
3778
3762
|
let reachableTypesCache;
|
3779
3763
|
function getReachableTypes(schema) {
|
3780
3764
|
// We don't want cache reachableTypes on test environment
|
@@ -3857,7 +3841,7 @@ function parse(code, options) {
|
|
3857
3841
|
return parseForESLint(code, options).ast;
|
3858
3842
|
}
|
3859
3843
|
function parseForESLint(code, options = {}) {
|
3860
|
-
const gqlConfig =
|
3844
|
+
const gqlConfig = loadGraphqlConfig(options);
|
3861
3845
|
const schema = getSchema(options, gqlConfig);
|
3862
3846
|
const parserServices = {
|
3863
3847
|
hasTypeInfo: schema !== null,
|
package/index.mjs
CHANGED
@@ -4,10 +4,10 @@ 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 { loadConfigSync, GraphQLConfig } from 'graphql-config';
|
8
|
-
import { CodeFileLoader } from '@graphql-tools/code-file-loader';
|
9
7
|
import depthLimit from 'graphql-depth-limit';
|
10
8
|
import { parseCode } from '@graphql-tools/graphql-tag-pluck';
|
9
|
+
import { loadConfigSync, GraphQLConfig } from 'graphql-config';
|
10
|
+
import { CodeFileLoader } from '@graphql-tools/code-file-loader';
|
11
11
|
import { RuleTester, Linter } from 'eslint';
|
12
12
|
import { codeFrameColumns } from '@babel/code-frame';
|
13
13
|
|
@@ -168,47 +168,6 @@ const configs = {
|
|
168
168
|
'operations-all': operationsAllConfig,
|
169
169
|
};
|
170
170
|
|
171
|
-
let graphQLConfig;
|
172
|
-
function loadCachedGraphQLConfig(options) {
|
173
|
-
// We don't want cache config on test environment
|
174
|
-
// Otherwise schema and documents will be same for all tests
|
175
|
-
if (process.env.NODE_ENV !== 'test' && graphQLConfig) {
|
176
|
-
return graphQLConfig;
|
177
|
-
}
|
178
|
-
graphQLConfig = loadGraphQLConfig(options);
|
179
|
-
return graphQLConfig;
|
180
|
-
}
|
181
|
-
function loadGraphQLConfig(options) {
|
182
|
-
const onDiskConfig = options.skipGraphQLConfig
|
183
|
-
? null
|
184
|
-
: loadConfigSync({
|
185
|
-
throwOnEmpty: false,
|
186
|
-
throwOnMissing: false,
|
187
|
-
extensions: [addCodeFileLoaderExtension],
|
188
|
-
});
|
189
|
-
const configOptions = options.projects
|
190
|
-
? { projects: options.projects }
|
191
|
-
: {
|
192
|
-
schema: (options.schema || ''),
|
193
|
-
documents: options.documents || options.operations,
|
194
|
-
extensions: options.extensions,
|
195
|
-
include: options.include,
|
196
|
-
exclude: options.exclude,
|
197
|
-
};
|
198
|
-
graphQLConfig =
|
199
|
-
onDiskConfig ||
|
200
|
-
new GraphQLConfig({
|
201
|
-
config: configOptions,
|
202
|
-
filepath: 'virtual-config',
|
203
|
-
}, [addCodeFileLoaderExtension]);
|
204
|
-
return graphQLConfig;
|
205
|
-
}
|
206
|
-
const addCodeFileLoaderExtension = api => {
|
207
|
-
api.loaders.schema.register(new CodeFileLoader());
|
208
|
-
api.loaders.documents.register(new CodeFileLoader());
|
209
|
-
return { name: 'graphql-eslint-loaders' };
|
210
|
-
};
|
211
|
-
|
212
171
|
function requireSiblingsOperations(ruleName, context) {
|
213
172
|
if (!context.parserServices) {
|
214
173
|
throw new Error(`Rule '${ruleName}' requires 'parserOptions.operations' to be set and loaded. See http://bit.ly/graphql-eslint-operations for more info`);
|
@@ -227,32 +186,6 @@ function requireGraphQLSchemaFromContext(ruleName, context) {
|
|
227
186
|
}
|
228
187
|
return context.parserServices.schema;
|
229
188
|
}
|
230
|
-
const schemaToExtendCache = new Map();
|
231
|
-
function getGraphQLSchemaToExtend(context) {
|
232
|
-
// If parserOptions.schema not set or not loaded, there is no reason to make partial schema aka schemaToExtend
|
233
|
-
if (!context.parserServices.hasTypeInfo) {
|
234
|
-
return null;
|
235
|
-
}
|
236
|
-
const filename = context.getPhysicalFilename();
|
237
|
-
if (!schemaToExtendCache.has(filename)) {
|
238
|
-
const { schema, schemaOptions } = context.parserOptions;
|
239
|
-
const gqlConfig = loadGraphQLConfig({ schema });
|
240
|
-
const projectForFile = gqlConfig.getProjectForFile(filename);
|
241
|
-
let schemaToExtend;
|
242
|
-
try {
|
243
|
-
schemaToExtend = projectForFile.loadSchemaSync(projectForFile.schema, 'GraphQLSchema', {
|
244
|
-
...schemaOptions,
|
245
|
-
ignore: filename,
|
246
|
-
});
|
247
|
-
}
|
248
|
-
catch (_a) {
|
249
|
-
// If error throws just ignore it because maybe schema is located in 1 file
|
250
|
-
schemaToExtend = null;
|
251
|
-
}
|
252
|
-
schemaToExtendCache.set(filename, schemaToExtend);
|
253
|
-
}
|
254
|
-
return schemaToExtendCache.get(filename);
|
255
|
-
}
|
256
189
|
function requireReachableTypesFromContext(ruleName, context) {
|
257
190
|
const schema = requireGraphQLSchemaFromContext(ruleName, context);
|
258
191
|
return context.parserServices.reachableTypes(schema);
|
@@ -387,14 +320,14 @@ function getLocation(loc, fieldName = '', offset) {
|
|
387
320
|
};
|
388
321
|
}
|
389
322
|
|
390
|
-
function validateDocument(sourceNode, context, schema
|
323
|
+
function validateDocument(sourceNode, context, schema, documentNode, rule) {
|
391
324
|
if (documentNode.definitions.length === 0) {
|
392
325
|
return;
|
393
326
|
}
|
394
327
|
try {
|
395
|
-
const validationErrors = schema
|
328
|
+
const validationErrors = schema
|
396
329
|
? validate(schema, documentNode, [rule])
|
397
|
-
: validateSDL(documentNode,
|
330
|
+
: validateSDL(documentNode, null, [rule]);
|
398
331
|
for (const error of validationErrors) {
|
399
332
|
/*
|
400
333
|
* TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
|
@@ -501,24 +434,18 @@ const validationToRule = (ruleId, ruleName, docs, getDocumentNode) => {
|
|
501
434
|
},
|
502
435
|
},
|
503
436
|
create(context) {
|
504
|
-
if (!ruleFn) {
|
505
|
-
// eslint-disable-next-line no-console
|
506
|
-
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...`);
|
507
|
-
return {};
|
508
|
-
}
|
509
|
-
let schema;
|
510
|
-
if (docs.requiresSchemaToExtend) {
|
511
|
-
schema = getGraphQLSchemaToExtend(context);
|
512
|
-
}
|
513
|
-
if (docs.requiresSchema) {
|
514
|
-
schema = requireGraphQLSchemaFromContext(ruleId, context);
|
515
|
-
}
|
516
437
|
return {
|
517
438
|
Document(node) {
|
439
|
+
if (!ruleFn) {
|
440
|
+
// eslint-disable-next-line no-console
|
441
|
+
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...`);
|
442
|
+
return;
|
443
|
+
}
|
444
|
+
const schema = docs.requiresSchema ? requireGraphQLSchemaFromContext(ruleId, context) : null;
|
518
445
|
const documentNode = getDocumentNode
|
519
446
|
? getDocumentNode({ ruleId, context, schema, node: node.rawNode() })
|
520
447
|
: node.rawNode();
|
521
|
-
validateDocument(node, context, schema, documentNode, ruleFn
|
448
|
+
validateDocument(node, context, schema, documentNode, ruleFn);
|
522
449
|
},
|
523
450
|
};
|
524
451
|
},
|
@@ -668,8 +595,7 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
668
595
|
}), validationToRule('possible-type-extension', 'PossibleTypeExtensions', {
|
669
596
|
category: 'Schema',
|
670
597
|
description: `A type extension is only valid if the type is defined and has the same kind.`,
|
671
|
-
recommended: false,
|
672
|
-
requiresSchemaToExtend: true,
|
598
|
+
recommended: false, // TODO: enable after https://github.com/dotansimha/graphql-eslint/issues/787 will be fixed
|
673
599
|
}), validationToRule('provided-required-arguments', 'ProvidedRequiredArguments', {
|
674
600
|
category: ['Schema', 'Operations'],
|
675
601
|
description: `A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.`,
|
@@ -1441,6 +1367,17 @@ const rule$4 = {
|
|
1441
1367
|
type Query {
|
1442
1368
|
users: [User!]!
|
1443
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
|
+
}
|
1444
1381
|
`,
|
1445
1382
|
},
|
1446
1383
|
],
|
@@ -1510,6 +1447,10 @@ const rule$4 = {
|
|
1510
1447
|
minItems: 1,
|
1511
1448
|
items: { type: 'string' },
|
1512
1449
|
},
|
1450
|
+
ignorePattern: {
|
1451
|
+
type: 'string',
|
1452
|
+
description: 'Option to skip validation of some words, e.g. acronyms',
|
1453
|
+
},
|
1513
1454
|
},
|
1514
1455
|
},
|
1515
1456
|
},
|
@@ -1564,15 +1505,15 @@ const rule$4 = {
|
|
1564
1505
|
if (!node) {
|
1565
1506
|
return;
|
1566
1507
|
}
|
1567
|
-
const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
|
1508
|
+
const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern } = normalisePropertyOption(selector);
|
1568
1509
|
const nodeType = KindToDisplayName[n.kind] || n.kind;
|
1569
1510
|
const nodeName = node.value;
|
1570
1511
|
const error = getError();
|
1571
1512
|
if (error) {
|
1572
1513
|
const { errorMessage, renameToName } = error;
|
1573
|
-
const [
|
1574
|
-
const [
|
1575
|
-
const suggestedName =
|
1514
|
+
const [leadingUnderscores] = nodeName.match(/^_*/);
|
1515
|
+
const [trailingUnderscores] = nodeName.match(/_*$/);
|
1516
|
+
const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
|
1576
1517
|
context.report({
|
1577
1518
|
loc: getLocation(node.loc, node.value),
|
1578
1519
|
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
@@ -1586,6 +1527,9 @@ const rule$4 = {
|
|
1586
1527
|
}
|
1587
1528
|
function getError() {
|
1588
1529
|
const name = nodeName.replace(/(^_+)|(_+$)/g, '');
|
1530
|
+
if (ignorePattern && new RegExp(ignorePattern, 'u').test(name)) {
|
1531
|
+
return;
|
1532
|
+
}
|
1589
1533
|
if (prefix && !name.startsWith(prefix)) {
|
1590
1534
|
return {
|
1591
1535
|
errorMessage: `have "${prefix}" prefix`,
|
@@ -1612,8 +1556,12 @@ const rule$4 = {
|
|
1612
1556
|
renameToName: name.replace(new RegExp(`${forbiddenSuffix}$`), ''),
|
1613
1557
|
};
|
1614
1558
|
}
|
1559
|
+
// Style is optional
|
1560
|
+
if (!style) {
|
1561
|
+
return;
|
1562
|
+
}
|
1615
1563
|
const caseRegex = StyleToRegex[style];
|
1616
|
-
if (
|
1564
|
+
if (!caseRegex.test(name)) {
|
1617
1565
|
return {
|
1618
1566
|
errorMessage: `be in ${style} format`,
|
1619
1567
|
renameToName: convertCase(style, name),
|
@@ -3769,6 +3717,42 @@ function getSiblingOperations(options, gqlConfig) {
|
|
3769
3717
|
return siblingOperations;
|
3770
3718
|
}
|
3771
3719
|
|
3720
|
+
let graphqlConfig;
|
3721
|
+
function loadGraphqlConfig(options) {
|
3722
|
+
// We don't want cache config on test environment
|
3723
|
+
// Otherwise schema and documents will be same for all tests
|
3724
|
+
if (process.env.NODE_ENV !== 'test' && graphqlConfig) {
|
3725
|
+
return graphqlConfig;
|
3726
|
+
}
|
3727
|
+
const onDiskConfig = options.skipGraphQLConfig
|
3728
|
+
? null
|
3729
|
+
: loadConfigSync({
|
3730
|
+
throwOnEmpty: false,
|
3731
|
+
throwOnMissing: false,
|
3732
|
+
extensions: [addCodeFileLoaderExtension],
|
3733
|
+
});
|
3734
|
+
graphqlConfig =
|
3735
|
+
onDiskConfig ||
|
3736
|
+
new GraphQLConfig({
|
3737
|
+
config: options.projects
|
3738
|
+
? { projects: options.projects }
|
3739
|
+
: {
|
3740
|
+
schema: (options.schema || ''),
|
3741
|
+
documents: options.documents || options.operations,
|
3742
|
+
extensions: options.extensions,
|
3743
|
+
include: options.include,
|
3744
|
+
exclude: options.exclude,
|
3745
|
+
},
|
3746
|
+
filepath: 'virtual-config',
|
3747
|
+
}, [addCodeFileLoaderExtension]);
|
3748
|
+
return graphqlConfig;
|
3749
|
+
}
|
3750
|
+
const addCodeFileLoaderExtension = api => {
|
3751
|
+
api.loaders.schema.register(new CodeFileLoader());
|
3752
|
+
api.loaders.documents.register(new CodeFileLoader());
|
3753
|
+
return { name: 'graphql-eslint-loaders' };
|
3754
|
+
};
|
3755
|
+
|
3772
3756
|
let reachableTypesCache;
|
3773
3757
|
function getReachableTypes(schema) {
|
3774
3758
|
// We don't want cache reachableTypes on test environment
|
@@ -3851,7 +3835,7 @@ function parse(code, options) {
|
|
3851
3835
|
return parseForESLint(code, options).ast;
|
3852
3836
|
}
|
3853
3837
|
function parseForESLint(code, options = {}) {
|
3854
|
-
const gqlConfig =
|
3838
|
+
const gqlConfig = loadGraphqlConfig(options);
|
3855
3839
|
const schema = getSchema(options, gqlConfig);
|
3856
3840
|
const parserServices = {
|
3857
3841
|
hasTypeInfo: schema !== null,
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
package/utils.d.ts
CHANGED
@@ -6,7 +6,6 @@ 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 function getGraphQLSchemaToExtend(context: GraphQLESLintRuleContext): GraphQLSchema | null;
|
10
9
|
export declare function requireReachableTypesFromContext(ruleName: string, context: GraphQLESLintRuleContext): ReachableTypes | never;
|
11
10
|
export declare function requireUsedFieldsFromContext(ruleName: string, context: GraphQLESLintRuleContext): UsedFields | never;
|
12
11
|
export declare function extractTokens(source: Source): AST.Token[];
|