Created
March 15, 2016 13:29
-
-
Save ahume/52044352942aff522d28 to your computer and use it in GitHub Desktop.
amd-to-commonjs.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Codemod to convert AMD modules to ES2015 syntax | |
*/ | |
module.exports = function(file, api) { | |
const j = api.jscodeshift; | |
const toSourceOpts = { quote: 'single' }; | |
/** | |
* UNUSED | |
* | |
* return a => module.exports a | |
*/ | |
const returnToExports = (path) => { | |
return j(path).replaceWith( | |
j.expressionStatement( | |
j.assignmentExpression( | |
"=", | |
j.memberExpression( | |
j.identifier("module"), | |
j.identifier("exports") | |
), | |
path.value.argument | |
) | |
) | |
); | |
}; | |
/** | |
* return a => export default a | |
*/ | |
const returnToExportDefault = (path) => { | |
return j(path).replaceWith( | |
j.exportDefaultDeclaration( | |
path.value.argument | |
) | |
); | |
}; | |
/** | |
define(['a', 'flight'], function (a, flight) { | |
var probablyNot = require('thisOne'); | |
return function () { | |
return 1 + 2; | |
}; | |
}); | |
becomes | |
var a = require("a"); | |
var flight = require("flight"); | |
var probablyNot = require('thisOne'); | |
module.exports = function () { | |
return 1 + 2; | |
}; | |
*/ | |
const amdToCommon = path => { | |
var dependencies = path.value.arguments[0].elements; | |
var identifiers = path.value.arguments[1].params; | |
var requires = dependencies.map((arg, i) => { | |
return j.variableDeclaration("var", [ | |
j.variableDeclarator( | |
j.identifier(identifiers[i].name), | |
j.callExpression( | |
j.identifier('require'), | |
[j.literal(arg.value)] | |
) | |
) | |
]); | |
}); | |
return j(path) | |
.replaceWith( | |
j.program( | |
requires.concat(path.value.arguments[1].body.body) | |
) | |
); | |
}; | |
/** | |
define(function (require) { | |
var andDefoNot = require('thisOtherOne'); | |
return function () { | |
return 1 + 2; | |
}; | |
}); | |
becomes | |
var andDefoNot = require('thisOtherOne'); | |
module.exports = function () { | |
return 1 + 2; | |
}; | |
*/ | |
const amdFunctionToCommon = path => { | |
return j(path).replaceWith( | |
j.program( | |
path.value.arguments[0].body.body | |
) | |
); | |
}; | |
/** | |
define({ a: 10 }); | |
becomes | |
module.exports = { a: 10 }; | |
*/ | |
const amdObjectToCommon = path => { | |
return j(path).replaceWith( | |
j.expressionStatement( | |
j.assignmentExpression( | |
"=", | |
j.memberExpression( | |
j.identifier("module"), | |
j.identifier("exports") | |
), | |
path.value.arguments[0] | |
) | |
) | |
); | |
}; | |
/** | |
var x = require('x'); | |
becomes | |
import x from 'x'; | |
*/ | |
const requireToImport = path => { | |
const declaration = path.value.declarations[0]; | |
const requireCall = declaration.init; | |
return j(path).replaceWith( | |
j.importDeclaration([ | |
j.importDefaultSpecifier(declaration.id) | |
], requireCall.arguments[0]) | |
); | |
}; | |
// THE TRANSFORM | |
var root = j(file.source); | |
// Use each of the transformers above | |
const transforms = [ | |
// define([], function () {}) | |
[j.CallExpression, { | |
callee: { name: 'define' }, | |
arguments: [{ type: "ArrayExpression" }, { type: 'FunctionExpression' }] | |
}, amdToCommon, { exportify: true }], | |
// require([], function () {}) | |
[j.CallExpression, { | |
callee: { name: 'require' }, | |
arguments: [{ type: "ArrayExpression" }, { type: 'FunctionExpression' }] | |
}, amdToCommon, { exportify: true }], | |
// define(function () {}) | |
[j.CallExpression, { | |
callee: { name: 'define' }, | |
arguments: [{ type: 'FunctionExpression' }] | |
}, amdFunctionToCommon, { exportify: true }], | |
// define({}) | |
[j.CallExpression, { | |
callee: { name: 'define' }, | |
arguments: [{ type: 'ObjectExpression' }] | |
}, amdObjectToCommon, { exportify: false }] | |
].forEach( | |
([type, query, fn, { exportify }]) => { | |
var matches = root.find(type, query); | |
if (exportify) { | |
matches.find(j.ReturnStatement, {}) | |
.filter(path => { | |
try { | |
return (path.parent.parent.parent.parent.parent.name === 'program'); | |
} catch (e) { | |
return false; | |
} | |
}) | |
.forEach(returnToExportDefault); | |
} | |
matches.forEach(fn); | |
} | |
); | |
// Convert it back to source and reparse to find new changes | |
root = j(root.toSource(toSourceOpts)); | |
// Switch to ES6 imports | |
root.find(j.VariableDeclaration, { | |
kind: 'var', | |
declarations: [{ | |
type: 'VariableDeclarator', | |
init: { | |
type: 'CallExpression', | |
callee: { name: 'require' } | |
} | |
}] | |
}).forEach(requireToImport); | |
// Remove empty statements | |
root.find(j.EmptyStatement).forEach(p => j(p).remove()); | |
// Done! | |
return root.toSource(toSourceOpts); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment