Changing all let variable declarations to const with codemods in a large javascript codebase

461 views Asked by At

The convention I want to use in the codebase is:

const a = 1;
const b = 2;

However, there are many areas in the code that are written like this:

let a = 1,
b = 2;

I want to write a codemod, probably using JSCodeshift that can change the second style of variable declaration to the first. I have been doing some research on ASTs and have been using AST explorer. However, I am having trouble accessing the variable declarator "kind" in the abstract syntax tree.

An example of something I've tried is this:

module.exports = function(file, api) {
  const j = api.jscodeshift;
  const root = j(file.source);

  // Step 1: Find all instances of the code to change
  const instances = root.find(VariableDeclarator.value.kind = 'let');

  // Step 2: Apply a code transformation and replace the code
  instances.forEach(instance => {
    j(path).replaceWith(VariableDeclarator.value.kind = 'const');
  });
  return root.toSource();
}
}

Any help or direction would be appreciated! Thank you!

2

There are 2 answers

1
coderaiser On

You can use Putout code transformer, I’m working on, with @putout/plugin-split-variable-declarations this way:

import putout from 'putout';

const {code} = putout('let a = 1, b = 2;', {
    plugins: [
        'split-variable-declarations',
        ['let-to-const', {
            report: () => 'convert let to const',
            replace: () => ({
                'let __a = __b': 'const __a = __b',
            }),
        }]
    ]
});
console.log(code);
// output
const a = 1;
const b = 2;

Here is example from Putout Editor: convert let to const

0
Chris Akers On

It looks like the main problem you're experiencing is using the correct syntax when using the find method. For example, changing your

const instances = root.find(VariableDeclarator.value.kind = 'let');

to

const instances = root.find(j.VariableDeclaration, {kind: 'let'});

You must use the type definition from api.jscodeshift.

A complete example that does what you ask looks like:

export default function transformer(file, api) {
  const j = api.jscodeshift;
  const root = j(file.source);
  
  const letDeclarations = root.find(j.VariableDeclaration, {kind: 'let'});

  letDeclarations.replaceWith(({value: {declarations}}) => {
    return declarations.map(dec => 
      j.variableDeclaration(dec.init ? 'const' : 'let', [dec])
    );
  });
  
  return root.toSource();
}

This will make the following transformation from:

let a = 1,
    b = 2,
    c;
const d = 3;
let e = 4;
var f = 4;
c = 3;

to:

const a = 1;
const b = 2;
let c;
const d = 3;
const e = 4;
var f = 4;
c = 3;

This codemod will only transform let declarations that include an initializer since that's required for const declarations.

See the full example on AST Explorer to play around with it.