You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.1 KiB
JavaScript
211 lines
6.1 KiB
JavaScript
"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 _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 referenceVisitor = {
|
|
ReferencedIdentifier: function ReferencedIdentifier(path, state) {
|
|
if (path.isJSXIdentifier() && _babelTypes.react.isCompatTag(path.node.name) && !path.parentPath.isJSXMemberExpression()) {
|
|
return;
|
|
}
|
|
|
|
if (path.node.name === "this") {
|
|
var scope = path.scope;
|
|
do {
|
|
if (scope.path.isFunction() && !scope.path.isArrowFunctionExpression()) break;
|
|
} while (scope = scope.parent);
|
|
if (scope) state.breakOnScopePaths.push(scope.path);
|
|
}
|
|
|
|
var binding = path.scope.getBinding(path.node.name);
|
|
if (!binding) return;
|
|
|
|
if (binding !== state.scope.getBinding(path.node.name)) return;
|
|
|
|
state.bindings[path.node.name] = binding;
|
|
}
|
|
};
|
|
|
|
var PathHoister = function () {
|
|
function PathHoister(path, scope) {
|
|
(0, _classCallCheck3.default)(this, PathHoister);
|
|
|
|
this.breakOnScopePaths = [];
|
|
|
|
this.bindings = {};
|
|
|
|
this.scopes = [];
|
|
|
|
this.scope = scope;
|
|
this.path = path;
|
|
|
|
this.attachAfter = false;
|
|
}
|
|
|
|
PathHoister.prototype.isCompatibleScope = function isCompatibleScope(scope) {
|
|
for (var key in this.bindings) {
|
|
var binding = this.bindings[key];
|
|
if (!scope.bindingIdentifierEquals(key, binding.identifier)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
PathHoister.prototype.getCompatibleScopes = function getCompatibleScopes() {
|
|
var scope = this.path.scope;
|
|
do {
|
|
if (this.isCompatibleScope(scope)) {
|
|
this.scopes.push(scope);
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (this.breakOnScopePaths.indexOf(scope.path) >= 0) {
|
|
break;
|
|
}
|
|
} while (scope = scope.parent);
|
|
};
|
|
|
|
PathHoister.prototype.getAttachmentPath = function getAttachmentPath() {
|
|
var path = this._getAttachmentPath();
|
|
if (!path) return;
|
|
|
|
var targetScope = path.scope;
|
|
|
|
if (targetScope.path === path) {
|
|
targetScope = path.scope.parent;
|
|
}
|
|
|
|
if (targetScope.path.isProgram() || targetScope.path.isFunction()) {
|
|
for (var name in this.bindings) {
|
|
if (!targetScope.hasOwnBinding(name)) continue;
|
|
|
|
var binding = this.bindings[name];
|
|
|
|
if (binding.kind === "param") continue;
|
|
|
|
if (this.getAttachmentParentForPath(binding.path).key > path.key) {
|
|
this.attachAfter = true;
|
|
path = binding.path;
|
|
|
|
for (var _iterator = binding.constantViolations, _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 violationPath = _ref;
|
|
|
|
if (this.getAttachmentParentForPath(violationPath).key > path.key) {
|
|
path = violationPath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (path.parentPath.isExportDeclaration()) {
|
|
path = path.parentPath;
|
|
}
|
|
|
|
return path;
|
|
};
|
|
|
|
PathHoister.prototype._getAttachmentPath = function _getAttachmentPath() {
|
|
var scopes = this.scopes;
|
|
|
|
var scope = scopes.pop();
|
|
|
|
if (!scope) return;
|
|
|
|
if (scope.path.isFunction()) {
|
|
if (this.hasOwnParamBindings(scope)) {
|
|
if (this.scope === scope) return;
|
|
|
|
return scope.path.get("body").get("body")[0];
|
|
} else {
|
|
return this.getNextScopeAttachmentParent();
|
|
}
|
|
} else if (scope.path.isProgram()) {
|
|
return this.getNextScopeAttachmentParent();
|
|
}
|
|
};
|
|
|
|
PathHoister.prototype.getNextScopeAttachmentParent = function getNextScopeAttachmentParent() {
|
|
var scope = this.scopes.pop();
|
|
if (scope) return this.getAttachmentParentForPath(scope.path);
|
|
};
|
|
|
|
PathHoister.prototype.getAttachmentParentForPath = function getAttachmentParentForPath(path) {
|
|
do {
|
|
if (!path.parentPath || Array.isArray(path.container) && path.isStatement() || path.isVariableDeclarator() && path.parentPath.node !== null && path.parentPath.node.declarations.length > 1) return path;
|
|
} while (path = path.parentPath);
|
|
};
|
|
|
|
PathHoister.prototype.hasOwnParamBindings = function hasOwnParamBindings(scope) {
|
|
for (var name in this.bindings) {
|
|
if (!scope.hasOwnBinding(name)) continue;
|
|
|
|
var binding = this.bindings[name];
|
|
|
|
if (binding.kind === "param" && binding.constant) return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
PathHoister.prototype.run = function run() {
|
|
var node = this.path.node;
|
|
if (node._hoisted) return;
|
|
node._hoisted = true;
|
|
|
|
this.path.traverse(referenceVisitor, this);
|
|
|
|
this.getCompatibleScopes();
|
|
|
|
var attachTo = this.getAttachmentPath();
|
|
if (!attachTo) return;
|
|
|
|
if (attachTo.getFunctionParent() === this.path.getFunctionParent()) return;
|
|
|
|
var uid = attachTo.scope.generateUidIdentifier("ref");
|
|
var declarator = t.variableDeclarator(uid, this.path.node);
|
|
|
|
var insertFn = this.attachAfter ? "insertAfter" : "insertBefore";
|
|
attachTo[insertFn]([attachTo.isVariableDeclarator() ? declarator : t.variableDeclaration("var", [declarator])]);
|
|
|
|
var parent = this.path.parentPath;
|
|
if (parent.isJSXElement() && this.path.container === parent.node.children) {
|
|
uid = t.JSXExpressionContainer(uid);
|
|
}
|
|
|
|
this.path.replaceWith(uid);
|
|
};
|
|
|
|
return PathHoister;
|
|
}();
|
|
|
|
exports.default = PathHoister;
|
|
module.exports = exports["default"]; |