codemods
code modifications with jscodeshift
Hi!
- Björn Brauer (ZauberNerd)
- Hamburg
- Freelance -> FTE at Costa Digital
What are codemods?
- code modifications
- large-scale refactoring / code changes
- usually automated; can also be manual
- automates codemods with a toolkit
- finds files based on extensions/patterns
- asks for confirmation on diff
codemod -m -d src/ --extensions php,html \
'<font *color="?(.*?)"?>(.*?)</font>' \
'<span style="color: \1;">\2</span>'
Why?
Adopt new language features
function() {}() => {}Object.assign({}, a, b){...a, ...b}React.createClass({
render: function() {...}
})class X extends Component {
render() {...}
}Automatically change language constructs throughout the whole code base
Adopt new APIs / migrate breaking changes
var X = React.createClass({
mixins: [ReactGraphQL.Mixin],
statics: {
queries: {
viewer: () => query`
viewer { name }
`,
},
},
render() {...},
})
module.exports = Xvar X = React.createClass({
render() {...}
})
module.exports = Relay.createContainer(X, {
queries: {
viewer: () => query`
viewer { name }
`,
},
})Consistent coding style / patterns
- Patterns in existing code are copied
- Part of code base smells -> general health of code base declines
Manual codemods
- changes get lost / are undocumented
- parts that need to be updated are missed
- work in master continues -> merge conflicts
Title Text

var X = React.createClass({
mixins: [ReactGraphQL.Mixin],
statics: {
queries: {
viewer: () => query`
viewer { name }
`,
},
},
render() {...},
})
module.exports = Xvar X = React.createClass({
render() {...}
})
module.exports = Relay.createContainer(X, {
queries: {
viewer: () => query`
viewer { name }
`,
},
})Or switching from ava, tape or mocha to Jest
or upgrading from React.createClass to ES6 classes
(there's codemods for both cases)

Enter jscodeshift
Abstract Syntax Tree (AST)
- jscodeshift leverages the AST
- similiar to the DOM (or any other tree)
- API with "Collection"s (similar to Arrays, jQuery, Backbone)
Built on recast
- a tool to make transformations on an AST
- pluggable parser (esprima, babel, flow)
- pretty print the output
- infer existing coding style / indentation
- print only code that has actually changed
- configurable options
parse -> find -> create -> update -> print
- parse, print -> recast
- find, create, update -> jscodeshift, ast-types
find
// simple example
j(file.source)
.find(j.Identifier)
.filter(path => /* check something */)
// structural pattern matching example
j(file.source)
.find(j.CallExpression, {
callee: {
object: {
name: 'console',
},
property: {
name: 'log',
},
},
})create
// create AST nodes by hand
var identifier = {
type: 'Identifier',
name: 'hello',
};
// or use a tool, for example: ast-types
var identifier = j.identifier('hello');update
// update a property of an AST node:
someAstNodeIdentifier.name = 'Hallo Hamburg!';
// replace an entire AST node / subtree:
j(path).replaceWith(myNewASTNode);
// insert a new AST node:
j(path).insertBefore(myNewASTNode);
j(path).insertAfter(myNewASTNode);Demo
Questions?
Thank you for your attention!
References / learning material
codemods with jscodeshift
By Björn Brauer
codemods with jscodeshift
Automating large scale changes to big portions of your codebase with jscodeshift
- 944