Steiger Documentation

repository·master·Indexed 18 days ago

https://github.com/feature-sliced/steiger

A tool for enforcing architectural rules, specifically supporting Feature-Sliced Design (FSD) through its plugin system. It includes rules to prevent ambiguous slice names, excessive slicing, forbidden imports (higher-level and cross-imports), inconsistent naming, and insignificant slices, while enforcing import locality and folder-based segments.

Tokens
17.5K
Snippets
69
Records
90
Agent score
61%

What's inside Steiger

  1. What is the `public-api` rule in Steiger?

    master

    The public-api rule enforces that every slice (and segments on layers that do not have slices) must contain a public API definition. This definition serves as the sole entry point into that slice or segment.

    By requiring a public API (typically an index.ts file), you ensure that modules outside of the slice can only reference the public API and cannot depend on the internal file structure. This provides stability during refactors, allowing you to change internal implementation details without breaking external consumers.

  2. Use the `inconsistent-naming` rule to enforce pluralization consistency

    master

    The inconsistent-naming rule ensures that all entities within a layer (such as entities) follow a consistent pluralization pattern. This prevents friction during code reviews by ensuring that if one entity is plural (e.g., users), all subsequent entities in that layer are also plural (e.g., posts instead of post).

    Note: This rule is in early development. It currently assumes that the slice name is a single English word. It does not yet support complex names like images-of-cats.

    // ✅ Passing structure
    📂 entities
      📂 users
        📂 ui
        📄 index.ts
      📂 posts
        📂 ui
        📄 index.ts
    
    // ❌ Failing structure
    📂 entities
      📂 users
        📂 ui
        📄 index.ts
      📂 post
        📂 ui
        📄 index.ts
  3. Understand Steiger configuration concepts

    master

    Steiger distinguishes between two stages of configuration and two types of configuration objects:

    Configuration Stages

    • Raw configuration: The initial configuration as written by the user in a configuration file.
    • Final configuration (rule instructions): The transformed, validated configuration that the application actually uses to execute rules.

    Configuration Object Types

    • Registration objects (Plugins): Used to register a plugin that provides the actual rule implementations for the application.
    • Configuration objects (ConfigObject, GlobalIgnore): Used to configure the rules that have already been registered (e.g., specifying which files to analyze, which to ignore, or passing specific options to rules).
  4. Use the `no-layer-public-api` rule to enforce FSD compliance

    master

    The no-layer-public-api rule enforces the Feature-Sliced Design (FSD) principle that layers should not expose a public API via an index.ts file. Instead, imports should target specific slices or segments within that layer. This ensures that modules only reference specific business domains rather than a monolithic layer entrypoint, which also helps prevent tree-shaking issues in some bundlers.

    Exception: index.ts files are permitted on the app layer to serve as the application's entrypoint.

    Correct Structure (Passes): Each slice or segment has its own index.ts, but the layer directory itself does not.

    Incorrect Structure (Fails): The layer directory contains an index.ts in addition to the slice/segment files.

    // ✅ PASS: Layer contains slices with their own index files
    📂 shared
      📂 ui
        📄 index.ts
      📂 lib
        📄 index.ts
    
    // ❌ FAIL: Layer has its own index file
    📂 shared
      📂 ui
        📄 index.ts
      📄 index.ts
  5. Understand the rationale for `no-segmentless-slices`

    master

    The no-segmentless-slices rule is designed to prevent technical debt in growing slices.

    Segments separate code by technical purpose. Without segments, slices tend to grow into unorganized collections of files. This leads to:

    1. Difficult Navigation: Harder to find specific logic as the slice grows.
    2. Refactoring Friction: Developers may avoid adding necessary separation later to avoid moving files and causing Git conflicts.
    3. Large Pull Requests: When separation is eventually added, it results in larger, more complex PRs due to file movements.
  6. Understand the `no-reserved-folder-names` rule

    master

    The no-reserved-folder-names rule prevents confusion in Feature-Sliced Design (FSD) project structures by forbidding subfolders within a segment that share names with conventional FSD segments.

    This rule ensures that when a developer sees a folder named ui or model, they can be certain it represents a segment level rather than just an internal subfolder of a slice. This maintains a predictable and unambiguous project hierarchy.

    Forbidden folder names:

    • ui
    • model
    • api
    • lib
    • config
  7. Understand the `no-file-segments` rule

    master

    The no-file-segments rule discourages using files as segments (e.g., ui.tsx or api.ts directly inside a layer or slice) and instead suggests using folder segments (e.g., a ui/ directory containing the relevant files).

    Rationale: File segments limit growth potential because all logic must reside in a single file. Using folder segments provides better long-term scalability, allowing you to add adjacent files or sub-directories as the segment's complexity grows.

  8. Understand glob matching in Steiger configuration

    master

    In Steiger, globs are matched exclusively against files. Folder severities are not explicitly set but are computed based on the files contained within them. The folder's severity is determined by the highest severity found among its child files (ordered: error > warn > off).

    Common Glob Patterns:

    • ./src/shared/**: Matches all files in the shared folder and all its subfolders.
    • ./src/shared/*: Matches all files that are direct children of the shared folder.
    • ./src/shared: Matches only a file named shared (with no extension) inside the src folder.
    • **/__mocks__/**: Matches all files inside any folder named __mocks__ anywhere in the project.
    • **/*.{test,spec}.{ts,tsx}: Matches all .test.ts, .test.tsx, .spec.ts, and .spec.tsx files in the project.
  9. Use the `shared-lib-grouping` rule to prevent module dumping

    master

    The shared-lib-grouping rule prevents the shared/lib directory from becoming a dumping ground for unrelated modules. It enforces a structure where modules within shared/lib are organized into sub-folders rather than being listed as a flat collection of files.

    Currently, the rule uses an arbitrary threshold of 15 ungrouped modules. If your shared/lib folder contains more than 15 files directly under the root of that folder, the rule will fail.

    To pass this rule, you should group related logic into specific sub-directories within shared/lib (e.g., shared/lib/date-utils/, shared/lib/api/) instead of placing all files directly in shared/lib.

    📂 shared
      📂 ui
        📄 index.ts
        📄 Button.tsx
      📂 lib
        📂 date-utils
          📄 index.ts
          📄 formatters.ts
        📂 api-client
          📄 index.ts
          📄 client.ts
  10. Understand the `no-higher-level-imports` rule

    master

    The no-higher-level-imports rule enforces the core Feature-Sliced Design (FSD) import rule: A module in a slice can only import other slices when they are located on layers strictly below.

    This rule prevents circular dependencies and ensures low coupling, making the codebase more predictable and easier to refactor. If a module attempts to import from a layer that is equal to or higher than its own, this rule will trigger a violation.

    Valid Import Patterns

    • pages can import from features, entities, and shared.
    • features can import from entities and shared.
    • entities can import from shared.
    • shared cannot import from any other layer.

    Invalid Import Patterns (Violations)

    • A feature importing from a page.
    • An entity importing from an app layer.
    • A slice importing from another slice on the same layer.
  11. Use the `no-segmentless-slices` rule to enforce FSD structure

    master

    The no-segmentless-slices rule forbids segments (such as ui, api, model, or lib) from appearing as direct children of sliced layers.

    In Feature-Sliced Design (FSD), segments should reside within a slice. If a segment appears directly under a layer (e.g., entities/api), it indicates either that the slice name is confusingly named after a segment, or that code has been placed in a segment without an intermediate slice layer. This rule ensures that every segment is contained within a properly named slice, maintaining the structural integrity and predictability of the project.

    📂 entities
      📂 user
        📂 ui
        📂 model
        📄 index.ts
    
    // ❌ This fails the rule because 'api' is a segment appearing directly under 'entities'
    📂 entities
      📂 api
        📄 index.ts
  12. Use the `no-public-api-sidestep` rule to enforce slice boundaries

    master

    The no-public-api-sidestep rule prevents developers from bypassing a slice's public API to import directly from its internal modules.

    To comply with this rule, you must only import from the top-level entry point (the public API) of a slice. This ensures that the internal structure of a slice can be refactored without breaking external dependencies.

    Valid imports (using the public API):

    • import { Button } from '@/shared/ui'
    • import { UserAvatar } from '@/entities/user'
    • import { EditorPage } from '@/pages/editor'

    Invalid imports (sidestepping the public API):

    • import { translator } from '@/shared/i18n/translator'
    • import { buttonStyles } from '@/shared/ui/button/styles'
    • import { UserAvatar } from '@/entities/user/ui/UserAvatar'
    // ✅ Correct: Importing from the public API
    import { UserAvatar } from '@/entities/user';
    
    // ❌ Incorrect: Sidestepping the public API to an internal module
    import { UserAvatar } from '@/entities/user/ui/UserAvatar';