Skip to content

Commit 5cc0ada

Browse files
pri1311ljharb
authored andcommitted
[New] newline-after-import: add considerComments option
1 parent 517fcb7 commit 5cc0ada

File tree

4 files changed

+207
-5
lines changed

4 files changed

+207
-5
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
66

77
## [Unreleased]
88

9+
### Added
10+
- [`newline-after-import`]: add `considerComments` option ([#2399], thanks [@pri1311])
11+
912
### Changed
1013
- [Tests] `named`: Run all TypeScript test ([#2427], thanks [@ProdigySim])
1114

@@ -983,6 +986,7 @@ for info on changes for earlier releases.
983986
[#2427]: https://github.com/import-js/eslint-plugin-import/pull/2427
984987
[#2417]: https://github.com/import-js/eslint-plugin-import/pull/2417
985988
[#2411]: https://github.com/import-js/eslint-plugin-import/pull/2411
989+
[#2399]: https://github.com/import-js/eslint-plugin-import/pull/2399
986990
[#2393]: https://github.com/import-js/eslint-plugin-import/pull/2393
987991
[#2388]: https://github.com/import-js/eslint-plugin-import/pull/2388
988992
[#2381]: https://github.com/import-js/eslint-plugin-import/pull/2381
@@ -1628,6 +1632,7 @@ for info on changes for earlier releases.
16281632
[@Pessimistress]: https://github.com/Pessimistress
16291633
[@pmcelhaney]: https://github.com/pmcelhaney
16301634
[@preco21]: https://github.com/preco21
1635+
[@pri1311]: https://github.com/pri1311
16311636
[@ProdigySim]: https://github.com/ProdigySim
16321637
[@pzhine]: https://github.com/pzhine
16331638
[@ramasilveyra]: https://github.com/ramasilveyra

docs/rules/newline-after-import.md

+28-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ Enforces having one or more empty lines after the last top-level import statemen
55

66
## Rule Details
77

8-
This rule has one option, `count` which sets the number of newlines that are enforced after the last top-level import statement or require call. This option defaults to `1`.
8+
This rule supports the following options:
9+
- `count` which sets the number of newlines that are enforced after the last top-level import statement or require call. This option defaults to `1`.
10+
11+
- `considerComments` which enforces the rule on comments after the last import-statement as well when set to true. This option defaults to `false`.
912

1013
Valid:
1114

@@ -71,6 +74,30 @@ import defaultExport from './foo'
7174
const FOO = 'BAR'
7275
```
7376

77+
With `considerComments` set to `false` this will be considered valid:
78+
79+
```js
80+
import defaultExport from './foo'
81+
// some comment here.
82+
const FOO = 'BAR'
83+
```
84+
85+
With `considerComments` set to `true` this will be considered valid:
86+
87+
```js
88+
import defaultExport from './foo'
89+
90+
// some comment here.
91+
const FOO = 'BAR'
92+
```
93+
94+
With `considerComments` set to `true` this will be considered invalid:
95+
96+
```js
97+
import defaultExport from './foo'
98+
// some comment here.
99+
const FOO = 'BAR'
100+
```
74101

75102
## Example options usage
76103
```json

src/rules/newline-after-import.js

+38-4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ module.exports = {
6767
'type': 'integer',
6868
'minimum': 1,
6969
},
70+
'considerComments': { 'type': 'boolean' },
7071
},
7172
'additionalProperties': false,
7273
},
@@ -75,6 +76,7 @@ module.exports = {
7576
create(context) {
7677
let level = 0;
7778
const requireCalls = [];
79+
const options = Object.assign({ count: 1, considerComments: false }, context.options[0]);
7880

7981
function checkForNewLine(node, nextNode, type) {
8082
if (isExportDefaultClass(nextNode) || isExportNameClass(nextNode)) {
@@ -87,7 +89,6 @@ module.exports = {
8789
nextNode = nextNode.decorators[0];
8890
}
8991

90-
const options = context.options[0] || { count: 1 };
9192
const lineDifference = getLineDifference(node, nextNode);
9293
const EXPECTED_LINE_DIFFERENCE = options.count + 1;
9394

@@ -103,8 +104,32 @@ module.exports = {
103104
line: node.loc.end.line,
104105
column,
105106
},
106-
message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} \
107-
after ${type} statement not followed by another ${type}.`,
107+
message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} after ${type} statement not followed by another ${type}.`,
108+
fix: fixer => fixer.insertTextAfter(
109+
node,
110+
'\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference),
111+
),
112+
});
113+
}
114+
}
115+
116+
function commentAfterImport(node, nextComment) {
117+
const lineDifference = getLineDifference(node, nextComment);
118+
const EXPECTED_LINE_DIFFERENCE = options.count + 1;
119+
120+
if (lineDifference < EXPECTED_LINE_DIFFERENCE) {
121+
let column = node.loc.start.column;
122+
123+
if (node.loc.start.line !== node.loc.end.line) {
124+
column = 0;
125+
}
126+
127+
context.report({
128+
loc: {
129+
line: node.loc.end.line,
130+
column,
131+
},
132+
message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} after import statement not followed by another import.`,
108133
fix: fixer => fixer.insertTextAfter(
109134
node,
110135
'\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference),
@@ -124,13 +149,22 @@ after ${type} statement not followed by another ${type}.`,
124149
const { parent } = node;
125150
const nodePosition = parent.body.indexOf(node);
126151
const nextNode = parent.body[nodePosition + 1];
152+
const endLine = node.loc.end.line;
153+
let nextComment;
154+
155+
if (typeof parent.comments !== 'undefined' && options.considerComments) {
156+
nextComment = parent.comments.find(o => o.loc.start.line === endLine + 1);
157+
}
158+
127159

128160
// skip "export import"s
129161
if (node.type === 'TSImportEqualsDeclaration' && node.isExport) {
130162
return;
131163
}
132164

133-
if (nextNode && nextNode.type !== 'ImportDeclaration' && (nextNode.type !== 'TSImportEqualsDeclaration' || nextNode.isExport)) {
165+
if (nextComment && typeof nextComment !== 'undefined') {
166+
commentAfterImport(node, nextComment);
167+
} else if (nextNode && nextNode.type !== 'ImportDeclaration' && (nextNode.type !== 'TSImportEqualsDeclaration' || nextNode.isExport)) {
134168
checkForNewLine(node, nextNode, 'import');
135169
}
136170
}

tests/src/rules/newline-after-import.js

+136
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,41 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
2424
, y = () => require('bar')`,
2525
parserOptions: { ecmaVersion: 6 } ,
2626
},
27+
{
28+
code: `
29+
const x = () => require('baz')
30+
, y = () => require('bar')
31+
32+
// some comment here
33+
`,
34+
parserOptions: { ecmaVersion: 6 } ,
35+
options: [{ considerComments: true }],
36+
},
2737
{
2838
code: `const x = () => require('baz') && require('bar')`,
2939
parserOptions: { ecmaVersion: 6 } ,
3040
},
41+
{
42+
code: `
43+
const x = () => require('baz') && require('bar')
44+
45+
// Some random single line comment
46+
var bar = 42;
47+
`,
48+
parserOptions: { ecmaVersion: 6 } ,
49+
options: [{ 'considerComments': true }],
50+
},
51+
{
52+
code: `
53+
const x = () => require('baz') && require('bar')
54+
/**
55+
* some multiline comment here
56+
* another line of comment
57+
**/
58+
var bar = 42;
59+
`,
60+
parserOptions: { ecmaVersion: 6 } ,
61+
},
3162
`function x() { require('baz'); }`,
3263
`a(require('b'), require('c'), require('d'));`,
3364
`function foo() {
@@ -255,9 +286,114 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
255286
`,
256287
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
257288
},
289+
{
290+
code: `
291+
import path from 'path';
292+
import foo from 'foo';
293+
/**
294+
* some multiline comment here
295+
* another line of comment
296+
**/
297+
var bar = 42;
298+
`,
299+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' } ,
300+
},
301+
{
302+
code: `
303+
import path from 'path';import foo from 'foo';
304+
305+
/**
306+
* some multiline comment here
307+
* another line of comment
308+
**/
309+
var bar = 42;
310+
`,
311+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' } ,
312+
options: [{ 'considerComments': true }],
313+
},
314+
{
315+
code: `
316+
import path from 'path';
317+
import foo from 'foo';
318+
319+
// Some random single line comment
320+
var bar = 42;
321+
`,
322+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' } ,
323+
},
258324
),
259325

260326
invalid: [].concat(
327+
{
328+
code: `
329+
import { A, B, C, D } from
330+
'../path/to/my/module/in/very/far/directory'
331+
// some comment
332+
var foo = 'bar';
333+
`,
334+
output: `
335+
import { A, B, C, D } from
336+
'../path/to/my/module/in/very/far/directory'
337+
338+
// some comment
339+
var foo = 'bar';
340+
`,
341+
errors: [ {
342+
line: 3,
343+
column: 1,
344+
message: IMPORT_ERROR_MESSAGE,
345+
} ],
346+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
347+
options: [{ 'considerComments': true }],
348+
},
349+
{
350+
code: `
351+
import path from 'path';
352+
import foo from 'foo';
353+
/**
354+
* some multiline comment here
355+
* another line of comment
356+
**/
357+
var bar = 42;
358+
`,
359+
output: `
360+
import path from 'path';
361+
import foo from 'foo';\n
362+
/**
363+
* some multiline comment here
364+
* another line of comment
365+
**/
366+
var bar = 42;
367+
`,
368+
errors: [ {
369+
line: 3,
370+
column: 9,
371+
message: IMPORT_ERROR_MESSAGE,
372+
} ],
373+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' } ,
374+
options: [{ 'considerComments': true }],
375+
},
376+
{
377+
code: `
378+
import path from 'path';
379+
import foo from 'foo';
380+
// Some random single line comment
381+
var bar = 42;
382+
`,
383+
output: `
384+
import path from 'path';
385+
import foo from 'foo';\n
386+
// Some random single line comment
387+
var bar = 42;
388+
`,
389+
errors: [ {
390+
line: 3,
391+
column: 9,
392+
message: IMPORT_ERROR_MESSAGE,
393+
} ],
394+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' } ,
395+
options: [{ 'considerComments': true, 'count': 1 }],
396+
},
261397
{
262398
code: `import foo from 'foo';\nexport default function() {};`,
263399
output: `import foo from 'foo';\n\nexport default function() {};`,

0 commit comments

Comments
 (0)