Import options
Markdown for agentsAdd one of these options to an import condition (mayNotImport, mayOnlyImport,
mayOnlyBeImportedBy) or to a table. The condition then applies only to some imports.
| Option | Values | The condition applies only to |
|---|---|---|
names |
list of names | imports that bring in one of these names |
specifier |
'relative', 'parent', 'alias', 'package' |
imports whose specifier has this form |
instance |
'same', 'different' |
imports inside one instance, or between two instances |
dependency |
'dev', 'undeclared' |
packages with this entry in the nearest package.json |
within |
set of files | imports into an area, or importers in an area |
imports |
'all', 'runtime', 'static', 'types' |
imports of this kind |
An import that one of these options leaves out does not count as judged by the rule.
archangel rules lists it under the imports that no rule judges.
rule('no-effects-in-views', { files: 'view', mayNotImport: { packages: ['react'] }, names: ['useEffect'], examples: { rejected: [{ 'src/view/a.ts': "import { useEffect } from 'react';" }] } })- A default import brings in the name
default. - A namespace import (
import * as x), anexport *, a dynamicimport()and arequire()bring in each name of the module, because archangel cannot see which names the code uses. - For a module in a package, archangel reads the names that the module exports, through its
relative
export * fromstatements too.import * as fr from 'date-fns/locale/fr'brings in only the names of that locale. When archangel cannot see every name (a CommonJS module, anexport =, anexport *from another package), the import brings in each name there is.
specifier
Section titled “specifier”rule('no-parent-imports', { files: 'feature', mayNotImport: 'feature', specifier: 'parent', examples: { rejected: [{ 'src/features/login/ui/a.ts': "import '../b';", 'src/features/login/b.ts': '' }] } })| Value | The specifier |
|---|---|
'relative' |
starts with ./ or ../ |
'parent' |
starts with ../ |
'alias' |
resolves through a tsconfig path or a package imports field |
'package' |
is a package name |
instance
Section titled “instance”rule('features-apart', { files: 'feature', mayNotImport: 'feature', instance: 'different', examples: { rejected: ['src/features/login/a.ts imports src/features/cart/b.ts'] } })'same': imports whose two ends are in one instance of the part infiles.'different': imports between two instances of that part.
files must be one part with a capture. Other files give
rule-needs-capture.
dependency
Section titled “dependency”rule('no-dev-packages-in-src', { files: 'src/**', mayNotImport: { packages: ['*'] }, dependency: 'dev', examples: { rejected: [{ 'package.json': '{ "devDependencies": { "react": "1.0.0" } }', 'src/a.ts': "import 'react';" }] } })| Value | The package |
|---|---|
'dev' |
is only in devDependencies of the nearest package.json of the importing file |
'undeclared' |
is not in the nearest package.json of the importing file |
The nearest package.json is the first one at or above the importing file. In a monorepo, this is
the package.json of the workspace package. Read Monorepos.
within
Section titled “within”within gives a rule an area. The rule then permits each import outside the area.
rule('entity-imports', { files: 'entity', mayOnlyImport: ['value-object', 'error'], within: 'src/**', examples: { rejected: ['src/entity/a.ts imports src/use-case/a.ts'] } })| On | within limits |
|---|---|
mayOnlyImport |
the targets: the rule judges only imports into the area |
mayOnlyBeImportedBy |
the importers: the rule judges only imports from files in the area |
| a table | the targets: each row judges only imports into the area |
- Without
within,mayOnlyImportjudges each import, packages too. withincan name an externals set. ThenmayOnlyImportjudges the imports of those packages.- With
within, the list ofmayOnlyImportcan be empty. The rule then refuses each import into the area, except into its own files. - Archangel refuses
withinonmayNotImport, and on a condition that judges no import. To limitmayNotImportto an area, write the area on the other side:mayNotImport: { all: ['use-case', 'src/**'] }. - An area that finds no file is an error.
imports
Section titled “imports”| Value | The rule counts |
|---|---|
'all' |
each import. This is the default. |
'runtime' |
the imports that stay in the compiled code. This is the default for noCycles and for a transitive rule. |
'static' |
the runtime imports other than import(): the imports that load with the file, in its bundle chunk. A require() is static. |
'types' |
only type imports, such as import type |
An import type, and an import that names only types with import { type A }, is a type import.
With verbatimModuleSyntax in your tsconfig, import { type A } from './a' stays in the compiled
code, so it is a runtime import.
transitively
Section titled “transitively”Add transitively: true to mayNotImport. The rule then finds chains of imports of each length,
not only direct imports. The report shows the shortest chain.
rule('client-stays-client', { files: { directive: 'use client' }, mayNotImport: { packages: ['node:*'] }, transitively: true, stopAt: { directive: 'use server' }, examples: { rejected: [{ 'src/view/a.tsx': "'use client';\nimport '../shared/a';", 'src/shared/a.ts': "import 'node:fs';" }], },})- A chain stops at a package, and at each file in
stopAt. - A transitive rule’s side cannot name a package subpath, such as
lodash-es/*, because a chain ends at a package. - With
imports: 'static', a chain also stops at eachimport(). A bundler starts a new chunk there, so the rule then means “the initial bundle may not reach this”:rule('initial-bundle-no-carousel', { files: 'feature', mayNotImport: { packages: ['carousel'] }, transitively: true, imports: 'static' }). - A transitive rule counts only runtime imports by default.
imports: 'types'is refused, because a chain of type imports never runs. - A transitive rule cannot also have
names,specifier,instance,dependencyorwithin. - For each file that reaches the other side, archangel reports one problem with the shortest chain. Among chains of one length, it shows the first in path order.
archangel why <file> <target> shows the shortest chain between two files, or from a file to a
package. It does not know stopAt, so it can show a chain that the rule stops.