eslint-plugin-jsdoc
Version:
JSDoc linting rules for ESLint.
362 lines (358 loc) • 10.4 kB
JavaScript
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 }; }
/**
* @param {import('@es-joy/jsdoccomment').JsdocBlockWithInline} jsdoc
* @param {import('../iterateJsdoc.js').Utils} utils
* @param {number} requireSingleLineUnderCount
*/
const checkForShortTags = (jsdoc, utils, requireSingleLineUnderCount) => {
if (!requireSingleLineUnderCount || !jsdoc.tags.length) {
return false;
}
let lastLineWithTag = 0;
let isUnderCountLimit = false;
let hasMultiDescOrType = false;
const tagLines = jsdoc.source.reduce((acc, {
tokens: {
delimiter,
description: desc,
name,
postDelimiter,
postName,
postTag,
postType,
start,
tag,
type
}
}, idx) => {
if (tag.length) {
lastLineWithTag = idx;
if (start.length + delimiter.length + postDelimiter.length + type.length + postType.length + name.length + postName.length + tag.length + postTag.length + desc.length < requireSingleLineUnderCount) {
isUnderCountLimit = true;
}
return acc + 1;
} else if (desc.length || type.length) {
hasMultiDescOrType = true;
return acc;
}
return acc;
}, 0);
// Could be tagLines > 1
if (!hasMultiDescOrType && isUnderCountLimit && tagLines === 1) {
const fixer = () => {
const tokens = jsdoc.source[lastLineWithTag].tokens;
jsdoc.source = [{
number: 0,
source: '',
tokens: utils.seedTokens({
delimiter: '/**',
description: tokens.description.trimEnd() + ' ',
end: '*/',
name: tokens.name,
postDelimiter: ' ',
postName: tokens.postName,
postTag: tokens.postTag,
postType: tokens.postType,
start: jsdoc.source[0].tokens.start,
tag: tokens.tag,
type: tokens.type
})
}];
};
utils.reportJSDoc('Description is too short to be multi-line.', null, fixer);
return true;
}
return false;
};
/**
* @param {import('@es-joy/jsdoccomment').JsdocBlockWithInline} jsdoc
* @param {import('../iterateJsdoc.js').Utils} utils
* @param {number} requireSingleLineUnderCount
*/
const checkForShortDescriptions = (jsdoc, utils, requireSingleLineUnderCount) => {
if (!requireSingleLineUnderCount || jsdoc.tags.length) {
return false;
}
let lastLineWithDesc = 0;
let isUnderCountLimit = false;
const descLines = jsdoc.source.reduce((acc, {
tokens: {
delimiter,
description: desc,
postDelimiter,
start
}
}, idx) => {
if (desc.length) {
lastLineWithDesc = idx;
if (start.length + delimiter.length + postDelimiter.length + desc.length < requireSingleLineUnderCount) {
isUnderCountLimit = true;
}
return acc + 1;
}
return acc;
}, 0);
// Could be descLines > 1
if (isUnderCountLimit && descLines === 1) {
const fixer = () => {
const desc = jsdoc.source[lastLineWithDesc].tokens.description;
jsdoc.source = [{
number: 0,
source: '',
tokens: utils.seedTokens({
delimiter: '/**',
description: desc.trimEnd() + ' ',
end: '*/',
postDelimiter: ' ',
start: jsdoc.source[0].tokens.start
})
}];
};
utils.reportJSDoc('Description is too short to be multi-line.', null, fixer);
return true;
}
return false;
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
utils
}) => {
const {
allowMultipleTags = true,
minimumLengthForMultiline = Number.POSITIVE_INFINITY,
multilineTags = ['*'],
noFinalLineText = true,
noMultilineBlocks = false,
noSingleLineBlocks = false,
noZeroLineText = true,
requireSingleLineUnderCount = null,
singleLineTags = ['lends', 'type']
} = context.options[0] || {};
const {
source: [{
tokens
}]
} = jsdoc;
const {
description,
tag
} = tokens;
const sourceLength = jsdoc.source.length;
/**
* @param {string} tagName
* @returns {boolean}
*/
const isInvalidSingleLine = tagName => {
return noSingleLineBlocks && (!tagName || !singleLineTags.includes(tagName) && !singleLineTags.includes('*'));
};
if (sourceLength === 1) {
if (!isInvalidSingleLine(tag.slice(1))) {
return;
}
const fixer = () => {
utils.makeMultiline();
};
utils.reportJSDoc('Single line blocks are not permitted by your configuration.', null, fixer, true);
return;
}
if (checkForShortDescriptions(jsdoc, utils, requireSingleLineUnderCount)) {
return;
}
if (checkForShortTags(jsdoc, utils, requireSingleLineUnderCount)) {
return;
}
const lineChecks = () => {
if (noZeroLineText && (tag || description)) {
const fixer = () => {
const line = {
...tokens
};
utils.emptyTokens(tokens);
const {
tokens: {
delimiter,
start
}
} = jsdoc.source[1];
utils.addLine(1, {
...line,
delimiter,
start
});
};
utils.reportJSDoc('Should have no text on the "0th" line (after the `/**`).', null, fixer);
return;
}
const finalLine = jsdoc.source[jsdoc.source.length - 1];
const finalLineTokens = finalLine.tokens;
if (noFinalLineText && finalLineTokens.description.trim()) {
const fixer = () => {
const line = {
...finalLineTokens
};
line.description = line.description.trimEnd();
const {
delimiter
} = line;
for (const prop of ['delimiter', 'postDelimiter', 'tag', 'type', 'lineEnd', 'postType', 'postTag', 'name', 'postName', 'description']) {
finalLineTokens[(
/**
* @type {"delimiter"|"postDelimiter"|"tag"|"type"|
* "lineEnd"|"postType"|"postTag"|"name"|
* "postName"|"description"}
*/
prop)] = '';
}
utils.addLine(jsdoc.source.length - 1, {
...line,
delimiter,
end: ''
});
};
utils.reportJSDoc('Should have no text on the final line (before the `*/`).', null, fixer);
}
};
if (noMultilineBlocks) {
if (jsdoc.tags.length && (multilineTags.includes('*') || utils.hasATag(multilineTags))) {
lineChecks();
return;
}
if (jsdoc.description.length >= minimumLengthForMultiline) {
lineChecks();
return;
}
if (noSingleLineBlocks && (!jsdoc.tags.length || !utils.filterTags(({
tag: tg
}) => {
return !isInvalidSingleLine(tg);
}).length)) {
utils.reportJSDoc('Multiline jsdoc blocks are prohibited by ' + 'your configuration but fixing would result in a single ' + 'line block which you have prohibited with `noSingleLineBlocks`.');
return;
}
if (jsdoc.tags.length > 1) {
if (!allowMultipleTags) {
utils.reportJSDoc('Multiline jsdoc blocks are prohibited by ' + 'your configuration but the block has multiple tags.');
return;
}
} else if (jsdoc.tags.length === 1 && jsdoc.description.trim()) {
if (!allowMultipleTags) {
utils.reportJSDoc('Multiline jsdoc blocks are prohibited by ' + 'your configuration but the block has a description with a tag.');
return;
}
} else {
const fixer = () => {
jsdoc.source = [{
number: 1,
source: '',
tokens: jsdoc.source.reduce((obj, {
tokens: {
description: desc,
lineEnd,
name: nme,
postName,
postTag,
postType,
tag: tg,
type: typ
}
}) => {
if (typ) {
obj.type = typ;
}
if (tg && typ && nme) {
obj.postType = postType;
}
if (nme) {
obj.name += nme;
}
if (nme && desc) {
obj.postName = postName;
}
obj.description += desc;
const nameOrDescription = obj.description || obj.name;
if (nameOrDescription && nameOrDescription.slice(-1) !== ' ') {
obj.description += ' ';
}
obj.lineEnd = lineEnd;
// Already filtered for multiple tags
obj.tag += tg;
if (tg) {
obj.postTag = postTag || ' ';
}
return obj;
}, utils.seedTokens({
delimiter: '/**',
end: '*/',
postDelimiter: ' '
}))
}];
};
utils.reportJSDoc('Multiline jsdoc blocks are prohibited by ' + 'your configuration.', null, fixer);
return;
}
}
lineChecks();
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Controls how and whether jsdoc blocks can be expressed as single or multiple line blocks.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/multiline-blocks.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
allowMultipleTags: {
type: 'boolean'
},
minimumLengthForMultiline: {
type: 'integer'
},
multilineTags: {
anyOf: [{
enum: ['*'],
type: 'string'
}, {
items: {
type: 'string'
},
type: 'array'
}]
},
noFinalLineText: {
type: 'boolean'
},
noMultilineBlocks: {
type: 'boolean'
},
noSingleLineBlocks: {
type: 'boolean'
},
noZeroLineText: {
type: 'boolean'
},
requireSingleLineUnderCount: {
type: 'number'
},
singleLineTags: {
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=multilineBlocks.cjs.map
;