UNPKG

eslint-doc-generator

Version:

Automatic documentation generator for ESLint plugins and rules.

83 lines (82 loc) 3.98 kB
import { EOL } from 'node:os'; // General helpers for dealing with markdown files / content. /** * Replace the header of a doc up to and including the specified marker. * Insert at beginning if header doesn't exist. * @param markdown - doc content * @param newHeader - new header including marker * @param marker - marker to indicate end of header */ export function replaceOrCreateHeader(markdown, newHeader, marker) { const lines = markdown.split(EOL); const titleLineIndex = lines.findIndex((line) => line.startsWith('# ')); const markerLineIndex = lines.indexOf(marker); const dashesLineIndex1 = lines.indexOf('---'); const dashesLineIndex2 = lines.indexOf('---', dashesLineIndex1 + 1); // Any YAML front matter or anything else above the title should be kept as-is ahead of the new header. const preHeader = lines .slice(0, Math.max(titleLineIndex, dashesLineIndex2 + 1)) .join(EOL); // Anything after the marker comment, title, or YAML front matter should be kept as-is after the new header. const postHeader = lines .slice(Math.max(markerLineIndex + 1, titleLineIndex + 1, dashesLineIndex2 + 1)) .join(EOL); return `${preHeader ? `${preHeader}${EOL}` : ''}${newHeader}${EOL}${postHeader}`; } /** * Find the section most likely to be the top-level section for a given string. */ export function findSectionHeader(markdown, str) { // Get all the matching strings. const regexp = new RegExp(`## .*${str}.*${EOL}`, 'giu'); const sectionPotentialMatches = [...markdown.matchAll(regexp)].map((match) => match[0]); if (sectionPotentialMatches.length === 0) { // No section found. return undefined; } if (sectionPotentialMatches.length === 1) { // If there's only one match, we can assume it's the section. return sectionPotentialMatches[0]; } // Otherwise assume the shortest match is the correct one. return sectionPotentialMatches.sort((a, b) => a.length - b.length)[0]; } export function findFinalHeaderLevel(str) { const lines = str.split(EOL); const finalHeader = lines.reverse().find((line) => line.match('^(#+) .+$')); return finalHeader ? finalHeader.indexOf(' ') : undefined; } /** * Ensure a doc contains (or doesn't contain) some particular content. * Upon failure, output the failure and set a failure exit code. * @param docName - name of doc for error message * @param contentName - name of content for error message * @param contents - the doc's contents * @param content - the content we are checking for * @param expected - whether the content should be present or not present */ export function expectContentOrFail(docName, contentName, contents, content, expected) { // Check for the content and also the versions of the content with escaped quotes // in case escaping is needed where the content is referenced. const hasContent = contents.includes(content) || contents.includes(content.replace(/"/gu, '\\"')) || contents.includes(content.replace(/'/gu, "\\'")); if (hasContent !== expected) { console.error(`${docName} should ${ /* istanbul ignore next -- TODO: test !expected or remove parameter */ expected ? '' : 'not '}have included ${contentName}: ${content}`); process.exitCode = 1; } } export function expectSectionHeaderOrFail(contentName, contents, possibleHeaders, expected) { const found = possibleHeaders.some((header) => findSectionHeader(contents, header)); if (found !== expected) { if (possibleHeaders.length > 1) { console.error(`${contentName} should ${expected ? '' : 'not '}have included ${expected ? 'one' : 'any'} of these headers: ${possibleHeaders.join(', ')}`); } else { console.error(`${contentName} should ${expected ? '' : 'not '}have included the header: ${possibleHeaders.join(', ')}`); } process.exitCode = 1; } }