Skip to main content
Version: 7.3.0

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 basePattern option of element descriptors did not restrict which files matched the descriptor.
  • Nested element paths: when the folders matched by an element descriptor pattern also appeared earlier in the file path, the element got a wrong path and fileInternalPath.

This page explains how to check whether your project is affected, and how to adapt your configuration if it is.

info

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/elements uses basePattern.
  • Some files match the descriptor pattern, but the left side of their path does not match basePattern.

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 component element anyway, without the values from baseCapture, so its module captured 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:

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.

tip

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"],
},
warning

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/elements matches from the right side of the path, which is the default (partialMatch: true).
  • The folders matched by its pattern also 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, the Button element path was src/components/Button, and its fileInternalPath was the whole file path. From v7.3.0, they are src/components/ButtonGroup/components/Button and Button.js.
  • src/components/Button/components/Button/Button.js: before v7.3.0, the nested Button element got the same path as its ancestor, src/components/Button, its fileInternalPath was components/Button/Button.js, and it had no parent. From v7.3.0, they are src/components/Button/components/Button, Button.js, and the ancestor Button element is its parent.

After upgrading, you may notice that:

  • Policies, selectors and custom messages that use the element path or fileInternalPath get 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/dependencies rule now checks them, even when checkInternals is disabled. Their relationships, such as child or parent, 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-disable comments, 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" } },
},
],
}