Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

oxc-walker

oxc-project1.6mMIT0.5.2TypeScript support: included

[![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.