text: /* @flow */
  
  // This file contains methods responsible for replacing a node with another.
  
  "use strict";
  
  var _getIterator = require("babel-runtime/core-js/get-iterator")["default"];
  
  var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
  
  var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
  
  exports.__esModule = true;
  exports.replaceWithMultiple = replaceWithMultiple;
  exports.replaceWithSourceString = replaceWithSourceString;
  exports.replaceWith = replaceWith;
  exports._replaceWith = _replaceWith;
  exports.replaceExpressionWithStatements = replaceExpressionWithStatements;
  exports.replaceInline = replaceInline;
  
  var _babelCodeFrame = require("babel-code-frame");
  
  var _babelCodeFrame2 = _interopRequireDefault(_babelCodeFrame);
  
  var _index = require("../index");
  
  var _index2 = _interopRequireDefault(_index);
  
  var _index3 = require("./index");
  
  var _index4 = _interopRequireDefault(_index3);
  
  var _babylon = require("babylon");
  
  var _babelTypes = require("babel-types");
  
  var t = _interopRequireWildcard(_babelTypes);
  
  var hoistVariablesVisitor = {
    Function: function Function(path) {
      path.skip();
    },
  
    VariableDeclaration: function VariableDeclaration(path) {
      if (path.node.kind !== "var") return;
  
      var bindings = path.getBindingIdentifiers();
      for (var key in bindings) {
        path.scope.push({ id: bindings[key] });
      }
  
      var exprs = [];
  
      for (var _iterator = (path.node.declarations /*: Array<Object>*/), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) {
        var _ref;
  
        if (_isArray) {
          if (_i >= _iterator.length) break;
          _ref = _iterator[_i++];
        } else {
          _i = _iterator.next();
          if (_i.done) break;
          _ref = _i.value;
        }
  
        var declar = _ref;
  
        if (declar.init) {
          exprs.push(t.expressionStatement(t.assignmentExpression("=", declar.id, declar.init)));
        }
      }
  
      path.replaceWithMultiple(exprs);
    }
  };
  
  /**
   * Replace a node with an array of multiple. This method performs the following steps:
   *
   *  - Inherit the comments of first provided node with that of the current node.
   *  - Insert the provided nodes after the current node.
   *  - Remove the current node.
   */
  
  function replaceWithMultiple(nodes /*: Array<Object>*/) {
    this.resync();
  
    nodes = this._verifyNodeList(nodes);
    t.inheritLeadingComments(nodes[0], this.node);
    t.inheritTrailingComments(nodes[nodes.length - 1], this.node);
    this.node = this.container[this.key] = null;
    this.insertAfter(nodes);
  
    if (this.node) {
      this.requeue();
    } else {
      this.remove();
    }
  }
  
  /**
   * Parse a string as an expression and replace the current node with the result.
   *
   * NOTE: This is typically not a good idea to use. Building source strings when
   * transforming ASTs is an antipattern and SHOULD NOT be encouraged. Even if it's
   * easier to use, your transforms will be extremely brittle.
   */
  
  function replaceWithSourceString(replacement) {
    this.resync();
  
    try {
      replacement = "(" + replacement + ")";
      replacement = _babylon.parse(replacement);
    } catch (err) {
      var loc = err.loc;
      if (loc) {
        err.message += " - make sure this is an expression.";
        err.message += "\n" + _babelCodeFrame2["default"](replacement, loc.line, loc.column + 1);
      }
      throw err;
    }
  
    replacement = replacement.program.body[0].expression;
    _index2["default"].removeProperties(replacement);
    return this.replaceWith(replacement);
  }
  
  /**
   * Replace the current node with another.
   */
  
  function replaceWith(replacement) {
    this.resync();
  
    if (this.removed) {
      throw new Error("You can't replace this node, we've already removed it");
    }
  
    if (replacement instanceof _index4["default"]) {
      replacement = replacement.node;
    }
  
    if (!replacement) {
      throw new Error("You passed `path.replaceWith()` a falsy node, use `path.remove()` instead");
    }
  
    if (this.node === replacement) {
      return;
    }
  
    if (this.isProgram() && !t.isProgram(replacement)) {
      throw new Error("You can only replace a Program root node with another Program node");
    }
  
    if (Array.isArray(replacement)) {
      throw new Error("Don't use `path.replaceWith()` with an array of nodes, use `path.replaceWithMultiple()`");
    }
  
    if (typeof replacement === "string") {
      throw new Error("Don't use `path.replaceWith()` with a source string, use `path.replaceWithSourceString()`");
    }
  
    // replacing a statement with an expression so wrap it in an expression statement
    if (this.isNodeType("Statement") && t.isExpression(replacement) && !this.canHaveVariableDeclarationOrExpression()) {
      replacement = t.expressionStatement(replacement);
    }
  
    // replacing an expression with a statement so let's explode it
    if (this.isNodeType("Expression") && t.isStatement(replacement)) {
      return this.replaceExpressionWithStatements([replacement]);
    }
  
    var oldNode = this.node;
    if (oldNode) {
      t.inheritsComments(replacement, oldNode);
      t.removeComments(oldNode);
    }
  
    // replace the node
    this._replaceWith(replacement);
    this.type = replacement.type;
  
    // potentially create new scope
    this.setScope();
  
    // requeue for visiting
    this.requeue();
  }
  
  /**
   * Description
   */
  
  function _replaceWith(node) {
    if (!this.container) {
      throw new ReferenceError("Container is falsy");
    }
  
    if (this.inList) {
      t.validate(this.parent, this.key, [node]);
    } else {
      t.validate(this.parent, this.key, node);
    }
  
    this.debug(function () {
      return "Replace with " + (node && node.type);
    });
  
    this.node = this.container[this.key] = node;
  }
  
  /**
   * This method takes an array of statements nodes and then explodes it
   * into expressions. This method retains completion records which is
   * extremely important to retain original semantics.
   */
  
  function replaceExpressionWithStatements(nodes /*: Array<Object>*/) {
    this.resync();
  
    var toSequenceExpression = t.toSequenceExpression(nodes, this.scope);
  
    if (t.isSequenceExpression(toSequenceExpression)) {
      var exprs = toSequenceExpression.expressions;
  
      if (exprs.length >= 2 && this.parentPath.isExpressionStatement()) {
        this._maybePopFromStatements(exprs);
      }
  
      // could be just one element due to the previous maybe popping
      if (exprs.length === 1) {
        this.replaceWith(exprs[0]);
      } else {
        this.replaceWith(toSequenceExpression);
      }
    } else if (toSequenceExpression) {
      this.replaceWith(toSequenceExpression);
    } else {
      var container = t.functionExpression(null, [], t.blockStatement(nodes));
      container.shadow = true;
  
      this.replaceWith(t.callExpression(container, []));
      this.traverse(hoistVariablesVisitor);
  
      // add implicit returns to all ending expression statements
      var completionRecords /*: Array<NodePath>*/ = this.get("callee").getCompletionRecords();
      for (var _iterator2 = completionRecords, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _getIterator(_iterator2);;) {
        var _ref2;
  
        if (_isArray2) {
          if (_i2 >= _iterator2.length) break;
          _ref2 = _iterator2[_i2++];
        } else {
          _i2 = _iterator2.next();
          if (_i2.done) break;
          _ref2 = _i2.value;
        }
  
        var path = _ref2;
  
        if (!path.isExpressionStatement()) continue;
  
        var loop = path.findParent(function (path) {
          return path.isLoop();
        });
        if (loop) {
          var callee = this.get("callee");
  
          var uid = callee.scope.generateDeclaredUidIdentifier("ret");
          callee.get("body").pushContainer("body", t.returnStatement(uid));
  
          path.get("expression").replaceWith(t.assignmentExpression("=", uid, path.node.expression));
        } else {
          path.replaceWith(t.returnStatement(path.node.expression));
        }
      }
  
      return this.node;
    }
  }
  
  function replaceInline(nodes /*: Object | Array<Object>*/) {
    this.resync();
  
    if (Array.isArray(nodes)) {
      if (Array.isArray(this.container)) {
        nodes = this._verifyNodeList(nodes);
        this._containerInsertAfter(nodes);
        return this.remove();
      } else {
        return this.replaceWithMultiple(nodes);
      }
    } else {
      return this.replaceWith(nodes);
    }
  }