Skip to content

Commit b28a407

Browse files
committed
fix(require-description, implements-on-classes, check-param-names, check-tag-names, require-param, require-returns, require-returns-check, require-hyphen-before-param-description, require-param-description, require-param-name, require-param-type, require-returns-description, require-returns-type): only report blocking of tag by user if problematic tag is present; fixes #332
1 parent 0eb7a0c commit b28a407

12 files changed

+230
-26
lines changed

README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2427,6 +2427,15 @@ function quux () {
24272427

24282428
}
24292429
// Message: @implements used on a non-constructor function
2430+
2431+
/**
2432+
* @implements {SomeClass}
2433+
*/
2434+
function quux () {
2435+
2436+
}
2437+
// Settings: {"jsdoc":{"tagNamePreference":{"implements":false}}}
2438+
// Message: Unexpected tag `@implements`
24302439
````
24312440

24322441
The following patterns are not considered problems:
@@ -2478,6 +2487,14 @@ const quux = class {
24782487
function quux () {
24792488

24802489
}
2490+
2491+
/**
2492+
*
2493+
*/
2494+
function quux () {
2495+
2496+
}
2497+
// Settings: {"jsdoc":{"tagNamePreference":{"implements":false}}}
24812498
````
24822499

24832500

@@ -2885,6 +2902,25 @@ const myObject = {
28852902
};
28862903
// Options: [{"contexts":["Property"]}]
28872904
// Message: JSDoc description does not satisfy the regex pattern.
2905+
2906+
/**
2907+
* @param foo Foo bar
2908+
*/
2909+
function quux (foo) {
2910+
2911+
}
2912+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
2913+
// Options: [{"tags":{"param":true}}]
2914+
// Message: JSDoc description does not satisfy the regex pattern.
2915+
2916+
/**
2917+
* Foo bar
2918+
*/
2919+
function quux (foo) {
2920+
2921+
}
2922+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
2923+
// Message: JSDoc description does not satisfy the regex pattern.
28882924
````
28892925

28902926
The following patterns are not considered problems:
@@ -3149,6 +3185,22 @@ function quux (foo) {
31493185

31503186
}
31513187
// Options: [{"tags":{"prop":true}}]
3188+
3189+
/**
3190+
* @param foo Foo bar.
3191+
*/
3192+
function quux (foo) {
3193+
3194+
}
3195+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
3196+
3197+
/**
3198+
*
3199+
*/
3200+
function quux () {
3201+
3202+
}
3203+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
31523204
````
31533205

31543206

@@ -3900,6 +3952,16 @@ function quux () {
39003952
}
39013953
// Options: [{"tags":["see"]}]
39023954
// Message: Sentence must end with a period.
3955+
3956+
/**
3957+
* @param foo Foo bar
3958+
*/
3959+
function quux (foo) {
3960+
3961+
}
3962+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
3963+
// Options: [{"tags":["param"]}]
3964+
// Message: Sentence must end with a period.
39033965
````
39043966

39053967
The following patterns are not considered problems:
@@ -4084,6 +4146,23 @@ function quux (foo) {
40844146

40854147
}
40864148
// Options: [{"tags":["param"]}]
4149+
4150+
/**
4151+
* @param foo Foo bar.
4152+
*/
4153+
function quux (foo) {
4154+
4155+
}
4156+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
4157+
// Options: [{"tags":["param"]}]
4158+
4159+
/**
4160+
*
4161+
*/
4162+
function quux (foo) {
4163+
4164+
}
4165+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
40874166
````
40884167

40894168

@@ -4233,6 +4312,16 @@ function quux () {
42334312
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
42344313
// Options: [{"descriptionStyle":"tag"}]
42354314
// Message: Unexpected tag `@description`
4315+
4316+
/**
4317+
* @description
4318+
*/
4319+
function quux () {
4320+
4321+
}
4322+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
4323+
// Options: [{"descriptionStyle":"any"}]
4324+
// Message: Missing JSDoc block description or @description declaration.
42364325
````
42374326

42384327
The following patterns are not considered problems:
@@ -4346,6 +4435,14 @@ function quux () {
43464435

43474436
}
43484437
// Options: [{"descriptionStyle":"any"}]
4438+
4439+
/**
4440+
*
4441+
*/
4442+
function quux () {
4443+
4444+
}
4445+
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
43494446
````
43504447

43514448

src/iterateJsdoc.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ const getUtils = (
9090
};
9191

9292
utils.getJsdocParameterNamesDeep = () => {
93-
const param = utils.getPreferredTagName('param');
93+
const param = utils.getPreferredTagName({tagName: 'param'});
9494
if (!param) {
9595
return false;
9696
}
@@ -99,20 +99,26 @@ const getUtils = (
9999
};
100100

101101
utils.getJsdocParameterNames = () => {
102-
const param = utils.getPreferredTagName('param');
102+
const param = utils.getPreferredTagName({tagName: 'param'});
103103
if (!param) {
104104
return false;
105105
}
106106

107107
return jsdocUtils.getJsdocParameterNames(jsdoc, param);
108108
};
109109

110-
utils.getPreferredTagName = (name, allowObjectReturn = false, defaultMessage = `Unexpected tag \`@${name}\``) => {
111-
const ret = jsdocUtils.getPreferredTagName(name, tagNamePreference);
110+
utils.getPreferredTagName = ({tagName, skipReportingBlockedTag = false, allowObjectReturn = false, defaultMessage = `Unexpected tag \`@${tagName}\``}) => {
111+
const ret = jsdocUtils.getPreferredTagName(tagName, tagNamePreference);
112112
const isObject = ret && typeof ret === 'object';
113-
if (ret === false || isObject && !ret.replacement) {
113+
if (utils.hasTag(tagName) && (ret === false || isObject && !ret.replacement)) {
114+
if (skipReportingBlockedTag) {
115+
return {
116+
blocked: true,
117+
tagName
118+
};
119+
}
114120
const message = isObject && ret.message || defaultMessage;
115-
report(message, null, utils.getTags(name)[0]);
121+
report(message, null, utils.getTags(tagName)[0]);
116122

117123
return false;
118124
}
@@ -239,9 +245,14 @@ const getUtils = (
239245
return classJsdoc && jsdocUtils.hasTag(classJsdoc, tagName);
240246
};
241247

242-
utils.forEachPreferredTag = (tagName, arrayHandler) => {
243-
const targetTagName = utils.getPreferredTagName(tagName);
244-
if (!targetTagName) {
248+
utils.forEachPreferredTag = (tagName, arrayHandler, skipReportingBlockedTag = false) => {
249+
const targetTagName = utils.getPreferredTagName({
250+
skipReportingBlockedTag,
251+
tagName
252+
});
253+
if (!targetTagName ||
254+
skipReportingBlockedTag && targetTagName && typeof targetTagName === 'object'
255+
) {
245256
return;
246257
}
247258
const matchingJsdocTags = _.filter(jsdoc.tags || [], {

src/rules/checkParamNames.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export default iterateJsdoc(({
100100
if (!jsdocParameterNamesDeep) {
101101
return;
102102
}
103-
const targetTagName = utils.getPreferredTagName('param');
103+
const targetTagName = utils.getPreferredTagName({tagName: 'param'});
104104
const isError = validateParameterNames(targetTagName, functionParameterNames, jsdoc, report);
105105

106106
if (isError) {

src/rules/checkTagNames.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ export default iterateJsdoc(({
4545
jsdoc.tags.forEach((jsdocTag) => {
4646
const tagName = jsdocTag.tag;
4747
if (utils.isValidTag(tagName, [...definedTags, ...definedPreferredTags, ...definedNonPreferredTags])) {
48-
let preferredTagName = utils.getPreferredTagName(
49-
tagName,
50-
true,
51-
`Blacklisted tag found (\`@${tagName}\`)`
52-
);
48+
let preferredTagName = utils.getPreferredTagName({
49+
allowObjectReturn: true,
50+
defaultMessage: `Blacklisted tag found (\`@${tagName}\`)`,
51+
tagName
52+
});
5353
let message = `Invalid JSDoc tag (preference). Replace "${tagName}" JSDoc tag with "${preferredTagName}".`;
5454
if (!preferredTagName) {
5555
return;

src/rules/matchDescription.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default iterateJsdoc(({
6060
if (hasOptionTag(targetTagName)) {
6161
validateDescription(description, matchingJsdocTag);
6262
}
63-
});
63+
}, true);
6464

6565
const whitelistedTags = utils.filterTags(({tag: tagName}) => {
6666
return hasOptionTag(tagName);

src/rules/requireDescription.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,30 @@ export default iterateJsdoc(({
1010
if (utils.avoidDocs()) {
1111
return;
1212
}
13+
const {descriptionStyle = 'body'} = context.options[0] || {};
1314

14-
const targetTagName = utils.getPreferredTagName('description');
15+
let targetTagName = utils.getPreferredTagName({
16+
// We skip reporting except when `@description` is essential to the rule,
17+
// so user can block the tag and still meaningfully use this rule
18+
// even if the tag is present (and `check-tag-names` is the one to
19+
// normally report the fact that it is blocked but present)
20+
skipReportingBlockedTag: descriptionStyle !== 'tag',
21+
tagName: 'description'
22+
});
1523
if (!targetTagName) {
1624
return;
1725
}
26+
const isBlocked = typeof targetTagName === 'object' && targetTagName.blocked;
27+
if (isBlocked) {
28+
targetTagName = targetTagName.tagName;
29+
}
1830

1931
const checkDescription = (description) => {
2032
const exampleContent = _.compact(description.trim().split('\n'));
2133

2234
return exampleContent.length;
2335
};
2436

25-
const {descriptionStyle = 'body'} = context.options[0] || {};
26-
2737
if (descriptionStyle !== 'tag') {
2838
if (checkDescription(jsdoc.description || '')) {
2939
return;
@@ -36,9 +46,11 @@ export default iterateJsdoc(({
3646
}
3747
}
3848

39-
const functionExamples = _.filter(jsdoc.tags, {
40-
tag: targetTagName
41-
});
49+
const functionExamples = isBlocked ?
50+
[] :
51+
_.filter(jsdoc.tags, {
52+
tag: targetTagName
53+
});
4254

4355
if (!functionExamples.length) {
4456
report(

src/rules/requireDescriptionCompleteSentence.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export default iterateJsdoc(({
138138
utils.forEachPreferredTag('description', (matchingJsdocTag) => {
139139
const description = `${matchingJsdocTag.name} ${matchingJsdocTag.description}`.trim();
140140
validateDescription(description, report, jsdocNode, sourceCode, matchingJsdocTag);
141-
});
141+
}, true);
142142

143143
const options = context.options[0] || {};
144144

src/rules/requireParam.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default iterateJsdoc(({
2424
const jsdocParameterName = jsdocParameterNames[index];
2525

2626
if (!jsdocParameterName) {
27-
report(`Missing JSDoc @${utils.getPreferredTagName('param')} "${functionParameterName}" declaration.`);
27+
report(`Missing JSDoc @${utils.getPreferredTagName({tagName: 'param'})} "${functionParameterName}" declaration.`);
2828

2929
return true;
3030
}

src/rules/requireReturns.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default iterateJsdoc(({
5959
forceReturnsWithAsync = false
6060
} = context.options[0] || {};
6161

62-
const tagName = utils.getPreferredTagName('returns');
62+
const tagName = utils.getPreferredTagName({tagName: 'returns'});
6363
if (!tagName) {
6464
return;
6565
}

src/rules/requireReturnsCheck.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default iterateJsdoc(({
2626
return;
2727
}
2828

29-
const tagName = utils.getPreferredTagName('returns');
29+
const tagName = utils.getPreferredTagName({tagName: 'returns'});
3030
if (!tagName) {
3131
return;
3232
}

test/rules/assertions/implementsOnClasses.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,29 @@ export default {
1515
message: '@implements used on a non-constructor function'
1616
}
1717
]
18+
},
19+
{
20+
code: `
21+
/**
22+
* @implements {SomeClass}
23+
*/
24+
function quux () {
25+
26+
}
27+
`,
28+
errors: [
29+
{
30+
line: 3,
31+
message: 'Unexpected tag `@implements`'
32+
}
33+
],
34+
settings: {
35+
jsdoc: {
36+
tagNamePreference: {
37+
implements: false
38+
}
39+
}
40+
}
1841
}
1942
],
2043
valid: [
@@ -79,6 +102,23 @@ export default {
79102
80103
}
81104
`
105+
},
106+
{
107+
code: `
108+
/**
109+
*
110+
*/
111+
function quux () {
112+
113+
}
114+
`,
115+
settings: {
116+
jsdoc: {
117+
tagNamePreference: {
118+
implements: false
119+
}
120+
}
121+
}
82122
}
83123
]
84124
};

0 commit comments

Comments
 (0)