Repository: javierbrea/eslint-plugin-boundaries
Pull request #98 (2021-03-25 17:58:59)

Add basePattern and baseCapture options to elements settings
### Added
- feat: Add basePattern and baseCapture options to elements settings (#97)


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b64c58c..9f3f934 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,8 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 ## [unreleased]
 ### Added
+- feat: Add basePattern and baseCapture options to elements settings (#97)
 ### Changed
-chore(deps): Update dependencies
+- chore(deps): Update dependencies
 ### Fixed
 ### Removed
 ### BREAKING CHANGES
diff --git a/README.md b/README.md
index 5e53776..400e357 100644
--- a/README.md
+++ b/README.md
@@ -165,11 +165,13 @@ Define patterns to recognize each file in the project as one of this element typ
 
 * __`type`__: `<string>` Element type to be assigned to files or imports matching the `pattern`. This type will be used afterwards in the rules configuration.
 * __`pattern`__: `<string>|<array>` [`micromatch` pattern](https://github.com/micromatch/micromatch). __By default the plugin will try to match this pattern progressively starting from the right side of each file path.__ This means that you don't have to define patterns matching from the base project path, but only the last part of the path that you want to be matched. This is made because the plugin supports elements being children of other elements, and otherwise it could wrongly recognize children elements as a part of the parent one. <br/>For example, given a path `src/helpers/awesome-helper/index.js`, it will try to assign the element to a pattern matching `index.js`, then `awesome-helper/index.js`, then `helpers/awesome-helper/index.js`, etc. Once a pattern matches, it assign the correspondent element type, and continues searching for parents elements with the same logic until the full path has been analyzed. __This behavior can be disabled setting the `mode` option to `full`__, then the provided pattern will try to match the full path.
+* __`basePattern`__: `<string>` Optional [`micromatch` pattern](https://github.com/micromatch/micromatch). If provided, the left side of the element path must match also with this pattern from the root of the project (like if pattern is `[basePattern]/**/[pattern]`). This option is useful when using the option `mode` with `file` or `folder` values, but capturing fragments from the rest of the full path is also needed (see `baseCapture` option below).
 * __`mode`__: `<string> file|folder|full` Optional.
   * When it is set to `folder` (default value), the element type will be assigned to the first file's parent folder matching the pattern. In the practice, it is like adding `**/*` to the given pattern, but the plugin makes it by itself because it needs to know exactly which parent folder has to be considered the element.
   * If it is set to `file`, the given pattern will not be modified, but the plugin will still try to match the last part of the path. So, a pattern like `*.model.js` would match with paths `src/foo.model.js`, `src/modules/foo/foo.model.js`, `src/modules/foo/models/foo.model.js`, etc.
   * If it is set to `full`, the given pattern will only match with patterns matching the full path. This means that you will have to provide patterns matching from the base project path. So, in order to match `src/modules/foo/foo.model.js` you'll have to provide patterns like `**/*.model.js`, `**/*/*.model.js`, `src/*/*/*.model.js`, etc. _(the chosen pattern will depend on what do you want to capture from the path)_
 * __`capture`__: `<array>` Optional. This is a very powerful feature of the plugin. It allows to capture values of some fragments in the matching path to use them later in the rules configuration. It uses [`micromatch` capture feature](https://github.com/micromatch/micromatch#capture) under the hood, and stores each value in an object with the given `capture` key being in the same index of the captured array.<br/>For example, given `pattern: "helpers/*/*.js"`, `capture: ["category", "elementName"]`, and a path `helpers/data/parsers.js`, it will result in `{ category: "data", elementName: "parsers" }`.
+* __`baseCapture`__: `<array>` Optional. [`micromatch` pattern](https://github.com/micromatch/micromatch). It allows capturing values from `basePattern` as `capture` does with `pattern`. All keys from `capture` and `baseCapture` can be used in the rules configuration.
 
 ```json
 {
diff --git a/jest.config.js b/jest.config.js
index 077ed3c..54438a3 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -29,5 +29,5 @@ module.exports = {
 
   // The glob patterns Jest uses to detect test files
   testMatch: ["<rootDir>/test/**/*.spec.js"],
-  // testMatch: ["<rootDir>/test/**/nestjs-example/*.spec.js"],
+  // testMatch: ["<rootDir>/test/**/base-pattern/*.spec.js"],
 };
diff --git a/src/core/elementsInfo.js b/src/core/elementsInfo.js
index 91df354..d8814da 100644
--- a/src/core/elementsInfo.js
+++ b/src/core/elementsInfo.js
@@ -90,55 +90,77 @@ function elementTypeAndParents(path, settings) {
   path
     .split("/")
     .reverse()
-    .reduce((accumulator, elementPathSegment, segmentIndex, elementPaths) => {
-      accumulator.unshift(elementPathSegment);
-      let elementFound = false;
-      getElements(settings).forEach((element) => {
-        const typeOfMatch = VALID_MODES.includes(element.mode) ? element.mode : VALID_MODES[0];
-        const elementPatterns = Array.isArray(element.pattern)
-          ? element.pattern
-          : [element.pattern];
-        elementPatterns.forEach((elementPattern) => {
-          if (!elementFound) {
-            const useFullPathMatch = typeOfMatch === VALID_MODES[2] && !elementResult.type;
-            const pattern =
-              typeOfMatch === VALID_MODES[0] && !elementResult.type
-                ? `${elementPattern}/**/*`
-                : elementPattern;
-            const capture = micromatch.capture(
-              pattern,
-              useFullPathMatch ? path : accumulator.join("/")
-            );
-            if (capture) {
-              elementFound = true;
-              const capturedValues = elementCaptureValues(capture, element.capture);
-              const elementPath = useFullPathMatch
-                ? path
-                : getElementPath(elementPattern, accumulator, elementPaths);
-              accumulator = [];
-              if (!elementResult.type) {
-                elementResult.type = element.type;
-                elementResult.elementPath = elementPath;
-                elementResult.capture = capture;
-                elementResult.capturedValues = capturedValues;
-                elementResult.internalPath =
-                  typeOfMatch === VALID_MODES[0]
-                    ? path.replace(`${elementPath}/`, "")
-                    : elementPath.split("/").pop();
-              } else {
-                parents.push({
-                  type: element.type,
-                  elementPath: elementPath,
-                  capture: capture,
-                  capturedValues: capturedValues,
-                });
+    .reduce(
+      ({ accumulator, lastSegmentMatching }, elementPathSegment, segmentIndex, elementPaths) => {
+        accumulator.unshift(elementPathSegment);
+        let elementFound = false;
+        getElements(settings).forEach((element) => {
+          const typeOfMatch = VALID_MODES.includes(element.mode) ? element.mode : VALID_MODES[0];
+          const elementPatterns = Array.isArray(element.pattern)
+            ? element.pattern
+            : [element.pattern];
+          elementPatterns.forEach((elementPattern) => {
+            if (!elementFound) {
+              const useFullPathMatch = typeOfMatch === VALID_MODES[2] && !elementResult.type;
+              const pattern =
+                typeOfMatch === VALID_MODES[0] && !elementResult.type
+                  ? `${elementPattern}/**/*`
+                  : elementPattern;
+              let basePatternCapture = true;
+
+              if (element.basePattern) {
+                basePatternCapture = micromatch.capture(
+                  [element.basePattern, "**", pattern].join("/"),
+                  path
+                    .split("/")
+                    .slice(0, path.split("/").length - lastSegmentMatching)
+                    .join("/")
+                );
+              }
+              const capture = micromatch.capture(
+                pattern,
+                useFullPathMatch ? path : accumulator.join("/")
+              );
+
+              if (capture && basePatternCapture) {
+                elementFound = true;
+                lastSegmentMatching = segmentIndex + 1;
+                let capturedValues = elementCaptureValues(capture, element.capture);
+                if (element.basePattern) {
+                  capturedValues = {
+                    ...elementCaptureValues(basePatternCapture, element.baseCapture),
+                    ...capturedValues,
+                  };
+                }
+                const elementPath = useFullPathMatch
+                  ? path
+                  : getElementPath(elementPattern, accumulator, elementPaths);
+                accumulator = [];
+                if (!elementResult.type) {
+                  elementResult.type = element.type;
+                  elementResult.elementPath = elementPath;
+                  elementResult.capture = capture;
+                  elementResult.capturedValues = capturedValues;
+                  elementResult.internalPath =
+                    typeOfMatch === VALID_MODES[0]
+                      ? path.replace(`${elementPath}/`, "")
+                      : elementPath.split("/").pop();
+                } else {
+                  parents.push({
+                    type: element.type,
+                    elementPath: elementPath,
+                    capture: capture,
+                    capturedValues: capturedValues,
+                  });
+                }
               }
             }
-          }
+          });
         });
-      });
-      return accumulator;
-    }, []);
+        return { accumulator, lastSegmentMatching };
+      },
+      { accumulator: [], lastSegmentMatching: 0 }
+    );
 
   return {
     ...elementResult,


Test patch:
diff --git a/test/fixtures/base-pattern/domains/domain-a/components/atoms/atom-a/AtomA.js b/test/fixtures/base-pattern/domains/domain-a/components/atoms/atom-a/AtomA.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/components/atoms/atom-a/index.js b/test/fixtures/base-pattern/domains/domain-a/components/atoms/atom-a/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/components/atoms/atom-b/AtomB.js b/test/fixtures/base-pattern/domains/domain-a/components/atoms/atom-b/AtomB.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/components/atoms/atom-b/index.js b/test/fixtures/base-pattern/domains/domain-a/components/atoms/atom-b/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/components/molecules/molecule-a/MoleculeA.js b/test/fixtures/base-pattern/domains/domain-a/components/molecules/molecule-a/MoleculeA.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/components/molecules/molecule-a/index.js b/test/fixtures/base-pattern/domains/domain-a/components/molecules/molecule-a/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/components/molecules/molecule-b/MoleculeB.js b/test/fixtures/base-pattern/domains/domain-a/components/molecules/molecule-b/MoleculeB.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/components/molecules/molecule-b/index.js b/test/fixtures/base-pattern/domains/domain-a/components/molecules/molecule-b/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-a/ModuleA.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-a/ModuleA.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/atoms/atom-e/AtomE.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/atoms/atom-e/AtomE.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/atoms/atom-e/index.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/atoms/atom-e/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/atoms/atom-f/AtomF.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/atoms/atom-f/AtomF.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/atoms/atom-f/index.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/atoms/atom-f/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/molecules/molecule-e/MoleculeE.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/molecules/molecule-e/MoleculeE.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/molecules/molecule-e/index.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-a/components/molecules/molecule-e/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-a/index.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-a/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-b/ModuleB.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-b/ModuleB.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-b/index.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-b/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-h/ModuleH.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-h/ModuleH.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-h/index.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-h/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-h/modules/module-j/ModuleJ.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-h/modules/module-j/ModuleJ.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-h/modules/module-j/index.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-h/modules/module-j/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-i/ModuleI.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-i/ModuleI.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-i/index.js b/test/fixtures/base-pattern/domains/domain-a/modules/module-b/modules/module-i/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/components/atoms/atom-c/AtomC.js b/test/fixtures/base-pattern/domains/domain-b/components/atoms/atom-c/AtomC.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/components/atoms/atom-c/index.js b/test/fixtures/base-pattern/domains/domain-b/components/atoms/atom-c/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/components/atoms/atom-d/AtomD.js b/test/fixtures/base-pattern/domains/domain-b/components/atoms/atom-d/AtomD.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/components/atoms/atom-d/index.js b/test/fixtures/base-pattern/domains/domain-b/components/atoms/atom-d/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/components/molecules/molecule-c/MoleculeC.js b/test/fixtures/base-pattern/domains/domain-b/components/molecules/molecule-c/MoleculeC.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/components/molecules/molecule-c/index.js b/test/fixtures/base-pattern/domains/domain-b/components/molecules/molecule-c/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/components/molecules/molecule-d/MoleculeD.js b/test/fixtures/base-pattern/domains/domain-b/components/molecules/molecule-d/MoleculeD.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/components/molecules/molecule-d/index.js b/test/fixtures/base-pattern/domains/domain-b/components/molecules/molecule-d/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-c/ModuleC.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-c/ModuleC.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-c/index.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-c/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-d/ModuleD.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-d/ModuleD.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-d/index.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-d/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-e/ModuleE.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-e/ModuleE.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-e/index.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-e/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-e/modules/module-g/ModuleG.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-e/modules/module-g/ModuleG.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-e/modules/module-g/index.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-e/modules/module-g/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-f/ModuleF.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-f/ModuleF.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-f/index.js b/test/fixtures/base-pattern/domains/domain-b/modules/module-d/modules/module-f/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/test/rules/base-pattern/element-types.spec.js b/test/rules/base-pattern/element-types.spec.js
new file mode 100644
index 0000000..b7f61e3
--- /dev/null
+++ b/test/rules/base-pattern/element-types.spec.js
@@ -0,0 +1,250 @@
+const { ELEMENT_TYPES: RULE } = require("../../../src/constants/rules");
+const { SETTINGS, createRuleTester, pathResolvers } = require("../../support/helpers");
+
+const rule = require(`../../../src/rules/${RULE}`);
+
+const errorMessage = (fileType, dependencyType) =>
+  `Usage of '${dependencyType}' is not allowed in '${fileType}'`;
+
+const settings = SETTINGS.basePattern;
+
+const { absoluteFilePath } = pathResolvers("base-pattern");
+
+const options = [
+  {
+    default: "disallow",
+    rules: [
+      {
+        from: [["modules", { domain: "domain-a" }]],
+        allow: [
+          ["modules", { domain: "${domain}" }],
+          ["components", { domain: "${domain}" }],
+        ],
+      },
+      {
+        from: [["modules", { domain: "domain-b" }]],
+        allow: ["modules", "components"],
+      },
+      {
+        from: [["components", { domain: "domain-a" }]],
+        allow: [["components", { domain: "domain-a" }]],
+      },
+      {
+        from: [["components", { domain: "domain-b" }]],
+        allow: ["components"],
+      },
+    ],
+  },
+];
+
+const ruleTester = createRuleTester(settings);
+
+ruleTester.run(RULE, rule, {
+  valid: [
+    // module from domain a can import module from domain a
+    {
+      filename: absoluteFilePath("domains/domain-a/modules/module-a/ModuleA.js"),
+      code: "import ModuleB from 'domains/domain-a/modules/module-b'",
+      options,
+    },
+    // module from domain a can import module from domain a
+    {
+      filename: absoluteFilePath("domains/domain-a/modules/module-b/ModuleB.js"),
+      code: "import ModuleB from 'domains/domain-a/modules/module-a'",
+      options,
+    },
+    // private module from domain a can import module from domain a
+    {
+      filename: absoluteFilePath("domains/domain-a/modules/module-b/modules/module-h/ModuleH.js"),
+      code: "import ModuleB from 'domains/domain-a/modules/module-a'",
+      options,
+    },
+    // module from domain a can import component from domain a
+    {
+      filename: absoluteFilePath("domains/domain-a/modules/module-a/ModuleA.js"),
+      code: "import ComponentA from 'domains/domain-a/components/atoms/atom-a'",
+      options,
+    },
+    // module from domain b can import module from domain b
+    {
+      filename: absoluteFilePath("domains/domain-b/modules/module-c/ModuleC.js"),
+      code: "import ModuleB from 'domains/domain-b/modules/module-d'",
+      options,
+    },
+    // module from domain b can import module from domain a
+    {
+      filename: absoluteFilePath("domains/domain-b/modules/module-c/ModuleC.js"),
+      code: "import ModuleB from 'domains/domain-a/modules/module-b'",
+      options,
+    },
+    // module from domain b can import component from domain b
+    {
+      filename: absoluteFilePath("domains/domain-b/modules/module-d/ModuleD.js"),
+      code: "import ComponentA from 'domains/domain-b/components/atoms/atom-c'",
+      options,
+    },
+    // module from domain b can import component from domain a
+    {
+      filename: absoluteFilePath("domains/domain-b/modules/module-d/ModuleD.js"),
+      code: "import ComponentA from 'domains/domain-a/components/atoms/atom-a'",
+      options,
+    },
+    // private module from domain b can import component from domain b
+    {
+      filename: absoluteFilePath(
+        "domains/domain-b/modules/module-d/modules/module-e/modules/module-g/ModuleG.js"
+      ),
+      code: "import ComponentA from 'domains/domain-b/components/molecules/molecule-c'",
+      options,
+    },
+    // private module from domain b can import component from domain a
+    {
+      filename: absoluteFilePath(
+        "domains/domain-b/modules/module-d/modules/module-e/modules/module-g/ModuleG.js"
+      ),
+      code: "import ComponentA from 'domains/domain-a/components/atoms/atom-a'",
+      options,
+    },
+    // component from domain a can import component from domain a
+    {
+      filename: absoluteFilePath("domains/domain-a/components/atoms/atom-a/AtomA.js"),
+      code:
+        "import ComponentA from 'domains/domain-a/components/molecules/molecule-a/MoleculeA.js'",
+      options,
+    },
+    // private component from domain a can import component from domain a
+    {
+      filename: absoluteFilePath(
+        "domains/domain-a/modules/module-a/components/atoms/atom-e/AtomE.js"
+      ),
+      code:
+        "import ComponentA from 'domains/domain-a/components/molecules/molecule-a/MoleculeA.js'",
+      options,
+    },
+    // component from domain b can import component from domain b
+    {
+      filename: absoluteFilePath("domains/domain-b/components/atoms/atom-c/AtomC.js"),
+      code: "import ComponentA from 'domains/domain-b/components/atoms/atom-d/AtomD.js'",
+      options,
+    },
+    // component from domain b can import component from domain a
+    {
+      filename: absoluteFilePath("domains/domain-b/components/atoms/atom-c/AtomC.js"),
+      code:
+        "import ComponentA from 'domains/domain-a/components/molecules/molecule-a/MoleculeA.js'",
+      options,
+    },
+  ],
+  invalid: [
+    // module from domain a can't import module from domain b
+    {
+      filename: absoluteFilePath("domains/domain-a/modules/module-a/ModuleA.js"),
+      code: "import ModuleC from 'domains/domain-b/modules/module-c'",
+      options,
+      errors: [
+        {
+          message: errorMessage("modules", "modules"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+    // private module from domain a can't import module from domain b
+    {
+      filename: absoluteFilePath("domains/domain-a/modules/module-b/modules/module-h/ModuleH.js"),
+      code: "import ModuleB from 'domains/domain-b/modules/module-c'",
+      options,
+      errors: [
+        {
+          message: errorMessage("modules", "modules"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+    // module from domain a can't import component from domain b
+    {
+      filename: absoluteFilePath("domains/domain-a/modules/module-a/ModuleA.js"),
+      code: "import ComponentC from 'domains/domain-b/components/atoms/atom-c'",
+      options,
+      errors: [
+        {
+          message: errorMessage("modules", "components"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+    // component from domain a can't import module from domain a
+    {
+      filename: absoluteFilePath("domains/domain-a/components/molecules/molecule-a/MoleculeA.js"),
+      code: "import ModuleA from 'domains/domain-a/modules/module-a'",
+      options,
+      errors: [
+        {
+          message: errorMessage("components", "modules"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+    // component from domain a can't import module from domain b
+    {
+      filename: absoluteFilePath("domains/domain-a/components/molecules/molecule-a/MoleculeA.js"),
+      code: "import ModuleC from 'domains/domain-b/modules/module-c'",
+      options,
+      errors: [
+        {
+          message: errorMessage("components", "modules"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+    // component from domain a can't import component from domain b
+    {
+      filename: absoluteFilePath("domains/domain-a/components/molecules/molecule-a/MoleculeA.js"),
+      code: "import ComponentC from 'domains/domain-b/components/atoms/atom-c'",
+      options,
+      errors: [
+        {
+          message: errorMessage("components", "components"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+    // private component from domain a can't import component from domain b
+    {
+      filename: absoluteFilePath(
+        "domains/domain-a/modules/module-a/components/molecules/molecule-e/MoleculeE.js"
+      ),
+      code: "import ComponentC from 'domains/domain-b/components/atoms/atom-c'",
+      options,
+      errors: [
+        {
+          message: errorMessage("components", "components"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+    // component from domain b can't import module from domain b
+    {
+      filename: absoluteFilePath("domains/domain-b/components/molecules/molecule-c/MoleculeC.js"),
+      code: "import ModuleC from 'domains/domain-b/modules/module-c'",
+      options,
+      errors: [
+        {
+          message: errorMessage("components", "modules"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+    // component from domain b can't import module from domain a
+    {
+      filename: absoluteFilePath("domains/domain-b/components/molecules/molecule-c/MoleculeC.js"),
+      code: "import ModuleC from 'domains/domain-a/modules/module-a'",
+      options,
+      errors: [
+        {
+          message: errorMessage("components", "modules"),
+          type: "ImportDeclaration",
+        },
+      ],
+    },
+  ],
+});
diff --git a/test/support/helpers.js b/test/support/helpers.js
index a0936a8..7eb318a 100644
--- a/test/support/helpers.js
+++ b/test/support/helpers.js
@@ -154,6 +154,32 @@ const SETTINGS = {
       },
     },
   },
+  basePattern: {
+    "boundaries/elements": [
+      {
+        type: "modules",
+        mode: "folder",
+        pattern: "modules/*",
+        basePattern: "**/domains/*",
+        capture: ["elementName"],
+        baseCapture: ["parentFolders", "domain"],
+      },
+      {
+        type: "components",
+        mode: "folder",
+        pattern: "components/{molecules,atoms}/*",
+        basePattern: "**/domains/*",
+        capture: ["type", "elementName"],
+        baseCapture: ["parentFolders", "domain"],
+      },
+    ],
+    "import/resolver": {
+      "eslint-import-resolver-node": {},
+      [path.resolve(process.cwd(), "resolver-legacy-alias")]: {
+        domains: `./${codeFilePath("base-pattern", "domains")}`,
+      },
+    },
+  },
 };
 
 const createRuleTester = (settings) => {