@graphql-eslint/eslint-plugin 3.3.0 → 3.4.0-alpha-c01d913.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 +6 -6
- package/docs/rules/possible-type-extension.md +1 -1
- package/docs/rules/unique-enum-value-names.md +1 -1
- package/docs/rules/unique-field-definition-names.md +1 -1
- package/graphql-config.d.ts +2 -1
- package/index.js +132 -78
- package/index.mjs +132 -78
- package/package.json +2 -1
- package/types.d.ts +1 -0
- package/utils.d.ts +10 -0
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
- Category: `Schema`
|
4
4
|
- Rule name: `@graphql-eslint/possible-type-extension`
|
5
|
-
- Requires 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 type extension is only valid if the type is defined and has the same kind.
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
- Category: `Schema`
|
4
4
|
- Rule name: `@graphql-eslint/unique-enum-value-names`
|
5
|
-
- Requires 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: `
|
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/graphql-config.d.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
1
|
import { GraphQLConfig } from 'graphql-config';
|
2
2
|
import { ParserOptions } from './types';
|
3
|
-
export declare function
|
3
|
+
export declare function loadCachedGraphQLConfig(options: ParserOptions): GraphQLConfig;
|
4
|
+
export declare function loadGraphQLConfig(options: ParserOptions): GraphQLConfig;
|
package/index.js
CHANGED
@@ -10,10 +10,11 @@ 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'));
|
14
|
+
const graphqlConfig = require('graphql-config');
|
15
|
+
const codeFileLoader = require('@graphql-tools/code-file-loader');
|
13
16
|
const depthLimit = _interopDefault(require('graphql-depth-limit'));
|
14
17
|
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
18
|
const eslint = require('eslint');
|
18
19
|
const codeFrame = require('@babel/code-frame');
|
19
20
|
|
@@ -174,6 +175,47 @@ const configs = {
|
|
174
175
|
'operations-all': operationsAllConfig,
|
175
176
|
};
|
176
177
|
|
178
|
+
let graphQLConfig;
|
179
|
+
function loadCachedGraphQLConfig(options) {
|
180
|
+
// We don't want cache config on test environment
|
181
|
+
// Otherwise schema and documents will be same for all tests
|
182
|
+
if (process.env.NODE_ENV !== 'test' && graphQLConfig) {
|
183
|
+
return graphQLConfig;
|
184
|
+
}
|
185
|
+
graphQLConfig = loadGraphQLConfig(options);
|
186
|
+
return graphQLConfig;
|
187
|
+
}
|
188
|
+
function loadGraphQLConfig(options) {
|
189
|
+
const onDiskConfig = options.skipGraphQLConfig
|
190
|
+
? null
|
191
|
+
: graphqlConfig.loadConfigSync({
|
192
|
+
throwOnEmpty: false,
|
193
|
+
throwOnMissing: false,
|
194
|
+
extensions: [addCodeFileLoaderExtension],
|
195
|
+
});
|
196
|
+
const configOptions = options.projects
|
197
|
+
? { projects: options.projects }
|
198
|
+
: {
|
199
|
+
schema: (options.schema || ''),
|
200
|
+
documents: options.documents || options.operations,
|
201
|
+
extensions: options.extensions,
|
202
|
+
include: options.include,
|
203
|
+
exclude: options.exclude,
|
204
|
+
};
|
205
|
+
graphQLConfig =
|
206
|
+
onDiskConfig ||
|
207
|
+
new graphqlConfig.GraphQLConfig({
|
208
|
+
config: configOptions,
|
209
|
+
filepath: 'virtual-config',
|
210
|
+
}, [addCodeFileLoaderExtension]);
|
211
|
+
return graphQLConfig;
|
212
|
+
}
|
213
|
+
const addCodeFileLoaderExtension = api => {
|
214
|
+
api.loaders.schema.register(new codeFileLoader.CodeFileLoader());
|
215
|
+
api.loaders.documents.register(new codeFileLoader.CodeFileLoader());
|
216
|
+
return { name: 'graphql-eslint-loaders' };
|
217
|
+
};
|
218
|
+
|
177
219
|
function requireSiblingsOperations(ruleName, context) {
|
178
220
|
if (!context.parserServices) {
|
179
221
|
throw new Error(`Rule '${ruleName}' requires 'parserOptions.operations' to be set and loaded. See http://bit.ly/graphql-eslint-operations for more info`);
|
@@ -192,6 +234,43 @@ function requireGraphQLSchemaFromContext(ruleName, context) {
|
|
192
234
|
}
|
193
235
|
return context.parserServices.schema;
|
194
236
|
}
|
237
|
+
const logger = {
|
238
|
+
// eslint-disable-next-line no-console
|
239
|
+
error: (...args) => console.error(chalk.red('error'), '[graphql-eslint]', chalk(...args)),
|
240
|
+
// eslint-disable-next-line no-console
|
241
|
+
warn: (...args) => console.warn(chalk.yellow('warning'), '[graphql-eslint]', chalk(...args)),
|
242
|
+
};
|
243
|
+
const schemaToExtendCache = new Map();
|
244
|
+
const getGraphQLSchemaToExtend = (ruleId, context) => {
|
245
|
+
// If schema is not loaded, there is no reason to make partial schema aka schemaToExtend
|
246
|
+
if (!context.parserServices.hasTypeInfo) {
|
247
|
+
if (!getGraphQLSchemaToExtend.warningPrintedMap[ruleId]) {
|
248
|
+
logger.warn(`Rule "${ruleId}" works best with schema loaded. See http://bit.ly/graphql-eslint-schema for more info`);
|
249
|
+
getGraphQLSchemaToExtend.warningPrintedMap[ruleId] = true;
|
250
|
+
}
|
251
|
+
return null;
|
252
|
+
}
|
253
|
+
const filename = context.getPhysicalFilename();
|
254
|
+
if (!schemaToExtendCache.has(filename)) {
|
255
|
+
const { schema, schemaOptions } = context.parserOptions;
|
256
|
+
const gqlConfig = loadGraphQLConfig({ schema });
|
257
|
+
const projectForFile = gqlConfig.getProjectForFile(filename);
|
258
|
+
let schemaToExtend;
|
259
|
+
try {
|
260
|
+
schemaToExtend = projectForFile.loadSchemaSync(projectForFile.schema, 'GraphQLSchema', {
|
261
|
+
...schemaOptions,
|
262
|
+
ignore: filename,
|
263
|
+
});
|
264
|
+
}
|
265
|
+
catch (_a) {
|
266
|
+
// If error throws just ignore it because maybe schema is located in 1 file
|
267
|
+
schemaToExtend = null;
|
268
|
+
}
|
269
|
+
schemaToExtendCache.set(filename, schemaToExtend);
|
270
|
+
}
|
271
|
+
return schemaToExtendCache.get(filename);
|
272
|
+
};
|
273
|
+
getGraphQLSchemaToExtend.warningPrintedMap = Object.create(null);
|
195
274
|
function requireReachableTypesFromContext(ruleName, context) {
|
196
275
|
const schema = requireGraphQLSchemaFromContext(ruleName, context);
|
197
276
|
return context.parserServices.reachableTypes(schema);
|
@@ -326,14 +405,14 @@ function getLocation(loc, fieldName = '', offset) {
|
|
326
405
|
};
|
327
406
|
}
|
328
407
|
|
329
|
-
function validateDocument(
|
408
|
+
function validateDocument(context, schema = null, documentNode, rule, isSchemaToExtend = false) {
|
330
409
|
if (documentNode.definitions.length === 0) {
|
331
410
|
return;
|
332
411
|
}
|
333
412
|
try {
|
334
|
-
const validationErrors = schema
|
413
|
+
const validationErrors = schema && !isSchemaToExtend
|
335
414
|
? graphql.validate(schema, documentNode, [rule])
|
336
|
-
: validate.validateSDL(documentNode,
|
415
|
+
: validate.validateSDL(documentNode, schema, [rule]);
|
337
416
|
for (const error of validationErrors) {
|
338
417
|
/*
|
339
418
|
* TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
|
@@ -358,7 +437,8 @@ function validateDocument(sourceNode, context, schema, documentNode, rule) {
|
|
358
437
|
}
|
359
438
|
catch (e) {
|
360
439
|
context.report({
|
361
|
-
|
440
|
+
// Report on first character
|
441
|
+
loc: { column: 0, line: 1 },
|
362
442
|
message: e.message,
|
363
443
|
});
|
364
444
|
}
|
@@ -440,18 +520,23 @@ const validationToRule = (ruleId, ruleName, docs, getDocumentNode) => {
|
|
440
520
|
},
|
441
521
|
},
|
442
522
|
create(context) {
|
523
|
+
if (!ruleFn) {
|
524
|
+
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...`);
|
525
|
+
return {};
|
526
|
+
}
|
527
|
+
let schema;
|
528
|
+
if (docs.requiresSchemaToExtend) {
|
529
|
+
schema = getGraphQLSchemaToExtend(ruleId, context);
|
530
|
+
}
|
531
|
+
else if (docs.requiresSchema) {
|
532
|
+
schema = requireGraphQLSchemaFromContext(ruleId, context);
|
533
|
+
}
|
443
534
|
return {
|
444
535
|
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;
|
451
536
|
const documentNode = getDocumentNode
|
452
537
|
? getDocumentNode({ ruleId, context, schema, node: node.rawNode() })
|
453
538
|
: node.rawNode();
|
454
|
-
validateDocument(
|
539
|
+
validateDocument(context, schema, documentNode, ruleFn, docs.requiresSchemaToExtend);
|
455
540
|
},
|
456
541
|
};
|
457
542
|
},
|
@@ -601,7 +686,9 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
601
686
|
}), validationToRule('possible-type-extension', 'PossibleTypeExtensions', {
|
602
687
|
category: 'Schema',
|
603
688
|
description: `A type extension is only valid if the type is defined and has the same kind.`,
|
604
|
-
recommended: false,
|
689
|
+
recommended: false,
|
690
|
+
requiresSchema: true,
|
691
|
+
requiresSchemaToExtend: true,
|
605
692
|
}), validationToRule('provided-required-arguments', 'ProvidedRequiredArguments', {
|
606
693
|
category: ['Schema', 'Operations'],
|
607
694
|
description: `A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.`,
|
@@ -629,9 +716,13 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
629
716
|
category: 'Schema',
|
630
717
|
description: `A GraphQL enum type is only valid if all its values are uniquely named.`,
|
631
718
|
recommended: false,
|
719
|
+
requiresSchema: true,
|
720
|
+
requiresSchemaToExtend: true,
|
632
721
|
}), validationToRule('unique-field-definition-names', 'UniqueFieldDefinitionNames', {
|
633
722
|
category: 'Schema',
|
634
723
|
description: `A GraphQL complex type is only valid if all its fields are uniquely named.`,
|
724
|
+
requiresSchema: true,
|
725
|
+
requiresSchemaToExtend: true,
|
635
726
|
}), validationToRule('unique-input-field-names', 'UniqueInputFieldNames', {
|
636
727
|
category: 'Operations',
|
637
728
|
description: `A GraphQL input object value is only valid if all supplied fields are uniquely named.`,
|
@@ -3034,12 +3125,13 @@ const rule$j = {
|
|
3034
3125
|
},
|
3035
3126
|
};
|
3036
3127
|
|
3128
|
+
const RULE_ID$2 = 'selection-set-depth';
|
3037
3129
|
const rule$k = {
|
3038
3130
|
meta: {
|
3039
3131
|
docs: {
|
3040
3132
|
category: 'Operations',
|
3041
3133
|
description: `Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://github.com/stems/graphql-depth-limit).`,
|
3042
|
-
url:
|
3134
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
|
3043
3135
|
requiresSiblings: true,
|
3044
3136
|
examples: [
|
3045
3137
|
{
|
@@ -3113,11 +3205,10 @@ const rule$k = {
|
|
3113
3205
|
create(context) {
|
3114
3206
|
let siblings = null;
|
3115
3207
|
try {
|
3116
|
-
siblings = requireSiblingsOperations(
|
3208
|
+
siblings = requireSiblingsOperations(RULE_ID$2, context);
|
3117
3209
|
}
|
3118
3210
|
catch (e) {
|
3119
|
-
|
3120
|
-
console.warn(`Rule "selection-set-depth" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3211
|
+
logger.warn(`Rule "${RULE_ID$2}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3121
3212
|
}
|
3122
3213
|
const { maxDepth } = context.options[0];
|
3123
3214
|
const ignore = context.options[0].ignore || [];
|
@@ -3142,8 +3233,7 @@ const rule$k = {
|
|
3142
3233
|
});
|
3143
3234
|
}
|
3144
3235
|
catch (e) {
|
3145
|
-
|
3146
|
-
console.warn(`Rule "selection-set-depth" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
|
3236
|
+
logger.warn(`Rule "${RULE_ID$2}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
|
3147
3237
|
}
|
3148
3238
|
},
|
3149
3239
|
};
|
@@ -3525,8 +3615,7 @@ function createGraphqlProcessor() {
|
|
3525
3615
|
}
|
3526
3616
|
}
|
3527
3617
|
catch (e) {
|
3528
|
-
|
3529
|
-
console.warn(`[graphql-eslint/processor]: Unable to process file "${filename}": `, e);
|
3618
|
+
logger.warn(`Unable to process file "${filename}"`, e);
|
3530
3619
|
}
|
3531
3620
|
}
|
3532
3621
|
return [text];
|
@@ -3566,20 +3655,25 @@ const schemaCache = new Map();
|
|
3566
3655
|
function getSchema(options = {}, gqlConfig) {
|
3567
3656
|
const realFilepath = options.filePath ? getOnDiskFilepath(options.filePath) : null;
|
3568
3657
|
const projectForFile = realFilepath ? gqlConfig.getProjectForFile(realFilepath) : gqlConfig.getDefault();
|
3569
|
-
const schemaKey = utils.asArray(projectForFile.schema)
|
3570
|
-
.sort()
|
3571
|
-
.join(',');
|
3658
|
+
const schemaKey = utils.asArray(projectForFile.schema).sort().join(',');
|
3572
3659
|
if (!schemaKey) {
|
3573
3660
|
return null;
|
3574
3661
|
}
|
3575
|
-
|
3576
|
-
|
3662
|
+
if (schemaCache.has(schemaKey)) {
|
3663
|
+
return schemaCache.get(schemaKey);
|
3664
|
+
}
|
3665
|
+
let schema;
|
3666
|
+
try {
|
3577
3667
|
schema = projectForFile.loadSchemaSync(projectForFile.schema, 'GraphQLSchema', {
|
3578
3668
|
cache: loaderCache,
|
3579
|
-
...options.schemaOptions
|
3669
|
+
...options.schemaOptions,
|
3580
3670
|
});
|
3581
|
-
schemaCache.set(schemaKey, schema);
|
3582
3671
|
}
|
3672
|
+
catch (e) {
|
3673
|
+
schema = null;
|
3674
|
+
logger.error('Error while loading schema\n', e);
|
3675
|
+
}
|
3676
|
+
schemaCache.set(schemaKey, schema);
|
3583
3677
|
return schema;
|
3584
3678
|
}
|
3585
3679
|
|
@@ -3592,10 +3686,10 @@ const handleVirtualPath = (documents) => {
|
|
3592
3686
|
return source;
|
3593
3687
|
}
|
3594
3688
|
(_a = filepathMap[location]) !== null && _a !== void 0 ? _a : (filepathMap[location] = -1);
|
3595
|
-
const index = filepathMap[location] += 1;
|
3689
|
+
const index = (filepathMap[location] += 1);
|
3596
3690
|
return {
|
3597
3691
|
...source,
|
3598
|
-
location: path.resolve(location, `${index}_document.graphql`)
|
3692
|
+
location: path.resolve(location, `${index}_document.graphql`),
|
3599
3693
|
};
|
3600
3694
|
});
|
3601
3695
|
};
|
@@ -3604,9 +3698,7 @@ const siblingOperationsCache = new Map();
|
|
3604
3698
|
const getSiblings = (filePath, gqlConfig) => {
|
3605
3699
|
const realFilepath = filePath ? getOnDiskFilepath(filePath) : null;
|
3606
3700
|
const projectForFile = realFilepath ? gqlConfig.getProjectForFile(realFilepath) : gqlConfig.getDefault();
|
3607
|
-
const documentsKey = utils.asArray(projectForFile.documents)
|
3608
|
-
.sort()
|
3609
|
-
.join(',');
|
3701
|
+
const documentsKey = utils.asArray(projectForFile.documents).sort().join(',');
|
3610
3702
|
if (!documentsKey) {
|
3611
3703
|
return [];
|
3612
3704
|
}
|
@@ -3614,7 +3706,7 @@ const getSiblings = (filePath, gqlConfig) => {
|
|
3614
3706
|
if (!siblings) {
|
3615
3707
|
const documents = projectForFile.loadDocumentsSync(projectForFile.documents, {
|
3616
3708
|
skipGraphQLImport: true,
|
3617
|
-
cache: loaderCache
|
3709
|
+
cache: loaderCache,
|
3618
3710
|
});
|
3619
3711
|
siblings = handleVirtualPath(documents);
|
3620
3712
|
operationsCache.set(documentsKey, siblings);
|
@@ -3627,8 +3719,7 @@ function getSiblingOperations(options, gqlConfig) {
|
|
3627
3719
|
let printed = false;
|
3628
3720
|
const noopWarn = () => {
|
3629
3721
|
if (!printed) {
|
3630
|
-
|
3631
|
-
console.warn(`getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!`);
|
3722
|
+
logger.warn(`getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!`);
|
3632
3723
|
printed = true;
|
3633
3724
|
}
|
3634
3725
|
return [];
|
@@ -3692,8 +3783,7 @@ function getSiblingOperations(options, gqlConfig) {
|
|
3692
3783
|
const name = spread.name.value;
|
3693
3784
|
const fragmentInfo = getFragment(name);
|
3694
3785
|
if (fragmentInfo.length === 0) {
|
3695
|
-
|
3696
|
-
console.warn(`Unable to locate fragment named "${name}", please make sure it's loaded using "parserOptions.operations"`);
|
3786
|
+
logger.warn(`Unable to locate fragment named "${name}", please make sure it's loaded using "parserOptions.operations"`);
|
3697
3787
|
return;
|
3698
3788
|
}
|
3699
3789
|
const fragment = fragmentInfo[0];
|
@@ -3723,42 +3813,6 @@ function getSiblingOperations(options, gqlConfig) {
|
|
3723
3813
|
return siblingOperations;
|
3724
3814
|
}
|
3725
3815
|
|
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
|
-
|
3762
3816
|
let reachableTypesCache;
|
3763
3817
|
function getReachableTypes(schema) {
|
3764
3818
|
// We don't want cache reachableTypes on test environment
|
@@ -3841,7 +3895,7 @@ function parse(code, options) {
|
|
3841
3895
|
return parseForESLint(code, options).ast;
|
3842
3896
|
}
|
3843
3897
|
function parseForESLint(code, options = {}) {
|
3844
|
-
const gqlConfig =
|
3898
|
+
const gqlConfig = loadCachedGraphQLConfig(options);
|
3845
3899
|
const schema = getSchema(options, gqlConfig);
|
3846
3900
|
const parserServices = {
|
3847
3901
|
hasTypeInfo: schema !== null,
|
@@ -3873,6 +3927,7 @@ function parseForESLint(code, options = {}) {
|
|
3873
3927
|
};
|
3874
3928
|
}
|
3875
3929
|
catch (e) {
|
3930
|
+
e.message = `[graphql-eslint] ${e.message}`;
|
3876
3931
|
// In case of GraphQL parser error, we report it to ESLint as a parser error that matches the requirements
|
3877
3932
|
// of ESLint. This will make sure to display it correctly in IDEs and lint results.
|
3878
3933
|
if (e instanceof graphql.GraphQLError) {
|
@@ -3880,11 +3935,10 @@ function parseForESLint(code, options = {}) {
|
|
3880
3935
|
index: e.positions[0],
|
3881
3936
|
lineNumber: e.locations[0].line,
|
3882
3937
|
column: e.locations[0].column,
|
3883
|
-
message:
|
3938
|
+
message: e.message,
|
3884
3939
|
};
|
3885
3940
|
throw eslintError;
|
3886
3941
|
}
|
3887
|
-
e.message = `[graphql-eslint]: ${e.message}`;
|
3888
3942
|
throw e;
|
3889
3943
|
}
|
3890
3944
|
}
|
package/index.mjs
CHANGED
@@ -4,10 +4,11 @@ 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
|
8
|
-
import { parseCode } from '@graphql-tools/graphql-tag-pluck';
|
7
|
+
import chalk from 'chalk';
|
9
8
|
import { loadConfigSync, GraphQLConfig } from 'graphql-config';
|
10
9
|
import { CodeFileLoader } from '@graphql-tools/code-file-loader';
|
10
|
+
import depthLimit from 'graphql-depth-limit';
|
11
|
+
import { parseCode } from '@graphql-tools/graphql-tag-pluck';
|
11
12
|
import { RuleTester, Linter } from 'eslint';
|
12
13
|
import { codeFrameColumns } from '@babel/code-frame';
|
13
14
|
|
@@ -168,6 +169,47 @@ const configs = {
|
|
168
169
|
'operations-all': operationsAllConfig,
|
169
170
|
};
|
170
171
|
|
172
|
+
let graphQLConfig;
|
173
|
+
function loadCachedGraphQLConfig(options) {
|
174
|
+
// We don't want cache config on test environment
|
175
|
+
// Otherwise schema and documents will be same for all tests
|
176
|
+
if (process.env.NODE_ENV !== 'test' && graphQLConfig) {
|
177
|
+
return graphQLConfig;
|
178
|
+
}
|
179
|
+
graphQLConfig = loadGraphQLConfig(options);
|
180
|
+
return graphQLConfig;
|
181
|
+
}
|
182
|
+
function loadGraphQLConfig(options) {
|
183
|
+
const onDiskConfig = options.skipGraphQLConfig
|
184
|
+
? null
|
185
|
+
: loadConfigSync({
|
186
|
+
throwOnEmpty: false,
|
187
|
+
throwOnMissing: false,
|
188
|
+
extensions: [addCodeFileLoaderExtension],
|
189
|
+
});
|
190
|
+
const configOptions = options.projects
|
191
|
+
? { projects: options.projects }
|
192
|
+
: {
|
193
|
+
schema: (options.schema || ''),
|
194
|
+
documents: options.documents || options.operations,
|
195
|
+
extensions: options.extensions,
|
196
|
+
include: options.include,
|
197
|
+
exclude: options.exclude,
|
198
|
+
};
|
199
|
+
graphQLConfig =
|
200
|
+
onDiskConfig ||
|
201
|
+
new GraphQLConfig({
|
202
|
+
config: configOptions,
|
203
|
+
filepath: 'virtual-config',
|
204
|
+
}, [addCodeFileLoaderExtension]);
|
205
|
+
return graphQLConfig;
|
206
|
+
}
|
207
|
+
const addCodeFileLoaderExtension = api => {
|
208
|
+
api.loaders.schema.register(new CodeFileLoader());
|
209
|
+
api.loaders.documents.register(new CodeFileLoader());
|
210
|
+
return { name: 'graphql-eslint-loaders' };
|
211
|
+
};
|
212
|
+
|
171
213
|
function requireSiblingsOperations(ruleName, context) {
|
172
214
|
if (!context.parserServices) {
|
173
215
|
throw new Error(`Rule '${ruleName}' requires 'parserOptions.operations' to be set and loaded. See http://bit.ly/graphql-eslint-operations for more info`);
|
@@ -186,6 +228,43 @@ function requireGraphQLSchemaFromContext(ruleName, context) {
|
|
186
228
|
}
|
187
229
|
return context.parserServices.schema;
|
188
230
|
}
|
231
|
+
const logger = {
|
232
|
+
// eslint-disable-next-line no-console
|
233
|
+
error: (...args) => console.error(chalk.red('error'), '[graphql-eslint]', chalk(...args)),
|
234
|
+
// eslint-disable-next-line no-console
|
235
|
+
warn: (...args) => console.warn(chalk.yellow('warning'), '[graphql-eslint]', chalk(...args)),
|
236
|
+
};
|
237
|
+
const schemaToExtendCache = new Map();
|
238
|
+
const getGraphQLSchemaToExtend = (ruleId, context) => {
|
239
|
+
// If schema is not loaded, there is no reason to make partial schema aka schemaToExtend
|
240
|
+
if (!context.parserServices.hasTypeInfo) {
|
241
|
+
if (!getGraphQLSchemaToExtend.warningPrintedMap[ruleId]) {
|
242
|
+
logger.warn(`Rule "${ruleId}" works best with schema loaded. See http://bit.ly/graphql-eslint-schema for more info`);
|
243
|
+
getGraphQLSchemaToExtend.warningPrintedMap[ruleId] = true;
|
244
|
+
}
|
245
|
+
return null;
|
246
|
+
}
|
247
|
+
const filename = context.getPhysicalFilename();
|
248
|
+
if (!schemaToExtendCache.has(filename)) {
|
249
|
+
const { schema, schemaOptions } = context.parserOptions;
|
250
|
+
const gqlConfig = loadGraphQLConfig({ schema });
|
251
|
+
const projectForFile = gqlConfig.getProjectForFile(filename);
|
252
|
+
let schemaToExtend;
|
253
|
+
try {
|
254
|
+
schemaToExtend = projectForFile.loadSchemaSync(projectForFile.schema, 'GraphQLSchema', {
|
255
|
+
...schemaOptions,
|
256
|
+
ignore: filename,
|
257
|
+
});
|
258
|
+
}
|
259
|
+
catch (_a) {
|
260
|
+
// If error throws just ignore it because maybe schema is located in 1 file
|
261
|
+
schemaToExtend = null;
|
262
|
+
}
|
263
|
+
schemaToExtendCache.set(filename, schemaToExtend);
|
264
|
+
}
|
265
|
+
return schemaToExtendCache.get(filename);
|
266
|
+
};
|
267
|
+
getGraphQLSchemaToExtend.warningPrintedMap = Object.create(null);
|
189
268
|
function requireReachableTypesFromContext(ruleName, context) {
|
190
269
|
const schema = requireGraphQLSchemaFromContext(ruleName, context);
|
191
270
|
return context.parserServices.reachableTypes(schema);
|
@@ -320,14 +399,14 @@ function getLocation(loc, fieldName = '', offset) {
|
|
320
399
|
};
|
321
400
|
}
|
322
401
|
|
323
|
-
function validateDocument(
|
402
|
+
function validateDocument(context, schema = null, documentNode, rule, isSchemaToExtend = false) {
|
324
403
|
if (documentNode.definitions.length === 0) {
|
325
404
|
return;
|
326
405
|
}
|
327
406
|
try {
|
328
|
-
const validationErrors = schema
|
407
|
+
const validationErrors = schema && !isSchemaToExtend
|
329
408
|
? validate(schema, documentNode, [rule])
|
330
|
-
: validateSDL(documentNode,
|
409
|
+
: validateSDL(documentNode, schema, [rule]);
|
331
410
|
for (const error of validationErrors) {
|
332
411
|
/*
|
333
412
|
* TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
|
@@ -352,7 +431,8 @@ function validateDocument(sourceNode, context, schema, documentNode, rule) {
|
|
352
431
|
}
|
353
432
|
catch (e) {
|
354
433
|
context.report({
|
355
|
-
|
434
|
+
// Report on first character
|
435
|
+
loc: { column: 0, line: 1 },
|
356
436
|
message: e.message,
|
357
437
|
});
|
358
438
|
}
|
@@ -434,18 +514,23 @@ const validationToRule = (ruleId, ruleName, docs, getDocumentNode) => {
|
|
434
514
|
},
|
435
515
|
},
|
436
516
|
create(context) {
|
517
|
+
if (!ruleFn) {
|
518
|
+
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...`);
|
519
|
+
return {};
|
520
|
+
}
|
521
|
+
let schema;
|
522
|
+
if (docs.requiresSchemaToExtend) {
|
523
|
+
schema = getGraphQLSchemaToExtend(ruleId, context);
|
524
|
+
}
|
525
|
+
else if (docs.requiresSchema) {
|
526
|
+
schema = requireGraphQLSchemaFromContext(ruleId, context);
|
527
|
+
}
|
437
528
|
return {
|
438
529
|
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;
|
445
530
|
const documentNode = getDocumentNode
|
446
531
|
? getDocumentNode({ ruleId, context, schema, node: node.rawNode() })
|
447
532
|
: node.rawNode();
|
448
|
-
validateDocument(
|
533
|
+
validateDocument(context, schema, documentNode, ruleFn, docs.requiresSchemaToExtend);
|
449
534
|
},
|
450
535
|
};
|
451
536
|
},
|
@@ -595,7 +680,9 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
595
680
|
}), validationToRule('possible-type-extension', 'PossibleTypeExtensions', {
|
596
681
|
category: 'Schema',
|
597
682
|
description: `A type extension is only valid if the type is defined and has the same kind.`,
|
598
|
-
recommended: false,
|
683
|
+
recommended: false,
|
684
|
+
requiresSchema: true,
|
685
|
+
requiresSchemaToExtend: true,
|
599
686
|
}), validationToRule('provided-required-arguments', 'ProvidedRequiredArguments', {
|
600
687
|
category: ['Schema', 'Operations'],
|
601
688
|
description: `A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.`,
|
@@ -623,9 +710,13 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
623
710
|
category: 'Schema',
|
624
711
|
description: `A GraphQL enum type is only valid if all its values are uniquely named.`,
|
625
712
|
recommended: false,
|
713
|
+
requiresSchema: true,
|
714
|
+
requiresSchemaToExtend: true,
|
626
715
|
}), validationToRule('unique-field-definition-names', 'UniqueFieldDefinitionNames', {
|
627
716
|
category: 'Schema',
|
628
717
|
description: `A GraphQL complex type is only valid if all its fields are uniquely named.`,
|
718
|
+
requiresSchema: true,
|
719
|
+
requiresSchemaToExtend: true,
|
629
720
|
}), validationToRule('unique-input-field-names', 'UniqueInputFieldNames', {
|
630
721
|
category: 'Operations',
|
631
722
|
description: `A GraphQL input object value is only valid if all supplied fields are uniquely named.`,
|
@@ -3028,12 +3119,13 @@ const rule$j = {
|
|
3028
3119
|
},
|
3029
3120
|
};
|
3030
3121
|
|
3122
|
+
const RULE_ID$2 = 'selection-set-depth';
|
3031
3123
|
const rule$k = {
|
3032
3124
|
meta: {
|
3033
3125
|
docs: {
|
3034
3126
|
category: 'Operations',
|
3035
3127
|
description: `Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://github.com/stems/graphql-depth-limit).`,
|
3036
|
-
url:
|
3128
|
+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_ID$2}.md`,
|
3037
3129
|
requiresSiblings: true,
|
3038
3130
|
examples: [
|
3039
3131
|
{
|
@@ -3107,11 +3199,10 @@ const rule$k = {
|
|
3107
3199
|
create(context) {
|
3108
3200
|
let siblings = null;
|
3109
3201
|
try {
|
3110
|
-
siblings = requireSiblingsOperations(
|
3202
|
+
siblings = requireSiblingsOperations(RULE_ID$2, context);
|
3111
3203
|
}
|
3112
3204
|
catch (e) {
|
3113
|
-
|
3114
|
-
console.warn(`Rule "selection-set-depth" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3205
|
+
logger.warn(`Rule "${RULE_ID$2}" works best with siblings operations loaded. For more info: http://bit.ly/graphql-eslint-operations`);
|
3115
3206
|
}
|
3116
3207
|
const { maxDepth } = context.options[0];
|
3117
3208
|
const ignore = context.options[0].ignore || [];
|
@@ -3136,8 +3227,7 @@ const rule$k = {
|
|
3136
3227
|
});
|
3137
3228
|
}
|
3138
3229
|
catch (e) {
|
3139
|
-
|
3140
|
-
console.warn(`Rule "selection-set-depth" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
|
3230
|
+
logger.warn(`Rule "${RULE_ID$2}" check failed due to a missing siblings operations. For more info: http://bit.ly/graphql-eslint-operations`, e);
|
3141
3231
|
}
|
3142
3232
|
},
|
3143
3233
|
};
|
@@ -3519,8 +3609,7 @@ function createGraphqlProcessor() {
|
|
3519
3609
|
}
|
3520
3610
|
}
|
3521
3611
|
catch (e) {
|
3522
|
-
|
3523
|
-
console.warn(`[graphql-eslint/processor]: Unable to process file "${filename}": `, e);
|
3612
|
+
logger.warn(`Unable to process file "${filename}"`, e);
|
3524
3613
|
}
|
3525
3614
|
}
|
3526
3615
|
return [text];
|
@@ -3560,20 +3649,25 @@ const schemaCache = new Map();
|
|
3560
3649
|
function getSchema(options = {}, gqlConfig) {
|
3561
3650
|
const realFilepath = options.filePath ? getOnDiskFilepath(options.filePath) : null;
|
3562
3651
|
const projectForFile = realFilepath ? gqlConfig.getProjectForFile(realFilepath) : gqlConfig.getDefault();
|
3563
|
-
const schemaKey = asArray(projectForFile.schema)
|
3564
|
-
.sort()
|
3565
|
-
.join(',');
|
3652
|
+
const schemaKey = asArray(projectForFile.schema).sort().join(',');
|
3566
3653
|
if (!schemaKey) {
|
3567
3654
|
return null;
|
3568
3655
|
}
|
3569
|
-
|
3570
|
-
|
3656
|
+
if (schemaCache.has(schemaKey)) {
|
3657
|
+
return schemaCache.get(schemaKey);
|
3658
|
+
}
|
3659
|
+
let schema;
|
3660
|
+
try {
|
3571
3661
|
schema = projectForFile.loadSchemaSync(projectForFile.schema, 'GraphQLSchema', {
|
3572
3662
|
cache: loaderCache,
|
3573
|
-
...options.schemaOptions
|
3663
|
+
...options.schemaOptions,
|
3574
3664
|
});
|
3575
|
-
schemaCache.set(schemaKey, schema);
|
3576
3665
|
}
|
3666
|
+
catch (e) {
|
3667
|
+
schema = null;
|
3668
|
+
logger.error('Error while loading schema\n', e);
|
3669
|
+
}
|
3670
|
+
schemaCache.set(schemaKey, schema);
|
3577
3671
|
return schema;
|
3578
3672
|
}
|
3579
3673
|
|
@@ -3586,10 +3680,10 @@ const handleVirtualPath = (documents) => {
|
|
3586
3680
|
return source;
|
3587
3681
|
}
|
3588
3682
|
(_a = filepathMap[location]) !== null && _a !== void 0 ? _a : (filepathMap[location] = -1);
|
3589
|
-
const index = filepathMap[location] += 1;
|
3683
|
+
const index = (filepathMap[location] += 1);
|
3590
3684
|
return {
|
3591
3685
|
...source,
|
3592
|
-
location: resolve(location, `${index}_document.graphql`)
|
3686
|
+
location: resolve(location, `${index}_document.graphql`),
|
3593
3687
|
};
|
3594
3688
|
});
|
3595
3689
|
};
|
@@ -3598,9 +3692,7 @@ const siblingOperationsCache = new Map();
|
|
3598
3692
|
const getSiblings = (filePath, gqlConfig) => {
|
3599
3693
|
const realFilepath = filePath ? getOnDiskFilepath(filePath) : null;
|
3600
3694
|
const projectForFile = realFilepath ? gqlConfig.getProjectForFile(realFilepath) : gqlConfig.getDefault();
|
3601
|
-
const documentsKey = asArray(projectForFile.documents)
|
3602
|
-
.sort()
|
3603
|
-
.join(',');
|
3695
|
+
const documentsKey = asArray(projectForFile.documents).sort().join(',');
|
3604
3696
|
if (!documentsKey) {
|
3605
3697
|
return [];
|
3606
3698
|
}
|
@@ -3608,7 +3700,7 @@ const getSiblings = (filePath, gqlConfig) => {
|
|
3608
3700
|
if (!siblings) {
|
3609
3701
|
const documents = projectForFile.loadDocumentsSync(projectForFile.documents, {
|
3610
3702
|
skipGraphQLImport: true,
|
3611
|
-
cache: loaderCache
|
3703
|
+
cache: loaderCache,
|
3612
3704
|
});
|
3613
3705
|
siblings = handleVirtualPath(documents);
|
3614
3706
|
operationsCache.set(documentsKey, siblings);
|
@@ -3621,8 +3713,7 @@ function getSiblingOperations(options, gqlConfig) {
|
|
3621
3713
|
let printed = false;
|
3622
3714
|
const noopWarn = () => {
|
3623
3715
|
if (!printed) {
|
3624
|
-
|
3625
|
-
console.warn(`getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!`);
|
3716
|
+
logger.warn(`getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!`);
|
3626
3717
|
printed = true;
|
3627
3718
|
}
|
3628
3719
|
return [];
|
@@ -3686,8 +3777,7 @@ function getSiblingOperations(options, gqlConfig) {
|
|
3686
3777
|
const name = spread.name.value;
|
3687
3778
|
const fragmentInfo = getFragment(name);
|
3688
3779
|
if (fragmentInfo.length === 0) {
|
3689
|
-
|
3690
|
-
console.warn(`Unable to locate fragment named "${name}", please make sure it's loaded using "parserOptions.operations"`);
|
3780
|
+
logger.warn(`Unable to locate fragment named "${name}", please make sure it's loaded using "parserOptions.operations"`);
|
3691
3781
|
return;
|
3692
3782
|
}
|
3693
3783
|
const fragment = fragmentInfo[0];
|
@@ -3717,42 +3807,6 @@ function getSiblingOperations(options, gqlConfig) {
|
|
3717
3807
|
return siblingOperations;
|
3718
3808
|
}
|
3719
3809
|
|
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
|
-
|
3756
3810
|
let reachableTypesCache;
|
3757
3811
|
function getReachableTypes(schema) {
|
3758
3812
|
// We don't want cache reachableTypes on test environment
|
@@ -3835,7 +3889,7 @@ function parse(code, options) {
|
|
3835
3889
|
return parseForESLint(code, options).ast;
|
3836
3890
|
}
|
3837
3891
|
function parseForESLint(code, options = {}) {
|
3838
|
-
const gqlConfig =
|
3892
|
+
const gqlConfig = loadCachedGraphQLConfig(options);
|
3839
3893
|
const schema = getSchema(options, gqlConfig);
|
3840
3894
|
const parserServices = {
|
3841
3895
|
hasTypeInfo: schema !== null,
|
@@ -3867,6 +3921,7 @@ function parseForESLint(code, options = {}) {
|
|
3867
3921
|
};
|
3868
3922
|
}
|
3869
3923
|
catch (e) {
|
3924
|
+
e.message = `[graphql-eslint] ${e.message}`;
|
3870
3925
|
// In case of GraphQL parser error, we report it to ESLint as a parser error that matches the requirements
|
3871
3926
|
// of ESLint. This will make sure to display it correctly in IDEs and lint results.
|
3872
3927
|
if (e instanceof GraphQLError) {
|
@@ -3874,11 +3929,10 @@ function parseForESLint(code, options = {}) {
|
|
3874
3929
|
index: e.positions[0],
|
3875
3930
|
lineNumber: e.locations[0].line,
|
3876
3931
|
column: e.locations[0].column,
|
3877
|
-
message:
|
3932
|
+
message: e.message,
|
3878
3933
|
};
|
3879
3934
|
throw eslintError;
|
3880
3935
|
}
|
3881
|
-
e.message = `[graphql-eslint]: ${e.message}`;
|
3882
3936
|
throw e;
|
3883
3937
|
}
|
3884
3938
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@graphql-eslint/eslint-plugin",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.4.0-alpha-c01d913.0",
|
4
4
|
"description": "GraphQL plugin for ESLint",
|
5
5
|
"sideEffects": false,
|
6
6
|
"peerDependencies": {
|
@@ -11,6 +11,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"
|
package/types.d.ts
CHANGED
package/utils.d.ts
CHANGED
@@ -6,6 +6,15 @@ 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
|
+
};
|
13
|
+
declare type GetGraphQLSchemaToExtend = {
|
14
|
+
(ruleId: string, ctx: GraphQLESLintRuleContext): GraphQLSchema | null;
|
15
|
+
warningPrintedMap: Record<string, boolean>;
|
16
|
+
};
|
17
|
+
export declare const getGraphQLSchemaToExtend: GetGraphQLSchemaToExtend;
|
9
18
|
export declare function requireReachableTypesFromContext(ruleName: string, context: GraphQLESLintRuleContext): ReachableTypes | never;
|
10
19
|
export declare function requireUsedFieldsFromContext(ruleName: string, context: GraphQLESLintRuleContext): UsedFields | never;
|
11
20
|
export declare function extractTokens(source: Source): AST.Token[];
|
@@ -27,3 +36,4 @@ export declare function getLocation(loc: Partial<AST.SourceLocation>, fieldName?
|
|
27
36
|
offsetStart?: number;
|
28
37
|
offsetEnd?: number;
|
29
38
|
}): AST.SourceLocation;
|
39
|
+
export {};
|