master
 1import type { RemarkPlugin } from '@astrojs/markdown-remark';
 2import crypto from 'crypto';
 3import { visit } from 'unist-util-visit';
 4
 5export const remarkArticleReferences: RemarkPlugin = function () {
 6  return (tree, { data }) => {
 7    visit(tree, 'text', (node) => {
 8      const references: {
 9        reference: string;
10        context: string;
11        offset: [number, number];
12        id: string;
13      }[] = [];
14      // \[\[ - 匹配开始的 [[
15      // ((?:\\.|[^\|\]])*?) - 第一个捕获组($1),匹配:
16      //   \\. - 任何转义字符(包括\|)
17      //   或 [^|\]] - 任何不是|和]的字符
18      // (?:\|(.*?))? - 非捕获组,可选的,匹配:
19      //   \| - 分隔符|
20      //   (.*?) - 第二个捕获组($2),非贪婪匹配任意字符
21      // \]\] - 匹配结束的 ]]
22      const linkPattern = /\[\[((?:\\.|[^|\]])*?)(?:\|(.*?))?\]\]/g;
23      node.value = node.value.replaceAll(
24        linkPattern,
25        (match, reference: string, alias: string | undefined, offset) => {
26          const startOffset = Math.max(0, offset - 40);
27          const endOffset = Math.min(node.value.length, offset + match.length + 40);
28          const matchOffsetStart = offset - startOffset + 3;
29          const matchOffsetEnd = match.length + matchOffsetStart;
30          const context = `...${node.value
31            .replaceAll(
32              linkPattern,
33              (_, ref: string, alias) =>
34                alias || ref.split('/').at(-1)?.split('.').slice(0, -1).join('') || ref
35            )
36            .substring(startOffset, endOffset)
37            .trim()}...`;
38          references.push({
39            reference,
40            context,
41            offset: [matchOffsetStart, matchOffsetEnd],
42            id: crypto.randomUUID(),
43          });
44          if (alias) return `%%%%${reference}%%${alias}%%%%`;
45          return `%%%%${reference}%%%%`;
46        }
47      );
48      if (references.length) data.astro!.frontmatter!.references = references;
49    });
50  };
51};