babel-plugin-transform-private-to-weakmap
Version:
Transforms class properties prefixed with an underscore to weakmaps.
132 lines (111 loc) • 6.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _default;
function _default(_ref) {
var t = _ref.types;
return {
visitor: {
Program: function Program(programPath) {
var propsToRemove = [];
programPath.traverse({
// Find all classes and test them for private properties
Class: function Class(classPath) {
var classPrivateProps = {};
var insertPath = classPath.parentPath.type === 'ExportNamedDeclaration' || classPath.parentPath.type === 'ExportDefaultDeclaration' ? classPath.parentPath : classPath;
var constructor = null;
var hasPrivate = false; // let body = classPath.get('body');
// for(let subPath of body.get('body')) console.log(subPath.type);
classPath.traverse({
ClassProperty: function ClassProperty(propPath) {
if (propPath.node.key.name.startsWith('_')) {
hasPrivate = true;
classPrivateProps[propPath.node.key.name] = propPath.node.value;
propsToRemove.push(propPath);
}
}
});
if (hasPrivate) {
// Transform private access into weakmap access
classPath.traverse({
AssignmentExpression: function AssignmentExpression(assignPath) {
handleAssignPrivateProperties(t, assignPath, classPrivateProps, classPath);
},
Expression: function Expression(exprPath) {
if (exprPath.type === 'MemberExpression') handleAccessPrivateProperties(t, exprPath, classPrivateProps, classPath);
},
ClassMethod: function ClassMethod(methodPath) {
if (methodPath.node.kind === 'constructor') constructor = methodPath;
methodPath.traverse({
AssignmentExpression: function AssignmentExpression(assignPath) {
handleAssignPrivateProperties(t, assignPath, classPrivateProps, classPath);
},
Expression: function Expression(exprPath) {
if (exprPath.type === 'MemberExpression') handleAccessPrivateProperties(t, exprPath, classPrivateProps, classPath);
}
});
}
}); // Insert the weakmap definition in the class
insertPath.insertAfter(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(classPath.node.id, t.identifier('private'), false, false), t.newExpression(t.identifier('WeakMap'), [])))); // Insert an empty object initialization for the weakmap
// in the constructor
// Generate object with private props that have a default value
var i,
objProps = [],
objPropNames = Object.keys(classPrivateProps);
for (i = 0; i < objPropNames.length; i++) {
if (classPrivateProps[objPropNames[i]] !== null) objProps.push(t.objectProperty(t.identifier(objPropNames[i].substr(1)), classPrivateProps[objPropNames[i]]));
}
var obj = t.objectExpression(objProps); // Generate the weakmap .set() call with the generated object
var privateInit = t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(classPath.node.id, t.identifier('private'), false, false), t.identifier('set'), false, false), [t.thisExpression(), obj])); // Append to existing constructor
if (constructor !== null) {
constructor.get('body').unshiftContainer('body', privateInit);
} // or create a constructor if there was none
else {
classPath.get('body').unshiftContainer('body', t.classMethod('constructor', t.identifier('constructor'), [], t.blockStatement([privateInit])));
}
}
}
});
propsToRemove.forEach(function (prop) {
return prop.remove();
});
}
}
};
}
var getPrivatePropName = function getPrivatePropName(path, classPrivateProps) {
var prop = null;
if (path.node.type === 'MemberExpression') {
if (path.node.object.type === 'ThisExpression') {
if (path.node.property.type === 'Identifier') {
if ('_' + path.node.property.name in classPrivateProps) prop = path.node.property.name;
}
}
}
return prop;
};
var getAssignPrivatePropName = function getAssignPrivatePropName(path, classPrivateProps) {
var prop = null;
if (path.node.left.object.type !== 'ThisExpression') return;
if (path.node.left.type === 'MemberExpression') {
if (path.node.left.property.type === 'Identifier') {
if ('_' + path.node.left.property.name in classPrivateProps) prop = path.node.left.property.name;
}
} else if (path.node.left.name in classPrivateProps) {
prop = path.node.left.name;
}
return prop;
};
var handleAccessPrivateProperties = function handleAccessPrivateProperties(t, path, classPrivateProps, classPath) {
var prop = getPrivatePropName(path, classPrivateProps);
if (prop !== null && typeof prop !== 'undefined') {
path.replaceWith(t.expressionStatement(t.memberExpression(t.callExpression(t.memberExpression(t.memberExpression(classPath.node.id, t.identifier('private'), false, false), t.identifier('get'), false, false), [t.thisExpression()]), t.identifier(prop), false, false)));
}
};
var handleAssignPrivateProperties = function handleAssignPrivateProperties(t, path, classPrivateProps, classPath) {
var prop = getAssignPrivatePropName(path, classPrivateProps);
if (prop !== null && typeof prop !== 'undefined') {
path.replaceWith(t.expressionStatement(t.assignmentExpression(path.node.operator, t.memberExpression(t.callExpression(t.memberExpression(t.memberExpression(classPath.node.id, t.identifier('private'), false, false), t.identifier('get'), false, false), [t.thisExpression()]), t.identifier(prop), false, false), path.node.right)));
}
};