Skip to content

Instantly share code, notes, and snippets.

@ockham
Last active March 27, 2017 21:33
Show Gist options
  • Save ockham/54159b8072ffb57da012b5bea3ac564d to your computer and use it in GitHub Desktop.
Save ockham/54159b8072ffb57da012b5bea3ac564d to your computer and use it in GitHub Desktop.
single-tree-render-codemod.js
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
function addNextMiddleware(path) {
if (path.value.params.length !== 1) {
// More than just a context arg, possibly not a middleware
return path.value;
}
let ret = path.value;
ret.params = [...ret.params, j.identifier('next')];
ret.body = j.blockStatement([
...path.value.body.body,
j.expressionStatement(j.callExpression(j.identifier('next'), []))
]);
return ret;
}
function getTarget(arg) {
if (arg.type === 'Literal') {
return arg.value;
}
if (arg.type === 'CallExpression') { // More checks?
return arg.arguments[0].value;
}
}
function transformRenderWithReduxStore(path) {
if (path.value.params.length !== 2) {
// More than just context and next args, possibly not a middleware
return path.value;
}
return j(path)
.find(j.CallExpression, {
callee: {
name: 'renderWithReduxStore'
}
})
.replaceWith(p => {
const contextArg = path.value.params[0];
const target = getTarget(p.value.arguments[1]);
return j.assignmentExpression(
'=',
j.memberExpression(contextArg, j.identifier(target)),
p.value.arguments[0]
)
})
}
root
.find(j.CallExpression, {
callee: {
name: 'renderWithReduxStore'
}
})
.closestScope()
.replaceWith(addNextMiddleware)
.forEach(transformRenderWithReduxStore)
// Remove `renderWithReduxStore` from `import { a, renderWithReduxStore, b } from 'lib/react-helpers'`
root
.find(j.ImportSpecifier, {
local: {
name: 'renderWithReduxStore'
}
})
.remove();
// Remove empty `import 'lib/react-helpers'`
root
.find(j.ImportDeclaration, {
source: {
value: 'lib/react-helpers'
}
})
.filter(p => !p.value.specifiers.length)
.remove();
// Add makeLayout and clientRender middlewares to route definitions
const routeDefs = root
.find(j.CallExpression, {
callee: {
name: 'page'
}
})
.filter(p => (p.value.arguments.length > 1 && p.value.arguments[0].value !== '*'))
.forEach(p => {
p.value.arguments.push(j.identifier('makeLayout'));
p.value.arguments.push(j.identifier('clientRender'));
})
if (routeDefs.nodes().length) {
// This adds a newline above the import, meh. https://github.com/benjamn/recast/issues/371
root.find(j.ImportDeclaration).at(-1).insertAfter('import { makeLayout, render as clientRender } from \'controller\';');
}
return root
.toSource({
useTabs: true
});
}
@ockham
Copy link
Author

ockham commented Mar 24, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment