babel-plugin-transform-class-inherited-hook
Babel plugin that transforms subclass declarations to call superclass.onInherited afterwards (if present).
Example
Before:
{ return 7; }
After:
var { // Declare the Apple class as an anonymous class _Apple. var _Apple = { return 7; } // Set _Apple's name property to 'Apple'. Object; // If Fruit has a property called onInherited, if "onInherited" in Fruit // and it's a function, if typeof FruitonInherited == 'function' // call it and save the returned value. var _Apple2 = Fruit; // If Fruit.onInherited returned a value, if _Apple2 !== undefined // if it's a function, define its name property as 'Apple', if it didn't have that already. if typeof _Apple2 == 'function' && _Apple2name !== "Apple" Object; // Use the returned value as the class instead of the declared one _Apple = _Apple2; // If Fruit.onInherited is present but not a function, else // complain about it throw "Attempted to call onInherited, but it was not a function"; // Return the class so it gets set as the variable return _Apple;};
NOTE: Actual implementation uses a helper function so this logic isn't repeated every single class declaration.
What?
Every class declaration with a superClass gets transformed into an expression that:
- Creates the child class
- calls
SuperClass.onInherited(ChildClass)
if present - evaluates to the return value of
SuperClass.onInherited(ChildClass)
if any, otherwise the created child class
Why?
This lets you hook class inheritance:
{ ... } // Add to a map, wire things up, etc static { console; ; }
It also lets you transform classes at inheritance time:
// Whether we need to shim the native behavior static { return true; } // If we don't need to shim the native behavior, then // this is the native class that should be used instead static { return null; } static { let needsToBeShimmed nativeClass = child; if ! // If we return a value from onInherited, it will be used // as the value of the class declaration instead of child return ; } static { return !windowPromise; } static { return windowPromise; } { ... } // Promise now refers to either window.Promise or the class defined by// the Promise class declaration above, depending on if needsToBeShimmed()// evaluated to true or false
No really, what are some practical uses?
Ok, how about automatically calling react-redux's connect
method?
;; Component static { let mapStateToProps mapDispatchToProps mergeProps = child; return child; } } static { ... }// NiceFlowerbed has already been connected to the store
Or maybe generating a reducer from a class with action handler methods?
; static { // The return value of onInherited doesn't have // to be a class; in this case, it's just a function. return ; } { ... } { ... }// Magic is a reducer function that will handle actions of type CAST_SPELL and LEARN_SPELL
You could even do the unthinkable...
let ActiveRecord = Base: class static { ; ; childfind = ; } { ... } ; Base {} ;
But this makes class extension untrustworthy and changes the semantics of a well-defined system!
Yeah, it does. But it can help provide a little bit of structure and get rid of a little boilerplate.
Installation
$ npm install --save babel-plugin-transform-class-inherited-hook
Usage
.babelrc
(Recommended)
Via .babelrc
Via CLI
$ babel --plugins transform-class-inherited-hook script.js
Via Node API
;
Thanks
- RReverser/babel-plugin-hello-world for the great babel plugin template
- thejameskyle/babel-handbook for the documentation to get me started