Skip to content

Commit

Permalink
extend the existing babel plugin to transform class property arrow fu…
Browse files Browse the repository at this point in the history
…nctions into hot-reloadable class methods (gaearon#242)
  • Loading branch information
calesce committed Jul 13, 2016
1 parent c07dcb0 commit 40e5e36
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 0 deletions.
34 changes: 34 additions & 0 deletions src/babel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ const buildTagger = template(`
})();
`);

const buildNewClassProperty = (t, key, identifier, params) => {
const returnExpression = t.callExpression(
t.memberExpression(t.thisExpression(), identifier),
params
);
const blockStatement = t.blockStatement([t.returnStatement(returnExpression)]);
const newArrowFunction = t.arrowFunctionExpression(params, blockStatement);
return t.classProperty(key, newArrowFunction);
};

module.exports = function plugin(args) {
// This is a Babel plugin, but the user put it in the Webpack config.
if (this && this.callback) {
Expand Down Expand Up @@ -125,6 +135,30 @@ module.exports = function plugin(args) {
node.body.push(buildSemi());
},
},

Class(classPath) {
const classBody = classPath.get('body');

classBody.get('body').forEach(path => {
if (path.isClassProperty()) {
const { node } = path;

if (node.value.type === 'ArrowFunctionExpression') {
const { params } = node.value;
const newIdentifier = t.identifier(`__${node.key.name}__REACT_HOT_LOADER__`);

// create a new method on the class that the original class property function
// calls, since the method is able to be replaced by RHL
const newMethod = t.classMethod('method', newIdentifier, params, node.value.body);
path.insertAfter(newMethod);

// replace the original class property function with a function that calls
// the new class method created above
path.replaceWith(buildNewClassProperty(t, node.key, newIdentifier, params));
}
}
});
},
},
};
};
4 changes: 4 additions & 0 deletions test/AppContainer/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["es2015", "stage-1", "react"],
"plugins": ["../../src/babel"]
}
4 changes: 4 additions & 0 deletions test/babel/fixtures/class-properties/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["es2015", "stage-1"],
"plugins": ["../../../../src/babel"]
}
5 changes: 5 additions & 0 deletions test/babel/fixtures/class-properties/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Foo {
bar = (a, b) => {
return a(b);
};
}
38 changes: 38 additions & 0 deletions test/babel/fixtures/class-properties/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use strict";

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Foo = function () {
function Foo() {
var _this = this;

_classCallCheck(this, Foo);

this.bar = function (a, b) {
return _this.__bar__REACT_HOT_LOADER__(a, b);
};
}

_createClass(Foo, [{
key: "__bar__REACT_HOT_LOADER__",
value: function __bar__REACT_HOT_LOADER__(a, b) {
return a(b);
}
}]);

return Foo;
}();

;

(function () {
if (typeof __REACT_HOT_LOADER__ === 'undefined') {
return;
}

__REACT_HOT_LOADER__.register(Foo, "Foo", __FILENAME__);
})();

;

0 comments on commit 40e5e36

Please sign in to comment.