89 lines · 2.8 KB · JavaScript
RawDownload
import { getPropertyName, isIdentifier, unwrapExpression } from "../utils.js"; const message =  "Use Predicate.isNotNull/isNotUndefined/isNotNullish from effect instead of hand-rolled nullish predicates."; const nullishOperators = new Set(["!==", "!=", "===", "=="]); const isNullLiteral = (node) => node?.type === "Literal" && node.value === null;const isUndefinedIdentifier = (node) => isIdentifier(node, "undefined");const isNullishLiteral = (node) => isNullLiteral(node) || isUndefinedIdentifier(node); const isNullishComparison = (node, identifierName) => {  const expression = unwrapExpression(node);  if (expression?.type !== "BinaryExpression" || !nullishOperators.has(expression.operator)) {    return false;  }   const left = unwrapExpression(expression.left);  const right = unwrapExpression(expression.right);  return (    (isIdentifier(left, identifierName) && isNullishLiteral(right)) ||    (isIdentifier(right, identifierName) && isNullishLiteral(left))  );}; const getSingleIdentifierParamName = (params) => {  if (params.length !== 1) return undefined;  const param = unwrapExpression(params[0]);  return param?.type === "Identifier" ? param.name : undefined;}; const isNullishPredicateFunction = (node) => {  const name = getSingleIdentifierParamName(node.params ?? []);  return name !== undefined && isNullishComparison(node.body, name);}; const isFilterCall = (node) => {  const callee = unwrapExpression(node.callee);  return callee?.type === "MemberExpression" && getPropertyName(callee.property) === "filter";}; const importsEffect = (node) => {  const source = node.source;  return source?.type === "Literal" && source.value === "effect";}; export default {  meta: {    type: "problem",    docs: {      description: message,    },  },  create(context) {    let hasEffectImport = false;     return {      ImportDeclaration(node) {        if (importsEffect(node)) {          hasEffectImport = true;        }      },      VariableDeclarator(node) {        if (!hasEffectImport) return;        const init = unwrapExpression(node.init);        if (init?.type === "ArrowFunctionExpression" && isNullishPredicateFunction(init)) {          context.report({ node: init, message });        }      },      FunctionDeclaration(node) {        if (!hasEffectImport) return;        if (isNullishPredicateFunction(node)) {          context.report({ node, message });        }      },      CallExpression(node) {        if (!hasEffectImport || !isFilterCall(node)) return;        const predicate = unwrapExpression(node.arguments[0]);        if (          (predicate?.type === "ArrowFunctionExpression" ||            predicate?.type === "FunctionExpression") &&          isNullishPredicateFunction(predicate)        ) {          context.report({ node: predicate, message });        }      },    };  },};