The jscodeshift API
As already mentioned, jscodeshift also provides a wrapper around [recast][]. 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][]. 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"'
}
It's OK to not know the structure of every AST node type. The [(esprima) AST explorer][ast-explorer] is an online tool to inspect the AST for a given piece of JS code.
Path objects
Recast itself relies heavily on [ast-types][] 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][].
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')]
);
::: danger 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 in a new tab).
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][] of paths and thus provides a different way of processing an AST than recast or ast-types.
- [Collections][] contain [nodepaths][],
- [nodepaths][] 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][] 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
});
The jscodeshift(src).find
method (opens in a new tab) has two parameters type
and filter
.
The type
parameter is a predicateType
object:
{
"name": "Name of the node",
"kind": "PredicateType",
"predicate": function(value, deep) { ... }
}
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();
}
::: danger 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][] 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 in a new tab)
See the code of the class Collection
in file Collection.js (opens in a new tab) and the API docs in Class: Collection (opens in a new tab) docs.
See its extensions (opens in a new tab).
Extensibility
jscodeshift provides an API to extend collections (opens in a new tab). 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][]. 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][].
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);
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);
}
});
Then we can use them this way:
jscodeshift(ast).findIdentifiers().logNames();
jscodeshift(ast).logNames(); // error, unless `ast` only consists of Identifier nodes
See an example
Passing options to [recast]
You may want to change some of the output settings (like setting '
instead of "
).
This can be done by passing config options to [recast].
.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 in a new tab)
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 (opens in a new tab) 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 in a new tab).
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 in a new tab)
From positional parameters to parameter object
Code for the example From positional parameters to parameter object (opens in a new tab)
Example inserting console.log at the beginning of a function
See the code at the folder crguezl/hello-jscodeshift/prefix-functions (opens in a new tab) in the master
branch
Trailing Commas
- crguezl/learning-jscodeshift/transforms/trailing-commas.js (opens in a new tab), forked from js-codemod (opens in a new tab) - Codemod scripts to transform code to next generation JS.
Example FunctionExpression to an ArrowFunctionExpression
Other Examples
- react-codemod (opens in a new tab) - React codemod scripts to update React APIs.
- js-transforms (opens in a new tab) - Some documented codemod experiments to help you learn.
JsCodeShift Documentation
See jscodeshift wiki: documentation (opens in a new tab) and crguezl/jscodeshift-api-docs (opens in a new tab) deployment
Recipes
!!!include(includes/jscodeshift-links.md)!!!
References
See the section references about AST transformations