1.0.6 β€’ Published 5 months ago

eslint-plugin-fsd-lint v1.0.6

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

πŸš€ 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.
Relative imports are allowed only within the same slice, but must be avoided across different slices or layers.


πŸ” 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.

RuleDescription
fsd/forbidden-importsPrevents imports from higher layers and cross-imports between slices.
fsd/no-relative-importsDisallows relative imports across different slices or layers. Allows relative imports within the same slice.
fsd/no-public-api-sidestepPrevents direct imports from internal modules, enforcing public API usage.
fsd/no-cross-slice-dependencyDisallows direct dependencies between feature slices.
fsd/no-ui-in-business-logicPrevents UI imports inside business logic layers (e.g., entities).
fsd/no-global-store-importsForbids direct imports of global state (store).
fsd/ordered-importsEnforces 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 across different slices)
import { fetchUser } from "../another-slice/model/api";

// βœ… Correct (relative import within the same slice)
import { fetchData } from "../model/api";

// βœ… Correct (alias import across slices or layers)
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:

RuleDescription
fsd/ordered-importsAutomatically 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.

1.0.6

5 months ago

1.0.5

6 months ago

1.0.4

6 months ago

1.0.3

6 months ago

1.0.2

6 months ago

1.0.1

6 months ago

1.0.0

6 months ago