|
| 1 | +import { text } from "mdast-builder"; |
| 2 | +import { visit } from "unist-util-visit"; |
| 3 | + |
| 4 | + |
| 5 | +/** |
| 6 | + * RFC 2119 / RFC 8174 keywords |
| 7 | + * Order matters: longer phrases first |
| 8 | + */ |
| 9 | +const KEYWORDS = [ |
| 10 | + "MUST NOT", |
| 11 | + "NOT REQUIRED", |
| 12 | + "SHALL NOT", |
| 13 | + "SHOULD NOT", |
| 14 | + "NOT RECOMMENDED", |
| 15 | + "MUST", |
| 16 | + "REQUIRED", |
| 17 | + "SHALL", |
| 18 | + "SHOULD", |
| 19 | + "RECOMMENDED", |
| 20 | + "MAY", |
| 21 | + "OPTIONAL" |
| 22 | +]; |
| 23 | + |
| 24 | +const KEYWORD_REGEX = new RegExp(`(?:${KEYWORDS.map((k) => k.replace(" ", "\\s")).join("|")})`, "gm"); |
| 25 | + |
| 26 | +const skipNodes = new Set(["code", "inlineCode", "link", "definition", "html"]); |
| 27 | + |
| 28 | +export default function remarkRfc2119() { |
| 29 | + return (tree) => { |
| 30 | + visit(tree, "text", (node, index, parent) => { |
| 31 | + // Do not touch code, inlineCode, links, or HTML |
| 32 | + if (skipNodes.has(parent.type)) { |
| 33 | + return; |
| 34 | + } |
| 35 | + |
| 36 | + const value = node.value; |
| 37 | + let match; |
| 38 | + let lastIndex = 0; |
| 39 | + const children = []; |
| 40 | + |
| 41 | + KEYWORD_REGEX.lastIndex = 0; |
| 42 | + |
| 43 | + while ((match = KEYWORD_REGEX.exec(value)) !== null) { |
| 44 | + const [keyword] = match; |
| 45 | + const start = match.index; |
| 46 | + const end = start + keyword.length; |
| 47 | + |
| 48 | + if (start > lastIndex) { |
| 49 | + children.push(text(value.slice(lastIndex, start))); |
| 50 | + } |
| 51 | + |
| 52 | + const keywordNode = text(keyword); |
| 53 | + keywordNode.data = { |
| 54 | + hName: "span", |
| 55 | + hProperties: { |
| 56 | + className: ["rfc2119", keyword.toLowerCase().replace(/\s+/g, "-")] |
| 57 | + } |
| 58 | + }; |
| 59 | + children.push(keywordNode); |
| 60 | + |
| 61 | + lastIndex = end; |
| 62 | + } |
| 63 | + |
| 64 | + if (children.length === 0) { |
| 65 | + return; |
| 66 | + } |
| 67 | + |
| 68 | + if (lastIndex < value.length) { |
| 69 | + children.push(text(value.slice(lastIndex))); |
| 70 | + } |
| 71 | + |
| 72 | + parent.children.splice(index, 1, ...children); |
| 73 | + }); |
| 74 | + }; |
| 75 | +} |
0 commit comments