forbidden

The from units must not import the to units, directly or through any chain of imports.

Example

[contracts.client-safety]
type = "forbidden"
name = "Client components never reach server-only code"
from = [{ path = "web/src/**", directive = "use client" }]
to = ["npm:sharp", "node:*", "npm:server-only"]
guidance = "Keep server-only modules out of client bundles."
"use client" components/upload.tsx "use client" components/chart.tsx app/page.tsx npm:sharp node:fs npm:server-only lib/images.ts npm:recharts from "use client" files under web/src to npm:sharp, node:*, npm:server-only no directive, so not a unit a chain of 2 imports not checked
upload.tsx reaches npm:sharp through lib/images.ts: one violation, shown with its chain.

No file under web/src that starts with "use client" may reach sharp, a Node builtin or server-only. Each client file is a unit of its own, so a violation names it and shows the chain, such as web/src/components/upload.tsx → web/src/lib/images.ts → npm:sharp. Server components and route handlers are unaffected, since they have no "use client".

Each pair of units is one violation, with every chain between them, shortest first: as in import-linter, once a chain is found its imports are set aside, and the search runs again until no chain is left.

Options

OptionDefault
fromrequiredSelectors.
torequiredSelectors, which may be external packages and builtins.
allow_indirect_importsfalseOnly direct imports count.

Common options

Like every contract type, forbidden also takes:

OptionDefault
typerequiredThe contract type, such as "layers".
namethe idThe title shown in the output.
severity"error""warn" reports a broken contract without failing the check, for rolling a contract out gradually.
exclude_kinds[analysis] exclude_kindsThe import kinds to leave out, e.g. ["type"].
reexports[analysis] reexports"transparent" sees an import of names from a [pass-through module](../concepts.md#pass-through modules) as imports of the modules that define them, as bundlers load them; "opaque" as an import of the pass-through module. Not for encapsulation.
ignore_imports[]"<selector> -> <selector>" entries: the direct imports between them are left out of the contract.
unmatched_ignore_imports"error"Whether an ignore_imports entry that matches no import breaks the contract ("error"), is a warning ("warn") or is fine ("none").
guidancenoneText shown under each violation, such as how to fix it.