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();
};
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?
.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();
};