(readme, contents)
| 82 | } |
| 83 | |
| 84 | function checkContentsHierarchy(readme, contents) { |
| 85 | const rules = extractSection(readme, "Rules"); |
| 86 | if (!rules) return; |
| 87 | |
| 88 | const ruleSubheadings = [...rules.matchAll(/^###\s+(.+?)\s*$/gm)].map((match) => match[1].trim()); |
| 89 | const contentLines = contents.split(/\r?\n/); |
| 90 | |
| 91 | for (const heading of ruleSubheadings) { |
| 92 | const anchor = markdownAnchor(heading); |
| 93 | const headingPattern = escapeRegExp(heading); |
| 94 | const anchorPattern = escapeRegExp(anchor); |
| 95 | const topLevelPattern = new RegExp(`^- \\[${headingPattern}\\]\\(#${anchorPattern}\\)\\s*$`); |
| 96 | const nestedPattern = new RegExp(`^\\s{2,}- \\[${headingPattern}\\]\\(#${anchorPattern}\\)\\s*$`); |
| 97 | |
| 98 | if (contentLines.some((line) => topLevelPattern.test(line))) { |
| 99 | failures.push(`README Contents must nest rule category links under Rules: ${heading}.`); |
| 100 | } else if (!contentLines.some((line) => nestedPattern.test(line))) { |
| 101 | failures.push(`README Contents must include nested rule category link under Rules: ${heading}.`); |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | function markdownAnchor(heading) { |
| 107 | return heading |
no test coverage detected