UNPKG

@ardatan/relay-compiler

Version:

A compiler tool for building GraphQL-driven applications.

1,382 lines (1,110 loc) 47.8 kB
/** * 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. * * * @format */ // flowlint ambiguous-object-type:error 'use strict'; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2")); var _createForOfIteratorHelper2 = _interopRequireDefault(require("@babel/runtime/helpers/createForOfIteratorHelper")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var Profiler = require('./GraphQLCompilerProfiler'); var orList = require('../util/orList'); var partitionArray = require('../util/partitionArray'); var _require = require('../util/DefaultHandleKey'), DEFAULT_HANDLE_KEY = _require.DEFAULT_HANDLE_KEY; var _require2 = require('./CompilerError'), createCompilerError = _require2.createCompilerError, createUserError = _require2.createUserError, eachWithCombinedError = _require2.eachWithCombinedError; var _require3 = require('./SchemaUtils'), isExecutableDefinitionAST = _require3.isExecutableDefinitionAST; var _require4 = require('./getFieldDefinition'), getFieldDefinitionLegacy = _require4.getFieldDefinitionLegacy; var _require5 = require('graphql'), parseGraphQL = _require5.parse, parseType = _require5.parseType, print = _require5.print, Source = _require5.Source; var ARGUMENT_DEFINITIONS = 'argumentDefinitions'; var ARGUMENTS = 'arguments'; var DEPRECATED_UNCHECKED_ARGUMENTS = 'uncheckedArguments_DEPRECATED'; var DIRECTIVE_WHITELIST = new Set([ARGUMENT_DEFINITIONS, DEPRECATED_UNCHECKED_ARGUMENTS, ARGUMENTS]); /** * @internal * * This directive is not intended for use by developers directly. To set a field * handle in product code use a compiler plugin. */ var CLIENT_FIELD = '__clientField'; var CLIENT_FIELD_HANDLE = 'handle'; var CLIENT_FIELD_KEY = 'key'; var CLIENT_FIELD_FILTERS = 'filters'; var INCLUDE = 'include'; var SKIP = 'skip'; var IF = 'if'; /** * Transforms GraphQL text into Relay Compiler's internal, strongly-typed * intermediate representation (IR). */ function parse(schema, text, filename) { var ast = parseGraphQL(new Source(text, filename)); var parser = new RelayParser(schema.extend(ast), ast.definitions); return parser.transform(); } /** * Transforms untyped GraphQL parse trees (ASTs) into Relay Compiler's * internal, strongly-typed intermediate representation (IR). */ function transform(schema, definitions) { return Profiler.run('RelayParser.transform', function () { var parser = new RelayParser(schema, definitions); return parser.transform(); }); } /** * @private */ var RelayParser = /*#__PURE__*/function () { function RelayParser(schema, definitions) { var _this = this; this._definitions = new Map(); // leaving this configurable to make it easy to experiment w changing later this._getFieldDefinition = getFieldDefinitionLegacy; this._schema = schema; var duplicated = new Set(); definitions.forEach(function (def) { if (isExecutableDefinitionAST(def)) { var name = getName(def); if (_this._definitions.has(name)) { duplicated.add(name); return; } _this._definitions.set(name, def); } }); if (duplicated.size) { throw createUserError('RelayParser: Encountered duplicate definitions for one or more ' + 'documents: each document must have a unique name. Duplicated documents:\n' + Array.from(duplicated, function (name) { return "- ".concat(name); }).join('\n')); } } var _proto = RelayParser.prototype; _proto.transform = function transform() { var _this2 = this; var nodes = []; var entries = new Map(); // Construct a mapping of name to definition ast + variable definitions. // This allows the subsequent AST -> IR tranformation to reference the // defined arguments of referenced fragments. eachWithCombinedError(this._definitions, function (_ref) { var name = _ref[0], definition = _ref[1]; var variableDefinitions = _this2._buildArgumentDefinitions(definition); entries.set(name, { definition: definition, variableDefinitions: variableDefinitions }); }); // Convert the ASTs to IR. eachWithCombinedError(entries.values(), function (_ref2) { var definition = _ref2.definition, variableDefinitions = _ref2.variableDefinitions; var node = parseDefinition(_this2._schema, _this2._getFieldDefinition, entries, definition, variableDefinitions); nodes.push(node); }); return nodes; } /** * Constructs a mapping of variable names to definitions for the given * operation/fragment definition. */ ; _proto._buildArgumentDefinitions = function _buildArgumentDefinitions(definition) { switch (definition.kind) { case 'OperationDefinition': return this._buildOperationArgumentDefinitions(definition); case 'FragmentDefinition': return this._buildFragmentArgumentDefinitions(definition); default: definition; throw createCompilerError("Unexpected ast kind '".concat(definition.kind, "'."), [definition]); } } /** * Constructs a mapping of variable names to definitions using the * variables defined in `@argumentDefinitions`. */ ; _proto._buildFragmentArgumentDefinitions = function _buildFragmentArgumentDefinitions(fragment) { var _this3 = this; var variableDirectives = (fragment.directives || []).filter(function (directive) { return getName(directive) === ARGUMENT_DEFINITIONS; }); if (!variableDirectives.length) { return new Map(); } if (variableDirectives.length !== 1) { throw createUserError("Directive @".concat(ARGUMENT_DEFINITIONS, " may be defined at most once per ") + 'fragment.', null, variableDirectives); } var variableDirective = variableDirectives[0]; // work, below accesses all report arguments could still be null/undefined. var args = variableDirective.arguments; if (variableDirective == null || !Array.isArray(args)) { return new Map(); } if (!args.length) { throw createUserError("Directive @".concat(ARGUMENT_DEFINITIONS, " requires arguments: remove the ") + 'directive to skip defining local variables for this fragment.', null, [variableDirective]); } var variables = new Map(); args.forEach(function (arg) { var _defaultValue$value; var argName = getName(arg); var previousVariable = variables.get(argName); if (previousVariable != null) { throw createUserError("Duplicate definition for variable '$".concat(argName, "'."), null, [previousVariable.ast, arg]); } if (arg.value.kind !== 'ObjectValue') { throw createUserError("Expected definition for variable '$".concat(argName, "' to be an object ") + "with the shape: '{type: string, defaultValue?: mixed}.", null, [arg.value]); } var defaultValueNode; var typeString; arg.value.fields.forEach(function (field) { var name = getName(field); if (name === 'type') { typeString = transformLiteralValue(field.value, field); } else if (name === 'defaultValue') { defaultValueNode = field.value; } else { throw createUserError("Expected definition for variable '$".concat(argName, "' to be an object ") + "with the shape: '{type: string, defaultValue?: mixed}.", null, [arg.value]); } }); if (typeof typeString !== 'string') { throw createUserError("Expected definition for variable '$".concat(argName, "' to be an object ") + "with the shape: '{type: string, defaultValue?: mixed}.", null, [arg.value]); } var typeFromAST = _this3._schema.getTypeFromAST(parseType(typeString)); if (typeFromAST == null) { throw createUserError( // $FlowFixMe[incompatible-type] "Unknown type \"".concat(typeString, "\" referenced in the argument definitions."), null, [arg]); } var type = _this3._schema.asInputType(typeFromAST); if (type == null) { throw createUserError( // $FlowFixMe[incompatible-type] "Expected type \"".concat(typeString, "\" to be an input type in the \"").concat(arg.name.value, "\" argument definitions."), null, [arg.value]); } var defaultValue = defaultValueNode != null ? transformValue(_this3._schema, defaultValueNode, type, function (variableAst) { throw createUserError("Expected 'defaultValue' to be a literal, got a variable.", null, [variableAst]); }) : null; if (defaultValue != null && defaultValue.kind !== 'Literal') { throw createUserError("Expected 'defaultValue' to be a literal, got a variable.", [defaultValue.loc]); } variables.set(argName, { ast: arg, defaultValue: (_defaultValue$value = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.value) !== null && _defaultValue$value !== void 0 ? _defaultValue$value : null, defined: true, name: argName, type: type }); }); return variables; } /** * Constructs a mapping of variable names to definitions using the * standard GraphQL syntax for variable definitions. */ ; _proto._buildOperationArgumentDefinitions = function _buildOperationArgumentDefinitions(operation) { var schema = this._schema; var variableDefinitions = new Map(); (operation.variableDefinitions || []).forEach(function (def) { var name = getName(def.variable); var typeFromAST = schema.getTypeFromAST(def.type); if (typeFromAST == null) { throw createUserError("Unknown type: '".concat(getTypeName(def.type), "'."), null, [def.type]); } var type = schema.asInputType(typeFromAST); if (type == null) { throw createUserError("Expected type \"".concat(getTypeName(def.type), "\" to be an input type."), null, [def.type]); } var defaultValue = def.defaultValue ? transformLiteralValue(def.defaultValue, def) : null; var previousDefinition = variableDefinitions.get(name); if (previousDefinition != null) { throw createUserError("Duplicate definition for variable '$".concat(name, "'."), null, [previousDefinition.ast, def]); } variableDefinitions.set(name, { ast: def, defaultValue: defaultValue, defined: true, name: name, type: type }); }); return variableDefinitions; }; return RelayParser; }(); /** * @private */ function parseDefinition(schema, getFieldDefinition, entries, definition, variableDefinitions) { var parser = new GraphQLDefinitionParser(schema, getFieldDefinition, entries, definition, variableDefinitions); return parser.transform(); } /** * @private */ var GraphQLDefinitionParser = /*#__PURE__*/function () { function GraphQLDefinitionParser(schema, getFieldDefinition, entries, definition, variableDefinitions) { this._definition = definition; this._entries = entries; this._getFieldDefinition = getFieldDefinition; this._schema = schema; this._variableDefinitions = variableDefinitions; this._unknownVariables = new Map(); } var _proto2 = GraphQLDefinitionParser.prototype; _proto2.transform = function transform() { var definition = this._definition; switch (definition.kind) { case 'OperationDefinition': return this._transformOperation(definition); case 'FragmentDefinition': return this._transformFragment(definition); default: definition; throw createCompilerError("Unsupported definition type ".concat(definition.kind), [definition]); } }; _proto2._recordAndVerifyVariableReference = function _recordAndVerifyVariableReference(variable, name, usedAsType) { // Special case for variables used in @arguments where we currently // aren't guaranteed to be able to resolve the type. if (usedAsType == null) { if (!this._variableDefinitions.has(name) && !this._unknownVariables.has(name)) { this._unknownVariables.set(name, { ast: variable, type: null }); } return; } var variableDefinition = this._variableDefinitions.get(name); if (variableDefinition != null) { // If the variable is defined, all usages must be compatible var effectiveType = variableDefinition.type; if (variableDefinition.defaultValue != null) { // If a default value is defined then it is guaranteed to be used // at runtime such that the effective type of the variable is non-null effectiveType = this._schema.getNonNullType(this._schema.getNullableType(effectiveType)); } if (!this._schema.isTypeSubTypeOf(effectiveType, usedAsType)) { throw createUserError("Variable '$".concat(name, "' was defined as type '").concat(String(variableDefinition.type), "' but used in a location expecting the type '").concat(String(usedAsType), "'"), null, [variableDefinition.ast, variable]); } } else { var previous = this._unknownVariables.get(name); if (!previous || !previous.type) { // No previous usage, current type is strongest this._unknownVariables.set(name, { ast: variable, type: usedAsType }); } else { var previousVariable = previous.ast, previousType = previous.type; if (!(this._schema.isTypeSubTypeOf(usedAsType, previousType) || this._schema.isTypeSubTypeOf(previousType, usedAsType))) { throw createUserError("Variable '$".concat(name, "' was used in locations expecting the conflicting types '").concat(String(previousType), "' and '").concat(String(usedAsType), "'."), null, [previousVariable, variable]); } // If the new used type has stronger requirements, use that type as reference, // otherwise keep referencing the previous type if (this._schema.isTypeSubTypeOf(usedAsType, previousType)) { this._unknownVariables.set(name, { ast: variable, type: usedAsType }); } } } }; _proto2._getDirectiveLocations = function _getDirectiveLocations() { if (!this._directiveLocations) { var directiveDefs = this._schema.getDirectives(); this._directiveLocations = new Map(); var _iterator = (0, _createForOfIteratorHelper2["default"])(directiveDefs), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var def = _step.value; this._directiveLocations.set(def.name, def.locations); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } return this._directiveLocations; }; _proto2._validateDirectivesLocation = function _validateDirectivesLocation(directives, allowedLocaction) { if (!directives || !directives.length) { return; } var directiveLocs = this._getDirectiveLocations(); var mismatches = directives.filter(function (directive) { var name = getName(directive); if (DIRECTIVE_WHITELIST.has(name)) { return false; } var locs = directiveLocs.get(name); if (locs == null) { throw createUserError("Unknown directive '".concat(name, "'."), null, [directive]); } return !locs.some(function (loc) { return loc === allowedLocaction; }); }); if (mismatches.length) { var invalidDirectives = mismatches.map(function (directive) { return '@' + getName(directive); }).join(', '); throw createUserError("Invalid directives ".concat(invalidDirectives, " found on ").concat(allowedLocaction, "."), null, mismatches); } }; _proto2._transformFragment = function _transformFragment(fragment) { var directives = this._transformDirectives((fragment.directives || []).filter(function (directive) { return getName(directive) !== ARGUMENT_DEFINITIONS; }), 'FRAGMENT_DEFINITION'); var typeFromAST = this._schema.getTypeFromAST(fragment.typeCondition); if (typeFromAST == null) { throw createUserError("Fragment \"".concat(fragment.name.value, "\" cannot condition on unknown ") + "type \"".concat(String(fragment.typeCondition.name.value), "\"."), null, [fragment.typeCondition]); } var type = this._schema.asCompositeType(typeFromAST); if (type == null) { throw createUserError("Fragment \"".concat(fragment.name.value, "\" cannot condition on non composite ") + "type \"".concat(String(type), "\"."), null, [fragment.typeCondition]); } var selections = this._transformSelections(fragment.selectionSet, type, fragment.typeCondition); var argumentDefinitions = (0, _toConsumableArray2["default"])(buildArgumentDefinitions(this._variableDefinitions)); var _iterator2 = (0, _createForOfIteratorHelper2["default"])(this._unknownVariables), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var _step2$value = _step2.value, name = _step2$value[0], variableReference = _step2$value[1]; argumentDefinitions.push({ kind: 'RootArgumentDefinition', loc: buildLocation(variableReference.ast.loc), name: name, type: variableReference.type }); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } return { kind: 'Fragment', directives: directives, loc: buildLocation(fragment.loc), metadata: null, name: getName(fragment), selections: selections, type: type, // $FlowFixMe[incompatible-return] - could be null argumentDefinitions: argumentDefinitions }; }; _proto2._getLocationFromOperation = function _getLocationFromOperation(definition) { switch (definition.operation) { case 'query': return 'QUERY'; case 'mutation': return 'MUTATION'; case 'subscription': return 'SUBSCRIPTION'; default: definition.operation; throw createCompilerError("Unknown operation type '".concat(definition.operation, "'."), null, [definition]); } }; _proto2._transformOperation = function _transformOperation(definition) { var name = getName(definition); var directives = this._transformDirectives(definition.directives || [], this._getLocationFromOperation(definition)); var type; var operation; var schema = this._schema; switch (definition.operation) { case 'query': operation = 'query'; type = schema.expectQueryType(); break; case 'mutation': operation = 'mutation'; type = schema.expectMutationType(); break; case 'subscription': operation = 'subscription'; type = schema.expectSubscriptionType(); break; default: definition.operation; throw createCompilerError("Unknown operation type '".concat(definition.operation, "'."), null, [definition]); } if (!definition.selectionSet) { throw createUserError('Expected operation to have selections.', null, [definition]); } var selections = this._transformSelections(definition.selectionSet, type); var argumentDefinitions = buildArgumentDefinitions(this._variableDefinitions); if (this._unknownVariables.size !== 0) { throw createUserError("Query '".concat(name, "' references undefined variables."), null, Array.from(this._unknownVariables.values(), function (variableReference) { return variableReference.ast; })); } return { kind: 'Root', operation: operation, loc: buildLocation(definition.loc), metadata: null, name: name, argumentDefinitions: argumentDefinitions, directives: directives, selections: selections, // $FlowFixMe[incompatible-return] type: type }; }; _proto2._transformSelections = function _transformSelections(selectionSet, parentType, parentTypeAST) { var _this4 = this; return selectionSet.selections.map(function (selection) { var node; if (selection.kind === 'Field') { node = _this4._transformField(selection, parentType); } else if (selection.kind === 'FragmentSpread') { node = _this4._transformFragmentSpread(selection, parentType, parentTypeAST); } else if (selection.kind === 'InlineFragment') { node = _this4._transformInlineFragment(selection, parentType, parentTypeAST); } else { selection.kind; throw createCompilerError("Unknown ast kind '".concat(selection.kind, "'."), [selection]); } var _this4$_splitConditio = _this4._splitConditions(node.directives), conditions = _this4$_splitConditio[0], directives = _this4$_splitConditio[1]; var conditionalNodes = applyConditions(conditions, // $FlowFixMe[incompatible-call] [(0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, node), {}, { directives: directives })]); if (conditionalNodes.length !== 1) { throw createCompilerError('Expected exactly one condition node.', null, selection.directives); } return conditionalNodes[0]; }); }; _proto2._transformInlineFragment = function _transformInlineFragment(fragment, parentType, parentTypeAST) { var schema = this._schema; var typeCondition = fragment.typeCondition != null ? schema.getTypeFromAST(fragment.typeCondition) : parentType; if (typeCondition == null) { var _fragment$typeConditi; throw createUserError('Inline fragments can only be on object, interface or union types' + ", got unknown type '".concat(getTypeName(fragment.typeCondition), "'."), null, [(_fragment$typeConditi = fragment.typeCondition) !== null && _fragment$typeConditi !== void 0 ? _fragment$typeConditi : fragment]); } var typeConditionName = schema.getTypeString(typeCondition); typeCondition = schema.asCompositeType(typeCondition); if (typeCondition == null) { var _fragment$typeConditi2; throw createUserError('Inline fragments can only be on object, interface or union types' + ", got '".concat(typeConditionName, "'."), null, [(_fragment$typeConditi2 = fragment.typeCondition) !== null && _fragment$typeConditi2 !== void 0 ? _fragment$typeConditi2 : fragment]); } var rawParentType = this._schema.assertCompositeType(this._schema.getRawType(parentType)); checkFragmentSpreadTypeCompatibility(this._schema, typeCondition, rawParentType, null, fragment.typeCondition, parentTypeAST); var directives = this._transformDirectives(fragment.directives || [], 'INLINE_FRAGMENT'); var selections = this._transformSelections(fragment.selectionSet, typeCondition, fragment.typeCondition); return { kind: 'InlineFragment', directives: directives, loc: buildLocation(fragment.loc), metadata: null, selections: selections, typeCondition: typeCondition }; }; _proto2._transformFragmentSpread = function _transformFragmentSpread(fragmentSpread, parentType, parentTypeAST) { var _this5 = this; var fragmentName = getName(fragmentSpread); var _partitionArray = partitionArray(fragmentSpread.directives || [], function (directive) { var name = getName(directive); return name === ARGUMENTS || name === DEPRECATED_UNCHECKED_ARGUMENTS; }), argumentDirectives = _partitionArray[0], otherDirectives = _partitionArray[1]; if (argumentDirectives.length > 1) { throw createUserError("Directive @".concat(ARGUMENTS, " may be used at most once per a fragment spread."), null, argumentDirectives); } var fragmentDefinition = this._entries.get(fragmentName); if (fragmentDefinition == null) { throw createUserError("Unknown fragment '".concat(fragmentName, "'."), null, [fragmentSpread.name]); } var fragmentTypeNode = getFragmentType(fragmentDefinition.definition); var fragmentType = this._schema.assertCompositeType(this._schema.expectTypeFromAST(fragmentTypeNode)); var rawParentType = this._schema.assertCompositeType(this._schema.getRawType(parentType)); checkFragmentSpreadTypeCompatibility(this._schema, fragmentType, rawParentType, fragmentSpread.name.value, fragmentSpread, parentTypeAST); var fragmentArgumentDefinitions = fragmentDefinition.variableDefinitions; var argumentsDirective = argumentDirectives[0]; var args; if (argumentsDirective != null) { var isDeprecatedUncheckedArguments = getName(argumentsDirective) === DEPRECATED_UNCHECKED_ARGUMENTS; var hasInvalidArgument = false; args = (argumentsDirective.arguments || []).map(function (arg) { var _argumentDefinition$t; var argName = getName(arg); var argValue = arg.value; var argumentDefinition = fragmentArgumentDefinitions.get(argName); var argumentType = (_argumentDefinition$t = argumentDefinition === null || argumentDefinition === void 0 ? void 0 : argumentDefinition.type) !== null && _argumentDefinition$t !== void 0 ? _argumentDefinition$t : null; if (argValue.kind === 'Variable') { if (argumentDefinition == null && !isDeprecatedUncheckedArguments) { throw createUserError("Variable @".concat(ARGUMENTS, " values are only supported when the ") + "argument is defined with @".concat(ARGUMENT_DEFINITIONS, ". Check ") + "the definition of fragment '".concat(fragmentName, "'."), null, [arg.value, fragmentDefinition.definition]); } hasInvalidArgument = hasInvalidArgument || argumentDefinition == null; // TODO: check the type of the variable and use the type return { kind: 'Argument', loc: buildLocation(arg.loc), name: argName, value: _this5._transformVariable(argValue, null), type: null }; } else { if (argumentType == null) { throw createUserError("Literal @".concat(ARGUMENTS, " values are only supported when the ") + "argument is defined with @".concat(ARGUMENT_DEFINITIONS, ". Check ") + "the definition of fragment '".concat(fragmentName, "'."), null, [arg.value, fragmentDefinition.definition]); } var value = _this5._transformValue(argValue, argumentType); return { kind: 'Argument', loc: buildLocation(arg.loc), name: argName, value: value, type: argumentType }; } }); if (isDeprecatedUncheckedArguments && !hasInvalidArgument) { throw createUserError("Invalid use of @".concat(DEPRECATED_UNCHECKED_ARGUMENTS, ": all arguments ") + "are defined, use @".concat(ARGUMENTS, " instead."), null, [argumentsDirective]); } } var directives = this._transformDirectives(otherDirectives, 'FRAGMENT_SPREAD'); return { kind: 'FragmentSpread', args: args || [], metadata: null, loc: buildLocation(fragmentSpread.loc), name: fragmentName, directives: directives }; }; _proto2._transformField = function _transformField(field, parentType) { var _field$alias$value, _field$alias; var schema = this._schema; var name = getName(field); var fieldDef = this._getFieldDefinition(schema, parentType, name, field); if (fieldDef == null) { throw createUserError("Unknown field '".concat(name, "' on type '").concat(schema.getTypeString(parentType), "'."), null, [field]); } var alias = (_field$alias$value = (_field$alias = field.alias) === null || _field$alias === void 0 ? void 0 : _field$alias.value) !== null && _field$alias$value !== void 0 ? _field$alias$value : name; var args = this._transformArguments(field.arguments || [], schema.getFieldArgs(fieldDef), fieldDef); var _partitionArray2 = partitionArray(field.directives || [], function (directive) { return getName(directive) !== CLIENT_FIELD; }), otherDirectives = _partitionArray2[0], clientFieldDirectives = _partitionArray2[1]; var directives = this._transformDirectives(otherDirectives, 'FIELD'); var type = schema.getFieldType(fieldDef); var handles = this._transformHandle(name, args, clientFieldDirectives); if (schema.isLeafType(schema.getRawType(type))) { if (field.selectionSet && field.selectionSet.selections && field.selectionSet.selections.length) { throw createUserError("Expected no selections for scalar field '".concat(name, "'."), null, [field]); } return { kind: 'ScalarField', alias: alias, args: args, directives: directives, handles: handles, loc: buildLocation(field.loc), metadata: null, name: name, type: schema.assertScalarFieldType(type) }; } else { var selections = field.selectionSet ? this._transformSelections(field.selectionSet, type) : null; if (selections == null || selections.length === 0) { throw createUserError("Expected at least one selection for non-scalar field '".concat(name, "' on type '").concat(schema.getTypeString(type), "'."), null, [field]); } return { kind: 'LinkedField', alias: alias, args: args, connection: false, directives: directives, handles: handles, loc: buildLocation(field.loc), metadata: null, name: name, selections: selections, type: schema.assertLinkedFieldType(type) }; } }; _proto2._transformHandle = function _transformHandle(fieldName, fieldArgs, clientFieldDirectives) { var handles = null; clientFieldDirectives.forEach(function (clientFieldDirective) { var handleArgument = (clientFieldDirective.arguments || []).find(function (arg) { return getName(arg) === CLIENT_FIELD_HANDLE; }); if (handleArgument) { var name = null; var key = DEFAULT_HANDLE_KEY; var filters = null; var maybeHandle = transformLiteralValue(handleArgument.value, handleArgument); if (typeof maybeHandle !== 'string') { throw createUserError("Expected a string literal argument for the @".concat(CLIENT_FIELD, " directive."), null, [handleArgument.value]); } name = maybeHandle; var keyArgument = (clientFieldDirective.arguments || []).find(function (arg) { return getName(arg) === CLIENT_FIELD_KEY; }); if (keyArgument) { var maybeKey = transformLiteralValue(keyArgument.value, keyArgument); if (typeof maybeKey !== 'string') { throw createUserError("Expected a string literal argument for the @".concat(CLIENT_FIELD, " directive."), null, [keyArgument.value]); } key = maybeKey; } var filtersArgument = (clientFieldDirective.arguments || []).find(function (arg) { return getName(arg) === CLIENT_FIELD_FILTERS; }); if (filtersArgument) { var maybeFilters = transformLiteralValue(filtersArgument.value, filtersArgument); if (!(Array.isArray(maybeFilters) && maybeFilters.every(function (filter) { return typeof filter === 'string' && fieldArgs.some(function (fieldArg) { return fieldArg.name === filter; }); }))) { throw createUserError("Expected an array of argument names on field '".concat(fieldName, "'."), null, [filtersArgument.value]); } // $FlowFixMe[incompatible-cast] filters = maybeFilters; } var dynamicKeyArgument = (clientFieldDirective.arguments || []).find(function (arg) { return getName(arg) === 'dynamicKey_UNSTABLE'; }); if (dynamicKeyArgument != null) { throw createUserError('Dynamic keys are only supported with @connection.', null, [dynamicKeyArgument.value]); } handles = handles || []; handles.push({ name: name, key: key, filters: filters, dynamicKey: null }); } }); return handles; }; _proto2._transformDirectives = function _transformDirectives(directives, location) { var _this6 = this; this._validateDirectivesLocation(directives, location); return directives.map(function (directive) { var name = getName(directive); var directiveDef = _this6._schema.getDirective(name); if (directiveDef == null) { throw createUserError("Unknown directive '".concat(name, "'."), null, [directive]); } var args = _this6._transformArguments(directive.arguments || [], directiveDef.args.map(function (item) { return { name: item.name, type: item.type, defaultValue: item.defaultValue }; }), null, name); return { kind: 'Directive', loc: buildLocation(directive.loc), name: name, args: args }; }); }; _proto2._transformArguments = function _transformArguments(args, argumentDefinitions, field, directiveName) { var _this7 = this; return args.map(function (arg) { var argName = getName(arg); var argDef = argumentDefinitions.find(function (def) { return def.name === argName; }); if (argDef == null) { var message = "Unknown argument '".concat(argName, "'") + (field ? " on field '".concat(_this7._schema.getFieldName(field), "'") + " of type '".concat(_this7._schema.getTypeString(_this7._schema.getFieldParentType(field)), "'.") : directiveName != null ? " on directive '@".concat(directiveName, "'.") : '.'); throw createUserError(message, null, [arg]); } var value = _this7._transformValue(arg.value, argDef.type); return { kind: 'Argument', loc: buildLocation(arg.loc), name: argName, value: value, type: argDef.type }; }); }; _proto2._splitConditions = function _splitConditions(mixedDirectives) { var _partitionArray3 = partitionArray(mixedDirectives, function (directive) { return directive.name === INCLUDE || directive.name === SKIP; }), conditionDirectives = _partitionArray3[0], otherDirectives = _partitionArray3[1]; var conditions = conditionDirectives.map(function (directive) { var passingValue = directive.name === INCLUDE; var arg = directive.args[0]; if (arg == null || arg.name !== IF) { throw createUserError("Expected an 'if' argument to @".concat(directive.name, "."), [directive.loc]); } if (!(arg.value.kind === 'Variable' || arg.value.kind === 'Literal')) { throw createUserError("Expected the 'if' argument to @".concat(directive.name, " to be a variable or literal."), [directive.loc]); } return { kind: 'Condition', condition: arg.value, loc: directive.loc, passingValue: passingValue, selections: [] }; }); var sortedConditions = conditions.sort(function (a, b) { if (a.condition.kind === 'Variable' && b.condition.kind === 'Variable') { return a.condition.variableName < b.condition.variableName ? -1 : a.condition.variableName > b.condition.variableName ? 1 : 0; } else { // sort literals earlier, variables later return a.condition.kind === 'Variable' ? 1 : b.condition.kind === 'Variable' ? -1 : 0; } }); return [sortedConditions, otherDirectives]; }; _proto2._transformVariable = function _transformVariable(ast, usedAsType) { var variableName = getName(ast); this._recordAndVerifyVariableReference(ast, variableName, usedAsType); return { kind: 'Variable', loc: buildLocation(ast.loc), variableName: variableName, type: usedAsType }; }; _proto2._transformValue = function _transformValue(ast, type) { var _this8 = this; return transformValue(this._schema, ast, type, function (variableAst, variableType) { return _this8._transformVariable(variableAst, variableType); }); }; return GraphQLDefinitionParser; }(); /** * Transforms and validates argument values according to the expected * type. */ function transformValue(schema, ast, type, transformVariable) { if (ast.kind === 'Variable') { // Special case variables since there is no value to parse return transformVariable(ast, type); } else if (ast.kind === 'NullValue') { // Special case null literals since there is no value to parse if (schema.isNonNull(type)) { throw createUserError("Expected a value matching type '".concat(String(type), "'."), null, [ast]); } return { kind: 'Literal', loc: buildLocation(ast.loc), value: null }; } else { return transformNonNullLiteral(schema, ast, type, transformVariable); } } /** * Transforms and validates non-null literal (non-variable) values * according to the expected type. */ function transformNonNullLiteral(schema, ast, type, transformVariable) { // Transform the value based on the type without a non-null wrapper. // Note that error messages should still use the original `type` // since that accurately describes to the user what the expected // type is (using nullableType would suggest that `null` is legal // even when it may not be, for example). var nullableType = schema.getNullableType(type); if (schema.isList(nullableType)) { if (ast.kind !== 'ListValue') { // Parse singular (non-list) values flowing into a list type // as scalars, ie without wrapping them in an array. if (!schema.isInputType(schema.getListItemType(nullableType))) { throw createUserError("Expected type ".concat(schema.getTypeString(nullableType), " to be an input type."), null, [ast]); } return transformValue(schema, ast, schema.assertInputType(schema.getListItemType(nullableType)), transformVariable); } var itemType = schema.assertInputType(schema.getListItemType(nullableType)); var literalList = []; var items = []; var areAllItemsScalar = true; ast.values.forEach(function (item) { var itemValue = transformValue(schema, item, itemType, transformVariable); if (itemValue.kind === 'Literal') { literalList.push(itemValue.value); } items.push(itemValue); areAllItemsScalar = areAllItemsScalar && itemValue.kind === 'Literal'; }); if (areAllItemsScalar) { return { kind: 'Literal', loc: buildLocation(ast.loc), value: literalList }; } else { return { kind: 'ListValue', loc: buildLocation(ast.loc), items: items }; } } else if (schema.isInputObject(nullableType)) { if (ast.kind !== 'ObjectValue') { throw createUserError("Expected a value matching type '".concat(schema.getTypeString(type), "'."), null, [ast]); } var literalObject = {}; var fields = []; var areAllFieldsScalar = true; var inputType = schema.assertInputObjectType(nullableType); var requiredFieldNames = new Set(schema.getFields(inputType).filter(function (field) { return schema.isNonNull(schema.getFieldType(field)); }).map(function (field) { return schema.getFieldName(field); })); var seenFields = new Map(); ast.fields.forEach(function (field) { var fieldName = getName(field); var seenField = seenFields.get(fieldName); if (seenField) { throw createUserError("Duplicated field name '".concat(fieldName, "' in the input object."), null, [field, seenField]); } var fieldID = schema.getFieldByName(inputType, fieldName); if (!fieldID) { throw createUserError("Unknown field '".concat(fieldName, "' on type '").concat(schema.getTypeString(inputType), "'."), null, [field]); } var fieldConfig = schema.getFieldConfig(fieldID); var fieldType = schema.assertInputType(fieldConfig.type); var fieldValue = transformValue(schema, field.value, fieldType, transformVariable); if (fieldValue.kind === 'Literal') { literalObject[field.name.value] = fieldValue.value; } fields.push({ kind: 'ObjectFieldValue', loc: buildLocation(field.loc), name: fieldName, value: fieldValue }); seenFields.set(fieldName, field); requiredFieldNames["delete"](fieldName); areAllFieldsScalar = areAllFieldsScalar && fieldValue.kind === 'Literal'; }); if (requiredFieldNames.size > 0) { var requiredFieldStr = Array.from(requiredFieldNames).map(function (item) { return "'".concat(item, "'"); }).join(', '); throw createUserError("Missing non-optional field".concat(requiredFieldNames.size > 1 ? 's:' : '', " ").concat(requiredFieldStr, " for input type '").concat(schema.getTypeString(inputType), "'."), null, [ast]); } if (areAllFieldsScalar) { return { kind: 'Literal', loc: buildLocation(ast.loc), value: literalObject }; } else { return { kind: 'ObjectValue', loc: buildLocation(ast.loc), fields: fields }; } } else if (schema.isId(nullableType)) { // GraphQLID's parseLiteral() always returns the string value. However // the int/string distinction may be important at runtime, so this // transform parses int/string literals into the corresponding JS types. if (ast.kind === 'IntValue') { return { kind: 'Literal', loc: buildLocation(ast.loc), value: parseInt(ast.value, 10) }; } else if (ast.kind === 'StringValue') { return { kind: 'Literal', loc: buildLocation(ast.loc), value: ast.value }; } else { throw createUserError("Invalid value, expected a value matching type '".concat(schema.getTypeString(type), "'."), null, [ast]); } } else if (schema.isEnum(nullableType)) { var enumType = schema.assertEnumType(nullableType); var value = schema.parseLiteral(enumType, ast); if (value == null) { var suggestions = schema.getEnumValues(enumType); // parseLiteral() should return a non-null JavaScript value // if the ast value is valid for the type. throw createUserError("Expected a value matching type '".concat(schema.getTypeString(type), "'. Possible values: ").concat(orList(suggestions), "?'"), null, [ast]); } return { kind: 'Literal', loc: buildLocation(ast.loc), value: value }; } else if (schema.isScalar(nullableType)) { var _value = schema.parseLiteral(schema.assertScalarType(nullableType), ast); if (_value == null) { // parseLiteral() should return a non-null JavaScript value // if the ast value is valid for the type. throw createUserError("Expected a value matching type '".concat(schema.getTypeString(type), "'."), null, [ast]); } return { kind: 'Literal', loc: buildLocation(ast.loc), value: _value }; } else { throw createCompilerError("Unsupported type '".concat(schema.getTypeString(type), "' for input value, expected a GraphQLList, ") + 'GraphQLInputObjectType, GraphQLEnumType, or GraphQLScalarType.', null, [ast]); } } /** * @private */ function transformLiteralValue(ast, context) { switch (ast.kind) { case 'IntValue': return parseInt(ast.value, 10); case 'FloatValue': return parseFloat(ast.value); case 'StringValue': return ast.value; case 'BooleanValue': // Note: duplicated because Flow does not understand fall-through cases return ast.value; case 'EnumValue': // Note: duplicated because Flow does not understand fall-through cases return ast.value; case 'ListValue': return ast.values.map(function (item) { return transformLiteralValue(item, context); }); case 'NullValue': return null; case 'ObjectValue': { var objectValue = {}; ast.fields.forEach(function (field) { var fieldName = getName(field); var value = transformLiteralValue(field.value, context); objectValue[fieldName] = value; }); return objectValue; } case 'Variable': throw createUserError('Unexpected variable where a literal (static) value is required.', null, [ast, context]); default: ast.kind; throw createCompilerError("Unknown ast kind '".concat(ast.kind, "'."), [ast]); } } /** * @private */ function buildArgumentDefinitions(variables) { return Array.from(variables.values(), function (_ref3) { var ast = _ref3.ast, name = _ref3.name, defaultValue = _ref3.defaultValue, type = _ref3.type; return { kind: 'LocalArgumentDefinition', loc: buildLocation(ast.loc), name: name, type: type, defaultValue: defaultValue }; }); } /** * @private */ function buildLocation(loc) { if (loc == null) { return { kind: 'Unknown' }; } return { kind: 'Source', start: loc.start, end: loc.end, source: loc.source }; } /** * @private */ function applyConditions(conditions, selections) { var nextSelections = selections; conditions.forEach(function (condition) { nextSelections = [(0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, condition), {}, { selections: nextSelections })]; }); return nextSelections; } /** * @private */ function getName(ast) { var _ast$name; var name = (_ast$name = ast.name) === null || _ast$name === void 0 ? void 0 : _ast$name.value; if (typeof name !== 'string') { throw createCompilerError("Expected ast node to have a 'name'.", null, [ast]); } return name; } function getTypeName(ast) { return ast ? print(ast) : 'Undefined Type Name'; } /** * @private */ function getFragmentType(ast) { if (ast.kind === 'FragmentDefinition') { return ast.typeCondition; } throw createCompilerError('Expected ast node to be a FragmentDefinition node.', null, [ast]); } function checkFragmentSpreadTypeCompatibility(schema, fragmentType, parentType, fragmentName, fragmentTypeAST, parentTypeAST) { if (!schema.doTypesOverlap(fragmentType, schema.assertCompositeType(parentType))) { var nodes = []; if (parentTypeAST) { nodes.push(parentTypeAST); } if (fragmentTypeAST) { nodes.push(fragmentTypeAST); } var possibleConcreteTypes = schema.isAbstractType(parentType) ? Array.from(schema.getPossibleTypes(schema.assertAbstractType(parentType))) : []; var suggestedTypesMessage = ''; if (possibleConcreteTypes.length !== 0) { suggestedTypesMessage = " Possible concrete types include ".concat(possibleConcreteTypes.sort().slice(0, 3).map(function (type) { return "'".concat(schema.getTypeString(type), "'"); }).join(', '), ", etc."); } throw createUserError((fragmentName != null ? "Fragment '".concat(fragmentName, "' cannot be spread here as objects of ") : 'Fragment cannot be spread here as objects of ') + "type '".concat(schema.getTypeString(parentType), "' ") + "can never be of type '".concat(schema.getTypeString(fragmentType), "'.") + suggestedTypesMessage, null, nodes); } } module.exports = { parse: parse, transform: transform };