"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 = [// '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 => { // Todo [engine:node@>8.11.0]: Uncomment following line with neg. lookbehind instead // return text.split(/(? { return [...par].reverse().join(''); }).reverse(); }; 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) => { 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 = _lodash.default.last(paragraph.split('\n')); text = text.replace(new RegExp(`${_lodash.default.escapeRegExp(line)}$`, 'mu'), `${line}.`); } var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = sentences.filter(sentence_ => { return !/^\s*$/u.test(sentence_) && !isCapitalized(sentence_) && !isTable(sentence_); })[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { const sentence = _step.value; 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(beginning, capitalize(beginning)); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return fixer.replaceText(jsdocNode, text); }; const report = (msg, fixer, tagObj) => { tagObj.line += 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 (!/[.!?|]$/u.test(paragraphNoAbbreviations)) { report('Sentence must end with a period.', fix, tag); return true; } if (!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 _options$abbreviation = options.abbreviations, abbreviations = _options$abbreviation === void 0 ? [] : _options$abbreviation; const abbreviationsRegex = abbreviations.length ? new RegExp('\\b' + abbreviations.map(abbreviation => { return _lodash.default.escapeRegExp(abbreviation.replace(/\.$/g, '') + '.'); }).join('|') + '(?:$|\\s)', 'gu') : ''; if (!jsdoc.tags || validateDescription(jsdoc.description, report, jsdocNode, abbreviationsRegex, sourceCode, { line: jsdoc.line + 1 })) { return; } utils.forEachPreferredTag('description', matchingJsdocTag => { const description = `${matchingJsdocTag.name} ${matchingJsdocTag.description}`.trim(); validateDescription(description, report, jsdocNode, abbreviationsRegex, sourceCode, matchingJsdocTag); }, true); const _utils$getTagsByType = utils.getTagsByType(jsdoc.tags), tagsWithNames = _utils$getTagsByType.tagsWithNames; const tagsWithoutNames = utils.filterTags(({ tag: tagName }) => { return otherDescriptiveTags.includes(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 description = _lodash.default.trimStart(tag.description, '- '); return validateDescription(description, report, jsdocNode, abbreviationsRegex, sourceCode, tag); }); tagsWithoutNames.some(tag => { const description = `${tag.name} ${tag.description}`.trim(); return validateDescription(description, report, jsdocNode, abbreviationsRegex, sourceCode, tag); }); }, { iterateAllJsdocs: true, meta: { fixable: 'code', schema: [{ additionalProperties: false, properties: { abbreviations: { items: { type: 'string' }, type: 'array' }, tags: { items: { type: 'string' }, type: 'array' } }, type: 'object' }], type: 'suggestion' } }); exports.default = _default; module.exports = exports.default; //# sourceMappingURL=requireDescriptionCompleteSentence.js.map