Skip to content

effozen/eslint-plugin-fsd-lint

Repository files navigation

πŸš€ eslint-plugin-fsd-lint πŸš€

npm version npm downloads npm bundle size License

English | ν•œκ΅­μ–΄

It supports ESLint 9+ and the new Flat Config system.

πŸ“– Introduction

eslint-plugin-fsd-lint is an ESLint plugin that enforces best practices for Feature-Sliced Design (FSD) architecture.

It is fully compatible with ESLint 9+ and follows the modern Flat Config format, ensuring seamless integration into modern JavaScript and TypeScript projects.

✨ Why use this plugin?

  • Flat Config support: Fully compatible with ESLint 9+ and the new Flat Config system.
  • Strict FSD compliance: Prevents architectural violations in feature-based project structures.
  • Improves maintainability: Encourages clear module separation and dependency control.
  • Ensures consistent code quality: Standardizes import patterns and best practices.

πŸ” What is Feature-Sliced Design?

Feature-Sliced Design (FSD) is a modern architecture pattern that provides a structured approach to organizing frontend applications.

This plugin enforces key FSD principles such as proper layer separation, import restrictions, and dependency management,
helping developers build scalable and maintainable codebases.


πŸ“¦ Installation

You can install eslint-plugin-fsd-lint via npm or pnpm:

Using npm:

npm install --save-dev eslint-plugin-fsd-lint

Using pnpm:

pnpm add -D eslint-plugin-fsd-lint

Peer Dependencies

This plugin requires ESLint 9+ to work properly.

Make sure you have ESLint installed in your project:

npm install --save-dev eslint

πŸ’‘ Tip: If you're using a monorepo with multiple packages, install eslint-plugin-fsd-lint at the root level to share the configuration across all workspaces.


πŸš€ Usage & Configuration

πŸ”§ Flat Config Setup (eslint.config.mjs)

eslint-plugin-fsd-lint is designed for ESLint 9+ and works seamlessly with the Flat Config system.
To use it in your project, add the following configuration to your eslint.config.mjs:

import fsdPlugin from "eslint-plugin-fsd-lint";

export default [
  {
    plugins: {
      fsd: fsdPlugin,
    },
    rules: {
      "fsd/forbidden-imports": "error",
      "fsd/no-relative-imports": "error",
      "fsd/no-public-api-sidestep": "error",
      "fsd/no-cross-slice-dependency": "error",
      "fsd/no-ui-in-business-logic": "error",
      "fsd/no-global-store-imports": "error",
      "fsd/ordered-imports": "warn"
    },
  },
];

πŸ“Œ Recommended Configurations

For a stricter FSD enforcement, you can extend the default rule set:

import fsdPlugin from "eslint-plugin-fsd-lint";

export default [
  {
    plugins: {
      fsd: fsdPlugin,
    },
    rules: {
      "fsd/forbidden-imports": "error",
      "fsd/no-relative-imports": "error",
      "fsd/no-public-api-sidestep": "error",
      "fsd/no-cross-slice-dependency": "error",
      "fsd/no-ui-in-business-logic": "error",
      "fsd/no-global-store-imports": "error",
      "fsd/ordered-imports": "error"
    },
  },
];

πŸ“‚ Example Project Structure

Here’s how an FSD-compliant project might look:

src/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ providers/
β”‚   β”œβ”€β”€ store.js
β”‚   β”œβ”€β”€ index.js
β”‚
β”œβ”€β”€ processes/
β”‚   β”œβ”€β”€ auth/
β”‚   β”œβ”€β”€ onboarding/
β”‚
β”œβ”€β”€ pages/
β”‚   β”œβ”€β”€ HomePage/
β”‚   β”œβ”€β”€ ProfilePage/
β”‚
β”œβ”€β”€ widgets/
β”‚   β”œβ”€β”€ Navbar/
β”‚   β”œβ”€β”€ Sidebar/
β”‚
β”œβ”€β”€ features/
β”‚   β”œβ”€β”€ login/
β”‚   β”œβ”€β”€ registration/
β”‚
β”œβ”€β”€ entities/
β”‚   β”œβ”€β”€ user/
β”‚   β”œβ”€β”€ post/
β”‚
β”œβ”€β”€ shared/
β”‚   β”œβ”€β”€ ui/
β”‚   β”œβ”€β”€ utils/

πŸ’‘ Tip: The plugin enforces correct layer imports according to FSD principles. For example, a feature can depend on entities and shared, but cannot directly import another feature.


πŸ” Supported Rules

This plugin provides a set of ESLint rules that enforce Feature-Sliced Design (FSD) best practices.
Each rule helps maintain a clear module structure, enforce import constraints, and prevent architectural violations.

Rule Description
fsd/forbidden-imports Prevents imports from higher layers and cross-imports between slices.
fsd/no-relative-imports Enforces alias usage instead of relative imports (../../shared/ui).
fsd/no-public-api-sidestep Prevents direct imports from internal modules, enforcing public API usage.
fsd/no-cross-slice-dependency Disallows direct dependencies between feature slices.
fsd/no-ui-in-business-logic Prevents UI imports inside business logic layers (e.g., entities).
fsd/no-global-store-imports Forbids direct imports of global state (store).
fsd/ordered-imports Enforces import grouping by layer.

πŸ“Œ Rule Details & Examples

1️⃣ fsd/forbidden-imports

Prevents imports from higher layers and cross-imports between slices.

βœ… Allowed: features can import from entities or shared
❌ Not Allowed: features importing directly from app

// ❌ Incorrect (feature importing from app)
import { config } from "../../app/config";

// βœ… Correct (feature importing from entities/shared)
import { getUser } from "../../entities/user";
import { formatCurrency } from "../../shared/utils";

2️⃣ fsd/no-relative-imports

Disallows relative imports and enforces alias usage.
βœ… Allowed: Using project-defined aliases
❌ Not Allowed: Using ../ or ./

// ❌ Incorrect (relative import)
import { Button } from "../shared/ui/Button";

// βœ… Correct (alias import)
import { Button } from "@shared/ui/Button";

3️⃣ fsd/no-public-api-sidestep

Prevents direct imports from internal modules of features, widgets, or entities.
βœ… Allowed: Importing from index.ts (public API)
❌ Not Allowed: Importing a feature’s internal file

// ❌ Incorrect (direct internal import)
import { authSlice } from "../../features/auth/slice.ts";

// βœ… Correct (importing via public API)
import { authSlice } from "../../features/auth";

4️⃣ fsd/no-cross-slice-dependency

Prevents direct dependencies between feature slices.
βœ… Allowed: features should communicate via entities or shared
❌ Not Allowed: Direct imports between different features

// ❌ Incorrect (feature importing from another feature)
import { processPayment } from "../../features/payment";

// βœ… Correct (using entities/shared as an intermediary)
import { PaymentEntity } from "../../entities/payment";

5️⃣ fsd/no-ui-in-business-logic

Prevents UI imports inside business logic layers (e.g., entities).
βœ… Allowed: UI should only be used inside widgets or pages
❌ Not Allowed: entities importing UI components

// ❌ Incorrect (entity importing widget)
import { ProfileCard } from "../../widgets/ProfileCard";

// βœ… Correct (widget using entity data)
import { getUser } from "../../entities/user";

6️⃣ fsd/no-global-store-imports

Forbids direct imports of global state (store).
βœ… Allowed: Using useStore or useSelector
❌ Not Allowed: Direct imports of the store

// ❌ Incorrect (direct import of store)
import { store } from "../../app/store";

// βœ… Correct (using hooks)
import { useStore } from "zustand";
import { useSelector } from "react-redux";

7️⃣ fsd/ordered-imports

Enforces import grouping by layer.
βœ… Allowed: Grouping imports by layer
❌ Not Allowed: Mixed import order

// ❌ Incorrect (random import order)
import { processPayment } from "../features/payment";
import { getUser } from "../entities/user";
import { formatCurrency } from "../shared/utils";
import { loginUser } from "../features/auth";
import { Header } from "../widgets/Header";
import { useStore } from "../app/store";

// βœ… Correct (layered grouping)
import { useStore } from "../app/store";  // App

import { loginUser } from "../features/auth";  // Features
import { processPayment } from "../features/payment";

import { getUser } from "../entities/user";  // Entities

import { formatCurrency } from "../shared/utils";  // Shared

import { Header } from "../widgets/Header";  // Widgets

πŸ’‘ Tip: Use npx eslint --fix to automatically reorder imports according to FSD layers.


πŸ›  Auto-fix Support

Certain rules in eslint-plugin-fsd-lint support automatic fixing using ESLint's --fix option.

This allows developers to quickly resolve violations without manual code adjustments.

βœ… Rules Supporting Auto-fix

The following rules can be automatically fixed:

Rule Description
fsd/ordered-imports Automatically sorts imports based on Feature-Sliced Design (FSD) layers.

πŸ”§ Using --fix in ESLint

To apply automatic fixes to your project, simply run:

npx eslint --fix your-file.js

Or, to fix all files in your project:

npx eslint --fix .

πŸ“Œ Example Before & After Auto-fix

❌ Before (fsd/ordered-imports violation)

import { processPayment } from "../features/payment";
import { getUser } from "../entities/user";
import { formatCurrency } from "../shared/utils";
import { loginUser } from "../features/auth";
import { Header } from "../widgets/Header";
import { useStore } from "../app/store";

βœ… After (npx eslint --fix applied)

import { useStore } from "../app/store";  // App

import { loginUser } from "../features/auth";  // Features
import { processPayment } from "../features/payment";

import { getUser } from "../entities/user";  // Entities

import { formatCurrency } from "../shared/utils";  // Shared

import { Header } from "../widgets/Header";  // Widgets

πŸ’‘ Tip: fsd/ordered-imports ensures a clean and structured import order based on FSD layers.


🀝 Contributing

We welcome contributions to improve eslint-plugin-fsd-lint!

If you have an idea for a new rule or an improvement, feel free to submit a Pull Request.

Check out our contribution guide.


πŸ“ License

This project is licensed under the MIT License.
See the LICENSE file for details.