mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-25 09:55:15 +00:00
187 lines
4.7 KiB
JavaScript
187 lines
4.7 KiB
JavaScript
"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; |