Skip to content

Commit 0eb7a0c

Browse files
brettz9chiawendt
authored andcommitted
feat(require-description-complete-sentence): limit checking to certain default tags likely to have descriptions or by tags array for additional choices; fixes #337
Tags that are checked for description by default are: 'param', 'arg', 'argument', 'property', 'prop', 'returns', 'return', 'summary', 'file', 'fileoverview', 'overview', 'classdesc', 'todo', 'deprecated', 'throws', 'exception', 'yields', 'yield'. Note that `see` and `copyright` are not included by default because of potentially allowing a non-description or potential sensitivity, respectively.
1 parent 2e2af0d commit 0eb7a0c

File tree

5 files changed

+267
-21
lines changed

5 files changed

+267
-21
lines changed

.README/rules/require-description-complete-sentence.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,33 @@ tag descriptions are written in complete sentences, i.e.,
99
* Every line in a paragraph (except the first) which starts with an uppercase
1010
character must be preceded by a line ending with a period.
1111

12+
#### Options
13+
14+
##### `tags`
15+
16+
If you want additional tags to be checked for their descriptions, you may
17+
add them within this option.
18+
19+
```js
20+
{
21+
'jsdoc/require-description-complete-sentence': ['error', {tags: ['see', 'copyright']}]
22+
}
23+
```
24+
25+
The tags `@param`/`@arg`/`@argument` and `@property`/`@prop` will be properly
26+
parsed to ensure that the checked "description" text includes only the text
27+
after the name.
28+
29+
All other tags will treat the text following the tag name, a space, and
30+
an optional curly-bracketed type expression (and another space) as part of
31+
its "description" (e.g., for `@returns {someType} some description`, the
32+
description is `some description` while for `@some-tag xyz`, the description
33+
is `xyz`).
34+
1235
|||
1336
|---|---|
1437
|Context|everywhere|
15-
|Tags|`param`, `returns`, `description`|
16-
|Aliases|`arg`, `argument`, `return`, `desc`|
17-
38+
|Tags|doc block, `param`, `returns`, `description`, `property`, `summary`, `file`, `classdesc`, `todo`, `deprecated`, `throws`, 'yields' and others added by `tags`|
39+
|Aliases|`arg`, `argument`, `return`, `desc`, `prop`, `fileoverview`, `overview`, `exception`, `yield`|
40+
|Options|`tags`|
1841
<!-- assertions requireDescriptionCompleteSentence -->

README.md

Lines changed: 97 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3666,12 +3666,37 @@ tag descriptions are written in complete sentences, i.e.,
36663666
* Every line in a paragraph (except the first) which starts with an uppercase
36673667
character must be preceded by a line ending with a period.
36683668

3669+
<a name="eslint-plugin-jsdoc-rules-require-description-complete-sentence-options-6"></a>
3670+
#### Options
3671+
3672+
<a name="eslint-plugin-jsdoc-rules-require-description-complete-sentence-options-6-tags-1"></a>
3673+
##### <code>tags</code>
3674+
3675+
If you want additional tags to be checked for their descriptions, you may
3676+
add them within this option.
3677+
3678+
```js
3679+
{
3680+
'jsdoc/require-description-complete-sentence': ['error', {tags: ['see', 'copyright']}]
3681+
}
3682+
```
3683+
3684+
The tags `@param`/`@arg`/`@argument` and `@property`/`@prop` will be properly
3685+
parsed to ensure that the checked "description" text includes only the text
3686+
after the name.
3687+
3688+
All other tags will treat the text following the tag name, a space, and
3689+
an optional curly-bracketed type expression (and another space) as part of
3690+
its "description" (e.g., for `@returns {someType} some description`, the
3691+
description is `some description` while for `@some-tag xyz`, the description
3692+
is `xyz`).
3693+
36693694
|||
36703695
|---|---|
36713696
|Context|everywhere|
3672-
|Tags|`param`, `returns`, `description`|
3673-
|Aliases|`arg`, `argument`, `return`, `desc`|
3674-
3697+
|Tags|doc block, `param`, `returns`, `description`, `property`, `summary`, `file`, `classdesc`, `todo`, `deprecated`, `throws`, 'yields' and others added by `tags`|
3698+
|Aliases|`arg`, `argument`, `return`, `desc`, `prop`, `fileoverview`, `overview`, `exception`, `yield`|
3699+
|Options|`tags`|
36753700
The following patterns are considered problems:
36763701

36773702
````js
@@ -3846,10 +3871,35 @@ function quux (foo) {
38463871
// Message: Sentence should start with an uppercase character.
38473872

38483873
/**
3849-
* @typedef {Object} Hello World
3874+
* @throws {Object} Hello World
38503875
* hello world
38513876
*/
38523877
// Message: Sentence must end with a period.
3878+
3879+
/**
3880+
* @summary Foo
3881+
*/
3882+
function quux () {
3883+
3884+
}
3885+
// Message: Sentence must end with a period.
3886+
3887+
/**
3888+
* @throws {SomeType} Foo
3889+
*/
3890+
function quux () {
3891+
3892+
}
3893+
// Message: Sentence must end with a period.
3894+
3895+
/**
3896+
* @see Foo
3897+
*/
3898+
function quux () {
3899+
3900+
}
3901+
// Options: [{"tags":["see"]}]
3902+
// Message: Sentence must end with a period.
38533903
````
38543904

38553905
The following patterns are not considered problems:
@@ -4001,6 +4051,39 @@ function quux () {
40014051
function quux () {
40024052

40034053
}
4054+
4055+
/**
4056+
* @example Foo
4057+
*/
4058+
function quux () {
4059+
4060+
}
4061+
4062+
/**
4063+
* @see Foo
4064+
*/
4065+
function quux () {
4066+
4067+
}
4068+
4069+
/**
4070+
* Foo.
4071+
*
4072+
* @param foo Foo.
4073+
*/
4074+
function quux (foo) {
4075+
4076+
}
4077+
4078+
/**
4079+
* Foo.
4080+
*
4081+
* @param foo Foo.
4082+
*/
4083+
function quux (foo) {
4084+
4085+
}
4086+
// Options: [{"tags":["param"]}]
40044087
````
40054088

40064089

@@ -4015,7 +4098,7 @@ Requires that all functions have a description.
40154098
`"tag"`) must have a non-empty description that explains the purpose of the
40164099
method.
40174100

4018-
<a name="eslint-plugin-jsdoc-rules-require-description-options-6"></a>
4101+
<a name="eslint-plugin-jsdoc-rules-require-description-options-7"></a>
40194102
#### Options
40204103

40214104
An options object may have any of the following properties:
@@ -4274,25 +4357,25 @@ Requires that all functions have examples.
42744357
* All functions must have one or more `@example` tags.
42754358
* Every example tag must have a non-empty description that explains the method's usage.
42764359

4277-
<a name="eslint-plugin-jsdoc-rules-require-example-options-7"></a>
4360+
<a name="eslint-plugin-jsdoc-rules-require-example-options-8"></a>
42784361
#### Options
42794362

42804363
This rule has an object option.
42814364

4282-
<a name="eslint-plugin-jsdoc-rules-require-example-options-7-exemptedby"></a>
4365+
<a name="eslint-plugin-jsdoc-rules-require-example-options-8-exemptedby"></a>
42834366
##### <code>exemptedBy</code>
42844367

42854368
Array of tags (e.g., `['type']`) whose presence on the document
42864369
block avoids the need for an `@example`. Defaults to an empty array.
42874370

4288-
<a name="eslint-plugin-jsdoc-rules-require-example-options-7-avoidexampleonconstructors"></a>
4371+
<a name="eslint-plugin-jsdoc-rules-require-example-options-8-avoidexampleonconstructors"></a>
42894372
##### <code>avoidExampleOnConstructors</code>
42904373

42914374
Set to `true` to avoid the need for an example on a constructor (whether
42924375
indicated as such by a jsdoc tag or by being within an ES6 `class`).
42934376
Defaults to `false`.
42944377

4295-
<a name="eslint-plugin-jsdoc-rules-require-example-options-7-contexts-1"></a>
4378+
<a name="eslint-plugin-jsdoc-rules-require-example-options-8-contexts-1"></a>
42964379
##### <code>contexts</code>
42974380

42984381
Set this to an array of strings representing the AST context
@@ -4456,7 +4539,7 @@ function quux () {
44564539

44574540
Requires a hyphen before the `@param` description.
44584541

4459-
<a name="eslint-plugin-jsdoc-rules-require-hyphen-before-param-description-options-8"></a>
4542+
<a name="eslint-plugin-jsdoc-rules-require-hyphen-before-param-description-options-9"></a>
44604543
#### Options
44614544

44624545
This rule takes one optional string argument. If it is `"always"` then a problem is raised when there is no hyphen before the description. If it is `"never"` then a problem is raised when there is a hyphen before the description. The default value is `"always"`.
@@ -4562,7 +4645,7 @@ function quux () {
45624645
Checks for presence of jsdoc comments, on class declarations as well as
45634646
functions.
45644647

4565-
<a name="eslint-plugin-jsdoc-rules-require-jsdoc-options-9"></a>
4648+
<a name="eslint-plugin-jsdoc-rules-require-jsdoc-options-10"></a>
45664649
#### Options
45674650

45684651
Accepts one optional options object with the following optional keys.
@@ -5605,7 +5688,7 @@ function quux (foo) {
56055688

56065689
Requires that all function parameters are documented.
56075690

5608-
<a name="eslint-plugin-jsdoc-rules-require-param-options-10"></a>
5691+
<a name="eslint-plugin-jsdoc-rules-require-param-options-11"></a>
56095692
#### Options
56105693

56115694
An options object accepts one optional property:
@@ -6536,7 +6619,7 @@ Requires returns are documented.
65366619

65376620
Will also report if multiple `@returns` tags are present.
65386621

6539-
<a name="eslint-plugin-jsdoc-rules-require-returns-options-11"></a>
6622+
<a name="eslint-plugin-jsdoc-rules-require-returns-options-12"></a>
65406623
#### Options
65416624

65426625
- `exemptedBy` - Array of tags (e.g., `['type']`) whose presence on the document
@@ -6992,7 +7075,7 @@ Also impacts behaviors on namepath (or event)-defining and pointing tags:
69927075
allow `#`, `.`, or `~` at the end (which is not allowed at the end of
69937076
normal paths).
69947077

6995-
<a name="eslint-plugin-jsdoc-rules-valid-types-options-12"></a>
7078+
<a name="eslint-plugin-jsdoc-rules-valid-types-options-13"></a>
69967079
#### Options
69977080

69987081
- `allowEmptyNamepaths` (default: true) - Set to `false` to disallow

src/jsdocUtils.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,10 @@ const filterTags = (tags = [], filter) => {
525525
};
526526

527527
const tagsWithNamesAndDescriptions = [
528-
'param', 'arg', 'argument', 'property', 'prop', 'returns', 'return'
528+
'param', 'arg', 'argument', 'property', 'prop',
529+
530+
// These two are parsed by our custom parser as though having a `name`
531+
'returns', 'return'
529532
];
530533

531534
const getTagsByType = (tags, tagPreference) => {

src/rules/requireDescriptionCompleteSentence.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export default iterateJsdoc(({
124124
jsdoc,
125125
report,
126126
jsdocNode,
127+
context,
127128
utils
128129
}) => {
129130
if (!jsdoc.tags ||
@@ -139,7 +140,27 @@ export default iterateJsdoc(({
139140
validateDescription(description, report, jsdocNode, sourceCode, matchingJsdocTag);
140141
});
141142

142-
const {tagsWithNames, tagsWithoutNames} = utils.getTagsByType(jsdoc.tags);
143+
const options = context.options[0] || {};
144+
145+
const hasOptionTag = (tagName) => {
146+
return Boolean(options.tags && options.tags.includes(tagName));
147+
};
148+
149+
const {tagsWithNames} = utils.getTagsByType(jsdoc.tags);
150+
const tagsWithoutNames = utils.filterTags(({tag: tagName}) => {
151+
return [
152+
// 'copyright' and 'see' might be good addition, but as the former may be
153+
// sensitive text, and the latter may have just a link, they are not
154+
// included by default
155+
'summary', 'file', 'fileoverview', 'overview', 'classdesc', 'todo',
156+
'deprecated', 'throws', 'exception', 'yields', 'yield'
157+
].includes(tagName) ||
158+
hasOptionTag(tagName) && !tagsWithNames.some(({tag}) => {
159+
// If user accidentally adds tags with names (or like `returns`
160+
// get parsed as having names), do not add to this list
161+
return tag === tagName;
162+
});
163+
});
143164

144165
tagsWithNames.some((tag) => {
145166
const description = _.trimStart(tag.description, '- ');
@@ -148,14 +169,28 @@ export default iterateJsdoc(({
148169
});
149170

150171
tagsWithoutNames.some((tag) => {
151-
const description = (tag.name + ' ' + tag.description).trim();
172+
const description = `${tag.name} ${tag.description}`.trim();
152173

153174
return validateDescription(description, report, jsdocNode, sourceCode, tag);
154175
});
155176
}, {
156177
iterateAllJsdocs: true,
157178
meta: {
158179
fixable: 'code',
180+
schema: [
181+
{
182+
additionalProperties: false,
183+
properties: {
184+
tags: {
185+
items: {
186+
type: 'string'
187+
},
188+
type: 'array'
189+
}
190+
},
191+
type: 'object'
192+
}
193+
],
159194
type: 'suggestion'
160195
}
161196
});

0 commit comments

Comments
 (0)