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

Tag name preference #339

Merged
merged 3 commits into from Jul 14, 2019
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
97 changes: 97 additions & 0 deletions README.md
Expand Up @@ -2427,6 +2427,15 @@ function quux () {

}
// Message: @implements used on a non-constructor function

/**
* @implements {SomeClass}
*/
function quux () {

}
// Settings: {"jsdoc":{"tagNamePreference":{"implements":false}}}
// Message: Unexpected tag `@implements`
````

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

}

/**
*
*/
function quux () {

}
// Settings: {"jsdoc":{"tagNamePreference":{"implements":false}}}
````


Expand Down Expand Up @@ -2885,6 +2902,25 @@ const myObject = {
};
// Options: [{"contexts":["Property"]}]
// Message: JSDoc description does not satisfy the regex pattern.

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

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
// Options: [{"tags":{"param":true}}]
// Message: JSDoc description does not satisfy the regex pattern.

/**
* Foo bar
*/
function quux (foo) {

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
// Message: JSDoc description does not satisfy the regex pattern.
````

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

}
// Options: [{"tags":{"prop":true}}]

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

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}

/**
*
*/
function quux () {

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
````


Expand Down Expand Up @@ -3900,6 +3952,16 @@ function quux () {
}
// Options: [{"tags":["see"]}]
// Message: Sentence must end with a period.

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

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
// Options: [{"tags":["param"]}]
// Message: Sentence must end with a period.
````

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

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

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

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
// Options: [{"tags":["param"]}]

/**
*
*/
function quux (foo) {

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
````


Expand Down Expand Up @@ -4233,6 +4312,16 @@ function quux () {
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
// Options: [{"descriptionStyle":"tag"}]
// Message: Unexpected tag `@description`

/**
* @description
*/
function quux () {

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
// Options: [{"descriptionStyle":"any"}]
// Message: Missing JSDoc block description or @description declaration.
````

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

}
// Options: [{"descriptionStyle":"any"}]

/**
*
*/
function quux () {

}
// Settings: {"jsdoc":{"tagNamePreference":{"description":false}}}
````


Expand Down
29 changes: 20 additions & 9 deletions src/iterateJsdoc.js
Expand Up @@ -90,7 +90,7 @@ const getUtils = (
};

utils.getJsdocParameterNamesDeep = () => {
const param = utils.getPreferredTagName('param');
const param = utils.getPreferredTagName({tagName: 'param'});
if (!param) {
return false;
}
Expand All @@ -99,20 +99,26 @@ const getUtils = (
};

utils.getJsdocParameterNames = () => {
const param = utils.getPreferredTagName('param');
const param = utils.getPreferredTagName({tagName: 'param'});
if (!param) {
return false;
}

return jsdocUtils.getJsdocParameterNames(jsdoc, param);
};

utils.getPreferredTagName = (name, allowObjectReturn = false, defaultMessage = `Unexpected tag \`@${name}\``) => {
const ret = jsdocUtils.getPreferredTagName(name, tagNamePreference);
utils.getPreferredTagName = ({tagName, skipReportingBlockedTag = false, allowObjectReturn = false, defaultMessage = `Unexpected tag \`@${tagName}\``}) => {
const ret = jsdocUtils.getPreferredTagName(tagName, tagNamePreference);
const isObject = ret && typeof ret === 'object';
if (ret === false || isObject && !ret.replacement) {
if (utils.hasTag(tagName) && (ret === false || isObject && !ret.replacement)) {
if (skipReportingBlockedTag) {
return {
blocked: true,
tagName
};
}
const message = isObject && ret.message || defaultMessage;
report(message, null, utils.getTags(name)[0]);
report(message, null, utils.getTags(tagName)[0]);

return false;
}
Expand Down Expand Up @@ -239,9 +245,14 @@ const getUtils = (
return classJsdoc && jsdocUtils.hasTag(classJsdoc, tagName);
};

utils.forEachPreferredTag = (tagName, arrayHandler) => {
const targetTagName = utils.getPreferredTagName(tagName);
if (!targetTagName) {
utils.forEachPreferredTag = (tagName, arrayHandler, skipReportingBlockedTag = false) => {
const targetTagName = utils.getPreferredTagName({
skipReportingBlockedTag,
tagName
});
if (!targetTagName ||
skipReportingBlockedTag && targetTagName && typeof targetTagName === 'object'
) {
return;
}
const matchingJsdocTags = _.filter(jsdoc.tags || [], {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/checkParamNames.js
Expand Up @@ -100,7 +100,7 @@ export default iterateJsdoc(({
if (!jsdocParameterNamesDeep) {
return;
}
const targetTagName = utils.getPreferredTagName('param');
const targetTagName = utils.getPreferredTagName({tagName: 'param'});
const isError = validateParameterNames(targetTagName, functionParameterNames, jsdoc, report);

if (isError) {
Expand Down
10 changes: 5 additions & 5 deletions src/rules/checkTagNames.js
Expand Up @@ -45,11 +45,11 @@ export default iterateJsdoc(({
jsdoc.tags.forEach((jsdocTag) => {
const tagName = jsdocTag.tag;
if (utils.isValidTag(tagName, [...definedTags, ...definedPreferredTags, ...definedNonPreferredTags])) {
let preferredTagName = utils.getPreferredTagName(
tagName,
true,
`Blacklisted tag found (\`@${tagName}\`)`
);
let preferredTagName = utils.getPreferredTagName({
allowObjectReturn: true,
defaultMessage: `Blacklisted tag found (\`@${tagName}\`)`,
tagName
});
let message = `Invalid JSDoc tag (preference). Replace "${tagName}" JSDoc tag with "${preferredTagName}".`;
if (!preferredTagName) {
return;
Expand Down
2 changes: 1 addition & 1 deletion src/rules/matchDescription.js
Expand Up @@ -60,7 +60,7 @@ export default iterateJsdoc(({
if (hasOptionTag(targetTagName)) {
validateDescription(description, matchingJsdocTag);
}
});
}, true);

const whitelistedTags = utils.filterTags(({tag: tagName}) => {
return hasOptionTag(tagName);
Expand Down
24 changes: 18 additions & 6 deletions src/rules/requireDescription.js
Expand Up @@ -10,20 +10,30 @@ export default iterateJsdoc(({
if (utils.avoidDocs()) {
return;
}
const {descriptionStyle = 'body'} = context.options[0] || {};

const targetTagName = utils.getPreferredTagName('description');
let targetTagName = utils.getPreferredTagName({
// We skip reporting except when `@description` is essential to the rule,
// so user can block the tag and still meaningfully use this rule
// even if the tag is present (and `check-tag-names` is the one to
// normally report the fact that it is blocked but present)
skipReportingBlockedTag: descriptionStyle !== 'tag',
tagName: 'description'
});
if (!targetTagName) {
return;
}
const isBlocked = typeof targetTagName === 'object' && targetTagName.blocked;
if (isBlocked) {
targetTagName = targetTagName.tagName;
}

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

return exampleContent.length;
};

const {descriptionStyle = 'body'} = context.options[0] || {};

if (descriptionStyle !== 'tag') {
if (checkDescription(jsdoc.description || '')) {
return;
Expand All @@ -36,9 +46,11 @@ export default iterateJsdoc(({
}
}

const functionExamples = _.filter(jsdoc.tags, {
tag: targetTagName
});
const functionExamples = isBlocked ?
[] :
_.filter(jsdoc.tags, {
tag: targetTagName
});

if (!functionExamples.length) {
report(
Expand Down
2 changes: 1 addition & 1 deletion src/rules/requireDescriptionCompleteSentence.js
Expand Up @@ -138,7 +138,7 @@ export default iterateJsdoc(({
utils.forEachPreferredTag('description', (matchingJsdocTag) => {
const description = `${matchingJsdocTag.name} ${matchingJsdocTag.description}`.trim();
validateDescription(description, report, jsdocNode, sourceCode, matchingJsdocTag);
});
}, true);

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

Expand Down
2 changes: 1 addition & 1 deletion src/rules/requireParam.js
Expand Up @@ -24,7 +24,7 @@ export default iterateJsdoc(({
const jsdocParameterName = jsdocParameterNames[index];

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

return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/rules/requireReturns.js
Expand Up @@ -59,7 +59,7 @@ export default iterateJsdoc(({
forceReturnsWithAsync = false
} = context.options[0] || {};

const tagName = utils.getPreferredTagName('returns');
const tagName = utils.getPreferredTagName({tagName: 'returns'});
if (!tagName) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/rules/requireReturnsCheck.js
Expand Up @@ -26,7 +26,7 @@ export default iterateJsdoc(({
return;
}

const tagName = utils.getPreferredTagName('returns');
const tagName = utils.getPreferredTagName({tagName: 'returns'});
if (!tagName) {
return;
}
Expand Down
40 changes: 40 additions & 0 deletions test/rules/assertions/implementsOnClasses.js
Expand Up @@ -15,6 +15,29 @@ export default {
message: '@implements used on a non-constructor function'
}
]
},
{
code: `
/**
* @implements {SomeClass}
*/
function quux () {

}
`,
errors: [
{
line: 3,
message: 'Unexpected tag `@implements`'
}
],
settings: {
jsdoc: {
tagNamePreference: {
implements: false
}
}
}
}
],
valid: [
Expand Down Expand Up @@ -79,6 +102,23 @@ export default {

}
`
},
{
code: `
/**
*
*/
function quux () {

}
`,
settings: {
jsdoc: {
tagNamePreference: {
implements: false
}
}
}
}
]
};