Initial import
This commit is contained in:
+189
@@ -0,0 +1,189 @@
|
||||
import iterateJsdoc from '../iterateJsdoc.js';
|
||||
import {
|
||||
areDocsInformative,
|
||||
} from 'are-docs-informative';
|
||||
|
||||
const defaultAliases = {
|
||||
a: [
|
||||
'an', 'our',
|
||||
],
|
||||
};
|
||||
|
||||
const defaultUselessWords = [
|
||||
'a', 'an', 'i', 'in', 'of', 's', 'the',
|
||||
];
|
||||
|
||||
/* eslint-disable complexity -- Temporary */
|
||||
|
||||
/**
|
||||
* @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node|null|undefined} node
|
||||
* @returns {string[]}
|
||||
*/
|
||||
const getNamesFromNode = (node) => {
|
||||
switch (node?.type) {
|
||||
case 'AccessorProperty':
|
||||
case 'MethodDefinition':
|
||||
case 'PropertyDefinition':
|
||||
case 'TSAbstractAccessorProperty':
|
||||
case 'TSAbstractMethodDefinition':
|
||||
case 'TSAbstractPropertyDefinition':
|
||||
return [
|
||||
...getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */ (
|
||||
node.parent
|
||||
).parent,
|
||||
),
|
||||
...getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.key),
|
||||
),
|
||||
];
|
||||
|
||||
case 'ExportDefaultDeclaration':
|
||||
case 'ExportNamedDeclaration':
|
||||
return getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.ExportNamedDeclaration} */
|
||||
(node).declaration
|
||||
);
|
||||
case 'ClassDeclaration':
|
||||
case 'ClassExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'FunctionExpression':
|
||||
case 'TSModuleDeclaration':
|
||||
case 'TSMethodSignature':
|
||||
case 'TSDeclareFunction':
|
||||
case 'TSEnumDeclaration':
|
||||
case 'TSEnumMember':
|
||||
case 'TSInterfaceDeclaration':
|
||||
case 'TSTypeAliasDeclaration':
|
||||
return getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.ClassDeclaration} */
|
||||
(node).id,
|
||||
);
|
||||
case 'Identifier':
|
||||
return [
|
||||
node.name,
|
||||
];
|
||||
case 'Property':
|
||||
return getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.key),
|
||||
);
|
||||
case 'VariableDeclaration':
|
||||
return getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.declarations[0]),
|
||||
);
|
||||
case 'VariableDeclarator':
|
||||
return [
|
||||
...getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.id),
|
||||
),
|
||||
...getNamesFromNode(
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.Node} */
|
||||
(node.init),
|
||||
),
|
||||
].filter(Boolean);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
/* eslint-enable complexity -- Temporary */
|
||||
|
||||
export default iterateJsdoc(({
|
||||
context,
|
||||
jsdoc,
|
||||
node,
|
||||
report,
|
||||
utils,
|
||||
}) => {
|
||||
const /** @type {{aliases: {[key: string]: string[]}, excludedTags: string[], uselessWords: string[]}} */ {
|
||||
aliases = defaultAliases,
|
||||
excludedTags = [],
|
||||
uselessWords = defaultUselessWords,
|
||||
} = context.options[0] || {};
|
||||
const nodeNames = getNamesFromNode(node);
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {string} extraName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const descriptionIsRedundant = (text, extraName = '') => {
|
||||
const textTrimmed = text.trim();
|
||||
return Boolean(textTrimmed) && !areDocsInformative(textTrimmed, [
|
||||
extraName, nodeNames,
|
||||
].filter(Boolean).join(' '), {
|
||||
aliases,
|
||||
uselessWords,
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
description,
|
||||
lastDescriptionLine,
|
||||
} = utils.getDescription();
|
||||
let descriptionReported = false;
|
||||
|
||||
for (const tag of jsdoc.tags) {
|
||||
if (excludedTags.includes(tag.tag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (descriptionIsRedundant(tag.description, tag.name)) {
|
||||
utils.reportJSDoc(
|
||||
'This tag description only repeats the name it describes.',
|
||||
tag,
|
||||
);
|
||||
}
|
||||
|
||||
descriptionReported ||= tag.description === description &&
|
||||
/** @type {import('comment-parser').Spec & {line: import('../iterateJsdoc.js').Integer}} */
|
||||
(tag).line === lastDescriptionLine;
|
||||
}
|
||||
|
||||
if (!descriptionReported && descriptionIsRedundant(description)) {
|
||||
report('This description only repeats the name it describes.');
|
||||
}
|
||||
}, {
|
||||
iterateAllJsdocs: true,
|
||||
meta: {
|
||||
docs: {
|
||||
description:
|
||||
'This rule reports doc comments that only restate their attached name.',
|
||||
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md#repos-sticky-header',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
aliases: {
|
||||
patternProperties: {
|
||||
'.*': {
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
},
|
||||
excludedTags: {
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
uselessWords: {
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
],
|
||||
type: 'suggestion',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user