Repository: import-js/eslint-plugin-import (JavaScript)

Issue:
Ignoring Flow `import type` statements when using `import/max-dependencies`
Currently, Flow `import type` statements are included in the dependency count when using the `import/max-dependencies` rule. This makes the rule far less useful for identifying code smells in projects using Flow, especially if you tend to put type defintions in separate files for the rest of the code (which then need separate imports). 

I think it would be useful to have an option to ignore `import type` statements when counting depencencies. Thoughts?
[max-dependencies]: new option 'ignoreTypeImports'
This rule aims to notify about potential code smell or a module doing too much based on the number of imported dependencies.

It doesn't differentiate between regular imports and type imports though. Type imports are neither indicating code smell nor do they indicate that a module is doing too much.

Therefore it might be good to implement a new option allowing or disallowing the consideration of type imports.

Note: this would only influence users of Typescript and Flow. 


Discussion:
I think they should not be ignored - types are deps too - but perhaps we could add an option that allows you to set a separate count for flow types?
Yeah, that would be better. (And I assume you could use it to ignore type imports if you wanted by setting the max count for those really high.)

That seems reasonable.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45aacceb79..d4163b8fce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 - [`no-dynamic-require`]: add option `esmodule` ([#1223], thanks [@vikr01])
 - [`named`]: add `commonjs` option ([#1222], thanks [@vikr01])
 - [`no-namespace`]: Add `ignore` option ([#2112], thanks [@aberezkin])
+- [`max-dependencies`]: add option `ignoreTypeImports` ([#1847], thanks [@rfermann])
 
 ### Fixed
 - [`no-duplicates`]: ensure autofix avoids excessive newlines ([#2028], thanks [@ertrzyiks])
@@ -853,6 +854,7 @@ for info on changes for earlier releases.
 [#1878]: https://github.com/benmosher/eslint-plugin-import/pull/1878
 [#1860]: https://github.com/benmosher/eslint-plugin-import/pull/1860
 [#1848]: https://github.com/benmosher/eslint-plugin-import/pull/1848
+[#1847]: https://github.com/benmosher/eslint-plugin-import/pull/1847
 [#1846]: https://github.com/benmosher/eslint-plugin-import/pull/1846
 [#1836]: https://github.com/benmosher/eslint-plugin-import/pull/1836
 [#1835]: https://github.com/benmosher/eslint-plugin-import/pull/1835
diff --git a/src/rules/max-dependencies.js b/src/rules/max-dependencies.js
index c8e1b3ab13..f9bdd12e51 100644
--- a/src/rules/max-dependencies.js
+++ b/src/rules/max-dependencies.js
@@ -2,15 +2,14 @@ import moduleVisitor from 'eslint-module-utils/moduleVisitor';
 import docsUrl from '../docsUrl';
 
 const DEFAULT_MAX = 10;
+const DEFAULT_IGNORE_TYPE_IMPORTS = false;
+const TYPE_IMPORT = 'type';
 
 const countDependencies = (dependencies, lastNode, context) => {
   const { max } = context.options[0] || { max: DEFAULT_MAX };
 
   if (dependencies.size > max) {
-    context.report(
-      lastNode,
-      `Maximum number of dependencies (${max}) exceeded.`
-    );
+    context.report(lastNode, `Maximum number of dependencies (${max}) exceeded.`);
   }
 };
 
@@ -26,6 +25,7 @@ module.exports = {
         'type': 'object',
         'properties': {
           'max': { 'type': 'number' },
+          'ignoreTypeImports': { 'type': 'boolean' },
         },
         'additionalProperties': false,
       },
@@ -33,6 +33,10 @@ module.exports = {
   },
 
   create: context => {
+    const {
+      ignoreTypeImports = DEFAULT_IGNORE_TYPE_IMPORTS,
+    } = context.options[0] || {};
+
     const dependencies = new Set(); // keep track of dependencies
     let lastNode; // keep track of the last node to report on
 
@@ -40,8 +44,10 @@ module.exports = {
       'Program:exit': function () {
         countDependencies(dependencies, lastNode, context);
       },
-    }, moduleVisitor((source) => {
-      dependencies.add(source.value);
+    }, moduleVisitor((source, { importKind }) => {
+      if (importKind !== TYPE_IMPORT || !ignoreTypeImports) {
+        dependencies.add(source.value);
+      }
       lastNode = source;
     }, { commonjs: true }));
   },


Test patch:
diff --git a/tests/src/rules/max-dependencies.js b/tests/src/rules/max-dependencies.js
index f4e5f9a976..1251af97e8 100644
--- a/tests/src/rules/max-dependencies.js
+++ b/tests/src/rules/max-dependencies.js
@@ -1,6 +1,8 @@
-import { test } from '../utils';
+import { test, getTSParsers } from '../utils';
 
 import { RuleTester } from 'eslint';
+import eslintPkg from 'eslint/package.json';
+import semver from 'semver';
 
 const ruleTester = new RuleTester();
 const rule = require('rules/max-dependencies');
@@ -74,5 +76,58 @@ ruleTester.run('max-dependencies', rule, {
         'Maximum number of dependencies (1) exceeded.',
       ],
     }),
+
+    test({
+      code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'; import type { z } from \'./baz\'',
+      parser: require.resolve('babel-eslint'),
+      options: [{
+        max: 2,
+        ignoreTypeImports: false,
+      }],
+      errors: [
+        'Maximum number of dependencies (2) exceeded.',
+      ],
+    }),
   ],
 });
+
+context('TypeScript', { skip: semver.satisfies(eslintPkg.version, '>5.0.0') }, () => {
+  getTSParsers().forEach((parser) => {
+    ruleTester.run(`max-dependencies (${parser.replace(process.cwd(), '.')})`, rule, {
+      valid: [    
+        test({
+          code: 'import type { x } from \'./foo\'; import { y } from \'./bar\';',
+          parser: parser,
+          options: [{
+            max: 1,
+            ignoreTypeImports: true,
+          }],
+        }),
+      ],
+      invalid: [    
+        test({
+          code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'',
+          parser: parser,
+          options: [{
+            max: 1,
+          }],
+          errors: [
+            'Maximum number of dependencies (1) exceeded.',
+          ],
+        }),
+    
+        test({
+          code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'; import type { z } from \'./baz\'',
+          parser: parser,
+          options: [{
+            max: 2,
+            ignoreTypeImports: false,
+          }],
+          errors: [
+            'Maximum number of dependencies (2) exceeded.',
+          ],
+        }),
+      ],
+    });
+  });
+});