UNPKG

babel-plugin-transform-private-to-weakmap

Version:

Transforms class properties prefixed with an underscore to weakmaps.

132 lines (111 loc) 6.11 kB
"use strict"; 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))); } };