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};