UNPKG

babel-core

Version:

Turn ES6 code into readable vanilla ES5 with source maps

297 lines (238 loc) 7.69 kB
"use strict"; var ReplaceSupers = require("../../helpers/replace-supers"); var nameMethod = require("../../helpers/name-method"); var defineMap = require("../../helpers/define-map"); var messages = require("../../../messages"); var util = require("../../../util"); var t = require("../../../types"); exports.check = t.isClass; exports.ClassDeclaration = function (node, parent, scope, file) { return new ClassTransformer(node, file, scope, true).run(); }; exports.ClassExpression = function (node, parent, scope, file) { if (!node.id) { if (t.isProperty(parent) && parent.value === node && !parent.computed && t.isIdentifier(parent.key)) { // var o = { foo: class {} }; node.id = parent.key; } if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) { // var foo = class {}; node.id = parent.id; } } return new ClassTransformer(node, file, scope, false).run(); }; /** * Description * * @param {Node} node * @param {File} file * @param {Scope} scope * @param {Boolean} isStatement */ function ClassTransformer(node, file, scope, isStatement) { this.isStatement = isStatement; this.scope = scope; this.node = node; this.file = file; this.hasInstanceMutators = false; this.hasStaticMutators = false; this.instanceMutatorMap = {}; this.staticMutatorMap = {}; this.hasConstructor = false; this.className = node.id || scope.generateUidIdentifier("class"); this.superName = node.superClass || t.identifier("Function"); this.hasSuper = !!node.superClass; this.isLoose = file.isLoose("es6.classes"); } /** * Description * * @returns {Array} */ ClassTransformer.prototype.run = function () { var superName = this.superName; var className = this.className; var file = this.file; // var body = this.body = []; var constructorBody = t.blockStatement([ t.expressionStatement(t.callExpression(file.addHelper("class-call-check"), [ t.thisExpression(), className ])) ]); var constructor; if (this.node.id) { constructor = t.functionDeclaration(className, [], constructorBody); body.push(constructor); } else { constructor = t.functionExpression(null, [], constructorBody); body.push(t.variableDeclaration("var", [ t.variableDeclarator(className, constructor) ])); } this.constructor = constructor; var closureParams = []; var closureArgs = []; // if (this.hasSuper) { closureArgs.push(superName); if (!t.isIdentifier(superName)) { superName = this.scope.generateUidBasedOnNode(superName, this.file); } closureParams.push(superName); this.superName = superName; body.push(t.expressionStatement(t.callExpression(file.addHelper("inherits"), [className, superName]))); } this.buildBody(); t.inheritsComments(body[0], this.node); var init; if (body.length === 1) { // only a constructor so no need for a closure container init = t.toExpression(constructor); } else { body.push(t.returnStatement(className)); init = t.callExpression( t.functionExpression(null, closureParams, t.blockStatement(body)), closureArgs ); } if (this.isStatement) { return t.variableDeclaration("let", [ t.variableDeclarator(className, init) ]); } else { return init; } }; /** * Description */ ClassTransformer.prototype.buildBody = function () { var constructor = this.constructor; var className = this.className; var superName = this.superName; var classBody = this.node.body.body; var body = this.body; for (var i = 0; i < classBody.length; i++) { var node = classBody[i]; if (t.isMethodDefinition(node)) { var replaceSupers = new ReplaceSupers({ methodNode: node, className: this.className, superName: this.superName, isStatic: node.static, isLoose: this.isLoose, scope: this.scope, file: this.file }, true); replaceSupers.replace(); if (node.key.name === "constructor") { this.pushConstructor(node); } else { this.pushMethod(node); } } else if (t.isPrivateDeclaration(node)) { this.closure = true; body.unshift(node); } else if (t.isClassProperty(node)) { this.pushProperty(node); } } // we have no constructor, we have a super, and the super doesn't appear to be falsy if (!this.hasConstructor && this.hasSuper && !t.isFalsyExpression(superName)) { var helperName = "class-super-constructor-call"; if (this.isLoose) helperName += "-loose"; constructor.body.body.push(util.template(helperName, { CLASS_NAME: className, SUPER_NAME: this.superName }, true)); } var instanceProps; var staticProps; if (this.hasInstanceMutators) { instanceProps = defineMap.build(this.instanceMutatorMap); } if (this.hasStaticMutators) { staticProps = defineMap.build(this.staticMutatorMap); } if (instanceProps || staticProps) { staticProps = staticProps || t.literal(null); var args = [className, staticProps]; if (instanceProps) args.push(instanceProps); body.push(t.expressionStatement( t.callExpression(this.file.addHelper("prototype-properties"), args) )); } }; /** * Push a method to its respective mutatorMap. * * @param {Node} node MethodDefinition */ ClassTransformer.prototype.pushMethod = function (node) { var methodName = node.key; var kind = node.kind; if (kind === "") { nameMethod.property(node, this.file, this.scope); if (this.isLoose) { // use assignments instead of define properties for loose classes var className = this.className; if (!node.static) className = t.memberExpression(className, t.identifier("prototype")); methodName = t.memberExpression(className, methodName, node.computed); var expr = t.expressionStatement(t.assignmentExpression("=", methodName, node.value)); t.inheritsComments(expr, node); this.body.push(expr); return; } kind = "value"; } var mutatorMap = this.instanceMutatorMap; if (node.static) { this.hasStaticMutators = true; mutatorMap = this.staticMutatorMap; } else { this.hasInstanceMutators = true; } defineMap.push(mutatorMap, methodName, kind, node.computed, node); defineMap.push(mutatorMap, methodName, "enumerable", node.computed, false); }; /** * Description * * @param {Node} node */ ClassTransformer.prototype.pushProperty = function (node) { if (!node.value) return; var key; if (node.static) { key = t.memberExpression(this.className, node.key); this.body.push( t.expressionStatement(t.assignmentExpression("=", key, node.value)) ); } else { key = t.memberExpression(t.thisExpression(), node.key); this.constructor.body.body.unshift( t.expressionStatement(t.assignmentExpression("=", key, node.value)) ); } }; /** * Replace the constructor body of our class. * * @param {Node} method MethodDefinition */ ClassTransformer.prototype.pushConstructor = function (method) { if (method.kind) { throw this.file.errorWithNode(method, messages.get("classesIllegalConstructorKind")); } var construct = this.constructor; var fn = method.value; this.hasConstructor = true; t.inherits(construct, fn); t.inheritsComments(construct, method); construct._ignoreUserWhitespace = true; construct.params = fn.params; construct.body.body = construct.body.body.concat(fn.body.body); };