UNPKG

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