javascriptjscodeshift

How to properly export a const in Jscodeshift


I am writing my first codemod using Jscodeshift. My current goal is to export a const that is assigned a certain identifier.

So that, if I target every variable named stuff, it will be named-exported after the script runs.

IN:

const stuff = 4;

OUT:

export const stuff = 4;

This is a stripped down version of what I have. It sort of works but it looks very brittle and has a number of drawbacks.

const constName = "stuff";

module.exports = (fileInfo, api) => {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  const declaration = root.find(j.VariableDeclaration, {
    declarations: [
      {
        id: {
          type: "Identifier",
          name: constName
        }
      }
    ]
  });

  declaration.forEach(n => {
    n.insertBefore("export");
  });

  return root.toSource();
};

AST

This will result in (notice the unwanted new line)

export
const stuff = 4;

This also crucially fails if this source is fed to the script.

IN:

// hey
const stuff = 4;

OUT:

export
// hey
const stuff = 4;

I am quite convinced that n.insertBefore("export"); is really the culprit here, and I'd like to build the named export myself using jscodeshift builders but really can't get it work.

Any suggestions here?


Solution

  • .insertBefore is not the right method to use. This is for inserting a whole new node before another node.

    How want to replace a VariableDeclaration with an ExportNamedDeclaration. If you look at the AST for export const stuff = 4; you can see that it has a property declaration whose value is a VariableDeclaration node. That makes the transformation easy for us: Find the VariableDeclaration, create a new ExportNamedDeclaration, set it's declaration property to the found node and replace the found node with the new node.

    To find out how to build the node we can look at ast-type's ast definitions.

    const constName = "stuff";
    
    module.exports = (fileInfo, api) => {
      const j = api.jscodeshift;
    
      return j(fileInfo.source)
        .find(j.VariableDeclaration, {
          declarations: [
            {
              id: {
                type: "Identifier",
                name: constName
              }
            }
          ]
        })
        .replaceWith(p => j.exportDeclaration(false, p.node))
        .toSource();
    };
    

    astexplorer