npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eslint-plugin-fsd-lint

v1.0.10

Published

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

Readme

🚀 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.
  • Cross-platform compatibility: Works seamlessly on both Windows and Unix-based systems.
  • Flexible folder naming: Supports custom folder naming patterns (e.g., 1_app, 2_pages).
  • Multiple alias formats: Supports both @shared and @/shared import styles.
  • Comprehensive test coverage: Thoroughly tested with real-world scenarios and edge cases.

🔍 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 [
  // Use the recommended preset
  fsdPlugin.configs.recommended,

  // Or configure rules individually
  {
    plugins: {
      fsd: fsdPlugin,
    },
    rules: {
      // Enforces FSD layer import rules (e.g., features cannot import pages)
      'fsd/forbidden-imports': 'error',

      // Disallows relative imports between slices/layers, use aliases (@)
      // Allows relative imports within the same slice by default (configurable)
      'fsd/no-relative-imports': 'error',

      // Enforces importing only via public API (index files)
      'fsd/no-public-api-sidestep': 'error',

      // Prevents direct imports between slices in the same layer
      'fsd/no-cross-slice-dependency': 'error',

      // Prevents UI imports in business logic layers (e.g., entities)
      'fsd/no-ui-in-business-logic': 'error',

      // Forbids direct import of the global store
      'fsd/no-global-store-imports': 'error',

      // Enforces import order based on FSD layers
      'fsd/ordered-imports': 'warn',
    },
  },
];

Key Principles Enforced by Default:

  • Layered Imports: Higher layers cannot import from lower layers (e.g., features cannot import pages).
  • Slice Isolation: Slices within the same layer should not directly import each other (no-cross-slice-dependency).
  • Public API Usage: Imports from other slices/layers must go through their public API (index.js or index.ts). Direct internal imports are forbidden (no-public-api-sidestep).
  • Absolute Paths (Aliases): Use absolute paths (configured via aliases like @) for imports between different slices or layers. Relative paths are generally disallowed (no-relative-imports).
  • Relative Paths within Slices: Relative paths are allowed for imports within the same slice (e.g., importing a helper from ../lib inside a feature). This is configurable via the allowSameSlice option in no-relative-imports (default: true).

📌 Available Configurations

The plugin provides three pre-defined configurations for different strictness levels:

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

export default [
  // Standard recommended configuration
  fsdPlugin.configs.recommended,

  // Strict configuration (all rules as error)
  // fsdPlugin.configs.strict,

  // Base configuration (less strict)
  // fsdPlugin.configs.base,
];

🛠️ Advanced Configuration

You can customize the behavior of the rules with advanced options:

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

export default [
  {
    plugins: {
      fsd: fsdPlugin,
    },
    rules: {
      // Configure alias format and folder patterns
      'fsd/forbidden-imports': [
        'error',
        {
          // Support for @shared or @/shared import styles
          alias: {
            value: '@',
            withSlash: false, // Use true for @/shared format
          },
          // Support for numbered folder prefixes
          folderPattern: {
            enabled: true,
            regex: '^(\\d+_)?(.*)',
            extractionGroup: 2,
          },
        },
      ],

      // Other rules...
    },
  },
];

📂 Example Project Structure

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

src/
├── app/         (or 1_app/)
│   ├── providers/
│   ├── store.js
│   ├── index.js  // Public API for app
│
├── processes/   (or 2_processes/)
│   ├── auth/
│   ├── onboarding/
│
├── pages/       (or 3_pages/)
│   ├── HomePage/
│   │   ├── ui/     // UI components for HomePage
│   │   └── index.ts  // Public API for HomePage
│   ├── ProfilePage/
│
├── widgets/     (or 4_widgets/)
│   ├── Navbar/
│   │   ├── ui/
│   │   └── index.ts  // Public API for Navbar
│   ├── Sidebar/
│
├── features/    (or 5_features/)
│   ├── login/
│   │   ├── ui/     // UI components for login feature
│   │   ├── model/  // Business logic for login
│   │   └── index.ts  // Public API for login feature
│   ├── registration/
│
├── entities/    (or 6_entities/)
│   ├── user/
│   │   ├── ui/
│   │   ├── model/
│   │   └── index.ts  // Public API for user entity
│   ├── post/
│
├── shared/      (or 7_shared/)
│   ├── ui/       // Reusable UI components
│   │   └── Button/ // Example: Button component
│   ├── lib/      // Utility functions, helpers
│   ├── config/   // Shared configuration
│   └── index.ts    // Public API for shared layer (optional)

Import Examples based on Structure:

  • Allowed (Alias Import - Cross Slice/Layer):
    // features/login/ui/LoginForm.tsx
    import { Button } from '@shared/ui/Button'; // OK: Feature uses Shared UI via alias
    import { getUser } from '@entities/user'; // OK: Feature uses User entity via public API
  • Allowed (Relative Import - Same Slice):
    // features/login/ui/LoginForm.tsx
    import { validateInput } from '../lib/validation'; // OK: Relative import within the 'login' feature slice
  • Disallowed (Relative Import - Cross Slice/Layer):
    // features/login/ui/LoginForm.tsx
    import { config } from '../../../app/config'; // BAD: Relative path to different layer
    import { User } from '../../entities/user/model/types'; // BAD: Relative path + Public API sidestep
  • Disallowed (Public API Sidestep):
    // features/login/ui/LoginForm.tsx
    import { userSlice } from '@entities/user/model/slice'; // BAD: Importing internal module directly
  • Disallowed (Cross-Slice Dependency):
    // features/login/model/auth.ts
    import { startRegistration } from '@features/registration'; // BAD: 'login' feature directly imports 'registration' feature

💡 Tip: Sticking to these import rules, especially using aliases and respecting public APIs, makes your codebase much easier to refactor and maintain.


🔍 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 | Disallows relative imports across different slices or layers. Allows relative imports within the same slice. | | fsd/no-public-api-sidestep | Prevents direct imports from internal modules, enforcing public API usage. | | fsd/no-cross-slice-dependency | Disallows direct dependencies between slices in the same layer (applies to all layers, not just features). | | 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 across different slices or layers. ✅ Allowed: Using project-defined aliases or relative imports within same slice ❌ Not Allowed: Relative imports between different slices

// ❌ 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';
// Also supports @/shared format
import { Button } from '@/shared/ui/Button';

Available options:

// In your eslint.config.js:
'fsd/no-relative-imports': ['error', {
  // Allow relative imports within the same slice (enabled by default)
  allowSameSlice: true,

  // Allow type-only imports to use relative paths (disabled by default)
  allowTypeImports: false,

  // Patterns for test files that can ignore this rule
  testFilesPatterns: ['\\.test\\.', '\\.spec\\.'],

  // Patterns for imports to ignore
  ignoreImportPatterns: []
}]

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

Prevents direct imports from internal modules of features, widgets, or entities. ✅ Allowed: Importing from index.ts (public API) or segment level ❌ Not Allowed: Importing internal files within segments

// ✅ Correct (importing via public API)
import { authSlice } from '../../features/auth';
import { userModel } from '@entities/user/model';  // Segment level is allowed

// ❌ Incorrect (direct internal import)
import { authSlice } from '../../features/auth/slice.ts';
import { userSlice } from '@entities/user/model/slice';  // Internal file not allowed

Note: Any segment name is allowed (not limited to model/ui/api/lib). This provides flexibility for teams to define their own segment structure.

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

Prevents direct dependencies between slices in the same layer (applies to all layers, not just features). ✅ Allowed: Communication via lower layers ❌ Not Allowed: Direct imports between different slices in the same layer

// ❌ Incorrect (slice importing from another slice in the same layer)
import { processPayment } from '../../features/payment';

// ✅ Correct (using entities/shared as an intermediary)
import { PaymentEntity } from '../../entities/payment';

// ❌ Also incorrect (entities slice importing from another entities slice)
import { Product } from '../../entities/product';
// This rule now applies to all layers, not just features!

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.


🆕 New Features

1. Cross-Platform Compatibility

The plugin now works seamlessly on both Windows and Unix-based systems by normalizing file paths internally.

2. Flexible Folder Naming Patterns

You can now use numbered prefixes or other naming conventions for your folders:

// Configure folder pattern support
"fsd/forbidden-imports": ["error", {
  folderPattern: {
    enabled: true,
    regex: "^(\\d+_)?(.*)",
    extractionGroup: 2
  }
}]

This allows using structures like:

src/
  1_app/
  2_pages/
  3_widgets/
  4_features/
  5_entities/
  6_shared/

3. Multiple Alias Format Support

The plugin now supports both @shared and @/shared import styles:

// Configure alias format
"fsd/forbidden-imports": ["error", {
  alias: {
    value: "@",
    withSlash: false  // Use true for @/shared format
  }
}]

4. Enhanced Cross-Slice Dependency Rule

The no-cross-slice-dependency rule now applies to all layers by default, not just features:

// Restrict to features layer only (legacy behavior)
"fsd/no-cross-slice-dependency": ["error", {
  featuresOnly: true
}]

5. Pre-defined Configuration Profiles

Multiple configuration presets are now available:

  • recommended - Standard recommended settings
  • strict - Maximum enforcement
  • base - Less strict settings for easy adoption

6. Comprehensive Test Coverage

The plugin now includes extensive test cases for all rules, covering:

  • Basic import scenarios
  • Edge cases and complex patterns
  • Path variations (Windows, Unix, mixed)
  • Custom configurations
  • Real-world usage examples

Path Alias Support

The plugin now properly handles various path alias formats:

// Both formats are supported
import { UserCard } from '@entities/user';
import { UserCard } from '@/entities/user';

Dynamic Import Support

All rules now support dynamic imports:

// Valid dynamic imports
const UserCard = await import('@entities/user');
const { UserCard } = await import('@entities/user');

// Invalid dynamic imports (will be caught by rules)
const UserCard = await import('@entities/user/ui');

Comprehensive Test Coverage

All rules are now thoroughly tested with:

  • Basic import scenarios
  • Edge cases and complex patterns
  • Path variations (Windows, Unix, mixed)
  • Custom configurations
  • Real-world usage examples
  • Path alias formats
  • Dynamic import patterns

🤝 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.