Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

require-description-complete-sentence: limit checking by default to likely tags and new option array #338

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 26 additions & 3 deletions .README/rules/require-description-complete-sentence.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,33 @@ tag descriptions are written in complete sentences, i.e.,
* Every line in a paragraph (except the first) which starts with an uppercase
character must be preceded by a line ending with a period.

#### Options

##### `tags`

If you want additional tags to be checked for their descriptions, you may
add them within this option.

```js
{
'jsdoc/require-description-complete-sentence': ['error', {tags: ['see', 'copyright']}]
}
```

The tags `@param`/`@arg`/`@argument` and `@property`/`@prop` will be properly
parsed to ensure that the checked "description" text includes only the text
after the name.

All other tags will treat the text following the tag name, a space, and
an optional curly-bracketed type expression (and another space) as part of
its "description" (e.g., for `@returns {someType} some description`, the
description is `some description` while for `@some-tag xyz`, the description
is `xyz`).

|||
|---|---|
|Context|everywhere|
|Tags|`param`, `returns`, `description`|
|Aliases|`arg`, `argument`, `return`, `desc`|

|Tags|doc block, `param`, `returns`, `description`, `property`, `summary`, `file`, `classdesc`, `todo`, `deprecated`, `throws`, 'yields' and others added by `tags`|
|Aliases|`arg`, `argument`, `return`, `desc`, `prop`, `fileoverview`, `overview`, `exception`, `yield`|
|Options|`tags`|
<!-- assertions requireDescriptionCompleteSentence -->
111 changes: 97 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3666,12 +3666,37 @@ tag descriptions are written in complete sentences, i.e.,
* Every line in a paragraph (except the first) which starts with an uppercase
character must be preceded by a line ending with a period.

<a name="eslint-plugin-jsdoc-rules-require-description-complete-sentence-options-6"></a>
#### Options

<a name="eslint-plugin-jsdoc-rules-require-description-complete-sentence-options-6-tags-1"></a>
##### <code>tags</code>

If you want additional tags to be checked for their descriptions, you may
add them within this option.

```js
{
'jsdoc/require-description-complete-sentence': ['error', {tags: ['see', 'copyright']}]
}
```

The tags `@param`/`@arg`/`@argument` and `@property`/`@prop` will be properly
parsed to ensure that the checked "description" text includes only the text
after the name.

All other tags will treat the text following the tag name, a space, and
an optional curly-bracketed type expression (and another space) as part of
its "description" (e.g., for `@returns {someType} some description`, the
description is `some description` while for `@some-tag xyz`, the description
is `xyz`).

|||
|---|---|
|Context|everywhere|
|Tags|`param`, `returns`, `description`|
|Aliases|`arg`, `argument`, `return`, `desc`|

|Tags|doc block, `param`, `returns`, `description`, `property`, `summary`, `file`, `classdesc`, `todo`, `deprecated`, `throws`, 'yields' and others added by `tags`|
|Aliases|`arg`, `argument`, `return`, `desc`, `prop`, `fileoverview`, `overview`, `exception`, `yield`|
|Options|`tags`|
The following patterns are considered problems:

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

/**
* @typedef {Object} Hello World
* @throws {Object} Hello World
* hello world
*/
// Message: Sentence must end with a period.

/**
* @summary Foo
*/
function quux () {

}
// Message: Sentence must end with a period.

/**
* @throws {SomeType} Foo
*/
function quux () {

}
// Message: Sentence must end with a period.

/**
* @see Foo
*/
function quux () {

}
// Options: [{"tags":["see"]}]
// Message: Sentence must end with a period.
````

The following patterns are not considered problems:
Expand Down Expand Up @@ -4001,6 +4051,39 @@ function quux () {
function quux () {

}

/**
* @example Foo
*/
function quux () {

}

/**
* @see Foo
*/
function quux () {

}

/**
* Foo.
*
* @param foo Foo.
*/
function quux (foo) {

}

/**
* Foo.
*
* @param foo Foo.
*/
function quux (foo) {

}
// Options: [{"tags":["param"]}]
````


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

<a name="eslint-plugin-jsdoc-rules-require-description-options-6"></a>
<a name="eslint-plugin-jsdoc-rules-require-description-options-7"></a>
#### Options

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

<a name="eslint-plugin-jsdoc-rules-require-example-options-7"></a>
<a name="eslint-plugin-jsdoc-rules-require-example-options-8"></a>
#### Options

This rule has an object option.

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

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

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

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

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

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

Requires a hyphen before the `@param` description.

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

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"`.
Expand Down Expand Up @@ -4562,7 +4645,7 @@ function quux () {
Checks for presence of jsdoc comments, on class declarations as well as
functions.

<a name="eslint-plugin-jsdoc-rules-require-jsdoc-options-9"></a>
<a name="eslint-plugin-jsdoc-rules-require-jsdoc-options-10"></a>
#### Options

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

Requires that all function parameters are documented.

<a name="eslint-plugin-jsdoc-rules-require-param-options-10"></a>
<a name="eslint-plugin-jsdoc-rules-require-param-options-11"></a>
#### Options

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

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

<a name="eslint-plugin-jsdoc-rules-require-returns-options-11"></a>
<a name="eslint-plugin-jsdoc-rules-require-returns-options-12"></a>
#### Options

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

<a name="eslint-plugin-jsdoc-rules-valid-types-options-12"></a>
<a name="eslint-plugin-jsdoc-rules-valid-types-options-13"></a>
#### Options

- `allowEmptyNamepaths` (default: true) - Set to `false` to disallow
Expand Down
5 changes: 4 additions & 1 deletion src/jsdocUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,10 @@ const filterTags = (tags = [], filter) => {
};

const tagsWithNamesAndDescriptions = [
'param', 'arg', 'argument', 'property', 'prop', 'returns', 'return'
'param', 'arg', 'argument', 'property', 'prop',

// These two are parsed by our custom parser as though having a `name`
'returns', 'return'
];

const getTagsByType = (tags, tagPreference) => {
Expand Down
39 changes: 37 additions & 2 deletions src/rules/requireDescriptionCompleteSentence.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export default iterateJsdoc(({
jsdoc,
report,
jsdocNode,
context,
utils
}) => {
if (!jsdoc.tags ||
Expand All @@ -139,7 +140,27 @@ export default iterateJsdoc(({
validateDescription(description, report, jsdocNode, sourceCode, matchingJsdocTag);
});

const {tagsWithNames, tagsWithoutNames} = utils.getTagsByType(jsdoc.tags);
const options = context.options[0] || {};

const hasOptionTag = (tagName) => {
return Boolean(options.tags && options.tags.includes(tagName));
};

const {tagsWithNames} = utils.getTagsByType(jsdoc.tags);
const tagsWithoutNames = utils.filterTags(({tag: tagName}) => {
return [
// 'copyright' and 'see' might be good addition, but as the former may be
// sensitive text, and the latter may have just a link, they are not
// included by default
'summary', 'file', 'fileoverview', 'overview', 'classdesc', 'todo',
'deprecated', 'throws', 'exception', 'yields', 'yield'
].includes(tagName) ||
hasOptionTag(tagName) && !tagsWithNames.some(({tag}) => {
// If user accidentally adds tags with names (or like `returns`
// get parsed as having names), do not add to this list
return tag === tagName;
});
});

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

tagsWithoutNames.some((tag) => {
const description = (tag.name + ' ' + tag.description).trim();
const description = `${tag.name} ${tag.description}`.trim();

return validateDescription(description, report, jsdocNode, sourceCode, tag);
});
}, {
iterateAllJsdocs: true,
meta: {
fixable: 'code',
schema: [
{
additionalProperties: false,
properties: {
tags: {
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
}
],
type: 'suggestion'
}
});