Back
vendored
traverse
v0.6.8
MIT

traverse

The published traverse package as a vendorable file, using the last dependency-free release. Useful when you want the classic recursive object walker locally without pulling in a deeper modern dependency graph first.

Why copy this?

This page ships the published traverse 0.6.8 file in both raw and normalized forms. The value is to keep a classic recursive walker local and inspectable without starting with the newer dependency-heavy release line.

Native alternative: There is no direct native replacement for traverse; teams typically end up rewriting custom recursive walkers, maps, and clone helpers ad hoc.

Note: Current traverse releases pull in deeper external dependencies. This snippet intentionally uses 0.6.8 as a simpler first-pass vendorable target.

This copy is your responsibility once you adopt it. It does not automatically receive upstream bug fixes or security updates.

Snippet
Copy-first distribution
Normalized
ESM / TS / normalized
Runtime: node, bun
/**
 * Derived from [email protected]
 * Rule-based normalized variant generated by this repository.
 * Preserve the upstream license and attribution notices when copying this file.
 */
/**
 * Derived from [email protected]
 * Rule-based normalized variant generated by this repository.
 * See THIRD_PARTY_NOTICES.md for upstream license and attribution details.
 */
"use strict";
function toS(obj) {
  return Object.prototype.toString.call(obj);
}
function isDate(obj) {
  return toS(obj) === "[object Date]";
}
function isRegExp(obj) {
  return toS(obj) === "[object RegExp]";
}
function isError(obj) {
  return toS(obj) === "[object Error]";
}
function isBoolean(obj) {
  return toS(obj) === "[object Boolean]";
}
function isNumber(obj) {
  return toS(obj) === "[object Number]";
}
function isString(obj) {
  return toS(obj) === "[object String]";
}

var isArray =
  Array.isArray ||
  function isArray(xs) {
    return Object.prototype.toString.call(xs) === "[object Array]";
  };

function forEach(xs, fn) {
  if (xs.forEach) {
    return xs.forEach(fn);
  }
  for (var i = 0; i < xs.length; i++) {
    fn(xs[i], i, xs);
  }
  return void undefined;
}

var objectKeys =
  Object.keys ||
  function keys(obj) {
    var res = [];
    for (var key in obj) {
      res.push(key);
    }
    return res;
  };

var propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
var getOwnPropertySymbols = Object.getOwnPropertySymbols;

function ownEnumerableKeys(obj) {
  var res = objectKeys(obj);

  if (getOwnPropertySymbols) {
    var symbols = getOwnPropertySymbols(obj);
    for (var i = 0; i < symbols.length; i++) {
      if (propertyIsEnumerable.call(obj, symbols[i])) {
        res.push(symbols[i]);
      }
    }
  }
  return res;
}

var hasOwnProperty =
  Object.prototype.hasOwnProperty ||
  function (obj, key) {
    return key in obj;
  };

function copy(src) {
  if (typeof src === "object" && src !== null) {
    var dst;

    if (isArray(src)) {
      dst = [];
    } else if (isDate(src)) {
      dst = new Date(src.getTime ? src.getTime() : src);
    } else if (isRegExp(src)) {
      dst = new RegExp(src);
    } else if (isError(src)) {
      dst = { message: src.message };
    } else if (isBoolean(src) || isNumber(src) || isString(src)) {
      dst = Object(src);
    } else if (Object.create && Object.getPrototypeOf) {
      dst = Object.create(Object.getPrototypeOf(src));
    } else if (src.constructor === Object) {
      dst = {};
    } else {
      var proto = (src.constructor && src.constructor.prototype) || src.__proto__ || {};
      var T = function T() {};
      T.prototype = proto;
      dst = new T();
    }

    forEach(ownEnumerableKeys(src), function (key) {
      dst[key] = src[key];
    });
    return dst;
  }
  return src;
}

function walk(root, cb, immutable) {
  var path = [];
  var parents = [];
  var alive = true;

  return (function walker(node_) {
    var node = immutable ? copy(node_) : node_;
    var modifiers = {};

    var keepGoing = true;

    var state = {
      node: node,
      node_: node_,
      path: [].concat(path),
      parent: parents[parents.length - 1],
      parents: parents,
      key: path[path.length - 1],
      isRoot: path.length === 0,
      level: path.length,
      circular: null,
      update: function (x, stopHere) {
        if (!state.isRoot) {
          state.parent.node[state.key] = x;
        }
        state.node = x;
        if (stopHere) {
          keepGoing = false;
        }
      },
      delete: function (stopHere) {
        delete state.parent.node[state.key];
        if (stopHere) {
          keepGoing = false;
        }
      },
      remove: function (stopHere) {
        if (isArray(state.parent.node)) {
          state.parent.node.splice(state.key, 1);
        } else {
          delete state.parent.node[state.key];
        }
        if (stopHere) {
          keepGoing = false;
        }
      },
      keys: null,
      before: function (f) {
        modifiers.before = f;
      },
      after: function (f) {
        modifiers.after = f;
      },
      pre: function (f) {
        modifiers.pre = f;
      },
      post: function (f) {
        modifiers.post = f;
      },
      stop: function () {
        alive = false;
      },
      block: function () {
        keepGoing = false;
      },
    };

    if (!alive) {
      return state;
    }

    function updateState() {
      if (typeof state.node === "object" && state.node !== null) {
        if (!state.keys || state.node_ !== state.node) {
          state.keys = ownEnumerableKeys(state.node);
        }

        state.isLeaf = state.keys.length === 0;

        for (var i = 0; i < parents.length; i++) {
          if (parents[i].node_ === node_) {
            state.circular = parents[i];
            break;
          }
        }
      } else {
        state.isLeaf = true;
        state.keys = null;
      }

      state.notLeaf = !state.isLeaf;
      state.notRoot = !state.isRoot;
    }

    updateState();

    var ret = cb.call(state, state.node);
    if (ret !== undefined && state.update) {
      state.update(ret);
    }

    if (modifiers.before) {
      modifiers.before.call(state, state.node);
    }

    if (!keepGoing) {
      return state;
    }

    if (typeof state.node === "object" && state.node !== null && !state.circular) {
      parents.push(state);

      updateState();

      forEach(state.keys, function (key, i) {
        path.push(key);

        if (modifiers.pre) {
          modifiers.pre.call(state, state.node[key], key);
        }

        var child = walker(state.node[key]);
        if (immutable && hasOwnProperty.call(state.node, key)) {
          state.node[key] = child.node;
        }

        child.isLast = i === state.keys.length - 1;
        child.isFirst = i === 0;

        if (modifiers.post) {
          modifiers.post.call(state, child);
        }

        path.pop();
      });
      parents.pop();
    }

    if (modifiers.after) {
      modifiers.after.call(state, state.node);
    }

    return state;
  })(root).node;
}

function Traverse(obj) {
  this.value = obj;
}

Traverse.prototype.get = function (ps) {
  var node = this.value;
  for (var i = 0; i < ps.length; i++) {
    var key = ps[i];
    if (!node || !hasOwnProperty.call(node, key)) {
      return void undefined;
    }
    node = node[key];
  }
  return node;
};

Traverse.prototype.has = function (ps) {
  var node = this.value;
  for (var i = 0; i < ps.length; i++) {
    var key = ps[i];
    if (!node || !hasOwnProperty.call(node, key)) {
      return false;
    }
    node = node[key];
  }
  return true;
};

Traverse.prototype.set = function (ps, value) {
  var node = this.value;
  for (var i = 0; i < ps.length - 1; i++) {
    var key = ps[i];
    if (!hasOwnProperty.call(node, key)) {
      node[key] = {};
    }
    node = node[key];
  }
  node[ps[i]] = value;
  return value;
};

Traverse.prototype.map = function (cb) {
  return walk(this.value, cb, true);
};

Traverse.prototype.forEach = function (cb) {
  this.value = walk(this.value, cb, false);
  return this.value;
};

Traverse.prototype.reduce = function (cb, init) {
  var skip = arguments.length === 1;
  var acc = skip ? this.value : init;
  this.forEach(function (x) {
    if (!this.isRoot || !skip) {
      acc = cb.call(this, acc, x);
    }
  });
  return acc;
};

Traverse.prototype.paths = function () {
  var acc = [];
  this.forEach(function () {
    acc.push(this.path);
  });
  return acc;
};

Traverse.prototype.nodes = function () {
  var acc = [];
  this.forEach(function () {
    acc.push(this.node);
  });
  return acc;
};

Traverse.prototype.clone = function () {
  var parents = [];
  var nodes = [];

  return (function clone(src) {
    for (var i = 0; i < parents.length; i++) {
      if (parents[i] === src) {
        return nodes[i];
      }
    }

    if (typeof src === "object" && src !== null) {
      var dst = copy(src);

      parents.push(src);
      nodes.push(dst);

      forEach(ownEnumerableKeys(src), function (key) {
        dst[key] = clone(src[key]);
      });

      parents.pop();
      nodes.pop();
      return dst;
    }

    return src;
  })(this.value);
};

function traverse(obj) {
  return new Traverse(obj);
}

forEach(ownEnumerableKeys(Traverse.prototype), function (key) {
  traverse[key] = function (obj) {
    var args = [].slice.call(arguments, 1);
    var t = new Traverse(obj);
    return t[key].apply(t, args);
  };
});

export default traverse;
Variant note: Rule-based normalization for modern TS projects. The goal is easier import and build compatibility, not changed behavior.
Transforms: module.exports-to-export-default, js-extension-to-ts-extension
Validation: This normalized variant is intended to stay oxlint and oxfmt clean in this repo.
Raw
CJS / JS / raw
Runtime: node, bun
'use strict';

function toS(obj) { return Object.prototype.toString.call(obj); }
function isDate(obj) { return toS(obj) === '[object Date]'; }
function isRegExp(obj) { return toS(obj) === '[object RegExp]'; }
function isError(obj) { return toS(obj) === '[object Error]'; }
function isBoolean(obj) { return toS(obj) === '[object Boolean]'; }
function isNumber(obj) { return toS(obj) === '[object Number]'; }
function isString(obj) { return toS(obj) === '[object String]'; }

var isArray = Array.isArray || function isArray(xs) {
  return Object.prototype.toString.call(xs) === '[object Array]';
};

function forEach(xs, fn) {
  if (xs.forEach) { return xs.forEach(fn); }
  for (var i = 0; i < xs.length; i++) {
    fn(xs[i], i, xs);
  }
  return void undefined;
}

var objectKeys = Object.keys || function keys(obj) {
  var res = [];
  for (var key in obj) { res.push(key); }
  return res;
};

var propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
var getOwnPropertySymbols = Object.getOwnPropertySymbols;

function ownEnumerableKeys(obj) {
  var res = objectKeys(obj);

  if (getOwnPropertySymbols) {
    var symbols = getOwnPropertySymbols(obj);
    for (var i = 0; i < symbols.length; i++) {
      if (propertyIsEnumerable.call(obj, symbols[i])) {
        res.push(symbols[i]);
      }
    }
  }
  return res;
}

var hasOwnProperty = Object.prototype.hasOwnProperty || function (obj, key) {
  return key in obj;
};

function copy(src) {
  if (typeof src === 'object' && src !== null) {
    var dst;

    if (isArray(src)) {
      dst = [];
    } else if (isDate(src)) {
      dst = new Date(src.getTime ? src.getTime() : src);
    } else if (isRegExp(src)) {
      dst = new RegExp(src);
    } else if (isError(src)) {
      dst = { message: src.message };
    } else if (isBoolean(src) || isNumber(src) || isString(src)) {
      dst = Object(src);
    } else if (Object.create && Object.getPrototypeOf) {
      dst = Object.create(Object.getPrototypeOf(src));
    } else if (src.constructor === Object) {
      dst = {};
    } else {
      var proto = (src.constructor && src.constructor.prototype)
        || src.__proto__
        || {};
      var T = function T() {};
      T.prototype = proto;
      dst = new T();
    }

    forEach(ownEnumerableKeys(src), function (key) {
      dst[key] = src[key];
    });
    return dst;
  }
  return src;
}

function walk(root, cb, immutable) {
  var path = [];
  var parents = [];
  var alive = true;

  return (function walker(node_) {
    var node = immutable ? copy(node_) : node_;
    var modifiers = {};

    var keepGoing = true;

    var state = {
      node: node,
      node_: node_,
      path: [].concat(path),
      parent: parents[parents.length - 1],
      parents: parents,
      key: path[path.length - 1],
      isRoot: path.length === 0,
      level: path.length,
      circular: null,
      update: function (x, stopHere) {
        if (!state.isRoot) {
          state.parent.node[state.key] = x;
        }
        state.node = x;
        if (stopHere) { keepGoing = false; }
      },
      delete: function (stopHere) {
        delete state.parent.node[state.key];
        if (stopHere) { keepGoing = false; }
      },
      remove: function (stopHere) {
        if (isArray(state.parent.node)) {
          state.parent.node.splice(state.key, 1);
        } else {
          delete state.parent.node[state.key];
        }
        if (stopHere) { keepGoing = false; }
      },
      keys: null,
      before: function (f) { modifiers.before = f; },
      after: function (f) { modifiers.after = f; },
      pre: function (f) { modifiers.pre = f; },
      post: function (f) { modifiers.post = f; },
      stop: function () { alive = false; },
      block: function () { keepGoing = false; }
    };

    if (!alive) { return state; }

    function updateState() {
      if (typeof state.node === 'object' && state.node !== null) {
        if (!state.keys || state.node_ !== state.node) {
          state.keys = ownEnumerableKeys(state.node);
        }

        state.isLeaf = state.keys.length === 0;

        for (var i = 0; i < parents.length; i++) {
          if (parents[i].node_ === node_) {
            state.circular = parents[i];
            break;
          }
        }
      } else {
        state.isLeaf = true;
        state.keys = null;
      }

      state.notLeaf = !state.isLeaf;
      state.notRoot = !state.isRoot;
    }

    updateState();

    var ret = cb.call(state, state.node);
    if (ret !== undefined && state.update) { state.update(ret); }

    if (modifiers.before) { modifiers.before.call(state, state.node); }

    if (!keepGoing) { return state; }

    if (
      typeof state.node === 'object'
      && state.node !== null
      && !state.circular
    ) {
      parents.push(state);

      updateState();

      forEach(state.keys, function (key, i) {
        path.push(key);

        if (modifiers.pre) { modifiers.pre.call(state, state.node[key], key); }

        var child = walker(state.node[key]);
        if (immutable && hasOwnProperty.call(state.node, key)) {
          state.node[key] = child.node;
        }

        child.isLast = i === state.keys.length - 1;
        child.isFirst = i === 0;

        if (modifiers.post) { modifiers.post.call(state, child); }

        path.pop();
      });
      parents.pop();
    }

    if (modifiers.after) { modifiers.after.call(state, state.node); }

    return state;
  }(root)).node;
}

function Traverse(obj) {
  this.value = obj;
}

Traverse.prototype.get = function (ps) {
  var node = this.value;
  for (var i = 0; i < ps.length; i++) {
    var key = ps[i];
    if (!node || !hasOwnProperty.call(node, key)) {
      return void undefined;
    }
    node = node[key];
  }
  return node;
};

Traverse.prototype.has = function (ps) {
  var node = this.value;
  for (var i = 0; i < ps.length; i++) {
    var key = ps[i];
    if (!node || !hasOwnProperty.call(node, key)) {
      return false;
    }
    node = node[key];
  }
  return true;
};

Traverse.prototype.set = function (ps, value) {
  var node = this.value;
  for (var i = 0; i < ps.length - 1; i++) {
    var key = ps[i];
    if (!hasOwnProperty.call(node, key)) { node[key] = {}; }
    node = node[key];
  }
  node[ps[i]] = value;
  return value;
};

Traverse.prototype.map = function (cb) {
  return walk(this.value, cb, true);
};

Traverse.prototype.forEach = function (cb) {
  this.value = walk(this.value, cb, false);
  return this.value;
};

Traverse.prototype.reduce = function (cb, init) {
  var skip = arguments.length === 1;
  var acc = skip ? this.value : init;
  this.forEach(function (x) {
    if (!this.isRoot || !skip) {
      acc = cb.call(this, acc, x);
    }
  });
  return acc;
};

Traverse.prototype.paths = function () {
  var acc = [];
  this.forEach(function () {
    acc.push(this.path);
  });
  return acc;
};

Traverse.prototype.nodes = function () {
  var acc = [];
  this.forEach(function () {
    acc.push(this.node);
  });
  return acc;
};

Traverse.prototype.clone = function () {
  var parents = [];
  var nodes = [];

  return (function clone(src) {
    for (var i = 0; i < parents.length; i++) {
      if (parents[i] === src) {
        return nodes[i];
      }
    }

    if (typeof src === 'object' && src !== null) {
      var dst = copy(src);

      parents.push(src);
      nodes.push(dst);

      forEach(ownEnumerableKeys(src), function (key) {
        dst[key] = clone(src[key]);
      });

      parents.pop();
      nodes.pop();
      return dst;
    }

    return src;

  }(this.value));
};

function traverse(obj) {
  return new Traverse(obj);
}

forEach(ownEnumerableKeys(Traverse.prototype), function (key) {
  traverse[key] = function (obj) {
    var args = [].slice.call(arguments, 1);
    var t = new Traverse(obj);
    return t[key].apply(t, args);
  };
});

module.exports = traverse;
Variant note: Published package file kept as-is from the last dependency-free release line.