diff --git a/src/index.ts b/src/index.ts index 1f80cf51f3..0634f69791 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,24 +34,23 @@ export type { GraphQLArgs } from './graphql.js'; export { graphql, graphqlSync } from './graphql.js'; // Create and operate on GraphQL type definitions and schema. -export type { - GraphQLField, - GraphQLArgument, - GraphQLEnumValue, - GraphQLInputField, -} from './type/index.js'; export { resolveObjMapThunk, resolveReadonlyArrayThunk, // Definitions GraphQLSchema, GraphQLDirective, + GraphQLSchemaElement, GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, + GraphQLField, + GraphQLArgument, + GraphQLEnumValue, + GraphQLInputField, GraphQLList, GraphQLNonNull, // Standard GraphQL Scalars diff --git a/src/type/__tests__/definition-test.ts b/src/type/__tests__/definition-test.ts index 8d9eeb4044..b997e310c9 100644 --- a/src/type/__tests__/definition-test.ts +++ b/src/type/__tests__/definition-test.ts @@ -329,6 +329,7 @@ describe('Type System: Objects', () => { }), }); expect(objType.getFields().f).to.deep.include({ + coordinate: 'SomeObject.f', parentType: objType, name: 'f', description: undefined, @@ -358,6 +359,7 @@ describe('Type System: Objects', () => { const f = objType.getFields().f; expect(f).to.deep.include({ + coordinate: 'SomeObject.f', parentType: objType, name: 'f', description: undefined, @@ -372,6 +374,7 @@ describe('Type System: Objects', () => { expect(f.args).to.have.lengthOf(1); expect(f.args[0]).to.deep.include({ + coordinate: 'SomeObject.f(arg:)', parent: f, name: 'arg', description: undefined, @@ -736,6 +739,8 @@ describe('Type System: Enums', () => { expect(values).to.have.lengthOf(3); expect(values[0]).to.deep.include({ + coordinate: 'EnumWithNullishValue.NULL', + parentEnum: EnumTypeWithNullishValue, name: 'NULL', description: undefined, value: null, @@ -745,6 +750,8 @@ describe('Type System: Enums', () => { }); expect(values[1]).to.deep.include({ + coordinate: 'EnumWithNullishValue.NAN', + parentEnum: EnumTypeWithNullishValue, name: 'NAN', description: undefined, value: NaN, @@ -754,6 +761,8 @@ describe('Type System: Enums', () => { }); expect(values[2]).to.deep.include({ + coordinate: 'EnumWithNullishValue.NO_CUSTOM_VALUE', + parentEnum: EnumTypeWithNullishValue, name: 'NO_CUSTOM_VALUE', description: undefined, value: 'NO_CUSTOM_VALUE', @@ -856,6 +865,7 @@ describe('Type System: Input Objects', () => { }, }); expect(inputObjType.getFields().f).to.deep.include({ + coordinate: 'SomeInputObject.f', parentType: inputObjType, name: 'f', description: undefined, @@ -876,6 +886,7 @@ describe('Type System: Input Objects', () => { }), }); expect(inputObjType.getFields().f).to.deep.include({ + coordinate: 'SomeInputObject.f', parentType: inputObjType, name: 'f', description: undefined, diff --git a/src/type/__tests__/directive-test.ts b/src/type/__tests__/directive-test.ts index 90510bd0f9..fa5f9c8b2a 100644 --- a/src/type/__tests__/directive-test.ts +++ b/src/type/__tests__/directive-test.ts @@ -40,6 +40,7 @@ describe('Type System: Directive', () => { expect(directive.args).to.have.lengthOf(2); expect(directive.args[0]).to.deep.include({ + coordinate: '@Foo(foo:)', parent: directive, name: 'foo', description: undefined, @@ -52,6 +53,7 @@ describe('Type System: Directive', () => { }); expect(directive.args[1]).to.deep.include({ + coordinate: '@Foo(bar:)', parent: directive, name: 'bar', description: undefined, diff --git a/src/type/__tests__/enumType-test.ts b/src/type/__tests__/enumType-test.ts index 007b00a78e..5dba442dc6 100644 --- a/src/type/__tests__/enumType-test.ts +++ b/src/type/__tests__/enumType-test.ts @@ -398,6 +398,7 @@ describe('Type System: Enum Values', () => { expect(values[0]).to.deep.include({ parentEnum: ComplexEnum, + coordinate: 'Complex.ONE', name: 'ONE', description: undefined, value: Complex1, @@ -408,6 +409,7 @@ describe('Type System: Enum Values', () => { expect(values[1]).to.deep.include({ parentEnum: ComplexEnum, + coordinate: 'Complex.TWO', name: 'TWO', description: undefined, value: Complex2, diff --git a/src/type/definition.ts b/src/type/definition.ts index ea96be5153..1fa5f45961 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -365,9 +365,7 @@ export function assertAbstractType(type: unknown): GraphQLAbstractType { * }) * ``` */ -export class GraphQLList - implements GraphQLSchemaElement -{ +export class GraphQLList { readonly ofType: T; constructor(ofType: T) { @@ -408,9 +406,7 @@ export class GraphQLList * ``` * Note: the enforcement of non-nullability occurs within the executor. */ -export class GraphQLNonNull - implements GraphQLSchemaElement -{ +export class GraphQLNonNull { readonly ofType: T; constructor(ofType: T) { @@ -535,12 +531,28 @@ export function getNamedType( } /** - * An interface for all Schema Elements. + * The base class for all Schema Elements. */ +export class GraphQLSchemaElement { + readonly coordinate: string; -export interface GraphQLSchemaElement { - toString: () => string; - toJSON: () => string; + constructor(coordinate: string) { + this.coordinate = coordinate; + } + + // TODO: add coverage + /* c8 ignore next 3*/ + get [Symbol.toStringTag]() { + return 'GraphQLSchemaElement'; + } + + toString(): string { + return this.coordinate; + } + + toJSON(): string { + return this.toString(); + } } /** @@ -648,9 +660,10 @@ export interface GraphQLScalarTypeExtensions { * `coerceInputLiteral()` method. * */ -export class GraphQLScalarType - implements GraphQLSchemaElement -{ +export class GraphQLScalarType< + TInternal = unknown, + TExternal = TInternal, +> extends GraphQLSchemaElement { name: string; description: Maybe; specifiedByURL: Maybe; @@ -669,6 +682,8 @@ export class GraphQLScalarType extensionASTNodes: ReadonlyArray; constructor(config: Readonly>) { + const coordinate = config.name; + super(coordinate); this.name = assertName(config.name); this.description = config.description; this.specifiedByURL = config.specifiedByURL; @@ -709,7 +724,7 @@ export class GraphQLScalarType } } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLScalarType'; } @@ -730,14 +745,6 @@ export class GraphQLScalarType extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } /* @deprecated in favor of GraphQLScalarOutputValueCoercer, will be removed in v18 */ @@ -866,9 +873,10 @@ export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> { * }); * ``` */ -export class GraphQLObjectType - implements GraphQLSchemaElement -{ +export class GraphQLObjectType< + TSource = any, + TContext = any, +> extends GraphQLSchemaElement { name: string; description: Maybe; isTypeOf: Maybe>; @@ -880,6 +888,8 @@ export class GraphQLObjectType private _interfaces: ThunkReadonlyArray; constructor(config: Readonly>) { + const coordinate = config.name; + super(coordinate); this.name = assertName(config.name); this.description = config.description; this.isTypeOf = config.isTypeOf; @@ -894,7 +904,7 @@ export class GraphQLObjectType this._interfaces = defineInterfaces.bind(undefined, config.interfaces); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLObjectType'; } @@ -924,14 +934,6 @@ export class GraphQLObjectType extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } function defineInterfaces( @@ -1090,9 +1092,11 @@ export type GraphQLFieldNormalizedConfigMap = ObjMap< GraphQLFieldNormalizedConfig >; -export class GraphQLField - implements GraphQLSchemaElement -{ +export class GraphQLField< + TSource = any, + TContext = any, + TArgs = any, +> extends GraphQLSchemaElement { parentType: | GraphQLObjectType | GraphQLInterfaceType @@ -1115,6 +1119,8 @@ export class GraphQLField name: string, config: GraphQLFieldConfig, ) { + const coordinate = `${parentType ?? ''}.${name}`; + super(coordinate); this.parentType = parentType; this.name = assertName(name); this.description = config.description; @@ -1135,7 +1141,7 @@ export class GraphQLField this.astNode = config.astNode; } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLField'; } @@ -1155,17 +1161,9 @@ export class GraphQLField astNode: this.astNode, }; } - - toString(): string { - return `${this.parentType ?? ''}.${this.name}`; - } - - toJSON(): string { - return this.toString(); - } } -export class GraphQLArgument implements GraphQLSchemaElement { +export class GraphQLArgument extends GraphQLSchemaElement { parent: GraphQLField | GraphQLDirective; name: string; description: Maybe; @@ -1181,6 +1179,8 @@ export class GraphQLArgument implements GraphQLSchemaElement { name: string, config: GraphQLArgumentConfig, ) { + const coordinate = `${parent}(${name}:)`; + super(coordinate); this.parent = parent; this.name = assertName(name); this.description = config.description; @@ -1192,7 +1192,7 @@ export class GraphQLArgument implements GraphQLSchemaElement { this.astNode = config.astNode; } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLArgument'; } @@ -1207,14 +1207,6 @@ export class GraphQLArgument implements GraphQLSchemaElement { astNode: this.astNode, }; } - - toString(): string { - return `${this.parent}(${this.name}:)`; - } - - toJSON(): string { - return this.toString(); - } } export function isRequiredArgument( @@ -1267,9 +1259,10 @@ export interface GraphQLInterfaceTypeExtensions { * }); * ``` */ -export class GraphQLInterfaceType - implements GraphQLSchemaElement -{ +export class GraphQLInterfaceType< + TSource = any, + TContext = any, +> extends GraphQLSchemaElement { name: string; description: Maybe; resolveType: Maybe>; @@ -1281,6 +1274,8 @@ export class GraphQLInterfaceType private _interfaces: ThunkReadonlyArray; constructor(config: Readonly>) { + const coordinate = config.name; + super(coordinate); this.name = assertName(config.name); this.description = config.description; this.resolveType = config.resolveType; @@ -1295,7 +1290,7 @@ export class GraphQLInterfaceType this._interfaces = defineInterfaces.bind(undefined, config.interfaces); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLInterfaceType'; } @@ -1325,14 +1320,6 @@ export class GraphQLInterfaceType extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } export interface GraphQLInterfaceTypeConfig { @@ -1396,7 +1383,7 @@ export interface GraphQLUnionTypeExtensions { * }); * ``` */ -export class GraphQLUnionType implements GraphQLSchemaElement { +export class GraphQLUnionType extends GraphQLSchemaElement { name: string; description: Maybe; resolveType: Maybe>; @@ -1407,6 +1394,8 @@ export class GraphQLUnionType implements GraphQLSchemaElement { private _types: ThunkReadonlyArray; constructor(config: Readonly>) { + const coordinate = config.name; + super(coordinate); this.name = assertName(config.name); this.description = config.description; this.resolveType = config.resolveType; @@ -1417,7 +1406,7 @@ export class GraphQLUnionType implements GraphQLSchemaElement { this._types = defineTypes.bind(undefined, config.types); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLUnionType'; } @@ -1439,14 +1428,6 @@ export class GraphQLUnionType implements GraphQLSchemaElement { extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } function defineTypes( @@ -1513,7 +1494,7 @@ export interface GraphQLEnumTypeExtensions { * Note: If a value is not provided in a definition, the name of the enum value * will be used as its internal value. */ -export class GraphQLEnumType /* */ implements GraphQLSchemaElement { +export class GraphQLEnumType /* */ extends GraphQLSchemaElement { name: string; description: Maybe; extensions: Readonly; @@ -1528,6 +1509,8 @@ export class GraphQLEnumType /* */ implements GraphQLSchemaElement { private _nameLookup: ObjMap | null; constructor(config: Readonly */>) { + const coordinate = config.name; + super(coordinate); this.name = assertName(config.name); this.description = config.description; this.extensions = toObjMapWithSymbols(config.extensions); @@ -1545,7 +1528,7 @@ export class GraphQLEnumType /* */ implements GraphQLSchemaElement { this._nameLookup = null; } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLEnumType'; } @@ -1674,14 +1657,6 @@ export class GraphQLEnumType /* */ implements GraphQLSchemaElement { extensionASTNodes: this.extensionASTNodes, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } function didYouMeanEnumValue( @@ -1741,7 +1716,7 @@ export interface GraphQLEnumValueNormalizedConfig extensions: Readonly; } -export class GraphQLEnumValue implements GraphQLSchemaElement { +export class GraphQLEnumValue extends GraphQLSchemaElement { parentEnum: GraphQLEnumType; name: string; description: Maybe; @@ -1755,6 +1730,7 @@ export class GraphQLEnumValue implements GraphQLSchemaElement { name: string, config: GraphQLEnumValueConfig, ) { + super(`${parentEnum}.${name}`); this.parentEnum = parentEnum; this.name = assertEnumValueName(name); this.description = config.description; @@ -1764,7 +1740,7 @@ export class GraphQLEnumValue implements GraphQLSchemaElement { this.astNode = config.astNode; } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLEnumValue'; } @@ -1777,14 +1753,6 @@ export class GraphQLEnumValue implements GraphQLSchemaElement { astNode: this.astNode, }; } - - toString(): string { - return `${this.parentEnum.name}.${this.name}`; - } - - toJSON(): string { - return this.toString(); - } } /** @@ -1821,7 +1789,7 @@ export interface GraphQLInputObjectTypeExtensions { * }); * ``` */ -export class GraphQLInputObjectType implements GraphQLSchemaElement { +export class GraphQLInputObjectType extends GraphQLSchemaElement { name: string; description: Maybe; extensions: Readonly; @@ -1832,6 +1800,7 @@ export class GraphQLInputObjectType implements GraphQLSchemaElement { private _fields: ThunkObjMap; constructor(config: Readonly) { + super(config.name); this.name = assertName(config.name); this.description = config.description; this.extensions = toObjMapWithSymbols(config.extensions); @@ -1842,7 +1811,7 @@ export class GraphQLInputObjectType implements GraphQLSchemaElement { this._fields = defineInputFieldMap.bind(undefined, this, config.fields); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLInputObjectType'; } @@ -1864,14 +1833,6 @@ export class GraphQLInputObjectType implements GraphQLSchemaElement { isOneOf: this.isOneOf, }; } - - toString(): string { - return this.name; - } - - toJSON(): string { - return this.toString(); - } } function defineInputFieldMap( @@ -1938,7 +1899,7 @@ export interface GraphQLInputFieldNormalizedConfig export type GraphQLInputFieldNormalizedConfigMap = ObjMap; -export class GraphQLInputField implements GraphQLSchemaElement { +export class GraphQLInputField extends GraphQLSchemaElement { parentType: GraphQLInputObjectType; name: string; description: Maybe; @@ -1954,9 +1915,12 @@ export class GraphQLInputField implements GraphQLSchemaElement { name: string, config: GraphQLInputFieldConfig, ) { + const coordinate = `${parentType}.${name}`; + super(coordinate); + devAssert( !('resolve' in config), - `${parentType}.${name} field has a resolve property, but Input Types cannot define resolvers.`, + `${coordinate} field has a resolve property, but Input Types cannot define resolvers.`, ); this.parentType = parentType; @@ -1970,7 +1934,7 @@ export class GraphQLInputField implements GraphQLSchemaElement { this.astNode = config.astNode; } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLInputField'; } @@ -1985,14 +1949,6 @@ export class GraphQLInputField implements GraphQLSchemaElement { astNode: this.astNode, }; } - - toString(): string { - return `${this.parentType}.${this.name}`; - } - - toJSON(): string { - return this.toString(); - } } export function isRequiredInputField(field: GraphQLInputField): boolean { diff --git a/src/type/directives.ts b/src/type/directives.ts index 96f5b6b65a..024cdf46a2 100644 --- a/src/type/directives.ts +++ b/src/type/directives.ts @@ -14,9 +14,12 @@ import { assertName } from './assertName.js'; import type { GraphQLArgumentConfig, GraphQLFieldNormalizedConfigArgumentMap, +} from './definition.js'; +import { + GraphQLArgument, + GraphQLNonNull, GraphQLSchemaElement, } from './definition.js'; -import { GraphQLArgument, GraphQLNonNull } from './definition.js'; import { GraphQLBoolean, GraphQLInt, GraphQLString } from './scalars.js'; /** @@ -52,7 +55,7 @@ export interface GraphQLDirectiveExtensions { * Directives are used by the GraphQL runtime as a way of modifying execution * behavior. Type system creators will usually not create these directly. */ -export class GraphQLDirective implements GraphQLSchemaElement { +export class GraphQLDirective extends GraphQLSchemaElement { name: string; description: Maybe; locations: ReadonlyArray; @@ -62,6 +65,8 @@ export class GraphQLDirective implements GraphQLSchemaElement { astNode: Maybe; constructor(config: Readonly) { + const coordinate = `@${config.name}`; + super(coordinate); this.name = assertName(config.name); this.description = config.description; this.locations = config.locations; @@ -71,13 +76,13 @@ export class GraphQLDirective implements GraphQLSchemaElement { devAssert( Array.isArray(config.locations), - `@${this.name} locations must be an Array.`, + `${coordinate} locations must be an Array.`, ); const args = config.args ?? {}; devAssert( isObjectLike(args) && !Array.isArray(args), - `@${this.name} args must be an object with argument names as keys.`, + `@${coordinate} args must be an object with argument names as keys.`, ); this.args = Object.entries(args).map( @@ -85,7 +90,7 @@ export class GraphQLDirective implements GraphQLSchemaElement { ); } - get [Symbol.toStringTag]() { + override get [Symbol.toStringTag]() { return 'GraphQLDirective'; } @@ -104,14 +109,6 @@ export class GraphQLDirective implements GraphQLSchemaElement { astNode: this.astNode, }; } - - toString(): string { - return '@' + this.name; - } - - toJSON(): string { - return this.toString(); - } } export interface GraphQLDirectiveConfig { diff --git a/src/type/index.ts b/src/type/index.ts index dd9d103868..d73a82b4bc 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -10,12 +10,6 @@ export { } from './schema.js'; export type { GraphQLSchemaConfig, GraphQLSchemaExtensions } from './schema.js'; -export type { - GraphQLField, - GraphQLArgument, - GraphQLEnumValue, - GraphQLInputField, -} from './definition.js'; export { resolveObjMapThunk, resolveReadonlyArrayThunk, @@ -69,12 +63,17 @@ export { getNullableType, getNamedType, // Definitions + GraphQLSchemaElement, GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, + GraphQLField, + GraphQLArgument, + GraphQLEnumValue, + GraphQLInputField, // Type Wrappers GraphQLList, GraphQLNonNull, diff --git a/src/utilities/__tests__/buildClientSchema-test.ts b/src/utilities/__tests__/buildClientSchema-test.ts index 00d99a0c8a..b7c779f0b3 100644 --- a/src/utilities/__tests__/buildClientSchema-test.ts +++ b/src/utilities/__tests__/buildClientSchema-test.ts @@ -381,6 +381,8 @@ describe('Type System: build schema from introspection', () => { expect(values).to.have.lengthOf(3); expect(values[0]).to.deep.include({ + coordinate: 'Food.VEGETABLES', + parentEnum: clientFoodEnum, name: 'VEGETABLES', description: 'Foods that are vegetables.', value: 'VEGETABLES', @@ -390,6 +392,8 @@ describe('Type System: build schema from introspection', () => { }); expect(values[1]).to.deep.include({ + coordinate: 'Food.FRUITS', + parentEnum: clientFoodEnum, name: 'FRUITS', description: null, value: 'FRUITS', @@ -399,6 +403,8 @@ describe('Type System: build schema from introspection', () => { }); expect(values[2]).to.deep.include({ + coordinate: 'Food.OILS', + parentEnum: clientFoodEnum, name: 'OILS', description: null, value: 'OILS',