npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

oxc-walker

v0.6.0

Published

[![npm version][npm-version-src]][npm-version-href] [![npm downloads][npm-downloads-src]][npm-downloads-href] [![Github Actions][github-actions-src]][github-actions-href] [![Codecov][codecov-src]][codecov-href]

Readme

oxc-walker

npm version npm downloads Github Actions Codecov

A strongly-typed ESTree AST walker built on top of oxc-parser.

Usage

Install package:

# npm
npm install oxc-walker

# pnpm
pnpm install oxc-walker

Walk a parsed AST

import { parseSync } from 'oxc-parser'
import { walk } from 'oxc-walker'

const ast = parseSync('example.js', 'const x = 1')

walk(ast.program, {
  enter(node, parent, ctx) {
    // ...
  },
})

Parse and walk directly

import { parseAndWalk } from 'oxc-walker'

parseAndWalk('const x = 1', 'example.js', (node, parent, ctx) => {
  // ...
})

⚙️ API

walk(ast, options)

Walk an AST.

// options
interface WalkOptions {
  /**
   * The function to be called when entering a node.
   */
  enter?: (node: Node, parent: Node | null, ctx: CallbackContext) => void
  /**
   * The function to be called when leaving a node.
   */
  leave?: (node: Node, parent: Node | null, ctx: CallbackContext) => void
  /**
   * The instance of `ScopeTracker` to use for tracking declarations and references.
   */
  scopeTracker?: ScopeTracker
}

interface CallbackContext {
  /**
   * The key of the current node within its parent node object, if applicable.
   */
  key: string | number | symbol | null | undefined
  /**
   * The zero-based index of the current node within its parent's children array, if applicable.
   */
  index: number | null
  /**
   * The full Abstract Syntax Tree (AST) that is being walked, starting from the root node.
   */
  ast: Program | Node
}

this.skip()

When called inside an enter callback, prevents the node's children from being walked. It is not available in leave.

this.replace(newNode)

Replaces the current node with newNode. When called inside enter, the new node's children will be walked. The leave callback will still be called with the original node.

⚠️ When a ScopeTracker is provided, calling this.replace() will not update its declarations.

this.remove()

Removes the current node from its parent. When called inside enter, the removed node's children will not be walked.

This has a higher precedence than this.replace(), so if both are called, the node will be removed.

⚠️ When a ScopeTracker is provided, calling this.remove() will not update its declarations.

parseAndWalk(source, filename, callback, options?)

Parse the source code using oxc-parser, walk the resulting AST and return the ParseResult.

Overloads:

  • parseAndWalk(code, filename, enter)
  • parseAndWalk(code, filename, options)
interface ParseAndWalkOptions {
  /**
   * The function to be called when entering a node.
   */
  enter?: (node: Node, parent: Node | null, ctx: CallbackContext) => void
  /**
   * The function to be called when leaving a node.
   */
  leave?: (node: Node, parent: Node | null, ctx: CallbackContext) => void
  /**
   * The instance of `ScopeTracker` to use for tracking declarations and references.
   */
  scopeTracker?: ScopeTracker
  /**
   * The options for `oxc-parser` to use when parsing the code.
   */
  parseOptions?: ParserOptions
}

ScopeTracker

A utility to track scopes and declarations while walking an AST. It is designed to be used with the walk function from this library.

interface ScopeTrackerOptions {
  /**
   * If true, the scope tracker will preserve exited scopes in memory.
   * @default false
   */
  preserveExitedScopes?: boolean
}

Example usage:

import { parseAndWalk, ScopeTracker } from 'oxc-walker'

const scopeTracker = new ScopeTracker()

parseAndWalk('const x = 1; function foo() { console.log(x) }', 'example.js', {
  scopeTracker,
  enter(node, parent) {
    if (node.type === 'Identifier' && node.name === 'x' && parent?.type === 'CallExpression') {
      const declaration = scopeTracker.getDeclaration(node.name)
      console.log(declaration) // ScopeTrackerVariable
    }
  },
})
import { parseAndWalk, ScopeTracker, walk } from 'oxc-walker'

const code = `
function foo() {
  console.log(a)
}

const a = 1
`

const scopeTracker = new ScopeTracker({
  preserveExitedScopes: true,
})

// pre-pass to collect hoisted declarations
const { program } = parseAndWalk(code, 'example.js', {
  scopeTracker,
})

// freeze the scope tracker to prevent further modifications
// and prepare it for second pass
scopeTracker.freeze()

// main pass to analyze references
walk(program, {
  scopeTracker,
  enter(node) {
    if (node.type === 'CallExpression' && node.callee.type === 'MemberExpression' /* ... */) {
      const declaration = scopeTracker.getDeclaration('a')
      console.log(declaration) // ScopeTrackerVariable; would be `null` without the pre-pass
    }
  }
})

Helpers:

  • scopeTracker.isDeclared(name: string): boolean - check if an identifier is declared in reference to the current scope
  • scopeTracker.getDeclaration(name: string): ScopeTrackerNode | null - get the scope tracker node with metadata for a given identifier name in reference to the current scope
  • scopeTracker.freeze() - freeze the scope tracker to prevent further modifications and prepare for second pass (useful for multi-pass analysis)
  • scopeTracker.getCurrentScope(): string - get the key of the current scope (a unique identifier for the scope, do not rely on its format)
  • scopeTracker.isCurrentScopeUnder(scopeKey: string): boolean - check if the current scope is a child of the given scope key

💻 Development

  • Clone this repository
  • Enable Corepack using corepack enable
  • Install dependencies using pnpm install
  • Run interactive tests using pnpm dev

License

Made with ❤️

Published under MIT License.