shadowfacts.net/lib/markdown.ts

93 lines
2.6 KiB
TypeScript

import MarkdownIt from "markdown-it";
import MarkdownItFootnote from "markdown-it-footnote";
import slugify from "@sindresorhus/slugify";
import * as util from "./util";
import { URL } from "url";
const md = new MarkdownIt({
highlight: util.highlight,
html: true,
typographer: true
});
md.use(MarkdownItFootnote);
// Inserts heading anchors
// Based on https://github.com/valeriangalliat/markdown-it-anchor
md.core.ruler.push("header anchor", (state) => {
const tokens = state.tokens;
tokens
.filter((token) => token.type === "heading_open")
.forEach((token) => {
const index = tokens.indexOf(token);
const title = tokens[index + 1]
.children
.filter((token) => token.type === "text" || token.type == "code_inline")
.reduce((acc, t) => acc + t.content, "");
let slug = token.attrGet("id") || slugify(title);
if (token.attrGet("id") == null) {
token.attrPush(["id", slug]);
}
const linkTokens = [
Object.assign(new state.Token("link_open", "a", 1), {
attrs: [
["class", "header-anchor"],
["href", "#" + slug],
["aria-hidden", "true"],
["role", "presentation"]
]
}),
Object.assign(new state.Token("html_block", "", 0), { content: token.markup }),
new state.Token("link_close", "a", -1),
Object.assign(new state.Token("text", "", 0), { content: " " })
];
state.tokens[index + 1].children.unshift(...linkTokens);
});
});
// Adds data-link attributes to anchor elements
const defaultRenderer = md.renderer.rules.link_open || function(tokens, index, options, env, self) {
return self.renderToken(tokens, index, options);
};
md.renderer.rules.link_open = function(tokens, index, options, env, self) {
const href = tokens[index].attrGet("href");
if (href != null) {
try {
const parsed = new URL(href);
let hostname = parsed.hostname;
// hide wwww from beginning of domain
if (hostname.startsWith("www.")) {
hostname = hostname.substr(4);
}
// if the path is /, omit it
const formattedPathname = parsed.pathname === "/" ? "" : parsed.pathname;
let formatted = `${hostname}${formattedPathname}`;
if (hostname.endsWith("youtube.com") && formattedPathname == "/watch") {
// otherwise yt video links end up as `youtube.com/watch` which looks weird
formatted += parsed.search;
}
if (formatted.length > 40) {
formatted = formatted.substr(0, 40) + "...";
}
tokens[index].attrPush(["data-link", formatted]);
} catch (e) {
tokens[index].attrPush(["data-link", href]);
}
}
return defaultRenderer(tokens, index, options, env, self);
};
export function render(text: string): string {
return md.render(text);
}