@roflans/eslint-plugin-fsd
v1.0.2
Published
Feature-Sliced Design guardrails for enforcing public API and layer boundaries.
Maintainers
Readme
@roflans/eslint-plugin-fsd
Production-grade ESLint rules that keep Feature-Sliced Design (FSD) architectures under control:
- Public API imports only – consuming layers can’t reach into slice internals
- Layer dependency order –
app → widgets → features → entities → shared - Built for monorepos: caching, memoized FS lookups, TypeScript
pathsawareness, auto-fixes, and actionable suggestions
Table of Contents
Installation
npm install --save-dev @roflans/eslint-plugin-fsd
# or
pnpm add -D @roflans/eslint-plugin-fsd
# or
yarn add -D @roflans/eslint-plugin-fsdRequirements: Node.js ≥ 18.18 and ESLint ≥ 9. The package is ESM-only and ships both JS + d.ts output.
Usage
Flat Config (ESLint 9+)
// eslint.config.mjs
import { defineConfig } from 'eslint/config';
import fsd from '@roflans/eslint-plugin-fsd';
export default defineConfig([
fsd.configs.recommended, // enables both rules
{
rules: {
'fsd/public-api-imports': [
'error',
{
publicApiFiles: ['index.ts', 'public.ts'],
allowInternalImports: ['@/shared/ui/**'],
},
],
},
},
]);Legacy config (.eslintrc)
{
"plugins": ["@roflans/eslint-plugin-fsd"],
"extends": ["plugin:@roflans/eslint-plugin-fsd/recommended"],
"rules": {
"fsd/layer-imports": [
"error",
{ "srcRoots": ["src", "packages/web/src", "packages/ui/src"] }
]
}
}Rule Reference
| Rule ID | Purpose | Fixable |
| ------------------------ | ---------------------------------------------------------------------------------------- | ------- |
| fsd/public-api-imports | Blocks deep imports (e.g. @/entities/user/ui/card). Suggests the closest public entry. | Yes |
| fsd/layer-imports | Ensures layers only depend on lower layers. Suggests valid alternatives when possible. | Yes |
fsd/public-api-imports
- Detects imports that bypass the slice’s public API (
index.ts,public.ts, etc.). - Auto-fix rewrites the path to the discovered public entry (prefers
index.ts). - Additional suggestion text highlights which public file exports the desired symbol.
allowInternalImportslets you whitelist globs (e.g. shared design-system internals).
fsd/layer-imports
- Compares importer/target layers and blocks “upward” imports (
entities→widgets, etc.). - Auto-fix mirrors the same slice path in the nearest allowed layer, when it exists on disk.
- Error messages list the exact layers your current layer is allowed to target.
Shared Options
Both rules accept the same configuration object (all fields optional):
| Option | Type | Default | Description |
| ---------------------- | ----------------------- | ----------------------------------------------------------- | ----------- |
| aliasPrefix | string | '@' | Root alias prefix used inside imports. |
| layers | string[] | ['shared','entities','features','widgets','pages','app'] | Custom order from lowest → highest layer. |
| publicApiFiles | string[] | ['index.ts','index.tsx','public.ts','public.tsx'] | File names treated as public entry points. |
| ignorePatterns | string[] | ['**/*.test.*','**/*.spec.*','**/*.stories.*','**/*.story.*'] | File globs that bypass both rules entirely. |
| allowInternalImports | string[] | [] | Import globs that skip public API enforcement. |
| layerAliases | Record<string,string> | {} | Maps aliases (e.g. @shared) directly to a layer. |
| srcRoots | string[] | ['src','app'] | Root folders that contain layered slices. |
Performance niceties
- Per-file layer detection, FS existence checks, and glob lookups are memoized.
- TypeScript path information is cached once per ESLint run.
- Both rules share the same service instances to avoid duplicated work.
Advanced Topics
TypeScript Awareness
If ESLint runs with parserOptions.project, the plugin consumes the underlying TypeScript Program to:
- Respect
baseUrlandpathsaliases automatically. - Resolve package-specific
tsconfig.jsonfiles (useful for monorepos). - Produce more accurate auto-fix targets because absolute paths are known.
No special configuration is required—just point ESLint at the relevant tsconfig.
Monorepos & Multiple Roots
Use srcRoots to enumerate every package that follows FSD layering:
{
rules: {
'fsd/layer-imports': [
'error',
{ srcRoots: ['apps/web/src', 'packages/ui/src', 'packages/features/src'] },
],
},
}You can combine absolute and relative paths; everything is resolved against process.cwd().
