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\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 }) => ``),
];
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*\/?>/gii:不区分大小写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\n`);
}
作用:在当前 Markdown 内容的末尾追加一张图片的 Markdown 语法。
- 先
trimEnd()去掉原内容末尾的空白。 - 拼接:
原内容 + 空行 +  + 换行。 - 调用
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 }) => ``),
];
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。references数组:存储从文本中提取到的所有图片引用。body:用markdown.replace(...)将所有图片语法直接替换为空字符串,达到移除图片引用的效果。在替换的同时回调函数会将每张图片的alt和url记录到references中。cleanBody:将剩余文字中连续 3 个及以上的换行压缩为\n\n,并去除首尾空白。sections:- 第一个元素是
cleanBody(这时已经是纯文字,不含图片语法)。 - 后面的元素是经过重新规范化的图片语法字符串(每个都形如
)。 - 这样设计是为了把图片从文中抽出,全部排在正文之后。
- 第一个元素是
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 中包含特殊字符、连续换行等),这些函数已经做了不少处理来规避关键问题。
AI 正在分析代码…
评论加载中...