Elements
Element descriptors are the foundation of the plugin. They define how to classify files in your project as part of known architectural elements.
Defining Element Descriptors
Element descriptors are configured in the boundaries/elements setting as an array of objects. Each descriptor defines:
- What type and/or category of element it represents or belongs to.
- What pattern to match in file paths
- What values to capture from those paths
export default [{
settings: {
"boundaries/elements": [
{
type: "helper",
pattern: "helpers/*/*.js",
mode: "file",
capture: ["domain", "elementName"]
},
{
type: "component",
pattern: "components/*/*",
capture: ["family", "elementName"]
},
{
type: "module",
pattern: "module/*",
capture: ["elementName"]
}
]
}
}]
From Descriptors to Runtime Descriptions
Element descriptors are configuration inputs. During analysis, the plugin transforms them into runtime element descriptions.
- These element descriptions include resolved properties such as
type,category,captured,parents,origin, etc. - When a dependency is analyzed, the plugin builds a dependency description containing:
from: Element description of the file being analyzedto: Element description of the imported targetdependency: Dependency metadata (kind,relationship,specifiers, etc.)
Read the Runtime Descriptions for a detailed breakdown of all available properties in runtime descriptions.
These runtime descriptions are used in two key places:
- Selectors: to match elements/dependencies in plugin rules.
- Rule custom messages: to render dynamic error messages.
Element Descriptor Properties
type (optional)
Type: <string>
The element type to be assigned to files or imports matching the pattern.
{
type: "helper"
}
category (optional)
Type: <string>
The element category to be assigned to files or imports matching the pattern.
{
category: "helper"
}
You have to define at least one of type or category in your element descriptors. The type and category properties are independent. You can define descriptors with only type, only category, or both. The plugin will assign whatever properties are defined in the matched descriptor.
pattern (required)
Type: <string> | <array>
A micromatch pattern to match against file paths.
By default, the plugin matches patterns progressively from the right side of each file path. This means you only need to define the last part of the path you want to match, not the full path from the project root (unless using mode: "full").
Example: Given a path src/helpers/awesome-helper/index.js:
- First tries to match
index.js - Then
awesome-helper/index.js - Then
helpers/awesome-helper/index.js - And so on...
Once a pattern matches, the plugin assigns the corresponding element and continues searching for parent elements using the same logic until the full path has been analyzed.
This behavior can be disabled by setting mode to full.
{
type: "helper",
category: "test",
pattern: "helpers/*/*.spec.js"
}
mode (optional)
Type: <string> - One of: "file" | "folder" | "full"
Default: "folder"
Controls how the pattern matching works:
-
folder(default): When analyzing a file path, the element is assigned to the first parent folder matching the pattern. Any file within that folder is considered part of the element. In practice, it's like adding**/*to your pattern.A pattern like
models/*would match:src/models/user.js(assigns elementmodeltouser.jsfile)src/modules/foo/bar.js(assigns elementmodeltobar.jsfile)
-
file: The pattern is not modified, but the plugin still tries to match the last part of the path. So, a pattern like*.model.jswould match:src/foo.model.jssrc/modules/foo/foo.model.jssrc/modules/foo/models/foo.model.js
-
full: The pattern only matches against the complete file path from the project root. You must provide patterns matching from the base project path. To matchsrc/modules/foo/foo.model.jsyou need patterns like:**/*.model.js**/*/*.model.jssrc/*/*/*.model.js
Patterns in element descriptors are relative to rootPath (which defaults to the current working directory).
The pattern matching behavior differs significantly across modes:
Example: Consider a file at packages/app/src/models/user.model.ts
With rootPath set to packages/app:
// file mode - matches right-to-left from end of path
{
type: "model",
pattern: "*.model.ts",
mode: "file"
}
// ✅ Matches! Pattern matches the filename ending
// folder mode - matches folder, adds **/* internally
{
type: "model",
pattern: "models/*",
mode: "folder"
}
// ✅ Matches! Right-to-left finds "models/user" folder
// full mode - requires complete path match from rootPath
{
type: "model",
pattern: "src/models/*.model.ts",
mode: "full"
}
// ✅ Matches! Full relative path from rootPath matches
// full mode with incomplete pattern
{
type: "model",
pattern: "models/*.model.ts",
mode: "full"
}
// ❌ Doesn't match - missing "src/" prefix in pattern
Key takeaway: In file and folder modes, right-to-left evaluation makes patterns more flexible. In full mode, you must specify the complete path relative to rootPath (unless the file is outside rootPath, in which case absolute paths are used. Read the Settings documentation for more details on this behavior).
capture (optional)
Type: <array>
A powerful feature that allows capturing values from path fragments to use later in rules configuration. Uses micromatch capture feature under the hood.
Each captured value is stored in an object with the key from the capture array at the same index as the captured fragment.
Example:
{
type: "helper",
pattern: "*/helpers/*.js",
capture: ["domain", "elementName"]
}
For a path users/helpers/parsers.js, this captures:
{
domain: "users",
elementName: "parsers"
}
These captured values can then be used in element selectors to create more specific and dynamic rules.
basePattern (optional)
Type: <string>
A micromatch pattern that the left side of the element path must also match from the project root.
This option is useful when using mode with file or folder values, but you also need to capture fragments from other parts of the full path (see baseCapture below).
The effective pattern becomes: [basePattern]/**/[pattern]
{
type: "component",
pattern: "*/component.js",
basePattern: "src/modules/*",
baseCapture: ["moduleName"]
}
baseCapture (optional)
Type: <array>
Works like capture, but for the basePattern. Allows capturing values from the base pattern portion of the path.
All keys from both capture and baseCapture can be used in rules configuration.
{
type: "component",
pattern: "components/*",
basePattern: "src/modules/*",
capture: ["componentName"],
baseCapture: ["moduleName"]
}
For a path src/modules/auth/components/LoginForm, this captures:
{
moduleName: "auth",
componentName: "LoginForm"
}
Be careful to avoid overlapping captures between capture and baseCapture. Each key must be unique across both arrays. In case of duplicates, the values from capture take precedence.
Element Matching Order
Element descriptors are evaluated in array order. The plugin assigns the element from the first matching pattern.
Best Practice: Sort element descriptors from most specific to least specific patterns.
"boundaries/elements": [
// Most specific first
{
type: "react-component",
pattern: "components/*/Component.tsx",
mode: "file",
},
// Less specific patterns after
{
type: "component",
category: "test",
pattern: "components/*/*.spec.tsx",
mode: "file",
},
// Less specific patterns after
{
type: "component",
pattern: "components/*"
},
]
Hierarchical Elements
The plugin supports elements being children of other elements. This relationship can be used later in the rules to restrict access to elements based on their relationship (e.g., only allow importing from child elements).
When analyzing a path, it continues searching for parent elements after finding the first match.
Example:
"boundaries/elements": [
{
type: "component",
pattern: "components/*",
capture: ["componentName"]
},
{
type: "module",
pattern: "modules/*",
capture: ["moduleName"]
}
]
For path src/modules/auth/components/LoginForm/index.js:
- First matches
componenttype (LoginForm) - Continues and matches
moduletype (auth) as parent
Runtime Description Properties
Based on the element descriptors, the plugin builds runtime descriptions for each dependency. These descriptions contains:
from: Element description of the file being analyzedto: Element description of the imported targetdependency: Dependency metadata (kind,relationship,specifiers, etc.)
Element Description (from / to)
Element descriptions contain the following properties:
path:<string | null>- Path of the element. It can be:- Relative to the rootPath when the file is within it.
- Absolute path if the element is outside the root path
null, when the dependency source can't be resolved to any file.
elementPath:<string | null>- Path of the element assigned to the file according to the descriptor. It is relative to the project root path (see Settings), ornullif the file doesn't match any element descriptorinternalPath:<string | null>- Path of the file relative to the element path (ornullif the file doesn't match any element descriptor)type:<string | null>- Element type according to the matched descriptor (ornullif the descriptor doesn't define a type or there's no match)category:<string | null>- Element category according to the matched descriptor (ornullif the descriptor doesn't define a category or there's no match)captured:<object | null>- Object with captured values from descriptors (ornullif there are no captures or no match)parents:<array | null>- Array of parent elements (ornull). Each parent contains:type:<string | null>- Parent element type.nullif the descriptor doesn't define a type.category:<string | null>- Parent element category.nullif the descriptor doesn't define a category.elementPath:<string>- Parent element pathcaptured:<object>- Captured values for the parent element
origin:<"local" | "external" | "core" | null>- Origin of the element, ornullwhen the dependency source can't be resolved to any file.isIgnored:<boolean>- True when the file is ignored due to ignore patterns in the settings.isUnknown:<boolean>- True when the file or dependency doesn't match any element descriptor.
Dependency Description (dependency)
source:<string>- The source string of the dependency as it appears in the code (e.g. import source)module:<string | null>- The base source without any path modifiers when the dependency is external or a Node.js core dependency (e.g. package name innode_modules), ornullfor local dependencieskind:"value" | "type" | "typeof"nodeKind:<string | null>- AST node kind creating the dependency (ornull)specifiers:<array | null>- imported/exported specifiers array (ornull)relationship.from:<string | null>- relation from importer perspective (ornull). Possible values:"internal"- The dependency is internal to the element"child"- The dependency is a child of the element"descendant"- The dependency is a descendant of the element"sibling"- The dependency is a sibling of the element (both have the same parent)"parent"- The dependency is a parent of the element"uncle"- The dependency is an uncle of the element"nephew"- The dependency is a nephew of the element"ancestor"- The dependency is an ancestor of the element
relationship.to: relation from imported element perspective (ornull). Possible values are the inverse ofrelationship.from:"internal"↔"internal""child"↔"parent""descendant"↔"ancestor""sibling"↔"sibling""uncle"↔"nephew"