eslint-plugin-jsdoc
Version:
JSDoc linting rules for ESLint.
215 lines (209 loc) • 7.43 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _escapeStringRegexp = _interopRequireDefault(require("escape-string-regexp"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
// https://babeljs.io/docs/en/babel-plugin-transform-react-jsx/
const jsxTagNames = new Set(['jsx', 'jsxFrag', 'jsxImportSource', 'jsxRuntime']);
const typedTagsAlwaysUnnecessary = new Set(['augments', 'callback', 'class', 'enum', 'implements', 'private', 'property', 'protected', 'public', 'readonly', 'this', 'type', 'typedef']);
const typedTagsNeedingName = new Set(['template']);
const typedTagsUnnecessaryOutsideDeclare = new Set(['abstract', 'access', 'class', 'constant', 'constructs', 'default', 'enum', 'export', 'exports', 'function', 'global', 'inherits', 'instance', 'interface', 'member', 'memberof', 'memberOf', 'method', 'mixes', 'mixin', 'module', 'name', 'namespace', 'override', 'property', 'requires', 'static', 'this']);
var _default = exports.default = (0, _iterateJsdoc.default)(({
sourceCode,
jsdoc,
report,
utils,
context,
node,
settings,
jsdocNode
}) => {
const
/**
* @type {{
* definedTags: string[],
* enableFixer: boolean,
* jsxTags: boolean,
* typed: boolean
}} */
{
definedTags = [],
enableFixer = true,
jsxTags,
typed
} = context.options[0] || {};
/** @type {(string|undefined)[]} */
let definedPreferredTags = [];
const {
tagNamePreference,
structuredTags
} = settings;
const definedStructuredTags = Object.keys(structuredTags);
const definedNonPreferredTags = Object.keys(tagNamePreference);
if (definedNonPreferredTags.length) {
definedPreferredTags = Object.values(tagNamePreference).map(preferredTag => {
if (typeof preferredTag === 'string') {
// May become an empty string but will be filtered out below
return preferredTag;
}
if (!preferredTag) {
return undefined;
}
if (typeof preferredTag !== 'object') {
utils.reportSettings('Invalid `settings.jsdoc.tagNamePreference`. Values must be falsy, a string, or an object.');
}
return preferredTag.replacement;
}).filter(Boolean);
}
/**
* @param {import('eslint').Rule.Node} subNode
* @returns {boolean}
*/
const isInAmbientContext = subNode => {
return subNode.type === 'Program' ? context.getFilename().endsWith('.d.ts') : Boolean( /** @type {import('@typescript-eslint/types').TSESTree.VariableDeclaration} */subNode.declare) || isInAmbientContext(subNode.parent);
};
/**
* @param {import('comment-parser').Spec} jsdocTag
* @returns {boolean}
*/
const tagIsRedundantWhenTyped = jsdocTag => {
var _node$parent;
if (!typedTagsUnnecessaryOutsideDeclare.has(jsdocTag.tag)) {
return false;
}
if (jsdocTag.tag === 'default') {
return false;
}
if (node === null) {
return false;
}
if (context.getFilename().endsWith('.d.ts') && ['Program', null, undefined].includes(node === null || node === void 0 || (_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type)) {
return false;
}
if (isInAmbientContext( /** @type {import('eslint').Rule.Node} */node)) {
return false;
}
return true;
};
/**
* @param {string} message
* @param {import('comment-parser').Spec} jsdocTag
* @param {import('../iterateJsdoc.js').Integer} tagIndex
* @param {Partial<import('comment-parser').Tokens>} [additionalTagChanges]
* @returns {void}
*/
const reportWithTagRemovalFixer = (message, jsdocTag, tagIndex, additionalTagChanges) => {
utils.reportJSDoc(message, jsdocTag, enableFixer ? () => {
if (jsdocTag.description.trim()) {
utils.changeTag(jsdocTag, {
postType: '',
type: '',
...additionalTagChanges
});
} else {
utils.removeTag(tagIndex, {
removeEmptyBlock: true
});
}
} : null, true);
};
/**
* @param {import('comment-parser').Spec} jsdocTag
* @param {import('../iterateJsdoc.js').Integer} tagIndex
* @returns {boolean}
*/
const checkTagForTypedValidity = (jsdocTag, tagIndex) => {
if (typedTagsAlwaysUnnecessary.has(jsdocTag.tag)) {
reportWithTagRemovalFixer(`'@${jsdocTag.tag}' is redundant when using a type system.`, jsdocTag, tagIndex, {
postTag: '',
tag: ''
});
return true;
}
if (tagIsRedundantWhenTyped(jsdocTag)) {
reportWithTagRemovalFixer(`'@${jsdocTag.tag}' is redundant outside of ambient (\`declare\`/\`.d.ts\`) contexts when using a type system.`, jsdocTag, tagIndex);
return true;
}
if (typedTagsNeedingName.has(jsdocTag.tag) && !jsdocTag.name) {
reportWithTagRemovalFixer(`'@${jsdocTag.tag}' without a name is redundant when using a type system.`, jsdocTag, tagIndex);
return true;
}
return false;
};
for (let tagIndex = 0; tagIndex < jsdoc.tags.length; tagIndex += 1) {
const jsdocTag = jsdoc.tags[tagIndex];
const tagName = jsdocTag.tag;
if (jsxTags && jsxTagNames.has(tagName)) {
continue;
}
if (typed && checkTagForTypedValidity(jsdocTag, tagIndex)) {
continue;
}
const validTags = [...definedTags, ...( /** @type {string[]} */definedPreferredTags), ...definedNonPreferredTags, ...definedStructuredTags, ...(typed ? typedTagsNeedingName : [])];
if (utils.isValidTag(tagName, validTags)) {
let preferredTagName = utils.getPreferredTagName({
allowObjectReturn: true,
defaultMessage: `Blacklisted tag found (\`@${tagName}\`)`,
tagName
});
if (!preferredTagName) {
continue;
}
let message;
if (typeof preferredTagName === 'object') {
({
message,
replacement: preferredTagName
} = /** @type {{message: string; replacement?: string | undefined;}} */
preferredTagName);
}
if (!message) {
message = `Invalid JSDoc tag (preference). Replace "${tagName}" JSDoc tag with "${preferredTagName}".`;
}
if (preferredTagName !== tagName) {
report(message, fixer => {
const replacement = sourceCode.getText(jsdocNode).replace(new RegExp(`@${(0, _escapeStringRegexp.default)(tagName)}\\b`, 'u'), `@${preferredTagName}`);
return fixer.replaceText(jsdocNode, replacement);
}, jsdocTag);
}
} else {
report(`Invalid JSDoc tag name "${tagName}".`, null, jsdocTag);
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Reports invalid block tag names.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-tag-names.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
definedTags: {
items: {
type: 'string'
},
type: 'array'
},
enableFixer: {
type: 'boolean'
},
jsxTags: {
type: 'boolean'
},
typed: {
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkTagNames.cjs.map
;