Thursday, June 22, 2017

Refactor Code in Your Lunch Break: Getting Started with Codemods

Maintaining a codebase can be a frustrating experience for any developer, especially a JavaScript codebase. With ever-changing standards, syntax, and third party package breaking changes, it can be hard to keep up.

In recent years, the JavaScript landscape has changed beyond recognition. Advancements in the core JavaScript language has meant that even the simplest simple task of variable declaration has been changed. ES6 introduced let and const, arrow functions, and many more core changes, each bringing improvements and benefits to developers and their applications.

Pressure on developers to produce and maintain code that will stand up to the test of time is on the increase. This article will show you how you can automate large-scale refactoring tasks with the use of codemods and the JSCodeshift tool, allowing you to easily update your code to take advantage of newer language features, for example.

Codemod

Codemod is a tool developed by Facebook to help with the refactor of large-scale codebases. It enables the developer to refactor a large codebase in a small amount of time. In some cases, a developer might use an IDE to perform the refactor of a class or variable name, however, this is usually scoped to one file at a time. The next tool in a developer's refactoring tool kit is a global find and replace. This can work in many cases with the use of complex regular expressions. Many scenarios are not suited to this method; for example, when there are multiple implementations that need to be changed.

Codemod is a Python tool that takes a number of parameters including the expression you wish to match and the replacement.

codemod -m -d /code/myAwesomeSite/pages --extensions php,html \
    '<font *color="?(.*?)"?>(.*?)</font>' \
    '<span style="color: \1;">\2</span>'

In the above example, we are replacing the usage of the <font> tag with a span and inlining the color style. The first two parameters are flags to indicate multiple line matching (-m) and the directory to start processing from (-d /code/myAwesomeSite/pages). We can also restrict the extensions that are processed (--extensions php,html). We then supply the match expression and the replacement. If the replacement is not provided we will be prompted for one at runtime. The tool works, but it is very similar to existing regular expression matching tools.

JSCodeshift

JSCodeshift is the next step up in the refactor toolkit. Also developed by Facebook, its a tool for running codemods across multiple files. As a Node module, JSCodeshift provides a clean and easy-to-use API, and uses Recast under the hood. Recast is an AST-to-AST (Abstract Syntax Tree) transformation tool.

Recast

Recast is a Node module that exposes an interface for parsing and reprinting JavaScript code. It can parse code in string format and generates an object from this which follows an AST structure. This allows us to inspect the code for patterns such as a function declarations.

var recast = require("recast");

var code = [
    "function add(a, b) {",
    "  return a + b",
    "}"
].join("\n");

var ast = recast.parse(code);
console.log(ast);
//output
{
    "program": {
        "type": "Program",
        "body": [
            {
                "type": "FunctionDeclaration",
                "id": {
                    "type": "Identifier",
                    "name": "add",
                    "loc": {
                        "start": {
                            "line": 1,
                            "column": 9
                        },
                        "end": {
                            "line": 1,
                            "column": 12
                        },
                        "lines": {},
                        "indent": 0
                    }
                },
        ...........    

As we can see from the above example, we pass in the code string for a function that adds two numbers. When we parse and log the object we can see the AST. We see the FunctionDeclaration and the name of the function etc. As this is just a JavaScript object we can modify it as we see fit. Then we can trigger the print function to return the updated code string.

AST (Abstract Syntax Tree)

As mentioned before, Recast builds an AST from our code string. An AST is a tree representation of the abstract syntax of source code. Each node of the tree represents a construct in the source code and the node provides important information about the construct. ASTExplorer is a browser-based tool that can help to parse and understand the tree of your code.

Using ASTExplorer we can view the AST of a simple code example. Starting with our code, we will declare a const called foo and this will equal the string of 'bar'.

const foo = 'bar';

This results in the below AST:

Screenshot of the resulting AST

We can see the VariableDeclaration under the body array, which contains our const. All VariableDeclarations have an id attribute that contains our important information such as name etc. If we were building a codemod to rename all instances of foo we can use this name attribute and iterate over all the instances to change the name.

Continue reading %Refactor Code in Your Lunch Break: Getting Started with Codemods%


by Chris Laughlin via SitePoint

No comments:

Post a Comment