Architecture decays one import at a time.
The diagram on the wiki says helpers never know about components. Then someone needs a quick fix, the import is three keystrokes away, and the review is busy. JS Boundaries makes the diagram executable.
- The lines are in your configElement and file descriptors map folders and files to the names you already use in design discussions.
- The rules read both sidesEach policy sees the element, file category and module origin of the importer and the imported, plus the import kind.
- Feedback is immediateThe editor underlines the import. CI fails the build. The message explains the architectural reason, not just the rule id.
- Adoption is progressiveStart with one layer and one policy on an existing project with the recommended config. Tighten with strict when ready.
From folder tree to enforced architecture in three edits
Classify files and elements
Element descriptors match folders. File descriptors match files on their own, so a file can be a component and a test at the same time. You only need one of the two to start.
settings: {
"boundaries/elements": [
{ type: "helper", pattern: "helpers/*" },
{ type: "component", pattern: "components/*/*" },
{ type: "module", pattern: "modules/*" },
],
"boundaries/files": [
{ pattern: "**/*.spec.js", category: "test" },
],
}
Write the policies
Pick a default, then allow or disallow by any combination of element type, file category, module origin and dependency kind. Captured path fragments let one policy cover a whole family of elements.
"boundaries/dependencies": [2, {
default: "disallow",
policies: [
{ from: { element: { type: "component" } },
allow: { to: { element: { type: "helper" } } } },
{ from: { element: { type: "module" } },
allow: { to: { element: { types: ["component", "helper"] } } } },
{ disallow: { to: { file: { categories: "test" } } },
message: "Never import test files from {{ from.element.types.[0] }}s" },
],
}]
Read the error where you wrote the import
Violations are ordinary ESLint errors: editor squiggles, CI failures, templated messages with the element types on both sides. Three more rules catch files outside your architecture, unknown dependencies and ignored ones.
src/helpers/format/index.js
1:22 error There is no policy allowing dependencies from
elements of type "helper" to elements of type "component"
boundaries/dependencies
✖ 1 problem (1 error, 0 warnings)What the plugin understands
- classification
- Three independent layersElement (the architectural unit), file (its own category) and module (where the import resolves). All three are computed for both sides of every dependency.
- selectors
- Match on anything describedTypes, categories, origins, captured path fragments and array queries. Reference the
fromside's captures in thetoside. - policies
- Allow and disallow, with a defaultPolicies are evaluated in order. Messages are templates with access to the full description of the dependency.
- rules
- Four rules, one setting
dependencies,no-unknown-files,no-unknown-dependencies,no-ignored-dependencies. Deprecated rules keep working. - performance
- 2.5× faster since 7.3Selectors are normalized once and message templates are cached, cutting lint time by 60% on real projects.
- compatibility
- ESLint 9, Oxlint, TypeScript, monoreposTyped config helpers (
satisfies Config), custom resolvers and path aliases, per-package element roots.
One file, described from every angle
Hover a layer to see the part of the runtime description it fills. Policies can read any of them, on both sides.
// components/Price/Price.spec.tsx → import "react"
{
from: {
element: { types: ["component"], captured: { name: "Price" } },
file: { categories: ["test"] },
module: { origin: "local" },
},
to: {
element: { types: null },
file: { categories: null },
module: { origin: "external", source: "react" },
},
dependency: { kind: "value", relationship: { from: null, to: null } },
}“Software architecture is the art of drawing lines that I call boundaries. Those boundaries separate software elements from one another, and restrict those on one side from knowing about those on the other.”Robert C. Martin · Clean Architecture
Try a config against a sketched project
Paste settings, lay out a folder tree, and watch each file get classified and each import get its verdict. In the browser, nothing to install.
Follow the progressDraw one boundary today
Install the plugin, name one folder, write one policy. Add the rest when the team is ready.