Skip to content

Instantly share code, notes, and snippets.

@ahume
Created March 15, 2016 13:29
Show Gist options
  • Save ahume/52044352942aff522d28 to your computer and use it in GitHub Desktop.
Save ahume/52044352942aff522d28 to your computer and use it in GitHub Desktop.
amd-to-commonjs.js
/**
* 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