Skip to content

Migrate from Steiger

Markdown for agents

Steiger is the linter of Feature-Sliced Design. Archangel’s fsd preset has a rule for each of Steiger’s 21 rules, with the same name and a link to Steiger’s page for it. A test in the archangel repository runs Steiger and the preset on the same repositories, and compares the files that each one reports.

Feature-Sliced Design tells you how to use the preset. This page tells you how to move from Steiger.

steiger.config.ts
import { defineConfig } from 'steiger';
import fsd from '@feature-sliced/steiger-plugin';
export default defineConfig([
...fsd.configs.recommended,
{
rules: {
'fsd/insignificant-slice': 'off',
},
},
]);
archangel.config.ts
import { defineConfig, fsd } from '@enhance-eng/archangel';
export default defineConfig({
extends: [fsd({ root: 'src' })],
off: ['fsd/insignificant-slice'],
});
Steiger archangel
...fsd.configs.recommended extends: [fsd({ root: 'src' })]
All rules on fsd({ root: 'src', strict: true })
'fsd/<rule>': 'off' off: ['fsd/<rule>']
'fsd/<rule>': 'warn' No equivalent. A rule is on or off.
ignores: [...] ignore: [...] in the config, in .gitignore syntax. It removes the files from the whole check.
files: [...] with a rule off Write a rule with the same name as the preset rule. Your rule replaces it.
A second FSD root A second fsd({ root, name }) with its own name, for example fsd-admin.
  1. Check the repository:

    Terminal
    $ npx archangel check
    error[may-not-import]: src/features/login/ui/login-form.ts may not import "../../signup", because files in "features" may not import "all of (fsd/features, not src/features/*/@x/**)" in a different instance
    --> src/features/login/ui/login-form.ts:1:28
    |
    1 | import { signupLink } from '../../signup';
    | ^^^^^^^^^^^^^^ remove the import, or move the code to files that may import it
    |
    = note: rule: fsd/forbidden-imports
    = note: reason: A module may import only from layers below its own, and a slice not from another slice of its layer, except through @x.
    = note: see: https://feature-sliced.github.io/steiger/rules/forbidden-imports
    checked 8 files and 8 imports: 1 problem
    fsd/shared matches nothing in this repository
    fsd/entities matches nothing in this repository
    fsd/widgets matches nothing in this repository
    fsd/processes matches nothing in this repository
    For what a code means, run `archangel docs may-not-import`.

    The feature login imports the feature signup, so fsd/forbidden-imports refuses it. The lines that say “matches nothing” are parts of the preset with no files. A repository usually has only some layers, so these lines are not errors.

  2. Run npx archangel test. It checks the examples of each preset rule in your repository, under the preset’s root, with your tsconfig.

  3. Remove Steiger when the results are the same.

Each rule name has the prefix of the preset, fsd/ by default. The rules that Steiger recommends are on by default. strict: true adds the four rules that Steiger has as options.

Steiger rule archangel rule On
fsd/forbidden-imports fsd/forbidden-imports Default
fsd/no-public-api-sidestep fsd/no-public-api-sidestep Default
fsd/public-api fsd/public-api Default
fsd/no-layer-public-api fsd/no-layer-public-api Default
fsd/no-reserved-folder-names fsd/no-reserved-folder-names Default
fsd/no-segmentless-slices fsd/no-segmentless-slices Default
fsd/no-segments-on-sliced-layers fsd/no-segments-on-sliced-layers Default
fsd/no-ui-in-app fsd/no-ui-in-app Default
fsd/segments-by-purpose fsd/segments-by-purpose Default
fsd/no-processes fsd/no-processes Default
fsd/typo-in-layer-name fsd/typo-in-layer-name Default
fsd/ambiguous-slice-names fsd/ambiguous-slice-names Default
fsd/inconsistent-naming fsd/inconsistent-naming Default
fsd/insignificant-slice fsd/insignificant-slice Default
fsd/repetitive-naming fsd/repetitive-naming Default
fsd/excessive-slicing fsd/excessive-slicing Default
fsd/shared-lib-grouping fsd/shared-lib-grouping Default
fsd/no-higher-level-imports fsd/no-higher-level-imports strict: true
fsd/no-cross-imports fsd/no-cross-imports strict: true
fsd/import-locality fsd/import-locality strict: true
fsd/no-wildcard-exports fsd/no-wildcard-exports strict: true

The rules about imports and folders are built-in conditions of archangel. The seven rules about names and counts, from typo-in-layer-name to shared-lib-grouping, are conditions of the preset’s plugin, and they read the whole tree as Steiger does.

These differences are on purpose. The test that compares the two tools names each one.

  • A slice is any folder in a sliced layer. The built-in rules take each folder directly in Entities, Features, Widgets or Pages as a slice, with or without segments. They do not know slice groups. Steiger finds a slice by the segments inside it. The plugin rules find slices and slice groups as Steiger does.
  • Each folder in shared/ui and shared/lib needs an index file. fsd/public-api asks for it even when the segment has an index file of its own. Steiger checks those folders only when the segment has none.
  • A misspelt layer is reported beside the real one. fsd/typo-in-layer-name reports src/featurez even when src/features exists. Steiger then reports nothing.
  • A layer has its exact name. The built-in rules take only a folder named exactly as a layer. Steiger also reads 1_shared or _app as a layer.
  • Code outside the layers is checked too. fsd/no-public-api-sidestep checks imports from files outside the layers, such as tests. A deep import from a test is still a sidestep. Steiger reads only the files in the layers.
  • A re-export is an import. Each rule counts export ... from as an import, because a re-export loads the module.