Skip to content

Commit c5fb2dc

Browse files
authored
Another solution: do not crash ESLint when schema/siblings contains validation errors (#1386)
* fix * update changeset * update eslint * Update packages/plugin/src/schema.ts
1 parent af89542 commit c5fb2dc

File tree

11 files changed

+256
-188
lines changed

11 files changed

+256
-188
lines changed

.changeset/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"access": "restricted",
66
"baseBranch": "master",
77
"updateInternalDependencies": "patch",
8-
"ignore": ["@graphql-eslint/example-*"],
8+
"ignore": ["@graphql-eslint/example-*", "website"],
99
"changelog": [
1010
"@changesets/changelog-github",
1111
{

.changeset/early-poems-reflect.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@graphql-eslint/eslint-plugin': minor
3+
---
4+
5+
The new version no longer crashes on VSCode-ESLint, when schema/siblings contain validation errors,
6+
also, GraphQL-ESLint now has `strict: true` in `tsconfig.json` and extends `@typescript-eslint`
7+
recommended config 🚀
8+
9+
P.S. GraphQL-ESLint now has its own website, all documentation moved here. Also, it contains a new
10+
fancy playground page 💅 for both schema/operations testing
11+
https://the-guild.dev/graphql/eslint/play

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"chalk": "4.1.2",
3232
"dedent": "0.7.0",
3333
"enquirer": "2.3.6",
34-
"eslint": "8.31.0",
34+
"eslint": "8.32.0",
3535
"eslint-plugin-eslint-plugin": "5.0.7",
3636
"eslint-plugin-tailwindcss": "3.8.2",
3737
"husky": "8.0.3",
@@ -49,7 +49,7 @@
4949
},
5050
"pnpm": {
5151
"patchedDependencies": {
52-
"eslint@8.31.0": "patches/[email protected]",
52+
"eslint@8.32.0": "patches/[email protected]",
5353
5454
5555

packages/plugin/src/estree-converter/converter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { GraphQLESTreeNode, TypeInformation } from './types.js';
1313
import { convertLocation } from './utils.js';
1414

15-
export function convertToESTree<T extends DocumentNode>(node: T, schema?: GraphQLSchema) {
15+
export function convertToESTree<T extends DocumentNode>(node: T, schema?: GraphQLSchema | null) {
1616
const typeInfo = schema && new TypeInfo(schema);
1717

1818
const visitor: ASTVisitor = {

packages/plugin/src/parser.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,22 @@ export function parseForESLint(code: string, options: ParserOptions): GraphQLESL
2727
const realFilepath = filePath.replace(VIRTUAL_DOCUMENT_REGEX, '');
2828
const project = gqlConfig.getProjectForFile(realFilepath);
2929

30-
const schema = project
31-
? getSchema(project, options.schemaOptions)
32-
: typeof options.schema === 'string'
33-
? buildSchema(options.schema)
34-
: null;
30+
let schema: GraphQLSchema | null = null;
3531

36-
const rootTree = convertToESTree(
37-
document,
38-
schema instanceof GraphQLSchema ? schema : undefined,
39-
);
32+
try {
33+
schema = project
34+
? getSchema(project, options.schemaOptions)
35+
: typeof options.schema === 'string'
36+
? buildSchema(options.schema)
37+
: null;
38+
} catch (error) {
39+
if (error instanceof Error) {
40+
error.message = `Error while loading schema: ${error.message}`;
41+
}
42+
throw error;
43+
}
44+
45+
const rootTree = convertToESTree(document, schema);
4046

4147
return {
4248
services: {

packages/plugin/src/schema.ts

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import chalk from 'chalk';
21
import debugFactory from 'debug';
32
import fg from 'fast-glob';
43
import { GraphQLSchema } from 'graphql';
54
import { GraphQLProjectConfig } from 'graphql-config';
65
import { ModuleCache } from './cache.js';
76
import { ParserOptions, Pointer, Schema } from './types.js';
87

9-
const schemaCache = new ModuleCache<Error | GraphQLSchema>();
8+
const schemaCache = new ModuleCache<GraphQLSchema>();
109
const debug = debugFactory('graphql-eslint:schema');
1110

1211
export function getSchema(
@@ -25,26 +24,17 @@ export function getSchema(
2524
return cache;
2625
}
2726

28-
let schema: Error | GraphQLSchema;
29-
30-
try {
31-
debug('Loading schema from %o', project.schema);
32-
schema = project.loadSchemaSync(project.schema, 'GraphQLSchema', {
33-
...schemaOptions,
34-
pluckConfig: project.extensions.pluckConfig,
35-
});
36-
if (debug.enabled) {
37-
debug('Schema loaded: %o', schema instanceof GraphQLSchema);
38-
const schemaPaths = fg.sync(project.schema as Pointer, { absolute: true });
39-
debug('Schema pointers %O', schemaPaths);
40-
}
41-
// Do not set error to cache, since cache reload will be done after some `lifetime` seconds
42-
schemaCache.set(schemaKey, schema);
43-
} catch (error) {
44-
if (error instanceof Error) {
45-
error.message = chalk.red(`Error while loading schema: ${error.message}`);
46-
}
47-
schema = error as Error;
27+
debug('Loading schema from %o', project.schema);
28+
const schema = project.loadSchemaSync(project.schema, 'GraphQLSchema', {
29+
...schemaOptions,
30+
pluckConfig: project.extensions.pluckConfig,
31+
});
32+
if (debug.enabled) {
33+
debug('Schema loaded: %o', schema instanceof GraphQLSchema);
34+
const schemaPaths = fg.sync(project.schema as Pointer, { absolute: true });
35+
debug('Schema pointers %O', schemaPaths);
4836
}
37+
schemaCache.set(schemaKey, schema);
38+
4939
return schema;
5040
}

packages/plugin/src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { JSONSchema } from 'json-schema-to-ts';
77
import { SiblingOperations } from './siblings.js';
88
import { GraphQLESLintRuleListener } from './testkit.js';
99

10-
export type Schema = Error | GraphQLSchema | null;
11-
export type Pointer = string[] | string;
10+
export type Schema = GraphQLSchema | null;
11+
export type Pointer = string | string[];
1212

1313
export interface ParserOptions {
1414
schema?: Pointer | Record<string, { headers: Record<string, string> }>;

packages/plugin/src/utils.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ export function requireGraphQLSchemaFromContext(
2828
throw new Error(
2929
`Rule \`${ruleId}\` requires \`parserOptions.schema\` to be set and loaded. See https://bit.ly/graphql-eslint-schema for more info`,
3030
);
31-
} else if (schema instanceof Error) {
32-
throw schema;
3331
}
3432
return schema;
3533
}

packages/plugin/tests/schema.spec.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { spawn } from 'node:child_process';
1+
import { ChildProcessWithoutNullStreams, spawn } from 'node:child_process';
22
import { readFile } from 'node:fs/promises';
33
import path from 'node:path';
44
import { GraphQLSchema, printSchema } from 'graphql';
@@ -39,8 +39,8 @@ describe('schema', async () => {
3939
});
4040

4141
describe('UrlLoader', () => {
42-
let local;
43-
let url;
42+
let local: ChildProcessWithoutNullStreams;
43+
let url: string;
4444

4545
beforeAll(
4646
() =>
@@ -64,8 +64,8 @@ describe('schema', async () => {
6464

6565
afterAll(
6666
() =>
67-
new Promise(resolve => {
68-
local.on('close', () => resolve());
67+
new Promise(done => {
68+
local.on('close', () => done());
6969
local.kill();
7070
}),
7171
);
@@ -75,7 +75,7 @@ describe('schema', async () => {
7575
});
7676

7777
describe('should passe headers', () => {
78-
let schemaUrl;
78+
let schemaUrl: string;
7979
let schemaOptions;
8080

8181
beforeAll(() => {
@@ -95,27 +95,23 @@ describe('schema', async () => {
9595
},
9696
filePath: '',
9797
});
98-
const error = getSchema(gqlConfig.getDefault()) as Error;
99-
expect(error).toBeInstanceOf(Error);
100-
expect(error.message).toMatch('authorization: "Bearer Foo"');
98+
expect(() => getSchema(gqlConfig.getDefault())).toThrow('authorization: "Bearer Foo"');
10199
});
102100

103101
// https://github.com/B2o5T/graphql-eslint/blob/master/docs/parser-options.md#schemaoptions
104102
it('with `parserOptions.schemaOptions`', () => {
105103
const gqlConfig = loadGraphQLConfig({ schema: schemaUrl, filePath: '' });
106-
const error = getSchema(gqlConfig.getDefault(), schemaOptions) as Error;
107-
expect(error).toBeInstanceOf(Error);
108-
expect(error.message).toMatch('authorization: "Bearer Foo"');
104+
expect(() => getSchema(gqlConfig.getDefault(), schemaOptions)).toThrow(
105+
'authorization: "Bearer Foo"',
106+
);
109107
});
110108
});
111109
});
112110

113111
describe('schema loading', () => {
114112
it('should return Error', () => {
115113
const gqlConfig = loadGraphQLConfig({ schema: 'not-exist.gql', filePath: '' });
116-
const error = getSchema(gqlConfig.getDefault()) as Error;
117-
expect(error).toBeInstanceOf(Error);
118-
expect(error.message).toMatch(
114+
expect(() => getSchema(gqlConfig.getDefault())).toThrow(
119115
'Unable to find any GraphQL type definitions for the following pointers',
120116
);
121117
});

0 commit comments

Comments
 (0)