UNPKG

eslint-plugin-effector

Version:

Enforcing best practices for Effector

82 lines (70 loc) 2.09 kB
const { ESLintUtils } = require("@typescript-eslint/utils"); const { createLinkToRule } = require("../../utils/create-link-to-rule"); const { isInsideReactComponent } = require("../../utils/react"); const { nodeTypeIs } = require("../../utils/node-type-is"); const { traverseParentByType } = require("../../utils/traverse-parent-by-type"); const { nodeIsType } = require("../../utils/node-is-type"); module.exports = { meta: { type: "problem", docs: { description: "Forbids `Event` and `Effect` usage without `useUnit` in React components.", category: "Quality", recommended: true, url: createLinkToRule("mandatory-scope-binding"), }, messages: { useEventNeeded: "{{ unitName }} must be wrapped with `useUnit` from `effector-react` before usage inside React components", }, schema: [], }, create(context) { let parserServices; try { parserServices = ESLintUtils.getParserServices(context); } catch (err) { // no types information } // TypeScript-only rule, since units can be imported from anywhere if (!parserServices?.program) { return {}; } return { Identifier(node) { if (!isInsideReactComponent(node)) { return; } if (nodeIsType({ node })) { return; } if ( nodeTypeIs.not.effect({ node, context }) && nodeTypeIs.not.event({ node, context }) ) { return; } if (isInsideEffectorHook({ node, context })) { return; } context.report({ node, messageId: "useEventNeeded", data: { unitName: node.name, }, }); }, }; }, }; function isInsideEffectorHook({ node, context }) { const calleeParentNode = traverseParentByType(node.parent, "CallExpression"); if (!calleeParentNode?.callee) return false; return nodeTypeIs.effectorReactHook({ node: calleeParentNode.callee, context, hook: ["useEvent", "useUnit", "useStore"], }); }