@graphql-eslint/eslint-plugin 3.14.0-alpha-20221222211539-5e993f5 → 3.14.0-alpha-20221223011223-bd3e820
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/cjs/configs/index.js +10 -10
- package/cjs/documents.js +5 -5
- package/cjs/estree-converter/converter.js +2 -2
- package/cjs/estree-converter/index.js +3 -3
- package/cjs/estree-converter/utils.js +2 -2
- package/cjs/flat-configs.js +36 -0
- package/cjs/index.js +16 -14
- package/cjs/parser.js +13 -13
- package/cjs/processor.js +2 -2
- package/cjs/rules/alphabetize.js +7 -7
- package/cjs/rules/graphql-js-validation.js +9 -9
- package/cjs/rules/index.js +66 -66
- package/cjs/rules/lone-executable-definition.js +4 -4
- package/cjs/rules/match-document-filename.js +4 -4
- package/cjs/rules/naming-convention.js +6 -6
- package/cjs/rules/no-anonymous-operations.js +2 -2
- package/cjs/rules/no-deprecated.js +2 -2
- package/cjs/rules/no-one-place-fragments.js +3 -4
- package/cjs/rules/no-root-type.js +3 -3
- package/cjs/rules/no-scalar-result-type-on-mutation.js +2 -2
- package/cjs/rules/no-unreachable-types.js +3 -3
- package/cjs/rules/no-unused-fields.js +3 -3
- package/cjs/rules/relay-arguments.js +2 -2
- package/cjs/rules/relay-edge-types.js +6 -6
- package/cjs/rules/relay-page-info.js +5 -5
- package/cjs/rules/require-deprecation-date.js +2 -2
- package/cjs/rules/require-deprecation-reason.js +2 -2
- package/cjs/rules/require-description.js +8 -8
- package/cjs/rules/require-field-of-type-query-in-mutation-result.js +3 -3
- package/cjs/rules/require-id-when-available.js +8 -8
- package/cjs/rules/selection-set-depth.js +5 -5
- package/cjs/rules/strict-id-in-types.js +7 -7
- package/cjs/rules/unique-fragment-name.js +4 -4
- package/cjs/rules/unique-operation-name.js +2 -2
- package/cjs/schema.js +2 -2
- package/esm/cache.js +25 -0
- package/esm/configs/base.js +4 -0
- package/esm/configs/index.js +12 -0
- package/esm/configs/operations-all.js +29 -0
- package/esm/configs/operations-recommended.js +53 -0
- package/esm/configs/relay.js +9 -0
- package/esm/configs/schema-all.js +22 -0
- package/esm/configs/schema-recommended.js +49 -0
- package/esm/documents.js +144 -0
- package/esm/estree-converter/converter.js +58 -0
- package/esm/estree-converter/index.js +3 -0
- package/esm/estree-converter/types.js +1 -0
- package/esm/estree-converter/utils.js +102 -0
- package/esm/flat-configs.js +33 -0
- package/esm/graphql-config.js +49 -0
- package/esm/index.js +9 -0
- package/esm/package.json +1 -0
- package/esm/parser.js +56 -0
- package/esm/processor.js +75 -0
- package/esm/rules/alphabetize.js +344 -0
- package/esm/rules/description-style.js +75 -0
- package/esm/rules/graphql-js-validation.js +498 -0
- package/esm/rules/index.js +71 -0
- package/esm/rules/input-name.js +133 -0
- package/esm/rules/lone-executable-definition.js +85 -0
- package/esm/rules/match-document-filename.js +232 -0
- package/esm/rules/naming-convention.js +307 -0
- package/esm/rules/no-anonymous-operations.js +64 -0
- package/esm/rules/no-case-insensitive-enum-values-duplicates.js +58 -0
- package/esm/rules/no-deprecated.js +121 -0
- package/esm/rules/no-duplicate-fields.js +109 -0
- package/esm/rules/no-hashtag-description.js +86 -0
- package/esm/rules/no-one-place-fragments.js +80 -0
- package/esm/rules/no-root-type.js +83 -0
- package/esm/rules/no-scalar-result-type-on-mutation.js +63 -0
- package/esm/rules/no-typename-prefix.js +62 -0
- package/esm/rules/no-unreachable-types.js +154 -0
- package/esm/rules/no-unused-fields.js +127 -0
- package/esm/rules/relay-arguments.js +118 -0
- package/esm/rules/relay-connection-types.js +104 -0
- package/esm/rules/relay-edge-types.js +186 -0
- package/esm/rules/relay-page-info.js +97 -0
- package/esm/rules/require-deprecation-date.js +120 -0
- package/esm/rules/require-deprecation-reason.js +53 -0
- package/esm/rules/require-description.js +190 -0
- package/esm/rules/require-field-of-type-query-in-mutation-result.js +69 -0
- package/esm/rules/require-id-when-available.js +196 -0
- package/esm/rules/require-nullable-fields-with-oneof.js +58 -0
- package/esm/rules/require-type-pattern-with-oneof.js +57 -0
- package/esm/rules/selection-set-depth.js +131 -0
- package/esm/rules/strict-id-in-types.js +159 -0
- package/esm/rules/unique-fragment-name.js +86 -0
- package/esm/rules/unique-operation-name.js +62 -0
- package/esm/schema.js +37 -0
- package/esm/testkit.js +181 -0
- package/esm/types.js +1 -0
- package/esm/utils.js +83 -0
- package/package.json +10 -1
- package/typings/estree-converter/converter.d.cts +1 -1
- package/typings/estree-converter/converter.d.ts +1 -1
- package/typings/estree-converter/index.d.cts +3 -3
- package/typings/estree-converter/index.d.ts +3 -3
- package/typings/estree-converter/types.d.cts +3 -3
- package/typings/estree-converter/types.d.ts +3 -3
- package/typings/estree-converter/utils.d.cts +2 -2
- package/typings/estree-converter/utils.d.ts +2 -2
- package/typings/flat-configs.d.cts +248 -0
- package/typings/flat-configs.d.ts +248 -0
- package/typings/graphql-config.d.cts +1 -1
- package/typings/graphql-config.d.ts +1 -1
- package/typings/index.d.cts +8 -7
- package/typings/index.d.ts +8 -7
- package/typings/parser.d.cts +1 -1
- package/typings/parser.d.ts +1 -1
- package/typings/rules/alphabetize.d.cts +1 -1
- package/typings/rules/alphabetize.d.ts +1 -1
- package/typings/rules/description-style.d.cts +1 -1
- package/typings/rules/description-style.d.ts +1 -1
- package/typings/rules/graphql-js-validation.d.cts +1 -1
- package/typings/rules/graphql-js-validation.d.ts +1 -1
- package/typings/rules/index.d.cts +45 -45
- package/typings/rules/index.d.ts +45 -45
- package/typings/rules/input-name.d.cts +1 -1
- package/typings/rules/input-name.d.ts +1 -1
- package/typings/rules/lone-executable-definition.d.cts +1 -1
- package/typings/rules/lone-executable-definition.d.ts +1 -1
- package/typings/rules/match-document-filename.d.cts +2 -2
- package/typings/rules/match-document-filename.d.ts +2 -2
- package/typings/rules/naming-convention.d.cts +1 -1
- package/typings/rules/naming-convention.d.ts +1 -1
- package/typings/rules/no-anonymous-operations.d.cts +1 -1
- package/typings/rules/no-anonymous-operations.d.ts +1 -1
- package/typings/rules/no-case-insensitive-enum-values-duplicates.d.cts +1 -1
- package/typings/rules/no-case-insensitive-enum-values-duplicates.d.ts +1 -1
- package/typings/rules/no-deprecated.d.cts +1 -1
- package/typings/rules/no-deprecated.d.ts +1 -1
- package/typings/rules/no-duplicate-fields.d.cts +1 -1
- package/typings/rules/no-duplicate-fields.d.ts +1 -1
- package/typings/rules/no-hashtag-description.d.cts +1 -1
- package/typings/rules/no-hashtag-description.d.ts +1 -1
- package/typings/rules/no-one-place-fragments.d.cts +1 -1
- package/typings/rules/no-one-place-fragments.d.ts +1 -1
- package/typings/rules/no-root-type.d.cts +1 -1
- package/typings/rules/no-root-type.d.ts +1 -1
- package/typings/rules/no-scalar-result-type-on-mutation.d.cts +1 -1
- package/typings/rules/no-scalar-result-type-on-mutation.d.ts +1 -1
- package/typings/rules/no-typename-prefix.d.cts +1 -1
- package/typings/rules/no-typename-prefix.d.ts +1 -1
- package/typings/rules/no-unreachable-types.d.cts +1 -1
- package/typings/rules/no-unreachable-types.d.ts +1 -1
- package/typings/rules/no-unused-fields.d.cts +1 -1
- package/typings/rules/no-unused-fields.d.ts +1 -1
- package/typings/rules/relay-arguments.d.cts +1 -1
- package/typings/rules/relay-arguments.d.ts +1 -1
- package/typings/rules/relay-connection-types.d.cts +1 -1
- package/typings/rules/relay-connection-types.d.ts +1 -1
- package/typings/rules/relay-edge-types.d.cts +1 -1
- package/typings/rules/relay-edge-types.d.ts +1 -1
- package/typings/rules/relay-page-info.d.cts +1 -1
- package/typings/rules/relay-page-info.d.ts +1 -1
- package/typings/rules/require-deprecation-date.d.cts +1 -1
- package/typings/rules/require-deprecation-date.d.ts +1 -1
- package/typings/rules/require-deprecation-reason.d.cts +1 -1
- package/typings/rules/require-deprecation-reason.d.ts +1 -1
- package/typings/rules/require-description.d.cts +1 -1
- package/typings/rules/require-description.d.ts +1 -1
- package/typings/rules/require-field-of-type-query-in-mutation-result.d.cts +1 -1
- package/typings/rules/require-field-of-type-query-in-mutation-result.d.ts +1 -1
- package/typings/rules/require-id-when-available.d.cts +1 -1
- package/typings/rules/require-id-when-available.d.ts +1 -1
- package/typings/rules/require-nullable-fields-with-oneof.d.cts +1 -1
- package/typings/rules/require-nullable-fields-with-oneof.d.ts +1 -1
- package/typings/rules/require-type-pattern-with-oneof.d.cts +1 -1
- package/typings/rules/require-type-pattern-with-oneof.d.ts +1 -1
- package/typings/rules/selection-set-depth.d.cts +1 -1
- package/typings/rules/selection-set-depth.d.ts +1 -1
- package/typings/rules/strict-id-in-types.d.cts +1 -1
- package/typings/rules/strict-id-in-types.d.ts +1 -1
- package/typings/rules/unique-fragment-name.d.cts +2 -2
- package/typings/rules/unique-fragment-name.d.ts +2 -2
- package/typings/rules/unique-operation-name.d.cts +1 -1
- package/typings/rules/unique-operation-name.d.ts +1 -1
- package/typings/schema.d.cts +1 -1
- package/typings/schema.d.ts +1 -1
- package/typings/testkit.d.cts +3 -3
- package/typings/testkit.d.ts +3 -3
- package/typings/types.d.cts +2 -2
- package/typings/types.d.ts +2 -2
- package/typings/utils.d.cts +2 -2
- package/typings/utils.d.ts +2 -2
@@ -0,0 +1,29 @@
|
|
1
|
+
/*
|
2
|
+
* 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
|
3
|
+
*/
|
4
|
+
export default {
|
5
|
+
extends: ['./configs/base', './configs/operations-recommended'],
|
6
|
+
rules: {
|
7
|
+
'@graphql-eslint/alphabetize': [
|
8
|
+
'error',
|
9
|
+
{
|
10
|
+
selections: ['OperationDefinition', 'FragmentDefinition'],
|
11
|
+
variables: ['OperationDefinition'],
|
12
|
+
arguments: ['Field', 'Directive'],
|
13
|
+
},
|
14
|
+
],
|
15
|
+
'@graphql-eslint/lone-executable-definition': 'error',
|
16
|
+
'@graphql-eslint/match-document-filename': [
|
17
|
+
'error',
|
18
|
+
{
|
19
|
+
query: 'kebab-case',
|
20
|
+
mutation: 'kebab-case',
|
21
|
+
subscription: 'kebab-case',
|
22
|
+
fragment: 'kebab-case',
|
23
|
+
},
|
24
|
+
],
|
25
|
+
'@graphql-eslint/no-one-place-fragments': 'error',
|
26
|
+
'@graphql-eslint/unique-fragment-name': 'error',
|
27
|
+
'@graphql-eslint/unique-operation-name': 'error',
|
28
|
+
},
|
29
|
+
};
|
@@ -0,0 +1,53 @@
|
|
1
|
+
/*
|
2
|
+
* 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
|
3
|
+
*/
|
4
|
+
export default {
|
5
|
+
extends: './configs/base',
|
6
|
+
rules: {
|
7
|
+
'@graphql-eslint/executable-definitions': 'error',
|
8
|
+
'@graphql-eslint/fields-on-correct-type': 'error',
|
9
|
+
'@graphql-eslint/fragments-on-composite-type': 'error',
|
10
|
+
'@graphql-eslint/known-argument-names': 'error',
|
11
|
+
'@graphql-eslint/known-directives': 'error',
|
12
|
+
'@graphql-eslint/known-fragment-names': 'error',
|
13
|
+
'@graphql-eslint/known-type-names': 'error',
|
14
|
+
'@graphql-eslint/lone-anonymous-operation': 'error',
|
15
|
+
'@graphql-eslint/naming-convention': [
|
16
|
+
'error',
|
17
|
+
{
|
18
|
+
VariableDefinition: 'camelCase',
|
19
|
+
OperationDefinition: {
|
20
|
+
style: 'PascalCase',
|
21
|
+
forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
|
22
|
+
forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
|
23
|
+
},
|
24
|
+
FragmentDefinition: {
|
25
|
+
style: 'PascalCase',
|
26
|
+
forbiddenPrefixes: ['Fragment'],
|
27
|
+
forbiddenSuffixes: ['Fragment'],
|
28
|
+
},
|
29
|
+
},
|
30
|
+
],
|
31
|
+
'@graphql-eslint/no-anonymous-operations': 'error',
|
32
|
+
'@graphql-eslint/no-deprecated': 'error',
|
33
|
+
'@graphql-eslint/no-duplicate-fields': 'error',
|
34
|
+
'@graphql-eslint/no-fragment-cycles': 'error',
|
35
|
+
'@graphql-eslint/no-undefined-variables': 'error',
|
36
|
+
'@graphql-eslint/no-unused-fragments': 'error',
|
37
|
+
'@graphql-eslint/no-unused-variables': 'error',
|
38
|
+
'@graphql-eslint/one-field-subscriptions': 'error',
|
39
|
+
'@graphql-eslint/overlapping-fields-can-be-merged': 'error',
|
40
|
+
'@graphql-eslint/possible-fragment-spread': 'error',
|
41
|
+
'@graphql-eslint/provided-required-arguments': 'error',
|
42
|
+
'@graphql-eslint/require-id-when-available': 'error',
|
43
|
+
'@graphql-eslint/scalar-leafs': 'error',
|
44
|
+
'@graphql-eslint/selection-set-depth': ['error', { maxDepth: 7 }],
|
45
|
+
'@graphql-eslint/unique-argument-names': 'error',
|
46
|
+
'@graphql-eslint/unique-directive-names-per-location': 'error',
|
47
|
+
'@graphql-eslint/unique-input-field-names': 'error',
|
48
|
+
'@graphql-eslint/unique-variable-names': 'error',
|
49
|
+
'@graphql-eslint/value-literals-of-correct-type': 'error',
|
50
|
+
'@graphql-eslint/variables-are-input-types': 'error',
|
51
|
+
'@graphql-eslint/variables-in-allowed-position': 'error',
|
52
|
+
},
|
53
|
+
};
|
@@ -0,0 +1,22 @@
|
|
1
|
+
/*
|
2
|
+
* 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
|
3
|
+
*/
|
4
|
+
export default {
|
5
|
+
extends: ['./configs/base', './configs/schema-recommended'],
|
6
|
+
rules: {
|
7
|
+
'@graphql-eslint/alphabetize': [
|
8
|
+
'error',
|
9
|
+
{
|
10
|
+
fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
|
11
|
+
values: ['EnumTypeDefinition'],
|
12
|
+
arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
|
13
|
+
},
|
14
|
+
],
|
15
|
+
'@graphql-eslint/input-name': 'error',
|
16
|
+
'@graphql-eslint/no-scalar-result-type-on-mutation': 'error',
|
17
|
+
'@graphql-eslint/require-deprecation-date': 'error',
|
18
|
+
'@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
|
19
|
+
'@graphql-eslint/require-nullable-fields-with-oneof': 'error',
|
20
|
+
'@graphql-eslint/require-type-pattern-with-oneof': 'error',
|
21
|
+
},
|
22
|
+
};
|
@@ -0,0 +1,49 @@
|
|
1
|
+
/*
|
2
|
+
* 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
|
3
|
+
*/
|
4
|
+
export default {
|
5
|
+
extends: './configs/base',
|
6
|
+
rules: {
|
7
|
+
'@graphql-eslint/description-style': 'error',
|
8
|
+
'@graphql-eslint/known-argument-names': 'error',
|
9
|
+
'@graphql-eslint/known-directives': 'error',
|
10
|
+
'@graphql-eslint/known-type-names': 'error',
|
11
|
+
'@graphql-eslint/lone-schema-definition': 'error',
|
12
|
+
'@graphql-eslint/naming-convention': [
|
13
|
+
'error',
|
14
|
+
{
|
15
|
+
types: 'PascalCase',
|
16
|
+
FieldDefinition: 'camelCase',
|
17
|
+
InputValueDefinition: 'camelCase',
|
18
|
+
Argument: 'camelCase',
|
19
|
+
DirectiveDefinition: 'camelCase',
|
20
|
+
EnumValueDefinition: 'UPPER_CASE',
|
21
|
+
'FieldDefinition[parent.name.value=Query]': {
|
22
|
+
forbiddenPrefixes: ['query', 'get'],
|
23
|
+
forbiddenSuffixes: ['Query'],
|
24
|
+
},
|
25
|
+
'FieldDefinition[parent.name.value=Mutation]': {
|
26
|
+
forbiddenPrefixes: ['mutation'],
|
27
|
+
forbiddenSuffixes: ['Mutation'],
|
28
|
+
},
|
29
|
+
'FieldDefinition[parent.name.value=Subscription]': {
|
30
|
+
forbiddenPrefixes: ['subscription'],
|
31
|
+
forbiddenSuffixes: ['Subscription'],
|
32
|
+
},
|
33
|
+
},
|
34
|
+
],
|
35
|
+
'@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
|
36
|
+
'@graphql-eslint/no-hashtag-description': 'error',
|
37
|
+
'@graphql-eslint/no-typename-prefix': 'error',
|
38
|
+
'@graphql-eslint/no-unreachable-types': 'error',
|
39
|
+
'@graphql-eslint/provided-required-arguments': 'error',
|
40
|
+
'@graphql-eslint/require-deprecation-reason': 'error',
|
41
|
+
'@graphql-eslint/require-description': ['error', { types: true, DirectiveDefinition: true }],
|
42
|
+
'@graphql-eslint/strict-id-in-types': 'error',
|
43
|
+
'@graphql-eslint/unique-directive-names': 'error',
|
44
|
+
'@graphql-eslint/unique-directive-names-per-location': 'error',
|
45
|
+
'@graphql-eslint/unique-field-definition-names': 'error',
|
46
|
+
'@graphql-eslint/unique-operation-types': 'error',
|
47
|
+
'@graphql-eslint/unique-type-names': 'error',
|
48
|
+
},
|
49
|
+
};
|
package/esm/documents.js
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
import { resolve } from 'path';
|
2
|
+
import { Kind, visit, } from 'graphql';
|
3
|
+
import debugFactory from 'debug';
|
4
|
+
import fg from 'fast-glob';
|
5
|
+
import { logger } from './utils.js';
|
6
|
+
import { ModuleCache } from './cache.js';
|
7
|
+
const debug = debugFactory('graphql-eslint:operations');
|
8
|
+
const handleVirtualPath = (documents) => {
|
9
|
+
const filepathMap = Object.create(null);
|
10
|
+
return documents.map(source => {
|
11
|
+
var _a;
|
12
|
+
const { location } = source;
|
13
|
+
if (['.gql', '.graphql'].some(extension => location.endsWith(extension))) {
|
14
|
+
return source;
|
15
|
+
}
|
16
|
+
(_a = filepathMap[location]) !== null && _a !== void 0 ? _a : (filepathMap[location] = -1);
|
17
|
+
const index = (filepathMap[location] += 1);
|
18
|
+
return {
|
19
|
+
...source,
|
20
|
+
location: resolve(location, `${index}_document.graphql`),
|
21
|
+
};
|
22
|
+
});
|
23
|
+
};
|
24
|
+
const operationsCache = new ModuleCache();
|
25
|
+
const siblingOperationsCache = new Map();
|
26
|
+
const getSiblings = (project) => {
|
27
|
+
const documentsKey = project.documents;
|
28
|
+
if (!documentsKey) {
|
29
|
+
return [];
|
30
|
+
}
|
31
|
+
let siblings = operationsCache.get(documentsKey);
|
32
|
+
if (!siblings) {
|
33
|
+
debug('Loading operations from %o', project.documents);
|
34
|
+
const documents = project.loadDocumentsSync(project.documents, {
|
35
|
+
skipGraphQLImport: true,
|
36
|
+
pluckConfig: project.extensions.pluckConfig,
|
37
|
+
});
|
38
|
+
if (debug.enabled) {
|
39
|
+
debug('Loaded %d operations', documents.length);
|
40
|
+
const operationsPaths = fg.sync(project.documents, { absolute: true });
|
41
|
+
debug('Operations pointers %O', operationsPaths);
|
42
|
+
}
|
43
|
+
siblings = handleVirtualPath(documents);
|
44
|
+
operationsCache.set(documentsKey, siblings);
|
45
|
+
}
|
46
|
+
return siblings;
|
47
|
+
};
|
48
|
+
export function getDocuments(project) {
|
49
|
+
const siblings = getSiblings(project);
|
50
|
+
if (siblings.length === 0) {
|
51
|
+
let printed = false;
|
52
|
+
const noopWarn = () => {
|
53
|
+
if (!printed) {
|
54
|
+
logger.warn('getSiblingOperations was called without any operations. Make sure to set "parserOptions.operations" to make this feature available!');
|
55
|
+
printed = true;
|
56
|
+
}
|
57
|
+
return [];
|
58
|
+
};
|
59
|
+
return {
|
60
|
+
available: false,
|
61
|
+
getFragment: noopWarn,
|
62
|
+
getFragments: noopWarn,
|
63
|
+
getFragmentByType: noopWarn,
|
64
|
+
getFragmentsInUse: noopWarn,
|
65
|
+
getOperation: noopWarn,
|
66
|
+
getOperations: noopWarn,
|
67
|
+
getOperationByType: noopWarn,
|
68
|
+
};
|
69
|
+
}
|
70
|
+
// Since the siblings array is cached, we can use it as cache key.
|
71
|
+
// We should get the same array reference each time we get
|
72
|
+
// to this point for the same graphql project
|
73
|
+
if (siblingOperationsCache.has(siblings)) {
|
74
|
+
return siblingOperationsCache.get(siblings);
|
75
|
+
}
|
76
|
+
let fragmentsCache = null;
|
77
|
+
const getFragments = () => {
|
78
|
+
if (fragmentsCache === null) {
|
79
|
+
const result = [];
|
80
|
+
for (const source of siblings) {
|
81
|
+
for (const definition of source.document.definitions) {
|
82
|
+
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
|
83
|
+
result.push({
|
84
|
+
filePath: source.location,
|
85
|
+
document: definition,
|
86
|
+
});
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
fragmentsCache = result;
|
91
|
+
}
|
92
|
+
return fragmentsCache;
|
93
|
+
};
|
94
|
+
let cachedOperations = null;
|
95
|
+
const getOperations = () => {
|
96
|
+
if (cachedOperations === null) {
|
97
|
+
const result = [];
|
98
|
+
for (const source of siblings) {
|
99
|
+
for (const definition of source.document.definitions) {
|
100
|
+
if (definition.kind === Kind.OPERATION_DEFINITION) {
|
101
|
+
result.push({
|
102
|
+
filePath: source.location,
|
103
|
+
document: definition,
|
104
|
+
});
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
cachedOperations = result;
|
109
|
+
}
|
110
|
+
return cachedOperations;
|
111
|
+
};
|
112
|
+
const getFragment = (name) => getFragments().filter(f => { var _a; return ((_a = f.document.name) === null || _a === void 0 ? void 0 : _a.value) === name; });
|
113
|
+
const collectFragments = (selectable, recursive, collected = new Map()) => {
|
114
|
+
visit(selectable, {
|
115
|
+
FragmentSpread(spread) {
|
116
|
+
const fragmentName = spread.name.value;
|
117
|
+
const [fragment] = getFragment(fragmentName);
|
118
|
+
if (!fragment) {
|
119
|
+
logger.warn(`Unable to locate fragment named "${fragmentName}", please make sure it's loaded using "parserOptions.operations"`);
|
120
|
+
return;
|
121
|
+
}
|
122
|
+
if (!collected.has(fragmentName)) {
|
123
|
+
collected.set(fragmentName, fragment.document);
|
124
|
+
if (recursive) {
|
125
|
+
collectFragments(fragment.document, recursive, collected);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
},
|
129
|
+
});
|
130
|
+
return collected;
|
131
|
+
};
|
132
|
+
const siblingOperations = {
|
133
|
+
available: true,
|
134
|
+
getFragment,
|
135
|
+
getFragments,
|
136
|
+
getFragmentByType: typeName => getFragments().filter(f => { var _a, _b; return ((_b = (_a = f.document.typeCondition) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.value) === typeName; }),
|
137
|
+
getFragmentsInUse: (selectable, recursive = true) => Array.from(collectFragments(selectable, recursive).values()),
|
138
|
+
getOperation: name => getOperations().filter(o => { var _a; return ((_a = o.document.name) === null || _a === void 0 ? void 0 : _a.value) === name; }),
|
139
|
+
getOperations,
|
140
|
+
getOperationByType: type => getOperations().filter(o => o.document.operation === type),
|
141
|
+
};
|
142
|
+
siblingOperationsCache.set(siblings, siblingOperations);
|
143
|
+
return siblingOperations;
|
144
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import { TypeInfo, visit, visitWithTypeInfo, Kind, } from 'graphql';
|
2
|
+
import { convertLocation } from './utils.js';
|
3
|
+
export function convertToESTree(node, schema) {
|
4
|
+
const typeInfo = schema ? new TypeInfo(schema) : null;
|
5
|
+
const visitor = {
|
6
|
+
leave(node, key, parent) {
|
7
|
+
const leadingComments = 'description' in node && node.description
|
8
|
+
? [
|
9
|
+
{
|
10
|
+
type: node.description.block ? 'Block' : 'Line',
|
11
|
+
value: node.description.value,
|
12
|
+
},
|
13
|
+
]
|
14
|
+
: [];
|
15
|
+
const calculatedTypeInfo = typeInfo
|
16
|
+
? {
|
17
|
+
argument: typeInfo.getArgument(),
|
18
|
+
defaultValue: typeInfo.getDefaultValue(),
|
19
|
+
directive: typeInfo.getDirective(),
|
20
|
+
enumValue: typeInfo.getEnumValue(),
|
21
|
+
fieldDef: typeInfo.getFieldDef(),
|
22
|
+
inputType: typeInfo.getInputType(),
|
23
|
+
parentInputType: typeInfo.getParentInputType(),
|
24
|
+
parentType: typeInfo.getParentType(),
|
25
|
+
gqlType: typeInfo.getType(),
|
26
|
+
}
|
27
|
+
: {};
|
28
|
+
const rawNode = () => {
|
29
|
+
if (parent && key !== undefined) {
|
30
|
+
return parent[key];
|
31
|
+
}
|
32
|
+
return node.kind === Kind.DOCUMENT
|
33
|
+
? {
|
34
|
+
...node,
|
35
|
+
definitions: node.definitions.map(definition => definition.rawNode()),
|
36
|
+
}
|
37
|
+
: node;
|
38
|
+
};
|
39
|
+
const commonFields = {
|
40
|
+
...node,
|
41
|
+
type: node.kind,
|
42
|
+
loc: convertLocation(node.loc),
|
43
|
+
range: [node.loc.start, node.loc.end],
|
44
|
+
leadingComments,
|
45
|
+
// Use function to prevent RangeError: Maximum call stack size exceeded
|
46
|
+
typeInfo: () => calculatedTypeInfo,
|
47
|
+
rawNode,
|
48
|
+
};
|
49
|
+
return 'type' in node
|
50
|
+
? {
|
51
|
+
...commonFields,
|
52
|
+
gqlType: node.type,
|
53
|
+
}
|
54
|
+
: commonFields;
|
55
|
+
},
|
56
|
+
};
|
57
|
+
return visit(node, typeInfo ? visitWithTypeInfo(typeInfo, visitor) : visitor);
|
58
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import { createRequire } from 'module';
|
2
|
+
const require = createRequire(import.meta.url);
|
3
|
+
import { TokenKind, isNonNullType, isListType, Source, } from 'graphql';
|
4
|
+
import { valueFromASTUntyped } from 'graphql/utilities/valueFromASTUntyped.js';
|
5
|
+
export const valueFromNode = (...args) => {
|
6
|
+
return valueFromASTUntyped(...args);
|
7
|
+
};
|
8
|
+
export function getBaseType(type) {
|
9
|
+
if (isNonNullType(type) || isListType(type)) {
|
10
|
+
return getBaseType(type.ofType);
|
11
|
+
}
|
12
|
+
return type;
|
13
|
+
}
|
14
|
+
export function convertToken(token, type) {
|
15
|
+
const { line, column, end, start, value } = token;
|
16
|
+
return {
|
17
|
+
type,
|
18
|
+
value,
|
19
|
+
/*
|
20
|
+
* ESLint has 0-based column number
|
21
|
+
* https://eslint.org/docs/developer-guide/working-with-rules#contextreport
|
22
|
+
*/
|
23
|
+
loc: {
|
24
|
+
start: {
|
25
|
+
line,
|
26
|
+
column: column - 1,
|
27
|
+
},
|
28
|
+
end: {
|
29
|
+
line,
|
30
|
+
column: column - 1 + (end - start),
|
31
|
+
},
|
32
|
+
},
|
33
|
+
range: [start, end],
|
34
|
+
};
|
35
|
+
}
|
36
|
+
function getLexer(source) {
|
37
|
+
// GraphQL v14
|
38
|
+
const gqlLanguage = require('graphql/language');
|
39
|
+
if (gqlLanguage && gqlLanguage.createLexer) {
|
40
|
+
return gqlLanguage.createLexer(source, {});
|
41
|
+
}
|
42
|
+
// GraphQL v15
|
43
|
+
const { Lexer: LexerCls } = require('graphql');
|
44
|
+
if (LexerCls && typeof LexerCls === 'function') {
|
45
|
+
return new LexerCls(source);
|
46
|
+
}
|
47
|
+
throw new Error('Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!');
|
48
|
+
}
|
49
|
+
export function extractTokens(filePath, code) {
|
50
|
+
const source = new Source(code, filePath);
|
51
|
+
const lexer = getLexer(source);
|
52
|
+
const tokens = [];
|
53
|
+
let token = lexer.advance();
|
54
|
+
while (token && token.kind !== TokenKind.EOF) {
|
55
|
+
const result = convertToken(token, token.kind);
|
56
|
+
tokens.push(result);
|
57
|
+
token = lexer.advance();
|
58
|
+
}
|
59
|
+
return tokens;
|
60
|
+
}
|
61
|
+
export function extractComments(loc) {
|
62
|
+
if (!loc) {
|
63
|
+
return [];
|
64
|
+
}
|
65
|
+
const comments = [];
|
66
|
+
let token = loc.startToken;
|
67
|
+
while (token) {
|
68
|
+
if (token.kind === TokenKind.COMMENT) {
|
69
|
+
const comment = convertToken(token,
|
70
|
+
// `eslint-disable` directive works only with `Block` type comment
|
71
|
+
token.value.trimStart().startsWith('eslint') ? 'Block' : 'Line');
|
72
|
+
comments.push(comment);
|
73
|
+
}
|
74
|
+
token = token.next;
|
75
|
+
}
|
76
|
+
return comments;
|
77
|
+
}
|
78
|
+
export function convertLocation(location) {
|
79
|
+
const { startToken, endToken, source, start, end } = location;
|
80
|
+
/*
|
81
|
+
* ESLint has 0-based column number
|
82
|
+
* https://eslint.org/docs/developer-guide/working-with-rules#contextreport
|
83
|
+
*/
|
84
|
+
const loc = {
|
85
|
+
start: {
|
86
|
+
/*
|
87
|
+
* Kind.Document has startToken: { line: 0, column: 0 }, we set line as 1 and column as 0
|
88
|
+
*/
|
89
|
+
line: startToken.line === 0 ? 1 : startToken.line,
|
90
|
+
column: startToken.column === 0 ? 0 : startToken.column - 1,
|
91
|
+
},
|
92
|
+
end: {
|
93
|
+
line: endToken.line,
|
94
|
+
column: endToken.column - 1,
|
95
|
+
},
|
96
|
+
source: source.body,
|
97
|
+
};
|
98
|
+
if (loc.start.column === loc.end.column) {
|
99
|
+
loc.end.column += end - start;
|
100
|
+
}
|
101
|
+
return loc;
|
102
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { parseForESLint } from './parser.js';
|
2
|
+
import { configs } from './configs/index.js';
|
3
|
+
const languageOptions = {
|
4
|
+
parser: { parseForESLint },
|
5
|
+
};
|
6
|
+
export const flatConfigs = {
|
7
|
+
'operations-all': {
|
8
|
+
languageOptions,
|
9
|
+
rules: {
|
10
|
+
...configs['operations-recommended'].rules,
|
11
|
+
...configs['operations-all'].rules,
|
12
|
+
},
|
13
|
+
},
|
14
|
+
'operations-recommended': {
|
15
|
+
languageOptions,
|
16
|
+
rules: configs['operations-recommended'].rules,
|
17
|
+
},
|
18
|
+
relay: {
|
19
|
+
languageOptions,
|
20
|
+
rules: configs.relay.rules,
|
21
|
+
},
|
22
|
+
'schema-all': {
|
23
|
+
languageOptions,
|
24
|
+
rules: {
|
25
|
+
...configs['schema-recommended'].rules,
|
26
|
+
...configs['schema-all'].rules,
|
27
|
+
},
|
28
|
+
},
|
29
|
+
'schema-recommended': {
|
30
|
+
languageOptions,
|
31
|
+
rules: configs['schema-recommended'].rules,
|
32
|
+
},
|
33
|
+
};
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import { dirname } from 'path';
|
2
|
+
import debugFactory from 'debug';
|
3
|
+
import { GraphQLConfig, loadConfigSync } from 'graphql-config';
|
4
|
+
import { CodeFileLoader } from '@graphql-tools/code-file-loader';
|
5
|
+
const debug = debugFactory('graphql-eslint:graphql-config');
|
6
|
+
let graphQLConfig;
|
7
|
+
export function loadOnDiskGraphQLConfig(filePath) {
|
8
|
+
return loadConfigSync({
|
9
|
+
// load config relative to the file being linted
|
10
|
+
rootDir: dirname(filePath),
|
11
|
+
throwOnEmpty: false,
|
12
|
+
throwOnMissing: false,
|
13
|
+
extensions: [codeFileLoaderExtension],
|
14
|
+
});
|
15
|
+
}
|
16
|
+
export function loadGraphQLConfig(options) {
|
17
|
+
// We don't want cache config on test environment
|
18
|
+
// Otherwise schema and documents will be same for all tests
|
19
|
+
if (process.env.NODE_ENV !== 'test' && graphQLConfig) {
|
20
|
+
return graphQLConfig;
|
21
|
+
}
|
22
|
+
const onDiskConfig = !options.skipGraphQLConfig && loadOnDiskGraphQLConfig(options.filePath);
|
23
|
+
debug('options.skipGraphQLConfig: %o', options.skipGraphQLConfig);
|
24
|
+
if (onDiskConfig) {
|
25
|
+
debug('Graphql-config path %o', onDiskConfig.filepath);
|
26
|
+
}
|
27
|
+
const configOptions = options.projects
|
28
|
+
? { projects: options.projects }
|
29
|
+
: {
|
30
|
+
schema: (options.schema || ''),
|
31
|
+
documents: options.documents || options.operations,
|
32
|
+
extensions: options.extensions,
|
33
|
+
include: options.include,
|
34
|
+
exclude: options.exclude,
|
35
|
+
};
|
36
|
+
graphQLConfig =
|
37
|
+
onDiskConfig ||
|
38
|
+
new GraphQLConfig({
|
39
|
+
config: configOptions,
|
40
|
+
filepath: 'virtual-config',
|
41
|
+
}, [codeFileLoaderExtension]);
|
42
|
+
return graphQLConfig;
|
43
|
+
}
|
44
|
+
const codeFileLoaderExtension = api => {
|
45
|
+
const { schema, documents } = api.loaders;
|
46
|
+
schema.register(new CodeFileLoader());
|
47
|
+
documents.register(new CodeFileLoader());
|
48
|
+
return { name: 'graphql-eslint-loaders' };
|
49
|
+
};
|
package/esm/index.js
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
import { processor } from './processor.js';
|
2
|
+
export { rules } from './rules/index.js';
|
3
|
+
export { parseForESLint } from './parser.js';
|
4
|
+
export * from './testkit.js';
|
5
|
+
export * from './types.js';
|
6
|
+
export { requireGraphQLSchemaFromContext, requireSiblingsOperations } from './utils.js';
|
7
|
+
export const processors = { graphql: processor };
|
8
|
+
export { configs } from './configs/index.js';
|
9
|
+
export { flatConfigs } from './flat-configs.js';
|
package/esm/package.json
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{ "type": "module" }
|
package/esm/parser.js
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
import { parseGraphQLSDL } from '@graphql-tools/utils';
|
2
|
+
import { GraphQLError, GraphQLSchema } from 'graphql';
|
3
|
+
import debugFactory from 'debug';
|
4
|
+
import { convertToESTree, extractComments, extractTokens } from './estree-converter/index.js';
|
5
|
+
import { getSchema } from './schema.js';
|
6
|
+
import { getDocuments } from './documents.js';
|
7
|
+
import { loadGraphQLConfig } from './graphql-config.js';
|
8
|
+
import { CWD, VIRTUAL_DOCUMENT_REGEX } from './utils.js';
|
9
|
+
const debug = debugFactory('graphql-eslint:parser');
|
10
|
+
debug('cwd %o', CWD);
|
11
|
+
export function parseForESLint(code, options) {
|
12
|
+
try {
|
13
|
+
const { filePath } = options;
|
14
|
+
// First parse code from file, in case of syntax error do not try load schema,
|
15
|
+
// documents or even graphql-config instance
|
16
|
+
const { document } = parseGraphQLSDL(filePath, code, {
|
17
|
+
...options.graphQLParserOptions,
|
18
|
+
noLocation: false,
|
19
|
+
});
|
20
|
+
const gqlConfig = loadGraphQLConfig(options);
|
21
|
+
const realFilepath = filePath.replace(VIRTUAL_DOCUMENT_REGEX, '');
|
22
|
+
const project = gqlConfig.getProjectForFile(realFilepath);
|
23
|
+
const schema = getSchema(project, options.schemaOptions);
|
24
|
+
const rootTree = convertToESTree(document, schema instanceof GraphQLSchema ? schema : null);
|
25
|
+
return {
|
26
|
+
services: {
|
27
|
+
schema,
|
28
|
+
siblingOperations: getDocuments(project),
|
29
|
+
},
|
30
|
+
ast: {
|
31
|
+
comments: extractComments(document.loc),
|
32
|
+
tokens: extractTokens(filePath, code),
|
33
|
+
loc: rootTree.loc,
|
34
|
+
range: rootTree.range,
|
35
|
+
type: 'Program',
|
36
|
+
sourceType: 'script',
|
37
|
+
body: [rootTree],
|
38
|
+
},
|
39
|
+
};
|
40
|
+
}
|
41
|
+
catch (error) {
|
42
|
+
error.message = `[graphql-eslint] ${error.message}`;
|
43
|
+
// In case of GraphQL parser error, we report it to ESLint as a parser error that matches the requirements
|
44
|
+
// of ESLint. This will make sure to display it correctly in IDEs and lint results.
|
45
|
+
if (error instanceof GraphQLError) {
|
46
|
+
const eslintError = {
|
47
|
+
index: error.positions[0],
|
48
|
+
lineNumber: error.locations[0].line,
|
49
|
+
column: error.locations[0].column - 1,
|
50
|
+
message: error.message,
|
51
|
+
};
|
52
|
+
throw eslintError;
|
53
|
+
}
|
54
|
+
throw error;
|
55
|
+
}
|
56
|
+
}
|