import type { ImageMetadata } from "../../../models/UserCenterModels";

export function normalizeMarkdown(value: string) {
    return value
        .replace(/<br\s*\/?>/gi, "\n")
        .replace(/\n{4,}/g, "\n\n\n")
        .trim();
}

export function stripMarkdown(value?: string) {
    return (value ?? "")
        .replace(/[#>*_`[\]()]/g, "")
        .replace(/\s+/g, " ")
        .trim();
}

export function appendImageMarkdown(markdown: string, url: string, alt = "image") {
    const prefix = markdown.trimEnd();
    return normalizeMarkdown(`${prefix}\n\n![${alt}](${url})\n`);
}

export function removeImageMarkdown(markdown: string, url: string) {
    const escapedUrl = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const imagePattern = new RegExp(`\\n*!?\\[[^\\]]*\\]\\(${escapedUrl}\\)\\n*`, "g");
    return normalizeMarkdown(markdown.replace(imagePattern, "\n\n"));
}

const IMAGE_REFERENCE_PATTERN = /!\[([^\]]*)\]\(([^)]+)\)/g;

export function formatMumbleMarkdown(
    markdown: string,
    images: ImageMetadata[],
) {
    const references: { alt: string; url: string }[] = [];
    const body = markdown.replace(
        IMAGE_REFERENCE_PATTERN,
        (_match, alt: string, url: string) => {
            references.push({ alt: alt.trim() || "image", url: url.trim() });
            return "";
        },
    );
    const cleanBody = body.replace(/\n{3,}/g, "\n\n").trim();
    const sections = [
        cleanBody,
        ...references.map(({ alt, url }) => `![${alt}](${url})`),
    ];
    const formatted = sections
        .filter((section) => section.length > 0)
        .join("\n\n");
    const urls = new Set(references.map(({ url }) => url));
    const syncedImages = images.filter((image) => urls.has(image.url));
    return { markdown: formatted, images: syncedImages };
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这段代码是一个用于 处理和规范化 Markdown 文本,尤其是针对包含图片引用的 Markdown 内容(如“Mumble”动态/帖子)的工具函数集合。它同时操作字符串和图片元数据(ImageMetadata),是前端内容编辑或展示逻辑中常用的一类辅助模块。

下面我们从整体到细节依次解释。


1. 类型导入

import type { ImageMetadata } from "../../../models/UserCenterModels";
  • ImageMetadata 应该是用户中心模型里定义的一个类型,通常包含 url 等字段,用来描述一张已上传图片的元信息。
  • 在该文件中,images 参数会使用这个类型,最终也可能作为返回结果的一部分。

2. normalizeMarkdown(value: string)

export function normalizeMarkdown(value: string) {
    return value
        .replace(/<br\s*\/?>/gi, "\n")
        .replace(/\n{4,}/g, "\n\n\n")
        .trim();
}

作用:将用户输入的一段 Markdown 文本标准化。

  • 将 HTML 标签 <br><br/><BR> 等替换为换行符 \n
    • 正则:/<br\s*\/?>/gi
      • i:不区分大小写
      • g:全局替换
  • 将连续的 4 个及以上换行压缩为 \n\n\n(即最多两个空行)。
  • trim() 去掉首尾空白字符。

用途:在保存编辑内容前清洗数据,或用于统一展示格式。


3. stripMarkdown(value?: string)

export function stripMarkdown(value?: string) {
    return (value ?? "")
        .replace(/[#>*_`[\]()]/g, "")
        .replace(/\s+/g, " ")
        .trim();
}

作用:粗暴地去掉常见 Markdown 语法符号,并压缩连续空白为一个空格。

  • value ?? "" 保证即使没传值也能安全执行。
  • 第一个 replace 去除以下字符:
    • # 标题符号
    • > 引用符号
    • *_ 强调 / 斜体
    • ` 行内代码
    • [ ] ( ) 链接/图片语法括号
  • 第二个 replace(/\s+/g, " ") 将所有空白(包括换行)折叠为单个空格。
  • 最后 trim() 去掉前后多余空格。

用途:生成纯文本摘要、列表预览、SEO 描述或搜索索引,不用真正解析 Markdown。


4. appendImageMarkdown(markdown, url, alt = "image")

export function appendImageMarkdown(markdown: string, url: string, alt = "image") {
    const prefix = markdown.trimEnd();
    return normalizeMarkdown(`${prefix}\n\n![${alt}](${url})\n`);
}

作用:在当前 Markdown 内容的末尾追加一张图片的 Markdown 语法。

  • trimEnd() 去掉原内容末尾的空白。
  • 拼接:原内容 + 空行 + ![alt](url) + 换行
  • 调用 normalizeMarkdown 保证整体格式符合统一规范。
  • alt 默认值为 "image"

用途:用户在编辑器中上传新图片后,需要把图片链接追加到 Markdown 文本末尾。


5. removeImageMarkdown(markdown, url)

export function removeImageMarkdown(markdown: string, url: string) {
    const escapedUrl = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const imagePattern = new RegExp(`\\n*!?\\[[^\\]]*\\]\\(${escapedUrl}\\)\\n*`, "g");
    return normalizeMarkdown(markdown.replace(imagePattern, "\n\n"));
}

作用:从 Markdown 文本中删除指定 URL 的图片引用。

  • escapedUrl:将 URL 中的正则特殊字符(. * + ? 等)转义,确保 URL 被当作字面量匹配。
  • 动态构造正则:
    \n*!?\[[^\]]*\]\(escapedUrl\)\n*
    
    解释:
    • \n*:匹配图片前后的连续换行
    • !?:可能有感叹号(图片语法中必须有 !,这里允许没有,但实际应该会有)
    • \[[^\]]*\]:匹配 alt 文本,任意非 ] 的字符
    • \(escapedUrl\):匹配括号中的 URL
    • 末尾 \n*:匹配后随换行
    • g:全局删除所有出现
  • 将匹配到的图片语法替换为 \n\n(两个换行,以保证上下文分隔)。
  • 再调用 normalizeMarkdown,将可能产生的多余换行压缩。

用途:当用户移除一张已上传的图片时,可从 Markdown 中精准删除该图片对应的文本内容。


6. formatMumbleMarkdown(markdown, images)

const IMAGE_REFERENCE_PATTERN = /!\[([^\]]*)\]\(([^)]+)\)/g;

export function formatMumbleMarkdown(
    markdown: string,
    images: ImageMetadata[],
) {
    const references: { alt: string; url: string }[] = [];
    const body = markdown.replace(
        IMAGE_REFERENCE_PATTERN,
        (_match, alt: string, url: string) => {
            references.push({ alt: alt.trim() || "image", url: url.trim() });
            return "";
        },
    );
    const cleanBody = body.replace(/\n{3,}/g, "\n\n").trim();
    const sections = [
        cleanBody,
        ...references.map(({ alt, url }) => `![${alt}](${url})`),
    ];
    const formatted = sections
        .filter((section) => section.length > 0)
        .join("\n\n");
    const urls = new Set(references.map(({ url }) => url));
    const syncedImages = images.filter((image) => urls.has(image.url));
    return { markdown: formatted, images: syncedImages };
}

这是该文件中最核心、最复杂的函数,作用是:将 Markdown 文本中的图片引用全部提取出来,使图片只放在正文之后统一排列;同时筛选出实际仍被引用的图片元数据列表。

逐步拆解:

  • IMAGE_REFERENCE_PATTERN:全局匹配 ![alt](url) 语法的正则,并捕获 alturl
  • references 数组:存储从文本中提取到的所有图片引用。
  • body:用 markdown.replace(...) 将所有图片语法直接替换为空字符串,达到移除图片引用的效果。在替换的同时回调函数会将每张图片的 alturl 记录到 references 中。
  • cleanBody:将剩余文字中连续 3 个及以上的换行压缩为 \n\n,并去除首尾空白。
  • sections
    • 第一个元素是 cleanBody(这时已经是纯文字,不含图片语法)。
    • 后面的元素是经过重新规范化的图片语法字符串(每个都形如 ![alt](url))。
    • 这样设计是为了把图片从文中抽出,全部排在正文之后。
  • formatted
    • .filter(section => section.length > 0):去掉空段落(比如当正文为空时就不添加空字符串)。
    • .join("\n\n"):用两个换行连接各段落。
  • urls:从 references 中取出所有图片 URL,放入一个 Set
  • syncedImages:从传入的 images 数组中,只保留那些 在 references 中出现过 的图片元数据。这样即使 images 包含很多历史上传图片,最后只保留实际仍被文本引用的图片。

返回值结构:

{
  markdown: formatted,   // 处理后的规范化 Markdown(图片全部放正文之后)
  images: syncedImages   // 实际仍被引用的图片元数据
}

用途

  • 当用户编辑一条动态时,可能会把图片插入到文本任意位置。后端或前端在保存时,需要将文本中的图片统一提取出来作为附件列表,便于展示或存储。这个函数就是做这种“提取并同步”的工作。

总结:这些函数的整体关系

函数功能使用场景
normalizeMarkdown统一换行,压缩空行,清理首尾空白所有对 Markdown 的写入/保存操作
stripMarkdown去除 Markdown 特殊符号,生成纯文本摘要列表页预览、搜索
appendImageMarkdown把新图片追加到 Markdown 末尾上传图片后插入内容
removeImageMarkdown按 URL 精确删除某个图片引用删除某张已上传图片
formatMumbleMarkdown分离正文和图片,过滤无效图片元数据保存 Mumble 动态,或进行内容规范化前处理

代码整体风格是纯函数式的,使用正则表达式和不可变数据操作(每次返回新字符串),非常适合状态管理或 Redux 中使用,也方便单元测试。但需要注意正则中的一些边界条件(例如空 alt、URL 中包含特殊字符、连续换行等),这些函数已经做了不少处理来规避关键问题。

评论加载中...