Repository: import-js/eslint-plugin-import
Pull request #2884 (2023-09-26 02:18:28)

[Fix] `newline-after-import`: fix `exactCount` with `considerComments` false positive, when there is a leading comment
This addresses #2882 and adds additional test cases to cover the combination of `exactCount` and `considerComments`


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 567ed264..6af7f748 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
 ### Added
 - TypeScript config: add .cts and .mts extensions ([#2851], thanks [@Zamiell])
 - [`newline-after-import`]: new option `exactCount` and docs update ([#1933], thanks [@anikethsaha] and [@reosarevok])
+- [`newline-after-import`]: fix `exactCount` with `considerComments` false positive, when there is a leading comment ([#2884], thanks [@kinland])
 
 ## [2.28.1] - 2023-08-18
 
@@ -1091,6 +1092,7 @@ for info on changes for earlier releases.
 
 [`memo-parser`]: ./memo-parser/README.md
 
+[#2884]: https://github.com/import-js/eslint-plugin-import/pull/2884
 [#2854]: https://github.com/import-js/eslint-plugin-import/pull/2854
 [#2851]: https://github.com/import-js/eslint-plugin-import/pull/2851
 [#2850]: https://github.com/import-js/eslint-plugin-import/pull/2850
@@ -1769,6 +1771,7 @@ for info on changes for earlier releases.
 [@kentcdodds]: https://github.com/kentcdodds
 [@kevin940726]: https://github.com/kevin940726
 [@kgregory]: https://github.com/kgregory
+[@kinland]: https://github.com/kinland
 [@kirill-konshin]: https://github.com/kirill-konshin
 [@kiwka]: https://github.com/kiwka
 [@klimashkin]: https://github.com/klimashkin
@@ -1776,8 +1779,8 @@ for info on changes for earlier releases.
 [@knpwrs]: https://github.com/knpwrs
 [@KostyaZgara]: https://github.com/KostyaZgara
 [@kylemh]: https://github.com/kylemh
-[@laysent]: https://github.com/laysent
 [@laurens-dg]: https://github.com/laurens-dg
+[@laysent]: https://github.com/laysent
 [@le0nik]: https://github.com/le0nik
 [@leipert]: https://github.com/leipert
 [@lemonmade]: https://github.com/lemonmade
diff --git a/src/rules/newline-after-import.js b/src/rules/newline-after-import.js
index 9b469110..a33bb615 100644
--- a/src/rules/newline-after-import.js
+++ b/src/rules/newline-after-import.js
@@ -169,7 +169,7 @@ module.exports = {
       let nextComment;
 
       if (typeof parent.comments !== 'undefined' && options.considerComments) {
-        nextComment = parent.comments.find((o) => o.loc.start.line === endLine + 1);
+        nextComment = parent.comments.find((o) => o.loc.start.line >= endLine && o.loc.start.line <= endLine + options.count + 1);
       }
 
       // skip "export import"s


Test patch:
diff --git a/tests/src/rules/newline-after-import.js b/tests/src/rules/newline-after-import.js
index bd11da41..6a8fb83e 100644
--- a/tests/src/rules/newline-after-import.js
+++ b/tests/src/rules/newline-after-import.js
@@ -142,6 +142,31 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
       parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
       options: [{ count: 1, exactCount: true }],
     },
+    {
+      code: `import foo from 'foo';\n\n// Some random comment\nvar bar = 'bar';`,
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+      options: [{ count: 2, exactCount: true }],
+    },
+    {
+      code: `import foo from 'foo';\n// Some random comment\nvar bar = 'bar';`,
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+      options: [{ count: 1, exactCount: true }],
+    },
+    {
+      code: `import foo from 'foo';\n\n\n// Some random comment\nvar bar = 'bar';`,
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+      options: [{ count: 2, exactCount: true, considerComments: true }],
+    },
+    {
+      code: `import foo from 'foo';\n\n// Some random comment\nvar bar = 'bar';`,
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+      options: [{ count: 1, exactCount: true, considerComments: true }],
+    },
+    {
+      code: `/**\n * A leading comment\n */\nimport foo from 'foo';\n\n// Some random comment\nexport {foo};`,
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+      options: [{ count: 2, exactCount: true }],
+    },
     {
       code: `import foo from 'foo';\n\n\nvar bar = 'bar';`,
       parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
@@ -171,6 +196,16 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
       parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
       options: [{ count: 4, exactCount: true }],
     },
+    {
+      code: `var foo = require('foo-module');\n\n// Some random comment\n\n\nvar foo = 'bar';`,
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+      options: [{ count: 4, exactCount: true }],
+    },
+    {
+      code: `var foo = require('foo-module');\n\n\n\n// Some random comment\nvar foo = 'bar';`,
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+      options: [{ count: 4, exactCount: true, considerComments: true }],
+    },
     {
       code: `require('foo-module');\n\nvar foo = 'bar';`,
       parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
@@ -683,6 +718,72 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
       }],
       parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
     },
+    {
+      code: `import foo from 'foo';\n// some random comment\nexport default function() {};`,
+      output: `import foo from 'foo';\n\n// some random comment\nexport default function() {};`,
+      options: [{ count: 2, exactCount: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
+      }],
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+    },
+    {
+      code: `import foo from 'foo';\n// some random comment\n\n\nexport default function() {};`,
+      output: `import foo from 'foo';\n// some random comment\n\n\nexport default function() {};`,
+      options: [{ count: 2, exactCount: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
+      }],
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+    },
+    {
+      code: `import foo from 'foo';\n// some random comment\n\n\n\nexport default function() {};`,
+      output: `import foo from 'foo';\n// some random comment\n\n\n\nexport default function() {};`,
+      options: [{ count: 2, exactCount: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
+      }],
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+    },
+    {
+      code: `import foo from 'foo';\n// some random comment\nexport default function() {};`,
+      output: `import foo from 'foo';\n\n\n// some random comment\nexport default function() {};`,
+      options: [{ count: 2, exactCount: true, considerComments: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
+      }],
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+    },
+    {
+      code: `import foo from 'foo';\n\n// some random comment\nexport default function() {};`,
+      output: `import foo from 'foo';\n\n\n// some random comment\nexport default function() {};`,
+      options: [{ count: 2, exactCount: true, considerComments: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
+      }],
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+    },
+    {
+      code: `import foo from 'foo';\n\n\n\n// some random comment\nexport default function() {};`,
+      output: `import foo from 'foo';\n\n\n\n// some random comment\nexport default function() {};`,
+      options: [{ count: 2, exactCount: true, considerComments: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
+      }],
+      parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
+    },
     {
       code: `
         import foo from 'foo';
@@ -728,5 +829,37 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
       }],
       parserOptions: { ecmaVersion: 2015 },
     },
+    {
+      code: `const foo = require('foo');\n\n\n\n// some random comment\nconst bar = function() {};`,
+      output: `const foo = require('foo');\n\n\n\n// some random comment\nconst bar = function() {};`,
+      options: [{ count: 2, exactCount: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: 'Expected 2 empty lines after require statement not followed by another require.',
+      }],
+      parserOptions: { ecmaVersion: 2015 },
+    },
+    {
+      code: `import foo from 'foo';// some random comment\nexport default function() {};`,
+      output: `import foo from 'foo';\n\n// some random comment\nexport default function() {};`,
+      options: [{ count: 1, exactCount: true, considerComments: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: IMPORT_ERROR_MESSAGE,
+      }],
+      parserOptions: { ecmaVersion: 2015, considerComments: true, sourceType: 'module' },
+    },
+    {
+      code: `const foo = require('foo');\n\n\n// some random comment\nconst bar = function() {};`,
+      options: [{ count: 2, exactCount: true, considerComments: true }],
+      errors: [{
+        line: 1,
+        column: 1,
+        message: 'Expected 2 empty lines after require statement not followed by another require.',
+      }],
+      parserOptions: { ecmaVersion: 2015 },
+    },
   ),
 });