“use strict”;

Object.defineProperty(exports, “__esModule”, {

value: true

}); exports.default = void 0;

var _path = _interopRequireDefault(require(“./path”));

var t = _interopRequireWildcard(require(“@babel/types”));

function _getRequireWildcardCache() { if (typeof WeakMap !== “function”) return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj = obj; } } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const testing = process.env.NODE_ENV === “test”;

class TraversalContext {

constructor(scope, opts, state, parentPath) {
  this.queue = null;
  this.parentPath = parentPath;
  this.scope = scope;
  this.state = state;
  this.opts = opts;
}

shouldVisit(node) {
  const opts = this.opts;
  if (opts.enter || opts.exit) return true;
  if (opts[node.type]) return true;
  const keys = t.VISITOR_KEYS[node.type];
  if (!keys || !keys.length) return false;

  for (const key of keys) {
    if (node[key]) return true;
  }

  return false;
}

create(node, obj, key, listKey) {
  return _path.default.get({
    parentPath: this.parentPath,
    parent: node,
    container: obj,
    key: key,
    listKey
  });
}

maybeQueue(path, notPriority) {
  if (this.trap) {
    throw new Error("Infinite cycle detected");
  }

  if (this.queue) {
    if (notPriority) {
      this.queue.push(path);
    } else {
      this.priorityQueue.push(path);
    }
  }
}

visitMultiple(container, parent, listKey) {
  if (container.length === 0) return false;
  const queue = [];

  for (let key = 0; key < container.length; key++) {
    const node = container[key];

    if (node && this.shouldVisit(node)) {
      queue.push(this.create(parent, container, key, listKey));
    }
  }

  return this.visitQueue(queue);
}

visitSingle(node, key) {
  if (this.shouldVisit(node[key])) {
    return this.visitQueue([this.create(node, node, key)]);
  } else {
    return false;
  }
}

visitQueue(queue) {
  this.queue = queue;
  this.priorityQueue = [];
  const visited = [];
  let stop = false;

  for (const path of queue) {
    path.resync();

    if (path.contexts.length === 0 || path.contexts[path.contexts.length - 1] !== this) {
      path.pushContext(this);
    }

    if (path.key === null) continue;

    if (testing && queue.length >= 10000) {
      this.trap = true;
    }

    if (visited.indexOf(path.node) >= 0) continue;
    visited.push(path.node);

    if (path.visit()) {
      stop = true;
      break;
    }

    if (this.priorityQueue.length) {
      stop = this.visitQueue(this.priorityQueue);
      this.priorityQueue = [];
      this.queue = queue;
      if (stop) break;
    }
  }

  for (const path of queue) {
    path.popContext();
  }

  this.queue = null;
  return stop;
}

visit(node, key) {
  const nodes = node[key];
  if (!nodes) return false;

  if (Array.isArray(nodes)) {
    return this.visitMultiple(nodes, node, key);
  } else {
    return this.visitSingle(node, key);
  }
}

}

exports.default = TraversalContext;