Presets
Markdown for agentsA preset gives you a set of rules in one line. It is a function that returns part of a config: parts, groups, rules with their examples, and the plugins that its rules use.
extends: [fsd({ root: 'src' }), nextjs()],Import a preset from @enhance-eng/archangel. A JSON config cannot use a preset, because a preset
is code.
fsd: Feature-Sliced Design
Section titled “fsd: Feature-Sliced Design”fsd(options) checks a front end that uses Feature-Sliced Design.
It has the 21 rules of Steiger, the linter of Feature-Sliced Design. For a full guide, read
Feature-Sliced Design.
| Option | Type | Default | What it does |
|---|---|---|---|
root |
string | required | The folder that holds the layers, such as src. |
strict |
boolean | false |
Also turns on the four rules that Steiger has as options. |
name |
string | 'fsd' |
The prefix of each name. A second use in one repository needs its own name. |
The parts of fsd
Section titled “The parts of fsd”Each pattern starts at root.
| Part | Pattern |
|---|---|
fsd/shared |
shared/** |
fsd/entities |
entities/{slice}/** |
fsd/features |
features/{slice}/** |
fsd/widgets |
widgets/{slice}/** |
fsd/pages |
pages/{slice}/** |
fsd/app |
app/** |
fsd/processes |
processes/** |
Each folder in a sliced layer is a slice. Steiger finds slices by the segments inside them, so the two tools can differ for a folder without segments.
The rules of fsd
Section titled “The rules of fsd”Each rule has the name of the Steiger rule, with the prefix fsd/. Each report links to the
Steiger page of its rule.
| Rule | On by default |
|---|---|
fsd/forbidden-imports |
yes |
fsd/no-public-api-sidestep |
yes |
fsd/public-api |
yes |
fsd/no-layer-public-api |
yes |
fsd/no-reserved-folder-names |
yes |
fsd/no-segmentless-slices |
yes |
fsd/no-segments-on-sliced-layers |
yes |
fsd/no-ui-in-app |
yes |
fsd/segments-by-purpose |
yes |
fsd/no-processes |
yes |
fsd/typo-in-layer-name |
yes |
fsd/ambiguous-slice-names |
yes |
fsd/inconsistent-naming |
yes |
fsd/insignificant-slice |
yes |
fsd/repetitive-naming |
yes |
fsd/excessive-slicing |
yes |
fsd/shared-lib-grouping |
yes |
fsd/no-higher-level-imports |
with strict: true |
fsd/no-cross-imports |
with strict: true |
fsd/import-locality |
with strict: true |
fsd/no-wildcard-exports |
with strict: true |
Seven rules use the conditions of the preset’s plugin fsd: typo-in-layer-name,
ambiguous-slice-names, inconsistent-naming, insignificant-slice, repetitive-naming,
excessive-slicing and shared-lib-grouping. Your own rule can use them too, with
plugin: 'fsd/insignificant-slice'.
nextjs: Next.js
Section titled “nextjs: Next.js”nextjs(options) keeps server code out of client components. A file that starts with
'use client', and each file that it reaches through runtime imports, may not import
server-only or a Node built-in module. A chain stops at a file that starts with 'use server',
because Next.js makes an import of a Server Action into a network call. For a full guide, read
Next.js.
| Option | Type | Default | What it does |
|---|---|---|---|
root |
string | the config folder | The folder of the Next.js app. |
name |
string | 'nextjs' |
The prefix of each name. |
It has one rule, nextjs/client-stays-client, and two groups: nextjs/client and
nextjs/server.
Change a preset
Section titled “Change a preset”import { defineConfig, fsd } from '@enhance-eng/archangel';
export default defineConfig({ extends: [fsd({ root: 'src', strict: true })], off: ['fsd/insignificant-slice'],});- Names have a prefix. Each name of a preset starts with the preset’s name, for example
fsd/features. Your own partshareddoes not replace the partfsd/shared. - A second use needs its own name. To use a preset two times, for example for two apps, give
the second use a
name, such asfsd({ root: 'apps/admin/src', name: 'fsd-admin' }). - Replace a rule. Write a rule with the same name as a preset rule, such as
rule('fsd/public-api', { ... }). Your rule replaces it.archangel rulesshows “replaces the rule public-api of the preset fsd”. - Remove a rule. Put its name in
off.archangel ruleslists the rules thatoffremoves. - Typing errors stop the check. A name in
offmust match a rule, and so must a replacement rule. When it does not, archangel stops and shows the nearest correct name. - Your names cannot have a slash, except to replace a preset rule. With
defineConfig, the editor marks a rule name with a slash that does not start with a preset that you extend.
Empty parts of a preset
Section titled “Empty parts of a preset”A preset declares each part of its architecture, and most repositories use only some parts. So a
preset part that finds no file is not an error. The summary of archangel check lists it. Here,
the repository has no widgets and no processes:
$ npx archangel checkchecked 12 files and 12 imports: no problems fsd/widgets matches nothing in this repository fsd/processes matches nothing in this repositoryWhen all parts of a preset find no file, the root is probably wrong. Archangel then shows
preset-empty.
The examples of a preset
Section titled “The examples of a preset”archangel test checks the examples of each preset rule in your repository, under the preset’s
root, with your tsconfig. So the examples prove that the rules work in your repository, not only in
the repository of the preset.
Write a preset
Section titled “Write a preset”A preset is a function that returns an object with these fields:
| Field | Type | What it is |
|---|---|---|
name |
string | The prefix of each name that the preset declares, such as fsd. |
root |
string | The folder that the preset applies to, from the config folder. Each pattern starts there. |
package |
string | The package that the preset comes from. A problem that its user cannot fix names it. |
parts |
object | Parts, as in a config. |
groups |
object | Groups, as in a config. |
externals |
object | Externals sets, as in a config. |
plugins |
object | The plugins that its rules use. |
rules |
list or function | The rules, each with a name. |
Each rule of a preset needs a name. The preset writes names without the prefix, and archangel adds
it. Publish a preset as a plain function, in the package of your plugin if you have one. Name the
package archangel-plugin-<name> or @scope/archangel-plugin.