Upgrading to v7.3.0
Overview
v7.3.0 is a minor release, and most projects can upgrade without any change. However, it fixes bugs that some configurations may rely on:
- Base pattern matching: since v5.1.0, the
basePatternoption of element descriptors did not restrict which files matched the descriptor. - Nested element paths: when the folders matched by an element descriptor
patternalso appeared earlier in the file path, the element got a wrongpathandfileInternalPath.
This page explains how to check whether your project is affected, and how to adapt your configuration if it is.
v7.3.0 also replaces micromatch with picomatch to remove the vulnerable braces dependency, and compiles each capture pattern and selector template only once, which makes linting about 2.5 times faster than in v7.2.1. These changes do not affect the pattern syntax or the matching results. Read the changelog for the full list of changes.
Base pattern matching
Since v5.1.0, the basePattern option of element descriptors did not restrict which files matched the descriptor. From v7.3.0, a file only matches a descriptor with basePattern when the left side of its path also matches basePattern, as the documentation describes.
Who is affected
Your project is affected only if both of these conditions are true:
- An element descriptor in
boundaries/elementsusesbasePattern. - Some files match the descriptor
pattern, but the left side of their path does not matchbasePattern.
For example, consider this descriptor:
{
type: "component",
pattern: "components/*",
basePattern: "src/modules/*",
baseCapture: ["module"],
capture: ["componentName"],
}
The file src/modules/auth/components/LoginForm/index.js belongs to a component element both before and after v7.3.0. The file src/shared/components/Button/index.js matches pattern, but src/shared does not match basePattern:
- Before v7.3.0, it belonged to a
componentelement anyway, without the values frombaseCapture, so itsmodulecaptured value was missing. - From v7.3.0, it does not match this descriptor. It belongs to the next element descriptor that matches it, or it is an unknown element if there is none.
After upgrading, you may notice that:
- Rules that report unknown files or dependencies, such as
boundaries/no-unknown-filesandboundaries/no-unknown-dependencies, report the affected files. - Policies that select the affected files by element type no longer apply to them, so some dependencies are reported or allowed differently.
Find the affected files
Run ESLint with debug mode enabled, and filter the file descriptions to print only the files that do not belong to any element:
export default [{
settings: {
"boundaries/debug": {
enabled: true,
messages: {
files: true,
dependencies: false,
violations: false,
},
filter: {
files: [{ element: { isUnknown: true } }],
},
},
},
}];
Run it with v7.2.1 and with v7.3.0. The files that are only printed with v7.3.0 are the affected ones. This configuration requires v7.0.0 or later, so if you are upgrading from an older major version, follow the migration guides first.
Files that now belong to a different element type are not unknown, so this filter does not print them. To find them too, filter by the type of the descriptor that uses basePattern, for example files: [{ element: { type: "component" } }], and look for the files that are no longer printed with v7.3.0.
Adapt your configuration
Choose the option that matches what the descriptor was meant to do.
The elements can be in any location
If basePattern was not meant to restrict where the elements are, and you do not need the values from baseCapture, remove both options:
{type: "component",pattern: "components/*",basePattern: "src/modules/*",baseCapture: ["module"],capture: ["componentName"],}
The elements are in several known locations
If the elements are in more than one location, add a descriptor with the same type for each location. Each descriptor can have its own basePattern and baseCapture, or none of them:
{type: "component",pattern: "components/*",basePattern: "src/modules/*",baseCapture: ["module"],capture: ["componentName"],},{type: "component",pattern: "components/*",basePattern: "src/shared",capture: ["componentName"],},
Avoid joining several locations in one basePattern with braces, such as src/{modules/*,shared}. Braces create a capture group, which changes the values assigned to the baseCapture names. In this example, module would be modules/auth or shared instead of auth.
The other files are not elements of that type
If the files outside basePattern should not belong to that element type, your configuration already says so, and v7.3.0 applies it. Add an element descriptor for them if they belong to another element type, and review the policies that selected them, because they no longer apply.
Nested element paths
Before v7.3.0, the plugin resolved the element path from the first place in the file path where the folders matched by the descriptor pattern appeared. When those folders also appeared earlier in the file path, the element got the path of that earlier occurrence, and its fileInternalPath was computed from that wrong path. From v7.3.0, the element path is resolved from the position where the pattern actually matched. The types and captured values of the elements were already right.
Who is affected
Your project is affected only if both of these conditions are true:
- An element descriptor in
boundaries/elementsmatches from the right side of the path, which is the default (partialMatch: true). - The folders matched by its
patternalso appear earlier in the path of some files. This usually happens with nested elements whose folder name starts with the folder name of an ancestor, or is equal to it.
For example, consider this descriptor:
{
type: "component",
pattern: "components/*",
capture: ["name"],
}
The file src/components/Card/components/Badge/Badge.js belongs to the Badge element, with path src/components/Card/components/Badge and fileInternalPath Badge.js, both before and after v7.3.0. But the folders components/Button also appear earlier in these file paths:
src/components/ButtonGroup/components/Button/Button.js: before v7.3.0, theButtonelement path wassrc/components/Button, and itsfileInternalPathwas the whole file path. From v7.3.0, they aresrc/components/ButtonGroup/components/ButtonandButton.js.src/components/Button/components/Button/Button.js: before v7.3.0, the nestedButtonelement got the same path as its ancestor,src/components/Button, itsfileInternalPathwascomponents/Button/Button.js, and it had no parent. From v7.3.0, they aresrc/components/Button/components/Button,Button.js, and the ancestorButtonelement is its parent.
After upgrading, you may notice that:
- Policies, selectors and custom messages that use the element
pathorfileInternalPathget the right values. For example, policies that only allow importing the main file of an element no longer report the affected files, and policies that disallow some internal files apply to them. - Dependencies between a nested element and an ancestor with the same name are no longer internal, so the
boundaries/dependenciesrule now checks them, even whencheckInternalsis disabled. Their relationships, such aschildorparent, are also right.
Find the affected files
Run ESLint with debug mode enabled, and filter the file descriptions to print only the files whose fileInternalPath includes the folders matched by the descriptor pattern:
export default [{
settings: {
"boundaries/debug": {
enabled: true,
messages: {
files: true,
dependencies: false,
violations: false,
},
filter: {
files: [{ element: { fileInternalPath: "**/components/*/**" } }],
},
},
},
}];
Run it with the version you use now. The files that are printed are the affected ones, because a nested folder matching the pattern belongs to a nested element, so it can not be part of the fileInternalPath. With v7.3.0, the filter does not print any of them. Repeat it with the pattern of each element descriptor.
Adapt your configuration
No configuration change is required, because v7.3.0 describes the elements as your configuration defines them. Review the lint results after upgrading:
- Remove the workarounds that you added for the errors that are no longer reported, such as
eslint-disablecomments, or policies that allowed more internal files than needed. - Fix the dependencies that are now reported, or allow them in your policies if they are expected. For example, to allow a component to import the main file of its child components, including the ones with the same name:
{from: { element: { type: "component" } },allow: [{to: {element: {type: "component",fileInternalPath: "{{to.element.captured.name}}.js",},},dependency: { relationship: { to: "child" } },},],}