Tree Transformations
Recast

Recast

Recast (opens in a new tab) is a library for parsing and modifying JavaScript code written on top of esprima and ast-types (opens in a new tab). Recast provides methods for

  1. Parsing the AST with different parsers (opens in a new tab)
  2. pretty printing ASTs
  3. API to construct new ASTs without parsing any source code
  4. tree transformation
  5. automatic source map generator (opens in a new tab)

Usage

Recast exposes two essential interfaces, one for parsing JavaScript code (require("recast").parse) and the other for reprinting modified syntax trees (require("recast").print).

See the example in /ULL-ESIT-PL/hello-jscodeshift/hello-recast.js (opens in a new tab)

This code example (opens in a new tab) takes as input the code

  function add(a, b) {
    return a - b;
  }

Here is the code:

const recast = require("recast");
const code = `
  function add(a, b) {
    return a - b;
  }
`;
const ast = recast.parse(code);
const add = ast.program.body[0]; // The node of the add function declaration

recast.types.namedTypes

The next statement asserts the the add node has type FunctionDeclaration:

const n = recast.types.namedTypes;
n.FunctionDeclaration.assert(add); // we can also: n.FunctionDeclaration.check(add)

recast.builders

If you choose to use recast.builders to construct new AST nodes, all builder arguments will be dynamically type-checked against the Mozilla Parser API.

const B = recast.types.builders;

Let's say we wanto to convert the function add declaration in a function expression. To do that, we may want to build an auxiliary AST like this one:

ast.program.body[0] = B.variableDeclaration("const", [
  B.variableDeclarator(add.id, B.functionExpression(
    null, // Anonymize the function expression.
    add.params,
    add.body
  ))
]);

How to write a builder

See section How to write a builder for an explanation of how the API works.

Switching the parameters

Let us continue with our transformation and just for fun let us switch the two parameters:

add.params.push(add.params.shift());
 
const output = recast.print(ast).code;
 
console.log(output);

The execution produces:

$ node hello-recast.js 
input code:
 
  function add(a, b) {
    return a * b;
  }
 
output code:
 
  const add = function(b, a) {
    return a * b;
  };

Incoming Sections

Continue now reading the sections

References