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); }