GoScrobble/web/node_modules/@babel/plugin-transform-react-constant-elements/lib/index.js

187 lines
4.7 KiB
JavaScript
Raw Permalink Normal View History

2022-04-25 02:47:15 +00:00
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _helperPluginUtils = require("@babel/helper-plugin-utils");
var _core = require("@babel/core");
var _default = (0, _helperPluginUtils.declare)((api, options) => {
api.assertVersion(7);
const {
allowMutablePropsOnTags
} = options;
if (allowMutablePropsOnTags != null && !Array.isArray(allowMutablePropsOnTags)) {
throw new Error(".allowMutablePropsOnTags must be an array, null, or undefined.");
}
const HOISTED = new WeakMap();
function declares(node, scope) {
if (_core.types.isJSXIdentifier(node, {
name: "this"
}) || _core.types.isJSXIdentifier(node, {
name: "arguments"
}) || _core.types.isJSXIdentifier(node, {
name: "super"
}) || _core.types.isJSXIdentifier(node, {
name: "new"
})) {
const {
path
} = scope;
return path.isFunctionParent() && !path.isArrowFunctionExpression();
}
return scope.hasOwnBinding(node.name);
}
function isHoistingScope({
path
}) {
return path.isFunctionParent() || path.isLoop() || path.isProgram();
}
function getHoistingScope(scope) {
while (!isHoistingScope(scope)) scope = scope.parent;
return scope;
}
const analyzer = {
enter(path, state) {
const stop = () => {
state.isImmutable = false;
path.stop();
};
if (path.isJSXClosingElement()) {
path.skip();
return;
}
if (path.isJSXIdentifier({
name: "ref"
}) && path.parentPath.isJSXAttribute({
name: path.node
})) {
return stop();
}
if (path.isJSXIdentifier() || path.isJSXMemberExpression() || path.isJSXNamespacedName()) {
return;
}
if (path.isIdentifier()) {
const binding = path.scope.getBinding(path.node.name);
if (binding && binding.constant) return;
}
if (!path.isImmutable()) {
if (path.isPure()) {
const expressionResult = path.evaluate();
if (expressionResult.confident) {
const {
value
} = expressionResult;
const isMutable = !state.mutablePropsAllowed && value && typeof value === "object" || typeof value === "function";
if (!isMutable) {
path.skip();
return;
}
} else if (_core.types.isIdentifier(expressionResult.deopt)) {
return;
}
}
stop();
}
},
ReferencedIdentifier(path, state) {
const {
node
} = path;
let {
scope
} = path;
while (scope) {
if (scope === state.targetScope) return;
if (declares(node, scope)) break;
scope = scope.parent;
}
state.targetScope = getHoistingScope(scope);
}
};
return {
name: "transform-react-constant-elements",
visitor: {
JSXElement(path) {
var _jsxScope;
if (HOISTED.has(path.node)) return;
HOISTED.set(path.node, path.scope);
const name = path.node.openingElement.name;
let mutablePropsAllowed = false;
if (allowMutablePropsOnTags != null) {
let lastSegment = name;
while (_core.types.isJSXMemberExpression(lastSegment)) {
lastSegment = lastSegment.property;
}
const elementName = lastSegment.name;
mutablePropsAllowed = allowMutablePropsOnTags.includes(elementName);
}
const state = {
isImmutable: true,
mutablePropsAllowed,
targetScope: path.scope.getProgramParent()
};
path.traverse(analyzer, state);
if (!state.isImmutable) return;
const {
targetScope
} = state;
HOISTED.set(path.node, targetScope);
let jsxScope;
let current = path;
while (!jsxScope && current.parentPath.isJSX()) {
current = current.parentPath;
jsxScope = HOISTED.get(current.node);
}
(_jsxScope = jsxScope) != null ? _jsxScope : jsxScope = getHoistingScope(path.scope);
if (targetScope === jsxScope) return;
const id = path.scope.generateUidBasedOnNode(name);
targetScope.push({
id: _core.types.identifier(id)
});
let replacement = _core.template.expression.ast`
${_core.types.identifier(id)} || (${_core.types.identifier(id)} = ${path.node})
`;
if (path.parentPath.isJSXElement() || path.parentPath.isJSXAttribute()) {
replacement = _core.types.jsxExpressionContainer(replacement);
}
path.replaceWith(replacement);
}
}
};
});
exports.default = _default;