9c7dc5a9d740b86650933fcd775efc81fbba25b63ef3045c06505467fe2537a8bbb30f7dd206b98ab7c1666efee2f88680919be422c149b2829a50af523025 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. "use strict";
  2. exports.__esModule = true;
  3. var _getIterator2 = require("babel-runtime/core-js/get-iterator");
  4. var _getIterator3 = _interopRequireDefault(_getIterator2);
  5. var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
  6. var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
  7. var _babelTypes = require("babel-types");
  8. var t = _interopRequireWildcard(_babelTypes);
  9. 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; } }
  10. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  11. var referenceVisitor = {
  12. ReferencedIdentifier: function ReferencedIdentifier(path, state) {
  13. if (path.isJSXIdentifier() && _babelTypes.react.isCompatTag(path.node.name) && !path.parentPath.isJSXMemberExpression()) {
  14. return;
  15. }
  16. if (path.node.name === "this") {
  17. var scope = path.scope;
  18. do {
  19. if (scope.path.isFunction() && !scope.path.isArrowFunctionExpression()) break;
  20. } while (scope = scope.parent);
  21. if (scope) state.breakOnScopePaths.push(scope.path);
  22. }
  23. var binding = path.scope.getBinding(path.node.name);
  24. if (!binding) return;
  25. if (binding !== state.scope.getBinding(path.node.name)) return;
  26. state.bindings[path.node.name] = binding;
  27. }
  28. };
  29. var PathHoister = function () {
  30. function PathHoister(path, scope) {
  31. (0, _classCallCheck3.default)(this, PathHoister);
  32. this.breakOnScopePaths = [];
  33. this.bindings = {};
  34. this.scopes = [];
  35. this.scope = scope;
  36. this.path = path;
  37. this.attachAfter = false;
  38. }
  39. PathHoister.prototype.isCompatibleScope = function isCompatibleScope(scope) {
  40. for (var key in this.bindings) {
  41. var binding = this.bindings[key];
  42. if (!scope.bindingIdentifierEquals(key, binding.identifier)) {
  43. return false;
  44. }
  45. }
  46. return true;
  47. };
  48. PathHoister.prototype.getCompatibleScopes = function getCompatibleScopes() {
  49. var scope = this.path.scope;
  50. do {
  51. if (this.isCompatibleScope(scope)) {
  52. this.scopes.push(scope);
  53. } else {
  54. break;
  55. }
  56. if (this.breakOnScopePaths.indexOf(scope.path) >= 0) {
  57. break;
  58. }
  59. } while (scope = scope.parent);
  60. };
  61. PathHoister.prototype.getAttachmentPath = function getAttachmentPath() {
  62. var path = this._getAttachmentPath();
  63. if (!path) return;
  64. var targetScope = path.scope;
  65. if (targetScope.path === path) {
  66. targetScope = path.scope.parent;
  67. }
  68. if (targetScope.path.isProgram() || targetScope.path.isFunction()) {
  69. for (var name in this.bindings) {
  70. if (!targetScope.hasOwnBinding(name)) continue;
  71. var binding = this.bindings[name];
  72. if (binding.kind === "param") continue;
  73. if (this.getAttachmentParentForPath(binding.path).key > path.key) {
  74. this.attachAfter = true;
  75. path = binding.path;
  76. for (var _iterator = binding.constantViolations, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : (0, _getIterator3.default)(_iterator);;) {
  77. var _ref;
  78. if (_isArray) {
  79. if (_i >= _iterator.length) break;
  80. _ref = _iterator[_i++];
  81. } else {
  82. _i = _iterator.next();
  83. if (_i.done) break;
  84. _ref = _i.value;
  85. }
  86. var violationPath = _ref;
  87. if (this.getAttachmentParentForPath(violationPath).key > path.key) {
  88. path = violationPath;
  89. }
  90. }
  91. }
  92. }
  93. }
  94. if (path.parentPath.isExportDeclaration()) {
  95. path = path.parentPath;
  96. }
  97. return path;
  98. };
  99. PathHoister.prototype._getAttachmentPath = function _getAttachmentPath() {
  100. var scopes = this.scopes;
  101. var scope = scopes.pop();
  102. if (!scope) return;
  103. if (scope.path.isFunction()) {
  104. if (this.hasOwnParamBindings(scope)) {
  105. if (this.scope === scope) return;
  106. return scope.path.get("body").get("body")[0];
  107. } else {
  108. return this.getNextScopeAttachmentParent();
  109. }
  110. } else if (scope.path.isProgram()) {
  111. return this.getNextScopeAttachmentParent();
  112. }
  113. };
  114. PathHoister.prototype.getNextScopeAttachmentParent = function getNextScopeAttachmentParent() {
  115. var scope = this.scopes.pop();
  116. if (scope) return this.getAttachmentParentForPath(scope.path);
  117. };
  118. PathHoister.prototype.getAttachmentParentForPath = function getAttachmentParentForPath(path) {
  119. do {
  120. if (!path.parentPath || Array.isArray(path.container) && path.isStatement() || path.isVariableDeclarator() && path.parentPath.node !== null && path.parentPath.node.declarations.length > 1) return path;
  121. } while (path = path.parentPath);
  122. };
  123. PathHoister.prototype.hasOwnParamBindings = function hasOwnParamBindings(scope) {
  124. for (var name in this.bindings) {
  125. if (!scope.hasOwnBinding(name)) continue;
  126. var binding = this.bindings[name];
  127. if (binding.kind === "param" && binding.constant) return true;
  128. }
  129. return false;
  130. };
  131. PathHoister.prototype.run = function run() {
  132. var node = this.path.node;
  133. if (node._hoisted) return;
  134. node._hoisted = true;
  135. this.path.traverse(referenceVisitor, this);
  136. this.getCompatibleScopes();
  137. var attachTo = this.getAttachmentPath();
  138. if (!attachTo) return;
  139. if (attachTo.getFunctionParent() === this.path.getFunctionParent()) return;
  140. var uid = attachTo.scope.generateUidIdentifier("ref");
  141. var declarator = t.variableDeclarator(uid, this.path.node);
  142. var insertFn = this.attachAfter ? "insertAfter" : "insertBefore";
  143. attachTo[insertFn]([attachTo.isVariableDeclarator() ? declarator : t.variableDeclaration("var", [declarator])]);
  144. var parent = this.path.parentPath;
  145. if (parent.isJSXElement() && this.path.container === parent.node.children) {
  146. uid = t.JSXExpressionContainer(uid);
  147. }
  148. this.path.replaceWith(uid);
  149. };
  150. return PathHoister;
  151. }();
  152. exports.default = PathHoister;
  153. module.exports = exports["default"];