Skip to content

Commit 8705121

Browse files
anikethsahareosarevok
authored andcommitted
[New] newline-after-import: new option exactCount and docs update
Fixes #1901. Fixes #514. Co-authored-by: Anix <[email protected]> Co-authored-by: reosarevok <[email protected]>
1 parent 6b95a02 commit 8705121

File tree

4 files changed

+193
-26
lines changed

4 files changed

+193
-26
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
88

99
### Added
1010
- TypeScript config: add .cts and .mts extensions ([#2851], thanks [@Zamiell])
11+
- [`newline-after-import`]: new option `exactCount` and docs update ([#1933], thanks [@anikethsaha] and [@reosarevok])
1112

1213
## [2.28.1] - 2023-08-18
1314

@@ -1901,4 +1902,4 @@ for info on changes for earlier releases.
19011902
[@yndajas]: https://github.com/yndajas
19021903
[@yordis]: https://github.com/yordis
19031904
[@Zamiell]: https://github.com/Zamiell
1904-
[@zloirock]: https://github.com/zloirock
1905+
[@zloirock]: https://github.com/zloirock

docs/rules/newline-after-import.md

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,70 +12,119 @@ This rule supports the following options:
1212

1313
- `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`.
1414

15+
- `exactCount` which enforce the exact numbers of newlines that is mentioned in `count`. This option defaults to `false`.
16+
1517
- `considerComments` which enforces the rule on comments after the last import-statement as well when set to true. This option defaults to `false`.
1618

1719
Valid:
1820

1921
```js
20-
import defaultExport from './foo'
22+
import defaultExport from './foo';
2123

22-
const FOO = 'BAR'
24+
const FOO = 'BAR';
2325
```
2426

2527
```js
26-
import defaultExport from './foo'
27-
import { bar } from 'bar-lib'
28+
import defaultExport from './foo';
29+
import { bar } from 'bar-lib';
2830

29-
const FOO = 'BAR'
31+
const FOO = 'BAR';
3032
```
3133

3234
```js
33-
const FOO = require('./foo')
34-
const BAR = require('./bar')
35+
const FOO = require('./foo');
36+
const BAR = require('./bar');
3537

36-
const BAZ = 1
38+
const BAZ = 1;
3739
```
3840

3941
Invalid:
4042

4143
```js
4244
import * as foo from 'foo'
43-
const FOO = 'BAR'
45+
const FOO = 'BAR';
4446
```
4547

4648
```js
47-
import * as foo from 'foo'
48-
const FOO = 'BAR'
49+
import * as foo from 'foo';
50+
const FOO = 'BAR';
4951

50-
import { bar } from 'bar-lib'
52+
import { bar } from 'bar-lib';
5153
```
5254

5355
```js
54-
const FOO = require('./foo')
55-
const BAZ = 1
56-
const BAR = require('./bar')
56+
const FOO = require('./foo');
57+
const BAZ = 1;
58+
const BAR = require('./bar');
5759
```
5860

5961
With `count` set to `2` this will be considered valid:
6062

6163
```js
62-
import defaultExport from './foo'
64+
import defaultExport from './foo';
6365

6466

65-
const FOO = 'BAR'
67+
const FOO = 'BAR';
68+
```
69+
70+
```js
71+
import defaultExport from './foo';
72+
73+
74+
75+
const FOO = 'BAR';
6676
```
6777

6878
With `count` set to `2` these will be considered invalid:
6979

7080
```js
71-
import defaultExport from './foo'
72-
const FOO = 'BAR'
81+
import defaultExport from './foo';
82+
const FOO = 'BAR';
7383
```
7484

7585
```js
76-
import defaultExport from './foo'
86+
import defaultExport from './foo';
7787

78-
const FOO = 'BAR'
88+
const FOO = 'BAR';
89+
```
90+
91+
With `count` set to `2` and `exactCount` set to `true` this will be considered valid:
92+
93+
```js
94+
import defaultExport from './foo';
95+
96+
97+
const FOO = 'BAR';
98+
```
99+
100+
With `count` set to `2` and `exactCount` set to `true` these will be considered invalid:
101+
102+
```js
103+
import defaultExport from './foo';
104+
const FOO = 'BAR';
105+
```
106+
107+
```js
108+
import defaultExport from './foo';
109+
110+
const FOO = 'BAR';
111+
```
112+
113+
```js
114+
import defaultExport from './foo';
115+
116+
117+
118+
const FOO = 'BAR';
119+
```
120+
121+
```js
122+
import defaultExport from './foo';
123+
124+
125+
126+
127+
const FOO = 'BAR';
79128
```
80129

81130
With `considerComments` set to `false` this will be considered valid:

src/rules/newline-after-import.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ module.exports = {
6969
type: 'integer',
7070
minimum: 1,
7171
},
72+
exactCount: { type: 'boolean' },
7273
considerComments: { type: 'boolean' },
7374
},
7475
additionalProperties: false,
@@ -78,7 +79,12 @@ module.exports = {
7879
create(context) {
7980
let level = 0;
8081
const requireCalls = [];
81-
const options = { count: 1, considerComments: false, ...context.options[0] };
82+
const options = {
83+
count: 1,
84+
exactCount: false,
85+
considerComments: false,
86+
...context.options[0],
87+
};
8288

8389
function checkForNewLine(node, nextNode, type) {
8490
if (isExportDefaultClass(nextNode) || isExportNameClass(nextNode)) {
@@ -94,7 +100,10 @@ module.exports = {
94100
const lineDifference = getLineDifference(node, nextNode);
95101
const EXPECTED_LINE_DIFFERENCE = options.count + 1;
96102

97-
if (lineDifference < EXPECTED_LINE_DIFFERENCE) {
103+
if (
104+
lineDifference < EXPECTED_LINE_DIFFERENCE
105+
|| options.exactCount && lineDifference !== EXPECTED_LINE_DIFFERENCE
106+
) {
98107
let column = node.loc.start.column;
99108

100109
if (node.loc.start.line !== node.loc.end.line) {
@@ -107,7 +116,7 @@ module.exports = {
107116
column,
108117
},
109118
message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} after ${type} statement not followed by another ${type}.`,
110-
fix: (fixer) => fixer.insertTextAfter(
119+
fix: options.exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference ? undefined : (fixer) => fixer.insertTextAfter(
111120
node,
112121
'\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference),
113122
),
@@ -132,7 +141,7 @@ module.exports = {
132141
column,
133142
},
134143
message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} after import statement not followed by another import.`,
135-
fix: (fixer) => fixer.insertTextAfter(
144+
fix: options.exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference ? undefined : (fixer) => fixer.insertTextAfter(
136145
node,
137146
'\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference),
138147
),

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

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
4646
parserOptions: { ecmaVersion: 6 },
4747
options: [{ considerComments: true }],
4848
},
49+
{
50+
code: `
51+
const x = () => require('baz') && require('bar')
52+
53+
// Some random single line comment
54+
var bar = 42;
55+
`,
56+
parserOptions: { ecmaVersion: 6 },
57+
options: [{ considerComments: true, count: 1, exactCount: true }],
58+
},
4959
{
5060
code: `
5161
const x = () => require('baz') && require('bar')
@@ -122,6 +132,21 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
122132
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
123133
options: [{ count: 2 }],
124134
},
135+
{
136+
code: `import foo from 'foo';\n\n\nvar bar = 'bar';`,
137+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
138+
options: [{ count: 2, exactCount: true }],
139+
},
140+
{
141+
code: `import foo from 'foo';\n\nvar bar = 'bar';`,
142+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
143+
options: [{ count: 1, exactCount: true }],
144+
},
145+
{
146+
code: `import foo from 'foo';\n\n\nvar bar = 'bar';`,
147+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
148+
options: [{ count: 1 }],
149+
},
125150
{
126151
code: `import foo from 'foo';\n\n\n\n\nvar bar = 'bar';`,
127152
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
@@ -141,6 +166,11 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
141166
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
142167
options: [{ count: 4 }],
143168
},
169+
{
170+
code: `var foo = require('foo-module');\n\n\n\n\nvar foo = 'bar';`,
171+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
172+
options: [{ count: 4, exactCount: true }],
173+
},
144174
{
145175
code: `require('foo-module');\n\nvar foo = 'bar';`,
146176
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
@@ -620,5 +650,83 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), {
620650
parserOptions: { sourceType: 'module' },
621651
parser: parsers.BABEL_OLD,
622652
})) || [],
653+
{
654+
code: `import foo from 'foo';\n\nexport default function() {};`,
655+
output: `import foo from 'foo';\n\n\nexport default function() {};`,
656+
options: [{ count: 2, exactCount: true }],
657+
errors: [{
658+
line: 1,
659+
column: 1,
660+
message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
661+
}],
662+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
663+
},
664+
{
665+
code: `import foo from 'foo';\n\n\n\nexport default function() {};`,
666+
output: `import foo from 'foo';\n\n\n\nexport default function() {};`,
667+
options: [{ count: 2, exactCount: true }],
668+
errors: [{
669+
line: 1,
670+
column: 1,
671+
message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
672+
}],
673+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
674+
},
675+
{
676+
code: `import foo from 'foo';\n\n\n\n\nexport default function() {};`,
677+
output: `import foo from 'foo';\n\n\n\n\nexport default function() {};`,
678+
options: [{ count: 2, exactCount: true }],
679+
errors: [{
680+
line: 1,
681+
column: 1,
682+
message: IMPORT_ERROR_MESSAGE_MULTIPLE(2),
683+
}],
684+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
685+
},
686+
{
687+
code: `
688+
import foo from 'foo';
689+
690+
691+
// Some random single line comment
692+
var bar = 42;
693+
`,
694+
output: `
695+
import foo from 'foo';
696+
697+
698+
// Some random single line comment
699+
var bar = 42;
700+
`,
701+
errors: [{
702+
line: 2,
703+
column: 9,
704+
message: IMPORT_ERROR_MESSAGE,
705+
}],
706+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
707+
options: [{ considerComments: true, count: 1, exactCount: true }],
708+
},
709+
{
710+
code: `import foo from 'foo';export default function() {};`,
711+
output: `import foo from 'foo';\n\nexport default function() {};`,
712+
options: [{ count: 1, exactCount: true }],
713+
errors: [{
714+
line: 1,
715+
column: 1,
716+
message: IMPORT_ERROR_MESSAGE,
717+
}],
718+
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
719+
},
720+
{
721+
code: `const foo = require('foo');\n\n\n\nconst bar = function() {};`,
722+
output: `const foo = require('foo');\n\n\n\nconst bar = function() {};`,
723+
options: [{ count: 2, exactCount: true }],
724+
errors: [{
725+
line: 1,
726+
column: 1,
727+
message: 'Expected 2 empty lines after require statement not followed by another require.',
728+
}],
729+
parserOptions: { ecmaVersion: 2015 },
730+
},
623731
),
624732
});

0 commit comments

Comments
 (0)