<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Deja Coded]]></title><description><![CDATA[Deja Coded]]></description><link>https://blog.royston.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 05 Jun 2026 20:30:26 GMT</lastBuildDate><atom:link href="https://blog.royston.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[CodeMod Tutorial - Part 3 - Paths, Moving Nodes]]></title><description><![CDATA[In part 2 we walked through the process
of developing a codemod, and built one from scratch.  This time we're
going to build a more complex one, introduce the concept of Paths, node
factory functions, and look at why we should "use it in a sentence"....]]></description><link>https://blog.royston.dev/codemod-tutorial-part-3-paths-moving-nodes</link><guid isPermaLink="true">https://blog.royston.dev/codemod-tutorial-part-3-paths-moving-nodes</guid><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Royston Shufflebotham]]></dc:creator><pubDate>Sat, 31 Aug 2019 21:00:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1596058486691/xUThYGB79.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target='_blank' rel='noopener noreferrer'  href="https://blog.royston.dev/codemod-tutorial-part-2-how-ckd7v8uyi00ovses15sv4hkmc">part 2</a> we walked through the process
of developing a codemod, and built one from scratch.  This time we&#39;re
going to build a more complex one, introduce the concept of <em>Path</em>s, node
factory functions, and look at why we should &quot;use it in a sentence&quot;.</p>
<h1 id="our-codemod">Our Codemod</h1>
<p>This time, we&#39;re going to write a codemod that rewrites array construction.</p>
<p>Let&#39;s say you&#39;ve just inherited a large old JS codebase that contains
lots of code like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> myArray = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>);
<span class="hljs-keyword">var</span> myOtherArray = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-string">'x'</span>, <span class="hljs-string">'y'</span>, <span class="hljs-string">'z'</span>);
</code></pre>
<p>Note the explicit array constructor: <code>new Array()</code>.  That&#39;s generally a pretty unnecessary thing
to be doing (in those forms), and can actually be very confusing.  We&#39;re going to write a codemod that changes those array construction calls into plain array syntax</p>
<p>i.e. to convert the above into:</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> myArray = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
<span class="hljs-keyword">var</span> myOtherArray = [<span class="hljs-string">'x'</span>, <span class="hljs-string">'y'</span>, <span class="hljs-string">'z'</span>];
</code></pre>
<p><em>Aside</em></p>
<p><strong>Array construction</strong></p>
<p>A brief interlude for a quick pop quiz...</p>
<p>Do you know what <code>new Array(5, &#39;b&#39;)</code> does?
You probably know, or can guess, that it creates a two-element array containing <code>5</code> and <code>b</code>.  It&#39;s more simply written as <code>[5, &#39;b&#39;]</code>.</p>
<p>What about <code>new Array(5)</code>?  Some readers may be surprised to hear that it <em>doesn&#39;t</em> create a one-element array; it creates a <em>five</em>-element array, initialized all as empty. Its equivalent is <code>[,,,,,]</code>.</p>
<p>There are many JS developers who don&#39;t know about that one-argument special case, and it&#39;s easy to
fall into it accidentally even if you do know (e.g. by removing the <code>b</code> element from the first example).</p>
<p>So, it&#39;s usually better to use the array syntax <code>[5, 6]</code> instead of explicit Array construction (<code>new Array(5, 6)</code>).  The codemod
we develop on this page will perform that change automatically across an entire codebase.</p>
<h1 id="building-the-codemod">Building the codemod</h1>
<p>We&#39;ll work through <a target='_blank' rel='noopener noreferrer'  href="../codemod-tutorial-pt2/#writing-a-codemod">the development steps we listed last time</a>.</p>
<h2 id="1-figure-out-what-we-want-to-change">1. Figure out what we want to change</h2>
<p>We want to convert any calls that look like <code>new Array(&#39;a&#39;, &#39;b&#39;)</code> with or
without parameters, into plain array literal syntax <code>[&#39;a&#39;, &#39;b&#39;]</code>.
The array literal will contain the arguments from the <code>new Array</code> call
as its contents, even if they&#39;re complex expressions.</p>
<h2 id="2-figure-out-what-we-don-t-want-to-change">2. Figure out what we don&#39;t want to change</h2>
<ul>
<li>It&#39;s important that we don&#39;t modify any one-parameter <code>new Array(5)</code>
calls.  <code>new Array(5)</code> is absolutely not replaceable by <code>[5]</code>.</li>
<li>We don&#39;t want to modify the construction of any other type, only <code>Array</code></li>
</ul>
<p><em>Aside</em></p>
<p>Actually, things are a little more nuanced than this.
The special case Array construction behaviour only happens when the
single parameter to <code>new Array()</code> is a number.</p>
<p>If we know that a single parameter isn&#39;t a number (e.g. <code>new Array(&#39;a string&#39;)</code>),
or if we had access to type information from, for instance, a TypeScript parser,
we <em>could</em> be smarter, but we&#39;ll leave those as &#39;extra credit&#39; exercises.</p>
<h2 id="3-construct-a-testbed">3. Construct a testbed</h2>
<p>We&#39;ll develop the testbed shortly, but first, let&#39;s look at what happens if
our testbed is a little... lacking.</p>
<p>It&#39;s very easy, when first writing codemods, to rush into targeting a
very precise piece of syntax.  In our case, we want to target</p>
<pre><code class="lang-js"><span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-string">'a'</span>)<span class="hljs-string">`</span>
</code></pre>
<p>so let&#39;s look at the AST for exactly that.  It&#39;s:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1596055678072/UDVg1kjVM.png" alt="ASTDiagram2.png"></p>
<p>It would be easy to conclude, from that code and diagram that we
should be looking for an <code>ExpressionStatement</code> containing a <code>NewExpression</code> with the appropriate <code>callee</code>.</p>
<p>Let&#39;s look at a very similar piece of code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> x = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>);
</code></pre>
<p>The AST for that is</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1596055619729/xhHJiZKUu.png" alt="ASTDiagram1.png"></p>
<p>Notice that there&#39;s no <code>ExpressionStatement</code> in that tree.</p>
<p>It turns out that the original <code>ExpressionStatement</code> was only there
because the <code>new Array</code> code was at the root of our source file. It&#39;s
actually only the <code>NewExpression</code> that we&#39;re interested in. The <code>ExpressionStatement</code>
was a red herring.</p>
<p>How do we avoid making this mistake?</p>
<h3 id="-use-it-in-a-sentence-">&quot;Use it in a sentence&quot;</h3>
<p>Imagine you&#39;re in an elementary spelling bee competition and you&#39;re asked
to spell the word &#39;pair&#39;. Of course, in such a competition you only
<em>hear</em> the word spoken, so you&#39;re not sure whether they said &#39;pair&#39; or &#39;pear&#39; (or  <a target='_blank' rel='noopener noreferrer'  href="https://www.homophone.com/h/pair-pare-pear-pere">&#39;pare&#39; or &#39;pere&#39;</a> )?</p>
<p>You can&#39;t ask them to spell the word to clarify, so what do you do? You ask the
caller to <em>use the word in a sentence</em>.
If they respond with &quot;I&#39;m going to <em>peer</em> through the window.&quot;, you know you
completely mis-heard originally, but you now know what the word is.</p>
<p>Similarly, if we&#39;re trying to figure out the AST structure for
a piece of code, we can easily make bad assumptions if we only
look at one example with no context.</p>
<p>So, when collecting interesting cases to match, try to &#39;use them in a
sentence&#39; to get a more realistic AST.  In fact, use them in
<em>multiple</em> different ways that are close to how your actual code uses them.</p>
<p>We&#39;ll create a set of sample code that illustrates our positive and negative
cases and doesn&#39;t simply leave the <code>new Array</code> at the top of the source
file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Empty array case</span>
<span class="hljs-comment">// -&gt; const x = [];</span>
<span class="hljs-keyword">const</span> x = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>();

<span class="hljs-comment">// Complex arguments case</span>
<span class="hljs-comment">// -&gt; somefunc(['a', 3 + 4]);</span>
somefunc(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-string">'a'</span>, <span class="hljs-number">3</span> + <span class="hljs-number">4</span>));

<span class="hljs-comment">// Single argument case; leave it alone</span>
somefunc(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-number">5</span>));

<span class="hljs-comment">// Not Array case; leave it alone</span>
somefunc(<span class="hljs-keyword">new</span> NotArray(<span class="hljs-string">'a'</span>, <span class="hljs-number">5</span>));
</code></pre>
<p>That includes things we <em>want</em> to change and similar things
we <em>don&#39;t</em> want to change. Good!</p>
<h2 id="4-build-a-target-profile">4. Build a target profile</h2>
<p>Follow <a target='_blank' rel='noopener noreferrer'  href="https://astexplorer.net/#/gist/505a176a4ab8900fdefd5b103bd306d7/d4c3a7bdc55076f2c2a6f2226334a16fee27e79f">this link to AST Explorer</a> and it&#39;ll take you directly to a pre-configured AST Explorer with that code in it. </p>
<p>The configuration is simply to do &#39;JavaScript parsing&#39; and to use &#39;recast&#39; with the &#39;esprima&#39; parser, and be set up for JSCodeShift transformations.</p>
<p>It also includes the same skeleton codemod from last time ready for us to develop, namely:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
  <span class="hljs-keyword">const</span> j = api.jscodeshift;

  <span class="hljs-keyword">return</span> j(file.source)
    .find(j.Identifier)
    .forEach(path =&gt; path.node.name = <span class="hljs-string">'Foo'</span>)
    .toSource();
}
</code></pre>
<h3 id="explore-the-ast">Explore the AST</h3>
<p>Click around the code and get a feel for the syntax tree.</p>
<p>You&#39;ll discover that we&#39;re looking for <code>NewExpression</code> nodes where
the <code>callee</code> is an <code>Identifier</code> with a <code>name</code> of <code>Array</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1596055883590/29269rg4o.png" alt="NewExpression.png"></p>
<h2 id="5-fire-tracer-bullets">5. Fire tracer bullets</h2>
<p>Let&#39;s update our <code>.find()</code> call to target those nodes.</p>
<p>We&#39;ll also
need to tweak our <code>.forEach()</code>: we were previously targeting <code>Identifier</code>s
and could simply set their <code>name</code> property. We&#39;re now targeting
<code>NewExpression</code>s and they don&#39;t have <code>name</code> property; it&#39;s their <code>callee</code>
that does.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
  <span class="hljs-keyword">const</span> j = api.jscodeshift;
  <span class="hljs-keyword">return</span> j(file.source)
    .find(j.NewExpression, { callee: { type: <span class="hljs-string">'Identifier'</span>, name: <span class="hljs-string">'Array'</span> } })
    .forEach(path =&gt; path.node.callee.name = <span class="hljs-string">'Foo'</span>)
    .toSource();
}
</code></pre>
<p>That&#39;s an excellent start. We&#39;re targeting all the <code>new Array</code> calls:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Empty array case</span>
<span class="hljs-comment">// -&gt; const x = [];</span>
<span class="hljs-keyword">const</span> x = Foo;

<span class="hljs-comment">// Complex arguments case</span>
<span class="hljs-comment">// -&gt; somefunc(['a', 3 + 4]);</span>
somefunc(Foo);

<span class="hljs-comment">// Single argument case; leave it alone</span>
somefunc(Foo);

<span class="hljs-comment">// Not Array case; leave it alone</span>
somefunc(<span class="hljs-keyword">new</span> NotArray(<span class="hljs-string">'a'</span>, <span class="hljs-number">5</span>));
</code></pre>
<p>We&#39;re still incorrectly targeting the one-argument <code>new Array(5)</code> call. Can we extend the <code>find</code> call to locate that?  Sadly not.
If we wanted to look for a <code>NewExpression</code> that had an <code>arguments</code> property with a <code>length</code> <em>equal to</em> 1, we could do it, but we want only those with a length <em>not equal to</em> 1.</p>
<p>It&#39;s time to introduce another JSCodeShift function that gives us more power: <code>.filter()</code>. But to make sense of that, we firstly need to introduce the concept of <em>Paths</em>.</p>
<h2 id="paths">Paths</h2>
<p>Let&#39;s take a look at a simple bit of code and its syntax tree:</p>
<pre><code class="lang-js"><span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-number">3</span>, <span class="hljs-string">'q'</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1596055935898/ymrwvf-H_.png" alt="NodesTree.png"></p>
<p>The <code>NewExpression</code> AST node has a <code>callee</code> property which points at an <code>Identifier</code> (with a <code>name</code> of <code>Array</code>). It also has an <code>arguments</code> property, but this doesn&#39;t point at an AST node object; it points at an <em>array</em> object which contains some nodes.</p>
<p>When processing ASTs we often discover candidate target nodes and want to
&#39;walk around the tree a bit&#39; to confirm that we&#39;ve found the correct pattern.
But navigating around trees like this can be very difficult:</p>
<ul>
<li>There are no &#39;parent&#39; pointers from a node to its parent.</li>
<li>For nodes such as the <code>NumericLiteral</code> and the <code>StringLiteral</code>, what <em>is</em> their parent anyway? Is it the <code>NewExpression</code> or the containing array?</li>
<li>Even if there was a &#39;parent&#39; pointer, we often want to know which property we would come through from parent to child (e.g. left or right side of a <code>+</code> operator?), and a simple &#39;parent&#39; pointer wouldn&#39;t provide that.</li>
<li>It&#39;s tricky to inspect all of a node&#39;s child nodes (e.g. to recurse through a tree): you&#39;d have to examine all the properties and deal with any arrays you came across.  Examining the properties safely (without tripping over internal implementation properties, potentially triggering infinite loops) requires knowledge of the properties available on each node type.</li>
</ul>
<p>This is where <em>Paths</em> come in. <code>Path</code> objects are a feature of the <a target='_blank' rel='noopener noreferrer'  href="https://github.com/benjamn/recast"><code>recast</code></a> library, which JSCodeShift uses.</p>
<p>Recast builds a tree of <em>Path</em> objects parallel to, and pointing at, the tree of AST nodes:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1596055969093/A9ggC8Sp_.png" alt="PathsToNodes.png"></p>
<p>Amongst the properties on a Path object are:</p>
<ul>
<li><code>value</code>: the corresponding entry in the syntax tree. Note that Path objects don&#39;t always point at syntax _Node_s. Path objects are also created for intermediate containers in the syntax tree, such as arrays, in which case the <code>value</code> property would point at the array.</li>
<li><code>parentPath</code> (not shown on the above diagram, for simplicity): this
enables us to walk <em>up</em> the tree</li>
<li><code>name</code>: the &#39;property&#39; (or numeric index for array Path objects) that was traversed when moving from parent Path object to child Path object</li>
<li><code>node</code>: the containing syntax tree node. That is, if a Path object&#39;s <code>value</code> property is pointing to an array for the <code>arguments</code> property of a syntax tree node, the <code>node</code> property will point at the syntax tree node itself</li>
</ul>
<h2 id="filtering">Filtering</h2>
<p>When we work with a chain of commands in JSCodeShift (e.g. <code>.find(j.Identifier).forEach(...)</code>), the chain
is actually operating on a collection of Path objects, not simply nodes.</p>
<p>In our particular case, we have found all the <code>NewExpression</code>
syntax tree nodes, and are manipulating the Path objects that
point at them.  We want to keep only those Path objects whose
<code>NewExpression</code>s have an argument count <code>!== 1</code>.</p>
<p>So we add a new <code>.filter</code> call as follows:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
  <span class="hljs-keyword">const</span> j = api.jscodeshift;

  <span class="hljs-keyword">return</span> j(file.source)
    .find(j.NewExpression, { callee: { type: <span class="hljs-string">'Identifier'</span>, name: <span class="hljs-string">'Array'</span> } })
    .filter(path =&gt; path.node.arguments.length !== <span class="hljs-number">1</span>)
    .forEach(path =&gt; path.node.callee.name = <span class="hljs-string">'Foo'</span>)
    .toSource();
}
</code></pre>
<p>The result is</p>
<pre><code class="lang-js"><span class="hljs-comment">// Convert this to</span>
<span class="hljs-comment">// const x = [];</span>
<span class="hljs-keyword">const</span> x = Foo;

<span class="hljs-comment">// Leave this alone</span>
somefunc(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-number">5</span>));

<span class="hljs-comment">// Convert this to</span>
<span class="hljs-comment">// somefunc(['a', 5]);</span>
somefunc(Foo);

<span class="hljs-comment">// Leave this alone</span>
somefunc(<span class="hljs-keyword">new</span> NotArray(<span class="hljs-string">'a'</span>, <span class="hljs-number">5</span>));
</code></pre>
<p>The difference this time is that, as we&#39;d hoped, the single-argument <code>new Array(5)</code> call is now left alone.</p>
<p>That&#39;s taken care of all the &#39;negative&#39; cases that we don&#39;t want to change. We now need to replace the array with something more useful than merely &#39;Foo&#39;.</p>
<h2 id="6-apply-changes">6. Apply changes</h2>
<p>Last time our codemod didn&#39;t <em>do</em> very much: it just changed the
name of an existing <code>Identifier</code>.  This time, we want to replace,
completely, a <code>NewExpression</code> with an array.</p>
<p>Let&#39;s start by replacing them with an empty array, to keep things simple.</p>
<p>What does an array look like - what AST structure do we need to create?
We can repeat the same exercise
we went through to find the structure to <em>target</em> (remembering to &quot;use it
in a sentence&quot;).</p>
<p>We need to create an <code>ArrayExpression</code> node like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1596056002392/jiuMledbR.png" alt="ArrayExpression.png"></p>
<h3 id="-replacewith-"><code>.replaceWith()</code></h3>
<p>Thankfully, JSCodeShift provides a neat little function called <code>.replaceWith()</code>.
It can be called on a collection of Paths, and it uses the navigation
smarts that we outlined earlier to replace the Paths&#39; nodes with ones
we specify.</p>
<p>We&#39;re going to start by using <code>.replaceWith()</code> to replace all our target
nodes with empty arrays.  But how do we actually create those nodes?</p>
<p>JSCodeShift provides us with a whole suite of AST node factory functions.</p>
<h3 id="ast-node-factories">AST Node Factories</h3>
<p>By now, we&#39;re used to writing <code>j.Identifier</code> or <code>j.NewExpression</code> to indicate a type
of node to <em>search</em> for.  JSCodeShift provides a parallel set of functions,
starting with lowercase letters, which <em>construct</em> nodes.</p>
<p>e.g. <code>j.identifier(&#39;Foo&#39;)</code> will create an <code>Identifier</code> with the <code>name</code> property set to <code>&#39;Foo&#39;</code>.</p>
<p>In our case, we need to create an <code>ArrayExpression</code>, so we&#39;ll need to use <code>j.arrayExpression()</code>. Let&#39;s try it:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
  <span class="hljs-keyword">const</span> j = api.jscodeshift;

  <span class="hljs-keyword">return</span> j(file.source)
    .find(j.NewExpression, { callee: { type: <span class="hljs-string">'Identifier'</span>, name: <span class="hljs-string">'Array'</span> } })
    .filter(path =&gt; path.node.arguments.length !== <span class="hljs-number">1</span>)
    .replaceWith(path =&gt; j.arrayExpression())
    .toSource();
}
</code></pre>
<p>Our output?</p>
<pre><code class="lang-text">no value or default function given for field &quot;elements&quot; of ArrayExpression(&quot;elements&quot;: [Expression | SpreadElement | RestElement | null])
</code></pre>
<p>Uh oh. That&#39;s not good. We&#39;ve obviously missed out a value for <code>elements</code>, whatever that is.  We can get a clue by looking at
the typing information in the <code>j.arrayExpression()</code> constructor:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1596056037027/1bymioizJ.png" alt="arrayExpressionCompletion.png"></p>
<p>It&#39;s expecting to be given an array of AST nodes for the array elements, but we didn&#39;t provide that.</p>
<p>Let&#39;s try again, providing an empty array of AST nodes:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
  <span class="hljs-keyword">const</span> j = api.jscodeshift;

  <span class="hljs-keyword">return</span> j(file.source)
    .find(j.NewExpression, { callee: { type: <span class="hljs-string">'Identifier'</span>, name: <span class="hljs-string">'Array'</span> } })
    .filter(path =&gt; path.node.arguments.length !== <span class="hljs-number">1</span>)
    .replaceWith(j.arrayExpression([]))
    .toSource();
}
</code></pre>
<p>The result is</p>
<pre><code class="lang-js"><span class="hljs-comment">// Empty array case</span>
<span class="hljs-comment">// -&gt; const x = [];</span>
<span class="hljs-keyword">const</span> x = [];

<span class="hljs-comment">// Complex arguments case</span>
<span class="hljs-comment">// -&gt; somefunc(['a', 3 + 4]);</span>
somefunc([]);

<span class="hljs-comment">// Single argument case; leave it alone</span>
somefunc(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-number">5</span>));

<span class="hljs-comment">// Not Array case; leave it alone</span>
somefunc(<span class="hljs-keyword">new</span> NotArray(<span class="hljs-string">'a'</span>, <span class="hljs-number">5</span>));
</code></pre>
<p>That&#39;s looking good. 3 of the 4 cases are now correct.  We just need to fix the case where there are
actually array parameters.</p>
<p><code>.replaceWith(j.arrayExpression([]))</code> replaces <em>all</em> of the found nodes with the same expression.
We want to examine each Path being replaced, and create an appropriate <code>ArrayExpression</code> for each
Path.</p>
<p>The good news is that <code>.replaceWith</code> also accepts a transformation function which is expected to transform
a Path object into a replacement expression.  We want to transform a <code>NewExpression</code> of a series
of array parameters into a literal array containing those same parameters.</p>
<h3 id="moving-nodes-around">Moving nodes around</h3>
<p>Of course, we don&#39;t want to replace <code>new Array(1, 2)</code> with <code>[]</code>.
We want to replace it with <code>[1, 2]</code>.  How do we construct
all the correct array parameters? They could be anything, not
just simple numbers.</p>
<p>Well, the good news is that we <em>already have them</em>.  The
<code>arguments</code> property to the <code>NewExpression</code> already contains
exactly the nodes that we need.</p>
<p>Our transformation ends up being surprisingly simple: instead of creating a new <code>ArrayExpression</code>
with an empty array of AST nodes, we reuse the AST nodes that were the <code>arguments</code> to the <code>NewExpression</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
  <span class="hljs-keyword">const</span> j = api.jscodeshift;

  <span class="hljs-keyword">return</span> j(file.source)
    .find(j.NewExpression, { callee: { type: <span class="hljs-string">'Identifier'</span>, name: <span class="hljs-string">'Array'</span> } })
    .filter(path =&gt; path.node.arguments.length !== <span class="hljs-number">1</span>)
    .replaceWith(path =&gt; j.arrayExpression(path.node.arguments))
    .toSource();
}
</code></pre>
<p>The result is our final correct desired output:</p>
<pre><code><span class="hljs-comment">// Empty array case</span>
<span class="hljs-comment">// -&gt; const x = [];</span>
<span class="hljs-keyword">const</span> x = [];

<span class="hljs-comment">// Complex arguments case</span>
<span class="hljs-comment">// -&gt; somefunc(['a', 3 + 4]);</span>
somefunc([<span class="hljs-string">'a'</span>, <span class="hljs-number">3</span> + <span class="hljs-number">4</span>]);

<span class="hljs-comment">// Single argument case; leave it alone</span>
somefunc(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-number">5</span>));

<span class="hljs-comment">// Not Array case; leave it alone</span>
somefunc(<span class="hljs-keyword">new</span> NotArray(<span class="hljs-string">'a'</span>, <span class="hljs-number">5</span>));
</code></pre><p>Hooray!</p>
<h1 id="summary">Summary</h1>
<p>We developed our understanding of codemods quite a bit this time. You should now have some familiarity with</p>
<ul>
<li>the Path object tree</li>
<li><code>.find()</code>ing and <code>.filter()</code>ing Path collections</li>
<li>creating replacement AST trees using AST node factory functions such as <code>j.identifier()</code></li>
</ul>
<p>We also discussed why it&#39;s a good idea to &#39;use it in a sentence&#39; when trying to understand an AST tree.</p>
<p>Next time, we&#39;ll talk about <code>Scope</code>s.</p>
]]></content:encoded></item><item><title><![CDATA[CodeMod Tutorial - Part 2 - How?]]></title><description><![CDATA[In part 1 of this series we did a lot of talking about what Codemods are and why you should (or shouldn't) write them. This time we're going to get our hands dirty and write one.
There's quite a bit to get through in this part: we're going to examine...]]></description><link>https://blog.royston.dev/codemod-tutorial-part-2-how</link><guid isPermaLink="true">https://blog.royston.dev/codemod-tutorial-part-2-how</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Royston Shufflebotham]]></dc:creator><pubDate>Mon, 26 Aug 2019 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1596051771990/8GcXmkh4W.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target='_blank' rel='noopener noreferrer'  href="./create-a-codemod-part-1-why-ckd7v6qw500opses112kz6sl4">part 1 of this series</a> we did a lot of talking about what Codemods are and why you should (or shouldn&#39;t) write them. This time we&#39;re going to get our hands dirty and write one.</p>
<p>There&#39;s quite a bit to get through in this part: we&#39;re going to examine an AST and construct a complete - albeit simple - codemod, little-by-little.</p>
<h1 id="our-codemod">Our Codemod</h1>
<p>To keep this particular article at a reasonable length,
we&#39;ll develop a <em>very</em> simple codemod. It&#39;ll simply rename
a method in all JavaScript classes it sees.  Specifically, it&#39;ll
rename <code>componentWillUpdate</code> to <code>UNSAFE_componentWillUpdate</code>.
(This change forms <a target='_blank' rel='noopener noreferrer'  href="https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles">part of a Codemod for React</a> but you don&#39;t need to know
anything about React to follow this tutorial.)</p>
<h1 id="writing-a-codemod">Writing a Codemod</h1>
<p>I&#39;ve found this set of steps handy when developing codemods. If you don&#39;t do these things you can <em>easily</em> develop a codemod that changes the wrong code or changes the right code in the wrong way.  The TL;DR is &quot;think before you code, and have some tests&quot;.</p>
<ol>
<li>Figure out what we want to change<ul>
<li>Ensure you understand precisely what structures you want the codemod to change, and precisely what you want it to change those things into.</li>
</ul>
</li>
<li>Figure out what we <em>don&#39;t</em> want to change<ul>
<li>Do you <em>always</em> want to make the change, or only when you&#39;re extending a particular class?</li>
<li>Do you <em>always</em> want to make the change, or only when there aren&#39;t any parameters?</li>
<li>Find structures similar to what you think you want to target and decide if you want those to change too</li>
</ul>
</li>
<li>Construct a &#39;test bed&#39;<ul>
<li>Develop one or more &#39;test&#39; files containing examples of positive (things you do want to change) and negative (things you don&#39;t want to change) sample code</li>
</ul>
</li>
<li>Build a target profile<ul>
<li>Identify the AST structures you want to change and construct</li>
</ul>
</li>
<li>Fire tracer bullets<ul>
<li>Write codemod code that targets the structure you&#39;re hoping to reach, and makes a tiny change there. Don&#39;t attempt to make the full final change straight away. Focus initially on targeting.</li>
<li>You may need to evolve your testbed as you do this</li>
</ul>
</li>
<li>Apply changes<ul>
<li>Now that you&#39;re targeting the correct nodes, write codemod code that makes the desired changes</li>
</ul>
</li>
<li>Use the codemod<ul>
<li>And be sure to check the output for unexpected changes!</li>
</ul>
</li>
<li>Evaluate<ul>
<li>Was it easier/quicker/better than making the changes by hand?</li>
</ul>
</li>
</ol>
<p>Let&#39;s work through those steps for our codemod:</p>
<h2 id="1-figure-out-what-we-want-to-change">1. Figure out what we want to change</h2>
<p>That&#39;s easy. We want to find all class methods called <code>componentWillUpdate</code> and rename them
to <code>UNSAFE_componentWillUpdate</code>. We don&#39;t need to change structure; we&#39;re just changing the
name of an identifier.</p>
<h2 id="2-figure-out-what-we-don-t-want-to-change">2. Figure out what we don&#39;t want to change</h2>
<p>Let&#39;s think about things <em>similar</em> to what we want to change, to identify related things we
<em>don&#39;t</em> want to change.</p>
<p>A convenient way to do this is to take the thing we want to target and create variants of
it, changing one thing at a time:</p>
<ul>
<li>Our start point is that we want to change methods called <code>componentWillUpdate</code>.</li>
<li>We don&#39;t want to change methods that <em>aren&#39;t</em> called <code>componentWillUpdate</code>.</li>
<li>We don&#39;t want to change <em>all</em> functions that are called <code>componentWillUpdate</code>; only class methods.</li>
<li>We don&#39;t want to change <code>static</code> class functions that are called <code>componentWillUpdate</code>; only instance methods.</li>
<li>We only want to change the <em>name</em> of the <code>componentWillUpdate</code> method. None of its contents should change.</li>
<li>Do we <em>always</em> want to change methods called <code>componentWillUpdate</code>?  If we were sharing
this codemod publicly and therefore wanted to be super-careful, we should probably only change that method
if it&#39;s on a class extending React&#39;s <code>Component</code> class, but let&#39;s just assume for the sake
of this tutorial that we don&#39;t need to worry about that because we happen to know there are no uses
of <code>componentWillUpdate</code> outside of our React components.</li>
</ul>
<h2 id="3-construct-a-testbed">3. Construct a testbed</h2>
<p>We now write some sample code to illustrate all the above positive and negative matches:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class1</span> </span>{
  <span class="hljs-comment">// This should be renamed</span>
  componentWillUpdate() {
    <span class="hljs-comment">// This code should not be lost</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-comment">// This should not be renamed</span>
  notComponentWillUpdate() {
  }
}

<span class="hljs-comment">// This should not be renamed</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">componentWillUpdate</span>(<span class="hljs-params"></span>) </span>{
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class2</span> </span>{
  <span class="hljs-comment">// This should not be renamed</span>
  static componentWillUpdate() {
  }
}
</code></pre>
<p>As we develop our codemod we&#39;ll run it against the above code to gain confidence that it&#39;s changing the correct pieces of code.</p>
<h2 id="4-build-a-target-profile">4. Build a target profile</h2>
<p>We need to identify the AST pattern we want to target.  A particularly easy
way to do this is to use the excellent <a target='_blank' rel='noopener noreferrer'  href="https://astexplorer.net/">AST Explorer</a>
web site to explore the AST.</p>
<p>JSCodeShift uses the <code>recast</code> library, so make sure that&#39;s the selected option.</p>
<p>Here is a link to AST Explorer with
the above code already entered, and the
correct options selected: <a target='_blank' rel='noopener noreferrer'  href="https://astexplorer.net/#/gist/4ba8eac986f73baf39c5c9683af03b93/10917053e0e1b9b41cc3dd9b806f0cec69d9ac00">https://astexplorer.net/#/gist/4ba8eac986f73baf39c5c9683af03b93/10917053e0e1b9b41cc3dd9b806f0cec69d9ac00</a></p>
<p>Go take a look at that. I&#39;ll wait.  Make sure you click around the sample
code (on the left) and examine the appropriate parts of the syntax tree (on the right).  Look to see what sorts of AST nodes we&#39;re going to be interested in.</p>
<p>After a little clicking around we can see that we&#39;re interested in <code>MethodDefinition</code> nodes whose <code>key</code> is an <code>Identifier</code> with a <code>name</code> of <code>componentWillUpdate</code>.</p>
<p><em>Aside</em></p>
<p>One area of difficulty when working with these ASTs is that different parsers produce slightly different AST structures. The <code>esprima</code> parser generates <code>MethodDefinition</code> nodes whereas the <code>babylon7</code> parser calls them <code>ClassMethod</code>s.</p>
<p>For this reason, it&#39;s vital to ensure you&#39;re using the exact same parser configuration in AST Explorer that you&#39;ll be using when running your codemod.</p>
<p>Note in particular that <code>recast</code> is merely a layer on top of a real parser, so when we use <code>recast</code> in AST Explorer, it&#39;s important to choose the appropriate parser from the recast settings.</p>
<h2 id="5-fire-tracer-bullets">5. Fire tracer bullets</h2>
<p>This is where things get interesting.</p>
<p>At this point, we&#39;re actually going to create and run a real codemod.  The neat thing is that we can do it <em>directly inside AST Explorer</em>.</p>
<p>Click the &#39;Transform&#39; button in AST Explorer and select &#39;JSCodeShift&#39;.  The two-window display will change into a four-window display.  The two new panes at the bottom are the codemod source (bottom-left) and the resulting output (bottom-right).</p>
<p>AST Explorer gives us a sample codemod that reverses all of the identifiers in the source.
That sample is a little more complex than we need, so replace the codemod code with this simpler skeleton:</p>
<pre><code class="lang-js"><span class="hljs-number">1.</span> <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
<span class="hljs-number">2.</span>   <span class="hljs-keyword">const</span> j = api.jscodeshift;
<span class="hljs-number">3.</span> 
<span class="hljs-number">4.</span>   <span class="hljs-keyword">return</span> j(file.source)
<span class="hljs-number">5.</span>     .find(j.Identifier)
<span class="hljs-number">6.</span>     .forEach(path =&gt; path.node.name = <span class="hljs-string">'Foo'</span>)
<span class="hljs-number">7.</span>     .toSource();
<span class="hljs-number">8.</span> }
</code></pre>
<p>It&#39;s only 8 lines long but it is a complete codemod! Let&#39;s walk through it, line-by-line:</p>
<ul>
<li>1: The codemod consists of a single exported function called with the current file and the JSCodeShift API.</li>
<li>2: We extract the <code>jscodeshift</code> helper from the API and call it <code>j</code> as we will be using it a lot.</li>
<li>4: Here we begin a multi-line chained operation passing objects from one function to another: we start by parsing the current file. JSCodeShift will return a wrapper around the file that allows us to perform sophisticated search and replace operations.</li>
<li>5: We search for anything of type <code>Identifier</code>. Note the uppercase <code>I</code> in <code>j.Identifier</code>: this references the <em>type</em> Identifier.</li>
<li>6: We tweak each (Identifier) node, changing its <code>name</code> to <code>Foo</code>.  We&#39;ll cover the <code>path</code> business in detail in the next article, so for now, just focus on the fact that we can write <code>path.node</code> to get the node in question.</li>
<li>7: We regenerate the output code from our modified tree by calling <code>.toSource()</code>.</li>
</ul>
<p>If you look at the output pane you&#39;ll see that our code has been heavily transformed to:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Foo</span> </span>{
  <span class="hljs-comment">// This should be renamed</span>
  Foo() {
    <span class="hljs-comment">// This code should not be lost</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-comment">// This should not be renamed</span>
  Foo() {
  }
}

<span class="hljs-comment">// This should not be renamed</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Foo</span>(<span class="hljs-params"></span>) </span>{
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Foo</span> </span>{
  <span class="hljs-comment">// This should not be renamed</span>
  static Foo() {
  }
}
</code></pre>
<p>Every <code>Identifier</code> - every mention of <code>x</code>, <code>somefunc</code>, <code>Array</code> and <code>NotArray</code> - has been changed into <code>Foo</code>, as we said it should in the code.</p>
<p>That&#39;s not (of course) what we want from our final codemod, but it serves to show how powerful codemods are, and it
provides a good basis for developing our codemod.
It&#39;ll require remarkably few changes to turn that into our final codemod!</p>
<h2 id="target-the-correct-thing">Target the correct thing</h2>
<p>Firstly, we don&#39;t want to target <em>all</em> <code>Identifier</code>s.
From our AST investigations earlier, we know we want to start by finding all the <code>MethodDefinition</code>s.
Our tracer bullet will simply be to set the name of the method to <code>Foo</code>.  That&#39;s achieved by setting the <code>.name</code> property of
the method&#39;s <code>.key</code> property.</p>
<p>i.e.</p>
<pre><code><span class="hljs-number">1.</span> <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
<span class="hljs-number">2.</span>   <span class="hljs-keyword">const</span> j = api.jscodeshift;
<span class="hljs-number">3.</span> 
<span class="hljs-number">4.</span>   <span class="hljs-keyword">return</span> j(file.source)
<span class="hljs-number">5.</span>     .find(j.MethodDefinition)
<span class="hljs-number">6.</span>     .forEach(<span class="hljs-function"><span class="hljs-params">path</span> =&gt;</span> path.node.key.name = <span class="hljs-string">'Foo'</span>)
<span class="hljs-number">7.</span>     .toSource();
<span class="hljs-number">8.</span> }
</code></pre><p>The output:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class1</span> </span>{
  <span class="hljs-comment">// This should be renamed</span>
  Foo() {
    <span class="hljs-comment">// This code should not be lost</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-comment">// This should not be renamed</span>
  Foo() {
  }
}

<span class="hljs-comment">// This should not be renamed</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">componentWillUpdate</span>(<span class="hljs-params"></span>) </span>{
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class2</span> </span>{
  <span class="hljs-comment">// This should not be renamed</span>
  static Foo() {
  }
}
</code></pre>
<p>We&#39;re now renaming all methods on classes, and we&#39;re not renaming the classes or functions.</p>
<p>But we&#39;re still hitting methods we don&#39;t want: we&#39;re renaming methods whose name is not <code>componentWillUpdate</code> and we&#39;re renaming <code>static</code> methods.</p>
<p>Happily, the <code>.find()</code> method takes a second parameter that supports property-based filtering.</p>
<p>We <em>only</em> want to target:</p>
<ul>
<li>methods named <code>componentWillUpdate</code>. We&#39;re looking for methods whose <code>key</code> property is an object with a <code>name</code> property equal to <code>componentWillUpdate</code>.</li>
<li>methods which are not <code>static</code>. We&#39;re looking for methods whose <code>static</code> property is false.</li>
</ul>
<p>We adjust our <code>.find()</code> call as follows:</p>
<pre><code class="lang-js"> <span class="hljs-number">1.</span> <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
 <span class="hljs-number">2.</span>   <span class="hljs-keyword">const</span> j = api.jscodeshift;
 <span class="hljs-number">3.</span> 
 <span class="hljs-number">4.</span>   <span class="hljs-keyword">return</span> j(file.source)
 <span class="hljs-number">5.</span>     .find(j.MethodDefinition, {
 <span class="hljs-number">6.</span>       key: { name: <span class="hljs-string">'componentWillUpdate'</span> },
 <span class="hljs-number">7.</span>       static: <span class="hljs-literal">false</span>
 <span class="hljs-number">8.</span>     })
 <span class="hljs-number">9.</span>     .forEach(path =&gt; (path.node.key.name = <span class="hljs-string">"Foo"</span>))
<span class="hljs-number">10.</span>    .toSource();
<span class="hljs-number">11.</span> }
</code></pre>
<p>which produces the transformed output:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class1</span> </span>{
  <span class="hljs-comment">// This should be renamed</span>
  Foo() {
    <span class="hljs-comment">// This code should not be lost</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-comment">// This should not be renamed</span>
  notComponentWillUpdate() {
  }
}

<span class="hljs-comment">// This should not be renamed</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">componentWillUpdate</span>(<span class="hljs-params"></span>) </span>{
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class2</span> </span>{
  <span class="hljs-comment">// This should not be renamed</span>
  static componentWillUpdate() {
  }
}
</code></pre>
<p>That&#39;s perfect. We&#39;re now targeting exactly the one method we want
to, and leaving everything else alone.</p>
<h1 id="6-apply-changes">6. Apply changes</h1>
<p>For this codemod, this bit&#39;s easy. We don&#39;t need any information
from the existing node. We just want to set its name to <code>UNSAFE_componentWillUpdate</code>, which merely requires us to change
from setting <code>Foo</code> to <code>UNSAFE_componentWillUpdate</code>:</p>
<pre><code class="lang-js"> <span class="hljs-number">1.</span> <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
 <span class="hljs-number">2.</span>  <span class="hljs-keyword">const</span> j = api.jscodeshift;
 <span class="hljs-number">3.</span>
 <span class="hljs-number">4.</span>  <span class="hljs-keyword">return</span> j(file.source)
 <span class="hljs-number">5.</span>    .find(j.MethodDefinition, {
 <span class="hljs-number">6.</span>      key: { name: <span class="hljs-string">'componentWillUpdate'</span> },
 <span class="hljs-number">7.</span>      static: <span class="hljs-literal">false</span>
 <span class="hljs-number">8.</span>    })
 <span class="hljs-number">9.</span>    .forEach(path =&gt; (path.node.key.name = <span class="hljs-string">"UNSAFE_componentWillUpdate"</span>))
<span class="hljs-number">10.</span>    .toSource();
<span class="hljs-number">11.</span> }
</code></pre>
<p>So the output is now:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class1</span> </span>{
  <span class="hljs-comment">// This should be renamed</span>
  UNSAFE_componentWillUpdate() {
    <span class="hljs-comment">// This code should not be lost</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-comment">// This should not be renamed</span>
  notComponentWillUpdate() {
  }
}

<span class="hljs-comment">// This should not be renamed</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">componentWillUpdate</span>(<span class="hljs-params"></span>) </span>{
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class2</span> </span>{
  <span class="hljs-comment">// This should not be renamed</span>
  static componentWillUpdate() {
  }
}
</code></pre>
<p>That looks perfect!</p>
<h1 id="7-use-the-codemod">7. Use the codemod</h1>
<p>At this point, we&#39;d run it across our codebase and carefully check
the output. We&#39;ve done some due diligence so we&#39;re hoping it&#39;ll work
well, but there are often more edge cases that we&#39;ve missed.</p>
<h1 id="8-evaluate">8. Evaluate</h1>
<p>This isn&#39;t a fabulous codemod. It only replaces one method name,
and it doesn&#39;t rename existing <em>references</em> to that method.
And to be honest, with what it <em>actually</em> does, we could probably have
achieved the same thing with a regular-expression-based search
and replace.</p>
<p>If this was a codemod we&#39;d be sharing with others, it would be
worth turning our sample code into a repeatable unit test, but we won&#39;t
cover that here.</p>
<p>Of course, our main objective here was to illustrate how to create
a simple codemod, and it hopefully served that purpose.  Let me know?</p>
<h1 id="summary">Summary</h1>
<p>We&#39;ve covered how to use JSCodeShift to create a simple codemod
from scratch, but, as importantly, we&#39;ve covered a
process for developing more reliable codemods.</p>
<p>Have fun developing some codemods, and let me know how you get on!</p>
<p>In <a target='_blank' rel='noopener noreferrer'  href="codemod-tutorial-part-3-paths-moving-nodes-ckd7ussl800metbs10pkzf7yh">part 3</a>, we&#39;ll cover a more advanced codemod which involves moving
existing code around.</p>
]]></content:encoded></item><item><title><![CDATA[Create a codemod - Part 1 - Why?]]></title><description><![CDATA[So, you've got a nice big codebase full of code you're reasonably happy with.
Great!
But, of course, it's rotting.
If you've been coding with JavaScript (or most other languages, to be honest)
for any amount of time, you'll have noticed that habits a...]]></description><link>https://blog.royston.dev/create-a-codemod-part-1-why</link><guid isPermaLink="true">https://blog.royston.dev/create-a-codemod-part-1-why</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[codemod]]></category><dc:creator><![CDATA[Royston Shufflebotham]]></dc:creator><pubDate>Thu, 27 Jun 2019 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1596051017045/kKLUeGg78.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So, you've got a nice big codebase full of code you're <em>reasonably</em> happy with.</p>
<p>Great!</p>
<p>But, of course, it's <em>rotting</em>.</p>
<p>If you've been coding with JavaScript (or most other languages, to be honest)
for any amount of time, you'll have noticed that habits and best practices
<em>change</em> over time. What was a cool way to do something six months ago isn't
quite so cool any more:</p>
<ul>
<li>maybe there's some new syntax in JS or a new Babel plugin</li>
<li>maybe there's a new library that makes it easier</li>
<li>maybe one of your libraries or frameworks has introduced a cool new feature</li>
</ul>
<p>What's stopping you adopting those new features or practices? Most likely it's your existing codebase.</p>
<p>Maybe you want to turn on a new eslint rule across your codebase, but you currently
have a lot of violations. You could mark them all as excluded so that you can
apply it to new code, but that old code simply isn't going to fix itself.</p>
<p>You <em>want</em> to improve your codebase, but as you get more and more of it, it feels harder and harder to change it.  So it starts to fall behind.  That's the <em>rot</em>.</p>
<h1 id="heading-codemod-it">Codemod it!</h1>
<p>A codemod (<em>code</em> <em>mod</em>-ification) is a tool that automatically applies a set of changes to an entire codebase.  So that little thing you'd like to add to your codebase that would make <em>everything better</em> suddenly becomes something you could actually consider.</p>
<h2 id="heading-should-i-write-a-codemod-or-just-make-the-changes-by-hand">Should I write a codemod or just make the changes by hand?</h2>
<p>This is a great question, and the excellent XKCD comic has addressed this 
<a target="_blank" href="https://xkcd.com/1205/">on</a>...
<a target="_blank" href="https://xkcd.com/1319/">several</a>...
<a target="_blank" href="https://xkcd.com/1445/">occasions</a>.</p>
<p>It's a simple equation:</p>
<ol>
<li><p>How long will it take you to develop the codemod? (Let's call that time <code>D</code>.)</p>
<p>Note that, like any code, getting something simple and basic working won't take very long, but making it cope with all the edge cases once it hits the reality of your codebase might increase this time.</p>
</li>
<li><p>How long will it take to make the changes manually, without the codemod? (Let's call that time <code>M</code>.)</p>
<p>Note that if this is an error-prone modification to make, the cost will be higher.</p>
</li>
</ol>
<p>Is <code>M &gt; D</code>? If so, the codemod will be worthwhile. If not, it's probably not going to be worth your time.</p>
<p>Writing a codemod might feel cool, but if you're only going to run it once, you either need to be able to write codemods quickly or your codebase had better be large enough - or you need to share your codemod with enough other people - to make it worthwhile.</p>
<p>So, as you can tell, the more efficiently you can write a codemod, the more value you'll get out of writing codemods.</p>
<p>That's <em>why</em> I've written this series of articles.</p>
<h1 id="heading-summary">Summary</h1>
<p>We've reviewed what a codemod is, why you might (or might not) want to write one. In <a target="_blank" href="codemod-tutorial-part-2-how-ckd7v8uyi00ovses15sv4hkmc">part 2</a>, we'll start to look into what they involve. Until next time!</p>
]]></content:encoded></item></channel></rss>