All files index.js

98.18% Statements 54/55
95.45% Branches 21/22
93.33% Functions 14/15
98.11% Lines 52/53
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
  };
}