UNPKG

parse-graphql

Version:
1,977 lines (1,680 loc) 71.7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** * 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. * * */ var nodejsCustomInspectSymbol = typeof Symbol === 'function' ? Symbol.for('nodejs.util.inspect.custom') : undefined; function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } var MAX_ARRAY_LENGTH = 10; var MAX_RECURSIVE_DEPTH = 2; /** * Used to print values in error messages. */ function inspect(value) { return formatValue(value, []); } function formatValue(value, seenValues) { switch (_typeof(value)) { case 'string': return JSON.stringify(value); case 'function': return value.name ? "[function ".concat(value.name, "]") : '[function]'; case 'object': return formatObjectValue(value, seenValues); default: return String(value); } } function formatObjectValue(value, previouslySeenValues) { if (previouslySeenValues.indexOf(value) !== -1) { return '[Circular]'; } var seenValues = [].concat(previouslySeenValues, [value]); if (value) { var customInspectFn = getCustomFn(value); if (customInspectFn) { // $FlowFixMe(>=0.90.0) var customValue = customInspectFn.call(value); // check for infinite recursion if (customValue !== value) { return typeof customValue === 'string' ? customValue : formatValue(customValue, seenValues); } } else if (Array.isArray(value)) { return formatArray(value, seenValues); } return formatObject(value, seenValues); } return String(value); } function formatObject(object, seenValues) { var keys = Object.keys(object); if (keys.length === 0) { return '{}'; } if (seenValues.length > MAX_RECURSIVE_DEPTH) { return '[' + getObjectTag(object) + ']'; } var properties = keys.map(function (key) { var value = formatValue(object[key], seenValues); return key + ': ' + value; }); return '{ ' + properties.join(', ') + ' }'; } function formatArray(array, seenValues) { if (array.length === 0) { return '[]'; } if (seenValues.length > MAX_RECURSIVE_DEPTH) { return '[Array]'; } var len = Math.min(MAX_ARRAY_LENGTH, array.length); var remaining = array.length - len; var items = []; for (var i = 0; i < len; ++i) { items.push(formatValue(array[i], seenValues)); } if (remaining === 1) { items.push('... 1 more item'); } else if (remaining > 1) { items.push("... ".concat(remaining, " more items")); } return '[' + items.join(', ') + ']'; } function getCustomFn(object) { var customInspectFn = object[String(nodejsCustomInspectSymbol)]; if (typeof customInspectFn === 'function') { return customInspectFn; } if (typeof object.inspect === 'function') { return object.inspect; } } function getObjectTag(object) { var tag = Object.prototype.toString.call(object).replace(/^\[object /, '').replace(/]$/, ''); if (tag === 'Object' && typeof object.constructor === 'function') { var name = object.constructor.name; if (typeof name === 'string') { return name; } } return tag; } /** * 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. * * */ /** * The `defineToJSON()` function defines toJSON() and inspect() prototype * methods, if no function provided they become aliases for toString(). */ function defineToJSON( // eslint-disable-next-line flowtype/no-weak-types classObject) { var fn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : classObject.prototype.toString; classObject.prototype.toJSON = fn; classObject.prototype.inspect = fn; if (nodejsCustomInspectSymbol) { classObject.prototype[nodejsCustomInspectSymbol] = fn; } } /** * 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. * * */ function invariant(condition, message) { /* istanbul ignore else */ if (!condition) { throw new Error(message); } } /** * 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. * * */ /** * The `defineToStringTag()` function checks first to see if the runtime * supports the `Symbol` class and then if the `Symbol.toStringTag` constant * is defined as a `Symbol` instance. If both conditions are met, the * Symbol.toStringTag property is defined as a getter that returns the * supplied class constructor's name. * * @method defineToStringTag * * @param {Class<any>} classObject a class such as Object, String, Number but * typically one of your own creation through the class keyword; `class A {}`, * for example. */ function defineToStringTag(classObject) { if (typeof Symbol === 'function' && Symbol.toStringTag) { Object.defineProperty(classObject.prototype, Symbol.toStringTag, { get: function get() { return this.constructor.name; } }); } } /** * 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. * * */ /** * A representation of source input to GraphQL. * `name` and `locationOffset` are optional. They are useful for clients who * store GraphQL documents in source files; for example, if the GraphQL input * starts at line 40 in a file named Foo.graphql, it might be useful for name to * be "Foo.graphql" and location to be `{ line: 40, column: 0 }`. * line and column in locationOffset are 1-indexed */ var Source = function Source(body, name, locationOffset) { this.body = body; this.name = name || 'GraphQL request'; this.locationOffset = locationOffset || { line: 1, column: 1 }; !(this.locationOffset.line > 0) ? invariant(0, 'line in locationOffset is 1-indexed and must be positive') : void 0; !(this.locationOffset.column > 0) ? invariant(0, 'column in locationOffset is 1-indexed and must be positive') : void 0; }; // Conditionally apply `[Symbol.toStringTag]` if `Symbol`s are supported defineToStringTag(Source); /** * 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. * * */ /** * Represents a location in a Source. */ /** * Takes a Source and a UTF-8 character offset, and returns the corresponding * line and column as a SourceLocation. */ function getLocation(source, position) { var lineRegexp = /\r\n|[\n\r]/g; var line = 1; var column = position + 1; var match; while ((match = lineRegexp.exec(source.body)) && match.index < position) { line += 1; column = position + 1 - (match.index + match[0].length); } return { line: line, column: column }; } /** * 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. * * */ /** * Prints a GraphQLError to a string, representing useful location information * about the error's position in the source. */ function printError(error) { var printedLocations = []; if (error.nodes) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = error.nodes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var node = _step.value; if (node.loc) { printedLocations.push(highlightSourceAtLocation(node.loc.source, getLocation(node.loc.source, node.loc.start))); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } else if (error.source && error.locations) { var source = error.source; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = error.locations[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var location = _step2.value; printedLocations.push(highlightSourceAtLocation(source, location)); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return != null) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } return printedLocations.length === 0 ? error.message : [error.message].concat(printedLocations).join('\n\n') + '\n'; } /** * Render a helpful description of the location of the error in the GraphQL * Source document. */ function highlightSourceAtLocation(source, location) { var firstLineColumnOffset = source.locationOffset.column - 1; var body = whitespace(firstLineColumnOffset) + source.body; var lineIndex = location.line - 1; var lineOffset = source.locationOffset.line - 1; var lineNum = location.line + lineOffset; var columnOffset = location.line === 1 ? firstLineColumnOffset : 0; var columnNum = location.column + columnOffset; var lines = body.split(/\r\n|[\n\r]/g); return "".concat(source.name, " (").concat(lineNum, ":").concat(columnNum, ")\n") + printPrefixedLines([// Lines specified like this: ["prefix", "string"], ["".concat(lineNum - 1, ": "), lines[lineIndex - 1]], ["".concat(lineNum, ": "), lines[lineIndex]], ['', whitespace(columnNum - 1) + '^'], ["".concat(lineNum + 1, ": "), lines[lineIndex + 1]]]); } function printPrefixedLines(lines) { var existingLines = lines.filter(function (_ref) { var _ = _ref[0], line = _ref[1]; return line !== undefined; }); var padLen = 0; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = existingLines[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var _ref4 = _step3.value; var prefix = _ref4[0]; padLen = Math.max(padLen, prefix.length); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return != null) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } return existingLines.map(function (_ref3) { var prefix = _ref3[0], line = _ref3[1]; return lpad(padLen, prefix) + line; }).join('\n'); } function whitespace(len) { return Array(len + 1).join(' '); } function lpad(len, str) { return whitespace(len - str.length) + str; } /** * 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. * * */ /** * A GraphQLError describes an Error found during the parse, validate, or * execute phases of performing a GraphQL operation. In addition to a message * and stack trace, it also includes information about the locations in a * GraphQL document and/or execution result that correspond to the Error. */ function GraphQLError( // eslint-disable-line no-redeclare message, nodes, source, positions, path, originalError, extensions) { // Compute list of blame nodes. var _nodes = Array.isArray(nodes) ? nodes.length !== 0 ? nodes : undefined : nodes ? [nodes] : undefined; // Compute locations in the source for the given nodes/positions. var _source = source; if (!_source && _nodes) { var node = _nodes[0]; _source = node && node.loc && node.loc.source; } var _positions = positions; if (!_positions && _nodes) { _positions = _nodes.reduce(function (list, node) { if (node.loc) { list.push(node.loc.start); } return list; }, []); } if (_positions && _positions.length === 0) { _positions = undefined; } var _locations; if (positions && source) { _locations = positions.map(function (pos) { return getLocation(source, pos); }); } else if (_nodes) { _locations = _nodes.reduce(function (list, node) { if (node.loc) { list.push(getLocation(node.loc.source, node.loc.start)); } return list; }, []); } var _extensions = extensions || originalError && originalError.extensions; Object.defineProperties(this, { message: { value: message, // By being enumerable, JSON.stringify will include `message` in the // resulting output. This ensures that the simplest possible GraphQL // service adheres to the spec. enumerable: true, writable: true }, locations: { // Coercing falsey values to undefined ensures they will not be included // in JSON.stringify() when not provided. value: _locations || undefined, // By being enumerable, JSON.stringify will include `locations` in the // resulting output. This ensures that the simplest possible GraphQL // service adheres to the spec. enumerable: Boolean(_locations) }, path: { // Coercing falsey values to undefined ensures they will not be included // in JSON.stringify() when not provided. value: path || undefined, // By being enumerable, JSON.stringify will include `path` in the // resulting output. This ensures that the simplest possible GraphQL // service adheres to the spec. enumerable: Boolean(path) }, nodes: { value: _nodes || undefined }, source: { value: _source || undefined }, positions: { value: _positions || undefined }, originalError: { value: originalError }, extensions: { // Coercing falsey values to undefined ensures they will not be included // in JSON.stringify() when not provided. value: _extensions || undefined, // By being enumerable, JSON.stringify will include `path` in the // resulting output. This ensures that the simplest possible GraphQL // service adheres to the spec. enumerable: Boolean(_extensions) } }); // Include (non-enumerable) stack trace. if (originalError && originalError.stack) { Object.defineProperty(this, 'stack', { value: originalError.stack, writable: true, configurable: true }); } else if (Error.captureStackTrace) { Error.captureStackTrace(this, GraphQLError); } else { Object.defineProperty(this, 'stack', { value: Error().stack, writable: true, configurable: true }); } } GraphQLError.prototype = Object.create(Error.prototype, { constructor: { value: GraphQLError }, name: { value: 'GraphQLError' }, toString: { value: function toString() { return printError(this); } } }); /** * 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. * * */ /** * Produces a GraphQLError representing a syntax error, containing useful * descriptive information about the syntax error's position in the source. */ function syntaxError(source, position, description) { return new GraphQLError("Syntax Error: ".concat(description), undefined, source, [position]); } /** * 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. * * */ /** * 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. * * */ /** * 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. * * */ /** * 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. * * */ /** * Produces the value of a block string from its parsed raw value, similar to * CoffeeScript's block string, Python's docstring trim or Ruby's strip_heredoc. * * This implements the GraphQL spec's BlockStringValue() static algorithm. */ function dedentBlockStringValue(rawString) { // Expand a block string's raw value into independent lines. var lines = rawString.split(/\r\n|[\n\r]/g); // Remove common indentation from all lines but first. var commonIndent = getBlockStringIndentation(lines); if (commonIndent !== 0) { for (var i = 1; i < lines.length; i++) { lines[i] = lines[i].slice(commonIndent); } } // Remove leading and trailing blank lines. while (lines.length > 0 && isBlank(lines[0])) { lines.shift(); } while (lines.length > 0 && isBlank(lines[lines.length - 1])) { lines.pop(); } // Return a string of the lines joined with U+000A. return lines.join('\n'); } // @internal function getBlockStringIndentation(lines) { var commonIndent = null; for (var i = 1; i < lines.length; i++) { var line = lines[i]; var indent = leadingWhitespace(line); if (indent === line.length) { continue; // skip empty lines } if (commonIndent === null || indent < commonIndent) { commonIndent = indent; if (commonIndent === 0) { break; } } } return commonIndent === null ? 0 : commonIndent; } function leadingWhitespace(str) { var i = 0; while (i < str.length && (str[i] === ' ' || str[i] === '\t')) { i++; } return i; } function isBlank(str) { return leadingWhitespace(str) === str.length; } /** * 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. * * */ /** * Given a Source object, this returns a Lexer for that source. * A Lexer is a stateful stream generator in that every time * it is advanced, it returns the next token in the Source. Assuming the * source lexes, the final Token emitted by the lexer will be of kind * EOF, after which the lexer will repeatedly return the same EOF token * whenever called. */ function createLexer(source, options) { var startOfFileToken = new Tok(TokenKind.SOF, 0, 0, 0, 0, null); var lexer = { source: source, options: options, lastToken: startOfFileToken, token: startOfFileToken, line: 1, lineStart: 0, advance: advanceLexer, lookahead: lookahead }; return lexer; } function advanceLexer() { this.lastToken = this.token; var token = this.token = this.lookahead(); return token; } function lookahead() { var token = this.token; if (token.kind !== TokenKind.EOF) { do { // Note: next is only mutable during parsing, so we cast to allow this. token = token.next || (token.next = readToken(this, token)); } while (token.kind === TokenKind.COMMENT); } return token; } /** * The return type of createLexer. */ /** * An exported enum describing the different kinds of tokens that the * lexer emits. */ var TokenKind = Object.freeze({ SOF: '<SOF>', EOF: '<EOF>', BANG: '!', DOLLAR: '$', AMP: '&', PAREN_L: '(', PAREN_R: ')', SPREAD: '...', COLON: ':', EQUALS: '=', AT: '@', BRACKET_L: '[', BRACKET_R: ']', BRACE_L: '{', PIPE: '|', BRACE_R: '}', NAME: 'Name', INT: 'Int', FLOAT: 'Float', STRING: 'String', BLOCK_STRING: 'BlockString', COMMENT: 'Comment' }); /** * A helper function to describe a token as a string for debugging */ function getTokenDesc(token) { var value = token.value; return value ? "".concat(token.kind, " \"").concat(value, "\"") : token.kind; } /** * Helper function for constructing the Token object. */ function Tok(kind, start, end, line, column, prev, value) { this.kind = kind; this.start = start; this.end = end; this.line = line; this.column = column; this.value = value; this.prev = prev; this.next = null; } // Print a simplified form when appearing in JSON/util.inspect. defineToJSON(Tok, function () { return { kind: this.kind, value: this.value, line: this.line, column: this.column }; }); function printCharCode(code) { return (// NaN/undefined represents access beyond the end of the file. isNaN(code) ? TokenKind.EOF : // Trust JSON for ASCII. code < 0x007f ? JSON.stringify(String.fromCharCode(code)) : // Otherwise print the escaped form. "\"\\u".concat(('00' + code.toString(16).toUpperCase()).slice(-4), "\"") ); } /** * Gets the next token from the source starting at the given position. * * This skips over whitespace until it finds the next lexable token, then lexes * punctuators immediately or calls the appropriate helper function for more * complicated tokens. */ function readToken(lexer, prev) { var source = lexer.source; var body = source.body; var bodyLength = body.length; var pos = positionAfterWhitespace(body, prev.end, lexer); var line = lexer.line; var col = 1 + pos - lexer.lineStart; if (pos >= bodyLength) { return new Tok(TokenKind.EOF, bodyLength, bodyLength, line, col, prev); } var code = body.charCodeAt(pos); // SourceCharacter switch (code) { // ! case 33: return new Tok(TokenKind.BANG, pos, pos + 1, line, col, prev); // # case 35: return readComment(source, pos, line, col, prev); // $ case 36: return new Tok(TokenKind.DOLLAR, pos, pos + 1, line, col, prev); // & case 38: return new Tok(TokenKind.AMP, pos, pos + 1, line, col, prev); // ( case 40: return new Tok(TokenKind.PAREN_L, pos, pos + 1, line, col, prev); // ) case 41: return new Tok(TokenKind.PAREN_R, pos, pos + 1, line, col, prev); // . case 46: if (body.charCodeAt(pos + 1) === 46 && body.charCodeAt(pos + 2) === 46) { return new Tok(TokenKind.SPREAD, pos, pos + 3, line, col, prev); } break; // : case 58: return new Tok(TokenKind.COLON, pos, pos + 1, line, col, prev); // = case 61: return new Tok(TokenKind.EQUALS, pos, pos + 1, line, col, prev); // @ case 64: return new Tok(TokenKind.AT, pos, pos + 1, line, col, prev); // [ case 91: return new Tok(TokenKind.BRACKET_L, pos, pos + 1, line, col, prev); // ] case 93: return new Tok(TokenKind.BRACKET_R, pos, pos + 1, line, col, prev); // { case 123: return new Tok(TokenKind.BRACE_L, pos, pos + 1, line, col, prev); // | case 124: return new Tok(TokenKind.PIPE, pos, pos + 1, line, col, prev); // } case 125: return new Tok(TokenKind.BRACE_R, pos, pos + 1, line, col, prev); // A-Z _ a-z case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: case 78: case 79: case 80: case 81: case 82: case 83: case 84: case 85: case 86: case 87: case 88: case 89: case 90: case 95: case 97: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 109: case 110: case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: return readName(source, pos, line, col, prev); // - 0-9 case 45: case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: return readNumber(source, pos, code, line, col, prev); // " case 34: if (body.charCodeAt(pos + 1) === 34 && body.charCodeAt(pos + 2) === 34) { return readBlockString(source, pos, line, col, prev, lexer); } return readString(source, pos, line, col, prev); } throw syntaxError(source, pos, unexpectedCharacterMessage(code)); } /** * Report a message that an unexpected character was encountered. */ function unexpectedCharacterMessage(code) { if (code < 0x0020 && code !== 0x0009 && code !== 0x000a && code !== 0x000d) { return "Cannot contain the invalid character ".concat(printCharCode(code), "."); } if (code === 39) { // ' return "Unexpected single quote character ('), did you mean to use " + 'a double quote (")?'; } return "Cannot parse the unexpected character ".concat(printCharCode(code), "."); } /** * Reads from body starting at startPosition until it finds a non-whitespace * character, then returns the position of that character for lexing. */ function positionAfterWhitespace(body, startPosition, lexer) { var bodyLength = body.length; var position = startPosition; while (position < bodyLength) { var code = body.charCodeAt(position); // tab | space | comma | BOM if (code === 9 || code === 32 || code === 44 || code === 0xfeff) { ++position; } else if (code === 10) { // new line ++position; ++lexer.line; lexer.lineStart = position; } else if (code === 13) { // carriage return if (body.charCodeAt(position + 1) === 10) { position += 2; } else { ++position; } ++lexer.line; lexer.lineStart = position; } else { break; } } return position; } /** * Reads a comment token from the source file. * * #[\u0009\u0020-\uFFFF]* */ function readComment(source, start, line, col, prev) { var body = source.body; var code; var position = start; do { code = body.charCodeAt(++position); } while (!isNaN(code) && ( // SourceCharacter but not LineTerminator code > 0x001f || code === 0x0009)); return new Tok(TokenKind.COMMENT, start, position, line, col, prev, body.slice(start + 1, position)); } /** * Reads a number token from the source file, either a float * or an int depending on whether a decimal point appears. * * Int: -?(0|[1-9][0-9]*) * Float: -?(0|[1-9][0-9]*)(\.[0-9]+)?((E|e)(+|-)?[0-9]+)? */ function readNumber(source, start, firstCode, line, col, prev) { var body = source.body; var code = firstCode; var position = start; var isFloat = false; if (code === 45) { // - code = body.charCodeAt(++position); } if (code === 48) { // 0 code = body.charCodeAt(++position); if (code >= 48 && code <= 57) { throw syntaxError(source, position, "Invalid number, unexpected digit after 0: ".concat(printCharCode(code), ".")); } } else { position = readDigits(source, position, code); code = body.charCodeAt(position); } if (code === 46) { // . isFloat = true; code = body.charCodeAt(++position); position = readDigits(source, position, code); code = body.charCodeAt(position); } if (code === 69 || code === 101) { // E e isFloat = true; code = body.charCodeAt(++position); if (code === 43 || code === 45) { // + - code = body.charCodeAt(++position); } position = readDigits(source, position, code); } return new Tok(isFloat ? TokenKind.FLOAT : TokenKind.INT, start, position, line, col, prev, body.slice(start, position)); } /** * Returns the new position in the source after reading digits. */ function readDigits(source, start, firstCode) { var body = source.body; var position = start; var code = firstCode; if (code >= 48 && code <= 57) { // 0 - 9 do { code = body.charCodeAt(++position); } while (code >= 48 && code <= 57); // 0 - 9 return position; } throw syntaxError(source, position, "Invalid number, expected digit but got: ".concat(printCharCode(code), ".")); } /** * Reads a string token from the source file. * * "([^"\\\u000A\u000D]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*" */ function readString(source, start, line, col, prev) { var body = source.body; var position = start + 1; var chunkStart = position; var code = 0; var value = ''; while (position < body.length && !isNaN(code = body.charCodeAt(position)) && // not LineTerminator code !== 0x000a && code !== 0x000d) { // Closing Quote (") if (code === 34) { value += body.slice(chunkStart, position); return new Tok(TokenKind.STRING, start, position + 1, line, col, prev, value); } // SourceCharacter if (code < 0x0020 && code !== 0x0009) { throw syntaxError(source, position, "Invalid character within String: ".concat(printCharCode(code), ".")); } ++position; if (code === 92) { // \ value += body.slice(chunkStart, position - 1); code = body.charCodeAt(position); switch (code) { case 34: value += '"'; break; case 47: value += '/'; break; case 92: value += '\\'; break; case 98: value += '\b'; break; case 102: value += '\f'; break; case 110: value += '\n'; break; case 114: value += '\r'; break; case 116: value += '\t'; break; case 117: // u var charCode = uniCharCode(body.charCodeAt(position + 1), body.charCodeAt(position + 2), body.charCodeAt(position + 3), body.charCodeAt(position + 4)); if (charCode < 0) { throw syntaxError(source, position, 'Invalid character escape sequence: ' + "\\u".concat(body.slice(position + 1, position + 5), ".")); } value += String.fromCharCode(charCode); position += 4; break; default: throw syntaxError(source, position, "Invalid character escape sequence: \\".concat(String.fromCharCode(code), ".")); } ++position; chunkStart = position; } } throw syntaxError(source, position, 'Unterminated string.'); } /** * Reads a block string token from the source file. * * """("?"?(\\"""|\\(?!=""")|[^"\\]))*""" */ function readBlockString(source, start, line, col, prev, lexer) { var body = source.body; var position = start + 3; var chunkStart = position; var code = 0; var rawValue = ''; while (position < body.length && !isNaN(code = body.charCodeAt(position))) { // Closing Triple-Quote (""") if (code === 34 && body.charCodeAt(position + 1) === 34 && body.charCodeAt(position + 2) === 34) { rawValue += body.slice(chunkStart, position); return new Tok(TokenKind.BLOCK_STRING, start, position + 3, line, col, prev, dedentBlockStringValue(rawValue)); } // SourceCharacter if (code < 0x0020 && code !== 0x0009 && code !== 0x000a && code !== 0x000d) { throw syntaxError(source, position, "Invalid character within String: ".concat(printCharCode(code), ".")); } if (code === 10) { // new line ++position; ++lexer.line; lexer.lineStart = position; } else if (code === 13) { // carriage return if (body.charCodeAt(position + 1) === 10) { position += 2; } else { ++position; } ++lexer.line; lexer.lineStart = position; } else if ( // Escape Triple-Quote (\""") code === 92 && body.charCodeAt(position + 1) === 34 && body.charCodeAt(position + 2) === 34 && body.charCodeAt(position + 3) === 34) { rawValue += body.slice(chunkStart, position) + '"""'; position += 4; chunkStart = position; } else { ++position; } } throw syntaxError(source, position, 'Unterminated string.'); } /** * Converts four hexadecimal chars to the integer that the * string represents. For example, uniCharCode('0','0','0','f') * will return 15, and uniCharCode('0','0','f','f') returns 255. * * Returns a negative number on error, if a char was invalid. * * This is implemented by noting that char2hex() returns -1 on error, * which means the result of ORing the char2hex() will also be negative. */ function uniCharCode(a, b, c, d) { return char2hex(a) << 12 | char2hex(b) << 8 | char2hex(c) << 4 | char2hex(d); } /** * Converts a hex character to its integer value. * '0' becomes 0, '9' becomes 9 * 'A' becomes 10, 'F' becomes 15 * 'a' becomes 10, 'f' becomes 15 * * Returns -1 on error. */ function char2hex(a) { return a >= 48 && a <= 57 ? a - 48 // 0-9 : a >= 65 && a <= 70 ? a - 55 // A-F : a >= 97 && a <= 102 ? a - 87 // a-f : -1; } /** * Reads an alphanumeric + underscore name from the source. * * [_A-Za-z][_0-9A-Za-z]* */ function readName(source, start, line, col, prev) { var body = source.body; var bodyLength = body.length; var position = start + 1; var code = 0; while (position !== bodyLength && !isNaN(code = body.charCodeAt(position)) && (code === 95 || // _ code >= 48 && code <= 57 || // 0-9 code >= 65 && code <= 90 || // A-Z code >= 97 && code <= 122) // a-z ) { ++position; } return new Tok(TokenKind.NAME, start, position, line, col, prev, body.slice(start, position)); } /** * 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. * * */ /** * The set of allowed kind values for AST nodes. */ var Kind = Object.freeze({ // Name NAME: 'Name', // Document DOCUMENT: 'Document', OPERATION_DEFINITION: 'OperationDefinition', VARIABLE_DEFINITION: 'VariableDefinition', SELECTION_SET: 'SelectionSet', FIELD: 'Field', ARGUMENT: 'Argument', // Fragments FRAGMENT_SPREAD: 'FragmentSpread', INLINE_FRAGMENT: 'InlineFragment', FRAGMENT_DEFINITION: 'FragmentDefinition', // Values VARIABLE: 'Variable', INT: 'IntValue', FLOAT: 'FloatValue', STRING: 'StringValue', BOOLEAN: 'BooleanValue', NULL: 'NullValue', ENUM: 'EnumValue', LIST: 'ListValue', OBJECT: 'ObjectValue', OBJECT_FIELD: 'ObjectField', // Directives DIRECTIVE: 'Directive', // Types NAMED_TYPE: 'NamedType', LIST_TYPE: 'ListType', NON_NULL_TYPE: 'NonNullType', // Type System Definitions SCHEMA_DEFINITION: 'SchemaDefinition', OPERATION_TYPE_DEFINITION: 'OperationTypeDefinition', // Type Definitions SCALAR_TYPE_DEFINITION: 'ScalarTypeDefinition', OBJECT_TYPE_DEFINITION: 'ObjectTypeDefinition', FIELD_DEFINITION: 'FieldDefinition', INPUT_VALUE_DEFINITION: 'InputValueDefinition', INTERFACE_TYPE_DEFINITION: 'InterfaceTypeDefinition', UNION_TYPE_DEFINITION: 'UnionTypeDefinition', ENUM_TYPE_DEFINITION: 'EnumTypeDefinition', ENUM_VALUE_DEFINITION: 'EnumValueDefinition', INPUT_OBJECT_TYPE_DEFINITION: 'InputObjectTypeDefinition', // Directive Definitions DIRECTIVE_DEFINITION: 'DirectiveDefinition', // Type System Extensions SCHEMA_EXTENSION: 'SchemaExtension', // Type Extensions SCALAR_TYPE_EXTENSION: 'ScalarTypeExtension', OBJECT_TYPE_EXTENSION: 'ObjectTypeExtension', INTERFACE_TYPE_EXTENSION: 'InterfaceTypeExtension', UNION_TYPE_EXTENSION: 'UnionTypeExtension', ENUM_TYPE_EXTENSION: 'EnumTypeExtension', INPUT_OBJECT_TYPE_EXTENSION: 'InputObjectTypeExtension' }); /** * The enum type representing the possible kind values of AST nodes. */ /** * 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. * * */ /** * The set of allowed directive location values. */ var DirectiveLocation = Object.freeze({ // Request Definitions QUERY: 'QUERY', MUTATION: 'MUTATION', SUBSCRIPTION: 'SUBSCRIPTION', FIELD: 'FIELD', FRAGMENT_DEFINITION: 'FRAGMENT_DEFINITION', FRAGMENT_SPREAD: 'FRAGMENT_SPREAD', INLINE_FRAGMENT: 'INLINE_FRAGMENT', VARIABLE_DEFINITION: 'VARIABLE_DEFINITION', // Type System Definitions SCHEMA: 'SCHEMA', SCALAR: 'SCALAR', OBJECT: 'OBJECT', FIELD_DEFINITION: 'FIELD_DEFINITION', ARGUMENT_DEFINITION: 'ARGUMENT_DEFINITION', INTERFACE: 'INTERFACE', UNION: 'UNION', ENUM: 'ENUM', ENUM_VALUE: 'ENUM_VALUE', INPUT_OBJECT: 'INPUT_OBJECT', INPUT_FIELD_DEFINITION: 'INPUT_FIELD_DEFINITION' }); /** * The enum type representing the directive location values. */ /** * 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. * * */ /** * Configuration options to control parser behavior */ /** * Given a GraphQL source, parses it into a Document. * Throws GraphQLError if a syntax error is encountered. */ 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); } /** * 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) }; } 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[Co