# The jscodeshift API

As already mentioned, jscodeshift also provides a wrapper around recast (opens new window). In order to properly use the jscodeshift API, one has to understand the basic building blocks of recast (and ASTs) as well.

# Core Concepts

# AST nodes

An AST node is a plain JavaScript object with a specific set of fields, in accordance with the Mozilla Parser API (opens new window). The primary way to identify nodes is via their type.

For example, string literals are represented via Literal nodes, which have the structure

// "foo"
{
  type: 'Literal',
  value: 'foo',
  raw: '"foo"'
}
1
2
3
4
5
6

It's OK to not know the structure of every AST node type. The (esprima) AST explorer (opens new window) is an online tool to inspect the AST for a given piece of JS code.

# Path objects

Recast itself relies heavily on ast-types (opens new window) which defines methods to

  1. traverse the AST,
  2. access node fields and
  3. build new nodes.

ast-types wraps every AST node into a path object. Paths contain meta-information and helper methods to process AST nodes.

For example, the child-parent relationship between two nodes is not explicitly defined. Given a plain AST node, it is not possible to traverse the tree up. Given a path object however, the parent can be traversed to via path.parent.

For more information about the path object API, please have a look at ast-types (opens new window).

# Builders

To make creating AST nodes a bit simpler and "safer", ast-types defines a couple of builder methods, which are also exposed on jscodeshift.

For example, the following creates an AST equivalent to foo(bar):

// inside a module transform
var j = jscodeshift;
// foo(bar);
var ast = j.callExpression(
  j.identifier('foo'),
  [j.identifier('bar')]
);
1
2
3
4
5
6
7

jscodeshift Lowercase vs Uppercase fields

If you access a jscodeshift field starting with lowercase like `j.callExpression, it will return a build instance.

If you access a jscodeshift field starting with uppercase, it will return a predicate which is used to filter and check nodes.

The signature of each builder function is best learned by having a look at the definition files (opens new window).

# Collections and Traversal

In order to transform the AST, you have to traverse it and find the nodes that need to be changed. jscodeshift is built around the idea of collections (opens new window) of paths and thus provides a different way of processing an AST than recast or ast-types.

  1. Collections (opens new window) contain nodepaths (opens new window),
  2. nodepaths (opens new window) contain nodes, and
  3. nodes are what the AST is made of.

(opens new window)

A collection has methods to process the nodes inside a collection, often resulting in a new collection

This results in a fluent interface, which can make the transform more readable.

Collections (opens new window) are "typed" which means that the type of a collection is the "lowest" type all AST nodes in the collection have in common. That means you cannot call a method for a FunctionExpression collection on an Identifier collection.

Here is an example of how one would find/traverse all Identifier nodes with jscodeshift:

// jscodeshift
jscodeshift(src)
  .find(jscodeshift.Identifier)
  .forEach(function(path) {
    // do something with path
  });
1
2
3
4
5
6

The jscodeshift(src).find method (opens new window) has two parameters type and filter.

The type parameter is a predicateType object:

{
    "name": "Name of the node",
    "kind": "PredicateType",
    "predicate": function(value, deep) { ... }
}
1
2
3
4
5

The filter parameter is optional and is a function or a Node. Not used in the former example. Here is an example of transformation using a filter:

export default (fileInfo, api) => {
    const j = api.jscodeshift;
    const root = j(fileInfo.source);
    const callExpressions = root.find(j.CallExpression, 
        { // filter 
            callee: {
                type: 'MemberExpression',
                object: { type: 'Identifier', name: 'console' },
            },
        }
    );
    callExpressions.remove();
    return root.toSource();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

jscodeshift Lowercase vs Uppercase fields

If you access a jscodeshift field starting with lowercase like `j.callExpression, it will return a build instance.

If you access a jscodeshift field starting with uppercase, it will return a predicate which is used to filter and check nodes.

The call root.find(j.CallExpression returns a collection of nodepaths (opens new window) containing just the nodes that are CallExpressions. Without the second filter option, The find would not just find the console CallExpressions, it would find every CallExpression in the source. To force greater specificity, we provide a second argument to .find: An object of additional parameters, specifying that we want the callee to be a MemberExpression and the object to be an Identifier with name equal to console.

See the full example in the folder remove-calls-to-console of the repo crguezl/hello-jscodeshift (opens new window)

See the code of the class Collection in file Collection.js (opens new window) and the API docs in Class: Collection (opens new window) docs.

See its extensions (opens new window).

# Extensibility

jscodeshift provides an API to extend collections (opens new window). By moving common operators into helper functions (which can be stored separately in other modules), a transform can be made more readable.

There are two types of extensions:

  1. generic extensions and
  2. type-specific extensions.

Generic extensions are applicable to all collections (opens new window). As such, they typically don't access specific node data, but rather traverse the AST from the nodes in the collection.

Type-specific extensions work only on specific node types and are not callable on differently typed collections (opens new window).

# jscodeshift.registerMethods Examples

Adding a method to all Identifiers







 

jscodeshift.registerMethods({
  logNames: function() {
    return this.forEach(function(path) {
      console.log(path.node.name);
    });
  }
}, jscodeshift.Identifier);
1
2
3
4
5
6
7

Inside the logNames function this refers to the current Collection.

Here is another example adding a method to all [collections][]

jscodeshift.registerMethods({
  findIdentifiers: function() {
    return this.find(jscodeshift.Identifier);
  }
});
1
2
3
4
5

Then we can use them this way:

jscodeshift(ast).findIdentifiers().logNames();
jscodeshift(ast).logNames(); // error, unless `ast` only consists of Identifier nodes
1
2

See an example

# Passing options to recast (opens new window)

You may want to change some of the output settings (like setting ' instead of "). This can be done by passing config options to recast (opens new window).

.toSource({quote: 'single'}); // sets strings to use single quotes in transformed code.
1

You can also pass options to recast's parse method by passing an object to jscodeshift as second argument:

jscodeshift(source, {...})
1

More on config options here (opens new window)

# Unit Testing

Véase la sección Unit Testing

# Examples from Write Code to Rewrite Your Code: jscodeshift tutorial

Read the tutorial at Write Code to Rewrite Your Code: jscodeshift Examples: removing console.log, replacing imported method calls, from positional parameters to parameter object

# Remove calls to console

Here you have the code of the example remove calls to console (opens new window).

Exercise

Write a transformation remove-console-logs.js that only removes console.logs but not console.warn and others

# Replacing imported method calls

Here is the code of the example Replacing imported method calls (opens new window)

# From positional parameters to parameter object

Code for the example From positional parameters to parameter object (opens new window)

# Example inserting console.log at the beginning of a function

See the code at the folder crguezl/hello-jscodeshift/prefix-functions (opens new window) in the master branch

# Trailing Commas

# Example FunctionExpression to an ArrowFunctionExpression

# Other Examples

# JsCodeShift Documentation

See jscodeshift wiki: documentation (opens new window) and crguezl/jscodeshift-api-docs (opens new window) deployment

# Recipes

# References

See the section references about AST transformations

Last Updated: 3 months ago