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

Package detail

estree-toolkit

sarsamurmu250.6kMIT1.7.13TypeScript support: included

Traverser, scope tracker, and more tools for working with ESTree AST

estree, ast, traverse, scope, analysis, walker, visitor, binding, fast, acorn, meriyah

readme

estree-toolkit

Tools for working with ESTree AST

npm Circle CI codecov License

Installation

npm i estree-toolkit
# or
yarn add estree-toolkit

Usage

// Supports both CommonJS and ES Modules
// ES Module
import { traverse, builders as b } from 'estree-toolkit';
// CommonJS
const { traverse, builders: b } = require('estree-toolkit');

Basic operations

Traversing an AST

const { traverse } = require('estree-toolkit');

traverse(ast, {
  Program(path) {
    // Do something with the path
  }
});

Building Nodes

const { builders: b } = require('estree-toolkit');

b.identifier('x'); // => { type: 'Identifier', name: 'x' }

Checking node types

const { traverse, is } = require('estree-toolkit');
const { parseModule } = require('meriyah');

const ast = parseModule(`x = 0`);

traverse(ast, {
  AssignmentExpression(path) {
    if (is.identifier(path.node.left, { name: 'x' })) {
      // `left` is an identifier with name `x`
    }
  }
});

Replacing a node

const { traverse, builders: b } = require('estree-toolkit');
const { parseModule } = require('meriyah');

const ast = parseModule('a = b');

traverse(ast, {
  Identifier(path) {
    if (path.node.name === 'a') {
      path.replaceWith(b.identifier('c'));
    }
  }
});

// Now the AST represents - `c = b`

Collecting scope information

const { traverse } = require('estree-toolkit');

traverse(ast, {
  // Enable scope
  $: { scope: true },
  Program(path) {
    // `path.scope` is now available in all paths
  }
});

Checking if a binding is available

const { traverse } = require('estree-toolkit');
const { parseModule } = require('meriyah');

const ast = parseModule(`
import { a } from 'source';

const { b, c: [d, { e }] } = a;
`);

traverse(ast, {
  $: { scope: true },
  Program(path) {
    path.scope.hasBinding('a') // => true
    path.scope.hasBinding('b') // => true
    path.scope.hasBinding('c') // => false
    path.scope.hasBinding('d') // => true
    path.scope.hasBinding('e') // => true
  }
});

Getting all references of a binding

const { traverse } = require('estree-toolkit');
const { parseModule } = require('meriyah');

const ast = parseModule(`
import { a } from 'source';

fn(a);
s = a;
let obj = { a };
`);

traverse(ast, {
  $: { scope: true },
  Program(path) {
    // Returns all the paths that reference the binding `a`
    path.scope.getBinding('a').references // => [NodePath, NodePath, NodePath]
  }
});

Checking if a global has been used

const { traverse } = require('estree-toolkit');
const { parseModule } = require('meriyah');

const ast = parseModule(`
const fx = require('fx-mod');
`);

traverse(ast, {
  $: { scope: true },
  Program(path) {
    path.scope.hasGlobalBinding('require') // => true
  }
});

Renaming a binding

const { traverse } = require('estree-toolkit');
const { parseModule } = require('meriyah');

const ast = parseModule(`
const a = 0

a.reload()
while (a.ok) a.run()
`);

traverse(ast, {
  $: { scope: true },
  Program(path) {
    // `a` -> `b`
    path.scope.renameBinding('a', 'b')
  }
});

// Output code:
// const b = 0
//
// b.reload()
// while (b.ok) b.run()

Utilities

There are several static utilities that you can use.

  • evaluate\ Evaluates the given path.

    const { utils: u, traverse } = require('estree-toolkit');
    // We are using `meriyah` but you can use any parser (like `acorn`)
    const { parseModule } = require('meriyah');
    
    traverse(parseModule(`1 + 2`), {
      BinaryExpression(path) {
        u.evaluate(path) // => { value: 3 }
      }
    });
    
    traverse(parseModule(`1 === 2`), {
      BinaryExpression(path) {
        u.evaluate(path) // => { value: false }
      }
    });
    
    traverse(parseModule(`iDoNotKnowWhatThisIs === 55`), {
      BinaryExpression(path) {
        u.evaluate(path) // => undefined
      }
    });
    
    traverse(parseModule(`
      ({
        text: 'This is an object',
        data: [1, 'two']
      })
    `), {
      ObjectExpression(path) {
        u.evaluate(path) // => { value: { text: 'This is an object', data: [1, 'two'] } }
      }
    });
    
    traverse(parseModule(`1 > 5 ? 'YES' : 'NO'`), {
      ConditionalExpression(path) {
        u.evaluate(path) // => { value: 'NO' }
      }
    });
  • evaluateTruthy\ Evaluates the path for truthiness and returns true, false or undefined depending on evaluation result.

There's more functionalities, please read the documentation.

Documentation

You can find the documentation at https://estree-toolkit.netlify.app/

Why another traverser?

I know there is Babel. But there are other tools which are faster than Babel. For example, meriyah is 3x faster than @babel/parser, astring is up to 50x faster than @babel/generator. But these tool only work with ESTree AST. I wanted to use these faster alternatives for one of my projects but could not find any traverser with batteries-included. So I built one myself, with awesome scope analysis, it has all the things that you would need for traversing an ESTree AST. Also, a little bit faster than Babel.

Need help?

If you need help in any kind of ESTree AST modification, then don't hesitate to open a new discussion in Q&A Discussions. I will try my best to help you :)

License

Licensed under the MIT License.

changelog

1.7.13

  • Fix incorrect renaming in AssignmentPattern inside ObjectPattern

1.7.12

  • Add default type for generic CompT of traverse function

1.7.10

  • Enforce @types/estree version

1.7.9

  • Better type support when sharing a visitor between different node types using the new comp field
  • Fix undefined being marked as a reserved keyword
  • Updated dependencies

1.7.8

  • Add isReference utility function
  • Fix reference collection issue with computed PropertyDefinition

1.7.7

  • More fix to JSX related builders

1.7.6

  • Fix JSX related builder names

1.7.5

  • Remove unused dependencies

1.7.4

  • Add CatchClause to hasBinding
  • Updated dependencies

1.7.3

  • Handle cases where CatchClause parameter is null

1.7.2

  • Fix crash when one of the children was null when calling skipChildren

1.7.1

  • Enabled "moduleResolution": "bundler" support for typescript

1.7.0

  • New method for NodePath - cloneNode, skipChildren, unSkipChildren

1.6.2

  • Updated dependencies
  • Added support for ES Modules

1.6.1

  • More bug fixes

1.6.0

  • New methods for Scope - generateUid, generateUidIdentifier, generateDeclaredUidIdentifier
  • Bug fixes related to scope

1.5.0

1.4.0

  • Updated all dependencies

1.3.1

  • Added ObjectExpression and ArrayExpression evaluators
  • Improved code

1.3.0

  • Added support for JSX

1.2.7

  • Fixed issue in Scope where parent scope were not being assigned

1.2.6

  • Fixed infinite loop in NodePath

1.2.4

  • Improved validators.
  • Fixed issue with Identifier validator (#1)

1.2.3

  • Disable validation in builders temporarily

1.2.2

  • Added node validator to all builders

1.2.0

  • Updated to latest @types/estree
  • Improved compatibility with latest TypeScript

1.1.0

  • More fixes

1.0.9

  • Fix NodePath's synchronization

1.0.8

  • Export more types

1.0.7

  • Add hasBinding method to Scope

1.0.6

  • Improve typings

1.0.5

  • Fix TypeScript types

1.0.4

  • Add support for ExportDeclaration alias.

1.0.3

  • Add support for evaluating UnaryExpression

1.0.2

  • Update @types/estree dependency

1.0.1

  • Remove useless files