86 lines · 2.6 KB · JavaScript
RawDownload
import {  getCallName,  hasObjectProperty,  isConfigOrTooling,  isDeclarationFile,  unwrapExpression,} from "../utils.js"; const readOnlyMutations = new Set([  "probeMcpEndpoint",  "startMcpOAuth",  "probeGoogleDiscovery",  "startGoogleDiscoveryOAuth",  "previewOpenApiSpec",  "startOpenApiOAuth",  "startOAuth",  "resolveSecret",  "detectSource",  "getDomainVerificationLink",]); export default {  meta: {    type: "problem",    docs: {      description: "Require write mutation calls to pass reactivityKeys.",    },  },  create(context) {    if (isConfigOrTooling(context.filename) || isDeclarationFile(context.filename)) return {};     const mutationCalls = new Map();     return {      VariableDeclarator(node) {        const binding = node.id?.type === "Identifier" ? node.id.name : undefined;        if (!binding) return;         const call = unwrapExpression(node.init);        if (call?.type !== "CallExpression" || getCallName(call.callee) !== "useAtomSet") return;        if (!isPromiseMode(call.arguments[1])) return;         const mutationName = getMutationName(call.arguments[0]);        if (!mutationName || readOnlyMutations.has(mutationName)) return;         mutationCalls.set(binding, { mutationName, node });      },      AwaitExpression(node) {        const call = unwrapExpression(node.argument);        if (call?.type !== "CallExpression") return;         const binding = getCallName(call.callee);        const mutation = mutationCalls.get(binding);        if (!mutation) return;        if (hasObjectProperty(call.arguments[0], "reactivityKeys")) return;         context.report({          node: call,          message: `Mutation ${mutation.mutationName} must pass reactivityKeys at the call site. Skill: wrdn-effect-atom-reactivity-keys.`,        });      },    };  },}; function isPromiseMode(options) {  const object = unwrapExpression(options);  if (object?.type !== "ObjectExpression") return false;   return object.properties.some((property) => {    if (property.type === "SpreadElement") return false;    if (property.key?.type !== "Identifier" || property.key.name !== "mode") return false;    const mode = unwrapExpression(property.value);    return (      (mode?.type === "Literal" || mode?.type === "StringLiteral") &&      (mode.value === "promise" || mode.value === "promiseExit")    );  });} function getMutationName(atomExpression) {  const expression = unwrapExpression(atomExpression);  if (expression?.type === "Identifier") return expression.name;  if (expression?.type === "CallExpression") return getCallName(expression.callee);  return undefined;}