UNPKG

eslint-plugin-fsd-lint

Version:

ESLint plugin for enforcing Feature-Sliced Design (FSD) architecture

97 lines (86 loc) 2.62 kB
/** * @fileoverview Disallows direct imports of global state (store). Use hooks or selectors instead. */ import { mergeConfig } from "../utils/config-utils.js"; import { isTestFile, normalizePath } from "../utils/path-utils.js"; export default { meta: { type: "problem", docs: { description: "Disallows direct imports of global state (store). Use hooks like useStore or useSelector instead.", recommended: true, }, messages: { noGlobalStore: "🚨 '{{ storeName }}' cannot be directly imported. Use hooks such as useStore or useSelector instead.", }, schema: [ { type: "object", properties: { forbiddenPaths: { type: "array", items: { type: "string" }, description: "Paths that should not be directly imported (default: ['/app/store', '/shared/store'])", }, allowedPaths: { type: "array", items: { type: "string" }, description: "Paths that are exceptions to the forbidden paths", }, testFilesPatterns: { type: "array", items: { type: "string" }, }, }, additionalProperties: false, }, ], }, create(context) { // Merge user config with default config const options = context.options[0] || {}; const config = mergeConfig(options); // Default forbidden store paths const forbiddenPaths = options.forbiddenPaths || [ "/app/store", "/shared/store", "/store/", "/redux/", "/zustand/", "/mobx/", "/recoil/", ]; // Paths that are exceptions to the rule const allowedPaths = options.allowedPaths || []; return { ImportDeclaration(node) { const filePath = normalizePath(context.filename); const importPath = node.source.value; // Skip test files - tests typically need direct store access if (isTestFile(filePath, config.testFilesPatterns)) { return; } // Skip if import is in the allowed paths list if (allowedPaths.some((path) => importPath.includes(path))) { return; } // Check if import includes any forbidden path const isForbidden = forbiddenPaths.some((path) => importPath.includes(path), ); if (isForbidden) { context.report({ node, messageId: "noGlobalStore", data: { storeName: importPath, }, }); } }, }; }, };