babel-plugin-transform-class
v0.3.0
Published
Compile ES2015 classes to ES5 or ES3 + polyfill
Downloads
246
Maintainers
Readme
babel-plugin-transform-class
A minimalist ES6 class babel transformer
Overview
Transpiler are very powerful, but can be frustrating when generated code do not fit to what we'd like.
Use this transform plugin if you like what it generates for you. The generated code is ES3 compliant as long as you also add the 2 required polyfills.
So, this transform plugin purely concentrate itself on the ES6 class notation.
It uses only one traverse visitor and... It does not inject any helper function
Class / Constructor
The class
Declaration & its constructor
, become function declarations
function MyClass(args) {/* code */}
Prototype methods
The class methods are assigned to the constructor prototype using Object.assign(MyClass.prototype, {/* methods */});
. The method shortand notation foo() {/* code */}
becomes foo: function foo() {/* code */}
(to get function name support)
Object.assign(MyClass.prototype, {
foo: function foo() {/* code */}
});
Static methods
The statics
methods are assigned to the constructor using Object.assign
.
Object.assign(MyClass, {/* static methods */});
Prototype inheritance
The prototype inheritance declared by the ES6 extends
instruction use Object.create()
to create the prototype
of the class. The class constructor is fixed back using Object.assign()
along the method definitions.
MyClass.prototype = Object.create(ParentClass.prototype);
Object.assign(MyClass.prototype, {
foo: function foo() {/* code */},
constructor: MyClass
});
new.target
The new.target
property, is transpiled to this.constructor
. As, with pure function constructor based prototype inheritance, the this
instance is created by the child class, and given to the parent class constructor & methods (see super
), this.constructor
refers to the child class prototype constructor property, itself fixed to the child class function itself, matching to the constructor invoked by new
.
Constructor, methods, & static methods super
call
The constructor super(arg1, arg2)
call is transpiled to ParentClass.call(this, arg1, arg2)
, the method super.methodName(arg1, arg2)
call, to ParentClass.prototype.methodName.call(this, arg1, arg2)
, and the static
method super.methodName(arg1, arg2)
call, to ParentClass.methodName.call(this, arg1, arg2)
.
Let's play
To get a better idea of the result, you can play with it there: https://astexplorer.net/#/gist/0178b41edea28820b2452e3422059cbd/latest
Up to come
- Sourcemap tests (to check impact of transformations approaches on babel sourcemap generation)
- ECMAScript stage 2 class properties notation and decorators may be potentially added as they make sense to complete class definition
Example
In
class AbstractClass {
constructor(a) {}
publicMethod(foo, bar) {
console.log('Abstract', new.target);
this.bar = bar;
}
}
class MyClass extends AbstractClass {
static staticMethod(a, b) {
console.log('static', a, b);
}
constructor(a) {
super(a);
this.bar = 51;
}
publicMethod(foo, bar) {
super.publicMethod(foo, bar);
console.log('MyClass', new.target);
}
}
Out
function AbstractClass(a) {}
Object.assign(AbstractClass.prototype, {
publicMethod: function publicMethod(foo, bar) {
console.log('Abstract', this.constructor);
this.bar = bar;
}
});
function MyClass(a) {
AbstractClass.call(this, a);
this.bar = 51;
}
MyClass.prototype = Object.create(AbstractClass.prototype);
Object.assign(MyClass.prototype, {
publicMethod: function publicMethod(foo, bar) {
AbstractClass.prototype.publicMethod.call(this, foo, bar);
console.log('MyClass', this.constructor);
},
constructor: MyClass
});
Object.assign(MyClass, {
staticMethod: function staticMethod(a, b) {
console.log('static', a, b);
}
});
Dependencies
This transform plugin requires the JS target environment to at least support Object.create()
& Object.assign()
, either natively or via a polyfill (polyfills are intentionally not included, use the one of your choice).
The Object.create(prototype, properties)
call currently only use the first parameter (prototype), so their is no need to include polyfill support of its second argument (the properties object definitions) for now.
Chosen Limitations from the ES6 standard
Many ES6 class features may either be checked at edit time instead of runtime or lead to dangerous and/or not reviewer friendly code patterns.
Here an example of some class related ESLint options:
- require constructor names to begin with a capital letter (new-cap)
- Verify calls of super() in constructors (constructor-super)
- Disallow modifying variables of class declarations (no-class-assign)
- Disallow duplicate name in class members (no-dupe-class-members)
- Disallow use of this/super before calling super() in constructors. (no-this-before-super)
- Disallow unnecessary constructor (no-useless-constructor)
and the chosen limitations that allows lighter, more readable, and faster code:
- It does not support expressions as parent class, only class/constructor names
- It does not support getter/setter
- It does not throw errors if you invoke the constructor without the new keyword (see rule
"capIsNew": true
) - It does not check if you use
this
before callingsuper()
(see ruleno-this-before-super: "error"
) - It does not support nested classes (classes defined inside a class method)
Last limitation
- This transform plugin do not properly extends native Objects such as
Date
,Array
,Function
, etc
Installation
$ npm install babel-plugin-transform-class
Usage
Via .babelrc
(Recommended)
.babelrc
{
"plugins": ["transform-class"]
}
Via CLI
$ babel --plugins transform-class script.js
Via Node API
require("babel-core")
.transform("class A extends B { constructor() { super(); }}", {
plugins: ["transform-class"]
});