diff --git a/src/babel/transformation/transformers/es6/classes/index.js b/src/babel/transformation/transformers/es6/classes/index.js new file mode 100644 index 0000000000..d2373435f4 --- /dev/null +++ b/src/babel/transformation/transformers/es6/classes/index.js @@ -0,0 +1,23 @@ +import LooseTransformer from "./loose"; +import VanillaTransformer from "./vanilla"; +import * as t from "../../../../types"; +import { bare } from "../../../helpers/name-method"; + +export var visitor = { + ClassDeclaration(node) { + return t.variableDeclaration("let", [ + t.variableDeclarator(node.id, t.toExpression(node)) + ]); + }, + + ClassExpression(node, parent, scope, file) { + var inferred = bare(node, parent, scope); + if (inferred) return inferred; + + if (file.isLoose("es6.classes")) { + return new LooseTransformer(this, file).run(); + } else { + return new VanillaTransformer(this, file).run(); + } + } +}; diff --git a/src/babel/transformation/transformers/es6/classes/loose.js b/src/babel/transformation/transformers/es6/classes/loose.js new file mode 100644 index 0000000000..3c42f53074 --- /dev/null +++ b/src/babel/transformation/transformers/es6/classes/loose.js @@ -0,0 +1,24 @@ +import VanillaTransformer from "./vanilla"; +import * as t from "../../../../types"; + +export default class LooseClassTransformer extends VanillaTransformer { + constructor() { + super(...arguments); + this.isLoose = true; + } + + _processMethod(node) { + if (!node.decorators) { + // use assignments instead of define properties for loose classes + + var classRef = this.classRef; + if (!node.static) classRef = t.memberExpression(classRef, t.identifier("prototype")); + var methodName = t.memberExpression(classRef, node.key, node.computed || t.isLiteral(node.key)); + + var expr = t.expressionStatement(t.assignmentExpression("=", methodName, node.value)); + t.inheritsComments(expr, node); + this.body.push(expr); + return true; + } + } +} diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes/vanilla.js similarity index 75% rename from src/babel/transformation/transformers/es6/classes.js rename to src/babel/transformation/transformers/es6/classes/vanilla.js index 2eaf33140a..eab91f4a05 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes/vanilla.js @@ -1,27 +1,15 @@ -import type NodePath from "../../../traversal/path"; -import type File from "../../file"; -import memoiseDecorators from "../../helpers/memoise-decorators"; -import ReplaceSupers from "../../helpers/replace-supers"; -import * as nameMethod from "../../helpers/name-method"; -import * as defineMap from "../../helpers/define-map"; -import * as messages from "../../../messages"; -import * as util from "../../../util"; -import * as t from "../../../types"; +import type NodePath from "../../../../traversal/path"; +import type File from "../../../file"; +import memoiseDecorators from "../../../helpers/memoise-decorators"; +import ReplaceSupers from "../../../helpers/replace-supers"; +import * as nameMethod from "../../../helpers/name-method"; +import * as defineMap from "../../../helpers/define-map"; +import * as messages from "../../../../messages"; +import * as util from "../../../../util"; +import * as t from "../../../../types"; const PROPERTY_COLLISION_METHOD_NAME = "__initializeProperties"; -export var visitor = { - ClassDeclaration(node) { - return t.variableDeclaration("let", [ - t.variableDeclarator(node.id, t.toExpression(node)) - ]); - }, - - ClassExpression(node, parent, scope, file) { - return new ClassTransformer(this, file).run(); - } -}; - var collectPropertyReferencesVisitor = { Identifier: { enter(node, parent, scope, state) { @@ -83,7 +71,7 @@ var verifyConstructorVisitor = { } }; -class ClassTransformer { +export default class ClassTransformer { /** * Description @@ -96,11 +84,7 @@ class ClassTransformer { this.path = path; this.file = file; - this.hasInstanceDescriptors = false; - this.hasStaticDescriptors = false; - - this.instanceMutatorMap = {}; - this.staticMutatorMap = {}; + this.clearDescriptors(); this.instancePropBody = []; this.instancePropRefs = {}; @@ -108,15 +92,21 @@ class ClassTransformer { this.body = []; this.pushedConstructor = false; - this.hasConstructor = false; + this.pushedInherits = false; this.hasDecorators = false; - this.className = this.node.id; - this.classRef = this.node.id || this.scope.generateUidIdentifier("class"); + this.isLoose = false; + + // class id + this.className = this.node.id; + + // this is the name of the binding that will **always** reference the class we've constructed + this.classRef = this.node.id || this.scope.generateUidIdentifier("class"); + + // this is a direct reference to the class we're building, class decorators can shadow the classRef + this.directRef = null; this.superName = this.node.superClass || t.identifier("Function"); this.hasSuper = !!this.node.superClass; - - this.isLoose = file.isLoose("es6.classes"); } /** @@ -137,15 +127,7 @@ class ClassTransformer { // var constructorBody = this.constructorBody = t.blockStatement([]); - var constructor; - - if (this.className) { - constructor = t.functionDeclaration(this.className, [], constructorBody); - } else { - constructor = t.functionExpression(null, [], constructorBody); - } - - this.constructor = constructor; + var constructor = this.constructor = this.buildConstructor(); // @@ -160,19 +142,15 @@ class ClassTransformer { closureParams.push(superName); this.superName = superName; - body.push(t.expressionStatement(t.callExpression(file.addHelper("inherits"), [classRef, superName]))); } // var decorators = this.node.decorators; if (decorators) { - // create a class reference to use later on - this.classRef = this.scope.generateUidIdentifier(classRef); - // this is so super calls and the decorators have access to the raw function - body.push(t.variableDeclaration("var", [ - t.variableDeclarator(this.classRef, classRef) - ])); + this.directRef = this.scope.generateUidIdentifier(this.classRef); + } else { + this.directRef = this.classRef; } // @@ -181,26 +159,11 @@ class ClassTransformer { // make sure this class isn't directly called constructorBody.body.unshift(t.expressionStatement(t.callExpression(file.addHelper("class-call-check"), [ t.thisExpression(), - this.classRef + this.directRef ]))); // - - if (decorators) { - // reverse the decorators so we execute them in the right order - decorators = decorators.reverse(); - - for (var i = 0; i < decorators.length; i++) { - var decorator = decorators[i]; - - var decoratorNode = util.template("class-decorator", { - DECORATOR: decorator.expression, - CLASS_REF: classRef - }, true); - decoratorNode.expression._ignoreModulesRemap = true; - body.push(decoratorNode); - } - } + this.pushDecorators(); body = body.concat(this.staticPropBody); @@ -210,8 +173,7 @@ class ClassTransformer { } // - - body.push(t.returnStatement(classRef)); + body.push(t.returnStatement(this.classRef)); return t.callExpression( t.functionExpression(null, closureParams, t.blockStatement(body)), @@ -219,6 +181,14 @@ class ClassTransformer { ); } + /** + * Description + */ + + buildConstructor() { + return t.functionDeclaration(this.classRef, [], this.constructorBody); + } + /** * Description */ @@ -261,7 +231,7 @@ class ClassTransformer { if (this.hasSuper) { constructor = util.template("class-derived-default-constructor"); } else { - constructor = t.functionExpression(null, [], t.blockStatement()); + constructor = t.functionExpression(null, [], t.blockStatement([])); } this.path.get("body").unshiftContainer("body", t.methodDefinition( @@ -277,10 +247,17 @@ class ClassTransformer { buildBody() { this.constructorMeMaybe(); + this.pushBody(); + this.placePropertyInitializers(); + this.pushDescriptors(); + } - var constructorBody = this.constructorBody; - var classBodyPaths = this.path.get("body.body"); - var body = this.body; + /** + * Description + */ + + pushBody() { + var classBodyPaths = this.path.get("body.body"); for (var path of (classBodyPaths: Array)) { var node = path.node; @@ -296,7 +273,7 @@ class ClassTransformer { var replaceSupers = new ReplaceSupers({ methodPath: path, methodNode: node, - objectRef: this.classRef, + objectRef: this.directRef, superRef: this.superName, isStatic: node.static, isLoose: this.isLoose, @@ -315,16 +292,28 @@ class ClassTransformer { this.pushProperty(node, path); } } + } - // - this.placePropertyInitializers(); + /** + * Description + */ - // - if (this.userConstructor) { - constructorBody.body = constructorBody.body.concat(this.userConstructor.body.body); - t.inherits(this.constructor, this.userConstructor); - t.inherits(this.constructorBody, this.userConstructor.body); - } + clearDescriptors() { + this.hasInstanceDescriptors = false; + this.hasStaticDescriptors = false; + + this.instanceMutatorMap = {}; + this.staticMutatorMap = {}; + } + + /** + * Description + */ + + pushDescriptors() { + this.pushInherits(); + + var body = this.body; var instanceProps; var staticProps; @@ -372,8 +361,14 @@ class ClassTransformer { t.callExpression(this.file.addHelper(classHelper), args) )); } + + this.clearDescriptors(); } + /** + * Description + */ + buildObjectAssignment(id) { return t.variableDeclaration("var", [ t.variableDeclarator(id, t.objectExpression([])) @@ -406,11 +401,7 @@ class ClassTransformer { } } else { if (this.hasSuper) { - if (this.hasConstructor) { - this.bareSuper.insertAfter(body); - } else { - this.constructorBody.body = this.constructorBody.body.concat(body); - } + this.bareSuper.insertAfter(body); } else { this.constructorBody.body = body.concat(this.constructorBody.body); } @@ -431,7 +422,7 @@ class ClassTransformer { } return false; - } + } /** * Description @@ -465,24 +456,16 @@ class ClassTransformer { if (node.kind === "method") { nameMethod.property(node, this.file, path ? path.get("value").scope : this.scope); - - if (this.isLoose && !node.decorators) { - // use assignments instead of define properties for loose classes - - var classRef = this.classRef; - if (!node.static) classRef = t.memberExpression(classRef, t.identifier("prototype")); - var methodName = t.memberExpression(classRef, node.key, node.computed || t.isLiteral(node.key)); - - var expr = t.expressionStatement(t.assignmentExpression("=", methodName, node.value)); - t.inheritsComments(expr, node); - this.body.push(expr); - return; - } + if (this._processMethod(node)) return; } this.pushToMap(node); } + _processMethod() { + return false; + } + /** * Description */ @@ -549,13 +532,16 @@ class ClassTransformer { fnPath.scope.rename(this.classRef.name); } - var construct = this.constructor; - var fn = method.value; + var constructorBody = this.constructorBody; + var construct = this.constructor; + var fn = method.value; this.userConstructorPath = fnPath; - this.userConstructor = fn; - this.hasConstructor = true; + constructorBody.body = constructorBody.body.concat(fn.body.body); + t.inherits(constructorBody, fn.body); + + t.inherits(construct, fn); t.inheritsComments(construct, method); construct._ignoreUserWhitespace = true; @@ -564,21 +550,59 @@ class ClassTransformer { t.inherits(construct.body, fn.body); // push constructor to body - if (!this.pushedConstructor) { - this.pushedConstructor = true; + this._pushConstructor(); + } - if (this.className) { - this.body.push(construct); - } else { - // infer class name if this is a nameless class expression - this.constructor = nameMethod.bare(construct, this.parent, this.scope) || construct; + _pushConstructor() { + if (this.pushedConstructor) return; + this.pushedConstructor = true; - this.body.push(t.variableDeclaration("var", [ - t.variableDeclarator(classRef, constructor) - ])); + // we haven't pushed any descriptors yet + if (this.hasInstanceDescriptors || this.hasStaticDescriptors) { + this.pushDescriptors(); + } - t.inheritsComments(this.body[0], this.node); - } + this.body.push(this.constructor); + + this.pushInherits(); + } + + /** + * Push inherits helper to body. + */ + + pushInherits() { + if (!this.hasSuper || this.pushedInherits) return; + + this.pushedInherits = true; + this.body.push(t.expressionStatement(t.callExpression( + this.file.addHelper("inherits"), + [this.classRef, this.superName] + ))); + } + + /** + * Push decorators to body. + */ + + pushDecorators() { + var decorators = this.node.decorators; + if (!decorators) return; + + this.body.push(t.variableDeclaration("var", [ + t.variableDeclarator(this.directRef, this.classRef) + ])); + + // reverse the decorators so we execute them in the right order + decorators = decorators.reverse(); + + for (var decorator of (decorators: Array)) { + var decoratorNode = util.template("class-decorator", { + DECORATOR: decorator.expression, + CLASS_REF: this.classRef + }, true); + decoratorNode.expression._ignoreModulesRemap = true; + this.body.push(decoratorNode); } } } diff --git a/test/core/fixtures/transformation/es6.classes-loose/constructor-order/expected.js b/test/core/fixtures/transformation/es6.classes-loose/constructor-order/expected.js index 50d72f5e80..1816c12f0a 100644 --- a/test/core/fixtures/transformation/es6.classes-loose/constructor-order/expected.js +++ b/test/core/fixtures/transformation/es6.classes-loose/constructor-order/expected.js @@ -11,7 +11,6 @@ var x = (function () { 4; 5; 6; - babelHelpers.classCallCheck(this, x); } diff --git a/test/core/fixtures/transformation/es6.classes/statement/expected.js b/test/core/fixtures/transformation/es6.classes/statement/expected.js index 08a1f01343..7319bedb7f 100644 --- a/test/core/fixtures/transformation/es6.classes/statement/expected.js +++ b/test/core/fixtures/transformation/es6.classes/statement/expected.js @@ -6,26 +6,22 @@ var BaseView = function BaseView() { this.autoRender = true; }; -var BaseView = (function () { - var _class = function BaseView() { - babelHelpers.classCallCheck(this, _class); +var BaseView = function BaseView() { + babelHelpers.classCallCheck(this, BaseView); - this.autoRender = true; - }; - - return _class; -})(); + this.autoRender = true; +}; var BaseView = (function () { - var _class2 = function BaseView() { - babelHelpers.classCallCheck(this, _class2); - }; + function BaseView() { + babelHelpers.classCallCheck(this, BaseView); + } - babelHelpers.createClass(_class2, [{ + babelHelpers.createClass(BaseView, [{ key: "foo", value: function foo() { this.autoRender = true; } }]); - return _class2; + return BaseView; })(); \ No newline at end of file diff --git a/test/core/fixtures/transformation/es6.classes/super-class-anonymous/expected.js b/test/core/fixtures/transformation/es6.classes/super-class-anonymous/expected.js index 52ad3b94f2..cc74cfb896 100644 --- a/test/core/fixtures/transformation/es6.classes/super-class-anonymous/expected.js +++ b/test/core/fixtures/transformation/es6.classes/super-class-anonymous/expected.js @@ -9,9 +9,9 @@ var TestEmpty = (function (_ref) { babelHelpers.inherits(TestEmpty, _ref); return TestEmpty; })((function () { - var _class = function _class() { + function _class() { babelHelpers.classCallCheck(this, _class); - }; + } return _class; })()); @@ -25,9 +25,9 @@ var TestConstructorOnly = (function (_ref2) { babelHelpers.inherits(TestConstructorOnly, _ref2); return TestConstructorOnly; })((function () { - var _class2 = function _class2() { + function _class2() { babelHelpers.classCallCheck(this, _class2); - }; + } return _class2; })()); @@ -41,9 +41,9 @@ var TestMethodOnly = (function (_ref3) { babelHelpers.inherits(TestMethodOnly, _ref3); return TestMethodOnly; })((function () { - var _class3 = function _class3() { + function _class3() { babelHelpers.classCallCheck(this, _class3); - }; + } babelHelpers.createClass(_class3, [{ key: "method", @@ -61,9 +61,9 @@ var TestConstructorAndMethod = (function (_ref4) { babelHelpers.inherits(TestConstructorAndMethod, _ref4); return TestConstructorAndMethod; })((function () { - var _class4 = function _class4() { + function _class4() { babelHelpers.classCallCheck(this, _class4); - }; + } babelHelpers.createClass(_class4, [{ key: "method", @@ -81,9 +81,9 @@ var TestMultipleMethods = (function (_ref5) { babelHelpers.inherits(TestMultipleMethods, _ref5); return TestMultipleMethods; })((function () { - var _class5 = function _class5() { + function _class5() { babelHelpers.classCallCheck(this, _class5); - }; + } babelHelpers.createClass(_class5, [{ key: "m1", diff --git a/test/core/fixtures/transformation/es6.modules-amd/exports-default/expected.js b/test/core/fixtures/transformation/es6.modules-amd/exports-default/expected.js index 6394d0f739..40eac20efb 100644 --- a/test/core/fixtures/transformation/es6.modules-amd/exports-default/expected.js +++ b/test/core/fixtures/transformation/es6.modules-amd/exports-default/expected.js @@ -9,13 +9,9 @@ define(["exports", "module"], function (exports, module) { module.exports = function () {}; - var _default = (function () { - var _class = function _default() { - babelHelpers.classCallCheck(this, _class); - }; - - return _class; - })(); + var _default = function _default() { + babelHelpers.classCallCheck(this, _default); + }; module.exports = _default; diff --git a/test/core/fixtures/transformation/es6.modules-common/exports-default/expected.js b/test/core/fixtures/transformation/es6.modules-common/exports-default/expected.js index c7f1821052..b4ee26ad84 100644 --- a/test/core/fixtures/transformation/es6.modules-common/exports-default/expected.js +++ b/test/core/fixtures/transformation/es6.modules-common/exports-default/expected.js @@ -11,13 +11,9 @@ exports["default"] = foo; exports["default"] = function () {}; -var _default = (function () { - var _class = function _default() { - babelHelpers.classCallCheck(this, _class); - }; - - return _class; -})(); +var _default = function _default() { + babelHelpers.classCallCheck(this, _default); +}; exports["default"] = _default; diff --git a/test/core/fixtures/transformation/es6.modules-ignore/exports-default/expected.js b/test/core/fixtures/transformation/es6.modules-ignore/exports-default/expected.js index 243f60206b..c624f28d92 100644 --- a/test/core/fixtures/transformation/es6.modules-ignore/exports-default/expected.js +++ b/test/core/fixtures/transformation/es6.modules-ignore/exports-default/expected.js @@ -2,13 +2,9 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -var _default = (function () { - var _class = function _default() { - _classCallCheck(this, _class); - }; - - return _class; -})(); +var _default = function _default() { + _classCallCheck(this, _default); +}; function foo() {} diff --git a/test/core/fixtures/transformation/es6.modules-system/exports-default/expected.js b/test/core/fixtures/transformation/es6.modules-system/exports-default/expected.js index b350cce441..37dd3bbf33 100644 --- a/test/core/fixtures/transformation/es6.modules-system/exports-default/expected.js +++ b/test/core/fixtures/transformation/es6.modules-system/exports-default/expected.js @@ -22,13 +22,9 @@ System.register([], function (_export) { _export("default", function () {}); - _default = (function () { - var _class = function _default() { - _classCallCheck(this, _class); - }; - - return _class; - })(); + _default = function _default() { + _classCallCheck(this, _default); + }; _export("default", _default); @@ -43,4 +39,4 @@ System.register([], function (_export) { })()); } }; -}); +}); \ No newline at end of file diff --git a/test/core/fixtures/transformation/es6.modules-umd/exports-default/expected.js b/test/core/fixtures/transformation/es6.modules-umd/exports-default/expected.js index 92684ad58e..3709b608c1 100644 --- a/test/core/fixtures/transformation/es6.modules-umd/exports-default/expected.js +++ b/test/core/fixtures/transformation/es6.modules-umd/exports-default/expected.js @@ -21,13 +21,9 @@ module.exports = function () {}; - var _default = (function () { - var _class = function _default() { - babelHelpers.classCallCheck(this, _class); - }; - - return _class; - })(); + var _default = function _default() { + babelHelpers.classCallCheck(this, _default); + }; module.exports = _default; diff --git a/test/core/fixtures/transformation/es7.decorators/class-super/expected.js b/test/core/fixtures/transformation/es7.decorators/class-super/expected.js index fc587a7e25..4150b8a957 100644 --- a/test/core/fixtures/transformation/es7.decorators/class-super/expected.js +++ b/test/core/fixtures/transformation/es7.decorators/class-super/expected.js @@ -14,14 +14,14 @@ var Foo = (function (_Bar) { })(Bar); var Foo2 = (function (_Bar2) { - var _class = function Foo2() { - babelHelpers.classCallCheck(this, _class2); + function Foo2() { + babelHelpers.classCallCheck(this, _Foo2); - babelHelpers.get(Object.getPrototypeOf(_class2.prototype), "constructor", this).call(this); - }; + babelHelpers.get(Object.getPrototypeOf(_Foo2.prototype), "constructor", this).call(this); + } - babelHelpers.inherits(_class, _Bar2); - var _class2 = _class; - _class = bar(_class) || _class; - return _class; -})(Bar); + babelHelpers.inherits(Foo2, _Bar2); + var _Foo2 = Foo2; + Foo2 = bar(Foo2) || Foo2; + return Foo2; +})(Bar); \ No newline at end of file