Skip to content

Commit 26276ba

Browse files
authored
feat(multiline-blocks): adds requireSingleLineUnderCount option; fixes #1158 (#1409)
* feat(`multiline-blocks`): adds `requireSingleLineUnderCount` option; fixes #1158 * docs: clarify
1 parent 35e95a1 commit 26276ba

File tree

4 files changed

+449
-2
lines changed

4 files changed

+449
-2
lines changed

.README/rules/multiline-blocks.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ line will be reported. (Text preceding a newline is not reported.)
3838
If this is `true`, any single line blocks will be reported, except those which
3939
are whitelisted in `singleLineTags`.
4040

41+
### `requireSingleLineUnderCount` (defaults to `null`)
42+
43+
If this number is set, it indicates a minimum line width for a single line of
44+
JSDoc content spread over a multi-line comment block. If a line is under the
45+
minimum length, it will be reported so as to enforce single line JSDoc blocks
46+
for such cases.
47+
4148
### `singleLineTags` (defaults to `['lends', 'type']`)
4249

4350
An array of tags which can nevertheless be allowed as single line blocks when
@@ -96,7 +103,7 @@ cannot be reliably added after the tag either).
96103
|Tags|Any (though `singleLineTags` and `multilineTags` control the application)|
97104
|Recommended|true|
98105
|Settings||
99-
|Options|`allowMultipleTags`, `minimumLengthForMultiline`, `multilineTags`, `noFinalLineText`, `noMultilineBlocks`, `noSingleLineBlocks`, `noZeroLineText`, `singleLineTags`|
106+
|Options|`allowMultipleTags`, `minimumLengthForMultiline`, `multilineTags`, `noFinalLineText`, `noMultilineBlocks`, `noSingleLineBlocks`, `noZeroLineText`, `requireSingleLineUnderCount`, `singleLineTags`|
100107

101108
## Failing examples
102109

docs/rules/multiline-blocks.md

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ line will be reported. (Text preceding a newline is not reported.)
5050
If this is `true`, any single line blocks will be reported, except those which
5151
are whitelisted in `singleLineTags`.
5252

53+
<a name="user-content-options-requiresinglelineundercount-defaults-to-null"></a>
54+
<a name="options-requiresinglelineundercount-defaults-to-null"></a>
55+
### <code>requireSingleLineUnderCount</code> (defaults to <code>null</code>)
56+
57+
If this number is set, it indicates a minimum line width for a single line of
58+
JSDoc content spread over a multi-line comment block. If a line is under the
59+
minimum length, it will be reported so as to enforce single line JSDoc blocks
60+
for such cases.
61+
5362
<a name="user-content-options-singlelinetags-defaults-to-lends-type"></a>
5463
<a name="options-singlelinetags-defaults-to-lends-type"></a>
5564
### <code>singleLineTags</code> (defaults to <code>[&#39;lends&#39;, &#39;type&#39;]</code>)
@@ -120,7 +129,7 @@ cannot be reliably added after the tag either).
120129
|Tags|Any (though `singleLineTags` and `multilineTags` control the application)|
121130
|Recommended|true|
122131
|Settings||
123-
|Options|`allowMultipleTags`, `minimumLengthForMultiline`, `multilineTags`, `noFinalLineText`, `noMultilineBlocks`, `noSingleLineBlocks`, `noZeroLineText`, `singleLineTags`|
132+
|Options|`allowMultipleTags`, `minimumLengthForMultiline`, `multilineTags`, `noFinalLineText`, `noMultilineBlocks`, `noSingleLineBlocks`, `noZeroLineText`, `requireSingleLineUnderCount`, `singleLineTags`|
124133

125134
<a name="user-content-failing-examples"></a>
126135
<a name="failing-examples"></a>
@@ -283,6 +292,38 @@ The following patterns are considered problems:
283292
* Description */
284293
// "jsdoc/multiline-blocks": ["error"|"warn", {"noFinalLineText":true}]
285294
// Message: Should have no text on the final line (before the `*/`).
295+
296+
/**
297+
* Description too short
298+
*/
299+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
300+
// Message: Description is too short to be multi-line.
301+
302+
/** Description too short
303+
*/
304+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
305+
// Message: Description is too short to be multi-line.
306+
307+
/**
308+
* Description too short */
309+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
310+
// Message: Description is too short to be multi-line.
311+
312+
/**
313+
* @someTag {someType} Description too short
314+
*/
315+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
316+
// Message: Description is too short to be multi-line.
317+
318+
/** @someTag {someType} Description too short
319+
*/
320+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
321+
// Message: Description is too short to be multi-line.
322+
323+
/**
324+
* @someTag {someType} Description too short */
325+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
326+
// Message: Description is too short to be multi-line.
286327
````
287328

288329

@@ -394,5 +435,39 @@ The following patterns are not considered problems:
394435

395436
/** @someTag with Description */
396437
// "jsdoc/multiline-blocks": ["error"|"warn", {"noFinalLineText":true}]
438+
439+
/**
440+
* This description here is very much long enough, I'd say, wouldn't you?
441+
*/
442+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
443+
444+
/**
445+
* This description here is
446+
* on multiple lines.
447+
*/
448+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
449+
450+
/** This description here is on a single line, so it doesn't matter if it goes over. */
451+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
452+
453+
/**
454+
* @someTag {someType} This description here is very much long enough, I'd say, wouldn't you?
455+
*/
456+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
457+
458+
/**
459+
* @someTag {someType} This description here is
460+
* on multiple lines.
461+
*/
462+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
463+
464+
/** @someTag {someTag} This description here is on a single line, so it doesn't matter if it goes over. */
465+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
466+
467+
/**
468+
* Description short but has...
469+
* @someTag
470+
*/
471+
// "jsdoc/multiline-blocks": ["error"|"warn", {"requireSingleLineUnderCount":80}]
397472
````
398473

src/rules/multilineBlocks.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,151 @@
11
import iterateJsdoc from '../iterateJsdoc.js';
22

3+
/**
4+
* @param {import('@es-joy/jsdoccomment').JsdocBlockWithInline} jsdoc
5+
* @param {import('../iterateJsdoc.js').Utils} utils
6+
* @param {number} requireSingleLineUnderCount
7+
*/
8+
const checkForShortTags = (jsdoc, utils, requireSingleLineUnderCount) => {
9+
if (!requireSingleLineUnderCount || !jsdoc.tags.length) {
10+
return false;
11+
}
12+
13+
let lastLineWithTag = 0;
14+
let exceedsLength = false;
15+
let hasDesc = false;
16+
const tagLines = jsdoc.source.reduce((acc, {
17+
tokens: {
18+
delimiter,
19+
description: desc,
20+
name,
21+
postDelimiter,
22+
postName,
23+
postTag,
24+
postType,
25+
start,
26+
tag,
27+
type,
28+
},
29+
}, idx) => {
30+
if (tag.length) {
31+
lastLineWithTag = idx;
32+
if (
33+
start.length + delimiter.length + postDelimiter.length +
34+
type.length + postType.length + name.length + postName.length +
35+
tag.length + postTag.length + desc.length <
36+
requireSingleLineUnderCount
37+
) {
38+
exceedsLength = true;
39+
}
40+
41+
return acc + 1;
42+
} else if (desc.length) {
43+
hasDesc = true;
44+
return acc;
45+
}
46+
47+
return acc;
48+
}, 0);
49+
// Could be tagLines > 1
50+
if (!hasDesc && exceedsLength && tagLines === 1) {
51+
const fixer = () => {
52+
const tokens = jsdoc.source[lastLineWithTag].tokens;
53+
jsdoc.source = [
54+
{
55+
number: 0,
56+
source: '',
57+
tokens: utils.seedTokens({
58+
delimiter: '/**',
59+
description: tokens.description.trimEnd() + ' ',
60+
end: '*/',
61+
name: tokens.name,
62+
postDelimiter: ' ',
63+
postName: tokens.postName,
64+
postTag: tokens.postTag,
65+
postType: tokens.postType,
66+
start: jsdoc.source[0].tokens.start,
67+
tag: tokens.tag,
68+
type: tokens.type,
69+
}),
70+
},
71+
];
72+
};
73+
74+
utils.reportJSDoc(
75+
'Description is too short to be multi-line.',
76+
null,
77+
fixer,
78+
);
79+
return true;
80+
}
81+
82+
return false;
83+
};
84+
85+
/**
86+
* @param {import('@es-joy/jsdoccomment').JsdocBlockWithInline} jsdoc
87+
* @param {import('../iterateJsdoc.js').Utils} utils
88+
* @param {number} requireSingleLineUnderCount
89+
*/
90+
const checkForShortDescriptions = (jsdoc, utils, requireSingleLineUnderCount) => {
91+
if (!requireSingleLineUnderCount || jsdoc.tags.length) {
92+
return false;
93+
}
94+
95+
let lastLineWithDesc = 0;
96+
let exceedsLength = false;
97+
const descLines = jsdoc.source.reduce((acc, {
98+
tokens: {
99+
delimiter,
100+
description: desc,
101+
postDelimiter,
102+
start,
103+
},
104+
}, idx) => {
105+
if (desc.length) {
106+
lastLineWithDesc = idx;
107+
if (
108+
start.length + delimiter.length + postDelimiter.length + desc.length <
109+
requireSingleLineUnderCount
110+
) {
111+
exceedsLength = true;
112+
}
113+
114+
return acc + 1;
115+
}
116+
117+
return acc;
118+
}, 0);
119+
// Could be descLines > 1
120+
if (exceedsLength && descLines === 1) {
121+
const fixer = () => {
122+
const desc = jsdoc.source[lastLineWithDesc].tokens.description;
123+
jsdoc.source = [
124+
{
125+
number: 0,
126+
source: '',
127+
tokens: utils.seedTokens({
128+
delimiter: '/**',
129+
description: desc.trimEnd() + ' ',
130+
end: '*/',
131+
postDelimiter: ' ',
132+
start: jsdoc.source[0].tokens.start,
133+
}),
134+
},
135+
];
136+
};
137+
138+
utils.reportJSDoc(
139+
'Description is too short to be multi-line.',
140+
null,
141+
fixer,
142+
);
143+
return true;
144+
}
145+
146+
return false;
147+
};
148+
3149
export default iterateJsdoc(({
4150
context,
5151
jsdoc,
@@ -15,6 +161,7 @@ export default iterateJsdoc(({
15161
noMultilineBlocks = false,
16162
noSingleLineBlocks = false,
17163
noZeroLineText = true,
164+
requireSingleLineUnderCount = null,
18165
singleLineTags = [
19166
'lends', 'type',
20167
],
@@ -62,6 +209,16 @@ export default iterateJsdoc(({
62209
return;
63210
}
64211

212+
if (checkForShortDescriptions(jsdoc, utils, requireSingleLineUnderCount)
213+
) {
214+
return;
215+
}
216+
217+
if (checkForShortTags(jsdoc, utils, requireSingleLineUnderCount)
218+
) {
219+
return;
220+
}
221+
65222
const lineChecks = () => {
66223
if (
67224
noZeroLineText &&
@@ -318,6 +475,9 @@ export default iterateJsdoc(({
318475
noZeroLineText: {
319476
type: 'boolean',
320477
},
478+
requireSingleLineUnderCount: {
479+
type: 'number',
480+
},
321481
singleLineTags: {
322482
items: {
323483
type: 'string',

0 commit comments

Comments
 (0)