1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | 1x 1x 17x 17x 2x 2x 2x 22x 22x 11x 11x 11x 6x 6x 5x 89x 75x 59x 51x 51x 89x 89x 89x 51x 13x 6x 6x 11x 11x 9x 9x 9x 5x 5x 5x 2x 3x 4x 9x 17x 17x 6x 17x 17x 6x 6x 17x 9x 17x 4x 17x 1x | export default function (babel) { const t = babel.types; var visitor, _class; visitor = { // CLASS ClassDeclaration: { enter(path) { _class = {id: path.node.id, parentId: path.node.superClass, statics: [], methods: []}; }, exit(path) { //console.log(es5Class(_class)); path.replaceWithMultiple(es5Class(_class)); } }, // SUPER CALL Super: superCall, // NEW.TARGET MetaProperty(path) { var node = path.node; Eif (node.meta.name === "new" && node.property.name === "target") { path.replaceWith(expression('this.constructor')); } }, // METHODS ClassMethod(path) { var node = path.node; if (node.kind === 'constructor') { // CONSTRUCTOR _class.constructor = t.functionDeclaration(_class.id, node.params, node.body); return; } if (node.static) { // STATIC METHODS _class.statics.push(es5Method(node)); return; } // PROTOTYPE METHODS _class.methods.push(es5Method(node)); } }; // Utils function isString(value) { return typeof value === 'string'; } function isMember(value) { return value.type = 'MemberExpression'; } function toIdentifier(key) { return t.identifier(key) } function toMember(target, property) { return t.memberExpression(target, property); } function expression() { var index = arguments.length - 1, members = [], member; do { member = arguments[index]; members = isString(member) ? members.concat(member.split('.').reverse().map(toIdentifier)) : members.concat([member]); index--; } while (index > -1); return members.reduceRight(toMember); } function objectAssign(target, members) { return t.expressionStatement(t.callExpression( // Object.assign(target, members) expression("Object.assign"), [target, t.objectExpression(members)] )); } function objectCreate(parentClass) { return t.callExpression( // Object.create(parentClass) expression("Object.create"), [parentClass] ); } function assign(key, value) { // key = value return t.expressionStatement(t.assignmentExpression('=', key, value)); } function es5Method(method) { var id = method.key; // foo: function foo(args) {/* code */} return t.objectProperty(id, t.functionExpression(id, method.params, method.body)); } function superCall(path) { var targetPath = path.parentPath, ParentClass = _class.parentId, caller, method; if (path.parent.type ==="MemberExpression") { method = targetPath.node.property; targetPath = targetPath.parentPath; if (path.getFunctionParent().node.static) { // caller => ParentClass.methodName.call caller = expression(ParentClass, method, 'call'); } else { // caller => ParentClass.prototype.methodName.call caller = expression(ParentClass, 'prototype', method, 'call'); } } else { // caller => ParentClass.call caller = expression(ParentClass, 'call'); } targetPath.replaceWith(t.callExpression( // {super target}.apply(this, args) caller, [t.Identifier('this')].concat(targetPath.node.arguments) )); } function es5Class(_class) { var _es5Class = [], MyClass = _class.id; // constructor if (!_class.hasOwnProperty('constructor')) { _class.constructor = t.functionDeclaration(_class.id, [], t.blockStatement([])); } _es5Class.push(_class.constructor); // parent class if (_class.parentId) { // MyClass.prototype = Object.create(MyParentClass.prototype); _es5Class.push( assign( expression(MyClass, 'prototype'), objectCreate(expression(_class.parentId, 'prototype')) )); _class.methods.push(t.objectProperty( t.identifier('constructor'), MyClass )); } // methods if (_class.methods.length > 0) { // Object.assign(MyClass.prototype, { /* my methods *//}); _es5Class.push( objectAssign(expression(MyClass, 'prototype'), _class.methods) ); } // statics if (_class.statics.length > 0) { // Object.assign(MyClass, { /* my statics *//}); _es5Class.push( objectAssign(MyClass, _class.statics) ); } return _es5Class; } return { name: "transform-class", visitor: visitor }; } |