/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * */ import inspect from '../jsutils/inspect'; import defineToJSON from '../jsutils/defineToJSON'; import { Source } from './source'; import { syntaxError } from '../error'; import { TokenKind, getTokenDesc, createLexer } from './lexer'; import { Kind } from './kinds'; import { DirectiveLocation } from './directiveLocation'; /** * Configuration options to control parser behavior */ /** * Given a GraphQL source, parses it into a Document. * Throws GraphQLError if a syntax error is encountered. */ export function parse(source, options) { var sourceObj = typeof source === 'string' ? new Source(source) : source; if (!(sourceObj instanceof Source)) { throw new TypeError("Must provide Source. Received: ".concat(inspect(sourceObj))); } var lexer = createLexer(sourceObj, options || {}); return parseDocument(lexer); } /** * Given a string containing a GraphQL value (ex. `[42]`), parse the AST for * that value. * Throws GraphQLError if a syntax error is encountered. * * This is useful within tools that operate upon GraphQL Values directly and * in isolation of complete GraphQL documents. * * Consider providing the results to the utility function: valueFromAST(). */ export function parseValue(source, options) { var sourceObj = typeof source === 'string' ? new Source(source) : source; var lexer = createLexer(sourceObj, options || {}); expectToken(lexer, TokenKind.SOF); var value = parseValueLiteral(lexer, false); expectToken(lexer, TokenKind.EOF); return value; } /** * Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for * that type. * Throws GraphQLError if a syntax error is encountered. * * This is useful within tools that operate upon GraphQL Types directly and * in isolation of complete GraphQL documents. * * Consider providing the results to the utility function: typeFromAST(). */ export function parseType(source, options) { var sourceObj = typeof source === 'string' ? new Source(source) : source; var lexer = createLexer(sourceObj, options || {}); expectToken(lexer, TokenKind.SOF); var type = parseTypeReference(lexer); expectToken(lexer, TokenKind.EOF); return type; } /** * Converts a name lex token into a name parse node. */ function parseName(lexer) { var token = expectToken(lexer, TokenKind.NAME); return { kind: Kind.NAME, value: token.value, loc: loc(lexer, token) }; } // Implements the parsing rules in the Document section. /** * Document : Definition+ */ function parseDocument(lexer) { var start = lexer.token; return { kind: Kind.DOCUMENT, definitions: many(lexer, TokenKind.SOF, parseDefinition, TokenKind.EOF), loc: loc(lexer, start) }; } /** * Definition : * - ExecutableDefinition * - TypeSystemDefinition * - TypeSystemExtension */ function parseDefinition(lexer) { if (peek(lexer, TokenKind.NAME)) { switch (lexer.token.value) { case 'query': case 'mutation': case 'subscription': case 'fragment': return parseExecutableDefinition(lexer); case 'schema': case 'scalar': case 'type': case 'interface': case 'union': case 'enum': case 'input': case 'directive': return parseTypeSystemDefinition(lexer); case 'extend': return parseTypeSystemExtension(lexer); } } else if (peek(lexer, TokenKind.BRACE_L)) { return parseExecutableDefinition(lexer); } else if (peekDescription(lexer)) { return parseTypeSystemDefinition(lexer); } throw unexpected(lexer); } /** * ExecutableDefinition : * - OperationDefinition * - FragmentDefinition */ function parseExecutableDefinition(lexer) { if (peek(lexer, TokenKind.NAME)) { switch (lexer.token.value) { case 'query': case 'mutation': case 'subscription': return parseOperationDefinition(lexer); case 'fragment': return parseFragmentDefinition(lexer); } } else if (peek(lexer, TokenKind.BRACE_L)) { return parseOperationDefinition(lexer); } throw unexpected(lexer); } // Implements the parsing rules in the Operations section. /** * OperationDefinition : * - SelectionSet * - OperationType Name? VariableDefinitions? Directives? SelectionSet */ function parseOperationDefinition(lexer) { var start = lexer.token; if (peek(lexer, TokenKind.BRACE_L)) { return { kind: Kind.OPERATION_DEFINITION, operation: 'query', name: undefined, variableDefinitions: [], directives: [], selectionSet: parseSelectionSet(lexer), loc: loc(lexer, start) }; } var operation = parseOperationType(lexer); var name; if (peek(lexer, TokenKind.NAME)) { name = parseName(lexer); } return { kind: Kind.OPERATION_DEFINITION, operation: operation, name: name, variableDefinitions: parseVariableDefinitions(lexer), directives: parseDirectives(lexer, false), selectionSet: parseSelectionSet(lexer), loc: loc(lexer, start) }; } /** * OperationType : one of query mutation subscription */ function parseOperationType(lexer) { var operationToken = expectToken(lexer, TokenKind.NAME); switch (operationToken.value) { case 'query': return 'query'; case 'mutation': return 'mutation'; case 'subscription': return 'subscription'; } throw unexpected(lexer, operationToken); } /** * VariableDefinitions : ( VariableDefinition+ ) */ function parseVariableDefinitions(lexer) { return peek(lexer, TokenKind.PAREN_L) ? many(lexer, TokenKind.PAREN_L, parseVariableDefinition, TokenKind.PAREN_R) : []; } /** * VariableDefinition : Variable : Type DefaultValue? Directives[Const]? */ function parseVariableDefinition(lexer) { var start = lexer.token; return { kind: Kind.VARIABLE_DEFINITION, variable: parseVariable(lexer), type: (expectToken(lexer, TokenKind.COLON), parseTypeReference(lexer)), defaultValue: expectOptionalToken(lexer, TokenKind.EQUALS) ? parseValueLiteral(lexer, true) : undefined, directives: parseDirectives(lexer, true), loc: loc(lexer, start) }; } /** * Variable : $ Name */ function parseVariable(lexer) { var start = lexer.token; expectToken(lexer, TokenKind.DOLLAR); return { kind: Kind.VARIABLE, name: parseName(lexer), loc: loc(lexer, start) }; } /** * SelectionSet : { Selection+ } */ function parseSelectionSet(lexer) { var start = lexer.token; return { kind: Kind.SELECTION_SET, selections: many(lexer, TokenKind.BRACE_L, parseSelection, TokenKind.BRACE_R), loc: loc(lexer, start) }; } /** * Selection : * - Field * - FragmentSpread * - InlineFragment */ function parseSelection(lexer) { return peek(lexer, TokenKind.SPREAD) ? parseFragment(lexer) : parseField(lexer); } /** * Field : Alias? Name Arguments? Directives? SelectionSet? * * Alias : Name : */ function parseField(lexer) { var start = lexer.token; var nameOrAlias = parseName(lexer); var alias; var name; if (expectOptionalToken(lexer, TokenKind.COLON)) { alias = nameOrAlias; name = parseName(lexer); } else { name = nameOrAlias; } return { kind: Kind.FIELD, alias: alias, name: name, arguments: parseArguments(lexer, false), directives: parseDirectives(lexer, false), selectionSet: peek(lexer, TokenKind.BRACE_L) ? parseSelectionSet(lexer) : undefined, loc: loc(lexer, start) }; } /** * Arguments[Const] : ( Argument[?Const]+ ) */ function parseArguments(lexer, isConst) { var item = isConst ? parseConstArgument : parseArgument; return peek(lexer, TokenKind.PAREN_L) ? many(lexer, TokenKind.PAREN_L, item, TokenKind.PAREN_R) : []; } /** * Argument[Const] : Name : Value[?Const] */ function parseArgument(lexer) { var start = lexer.token; var name = parseName(lexer); expectToken(lexer, TokenKind.COLON); return { kind: Kind.ARGUMENT, name: name, value: parseValueLiteral(lexer, false), loc: loc(lexer, start) }; } function parseConstArgument(lexer) { var start = lexer.token; return { kind: Kind.ARGUMENT, name: parseName(lexer), value: (expectToken(lexer, TokenKind.COLON), parseConstValue(lexer)), loc: loc(lexer, start) }; } // Implements the parsing rules in the Fragments section. /** * Corresponds to both FragmentSpread and InlineFragment in the spec. * * FragmentSpread : ... FragmentName Directives? * * InlineFragment : ... TypeCondition? Directives? SelectionSet */ function parseFragment(lexer) { var start = lexer.token; expectToken(lexer, TokenKind.SPREAD); var hasTypeCondition = expectOptionalKeyword(lexer, 'on'); if (!hasTypeCondition && peek(lexer, TokenKind.NAME)) { return { kind: Kind.FRAGMENT_SPREAD, name: parseFragmentName(lexer), directives: parseDirectives(lexer, false), loc: loc(lexer, start) }; } return { kind: Kind.INLINE_FRAGMENT, typeCondition: hasTypeCondition ? parseNamedType(lexer) : undefined, directives: parseDirectives(lexer, false), selectionSet: parseSelectionSet(lexer), loc: loc(lexer, start) }; } /** * FragmentDefinition : * - fragment FragmentName on TypeCondition Directives? SelectionSet * * TypeCondition : NamedType */ function parseFragmentDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'fragment'); // Experimental support for defining variables within fragments changes // the grammar of FragmentDefinition: // - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet if (lexer.options.experimentalFragmentVariables) { return { kind: Kind.FRAGMENT_DEFINITION, name: parseFragmentName(lexer), variableDefinitions: parseVariableDefinitions(lexer), typeCondition: (expectKeyword(lexer, 'on'), parseNamedType(lexer)), directives: parseDirectives(lexer, false), selectionSet: parseSelectionSet(lexer), loc: loc(lexer, start) }; } return { kind: Kind.FRAGMENT_DEFINITION, name: parseFragmentName(lexer), typeCondition: (expectKeyword(lexer, 'on'), parseNamedType(lexer)), directives: parseDirectives(lexer, false), selectionSet: parseSelectionSet(lexer), loc: loc(lexer, start) }; } /** * FragmentName : Name but not `on` */ function parseFragmentName(lexer) { if (lexer.token.value === 'on') { throw unexpected(lexer); } return parseName(lexer); } // Implements the parsing rules in the Values section. /** * Value[Const] : * - [~Const] Variable * - IntValue * - FloatValue * - StringValue * - BooleanValue * - NullValue * - EnumValue * - ListValue[?Const] * - ObjectValue[?Const] * * BooleanValue : one of `true` `false` * * NullValue : `null` * * EnumValue : Name but not `true`, `false` or `null` */ function parseValueLiteral(lexer, isConst) { var token = lexer.token; switch (token.kind) { case TokenKind.BRACKET_L: return parseList(lexer, isConst); case TokenKind.BRACE_L: return parseObject(lexer, isConst); case TokenKind.INT: lexer.advance(); return { kind: Kind.INT, value: token.value, loc: loc(lexer, token) }; case TokenKind.FLOAT: lexer.advance(); return { kind: Kind.FLOAT, value: token.value, loc: loc(lexer, token) }; case TokenKind.STRING: case TokenKind.BLOCK_STRING: return parseStringLiteral(lexer); case TokenKind.NAME: if (token.value === 'true' || token.value === 'false') { lexer.advance(); return { kind: Kind.BOOLEAN, value: token.value === 'true', loc: loc(lexer, token) }; } else if (token.value === 'null') { lexer.advance(); return { kind: Kind.NULL, loc: loc(lexer, token) }; } lexer.advance(); return { kind: Kind.ENUM, value: token.value, loc: loc(lexer, token) }; case TokenKind.DOLLAR: if (!isConst) { return parseVariable(lexer); } break; } throw unexpected(lexer); } function parseStringLiteral(lexer) { var token = lexer.token; lexer.advance(); return { kind: Kind.STRING, value: token.value, block: token.kind === TokenKind.BLOCK_STRING, loc: loc(lexer, token) }; } export function parseConstValue(lexer) { return parseValueLiteral(lexer, true); } function parseValueValue(lexer) { return parseValueLiteral(lexer, false); } /** * ListValue[Const] : * - [ ] * - [ Value[?Const]+ ] */ function parseList(lexer, isConst) { var start = lexer.token; var item = isConst ? parseConstValue : parseValueValue; return { kind: Kind.LIST, values: any(lexer, TokenKind.BRACKET_L, item, TokenKind.BRACKET_R), loc: loc(lexer, start) }; } /** * ObjectValue[Const] : * - { } * - { ObjectField[?Const]+ } */ function parseObject(lexer, isConst) { var start = lexer.token; var item = function item() { return parseObjectField(lexer, isConst); }; return { kind: Kind.OBJECT, fields: any(lexer, TokenKind.BRACE_L, item, TokenKind.BRACE_R), loc: loc(lexer, start) }; } /** * ObjectField[Const] : Name : Value[?Const] */ function parseObjectField(lexer, isConst) { var start = lexer.token; var name = parseName(lexer); expectToken(lexer, TokenKind.COLON); return { kind: Kind.OBJECT_FIELD, name: name, value: parseValueLiteral(lexer, isConst), loc: loc(lexer, start) }; } // Implements the parsing rules in the Directives section. /** * Directives[Const] : Directive[?Const]+ */ function parseDirectives(lexer, isConst) { var directives = []; while (peek(lexer, TokenKind.AT)) { directives.push(parseDirective(lexer, isConst)); } return directives; } /** * Directive[Const] : @ Name Arguments[?Const]? */ function parseDirective(lexer, isConst) { var start = lexer.token; expectToken(lexer, TokenKind.AT); return { kind: Kind.DIRECTIVE, name: parseName(lexer), arguments: parseArguments(lexer, isConst), loc: loc(lexer, start) }; } // Implements the parsing rules in the Types section. /** * Type : * - NamedType * - ListType * - NonNullType */ export function parseTypeReference(lexer) { var start = lexer.token; var type; if (expectOptionalToken(lexer, TokenKind.BRACKET_L)) { type = parseTypeReference(lexer); expectToken(lexer, TokenKind.BRACKET_R); type = { kind: Kind.LIST_TYPE, type: type, loc: loc(lexer, start) }; } else { type = parseNamedType(lexer); } if (expectOptionalToken(lexer, TokenKind.BANG)) { return { kind: Kind.NON_NULL_TYPE, type: type, loc: loc(lexer, start) }; } return type; } /** * NamedType : Name */ export function parseNamedType(lexer) { var start = lexer.token; return { kind: Kind.NAMED_TYPE, name: parseName(lexer), loc: loc(lexer, start) }; } // Implements the parsing rules in the Type Definition section. /** * TypeSystemDefinition : * - SchemaDefinition * - TypeDefinition * - DirectiveDefinition * * TypeDefinition : * - ScalarTypeDefinition * - ObjectTypeDefinition * - InterfaceTypeDefinition * - UnionTypeDefinition * - EnumTypeDefinition * - InputObjectTypeDefinition */ function parseTypeSystemDefinition(lexer) { // Many definitions begin with a description and require a lookahead. var keywordToken = peekDescription(lexer) ? lexer.lookahead() : lexer.token; if (keywordToken.kind === TokenKind.NAME) { switch (keywordToken.value) { case 'schema': return parseSchemaDefinition(lexer); case 'scalar': return parseScalarTypeDefinition(lexer); case 'type': return parseObjectTypeDefinition(lexer); case 'interface': return parseInterfaceTypeDefinition(lexer); case 'union': return parseUnionTypeDefinition(lexer); case 'enum': return parseEnumTypeDefinition(lexer); case 'input': return parseInputObjectTypeDefinition(lexer); case 'directive': return parseDirectiveDefinition(lexer); } } throw unexpected(lexer, keywordToken); } function peekDescription(lexer) { return peek(lexer, TokenKind.STRING) || peek(lexer, TokenKind.BLOCK_STRING); } /** * Description : StringValue */ function parseDescription(lexer) { if (peekDescription(lexer)) { return parseStringLiteral(lexer); } } /** * SchemaDefinition : schema Directives[Const]? { OperationTypeDefinition+ } */ function parseSchemaDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'schema'); var directives = parseDirectives(lexer, true); var operationTypes = many(lexer, TokenKind.BRACE_L, parseOperationTypeDefinition, TokenKind.BRACE_R); return { kind: Kind.SCHEMA_DEFINITION, directives: directives, operationTypes: operationTypes, loc: loc(lexer, start) }; } /** * OperationTypeDefinition : OperationType : NamedType */ function parseOperationTypeDefinition(lexer) { var start = lexer.token; var operation = parseOperationType(lexer); expectToken(lexer, TokenKind.COLON); var type = parseNamedType(lexer); return { kind: Kind.OPERATION_TYPE_DEFINITION, operation: operation, type: type, loc: loc(lexer, start) }; } /** * ScalarTypeDefinition : Description? scalar Name Directives[Const]? */ function parseScalarTypeDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); expectKeyword(lexer, 'scalar'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); return { kind: Kind.SCALAR_TYPE_DEFINITION, description: description, name: name, directives: directives, loc: loc(lexer, start) }; } /** * ObjectTypeDefinition : * Description? * type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition? */ function parseObjectTypeDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); expectKeyword(lexer, 'type'); var name = parseName(lexer); var interfaces = parseImplementsInterfaces(lexer); var directives = parseDirectives(lexer, true); var fields = parseFieldsDefinition(lexer); return { kind: Kind.OBJECT_TYPE_DEFINITION, description: description, name: name, interfaces: interfaces, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * ImplementsInterfaces : * - implements `&`? NamedType * - ImplementsInterfaces & NamedType */ function parseImplementsInterfaces(lexer) { var types = []; if (expectOptionalKeyword(lexer, 'implements')) { // Optional leading ampersand expectOptionalToken(lexer, TokenKind.AMP); do { types.push(parseNamedType(lexer)); } while (expectOptionalToken(lexer, TokenKind.AMP) || // Legacy support for the SDL? lexer.options.allowLegacySDLImplementsInterfaces && peek(lexer, TokenKind.NAME)); } return types; } /** * FieldsDefinition : { FieldDefinition+ } */ function parseFieldsDefinition(lexer) { // Legacy support for the SDL? if (lexer.options.allowLegacySDLEmptyFields && peek(lexer, TokenKind.BRACE_L) && lexer.lookahead().kind === TokenKind.BRACE_R) { lexer.advance(); lexer.advance(); return []; } return peek(lexer, TokenKind.BRACE_L) ? many(lexer, TokenKind.BRACE_L, parseFieldDefinition, TokenKind.BRACE_R) : []; } /** * FieldDefinition : * - Description? Name ArgumentsDefinition? : Type Directives[Const]? */ function parseFieldDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); var name = parseName(lexer); var args = parseArgumentDefs(lexer); expectToken(lexer, TokenKind.COLON); var type = parseTypeReference(lexer); var directives = parseDirectives(lexer, true); return { kind: Kind.FIELD_DEFINITION, description: description, name: name, arguments: args, type: type, directives: directives, loc: loc(lexer, start) }; } /** * ArgumentsDefinition : ( InputValueDefinition+ ) */ function parseArgumentDefs(lexer) { if (!peek(lexer, TokenKind.PAREN_L)) { return []; } return many(lexer, TokenKind.PAREN_L, parseInputValueDef, TokenKind.PAREN_R); } /** * InputValueDefinition : * - Description? Name : Type DefaultValue? Directives[Const]? */ function parseInputValueDef(lexer) { var start = lexer.token; var description = parseDescription(lexer); var name = parseName(lexer); expectToken(lexer, TokenKind.COLON); var type = parseTypeReference(lexer); var defaultValue; if (expectOptionalToken(lexer, TokenKind.EQUALS)) { defaultValue = parseConstValue(lexer); } var directives = parseDirectives(lexer, true); return { kind: Kind.INPUT_VALUE_DEFINITION, description: description, name: name, type: type, defaultValue: defaultValue, directives: directives, loc: loc(lexer, start) }; } /** * InterfaceTypeDefinition : * - Description? interface Name Directives[Const]? FieldsDefinition? */ function parseInterfaceTypeDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); expectKeyword(lexer, 'interface'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); var fields = parseFieldsDefinition(lexer); return { kind: Kind.INTERFACE_TYPE_DEFINITION, description: description, name: name, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * UnionTypeDefinition : * - Description? union Name Directives[Const]? UnionMemberTypes? */ function parseUnionTypeDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); expectKeyword(lexer, 'union'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); var types = parseUnionMemberTypes(lexer); return { kind: Kind.UNION_TYPE_DEFINITION, description: description, name: name, directives: directives, types: types, loc: loc(lexer, start) }; } /** * UnionMemberTypes : * - = `|`? NamedType * - UnionMemberTypes | NamedType */ function parseUnionMemberTypes(lexer) { var types = []; if (expectOptionalToken(lexer, TokenKind.EQUALS)) { // Optional leading pipe expectOptionalToken(lexer, TokenKind.PIPE); do { types.push(parseNamedType(lexer)); } while (expectOptionalToken(lexer, TokenKind.PIPE)); } return types; } /** * EnumTypeDefinition : * - Description? enum Name Directives[Const]? EnumValuesDefinition? */ function parseEnumTypeDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); expectKeyword(lexer, 'enum'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); var values = parseEnumValuesDefinition(lexer); return { kind: Kind.ENUM_TYPE_DEFINITION, description: description, name: name, directives: directives, values: values, loc: loc(lexer, start) }; } /** * EnumValuesDefinition : { EnumValueDefinition+ } */ function parseEnumValuesDefinition(lexer) { return peek(lexer, TokenKind.BRACE_L) ? many(lexer, TokenKind.BRACE_L, parseEnumValueDefinition, TokenKind.BRACE_R) : []; } /** * EnumValueDefinition : Description? EnumValue Directives[Const]? * * EnumValue : Name */ function parseEnumValueDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); var name = parseName(lexer); var directives = parseDirectives(lexer, true); return { kind: Kind.ENUM_VALUE_DEFINITION, description: description, name: name, directives: directives, loc: loc(lexer, start) }; } /** * InputObjectTypeDefinition : * - Description? input Name Directives[Const]? InputFieldsDefinition? */ function parseInputObjectTypeDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); expectKeyword(lexer, 'input'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); var fields = parseInputFieldsDefinition(lexer); return { kind: Kind.INPUT_OBJECT_TYPE_DEFINITION, description: description, name: name, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * InputFieldsDefinition : { InputValueDefinition+ } */ function parseInputFieldsDefinition(lexer) { return peek(lexer, TokenKind.BRACE_L) ? many(lexer, TokenKind.BRACE_L, parseInputValueDef, TokenKind.BRACE_R) : []; } /** * TypeSystemExtension : * - SchemaExtension * - TypeExtension * * TypeExtension : * - ScalarTypeExtension * - ObjectTypeExtension * - InterfaceTypeExtension * - UnionTypeExtension * - EnumTypeExtension * - InputObjectTypeDefinition */ function parseTypeSystemExtension(lexer) { var keywordToken = lexer.lookahead(); if (keywordToken.kind === TokenKind.NAME) { switch (keywordToken.value) { case 'schema': return parseSchemaExtension(lexer); case 'scalar': return parseScalarTypeExtension(lexer); case 'type': return parseObjectTypeExtension(lexer); case 'interface': return parseInterfaceTypeExtension(lexer); case 'union': return parseUnionTypeExtension(lexer); case 'enum': return parseEnumTypeExtension(lexer); case 'input': return parseInputObjectTypeExtension(lexer); } } throw unexpected(lexer, keywordToken); } /** * SchemaExtension : * - extend schema Directives[Const]? { OperationTypeDefinition+ } * - extend schema Directives[Const] */ function parseSchemaExtension(lexer) { var start = lexer.token; expectKeyword(lexer, 'extend'); expectKeyword(lexer, 'schema'); var directives = parseDirectives(lexer, true); var operationTypes = peek(lexer, TokenKind.BRACE_L) ? many(lexer, TokenKind.BRACE_L, parseOperationTypeDefinition, TokenKind.BRACE_R) : []; if (directives.length === 0 && operationTypes.length === 0) { throw unexpected(lexer); } return { kind: Kind.SCHEMA_EXTENSION, directives: directives, operationTypes: operationTypes, loc: loc(lexer, start) }; } /** * ScalarTypeExtension : * - extend scalar Name Directives[Const] */ function parseScalarTypeExtension(lexer) { var start = lexer.token; expectKeyword(lexer, 'extend'); expectKeyword(lexer, 'scalar'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); if (directives.length === 0) { throw unexpected(lexer); } return { kind: Kind.SCALAR_TYPE_EXTENSION, name: name, directives: directives, loc: loc(lexer, start) }; } /** * ObjectTypeExtension : * - extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition * - extend type Name ImplementsInterfaces? Directives[Const] * - extend type Name ImplementsInterfaces */ function parseObjectTypeExtension(lexer) { var start = lexer.token; expectKeyword(lexer, 'extend'); expectKeyword(lexer, 'type'); var name = parseName(lexer); var interfaces = parseImplementsInterfaces(lexer); var directives = parseDirectives(lexer, true); var fields = parseFieldsDefinition(lexer); if (interfaces.length === 0 && directives.length === 0 && fields.length === 0) { throw unexpected(lexer); } return { kind: Kind.OBJECT_TYPE_EXTENSION, name: name, interfaces: interfaces, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * InterfaceTypeExtension : * - extend interface Name Directives[Const]? FieldsDefinition * - extend interface Name Directives[Const] */ function parseInterfaceTypeExtension(lexer) { var start = lexer.token; expectKeyword(lexer, 'extend'); expectKeyword(lexer, 'interface'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); var fields = parseFieldsDefinition(lexer); if (directives.length === 0 && fields.length === 0) { throw unexpected(lexer); } return { kind: Kind.INTERFACE_TYPE_EXTENSION, name: name, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * UnionTypeExtension : * - extend union Name Directives[Const]? UnionMemberTypes * - extend union Name Directives[Const] */ function parseUnionTypeExtension(lexer) { var start = lexer.token; expectKeyword(lexer, 'extend'); expectKeyword(lexer, 'union'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); var types = parseUnionMemberTypes(lexer); if (directives.length === 0 && types.length === 0) { throw unexpected(lexer); } return { kind: Kind.UNION_TYPE_EXTENSION, name: name, directives: directives, types: types, loc: loc(lexer, start) }; } /** * EnumTypeExtension : * - extend enum Name Directives[Const]? EnumValuesDefinition * - extend enum Name Directives[Const] */ function parseEnumTypeExtension(lexer) { var start = lexer.token; expectKeyword(lexer, 'extend'); expectKeyword(lexer, 'enum'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); var values = parseEnumValuesDefinition(lexer); if (directives.length === 0 && values.length === 0) { throw unexpected(lexer); } return { kind: Kind.ENUM_TYPE_EXTENSION, name: name, directives: directives, values: values, loc: loc(lexer, start) }; } /** * InputObjectTypeExtension : * - extend input Name Directives[Const]? InputFieldsDefinition * - extend input Name Directives[Const] */ function parseInputObjectTypeExtension(lexer) { var start = lexer.token; expectKeyword(lexer, 'extend'); expectKeyword(lexer, 'input'); var name = parseName(lexer); var directives = parseDirectives(lexer, true); var fields = parseInputFieldsDefinition(lexer); if (directives.length === 0 && fields.length === 0) { throw unexpected(lexer); } return { kind: Kind.INPUT_OBJECT_TYPE_EXTENSION, name: name, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * DirectiveDefinition : * - Description? directive @ Name ArgumentsDefinition? on DirectiveLocations */ function parseDirectiveDefinition(lexer) { var start = lexer.token; var description = parseDescription(lexer); expectKeyword(lexer, 'directive'); expectToken(lexer, TokenKind.AT); var name = parseName(lexer); var args = parseArgumentDefs(lexer); expectKeyword(lexer, 'on'); var locations = parseDirectiveLocations(lexer); return { kind: Kind.DIRECTIVE_DEFINITION, description: description, name: name, arguments: args, locations: locations, loc: loc(lexer, start) }; } /** * DirectiveLocations : * - `|`? DirectiveLocation * - DirectiveLocations | DirectiveLocation */ function parseDirectiveLocations(lexer) { // Optional leading pipe expectOptionalToken(lexer, TokenKind.PIPE); var locations = []; do { locations.push(parseDirectiveLocation(lexer)); } while (expectOptionalToken(lexer, TokenKind.PIPE)); return locations; } /* * DirectiveLocation : * - ExecutableDirectiveLocation * - TypeSystemDirectiveLocation * * ExecutableDirectiveLocation : one of * `QUERY` * `MUTATION` * `SUBSCRIPTION` * `FIELD` * `FRAGMENT_DEFINITION` * `FRAGMENT_SPREAD` * `INLINE_FRAGMENT` * * TypeSystemDirectiveLocation : one of * `SCHEMA` * `SCALAR` * `OBJECT` * `FIELD_DEFINITION` * `ARGUMENT_DEFINITION` * `INTERFACE` * `UNION` * `ENUM` * `ENUM_VALUE` * `INPUT_OBJECT` * `INPUT_FIELD_DEFINITION` */ function parseDirectiveLocation(lexer) { var start = lexer.token; var name = parseName(lexer); if (DirectiveLocation[name.value] !== undefined) { return name; } throw unexpected(lexer, start); } // Core parsing utility functions /** * Returns a location object, used to identify the place in * the source that created a given parsed object. */ function loc(lexer, startToken) { if (!lexer.options.noLocation) { return new Loc(startToken, lexer.lastToken, lexer.source); } } function Loc(startToken, endToken, source) { this.start = startToken.start; this.end = endToken.end; this.startToken = startToken; this.endToken = endToken; this.source = source; } // Print a simplified form when appearing in JSON/util.inspect. defineToJSON(Loc, function () { return { start: this.start, end: this.end }; }); /** * Determines if the next token is of a given kind */ function peek(lexer, kind) { return lexer.token.kind === kind; } /** * If the next token is of the given kind, return that token after advancing * the lexer. Otherwise, do not change the parser state and throw an error. */ function expectToken(lexer, kind) { var token = lexer.token; if (token.kind === kind) { lexer.advance(); return token; } throw syntaxError(lexer.source, token.start, "Expected ".concat(kind, ", found ").concat(getTokenDesc(token))); } /** * If the next token is of the given kind, return that token after advancing * the lexer. Otherwise, do not change the parser state and return undefined. */ function expectOptionalToken(lexer, kind) { var token = lexer.token; if (token.kind === kind) { lexer.advance(); return token; } return undefined; } /** * If the next token is a given keyword, return that token after advancing * the lexer. Otherwise, do not change the parser state and throw an error. */ function expectKeyword(lexer, value) { var token = lexer.token; if (token.kind === TokenKind.NAME && token.value === value) { lexer.advance(); return token; } throw syntaxError(lexer.source, token.start, "Expected \"".concat(value, "\", found ").concat(getTokenDesc(token))); } /** * If the next token is a given keyword, return that token after advancing * the lexer. Otherwise, do not change the parser state and return undefined. */ function expectOptionalKeyword(lexer, value) { var token = lexer.token; if (token.kind === TokenKind.NAME && token.value === value) { lexer.advance(); return token; } return undefined; } /** * Helper function for creating an error when an unexpected lexed token * is encountered. */ function unexpected(lexer, atToken) { var token = atToken || lexer.token; return syntaxError(lexer.source, token.start, "Unexpected ".concat(getTokenDesc(token))); } /** * Returns a possibly empty list of parse nodes, determined by * the parseFn. This list begins with a lex token of openKind * and ends with a lex token of closeKind. Advances the parser * to the next lex token after the closing token. */ function any(lexer, openKind, parseFn, closeKind) { expectToken(lexer, openKind); var nodes = []; while (!expectOptionalToken(lexer, closeKind)) { nodes.push(parseFn(lexer)); } return nodes; } /** * Returns a non-empty list of parse nodes, determined by * the parseFn. This list begins with a lex token of openKind * and ends with a lex token of closeKind. Advances the parser * to the next lex token after the closing token. */ function many(lexer, openKind, parseFn, closeKind) { expectToken(lexer, openKind); var nodes = [parseFn(lexer)]; while (!expectOptionalToken(lexer, closeKind)) { nodes.push(parseFn(lexer)); } return nodes; }