66 lines
2.0 KiB
JavaScript

import { isTransparentExprWrapper } from "@babel/helper-skip-transparent-expression-wrappers";
/**
* Test if a NodePath will be cast to boolean when evaluated.
* It respects transparent expression wrappers defined in
* "@babel/helper-skip-transparent-expression-wrappers"
*
* @example
* // returns true
* const nodePathADotB = NodePath("if (a.b) {}").get("test"); // a.b
* willPathCastToBoolean(nodePathADotB)
* @example
* // returns false
* willPathCastToBoolean(NodePath("a.b"))
* @param {NodePath} path
* @returns {boolean}
*/
export function willPathCastToBoolean(path: NodePath): boolean {
const maybeWrapped = findOutermostTransparentParent(path);
const { node, parentPath } = maybeWrapped;
if (parentPath.isLogicalExpression()) {
const { operator, right } = parentPath.node;
if (
operator === "&&" ||
operator === "||" ||
(operator === "??" && node === right)
) {
return willPathCastToBoolean(parentPath);
}
}
if (parentPath.isSequenceExpression()) {
const { expressions } = parentPath.node;
if (expressions[expressions.length - 1] === node) {
return willPathCastToBoolean(parentPath);
} else {
// if it is in the middle of a sequence expression, we don't
// care the return value so just cast to boolean for smaller
// output
return true;
}
}
return (
parentPath.isConditional({ test: node }) ||
parentPath.isUnaryExpression({ operator: "!" }) ||
parentPath.isLoop({ test: node })
);
}
/**
* Return the outermost transparent expression wrapper of a given path,
* otherwise returns path itself.
* @example
* const nodePathADotB = NodePath("(a.b as any)").get("expression"); // a.b
* // returns NodePath("(a.b as any)")
* findOutermostTransparentParent(nodePathADotB);
* @param {NodePath} path
* @returns {NodePath}
*/
export function findOutermostTransparentParent(path: NodePath): NodePath {
let maybeWrapped = path;
path.findParent(p => {
if (!isTransparentExprWrapper(p)) return true;
maybeWrapped = p;
});
return maybeWrapped;
}