Skip to content

Commit d7fec9a

Browse files
committed
feat(no-defaults): add new rule to reports defaults on @param or @default and optionally report optional args; fixes #477
1 parent 1383c02 commit d7fec9a

File tree

6 files changed

+410
-1
lines changed

6 files changed

+410
-1
lines changed

.README/rules/no-defaults.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
### `no-defaults`
2+
3+
This rule reports defaults being used on the relevant portion of `@param`
4+
or `@default`. It also optionally reports the presence of the
5+
square-bracketed optional arguments at all.
6+
7+
The rule is intended to prevent the indication of defaults on tags where
8+
this would be redundant with ES6 default parameters (or for `@default`,
9+
where it would be redundant with the context to which the `@default`
10+
tag is attached).
11+
12+
Unless your `@default` is on a function, you will need to set `contexts`
13+
to an appropriate context, including, if you wish, "any".
14+
15+
#### Options
16+
17+
##### `noOptionalParamNames`
18+
19+
Set this to `true` to report the presence of optional parameters. May be
20+
used if the project is insisting on optionality being indicated by
21+
the presence of ES6 default parameters (bearing in mind that such
22+
"defaults" are only applied when the supplied value is missing or
23+
`undefined` but not for `null` or other "falsey" values).
24+
25+
##### `contexts`
26+
27+
Set this to an array of strings representing the AST context
28+
where you wish the rule to be applied.
29+
Overrides the default contexts (see below). Set to `"any"` if you want
30+
the rule to apply to any jsdoc block throughout your files (as is necessary
31+
for finding function blocks not attached to a function declaration or
32+
expression, i.e., `@callback` or `@function` (or its aliases `@func` or
33+
`@method`) (including those associated with an `@interface`).
34+
35+
|||
36+
|---|---|
37+
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`; others when `contexts` option enabled|
38+
|Tags|`param`, `default`|
39+
|Aliases|`arg`, `argument`, `defaultvalue`|
40+
|Options|`contexts`, `noOptionalParamNames`|
41+
42+
<!-- assertions noDefaults -->

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import emptyTags from './rules/emptyTags';
1313
import implementsOnClasses from './rules/implementsOnClasses';
1414
import matchDescription from './rules/matchDescription';
1515
import newlineAfterDescription from './rules/newlineAfterDescription';
16+
import noDefaults from './rules/noDefaults';
1617
import noTypes from './rules/noTypes';
1718
import noUndefinedTypes from './rules/noUndefinedTypes';
1819
import requireDescriptionCompleteSentence from './rules/requireDescriptionCompleteSentence';
@@ -54,6 +55,7 @@ export default {
5455
'jsdoc/implements-on-classes': 'warn',
5556
'jsdoc/match-description': 'off',
5657
'jsdoc/newline-after-description': 'warn',
58+
'jsdoc/no-defaults': 'off',
5759
'jsdoc/no-types': 'off',
5860
'jsdoc/no-undefined-types': 'warn',
5961
'jsdoc/require-description': 'off',
@@ -93,6 +95,7 @@ export default {
9395
'implements-on-classes': implementsOnClasses,
9496
'match-description': matchDescription,
9597
'newline-after-description': newlineAfterDescription,
98+
'no-defaults': noDefaults,
9699
'no-types': noTypes,
97100
'no-undefined-types': noUndefinedTypes,
98101
'require-description': requireDescription,

src/iterateJsdoc.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,21 @@ const parseComment = (commentNode, indent, trim = true) => {
2929
// @see https://github.com/yavorskiy/comment-parser/issues/21
3030
parsers: [
3131
commentParser.PARSERS.parse_tag,
32-
skipSeeLink(commentParser.PARSERS.parse_type),
32+
skipSeeLink(
33+
(str, data) => {
34+
if (['default', 'defaultvalue'].includes(data.tag)) {
35+
return null;
36+
}
37+
38+
return commentParser.PARSERS.parse_type(str, data);
39+
},
40+
),
3341
skipSeeLink(
3442
(str, data) => {
3543
if ([
3644
'example', 'return', 'returns', 'throws', 'exception',
3745
'access', 'version', 'since', 'license', 'author',
46+
'default', 'defaultvalue',
3847
].includes(data.tag)) {
3948
return null;
4049
}

src/rules/noDefaults.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import iterateJsdoc from '../iterateJsdoc';
2+
3+
export default iterateJsdoc(({
4+
context,
5+
utils,
6+
}) => {
7+
const {noOptionalParamNames} = context.options[0] || {};
8+
const paramTags = utils.getPresentTags(['param', 'arg', 'argument']);
9+
paramTags.forEach((tag) => {
10+
if (noOptionalParamNames && tag.optional) {
11+
utils.reportJSDoc(`Optional param names are not permitted on @${tag.tag}.`, tag, () => {
12+
tag.default = '';
13+
tag.optional = false;
14+
});
15+
} else if (tag.default) {
16+
utils.reportJSDoc(`Defaults are not permitted on @${tag.tag}.`, tag, () => {
17+
tag.default = '';
18+
});
19+
}
20+
});
21+
const defaultTags = utils.getPresentTags(['default', 'defaultvalue']);
22+
defaultTags.forEach((tag) => {
23+
if (tag.description) {
24+
utils.reportJSDoc(`Default values are not permitted on @${tag.tag}.`, tag, () => {
25+
tag.description = '';
26+
});
27+
}
28+
});
29+
}, {
30+
contextDefaults: true,
31+
meta: {
32+
fixable: 'code',
33+
schema: [
34+
{
35+
additionalProperties: false,
36+
properties: {
37+
contexts: {
38+
items: {
39+
type: 'string',
40+
},
41+
type: 'array',
42+
},
43+
noOptionalParamNames: {
44+
type: 'boolean',
45+
},
46+
},
47+
type: 'object',
48+
},
49+
],
50+
type: 'suggestion',
51+
},
52+
});

0 commit comments

Comments
 (0)