diff --git a/.changeset/breezy-seals-sparkle.md b/.changeset/breezy-seals-sparkle.md new file mode 100644 index 00000000000..3dd0c629203 --- /dev/null +++ b/.changeset/breezy-seals-sparkle.md @@ -0,0 +1,5 @@ +--- +'@graphql-eslint/eslint-plugin': patch +--- + +fix caching issues for `no-unreachable-types` / `no-unused-fields` rules for multi projects diff --git a/examples/multiple-projects-graphql-config/query.second-project.js b/examples/multiple-projects-graphql-config/query.second-project.js index 92b444d6dfc..fec40538d45 100644 --- a/examples/multiple-projects-graphql-config/query.second-project.js +++ b/examples/multiple-projects-graphql-config/query.second-project.js @@ -1,7 +1,7 @@ import { custom } from 'custom-graphql-tag'; /* MyGraphQL */ ` - fragment UserFields on User { + fragment UserFields on AnotherUser { firstName lastName } diff --git a/examples/multiple-projects-graphql-config/schema.second-project.graphql b/examples/multiple-projects-graphql-config/schema.second-project.graphql index 6974e231f9c..a72f79c79b8 100644 --- a/examples/multiple-projects-graphql-config/schema.second-project.graphql +++ b/examples/multiple-projects-graphql-config/schema.second-project.graphql @@ -1,8 +1,8 @@ -type User { +type AnotherUser { firstName: String lastName: String } type Query { - users: [User] + users: [AnotherUser] } diff --git a/packages/plugin/__tests__/__snapshots__/examples.spec.md b/packages/plugin/__tests__/__snapshots__/examples.spec.md index 13d27e8d91e..7d297da2cb3 100644 --- a/packages/plugin/__tests__/__snapshots__/examples.spec.md +++ b/packages/plugin/__tests__/__snapshots__/examples.spec.md @@ -236,8 +236,8 @@ exports[`Examples > should work in multiple projects 1`] = ` desc: Rename to \`users\`, fix: { range: [ - 141, - 141, + 148, + 148, ], text: query users , }, @@ -268,10 +268,10 @@ Accepted type: ID., messages: [ { column: 6, - endColumn: 10, + endColumn: 17, endLine: 1, line: 1, - message: type "User" must have exactly one non-nullable unique identifier. + message: type "AnotherUser" must have exactly one non-nullable unique identifier. Accepted name: id. Accepted type: ID., nodeType: Name, diff --git a/packages/plugin/src/cache.ts b/packages/plugin/src/cache.ts index 4fabd514206..439ab1eb41e 100644 --- a/packages/plugin/src/cache.ts +++ b/packages/plugin/src/cache.ts @@ -4,7 +4,7 @@ import debugFactory from 'debug'; const log = debugFactory('graphql-eslint:ModuleCache'); -export class ModuleCache { +export class ModuleCache { map = new Map(); set(cacheKey: K, result: T): void { diff --git a/packages/plugin/src/documents.ts b/packages/plugin/src/documents.ts index 947c0e6c594..7399c9593e1 100644 --- a/packages/plugin/src/documents.ts +++ b/packages/plugin/src/documents.ts @@ -7,7 +7,7 @@ import { ModuleCache } from './cache.js'; import { Pointer } from './types.js'; const debug = debugFactory('graphql-eslint:operations'); -const operationsCache = new ModuleCache(); +const operationsCache = new ModuleCache(); const handleVirtualPath = (documents: Source[]): Source[] => { const filepathMap: Record = Object.create(null); diff --git a/packages/plugin/src/rules/no-unreachable-types.ts b/packages/plugin/src/rules/no-unreachable-types.ts index 2f7dc3ee6da..6c5cdfdfa44 100644 --- a/packages/plugin/src/rules/no-unreachable-types.ts +++ b/packages/plugin/src/rules/no-unreachable-types.ts @@ -8,7 +8,9 @@ import { NameNode, visit, } from 'graphql'; +import { GraphQLProjectConfig } from 'graphql-config'; import lowerCase from 'lodash.lowercase'; +import { ModuleCache } from '../cache.js'; import { GraphQLESTreeNode } from '../estree-converter/index.js'; import { GraphQLESLintRule } from '../types.js'; import { getTypeName, requireGraphQLSchemaFromContext } from '../utils.js'; @@ -33,7 +35,7 @@ const KINDS = [ type ReachableTypes = Set; -let reachableTypesCache: ReachableTypes; +const reachableTypesCache = new ModuleCache(); const RequestDirectiveLocations = new Set([ DirectiveLocation.QUERY, @@ -49,8 +51,9 @@ const RequestDirectiveLocations = new Set([ function getReachableTypes(schema: GraphQLSchema): ReachableTypes { // We don't want cache reachableTypes on test environment // Otherwise reachableTypes will be same for all tests - if (process.env.NODE_ENV !== 'test' && reachableTypesCache) { - return reachableTypesCache; + const cachedValue = reachableTypesCache.get(schema); + if (process.env.NODE_ENV !== 'test' && cachedValue) { + return cachedValue; } const reachableTypes: ReachableTypes = new Set(); @@ -106,9 +109,8 @@ function getReachableTypes(schema: GraphQLSchema): ReachableTypes { } } } - - reachableTypesCache = reachableTypes; - return reachableTypesCache; + reachableTypesCache.set(schema, reachableTypes); + return reachableTypes; } export const rule: GraphQLESLintRule = { diff --git a/packages/plugin/src/rules/no-unused-fields.ts b/packages/plugin/src/rules/no-unused-fields.ts index 60bd7c3af90..06228f77707 100644 --- a/packages/plugin/src/rules/no-unused-fields.ts +++ b/packages/plugin/src/rules/no-unused-fields.ts @@ -1,4 +1,6 @@ import { GraphQLSchema, TypeInfo, visit, visitWithTypeInfo } from 'graphql'; +import { GraphQLProjectConfig } from 'graphql-config'; +import { ModuleCache } from '../cache.js'; import { SiblingOperations } from '../siblings.js'; import { GraphQLESLintRule } from '../types.js'; import { requireGraphQLSchemaFromContext, requireSiblingsOperations } from '../utils.js'; @@ -7,13 +9,14 @@ const RULE_ID = 'no-unused-fields'; type UsedFields = Record>; -let usedFieldsCache: UsedFields; +const usedFieldsCache = new ModuleCache(); function getUsedFields(schema: GraphQLSchema, operations: SiblingOperations): UsedFields { // We don't want cache usedFields on test environment // Otherwise usedFields will be same for all tests - if (process.env.NODE_ENV !== 'test' && usedFieldsCache) { - return usedFieldsCache; + const cachedValue = usedFieldsCache.get(schema); + if (process.env.NODE_ENV !== 'test' && cachedValue) { + return cachedValue; } const usedFields: UsedFields = Object.create(null); const typeInfo = new TypeInfo(schema); @@ -37,8 +40,8 @@ function getUsedFields(schema: GraphQLSchema, operations: SiblingOperations): Us for (const { document } of allDocuments) { visit(document, visitor); } - usedFieldsCache = usedFields; - return usedFieldsCache; + usedFieldsCache.set(schema, usedFields); + return usedFields; } export const rule: GraphQLESLintRule = { diff --git a/packages/plugin/src/schema.ts b/packages/plugin/src/schema.ts index 9c940a1fd40..48d6bb49cf1 100644 --- a/packages/plugin/src/schema.ts +++ b/packages/plugin/src/schema.ts @@ -5,7 +5,7 @@ import { GraphQLProjectConfig } from 'graphql-config'; import { ModuleCache } from './cache.js'; import { Pointer, Schema } from './types.js'; -const schemaCache = new ModuleCache(); +const schemaCache = new ModuleCache(); const debug = debugFactory('graphql-eslint:schema'); export function getSchema(project: GraphQLProjectConfig): Schema {