@fedify/markdown-it-mention
Version:
A markdown-it plugin that parses and renders Mastodon-style @mentions.
111 lines (110 loc) • 3.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.mention = void 0;
const html_js_1 = require("./html.js");
const label_js_1 = require("./label.js");
/**
* A markdown-it plugin to parse and render Mastodon-style mentions.
*/
const mention = (md, options) => {
md.core.ruler.after("inline", "mention", (state) => parseMention(state, options));
md.renderer.rules.mention = renderMention;
};
exports.mention = mention;
function parseMention(state, options) {
for (const blockToken of state.tokens) {
if (blockToken.type !== "inline")
continue;
if (blockToken.children == null)
continue;
let linkDepth = 0;
let htmlLinkDepth = 0;
blockToken.children = blockToken.children.flatMap((token) => {
if (token.type === "link_open") {
linkDepth++;
}
else if (token.type === "link_close") {
linkDepth--;
}
else if (token.type === "html_inline") {
if ((0, html_js_1.isLinkOpen)(token.content)) {
htmlLinkDepth++;
}
else if ((0, html_js_1.isLinkClose)(token.content)) {
htmlLinkDepth--;
}
}
if (linkDepth > 0 || htmlLinkDepth > 0 || token.type !== "text") {
return [token];
}
return splitTokens(token, state, options);
});
}
}
const MENTION_PATTERN = /@[\p{L}\p{N}._-]+(@(?:[\p{L}\p{N}][\p{L}\p{N}_-]*\.)+[\p{L}\p{N}]{2,})?/giu;
function splitTokens(token, state, options) {
const { content, level } = token;
const tokens = [];
let pos = 0;
for (const match of content.matchAll(MENTION_PATTERN)) {
if (match.index == null)
continue;
let handle = match[0];
if (match[1] == null) {
const localDomain = options?.localDomain == null
? null
: options.localDomain(handle, state.env);
if (localDomain == null)
continue;
handle += `@${localDomain}`;
}
if (match.index > pos) {
const token = new state.Token("text", "", 0);
token.content = content.substring(pos, match.index);
token.level = level;
tokens.push(token);
}
const href = options?.link?.(handle, state.env);
if (href == null && options?.link != null) {
const token = new state.Token("text", "", 0);
token.content = match[0];
token.level = level;
tokens.push(token);
pos = match.index + match[0].length;
continue;
}
const token = new state.Token("mention", "", 0);
token.content = options?.label?.(handle, state.env) ??
(0, label_js_1.toBareHandle)(handle, state.env);
token.level = level;
const attrs = options?.linkAttributes?.(handle, state.env) ?? {};
attrs.href = href ?? `acct:${handle}`;
token.attrs = Object.entries(attrs);
token.info = handle;
tokens.push(token);
pos = match.index + match[0].length;
}
if (pos < content.length) {
const token = new state.Token("text", "", 0);
token.content = content.substring(pos);
token.level = level;
tokens.push(token);
}
return tokens;
}
function renderMention(tokens, idx, opts,
// deno-lint-ignore no-explicit-any
env, self) {
if (tokens.length <= idx)
return "";
const token = tokens[idx];
if (token.type !== "mention")
return self.renderToken(tokens, idx, opts);
if (typeof env === "object" && env !== null) {
if (!("mentions" in env)) {
env.mentions = [];
}
env.mentions.push(token.info);
}
return `<a ${self.renderAttrs(token)}>${token.content}</a>`;
}