| 1 | import { |
| 2 | findAndReplaceText, |
| 3 | type MarkdownNode, |
| 4 | type TextMatch, |
| 5 | } from "~/vendor/mdast-find-and-replace"; |
| 6 | |
| 7 | export { |
| 8 | splitPullRequestBody, |
| 9 | type PullRequestBodySegment, |
| 10 | } from "@t3tools/client-runtime/pull-request-markdown"; |
| 11 | |
| 12 | const AUTOLINK_CANDIDATE_PATTERN = /#[1-9]\d*|[0-9a-f]{40}/giu; |
| 13 | const AUTOLINK_WORD_CHARACTER_PATTERN = /[A-Za-z0-9_]/u; |
| 14 | const AUTOLINK_COMMIT_PREFIX_PATTERN = /[\s([{]/u; |
| 15 | const AUTOLINK_IGNORED_TYPES = new Set(["link", "linkReference"]); |
| 16 | |
| 17 | /** GitHub-style same-repository references for pull request prose, never code or authored links. */ |
| 18 | export function remarkPullRequestAutolinks(options: { readonly repositoryUrl: string }) { |
| 19 | const repositoryUrl = options.repositoryUrl.replace(/\/+$/u, ""); |
| 20 | return (tree: MarkdownNode) => { |
| 21 | findAndReplaceText( |
| 22 | tree, |
| 23 | AUTOLINK_CANDIDATE_PATTERN, |
| 24 | (matched: string, match: TextMatch) => { |
| 25 | const reference = matched.startsWith("#"); |
| 26 | const before = match.input[match.index - 1]; |
| 27 | const after = match.input[match.index + matched.length]; |
| 28 | if ( |
| 29 | (before !== undefined && |
| 30 | (reference |
| 31 | ? AUTOLINK_WORD_CHARACTER_PATTERN.test(before) |
| 32 | : !AUTOLINK_COMMIT_PREFIX_PATTERN.test(before))) || |
| 33 | (after !== undefined && AUTOLINK_WORD_CHARACTER_PATTERN.test(after)) |
| 34 | ) { |
| 35 | return false; |
| 36 | } |
| 37 | return { |
| 38 | type: "link", |
| 39 | url: reference |
| 40 | ? `${repositoryUrl}/issues/${matched.slice(1)}` |
| 41 | : `${repositoryUrl}/commit/${matched}`, |
| 42 | data: { |
| 43 | hProperties: { |
| 44 | dataPullRequestAutolink: reference ? "reference" : "commit", |
| 45 | }, |
| 46 | }, |
| 47 | children: [{ type: "text", value: reference ? matched : matched.slice(0, 7) }], |
| 48 | }; |
| 49 | }, |
| 50 | AUTOLINK_IGNORED_TYPES, |
| 51 | ); |
| 52 | }; |
| 53 | } |