forked from denoland/deno-gfm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mod.ts
126 lines (118 loc) · 3.65 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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { emojify, htmlEscape, 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 =
language && Object.hasOwnProperty.call(Prism.languages, language)
? Prism.languages[language]
: undefined;
if (grammar === undefined) {
return `<pre><code>${htmlEscape(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 interface RenderOptions {
baseUrl?: string;
allowIframes?: boolean;
}
export function render(markdown: string, opts: RenderOptions = {}): string {
markdown = emojify(markdown);
const html = Marked.marked(markdown, {
baseUrl: opts.baseUrl,
gfm: true,
renderer: new Renderer(),
});
const allowedTags = sanitizeHtml.defaults.allowedTags.concat([
"img",
"video",
"svg",
"path",
"details",
"summary",
"cite",
"mark",
"acronym",
"abbr",
"address",
"ins",
"del",
"meter"
]);
if (opts.allowIframes) {
allowedTags.push("iframe");
}
return sanitizeHtml(html, {
allowedTags,
allowedAttributes: {
...sanitizeHtml.defaults.allowedAttributes,
img: ["src", "alt", "height", "width", "align", "title"],
video: [
"src",
"alt",
"height",
"width",
"autoplay",
"muted",
"loop",
"playsinline",
],
a: ["id", "aria-hidden", "href", "tabindex", "rel", "target", "title"],
svg: ["viewbox", "width", "height", "aria-hidden"],
path: ["fill-rule", "d"],
h1: ["id"],
h2: ["id"],
h3: ["id"],
h4: ["id"],
h5: ["id"],
h6: ["id"],
iframe: ["src", "width", "height"], // Only used when iframe tags are allowed in the first place.
span: ["id","style"]
},
allowedClasses: {
div: ["highlight","callout1","callout2","callout3","callout4","callout5"],
span: [
"token",
"keyword",
"operator",
"number",
"boolean",
"function",
"string",
"comment",
"class-name",
"regex",
"regex-delimiter",
"tag",
"attr-name",
"punctuation",
"script-punctuation",
"script",
"plain-text",
"property"
],
a: ["anchor"],
svg: ["octicon", "octicon-link"],
},
allowProtocolRelative: false,
});
}