UNPKG

eslint-plugin-jsdoc

Version:
356 lines (331 loc) 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // If supporting Node >= 10, we could loosen the default to this for the // initial letter: \\p{Upper} const matchDescriptionDefault = '^\n?([A-Z`\\d_][\\s\\S]*[.?!`\\p{RGI_Emoji}]\\s*)?$'; /** * @param {string} value * @param {string} userDefault * @returns {string} */ const stringOrDefault = (value, userDefault) => { return typeof value === 'string' ? value : userDefault || matchDescriptionDefault; }; var _default = exports.default = (0, _iterateJsdoc.default)(({ context, jsdoc, report, utils }) => { const { mainDescription, matchDescription, message, nonemptyTags = true, tags = {} } = context.options[0] || {}; /** * @param {string} desc * @param {import('comment-parser').Spec} [tag] * @returns {void} */ const validateDescription = (desc, tag) => { let mainDescriptionMatch = mainDescription; let errorMessage = message; if (typeof mainDescription === 'object') { mainDescriptionMatch = mainDescription.match; errorMessage = mainDescription.message; } if (mainDescriptionMatch === false && (!tag || !Object.hasOwn(tags, tag.tag))) { return; } let tagValue = mainDescriptionMatch; if (tag) { const tagName = tag.tag; if (typeof tags[tagName] === 'object') { tagValue = tags[tagName].match; errorMessage = tags[tagName].message; } else { tagValue = tags[tagName]; } } const regex = utils.getRegexFromString(stringOrDefault(tagValue, matchDescription)); if (!regex.test(desc)) { report(errorMessage || 'JSDoc description does not satisfy the regex pattern.', null, tag || { // Add one as description would typically be into block line: jsdoc.source[0].number + 1 }); } }; const { description } = utils.getDescription(); if (description) { validateDescription(description); } /** * @param {string} tagName * @returns {boolean} */ const hasNoTag = tagName => { return !tags[tagName]; }; for (const tag of ['description', 'summary', 'file', 'classdesc']) { utils.forEachPreferredTag(tag, (matchingJsdocTag, targetTagName) => { const desc = (matchingJsdocTag.name + ' ' + utils.getTagDescription(matchingJsdocTag)).trim(); if (hasNoTag(targetTagName)) { validateDescription(desc, matchingJsdocTag); } }, true); } if (nonemptyTags) { for (const tag of ['copyright', 'example', 'see', 'todo']) { utils.forEachPreferredTag(tag, (matchingJsdocTag, targetTagName) => { const desc = (matchingJsdocTag.name + ' ' + utils.getTagDescription(matchingJsdocTag)).trim(); if (hasNoTag(targetTagName) && !/.+/v.test(desc)) { report('JSDoc description must not be empty.', null, matchingJsdocTag); } }); } } if (!Object.keys(tags).length) { return; } /** * @param {string} tagName * @returns {boolean} */ const hasOptionTag = tagName => { return Boolean(tags[tagName]); }; const whitelistedTags = utils.filterTags(({ tag: tagName }) => { return hasOptionTag(tagName); }); const { tagsWithNames, tagsWithoutNames } = utils.getTagsByType(whitelistedTags); tagsWithNames.some(tag => { const desc = /** @type {string} */utils.getTagDescription(tag).replace(/^[\- ]*/v, '').trim(); return validateDescription(desc, tag); }); tagsWithoutNames.some(tag => { const desc = (tag.name + ' ' + utils.getTagDescription(tag)).trim(); return validateDescription(desc, tag); }); }, { contextDefaults: true, meta: { docs: { description: 'Enforces a regular expression pattern on descriptions.', url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/match-description.md#repos-sticky-header' }, schema: [{ additionalProperties: false, properties: { contexts: { description: `Set this to an array of strings representing the AST context (or an object with optional \`context\` and \`comment\` properties) where you wish the rule to be applied (e.g., \`ClassDeclaration\` for ES6 classes). \`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context. Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`, \`FunctionExpression\`). Set to \`"any"\` if you want the rule to apply to any JSDoc block throughout your files. See the ["AST and Selectors"](../#advanced-ast-and-selectors) section of our Advanced docs for more on the expected format.`, items: { anyOf: [{ type: 'string' }, { additionalProperties: false, properties: { comment: { type: 'string' }, context: { type: 'string' } }, type: 'object' }] }, type: 'array' }, mainDescription: { description: `If you wish to override the main block description without changing the default \`match-description\` (which can cascade to the \`tags\` with \`true\`), you may use \`mainDescription\`: \`\`\`js { 'jsdoc/match-description': ['error', { mainDescription: '[A-Z].*\\\\.', tags: { param: true, returns: true } }] } \`\`\` There is no need to add \`mainDescription: true\`, as by default, the main block description (and only the main block description) is linted, though you may disable checking it by setting it to \`false\`. You may also provide an object with \`message\`: \`\`\`js { 'jsdoc/match-description': ['error', { mainDescription: { message: 'Capitalize first word of JSDoc block descriptions', match: '[A-Z].*\\\\.' }, tags: { param: true, returns: true } }] } \`\`\``, oneOf: [{ format: 'regex', type: 'string' }, { type: 'boolean' }, { additionalProperties: false, properties: { match: { oneOf: [{ format: 'regex', type: 'string' }, { type: 'boolean' }] }, message: { type: 'string' } }, type: 'object' }] }, matchDescription: { description: `You can supply your own expression to override the default, passing a \`matchDescription\` string on the options object. Defaults to using (only) the \`v\` flag, so to add your own flags, encapsulate your expression as a string, but like a literal, e.g., \`/[A-Z].*\\./vi\`. \`\`\`js { 'jsdoc/match-description': ['error', {matchDescription: '[A-Z].*\\\\.'}] } \`\`\``, format: 'regex', type: 'string' }, message: { description: `You may provide a custom default message by using the following format: \`\`\`js { 'jsdoc/match-description': ['error', { message: 'The default description should begin with a capital letter.' }] } \`\`\` This can be overridden per tag or for the main block description by setting \`message\` within \`tags\` or \`mainDescription\`, respectively.`, type: 'string' }, nonemptyTags: { description: `If not set to \`false\`, will enforce that the following tags have at least some content: - \`@copyright\` - \`@example\` - \`@see\` - \`@todo\` If you supply your own tag description for any of the above tags in \`tags\`, your description will take precedence.`, type: 'boolean' }, tags: { description: `If you want different regular expressions to apply to tags, you may use the \`tags\` option object: \`\`\`js { 'jsdoc/match-description': ['error', {tags: { param: '\\\\- [A-Z].*\\\\.', returns: '[A-Z].*\\\\.' }}] } \`\`\` In place of a string, you can also add \`true\` to indicate that a particular tag should be linted with the \`matchDescription\` value (or the default). \`\`\`js { 'jsdoc/match-description': ['error', {tags: { param: true, returns: true }}] } \`\`\` Alternatively, you may supply an object with a \`message\` property to indicate the error message for that tag. \`\`\`js { 'jsdoc/match-description': ['error', {tags: { param: {message: 'Begin with a hyphen', match: '\\\\- [A-Z].*\\\\.'}, returns: {message: 'Capitalize for returns (the default)', match: true} }}] } \`\`\` The tags \`@param\`/\`@arg\`/\`@argument\` and \`@property\`/\`@prop\` will be properly parsed to ensure that the matched "description" text includes only the text after the name. All other tags will treat the text following the tag name, a space, and an optional curly-bracketed type expression (and another space) as part of its "description" (e.g., for \`@returns {someType} some description\`, the description is \`some description\` while for \`@some-tag xyz\`, the description is \`xyz\`).`, patternProperties: { '.*': { oneOf: [{ format: 'regex', type: 'string' }, { enum: [true], type: 'boolean' }, { additionalProperties: false, properties: { match: { oneOf: [{ format: 'regex', type: 'string' }, { enum: [true], type: 'boolean' }] }, message: { type: 'string' } }, type: 'object' }] } }, type: 'object' } }, type: 'object' }], type: 'suggestion' } }); module.exports = exports.default; //# sourceMappingURL=matchDescription.cjs.map