UNPKG

eslint-plugin-jsdoc

Version:
159 lines (139 loc) 3.94 kB
import iterateJsdoc from '../iterateJsdoc.js'; /** * @param {string} targetTagName * @param {boolean} enableFixer * @param {import('comment-parser').Block} jsdoc * @param {import('../iterateJsdoc.js').Utils} utils * @returns {boolean} */ const validatePropertyNames = ( targetTagName, enableFixer, jsdoc, utils, ) => { const propertyTags = Object.entries(jsdoc.tags).filter(([ , tag, ]) => { return tag.tag === targetTagName; }); return propertyTags.some(([ , tag, ], index) => { /** @type {import('../iterateJsdoc.js').Integer} */ let tagsIndex; const dupeTagInfo = propertyTags.find(([ tgsIndex, tg, ], idx) => { tagsIndex = Number(tgsIndex); return tg.name === tag.name && idx !== index; }); if (dupeTagInfo) { utils.reportJSDoc(`Duplicate @${targetTagName} "${tag.name}"`, dupeTagInfo[1], enableFixer ? () => { utils.removeTag(tagsIndex); } : null); return true; } return false; }); }; /** * @param {string} targetTagName * @param {{ * idx: number; * name: string; * type: string; * }[]} jsdocPropertyNames * @param {import('comment-parser').Block} jsdoc * @param {import('../iterateJsdoc.js').Report} report */ const validatePropertyNamesDeep = ( targetTagName, jsdocPropertyNames, jsdoc, report, ) => { /** @type {string} */ let lastRealProperty; return jsdocPropertyNames.some(({ idx, name: jsdocPropertyName, }) => { const isPropertyPath = jsdocPropertyName.includes('.'); if (isPropertyPath) { if (!lastRealProperty) { report(`@${targetTagName} path declaration ("${jsdocPropertyName}") appears before any real property.`, null, jsdoc.tags[idx]); return true; } let pathRootNodeName = jsdocPropertyName.slice(0, jsdocPropertyName.indexOf('.')); if (pathRootNodeName.endsWith('[]')) { pathRootNodeName = pathRootNodeName.slice(0, -2); } if (pathRootNodeName !== lastRealProperty) { report( `@${targetTagName} path declaration ("${jsdocPropertyName}") root node name ("${pathRootNodeName}") ` + `does not match previous real property name ("${lastRealProperty}").`, null, jsdoc.tags[idx], ); return true; } } else { lastRealProperty = jsdocPropertyName; } return false; }); }; export default iterateJsdoc(({ context, jsdoc, report, utils, }) => { const { enableFixer = false, } = context.options[0] || {}; const jsdocPropertyNamesDeep = utils.getJsdocTagsDeep('property'); if (!jsdocPropertyNamesDeep || !jsdocPropertyNamesDeep.length) { return; } const targetTagName = /** @type {string} */ (utils.getPreferredTagName({ tagName: 'property', })); const isError = validatePropertyNames( targetTagName, enableFixer, jsdoc, utils, ); if (isError) { return; } validatePropertyNamesDeep( targetTagName, jsdocPropertyNamesDeep, jsdoc, report, ); }, { iterateAllJsdocs: true, meta: { docs: { description: 'Ensures that property names in JSDoc are not duplicated on the same block and that nested properties have defined roots.', url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-property-names.md#repos-sticky-header', }, fixable: 'code', schema: [ { additionalProperties: false, properties: { enableFixer: { description: `Set to \`true\` to auto-remove \`@property\` duplicates (based on identical names). Note that this option will remove duplicates of the same name even if the definitions do not match in other ways (e.g., the second property will be removed even if it has a different type or description).`, type: 'boolean', }, }, type: 'object', }, ], type: 'suggestion', }, });