javascriptabstract-syntax-treejscodeshift

codemod from ForInStatement to ForStatement


I have a codemod where I want to transform

for (var key in foo){}

into

for (var keys = 0; key < foo; key++){}

I managed this far:

return j(file.source)
    .find(j.ForInStatement)
    .replaceWith(p => {

        var prop = p.node.left.declarations[0].id;

        var v = [j.variableDeclarator(prop, null)];
        var varDec = j.variableDeclaration('var', v);
        var binary = j.binaryExpression('<', prop, j.identifier('foo'));
        var uE = j.updateExpression('++', prop, false);
        var block = j.blockStatement([]);

        var forIn = j.forStatement(varDec, binary, uE, block);
        return forIn;
    })
    .toSource();

and that brought me this far:

for (var key; key < foo; key++)
  {}

I'm still a bit lost creating things from scratch...

Question:

PS: Felix, if you are reading this, do point me where can I send pull requests to improve the docs! I am happy to help with that while I trying to learn this API. Oh, and Felix, what a nice tool you guys made!


Solution

  • Creating AST nodes directly is indeed verbose. However, jscodeshift exports some helper "methods" that allow you to generate AST nodes from a string and let you interpolate existing AST nodes (tagged templates ftw!).

    Your above example could be simplified to:

      return j(file.source)
        .find(j.ForInStatement)
        .replaceWith(p => {
          var prop = p.node.left.declarations[0].id;
          return statement`for (var ${prop} = 0; ${prop} < foo.length; ${prop}++) ${p.node.body}`;
        })
        .toSource();
    

    DEMO

    Note though that those template methods (expression, statement, statements) have their limitations (you can only interpolate AST nodes where you can put an identifier).


    If you wanted to create foo.length manually, you would have to create a MemberExpression:

    enter image description here

    j.memberExpression(
      prop,
      j.identifier('length')
    );