Skip to content

Commit 73a8bce

Browse files
committed
feat(no-lines-before-blocks): add rule; fixes #1211
BREAKING CHANGE: Adds rule to recommended configs; prevents lines intervening between functions or other structures and JSDoc blocks
1 parent 376d583 commit 73a8bce

File tree

10 files changed

+406
-2
lines changed

10 files changed

+406
-2
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# `no-lines-before-blocks`
2+
3+
Reports extra lines between functions (and other language structures) and their
4+
JSDoc blocks.
5+
6+
## Fixer
7+
8+
Removes extra lines between functions (and other language structures) and their
9+
JSDoc blocks. Uses the `maxLines` setting to determine whether to remove lines.
10+
11+
## Options
12+
13+
{"gitdown": "options"}
14+
15+
|||
16+
|---|---|
17+
|Context|everywhere|
18+
|Tags|N/A|
19+
|Recommended|true|
20+
|Settings|`maxLines`, `minLines`|
21+
|Options|`enableFixer`, `preferMinLines`|
22+
23+
## Failing examples
24+
25+
<!-- assertions-failing noLinesBeforeBlocks -->
26+
27+
## Passing examples
28+
29+
<!-- assertions-passing noLinesBeforeBlocks -->

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ non-default-recommended fixer).
451451
||:wrench:| [no-blank-block-descriptions](./docs/rules/no-blank-block-descriptions.md#readme) | If tags are present, this rule will prevent empty lines in the block description. If no tags are present, this rule will prevent extra empty lines in the block description. |
452452
||:wrench:| [no-blank-blocks](./docs/rules/no-blank-blocks.md#readme) | Removes empty blocks with nothing but possibly line breaks |
453453
|:heavy_check_mark:|:wrench:| [no-defaults](./docs/rules/no-defaults.md#readme) | This rule reports defaults being used on the relevant portion of `@param` or `@default`. |
454+
|:heavy_check_mark:|:wrench:| [no-lines-before-blocks](./docs/rules/no-lines-before-blocks.md#readme) | Reports extra lines between functions (and other language structures) and their JSDoc blocks. |
454455
||| [no-missing-syntax](./docs/rules/no-missing-syntax.md#readme) | Reports when certain comment structures are always expected. |
455456
|:heavy_check_mark:|:wrench:| [no-multi-asterisks](./docs/rules/no-multi-asterisks.md#readme) | Prevents use of multiple asterisks at the beginning of lines. |
456457
||| [no-restricted-syntax](./docs/rules/no-restricted-syntax.md#readme) | Reports when certain comment structures are present. |
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<a name="user-content-no-lines-before-blocks"></a>
2+
<a name="no-lines-before-blocks"></a>
3+
# <code>no-lines-before-blocks</code>
4+
5+
Reports extra lines between functions (and other language structures) and their
6+
JSDoc blocks.
7+
8+
<a name="user-content-no-lines-before-blocks-fixer"></a>
9+
<a name="no-lines-before-blocks-fixer"></a>
10+
## Fixer
11+
12+
Removes extra lines between functions (and other language structures) and their
13+
JSDoc blocks. Uses the `maxLines` setting to determine whether to remove lines.
14+
15+
<a name="user-content-no-lines-before-blocks-options"></a>
16+
<a name="no-lines-before-blocks-options"></a>
17+
## Options
18+
19+
A single options object has the following properties.
20+
21+
<a name="user-content-no-lines-before-blocks-options-enablefixer"></a>
22+
<a name="no-lines-before-blocks-options-enablefixer"></a>
23+
### <code>enableFixer</code>
24+
25+
Whether to enable the fixer to remove line breaks
26+
<a name="user-content-no-lines-before-blocks-options-preferminlines"></a>
27+
<a name="no-lines-before-blocks-options-preferminlines"></a>
28+
### <code>preferMinLines</code>
29+
30+
Whether to use the setting `minLines` as the basis for fixing lines going past `maxLines`
31+
32+
33+
|||
34+
|---|---|
35+
|Context|everywhere|
36+
|Tags|N/A|
37+
|Recommended|true|
38+
|Settings|`maxLines`, `minLines`|
39+
|Options|`enableFixer`, `preferMinLines`|
40+
41+
<a name="user-content-no-lines-before-blocks-failing-examples"></a>
42+
<a name="no-lines-before-blocks-failing-examples"></a>
43+
## Failing examples
44+
45+
The following patterns are considered problems:
46+
47+
````ts
48+
/** This is a description of some function!*/
49+
50+
51+
52+
53+
54+
55+
function someFunction() {}
56+
// Message: There should be no extra lines above structures with JSDoc blocks
57+
58+
/** This is a description of some function!*/
59+
60+
function someFunction() {}
61+
// "jsdoc/no-lines-before-blocks": ["error"|"warn", {"enableFixer":false}]
62+
// Message: There should be no extra lines above structures with JSDoc blocks
63+
64+
/** This is a description of some function!*/
65+
66+
67+
function someFunction() {}
68+
// Settings: {"jsdoc":{"maxLines":2}}
69+
// Message: There should be no extra lines above structures with JSDoc blocks
70+
71+
/** This is a description of some function!*/
72+
73+
74+
function someFunction() {}
75+
// Settings: {"jsdoc":{"maxLines":2,"minLines":1}}
76+
// "jsdoc/no-lines-before-blocks": ["error"|"warn", {"preferMinLines":true}]
77+
// Message: There should be no extra lines above structures with JSDoc blocks
78+
````
79+
80+
81+
82+
<a name="user-content-no-lines-before-blocks-passing-examples"></a>
83+
<a name="no-lines-before-blocks-passing-examples"></a>
84+
## Passing examples
85+
86+
The following patterns are not considered problems:
87+
88+
````ts
89+
function someFunction() {}
90+
91+
/** JSDoc */ function someFunction() {}
92+
93+
/** This is a description of some function! */
94+
// extra comment
95+
function someFunction() {}
96+
97+
/** Standalone comment (e.g. a type definition) */
98+
99+
/** The actual description */
100+
function someFunction() {}
101+
102+
/* Regular block comment */
103+
104+
function someFunction() {}
105+
106+
// Regular line comment
107+
108+
function someFunction() {}
109+
110+
/** This is a description of some function!*/
111+
112+
function someFunction() {}
113+
// Settings: {"jsdoc":{"maxLines":2}}
114+
````
115+

src/index-cjs.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import noBadBlocks from './rules/noBadBlocks.js';
3232
import noBlankBlockDescriptions from './rules/noBlankBlockDescriptions.js';
3333
import noBlankBlocks from './rules/noBlankBlocks.js';
3434
import noDefaults from './rules/noDefaults.js';
35+
import noLinesBeforeBlocks from './rules/noLinesBeforeBlocks.js';
3536
import noMissingSyntax from './rules/noMissingSyntax.js';
3637
import noMultiAsterisks from './rules/noMultiAsterisks.js';
3738
import noRestrictedSyntax from './rules/noRestrictedSyntax.js';
@@ -107,6 +108,7 @@ index.rules = {
107108
'no-blank-block-descriptions': noBlankBlockDescriptions,
108109
'no-blank-blocks': noBlankBlocks,
109110
'no-defaults': noDefaults,
111+
'no-lines-before-blocks': noLinesBeforeBlocks,
110112
'no-missing-syntax': noMissingSyntax,
111113
'no-multi-asterisks': noMultiAsterisks,
112114
'no-restricted-syntax': noRestrictedSyntax,
@@ -280,6 +282,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
280282
'jsdoc/no-blank-block-descriptions': 'off',
281283
'jsdoc/no-blank-blocks': 'off',
282284
'jsdoc/no-defaults': warnOrError,
285+
'jsdoc/no-lines-before-blocks': warnOrError,
283286
'jsdoc/no-missing-syntax': 'off',
284287
'jsdoc/no-multi-asterisks': warnOrError,
285288
'jsdoc/no-restricted-syntax': 'off',

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import noBadBlocks from './rules/noBadBlocks.js';
3838
import noBlankBlockDescriptions from './rules/noBlankBlockDescriptions.js';
3939
import noBlankBlocks from './rules/noBlankBlocks.js';
4040
import noDefaults from './rules/noDefaults.js';
41+
import noLinesBeforeBlocks from './rules/noLinesBeforeBlocks.js';
4142
import noMissingSyntax from './rules/noMissingSyntax.js';
4243
import noMultiAsterisks from './rules/noMultiAsterisks.js';
4344
import noRestrictedSyntax from './rules/noRestrictedSyntax.js';
@@ -113,6 +114,7 @@ index.rules = {
113114
'no-blank-block-descriptions': noBlankBlockDescriptions,
114115
'no-blank-blocks': noBlankBlocks,
115116
'no-defaults': noDefaults,
117+
'no-lines-before-blocks': noLinesBeforeBlocks,
116118
'no-missing-syntax': noMissingSyntax,
117119
'no-multi-asterisks': noMultiAsterisks,
118120
'no-restricted-syntax': noRestrictedSyntax,
@@ -286,6 +288,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
286288
'jsdoc/no-blank-block-descriptions': 'off',
287289
'jsdoc/no-blank-blocks': 'off',
288290
'jsdoc/no-defaults': warnOrError,
291+
'jsdoc/no-lines-before-blocks': warnOrError,
289292
'jsdoc/no-missing-syntax': 'off',
290293
'jsdoc/no-multi-asterisks': warnOrError,
291294
'jsdoc/no-restricted-syntax': 'off',

src/iterateJsdoc.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,7 @@ const getIndentAndJSDoc = function (lines, jsdocNode) {
21162116
* @property {true} [nonGlobalSettings] Whether to avoid relying on settings for global contexts
21172117
* @property {true} [noTracking] Whether to disable the tracking of visited comment nodes (as
21182118
* non-tracked may conduct further actions)
2119+
* @property {Partial<Settings>} [ruleSettings] Any additional settings
21192120
* @property {true} [matchContext] Whether the rule expects contexts to be based on a match option
21202121
* @property {(args: {
21212122
* context: import('eslint').Rule.RuleContext,
@@ -2293,7 +2294,10 @@ const iterateAllJsdocs = (iterator, ruleConfig, contexts, additiveCommentContext
22932294
*/
22942295
'*:not(Program)' (node) {
22952296
const commentNode = getJSDocComment(
2296-
sourceCode, node, /** @type {Settings} */ (settings),
2297+
sourceCode, node, /** @type {Settings} */ ({
2298+
...settings,
2299+
...ruleConfig.ruleSettings,
2300+
}),
22972301
);
22982302
if (!ruleConfig.noTracking && trackedJsdocs.has(commentNode)) {
22992303
return;

src/rules.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,22 @@ export interface Rules {
10911091
}
10921092
];
10931093

1094+
/** Reports extra lines between functions (and other language structures) and their JSDoc blocks. */
1095+
"jsdoc/no-lines-before-blocks":
1096+
| []
1097+
| [
1098+
{
1099+
/**
1100+
* Whether to enable the fixer to remove line breaks
1101+
*/
1102+
enableFixer?: boolean;
1103+
/**
1104+
* Whether to use the setting `minLines` as the basis for fixing lines going past `maxLines`
1105+
*/
1106+
preferMinLines?: boolean;
1107+
}
1108+
];
1109+
10941110
/** Reports when certain comment structures are always expected. */
10951111
"jsdoc/no-missing-syntax":
10961112
| []

src/rules/noLinesBeforeBlocks.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import iterateJsdoc from '../iterateJsdoc.js';
2+
3+
export default iterateJsdoc(({
4+
context,
5+
indent,
6+
node,
7+
settings,
8+
sourceCode,
9+
utils,
10+
}) => {
11+
if (!node) {
12+
return;
13+
}
14+
15+
const {
16+
enableFixer = true,
17+
preferMinLines = false,
18+
} = context.options[0] || {};
19+
20+
const {
21+
maxLines,
22+
minLines,
23+
} = settings;
24+
25+
const prevToken = sourceCode.getTokenBefore(node, {
26+
includeComments: true,
27+
});
28+
29+
/* c8 ignore next 3 -- TS */
30+
if (!prevToken) {
31+
return;
32+
}
33+
34+
const interveningRange = /** @type {[number, number]} */ ([
35+
/** @type {number} */ (prevToken.range?.[1]),
36+
/** @type {number} */ (node.range?.[0]),
37+
]);
38+
39+
const ws = sourceCode.getText().slice(interveningRange[0], interveningRange[1]);
40+
41+
const newLines = ws.match(/\n/gv)?.length ?? 0;
42+
43+
if (newLines <= maxLines) {
44+
return;
45+
}
46+
47+
utils.reportJSDoc(
48+
'There should be no extra lines above structures with JSDoc blocks',
49+
null,
50+
enableFixer ? (fixer) => {
51+
return fixer.replaceTextRange(
52+
interveningRange,
53+
'\n'.repeat(preferMinLines ? minLines : maxLines) + indent,
54+
);
55+
} : null,
56+
);
57+
}, {
58+
iterateAllJsdocs: true,
59+
meta: {
60+
docs: {
61+
description: 'Reports extra lines between functions (and other language structures) and their JSDoc blocks.',
62+
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-lines-before-blocks.md#repos-sticky-header',
63+
},
64+
fixable: 'whitespace',
65+
schema: [
66+
{
67+
additionalProperties: false,
68+
properties: {
69+
enableFixer: {
70+
description: 'Whether to enable the fixer to remove line breaks',
71+
type: 'boolean',
72+
},
73+
preferMinLines: {
74+
description: 'Whether to use the setting `minLines` as the basis for fixing lines going past `maxLines`',
75+
type: 'boolean',
76+
},
77+
},
78+
type: 'object',
79+
},
80+
],
81+
type: 'suggestion',
82+
},
83+
ruleSettings: {
84+
maxLines: Number.POSITIVE_INFINITY,
85+
},
86+
});

0 commit comments

Comments
 (0)