Prototyped http://www.nonblocking.io/2011/12/experimental-support-for-common-js-and.html without depending on closure and it's strict/arcane dependency resolution
Motivated by: https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/
var TEST_MODULE = `
var a = require('foo'),
b = require('bar'),
c = require('baz').baz;
var test2 = 5 + c.a.b;
var test = function(c) {
5 + c.a.b();
};
var test3 = {
a: 1
};
test3[a] = 5;
exports.bar = function() {
return exports.baz + a;
};
module.exports.foo = function() {
return foo + b();
};
`;
var recast = require('recast');
var types = require('ast-types');
var b = types.builders;
var n = types.namedTypes;
var ast = recast.parse(TEST_MODULE);
var moduleName = 'test';
var modulePrefix = '__module-';
var localPrefix = '__local-' + moduleName + '-';
var moduleExports = modulePrefix + moduleName;
var localVariables = {};
// Collect all local variables so we can rescope them
types.visit(ast, {
visitVariableDeclarator: function(path) {
localVariables[path.node.id.name] = true;
return false;
}
});
// Rescope non property identifiers (so we don't have scope collision)
types.visit(ast, {
visitIdentifier: function(path) {
if (!localVariables[path.node.name]) {
this.traverse(path);
return;
}
if (path.name == 'key') {
this.traverse(path);
return;
}
if (path.name == 'property' &&!path.parentPath.value.computed) {
this.traverse(path);
return;
}
// if (path.node.name == 'a') {
// console.log(path.node, path.name);
// }
path.replace(b.identifier(localPrefix + path.node.name));
return false;
}
});
types.visit(ast, {
// Replace require calls with literals
visitCallExpression: function(path) {
if (path.node.callee.name == 'require') {
var name = modulePrefix + path.node.arguments[0].value;
var replacementNode = b.identifier(name);
path.replace(replacementNode);
return false;
}
this.traverse(path);
},
// Replace module.exports and exports.* with literals
visitMemberExpression: function(path) {
if (n.Identifier.check(path.node.object) && path.node.object.name == 'exports') {
var replacementNode = b.memberExpression(
b.identifier(moduleExports),
path.node.property,
false)
path.replace(replacementNode);
return false;
}
if (n.Identifier.check(path.node.object) && path.node.object.name == 'module') {
path.replace(b.identifier(moduleExports));
return false;
}
this.traverse(path);
}
});
// Prepend the module.exports variable declaration
var varStatement = b.variableDeclaration('var', [
b.variableDeclarator(b.identifier(moduleExports), null)
]);
ast.program.body.unshift(varStatement);
var output = recast.print(ast).code;