emdp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/cjs/cli.js +36 -0
- package/dist/cjs/gfm.js +26 -0
- package/dist/cjs/index.js +26 -0
- package/dist/cjs/parser/block-parser.js +644 -0
- package/dist/cjs/parser/blocks/blockquote.js +28 -0
- package/dist/cjs/parser/blocks/code-block-fenced.js +58 -0
- package/dist/cjs/parser/blocks/code-block-indented.js +47 -0
- package/dist/cjs/parser/blocks/heading-atx.js +29 -0
- package/dist/cjs/parser/blocks/heading-setext.js +24 -0
- package/dist/cjs/parser/blocks/html-block.js +83 -0
- package/dist/cjs/parser/blocks/link-reference.js +109 -0
- package/dist/cjs/parser/blocks/list.js +155 -0
- package/dist/cjs/parser/blocks/paragraph.js +20 -0
- package/dist/cjs/parser/blocks/table.js +163 -0
- package/dist/cjs/parser/blocks/task-list.js +66 -0
- package/dist/cjs/parser/blocks/thematic-break.js +17 -0
- package/dist/cjs/parser/entities.js +2133 -0
- package/dist/cjs/parser/gfm/block-parser.js +773 -0
- package/dist/cjs/parser/gfm/index.js +125 -0
- package/dist/cjs/parser/gfm/inline-parser.js +813 -0
- package/dist/cjs/parser/gfm/renderer.js +513 -0
- package/dist/cjs/parser/index.js +104 -0
- package/dist/cjs/parser/inline-parser.js +564 -0
- package/dist/cjs/parser/inlines/autolink-extended.js +364 -0
- package/dist/cjs/parser/inlines/autolink.js +44 -0
- package/dist/cjs/parser/inlines/code-span.js +48 -0
- package/dist/cjs/parser/inlines/emphasis.js +64 -0
- package/dist/cjs/parser/inlines/entity.js +25 -0
- package/dist/cjs/parser/inlines/escape.js +25 -0
- package/dist/cjs/parser/inlines/footnote.js +41 -0
- package/dist/cjs/parser/inlines/hard-break.js +45 -0
- package/dist/cjs/parser/inlines/image.js +9 -0
- package/dist/cjs/parser/inlines/link.js +166 -0
- package/dist/cjs/parser/inlines/soft-break.js +18 -0
- package/dist/cjs/parser/inlines/strikethrough.js +48 -0
- package/dist/cjs/parser/inlines/text.js +20 -0
- package/dist/cjs/parser/renderer.js +345 -0
- package/dist/cjs/parser/types.js +5 -0
- package/dist/cjs/parser/utils.js +277 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +36 -0
- package/dist/esm/cli.js +34 -0
- package/dist/esm/gfm.js +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/parser/block-parser.js +640 -0
- package/dist/esm/parser/blocks/blockquote.js +22 -0
- package/dist/esm/parser/blocks/code-block-fenced.js +52 -0
- package/dist/esm/parser/blocks/code-block-indented.js +42 -0
- package/dist/esm/parser/blocks/heading-atx.js +24 -0
- package/dist/esm/parser/blocks/heading-setext.js +19 -0
- package/dist/esm/parser/blocks/html-block.js +77 -0
- package/dist/esm/parser/blocks/link-reference.js +105 -0
- package/dist/esm/parser/blocks/list.js +145 -0
- package/dist/esm/parser/blocks/paragraph.js +15 -0
- package/dist/esm/parser/blocks/table.js +152 -0
- package/dist/esm/parser/blocks/task-list.js +61 -0
- package/dist/esm/parser/blocks/thematic-break.js +13 -0
- package/dist/esm/parser/entities.js +2130 -0
- package/dist/esm/parser/gfm/block-parser.js +769 -0
- package/dist/esm/parser/gfm/index.js +115 -0
- package/dist/esm/parser/gfm/inline-parser.js +809 -0
- package/dist/esm/parser/gfm/renderer.js +509 -0
- package/dist/esm/parser/index.js +80 -0
- package/dist/esm/parser/inline-parser.js +560 -0
- package/dist/esm/parser/inlines/autolink-extended.js +357 -0
- package/dist/esm/parser/inlines/autolink.js +40 -0
- package/dist/esm/parser/inlines/code-span.js +44 -0
- package/dist/esm/parser/inlines/emphasis.js +59 -0
- package/dist/esm/parser/inlines/entity.js +21 -0
- package/dist/esm/parser/inlines/escape.js +21 -0
- package/dist/esm/parser/inlines/footnote.js +38 -0
- package/dist/esm/parser/inlines/hard-break.js +41 -0
- package/dist/esm/parser/inlines/image.js +4 -0
- package/dist/esm/parser/inlines/link.js +156 -0
- package/dist/esm/parser/inlines/soft-break.js +14 -0
- package/dist/esm/parser/inlines/strikethrough.js +42 -0
- package/dist/esm/parser/inlines/text.js +16 -0
- package/dist/esm/parser/renderer.js +341 -0
- package/dist/esm/parser/types.js +4 -0
- package/dist/esm/parser/utils.js +254 -0
- package/dist/gfm.d.ts +6 -0
- package/dist/gfm.js +26 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +26 -0
- package/dist/parser/block-parser.d.ts +25 -0
- package/dist/parser/block-parser.js +644 -0
- package/dist/parser/blocks/blockquote.d.ts +8 -0
- package/dist/parser/blocks/blockquote.js +28 -0
- package/dist/parser/blocks/code-block-fenced.d.ts +14 -0
- package/dist/parser/blocks/code-block-fenced.js +58 -0
- package/dist/parser/blocks/code-block-indented.d.ts +7 -0
- package/dist/parser/blocks/code-block-indented.js +47 -0
- package/dist/parser/blocks/heading-atx.d.ts +10 -0
- package/dist/parser/blocks/heading-atx.js +29 -0
- package/dist/parser/blocks/heading-setext.d.ts +8 -0
- package/dist/parser/blocks/heading-setext.js +24 -0
- package/dist/parser/blocks/html-block.d.ts +9 -0
- package/dist/parser/blocks/html-block.js +83 -0
- package/dist/parser/blocks/link-reference.d.ts +11 -0
- package/dist/parser/blocks/link-reference.js +109 -0
- package/dist/parser/blocks/list.d.ts +25 -0
- package/dist/parser/blocks/list.js +155 -0
- package/dist/parser/blocks/paragraph.d.ts +7 -0
- package/dist/parser/blocks/paragraph.js +20 -0
- package/dist/parser/blocks/table.d.ts +13 -0
- package/dist/parser/blocks/table.js +163 -0
- package/dist/parser/blocks/task-list.d.ts +10 -0
- package/dist/parser/blocks/task-list.js +66 -0
- package/dist/parser/blocks/thematic-break.d.ts +6 -0
- package/dist/parser/blocks/thematic-break.js +17 -0
- package/dist/parser/entities.d.ts +4 -0
- package/dist/parser/entities.js +2133 -0
- package/dist/parser/gfm/block-parser.d.ts +32 -0
- package/dist/parser/gfm/block-parser.js +773 -0
- package/dist/parser/gfm/index.d.ts +31 -0
- package/dist/parser/gfm/index.js +125 -0
- package/dist/parser/gfm/inline-parser.d.ts +25 -0
- package/dist/parser/gfm/inline-parser.js +813 -0
- package/dist/parser/gfm/renderer.d.ts +43 -0
- package/dist/parser/gfm/renderer.js +513 -0
- package/dist/parser/index.d.ts +33 -0
- package/dist/parser/index.js +104 -0
- package/dist/parser/inline-parser.d.ts +16 -0
- package/dist/parser/inline-parser.js +564 -0
- package/dist/parser/inlines/autolink-extended.d.ts +24 -0
- package/dist/parser/inlines/autolink-extended.js +364 -0
- package/dist/parser/inlines/autolink.d.ts +9 -0
- package/dist/parser/inlines/autolink.js +44 -0
- package/dist/parser/inlines/code-span.d.ts +9 -0
- package/dist/parser/inlines/code-span.js +48 -0
- package/dist/parser/inlines/emphasis.d.ts +14 -0
- package/dist/parser/inlines/emphasis.js +64 -0
- package/dist/parser/inlines/entity.d.ts +8 -0
- package/dist/parser/inlines/entity.js +25 -0
- package/dist/parser/inlines/escape.d.ts +8 -0
- package/dist/parser/inlines/escape.js +25 -0
- package/dist/parser/inlines/footnote.d.ts +9 -0
- package/dist/parser/inlines/footnote.js +41 -0
- package/dist/parser/inlines/hard-break.d.ts +9 -0
- package/dist/parser/inlines/hard-break.js +45 -0
- package/dist/parser/inlines/image.d.ts +4 -0
- package/dist/parser/inlines/image.js +9 -0
- package/dist/parser/inlines/link.d.ts +33 -0
- package/dist/parser/inlines/link.js +166 -0
- package/dist/parser/inlines/soft-break.d.ts +9 -0
- package/dist/parser/inlines/soft-break.js +18 -0
- package/dist/parser/inlines/strikethrough.d.ts +16 -0
- package/dist/parser/inlines/strikethrough.js +48 -0
- package/dist/parser/inlines/text.d.ts +6 -0
- package/dist/parser/inlines/text.js +20 -0
- package/dist/parser/renderer.d.ts +33 -0
- package/dist/parser/renderer.js +345 -0
- package/dist/parser/types.d.ts +152 -0
- package/dist/parser/types.js +5 -0
- package/dist/parser/utils.d.ts +32 -0
- package/dist/parser/utils.js +277 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/gfm.d.ts +6 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/parser/block-parser.d.ts +25 -0
- package/dist/types/parser/blocks/blockquote.d.ts +8 -0
- package/dist/types/parser/blocks/code-block-fenced.d.ts +14 -0
- package/dist/types/parser/blocks/code-block-indented.d.ts +7 -0
- package/dist/types/parser/blocks/heading-atx.d.ts +10 -0
- package/dist/types/parser/blocks/heading-setext.d.ts +8 -0
- package/dist/types/parser/blocks/html-block.d.ts +9 -0
- package/dist/types/parser/blocks/link-reference.d.ts +11 -0
- package/dist/types/parser/blocks/list.d.ts +25 -0
- package/dist/types/parser/blocks/paragraph.d.ts +7 -0
- package/dist/types/parser/blocks/table.d.ts +13 -0
- package/dist/types/parser/blocks/task-list.d.ts +10 -0
- package/dist/types/parser/blocks/thematic-break.d.ts +6 -0
- package/dist/types/parser/entities.d.ts +4 -0
- package/dist/types/parser/gfm/block-parser.d.ts +32 -0
- package/dist/types/parser/gfm/index.d.ts +31 -0
- package/dist/types/parser/gfm/inline-parser.d.ts +25 -0
- package/dist/types/parser/gfm/renderer.d.ts +43 -0
- package/dist/types/parser/index.d.ts +33 -0
- package/dist/types/parser/inline-parser.d.ts +16 -0
- package/dist/types/parser/inlines/autolink-extended.d.ts +24 -0
- package/dist/types/parser/inlines/autolink.d.ts +9 -0
- package/dist/types/parser/inlines/code-span.d.ts +9 -0
- package/dist/types/parser/inlines/emphasis.d.ts +14 -0
- package/dist/types/parser/inlines/entity.d.ts +8 -0
- package/dist/types/parser/inlines/escape.d.ts +8 -0
- package/dist/types/parser/inlines/footnote.d.ts +9 -0
- package/dist/types/parser/inlines/hard-break.d.ts +9 -0
- package/dist/types/parser/inlines/image.d.ts +4 -0
- package/dist/types/parser/inlines/link.d.ts +33 -0
- package/dist/types/parser/inlines/soft-break.d.ts +9 -0
- package/dist/types/parser/inlines/strikethrough.d.ts +16 -0
- package/dist/types/parser/inlines/text.d.ts +6 -0
- package/dist/types/parser/renderer.d.ts +33 -0
- package/dist/types/parser/types.d.ts +152 -0
- package/dist/types/parser/utils.d.ts +32 -0
- package/package.json +54 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GFM HTML renderer that adds tables, strikethrough, and task list rendering on top of CommonMark.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GFMHtmlRenderer = void 0;
|
|
7
|
+
const utils_js_1 = require("../utils.js");
|
|
8
|
+
function encodeUrl(url) {
|
|
9
|
+
let result = '';
|
|
10
|
+
let i = 0;
|
|
11
|
+
while (i < url.length) {
|
|
12
|
+
const char = url[i];
|
|
13
|
+
const code = char.charCodeAt(0);
|
|
14
|
+
if (char === '%' && i + 2 < url.length && /^[0-9a-fA-F]{2}$/.test(url.slice(i + 1, i + 3))) {
|
|
15
|
+
result += url.slice(i, i + 3).toUpperCase();
|
|
16
|
+
i += 3;
|
|
17
|
+
}
|
|
18
|
+
else if (/[A-Za-z0-9\-._~:/?#@!$&'()*+,;=]/.test(char)) {
|
|
19
|
+
result += char;
|
|
20
|
+
i++;
|
|
21
|
+
}
|
|
22
|
+
else if (code >= 0xD800 && code <= 0xDBFF && i + 1 < url.length) {
|
|
23
|
+
const fullChar = url.slice(i, i + 2);
|
|
24
|
+
const encoded = encodeURIComponent(fullChar);
|
|
25
|
+
result += encoded;
|
|
26
|
+
i += 2;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
const encoded = encodeURIComponent(char);
|
|
30
|
+
result += encoded;
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
class GFMHtmlRenderer {
|
|
37
|
+
options;
|
|
38
|
+
footnoteDefinitions = new Map();
|
|
39
|
+
footnoteOrder = [];
|
|
40
|
+
footnoteRefCounts = new Map();
|
|
41
|
+
footnoteRefIds = new Map();
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
this.options = {
|
|
44
|
+
softbreak: '\n',
|
|
45
|
+
safe: false,
|
|
46
|
+
...options,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
render(document) {
|
|
50
|
+
this.footnoteDefinitions = document.footnoteDefinitions ?? new Map();
|
|
51
|
+
this.footnoteOrder = [];
|
|
52
|
+
this.footnoteRefCounts = new Map();
|
|
53
|
+
this.footnoteRefIds = new Map();
|
|
54
|
+
const content = this.renderBlocks(document.children);
|
|
55
|
+
const footnotes = this.renderFootnotes();
|
|
56
|
+
const combined = [content, footnotes].filter(Boolean).join('\n');
|
|
57
|
+
return combined ? combined + '\n' : '';
|
|
58
|
+
}
|
|
59
|
+
renderBlocks(blocks, tight = false) {
|
|
60
|
+
return blocks.map(block => this.renderBlock(block, tight)).join('\n');
|
|
61
|
+
}
|
|
62
|
+
renderBlock(block, tight = false) {
|
|
63
|
+
switch (block.type) {
|
|
64
|
+
case 'paragraph':
|
|
65
|
+
return this.renderParagraph(block, tight);
|
|
66
|
+
case 'heading':
|
|
67
|
+
return this.renderHeading(block);
|
|
68
|
+
case 'thematic_break':
|
|
69
|
+
return this.renderThematicBreak(block);
|
|
70
|
+
case 'code_block':
|
|
71
|
+
return this.renderCodeBlock(block);
|
|
72
|
+
case 'blockquote':
|
|
73
|
+
return this.renderBlockquote(block);
|
|
74
|
+
case 'list':
|
|
75
|
+
return this.renderList(block);
|
|
76
|
+
case 'list_item':
|
|
77
|
+
return this.renderListItem(block);
|
|
78
|
+
case 'html_block':
|
|
79
|
+
return this.renderHtmlBlock(block);
|
|
80
|
+
case 'table':
|
|
81
|
+
return this.renderTable(block);
|
|
82
|
+
default:
|
|
83
|
+
return '';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
renderParagraph(node, tight = false) {
|
|
87
|
+
const content = this.renderInlines(node.children);
|
|
88
|
+
if (tight) {
|
|
89
|
+
return content;
|
|
90
|
+
}
|
|
91
|
+
return `<p>${content}</p>`;
|
|
92
|
+
}
|
|
93
|
+
renderHeading(node) {
|
|
94
|
+
const content = this.renderInlines(node.children);
|
|
95
|
+
return `<h${node.level}>${content}</h${node.level}>`;
|
|
96
|
+
}
|
|
97
|
+
renderThematicBreak(_node) {
|
|
98
|
+
return '<hr />';
|
|
99
|
+
}
|
|
100
|
+
renderCodeBlock(node) {
|
|
101
|
+
const escaped = (0, utils_js_1.escapeHtml)(node.literal);
|
|
102
|
+
if (node.info) {
|
|
103
|
+
const langPart = node.info.split(/\s+/)[0];
|
|
104
|
+
const lang = (0, utils_js_1.escapeHtml)(langPart);
|
|
105
|
+
let metaAttr = '';
|
|
106
|
+
if (this.options.fullInfoString) {
|
|
107
|
+
const meta = node.info.slice(langPart.length).replace(/^\s+/, '').replace(/\u0000/g, '\ufffd');
|
|
108
|
+
if (meta) {
|
|
109
|
+
metaAttr = ` data-meta="${(0, utils_js_1.escapeHtml)(meta)}"`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return `<pre><code class="language-${lang}"${metaAttr}>${escaped}</code></pre>`;
|
|
113
|
+
}
|
|
114
|
+
return `<pre><code>${escaped}</code></pre>`;
|
|
115
|
+
}
|
|
116
|
+
renderBlockquote(node) {
|
|
117
|
+
const content = this.renderBlocks(node.children);
|
|
118
|
+
if (content) {
|
|
119
|
+
return `<blockquote>\n${content}\n</blockquote>`;
|
|
120
|
+
}
|
|
121
|
+
return '<blockquote>\n</blockquote>';
|
|
122
|
+
}
|
|
123
|
+
renderList(node) {
|
|
124
|
+
const tag = node.listType === 'bullet' ? 'ul' : 'ol';
|
|
125
|
+
const startAttr = node.listType === 'ordered' && node.start !== 1 ? ` start="${node.start}"` : '';
|
|
126
|
+
const items = node.children.map(item => this.renderListItem(item, node.tight)).join('\n');
|
|
127
|
+
return `<${tag}${startAttr}>\n${items}\n</${tag}>`;
|
|
128
|
+
}
|
|
129
|
+
renderListItem(node, tight = false) {
|
|
130
|
+
const hasCheckbox = typeof node.checked === 'boolean';
|
|
131
|
+
if (node.children.length === 0) {
|
|
132
|
+
if (hasCheckbox) {
|
|
133
|
+
const checkbox = node.checked
|
|
134
|
+
? '<input checked="" disabled="" type="checkbox">'
|
|
135
|
+
: '<input disabled="" type="checkbox">';
|
|
136
|
+
return `<li>${checkbox}</li>`;
|
|
137
|
+
}
|
|
138
|
+
return '<li></li>';
|
|
139
|
+
}
|
|
140
|
+
if (hasCheckbox) {
|
|
141
|
+
const checkbox = node.checked
|
|
142
|
+
? '<input checked="" disabled="" type="checkbox"> '
|
|
143
|
+
: '<input disabled="" type="checkbox"> ';
|
|
144
|
+
const firstIsParagraph = node.children[0].type === 'paragraph';
|
|
145
|
+
if (tight && firstIsParagraph) {
|
|
146
|
+
const firstContent = this.renderBlock(node.children[0], tight);
|
|
147
|
+
if (node.children.length === 1) {
|
|
148
|
+
return `<li>${checkbox}${firstContent}</li>`;
|
|
149
|
+
}
|
|
150
|
+
const restContent = this.renderBlocks(node.children.slice(1), tight);
|
|
151
|
+
return `<li>${checkbox}${firstContent}\n${restContent}\n</li>`;
|
|
152
|
+
}
|
|
153
|
+
const content = this.renderBlocks(node.children, tight);
|
|
154
|
+
if (content) {
|
|
155
|
+
const contentWithCheckbox = content.replace(/^(<p>)?/, `$1${checkbox}`);
|
|
156
|
+
return `<li>\n${contentWithCheckbox}\n</li>`;
|
|
157
|
+
}
|
|
158
|
+
return `<li>${checkbox}</li>`;
|
|
159
|
+
}
|
|
160
|
+
const content = this.renderBlocks(node.children, tight);
|
|
161
|
+
if (tight && node.children.length === 1 && node.children[0].type === 'paragraph') {
|
|
162
|
+
return `<li>${content}</li>`;
|
|
163
|
+
}
|
|
164
|
+
if (content) {
|
|
165
|
+
return `<li>\n${content}\n</li>`;
|
|
166
|
+
}
|
|
167
|
+
return '<li></li>';
|
|
168
|
+
}
|
|
169
|
+
renderHtmlBlock(node) {
|
|
170
|
+
if (this.options.safe) {
|
|
171
|
+
return '<!-- raw HTML omitted -->';
|
|
172
|
+
}
|
|
173
|
+
const literal = node.literal.replace(/\n$/, '');
|
|
174
|
+
return this.options.tagfilter ? (0, utils_js_1.applyTagFilter)(literal) : literal;
|
|
175
|
+
}
|
|
176
|
+
renderTable(node) {
|
|
177
|
+
const lines = ['<table>'];
|
|
178
|
+
const headerRows = node.children.filter(row => row.isHeader);
|
|
179
|
+
const bodyRows = node.children.filter(row => !row.isHeader);
|
|
180
|
+
if (headerRows.length > 0) {
|
|
181
|
+
lines.push('<thead>');
|
|
182
|
+
for (const row of headerRows) {
|
|
183
|
+
lines.push(this.renderTableRow(row));
|
|
184
|
+
}
|
|
185
|
+
lines.push('</thead>');
|
|
186
|
+
}
|
|
187
|
+
if (bodyRows.length > 0) {
|
|
188
|
+
lines.push('<tbody>');
|
|
189
|
+
for (const row of bodyRows) {
|
|
190
|
+
lines.push(this.renderTableRow(row));
|
|
191
|
+
}
|
|
192
|
+
lines.push('</tbody>');
|
|
193
|
+
}
|
|
194
|
+
lines.push('</table>');
|
|
195
|
+
return lines.join('\n');
|
|
196
|
+
}
|
|
197
|
+
renderTableRow(row) {
|
|
198
|
+
const cells = row.children.map(cell => this.renderTableCell(cell)).join('\n');
|
|
199
|
+
return `<tr>\n${cells}\n</tr>`;
|
|
200
|
+
}
|
|
201
|
+
renderTableCell(cell) {
|
|
202
|
+
const tag = cell.isHeader ? 'th' : 'td';
|
|
203
|
+
const alignAttr = cell.align
|
|
204
|
+
? this.options.tablePreferStyleAttributes
|
|
205
|
+
? ` style="text-align: ${cell.align}"`
|
|
206
|
+
: ` align="${cell.align}"`
|
|
207
|
+
: '';
|
|
208
|
+
const content = this.renderInlines(cell.children);
|
|
209
|
+
return `<${tag}${alignAttr}>${content}</${tag}>`;
|
|
210
|
+
}
|
|
211
|
+
renderInlines(inlines) {
|
|
212
|
+
if (this.options.smart) {
|
|
213
|
+
return this.renderInlinesSmart(inlines);
|
|
214
|
+
}
|
|
215
|
+
return inlines.map(inline => this.renderInline(inline)).join('');
|
|
216
|
+
}
|
|
217
|
+
renderInline(inline) {
|
|
218
|
+
switch (inline.type) {
|
|
219
|
+
case 'text':
|
|
220
|
+
return this.renderText(inline);
|
|
221
|
+
case 'softbreak':
|
|
222
|
+
return this.renderSoftbreak(inline);
|
|
223
|
+
case 'hardbreak':
|
|
224
|
+
return this.renderHardbreak(inline);
|
|
225
|
+
case 'code_span':
|
|
226
|
+
return this.renderCodeSpan(inline);
|
|
227
|
+
case 'emphasis':
|
|
228
|
+
return this.renderEmphasis(inline);
|
|
229
|
+
case 'strong':
|
|
230
|
+
return this.renderStrong(inline);
|
|
231
|
+
case 'strikethrough':
|
|
232
|
+
return this.renderStrikethrough(inline);
|
|
233
|
+
case 'footnote_ref':
|
|
234
|
+
return this.renderFootnoteRef(inline);
|
|
235
|
+
case 'link':
|
|
236
|
+
return this.renderLink(inline);
|
|
237
|
+
case 'image':
|
|
238
|
+
return this.renderImage(inline);
|
|
239
|
+
case 'html_inline':
|
|
240
|
+
return this.renderHtmlInline(inline);
|
|
241
|
+
default:
|
|
242
|
+
return '';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
renderText(node) {
|
|
246
|
+
return (0, utils_js_1.escapeHtml)(node.literal);
|
|
247
|
+
}
|
|
248
|
+
renderSoftbreak(_node) {
|
|
249
|
+
return this.options.softbreak;
|
|
250
|
+
}
|
|
251
|
+
renderHardbreak(_node) {
|
|
252
|
+
return '<br />\n';
|
|
253
|
+
}
|
|
254
|
+
renderCodeSpan(node) {
|
|
255
|
+
return `<code>${(0, utils_js_1.escapeHtml)(node.literal)}</code>`;
|
|
256
|
+
}
|
|
257
|
+
renderEmphasis(node) {
|
|
258
|
+
const content = this.renderInlines(node.children);
|
|
259
|
+
return `<em>${content}</em>`;
|
|
260
|
+
}
|
|
261
|
+
renderStrong(node) {
|
|
262
|
+
const content = this.renderInlines(node.children);
|
|
263
|
+
return `<strong>${content}</strong>`;
|
|
264
|
+
}
|
|
265
|
+
renderStrikethrough(node) {
|
|
266
|
+
const content = this.renderInlines(node.children);
|
|
267
|
+
return `<del>${content}</del>`;
|
|
268
|
+
}
|
|
269
|
+
renderFootnoteRef(node) {
|
|
270
|
+
const def = this.footnoteDefinitions.get(node.key);
|
|
271
|
+
if (!def) {
|
|
272
|
+
return `[^${(0, utils_js_1.escapeHtml)(node.label)}]`;
|
|
273
|
+
}
|
|
274
|
+
let index = this.footnoteOrder.indexOf(node.key);
|
|
275
|
+
if (index === -1) {
|
|
276
|
+
this.footnoteOrder.push(node.key);
|
|
277
|
+
index = this.footnoteOrder.length - 1;
|
|
278
|
+
}
|
|
279
|
+
const number = index + 1;
|
|
280
|
+
const count = (this.footnoteRefCounts.get(node.key) ?? 0) + 1;
|
|
281
|
+
this.footnoteRefCounts.set(node.key, count);
|
|
282
|
+
const encodedLabel = encodeUrl(def.label);
|
|
283
|
+
const refId = count === 1 ? `fnref-${encodedLabel}` : `fnref-${encodedLabel}-${count}`;
|
|
284
|
+
const refIds = this.footnoteRefIds.get(node.key) ?? [];
|
|
285
|
+
refIds.push(refId);
|
|
286
|
+
this.footnoteRefIds.set(node.key, refIds);
|
|
287
|
+
return `<sup class="footnote-ref"><a href="#fn-${encodedLabel}" id="${refId}" data-footnote-ref>${number}</a></sup>`;
|
|
288
|
+
}
|
|
289
|
+
renderLink(node) {
|
|
290
|
+
if (this.options.safe && /^javascript:/i.test(node.destination)) {
|
|
291
|
+
return this.renderInlines(node.children);
|
|
292
|
+
}
|
|
293
|
+
const href = (0, utils_js_1.escapeHtml)(encodeUrl(node.destination));
|
|
294
|
+
const title = node.title ? ` title="${(0, utils_js_1.escapeHtml)(node.title)}"` : '';
|
|
295
|
+
const content = this.renderInlines(node.children);
|
|
296
|
+
return `<a href="${href}"${title}>${content}</a>`;
|
|
297
|
+
}
|
|
298
|
+
renderImage(node) {
|
|
299
|
+
if (this.options.safe && /^javascript:/i.test(node.destination)) {
|
|
300
|
+
return (0, utils_js_1.escapeHtml)(node.alt);
|
|
301
|
+
}
|
|
302
|
+
const src = (0, utils_js_1.escapeHtml)(encodeUrl(node.destination));
|
|
303
|
+
const alt = (0, utils_js_1.escapeHtml)(node.alt);
|
|
304
|
+
const title = node.title ? ` title="${(0, utils_js_1.escapeHtml)(node.title)}"` : '';
|
|
305
|
+
return `<img src="${src}" alt="${alt}"${title} />`;
|
|
306
|
+
}
|
|
307
|
+
renderHtmlInline(node) {
|
|
308
|
+
if (this.options.safe) {
|
|
309
|
+
return '<!-- raw HTML omitted -->';
|
|
310
|
+
}
|
|
311
|
+
return this.options.tagfilter ? (0, utils_js_1.applyTagFilter)(node.literal) : node.literal;
|
|
312
|
+
}
|
|
313
|
+
renderInlinesSmart(inlines) {
|
|
314
|
+
const tokens = [];
|
|
315
|
+
const nodeTokens = new Map();
|
|
316
|
+
const pushTextTokens = (node) => {
|
|
317
|
+
const indices = [];
|
|
318
|
+
for (let i = 0; i < node.literal.length; i++) {
|
|
319
|
+
const idx = tokens.length;
|
|
320
|
+
tokens.push({
|
|
321
|
+
char: node.literal[i],
|
|
322
|
+
node,
|
|
323
|
+
nodeOffset: i,
|
|
324
|
+
noSmart: !!node.noSmart,
|
|
325
|
+
});
|
|
326
|
+
indices.push(idx);
|
|
327
|
+
}
|
|
328
|
+
nodeTokens.set(node, indices);
|
|
329
|
+
};
|
|
330
|
+
for (const inline of inlines) {
|
|
331
|
+
if (inline.type === 'text') {
|
|
332
|
+
pushTextTokens(inline);
|
|
333
|
+
}
|
|
334
|
+
else if (inline.type === 'softbreak' || inline.type === 'hardbreak') {
|
|
335
|
+
tokens.push({ char: '\n', node: null, nodeOffset: 0, noSmart: false });
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
tokens.push({ char: 'A', node: null, nodeOffset: 0, noSmart: false });
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const replacements = new Map();
|
|
342
|
+
const doubleStack = [];
|
|
343
|
+
const singleStack = [];
|
|
344
|
+
const isWhitespace = (c) => /\s/.test(c);
|
|
345
|
+
const isPunctuation = (c) => /[\p{P}\p{S}]/u.test(c);
|
|
346
|
+
const isAlphaNum = (c) => /[A-Za-z0-9]/.test(c);
|
|
347
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
348
|
+
const token = tokens[i];
|
|
349
|
+
if (token.noSmart)
|
|
350
|
+
continue;
|
|
351
|
+
if (token.char !== '"' && token.char !== "'")
|
|
352
|
+
continue;
|
|
353
|
+
const before = i > 0 ? tokens[i - 1].char : '\n';
|
|
354
|
+
const after = i + 1 < tokens.length ? tokens[i + 1].char : '\n';
|
|
355
|
+
const beforeIsWhitespace = isWhitespace(before);
|
|
356
|
+
const afterIsWhitespace = isWhitespace(after);
|
|
357
|
+
const beforeIsPunct = isPunctuation(before);
|
|
358
|
+
const afterIsPunct = isPunctuation(after);
|
|
359
|
+
let leftFlanking = !afterIsWhitespace &&
|
|
360
|
+
(!afterIsPunct || beforeIsWhitespace || beforeIsPunct);
|
|
361
|
+
let rightFlanking = !beforeIsWhitespace &&
|
|
362
|
+
(!beforeIsPunct || afterIsWhitespace || afterIsPunct);
|
|
363
|
+
if (before === ')' || before === ']') {
|
|
364
|
+
leftFlanking = false;
|
|
365
|
+
}
|
|
366
|
+
if (token.char === "'") {
|
|
367
|
+
if (isAlphaNum(before) && isAlphaNum(after)) {
|
|
368
|
+
replacements.set(i, '’');
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if ((before === ')' || before === ']') && isAlphaNum(after)) {
|
|
372
|
+
replacements.set(i, '’');
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (rightFlanking && singleStack.length > 0) {
|
|
376
|
+
const opener = singleStack.pop();
|
|
377
|
+
replacements.set(opener, '‘');
|
|
378
|
+
replacements.set(i, '’');
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (leftFlanking) {
|
|
382
|
+
singleStack.push(i);
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
replacements.set(i, '’');
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
if (rightFlanking && doubleStack.length > 0) {
|
|
389
|
+
const opener = doubleStack.pop();
|
|
390
|
+
replacements.set(opener, '“');
|
|
391
|
+
replacements.set(i, '”');
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
if (leftFlanking) {
|
|
395
|
+
doubleStack.push(i);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
replacements.set(i, '“');
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
for (const idx of doubleStack) {
|
|
402
|
+
replacements.set(idx, '“');
|
|
403
|
+
}
|
|
404
|
+
for (const idx of singleStack) {
|
|
405
|
+
replacements.set(idx, '’');
|
|
406
|
+
}
|
|
407
|
+
const renderTextNodes = new Map();
|
|
408
|
+
for (const [node, indices] of nodeTokens.entries()) {
|
|
409
|
+
if (node.noSmart) {
|
|
410
|
+
renderTextNodes.set(node, (0, utils_js_1.escapeHtml)(node.literal));
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
let result = '';
|
|
414
|
+
for (let i = 0; i < indices.length; i++) {
|
|
415
|
+
const tokenIndex = indices[i];
|
|
416
|
+
const token = tokens[tokenIndex];
|
|
417
|
+
const replacement = replacements.get(tokenIndex);
|
|
418
|
+
result += replacement ?? token.char;
|
|
419
|
+
}
|
|
420
|
+
result = this.applyDashesAndEllipses(result);
|
|
421
|
+
renderTextNodes.set(node, (0, utils_js_1.escapeHtml)(result));
|
|
422
|
+
}
|
|
423
|
+
return inlines.map(inline => {
|
|
424
|
+
if (inline.type === 'text') {
|
|
425
|
+
return renderTextNodes.get(inline) ?? '';
|
|
426
|
+
}
|
|
427
|
+
return this.renderInline(inline);
|
|
428
|
+
}).join('');
|
|
429
|
+
}
|
|
430
|
+
applyDashesAndEllipses(text) {
|
|
431
|
+
let result = text.replace(/\.{3}/g, '…');
|
|
432
|
+
result = result.replace(/-{2,}/g, (match) => {
|
|
433
|
+
const count = match.length;
|
|
434
|
+
if (count % 3 === 0) {
|
|
435
|
+
return '—'.repeat(count / 3);
|
|
436
|
+
}
|
|
437
|
+
if (count % 2 === 0) {
|
|
438
|
+
return '–'.repeat(count / 2);
|
|
439
|
+
}
|
|
440
|
+
let emCount = Math.floor(count / 3);
|
|
441
|
+
const remainder = count % 3;
|
|
442
|
+
let enCount = 0;
|
|
443
|
+
if (remainder === 1) {
|
|
444
|
+
emCount = Math.max(0, emCount - 1);
|
|
445
|
+
enCount = 2;
|
|
446
|
+
}
|
|
447
|
+
else if (remainder === 2) {
|
|
448
|
+
enCount = 1;
|
|
449
|
+
}
|
|
450
|
+
return '—'.repeat(emCount) + '–'.repeat(enCount);
|
|
451
|
+
});
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
renderFootnotes() {
|
|
455
|
+
if (this.footnoteOrder.length === 0) {
|
|
456
|
+
return '';
|
|
457
|
+
}
|
|
458
|
+
const lines = ['<section class="footnotes" data-footnotes>', '<ol>'];
|
|
459
|
+
for (let i = 0; i < this.footnoteOrder.length; i++) {
|
|
460
|
+
const key = this.footnoteOrder[i];
|
|
461
|
+
const def = this.footnoteDefinitions.get(key);
|
|
462
|
+
if (!def)
|
|
463
|
+
continue;
|
|
464
|
+
const encodedLabel = encodeUrl(def.label);
|
|
465
|
+
const number = i + 1;
|
|
466
|
+
const refIds = this.footnoteRefIds.get(key) ?? [];
|
|
467
|
+
const backrefs = this.renderFootnoteBackrefs(number, refIds);
|
|
468
|
+
const blocks = def.blocks;
|
|
469
|
+
lines.push(`<li id="fn-${encodedLabel}">`);
|
|
470
|
+
if (blocks.length === 0) {
|
|
471
|
+
if (backrefs) {
|
|
472
|
+
lines.push(backrefs);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
const lastBlock = blocks[blocks.length - 1];
|
|
477
|
+
const renderedBlocks = [];
|
|
478
|
+
for (let b = 0; b < blocks.length; b++) {
|
|
479
|
+
const block = blocks[b];
|
|
480
|
+
if (b === blocks.length - 1 && block.type === 'paragraph' && backrefs) {
|
|
481
|
+
const content = this.renderInlines(block.children);
|
|
482
|
+
renderedBlocks.push(`<p>${content}${backrefs}</p>`);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
renderedBlocks.push(this.renderBlock(block, false));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (lastBlock.type !== 'paragraph' && backrefs) {
|
|
489
|
+
renderedBlocks.push(backrefs.trim());
|
|
490
|
+
}
|
|
491
|
+
lines.push(renderedBlocks.join('\n'));
|
|
492
|
+
}
|
|
493
|
+
lines.push('</li>');
|
|
494
|
+
}
|
|
495
|
+
lines.push('</ol>');
|
|
496
|
+
lines.push('</section>');
|
|
497
|
+
return lines.join('\n');
|
|
498
|
+
}
|
|
499
|
+
renderFootnoteBackrefs(number, refIds) {
|
|
500
|
+
if (refIds.length === 0) {
|
|
501
|
+
return '';
|
|
502
|
+
}
|
|
503
|
+
const links = refIds.map((refId, index) => {
|
|
504
|
+
const refIndex = index + 1;
|
|
505
|
+
const dataIdx = refIndex === 1 ? `${number}` : `${number}-${refIndex}`;
|
|
506
|
+
const label = `Back to reference ${dataIdx}`;
|
|
507
|
+
const suffix = refIndex === 1 ? '' : `<sup class="footnote-ref">${refIndex}</sup>`;
|
|
508
|
+
return `<a href="#${refId}" class="footnote-backref" data-footnote-backref data-footnote-backref-idx="${dataIdx}" aria-label="${label}">↩${suffix}</a>`;
|
|
509
|
+
});
|
|
510
|
+
return ` ${links.join(' ')}`;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
exports.GFMHtmlRenderer = GFMHtmlRenderer;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Core parser module that coordinates block and inline parsing and exposes the main parser
|
|
4
|
+
* class and convenience helpers.
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.MarkdownParser = exports.HtmlRenderer = exports.InlineParser = exports.BlockParser = void 0;
|
|
22
|
+
exports.parse = parse;
|
|
23
|
+
exports.render = render;
|
|
24
|
+
exports.markdown = markdown;
|
|
25
|
+
const block_parser_js_1 = require("./block-parser.js");
|
|
26
|
+
const inline_parser_js_1 = require("./inline-parser.js");
|
|
27
|
+
const renderer_js_1 = require("./renderer.js");
|
|
28
|
+
__exportStar(require("./types.js"), exports);
|
|
29
|
+
var block_parser_js_2 = require("./block-parser.js");
|
|
30
|
+
Object.defineProperty(exports, "BlockParser", { enumerable: true, get: function () { return block_parser_js_2.BlockParser; } });
|
|
31
|
+
var inline_parser_js_2 = require("./inline-parser.js");
|
|
32
|
+
Object.defineProperty(exports, "InlineParser", { enumerable: true, get: function () { return inline_parser_js_2.InlineParser; } });
|
|
33
|
+
var renderer_js_2 = require("./renderer.js");
|
|
34
|
+
Object.defineProperty(exports, "HtmlRenderer", { enumerable: true, get: function () { return renderer_js_2.HtmlRenderer; } });
|
|
35
|
+
class MarkdownParser {
|
|
36
|
+
blockParser;
|
|
37
|
+
inlineParser;
|
|
38
|
+
options;
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.options = options;
|
|
41
|
+
this.blockParser = new block_parser_js_1.BlockParser();
|
|
42
|
+
this.inlineParser = new inline_parser_js_1.InlineParser();
|
|
43
|
+
}
|
|
44
|
+
parse(input) {
|
|
45
|
+
const { document, linkReferences } = this.blockParser.parse(input);
|
|
46
|
+
this.inlineParser.setLinkReferences(linkReferences);
|
|
47
|
+
this.processInlines(document, linkReferences);
|
|
48
|
+
return document;
|
|
49
|
+
}
|
|
50
|
+
processInlines(document, linkReferences) {
|
|
51
|
+
const processBlock = (block) => {
|
|
52
|
+
if (block.type === 'paragraph') {
|
|
53
|
+
const para = block;
|
|
54
|
+
const rawContent = para.rawContent;
|
|
55
|
+
if (rawContent) {
|
|
56
|
+
para.children = this.inlineParser.parse(rawContent);
|
|
57
|
+
delete para.rawContent;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (block.type === 'heading') {
|
|
61
|
+
const heading = block;
|
|
62
|
+
const rawContent = heading.rawContent;
|
|
63
|
+
if (rawContent) {
|
|
64
|
+
heading.children = this.inlineParser.parse(rawContent);
|
|
65
|
+
delete heading.rawContent;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (block.type === 'blockquote') {
|
|
69
|
+
block.children.forEach(processBlock);
|
|
70
|
+
}
|
|
71
|
+
else if (block.type === 'list') {
|
|
72
|
+
block.children.forEach(item => {
|
|
73
|
+
item.children.forEach(processBlock);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else if (block.type === 'list_item') {
|
|
77
|
+
block.children.forEach(processBlock);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
document.children.forEach(processBlock);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.MarkdownParser = MarkdownParser;
|
|
84
|
+
function parse(input, options) {
|
|
85
|
+
const parser = new MarkdownParser(options);
|
|
86
|
+
return parser.parse(input);
|
|
87
|
+
}
|
|
88
|
+
function render(document, options) {
|
|
89
|
+
const renderer = new renderer_js_1.HtmlRenderer(options);
|
|
90
|
+
return renderer.render(document);
|
|
91
|
+
}
|
|
92
|
+
function markdown(input, options) {
|
|
93
|
+
const document = parse(input, options);
|
|
94
|
+
return render(document, options);
|
|
95
|
+
}
|
|
96
|
+
exports.default = {
|
|
97
|
+
parse,
|
|
98
|
+
render,
|
|
99
|
+
markdown,
|
|
100
|
+
MarkdownParser,
|
|
101
|
+
BlockParser: block_parser_js_1.BlockParser,
|
|
102
|
+
InlineParser: inline_parser_js_1.InlineParser,
|
|
103
|
+
HtmlRenderer: renderer_js_1.HtmlRenderer,
|
|
104
|
+
};
|