text: /**
   * 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
   */
  'use strict';
  
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  
  var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
  
  var IRVisitor = require('../core/IRVisitor');
  
  var getLiteralArgumentValues = require('../core/getLiteralArgumentValues');
  
  var inferRootArgumentDefinitions = require('../core/inferRootArgumentDefinitions');
  
  var _require = require('../core/CompilerError'),
      createUserError = _require.createUserError,
      eachWithCombinedError = _require.eachWithCombinedError;
  
  var _require2 = require('./query-generators'),
      buildRefetchOperation = _require2.buildRefetchOperation;
  
  var SCHEMA_EXTENSION = "\n  directive @refetchable(\n    queryName: String!\n  ) on FRAGMENT_DEFINITION\n";
  /**
   * This transform synthesizes "refetch" queries for fragments that
   * are trivially refetchable. This is comprised of three main stages:
   *
   * 1. Validating that fragments marked with @refetchable qualify for
   *    refetch query generation; mainly this means that the fragment
   *    type is able to be refetched in some canonical way.
   * 2. Determining the variable definitions to use for each generated
   *    query. GraphQL does not have a notion of fragment-local variables
   *    at all, and although Relay adds this concept developers are still
   *    allowed to reference global variables. This necessitates a
   *    visiting all reachable fragments for each @refetchable fragment,
   *    and finding the union of all global variables expceted to be defined.
   * 3. Building the refetch queries, a straightforward copying transform from
   *    Fragment to Root IR nodes.
   */
  
  function refetchableFragmentTransform(context) {
    var schema = context.getSchema();
    var refetchOperations = buildRefetchMap(context);
    var nextContext = context;
    eachWithCombinedError(refetchOperations, function (_ref2) {
      var _connectionMetadata;
  
      var refetchName = _ref2[0],
          fragment = _ref2[1];
  
      var _buildRefetchOperatio = buildRefetchOperation(schema, fragment, refetchName),
          path = _buildRefetchOperatio.path,
          node = _buildRefetchOperatio.node,
          transformedFragment = _buildRefetchOperatio.transformedFragment;
  
      var connectionMetadata = extractConnectionMetadata(context.getSchema(), transformedFragment);
      nextContext = nextContext.replace((0, _objectSpread2["default"])({}, transformedFragment, {
        metadata: (0, _objectSpread2["default"])({}, transformedFragment.metadata || {}, {
          refetch: {
            connection: (_connectionMetadata = connectionMetadata) !== null && _connectionMetadata !== void 0 ? _connectionMetadata : null,
            operation: refetchName,
            fragmentPathInResult: path
          }
        })
      }));
      nextContext = nextContext.add((0, _objectSpread2["default"])({}, node, {
        metadata: (0, _objectSpread2["default"])({}, node.metadata || {}, {
          derivedFrom: transformedFragment.name,
          isRefetchableQuery: true
        })
      }));
    });
    return nextContext;
  }
  /**
   * Walk the documents of a compiler context and create a mapping of
   * refetch operation names to the source fragment from which the refetch
   * operation should be derived.
   */
  
  
  function buildRefetchMap(context) {
    var refetchOperations = new Map();
    eachWithCombinedError(context.documents(), function (node) {
      if (node.kind !== 'Fragment') {
        return;
      }
  
      var refetchName = getRefetchQueryName(node);
  
      if (refetchName === null) {
        return;
      }
  
      var previousOperation = refetchOperations.get(refetchName);
  
      if (previousOperation != null) {
        throw createUserError("Duplicate definition for @refetchable operation '".concat(refetchName, "' from fragments '").concat(node.name, "' and '").concat(previousOperation.name, "'"), [node.loc, previousOperation.loc]);
      }
  
      refetchOperations.set(refetchName, node);
    });
    var transformed = inferRootArgumentDefinitions(context);
    return new Map(Array.from(refetchOperations.entries(), function (_ref3) {
      var name = _ref3[0],
          fragment = _ref3[1];
      return [name, transformed.getFragment(fragment.name)];
    }));
  }
  /**
   * Validate that any @connection usage is valid for refetching:
   * - Variables are used for both the "count" and "cursor" arguments
   *   (after/first or before/last)
   * - Exactly one connection
   * - Has a stable path to the connection data
   *
   * Returns connection metadata to add to the transformed fragment or undefined
   * if there is no connection.
   */
  
  
  function extractConnectionMetadata(schema, fragment) {
    var fields = [];
    var connectionField = null;
    var path = null;
    IRVisitor.visit(fragment, {
      ConnectionField: {
        enter: function enter(field) {
          fields.push(field); // Disallow connections within plurals
  
          var pluralOnPath = fields.find(function (pathField) {
            return schema.isList(schema.getNullableType(pathField.type));
          });
  
          if (pluralOnPath) {
            throw createUserError("Invalid use of @refetchable with @connection in fragment '".concat(fragment.name, "', refetchable connections cannot appear inside plural fields."), [field.loc, pluralOnPath.loc]);
          }
  
          connectionField = field;
          path = fields.map(function (pathField) {
            return pathField.alias;
          });
        },
        leave: function leave() {
          fields.pop();
        }
      },
      LinkedField: {
        enter: function enter(field) {
          fields.push(field);
  
          if (field.connection === true || field.handles && field.handles.some(function (handle) {
            return handle.name === 'connection';
          })) {
            // Disallow multiple @connections
            if (connectionField != null) {
              throw createUserError("Invalid use of @refetchable with @connection in fragment '".concat(fragment.name, "', at most once @connection can appear in a refetchable fragment."), [field.loc]);
            } // Disallow connections within plurals
  
  
            var pluralOnPath = fields.find(function (pathField) {
              return schema.isList(schema.getNullableType(pathField.type));
            });
  
            if (pluralOnPath) {
              throw createUserError("Invalid use of @refetchable with @connection in fragment '".concat(fragment.name, "', refetchable connections cannot appear inside plural fields."), [field.loc, pluralOnPath.loc]);
            }
  
            connectionField = field;
            path = fields.map(function (pathField) {
              return pathField.alias;
            });
          }
        },
        leave: function leave() {
          fields.pop();
        }
      }
    });
  
    if (connectionField == null || path == null) {
      return;
    } // Validate arguments: if either of before/last appear they must both appear
    // and use variables (not scalar values)
  
  
    var backward = null;
    var before = findArgument(connectionField, 'before');
    var last = findArgument(connectionField, 'last');
  
    if (before || last) {
      if (!before || !last || before.value.kind !== 'Variable' || last.value.kind !== 'Variable') {
        throw createUserError("Invalid use of @refetchable with @connection in fragment '".concat(fragment.name, "', refetchable connections must use variables for the before and last arguments."), [connectionField.loc, before && before.value.kind !== 'Variable' ? before.value.loc : null, last && last.value.kind !== 'Variable' ? last.value.loc : null].filter(Boolean));
      }
  
      backward = {
        count: last.value.variableName,
        cursor: before.value.variableName
      };
    } // Validate arguments: if either of after/first appear they must both appear
    // and use variables (not scalar values)
  
  
    var forward = null;
    var after = findArgument(connectionField, 'after');
    var first = findArgument(connectionField, 'first');
  
    if (after || first) {
      if (!after || !first || after.value.kind !== 'Variable' || first.value.kind !== 'Variable') {
        throw createUserError("Invalid use of @refetchable with @connection in fragment '".concat(fragment.name, "', refetchable connections must use variables for the after and first arguments."), [connectionField.loc, after && after.value.kind !== 'Variable' ? after.value.loc : null, first && first.value.kind !== 'Variable' ? first.value.loc : null].filter(Boolean));
      }
  
      forward = {
        count: first.value.variableName,
        cursor: after.value.variableName
      };
    }
  
    return {
      forward: forward,
      backward: backward,
      path: path
    };
  }
  
  function getRefetchQueryName(fragment) {
    var refetchableDirective = fragment.directives.find(function (directive) {
      return directive.name === 'refetchable';
    });
  
    if (refetchableDirective == null) {
      return null;
    }
  
    var refetchArguments = getLiteralArgumentValues(refetchableDirective.args);
    var queryName = refetchArguments.queryName;
  
    if (queryName == null) {
      throw createUserError("Expected the 'queryName' argument of @refetchable to be provided", [refetchableDirective.loc]);
    } else if (typeof queryName !== 'string') {
      var _ref;
  
      var queryNameArg = refetchableDirective.args.find(function (arg) {
        return arg.name === 'queryName';
      });
      throw createUserError("Expected the 'queryName' argument of @refetchable to be a string, got '".concat(String(queryName), "'."), [(_ref = queryNameArg === null || queryNameArg === void 0 ? void 0 : queryNameArg.loc) !== null && _ref !== void 0 ? _ref : refetchableDirective.loc]);
    }
  
    return queryName;
  }
  
  function findArgument(field, argumentName) {
    var _field$args$find;
  
    return (_field$args$find = field.args.find(function (arg) {
      return arg.name === argumentName;
    })) !== null && _field$args$find !== void 0 ? _field$args$find : null;
  }
  
  module.exports = {
    SCHEMA_EXTENSION: SCHEMA_EXTENSION,
    transform: refetchableFragmentTransform
  };