diff --git a/src/language/__tests__/parser-test.ts b/src/language/__tests__/parser-test.ts index ba3ef79cd9..c0d247ddf5 100644 --- a/src/language/__tests__/parser-test.ts +++ b/src/language/__tests__/parser-test.ts @@ -703,14 +703,14 @@ describe('Parser', () => { it('parses Name . Name', () => { const result = parseSchemaCoordinate('MyType.field'); expectJSON(result).toDeepEqual({ - kind: Kind.FIELD_COORDINATE, + kind: Kind.MEMBER_COORDINATE, loc: { start: 0, end: 12 }, name: { kind: Kind.NAME, loc: { start: 0, end: 6 }, value: 'MyType', }, - fieldName: { + memberName: { kind: Kind.NAME, loc: { start: 7, end: 12 }, value: 'field', @@ -727,24 +727,6 @@ describe('Parser', () => { }); }); - it('parses Name :: Name', () => { - const result = parseSchemaCoordinate('MyEnum::value'); - expectJSON(result).toDeepEqual({ - kind: Kind.VALUE_COORDINATE, - loc: { start: 0, end: 13 }, - name: { - kind: Kind.NAME, - loc: { start: 0, end: 6 }, - value: 'MyEnum', - }, - valueName: { - kind: Kind.NAME, - loc: { start: 8, end: 13 }, - value: 'value', - }, - }); - }); - it('parses Name . Name ( Name : )', () => { const result = parseSchemaCoordinate('MyType.field(arg:)'); expectJSON(result).toDeepEqual({ diff --git a/src/language/__tests__/predicates-test.ts b/src/language/__tests__/predicates-test.ts index 7455fd73e4..57907d6aa6 100644 --- a/src/language/__tests__/predicates-test.ts +++ b/src/language/__tests__/predicates-test.ts @@ -148,9 +148,8 @@ describe('AST node predicates', () => { 'ArgumentCoordinate', 'DirectiveArgumentCoordinate', 'DirectiveCoordinate', - 'FieldCoordinate', + 'MemberCoordinate', 'TypeCoordinate', - 'ValueCoordinate', ]); }); }); diff --git a/src/language/ast.ts b/src/language/ast.ts index 268a2ddd98..812b988835 100644 --- a/src/language/ast.ts +++ b/src/language/ast.ts @@ -183,9 +183,8 @@ export type ASTNode = | EnumTypeExtensionNode | InputObjectTypeExtensionNode | TypeCoordinateNode - | FieldCoordinateNode + | MemberCoordinateNode | ArgumentCoordinateNode - | ValueCoordinateNode | DirectiveCoordinateNode | DirectiveArgumentCoordinateNode; @@ -296,9 +295,8 @@ export const QueryDocumentKeys: { // Schema Coordinates TypeCoordinate: ['name'], - FieldCoordinate: ['name', 'fieldName'], + MemberCoordinate: ['name', 'memberName'], ArgumentCoordinate: ['name', 'fieldName', 'argumentName'], - ValueCoordinate: ['name', 'valueName'], DirectiveCoordinate: ['name'], DirectiveArgumentCoordinate: ['name', 'argumentName'], }; @@ -781,9 +779,8 @@ export interface InputObjectTypeExtensionNode { export type SchemaCoordinateNode = | TypeCoordinateNode - | FieldCoordinateNode + | MemberCoordinateNode | ArgumentCoordinateNode - | ValueCoordinateNode | DirectiveCoordinateNode | DirectiveArgumentCoordinateNode; @@ -793,11 +790,11 @@ export interface TypeCoordinateNode { readonly name: NameNode; } -export interface FieldCoordinateNode { - readonly kind: typeof Kind.FIELD_COORDINATE; +export interface MemberCoordinateNode { + readonly kind: typeof Kind.MEMBER_COORDINATE; readonly loc?: Location; readonly name: NameNode; - readonly fieldName: NameNode; + readonly memberName: NameNode; } export interface ArgumentCoordinateNode { @@ -808,13 +805,6 @@ export interface ArgumentCoordinateNode { readonly argumentName: NameNode; } -export interface ValueCoordinateNode { - readonly kind: typeof Kind.VALUE_COORDINATE; - readonly loc?: Location; - readonly name: NameNode; - readonly valueName: NameNode; -} - export interface DirectiveCoordinateNode { readonly kind: typeof Kind.DIRECTIVE_COORDINATE; readonly loc?: Location; diff --git a/src/language/kinds_.ts b/src/language/kinds_.ts index 24d909fdfe..252feb6107 100644 --- a/src/language/kinds_.ts +++ b/src/language/kinds_.ts @@ -113,15 +113,12 @@ export type INPUT_OBJECT_TYPE_EXTENSION = typeof INPUT_OBJECT_TYPE_EXTENSION; export const TYPE_COORDINATE = 'TypeCoordinate'; export type TYPE_COORDINATE = typeof TYPE_COORDINATE; -export const FIELD_COORDINATE = 'FieldCoordinate'; -export type FIELD_COORDINATE = typeof FIELD_COORDINATE; +export const MEMBER_COORDINATE = 'MemberCoordinate'; +export type MEMBER_COORDINATE = typeof MEMBER_COORDINATE; export const ARGUMENT_COORDINATE = 'ArgumentCoordinate'; export type ARGUMENT_COORDINATE = typeof ARGUMENT_COORDINATE; -export const VALUE_COORDINATE = 'ValueCoordinate'; -export type VALUE_COORDINATE = typeof VALUE_COORDINATE; - export const DIRECTIVE_COORDINATE = 'DirectiveCoordinate'; export type DIRECTIVE_COORDINATE = typeof DIRECTIVE_COORDINATE; diff --git a/src/language/lexer.ts b/src/language/lexer.ts index a2d305e645..44abc05197 100644 --- a/src/language/lexer.ts +++ b/src/language/lexer.ts @@ -98,7 +98,6 @@ export function isPunctuatorTokenKind(kind: TokenKind): boolean { kind === TokenKind.DOT || kind === TokenKind.SPREAD || kind === TokenKind.COLON || - kind === TokenKind.TWO_COLON || kind === TokenKind.EQUALS || kind === TokenKind.AT || kind === TokenKind.BRACKET_L || @@ -272,14 +271,6 @@ function readNextToken(lexer: Lexer, start: number): Token { return readDot(lexer, position); } case 0x003a: // : - if (body.charCodeAt(position + 1) === 0x003a) { - return createToken( - lexer, - TokenKind.TWO_COLON, - position, - position + 2, - ); - } return createToken(lexer, TokenKind.COLON, position, position + 1); case 0x003d: // = return createToken(lexer, TokenKind.EQUALS, position, position + 1); diff --git a/src/language/parser.ts b/src/language/parser.ts index 31fa99d074..de049abeb5 100644 --- a/src/language/parser.ts +++ b/src/language/parser.ts @@ -23,7 +23,6 @@ import type { EnumTypeExtensionNode, EnumValueDefinitionNode, EnumValueNode, - FieldCoordinateNode, FieldDefinitionNode, FieldNode, FloatValueNode, @@ -39,6 +38,7 @@ import type { IntValueNode, ListTypeNode, ListValueNode, + MemberCoordinateNode, NamedTypeNode, NameNode, NonNullTypeNode, @@ -63,7 +63,6 @@ import type { TypeSystemExtensionNode, UnionTypeDefinitionNode, UnionTypeExtensionNode, - ValueCoordinateNode, ValueNode, VariableDefinitionNode, VariableNode, @@ -1467,7 +1466,6 @@ export class Parser { * - Name * - Name . Name * - Name . Name ( Name : ) - * - Name :: Name * - @ Name * - @ Name ( Name : ) */ @@ -1475,16 +1473,6 @@ export class Parser { const start = this._lexer.token; const ofDirective = this.expectOptionalToken(TokenKind.AT); const name = this.parseName(); - - if (!ofDirective && this.expectOptionalToken(TokenKind.TWO_COLON)) { - const valueName = this.parseName(); - return this.node(start, { - kind: Kind.VALUE_COORDINATE, - name, - valueName, - }); - } - let memberName: NameNode | undefined; if (!ofDirective && this.expectOptionalToken(TokenKind.DOT)) { memberName = this.parseName(); @@ -1520,10 +1508,10 @@ export class Parser { argumentName, }); } - return this.node(start, { - kind: Kind.FIELD_COORDINATE, + return this.node(start, { + kind: Kind.MEMBER_COORDINATE, name, - fieldName: memberName, + memberName, }); } diff --git a/src/language/predicates.ts b/src/language/predicates.ts index 488e9828f2..5146e8244e 100644 --- a/src/language/predicates.ts +++ b/src/language/predicates.ts @@ -117,9 +117,8 @@ export function isSchemaCoordinateNode( ): node is SchemaCoordinateNode { return ( node.kind === Kind.TYPE_COORDINATE || - node.kind === Kind.FIELD_COORDINATE || + node.kind === Kind.MEMBER_COORDINATE || node.kind === Kind.ARGUMENT_COORDINATE || - node.kind === Kind.VALUE_COORDINATE || node.kind === Kind.DIRECTIVE_COORDINATE || node.kind === Kind.DIRECTIVE_ARGUMENT_COORDINATE ); diff --git a/src/language/printer.ts b/src/language/printer.ts index 2701f8373b..823b14a02d 100644 --- a/src/language/printer.ts +++ b/src/language/printer.ts @@ -325,8 +325,8 @@ const printDocASTReducer: ASTReducer = { TypeCoordinate: { leave: ({ name }) => name }, - FieldCoordinate: { - leave: ({ name, fieldName }) => join([name, wrap('.', fieldName)]), + MemberCoordinate: { + leave: ({ name, memberName }) => join([name, wrap('.', memberName)]), }, ArgumentCoordinate: { @@ -334,10 +334,6 @@ const printDocASTReducer: ASTReducer = { join([name, wrap('.', fieldName), wrap('(', argumentName, ':)')]), }, - ValueCoordinate: { - leave: ({ name, valueName }) => join([name, wrap('::', valueName)]), - }, - DirectiveCoordinate: { leave: ({ name }) => join(['@', name]) }, DirectiveArgumentCoordinate: { diff --git a/src/language/tokenKind.ts b/src/language/tokenKind.ts index b14fe45a05..eae0972b81 100644 --- a/src/language/tokenKind.ts +++ b/src/language/tokenKind.ts @@ -13,7 +13,6 @@ export const TokenKind = { DOT: '.' as const, SPREAD: '...' as const, COLON: ':' as const, - TWO_COLON: '::' as const, EQUALS: '=' as const, AT: '@' as const, BRACKET_L: '[' as const, diff --git a/src/utilities/__tests__/resolveSchemaCoordinate-test.ts b/src/utilities/__tests__/resolveSchemaCoordinate-test.ts index 0fa9cfdf10..42d4310e0e 100644 --- a/src/utilities/__tests__/resolveSchemaCoordinate-test.ts +++ b/src/utilities/__tests__/resolveSchemaCoordinate-test.ts @@ -3,6 +3,7 @@ import { describe, it } from 'mocha'; import type { GraphQLEnumType, + GraphQLField, GraphQLInputObjectType, GraphQLObjectType, } from '../../type/definition.js'; @@ -71,16 +72,10 @@ describe('resolveSchemaCoordinate', () => { ); expect(() => resolveSchemaCoordinate(schema, 'String.field')).to.throw( - 'Expected "String" to be an Input Object, Object or Interface type.', + 'Expected "String" to be an Enum, Input Object, Object or Interface type.', ); }); - it('does not resolve meta-fields', () => { - expect( - resolveSchemaCoordinate(schema, 'Business.__typename'), - ).to.deep.equal(undefined); - }); - it('resolves a Input Field', () => { const type = schema.getType('SearchCriteria') as GraphQLInputObjectType; const inputField = type.getFields().filter; @@ -101,7 +96,7 @@ describe('resolveSchemaCoordinate', () => { const type = schema.getType('SearchFilter') as GraphQLEnumType; const enumValue = type.getValue('OPEN_NOW'); expect( - resolveSchemaCoordinate(schema, 'SearchFilter::OPEN_NOW'), + resolveSchemaCoordinate(schema, 'SearchFilter.OPEN_NOW'), ).to.deep.equal({ kind: 'EnumValue', type, @@ -109,7 +104,7 @@ describe('resolveSchemaCoordinate', () => { }); expect( - resolveSchemaCoordinate(schema, 'SearchFilter::UNKNOWN'), + resolveSchemaCoordinate(schema, 'SearchFilter.UNKNOWN'), ).to.deep.equal(undefined); }); @@ -186,4 +181,59 @@ describe('resolveSchemaCoordinate', () => { 'Expected "unknown" to be defined as a directive in the schema.', ); }); + + it('resolves a meta-field', () => { + const type = schema.getType('Business') as GraphQLObjectType; + const field = schema.getField(type, '__typename'); + expect( + resolveSchemaCoordinate(schema, 'Business.__typename'), + ).to.deep.equal({ + kind: 'Field', + type, + field, + }); + }); + + it('resolves a meta-field argument', () => { + const type = schema.getType('Query') as GraphQLObjectType; + const field = schema.getField(type, '__type') as GraphQLField; + const fieldArgument = field.args.find((arg) => arg.name === 'name'); + expect( + resolveSchemaCoordinate(schema, 'Query.__type(name:)'), + ).to.deep.equal({ + kind: 'FieldArgument', + type, + field, + fieldArgument, + }); + }); + + it('resolves an Introspection Type', () => { + expect(resolveSchemaCoordinate(schema, '__Type')).to.deep.equal({ + kind: 'NamedType', + type: schema.getType('__Type'), + }); + }); + + it('resolves an Introspection Type Field', () => { + const type = schema.getType('__Directive') as GraphQLObjectType; + const field = type.getFields().name; + expect(resolveSchemaCoordinate(schema, '__Directive.name')).to.deep.equal({ + kind: 'Field', + type, + field, + }); + }); + + it('resolves an Introspection Type Enum Value', () => { + const type = schema.getType('__DirectiveLocation') as GraphQLEnumType; + const enumValue = type.getValue('INLINE_FRAGMENT'); + expect( + resolveSchemaCoordinate(schema, '__DirectiveLocation.INLINE_FRAGMENT'), + ).to.deep.equal({ + kind: 'EnumValue', + type, + enumValue, + }); + }); }); diff --git a/src/utilities/resolveSchemaCoordinate.ts b/src/utilities/resolveSchemaCoordinate.ts index afebe13199..3613a07f16 100644 --- a/src/utilities/resolveSchemaCoordinate.ts +++ b/src/utilities/resolveSchemaCoordinate.ts @@ -4,10 +4,9 @@ import type { ArgumentCoordinateNode, DirectiveArgumentCoordinateNode, DirectiveCoordinateNode, - FieldCoordinateNode, + MemberCoordinateNode, SchemaCoordinateNode, TypeCoordinateNode, - ValueCoordinateNode, } from '../language/ast.js'; import { Kind } from '../language/kinds.js'; import { parseSchemaCoordinate } from '../language/parser.js'; @@ -119,37 +118,52 @@ function resolveTypeCoordinate( } /** - * FieldCoordinate : Name . Name + * MemberCoordinate : Name . Name */ -function resolveFieldCoordinate( +function resolveMemberCoordinate( schema: GraphQLSchema, - schemaCoordinate: FieldCoordinateNode, -): ResolvedField | ResolvedInputField | undefined { + schemaCoordinate: MemberCoordinateNode, +): ResolvedField | ResolvedInputField | ResolvedEnumValue | undefined { // 1. Let {typeName} be the value of the first {Name}. // 2. Let {type} be the type in the {schema} named {typeName}. const typeName = schemaCoordinate.name.value; const type = schema.getType(typeName); - // 3. Assert: {type} must exist, and must be an Input Object, Object or Interface type. + // 3. Assert: {type} must exist, and must be an Enum, Input Object, Object or Interface type. if (!type) { throw new Error( `Expected ${inspect(typeName)} to be defined as a type in the schema.`, ); } if ( + !isEnumType(type) && !isInputObjectType(type) && !isObjectType(type) && !isInterfaceType(type) ) { throw new Error( - `Expected ${inspect(typeName)} to be an Input Object, Object or Interface type.`, + `Expected ${inspect(typeName)} to be an Enum, Input Object, Object or Interface type.`, ); } - // 4. If {type} is an Input Object type: + // 4. If {type} is an Enum type: + if (isEnumType(type)) { + // 1. Let {enumValueName} be the value of the second {Name}. + const enumValueName = schemaCoordinate.memberName.value; + const enumValue = type.getValue(enumValueName); + + // 2. Return the enum value of {type} named {enumValueName}, or {null} if no such value exists. + if (enumValue == null) { + return; + } + + return { kind: 'EnumValue', type, enumValue }; + } + + // 5. Otherwise, if {type} is an Input Object type: if (isInputObjectType(type)) { // 1. Let {inputFieldName} be the value of the second {Name}. - const inputFieldName = schemaCoordinate.fieldName.value; + const inputFieldName = schemaCoordinate.memberName.value; const inputField = type.getFields()[inputFieldName]; // 2. Return the input field of {type} named {inputFieldName}, or {null} if no such input field exists. @@ -160,10 +174,10 @@ function resolveFieldCoordinate( return { kind: 'InputField', type, inputField }; } - // 5. Otherwise: + // 6. Otherwise: // 1. Let {fieldName} be the value of the second {Name}. - const fieldName = schemaCoordinate.fieldName.value; - const field = type.getFields()[fieldName]; + const fieldName = schemaCoordinate.memberName.value; + const field = schema.getField(type, fieldName); // 2. Return the field of {type} named {fieldName}, or {null} if no such field exists. if (field == null) { @@ -200,7 +214,7 @@ function resolveArgumentCoordinate( // 4. Let {fieldName} be the value of the second {Name}. // 5. Let {field} be the field of {type} named {fieldName}. const fieldName = schemaCoordinate.fieldName.value; - const field = type.getFields()[fieldName]; + const field = schema.getField(type, fieldName); // 7. Assert: {field} must exist. if (field == null) { @@ -223,40 +237,6 @@ function resolveArgumentCoordinate( return { kind: 'FieldArgument', type, field, fieldArgument }; } -/** - * ValueCoordinate : Name :: Name - */ -function resolveValueCoordinate( - schema: GraphQLSchema, - schemaCoordinate: ValueCoordinateNode, -): ResolvedEnumValue | undefined { - // 1. Let {typeName} be the value of the first {Name}. - // 2. Let {type} be the type in the {schema} named {typeName}. - const typeName = schemaCoordinate.name.value; - const type = schema.getType(typeName); - - // 3. Assert: {type} must exist, and must be an Enum type. - if (!type) { - throw new Error( - `Expected ${inspect(typeName)} to be defined as a type in the schema.`, - ); - } - if (!isEnumType(type)) { - throw new Error(`Expected ${inspect(typeName)} to be an Enum type.`); - } - - // 4. Let {enumValueName} be the value of the second {Name}. - const enumValueName = schemaCoordinate.valueName.value; - const enumValue = type.getValue(enumValueName); - - // 5. Return the enum value of {type} named {enumValueName}, or {null} if no such value exists. - if (enumValue == null) { - return; - } - - return { kind: 'EnumValue', type, enumValue }; -} - /** * DirectiveCoordinate : @ Name */ @@ -321,12 +301,10 @@ export function resolveASTSchemaCoordinate( switch (schemaCoordinate.kind) { case Kind.TYPE_COORDINATE: return resolveTypeCoordinate(schema, schemaCoordinate); - case Kind.FIELD_COORDINATE: - return resolveFieldCoordinate(schema, schemaCoordinate); + case Kind.MEMBER_COORDINATE: + return resolveMemberCoordinate(schema, schemaCoordinate); case Kind.ARGUMENT_COORDINATE: return resolveArgumentCoordinate(schema, schemaCoordinate); - case Kind.VALUE_COORDINATE: - return resolveValueCoordinate(schema, schemaCoordinate); case Kind.DIRECTIVE_COORDINATE: return resolveDirectiveCoordinate(schema, schemaCoordinate); case Kind.DIRECTIVE_ARGUMENT_COORDINATE: