{"id":22488983,"url":"https://github.com/PuruVJ/neotraverse","last_synced_at":"2025-08-02T21:31:58.478Z","repository":{"id":247851037,"uuid":"826794914","full_name":"PuruVJ/neotraverse","owner":"PuruVJ","description":null,"archived":false,"fork":false,"pushed_at":"2024-08-17T11:23:20.000Z","size":162,"stargazers_count":218,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-06T06:02:44.316Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PuruVJ.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-10T11:47:53.000Z","updated_at":"2024-11-25T22:50:14.000Z","dependencies_parsed_at":"2024-08-17T12:54:48.369Z","dependency_job_id":null,"html_url":"https://github.com/PuruVJ/neotraverse","commit_stats":null,"previous_names":["puruvj/neotraverse"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PuruVJ%2Fneotraverse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PuruVJ%2Fneotraverse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PuruVJ%2Fneotraverse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PuruVJ%2Fneotraverse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PuruVJ","download_url":"https://codeload.github.com/PuruVJ/neotraverse/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228419604,"owners_count":17916772,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-06T17:19:01.967Z","updated_at":"2024-12-06T17:21:07.915Z","avatar_url":"https://github.com/PuruVJ.png","language":"TypeScript","readme":"# neotraverse\n\nTraverse and transform objects by visiting every node on a recursive walk. This is a fork and TypeScript rewrite of [traverse](https://github.com/ljharb/js-traverse) with 0 dependencies and major improvements:\n\n- 🤌 1.38KB min+brotli\n- 🚥 Zero dependencies\n- 🎹 TypeScript. Throw away the `@types/traverse` package\n- ❎ No polyfills\n- 🛸 ESM-first\n- 📜 Legacy mode supporting ES5\n\n# Principles\n\nRules this package aims to follow for an indefinite period of time:\n\n- No dependencies.\n- No polyfills.\n- ESM-first.\n- Pushing to be modern\n- Always provide a legacy mode\n- Always follow `traverse` API. There already are many packages that do this. `neotraverse` intends to be a drop-in replacement for `traverse` and provide the same API with 0 dependencies and enhanced Developer Experience.\n- All deviating changes happen in `neotraverse/modern` build.\n\n# Modern build\n\n`neotraverse/modern` provides a new class `new Traverse()`, and all methods and state is provided as first argument `ctx` (`this.update -\u003e ctx.update`, `this.isLeaf -\u003e ctx.isLeaf`, etc.)\n\nBefore:\n\n```js\nimport traverse from 'neotraverse';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\n\ntraverse(obj).forEach(function (x) {\n  if (x \u003c 0) this.update(x + 128);\n});\n```\n\nAfter:\n\n```js\nimport { Traverse } from 'neotraverse/modern';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\n\nnew Traverse(obj).forEach((ctx, x) =\u003e {\n  if (x \u003c 0) ctx.update(x + 128);\n});\n```\n\n# Which build to use?\n\n`neotraverse` provides 3 builds:\n\n- default: Backwards compatible with `traverse` and provides the same API, but ESM only and compiled to ES2022 with Node 18+\n- modern: Modern build with ESM only and compiled to ES2022 with Node 18+. Provides a new class `new Traverse()`, and all methods and state is provided as first argument `ctx` (`this.update -\u003e ctx.update`, `this.isLeaf -\u003e ctx.isLeaf`, etc.)\n- legacy: Legacy build with ES5 and CJS, compatible with `traverse` and provides the same API.\n\nHere's a matrix of the different builds:\n\n| Build   | ESM       | CJS | Browser | Node | Polyfills | Size              |\n| ------- | --------- | --- | ------- | ---- | --------- | ----------------- |\n| default | ✅ ES2022 |     | ✅      | ✅   | ❌        | 1.54KB min+brotli |\n| modern  | ✅ ES2022 |     | ✅      | ✅   | ❌        | 1.38KB min+brotli |\n| legacy  | ✅ ES5    | ✅  | ✅      | ✅   | ❌        | 2.73KB min+brotli |\n\nIf you are:\n\n## starting from scratch\n\n```ts\nimport { Traverse } from 'neotraverse/modern';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\n\nnew Traverse(obj).forEach((ctx, x) =\u003e {\n  if (x \u003c 0) ctx.update(x + 128); // `this` is same as `ctx` when using regular function\n});\n```\n\n## migrating from `traverse`\n\n### and you don't care about old browsers or Node versions:\n\nUse default build for no breaking changes, and a modern build for better developer experience.\n\n```ts\nimport traverse from 'neotraverse';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\n\ntraverse(obj).forEach(function (x) {\n  if (x \u003c 0) this.update(x + 128);\n});\n```\n\n### and you care about old browsers or Node versions:\n\nUse legacy build for compatibility with old browsers and Node versions.\n\n```js\nconst traverse = require('neotraverse/legacy');\n```\n\nESM:\n\n```js\nimport traverse from 'neotraverse/legacy';\n```\n\n# examples\n\n## transform negative numbers in-place\n\nnegative.js\n\n```js\nimport { Traverse } from 'neotraverse/modern';\nconst obj = [5, 6, -3, [7, 8, -2, 1], { f: 10, g: -13 }];\n\nnew Traverse(obj).forEach(function (ctx, x) {\n  if (x \u003c 0) ctx.update(x + 128);\n});\n\nconsole.dir(obj);\n```\n\nor in legacy mode:\n\n```js\nimport traverse from 'neotraverse';\n// OR import traverse from 'neotraverse/legacy';\n\nconst obj = [5, 6, -3, [7, 8, -2, 1], { f: 10, g: -13 }];\n\ntraverse(obj).forEach(function (x) {\n  if (x \u003c 0) this.update(x + 128);\n});\n\n// This is identical to the above\ntraverse.forEach(obj, function (x) {\n  if (x \u003c 0) this.update(x + 128);\n});\n\nconsole.dir(obj);\n```\n\nOutput:\n\n    [ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ]\n\n## collect leaf nodes\n\nleaves.js\n\n```js\nimport { Traverse } from 'neotraverse/modern';\n\nconst obj = {\n  a: [1, 2, 3],\n  b: 4,\n  c: [5, 6],\n  d: { e: [7, 8], f: 9 }\n};\n\nconst leaves = new Traverse(obj).reduce((ctx, acc, x) =\u003e {\n  if (ctx.isLeaf) acc.push(x);\n  return acc;\n}, []);\n\nconsole.dir(leaves);\n```\n\nor in legacy mode:\n\n```js\nimport traverse from 'neotraverse';\n// OR import traverse from 'neotraverse/legacy';\n\nconst obj = {\n  a: [1, 2, 3],\n  b: 4,\n  c: [5, 6],\n  d: { e: [7, 8], f: 9 }\n};\n\nconst leaves = traverse(obj).reduce(function (acc, x) {\n  if (this.isLeaf) acc.push(x);\n  return acc;\n}, []);\n\n// Equivalent to the above\nconst leavesLegacy = traverse.reduce(\n  obj,\n  function (acc, x) {\n    if (this.isLeaf) acc.push(x);\n    return acc;\n  },\n  []\n);\n\nconsole.dir(leaves);\nconsole.dir(leavesLegacy);\n```\n\nOutput:\n\n    [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]\n\n## scrub circular references\n\nscrub.js:\n\n```js\nimport { Traverse } from 'neotraverse/modern';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\nobj.c.push(obj);\n\nconst scrubbed = new Traverse(obj).map(function (ctx, x) {\n  if (ctx.circular) ctx.remove();\n});\n\nconsole.dir(scrubbed);\n```\n\nor in legacy mode:\n\n```js\nimport traverse from 'neotraverse';\n// OR import traverse from 'neotraverse/legacy';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\nobj.c.push(obj);\n\nconst scrubbed = traverse(obj).map(function (x) {\n  if (this.circular) this.remove();\n});\n\n// Equivalent to the above\nconst scrubbedLegacy = traverse.map(obj, function (x) {\n  if (this.circular) this.remove();\n});\n\nconsole.dir(scrubbed);\nconsole.dir(scrubbedLegacy);\n```\n\noutput:\n\n    { a: 1, b: 2, c: [ 3, 4 ] }\n\n## commonjs\n\nneotraverse/legacy is compatible with commonjs and provides the same API as `traverse`, acting as a drop-in replacement:\n\n```js\nconst traverse = require('neotraverse/legacy');\n```\n\n## esm\n\n```js\nimport { Traverse } from 'neotraverse/modern';\n```\n\n```js\nimport traverse from 'neotraverse';\n```\n\n# Differences from `traverse`\n\n- ESM-first\n- ES2022, Node 18+\n- Types included by default. No need to install `@types/traverse`\n- Works as-is in all major browsers and Deno\n- No polyfills\n- `new Traverse()` class instead of regular old `traverse()`\n- Legacy mode supporting `ES5` and `CJS`\n\nThere is a legacy mode that provides the same API as `traverse`, acting as a drop-in replacement:\n\n```js\nimport traverse from 'neotraverse';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\n\ntraverse(obj).forEach(function (x) {\n  if (x \u003c 0) this.update(x + 128);\n});\n```\n\nIf you want to support really old browsers or NodeJS, supporting ES5, there's `neotraverse/legacy` which is compatible with ES5 and provides the same API as `traverse`, acting as a drop-in replacement for older browsers:\n\n```js\nimport traverse from 'neotraverse/legacy';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\n\ntraverse(obj).forEach(function (x) {\n  if (x \u003c 0) this.update(x + 128);\n});\n```\n\n# Migrating from `traverse`\n\n### Step 1: Install `neotraverse`\n\n```sh\nnpm install neotraverse\nnpm uninstall traverse @types/traverse # Remove the old dependencies\n```\n\n### Step 2: Replace `traverse` with `neotraverse`\n\n```diff\n-import traverse from 'traverse';\n+import traverse from 'neotraverse';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\n\n-traverse(obj).forEach(function (x) {\n+traverse(obj).forEach(function (x) {\n  if (x \u003c 0) this.update(x + 128);\n});\n```\n\nOptionally, there's also a legacy mode that provides the same API as `traverse`, acting as a drop-in replacement:\n\n```js\nimport traverse from 'neotraverse/legacy';\n\nconst obj = { a: 1, b: 2, c: [3, 4] };\n\ntraverse(obj).forEach(function (x) {\n  if (x \u003c 0) this.update(x + 128);\n});\n```\n\n### Step 3(Optional): Bundle time aliasing\n\nIf you use Vite, you can aliss `traverse` to `neotravers/legacy` in your `vite.config.js`:\n\n```js\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n  resolve: {\n    alias: {\n      traverse: 'neotraverse' // or 'neotraverse/legacy'\n    }\n  }\n});\n```\n\n# methods\n\nEach method that takes an `fn` uses the context documented below in the context section.\n\n## .map(fn)\n\nExecute `fn` for each node in the object and return a new object with the results of the walk. To update nodes in the result use `ctx.update(value)`(modern) or `this.update(value)`(legacy).\n\n## .forEach(fn)\n\nExecute `fn` for each node in the object but unlike `.map()`, when `ctx.update()`(modern) or `this.update()`(legacy) is called it updates the object in-place.\n\n## .reduce(fn, acc)\n\nFor each node in the object, perform a [left-fold](\u003chttp://en.wikipedia.org/wiki/Fold_(higher-order_function)\u003e) with the return value of `fn(acc, node)`.\n\nIf `acc` isn't specified, `acc` is set to the root object for the first step and the root element is skipped.\n\n## .paths()\n\nReturn an `Array` of every possible non-cyclic path in the object. Paths are `Array`s of string keys.\n\n## .nodes()\n\nReturn an `Array` of every node in the object.\n\n## .clone()\n\nCreate a deep clone of the object.\n\n## .get(path)\n\nGet the element at the array `path`.\n\n## .set(path, value)\n\nSet the element at the array `path` to `value`.\n\n## .has(path)\n\nReturn whether the element at the array `path` exists.\n\n# context\n\nEach method that takes a callback has a context (its `ctx` object, or `this` object in legacy mode) with these attributes:\n\n## this.node\n\nThe present node on the recursive walk\n\n## this.path\n\nAn array of string keys from the root to the present node\n\n## this.parent\n\nThe context of the node's parent. This is `undefined` for the root node.\n\n## this.key\n\nThe name of the key of the present node in its parent. This is `undefined` for the root node.\n\n## this.isRoot, this.notRoot\n\nWhether the present node is the root node\n\n## this.isLeaf, this.notLeaf\n\nWhether or not the present node is a leaf node (has no children)\n\n## this.level\n\nDepth of the node within the traversal\n\n## this.circular\n\nIf the node equals one of its parents, the `circular` attribute is set to the context of that parent and the traversal progresses no deeper.\n\n## this.update(value, stopHere=false)\n\nSet a new value for the present node.\n\nAll the elements in `value` will be recursively traversed unless `stopHere` is true.\n\n## this.remove(stopHere=false)\n\nRemove the current element from the output. If the node is in an Array it will be spliced off. Otherwise it will be deleted from its parent.\n\n## this.delete(stopHere=false)\n\nDelete the current element from its parent in the output. Calls `delete` even on Arrays.\n\n## this.before(fn)\n\nCall this function before any of the children are traversed.\n\nYou can assign into `ctx.keys`(modern) or `this.keys`(legacy) here to traverse in a custom order.\n\n## this.after(fn)\n\nCall this function after any of the children are traversed.\n\n## this.pre(fn)\n\nCall this function before each of the children are traversed.\n\n## this.post(fn)\n\nCall this function after each of the children are traversed.\n\n# license\n\nMIT\n","funding_links":[],"categories":["TypeScript","Utilities"],"sub_categories":["Data Structures"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPuruVJ%2Fneotraverse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FPuruVJ%2Fneotraverse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPuruVJ%2Fneotraverse/lists"}