1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 | var _helperPluginUtils = require("@babel/helper-plugin-utils");
|
8 | var _core = require("@babel/core");
|
9 | var _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 |
|