Skip to content

Sets of files

Markdown for agents

A set of files says which files a rule is about. You write a set in a group, in a rule’s files, on the other side of an import condition, in within, in stopAt, and in a table’s files.

You write The set has
'feature' the files of the part or group with this name
'src/**/*.view.tsx' the files that the path pattern finds
['app', 'feature'] the files in one or more of these sets
{ any: ['app', 'feature'] } the files in one or more of these sets
{ all: ['src/**', { not: 'test' }] } the files that are in each of these sets
{ not: 'test' } each file except these files
{ element: 'feature' } the files of the part feature
{ element: 'feature', capture: { feature: 'login' } } the files of one instance of a part
{ element: '*' } each file that is in a part
{ directive: 'use client' } the files that start with this directive
{ tag: '@side-effect-free' } the files whose comments before the first statement carry this JSDoc tag

On the other side of an import condition, you can also write packages:

You write The other side has
{ packages: ['react', 'react-dom'] } these packages and their subpaths
{ packages: ['@radix-ui/*'] } each package in the scope
{ packages: ['lodash-es/*'] } the subpaths of the package, such as lodash-es/get, and not its root
{ packages: ['*'] } each package
'driver' the packages of the externals set driver

Archangel reads a string in this order:

  1. When it is the name of a part, a group or an externals set, it means that name. The names of a preset, such as fsd/features, count too.
  2. When it has a / or a *, it is a path pattern.
  3. Each other string is an error: rule-unknown-element. The message shows the declared names nearest to it.

So mayNotImport: 'lodash' is an error, not a package. Write { packages: ['lodash'] }.

A kind is not a name here. A kind has a meaning only in its table.

A pattern starts at the config folder. Archangel compares it with paths relative to the config folder, with / between folders on each operating system.

In a pattern It matches
** zero or more folders. It must be a full folder name.
* zero or more characters in one folder name or file name
{a,b} a or b
{name} a capture: one or more characters in one folder name or file name

Braces with a comma are alternatives. Braces with one name are a capture. A capture records the text that it matched. Read Captures and instances.

Archangel refuses a pattern with an empty folder name (src//x), a ** that is not a full folder name (src/ui**/x), a capture without a name, or one capture name two times. The error is pattern-invalid.

{ directive: 'use client' } finds each file whose first statement is that directive. Use it for React Server Components and Server Actions:

rule('client-has-no-db', {
files: { directive: 'use client' },
mayNotImport: 'db',
examples: {
rejected: [{ 'src/view/a.tsx': "'use client';\nimport '../db/a';" }],
},
})

A pattern that starts with ./ names a path from the config folder. Write './app.config.ts' for a file at the root, because 'app.config.ts' reads as the name of a part or a group.

In a part’s list, a pattern that starts with ! leaves those files out of the part. Write pack: ['src/shared/{pack}/**', '!src/shared/widgets/**'] beside widget: 'src/shared/widgets/{widget}/**', so that each widget is in the part widget only.

A package name with a subpath names only those subpaths. This rule bans deep imports into lodash-es and permits its root:

rule('no-deep-lodash', { files: 'shared', mayNotImport: { packages: ['lodash-es/*'] } })

{ tag: '@side-effect-free' } finds each file whose comments before its first statement carry that JSDoc tag, such as /** @side-effect-free */. A tag is the word that starts with @ at the start of a comment line, after any *. A license comment above the docblock does not hide it, and a tag in a comment after the first statement does not count. Use it to check what a tag that a bundler acts on promises. This rule keeps tagged files from importing untagged ones:

rule('pure-imports-pure', { files: { tag: '@side-effect-free' }, mayOnlyImport: { tag: '@side-effect-free' } })

A rule that selects files by directive or by tag cannot use a one-line example, because the text of the file decides the result.

A part or a group that finds no file is an error, because a rule about an empty set can never find a problem. The codes are element-empty and selector-empty.

A preset part that finds no file is not an error, because most repositories use only some parts of a preset. Archangel lists it in the summary. Read Presets.