"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"];