@ardatan/relay-compiler
Version:
A compiler tool for building GraphQL-driven applications.
643 lines (535 loc) • 27.2 kB
JavaScript
/**
* 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
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var IRTransformer = require('../core/IRTransformer');
var RelayParser = require('../core/RelayParser');
var SchemaUtils = require('../core/SchemaUtils');
var getLiteralArgumentValues = require('../core/getLiteralArgumentValues');
var _require = require('../core/CompilerError'),
createCompilerError = _require.createCompilerError,
createUserError = _require.createUserError;
var _require2 = require('graphql'),
parse = _require2.parse;
var _require3 = require('relay-runtime'),
ConnectionInterface = _require3.ConnectionInterface,
RelayFeatureFlags = _require3.RelayFeatureFlags;
var AFTER = 'after';
var BEFORE = 'before';
var FIRST = 'first';
var KEY = 'key';
var LAST = 'last';
var CONNECTION = 'connection';
var STREAM_CONNECTION = 'stream_connection';
var HANDLER = 'handler';
/**
* @public
*
* Transforms fields with the `@connection` directive:
* - Verifies that the field type is connection-like.
* - Adds a `handle` property to the field, either the user-provided `handle`
* argument or the default value "connection".
* - Inserts a sub-fragment on the field to ensure that standard connection
* fields are fetched (e.g. cursors, node ids, page info).
*/
function connectionTransform(context) {
return IRTransformer.transform(context, {
Fragment: visitFragmentOrRoot,
LinkedField: visitLinkedField,
Root: visitFragmentOrRoot
}, function (node) {
return {
documentName: node.name,
path: [],
connectionMetadata: []
};
});
}
var SCHEMA_EXTENSION = "\n directive @connection(\n key: String!\n filters: [String]\n handler: String\n dynamicKey_UNSTABLE: String\n ) on FIELD\n\n directive @stream_connection(\n key: String!\n filters: [String]\n handler: String\n initial_count: Int!\n if: Boolean = true\n use_customized_batch: Boolean = false\n dynamicKey_UNSTABLE: String\n ) on FIELD\n";
/**
* @internal
*/
function visitFragmentOrRoot(node, options) {
// $FlowFixMe[incompatible-use]
var transformedNode = this.traverse(node, options);
var connectionMetadata = options.connectionMetadata;
if (connectionMetadata.length) {
return (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, transformedNode), {}, {
metadata: (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, transformedNode.metadata), {}, {
connection: connectionMetadata
})
});
}
return transformedNode;
}
/**
* @internal
*/
function visitLinkedField(field, options) {
var _connectionArguments$;
// $FlowFixMe[incompatible-use]
var context = this.getContext();
var schema = context.getSchema();
var nullableType = schema.getNullableType(field.type);
var isPlural = schema.isList(nullableType);
var path = options.path.concat(isPlural ? null : field.alias || field.name); // $FlowFixMe[incompatible-use]
var transformedField = this.traverse(field, (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, options), {}, {
path: path
}));
var connectionDirective = field.directives.find(function (directive) {
return directive.name === CONNECTION || directive.name === STREAM_CONNECTION;
});
if (!connectionDirective) {
return transformedField;
}
if (!schema.isObject(nullableType) && !schema.isInterface(nullableType)) {
throw new createUserError("@".concat(connectionDirective.name, " used on invalid field '").concat(field.name, "'. ") + 'Expected the return type to be a non-plural interface or object, ' + "got '".concat(schema.getTypeString(field.type), "'."), [transformedField.loc]);
}
validateConnectionSelection(transformedField);
validateConnectionType(schema, transformedField, schema.assertCompositeType(nullableType), connectionDirective);
var connectionArguments = buildConnectionArguments(transformedField, connectionDirective);
var connectionMetadata = buildConnectionMetadata(transformedField, path, connectionArguments.stream != null);
options.connectionMetadata.push(connectionMetadata);
var handle = {
name: (_connectionArguments$ = connectionArguments.handler) !== null && _connectionArguments$ !== void 0 ? _connectionArguments$ : CONNECTION,
key: connectionArguments.key,
dynamicKey: connectionArguments.dynamicKey,
filters: connectionArguments.filters
};
var direction = connectionMetadata.direction;
if (direction != null) {
var selections = transformConnectionSelections( // $FlowFixMe[incompatible-use]
this.getContext(), transformedField, schema.assertCompositeType(nullableType), direction, connectionArguments, connectionDirective.loc, options.documentName);
transformedField = (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, transformedField), {}, {
selections: selections
});
}
return (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, transformedField), {}, {
directives: transformedField.directives.filter(function (directive) {
return directive !== connectionDirective;
}),
connection: true,
handles: transformedField.handles ? [].concat((0, _toConsumableArray2["default"])(transformedField.handles), [handle]) : [handle]
});
}
function buildConnectionArguments(field, connectionDirective) {
var _getLiteralArgumentVa = getLiteralArgumentValues(connectionDirective.args),
handler = _getLiteralArgumentVa.handler,
key = _getLiteralArgumentVa.key,
label = _getLiteralArgumentVa.label,
literalFilters = _getLiteralArgumentVa.filters;
if (handler != null && typeof handler !== 'string') {
var _handleArg$value$loc, _handleArg$value;
var handleArg = connectionDirective.args.find(function (arg) {
return arg.name === 'handler';
});
throw createUserError("Expected the ".concat(HANDLER, " argument to @").concat(connectionDirective.name, " to ") + "be a string literal for field ".concat(field.name, "."), [(_handleArg$value$loc = handleArg === null || handleArg === void 0 ? void 0 : (_handleArg$value = handleArg.value) === null || _handleArg$value === void 0 ? void 0 : _handleArg$value.loc) !== null && _handleArg$value$loc !== void 0 ? _handleArg$value$loc : connectionDirective.loc]);
}
if (typeof key !== 'string') {
var _keyArg$value$loc, _keyArg$value;
var keyArg = connectionDirective.args.find(function (arg) {
return arg.name === 'key';
});
throw createUserError("Expected the ".concat(KEY, " argument to @").concat(connectionDirective.name, " to be a ") + "string literal for field ".concat(field.name, "."), [(_keyArg$value$loc = keyArg === null || keyArg === void 0 ? void 0 : (_keyArg$value = keyArg.value) === null || _keyArg$value === void 0 ? void 0 : _keyArg$value.loc) !== null && _keyArg$value$loc !== void 0 ? _keyArg$value$loc : connectionDirective.loc]);
}
var postfix = field.alias || field.name;
if (!key.endsWith('_' + postfix)) {
var _keyArg$value$loc2, _keyArg$value2;
var _keyArg = connectionDirective.args.find(function (arg) {
return arg.name === 'key';
});
throw createUserError("Expected the ".concat(KEY, " argument to @").concat(connectionDirective.name, " to be of ") + "form <SomeName>_".concat(postfix, ", got '").concat(key, "'. ") + 'For a detailed explanation, check out ' + 'https://relay.dev/docs/en/pagination-container#connection', [(_keyArg$value$loc2 = _keyArg === null || _keyArg === void 0 ? void 0 : (_keyArg$value2 = _keyArg.value) === null || _keyArg$value2 === void 0 ? void 0 : _keyArg$value2.loc) !== null && _keyArg$value$loc2 !== void 0 ? _keyArg$value$loc2 : connectionDirective.loc]);
}
if (literalFilters != null && (!Array.isArray(literalFilters) || literalFilters.some(function (filter) {
return typeof filter !== 'string';
}))) {
var _filtersArg$value$loc, _filtersArg$value;
var filtersArg = connectionDirective.args.find(function (arg) {
return arg.name === 'filters';
});
throw createUserError("Expected the 'filters' argument to @".concat(connectionDirective.name, " to be ") + 'a string literal.', [(_filtersArg$value$loc = filtersArg === null || filtersArg === void 0 ? void 0 : (_filtersArg$value = filtersArg.value) === null || _filtersArg$value === void 0 ? void 0 : _filtersArg$value.loc) !== null && _filtersArg$value$loc !== void 0 ? _filtersArg$value$loc : connectionDirective.loc]);
}
var filters = literalFilters;
if (filters == null) {
var generatedFilters = field.args.filter(function (arg) {
return !ConnectionInterface.isConnectionCall({
name: arg.name,
value: null
});
}).map(function (arg) {
return arg.name;
});
filters = generatedFilters.length !== 0 ? generatedFilters : null;
}
var stream = null;
if (connectionDirective.name === STREAM_CONNECTION) {
var initialCountArg = connectionDirective.args.find(function (arg) {
return arg.name === 'initial_count';
});
var useCustomizedBatchArg = connectionDirective.args.find(function (arg) {
return arg.name === 'use_customized_batch';
});
var ifArg = connectionDirective.args.find(function (arg) {
return arg.name === 'if';
});
stream = {
"if": ifArg,
initialCount: initialCountArg,
useCustomizedBatch: useCustomizedBatchArg,
label: key
};
}
var dynamicKeyArg = connectionDirective.args.find(function (arg) {
return arg.name === 'dynamicKey_UNSTABLE';
});
var dynamicKey = null;
if (dynamicKeyArg != null) {
if (RelayFeatureFlags.ENABLE_VARIABLE_CONNECTION_KEY && dynamicKeyArg.value.kind === 'Variable') {
dynamicKey = dynamicKeyArg.value;
} else {
throw createUserError("Unsupported 'dynamicKey_UNSTABLE' argument to @".concat(connectionDirective.name, ". This argument is only valid when the feature flag is enabled and ") + 'the variable must be a variable', [connectionDirective.loc]);
}
}
return {
handler: handler,
key: key,
dynamicKey: dynamicKey,
filters: filters,
stream: stream
};
}
function buildConnectionMetadata(field, path, stream) {
var pathHasPlural = path.includes(null);
var firstArg = findArg(field, FIRST);
var lastArg = findArg(field, LAST);
var direction = null;
var countArg = null;
var cursorArg = null;
if (firstArg && !lastArg) {
direction = 'forward';
countArg = firstArg;
cursorArg = findArg(field, AFTER);
} else if (lastArg && !firstArg) {
direction = 'backward';
countArg = lastArg;
cursorArg = findArg(field, BEFORE);
} else if (lastArg && firstArg) {
direction = 'bidirectional'; // TODO(T26511885) Maybe add connection metadata to this case
}
var countVariable = countArg && countArg.value.kind === 'Variable' ? countArg.value.variableName : null;
var cursorVariable = cursorArg && cursorArg.value.kind === 'Variable' ? cursorArg.value.variableName : null;
if (stream) {
return {
count: countVariable,
cursor: cursorVariable,
direction: direction,
path: pathHasPlural ? null : path,
stream: true
};
}
return {
count: countVariable,
cursor: cursorVariable,
direction: direction,
path: pathHasPlural ? null : path
};
}
/**
* @internal
*
* Transforms the selections on a connection field, generating fields necessary
* for pagination (edges.cursor, pageInfo, etc) and adding/merging them with
* existing selections.
*/
function transformConnectionSelections(context, field, nullableType, direction, connectionArguments, directiveLocation, documentName) {
var schema = context.getSchema();
var derivedFieldLocation = {
kind: 'Derived',
source: field.loc
};
var derivedDirectiveLocation = {
kind: 'Derived',
source: directiveLocation
};
var _ConnectionInterface$ = ConnectionInterface.get(),
CURSOR = _ConnectionInterface$.CURSOR,
EDGES = _ConnectionInterface$.EDGES,
END_CURSOR = _ConnectionInterface$.END_CURSOR,
HAS_NEXT_PAGE = _ConnectionInterface$.HAS_NEXT_PAGE,
HAS_PREV_PAGE = _ConnectionInterface$.HAS_PREV_PAGE,
NODE = _ConnectionInterface$.NODE,
PAGE_INFO = _ConnectionInterface$.PAGE_INFO,
START_CURSOR = _ConnectionInterface$.START_CURSOR; // Find existing edges/pageInfo selections
var edgesSelection;
var pageInfoSelection;
field.selections.forEach(function (selection) {
if (selection.kind === 'LinkedField') {
if (selection.name === EDGES) {
if (edgesSelection != null) {
throw createCompilerError("ConnectionTransform: Unexpected duplicate field '".concat(EDGES, "'."), [edgesSelection.loc, selection.loc]);
}
edgesSelection = selection;
return;
} else if (selection.name === PAGE_INFO) {
if (pageInfoSelection != null) {
throw createCompilerError("ConnectionTransform: Unexpected duplicate field '".concat(PAGE_INFO, "'."), [pageInfoSelection.loc, selection.loc]);
}
pageInfoSelection = selection;
return;
}
}
}); // If streaming is enabled, construct directives to apply to the edges/
// pageInfo fields
var streamDirective;
var stream = connectionArguments.stream;
if (stream != null) {
streamDirective = {
args: [stream["if"], stream.initialCount, stream.useCustomizedBatch, {
kind: 'Argument',
loc: derivedDirectiveLocation,
name: 'label',
type: SchemaUtils.getNullableStringInput(schema),
value: {
kind: 'Literal',
loc: derivedDirectiveLocation,
value: stream.label
}
}].filter(Boolean),
kind: 'Directive',
loc: derivedDirectiveLocation,
name: 'stream'
};
} // For backwards compatibility with earlier versions of this transform,
// edges/pageInfo have to be generated as non-aliased fields (since product
// code may be accessing the non-aliased response keys). But for streaming
// mode we need to generate @stream/@defer directives on these fields *and*
// we prefer to avoid generating extra selections (we want one payload per
// item, not two as could happen with separate @stream directives on the
// aliased and non-aliased edges fields). So we keep things simple by
// disallowing aliases on edges/pageInfo in streaming mode.
if (edgesSelection && edgesSelection.alias !== edgesSelection.name) {
if (stream) {
throw createUserError("@stream_connection does not support aliasing the '".concat(EDGES, "' field."), [edgesSelection.loc]);
}
edgesSelection = null;
}
if (pageInfoSelection && pageInfoSelection.alias !== pageInfoSelection.name) {
if (stream) {
throw createUserError("@stream_connection does not support aliasing the '".concat(PAGE_INFO, "' field."), [pageInfoSelection.loc]);
}
pageInfoSelection = null;
} // Separately create transformed versions of edges/pageInfo so that we can
// later replace the originals at the same point within the selection array
var transformedEdgesSelection = edgesSelection;
var transformedPageInfoSelection = pageInfoSelection;
var edgesType = schema.getFieldConfig(schema.expectField(nullableType, EDGES)).type;
var pageInfoType = schema.getFieldConfig(schema.expectField(nullableType, PAGE_INFO)).type;
if (transformedEdgesSelection == null) {
transformedEdgesSelection = {
alias: EDGES,
args: [],
connection: false,
directives: [],
handles: null,
kind: 'LinkedField',
loc: derivedFieldLocation,
metadata: null,
name: EDGES,
selections: [],
type: schema.assertLinkedFieldType(edgesType)
};
}
if (transformedPageInfoSelection == null) {
transformedPageInfoSelection = {
alias: PAGE_INFO,
args: [],
connection: false,
directives: [],
handles: null,
kind: 'LinkedField',
loc: derivedFieldLocation,
metadata: null,
name: PAGE_INFO,
selections: [],
type: schema.assertLinkedFieldType(pageInfoType)
};
} // Generate (additional) fields on pageInfo and add to the transformed
// pageInfo field
var pageInfoRawType = schema.getRawType(pageInfoType);
var pageInfoText;
if (direction === 'forward') {
pageInfoText = "fragment PageInfo on ".concat(schema.getTypeString(pageInfoRawType), " {\n ").concat(END_CURSOR, "\n ").concat(HAS_NEXT_PAGE, "\n }");
} else if (direction === 'backward') {
pageInfoText = "fragment PageInfo on ".concat(schema.getTypeString(pageInfoRawType), " {\n ").concat(HAS_PREV_PAGE, "\n ").concat(START_CURSOR, "\n }");
} else {
pageInfoText = "fragment PageInfo on ".concat(schema.getTypeString(pageInfoRawType), " {\n ").concat(END_CURSOR, "\n ").concat(HAS_NEXT_PAGE, "\n ").concat(HAS_PREV_PAGE, "\n ").concat(START_CURSOR, "\n }");
}
var pageInfoAst = parse(pageInfoText);
var pageInfoFragment = RelayParser.transform(schema, [pageInfoAst.definitions[0]])[0];
if (transformedPageInfoSelection.kind !== 'LinkedField') {
throw createCompilerError('ConnectionTransform: Expected generated pageInfo selection to be ' + 'a LinkedField', [field.loc]);
}
transformedPageInfoSelection = (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, transformedPageInfoSelection), {}, {
selections: [].concat((0, _toConsumableArray2["default"])(transformedPageInfoSelection.selections), [{
directives: [],
kind: 'InlineFragment',
loc: derivedFieldLocation,
metadata: null,
selections: pageInfoFragment.selections,
typeCondition: pageInfoFragment.type
}])
}); // When streaming the pageInfo field has to be deferred
if (stream != null) {
var _stream$if$value, _stream$if;
transformedPageInfoSelection = {
"if": (_stream$if$value = (_stream$if = stream["if"]) === null || _stream$if === void 0 ? void 0 : _stream$if.value) !== null && _stream$if$value !== void 0 ? _stream$if$value : null,
label: "".concat(documentName, "$defer$").concat(stream.label, "$").concat(PAGE_INFO),
kind: 'Defer',
loc: derivedFieldLocation,
selections: [transformedPageInfoSelection]
};
} // Generate additional fields on edges and append to the transformed edges
// selection
var edgeText = "\n fragment Edges on ".concat(schema.getTypeString(schema.getRawType(edgesType)), " {\n ").concat(CURSOR, "\n ").concat(NODE, " {\n __typename # rely on GenerateRequisiteFieldTransform to add \"id\"\n }\n }\n ");
var edgeAst = parse(edgeText);
var edgeFragment = RelayParser.transform(schema, [edgeAst.definitions[0]])[0]; // When streaming the edges field needs @stream
transformedEdgesSelection = (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, transformedEdgesSelection), {}, {
directives: streamDirective != null ? [].concat((0, _toConsumableArray2["default"])(transformedEdgesSelection.directives), [streamDirective]) : transformedEdgesSelection.directives,
selections: [].concat((0, _toConsumableArray2["default"])(transformedEdgesSelection.selections), [{
directives: [],
kind: 'InlineFragment',
loc: derivedFieldLocation,
metadata: null,
selections: edgeFragment.selections,
typeCondition: edgeFragment.type
}])
}); // Copy the original selections, replacing edges/pageInfo (if present)
// with the generated locations. This is to maintain the original field
// ordering.
var selections = field.selections.map(function (selection) {
if (transformedEdgesSelection != null && edgesSelection != null && selection === edgesSelection) {
return transformedEdgesSelection;
} else if (transformedPageInfoSelection != null && pageInfoSelection != null && selection === pageInfoSelection) {
return transformedPageInfoSelection;
} else {
return selection;
}
}); // If edges/pageInfo were missing, append the generated versions instead.
if (edgesSelection == null && transformedEdgesSelection != null) {
selections.push(transformedEdgesSelection);
}
if (pageInfoSelection == null && transformedPageInfoSelection != null) {
selections.push(transformedPageInfoSelection);
}
return selections;
}
function findArg(field, argName) {
return field.args && field.args.find(function (arg) {
return arg.name === argName;
});
}
/**
* @internal
*
* Validates that the selection is a valid connection:
* - Specifies a first or last argument to prevent accidental, unconstrained
* data access.
* - Has an `edges` selection, otherwise there is nothing to paginate.
*
* TODO: This implementation requires the edges field to be a direct selection
* and not contained within an inline fragment or fragment spread. It's
* technically possible to remove this restriction if this pattern becomes
* common/necessary.
*/
function validateConnectionSelection(field) {
var _ConnectionInterface$2 = ConnectionInterface.get(),
EDGES = _ConnectionInterface$2.EDGES;
if (!findArg(field, FIRST) && !findArg(field, LAST)) {
throw createUserError("Expected field '".concat(field.name, "' to have a '").concat(FIRST, "' or '").concat(LAST, "' ") + 'argument.', [field.loc]);
}
if (!field.selections.some(function (selection) {
return selection.kind === 'LinkedField' && selection.name === EDGES;
})) {
throw createUserError("Expected field '".concat(field.name, "' to have an '").concat(EDGES, "' selection."), [field.loc]);
}
}
/**
* @internal
*
* Validates that the type satisfies the Connection specification:
* - The type has an edges field, and edges have scalar `cursor` and object
* `node` fields.
* - The type has a page info field which is an object with the correct
* subfields.
*/
function validateConnectionType(schema, field, nullableType, connectionDirective) {
var directiveName = connectionDirective.name;
var _ConnectionInterface$3 = ConnectionInterface.get(),
CURSOR = _ConnectionInterface$3.CURSOR,
EDGES = _ConnectionInterface$3.EDGES,
END_CURSOR = _ConnectionInterface$3.END_CURSOR,
HAS_NEXT_PAGE = _ConnectionInterface$3.HAS_NEXT_PAGE,
HAS_PREV_PAGE = _ConnectionInterface$3.HAS_PREV_PAGE,
NODE = _ConnectionInterface$3.NODE,
PAGE_INFO = _ConnectionInterface$3.PAGE_INFO,
START_CURSOR = _ConnectionInterface$3.START_CURSOR;
var typeName = schema.getTypeString(nullableType);
if (!schema.hasField(nullableType, EDGES)) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have an '").concat(EDGES, "' field"), [field.loc]);
}
var edges = schema.getFieldConfig(schema.expectField(nullableType, EDGES));
var edgesType = schema.getNullableType(edges.type);
if (!schema.isList(edgesType)) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have an '").concat(EDGES, "' field that returns ") + 'a list of objects.', [field.loc]);
}
var edgeType = schema.getNullableType(schema.getListItemType(edgesType));
if (!schema.isObject(edgeType) && !schema.isInterface(edgeType)) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have an '").concat(EDGES, "' field that returns ") + 'a list of objects.', [field.loc]);
}
edgeType = schema.assertCompositeType(edgeType);
if (!schema.hasField(edgeType, NODE)) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have an '").concat(EDGES, " { ").concat(NODE, " }' field ") + 'that returns an object, interface, or union.', [field.loc]);
}
var node = schema.getFieldConfig(schema.expectField(edgeType, NODE));
var nodeType = schema.getNullableType(node.type);
if (!(schema.isAbstractType(nodeType) || schema.isObject(nodeType))) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have an '").concat(EDGES, " { ").concat(NODE, " }' field ") + 'that returns an object, interface, or union.', [field.loc]);
}
if (!schema.hasField(edgeType, CURSOR)) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have an '").concat(EDGES, " { ").concat(CURSOR, " }' field ") + 'that returns a scalar value.', [field.loc]);
}
var cursor = schema.getFieldConfig(schema.expectField(edgeType, CURSOR));
if (!schema.isScalar(schema.getNullableType(cursor.type))) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have an '").concat(EDGES, " { ").concat(CURSOR, " }' field ") + 'that returns a scalar value.', [field.loc]);
}
if (!schema.hasField(nullableType, PAGE_INFO)) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have a '").concat(PAGE_INFO, "' field that returns ") + 'an object.', [field.loc]);
}
var pageInfo = schema.getFieldConfig(schema.expectField(nullableType, PAGE_INFO));
var pageInfoType = schema.getNullableType(pageInfo.type);
if (!schema.isObject(pageInfoType)) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected the ") + "field type '".concat(typeName, "' to have a '").concat(PAGE_INFO, "' field that ") + 'returns an object.', [field.loc]);
}
[END_CURSOR, HAS_NEXT_PAGE, HAS_PREV_PAGE, START_CURSOR].forEach(function (fieldName) {
var pageInfoField = schema.getFieldConfig(schema.expectField(schema.assertObjectType(pageInfoType), fieldName));
if (!schema.isScalar(schema.getNullableType(pageInfoField.type))) {
throw createUserError("@".concat(directiveName, " used on invalid field '").concat(field.name, "'. Expected ") + "the field type '".concat(typeName, "' to have a '").concat(PAGE_INFO, " { ").concat(fieldName, " }' ") + 'field returns a scalar.', [field.loc]);
}
});
}
module.exports = {
buildConnectionMetadata: buildConnectionMetadata,
CONNECTION: CONNECTION,
SCHEMA_EXTENSION: SCHEMA_EXTENSION,
transform: connectionTransform
};