Skip to content

Commit b36a67a

Browse files
committed
feat(tag-lines): add startLinesWithNoTags option; fixes #1661
1 parent c7b132f commit b36a67a

File tree

5 files changed

+143
-9
lines changed

5 files changed

+143
-9
lines changed

.README/rules/tag-lines.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Removes or adds lines between tags or trailing tags.
2727
|Tags|Any|
2828
|Recommended|true|
2929
|Settings|N/A|
30-
|Options|string ("always", "any", "never") followed by object with `applyToEndTag`, `count`, `endLines`, `maxBlockLines`, `startLines`, `tags`|
30+
|Options|string ("always", "any", "never") followed by object with `applyToEndTag`, `count`, `endLines`, `maxBlockLines`, `startLines`, `startLinesWithNoTags`, `tags`|
3131

3232
## Failing examples
3333

docs/rules/tag-lines.md

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* [`endLines`](#user-content-tag-lines-options-endlines)
1010
* [`maxBlockLines`](#user-content-tag-lines-options-maxblocklines)
1111
* [`startLines`](#user-content-tag-lines-options-startlines)
12+
* [`startLinesWithNoTags`](#user-content-tag-lines-options-startlineswithnotags)
1213
* [`tags`](#user-content-tag-lines-options-tags)
1314
* [Context and settings](#user-content-tag-lines-context-and-settings)
1415
* [Failing examples](#user-content-tag-lines-failing-examples)
@@ -36,7 +37,7 @@ Removes or adds lines between tags or trailing tags.
3637

3738
The first option is a string with the following possible values: "always", "any", "never".
3839
Defaults to "never". "any" is only useful with `tags` (allowing non-enforcement of lines except
39-
for particular tags) or with `startLines`, `endLines`, or `maxBlockLines`. It is also
40+
for particular tags) or with `startLines`, `startLinesWithNoTags` `endLines`, or `maxBlockLines`. It is also
4041
necessary if using the linebreak-setting options of the `sort-tags` rule
4142
so that the two rules won't conflict in both attempting to set lines
4243
between tags.
@@ -89,6 +90,12 @@ a line count will not be enforced.
8990

9091
Defaults to `0`.
9192

93+
<a name="user-content-tag-lines-options-startlineswithnotags"></a>
94+
<a name="tag-lines-options-startlineswithnotags"></a>
95+
### <code>startLinesWithNoTags</code>
96+
97+
If set to a number, will enforce a starting lines count when there are no tags. Defaults to `undefined`.
98+
9299
<a name="user-content-tag-lines-options-tags"></a>
93100
<a name="tag-lines-options-tags"></a>
94101
### <code>tags</code>
@@ -114,7 +121,7 @@ Defaults to empty object.
114121
|Tags|Any|
115122
|Recommended|true|
116123
|Settings|N/A|
117-
|Options|string ("always", "any", "never") followed by object with `applyToEndTag`, `count`, `endLines`, `maxBlockLines`, `startLines`, `tags`|
124+
|Options|string ("always", "any", "never") followed by object with `applyToEndTag`, `count`, `endLines`, `maxBlockLines`, `startLines`, `startLinesWithNoTags`, `tags`|
118125

119126
<a name="user-content-tag-lines-failing-examples"></a>
120127
<a name="tag-lines-failing-examples"></a>
@@ -422,6 +429,24 @@ function myTestFunction(bar) {
422429
*/
423430
// "jsdoc/tag-lines": ["error"|"warn", "any",{"startLines":1}]
424431
// Message: Expected 1 lines after block description
432+
433+
/**
434+
* Some text
435+
*/
436+
function quux () {
437+
}
438+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"startLinesWithNoTags":1}]
439+
// Message: Expected 1 lines after block description
440+
441+
export interface SubOptionTypeMap {
442+
/**
443+
* Checkboxes have two states - true (checked) and false (unchecked)
444+
*
445+
*/
446+
checkbox: boolean
447+
}
448+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"startLinesWithNoTags":0}]
449+
// Message: Expected only 0 lines after block description
425450
````
426451

427452

@@ -668,5 +693,14 @@ class _Foo {
668693
* @param {string} a
669694
*/
670695
// "jsdoc/tag-lines": ["error"|"warn", "any",{"maxBlockLines":2}]
696+
697+
export interface SubOptionTypeMap {
698+
/**
699+
* Checkboxes have two states - true (checked) and false (unchecked)
700+
*
701+
*/
702+
checkbox: boolean
703+
}
704+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"endLines":0,"startLines":0}]
671705
````
672706

src/rules.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2948,6 +2948,10 @@ export interface Rules {
29482948
* Defaults to `0`.
29492949
*/
29502950
startLines?: number | null;
2951+
/**
2952+
* If set to a number, will enforce a starting lines count when there are no tags. Defaults to `undefined`.
2953+
*/
2954+
startLinesWithNoTags?: number;
29512955
/**
29522956
* Overrides the default behavior depending on specific tags.
29532957
*

src/rules/tagLines.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export default iterateJsdoc(({
7878
endLines = 0,
7979
maxBlockLines = null,
8080
startLines = 0,
81+
startLinesWithNoTags = null,
8182
tags = {},
8283
} = {},
8384
] = context.options;
@@ -292,8 +293,10 @@ export default iterateJsdoc(({
292293
return;
293294
}
294295

295-
if (typeof startLines === 'number') {
296-
if (!jsdoc.tags.length) {
296+
if (typeof startLines === 'number' || typeof startLinesWithNoTags === 'number') {
297+
const noTags = !jsdoc.tags.length;
298+
299+
if (noTags && startLinesWithNoTags === null) {
297300
return;
298301
}
299302

@@ -305,11 +308,13 @@ export default iterateJsdoc(({
305308
return;
306309
}
307310

311+
const startingLines = noTags ? startLinesWithNoTags : startLines;
312+
308313
const trailingLines = description.match(/\n+$/v)?.[0]?.length;
309-
const trailingDiff = (trailingLines ?? 0) - startLines;
314+
const trailingDiff = (trailingLines ?? 0) - startingLines;
310315
if (trailingDiff > 0) {
311316
utils.reportJSDoc(
312-
`Expected only ${startLines} line${startLines === 1 ? '' : 's'} after block description`,
317+
`Expected only ${startingLines} line${startingLines === 1 ? '' : 's'} after block description`,
313318
{
314319
line: lastDescriptionLine - trailingDiff,
315320
},
@@ -331,7 +336,7 @@ export default iterateJsdoc(({
331336
);
332337
} else if (trailingDiff < 0) {
333338
utils.reportJSDoc(
334-
`Expected ${startLines} lines after block description`,
339+
`Expected ${startingLines} lines after block description`,
335340
{
336341
line: lastDescriptionLine,
337342
},
@@ -371,7 +376,7 @@ export default iterateJsdoc(({
371376
schema: [
372377
{
373378
description: `Defaults to "never". "any" is only useful with \`tags\` (allowing non-enforcement of lines except
374-
for particular tags) or with \`startLines\`, \`endLines\`, or \`maxBlockLines\`. It is also
379+
for particular tags) or with \`startLines\`, \`startLinesWithNoTags\` \`endLines\`, or \`maxBlockLines\`. It is also
375380
necessary if using the linebreak-setting options of the \`sort-tags\` rule
376381
so that the two rules won't conflict in both attempting to set lines
377382
between tags.`,
@@ -440,6 +445,10 @@ a line count will not be enforced.
440445
441446
Defaults to \`0\`.`,
442447
},
448+
startLinesWithNoTags: {
449+
description: 'If set to a number, will enforce a starting lines count when there are no tags. Defaults to `undefined`.',
450+
type: 'number',
451+
},
443452
tags: {
444453
description: `Overrides the default behavior depending on specific tags.
445454

test/rules/assertions/tagLines.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,72 @@ export default /** @type {import('../index.js').TestCases} */ ({
930930
*/
931931
`,
932932
},
933+
{
934+
code: `
935+
/**
936+
* Some text
937+
*/
938+
function quux () {
939+
}
940+
`,
941+
errors: [
942+
{
943+
line: 3,
944+
message: 'Expected 1 lines after block description',
945+
},
946+
],
947+
languageOptions: {
948+
parser: typescriptEslintParser,
949+
},
950+
options: [
951+
'any',
952+
{
953+
startLinesWithNoTags: 1,
954+
},
955+
],
956+
output: `
957+
/**
958+
* Some text
959+
*
960+
*/
961+
function quux () {
962+
}
963+
`,
964+
},
965+
{
966+
code: `
967+
export interface SubOptionTypeMap {
968+
/**
969+
* Checkboxes have two states - true (checked) and false (unchecked)
970+
*
971+
*/
972+
checkbox: boolean
973+
}
974+
`,
975+
errors: [
976+
{
977+
line: 4,
978+
message: 'Expected only 0 lines after block description',
979+
},
980+
],
981+
languageOptions: {
982+
parser: typescriptEslintParser,
983+
},
984+
options: [
985+
'any',
986+
{
987+
startLinesWithNoTags: 0,
988+
},
989+
],
990+
output: `
991+
export interface SubOptionTypeMap {
992+
/**
993+
* Checkboxes have two states - true (checked) and false (unchecked)
994+
*/
995+
checkbox: boolean
996+
}
997+
`,
998+
},
933999
],
9341000
valid: [
9351001
{
@@ -1412,5 +1478,26 @@ export default /** @type {import('../index.js').TestCases} */ ({
14121478
},
14131479
],
14141480
},
1481+
{
1482+
code: `
1483+
export interface SubOptionTypeMap {
1484+
/**
1485+
* Checkboxes have two states - true (checked) and false (unchecked)
1486+
*
1487+
*/
1488+
checkbox: boolean
1489+
}
1490+
`,
1491+
languageOptions: {
1492+
parser: typescriptEslintParser,
1493+
},
1494+
options: [
1495+
'any',
1496+
{
1497+
endLines: 0,
1498+
startLines: 0,
1499+
},
1500+
],
1501+
},
14151502
],
14161503
});

0 commit comments

Comments
 (0)