diff --git a/.eslintrc.cjs b/.eslintrc.cjs index bfbc47a776..9db26da0ec 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -605,7 +605,10 @@ module.exports = { '@typescript-eslint/restrict-plus-operands': 'off', // TODO: temporarily disabled '@typescript-eslint/restrict-template-expressions': 'off', // TODO: temporarily disabled '@typescript-eslint/sort-type-union-intersection-members': 'off', // TODO: consider - '@typescript-eslint/strict-boolean-expressions': 'off', // TODO: consider + '@typescript-eslint/strict-boolean-expressions': [ + 'error', + { allowNullableBoolean: true }, // TODO: consider removing + ], '@typescript-eslint/switch-exhaustiveness-check': 'error', '@typescript-eslint/triple-slash-reference': 'error', '@typescript-eslint/typedef': 'off', diff --git a/resources/gen-changelog.ts b/resources/gen-changelog.ts index 9d106fbacc..15192bb4ed 100644 --- a/resources/gen-changelog.ts +++ b/resources/gen-changelog.ts @@ -33,7 +33,7 @@ const labelsConfig: { [label: string]: { section: string; fold?: boolean } } = { }; const { GH_TOKEN } = process.env; -if (!GH_TOKEN) { +if (GH_TOKEN == null) { console.error('Must provide GH_TOKEN as environment variable!'); process.exit(1); } @@ -88,7 +88,7 @@ async function genChangeLog(): Promise { } const label = labels[0]; - if (!labelsConfig[label]) { + if (labelsConfig[label] != null) { throw new Error(`Unknown label: ${label}. See ${pr.url}`); } byLabel[label] ??= []; @@ -99,7 +99,7 @@ async function genChangeLog(): Promise { let changelog = `## ${tag ?? 'Unreleased'} (${date})\n`; for (const [label, config] of Object.entries(labelsConfig)) { const prs = byLabel[label]; - if (prs) { + if (prs != null) { const shouldFold = config.fold && prs.length > 1; changelog += `\n#### ${config.section}\n`; @@ -149,7 +149,7 @@ async function graphqlRequest(query: string) { } const json = await response.json(); - if (json.errors) { + if (json.errors != null) { throw new Error('Errors: ' + JSON.stringify(json.errors, null, 2)); } return json.data; diff --git a/src/error/GraphQLError.ts b/src/error/GraphQLError.ts index d4c2508b6d..a3ce229968 100644 --- a/src/error/GraphQLError.ts +++ b/src/error/GraphQLError.ts @@ -141,13 +141,13 @@ export class GraphQLError extends Error { // Include (non-enumerable) stack trace. /* c8 ignore start */ // FIXME: https://github.com/graphql/graphql-js/issues/2317 - if (originalError?.stack) { + if (originalError?.stack != null) { Object.defineProperty(this, 'stack', { value: originalError.stack, writable: true, configurable: true, }); - } else if (Error.captureStackTrace) { + } else if (Error.captureStackTrace != null) { Error.captureStackTrace(this, GraphQLError); } else { Object.defineProperty(this, 'stack', { diff --git a/src/execution/__tests__/abstract-test.ts b/src/execution/__tests__/abstract-test.ts index 9bfa84b54e..6349d59b94 100644 --- a/src/execution/__tests__/abstract-test.ts +++ b/src/execution/__tests__/abstract-test.ts @@ -19,6 +19,10 @@ import { buildSchema } from '../../utilities/buildASTSchema.js'; import { execute, executeSync } from '../execute.js'; +interface Context { + async: boolean; +} + async function executeQuery(args: { schema: GraphQLSchema; query: string; @@ -30,13 +34,13 @@ async function executeQuery(args: { schema, document, rootValue, - contextValue: { async: false }, + contextValue: { async: false } satisfies Context, }); const asyncResult = await execute({ schema, document, rootValue, - contextValue: { async: true }, + contextValue: { async: true } satisfies Context, }); expectJSON(result).toDeepEqual(asyncResult); @@ -72,7 +76,7 @@ describe('Execute: Handles execution of abstract types', () => { }, }); - const DogType = new GraphQLObjectType({ + const DogType = new GraphQLObjectType({ name: 'Dog', interfaces: [PetType], isTypeOf(obj, context) { @@ -85,7 +89,7 @@ describe('Execute: Handles execution of abstract types', () => { }, }); - const CatType = new GraphQLObjectType({ + const CatType = new GraphQLObjectType({ name: 'Cat', interfaces: [PetType], isTypeOf(obj, context) { @@ -151,7 +155,7 @@ describe('Execute: Handles execution of abstract types', () => { }, }); - const DogType = new GraphQLObjectType({ + const DogType = new GraphQLObjectType({ name: 'Dog', interfaces: [PetType], isTypeOf(_source, context) { @@ -167,7 +171,7 @@ describe('Execute: Handles execution of abstract types', () => { }, }); - const CatType = new GraphQLObjectType({ + const CatType = new GraphQLObjectType({ name: 'Cat', interfaces: [PetType], isTypeOf: undefined, @@ -233,7 +237,7 @@ describe('Execute: Handles execution of abstract types', () => { }, }); - const DogType = new GraphQLObjectType({ + const DogType = new GraphQLObjectType({ name: 'Dog', interfaces: [PetType], isTypeOf(_source, context) { @@ -280,7 +284,7 @@ describe('Execute: Handles execution of abstract types', () => { }); it('isTypeOf used to resolve runtime type for Union', async () => { - const DogType = new GraphQLObjectType({ + const DogType = new GraphQLObjectType({ name: 'Dog', isTypeOf(obj, context) { const isDog = obj instanceof Dog; @@ -292,7 +296,7 @@ describe('Execute: Handles execution of abstract types', () => { }, }); - const CatType = new GraphQLObjectType({ + const CatType = new GraphQLObjectType({ name: 'Cat', isTypeOf(obj, context) { const isCat = obj instanceof Cat; @@ -357,7 +361,7 @@ describe('Execute: Handles execution of abstract types', () => { name: 'Pet', resolveType(_source, context) { const error = new Error('We are testing this error'); - if (context.async) { + if (context.async === true) { return Promise.reject(error); } throw error; @@ -367,7 +371,7 @@ describe('Execute: Handles execution of abstract types', () => { }, }); - const DogType = new GraphQLObjectType({ + const DogType = new GraphQLObjectType({ name: 'Dog', interfaces: [PetType], fields: { @@ -376,7 +380,7 @@ describe('Execute: Handles execution of abstract types', () => { }, }); - const CatType = new GraphQLObjectType({ + const CatType = new GraphQLObjectType({ name: 'Cat', interfaces: [PetType], fields: { diff --git a/src/execution/__tests__/executor-test.ts b/src/execution/__tests__/executor-test.ts index 5e25dddb5f..c29b4ae60d 100644 --- a/src/execution/__tests__/executor-test.ts +++ b/src/execution/__tests__/executor-test.ts @@ -1143,11 +1143,11 @@ describe('Execute: Handles basic execution tasks', () => { } } - const SpecialType = new GraphQLObjectType({ + const SpecialType = new GraphQLObjectType({ name: 'SpecialType', isTypeOf(obj, context) { const result = obj instanceof Special; - return context?.async ? Promise.resolve(result) : result; + return context.async ? Promise.resolve(result) : result; }, fields: { value: { type: GraphQLString } }, }); @@ -1166,7 +1166,12 @@ describe('Execute: Handles basic execution tasks', () => { specials: [new Special('foo'), new NotSpecial('bar')], }; - const result = executeSync({ schema, document, rootValue }); + const result = executeSync({ + schema, + document, + rootValue, + contextValue: { async: false }, + }); expectJSON(result).toDeepEqual({ data: { specials: [{ value: 'foo' }, null], @@ -1181,12 +1186,11 @@ describe('Execute: Handles basic execution tasks', () => { ], }); - const contextValue = { async: true }; const asyncResult = await execute({ schema, document, rootValue, - contextValue, + contextValue: { async: true }, }); expect(asyncResult).to.deep.equal(result); }); diff --git a/src/execution/__tests__/stream-test.ts b/src/execution/__tests__/stream-test.ts index cd9b9b3965..2b9ad82721 100644 --- a/src/execution/__tests__/stream-test.ts +++ b/src/execution/__tests__/stream-test.ts @@ -1839,7 +1839,7 @@ describe('Execute: stream directive', () => { [Symbol.asyncIterator]: () => ({ next: () => { const friend = friends[index++]; - if (!friend) { + if (friend == null) { return Promise.resolve({ done: true, value: undefined }); } return Promise.resolve({ done: false, value: friend }); @@ -1898,7 +1898,7 @@ describe('Execute: stream directive', () => { [Symbol.asyncIterator]: () => ({ next: () => { const friend = friends[index++]; - if (!friend) { + if (friend == null) { return Promise.resolve({ done: true, value: undefined }); } return Promise.resolve({ done: false, value: friend }); @@ -1954,7 +1954,7 @@ describe('Execute: stream directive', () => { [Symbol.asyncIterator]: () => ({ next: () => { const friend = friends[index++]; - if (!friend) { + if (friend == null) { return Promise.resolve({ done: true, value: undefined }); } return Promise.resolve({ done: false, value: friend }); diff --git a/src/execution/collectFields.ts b/src/execution/collectFields.ts index 17468b791f..91ac792505 100644 --- a/src/execution/collectFields.ts +++ b/src/execution/collectFields.ts @@ -191,7 +191,7 @@ function collectFieldsImpl( const fragment = fragments[fragName]; if ( - !fragment || + fragment == null || !doesFragmentConditionMatch(schema, fragment, runtimeType) ) { continue; diff --git a/src/execution/execute.ts b/src/execution/execute.ts index 1bc6c4267b..a722563289 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -2082,7 +2082,7 @@ function getCompletedIncrementalResults( } incrementalResult.path = asyncPayloadRecord.path; - if (asyncPayloadRecord.label) { + if (asyncPayloadRecord.label != null) { incrementalResult.label = asyncPayloadRecord.label; } if (asyncPayloadRecord.errors.length > 0) { diff --git a/src/execution/values.ts b/src/execution/values.ts index ea11fcaa9b..5511911c78 100644 --- a/src/execution/values.ts +++ b/src/execution/values.ts @@ -1,5 +1,4 @@ import { inspect } from '../jsutils/inspect.js'; -import { keyMap } from '../jsutils/keyMap.js'; import type { Maybe } from '../jsutils/Maybe.js'; import type { ObjMap } from '../jsutils/ObjMap.js'; import { printPathArray } from '../jsutils/printPathArray.js'; @@ -159,14 +158,14 @@ export function getArgumentValues( // FIXME: https://github.com/graphql/graphql-js/issues/2203 /* c8 ignore next */ const argumentNodes = node.arguments ?? []; - const argNodeMap = keyMap(argumentNodes, (arg) => arg.name.value); + const argNodeMap = new Map(argumentNodes.map((arg) => [arg.name.value, arg])); for (const argDef of def.args) { const name = argDef.name; const argType = argDef.type; - const argumentNode = argNodeMap[name]; + const argumentNode = argNodeMap.get(name); - if (!argumentNode) { + if (argumentNode == null) { if (argDef.defaultValue !== undefined) { coercedValues[name] = argDef.defaultValue; } else if (isNonNullType(argType)) { diff --git a/src/jsutils/didYouMean.ts b/src/jsutils/didYouMean.ts index 15735bdb7e..d532b364ca 100644 --- a/src/jsutils/didYouMean.ts +++ b/src/jsutils/didYouMean.ts @@ -23,7 +23,7 @@ export function didYouMean( } let message = ' Did you mean '; - if (subMessage) { + if (subMessage != null) { message += subMessage + ' '; } diff --git a/src/language/printer.ts b/src/language/printer.ts index 6ccaa7d9b4..e91c8c03f7 100644 --- a/src/language/printer.ts +++ b/src/language/printer.ts @@ -143,7 +143,8 @@ const printDocASTReducer: ASTReducer = { FloatValue: { leave: ({ value }) => value }, StringValue: { leave: ({ value, block: isBlockString }) => - isBlockString ? printBlockString(value) : printString(value), + // @ts-expect-error FIXME: it's a problem with ASTReducer, will be fixed in separate PR + isBlockString === true ? printBlockString(value) : printString(value), }, BooleanValue: { leave: ({ value }) => (value ? 'true' : 'false') }, NullValue: { leave: () => 'null' }, diff --git a/src/language/visitor.ts b/src/language/visitor.ts index 86fc48a826..e5a9aa89e8 100644 --- a/src/language/visitor.ts +++ b/src/language/visitor.ts @@ -234,7 +234,7 @@ export function visit( edits = stack.edits; inArray = stack.inArray; stack = stack.prev; - } else if (parent) { + } else if (parent != null) { key = inArray ? index : keys[index]; node = parent[key]; if (node === null || node === undefined) { @@ -287,7 +287,7 @@ export function visit( keys = inArray ? node : (visitorKeys as any)[node.kind] ?? []; index = -1; edits = []; - if (parent) { + if (parent != null) { ancestors.push(parent); } parent = node; diff --git a/src/type/__tests__/enumType-test.ts b/src/type/__tests__/enumType-test.ts index 77f36b4375..f2786947c6 100644 --- a/src/type/__tests__/enumType-test.ts +++ b/src/type/__tests__/enumType-test.ts @@ -71,12 +71,12 @@ const QueryType = new GraphQLObjectType({ provideBadValue: { type: GraphQLBoolean }, }, resolve(_source, { fromEnum, provideGoodValue, provideBadValue }) { - if (provideGoodValue) { + if (provideGoodValue === true) { // Note: this is one of the references of the internal values which // ComplexEnum allows. return Complex2; } - if (provideBadValue) { + if (provideBadValue === true) { // Note: similar shape, but not the same *reference* // as Complex2 above. Enum internal values require === equality. return { someRandomValue: 123 }; diff --git a/src/type/introspection.ts b/src/type/introspection.ts index aedce3e6a8..f5f593629d 100644 --- a/src/type/introspection.ts +++ b/src/type/introspection.ts @@ -112,7 +112,7 @@ export const __Directive: GraphQLObjectType = new GraphQLObjectType({ }, }, resolve(field, { includeDeprecated }) { - return includeDeprecated + return includeDeprecated === true ? field.args : field.args.filter((arg) => arg.deprecationReason == null); }, @@ -266,7 +266,7 @@ export const __Type: GraphQLObjectType = new GraphQLObjectType({ resolve(type, { includeDeprecated }) { if (isObjectType(type) || isInterfaceType(type)) { const fields = Object.values(type.getFields()); - return includeDeprecated + return includeDeprecated === true ? fields : fields.filter((field) => field.deprecationReason == null); } @@ -296,7 +296,7 @@ export const __Type: GraphQLObjectType = new GraphQLObjectType({ resolve(type, { includeDeprecated }) { if (isEnumType(type)) { const values = type.getValues(); - return includeDeprecated + return includeDeprecated === true ? values : values.filter((field) => field.deprecationReason == null); } @@ -313,7 +313,7 @@ export const __Type: GraphQLObjectType = new GraphQLObjectType({ resolve(type, { includeDeprecated }) { if (isInputObjectType(type)) { const values = Object.values(type.getFields()); - return includeDeprecated + return includeDeprecated === true ? values : values.filter((field) => field.deprecationReason == null); } @@ -351,7 +351,7 @@ export const __Field: GraphQLObjectType = new GraphQLObjectType({ }, }, resolve(field, { includeDeprecated }) { - return includeDeprecated + return includeDeprecated === true ? field.args : field.args.filter((arg) => arg.deprecationReason == null); }, diff --git a/src/type/validate.ts b/src/type/validate.ts index 6753534c69..86461d5dde 100644 --- a/src/type/validate.ts +++ b/src/type/validate.ts @@ -373,7 +373,7 @@ function validateTypeImplementsInterface( const typeField = typeFieldMap[fieldName]; // Assert interface field exists on type. - if (!typeField) { + if (typeField == null) { context.reportError( `Interface field ${iface.name}.${fieldName} expected but ${type.name} does not provide it.`, [ifaceField.astNode, type.astNode, ...type.extensionASTNodes], diff --git a/src/utilities/TypeInfo.ts b/src/utilities/TypeInfo.ts index 64a8041493..ffcc964b4a 100644 --- a/src/utilities/TypeInfo.ts +++ b/src/utilities/TypeInfo.ts @@ -208,7 +208,7 @@ export class TypeInfo { let inputField: GraphQLInputField | undefined; if (isInputObjectType(objectType)) { inputField = objectType.getFields()[node.name.value]; - if (inputField) { + if (inputField != null) { inputFieldType = inputField.type; } } diff --git a/src/utilities/buildClientSchema.ts b/src/utilities/buildClientSchema.ts index b2f6771c63..ab50728554 100644 --- a/src/utilities/buildClientSchema.ts +++ b/src/utilities/buildClientSchema.ts @@ -78,37 +78,42 @@ export function buildClientSchema( const schemaIntrospection = introspection.__schema; // Iterate through all types, getting the type definition for each. - const typeMap = keyValMap( - schemaIntrospection.types, - (typeIntrospection) => typeIntrospection.name, - (typeIntrospection) => buildType(typeIntrospection), + const typeMap = new Map( + schemaIntrospection.types.map((typeIntrospection) => [ + typeIntrospection.name, + buildType(typeIntrospection), + ]), ); // Include standard types only if they are used. for (const stdType of [...specifiedScalarTypes, ...introspectionTypes]) { - if (typeMap[stdType.name]) { - typeMap[stdType.name] = stdType; + if (typeMap.has(stdType.name)) { + typeMap.set(stdType.name, stdType); } } // Get the root Query, Mutation, and Subscription types. - const queryType = schemaIntrospection.queryType - ? getObjectType(schemaIntrospection.queryType) - : null; + const queryType = + schemaIntrospection.queryType != null + ? getObjectType(schemaIntrospection.queryType) + : null; - const mutationType = schemaIntrospection.mutationType - ? getObjectType(schemaIntrospection.mutationType) - : null; + const mutationType = + schemaIntrospection.mutationType != null + ? getObjectType(schemaIntrospection.mutationType) + : null; - const subscriptionType = schemaIntrospection.subscriptionType - ? getObjectType(schemaIntrospection.subscriptionType) - : null; + const subscriptionType = + schemaIntrospection.subscriptionType != null + ? getObjectType(schemaIntrospection.subscriptionType) + : null; // Get the directives supported by Introspection, assuming empty-set if // directives were not queried for. - const directives = schemaIntrospection.directives - ? schemaIntrospection.directives.map(buildDirective) - : []; + const directives = + schemaIntrospection.directives != null + ? schemaIntrospection.directives.map(buildDirective) + : []; // Then produce and return a Schema with these types. return new GraphQLSchema({ @@ -116,7 +121,7 @@ export function buildClientSchema( query: queryType, mutation: mutationType, subscription: subscriptionType, - types: Object.values(typeMap), + types: [...typeMap.values()], directives, assumeValid: options?.assumeValid, }); @@ -126,14 +131,14 @@ export function buildClientSchema( function getType(typeRef: IntrospectionTypeRef): GraphQLType { if (typeRef.kind === TypeKind.LIST) { const itemRef = typeRef.ofType; - if (!itemRef) { + if (itemRef == null) { throw new Error('Decorated type deeper than introspection query.'); } return new GraphQLList(getType(itemRef)); } if (typeRef.kind === TypeKind.NON_NULL) { const nullableRef = typeRef.ofType; - if (!nullableRef) { + if (nullableRef == null) { throw new Error('Decorated type deeper than introspection query.'); } const nullableType = getType(nullableRef); @@ -148,8 +153,8 @@ export function buildClientSchema( throw new Error(`Unknown type reference: ${inspect(typeRef)}.`); } - const type = typeMap[typeName]; - if (!type) { + const type = typeMap.get(typeName); + if (type == null) { throw new Error( `Invalid or incomplete schema, unknown type: ${typeName}. Ensure that a full introspection query is used in order to build a client schema.`, ); @@ -222,7 +227,7 @@ export function buildClientSchema( return []; } - if (!implementingIntrospection.interfaces) { + if (implementingIntrospection.interfaces == null) { const implementingIntrospectionStr = inspect(implementingIntrospection); throw new Error( `Introspection result missing interfaces: ${implementingIntrospectionStr}.`, @@ -257,7 +262,7 @@ export function buildClientSchema( function buildUnionDef( unionIntrospection: IntrospectionUnionType, ): GraphQLUnionType { - if (!unionIntrospection.possibleTypes) { + if (unionIntrospection.possibleTypes == null) { const unionIntrospectionStr = inspect(unionIntrospection); throw new Error( `Introspection result missing possibleTypes: ${unionIntrospectionStr}.`, @@ -273,7 +278,7 @@ export function buildClientSchema( function buildEnumDef( enumIntrospection: IntrospectionEnumType, ): GraphQLEnumType { - if (!enumIntrospection.enumValues) { + if (enumIntrospection.enumValues == null) { const enumIntrospectionStr = inspect(enumIntrospection); throw new Error( `Introspection result missing enumValues: ${enumIntrospectionStr}.`, @@ -296,7 +301,7 @@ export function buildClientSchema( function buildInputObjectDef( inputObjectIntrospection: IntrospectionInputObjectType, ): GraphQLInputObjectType { - if (!inputObjectIntrospection.inputFields) { + if (inputObjectIntrospection.inputFields == null) { const inputObjectIntrospectionStr = inspect(inputObjectIntrospection); throw new Error( `Introspection result missing inputFields: ${inputObjectIntrospectionStr}.`, @@ -312,7 +317,7 @@ export function buildClientSchema( function buildFieldDefMap( typeIntrospection: IntrospectionObjectType | IntrospectionInterfaceType, ): GraphQLFieldConfigMap { - if (!typeIntrospection.fields) { + if (typeIntrospection.fields == null) { throw new Error( `Introspection result missing fields: ${inspect(typeIntrospection)}.`, ); @@ -336,7 +341,7 @@ export function buildClientSchema( ); } - if (!fieldIntrospection.args) { + if (fieldIntrospection.args == null) { const fieldIntrospectionStr = inspect(fieldIntrospection); throw new Error( `Introspection result missing field args: ${fieldIntrospectionStr}.`, @@ -385,13 +390,13 @@ export function buildClientSchema( function buildDirective( directiveIntrospection: IntrospectionDirective, ): GraphQLDirective { - if (!directiveIntrospection.args) { + if (directiveIntrospection.args == null) { const directiveIntrospectionStr = inspect(directiveIntrospection); throw new Error( `Introspection result missing directive args: ${directiveIntrospectionStr}.`, ); } - if (!directiveIntrospection.locations) { + if (directiveIntrospection.locations == null) { const directiveIntrospectionStr = inspect(directiveIntrospection); throw new Error( `Introspection result missing directive locations: ${directiveIntrospectionStr}.`, diff --git a/src/utilities/coerceInputValue.ts b/src/utilities/coerceInputValue.ts index d1decf86a1..eebddcba83 100644 --- a/src/utilities/coerceInputValue.ts +++ b/src/utilities/coerceInputValue.ts @@ -127,7 +127,7 @@ function coerceInputValueImpl( // Ensure every provided field is defined. for (const fieldName of Object.keys(inputValue)) { - if (!fieldDefs[fieldName]) { + if (fieldDefs[fieldName] == null) { const suggestions = suggestionList( fieldName, Object.keys(type.getFields()), diff --git a/src/utilities/valueFromAST.ts b/src/utilities/valueFromAST.ts index 18981712f4..eeb538d2a8 100644 --- a/src/utilities/valueFromAST.ts +++ b/src/utilities/valueFromAST.ts @@ -1,6 +1,5 @@ import { inspect } from '../jsutils/inspect.js'; import { invariant } from '../jsutils/invariant.js'; -import { keyMap } from '../jsutils/keyMap.js'; import type { Maybe } from '../jsutils/Maybe.js'; import type { ObjMap } from '../jsutils/ObjMap.js'; @@ -108,10 +107,12 @@ export function valueFromAST( return; // Invalid: intentionally return no value. } const coercedObj = Object.create(null); - const fieldNodes = keyMap(valueNode.fields, (field) => field.name.value); + const fieldNodes = new Map( + valueNode.fields.map((field) => [field.name.value, field]), + ); for (const field of Object.values(type.getFields())) { - const fieldNode = fieldNodes[field.name]; - if (!fieldNode || isMissingVariable(fieldNode.value, variables)) { + const fieldNode = fieldNodes.get(field.name); + if (fieldNode == null || isMissingVariable(fieldNode.value, variables)) { if (field.defaultValue !== undefined) { coercedObj[field.name] = field.defaultValue; } else if (isNonNullType(field.type)) { diff --git a/src/validation/rules/DeferStreamDirectiveLabelRule.ts b/src/validation/rules/DeferStreamDirectiveLabelRule.ts index a0fc3cc424..45537af436 100644 --- a/src/validation/rules/DeferStreamDirectiveLabelRule.ts +++ b/src/validation/rules/DeferStreamDirectiveLabelRule.ts @@ -1,5 +1,6 @@ import { GraphQLError } from '../../error/GraphQLError.js'; +import type { DirectiveNode } from '../../language/ast.js'; import { Kind } from '../../language/kinds.js'; import type { ASTVisitor } from '../../language/visitor.js'; @@ -18,7 +19,7 @@ import type { ValidationContext } from '../ValidationContext.js'; export function DeferStreamDirectiveLabelRule( context: ValidationContext, ): ASTVisitor { - const knownLabels = Object.create(null); + const knownLabels = new Map(); return { Directive(node) { if ( @@ -39,15 +40,19 @@ export function DeferStreamDirectiveLabelRule( { nodes: node }, ), ); - } else if (knownLabels[labelValue.value]) { + return; + } + + const knownLabel = knownLabels.get(labelValue.value); + if (knownLabel != null) { context.reportError( new GraphQLError( 'Defer/Stream directive label argument must be unique.', - { nodes: [knownLabels[labelValue.value], node] }, + { nodes: [knownLabel, node] }, ), ); } else { - knownLabels[labelValue.value] = node; + knownLabels.set(labelValue.value, node); } } }, diff --git a/src/validation/rules/FieldsOnCorrectTypeRule.ts b/src/validation/rules/FieldsOnCorrectTypeRule.ts index a9bb26a827..5d61e08b03 100644 --- a/src/validation/rules/FieldsOnCorrectTypeRule.ts +++ b/src/validation/rules/FieldsOnCorrectTypeRule.ts @@ -85,7 +85,7 @@ function getSuggestedTypeNames( const suggestedTypes = new Set(); const usageCount = Object.create(null); for (const possibleType of schema.getPossibleTypes(type)) { - if (!possibleType.getFields()[fieldName]) { + if (possibleType.getFields()[fieldName] == null) { continue; } @@ -94,7 +94,7 @@ function getSuggestedTypeNames( usageCount[possibleType.name] = 1; for (const possibleInterface of possibleType.getInterfaces()) { - if (!possibleInterface.getFields()[fieldName]) { + if (possibleInterface.getFields()[fieldName] == null) { continue; } diff --git a/src/validation/rules/KnownArgumentNamesRule.ts b/src/validation/rules/KnownArgumentNamesRule.ts index d2668d40ef..2f09348778 100644 --- a/src/validation/rules/KnownArgumentNamesRule.ts +++ b/src/validation/rules/KnownArgumentNamesRule.ts @@ -53,14 +53,17 @@ export function KnownArgumentNamesRule(context: ValidationContext): ASTVisitor { export function KnownArgumentNamesOnDirectivesRule( context: ValidationContext | SDLValidationContext, ): ASTVisitor { - const directiveArgs = Object.create(null); + const directiveArgs = new Map>(); const schema = context.getSchema(); const definedDirectives = schema ? schema.getDirectives() : specifiedDirectives; for (const directive of definedDirectives) { - directiveArgs[directive.name] = directive.args.map((arg) => arg.name); + directiveArgs.set( + directive.name, + directive.args.map((arg) => arg.name), + ); } const astDefinitions = context.getDocument().definitions; @@ -70,16 +73,19 @@ export function KnownArgumentNamesOnDirectivesRule( /* c8 ignore next */ const argsNodes = def.arguments ?? []; - directiveArgs[def.name.value] = argsNodes.map((arg) => arg.name.value); + directiveArgs.set( + def.name.value, + argsNodes.map((arg) => arg.name.value), + ); } } return { Directive(directiveNode) { const directiveName = directiveNode.name.value; - const knownArgs = directiveArgs[directiveName]; + const knownArgs = directiveArgs.get(directiveName); - if (directiveNode.arguments && knownArgs) { + if (directiveNode.arguments != null && knownArgs != null) { for (const argNode of directiveNode.arguments) { const argName = argNode.name.value; if (!knownArgs.includes(argName)) { diff --git a/src/validation/rules/KnownDirectivesRule.ts b/src/validation/rules/KnownDirectivesRule.ts index 7170593ac6..a2c7ec81eb 100644 --- a/src/validation/rules/KnownDirectivesRule.ts +++ b/src/validation/rules/KnownDirectivesRule.ts @@ -27,29 +27,32 @@ import type { export function KnownDirectivesRule( context: ValidationContext | SDLValidationContext, ): ASTVisitor { - const locationsMap = Object.create(null); + const locationsMap = new Map>(); const schema = context.getSchema(); const definedDirectives = schema ? schema.getDirectives() : specifiedDirectives; for (const directive of definedDirectives) { - locationsMap[directive.name] = directive.locations; + locationsMap.set(directive.name, directive.locations); } const astDefinitions = context.getDocument().definitions; for (const def of astDefinitions) { if (def.kind === Kind.DIRECTIVE_DEFINITION) { - locationsMap[def.name.value] = def.locations.map((name) => name.value); + locationsMap.set( + def.name.value, + def.locations.map((name) => name.value), + ); } } return { Directive(node, _key, _parent, _path, ancestors) { const name = node.name.value; - const locations = locationsMap[name]; + const locations = locationsMap.get(name); - if (!locations) { + if (locations == null) { context.reportError( new GraphQLError(`Unknown directive "@${name}".`, { nodes: node }), ); @@ -57,7 +60,7 @@ export function KnownDirectivesRule( } const candidateLocation = getDirectiveLocationForASTPath(ancestors); - if (candidateLocation && !locations.includes(candidateLocation)) { + if (candidateLocation != null && !locations.includes(candidateLocation)) { context.reportError( new GraphQLError( `Directive "@${name}" may not be used on ${candidateLocation}.`, diff --git a/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts b/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts index e2444047c6..185648fe29 100644 --- a/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts +++ b/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts @@ -1,6 +1,5 @@ import { inspect } from '../../jsutils/inspect.js'; import type { Maybe } from '../../jsutils/Maybe.js'; -import type { ObjMap } from '../../jsutils/ObjMap.js'; import { GraphQLError } from '../../error/GraphQLError.js'; @@ -107,7 +106,7 @@ type NodeAndDef = [ Maybe>, ]; // Map of array of those. -type NodeAndDefCollection = ObjMap>; +type NodeAndDefCollection = Map>; type FragmentNames = ReadonlyArray; type FieldsAndFragmentNames = readonly [NodeAndDefCollection, FragmentNames]; @@ -485,7 +484,7 @@ function collectConflictsWithin( // name and the value at that key is a list of all fields which provide that // response name. For every response name, if there are multiple fields, they // must be compared to find a potential conflict. - for (const [responseName, fields] of Object.entries(fieldMap)) { + for (const [responseName, fields] of fieldMap.entries()) { // This compares every field in the list to every other field in this list // (except to itself). If the list only has one item, nothing needs to // be compared. @@ -529,9 +528,9 @@ function collectConflictsBetween( // response name. For any response name which appears in both provided field // maps, each field from the first field map must be compared to every field // in the second field map to find potential conflicts. - for (const [responseName, fields1] of Object.entries(fieldMap1)) { - const fields2 = fieldMap2[responseName]; - if (fields2) { + for (const [responseName, fields1] of fieldMap1.entries()) { + const fields2 = fieldMap2.get(responseName); + if (fields2 != null) { for (const field1 of fields1) { for (const field2 of fields2) { const conflict = findConflict( @@ -730,7 +729,7 @@ function getFieldsAndFragmentNames( if (cached) { return cached; } - const nodeAndDefs: NodeAndDefCollection = Object.create(null); + const nodeAndDefs: NodeAndDefCollection = new Map(); const fragmentNames = new Set(); _collectFieldsAndFragmentNames( context, @@ -784,10 +783,13 @@ function _collectFieldsAndFragmentNames( const responseName = selection.alias ? selection.alias.value : fieldName; - if (!nodeAndDefs[responseName]) { - nodeAndDefs[responseName] = []; + + let nodeAndDefsList = nodeAndDefs.get(responseName); + if (nodeAndDefsList == null) { + nodeAndDefsList = []; + nodeAndDefs.set(responseName, nodeAndDefsList); } - nodeAndDefs[responseName].push([parentType, selection, fieldDef]); + nodeAndDefsList.push([parentType, selection, fieldDef]); break; } case Kind.FRAGMENT_SPREAD: diff --git a/src/validation/rules/PossibleTypeExtensionsRule.ts b/src/validation/rules/PossibleTypeExtensionsRule.ts index dfc7889895..d9ccb73cfa 100644 --- a/src/validation/rules/PossibleTypeExtensionsRule.ts +++ b/src/validation/rules/PossibleTypeExtensionsRule.ts @@ -1,12 +1,14 @@ import { didYouMean } from '../../jsutils/didYouMean.js'; import { inspect } from '../../jsutils/inspect.js'; import { invariant } from '../../jsutils/invariant.js'; -import type { ObjMap } from '../../jsutils/ObjMap.js'; import { suggestionList } from '../../jsutils/suggestionList.js'; import { GraphQLError } from '../../error/GraphQLError.js'; -import type { DefinitionNode, TypeExtensionNode } from '../../language/ast.js'; +import type { + TypeDefinitionNode, + TypeExtensionNode, +} from '../../language/ast.js'; import { Kind } from '../../language/kinds.js'; import { isTypeDefinitionNode } from '../../language/predicates.js'; import type { ASTVisitor } from '../../language/visitor.js'; @@ -32,11 +34,11 @@ export function PossibleTypeExtensionsRule( context: SDLValidationContext, ): ASTVisitor { const schema = context.getSchema(); - const definedTypes: ObjMap = Object.create(null); + const definedTypes = new Map(); for (const def of context.getDocument().definitions) { if (isTypeDefinitionNode(def)) { - definedTypes[def.name.value] = def; + definedTypes.set(def.name.value, def); } } @@ -51,17 +53,17 @@ export function PossibleTypeExtensionsRule( function checkExtension(node: TypeExtensionNode): void { const typeName = node.name.value; - const defNode = definedTypes[typeName]; + const defNode = definedTypes.get(typeName); const existingType = schema?.getType(typeName); let expectedKind: Kind | undefined; - if (defNode) { + if (defNode != null) { expectedKind = defKindToExtKind[defNode.kind]; } else if (existingType) { expectedKind = typeToExtKind(existingType); } - if (expectedKind) { + if (expectedKind != null) { if (expectedKind !== node.kind) { const kindStr = extensionKindToTypeName(node.kind); context.reportError( @@ -71,10 +73,10 @@ export function PossibleTypeExtensionsRule( ); } } else { - const allTypeNames = Object.keys({ - ...definedTypes, - ...schema?.getTypeMap(), - }); + const allTypeNames = [ + ...definedTypes.keys(), + ...Object.keys(schema?.getTypeMap() ?? {}), + ]; const suggestedTypes = suggestionList(typeName, allTypeNames); context.reportError( @@ -88,14 +90,14 @@ export function PossibleTypeExtensionsRule( } } -const defKindToExtKind: ObjMap = { +const defKindToExtKind = { [Kind.SCALAR_TYPE_DEFINITION]: Kind.SCALAR_TYPE_EXTENSION, [Kind.OBJECT_TYPE_DEFINITION]: Kind.OBJECT_TYPE_EXTENSION, [Kind.INTERFACE_TYPE_DEFINITION]: Kind.INTERFACE_TYPE_EXTENSION, [Kind.UNION_TYPE_DEFINITION]: Kind.UNION_TYPE_EXTENSION, [Kind.ENUM_TYPE_DEFINITION]: Kind.ENUM_TYPE_EXTENSION, [Kind.INPUT_OBJECT_TYPE_DEFINITION]: Kind.INPUT_OBJECT_TYPE_EXTENSION, -}; +} as const; function typeToExtKind(type: GraphQLNamedType): Kind { if (isScalarType(type)) { diff --git a/src/validation/rules/ProvidedRequiredArgumentsRule.ts b/src/validation/rules/ProvidedRequiredArgumentsRule.ts index 350264496f..72d104b852 100644 --- a/src/validation/rules/ProvidedRequiredArgumentsRule.ts +++ b/src/validation/rules/ProvidedRequiredArgumentsRule.ts @@ -1,6 +1,4 @@ import { inspect } from '../../jsutils/inspect.js'; -import { keyMap } from '../../jsutils/keyMap.js'; -import type { ObjMap } from '../../jsutils/ObjMap.js'; import { GraphQLError } from '../../error/GraphQLError.js'; @@ -65,16 +63,19 @@ export function ProvidedRequiredArgumentsRule( export function ProvidedRequiredArgumentsOnDirectivesRule( context: ValidationContext | SDLValidationContext, ): ASTVisitor { - const requiredArgsMap: ObjMap< - ObjMap - > = Object.create(null); + const requiredArgsMap = new Map< + string, + Map + >(); const schema = context.getSchema(); const definedDirectives = schema?.getDirectives() ?? specifiedDirectives; for (const directive of definedDirectives) { - requiredArgsMap[directive.name] = keyMap( - directive.args.filter(isRequiredArgument), - (arg) => arg.name, + requiredArgsMap.set( + directive.name, + new Map( + directive.args.filter(isRequiredArgument).map((arg) => [arg.name, arg]), + ), ); } @@ -85,9 +86,13 @@ export function ProvidedRequiredArgumentsOnDirectivesRule( /* c8 ignore next */ const argNodes = def.arguments ?? []; - requiredArgsMap[def.name.value] = keyMap( - argNodes.filter(isRequiredArgumentNode), - (arg) => arg.name.value, + requiredArgsMap.set( + def.name.value, + new Map( + argNodes + .filter(isRequiredArgumentNode) + .map((arg) => [arg.name.value, arg]), + ), ); } } @@ -97,13 +102,13 @@ export function ProvidedRequiredArgumentsOnDirectivesRule( // Validate on leave to allow for deeper errors to appear first. leave(directiveNode) { const directiveName = directiveNode.name.value; - const requiredArgs = requiredArgsMap[directiveName]; - if (requiredArgs) { + const requiredArgs = requiredArgsMap.get(directiveName); + if (requiredArgs != null) { // FIXME: https://github.com/graphql/graphql-js/issues/2203 /* c8 ignore next */ const argNodes = directiveNode.arguments ?? []; const argNodeMap = new Set(argNodes.map((arg) => arg.name.value)); - for (const [argName, argDef] of Object.entries(requiredArgs)) { + for (const [argName, argDef] of requiredArgs.entries()) { if (!argNodeMap.has(argName)) { const argType = isType(argDef.type) ? inspect(argDef.type) diff --git a/src/validation/rules/UniqueDirectiveNamesRule.ts b/src/validation/rules/UniqueDirectiveNamesRule.ts index 9c845ffe8e..d0decd2ac0 100644 --- a/src/validation/rules/UniqueDirectiveNamesRule.ts +++ b/src/validation/rules/UniqueDirectiveNamesRule.ts @@ -1,5 +1,6 @@ import { GraphQLError } from '../../error/GraphQLError.js'; +import type { NameNode } from '../../language/ast.js'; import type { ASTVisitor } from '../../language/visitor.js'; import type { SDLValidationContext } from '../ValidationContext.js'; @@ -12,7 +13,7 @@ import type { SDLValidationContext } from '../ValidationContext.js'; export function UniqueDirectiveNamesRule( context: SDLValidationContext, ): ASTVisitor { - const knownDirectiveNames = Object.create(null); + const knownDirectiveNames = new Map(); const schema = context.getSchema(); return { @@ -29,15 +30,16 @@ export function UniqueDirectiveNamesRule( return; } - if (knownDirectiveNames[directiveName]) { + const knownName = knownDirectiveNames.get(directiveName); + if (knownName) { context.reportError( new GraphQLError( `There can be only one directive named "@${directiveName}".`, - { nodes: [knownDirectiveNames[directiveName], node.name] }, + { nodes: [knownName, node.name] }, ), ); } else { - knownDirectiveNames[directiveName] = node.name; + knownDirectiveNames.set(directiveName, node.name); } return false; diff --git a/src/validation/rules/UniqueDirectivesPerLocationRule.ts b/src/validation/rules/UniqueDirectivesPerLocationRule.ts index 5b62c651c4..795448488d 100644 --- a/src/validation/rules/UniqueDirectivesPerLocationRule.ts +++ b/src/validation/rules/UniqueDirectivesPerLocationRule.ts @@ -1,5 +1,6 @@ import { GraphQLError } from '../../error/GraphQLError.js'; +import type { DirectiveNode } from '../../language/ast.js'; import { Kind } from '../../language/kinds.js'; import { isTypeDefinitionNode, @@ -25,25 +26,25 @@ import type { export function UniqueDirectivesPerLocationRule( context: ValidationContext | SDLValidationContext, ): ASTVisitor { - const uniqueDirectiveMap = Object.create(null); + const uniqueDirectiveMap = new Map(); const schema = context.getSchema(); const definedDirectives = schema ? schema.getDirectives() : specifiedDirectives; for (const directive of definedDirectives) { - uniqueDirectiveMap[directive.name] = !directive.isRepeatable; + uniqueDirectiveMap.set(directive.name, !directive.isRepeatable); } const astDefinitions = context.getDocument().definitions; for (const def of astDefinitions) { if (def.kind === Kind.DIRECTIVE_DEFINITION) { - uniqueDirectiveMap[def.name.value] = !def.repeatable; + uniqueDirectiveMap.set(def.name.value, !def.repeatable); } } - const schemaDirectives = Object.create(null); - const typeDirectivesMap = Object.create(null); + const schemaDirectives = new Map(); + const typeDirectivesMap = new Map>(); return { // Many different AST nodes may contain directives. Rather than listing @@ -62,27 +63,29 @@ export function UniqueDirectivesPerLocationRule( seenDirectives = schemaDirectives; } else if (isTypeDefinitionNode(node) || isTypeExtensionNode(node)) { const typeName = node.name.value; - seenDirectives = typeDirectivesMap[typeName]; + seenDirectives = typeDirectivesMap.get(typeName); if (seenDirectives === undefined) { - typeDirectivesMap[typeName] = seenDirectives = Object.create(null); + seenDirectives = new Map(); + typeDirectivesMap.set(typeName, seenDirectives); } } else { - seenDirectives = Object.create(null); + seenDirectives = new Map(); } for (const directive of node.directives) { const directiveName = directive.name.value; - if (uniqueDirectiveMap[directiveName]) { - if (seenDirectives[directiveName]) { + if (uniqueDirectiveMap.get(directiveName) === true) { + const seenDirective = seenDirectives.get(directiveName); + if (seenDirective != null) { context.reportError( new GraphQLError( `The directive "@${directiveName}" can only be used once at this location.`, - { nodes: [seenDirectives[directiveName], directive] }, + { nodes: [seenDirective, directive] }, ), ); } else { - seenDirectives[directiveName] = directive; + seenDirectives.set(directiveName, directive); } } } diff --git a/src/validation/rules/UniqueEnumValueNamesRule.ts b/src/validation/rules/UniqueEnumValueNamesRule.ts index 00a1d6a63f..8342880f82 100644 --- a/src/validation/rules/UniqueEnumValueNamesRule.ts +++ b/src/validation/rules/UniqueEnumValueNamesRule.ts @@ -3,6 +3,7 @@ import { GraphQLError } from '../../error/GraphQLError.js'; import type { EnumTypeDefinitionNode, EnumTypeExtensionNode, + NameNode, } from '../../language/ast.js'; import type { ASTVisitor } from '../../language/visitor.js'; @@ -20,7 +21,7 @@ export function UniqueEnumValueNamesRule( ): ASTVisitor { const schema = context.getSchema(); const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null); - const knownValueNames = Object.create(null); + const knownValueNames = new Map>(); return { EnumTypeDefinition: checkValueUniqueness, @@ -32,14 +33,15 @@ export function UniqueEnumValueNamesRule( ) { const typeName = node.name.value; - if (!knownValueNames[typeName]) { - knownValueNames[typeName] = Object.create(null); + let valueNames = knownValueNames.get(typeName); + if (valueNames == null) { + valueNames = new Map(); + knownValueNames.set(typeName, valueNames); } // FIXME: https://github.com/graphql/graphql-js/issues/2203 /* c8 ignore next */ const valueNodes = node.values ?? []; - const valueNames = knownValueNames[typeName]; for (const valueDef of valueNodes) { const valueName = valueDef.name.value; @@ -52,15 +54,19 @@ export function UniqueEnumValueNamesRule( { nodes: valueDef.name }, ), ); - } else if (valueNames[valueName]) { + continue; + } + + const knownValueName = valueNames.get(valueName); + if (knownValueName != null) { context.reportError( new GraphQLError( `Enum value "${typeName}.${valueName}" can only be defined once.`, - { nodes: [valueNames[valueName], valueDef.name] }, + { nodes: [knownValueName, valueDef.name] }, ), ); } else { - valueNames[valueName] = valueDef.name; + valueNames.set(valueName, valueDef.name); } } diff --git a/src/validation/rules/UniqueFieldDefinitionNamesRule.ts b/src/validation/rules/UniqueFieldDefinitionNamesRule.ts index 96d88a4afa..e776bb4d06 100644 --- a/src/validation/rules/UniqueFieldDefinitionNamesRule.ts +++ b/src/validation/rules/UniqueFieldDefinitionNamesRule.ts @@ -26,7 +26,7 @@ export function UniqueFieldDefinitionNamesRule( ): ASTVisitor { const schema = context.getSchema(); const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null); - const knownFieldNames = Object.create(null); + const knownFieldNames = new Map>(); return { InputObjectTypeDefinition: checkFieldUniqueness, @@ -45,14 +45,15 @@ export function UniqueFieldDefinitionNamesRule( }) { const typeName = node.name.value; - if (!knownFieldNames[typeName]) { - knownFieldNames[typeName] = Object.create(null); + let fieldNames = knownFieldNames.get(typeName); + if (fieldNames == null) { + fieldNames = new Map(); + knownFieldNames.set(typeName, fieldNames); } // FIXME: https://github.com/graphql/graphql-js/issues/2203 /* c8 ignore next */ const fieldNodes = node.fields ?? []; - const fieldNames = knownFieldNames[typeName]; for (const fieldDef of fieldNodes) { const fieldName = fieldDef.name.value; @@ -64,15 +65,19 @@ export function UniqueFieldDefinitionNamesRule( { nodes: fieldDef.name }, ), ); - } else if (fieldNames[fieldName]) { + continue; + } + + const knownFieldName = fieldNames.get(fieldName); + if (knownFieldName != null) { context.reportError( new GraphQLError( `Field "${typeName}.${fieldName}" can only be defined once.`, - { nodes: [fieldNames[fieldName], fieldDef.name] }, + { nodes: [knownFieldName, fieldDef.name] }, ), ); } else { - fieldNames[fieldName] = fieldDef.name; + fieldNames.set(fieldName, fieldDef.name); } } diff --git a/src/validation/rules/UniqueFragmentNamesRule.ts b/src/validation/rules/UniqueFragmentNamesRule.ts index db59b47396..72167bda92 100644 --- a/src/validation/rules/UniqueFragmentNamesRule.ts +++ b/src/validation/rules/UniqueFragmentNamesRule.ts @@ -1,5 +1,6 @@ import { GraphQLError } from '../../error/GraphQLError.js'; +import type { NameNode } from '../../language/ast.js'; import type { ASTVisitor } from '../../language/visitor.js'; import type { ASTValidationContext } from '../ValidationContext.js'; @@ -14,20 +15,21 @@ import type { ASTValidationContext } from '../ValidationContext.js'; export function UniqueFragmentNamesRule( context: ASTValidationContext, ): ASTVisitor { - const knownFragmentNames = Object.create(null); + const knownFragmentNames = new Map(); return { OperationDefinition: () => false, FragmentDefinition(node) { const fragmentName = node.name.value; - if (knownFragmentNames[fragmentName]) { + const knownFragmentName = knownFragmentNames.get(fragmentName); + if (knownFragmentName != null) { context.reportError( new GraphQLError( `There can be only one fragment named "${fragmentName}".`, - { nodes: [knownFragmentNames[fragmentName], node.name] }, + { nodes: [knownFragmentName, node.name] }, ), ); } else { - knownFragmentNames[fragmentName] = node.name; + knownFragmentNames.set(fragmentName, node.name); } return false; }, diff --git a/src/validation/rules/UniqueInputFieldNamesRule.ts b/src/validation/rules/UniqueInputFieldNamesRule.ts index 7344e9439b..a7a9dfad10 100644 --- a/src/validation/rules/UniqueInputFieldNamesRule.ts +++ b/src/validation/rules/UniqueInputFieldNamesRule.ts @@ -1,5 +1,4 @@ import { invariant } from '../../jsutils/invariant.js'; -import type { ObjMap } from '../../jsutils/ObjMap.js'; import { GraphQLError } from '../../error/GraphQLError.js'; @@ -19,14 +18,14 @@ import type { ASTValidationContext } from '../ValidationContext.js'; export function UniqueInputFieldNamesRule( context: ASTValidationContext, ): ASTVisitor { - const knownNameStack: Array> = []; - let knownNames: ObjMap = Object.create(null); + const knownNameStack: Array> = []; + let knownNames = new Map(); return { ObjectValue: { enter() { knownNameStack.push(knownNames); - knownNames = Object.create(null); + knownNames = new Map(); }, leave() { const prevKnownNames = knownNameStack.pop(); @@ -36,15 +35,16 @@ export function UniqueInputFieldNamesRule( }, ObjectField(node) { const fieldName = node.name.value; - if (knownNames[fieldName]) { + const knownName = knownNames.get(fieldName); + if (knownName != null) { context.reportError( new GraphQLError( `There can be only one input field named "${fieldName}".`, - { nodes: [knownNames[fieldName], node.name] }, + { nodes: [knownName, node.name] }, ), ); } else { - knownNames[fieldName] = node.name; + knownNames.set(fieldName, node.name); } }, }; diff --git a/src/validation/rules/UniqueOperationNamesRule.ts b/src/validation/rules/UniqueOperationNamesRule.ts index eb1580423e..f5a5f06d11 100644 --- a/src/validation/rules/UniqueOperationNamesRule.ts +++ b/src/validation/rules/UniqueOperationNamesRule.ts @@ -1,5 +1,6 @@ import { GraphQLError } from '../../error/GraphQLError.js'; +import type { NameNode } from '../../language/ast.js'; import type { ASTVisitor } from '../../language/visitor.js'; import type { ASTValidationContext } from '../ValidationContext.js'; @@ -14,25 +15,21 @@ import type { ASTValidationContext } from '../ValidationContext.js'; export function UniqueOperationNamesRule( context: ASTValidationContext, ): ASTVisitor { - const knownOperationNames = Object.create(null); + const knownOperationNames = new Map(); return { OperationDefinition(node) { const operationName = node.name; - if (operationName) { - if (knownOperationNames[operationName.value]) { + if (operationName != null) { + const knownOperationName = knownOperationNames.get(operationName.value); + if (knownOperationName != null) { context.reportError( new GraphQLError( `There can be only one operation named "${operationName.value}".`, - { - nodes: [ - knownOperationNames[operationName.value], - operationName, - ], - }, + { nodes: [knownOperationName, operationName] }, ), ); } else { - knownOperationNames[operationName.value] = operationName; + knownOperationNames.set(operationName.value, operationName); } } return false; diff --git a/src/validation/rules/UniqueOperationTypesRule.ts b/src/validation/rules/UniqueOperationTypesRule.ts index 40fa71d11e..51e4cfe6b1 100644 --- a/src/validation/rules/UniqueOperationTypesRule.ts +++ b/src/validation/rules/UniqueOperationTypesRule.ts @@ -1,6 +1,7 @@ import { GraphQLError } from '../../error/GraphQLError.js'; import type { + OperationTypeDefinitionNode, SchemaDefinitionNode, SchemaExtensionNode, } from '../../language/ast.js'; @@ -17,7 +18,7 @@ export function UniqueOperationTypesRule( context: SDLValidationContext, ): ASTVisitor { const schema = context.getSchema(); - const definedOperationTypes = Object.create(null); + const definedOperationTypes = new Map(); const existingOperationTypes = schema ? { query: schema.getQueryType(), @@ -40,7 +41,7 @@ export function UniqueOperationTypesRule( for (const operationType of operationTypesNodes) { const operation = operationType.operation; - const alreadyDefinedOperationType = definedOperationTypes[operation]; + const alreadyDefinedOperationType = definedOperationTypes.get(operation); if (existingOperationTypes[operation]) { context.reportError( @@ -57,7 +58,7 @@ export function UniqueOperationTypesRule( ), ); } else { - definedOperationTypes[operation] = operationType; + definedOperationTypes.set(operation, operationType); } } diff --git a/src/validation/rules/UniqueTypeNamesRule.ts b/src/validation/rules/UniqueTypeNamesRule.ts index 272681835d..fe52b9a3aa 100644 --- a/src/validation/rules/UniqueTypeNamesRule.ts +++ b/src/validation/rules/UniqueTypeNamesRule.ts @@ -1,6 +1,6 @@ import { GraphQLError } from '../../error/GraphQLError.js'; -import type { TypeDefinitionNode } from '../../language/ast.js'; +import type { NameNode, TypeDefinitionNode } from '../../language/ast.js'; import type { ASTVisitor } from '../../language/visitor.js'; import type { SDLValidationContext } from '../ValidationContext.js'; @@ -11,7 +11,7 @@ import type { SDLValidationContext } from '../ValidationContext.js'; * A GraphQL document is only valid if all defined types have unique names. */ export function UniqueTypeNamesRule(context: SDLValidationContext): ASTVisitor { - const knownTypeNames = Object.create(null); + const knownTypeNames = new Map(); const schema = context.getSchema(); return { @@ -36,14 +36,15 @@ export function UniqueTypeNamesRule(context: SDLValidationContext): ASTVisitor { return; } - if (knownTypeNames[typeName]) { + const knownNameNode = knownTypeNames.get(typeName); + if (knownNameNode != null) { context.reportError( new GraphQLError(`There can be only one type named "${typeName}".`, { - nodes: [knownTypeNames[typeName], node.name], + nodes: [knownNameNode, node.name], }), ); } else { - knownTypeNames[typeName] = node.name; + knownTypeNames.set(typeName, node.name); } return false; diff --git a/src/validation/rules/ValuesOfCorrectTypeRule.ts b/src/validation/rules/ValuesOfCorrectTypeRule.ts index cddc5ed979..09a7316787 100644 --- a/src/validation/rules/ValuesOfCorrectTypeRule.ts +++ b/src/validation/rules/ValuesOfCorrectTypeRule.ts @@ -1,6 +1,5 @@ import { didYouMean } from '../../jsutils/didYouMean.js'; import { inspect } from '../../jsutils/inspect.js'; -import { keyMap } from '../../jsutils/keyMap.js'; import { suggestionList } from '../../jsutils/suggestionList.js'; import { GraphQLError } from '../../error/GraphQLError.js'; @@ -49,9 +48,11 @@ export function ValuesOfCorrectTypeRule( return false; // Don't traverse further. } // Ensure every required field exists. - const fieldNodeMap = keyMap(node.fields, (field) => field.name.value); + const fieldNodeMap = new Map( + node.fields.map((field) => [field.name.value, field]), + ); for (const fieldDef of Object.values(type.getFields())) { - const fieldNode = fieldNodeMap[fieldDef.name]; + const fieldNode = fieldNodeMap.get(fieldDef.name); if (!fieldNode && isRequiredInputField(fieldDef)) { const typeStr = inspect(fieldDef.type); context.reportError( diff --git a/src/validation/rules/VariablesInAllowedPositionRule.ts b/src/validation/rules/VariablesInAllowedPositionRule.ts index 4039540eba..e662ba443c 100644 --- a/src/validation/rules/VariablesInAllowedPositionRule.ts +++ b/src/validation/rules/VariablesInAllowedPositionRule.ts @@ -3,7 +3,7 @@ import type { Maybe } from '../../jsutils/Maybe.js'; import { GraphQLError } from '../../error/GraphQLError.js'; -import type { ValueNode } from '../../language/ast.js'; +import type { ValueNode, VariableDefinitionNode } from '../../language/ast.js'; import { Kind } from '../../language/kinds.js'; import type { ASTVisitor } from '../../language/visitor.js'; @@ -26,19 +26,19 @@ import type { ValidationContext } from '../ValidationContext.js'; export function VariablesInAllowedPositionRule( context: ValidationContext, ): ASTVisitor { - let varDefMap = Object.create(null); + let varDefMap: Map; return { OperationDefinition: { enter() { - varDefMap = Object.create(null); + varDefMap = new Map(); }, leave(operation) { const usages = context.getRecursiveVariableUsages(operation); for (const { node, type, defaultValue } of usages) { const varName = node.name.value; - const varDef = varDefMap[varName]; + const varDef = varDefMap.get(varName); if (varDef && type) { // A var type is allowed if it is the same or more strict (e.g. is // a subtype of) than the expected type. It can be more strict if @@ -71,7 +71,7 @@ export function VariablesInAllowedPositionRule( }, }, VariableDefinition(node) { - varDefMap[node.variable.name.value] = node; + varDefMap.set(node.variable.name.value, node); }, }; }