babel-core
Version:
Turn ES6 code into readable vanilla ES5 with source maps
356 lines (263 loc) • 10 kB
JavaScript
"use strict";
var _interopRequireWildcard = function (obj) { return obj && obj.__esModule ? obj : { "default": obj }; };
var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
exports.ClassDeclaration = ClassDeclaration;
exports.ClassExpression = ClassExpression;
var ReplaceSupers = _interopRequire(require("../../helpers/replace-supers"));
var nameMethod = _interopRequireWildcard(require("../../helpers/name-method"));
var defineMap = _interopRequireWildcard(require("../../helpers/define-map"));
var messages = _interopRequireWildcard(require("../../../messages"));
var util = _interopRequireWildcard(require("../../../util"));
var traverse = _interopRequire(require("../../../traversal"));
var t = _interopRequireWildcard(require("../../../types"));
var check = exports.check = t.isClass;
function ClassDeclaration(node, parent, scope, file) {
return t.variableDeclaration("let", [t.variableDeclarator(node.id, t.toExpression(node))]);
}
function ClassExpression(node, parent, scope, file) {
return new ClassTransformer(node, parent, scope, file).run();
}
var verifyConstructorVisitor = traverse.explode({
MethodDefinition: {
enter: function enter() {
this.skip();
}
},
Property: {
enter: function enter(node) {
if (node.method) this.skip();
}
},
CallExpression: {
enter: function enter(node, parent, scope, state) {
if (t.isIdentifier(node.callee, { name: "super" })) {
state.hasBareSuper = true;
if (!state.hasSuper) {
throw state.file.errorWithNode(node, "super call is only allowed in derived constructor");
}
}
}
},
ThisExpression: {
enter: function enter(node, parent, scope, state) {
if (state.hasSuper && !state.hasBareSuper) {
throw state.file.errorWithNode(node, "'this' is not allowed before super()");
}
}
}
});
var ClassTransformer = (function () {
/**
* Description
*/
function ClassTransformer(node, parent, scope, file) {
_classCallCheck(this, ClassTransformer);
this.parent = parent;
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;
this.classRef = 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 run() {
var superName = this.superName;
var className = this.className;
var classBody = this.node.body.body;
var classRef = this.classRef;
var file = this.file;
//
var body = this.body = [];
//
var constructorBody = t.blockStatement([t.expressionStatement(t.callExpression(file.addHelper("class-call-check"), [t.thisExpression(), classRef]))]);
var constructor;
if (this.className) {
constructor = t.functionDeclaration(this.className, [], constructorBody);
body.push(constructor);
} else {
constructor = t.functionExpression(null, [], constructorBody);
}
this.constructor = constructor;
//
var closureParams = [];
var closureArgs = [];
//
if (this.hasSuper) {
closureArgs.push(superName);
superName = this.scope.generateUidBasedOnNode(superName, this.file);
closureParams.push(superName);
this.superName = superName;
body.push(t.expressionStatement(t.callExpression(file.addHelper("inherits"), [classRef, superName])));
}
//
this.buildBody();
if (this.className) {
// named class with only a constructor
if (body.length === 1) return t.toExpression(body[0]);
} else {
// infer class name if this is a nameless class expression
constructor = nameMethod.bare(constructor, this.parent, this.scope);
body.unshift(t.variableDeclaration("var", [t.variableDeclarator(classRef, constructor)]));
t.inheritsComments(body[0], this.node);
}
//
body.push(t.returnStatement(classRef));
return t.callExpression(t.functionExpression(null, closureParams, t.blockStatement(body)), closureArgs);
};
/**
* Description
*/
ClassTransformer.prototype.buildBody = function buildBody() {
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 isConstructor = !node.computed && t.isIdentifier(node.key, { name: "constructor" }) || t.isLiteral(node.key, { value: "constructor" });
if (isConstructor) this.verifyConstructor(node);
var replaceSupers = new ReplaceSupers({
methodNode: node,
objectRef: this.classRef,
superRef: this.superName,
isStatic: node["static"],
isLoose: this.isLoose,
scope: this.scope,
file: this.file
}, true);
replaceSupers.replace();
if (isConstructor) {
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.evaluateTruthy(superName, this.scope) !== false) {
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;
var classHelper = "create-class";
if (this.hasInstanceMutators) {
instanceProps = defineMap.toClassObject(this.instanceMutatorMap);
}
if (this.hasStaticMutators) {
staticProps = defineMap.toClassObject(this.staticMutatorMap);
}
if (instanceProps || staticProps) {
if (defineMap.hasComputed(this.instanceMutatorMap) || defineMap.hasComputed(this.staticMutatorMap)) {
if (instanceProps) instanceProps = defineMap.toComputedObjectFromClass(instanceProps);
if (staticProps) staticProps = defineMap.toComputedObjectFromClass(staticProps);
classHelper = "create-computed-class";
}
if (!instanceProps) instanceProps = t.literal(null);
var args = [this.classRef, instanceProps];
if (staticProps) args.push(staticProps);
body.push(t.expressionStatement(t.callExpression(this.file.addHelper(classHelper), args)));
}
};
/**
* Description
*/
ClassTransformer.prototype.verifyConstructor = function verifyConstructor(node) {
return; // enable this for the next major
var state = {
hasBareSuper: false,
hasSuper: this.hasSuper,
file: this.file
};
traverse(node, verifyConstructorVisitor, this.scope, state);
if (!state.hasBareSuper && this.hasSuper) {
throw this.file.errorWithNode(node, "Derived constructor must call super()");
}
};
/**
* Push a method to its respective mutatorMap.
*/
ClassTransformer.prototype.pushMethod = function pushMethod(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 classRef = this.classRef;
if (!node["static"]) classRef = t.memberExpression(classRef, t.identifier("prototype"));
methodName = t.memberExpression(classRef, 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);
};
/**
* Description
*/
ClassTransformer.prototype.pushProperty = function pushProperty(node) {
if (!node.value) return;
var key;
if (node["static"]) {
key = t.memberExpression(this.classRef, 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.
*/
ClassTransformer.prototype.pushConstructor = function pushConstructor(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;
t.inherits(construct.body, fn.body);
construct.body.body = construct.body.body.concat(fn.body.body);
};
return ClassTransformer;
})();
exports.__esModule = true;