UNPKG

graphql

Version:

A Query Language and Runtime which can target any service.

521 lines (468 loc) 13.8 kB
/** * Copyright (c) 2015, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ 'use strict'; /** * Configuration options to control parser behavior */ var _Object$defineProperty = require('babel-runtime/core-js/object/define-property')['default']; _Object$defineProperty(exports, '__esModule', { value: true }); exports.parse = parse; var _source = require('./source'); var _error = require('./error'); var _lexer = require('./lexer'); var _kinds = require('./kinds'); /** * Given a GraphQL source, parses it into a Document. Throws on error. */ function parse(source, options) { var sourceObj = source instanceof _source.Source ? source : new _source.Source(source); var parser = makeParser(sourceObj, options || {}); return parseDocument(parser); } /** * Returns the parser object that is used to store state throughout the * process of parsing. */ function makeParser(source, options) { var _lexToken = (0, _lexer.lex)(source); return { _lexToken: _lexToken, source: source, options: options, prevEnd: 0, token: _lexToken() }; } /** * Returns a location object, used to identify the place in * the source that created a given parsed object. */ function loc(parser, start) { if (parser.options.noLocation) { return null; } if (parser.options.noSource) { return { start: start, end: parser.prevEnd }; } return { start: start, end: parser.prevEnd, source: parser.source }; } /** * Moves the internal parser object to the next lexed token. */ function advance(parser) { var prevEnd = parser.token.end; parser.prevEnd = prevEnd; parser.token = parser._lexToken(prevEnd); } /** * Determines if the next token is of a given kind */ function peek(parser, kind) { return parser.token.kind === kind; } /** * If the next token is of the given kind, return true after advancing * the parser. Otherwise, do not change the parser state and return false. */ function skip(parser, kind) { var match = parser.token.kind === kind; if (match) { advance(parser); } return match; } /** * If the next token is of the given kind, return that token after advancing * the parser. Otherwise, do not change the parser state and return false. */ function expect(parser, kind) { var token = parser.token; if (token.kind === kind) { advance(parser); return token; } throw (0, _error.error)(parser.source, token.start, 'Expected ' + (0, _lexer.getTokenKindDesc)(kind) + ', found ' + (0, _lexer.getTokenDesc)(token)); } /** * If the next token is a keyword with the given value, return that token after * advancing the parser. Otherwise, do not change the parser state and return * false. */ function expectKeyword(parser, value) { var token = parser.token; if (token.kind === _lexer.TokenKind.NAME && token.value === value) { advance(parser); return token; } throw (0, _error.error)(parser.source, token.start, 'Expected "' + value + '", found ' + (0, _lexer.getTokenDesc)(token)); } /** * Helper function for creating an error when an unexpected lexed token * is encountered. */ function unexpected(parser, atToken) { var token = atToken || parser.token; return (0, _error.error)(parser.source, token.start, 'Unexpected ' + (0, _lexer.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(parser, openKind, parseFn, closeKind) { expect(parser, openKind); var nodes = []; while (!skip(parser, closeKind)) { nodes.push(parseFn(parser)); } 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(parser, openKind, parseFn, closeKind) { expect(parser, openKind); var nodes = [parseFn(parser)]; while (!skip(parser, closeKind)) { nodes.push(parseFn(parser)); } return nodes; } /** * Converts a name lex token into a name parse node. */ function parseName(parser) { var token = expect(parser, _lexer.TokenKind.NAME); return { kind: _kinds.NAME, value: token.value, loc: loc(parser, token.start) }; } // Implements the parsing rules in the Document section. function parseDocument(parser) { var start = parser.token.start; var definitions = []; do { if (peek(parser, _lexer.TokenKind.BRACE_L)) { definitions.push(parseOperationDefinition(parser)); } else if (peek(parser, _lexer.TokenKind.NAME)) { if (parser.token.value === 'query' || parser.token.value === 'mutation') { definitions.push(parseOperationDefinition(parser)); } else if (parser.token.value === 'fragment') { definitions.push(parseFragmentDefinition(parser)); } else { throw unexpected(parser); } } else { throw unexpected(parser); } } while (!skip(parser, _lexer.TokenKind.EOF)); return { kind: _kinds.DOCUMENT, definitions: definitions, loc: loc(parser, start) }; } // Implements the parsing rules in the Operations section. function parseOperationDefinition(parser) { var start = parser.token.start; if (peek(parser, _lexer.TokenKind.BRACE_L)) { return { kind: _kinds.OPERATION_DEFINITION, operation: 'query', name: null, variableDefinitions: null, directives: [], selectionSet: parseSelectionSet(parser), loc: loc(parser, start) }; } var operationToken = expect(parser, _lexer.TokenKind.NAME); var operation = operationToken.value; return { kind: _kinds.OPERATION_DEFINITION, operation: operation, name: parseName(parser), variableDefinitions: parseVariableDefinitions(parser), directives: parseDirectives(parser), selectionSet: parseSelectionSet(parser), loc: loc(parser, start) }; } function parseVariableDefinitions(parser) { return peek(parser, _lexer.TokenKind.PAREN_L) ? many(parser, _lexer.TokenKind.PAREN_L, parseVariableDefinition, _lexer.TokenKind.PAREN_R) : []; } function parseVariableDefinition(parser) { var start = parser.token.start; return { kind: _kinds.VARIABLE_DEFINITION, variable: parseVariable(parser), type: (expect(parser, _lexer.TokenKind.COLON), parseType(parser)), defaultValue: skip(parser, _lexer.TokenKind.EQUALS) ? parseValue(parser, true) : null, loc: loc(parser, start) }; } function parseVariable(parser) { var start = parser.token.start; expect(parser, _lexer.TokenKind.DOLLAR); return { kind: _kinds.VARIABLE, name: parseName(parser), loc: loc(parser, start) }; } function parseSelectionSet(parser) { var start = parser.token.start; return { kind: _kinds.SELECTION_SET, selections: many(parser, _lexer.TokenKind.BRACE_L, parseSelection, _lexer.TokenKind.BRACE_R), loc: loc(parser, start) }; } function parseSelection(parser) { return peek(parser, _lexer.TokenKind.SPREAD) ? parseFragment(parser) : parseField(parser); } /** * Corresponds to both Field and Alias in the spec */ function parseField(parser) { var start = parser.token.start; var nameOrAlias = parseName(parser); var alias; var name; if (skip(parser, _lexer.TokenKind.COLON)) { alias = nameOrAlias; name = parseName(parser); } else { alias = null; name = nameOrAlias; } return { kind: _kinds.FIELD, alias: alias, name: name, arguments: parseArguments(parser), directives: parseDirectives(parser), selectionSet: peek(parser, _lexer.TokenKind.BRACE_L) ? parseSelectionSet(parser) : null, loc: loc(parser, start) }; } function parseArguments(parser) { return peek(parser, _lexer.TokenKind.PAREN_L) ? many(parser, _lexer.TokenKind.PAREN_L, parseArgument, _lexer.TokenKind.PAREN_R) : []; } function parseArgument(parser) { var start = parser.token.start; return { kind: _kinds.ARGUMENT, name: parseName(parser), value: (expect(parser, _lexer.TokenKind.COLON), parseValue(parser, false)), loc: loc(parser, start) }; } // Implements the parsing rules in the Fragments section. /** * Corresponds to both FragmentSpread and InlineFragment in the spec */ function parseFragment(parser) { var start = parser.token.start; expect(parser, _lexer.TokenKind.SPREAD); if (parser.token.value === 'on') { advance(parser); return { kind: _kinds.INLINE_FRAGMENT, typeCondition: parseName(parser), directives: parseDirectives(parser), selectionSet: parseSelectionSet(parser), loc: loc(parser, start) }; } return { kind: _kinds.FRAGMENT_SPREAD, name: parseName(parser), directives: parseDirectives(parser), loc: loc(parser, start) }; } function parseFragmentDefinition(parser) { var start = parser.token.start; expectKeyword(parser, 'fragment'); return { kind: _kinds.FRAGMENT_DEFINITION, name: parseName(parser), typeCondition: (expectKeyword(parser, 'on'), parseName(parser)), directives: parseDirectives(parser), selectionSet: parseSelectionSet(parser), loc: loc(parser, start) }; } // Implements the parsing rules in the Values section. function parseVariableValue(parser) { return parseValue(parser, false); } function parseConstValue(parser) { return parseValue(parser, true); } function parseValue(parser, isConst) { var token = parser.token; switch (token.kind) { case _lexer.TokenKind.BRACKET_L: return parseArray(parser, isConst); case _lexer.TokenKind.BRACE_L: return parseObject(parser, isConst); case _lexer.TokenKind.INT: advance(parser); return { kind: _kinds.INT, value: token.value, loc: loc(parser, token.start) }; case _lexer.TokenKind.FLOAT: advance(parser); return { kind: _kinds.FLOAT, value: token.value, loc: loc(parser, token.start) }; case _lexer.TokenKind.STRING: advance(parser); return { kind: _kinds.STRING, value: token.value, loc: loc(parser, token.start) }; case _lexer.TokenKind.NAME: advance(parser); switch (token.value) { case 'true': case 'false': return { kind: _kinds.BOOLEAN, value: token.value === 'true', loc: loc(parser, token.start) }; } return { kind: _kinds.ENUM, value: token.value, loc: loc(parser, token.start) }; case _lexer.TokenKind.DOLLAR: if (!isConst) { return parseVariable(parser); } break; } throw unexpected(parser); } function parseArray(parser, isConst) { var start = parser.token.start; var item = isConst ? parseConstValue : parseVariableValue; return { kind: _kinds.ARRAY, values: any(parser, _lexer.TokenKind.BRACKET_L, item, _lexer.TokenKind.BRACKET_R), loc: loc(parser, start) }; } function parseObject(parser, isConst) { var start = parser.token.start; expect(parser, _lexer.TokenKind.BRACE_L); var fieldNames = {}; var fields = []; while (!skip(parser, _lexer.TokenKind.BRACE_R)) { fields.push(parseObjectField(parser, isConst, fieldNames)); } return { kind: _kinds.OBJECT, fields: fields, loc: loc(parser, start) }; } function parseObjectField(parser, isConst, fieldNames) { var start = parser.token.start; var name = parseName(parser); if (fieldNames.hasOwnProperty(name.value)) { throw (0, _error.error)(parser.source, start, 'Duplicate input object field ' + name.value + '.'); } fieldNames[name.value] = true; return { kind: _kinds.OBJECT_FIELD, name: name, value: (expect(parser, _lexer.TokenKind.COLON), parseValue(parser, isConst)), loc: loc(parser, start) }; } // Implements the parsing rules in the Directives section. function parseDirectives(parser) { var directives = []; while (peek(parser, _lexer.TokenKind.AT)) { directives.push(parseDirective(parser)); } return directives; } function parseDirective(parser) { var start = parser.token.start; expect(parser, _lexer.TokenKind.AT); return { kind: _kinds.DIRECTIVE, name: parseName(parser), value: skip(parser, _lexer.TokenKind.COLON) ? parseValue(parser, false) : null, loc: loc(parser, start) }; } // Implements the parsing rules in the Types section. /** * Handles the Type: TypeName, ListType, and NonNullType parsing rules. */ function parseType(parser) { var start = parser.token.start; var type; if (skip(parser, _lexer.TokenKind.BRACKET_L)) { type = parseType(parser); expect(parser, _lexer.TokenKind.BRACKET_R); type = { kind: _kinds.LIST_TYPE, type: type, loc: loc(parser, start) }; } else { type = parseName(parser); } if (skip(parser, _lexer.TokenKind.BANG)) { return { kind: _kinds.NON_NULL_TYPE, type: type, loc: loc(parser, start) }; } return type; } /** * By default, the parser creates AST nodes that know the location * in the source that they correspond to. This configuration flag * disables that behavior for performance or testing. */ /** * By default, the parser creates AST nodes that contain a reference * to the source that they were created from. This configuration flag * disables that behavior for performance or testing. */