Skip to content

Commit

Permalink
GraphQL v16 Support (#724)
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Nov 2, 2021
1 parent 2abd74b commit 81fae5a
Show file tree
Hide file tree
Showing 26 changed files with 117 additions and 70 deletions.
5 changes: 5 additions & 0 deletions .changeset/green-lions-grow.md
@@ -0,0 +1,5 @@
---
'@graphql-eslint/eslint-plugin': minor
---

GraphQL v16 support
6 changes: 3 additions & 3 deletions .github/workflows/canary.yml
Expand Up @@ -32,10 +32,10 @@ jobs:
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-16-node-modules-${{ hashFiles('yarn.lock') }}
key: ${{ runner.os }}-16-8-16-node-modules-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-16-node-modules-${{ hashFiles('yarn.lock') }}
${{ runner.os }}-16-node-modules-
${{ runner.os }}-16-8-16-node-modules-${{ hashFiles('yarn.lock') }}
${{ runner.os }}-16-8-16-node-modules-
- name: Install Dependencies using Yarn
run: yarn install && git checkout yarn.lock && yarn patch-package
- name: Release Canary
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Expand Up @@ -35,10 +35,10 @@ jobs:
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-16-node-modules-${{ hashFiles('yarn.lock') }}
key: ${{ runner.os }}-16-8-16-node-modules-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-16-node-modules-${{ hashFiles('yarn.lock') }}
${{ runner.os }}-16-node-modules-
${{ runner.os }}-16-8-16-node-modules-${{ hashFiles('yarn.lock') }}
${{ runner.os }}-16-8-16-node-modules-
- name: Install Dependencies using Yarn
run: yarn install && git checkout yarn.lock && yarn patch-package
- name: Create Release Pull Request or Publish to npm
Expand Down
36 changes: 25 additions & 11 deletions .github/workflows/tests.yml
Expand Up @@ -25,17 +25,23 @@ jobs:
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-16-node-modules-${{ hashFiles('yarn.lock') }}
key: ${{ runner.os }}-16-8-16-node-modules-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-16-node-modules-${{ hashFiles('yarn.lock') }}
${{ runner.os }}-16-node-modules-
${{ runner.os }}-16-8-16-node-modules-${{ hashFiles('yarn.lock') }}
${{ runner.os }}-16-8-16-node-modules-
- name: Install Dependencies using Yarn
run: yarn install && git checkout yarn.lock
- name: Lint
run: yarn lint
typecheck:
name: TypeScript Type Checking
runs-on: ubuntu-latest
strategy:
matrix:
graphql_version:
# - 14
- 15
- 16
steps:
- name: Checkout Master
uses: actions/checkout@v2
Expand All @@ -49,10 +55,12 @@ jobs:
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-16-node-modules-${{ hashFiles('yarn.lock') }}
key: ${{runner.os}}-16-8-${{matrix.graphql_version}}-node-modules-${{hashFiles('yarn.lock')}}
restore-keys: |
${{ runner.os }}-16-node-modules-${{ hashFiles('yarn.lock') }}
${{ runner.os }}-16-node-modules-
${{runner.os}}-16-8-${{matrix.graphql_version}}-node-modules-${{hashFiles('yarn.lock')}}
${{runner.os}}-16-8-${{matrix.graphql_version}}-node-modules-
- name: Use GraphQL v${{matrix.graphql_version}}
run: node ./scripts/match-graphql.js ${{matrix.graphql_version}}
- name: Install Dependencies using Yarn
run: yarn install && git checkout yarn.lock
- name: Build
Expand All @@ -64,14 +72,18 @@ jobs:
path: packages/plugin/dist

test:
name: Testing on Node ${{matrix.node_version}} with ESLint v${{matrix.eslint_version}}
name: Testing on Node ${{matrix.node_version}} with ESLint v${{matrix.eslint_version}} and GraphQL v${{matrix.graphql_version}}
timeout-minutes: 60
runs-on: ubuntu-latest
needs: [lint, typecheck]
needs: [typecheck]
strategy:
matrix:
node_version: [12, 16]
eslint_version: [7.32.0, 8]
graphql_version:
# - 14
- 15
- 16
steps:
- name: Checkout Master
uses: actions/checkout@v2
Expand All @@ -85,10 +97,12 @@ jobs:
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{runner.os}}-${{matrix.node_version}}-${{matrix.eslint_version}}-node-modules-${{hashFiles('yarn.lock')}}
key: ${{runner.os}}-${{matrix.node_version}}-${{matrix.eslint_version}}-${{matrix.graphql_version}}-node-modules-${{hashFiles('yarn.lock')}}
restore-keys: |
${{runner.os}}-${{matrix.node_version}}-${{matrix.eslint_version}}-node-modules-${{hashFiles('yarn.lock')}}
${{runner.os}}-${{matrix.node_version}}-${{matrix.eslint_version}}-node-modules-
${{runner.os}}-${{matrix.node_version}}-${{matrix.eslint_version}}-${{matrix.graphql_version}}-node-modules-${{hashFiles('yarn.lock')}}
${{runner.os}}-${{matrix.node_version}}-${{matrix.eslint_version}}-${{matrix.graphql_version}}-node-modules-
- name: Use GraphQL v${{matrix.graphql_version}}
run: node ./scripts/match-graphql.js ${{matrix.graphql_version}}
- name: Use ESLint v${{matrix.eslint_version}}
run: node scripts/match-eslint.mjs ${{matrix.eslint_version}}
- name: Install Dependencies using Yarn
Expand Down
13 changes: 12 additions & 1 deletion .vscode/settings.json
@@ -1,3 +1,14 @@
{
"eslint.enable": true
"eslint.enable": true,
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/node_modules": true,
"test-lib": true,
"lib": true,
"coverage": true,
"npm": true,
"**/dist": true
},
"typescript.tsdk": "node_modules/typescript/lib"
}
5 changes: 4 additions & 1 deletion babel.config.js
@@ -1,3 +1,6 @@
module.exports = {
presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'],
presets: [
['@babel/preset-env', { targets: { node: process.versions.node.split('.')[0] } }],
'@babel/preset-typescript',
],
};
2 changes: 1 addition & 1 deletion examples/basic/package.json
Expand Up @@ -9,7 +9,7 @@
"lint": "eslint ."
},
"dependencies": {
"graphql": "15.7.1"
"graphql": "16.0.1"
},
"devDependencies": {
"@graphql-eslint/eslint-plugin": "2.3.2",
Expand Down
2 changes: 1 addition & 1 deletion examples/code-file/package.json
Expand Up @@ -9,7 +9,7 @@
"lint": "eslint ."
},
"dependencies": {
"graphql": "15.7.1"
"graphql": "16.0.1"
},
"devDependencies": {
"@graphql-eslint/eslint-plugin": "2.3.2",
Expand Down
2 changes: 1 addition & 1 deletion examples/graphql-config-code-file/package.json
Expand Up @@ -9,7 +9,7 @@
"lint": "eslint --ext graphql,js ."
},
"dependencies": {
"graphql": "15.7.1",
"graphql": "16.0.1",
"graphql-tag": "^2.12.5"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion examples/graphql-config/package.json
Expand Up @@ -9,7 +9,7 @@
"lint": "eslint ."
},
"dependencies": {
"graphql": "15.7.1"
"graphql": "16.0.1"
},
"devDependencies": {
"@graphql-eslint/eslint-plugin": "2.3.2",
Expand Down
2 changes: 1 addition & 1 deletion examples/prettier/package.json
Expand Up @@ -9,7 +9,7 @@
"lint": "eslint ."
},
"dependencies": {
"graphql": "15.7.1"
"graphql": "16.0.1"
},
"devDependencies": {
"@graphql-eslint/eslint-plugin": "2.3.2",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -56,7 +56,8 @@
"typescript": "4.4.4"
},
"resolutions": {
"@changesets/git": "1.1.2"
"@changesets/git": "1.1.2",
"graphql": "16.0.1"
},
"lint-staged": {
"{packages,scripts}/**/*.{ts,tsx,js,jsx,cjs,mjs}": [
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin/package.json
Expand Up @@ -41,7 +41,7 @@
"@types/graphql-depth-limit": "1.1.3",
"@types/lodash.lowercase": "4.3.6",
"bob-the-bundler": "1.5.1",
"graphql": "15.7.2",
"graphql": "16.0.1",
"typescript": "4.4.4"
},
"peerDependencies": {
Expand Down
9 changes: 4 additions & 5 deletions packages/plugin/src/estree-parser/converter.ts
@@ -1,15 +1,14 @@
import { convertDescription, convertLocation, convertRange, extractCommentsFromAst } from './utils';
import { GraphQLESTreeNode, SafeGraphQLType } from './estree-ast';
import { ASTNode, TypeNode, TypeInfo, visit, visitWithTypeInfo, Location, Kind, DocumentNode } from 'graphql';
import { Comment } from 'estree';
import { ASTNode, TypeNode, TypeInfo, visit, visitWithTypeInfo, Location, Kind, DocumentNode, ASTVisitor } from 'graphql';

export function convertToESTree<T extends ASTNode>(
node: T,
typeInfo?: TypeInfo
): { rootTree: GraphQLESTreeNode<T>; comments: Comment[] } {
const visitor = { leave: convertNode(typeInfo) };
) {
const visitor: ASTVisitor = { leave: convertNode(typeInfo) };
return {
rootTree: visit(node, typeInfo ? visitWithTypeInfo(typeInfo, visitor) : visitor),
rootTree: visit(node, typeInfo ? visitWithTypeInfo(typeInfo, visitor) : visitor) as GraphQLESTreeNode<T>,
comments: extractCommentsFromAst(node.loc),
};
}
Expand Down
5 changes: 2 additions & 3 deletions packages/plugin/src/graphql-ast.ts
@@ -1,9 +1,8 @@
import {
ASTNode,
Visitor,
ASTVisitor,
TypeInfo,
GraphQLSchema,
ASTKindToNode,
visit,
isInterfaceType,
visitWithTypeInfo,
Expand Down Expand Up @@ -41,7 +40,7 @@ export function getReachableTypes(schema: GraphQLSchema): ReachableTypes {
}
};

const visitor: Visitor<ASTKindToNode> = {
const visitor: ASTVisitor = {
InterfaceTypeDefinition: collect,
ObjectTypeDefinition: collect,
InputValueDefinition: collect,
Expand Down
6 changes: 3 additions & 3 deletions packages/plugin/src/parser.ts
@@ -1,5 +1,5 @@
import { parseGraphQLSDL } from '@graphql-tools/utils';
import { GraphQLError, TypeInfo } from 'graphql';
import { ASTNode, GraphQLError, TypeInfo, Source } from 'graphql';
import { Linter } from 'eslint';
import { convertToESTree } from './estree-parser';
import { GraphQLESLintParseResult, ParserOptions, ParserServices } from './types';
Expand Down Expand Up @@ -32,8 +32,8 @@ export function parseForESLint(code: string, options: ParserOptions = {}): Graph
noLocation: false,
});

const { rootTree, comments } = convertToESTree(graphqlAst.document, schema ? new TypeInfo(schema) : null);
const tokens = extractTokens(code);
const { rootTree, comments } = convertToESTree(graphqlAst.document as ASTNode, schema ? new TypeInfo(schema) : null);
const tokens = extractTokens(new Source(code, filePath));

return {
services: parserServices,
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin/src/rules/graphql-js-validation.ts
Expand Up @@ -22,7 +22,7 @@ export function validateDoc(
): void {
if (documentNode?.definitions?.length > 0) {
try {
const validationErrors = schema ? validate(schema, documentNode, rules) : validateSDL(documentNode, null, rules);
const validationErrors = schema ? validate(schema, documentNode, rules) : validateSDL(documentNode, null, rules as any);

for (const error of validationErrors) {
const validateRuleName = ruleName || `[${extractRuleName(error.stack)}]`;
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin/src/rules/no-deprecated.ts
Expand Up @@ -86,7 +86,7 @@ const rule: GraphQLESLintRule<[], true> = {
const typeInfo = node.typeInfo();

if (typeInfo && typeInfo.enumValue) {
if (typeInfo.enumValue.isDeprecated) {
if (typeInfo.enumValue.deprecationReason) {
const enumValueName = node.value;
context.report({
loc: getLocation(node.loc, enumValueName),
Expand All @@ -104,7 +104,7 @@ const rule: GraphQLESLintRule<[], true> = {
const typeInfo = node.typeInfo();

if (typeInfo && typeInfo.fieldDef) {
if (typeInfo.fieldDef.isDeprecated) {
if (typeInfo.fieldDef.deprecationReason) {
const fieldName = node.name.value;
context.report({
loc: getLocation(node.loc, fieldName),
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin/src/rules/require-description.ts
Expand Up @@ -49,7 +49,7 @@ const rule: GraphQLESLintRule<RequireDescriptionRuleConfig> = {
examples: [
{
title: 'Incorrect',
usage: [{ on: ['ObjectTypeDefinition', 'FieldDefinition'] }],
usage: [{ on: [Kind.OBJECT_TYPE_DEFINITION, Kind.FIELD_DEFINITION] }],
code: /* GraphQL */ `
type someTypeName {
name: String
Expand All @@ -58,7 +58,7 @@ const rule: GraphQLESLintRule<RequireDescriptionRuleConfig> = {
},
{
title: 'Correct',
usage: [{ on: ['ObjectTypeDefinition', 'FieldDefinition'] }],
usage: [{ on: [Kind.OBJECT_TYPE_DEFINITION, Kind.FIELD_DEFINITION] }],
code: /* GraphQL */ `
"""
Some type description
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin/src/sibling-operations.ts
Expand Up @@ -120,7 +120,7 @@ export function getSiblingOperations(options: ParserOptions, gqlConfig: GraphQLC
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
result.push({
filePath: source.location,
document: definition,
document: definition as FragmentDefinitionNode,
});
}
}
Expand All @@ -141,7 +141,7 @@ export function getSiblingOperations(options: ParserOptions, gqlConfig: GraphQLC
if (definition.kind === Kind.OPERATION_DEFINITION) {
result.push({
filePath: source.location,
document: definition,
document: definition as OperationDefinitionNode,
});
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin/src/utils.ts
Expand Up @@ -77,8 +77,8 @@ function getLexer(source: Source): Lexer {
throw new Error(`Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!`);
}

export function extractTokens(source: string): AST.Token[] {
const lexer = getLexer(new Source(source));
export function extractTokens(source: Source): AST.Token[] {
const lexer = getLexer(source);
const tokens: AST.Token[] = [];
let token = lexer.advance();

Expand Down
10 changes: 7 additions & 3 deletions packages/plugin/tests/mocks/graphql-server.ts
@@ -1,11 +1,13 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type errors from `graphql` package
// @ts-nocheck
import { readFileSync } from 'fs';
import { resolve } from 'path';
import { createServer, Server, IncomingMessage, ServerResponse } from 'http';
import { buildSchema, getIntrospectionQuery, graphqlSync } from 'graphql';
import { buildSchema, introspectionFromSchema } from 'graphql';

const sdlSchema = readFileSync(resolve(__dirname, 'user-schema.graphql'), 'utf8');
const graphqlSchemaObj = buildSchema(sdlSchema);
const introspectionQueryResult = graphqlSync(graphqlSchemaObj, getIntrospectionQuery());
const introspectionQueryResult = introspectionFromSchema(graphqlSchemaObj);

class TestGraphQLServer {
private server: Server;
Expand Down Expand Up @@ -38,7 +40,9 @@ class TestGraphQLServer {
if (pathname === '/') {
const { query } = await this.parseData(req);
if (query.includes('query IntrospectionQuery')) {
res.end(JSON.stringify(introspectionQueryResult));
res.end(JSON.stringify({
data: introspectionQueryResult
}));
}
} else if (pathname === '/my-headers') {
res.end(JSON.stringify(req.headers));
Expand Down
5 changes: 4 additions & 1 deletion packages/plugin/tests/schema.spec.ts
Expand Up @@ -17,7 +17,7 @@ describe('schema', () => {
expect(graphQLSchema).toBeInstanceOf(GraphQLSchema);

const sdlString = printSchema(graphQLSchema);
expect(sdlString).toBe(schemaOnDisk);
expect(sdlString.trim()).toBe(schemaOnDisk.trim());
};

describe('GraphQLFileLoader', () => {
Expand Down Expand Up @@ -54,6 +54,9 @@ describe('schema', () => {
url = chunk.toString().trimRight();
done();
});
local.stderr.on('data', chunk => {
throw new Error(chunk.toString().trimRight());
});
});

afterAll(done => {
Expand Down
5 changes: 2 additions & 3 deletions patches/eslint+8.1.0.patch
@@ -1,8 +1,8 @@
diff --git a/node_modules/eslint/lib/rule-tester/rule-tester.js b/node_modules/eslint/lib/rule-tester/rule-tester.js
index 324af7b..e771420 100644
index 7f590a5..5368321 100644
--- a/node_modules/eslint/lib/rule-tester/rule-tester.js
+++ b/node_modules/eslint/lib/rule-tester/rule-tester.js
@@ -943,8 +943,18 @@ class RuleTester {
@@ -946,7 +946,17 @@ class RuleTester {
"Expected no autofixes to be suggested"
);
} else {
Expand All @@ -21,4 +21,3 @@ index 324af7b..e771420 100644
}
} else {
assert.strictEqual(
result.output,

0 comments on commit 81fae5a

Please sign in to comment.