You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
559 lines
17 KiB
559 lines
17 KiB
"use strict"; |
|
|
|
exports.__esModule = true; |
|
|
|
var _getIterator2 = require("babel-runtime/core-js/get-iterator"); |
|
|
|
var _getIterator3 = _interopRequireDefault(_getIterator2); |
|
|
|
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); |
|
|
|
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); |
|
|
|
var _babelTraverse = require("babel-traverse"); |
|
|
|
var _babelHelperReplaceSupers = require("babel-helper-replace-supers"); |
|
|
|
var _babelHelperReplaceSupers2 = _interopRequireDefault(_babelHelperReplaceSupers); |
|
|
|
var _babelHelperOptimiseCallExpression = require("babel-helper-optimise-call-expression"); |
|
|
|
var _babelHelperOptimiseCallExpression2 = _interopRequireDefault(_babelHelperOptimiseCallExpression); |
|
|
|
var _babelHelperDefineMap = require("babel-helper-define-map"); |
|
|
|
var defineMap = _interopRequireWildcard(_babelHelperDefineMap); |
|
|
|
var _babelTemplate = require("babel-template"); |
|
|
|
var _babelTemplate2 = _interopRequireDefault(_babelTemplate); |
|
|
|
var _babelTypes = require("babel-types"); |
|
|
|
var t = _interopRequireWildcard(_babelTypes); |
|
|
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } |
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } |
|
|
|
var buildDerivedConstructor = (0, _babelTemplate2.default)("\n (function () {\n super(...arguments);\n })\n"); |
|
|
|
var noMethodVisitor = { |
|
"FunctionExpression|FunctionDeclaration": function FunctionExpressionFunctionDeclaration(path) { |
|
if (!path.is("shadow")) { |
|
path.skip(); |
|
} |
|
}, |
|
Method: function Method(path) { |
|
path.skip(); |
|
} |
|
}; |
|
|
|
var verifyConstructorVisitor = _babelTraverse.visitors.merge([noMethodVisitor, { |
|
Super: function Super(path) { |
|
if (this.isDerived && !this.hasBareSuper && !path.parentPath.isCallExpression({ callee: path.node })) { |
|
throw path.buildCodeFrameError("'super.*' is not allowed before super()"); |
|
} |
|
}, |
|
|
|
|
|
CallExpression: { |
|
exit: function exit(path) { |
|
if (path.get("callee").isSuper()) { |
|
this.hasBareSuper = true; |
|
|
|
if (!this.isDerived) { |
|
throw path.buildCodeFrameError("super() is only allowed in a derived constructor"); |
|
} |
|
} |
|
} |
|
}, |
|
|
|
ThisExpression: function ThisExpression(path) { |
|
if (this.isDerived && !this.hasBareSuper) { |
|
if (!path.inShadow("this")) { |
|
throw path.buildCodeFrameError("'this' is not allowed before super()"); |
|
} |
|
} |
|
} |
|
}]); |
|
|
|
var findThisesVisitor = _babelTraverse.visitors.merge([noMethodVisitor, { |
|
ThisExpression: function ThisExpression(path) { |
|
this.superThises.push(path); |
|
} |
|
}]); |
|
|
|
var ClassTransformer = function () { |
|
function ClassTransformer(path, file) { |
|
(0, _classCallCheck3.default)(this, ClassTransformer); |
|
|
|
this.parent = path.parent; |
|
this.scope = path.scope; |
|
this.node = path.node; |
|
this.path = path; |
|
this.file = file; |
|
|
|
this.clearDescriptors(); |
|
|
|
this.instancePropBody = []; |
|
this.instancePropRefs = {}; |
|
this.staticPropBody = []; |
|
this.body = []; |
|
|
|
this.bareSuperAfter = []; |
|
this.bareSupers = []; |
|
|
|
this.pushedConstructor = false; |
|
this.pushedInherits = false; |
|
this.isLoose = false; |
|
|
|
this.superThises = []; |
|
|
|
this.classId = this.node.id; |
|
|
|
this.classRef = this.node.id ? t.identifier(this.node.id.name) : this.scope.generateUidIdentifier("class"); |
|
|
|
this.superName = this.node.superClass || t.identifier("Function"); |
|
this.isDerived = !!this.node.superClass; |
|
} |
|
|
|
ClassTransformer.prototype.run = function run() { |
|
var _this = this; |
|
|
|
var superName = this.superName; |
|
var file = this.file; |
|
var body = this.body; |
|
|
|
var constructorBody = this.constructorBody = t.blockStatement([]); |
|
this.constructor = this.buildConstructor(); |
|
|
|
var closureParams = []; |
|
var closureArgs = []; |
|
|
|
if (this.isDerived) { |
|
closureArgs.push(superName); |
|
|
|
superName = this.scope.generateUidIdentifierBasedOnNode(superName); |
|
closureParams.push(superName); |
|
|
|
this.superName = superName; |
|
} |
|
|
|
this.buildBody(); |
|
|
|
constructorBody.body.unshift(t.expressionStatement(t.callExpression(file.addHelper("classCallCheck"), [t.thisExpression(), this.classRef]))); |
|
|
|
body = body.concat(this.staticPropBody.map(function (fn) { |
|
return fn(_this.classRef); |
|
})); |
|
|
|
if (this.classId) { |
|
if (body.length === 1) return t.toExpression(body[0]); |
|
} |
|
|
|
body.push(t.returnStatement(this.classRef)); |
|
|
|
var container = t.functionExpression(null, closureParams, t.blockStatement(body)); |
|
container.shadow = true; |
|
return t.callExpression(container, closureArgs); |
|
}; |
|
|
|
ClassTransformer.prototype.buildConstructor = function buildConstructor() { |
|
var func = t.functionDeclaration(this.classRef, [], this.constructorBody); |
|
t.inherits(func, this.node); |
|
return func; |
|
}; |
|
|
|
ClassTransformer.prototype.pushToMap = function pushToMap(node, enumerable) { |
|
var kind = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "value"; |
|
var scope = arguments[3]; |
|
|
|
var mutatorMap = void 0; |
|
if (node.static) { |
|
this.hasStaticDescriptors = true; |
|
mutatorMap = this.staticMutatorMap; |
|
} else { |
|
this.hasInstanceDescriptors = true; |
|
mutatorMap = this.instanceMutatorMap; |
|
} |
|
|
|
var map = defineMap.push(mutatorMap, node, kind, this.file, scope); |
|
|
|
if (enumerable) { |
|
map.enumerable = t.booleanLiteral(true); |
|
} |
|
|
|
return map; |
|
}; |
|
|
|
ClassTransformer.prototype.constructorMeMaybe = function constructorMeMaybe() { |
|
var hasConstructor = false; |
|
var paths = this.path.get("body.body"); |
|
for (var _iterator = paths, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : (0, _getIterator3.default)(_iterator);;) { |
|
var _ref; |
|
|
|
if (_isArray) { |
|
if (_i >= _iterator.length) break; |
|
_ref = _iterator[_i++]; |
|
} else { |
|
_i = _iterator.next(); |
|
if (_i.done) break; |
|
_ref = _i.value; |
|
} |
|
|
|
var path = _ref; |
|
|
|
hasConstructor = path.equals("kind", "constructor"); |
|
if (hasConstructor) break; |
|
} |
|
if (hasConstructor) return; |
|
|
|
var params = void 0, |
|
body = void 0; |
|
|
|
if (this.isDerived) { |
|
var _constructor = buildDerivedConstructor().expression; |
|
params = _constructor.params; |
|
body = _constructor.body; |
|
} else { |
|
params = []; |
|
body = t.blockStatement([]); |
|
} |
|
|
|
this.path.get("body").unshiftContainer("body", t.classMethod("constructor", t.identifier("constructor"), params, body)); |
|
}; |
|
|
|
ClassTransformer.prototype.buildBody = function buildBody() { |
|
this.constructorMeMaybe(); |
|
this.pushBody(); |
|
this.verifyConstructor(); |
|
|
|
if (this.userConstructor) { |
|
var constructorBody = this.constructorBody; |
|
constructorBody.body = constructorBody.body.concat(this.userConstructor.body.body); |
|
t.inherits(this.constructor, this.userConstructor); |
|
t.inherits(constructorBody, this.userConstructor.body); |
|
} |
|
|
|
this.pushDescriptors(); |
|
}; |
|
|
|
ClassTransformer.prototype.pushBody = function pushBody() { |
|
var classBodyPaths = this.path.get("body.body"); |
|
|
|
for (var _iterator2 = classBodyPaths, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : (0, _getIterator3.default)(_iterator2);;) { |
|
var _ref2; |
|
|
|
if (_isArray2) { |
|
if (_i2 >= _iterator2.length) break; |
|
_ref2 = _iterator2[_i2++]; |
|
} else { |
|
_i2 = _iterator2.next(); |
|
if (_i2.done) break; |
|
_ref2 = _i2.value; |
|
} |
|
|
|
var path = _ref2; |
|
|
|
var node = path.node; |
|
|
|
if (path.isClassProperty()) { |
|
throw path.buildCodeFrameError("Missing class properties transform."); |
|
} |
|
|
|
if (node.decorators) { |
|
throw path.buildCodeFrameError("Method has decorators, put the decorator plugin before the classes one."); |
|
} |
|
|
|
if (t.isClassMethod(node)) { |
|
var isConstructor = node.kind === "constructor"; |
|
|
|
if (isConstructor) { |
|
path.traverse(verifyConstructorVisitor, this); |
|
|
|
if (!this.hasBareSuper && this.isDerived) { |
|
throw path.buildCodeFrameError("missing super() call in constructor"); |
|
} |
|
} |
|
|
|
var replaceSupers = new _babelHelperReplaceSupers2.default({ |
|
forceSuperMemoisation: isConstructor, |
|
methodPath: path, |
|
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(replaceSupers, node, path); |
|
} else { |
|
this.pushMethod(node, path); |
|
} |
|
} |
|
} |
|
}; |
|
|
|
ClassTransformer.prototype.clearDescriptors = function clearDescriptors() { |
|
this.hasInstanceDescriptors = false; |
|
this.hasStaticDescriptors = false; |
|
|
|
this.instanceMutatorMap = {}; |
|
this.staticMutatorMap = {}; |
|
}; |
|
|
|
ClassTransformer.prototype.pushDescriptors = function pushDescriptors() { |
|
this.pushInherits(); |
|
|
|
var body = this.body; |
|
|
|
var instanceProps = void 0; |
|
var staticProps = void 0; |
|
|
|
if (this.hasInstanceDescriptors) { |
|
instanceProps = defineMap.toClassObject(this.instanceMutatorMap); |
|
} |
|
|
|
if (this.hasStaticDescriptors) { |
|
staticProps = defineMap.toClassObject(this.staticMutatorMap); |
|
} |
|
|
|
if (instanceProps || staticProps) { |
|
if (instanceProps) instanceProps = defineMap.toComputedObjectFromClass(instanceProps); |
|
if (staticProps) staticProps = defineMap.toComputedObjectFromClass(staticProps); |
|
|
|
var nullNode = t.nullLiteral(); |
|
|
|
var args = [this.classRef, nullNode, nullNode, nullNode, nullNode]; |
|
|
|
if (instanceProps) args[1] = instanceProps; |
|
if (staticProps) args[2] = staticProps; |
|
|
|
if (this.instanceInitializersId) { |
|
args[3] = this.instanceInitializersId; |
|
body.unshift(this.buildObjectAssignment(this.instanceInitializersId)); |
|
} |
|
|
|
if (this.staticInitializersId) { |
|
args[4] = this.staticInitializersId; |
|
body.unshift(this.buildObjectAssignment(this.staticInitializersId)); |
|
} |
|
|
|
var lastNonNullIndex = 0; |
|
for (var i = 0; i < args.length; i++) { |
|
if (args[i] !== nullNode) lastNonNullIndex = i; |
|
} |
|
args = args.slice(0, lastNonNullIndex + 1); |
|
|
|
body.push(t.expressionStatement(t.callExpression(this.file.addHelper("createClass"), args))); |
|
} |
|
|
|
this.clearDescriptors(); |
|
}; |
|
|
|
ClassTransformer.prototype.buildObjectAssignment = function buildObjectAssignment(id) { |
|
return t.variableDeclaration("var", [t.variableDeclarator(id, t.objectExpression([]))]); |
|
}; |
|
|
|
ClassTransformer.prototype.wrapSuperCall = function wrapSuperCall(bareSuper, superRef, thisRef, body) { |
|
var bareSuperNode = bareSuper.node; |
|
|
|
if (this.isLoose) { |
|
bareSuperNode.arguments.unshift(t.thisExpression()); |
|
if (bareSuperNode.arguments.length === 2 && t.isSpreadElement(bareSuperNode.arguments[1]) && t.isIdentifier(bareSuperNode.arguments[1].argument, { name: "arguments" })) { |
|
bareSuperNode.arguments[1] = bareSuperNode.arguments[1].argument; |
|
bareSuperNode.callee = t.memberExpression(superRef, t.identifier("apply")); |
|
} else { |
|
bareSuperNode.callee = t.memberExpression(superRef, t.identifier("call")); |
|
} |
|
} else { |
|
bareSuperNode = (0, _babelHelperOptimiseCallExpression2.default)(t.logicalExpression("||", t.memberExpression(this.classRef, t.identifier("__proto__")), t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")), [this.classRef])), t.thisExpression(), bareSuperNode.arguments); |
|
} |
|
|
|
var call = t.callExpression(this.file.addHelper("possibleConstructorReturn"), [t.thisExpression(), bareSuperNode]); |
|
|
|
var bareSuperAfter = this.bareSuperAfter.map(function (fn) { |
|
return fn(thisRef); |
|
}); |
|
|
|
if (bareSuper.parentPath.isExpressionStatement() && bareSuper.parentPath.container === body.node.body && body.node.body.length - 1 === bareSuper.parentPath.key) { |
|
|
|
if (this.superThises.length || bareSuperAfter.length) { |
|
bareSuper.scope.push({ id: thisRef }); |
|
call = t.assignmentExpression("=", thisRef, call); |
|
} |
|
|
|
if (bareSuperAfter.length) { |
|
call = t.toSequenceExpression([call].concat(bareSuperAfter, [thisRef])); |
|
} |
|
|
|
bareSuper.parentPath.replaceWith(t.returnStatement(call)); |
|
} else { |
|
bareSuper.replaceWithMultiple([t.variableDeclaration("var", [t.variableDeclarator(thisRef, call)])].concat(bareSuperAfter, [t.expressionStatement(thisRef)])); |
|
} |
|
}; |
|
|
|
ClassTransformer.prototype.verifyConstructor = function verifyConstructor() { |
|
var _this2 = this; |
|
|
|
if (!this.isDerived) return; |
|
|
|
var path = this.userConstructorPath; |
|
var body = path.get("body"); |
|
|
|
path.traverse(findThisesVisitor, this); |
|
|
|
var guaranteedSuperBeforeFinish = !!this.bareSupers.length; |
|
|
|
var superRef = this.superName || t.identifier("Function"); |
|
var thisRef = path.scope.generateUidIdentifier("this"); |
|
|
|
for (var _iterator3 = this.bareSupers, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : (0, _getIterator3.default)(_iterator3);;) { |
|
var _ref3; |
|
|
|
if (_isArray3) { |
|
if (_i3 >= _iterator3.length) break; |
|
_ref3 = _iterator3[_i3++]; |
|
} else { |
|
_i3 = _iterator3.next(); |
|
if (_i3.done) break; |
|
_ref3 = _i3.value; |
|
} |
|
|
|
var bareSuper = _ref3; |
|
|
|
this.wrapSuperCall(bareSuper, superRef, thisRef, body); |
|
|
|
if (guaranteedSuperBeforeFinish) { |
|
bareSuper.find(function (parentPath) { |
|
if (parentPath === path) { |
|
return true; |
|
} |
|
|
|
if (parentPath.isLoop() || parentPath.isConditional()) { |
|
guaranteedSuperBeforeFinish = false; |
|
return true; |
|
} |
|
}); |
|
} |
|
} |
|
|
|
for (var _iterator4 = this.superThises, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : (0, _getIterator3.default)(_iterator4);;) { |
|
var _ref4; |
|
|
|
if (_isArray4) { |
|
if (_i4 >= _iterator4.length) break; |
|
_ref4 = _iterator4[_i4++]; |
|
} else { |
|
_i4 = _iterator4.next(); |
|
if (_i4.done) break; |
|
_ref4 = _i4.value; |
|
} |
|
|
|
var thisPath = _ref4; |
|
|
|
thisPath.replaceWith(thisRef); |
|
} |
|
|
|
var wrapReturn = function wrapReturn(returnArg) { |
|
return t.callExpression(_this2.file.addHelper("possibleConstructorReturn"), [thisRef].concat(returnArg || [])); |
|
}; |
|
|
|
var bodyPaths = body.get("body"); |
|
if (bodyPaths.length && !bodyPaths.pop().isReturnStatement()) { |
|
body.pushContainer("body", t.returnStatement(guaranteedSuperBeforeFinish ? thisRef : wrapReturn())); |
|
} |
|
|
|
for (var _iterator5 = this.superReturns, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : (0, _getIterator3.default)(_iterator5);;) { |
|
var _ref5; |
|
|
|
if (_isArray5) { |
|
if (_i5 >= _iterator5.length) break; |
|
_ref5 = _iterator5[_i5++]; |
|
} else { |
|
_i5 = _iterator5.next(); |
|
if (_i5.done) break; |
|
_ref5 = _i5.value; |
|
} |
|
|
|
var returnPath = _ref5; |
|
|
|
if (returnPath.node.argument) { |
|
var ref = returnPath.scope.generateDeclaredUidIdentifier("ret"); |
|
returnPath.get("argument").replaceWithMultiple([t.assignmentExpression("=", ref, returnPath.node.argument), wrapReturn(ref)]); |
|
} else { |
|
returnPath.get("argument").replaceWith(wrapReturn()); |
|
} |
|
} |
|
}; |
|
|
|
ClassTransformer.prototype.pushMethod = function pushMethod(node, path) { |
|
var scope = path ? path.scope : this.scope; |
|
|
|
if (node.kind === "method") { |
|
if (this._processMethod(node, scope)) return; |
|
} |
|
|
|
this.pushToMap(node, false, null, scope); |
|
}; |
|
|
|
ClassTransformer.prototype._processMethod = function _processMethod() { |
|
return false; |
|
}; |
|
|
|
ClassTransformer.prototype.pushConstructor = function pushConstructor(replaceSupers, method, path) { |
|
this.bareSupers = replaceSupers.bareSupers; |
|
this.superReturns = replaceSupers.returns; |
|
|
|
if (path.scope.hasOwnBinding(this.classRef.name)) { |
|
path.scope.rename(this.classRef.name); |
|
} |
|
|
|
var construct = this.constructor; |
|
|
|
this.userConstructorPath = path; |
|
this.userConstructor = method; |
|
this.hasConstructor = true; |
|
|
|
t.inheritsComments(construct, method); |
|
|
|
construct._ignoreUserWhitespace = true; |
|
construct.params = method.params; |
|
|
|
t.inherits(construct.body, method.body); |
|
construct.body.directives = method.body.directives; |
|
|
|
this._pushConstructor(); |
|
}; |
|
|
|
ClassTransformer.prototype._pushConstructor = function _pushConstructor() { |
|
if (this.pushedConstructor) return; |
|
this.pushedConstructor = true; |
|
|
|
if (this.hasInstanceDescriptors || this.hasStaticDescriptors) { |
|
this.pushDescriptors(); |
|
} |
|
|
|
this.body.push(this.constructor); |
|
|
|
this.pushInherits(); |
|
}; |
|
|
|
ClassTransformer.prototype.pushInherits = function pushInherits() { |
|
if (!this.isDerived || this.pushedInherits) return; |
|
|
|
this.pushedInherits = true; |
|
this.body.unshift(t.expressionStatement(t.callExpression(this.file.addHelper("inherits"), [this.classRef, this.superName]))); |
|
}; |
|
|
|
return ClassTransformer; |
|
}(); |
|
|
|
exports.default = ClassTransformer; |
|
module.exports = exports["default"]; |