Install @codemod/cli
globally:
$ npm install -g @codemod/cli
# or
$ yarn global add @codemod/cli
This package works out of the box with most code bases, because it comes bundled with @babel/preset-env
and @babel/preset-typescript
. If you need other presets or plugins for parsing your source code you can use a custom Babel config as well. Note that the codemod will not apply the transformations from these presets and plugins - they are only used for parsing. Therefor you keep your TypeScript types in your source code for example. Formatting will be kept the same as much as possible.
If you want to apply a transformation you set the plugin which should be used for this transformation explicitely.
Let's write a custom Babel plugin which will prepend readonly
to all params in a constructor. (E.g. sonarlint wants you to do that, if that is your flavour. 🙂) Note that this will be just a quick example and very likely not feature complete.
This will transform this code:
class SomeComponent {
constructor(private growl: Growl) {}
}
To this code:
class SomeComponent {
constructor(private readonly growl: Growl) {}
}
This can be done with this custom Babel plugin:
module.exports = function transform() {
return {
visitor: {
ClassMethod({ node }) {
const isConstructor = node.kind === 'constructor';
if (!isConstructor) return;
node.params.forEach((param) => {
if (param.type !== 'TSParameterProperty') return;
param.readonly = true;
});
}
}
};
};
It looks for ClassMethod
's with a kind
of 'constructor'
and then loops over all params with a type
of 'TSParameterProperty'
to set readonly
to true
.
That's it. Now we can run this transformation for all files in a directory like src/
:
$ codemod --plugin ./your-custom-plugin.js src/
If you want to debug your AST please check out AST explorer
. Paste an example on the left and choose "JavaScript" as the language, "babylon7" as the parser and make sure to uncheck "flow" and check "typescript" in the "babylon7 Settings".
You also use jscodeshift
, if you want to:
$ npm install -g jscodeshift
# or
$ yarn global add jscodeshift
The transform file is very similar:
module.exports = function transform(file, { jscodeshift }) {
return jscodeshift(file.source)
.find(jscodeshift.ClassMethod)
.replaceWith(({ node }) => {
const isConstructor = node.kind === 'constructor';
if (!isConstructor) return node;
node.params.forEach((param) => {
if (param.type !== 'TSParameterProperty') return;
param.readonly = true;
});
return node;
})
.toSource();
};
module.exports.parser = 'ts';
module.exports.extensions = 'ts';
And you run it like this:
$ jscodeshift --transform=your-custom-transform.js src/**/*.ts
Sadly globbing will not work on old Macs with Bash 3. Either update your Bash or try this (slow) workaround:
$ find ./src -name '*.ts' | xargs -I F jscodeshift "F" --transform=your-custom-transform.js