forked from CodingCatDev/codingcat.dev
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmarkdown.ts
More file actions
137 lines (121 loc) · 3.56 KB
/
markdown.ts
File metadata and controls
137 lines (121 loc) · 3.56 KB
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
127
128
129
130
131
132
133
134
135
136
137
import PrismJS from 'prismjs';
import 'prismjs/components/prism-bash.js';
import 'prismjs/components/prism-diff.js';
import 'prismjs/components/prism-typescript.js';
import 'prismjs/components/prism-jsx';
import 'prism-svelte';
import { marked, Renderer } from 'marked';
const languages = {
bash: 'bash',
env: 'bash',
html: 'markup',
svelte: 'svelte',
js: 'javascript',
css: 'css',
diff: 'diff',
ts: 'typescript',
jsx: 'jsx',
'': ''
};
function escape(html: string) {
const chars = new Map<string, string>([
['&', '&'],
['<', '<'],
['>', '>']
]);
return html.replace(/[&<>]/g, (c) => chars.get(c) || '');
}
const delimiter_substitutes = {
'+++': ' ',
'---': ' ',
':::': ' '
};
function highlight_spans(content: string, classname: string) {
return `<span class="${classname}">${content}</span>`;
// return content.replace(/<span class="([^"]+)"/g, (_, classnames) => {
// return `<span class="${classname} ${classnames}"`;
// });
}
const default_renderer = {
code: (source: string, language = '') => {
console.log('markdown:lang', language);
/** @type {Record<string, string>} */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const options: any = {};
let html = '';
source = source
.replace(/\/\/\/ (.+?): (.+)\n/gm, (_, key, value) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options[key] = value;
return '';
})
.replace(/^([-+])?((?: {4})+)/gm, (match, prefix = '', spaces) => {
if (prefix && language !== 'diff') return match;
// for no good reason at all, marked replaces tabs with spaces
let tabs = '';
for (let i = 0; i < spaces.length; i += 4) {
tabs += '\t';
}
return prefix + tabs;
})
.replace(/(\+\+\+|---|:::)/g, (_, /** @type {keyof delimiter_substitutes} */ delimiter) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return delimiter_substitutes[delimiter];
})
.replace(/\*\\\//g, '*/');
if (language === 'diff') {
const lines = source.split('\n').map((content) => {
let type = null;
if (/^[+-]/.test(content)) {
type = content[0] === '+' ? 'inserted' : 'deleted';
content = content.slice(1);
}
return {
type,
content: escape(content)
};
});
html = `<div class="code-block"><pre class="language-diff"><code>${lines
.map((line) => {
if (line.type) return `<span class="${line.type}">${line.content}\n</span>`;
return line.content + '\n';
})
.join('')}</code></pre></div>`;
} else {
const lang = language;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const plang = languages[lang];
const highlighted = plang
? PrismJS.highlight(source, PrismJS.languages[plang], language)
: escape(source);
html = `<div class="code-block">${
options.file ? `<span class="filename">${options.file}</span>` : ''
}<pre class='language-${plang}'><code>${highlighted}</code></pre></div>`;
}
return html
.replace(/ {13}([^ ][^]+?) {13}/g, (_, content) => {
return highlight_spans(content, 'highlight add');
})
.replace(/ {11}([^ ][^]+?) {11}/g, (_, content) => {
return highlight_spans(content, 'highlight remove');
})
.replace(/ {9}([^ ][^]+?) {9}/g, (_, content) => {
return highlight_spans(content, 'highlight');
});
}
};
marked.use({
renderer: {}
});
export function transform(markdown: string, options: Renderer) {
marked.use({
renderer: {
...default_renderer,
...options
}
});
return marked(markdown);
}