estree-toolkit

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

Usage no npm install needed!

<script type="module">
  import estreeToolkit from 'https://cdn.skypack.dev/estree-toolkit';
</script>

README

estree-toolkit

Tools for working with ESTree AST

npm Circle CI codecov License

Installation

npm i estree-toolkit
# or
yarn add 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 have 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.hasGlobalBinding('require') // => true
  }
});

Utilities

There are several static utilities that you can use.

  • evaluate
    Evaluates the given path. For now it only supports evaluation of logical, binary and unary operations.
    const { utils: u } = 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
      }
    });
    
  • evaluateTruthy
    Evaluates the path for truthiness and returns true, false or undefined depending on evaluation result.
  • hasBinding
    Checks if any binding with the name is available in the containing scope.
    const { utils: u } = require('estree-toolkit');
    const { parseModule } = require('meriyah');
    
    const ast = parseModule(`
      {
        let a;
        {
          id1;
        }
      }
    
      id2;
    `);
    
    traverse(ast, {
      Identifier(path) {
        if (path.node.name === 'id1') {
          u.hasBinding(path, 'a') // => true
        } else if (path.node.name === 'id2') {
          u.hasBinding(path, 'a') // => false
        }
      }
    });
    

Documentation

The documentation is work in progress. You can find the documentation at https://estree-toolkit.netlify.app/

License

Licensed under the MIT License.