whole_func_string: function astTraverse(root, options = {}) {
    const pre = options.pre;
    const post = options.post;
    const skipProperty = options.skipProperty;
  
    function visit(node, parent, prop, idx) {
      if (!node || typeof node.type !== 'string') {
        return;
      }
  
      let res = undefined;
      if (pre) {
        res = pre(node, parent, prop, idx);
      }
  
      if (res !== false) {
  
        // A node can be replaced during traversal, so we have to
        // recalculate it from the parent, to avoid traversing "dead" nodes.
        if (parent && parent[prop]) {
          if (!isNaN(idx)) {
            node = parent[prop][idx];
          } else {
            node = parent[prop];
          }
        }
  
        for (let prop in node) if (node.hasOwnProperty(prop)) {
          if (skipProperty ? skipProperty(prop, node) : prop[0] === '$') {
            continue;
          }
  
          const child = node[prop];
  
          // Collection node.
          //
          // NOTE: a node (or several nodes) can be removed or inserted
          // during traversal.
          //
          // Current traversing index is stored on top of the
          // `NodePath.traversingIndexStack`. The stack is used to support
          // recursive nature of the traversal.
          //
          // In this case `NodePath.traversingIndex` (which we use here) is
          // updated in the NodePath remove/insert methods.
          //
          if (Array.isArray(child)) {
            let index = 0;
            NodePath.traversingIndexStack.push(index);
            while (index < child.length) {
              visit(
                child[index],
                node,
                prop,
                index
              );
              index = NodePath.updateTraversingIndex(+1);
            }
            NodePath.traversingIndexStack.pop();
          }
  
          // Simple node.
          else {
            visit(child, node, prop);
          }
        }
      }
  
      if (post) {
        post(node, parent, prop, idx);
      }
    }
  
    visit(root, null);
  }
func_code_string: function astTraverse(root, options = {}) {
    const pre = options.pre;
    const post = options.post;
    const skipProperty = options.skipProperty;
  
    function visit(node, parent, prop, idx) {
      if (!node || typeof node.type !== 'string') {
        return;
      }
  
      let res = undefined;
      if (pre) {
        res = pre(node, parent, prop, idx);
      }
  
      if (res !== false) {
  
        // A node can be replaced during traversal, so we have to
        // recalculate it from the parent, to avoid traversing "dead" nodes.
        if (parent && parent[prop]) {
          if (!isNaN(idx)) {
            node = parent[prop][idx];
          } else {
            node = parent[prop];
          }
        }
  
        for (let prop in node) if (node.hasOwnProperty(prop)) {
          if (skipProperty ? skipProperty(prop, node) : prop[0] === '$') {
            continue;
          }
  
          const child = node[prop];
  
          // Collection node.
          //
          // NOTE: a node (or several nodes) can be removed or inserted
          // during traversal.
          //
          // Current traversing index is stored on top of the
          // `NodePath.traversingIndexStack`. The stack is used to support
          // recursive nature of the traversal.
          //
          // In this case `NodePath.traversingIndex` (which we use here) is
          // updated in the NodePath remove/insert methods.
          //
          if (Array.isArray(child)) {
            let index = 0;
            NodePath.traversingIndexStack.push(index);
            while (index < child.length) {
              visit(
                child[index],
                node,
                prop,
                index
              );
              index = NodePath.updateTraversingIndex(+1);
            }
            NodePath.traversingIndexStack.pop();
          }
  
          // Simple node.
          else {
            visit(child, node, prop);
          }
        }
      }
  
      if (post) {
        post(node, parent, prop, idx);
      }
    }
  
    visit(root, null);
  }
func_documentation_string: Does an actual AST traversal, using visitor pattern,
  and calling set of callbacks.
  
  Based on https://github.com/olov/ast-traverse
  
  Expects AST in Mozilla Parser API: nodes which are supposed to be
  handled should have `type` property.
  
  @param Object root - a root node to start traversal from.
  
  @param Object options - an object with set of callbacks:
  
  - `pre(node, parent, prop, index)` - a hook called on node enter
  - `post`(node, parent, prop, index) - a hook called on node exit
  - `skipProperty(prop)` - a predicated whether a property should be skipped