forked from denoland/deno-gfm
/
mod.ts
85 lines (79 loc) · 2.77 KB
/
mod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { emojify, marked, Prism, sanitizeHtml } from "./deps.ts";
import { CSS } from "./style.js";
export { CSS };
class Renderer extends marked.Renderer {
heading(
text: string,
level: 1 | 2 | 3 | 4 | 5 | 6,
raw: string,
slugger: marked.Slugger,
): string {
const slug = slugger.slug(raw);
return `<h${level} id="${slug}"><a class="anchor" aria-hidden="true" tabindex="-1" href="#${slug}"><svg class="octicon octicon-link" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>${text}</h${level}>`;
}
code(code: string, language: string) {
// a language of `ts, ignore` should really be `ts`
language = language.split(",")[0];
const grammar = Object.hasOwnProperty.call(Prism.languages, language)
? Prism.languages[language]
: undefined;
if (grammar === undefined) {
return `<pre><code>${code}</code></pre>`;
}
const html = Prism.highlight(code, grammar, language);
return `<div class="highlight highlight-source-${language}"><pre>${html}</pre></div>`;
}
link(href: string, title: string, text: string) {
if (href.startsWith("#")) {
return `<a href="${href}" title="${title}">${text}</a>`;
}
return `<a href="${href}" title="${title}" rel="noopener noreferrer">${text}</a>`;
}
}
export function render(markdown: string, baseUrl: string | undefined): string {
markdown = emojify(markdown);
const html = marked(markdown, {
baseUrl,
gfm: true,
renderer: new Renderer(),
});
return sanitizeHtml(html, {
allowedTags: sanitizeHtml.defaults.allowedTags.concat([
"img",
"svg",
"path",
]),
allowedAttributes: {
...sanitizeHtml.defaults.allowedAttributes,
img: ["src", "alt", "height", "width", "align"],
a: ["id", "aria-hidden", "href", "tabindex", "rel"],
svg: ["viewbox", "width", "height", "aria-hidden"],
path: ["fill-rule", "d"],
h1: ["id"],
h2: ["id"],
h3: ["id"],
h4: ["id"],
h5: ["id"],
h6: ["id"],
},
allowedClasses: {
div: ["highlight"],
span: [
"token",
"keyword",
"operator",
"number",
"boolean",
"function",
"string",
"comment",
"class-name",
"regex",
"regex-delimiter",
],
a: ["anchor"],
svg: ["octicon", "octicon-link"],
},
allowProtocolRelative: false,
});
}