Repository: javierbrea/eslint-plugin-boundaries
Pull request #326 (2024-01-16 18:00:05)

feat: Add require dependency node
### Added
- feat: Add `require` dependency node, enabling to analyze dependencies in `require(...)` calls
- chore: Lint code using eslint-plugin-boundaries in its own codebase


Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index a81cbb3..94615b7 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,4 +1,5 @@
 module.exports = {
+  root: true,
   env: {
     node: true,
     es6: true,
@@ -6,7 +7,8 @@ module.exports = {
   parserOptions: {
     ecmaVersion: 2022,
   },
-  plugins: ["prettier"],
+  extends: ["prettier"],
+  plugins: ["prettier", "eslint-plugin-local-rules"],
   rules: {
     "prettier/prettier": [
       "error",
@@ -19,6 +21,95 @@ module.exports = {
     "no-undef": "error",
     "no-unused-vars": ["error", { vars: "all", args: "after-used", ignoreRestSiblings: false }],
   },
-  extends: ["prettier"],
-  root: true,
+  overrides: [
+    {
+      files: ["src/**/*.js"],
+      settings: {
+        "boundaries/dependency-nodes": ["require", "import", "dynamic-import", "export"],
+        "boundaries/elements": [
+          {
+            type: "config",
+            mode: "file",
+            pattern: ["src/configs/*.js", "(package.json)"],
+            capture: ["name"],
+          },
+          {
+            type: "constants",
+            mode: "file",
+            pattern: "src/constants/*.js",
+            capture: ["name"],
+          },
+          {
+            type: "core",
+            mode: "file",
+            pattern: "src/core/*.js",
+            capture: ["name"],
+          },
+          {
+            type: "helper",
+            mode: "file",
+            pattern: "src/helpers/*.js",
+            capture: ["name"],
+          },
+          {
+            type: "rule",
+            mode: "file",
+            pattern: "src/rules/*.js",
+            capture: ["name"],
+          },
+          {
+            type: "rule-factory",
+            mode: "file",
+            pattern: "src/rules-factories/*.js",
+            capture: ["name"],
+          },
+          {
+            type: "plugin",
+            mode: "full",
+            pattern: ["src/index.js"],
+          },
+        ],
+      },
+      rules: {
+        "local-rules/boundaries/element-types": [
+          2,
+          {
+            default: "disallow",
+            rules: [
+              {
+                from: "plugin",
+                allow: ["constants", "config", "rule"],
+              },
+              {
+                from: "config",
+                allow: ["constants", "config"],
+              },
+              {
+                from: "constants",
+                allow: ["constants"],
+              },
+              {
+                from: "core",
+                allow: ["constants", "helper", "core"],
+              },
+              {
+                from: "helper",
+                allow: ["constants", "helper"],
+              },
+              {
+                from: "rule",
+                allow: ["constants", "helper", "core", "rule-factory"],
+              },
+              {
+                from: "rule-factory",
+                allow: ["constants", "helper", "core"],
+              },
+            ],
+          },
+        ],
+        "local-rules/boundaries/no-unknown": [2],
+        "local-rules/boundaries/no-unknown-files": [2],
+      },
+    },
+  ],
 };
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe45dfc..8130b31 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
 ### Removed
 ### BREAKING CHANGES
 
+## [4.2.0] - 2024-01-16
+
+### Added
+- feat: Add `require` dependency node, enabling to analyze dependencies in `require(...)` calls
+- chore: Lint code using eslint-plugin-boundaries in its own codebase
+
 ## [4.1.0] - 2024-01-13
 
 ### Changed
diff --git a/README.md b/README.md
index 750f953..16ea327 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ In words of Robert C. Martin, _"Software architecture is the art of drawing line
 
 __This plugin ensures that your architecture boundaries are respected by the elements in your project__ checking the folders and files structure and the dependencies between them. __It is not a replacement for [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import), on the contrary, the combination of both plugins is recommended.__
 
-By default, __the plugin works by checking `import` statements, but it is also able to analyze exports, dynamic imports, and can be configured to check any other [AST nodes](https://eslint.org/docs/latest/extend/selectors)__. (_Read the [main rules overview](#main-rules-overview) and [configuration](#configuration) chapters for better comprehension_)
+By default, __the plugin works by checking `import` statements, but it is also able to analyze "require", "exports" and dynamic imports, and can be configured to check any other [AST nodes](https://eslint.org/docs/latest/extend/selectors)__. (_Read the [main rules overview](#main-rules-overview) and [configuration](#configuration) chapters for better comprehension_)
 
 ## Table of Contents
 
@@ -207,6 +207,7 @@ This setting allows to modify built-in default dependency nodes. By default, the
 
 The setting should be an array of the following strings:
 
+* `'require'`: analyze `require` statements.
 * `'import'`: analyze `import` statements.
 * `'export'`: analyze `export` statements.
 * `'dynamic-import'`: analyze [dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) statements.
diff --git a/eslint-local-rules.js b/eslint-local-rules.js
new file mode 100644
index 0000000..f2d221f
--- /dev/null
+++ b/eslint-local-rules.js
@@ -0,0 +1,8 @@
+const eslintPluginBoundaries = require("./src/index.js");
+
+module.exports = Object.keys(eslintPluginBoundaries.rules).reduce((rules, ruleName) => {
+  return {
+    ...rules,
+    [`boundaries/${ruleName}`]: eslintPluginBoundaries.rules[ruleName],
+  };
+}, {});
diff --git a/package-lock.json b/package-lock.json
index df21462..8eae492 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "eslint-plugin-boundaries",
-  "version": "4.1.0",
+  "version": "4.2.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "eslint-plugin-boundaries",
-      "version": "4.1.0",
+      "version": "4.2.0",
       "license": "MIT",
       "dependencies": {
         "chalk": "4.1.2",
@@ -22,6 +22,7 @@
         "eslint": "8.52.0",
         "eslint-config-prettier": "9.0.0",
         "eslint-import-resolver-typescript": "3.6.1",
+        "eslint-plugin-local-rules": "2.0.1",
         "eslint-plugin-prettier": "5.0.1",
         "husky": "8.0.3",
         "is-ci": "3.0.1",
@@ -3032,6 +3033,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/eslint-plugin-local-rules": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-2.0.1.tgz",
+      "integrity": "sha512-AJhGd+GcI5r2dbjiGPixM8jnBl0XFxqoVbqzwKbYz+nTk+Cj5dNE3+OlhC176bl5r25KsGsIthLi1VqIW5Ga+A==",
+      "dev": true
+    },
     "node_modules/eslint-plugin-prettier": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
@@ -9258,6 +9265,12 @@
         }
       }
     },
+    "eslint-plugin-local-rules": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-2.0.1.tgz",
+      "integrity": "sha512-AJhGd+GcI5r2dbjiGPixM8jnBl0XFxqoVbqzwKbYz+nTk+Cj5dNE3+OlhC176bl5r25KsGsIthLi1VqIW5Ga+A==",
+      "dev": true
+    },
     "eslint-plugin-prettier": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
diff --git a/package.json b/package.json
index ffebb9c..4cd2748 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-boundaries",
-  "version": "4.1.0",
+  "version": "4.2.0",
   "description": "Eslint plugin checking architecture boundaries between elements",
   "keywords": [
     "eslint",
@@ -21,6 +21,7 @@
   ],
   "main": "index.js",
   "scripts": {
+    "eslint": "eslint",
     "lint": "eslint src *.js test",
     "lint-staged": "lint-staged",
     "test": "jest",
@@ -45,6 +46,7 @@
     "eslint-config-prettier": "9.0.0",
     "eslint-import-resolver-typescript": "3.6.1",
     "eslint-plugin-prettier": "5.0.1",
+    "eslint-plugin-local-rules": "2.0.1",
     "husky": "8.0.3",
     "is-ci": "3.0.1",
     "jest": "29.7.0",
diff --git a/sonar-project.properties b/sonar-project.properties
index 6a22222..d6bc9a7 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -1,11 +1,11 @@
 sonar.organization=javierbrea
 sonar.projectKey=javierbrea_eslint-plugin-boundaries
-sonar.projectVersion=4.1.0
+sonar.projectVersion=4.2.0
 
 sonar.javascript.file.suffixes=.js
 sonar.sourceEncoding=UTF-8
 sonar.exclusions=node_modules/**
 sonar.cpd.exclusions=test/**/*
-sonar.coverage.exclusions=test/**/*,index.js,stryker.conf.js,jest.config.js,resolver-legacy-alias/**/*
+sonar.coverage.exclusions=test/**/*,index.js,stryker.conf.js,jest.config.js,resolver-legacy-alias/**/*,eslint-local-rules.js
 sonar.javascript.lcov.reportPaths=coverage/lcov.info
 sonar.host.url=https://sonarcloud.io
diff --git a/src/constants/settings.js b/src/constants/settings.js
index c543286..3b22e23 100644
--- a/src/constants/settings.js
+++ b/src/constants/settings.js
@@ -41,6 +41,13 @@ module.exports = {
 
   VALID_DEPENDENCY_NODE_KINDS: ["value", "type"],
   DEFAULT_DEPENDENCY_NODES: {
+    require: [
+      // Note: detects "require('source')"
+      {
+        selector: "CallExpression[callee.name=require] > Literal",
+        kind: "value",
+      },
+    ],
     import: [
       // Note: detects "import x from 'source'"
       { selector: "ImportDeclaration:not([importKind=type]) > Literal", kind: "value" },


Test patch:
diff --git a/test/rules/one-level/element-types-dependency-nodes.spec.js b/test/rules/one-level/element-types-dependency-nodes.spec.js
index 3302d5f..65c5b64 100644
--- a/test/rules/one-level/element-types-dependency-nodes.spec.js
+++ b/test/rules/one-level/element-types-dependency-nodes.spec.js
@@ -24,7 +24,7 @@ const typescriptSettings = {
   "boundaries/dependency-nodes": ["import"],
 };
 const dependencyNodesSettings = {
-  "boundaries/dependency-nodes": ["import", "export", "dynamic-import"],
+  "boundaries/dependency-nodes": ["require", "import", "export", "dynamic-import"],
   "boundaries/additional-dependency-nodes": [
     {
       // mock('source')
@@ -146,6 +146,12 @@ createRuleTester({
       code: "mock('helpers/helper-a')",
       options,
     },
+    // Components can require helpers
+    {
+      filename: absoluteFilePath("components/component-a/ComponentA.js"),
+      code: "require('helpers/helper-a')",
+      options,
+    },
   ],
   invalid: [
     // Helpers can't export value from another helper
@@ -192,6 +198,17 @@ createRuleTester({
         },
       ],
     },
+    // Helpers can't require another helper
+    {
+      filename: absoluteFilePath("helpers/helper-a/HelperA.js"),
+      code: "require('helpers/helper-b')",
+      options,
+      errors: [
+        {
+          type: "Literal",
+        },
+      ],
+    },
   ],
 });