UNPKG

eslint-plugin-jsdoc

Version:
270 lines (266 loc) 7.66 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 }; } var _default = exports.default = (0, _iterateJsdoc.default)(({ context, jsdoc, utils }) => { const [alwaysNever = 'never', { applyToEndTag = true, count = 1, endLines = 0, startLines = 0, tags = {} } = {}] = context.options; // eslint-disable-next-line complexity -- Temporary jsdoc.tags.some((tg, tagIdx) => { let lastTag; /** * @type {null|import('../iterateJsdoc.js').Integer} */ let lastEmpty = null; /** * @type {null|import('../iterateJsdoc.js').Integer} */ let reportIndex = null; let emptyLinesCount = 0; for (const [idx, { tokens: { description, end, name, tag, type } }] of tg.source.entries()) { // May be text after a line break within a tag description if (description) { reportIndex = null; } if (lastTag && ['always', 'any'].includes(tags[lastTag.slice(1)]?.lines)) { continue; } const empty = !tag && !name && !type && !description; if (empty && !end && (alwaysNever === 'never' || lastTag && tags[lastTag.slice(1)]?.lines === 'never')) { reportIndex = idx; continue; } if (!end) { if (empty) { emptyLinesCount++; } else { emptyLinesCount = 0; } lastEmpty = empty ? idx : null; } lastTag = tag; } if (typeof endLines === 'number' && lastEmpty !== null && tagIdx === jsdoc.tags.length - 1) { const lineDiff = endLines - emptyLinesCount; if (lineDiff < 0) { const fixer = () => { utils.removeTag(tagIdx, { tagSourceOffset: /** @type {import('../iterateJsdoc.js').Integer} */lastEmpty + lineDiff + 1 }); }; utils.reportJSDoc(`Expected ${endLines} trailing lines`, { line: tg.source[lastEmpty].number + lineDiff + 1 }, fixer); } else if (lineDiff > 0) { const fixer = () => { utils.addLines(tagIdx, /** @type {import('../iterateJsdoc.js').Integer} */lastEmpty, endLines - emptyLinesCount); }; utils.reportJSDoc(`Expected ${endLines} trailing lines`, { line: tg.source[lastEmpty].number }, fixer); } return true; } if (reportIndex !== null) { const fixer = () => { utils.removeTag(tagIdx, { tagSourceOffset: (/** @type {import('../iterateJsdoc.js').Integer} */ reportIndex) }); }; utils.reportJSDoc('Expected no lines between tags', { line: tg.source[0].number + 1 }, fixer); return true; } return false; }); (applyToEndTag ? jsdoc.tags : jsdoc.tags.slice(0, -1)).some((tg, tagIdx) => { /** * @type {{ * idx: import('../iterateJsdoc.js').Integer, * number: import('../iterateJsdoc.js').Integer * }[]} */ const lines = []; let currentTag; let tagSourceIdx = 0; for (const [idx, { number, tokens: { description, end, name, tag, type } }] of tg.source.entries()) { if (description) { lines.splice(0, lines.length); tagSourceIdx = idx; } if (tag) { currentTag = tag; } if (!tag && !name && !type && !description && !end) { lines.push({ idx, number }); } } const currentTg = currentTag && tags[currentTag.slice(1)]; const tagCount = currentTg?.count; const defaultAlways = alwaysNever === 'always' && currentTg?.lines !== 'never' && currentTg?.lines !== 'any' && lines.length < count; let overrideAlways; let fixCount = count; if (!defaultAlways) { fixCount = typeof tagCount === 'number' ? tagCount : count; overrideAlways = currentTg?.lines === 'always' && lines.length < fixCount; } if (defaultAlways || overrideAlways) { const fixer = () => { utils.addLines(tagIdx, lines[lines.length - 1]?.idx || tagSourceIdx + 1, fixCount - lines.length); }; const line = lines[lines.length - 1]?.number || tg.source[tagSourceIdx].number; utils.reportJSDoc(`Expected ${fixCount} line${fixCount === 1 ? '' : 's'} between tags but found ${lines.length}`, { line }, fixer); return true; } return false; }); if (typeof startLines === 'number') { if (!jsdoc.tags.length) { return; } const { description, lastDescriptionLine } = utils.getDescription(); if (!/\S/u.test(description)) { return; } const trailingLines = description.match(/\n+$/u)?.[0]?.length; const trailingDiff = (trailingLines ?? 0) - startLines; if (trailingDiff > 0) { utils.reportJSDoc(`Expected only ${startLines} line after block description`, { line: lastDescriptionLine - trailingDiff }, () => { utils.setBlockDescription((info, seedTokens, descLines) => { return descLines.slice(0, -trailingDiff).map(desc => { return { number: 0, source: '', tokens: seedTokens({ ...info, description: desc, postDelimiter: desc.trim() ? info.postDelimiter : '' }) }; }); }); }); } else if (trailingDiff < 0) { utils.reportJSDoc(`Expected ${startLines} lines after block description`, { line: lastDescriptionLine }, () => { utils.setBlockDescription((info, seedTokens, descLines) => { return [...descLines, ...Array.from({ length: -trailingDiff }, () => { return ''; })].map(desc => { return { number: 0, source: '', tokens: seedTokens({ ...info, description: desc, postDelimiter: desc.trim() ? info.postDelimiter : '' }) }; }); }); }); } } }, { iterateAllJsdocs: true, meta: { docs: { description: 'Enforces lines (or no lines) between tags.', url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/tag-lines.md#repos-sticky-header' }, fixable: 'code', schema: [{ enum: ['always', 'any', 'never'], type: 'string' }, { additionalProperties: false, properties: { applyToEndTag: { type: 'boolean' }, count: { type: 'integer' }, endLines: { anyOf: [{ type: 'integer' }, { type: 'null' }] }, startLines: { anyOf: [{ type: 'integer' }, { type: 'null' }] }, tags: { patternProperties: { '.*': { additionalProperties: false, properties: { count: { type: 'integer' }, lines: { enum: ['always', 'never', 'any'], type: 'string' } } } }, type: 'object' } }, type: 'object' }], type: 'suggestion' } }); module.exports = exports.default; //# sourceMappingURL=tagLines.cjs.map