@graphql-eslint/eslint-plugin 2.3.1 → 2.3.2-alpha-ace9d56.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/custom-rules.md +1 -1
- package/index.js +115 -31
- package/index.mjs +115 -31
- package/package.json +1 -1
- package/testkit.d.ts +5 -3
- package/utils.d.ts +4 -0
package/docs/custom-rules.md
CHANGED
@@ -58,7 +58,7 @@ This is useful if you wish to use other GraphQL tools that works with the origin
|
|
58
58
|
Here's an example for using original `graphql-js` validate method to validate `OperationDefinition`:
|
59
59
|
|
60
60
|
```ts
|
61
|
-
import { validate } from 'graphql
|
61
|
+
import { validate } from 'graphql';
|
62
62
|
import { requireGraphQLSchemaFromContext } from '@graphql-eslint/eslint-plugin';
|
63
63
|
|
64
64
|
export const rule = {
|
package/index.js
CHANGED
@@ -15,6 +15,8 @@ const depthLimit = _interopDefault(require('graphql-depth-limit'));
|
|
15
15
|
const graphqlTagPluck = require('@graphql-tools/graphql-tag-pluck');
|
16
16
|
const graphqlConfig$1 = require('graphql-config');
|
17
17
|
const codeFileLoader = require('@graphql-tools/code-file-loader');
|
18
|
+
const eslint = require('eslint');
|
19
|
+
const codeFrame = require('@babel/code-frame');
|
18
20
|
|
19
21
|
/*
|
20
22
|
* 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
|
@@ -242,6 +244,24 @@ const convertCase = (style, str) => {
|
|
242
244
|
return lowerCase(str).replace(/ /g, '-');
|
243
245
|
}
|
244
246
|
};
|
247
|
+
function getLocation(loc, fieldName = '', offset) {
|
248
|
+
const { start } = loc;
|
249
|
+
/*
|
250
|
+
* ESLint has 0-based column number
|
251
|
+
* https://eslint.org/docs/developer-guide/working-with-rules#contextreport
|
252
|
+
*/
|
253
|
+
const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
|
254
|
+
return {
|
255
|
+
start: {
|
256
|
+
line: start.line,
|
257
|
+
column: start.column - offsetStart,
|
258
|
+
},
|
259
|
+
end: {
|
260
|
+
line: start.line,
|
261
|
+
column: start.column - offsetEnd + fieldName.length,
|
262
|
+
},
|
263
|
+
};
|
264
|
+
}
|
245
265
|
|
246
266
|
function extractRuleName(stack) {
|
247
267
|
const match = (stack || '').match(/validation[/\\\\]rules[/\\\\](.*?)\.js:/) || [];
|
@@ -968,16 +988,20 @@ const rule$3 = {
|
|
968
988
|
if (!mutationType) {
|
969
989
|
return {};
|
970
990
|
}
|
971
|
-
const selector =
|
991
|
+
const selector = [
|
992
|
+
`:matches(${graphql.Kind.OBJECT_TYPE_DEFINITION}, ${graphql.Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
|
993
|
+
'>',
|
994
|
+
graphql.Kind.FIELD_DEFINITION,
|
995
|
+
graphql.Kind.NAMED_TYPE,
|
996
|
+
].join(' ');
|
972
997
|
return {
|
973
998
|
[selector](node) {
|
974
|
-
const
|
975
|
-
const typeName = getTypeName(rawNode);
|
999
|
+
const typeName = node.name.value;
|
976
1000
|
const graphQLType = schema.getType(typeName);
|
977
1001
|
if (graphql.isScalarType(graphQLType)) {
|
978
1002
|
context.report({
|
979
|
-
node,
|
980
|
-
message: `Unexpected scalar result type "${typeName}"
|
1003
|
+
loc: getLocation(node.loc, typeName),
|
1004
|
+
message: `Unexpected scalar result type "${typeName}"`,
|
981
1005
|
});
|
982
1006
|
}
|
983
1007
|
},
|
@@ -1370,7 +1394,8 @@ const rule$7 = {
|
|
1370
1394
|
var _a;
|
1371
1395
|
if (options.fileExtension && options.fileExtension !== fileExtension) {
|
1372
1396
|
context.report({
|
1373
|
-
|
1397
|
+
// Report on first character
|
1398
|
+
loc: { column: 0, line: 1 },
|
1374
1399
|
messageId: MATCH_EXTENSION,
|
1375
1400
|
data: {
|
1376
1401
|
fileExtension,
|
@@ -1402,7 +1427,8 @@ const rule$7 = {
|
|
1402
1427
|
const filenameWithExtension = filename + expectedExtension;
|
1403
1428
|
if (expectedFilename !== filenameWithExtension) {
|
1404
1429
|
context.report({
|
1405
|
-
|
1430
|
+
// Report on first character
|
1431
|
+
loc: { column: 0, line: 1 },
|
1406
1432
|
messageId: MATCH_STYLE,
|
1407
1433
|
data: {
|
1408
1434
|
expectedFilename,
|
@@ -1872,8 +1898,8 @@ const rule$b = {
|
|
1872
1898
|
mutation {
|
1873
1899
|
changeSomething(
|
1874
1900
|
type: OLD # This is deprecated, so you'll get an error
|
1875
|
-
) {
|
1876
|
-
...
|
1901
|
+
) {
|
1902
|
+
...
|
1877
1903
|
}
|
1878
1904
|
}
|
1879
1905
|
`,
|
@@ -1911,8 +1937,9 @@ const rule$b = {
|
|
1911
1937
|
const typeInfo = node.typeInfo();
|
1912
1938
|
if (typeInfo && typeInfo.enumValue) {
|
1913
1939
|
if (typeInfo.enumValue.isDeprecated) {
|
1940
|
+
const enumValueName = node.value;
|
1914
1941
|
context.report({
|
1915
|
-
loc: node.loc,
|
1942
|
+
loc: getLocation(node.loc, enumValueName),
|
1916
1943
|
messageId: NO_DEPRECATED,
|
1917
1944
|
data: {
|
1918
1945
|
type: 'enum value',
|
@@ -1927,8 +1954,9 @@ const rule$b = {
|
|
1927
1954
|
const typeInfo = node.typeInfo();
|
1928
1955
|
if (typeInfo && typeInfo.fieldDef) {
|
1929
1956
|
if (typeInfo.fieldDef.isDeprecated) {
|
1957
|
+
const fieldName = node.name.value;
|
1930
1958
|
context.report({
|
1931
|
-
loc: node.loc,
|
1959
|
+
loc: getLocation(node.loc, fieldName),
|
1932
1960
|
messageId: NO_DEPRECATED,
|
1933
1961
|
data: {
|
1934
1962
|
type: 'field',
|
@@ -3185,11 +3213,7 @@ const rule$m = {
|
|
3185
3213
|
const RULE_NAME$3 = 'unique-fragment-name';
|
3186
3214
|
const UNIQUE_FRAGMENT_NAME = 'UNIQUE_FRAGMENT_NAME';
|
3187
3215
|
const checkNode = (context, node, ruleName, messageId) => {
|
3188
|
-
|
3189
|
-
const documentName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.value;
|
3190
|
-
if (!documentName) {
|
3191
|
-
return;
|
3192
|
-
}
|
3216
|
+
const documentName = node.name.value;
|
3193
3217
|
const siblings = requireSiblingsOperations(ruleName, context);
|
3194
3218
|
const siblingDocuments = node.kind === graphql.Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
|
3195
3219
|
const filepath = context.getFilename();
|
@@ -3200,7 +3224,6 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3200
3224
|
return isSameName && !isSamePath;
|
3201
3225
|
});
|
3202
3226
|
if (conflictingDocuments.length > 0) {
|
3203
|
-
const { start, end } = node.name.loc;
|
3204
3227
|
context.report({
|
3205
3228
|
messageId,
|
3206
3229
|
data: {
|
@@ -3209,16 +3232,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3209
3232
|
.map(f => `\t${path.relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
3210
3233
|
.join('\n'),
|
3211
3234
|
},
|
3212
|
-
loc:
|
3213
|
-
start: {
|
3214
|
-
line: start.line,
|
3215
|
-
column: start.column - 1,
|
3216
|
-
},
|
3217
|
-
end: {
|
3218
|
-
line: end.line,
|
3219
|
-
column: end.column - 1,
|
3220
|
-
},
|
3221
|
-
},
|
3235
|
+
loc: getLocation(node.name.loc, documentName),
|
3222
3236
|
});
|
3223
3237
|
}
|
3224
3238
|
};
|
@@ -3335,7 +3349,7 @@ const rule$o = {
|
|
3335
3349
|
},
|
3336
3350
|
create(context) {
|
3337
3351
|
return {
|
3338
|
-
OperationDefinition(node) {
|
3352
|
+
'OperationDefinition[name!=undefined]'(node) {
|
3339
3353
|
checkNode(context, node, RULE_NAME$4, UNIQUE_OPERATION_NAME);
|
3340
3354
|
},
|
3341
3355
|
};
|
@@ -3765,23 +3779,93 @@ function parseForESLint(code, options = {}) {
|
|
3765
3779
|
}
|
3766
3780
|
}
|
3767
3781
|
|
3768
|
-
class GraphQLRuleTester extends
|
3782
|
+
class GraphQLRuleTester extends eslint.RuleTester {
|
3769
3783
|
constructor(parserOptions = {}) {
|
3770
|
-
|
3784
|
+
const config = {
|
3771
3785
|
parser: require.resolve('@graphql-eslint/eslint-plugin'),
|
3772
3786
|
parserOptions: {
|
3773
3787
|
...parserOptions,
|
3774
3788
|
skipGraphQLConfig: true,
|
3775
3789
|
},
|
3776
|
-
}
|
3790
|
+
};
|
3791
|
+
super(config);
|
3792
|
+
this.config = config;
|
3777
3793
|
}
|
3778
3794
|
fromMockFile(path$1) {
|
3779
3795
|
return fs.readFileSync(path.resolve(__dirname, `../tests/mocks/${path$1}`), 'utf-8');
|
3780
3796
|
}
|
3781
3797
|
runGraphQLTests(name, rule, tests) {
|
3782
3798
|
super.run(name, rule, tests);
|
3799
|
+
// Skip snapshot testing if `expect` variable is not defined
|
3800
|
+
if (typeof expect === 'undefined') {
|
3801
|
+
return;
|
3802
|
+
}
|
3803
|
+
const linter = new eslint.Linter();
|
3804
|
+
linter.defineRule(name, rule);
|
3805
|
+
for (const testCase of tests.invalid) {
|
3806
|
+
const verifyConfig = getVerifyConfig(name, this.config, testCase);
|
3807
|
+
defineParser(linter, verifyConfig.parser);
|
3808
|
+
const { code, filename } = testCase;
|
3809
|
+
const messages = linter.verify(code, verifyConfig, { filename });
|
3810
|
+
for (const message of messages) {
|
3811
|
+
if (message.fatal) {
|
3812
|
+
throw new Error(message.message);
|
3813
|
+
}
|
3814
|
+
const messageForSnapshot = visualizeEslintMessage(code, message);
|
3815
|
+
// eslint-disable-next-line no-undef
|
3816
|
+
expect(messageForSnapshot).toMatchSnapshot();
|
3817
|
+
}
|
3818
|
+
}
|
3783
3819
|
}
|
3784
3820
|
}
|
3821
|
+
function getVerifyConfig(ruleId, testerConfig, testCase) {
|
3822
|
+
const { options, parserOptions, parser = testerConfig.parser } = testCase;
|
3823
|
+
return {
|
3824
|
+
...testerConfig,
|
3825
|
+
parser,
|
3826
|
+
parserOptions: {
|
3827
|
+
...testerConfig.parserOptions,
|
3828
|
+
...parserOptions,
|
3829
|
+
},
|
3830
|
+
rules: {
|
3831
|
+
[ruleId]: ['error', ...(Array.isArray(options) ? options : [])],
|
3832
|
+
},
|
3833
|
+
};
|
3834
|
+
}
|
3835
|
+
const parsers = new WeakMap();
|
3836
|
+
function defineParser(linter, parser) {
|
3837
|
+
if (!parser) {
|
3838
|
+
return;
|
3839
|
+
}
|
3840
|
+
if (!parsers.has(linter)) {
|
3841
|
+
parsers.set(linter, new Set());
|
3842
|
+
}
|
3843
|
+
const defined = parsers.get(linter);
|
3844
|
+
if (!defined.has(parser)) {
|
3845
|
+
defined.add(parser);
|
3846
|
+
linter.defineParser(parser, require(parser));
|
3847
|
+
}
|
3848
|
+
}
|
3849
|
+
function visualizeEslintMessage(text, result) {
|
3850
|
+
const { line, column, endLine, endColumn, message } = result;
|
3851
|
+
const location = {
|
3852
|
+
start: {
|
3853
|
+
line,
|
3854
|
+
column,
|
3855
|
+
},
|
3856
|
+
};
|
3857
|
+
if (typeof endLine === 'number' && typeof endColumn === 'number') {
|
3858
|
+
location.end = {
|
3859
|
+
line: endLine,
|
3860
|
+
column: endColumn,
|
3861
|
+
};
|
3862
|
+
}
|
3863
|
+
return codeFrame.codeFrameColumns(text, location, {
|
3864
|
+
linesAbove: Number.POSITIVE_INFINITY,
|
3865
|
+
linesBelow: Number.POSITIVE_INFINITY,
|
3866
|
+
message,
|
3867
|
+
});
|
3868
|
+
}
|
3785
3869
|
|
3786
3870
|
exports.GraphQLRuleTester = GraphQLRuleTester;
|
3787
3871
|
exports.configs = configs;
|
package/index.mjs
CHANGED
@@ -9,6 +9,8 @@ import depthLimit from 'graphql-depth-limit';
|
|
9
9
|
import { parseCode } from '@graphql-tools/graphql-tag-pluck';
|
10
10
|
import { loadConfigSync, GraphQLConfig } from 'graphql-config';
|
11
11
|
import { CodeFileLoader } from '@graphql-tools/code-file-loader';
|
12
|
+
import { RuleTester, Linter } from 'eslint';
|
13
|
+
import { codeFrameColumns } from '@babel/code-frame';
|
12
14
|
|
13
15
|
/*
|
14
16
|
* 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
|
@@ -236,6 +238,24 @@ const convertCase = (style, str) => {
|
|
236
238
|
return lowerCase(str).replace(/ /g, '-');
|
237
239
|
}
|
238
240
|
};
|
241
|
+
function getLocation(loc, fieldName = '', offset) {
|
242
|
+
const { start } = loc;
|
243
|
+
/*
|
244
|
+
* ESLint has 0-based column number
|
245
|
+
* https://eslint.org/docs/developer-guide/working-with-rules#contextreport
|
246
|
+
*/
|
247
|
+
const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
|
248
|
+
return {
|
249
|
+
start: {
|
250
|
+
line: start.line,
|
251
|
+
column: start.column - offsetStart,
|
252
|
+
},
|
253
|
+
end: {
|
254
|
+
line: start.line,
|
255
|
+
column: start.column - offsetEnd + fieldName.length,
|
256
|
+
},
|
257
|
+
};
|
258
|
+
}
|
239
259
|
|
240
260
|
function extractRuleName(stack) {
|
241
261
|
const match = (stack || '').match(/validation[/\\\\]rules[/\\\\](.*?)\.js:/) || [];
|
@@ -962,16 +982,20 @@ const rule$3 = {
|
|
962
982
|
if (!mutationType) {
|
963
983
|
return {};
|
964
984
|
}
|
965
|
-
const selector =
|
985
|
+
const selector = [
|
986
|
+
`:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
|
987
|
+
'>',
|
988
|
+
Kind.FIELD_DEFINITION,
|
989
|
+
Kind.NAMED_TYPE,
|
990
|
+
].join(' ');
|
966
991
|
return {
|
967
992
|
[selector](node) {
|
968
|
-
const
|
969
|
-
const typeName = getTypeName(rawNode);
|
993
|
+
const typeName = node.name.value;
|
970
994
|
const graphQLType = schema.getType(typeName);
|
971
995
|
if (isScalarType(graphQLType)) {
|
972
996
|
context.report({
|
973
|
-
node,
|
974
|
-
message: `Unexpected scalar result type "${typeName}"
|
997
|
+
loc: getLocation(node.loc, typeName),
|
998
|
+
message: `Unexpected scalar result type "${typeName}"`,
|
975
999
|
});
|
976
1000
|
}
|
977
1001
|
},
|
@@ -1364,7 +1388,8 @@ const rule$7 = {
|
|
1364
1388
|
var _a;
|
1365
1389
|
if (options.fileExtension && options.fileExtension !== fileExtension) {
|
1366
1390
|
context.report({
|
1367
|
-
|
1391
|
+
// Report on first character
|
1392
|
+
loc: { column: 0, line: 1 },
|
1368
1393
|
messageId: MATCH_EXTENSION,
|
1369
1394
|
data: {
|
1370
1395
|
fileExtension,
|
@@ -1396,7 +1421,8 @@ const rule$7 = {
|
|
1396
1421
|
const filenameWithExtension = filename + expectedExtension;
|
1397
1422
|
if (expectedFilename !== filenameWithExtension) {
|
1398
1423
|
context.report({
|
1399
|
-
|
1424
|
+
// Report on first character
|
1425
|
+
loc: { column: 0, line: 1 },
|
1400
1426
|
messageId: MATCH_STYLE,
|
1401
1427
|
data: {
|
1402
1428
|
expectedFilename,
|
@@ -1866,8 +1892,8 @@ const rule$b = {
|
|
1866
1892
|
mutation {
|
1867
1893
|
changeSomething(
|
1868
1894
|
type: OLD # This is deprecated, so you'll get an error
|
1869
|
-
) {
|
1870
|
-
...
|
1895
|
+
) {
|
1896
|
+
...
|
1871
1897
|
}
|
1872
1898
|
}
|
1873
1899
|
`,
|
@@ -1905,8 +1931,9 @@ const rule$b = {
|
|
1905
1931
|
const typeInfo = node.typeInfo();
|
1906
1932
|
if (typeInfo && typeInfo.enumValue) {
|
1907
1933
|
if (typeInfo.enumValue.isDeprecated) {
|
1934
|
+
const enumValueName = node.value;
|
1908
1935
|
context.report({
|
1909
|
-
loc: node.loc,
|
1936
|
+
loc: getLocation(node.loc, enumValueName),
|
1910
1937
|
messageId: NO_DEPRECATED,
|
1911
1938
|
data: {
|
1912
1939
|
type: 'enum value',
|
@@ -1921,8 +1948,9 @@ const rule$b = {
|
|
1921
1948
|
const typeInfo = node.typeInfo();
|
1922
1949
|
if (typeInfo && typeInfo.fieldDef) {
|
1923
1950
|
if (typeInfo.fieldDef.isDeprecated) {
|
1951
|
+
const fieldName = node.name.value;
|
1924
1952
|
context.report({
|
1925
|
-
loc: node.loc,
|
1953
|
+
loc: getLocation(node.loc, fieldName),
|
1926
1954
|
messageId: NO_DEPRECATED,
|
1927
1955
|
data: {
|
1928
1956
|
type: 'field',
|
@@ -3179,11 +3207,7 @@ const rule$m = {
|
|
3179
3207
|
const RULE_NAME$3 = 'unique-fragment-name';
|
3180
3208
|
const UNIQUE_FRAGMENT_NAME = 'UNIQUE_FRAGMENT_NAME';
|
3181
3209
|
const checkNode = (context, node, ruleName, messageId) => {
|
3182
|
-
|
3183
|
-
const documentName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.value;
|
3184
|
-
if (!documentName) {
|
3185
|
-
return;
|
3186
|
-
}
|
3210
|
+
const documentName = node.name.value;
|
3187
3211
|
const siblings = requireSiblingsOperations(ruleName, context);
|
3188
3212
|
const siblingDocuments = node.kind === Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
|
3189
3213
|
const filepath = context.getFilename();
|
@@ -3194,7 +3218,6 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3194
3218
|
return isSameName && !isSamePath;
|
3195
3219
|
});
|
3196
3220
|
if (conflictingDocuments.length > 0) {
|
3197
|
-
const { start, end } = node.name.loc;
|
3198
3221
|
context.report({
|
3199
3222
|
messageId,
|
3200
3223
|
data: {
|
@@ -3203,16 +3226,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3203
3226
|
.map(f => `\t${relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
3204
3227
|
.join('\n'),
|
3205
3228
|
},
|
3206
|
-
loc:
|
3207
|
-
start: {
|
3208
|
-
line: start.line,
|
3209
|
-
column: start.column - 1,
|
3210
|
-
},
|
3211
|
-
end: {
|
3212
|
-
line: end.line,
|
3213
|
-
column: end.column - 1,
|
3214
|
-
},
|
3215
|
-
},
|
3229
|
+
loc: getLocation(node.name.loc, documentName),
|
3216
3230
|
});
|
3217
3231
|
}
|
3218
3232
|
};
|
@@ -3329,7 +3343,7 @@ const rule$o = {
|
|
3329
3343
|
},
|
3330
3344
|
create(context) {
|
3331
3345
|
return {
|
3332
|
-
OperationDefinition(node) {
|
3346
|
+
'OperationDefinition[name!=undefined]'(node) {
|
3333
3347
|
checkNode(context, node, RULE_NAME$4, UNIQUE_OPERATION_NAME);
|
3334
3348
|
},
|
3335
3349
|
};
|
@@ -3759,22 +3773,92 @@ function parseForESLint(code, options = {}) {
|
|
3759
3773
|
}
|
3760
3774
|
}
|
3761
3775
|
|
3762
|
-
class GraphQLRuleTester extends
|
3776
|
+
class GraphQLRuleTester extends RuleTester {
|
3763
3777
|
constructor(parserOptions = {}) {
|
3764
|
-
|
3778
|
+
const config = {
|
3765
3779
|
parser: require.resolve('@graphql-eslint/eslint-plugin'),
|
3766
3780
|
parserOptions: {
|
3767
3781
|
...parserOptions,
|
3768
3782
|
skipGraphQLConfig: true,
|
3769
3783
|
},
|
3770
|
-
}
|
3784
|
+
};
|
3785
|
+
super(config);
|
3786
|
+
this.config = config;
|
3771
3787
|
}
|
3772
3788
|
fromMockFile(path) {
|
3773
3789
|
return readFileSync(resolve(__dirname, `../tests/mocks/${path}`), 'utf-8');
|
3774
3790
|
}
|
3775
3791
|
runGraphQLTests(name, rule, tests) {
|
3776
3792
|
super.run(name, rule, tests);
|
3793
|
+
// Skip snapshot testing if `expect` variable is not defined
|
3794
|
+
if (typeof expect === 'undefined') {
|
3795
|
+
return;
|
3796
|
+
}
|
3797
|
+
const linter = new Linter();
|
3798
|
+
linter.defineRule(name, rule);
|
3799
|
+
for (const testCase of tests.invalid) {
|
3800
|
+
const verifyConfig = getVerifyConfig(name, this.config, testCase);
|
3801
|
+
defineParser(linter, verifyConfig.parser);
|
3802
|
+
const { code, filename } = testCase;
|
3803
|
+
const messages = linter.verify(code, verifyConfig, { filename });
|
3804
|
+
for (const message of messages) {
|
3805
|
+
if (message.fatal) {
|
3806
|
+
throw new Error(message.message);
|
3807
|
+
}
|
3808
|
+
const messageForSnapshot = visualizeEslintMessage(code, message);
|
3809
|
+
// eslint-disable-next-line no-undef
|
3810
|
+
expect(messageForSnapshot).toMatchSnapshot();
|
3811
|
+
}
|
3812
|
+
}
|
3777
3813
|
}
|
3778
3814
|
}
|
3815
|
+
function getVerifyConfig(ruleId, testerConfig, testCase) {
|
3816
|
+
const { options, parserOptions, parser = testerConfig.parser } = testCase;
|
3817
|
+
return {
|
3818
|
+
...testerConfig,
|
3819
|
+
parser,
|
3820
|
+
parserOptions: {
|
3821
|
+
...testerConfig.parserOptions,
|
3822
|
+
...parserOptions,
|
3823
|
+
},
|
3824
|
+
rules: {
|
3825
|
+
[ruleId]: ['error', ...(Array.isArray(options) ? options : [])],
|
3826
|
+
},
|
3827
|
+
};
|
3828
|
+
}
|
3829
|
+
const parsers = new WeakMap();
|
3830
|
+
function defineParser(linter, parser) {
|
3831
|
+
if (!parser) {
|
3832
|
+
return;
|
3833
|
+
}
|
3834
|
+
if (!parsers.has(linter)) {
|
3835
|
+
parsers.set(linter, new Set());
|
3836
|
+
}
|
3837
|
+
const defined = parsers.get(linter);
|
3838
|
+
if (!defined.has(parser)) {
|
3839
|
+
defined.add(parser);
|
3840
|
+
linter.defineParser(parser, require(parser));
|
3841
|
+
}
|
3842
|
+
}
|
3843
|
+
function visualizeEslintMessage(text, result) {
|
3844
|
+
const { line, column, endLine, endColumn, message } = result;
|
3845
|
+
const location = {
|
3846
|
+
start: {
|
3847
|
+
line,
|
3848
|
+
column,
|
3849
|
+
},
|
3850
|
+
};
|
3851
|
+
if (typeof endLine === 'number' && typeof endColumn === 'number') {
|
3852
|
+
location.end = {
|
3853
|
+
line: endLine,
|
3854
|
+
column: endColumn,
|
3855
|
+
};
|
3856
|
+
}
|
3857
|
+
return codeFrameColumns(text, location, {
|
3858
|
+
linesAbove: Number.POSITIVE_INFINITY,
|
3859
|
+
linesBelow: Number.POSITIVE_INFINITY,
|
3860
|
+
message,
|
3861
|
+
});
|
3862
|
+
}
|
3779
3863
|
|
3780
3864
|
export { GraphQLRuleTester, configs, convertDescription, convertLocation, convertRange, convertToESTree, extractCommentsFromAst, getBaseType, isNodeWithDescription, parse, parseForESLint, processors, rules, valueFromNode };
|
package/package.json
CHANGED
package/testkit.d.ts
CHANGED
@@ -13,8 +13,11 @@ export declare type GraphQLInvalidTestCase<T> = GraphQLValidTestCase<T> & {
|
|
13
13
|
errors: number | Array<RuleTester.TestCaseError | string>;
|
14
14
|
output?: string | null;
|
15
15
|
};
|
16
|
-
declare
|
17
|
-
|
16
|
+
export declare class GraphQLRuleTester extends RuleTester {
|
17
|
+
config: {
|
18
|
+
parser: string;
|
19
|
+
parserOptions: ParserOptions;
|
20
|
+
};
|
18
21
|
constructor(parserOptions?: ParserOptions);
|
19
22
|
fromMockFile(path: string): string;
|
20
23
|
runGraphQLTests<Config>(name: string, rule: GraphQLESLintRule, tests: {
|
@@ -22,4 +25,3 @@ export declare class GraphQLRuleTester extends GraphQLRuleTester_base {
|
|
22
25
|
invalid: GraphQLInvalidTestCase<Config>[];
|
23
26
|
}): void;
|
24
27
|
}
|
25
|
-
export {};
|
package/utils.d.ts
CHANGED
@@ -33,4 +33,8 @@ export declare enum CaseStyle {
|
|
33
33
|
}
|
34
34
|
export declare const camelCase: (str: string) => string;
|
35
35
|
export declare const convertCase: (style: CaseStyle, str: string) => string;
|
36
|
+
export declare function getLocation(loc: Partial<AST.SourceLocation>, fieldName?: string, offset?: {
|
37
|
+
offsetStart?: number;
|
38
|
+
offsetEnd?: number;
|
39
|
+
}): AST.SourceLocation;
|
36
40
|
export {};
|