index.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _helperPluginUtils = require("@babel/helper-plugin-utils");
  7. var _core = require("@babel/core");
  8. var _default = exports.default = (0, _helperPluginUtils.declare)((api, options) => {
  9. api.assertVersion(7);
  10. const {
  11. allowMutablePropsOnTags
  12. } = options;
  13. if (allowMutablePropsOnTags != null && !Array.isArray(allowMutablePropsOnTags)) {
  14. throw new Error(".allowMutablePropsOnTags must be an array, null, or undefined.");
  15. }
  16. const HOISTED = new WeakMap();
  17. function declares(node, scope) {
  18. if (_core.types.isJSXIdentifier(node, {
  19. name: "this"
  20. }) || _core.types.isJSXIdentifier(node, {
  21. name: "arguments"
  22. }) || _core.types.isJSXIdentifier(node, {
  23. name: "super"
  24. }) || _core.types.isJSXIdentifier(node, {
  25. name: "new"
  26. })) {
  27. const {
  28. path
  29. } = scope;
  30. return path.isFunctionParent() && !path.isArrowFunctionExpression();
  31. }
  32. return scope.hasOwnBinding(node.name);
  33. }
  34. function isHoistingScope({
  35. path
  36. }) {
  37. return path.isFunctionParent() || path.isLoop() || path.isProgram();
  38. }
  39. function getHoistingScope(scope) {
  40. while (!isHoistingScope(scope)) scope = scope.parent;
  41. return scope;
  42. }
  43. const targetScopeVisitor = {
  44. ReferencedIdentifier(path, state) {
  45. const {
  46. node
  47. } = path;
  48. let {
  49. scope
  50. } = path;
  51. while (scope !== state.jsxScope) {
  52. if (declares(node, scope)) return;
  53. scope = scope.parent;
  54. }
  55. while (scope) {
  56. if (scope === state.targetScope) return;
  57. if (declares(node, scope)) break;
  58. scope = scope.parent;
  59. }
  60. state.targetScope = getHoistingScope(scope);
  61. }
  62. };
  63. const immutabilityVisitor = {
  64. enter(path, state) {
  65. var _expressionResult$deo;
  66. const stop = () => {
  67. state.isImmutable = false;
  68. path.stop();
  69. };
  70. const skip = () => {
  71. path.skip();
  72. };
  73. if (path.isJSXClosingElement()) {
  74. skip();
  75. return;
  76. }
  77. if (path.isJSXIdentifier({
  78. name: "ref"
  79. }) && path.parentPath.isJSXAttribute({
  80. name: path.node
  81. })) {
  82. stop();
  83. return;
  84. }
  85. if (path.isJSXIdentifier() || path.isJSXMemberExpression() || path.isJSXNamespacedName() || path.isImmutable()) {
  86. return;
  87. }
  88. if (path.isIdentifier()) {
  89. const binding = path.scope.getBinding(path.node.name);
  90. if (binding && binding.constant) return;
  91. }
  92. const {
  93. mutablePropsAllowed
  94. } = state;
  95. if (mutablePropsAllowed && path.isFunction()) {
  96. path.traverse(targetScopeVisitor, state);
  97. skip();
  98. return;
  99. }
  100. if (!path.isPure()) {
  101. stop();
  102. return;
  103. }
  104. const expressionResult = path.evaluate();
  105. if (expressionResult.confident) {
  106. const {
  107. value
  108. } = expressionResult;
  109. if (mutablePropsAllowed || value === null || typeof value !== "object" && typeof value !== "function") {
  110. skip();
  111. return;
  112. }
  113. } else if ((_expressionResult$deo = expressionResult.deopt) != null && _expressionResult$deo.isIdentifier()) {
  114. return;
  115. }
  116. stop();
  117. }
  118. };
  119. const hoistingVisitor = Object.assign({}, immutabilityVisitor, targetScopeVisitor);
  120. return {
  121. name: "transform-react-constant-elements",
  122. visitor: {
  123. JSXElement(path) {
  124. var _jsxScope;
  125. if (HOISTED.has(path.node)) return;
  126. const name = path.node.openingElement.name;
  127. let mutablePropsAllowed = false;
  128. if (allowMutablePropsOnTags != null) {
  129. let lastSegment = name;
  130. while (_core.types.isJSXMemberExpression(lastSegment)) {
  131. lastSegment = lastSegment.property;
  132. }
  133. const elementName = lastSegment.name;
  134. mutablePropsAllowed = allowMutablePropsOnTags.includes(elementName);
  135. }
  136. let jsxScope;
  137. let current = path;
  138. while (!jsxScope && current.parentPath.isJSX()) {
  139. current = current.parentPath;
  140. jsxScope = HOISTED.get(current.node);
  141. }
  142. (_jsxScope = jsxScope) != null ? _jsxScope : jsxScope = path.scope;
  143. HOISTED.set(path.node, jsxScope);
  144. const visitorState = {
  145. isImmutable: true,
  146. mutablePropsAllowed,
  147. jsxScope,
  148. targetScope: path.scope.getProgramParent()
  149. };
  150. path.traverse(hoistingVisitor, visitorState);
  151. if (!visitorState.isImmutable) return;
  152. const {
  153. targetScope
  154. } = visitorState;
  155. for (let currentScope = jsxScope;;) {
  156. if (targetScope === currentScope) return;
  157. if (isHoistingScope(currentScope)) break;
  158. currentScope = currentScope.parent;
  159. if (!currentScope) {
  160. throw new Error("Internal @babel/plugin-transform-react-constant-elements error: " + "targetScope must be an ancestor of jsxScope. " + "This is a Babel bug, please report it.");
  161. }
  162. }
  163. const id = path.scope.generateUidBasedOnNode(name);
  164. targetScope.push({
  165. id: _core.types.identifier(id)
  166. });
  167. HOISTED.set(path.node, targetScope);
  168. let replacement = _core.template.expression.ast`
  169. ${_core.types.identifier(id)} || (${_core.types.identifier(id)} = ${path.node})
  170. `;
  171. if (path.parentPath.isJSXElement() || path.parentPath.isJSXAttribute()) {
  172. replacement = _core.types.jsxExpressionContainer(replacement);
  173. }
  174. path.replaceWith(replacement);
  175. }
  176. }
  177. };
  178. });
  179. //# sourceMappingURL=index.js.map