chore: Монорепо с общими пакетами
This commit is contained in:
115
packages/content-suggestions/src/core/parser.ts
Normal file
115
packages/content-suggestions/src/core/parser.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type {
|
||||
ContentEntity,
|
||||
LinkEntity,
|
||||
MentionEntity,
|
||||
ParsedMention,
|
||||
ProcessedContent,
|
||||
TagEntity,
|
||||
} from "./types";
|
||||
|
||||
export const mentionLinkRegexp =
|
||||
/@\[[^\]]+]\([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\)/g;
|
||||
|
||||
export const parseMention = (mention: string): ParsedMention | null => {
|
||||
const regex = /@\[([^\]]+)\]\(([\w-]{36})\)/;
|
||||
const match = mention.match(regex);
|
||||
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const mentionText = match[1];
|
||||
const mentionId = match[2];
|
||||
if (!mentionText || !mentionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
mention: `@${mentionText}`,
|
||||
id: mentionId,
|
||||
};
|
||||
};
|
||||
|
||||
export const findMentions = (text: string): MentionEntity[] => {
|
||||
let match: RegExpExecArray | null;
|
||||
const matches: MentionEntity[] = [];
|
||||
|
||||
while ((match = mentionLinkRegexp.exec(text)) !== null) {
|
||||
const parsed = parseMention(match[0]);
|
||||
const baseMention: Omit<MentionEntity, "userId"> = {
|
||||
start: match.index,
|
||||
end: mentionLinkRegexp.lastIndex,
|
||||
text: match[0],
|
||||
type: "mention",
|
||||
displayText: parsed?.mention ?? match[0],
|
||||
};
|
||||
|
||||
matches.push(parsed?.id ? { ...baseMention, userId: parsed.id } : baseMention);
|
||||
}
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
export const findTags = (content: string): TagEntity[] => {
|
||||
const regex = /#[^\s]{1,201}/g;
|
||||
const results: TagEntity[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const value = match[0];
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + value.length,
|
||||
text: value,
|
||||
type: "tag",
|
||||
tag: value.replace("#", ""),
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export const findLinks = (content: string): LinkEntity[] => {
|
||||
const regex =
|
||||
/\b((https?:\/\/)?(?:[\w-]+\.)+[a-z]{2,}(\/[\w\-._~:/?#[\]@!$&'()*+,;=]*)?)/gi;
|
||||
const results: LinkEntity[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const rawUrl = match[0];
|
||||
const hasProtocol = /^https?:\/\//i.test(rawUrl);
|
||||
const fullUrl = hasProtocol ? rawUrl : `https://${rawUrl}`;
|
||||
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + rawUrl.length,
|
||||
text: rawUrl,
|
||||
url: fullUrl,
|
||||
type: "link",
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export const findAllEntities = (content: string): ContentEntity[] => {
|
||||
const mentions = findMentions(content);
|
||||
const tags = findTags(content);
|
||||
const links = findLinks(content);
|
||||
|
||||
return [...mentions, ...tags, ...links].sort((a, b) => a.start - b.start);
|
||||
};
|
||||
|
||||
export const processContent = (content: string): ProcessedContent => {
|
||||
const processedText = content.replace(mentionLinkRegexp, match => {
|
||||
const parsed = parseMention(match);
|
||||
return parsed ? parsed.mention : match;
|
||||
});
|
||||
|
||||
const tags = findTags(content).map(tag => tag.tag);
|
||||
|
||||
return {
|
||||
processedText,
|
||||
tags,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user