# 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"'
}
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
- traverse the AST,
- access node fields and
- 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')]
);
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.
- Collections (opens new window) contain nodepaths (opens new window),
- nodepaths (opens new window) contain nodes, and
- nodes are what the AST is made of.
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
});
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) { ... }
}
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();
}
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:
- generic extensions and
- 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);
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);
}
});
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
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.
You can also pass options to recast's parse
method by passing an object to
jscodeshift as second argument:
jscodeshift(source, {...})
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
- crguezl/learning-jscodeshift/transforms/trailing-commas.js (opens new window), forked from js-codemod (opens new window) - Codemod scripts to transform code to next generation JS.
# Example FunctionExpression to an ArrowFunctionExpression
# Other Examples
- react-codemod (opens new window) - React codemod scripts to update React APIs.
- js-transforms (opens new window) - Some documented codemod experiments to help you learn.
# 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