123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- "use strict";
-
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.default = void 0;
-
- var _lodash = _interopRequireDefault(require("lodash"));
-
- var _mainUmd = require("regextras/dist/main-umd");
-
- var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc"));
-
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
- const otherDescriptiveTags = new Set([// 'copyright' and 'see' might be good addition, but as the former may be
- // sensitive text, and the latter may have just a link, they are not
- // included by default
- 'summary', 'file', 'fileoverview', 'overview', 'classdesc', 'todo', 'deprecated', 'throws', 'exception', 'yields', 'yield']);
-
- const extractParagraphs = text => {
- return text.split(/(?<![;:])\n\n/u);
- };
-
- const extractSentences = (text, abbreviationsRegex) => {
- const txt = text // Remove all {} tags.
- .replace(/\{[\s\S]*?\}\s*/gu, '') // Remove custom abbreviations
- .replace(abbreviationsRegex, '');
- const sentenceEndGrouping = /([.?!])(?:\s+|$)/u;
- const puncts = (0, _mainUmd.RegExtras)(sentenceEndGrouping).map(txt, punct => {
- return punct;
- });
- return txt.split(/[.?!](?:\s+|$)/u) // Re-add the dot.
- .map((sentence, idx) => {
- return /^\s*$/u.test(sentence) ? sentence : `${sentence}${puncts[idx] || ''}`;
- });
- };
-
- const isNewLinePrecededByAPeriod = text => {
- let lastLineEndsSentence;
- const lines = text.split('\n');
- return !lines.some(line => {
- if (lastLineEndsSentence === false && /^[A-Z][a-z]/u.test(line)) {
- return true;
- }
-
- lastLineEndsSentence = /[.:?!|]$/u.test(line);
- return false;
- });
- };
-
- const isCapitalized = str => {
- return str[0] === str[0].toUpperCase();
- };
-
- const isTable = str => {
- return str.charAt() === '|';
- };
-
- const capitalize = str => {
- return str.charAt(0).toUpperCase() + str.slice(1);
- };
-
- const validateDescription = (description, reportOrig, jsdocNode, abbreviationsRegex, sourceCode, tag, newlineBeforeCapsAssumesBadSentenceEnd) => {
- if (!description) {
- return false;
- }
-
- const paragraphs = extractParagraphs(description);
- return paragraphs.some((paragraph, parIdx) => {
- const sentences = extractSentences(paragraph, abbreviationsRegex);
-
- const fix = fixer => {
- let text = sourceCode.getText(jsdocNode);
-
- if (!/[.:?!]$/u.test(paragraph)) {
- const line = paragraph.split('\n').pop();
- text = text.replace(new RegExp(`${_lodash.default.escapeRegExp(line)}$`, 'mu'), `${line}.`);
- }
-
- for (const sentence of sentences.filter(sentence_ => {
- return !/^\s*$/u.test(sentence_) && !isCapitalized(sentence_) && !isTable(sentence_);
- })) {
- const beginning = sentence.split('\n')[0];
-
- if (tag.tag) {
- const reg = new RegExp(`(@${_lodash.default.escapeRegExp(tag.tag)}.*)${_lodash.default.escapeRegExp(beginning)}`, 'u');
- text = text.replace(reg, (_$0, $1) => {
- return $1 + capitalize(beginning);
- });
- } else {
- text = text.replace(new RegExp('((?:[.!?]|\\*|\\})\\s*)' + _lodash.default.escapeRegExp(beginning), 'u'), '$1' + capitalize(beginning));
- }
- }
-
- return fixer.replaceText(jsdocNode, text);
- };
-
- const report = (msg, fixer, tagObj) => {
- if ('line' in tagObj) {
- tagObj.line += parIdx * 2;
- } else {
- tagObj.source[0].number += parIdx * 2;
- } // Avoid errors if old column doesn't exist here
-
-
- tagObj.column = 0;
- reportOrig(msg, fixer, tagObj);
- };
-
- if (sentences.some(sentence => {
- return !/^\s*$/u.test(sentence) && !isCapitalized(sentence) && !isTable(sentence);
- })) {
- report('Sentence should start with an uppercase character.', fix, tag);
- }
-
- const paragraphNoAbbreviations = paragraph.replace(abbreviationsRegex, '');
-
- if (!/[.!?|]\s*$/u.test(paragraphNoAbbreviations)) {
- report('Sentence must end with a period.', fix, tag);
- return true;
- }
-
- if (newlineBeforeCapsAssumesBadSentenceEnd && !isNewLinePrecededByAPeriod(paragraphNoAbbreviations)) {
- report('A line of text is started with an uppercase character, but preceding line does not end the sentence.', null, tag);
- return true;
- }
-
- return false;
- });
- };
-
- var _default = (0, _iterateJsdoc.default)(({
- sourceCode,
- context,
- jsdoc,
- report,
- jsdocNode,
- utils
- }) => {
- const options = context.options[0] || {};
- const {
- abbreviations = [],
- newlineBeforeCapsAssumesBadSentenceEnd = false
- } = options;
- const abbreviationsRegex = abbreviations.length ? new RegExp('\\b' + abbreviations.map(abbreviation => {
- return _lodash.default.escapeRegExp(abbreviation.replace(/\.$/g, '') + '.');
- }).join('|') + '(?:$|\\s)', 'gu') : '';
- const {
- description
- } = utils.getDescription();
-
- if (validateDescription(description, report, jsdocNode, abbreviationsRegex, sourceCode, {
- line: jsdoc.source[0].number + 1
- }, newlineBeforeCapsAssumesBadSentenceEnd)) {
- return;
- }
-
- utils.forEachPreferredTag('description', matchingJsdocTag => {
- const desc = `${matchingJsdocTag.name} ${utils.getTagDescription(matchingJsdocTag)}`.trim();
- validateDescription(desc, report, jsdocNode, abbreviationsRegex, sourceCode, matchingJsdocTag, newlineBeforeCapsAssumesBadSentenceEnd);
- }, true);
- const {
- tagsWithNames
- } = utils.getTagsByType(jsdoc.tags);
- const tagsWithoutNames = utils.filterTags(({
- tag: tagName
- }) => {
- return otherDescriptiveTags.has(tagName) || utils.hasOptionTag(tagName) && !tagsWithNames.some(({
- tag
- }) => {
- // If user accidentally adds tags with names (or like `returns`
- // get parsed as having names), do not add to this list
- return tag === tagName;
- });
- });
- tagsWithNames.some(tag => {
- const desc = _lodash.default.trimStart(utils.getTagDescription(tag), '- ').trimEnd();
-
- return validateDescription(desc, report, jsdocNode, abbreviationsRegex, sourceCode, tag, newlineBeforeCapsAssumesBadSentenceEnd);
- });
- tagsWithoutNames.some(tag => {
- const desc = `${tag.name} ${utils.getTagDescription(tag)}`.trim();
- return validateDescription(desc, report, jsdocNode, abbreviationsRegex, sourceCode, tag, newlineBeforeCapsAssumesBadSentenceEnd);
- });
- }, {
- iterateAllJsdocs: true,
- meta: {
- docs: {
- description: 'Requires that block description, explicit `@description`, and `@param`/`@returns` tag descriptions are written in complete sentences.',
- url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-require-description-complete-sentence'
- },
- fixable: 'code',
- schema: [{
- additionalProperties: false,
- properties: {
- abbreviations: {
- items: {
- type: 'string'
- },
- type: 'array'
- },
- newlineBeforeCapsAssumesBadSentenceEnd: {
- type: 'boolean'
- },
- tags: {
- items: {
- type: 'string'
- },
- type: 'array'
- }
- },
- type: 'object'
- }],
- type: 'suggestion'
- }
- });
-
- exports.default = _default;
- module.exports = exports.default;
- //# sourceMappingURL=requireDescriptionCompleteSentence.js.map
|