Repository: javierbrea/eslint-plugin-boundaries
Pull request #165 (2021-11-13 08:47:04)

Custom error messages
### Added
- feat(#87): Support custom messages in `element-types` config

### Changed
- feat(#87): Add context about the specific rule forbidding the import to the `element-types` error message. Add information about file and dependency when import is forbidden due to the default configuration.
- refactor: Add isArray and isString utils

### Fixed
- fix: Support array of micromatch patterns when replacing captured values
- docs: Fix broken links

### Removed
- feat: Remove cache traces from debug mode


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd29b0e..26215ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Removed
 ### BREAKING CHANGES
 
+## [unreleased]
+
+### Added
+- feat(#87): Support custom messages in `element-types` config
+
+### Changed
+- feat(#87): Add context about the specific rule forbidding the import to the `element-types` error message. Add information about file and dependency when import is forbidden due to the default configuration.
+- refactor: Add isArray and isString utils
+- chore(deps): Support any eslint version greater than 6.x in peerDependencies
+
+### Fixed
+- fix: Support array of micromatch patterns when replacing captured values
+- docs: Fix broken links
+
+### Removed
+- feat: Remove cache traces from debug mode
+
 ## [2.5.1] - 2021-11-06
 
 ### Fixed
diff --git a/README.md b/README.md
index a876ce9..e8d7f8d 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ __This plugin ensures that your architecture boundaries are respected by the ele
   * [Rules configuration](#rules-configuration)
     * [Main format of rules options](#main-format-of-rules-options)
     * [Elements matchers](#elements-matchers)
+    * [Error messages](#error-messages)
     * [Advanced example](#advanced-example)
 - [Resolvers](#resolvers)
 - [Usage with TypeScript](#usage-with-typescript)
@@ -199,7 +200,7 @@ Define patterns to recognize each file in the project as one of this element typ
 }
 ```
 
-> Tip: You can enable the [debug mode](#debug-mode) when configuring the plugin, and you will get information about the type assigned to each file in the project.
+> Tip: You can enable the [debug mode](#debug-mode) when configuring the plugin, and you will get information about the type assigned to each file in the project, as well as captured properties and values.
 
 #### __`boundaries/include`__
 
@@ -258,23 +259,31 @@ Some rules require extra configuration, and it has to be defined in each specifi
 
 #### Main format of rules options
 
-The docs of each rule contains an specification of their own options, but the main rules share the format in which the options have to be defined. The format described here is valid for options of [`element-types`](docs/rules/element-types.md), [`external`](docs/rules/external.md) and [`entry-point`](docs/rules/entry-point.md) rules.
+The docs of each rule contains an specification of their own options, but __the main rules share the format in which the options have to be defined__. The format described here is valid for options of [`element-types`](docs/rules/element-types.md), [`external`](docs/rules/external.md) and [`entry-point`](docs/rules/entry-point.md) rules, __except the `message` properties, which for the moment are only supported in the [`element-types`](docs/rules/element-types.md) rule settings.
 
 Options set an "allow/disallow" value by default, and provide an array of rules. Each matching rule will override the default value and the value returned by previous matching rules. So, the final result of the options, once processed for each case, will be "allow" or "disallow", and this value will be applied by the plugin rule in the correspondant way, making it to produce an eslint error or not.
 
-```json
+```jsonc
 {
   "rules": {
     "boundaries/element-types": [2, {
+      // Allow or disallow any dependency by default
       "default": "allow",
+      // Define a custom message for this rule
+      "message": "${file.type} is not allowed to import ${dependency.type}",
       "rules": [
         {
+          // In this type of files...
           "from": ["helpers"],
-          "disallow": ["modules", "components", "helpers"]
+          // ...disallow importing this type of elements
+          "disallow": ["modules", "components"],
+          // ...and return this custom error message
+          "message": "Helpers must not import other thing than helpers"
         },
         {
           "from": ["components"],
           "disallow": ["modules"]
+          // As this rule has not "message" property, it will use the message defined at first level
         }
       ]
     }]
@@ -291,8 +300,9 @@ Remember that:
 
 * __`from/target`__: `<element matchers>` Depending of the rule to which the options are for, the rule will be applied only if the file being analized matches with this element matcher (`from`), or the dependency being imported matches with this element matcher (`target`).
 * __`disallow/allow`__: `<value matchers>` If the plugin rule target matches with this, then the result of the rule will be "disallow/allow". Each rule will require a type of value here depending of what it is checking. In the case of the `element-types` rule, for example, another `<element matcher>` has to be provided in order to check the type of the local dependency.
+* __`message`__: `<string>` Optional. If the rule results in an error, the plugin will return this message.
 
-> Tip: All properties can receive a single matcher, or an array of matchers.
+> Tip: Properties `from/target` and `disallow/allow` can receive a single matcher, or an array of matchers.
 
 ##### Elements matchers
 
@@ -303,6 +313,22 @@ Elements matchers used in the rules options can have the next formats:
   * `["helpers", { category: "data", elementName: "parsers"}]`: Will only match with helpers with category "data" and elementName "parsers" (`helpers/data/parsers.js`).
   * `["helpers", { category: "data" }]`: Will match with all helpers with category "data" (`helpers/data/*.js`)
 
+##### Error messages
+
+The plugin returns a different default message for each rule, check the documentation of each one for further info. But some rules support defining custom messages in their configuration, as seen in ["Main format of rules options"](#main-format-of-rules-options).
+
+When defining custom messages, it is possible to provide information about the type or captured properties of the current file or dependency. Use `${file.PROPERTY}` or `${dependency.PROPERTY}`, and it will be replaced by the correspondent captured value from the file or the dependency:
+
+```jsonc
+{
+  "message": "${file.type}s of category ${file.category} are not allowed to import ${dependency.category}s"
+  // If the error was produced by a file with type "component" and captured value "category" being "atom", trying to import a dependency with category "molecule", the message would be:
+  // "components of category atom are not allowed to import molecules"
+}
+```
+
+> Tip: Read ["Global settings"](#global-settings) for further info about how to capture values from elements.
+
 ##### Advanced example of a rule configuration
 
 Just to illustrate the high level of customization that the plugin supports, here is an example of advanced options for the `boundaries/element-types` rule based on the previous global `elements` settings example:
@@ -343,7 +369,9 @@ Just to illustrate the high level of customization that the plugin supports, her
           "disallow": [
             // disallow importing helpers with captured category "data"
             ["helpers", { "category": "data" }]
-          ]
+          ],
+          // Custom message only for this specific error
+          "message": "Atom components can't import data helpers"
         },
         {
           // when file is inside a module
@@ -360,7 +388,9 @@ Just to illustrate the high level of customization that the plugin supports, her
           "disallow": [
             // disallow importing any type of component not being of family layout
             ["components", { "family": "!layout" }]
-          ]
+          ],
+          // Custom message only for this specific error
+          "message": "Modules with name starting by 'page-' can't import not layout components. You tried to import a component of family ${dependency.family} from a module with name ${file.elementName}"
         }
       ]
     }]
diff --git a/docs/rules/element-types.md b/docs/rules/element-types.md
index 6804ac3..1e1bf46 100644
--- a/docs/rules/element-types.md
+++ b/docs/rules/element-types.md
@@ -14,14 +14,16 @@ It checks `import` statements between the element types of the project based on
 
 * `enabled`: for enabling the rule. 0=off, 1=warn, 2=error.
 * `default`: `allow` or `disallow`. If no one `rule` matches, the dependency will be allowed or disallowed based on this value.
-* `rules`: Rules to be processed in order to decide if the `import` statement has to be allowed or not.
+* `message`: Custom message for the rule errors. Note that __the rule default message provides a lot of information about why the error was produced__, so you should define a custom message only if you are sure about what you are doing. Read ["error messages"](#error-messages) for further information.
+* `rules`: Rules to be processed in order to decide if the `import` statement has to be allowed or not. It must be an array of objects containing:
   * `from`: `<element matchers>` If the file being analyzed matches with this, then the rule will be executed to know if it allows/disallows the `import`. If not, the rule is skipped.
   * `disallow`: `<element matchers>` If the element being imported matches with this, then the result of the rule will be "disallow", and the import will be notified as an `eslint` error (this value can be overwritten by a next rule returning "allow")
   * `allow`: `<element matchers>` If the element being imported matches with this, then the result of the rule will be "allow", and the import will not be notified as an `eslint` error (this value can be overwritten by a next rule returning "disallow")
+  * `message`: `<string>` Custom error message only for this rule. Read ["error messages"](../../README.md#error-messages) for further info.
 
 ##### Further reading:
 * [Main format of rules options](../../README.md#main-format-of-rules-options)
-* [Element matchers](../../README.md#element-matchers)
+* [Element matchers](../../README.md#elements-matchers)
 
 ##### Comparing captures of the file element with captures of the imported element
 
@@ -79,6 +81,15 @@ So, if the `from` element has captured values `{ family: "atom", elementName: "c
 }
 ```
 
+### Error messages
+
+This rule provides a lot of information about the specific option producing an error, so the user can have enough context to solve it.
+
+* If the error is produced because all imports are disallowed by default, and no rule is specificly allowing it, then the message provides information about the file and the dependency types and captured values: `No rule allowing this dependency was found. File is of type 'components' with category 'molecules' and elementName 'molecule-c'. Dependency is of type 'modules' with domain 'domain-a' and elementName 'module-a'`.
+* If the error is produced by a specific option, the the message includes information about the option producing it: `Importing elements of type 'components' with category 'atoms' and elementName '*-a' is not allowed in elements of type 'helpers' with elementName 'helper-c'. Disallowed in rule 1`
+
+Anyway, you can configure a custom error message for changing this behaviour, or even custom error messages only for a specific rule option. Read ["error messages"](../../README.md#error-messages) for further info about how to configure messages.
+
 ### Settings
 
 Examples in the next sections are based on the previous options example and these files and settings.
diff --git a/docs/rules/entry-point.md b/docs/rules/entry-point.md
index 9c129e2..504b0da 100644
--- a/docs/rules/entry-point.md
+++ b/docs/rules/entry-point.md
@@ -21,7 +21,7 @@ It checks `import` statements to the elements of the project and ensure that eac
 
 ##### Further reading:
 * [Main format of rules options](../../README.md#main-format-of-rules-options)
-* [Element matchers](../../README.md#element-matchers)
+* [Element matchers](../../README.md#elements-matchers)
 
 ##### Options example
 
diff --git a/docs/rules/external.md b/docs/rules/external.md
index 60d1d65..2336536 100644
--- a/docs/rules/external.md
+++ b/docs/rules/external.md
@@ -51,7 +51,7 @@ __Examples__
 
 ##### Further reading:
 * [Main format of rules options](../../README.md#main-format-of-rules-options)
-* [Element matchers](../../README.md#element-matchers)
+* [Element matchers](../../README.md#elements-matchers)
 
 ##### Options example
 
diff --git a/jest.config.js b/jest.config.js
index 84cdc52..ae8bb6e 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -24,7 +24,7 @@ module.exports = {
     },
     // Ignore coverage of debugger
     "./src/helpers/debug.js": {
-      branches: 91,
+      branches: 83,
     },
     // Decrease coverage due to cache branches
     "./src/core/elementsInfo.js": {
@@ -38,5 +38,5 @@ module.exports = {
 
   // The glob patterns Jest uses to detect test files
   testMatch: ["<rootDir>/test/**/*.spec.js"],
-  // testMatch: ["<rootDir>/test/**/base-pattern/*.spec.js"],
+  // testMatch: ["<rootDir>/test/**/nestjs-example/element-types.spec.js"],
 };
diff --git a/package.json b/package.json
index c6ed100..1151dcf 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
     "prepare": "is-ci || husky install"
   },
   "peerDependencies": {
-    "eslint": "6.x || 7.x || 8.x"
+    "eslint": ">=6.0.0"
   },
   "dependencies": {
     "eslint-import-resolver-node": "0.3.6",
diff --git a/src/core/cache.js b/src/core/cache.js
index 5b8eeca..52ab0b2 100644
--- a/src/core/cache.js
+++ b/src/core/cache.js
@@ -1,5 +1,3 @@
-const { debug } = require("../helpers/debug");
-
 class Cache {
   constructor(name, settings) {
     this._cache = {};
@@ -13,7 +11,6 @@ class Cache {
 
   load(key) {
     if (this._cache[key]) {
-      debug(`Returning "${key}" ${this._name} info from cache`);
       return this._cache[key];
     }
 
@@ -32,7 +29,6 @@ class CachesManager {
       return cacheCandidate._settings === settings;
     });
     if (!cache) {
-      debug(`${this._name} cache not found, creating new`);
       cache = new Cache(this._name, settings);
       this._caches.push(cache);
     }
diff --git a/src/core/elementsInfo.js b/src/core/elementsInfo.js
index 306b861..2259e00 100644
--- a/src/core/elementsInfo.js
+++ b/src/core/elementsInfo.js
@@ -5,6 +5,7 @@ const resolve = require("eslint-module-utils/resolve").default;
 const { IGNORE, INCLUDE, VALID_MODES } = require("../constants/settings");
 const { getElements } = require("../helpers/settings");
 const { debugFileInfo } = require("../helpers/debug");
+const { isArray } = require("../helpers/utils");
 
 const { filesCache, importsCache, elementsCache } = require("./cache");
 
@@ -112,9 +113,7 @@ function elementTypeAndParents(path, settings) {
         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];
+          const elementPatterns = isArray(element.pattern) ? element.pattern : [element.pattern];
           elementPatterns.forEach((elementPattern) => {
             if (!elementFound) {
               const useFullPathMatch = typeOfMatch === VALID_MODES[2] && !elementResult.type;
diff --git a/src/helpers/messages.js b/src/helpers/messages.js
new file mode 100644
index 0000000..1b6f1bf
--- /dev/null
+++ b/src/helpers/messages.js
@@ -0,0 +1,117 @@
+const { isString, isArray, replaceObjectValuesInTemplates } = require("./utils");
+const { micromatchPatternReplacingObjectValues } = require("./rules");
+
+function quote(str) {
+  return `'${str}'`;
+}
+
+function typeMessage(elementMatcher) {
+  return `elements of type ${quote(elementMatcher)}`;
+}
+
+function propertiesConcater(properties, index) {
+  if (properties.length > 1 && index === properties.length - 1) {
+    return " and";
+  }
+  if (index === 0) {
+    return " with";
+  }
+  return ",";
+}
+
+function micromatchPatternMessage(micromatchPatterns, elementCapturedValues) {
+  const micromatchPatternsWithValues = micromatchPatternReplacingObjectValues(
+    micromatchPatterns,
+    elementCapturedValues
+  );
+  if (isArray(micromatchPatternsWithValues)) {
+    if (micromatchPatternsWithValues.length === 1) {
+      return quote(micromatchPatternsWithValues[0]);
+    }
+    return micromatchPatternsWithValues.reduce((message, micromatchPattern, index) => {
+      if (index === 0) {
+        return quote(micromatchPattern);
+      }
+      if (index === micromatchPatternsWithValues.length - 1) {
+        return `${message} or ${quote(micromatchPattern)}`;
+      }
+      return `${message}, ${quote(micromatchPattern)}`;
+    }, "");
+  }
+  return quote(micromatchPatternsWithValues);
+}
+
+function capturedValuesMatcherMessage(capturedValuesPattern, elementCapturedValues) {
+  const capturedValuesPatternKeys = Object.keys(capturedValuesPattern);
+  return capturedValuesPatternKeys
+    .map((key) => {
+      return [key, capturedValuesPattern[key]];
+    })
+    .reduce((message, propertyNameAndMatcher, index) => {
+      return `${message}${propertiesConcater(capturedValuesPatternKeys, index)} ${
+        propertyNameAndMatcher[0]
+      } ${micromatchPatternMessage(propertyNameAndMatcher[1], elementCapturedValues)}`;
+    }, "");
+}
+
+function elementMatcherMessage(elementMatcher, elementCapturedValues) {
+  if (isString(elementMatcher)) {
+    return typeMessage(elementMatcher);
+  }
+  return `${typeMessage(elementMatcher[0])}${capturedValuesMatcherMessage(
+    elementMatcher[1],
+    elementCapturedValues
+  )}`;
+}
+
+function ruleElementMessage(elementPatterns, elementCapturedValues) {
+  if (isArray(elementPatterns)) {
+    if (elementPatterns.length === 1) {
+      return elementMatcherMessage(elementPatterns[0], elementCapturedValues);
+    }
+    return elementPatterns.reduce((message, elementPattern, index) => {
+      if (index === 0) {
+        return elementMatcherMessage(elementPattern, elementCapturedValues);
+      }
+      return `${message}, or ${elementMatcherMessage(elementPattern, elementCapturedValues)}`;
+    }, "");
+  }
+  return elementMatcherMessage(elementPatterns, elementCapturedValues);
+}
+
+function customErrorMessage(message, file, dependency) {
+  return replaceObjectValuesInTemplates(
+    replaceObjectValuesInTemplates(message, { ...file.capturedValues, type: file.type }, "file"),
+    { ...dependency.capturedValues, type: dependency.type },
+    "dependency"
+  );
+}
+
+function elementCapturedValuesMessage(capturedValues) {
+  if (!capturedValues) {
+    return "";
+  }
+  const capturedValuesKeys = Object.keys(capturedValues);
+  return capturedValuesKeys
+    .map((key) => {
+      return [key, capturedValues[key]];
+    })
+    .reduce((message, propertyNameAndValue, index) => {
+      return `${message}${propertiesConcater(capturedValuesKeys, index)} ${
+        propertyNameAndValue[0]
+      } ${quote(propertyNameAndValue[1])}`;
+    }, "");
+}
+
+function elementMessage(elementInfo) {
+  return `of type ${quote(elementInfo.type)}${elementCapturedValuesMessage(
+    elementInfo.capturedValues
+  )}`;
+}
+
+module.exports = {
+  quote,
+  ruleElementMessage,
+  customErrorMessage,
+  elementMessage,
+};
diff --git a/src/helpers/rules.js b/src/helpers/rules.js
index f86b43c..e7723fb 100644
--- a/src/helpers/rules.js
+++ b/src/helpers/rules.js
@@ -1,5 +1,7 @@
 const micromatch = require("micromatch");
 
+const { isArray, replaceObjectValuesInTemplates } = require("./utils");
+
 const REPO_URL = "https://github.com/javierbrea/eslint-plugin-boundaries";
 
 function removePluginNamespace(ruleName) {
@@ -43,9 +45,7 @@ function dependencyLocation(node, context) {
 }
 
 function micromatchPatternReplacingObjectValues(pattern, object) {
-  return Object.keys(object).reduce((result, objectKey) => {
-    return result.replace(`\${${objectKey}}`, object[objectKey]);
-  }, pattern);
+  return replaceObjectValuesInTemplates(pattern, object);
 }
 
 function isObjectMatch(objectWithMatchers, object, objectWithValuesToReplace) {
@@ -69,10 +69,10 @@ function rulesMainKey(key) {
 
 function ruleMatch(ruleMatchers, elementInfo, isMatch, elementToCompare = {}) {
   let match = { result: false, report: null };
-  const matchers = !Array.isArray(ruleMatchers) ? [ruleMatchers] : ruleMatchers;
+  const matchers = !isArray(ruleMatchers) ? [ruleMatchers] : ruleMatchers;
   matchers.forEach((matcher) => {
     if (!match.result) {
-      if (Array.isArray(matcher)) {
+      if (isArray(matcher)) {
         const [value, captures] = matcher;
         match = isMatch(elementInfo, value, captures, elementToCompare.capturedValues);
       } else {
@@ -110,9 +110,16 @@ function getElementRules(elementInfo, options, mainKey) {
     return [];
   }
   const key = rulesMainKey(mainKey);
-  return options.rules.filter((rule) => {
-    return ruleMatch(rule[key], elementInfo, isMatchElementType).result;
-  });
+  return options.rules
+    .map((rule, index) => {
+      return {
+        ...rule,
+        index,
+      };
+    })
+    .filter((rule) => {
+      return ruleMatch(rule[key], elementInfo, isMatchElementType).result;
+    });
 }
 
 function elementRulesAllowDependency({
@@ -122,12 +129,21 @@ function elementRulesAllowDependency({
   isMatch,
   rulesMainKey: mainKey,
 }) {
-  const [result, report] = getElementRules(element, options, mainKey).reduce(
+  const [result, report, ruleReport] = getElementRules(element, options, mainKey).reduce(
     (allowed, rule) => {
       if (rule.disallow) {
         const match = ruleMatch(rule.disallow, dependency, isMatch, element);
         if (match.result) {
-          return [false, match.report];
+          return [
+            false,
+            match.report,
+            {
+              element: rule[rulesMainKey(mainKey)],
+              disallow: rule.disallow,
+              index: rule.index,
+              message: rule.message || options.message,
+            },
+          ];
         }
       }
       if (rule.allow) {
@@ -138,11 +154,19 @@ function elementRulesAllowDependency({
       }
       return allowed;
     },
-    [options.default === "allow", null]
+    [
+      options.default === "allow",
+      null,
+      {
+        isDefault: true,
+        message: options.message,
+      },
+    ]
   );
   return {
     result,
     report,
+    ruleReport,
   };
 }
 
@@ -155,4 +179,5 @@ module.exports = {
   elementRulesAllowDependency,
   getElementRules,
   rulesMainKey,
+  micromatchPatternReplacingObjectValues,
 };
diff --git a/src/helpers/settings.js b/src/helpers/settings.js
index d54ec07..dc846f4 100644
--- a/src/helpers/settings.js
+++ b/src/helpers/settings.js
@@ -1,7 +1,8 @@
 const { TYPES, ELEMENTS, VALID_MODES } = require("../constants/settings");
+const { isString } = require("./utils");
 
 function isLegacyType(type) {
-  return typeof type === "string";
+  return isString(type);
 }
 
 // TODO, remove in next major version
diff --git a/src/helpers/utils.js b/src/helpers/utils.js
new file mode 100644
index 0000000..7e41a9a
--- /dev/null
+++ b/src/helpers/utils.js
@@ -0,0 +1,30 @@
+function isString(object) {
+  return typeof object === "string";
+}
+
+function isArray(object) {
+  return Array.isArray(object);
+}
+
+function replaceObjectValueInTemplate(template, key, value, namespace) {
+  const keyToReplace = namespace ? `${namespace}.${key}` : key;
+  return template.replace(`\${${keyToReplace}}`, value);
+}
+
+function replaceObjectValuesInTemplates(strings, object, namespace) {
+  return Object.keys(object).reduce((result, objectKey) => {
+    // If template is an array, replace key by value in all patterns
+    if (isArray(result)) {
+      return result.map((resultEntry) => {
+        return replaceObjectValueInTemplate(resultEntry, objectKey, object[objectKey], namespace);
+      });
+    }
+    return replaceObjectValueInTemplate(result, objectKey, object[objectKey], namespace);
+  }, strings);
+}
+
+module.exports = {
+  isString,
+  isArray,
+  replaceObjectValuesInTemplates,
+};
diff --git a/src/helpers/validations.js b/src/helpers/validations.js
index b367e42..1454ac5 100644
--- a/src/helpers/validations.js
+++ b/src/helpers/validations.js
@@ -5,6 +5,7 @@ const { TYPES, ALIAS, ELEMENTS, VALID_MODES } = require("../constants/settings")
 const { getElementsTypeNames, isLegacyType } = require("./settings");
 const { rulesMainKey } = require("./rules");
 const { warnOnce } = require("./debug");
+const { isArray, isString } = require("./utils");
 
 const invalidMatchers = [];
 
@@ -41,9 +42,9 @@ function elementsMatcherSchema(matcherOptions = DEFAULT_MATCHER_OPTIONS) {
   };
 }
 
-function rulesOptionsSchema(options = {}) {
+function rulesOptionsSchema(options) {
   const mainKey = rulesMainKey(options.rulesMainKey);
-  return [
+  const schema = [
     {
       type: "object",
       properties: {
@@ -78,15 +79,26 @@ function rulesOptionsSchema(options = {}) {
       additionalProperties: false,
     },
   ];
+  if (options.customMessage) {
+    schema[0].properties.message = {
+      type: "string",
+    };
+  }
+  if (options.customRuleMessage) {
+    schema[0].properties.rules.items.properties.message = {
+      type: "string",
+    };
+  }
+  return schema;
 }
 
 function isValidElementTypesMatcher(matcher, settings) {
-  const mathcherToCheck = Array.isArray(matcher) ? matcher[0] : matcher;
+  const mathcherToCheck = isArray(matcher) ? matcher[0] : matcher;
   return !matcher || micromatch.some(getElementsTypeNames(settings), mathcherToCheck);
 }
 
 function validateElementTypesMatcher(elementsMatcher, settings) {
-  const [matcher] = Array.isArray(elementsMatcher) ? elementsMatcher : [elementsMatcher];
+  const [matcher] = isArray(elementsMatcher) ? elementsMatcher : [elementsMatcher];
   if (!invalidMatchers.includes(matcher) && !isValidElementTypesMatcher(matcher, settings)) {
     invalidMatchers.push(matcher);
     warnOnce(`Option '${matcher}' does not match any element type from '${ELEMENTS}' setting`);
@@ -94,7 +106,7 @@ function validateElementTypesMatcher(elementsMatcher, settings) {
 }
 
 function validateElements(elements) {
-  if (!elements || !Array.isArray(elements) || !elements.length) {
+  if (!elements || !isArray(elements) || !elements.length) {
     warnOnce(`Please provide element types using the '${ELEMENTS}' setting`);
     return;
   }
@@ -106,7 +118,7 @@ function validateElements(elements) {
       );
     } else {
       Object.keys(element).forEach(() => {
-        if (!element.type || typeof element.type !== "string") {
+        if (!element.type || !isString(element.type)) {
           warnOnce(`Please provide type in '${ELEMENTS}' setting`);
         }
         if (element.mode && !VALID_MODES.includes(element.mode)) {
@@ -116,13 +128,10 @@ function validateElements(elements) {
             )}. Default value "${VALID_MODES[0]}" will be used instead`
           );
         }
-        if (
-          !element.pattern ||
-          !(typeof element.pattern === "string" || Array.isArray(element.pattern))
-        ) {
+        if (!element.pattern || !(isString(element.pattern) || isArray(element.pattern))) {
           warnOnce(`Please provide a valid pattern in '${ELEMENTS}' setting`);
         }
-        if (element.capture && !Array.isArray(element.capture)) {
+        if (element.capture && !isArray(element.capture)) {
           warnOnce(`Invalid capture property in '${ELEMENTS}' setting`);
         }
       });
diff --git a/src/rules/element-types.js b/src/rules/element-types.js
index b6fa90c..a5a4373 100644
--- a/src/rules/element-types.js
+++ b/src/rules/element-types.js
@@ -8,6 +8,7 @@ const {
   isMatchElementType,
   elementRulesAllowDependency,
 } = require("../helpers/rules");
+const { customErrorMessage, ruleElementMessage, elementMessage } = require("../helpers/messages");
 
 function elementRulesAllowDependencyType(element, dependency, options) {
   return elementRulesAllowDependency({
@@ -15,28 +16,47 @@ function elementRulesAllowDependencyType(element, dependency, options) {
     dependency,
     options,
     isMatch: isMatchElementType,
-  }).result;
+  });
+}
+
+function errorMessage(ruleData, file, dependency) {
+  const ruleReport = ruleData.ruleReport;
+  if (ruleReport.message) {
+    return customErrorMessage(ruleReport.message, file, dependency);
+  }
+  if (ruleReport.isDefault) {
+    return `No rule allowing this dependency was found. File is ${elementMessage(
+      file
+    )}. Dependency is ${elementMessage(dependency)}`;
+  }
+  return `Importing ${ruleElementMessage(
+    ruleReport.disallow,
+    file.capturedValues
+  )} is not allowed in ${ruleElementMessage(
+    ruleReport.element,
+    file.capturedValues
+  )}. Disallowed in rule ${ruleReport.index + 1}`;
 }
 
 module.exports = dependencyRule(
   {
     ruleName: RULE_ELEMENT_TYPES,
     description: `Check allowed dependencies between element types`,
-    schema: rulesOptionsSchema(),
+    schema: rulesOptionsSchema({
+      customMessage: true,
+      customRuleMessage: true,
+    }),
   },
   function ({ dependency, file, node, context, options }) {
-    if (
-      dependency.isLocal &&
-      !dependency.isIgnored &&
-      dependency.type &&
-      !dependency.isInternal &&
-      !elementRulesAllowDependencyType(file, dependency, options)
-    ) {
-      context.report({
-        message: `Usage of '${dependency.type}' is not allowed in '${file.type}'`,
-        node: node,
-        ...dependencyLocation(node, context),
-      });
+    if (dependency.isLocal && !dependency.isIgnored && dependency.type && !dependency.isInternal) {
+      const ruleData = elementRulesAllowDependencyType(file, dependency, options);
+      if (!ruleData.result) {
+        context.report({
+          message: errorMessage(ruleData, file, dependency),
+          node: node,
+          ...dependencyLocation(node, context),
+        });
+      }
     }
   }
 );


Test patch:
diff --git a/test/rules/base-pattern/element-types.spec.js b/test/rules/base-pattern/element-types.spec.js
index 05cc9cf..b3a2081 100644
--- a/test/rules/base-pattern/element-types.spec.js
+++ b/test/rules/base-pattern/element-types.spec.js
@@ -1,11 +1,9 @@
 const { ELEMENT_TYPES: RULE } = require("../../../src/constants/rules");
 const { SETTINGS, createRuleTester, pathResolvers } = require("../../support/helpers");
+const { elementTypesNoRuleMessage } = require("../../support/messages");
 
 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");
@@ -140,7 +138,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("modules", "modules"),
+          message: elementTypesNoRuleMessage({
+            file: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a' and elementName 'module-a'",
+            dep: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b' and elementName 'module-c'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -152,7 +153,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("modules", "modules"),
+          message: elementTypesNoRuleMessage({
+            file: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a' and elementName 'module-h'",
+            dep: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b' and elementName 'module-c'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -164,7 +168,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("modules", "components"),
+          message: elementTypesNoRuleMessage({
+            file: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a' and elementName 'module-a'",
+            dep: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b', type 'atoms' and elementName 'atom-c'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -176,7 +183,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "modules"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a', type 'molecules' and elementName 'molecule-a'",
+            dep: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a' and elementName 'module-a'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -188,7 +198,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "modules"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a', type 'molecules' and elementName 'molecule-a'",
+            dep: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b' and elementName 'module-c'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -200,7 +213,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "components"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a', type 'molecules' and elementName 'molecule-a'",
+            dep: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b', type 'atoms' and elementName 'atom-c'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -214,7 +230,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "components"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a', type 'molecules' and elementName 'molecule-e'",
+            dep: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b', type 'atoms' and elementName 'atom-c'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -226,7 +245,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "modules"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b', type 'molecules' and elementName 'molecule-c'",
+            dep: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b' and elementName 'module-c'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -238,7 +260,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "modules"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with parentFolders 'test/fixtures/base-pattern', domain 'domain-b', type 'molecules' and elementName 'molecule-c'",
+            dep: "'modules' with parentFolders 'test/fixtures/base-pattern', domain 'domain-a' and elementName 'module-a'",
+          }),
           type: "ImportDeclaration",
         },
       ],
diff --git a/test/rules/docs-examples/element-types.spec.js b/test/rules/docs-examples/element-types.spec.js
index b935fff..3952166 100644
--- a/test/rules/docs-examples/element-types.spec.js
+++ b/test/rules/docs-examples/element-types.spec.js
@@ -1,11 +1,9 @@
 const { ELEMENT_TYPES: RULE } = require("../../../src/constants/rules");
 const { SETTINGS, createRuleTester, pathResolvers } = require("../../support/helpers");
+const { elementTypesNoRuleMessage } = require("../../support/messages");
 
 const rule = require(`../../../src/rules/${RULE}`);
 
-const errorMessage = (fileType, dependencyType) =>
-  `Usage of '${dependencyType}' is not allowed in '${fileType}'`;
-
 const { absoluteFilePath } = pathResolvers("docs-examples");
 
 const settings = SETTINGS.docsExamples;
@@ -103,7 +101,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("helpers", "components"),
+          message: elementTypesNoRuleMessage({
+            file: "'helpers' with category 'permissions' and elementName 'roles'",
+            dep: "'components' with family 'atoms' and elementName 'atom-a'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -115,7 +116,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("helpers", "modules"),
+          message: elementTypesNoRuleMessage({
+            file: "'helpers' with category 'permissions' and elementName 'roles'",
+            dep: "'modules' with elementName 'module-a'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -127,7 +131,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "components"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with family 'atoms' and elementName 'atom-a'",
+            dep: "'components' with family 'molecules' and elementName 'molecule-a'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -139,7 +146,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "helpers"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with family 'atoms' and elementName 'atom-a'",
+            dep: "'helpers' with category 'permissions' and elementName 'roles'",
+          }),
           type: "ImportDeclaration",
         },
       ],
@@ -151,7 +161,10 @@ ruleTester.run(RULE, rule, {
       options,
       errors: [
         {
-          message: errorMessage("components", "modules"),
+          message: elementTypesNoRuleMessage({
+            file: "'components' with family 'atoms' and elementName 'atom-a'",
+            dep: "'modules' with elementName 'module-a'",
+          }),
           type: "ImportDeclaration",
         },
       ],
diff --git a/test/rules/nestjs-example/element-types.spec.js b/test/rules/nestjs-example/element-types.spec.js
index 718c6bd..772a4a6 100644
--- a/test/rules/nestjs-example/element-types.spec.js
+++ b/test/rules/nestjs-example/element-types.spec.js
@@ -1,12 +1,10 @@
 const { ELEMENT_TYPES: RULE } = require("../../../src/constants/rules");
 const { createRuleTester, pathResolvers } = require("../../support/helpers");
+const { elementTypesErrorMessage, elementTypesNoRuleMessage } = require("../../support/messages");
 
 const rule = require(`../../../src/rules/${RULE}`);
 
-const errorMessage = (fileType, dependencyType) =>
-  `Usage of '${dependencyType}' is not allowed in '${fileType}'`;
-
-const test = (settings, options, { absoluteFilePath }) => {
+const test = (settings, options, { absoluteFilePath }, errorMessages, base) => {
   const ruleTester = createRuleTester(settings);
 
   ruleTester.run(RULE, rule, {
@@ -110,7 +108,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("app", "interface"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              0,
+              elementTypesNoRuleMessage({
+                file: "'app'",
+                dep: "'interface' with base '', feature 'cats' and fileName 'cats'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -122,7 +127,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("app", "controller"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              1,
+              elementTypesNoRuleMessage({
+                file: "'app'",
+                dep: "'controller' with base '', feature 'cats' and fileName 'cats'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -134,7 +146,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("app", "service"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              2,
+              elementTypesNoRuleMessage({
+                file: "'app'",
+                dep: "'service' with base '', feature 'cats' and fileName 'cats'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -146,7 +165,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("module", "controller"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              3,
+              elementTypesNoRuleMessage({
+                file: "'module' with base '', feature 'core' and fileName 'core'",
+                dep: "'controller' with base '', feature 'cats' and fileName 'cats'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -158,7 +184,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("module", "service"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              4,
+              elementTypesNoRuleMessage({
+                file: "'module' with base '', feature 'core' and fileName 'core'",
+                dep: "'service' with base '', feature 'cats' and fileName 'cats'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -170,7 +203,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("module", "interceptor"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              5,
+              elementTypesNoRuleMessage({
+                file: "'module' with base '', feature 'cats' and fileName 'cats'",
+                dep: "'interceptor' with base '', feature 'core' and fileName 'logging'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -182,7 +222,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("module", "interceptor"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              6,
+              elementTypesNoRuleMessage({
+                file: "'module' with base '', feature 'cats' and fileName 'cats'",
+                dep: "'interceptor' with base '', feature 'core' and fileName 'transform'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -194,7 +241,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("controller", "service"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              7,
+              elementTypesNoRuleMessage({
+                file: "'controller' with base '', feature 'core' and fileName 'core'",
+                dep: "'service' with base '', feature 'cats' and fileName 'cats'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -206,7 +260,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("controller", "dto"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              8,
+              elementTypesNoRuleMessage({
+                file: "'controller' with base '', feature 'core' and fileName 'core'",
+                dep: "'dto' with base '', feature 'cats' and fileName 'create-cat'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -218,7 +279,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("controller", "interface"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              9,
+              elementTypesNoRuleMessage({
+                file: "'controller' with base '', feature 'core' and fileName 'core'",
+                dep: "'interface' with base '', feature 'cats' and fileName 'cats'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -230,7 +298,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("controller", "model"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              10,
+              elementTypesNoRuleMessage({
+                file: "'controller' with base '', feature 'core' and fileName 'core'",
+                dep: `'model' with base '${base}', feature 'cats' and fileName 'persian-cat'`,
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -242,7 +317,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("controller", "model"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              11,
+              elementTypesNoRuleMessage({
+                file: "'controller' with base '', feature 'core' and fileName 'core'",
+                dep: `'model' with base '${base}', feature 'cats' and fileName 'siamese-cat'`,
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -254,7 +336,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("controller", "model"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              12,
+              elementTypesNoRuleMessage({
+                file: "'controller' with base '', feature 'cats' and fileName 'cats'",
+                dep: `'model' with base '${base}', feature 'core' and fileName 'core'`,
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -362,7 +451,9 @@ test(
     ],
   },
   ruleOptions,
-  pathResolvers("nestjs-example")
+  pathResolvers("nestjs-example"),
+  {},
+  "test/fixtures/nestjs-example"
 );
 
 test(
@@ -435,7 +526,14 @@ test(
     ],
   },
   ruleOptions,
-  pathResolvers("nestjs-example")
+  pathResolvers("nestjs-example"),
+  {
+    12: elementTypesNoRuleMessage({
+      file: "'controller' with base '', feature 'cats' and fileName 'cats'",
+      dep: `'model' with base 'nestjs-example', feature 'core' and fileName 'core'`,
+    }),
+  },
+  ""
 );
 
 test(
@@ -508,5 +606,12 @@ test(
     ],
   },
   ruleOptions,
-  pathResolvers("nestjs-example")
+  pathResolvers("nestjs-example"),
+  {
+    12: elementTypesNoRuleMessage({
+      file: "'controller' with base '', feature 'cats' and fileName 'cats'",
+      dep: `'model' with base '', feature 'core' and fileName 'core'`,
+    }),
+  },
+  "test/fixtures/nestjs-example"
 );
diff --git a/test/rules/one-level/element-types.spec.js b/test/rules/one-level/element-types.spec.js
index 8b2ae14..3b861c5 100644
--- a/test/rules/one-level/element-types.spec.js
+++ b/test/rules/one-level/element-types.spec.js
@@ -1,14 +1,12 @@
 const { ELEMENT_TYPES: RULE } = require("../../../src/constants/rules");
 const { SETTINGS, createRuleTester, pathResolvers } = require("../../support/helpers");
+const { elementTypesErrorMessage, elementTypesNoRuleMessage } = require("../../support/messages");
 
 const rule = require(`../../../src/rules/${RULE}`);
 
 const { absoluteFilePath, codeFilePath } = pathResolvers("one-level");
 
-const errorMessage = (fileType, dependencyType) =>
-  `Usage of '${dependencyType}' is not allowed in '${fileType}'`;
-
-const test = (settings, options) => {
+const test = (settings, options, errorMessages) => {
   const ruleTester = createRuleTester(settings);
 
   ruleTester.run(RULE, rule, {
@@ -180,7 +178,14 @@ const test = (settings, options) => {
         ],
         errors: [
           {
-            message: errorMessage("helpers", "helpers"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              0,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-a'",
+                dep: "'helpers' with elementName 'helper-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -192,7 +197,14 @@ const test = (settings, options) => {
         options,
         errors: [
           {
-            message: errorMessage("helpers", "helpers"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              1,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-a'",
+                dep: "'helpers' with elementName 'helper-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -204,7 +216,14 @@ const test = (settings, options) => {
         options,
         errors: [
           {
-            message: errorMessage("helpers", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              2,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-a'",
+                dep: "'components' with elementName 'component-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -216,7 +235,14 @@ const test = (settings, options) => {
         options,
         errors: [
           {
-            message: errorMessage("helpers", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              3,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-a'",
+                dep: "'modules' with elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -228,7 +254,14 @@ const test = (settings, options) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              4,
+              elementTypesNoRuleMessage({
+                file: "'components' with elementName 'component-a'",
+                dep: "'modules' with elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -237,7 +270,7 @@ const test = (settings, options) => {
   });
 };
 
-const testCapture = (settings, options) => {
+const testCapture = (settings, options, errorMessages) => {
   const ruleTester = createRuleTester(settings);
 
   ruleTester.run(RULE, rule, {
@@ -281,7 +314,14 @@ const testCapture = (settings, options) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "helpers"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              0,
+              elementTypesNoRuleMessage({
+                file: "'components' with elementName 'component-a'",
+                dep: "'helpers' with elementName 'helper-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -293,7 +333,14 @@ const testCapture = (settings, options) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "helpers"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              1,
+              elementTypesNoRuleMessage({
+                file: "'components' with elementName 'component-a'",
+                dep: "'helpers' with elementName 'helper-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -305,7 +352,14 @@ const testCapture = (settings, options) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              2,
+              elementTypesNoRuleMessage({
+                file: "'components' with elementName 'component-b'",
+                dep: "'components' with elementName 'component-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -317,7 +371,14 @@ const testCapture = (settings, options) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "helpers"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              3,
+              elementTypesNoRuleMessage({
+                file: "'modules' with elementName 'module-a'",
+                dep: "'helpers' with elementName 'helper-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -328,21 +389,25 @@ const testCapture = (settings, options) => {
 
 // deprecated settings
 
-test(SETTINGS.deprecated, [
-  {
-    default: "disallow",
-    rules: [
-      {
-        from: "components",
-        allow: ["helpers", "components"],
-      },
-      {
-        from: "modules",
-        allow: ["helpers", "components", "modules"],
-      },
-    ],
-  },
-]);
+test(
+  SETTINGS.deprecated,
+  [
+    {
+      default: "disallow",
+      rules: [
+        {
+          from: "components",
+          allow: ["helpers", "components"],
+        },
+        {
+          from: "modules",
+          allow: ["helpers", "components", "modules"],
+        },
+      ],
+    },
+  ],
+  {}
+);
 
 // settings with no capture option
 test(
@@ -377,117 +442,268 @@ test(
         },
       ],
     },
-  ]
+  ],
+  {
+    0: elementTypesNoRuleMessage({
+      file: "'helpers'",
+      dep: "'helpers'",
+    }),
+    1: "Importing elements of type 'modules', or elements of type 'components', or elements of type 'helpers' is not allowed in elements of type 'helpers'. Disallowed in rule 1",
+    2: "Importing elements of type 'modules', or elements of type 'components', or elements of type 'helpers' is not allowed in elements of type 'helpers'. Disallowed in rule 1",
+    3: "Importing elements of type 'modules', or elements of type 'components', or elements of type 'helpers' is not allowed in elements of type 'helpers'. Disallowed in rule 1",
+    4: "Importing elements of type 'modules' is not allowed in elements of type 'components'. Disallowed in rule 2",
+  }
 );
 
 // disallow-based options
 
-test(SETTINGS.oneLevel, [
-  {
-    default: "disallow",
-    rules: [
-      {
-        from: "components",
-        allow: ["helpers", "components"],
-      },
-      {
-        from: "modules",
-        allow: ["helpers", "components", "modules"],
-      },
-    ],
-  },
-]);
+test(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      rules: [
+        {
+          from: "components",
+          allow: ["helpers", "components"],
+        },
+        {
+          from: "modules",
+          allow: ["helpers", "components", "modules"],
+        },
+      ],
+    },
+  ],
+  {}
+);
 
 // micromatch-based options
 
-test(SETTINGS.oneLevel, [
-  {
-    default: "disallow",
-    rules: [
-      {
-        from: "c*",
-        allow: ["h*", "c*"],
-      },
-      {
-        from: "m*",
-        allow: ["h*", "c*", "m*"],
-      },
-    ],
-  },
-]);
+test(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      rules: [
+        {
+          from: "c*",
+          allow: ["h*", "c*"],
+        },
+        {
+          from: "m*",
+          allow: ["h*", "c*", "m*"],
+        },
+      ],
+    },
+  ],
+  {}
+);
 
 // allow-based options
-test(SETTINGS.oneLevel, [
+test(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "allow",
+      rules: [
+        {
+          from: "helpers",
+          disallow: ["modules", "components", "helpers"],
+        },
+        {
+          from: "components",
+          disallow: ["modules"],
+        },
+      ],
+    },
+  ],
   {
-    default: "allow",
-    rules: [
-      {
-        from: "helpers",
-        disallow: ["modules", "components", "helpers"],
-      },
-      {
-        from: "components",
-        disallow: ["modules"],
-      },
-    ],
-  },
-]);
+    1: "Importing elements of type 'modules', or elements of type 'components', or elements of type 'helpers' is not allowed in elements of type 'helpers'. Disallowed in rule 1",
+    2: "Importing elements of type 'modules', or elements of type 'components', or elements of type 'helpers' is not allowed in elements of type 'helpers'. Disallowed in rule 1",
+    3: "Importing elements of type 'modules', or elements of type 'components', or elements of type 'helpers' is not allowed in elements of type 'helpers'. Disallowed in rule 1",
+    4: "Importing elements of type 'modules' is not allowed in elements of type 'components'. Disallowed in rule 2",
+  }
+);
 
 // capture options
 
-testCapture(SETTINGS.oneLevel, [
+testCapture(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      rules: [
+        {
+          from: "components",
+          allow: [["helpers", { elementName: "helper-a" }], "components"],
+          disallow: [["components", { elementName: "component-a" }]],
+        },
+        {
+          from: "modules",
+          allow: [["helpers", { elementName: "helper-a" }], "components", "modules"],
+        },
+      ],
+    },
+  ],
   {
-    default: "disallow",
-    rules: [
-      {
-        from: "components",
-        allow: [["helpers", { elementName: "helper-a" }], "components"],
-        disallow: [["components", { elementName: "component-a" }]],
-      },
-      {
-        from: "modules",
-        allow: [["helpers", { elementName: "helper-a" }], "components", "modules"],
-      },
-    ],
-  },
-]);
+    2: "Importing elements of type 'components' with elementName 'component-a' is not allowed in elements of type 'components'. Disallowed in rule 1",
+  }
+);
 
 // capture options with micromatch negative expression
 
-testCapture(SETTINGS.oneLevel, [
-  {
-    default: "disallow",
-    rules: [
-      {
-        from: "components",
-        allow: [
-          ["helpers", { elementName: "helper-a" }],
-          ["components", { elementName: "!component-a" }],
-        ],
-      },
-      {
-        from: "modules",
-        allow: [["helpers", { elementName: "helper-a" }], "components", "modules"],
-      },
-    ],
-  },
-]);
+testCapture(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      rules: [
+        {
+          from: "components",
+          allow: [
+            ["helpers", { elementName: "helper-a" }],
+            ["components", { elementName: "!component-a" }],
+          ],
+        },
+        {
+          from: "modules",
+          allow: [["helpers", { elementName: "helper-a" }], "components", "modules"],
+        },
+      ],
+    },
+  ],
+  {}
+);
 
 // capture options with micromatch
 
-testCapture(SETTINGS.oneLevel, [
+testCapture(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      rules: [
+        {
+          from: "c*",
+          allow: [["helpers", { elementName: "*-a" }], "c*"],
+          disallow: [["c*", { elementName: "*-a" }]],
+        },
+        {
+          from: "modules",
+          allow: [["h*", { elementName: "*-a" }], "c*", "m*"],
+        },
+      ],
+    },
+  ],
   {
-    default: "disallow",
-    rules: [
-      {
-        from: "c*",
-        allow: [["helpers", { elementName: "*-a" }], "c*"],
-        disallow: [["c*", { elementName: "*-a" }]],
-      },
-      {
-        from: "modules",
-        allow: [["h*", { elementName: "*-a" }], "c*", "m*"],
-      },
-    ],
-  },
-]);
+    2: "Importing elements of type 'c*' with elementName '*-a' is not allowed in elements of type 'c*'. Disallowed in rule 1",
+  }
+);
+
+// Custom error message
+
+testCapture(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      message:
+        "Importing ${dependency.type} with name ${dependency.elementName} is not allowed in ${file.type} with name ${file.elementName}",
+      rules: [
+        {
+          from: "c*",
+          allow: [["helpers", { elementName: "*-a" }], "c*"],
+          disallow: [["c*", { elementName: "*-a" }]],
+          message:
+            "Do not import ${dependency.type} named ${dependency.elementName} from ${file.type} named ${file.elementName}",
+        },
+        {
+          from: "modules",
+          allow: [["h*", { elementName: "*-a" }], "c*", "m*"],
+        },
+      ],
+    },
+  ],
+  {
+    0: "Importing helpers with name helper-b is not allowed in components with name component-a",
+    1: "Importing helpers with name helper-b is not allowed in components with name component-a",
+    2: "Do not import components named component-a from components named component-b",
+    3: "Importing helpers with name helper-b is not allowed in modules with name module-a",
+  }
+);
+
+// Custom error message default
+
+testCapture(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      message:
+        "Importing ${dependency.type} with name ${dependency.elementName} is not allowed in ${file.type} with name ${file.elementName}",
+      rules: [
+        {
+          from: "c*",
+          allow: [["helpers", { elementName: "*-a" }], "c*"],
+          disallow: [["c*", { elementName: "*-a" }]],
+        },
+        {
+          from: "modules",
+          allow: [["h*", { elementName: "*-a" }], "c*", "m*"],
+        },
+      ],
+    },
+  ],
+  {
+    0: "Importing helpers with name helper-b is not allowed in components with name component-a",
+    1: "Importing helpers with name helper-b is not allowed in components with name component-a",
+    2: "Importing components with name component-a is not allowed in components with name component-b",
+    3: "Importing helpers with name helper-b is not allowed in modules with name module-a",
+  }
+);
+
+testCapture(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      rules: [
+        {
+          from: "c*",
+          allow: [["helpers", { elementName: "*-a" }], "c*"],
+          disallow: [["c*", { elementName: ["*-a", "component-a", "*t-a"] }]],
+        },
+        {
+          from: "modules",
+          allow: [["h*", { elementName: "*-a" }], "c*", "m*"],
+        },
+      ],
+    },
+  ],
+  {
+    2: "Importing elements of type 'c*' with elementName '*-a', 'component-a' or '*t-a' is not allowed in elements of type 'c*'. Disallowed in rule 1",
+  }
+);
+
+testCapture(
+  SETTINGS.oneLevel,
+  [
+    {
+      default: "disallow",
+      rules: [
+        {
+          from: ["c*"],
+          allow: [["helpers", { elementName: "*-a" }], "c*"],
+          disallow: [["c*", { elementName: ["*-a"] }]],
+        },
+        {
+          from: "modules",
+          allow: [["h*", { elementName: "*-a" }], "c*", "m*"],
+        },
+      ],
+    },
+  ],
+  {
+    2: "Importing elements of type 'c*' with elementName '*-a' is not allowed in elements of type 'c*'. Disallowed in rule 1",
+  }
+);
diff --git a/test/rules/one-level/invalid-settings.spec.js b/test/rules/one-level/invalid-settings.spec.js
index ccc0bfe..c185a0d 100644
--- a/test/rules/one-level/invalid-settings.spec.js
+++ b/test/rules/one-level/invalid-settings.spec.js
@@ -1,14 +1,12 @@
 const { ELEMENT_TYPES: RULE } = require("../../../src/constants/rules");
 const { SETTINGS, createRuleTester, pathResolvers } = require("../../support/helpers");
+const { elementTypesErrorMessage, elementTypesNoRuleMessage } = require("../../support/messages");
 
 const rule = require(`../../../src/rules/${RULE}`);
 
 const { absoluteFilePath } = pathResolvers("one-level");
 
-const errorMessage = (fileType, dependencyType) =>
-  `Usage of '${dependencyType}' is not allowed in '${fileType}'`;
-
-const test = (settings, options) => {
+const test = (settings, options, errorMessages) => {
   const ruleTester = createRuleTester(settings);
 
   ruleTester.run(RULE, rule, {
@@ -62,7 +60,14 @@ const test = (settings, options) => {
         ],
         errors: [
           {
-            message: errorMessage("helpers", "helpers"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              0,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-a'",
+                dep: "'helpers' with elementName 'helper-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -82,7 +87,8 @@ test(
     {
       default: "disallow",
     },
-  ]
+  ],
+  {}
 );
 
 // no type
@@ -97,7 +103,8 @@ test(
       },
     ],
   },
-  []
+  [],
+  {}
 );
 
 // no valid mode
@@ -111,7 +118,8 @@ test(
       },
     ],
   },
-  []
+  [],
+  {}
 );
 
 // no valid capture
@@ -126,5 +134,6 @@ test(
       },
     ],
   },
-  []
+  [],
+  {}
 );
diff --git a/test/rules/two-levels/element-types.spec.js b/test/rules/two-levels/element-types.spec.js
index 6c13914..c9882d5 100644
--- a/test/rules/two-levels/element-types.spec.js
+++ b/test/rules/two-levels/element-types.spec.js
@@ -1,12 +1,10 @@
 const { ELEMENT_TYPES: RULE } = require("../../../src/constants/rules");
 const { SETTINGS, createRuleTester, pathResolvers } = require("../../support/helpers");
+const { elementTypesErrorMessage, elementTypesNoRuleMessage } = require("../../support/messages");
 
 const rule = require(`../../../src/rules/${RULE}`);
 
-const errorMessage = (fileType, dependencyType) =>
-  `Usage of '${dependencyType}' is not allowed in '${fileType}'`;
-
-const test = (settings, options, { absoluteFilePath }) => {
+const test = (settings, options, { absoluteFilePath }, errorMessages) => {
   const ruleTester = createRuleTester(settings);
 
   ruleTester.run(RULE, rule, {
@@ -172,7 +170,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("helpers", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              0,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-a'",
+                dep: "'components' with category 'atoms' and elementName 'atom-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -184,7 +189,15 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("helpers", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              1,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-a'",
+                dep: "'modules' with domain 'domain-a' and elementName 'module-a'",
+              })
+            ),
+
             type: "ImportDeclaration",
           },
         ],
@@ -196,7 +209,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              2,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'atoms' and elementName 'atom-a'",
+                dep: "'components' with category 'molecules' and elementName 'molecule-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -208,7 +228,15 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              3,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'atoms' and elementName 'atom-a'",
+                dep: "'components' with category 'layouts' and elementName 'layout-a'",
+              })
+            ),
+
             type: "ImportDeclaration",
           },
         ],
@@ -220,7 +248,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              4,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'atoms' and elementName 'atom-a'",
+                dep: "'components' with category 'layouts' and elementName 'layout-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -232,7 +267,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              5,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'molecules' and elementName 'molecule-a'",
+                dep: "'components' with category 'layouts' and elementName 'layout-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -244,7 +286,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              6,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'molecules' and elementName 'molecule-a'",
+                dep: "'modules' with domain 'domain-a' and elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -256,7 +305,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              7,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'layouts' and elementName 'layout-a'",
+                dep: "'components' with category 'atoms' and elementName 'atom-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -268,7 +324,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              8,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'layouts' and elementName 'layout-a'",
+                dep: "'modules' with domain 'domain-a' and elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -280,7 +343,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              9,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'pages' and elementName 'page-a'",
+                dep: "'components' with category 'atoms' and elementName 'atom-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -292,7 +362,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              10,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'pages' and elementName 'page-a'",
+                dep: "'components' with category 'molecules' and elementName 'molecule-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -304,7 +381,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              11,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'pages' and elementName 'page-a'",
+                dep: "'modules' with domain 'pages' and elementName 'page-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -316,7 +400,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              12,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'pages' and elementName 'page-a'",
+                dep: "'modules' with domain 'domain-a' and elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -328,7 +419,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              13,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'pages' and elementName 'page-a'",
+                dep: "'modules' with domain 'domain-a' and elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -341,7 +439,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              14,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a' and elementName 'module-b'",
+                dep: "'components' with category 'atoms' and elementName 'atom-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -353,7 +458,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              15,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a' and elementName 'module-b'",
+                dep: "'components' with category 'atoms' and elementName 'atom-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -365,7 +477,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              16,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a' and elementName 'module-a'",
+                dep: "'components' with category 'molecules' and elementName 'molecule-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -377,7 +496,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              17,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a' and elementName 'module-a'",
+                dep: "'modules' with domain 'pages' and elementName 'page-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -389,7 +515,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              18,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a' and elementName 'module-a'",
+                dep: "'modules' with domain 'domain-b' and elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -401,7 +534,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              19,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a' and elementName 'module-a'",
+                dep: "'modules' with domain 'domain-b' and elementName 'module-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -413,7 +553,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              20,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-b' and elementName 'module-a'",
+                dep: "'components' with category 'layouts' and elementName 'layout-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -425,7 +572,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              21,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-b' and elementName 'module-a'",
+                dep: "'components' with category 'molecules' and elementName 'molecule-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -437,7 +591,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              22,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-b' and elementName 'module-a'",
+                dep: "'modules' with domain 'pages' and elementName 'page-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -449,7 +610,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              23,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-b' and elementName 'module-a'",
+                dep: "'components' with category 'atoms' and elementName 'atom-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -461,7 +629,14 @@ const test = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              24,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a' and elementName 'module-a'",
+                dep: "'components' with category 'atoms' and elementName 'atom-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -470,7 +645,7 @@ const test = (settings, options, { absoluteFilePath }) => {
   });
 };
 
-const testPrivate = (settings, options, { absoluteFilePath }) => {
+const testPrivate = (settings, options, { absoluteFilePath }, errorMessages) => {
   const ruleTester = createRuleTester(settings);
 
   ruleTester.run(RULE, rule, {
@@ -586,7 +761,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("helpers", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              0,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-c'",
+                dep: "'components' with category 'atoms' and elementName 'atom-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -598,7 +780,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("helpers", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              1,
+              elementTypesNoRuleMessage({
+                file: "'helpers' with elementName 'helper-c'",
+                dep: "'modules' with domain 'domain-a' and elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -610,7 +799,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              2,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'atoms' and elementName 'atom-a'",
+                dep: "'components' with category 'molecules' and elementName 'molecule-c'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -622,7 +818,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              3,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'atoms' and elementName 'atom-a'",
+                dep: "'components' with category 'molecules' and elementName 'molecule-d'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -636,7 +839,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              4,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'molecules' and elementName 'molecule-c'",
+                dep: "'components' with category 'layouts' and elementName 'layout-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -650,7 +860,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              5,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'molecules' and elementName 'molecule-c'",
+                dep: "'modules' with domain 'domain-a' and elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -662,7 +879,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("components", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              6,
+              elementTypesNoRuleMessage({
+                file: "'components' with category 'layouts' and elementName 'layout-a'",
+                dep: "'modules' with domain 'domain-a', ancestorsPaths 'module-a' and elementName 'module-c'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -674,7 +898,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              7,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'pages' and elementName 'page-a'",
+                dep: "'components' with category 'molecules' and elementName 'molecule-c'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -686,7 +917,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              8,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'pages' and elementName 'page-a'",
+                dep: "'modules' with domain 'domain-a', ancestorsPaths 'module-a' and elementName 'module-c'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -698,7 +936,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "components"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              9,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a', ancestorsPaths 'module-a' and elementName 'module-c'",
+                dep: "'components' with category 'molecules' and elementName 'molecule-c'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -710,7 +955,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              10,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a', ancestorsPaths 'module-a' and elementName 'module-c'",
+                dep: "'modules' with domain 'pages' and elementName 'page-b'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -722,7 +974,14 @@ const testPrivate = (settings, options, { absoluteFilePath }) => {
         options,
         errors: [
           {
-            message: errorMessage("modules", "modules"),
+            message: elementTypesErrorMessage(
+              errorMessages,
+              11,
+              elementTypesNoRuleMessage({
+                file: "'modules' with domain 'domain-a', ancestorsPaths 'module-a' and elementName 'module-c'",
+                dep: "'modules' with domain 'domain-b' and elementName 'module-a'",
+              })
+            ),
             type: "ImportDeclaration",
           },
         ],
@@ -784,7 +1043,8 @@ test(
       ],
     },
   ],
-  pathResolvers("two-levels")
+  pathResolvers("two-levels"),
+  {}
 );
 
 testPrivate(
@@ -850,5 +1110,72 @@ testPrivate(
       ],
     },
   ],
-  pathResolvers("two-levels-with-private")
+  pathResolvers("two-levels-with-private"),
+  {}
+);
+
+testPrivate(
+  SETTINGS.twoLevelsWithPrivate,
+  [
+    {
+      default: "allow",
+      rules: [
+        {
+          from: [["helpers", { elementName: "helper-c" }]],
+          disallow: [["components", { category: "atoms", elementName: "*-a" }]],
+        },
+        {
+          from: [["helpers", { elementName: "helper-c" }]],
+          disallow: [["modules", { domain: "domain-a", elementName: "*-a" }]],
+        },
+        {
+          from: [["components", { category: "atoms" }]],
+          disallow: [["components", { category: "molecules" }]],
+        },
+        {
+          from: [["components", { category: "molecules" }]],
+          disallow: [["components", { category: "layouts" }]],
+        },
+        {
+          from: [["components", { category: "*" }]],
+          disallow: ["modules"],
+        },
+        {
+          from: [["modules", { domain: "pages" }]],
+          disallow: [["modules", { domain: "domain-a" }]],
+        },
+        {
+          from: [["modules", { domain: "pages" }]],
+          disallow: [["components", { category: "molecules" }]],
+        },
+        {
+          from: [["modules", { domain: "domain-a" }]],
+          disallow: [["components", { category: "molecules" }]],
+        },
+        {
+          from: [["modules", { domain: "domain-a" }]],
+          disallow: [["modules", { domain: "pages" }]],
+        },
+        {
+          from: [["modules", { domain: "domain-a" }]],
+          disallow: [["modules", { domain: ["!${domain}", "domain-b"] }]],
+        },
+      ],
+    },
+  ],
+  pathResolvers("two-levels-with-private"),
+  {
+    0: "Importing elements of type 'components' with category 'atoms' and elementName '*-a' is not allowed in elements of type 'helpers' with elementName 'helper-c'. Disallowed in rule 1",
+    1: "Importing elements of type 'modules' with domain 'domain-a' and elementName '*-a' is not allowed in elements of type 'helpers' with elementName 'helper-c'. Disallowed in rule 2",
+    2: "Importing elements of type 'components' with category 'molecules' is not allowed in elements of type 'components' with category 'atoms'. Disallowed in rule 3",
+    3: "Importing elements of type 'components' with category 'molecules' is not allowed in elements of type 'components' with category 'atoms'. Disallowed in rule 3",
+    4: "Importing elements of type 'components' with category 'layouts' is not allowed in elements of type 'components' with category 'molecules'. Disallowed in rule 4",
+    5: "Importing elements of type 'modules' is not allowed in elements of type 'components' with category '*'. Disallowed in rule 5",
+    6: "Importing elements of type 'modules' is not allowed in elements of type 'components' with category '*'. Disallowed in rule 5",
+    7: "Importing elements of type 'components' with category 'molecules' is not allowed in elements of type 'modules' with domain 'pages'. Disallowed in rule 7",
+    8: "Importing elements of type 'modules' with domain 'domain-a' is not allowed in elements of type 'modules' with domain 'pages'. Disallowed in rule 6",
+    9: "Importing elements of type 'components' with category 'molecules' is not allowed in elements of type 'modules' with domain 'domain-a'. Disallowed in rule 8",
+    10: "Importing elements of type 'modules' with domain '!domain-a' or 'domain-b' is not allowed in elements of type 'modules' with domain 'domain-a'. Disallowed in rule 10",
+    11: "Importing elements of type 'modules' with domain '!domain-a' or 'domain-b' is not allowed in elements of type 'modules' with domain 'domain-a'. Disallowed in rule 10",
+  }
 );
diff --git a/test/support/messages.js b/test/support/messages.js
new file mode 100644
index 0000000..82cf6a2
--- /dev/null
+++ b/test/support/messages.js
@@ -0,0 +1,16 @@
+function errorMessage(errors, index, defaultMessage) {
+  return errors[index] || defaultMessage;
+}
+
+function elementTypesErrorMessage(errors, index, defaultMessage) {
+  return errorMessage(errors, index, defaultMessage);
+}
+
+function elementTypesNoRuleMessage({ file, dep }) {
+  return `No rule allowing this dependency was found. File is of type ${file}. Dependency is of type ${dep}`;
+}
+
+module.exports = {
+  elementTypesErrorMessage,
+  elementTypesNoRuleMessage,
+};