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,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HTML renderer that converts a Markdown AST into HTML with configurable list, URL safety,
|
|
4
|
+
* and soft-break handling.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.HtmlRenderer = void 0;
|
|
8
|
+
const utils_js_1 = require("./utils.js");
|
|
9
|
+
function encodeUrl(url) {
|
|
10
|
+
let result = '';
|
|
11
|
+
let i = 0;
|
|
12
|
+
while (i < url.length) {
|
|
13
|
+
const char = url[i];
|
|
14
|
+
const code = char.charCodeAt(0);
|
|
15
|
+
if (char === '%' && i + 2 < url.length && /^[0-9a-fA-F]{2}$/.test(url.slice(i + 1, i + 3))) {
|
|
16
|
+
result += url.slice(i, i + 3).toUpperCase();
|
|
17
|
+
i += 3;
|
|
18
|
+
}
|
|
19
|
+
else if (/[A-Za-z0-9\-._~:/?#@!$&'()*+,;=]/.test(char)) {
|
|
20
|
+
result += char;
|
|
21
|
+
i++;
|
|
22
|
+
}
|
|
23
|
+
else if (code >= 0xD800 && code <= 0xDBFF && i + 1 < url.length) {
|
|
24
|
+
const fullChar = url.slice(i, i + 2);
|
|
25
|
+
const encoded = encodeURIComponent(fullChar);
|
|
26
|
+
result += encoded;
|
|
27
|
+
i += 2;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const encoded = encodeURIComponent(char);
|
|
31
|
+
result += encoded;
|
|
32
|
+
i++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
class HtmlRenderer {
|
|
38
|
+
options;
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.options = {
|
|
41
|
+
softbreak: '\n',
|
|
42
|
+
safe: false,
|
|
43
|
+
...options,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
render(document) {
|
|
47
|
+
const content = this.renderBlocks(document.children);
|
|
48
|
+
return content ? content + '\n' : '';
|
|
49
|
+
}
|
|
50
|
+
renderBlocks(blocks, tight = false) {
|
|
51
|
+
return blocks.map(block => this.renderBlock(block, tight)).join('\n');
|
|
52
|
+
}
|
|
53
|
+
renderBlock(block, tight = false) {
|
|
54
|
+
switch (block.type) {
|
|
55
|
+
case 'paragraph':
|
|
56
|
+
return this.renderParagraph(block, tight);
|
|
57
|
+
case 'heading':
|
|
58
|
+
return this.renderHeading(block);
|
|
59
|
+
case 'thematic_break':
|
|
60
|
+
return this.renderThematicBreak(block);
|
|
61
|
+
case 'code_block':
|
|
62
|
+
return this.renderCodeBlock(block);
|
|
63
|
+
case 'blockquote':
|
|
64
|
+
return this.renderBlockquote(block);
|
|
65
|
+
case 'list':
|
|
66
|
+
return this.renderList(block);
|
|
67
|
+
case 'list_item':
|
|
68
|
+
return this.renderListItem(block);
|
|
69
|
+
case 'html_block':
|
|
70
|
+
return this.renderHtmlBlock(block);
|
|
71
|
+
default:
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
renderParagraph(node, tight = false) {
|
|
76
|
+
const content = this.renderInlines(node.children);
|
|
77
|
+
if (tight) {
|
|
78
|
+
return content;
|
|
79
|
+
}
|
|
80
|
+
return `<p>${content}</p>`;
|
|
81
|
+
}
|
|
82
|
+
renderHeading(node) {
|
|
83
|
+
const content = this.renderInlines(node.children);
|
|
84
|
+
return `<h${node.level}>${content}</h${node.level}>`;
|
|
85
|
+
}
|
|
86
|
+
renderThematicBreak(_node) {
|
|
87
|
+
return '<hr />';
|
|
88
|
+
}
|
|
89
|
+
renderCodeBlock(node) {
|
|
90
|
+
const escaped = (0, utils_js_1.escapeHtml)(node.literal);
|
|
91
|
+
if (node.info) {
|
|
92
|
+
const lang = (0, utils_js_1.escapeHtml)(node.info.split(/\s+/)[0]);
|
|
93
|
+
return `<pre><code class="language-${lang}">${escaped}</code></pre>`;
|
|
94
|
+
}
|
|
95
|
+
return `<pre><code>${escaped}</code></pre>`;
|
|
96
|
+
}
|
|
97
|
+
renderBlockquote(node) {
|
|
98
|
+
const content = this.renderBlocks(node.children);
|
|
99
|
+
if (content) {
|
|
100
|
+
return `<blockquote>\n${content}\n</blockquote>`;
|
|
101
|
+
}
|
|
102
|
+
return '<blockquote>\n</blockquote>';
|
|
103
|
+
}
|
|
104
|
+
renderList(node) {
|
|
105
|
+
const tag = node.listType === 'bullet' ? 'ul' : 'ol';
|
|
106
|
+
const startAttr = node.listType === 'ordered' && node.start !== 1 ? ` start="${node.start}"` : '';
|
|
107
|
+
const items = node.children.map(item => this.renderListItem(item, node.tight)).join('\n');
|
|
108
|
+
return `<${tag}${startAttr}>\n${items}\n</${tag}>`;
|
|
109
|
+
}
|
|
110
|
+
renderListItem(node, tight = false) {
|
|
111
|
+
if (node.children.length === 0) {
|
|
112
|
+
return '<li></li>';
|
|
113
|
+
}
|
|
114
|
+
const content = this.renderBlocks(node.children, tight);
|
|
115
|
+
if (tight && node.children.length === 1 && node.children[0].type === 'paragraph') {
|
|
116
|
+
return `<li>${content}</li>`;
|
|
117
|
+
}
|
|
118
|
+
if (content) {
|
|
119
|
+
return `<li>\n${content}\n</li>`;
|
|
120
|
+
}
|
|
121
|
+
return '<li></li>';
|
|
122
|
+
}
|
|
123
|
+
renderHtmlBlock(node) {
|
|
124
|
+
if (this.options.safe) {
|
|
125
|
+
return '<!-- raw HTML omitted -->';
|
|
126
|
+
}
|
|
127
|
+
return node.literal.replace(/\n$/, '');
|
|
128
|
+
}
|
|
129
|
+
renderInlines(inlines) {
|
|
130
|
+
if (this.options.smart) {
|
|
131
|
+
return this.renderInlinesSmart(inlines);
|
|
132
|
+
}
|
|
133
|
+
return inlines.map(inline => this.renderInline(inline)).join('');
|
|
134
|
+
}
|
|
135
|
+
renderInline(inline) {
|
|
136
|
+
switch (inline.type) {
|
|
137
|
+
case 'text':
|
|
138
|
+
return this.renderText(inline);
|
|
139
|
+
case 'softbreak':
|
|
140
|
+
return this.renderSoftbreak(inline);
|
|
141
|
+
case 'hardbreak':
|
|
142
|
+
return this.renderHardbreak(inline);
|
|
143
|
+
case 'code_span':
|
|
144
|
+
return this.renderCodeSpan(inline);
|
|
145
|
+
case 'emphasis':
|
|
146
|
+
return this.renderEmphasis(inline);
|
|
147
|
+
case 'strong':
|
|
148
|
+
return this.renderStrong(inline);
|
|
149
|
+
case 'link':
|
|
150
|
+
return this.renderLink(inline);
|
|
151
|
+
case 'image':
|
|
152
|
+
return this.renderImage(inline);
|
|
153
|
+
case 'html_inline':
|
|
154
|
+
return this.renderHtmlInline(inline);
|
|
155
|
+
default:
|
|
156
|
+
return '';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
renderText(node) {
|
|
160
|
+
return (0, utils_js_1.escapeHtml)(node.literal);
|
|
161
|
+
}
|
|
162
|
+
renderSoftbreak(_node) {
|
|
163
|
+
return this.options.softbreak;
|
|
164
|
+
}
|
|
165
|
+
renderHardbreak(_node) {
|
|
166
|
+
return '<br />\n';
|
|
167
|
+
}
|
|
168
|
+
renderCodeSpan(node) {
|
|
169
|
+
return `<code>${(0, utils_js_1.escapeHtml)(node.literal)}</code>`;
|
|
170
|
+
}
|
|
171
|
+
renderEmphasis(node) {
|
|
172
|
+
const content = this.renderInlines(node.children);
|
|
173
|
+
return `<em>${content}</em>`;
|
|
174
|
+
}
|
|
175
|
+
renderStrong(node) {
|
|
176
|
+
const content = this.renderInlines(node.children);
|
|
177
|
+
return `<strong>${content}</strong>`;
|
|
178
|
+
}
|
|
179
|
+
renderLink(node) {
|
|
180
|
+
if (this.options.safe && /^javascript:/i.test(node.destination)) {
|
|
181
|
+
return this.renderInlines(node.children);
|
|
182
|
+
}
|
|
183
|
+
const href = (0, utils_js_1.escapeHtml)(encodeUrl(node.destination));
|
|
184
|
+
const title = node.title ? ` title="${(0, utils_js_1.escapeHtml)(node.title)}"` : '';
|
|
185
|
+
const content = this.renderInlines(node.children);
|
|
186
|
+
return `<a href="${href}"${title}>${content}</a>`;
|
|
187
|
+
}
|
|
188
|
+
renderImage(node) {
|
|
189
|
+
if (this.options.safe && /^javascript:/i.test(node.destination)) {
|
|
190
|
+
return (0, utils_js_1.escapeHtml)(node.alt);
|
|
191
|
+
}
|
|
192
|
+
const src = (0, utils_js_1.escapeHtml)(encodeUrl(node.destination));
|
|
193
|
+
const alt = (0, utils_js_1.escapeHtml)(node.alt);
|
|
194
|
+
const title = node.title ? ` title="${(0, utils_js_1.escapeHtml)(node.title)}"` : '';
|
|
195
|
+
return `<img src="${src}" alt="${alt}"${title} />`;
|
|
196
|
+
}
|
|
197
|
+
renderHtmlInline(node) {
|
|
198
|
+
if (this.options.safe) {
|
|
199
|
+
return '<!-- raw HTML omitted -->';
|
|
200
|
+
}
|
|
201
|
+
return node.literal;
|
|
202
|
+
}
|
|
203
|
+
renderInlinesSmart(inlines) {
|
|
204
|
+
const tokens = [];
|
|
205
|
+
const nodeTokens = new Map();
|
|
206
|
+
const pushTextTokens = (node) => {
|
|
207
|
+
const indices = [];
|
|
208
|
+
for (let i = 0; i < node.literal.length; i++) {
|
|
209
|
+
const idx = tokens.length;
|
|
210
|
+
tokens.push({
|
|
211
|
+
char: node.literal[i],
|
|
212
|
+
node,
|
|
213
|
+
nodeOffset: i,
|
|
214
|
+
noSmart: !!node.noSmart,
|
|
215
|
+
});
|
|
216
|
+
indices.push(idx);
|
|
217
|
+
}
|
|
218
|
+
nodeTokens.set(node, indices);
|
|
219
|
+
};
|
|
220
|
+
for (const inline of inlines) {
|
|
221
|
+
if (inline.type === 'text') {
|
|
222
|
+
pushTextTokens(inline);
|
|
223
|
+
}
|
|
224
|
+
else if (inline.type === 'softbreak' || inline.type === 'hardbreak') {
|
|
225
|
+
tokens.push({ char: '\n', node: null, nodeOffset: 0, noSmart: false });
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
tokens.push({ char: 'A', node: null, nodeOffset: 0, noSmart: false });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const replacements = new Map();
|
|
232
|
+
const doubleStack = [];
|
|
233
|
+
const singleStack = [];
|
|
234
|
+
const isWhitespace = (c) => /\s/.test(c);
|
|
235
|
+
const isPunctuation = (c) => /[\p{P}\p{S}]/u.test(c);
|
|
236
|
+
const isAlphaNum = (c) => /[A-Za-z0-9]/.test(c);
|
|
237
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
238
|
+
const token = tokens[i];
|
|
239
|
+
if (token.noSmart)
|
|
240
|
+
continue;
|
|
241
|
+
if (token.char !== '"' && token.char !== "'")
|
|
242
|
+
continue;
|
|
243
|
+
const before = i > 0 ? tokens[i - 1].char : '\n';
|
|
244
|
+
const after = i + 1 < tokens.length ? tokens[i + 1].char : '\n';
|
|
245
|
+
const beforeIsWhitespace = isWhitespace(before);
|
|
246
|
+
const afterIsWhitespace = isWhitespace(after);
|
|
247
|
+
const beforeIsPunct = isPunctuation(before);
|
|
248
|
+
const afterIsPunct = isPunctuation(after);
|
|
249
|
+
let leftFlanking = !afterIsWhitespace &&
|
|
250
|
+
(!afterIsPunct || beforeIsWhitespace || beforeIsPunct);
|
|
251
|
+
let rightFlanking = !beforeIsWhitespace &&
|
|
252
|
+
(!beforeIsPunct || afterIsWhitespace || afterIsPunct);
|
|
253
|
+
if (before === ')' || before === ']') {
|
|
254
|
+
leftFlanking = false;
|
|
255
|
+
}
|
|
256
|
+
if (token.char === "'") {
|
|
257
|
+
if (isAlphaNum(before) && isAlphaNum(after)) {
|
|
258
|
+
replacements.set(i, '’');
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if ((before === ')' || before === ']') && isAlphaNum(after)) {
|
|
262
|
+
replacements.set(i, '’');
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (rightFlanking && singleStack.length > 0) {
|
|
266
|
+
const opener = singleStack.pop();
|
|
267
|
+
replacements.set(opener, '‘');
|
|
268
|
+
replacements.set(i, '’');
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (leftFlanking) {
|
|
272
|
+
singleStack.push(i);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
replacements.set(i, '’');
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
if (rightFlanking && doubleStack.length > 0) {
|
|
279
|
+
const opener = doubleStack.pop();
|
|
280
|
+
replacements.set(opener, '“');
|
|
281
|
+
replacements.set(i, '”');
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (leftFlanking) {
|
|
285
|
+
doubleStack.push(i);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
replacements.set(i, '“');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
for (const idx of doubleStack) {
|
|
292
|
+
replacements.set(idx, '“');
|
|
293
|
+
}
|
|
294
|
+
for (const idx of singleStack) {
|
|
295
|
+
replacements.set(idx, '’');
|
|
296
|
+
}
|
|
297
|
+
const renderTextNodes = new Map();
|
|
298
|
+
for (const [node, indices] of nodeTokens.entries()) {
|
|
299
|
+
if (node.noSmart) {
|
|
300
|
+
renderTextNodes.set(node, (0, utils_js_1.escapeHtml)(node.literal));
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
let result = '';
|
|
304
|
+
for (let i = 0; i < indices.length; i++) {
|
|
305
|
+
const tokenIndex = indices[i];
|
|
306
|
+
const token = tokens[tokenIndex];
|
|
307
|
+
const replacement = replacements.get(tokenIndex);
|
|
308
|
+
result += replacement ?? token.char;
|
|
309
|
+
}
|
|
310
|
+
result = this.applyDashesAndEllipses(result);
|
|
311
|
+
renderTextNodes.set(node, (0, utils_js_1.escapeHtml)(result));
|
|
312
|
+
}
|
|
313
|
+
return inlines.map(inline => {
|
|
314
|
+
if (inline.type === 'text') {
|
|
315
|
+
return renderTextNodes.get(inline) ?? '';
|
|
316
|
+
}
|
|
317
|
+
return this.renderInline(inline);
|
|
318
|
+
}).join('');
|
|
319
|
+
}
|
|
320
|
+
applyDashesAndEllipses(text) {
|
|
321
|
+
let result = text.replace(/\.{3}/g, '…');
|
|
322
|
+
result = result.replace(/-{2,}/g, (match) => {
|
|
323
|
+
const count = match.length;
|
|
324
|
+
if (count % 3 === 0) {
|
|
325
|
+
return '—'.repeat(count / 3);
|
|
326
|
+
}
|
|
327
|
+
if (count % 2 === 0) {
|
|
328
|
+
return '–'.repeat(count / 2);
|
|
329
|
+
}
|
|
330
|
+
let emCount = Math.floor(count / 3);
|
|
331
|
+
const remainder = count % 3;
|
|
332
|
+
let enCount = 0;
|
|
333
|
+
if (remainder === 1) {
|
|
334
|
+
emCount = Math.max(0, emCount - 1);
|
|
335
|
+
enCount = 2;
|
|
336
|
+
}
|
|
337
|
+
else if (remainder === 2) {
|
|
338
|
+
enCount = 1;
|
|
339
|
+
}
|
|
340
|
+
return '—'.repeat(emCount) + '–'.repeat(enCount);
|
|
341
|
+
});
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
exports.HtmlRenderer = HtmlRenderer;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the Markdown AST nodes and parser/render options.
|
|
3
|
+
*/
|
|
4
|
+
export type BlockType = 'document' | 'paragraph' | 'heading' | 'thematic_break' | 'code_block' | 'blockquote' | 'list' | 'list_item' | 'html_block' | 'link_reference_definition' | 'table';
|
|
5
|
+
export type InlineType = 'text' | 'softbreak' | 'hardbreak' | 'code_span' | 'emphasis' | 'strong' | 'strikethrough' | 'footnote_ref' | 'link' | 'image' | 'html_inline';
|
|
6
|
+
export interface Position {
|
|
7
|
+
line: number;
|
|
8
|
+
column: number;
|
|
9
|
+
offset: number;
|
|
10
|
+
}
|
|
11
|
+
export interface SourceLocation {
|
|
12
|
+
start: Position;
|
|
13
|
+
end: Position;
|
|
14
|
+
}
|
|
15
|
+
export interface BaseNode {
|
|
16
|
+
type: string;
|
|
17
|
+
sourcepos?: SourceLocation;
|
|
18
|
+
}
|
|
19
|
+
export interface DocumentNode extends BaseNode {
|
|
20
|
+
type: 'document';
|
|
21
|
+
children: BlockNode[];
|
|
22
|
+
footnoteDefinitions?: Map<string, FootnoteDefinition>;
|
|
23
|
+
}
|
|
24
|
+
export interface ParagraphNode extends BaseNode {
|
|
25
|
+
type: 'paragraph';
|
|
26
|
+
children: InlineNode[];
|
|
27
|
+
}
|
|
28
|
+
export interface HeadingNode extends BaseNode {
|
|
29
|
+
type: 'heading';
|
|
30
|
+
level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
31
|
+
children: InlineNode[];
|
|
32
|
+
}
|
|
33
|
+
export interface ThematicBreakNode extends BaseNode {
|
|
34
|
+
type: 'thematic_break';
|
|
35
|
+
}
|
|
36
|
+
export interface CodeBlockNode extends BaseNode {
|
|
37
|
+
type: 'code_block';
|
|
38
|
+
info: string;
|
|
39
|
+
literal: string;
|
|
40
|
+
fenced: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface BlockquoteNode extends BaseNode {
|
|
43
|
+
type: 'blockquote';
|
|
44
|
+
children: BlockNode[];
|
|
45
|
+
}
|
|
46
|
+
export interface ListNode extends BaseNode {
|
|
47
|
+
type: 'list';
|
|
48
|
+
listType: 'bullet' | 'ordered';
|
|
49
|
+
start: number;
|
|
50
|
+
tight: boolean;
|
|
51
|
+
delimiter: '.' | ')' | null;
|
|
52
|
+
bulletChar: '-' | '+' | '*' | null;
|
|
53
|
+
children: ListItemNode[];
|
|
54
|
+
}
|
|
55
|
+
export interface ListItemNode extends BaseNode {
|
|
56
|
+
type: 'list_item';
|
|
57
|
+
children: BlockNode[];
|
|
58
|
+
checked?: boolean | null;
|
|
59
|
+
}
|
|
60
|
+
export interface HtmlBlockNode extends BaseNode {
|
|
61
|
+
type: 'html_block';
|
|
62
|
+
literal: string;
|
|
63
|
+
}
|
|
64
|
+
export type TableAlignment = 'left' | 'center' | 'right' | null;
|
|
65
|
+
export interface TableCellNode extends BaseNode {
|
|
66
|
+
type: 'table_cell';
|
|
67
|
+
children: InlineNode[];
|
|
68
|
+
align: TableAlignment;
|
|
69
|
+
isHeader: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface TableRowNode extends BaseNode {
|
|
72
|
+
type: 'table_row';
|
|
73
|
+
children: TableCellNode[];
|
|
74
|
+
isHeader: boolean;
|
|
75
|
+
}
|
|
76
|
+
export interface TableNode extends BaseNode {
|
|
77
|
+
type: 'table';
|
|
78
|
+
alignments: TableAlignment[];
|
|
79
|
+
children: TableRowNode[];
|
|
80
|
+
}
|
|
81
|
+
export interface LinkReferenceDefinition {
|
|
82
|
+
destination: string;
|
|
83
|
+
title: string;
|
|
84
|
+
}
|
|
85
|
+
export type BlockNode = ParagraphNode | HeadingNode | ThematicBreakNode | CodeBlockNode | BlockquoteNode | ListNode | ListItemNode | HtmlBlockNode | TableNode;
|
|
86
|
+
export interface TextNode extends BaseNode {
|
|
87
|
+
type: 'text';
|
|
88
|
+
literal: string;
|
|
89
|
+
noDelim?: boolean;
|
|
90
|
+
noSmart?: boolean;
|
|
91
|
+
}
|
|
92
|
+
export interface SoftbreakNode extends BaseNode {
|
|
93
|
+
type: 'softbreak';
|
|
94
|
+
}
|
|
95
|
+
export interface HardbreakNode extends BaseNode {
|
|
96
|
+
type: 'hardbreak';
|
|
97
|
+
}
|
|
98
|
+
export interface CodeSpanNode extends BaseNode {
|
|
99
|
+
type: 'code_span';
|
|
100
|
+
literal: string;
|
|
101
|
+
}
|
|
102
|
+
export interface EmphasisNode extends BaseNode {
|
|
103
|
+
type: 'emphasis';
|
|
104
|
+
children: InlineNode[];
|
|
105
|
+
}
|
|
106
|
+
export interface StrongNode extends BaseNode {
|
|
107
|
+
type: 'strong';
|
|
108
|
+
children: InlineNode[];
|
|
109
|
+
}
|
|
110
|
+
export interface StrikethroughNode extends BaseNode {
|
|
111
|
+
type: 'strikethrough';
|
|
112
|
+
children: InlineNode[];
|
|
113
|
+
}
|
|
114
|
+
export interface FootnoteRefNode extends BaseNode {
|
|
115
|
+
type: 'footnote_ref';
|
|
116
|
+
label: string;
|
|
117
|
+
key: string;
|
|
118
|
+
}
|
|
119
|
+
export interface LinkNode extends BaseNode {
|
|
120
|
+
type: 'link';
|
|
121
|
+
destination: string;
|
|
122
|
+
title: string;
|
|
123
|
+
children: InlineNode[];
|
|
124
|
+
}
|
|
125
|
+
export interface ImageNode extends BaseNode {
|
|
126
|
+
type: 'image';
|
|
127
|
+
destination: string;
|
|
128
|
+
title: string;
|
|
129
|
+
alt: string;
|
|
130
|
+
}
|
|
131
|
+
export interface HtmlInlineNode extends BaseNode {
|
|
132
|
+
type: 'html_inline';
|
|
133
|
+
literal: string;
|
|
134
|
+
}
|
|
135
|
+
export type InlineNode = TextNode | SoftbreakNode | HardbreakNode | CodeSpanNode | EmphasisNode | StrongNode | StrikethroughNode | FootnoteRefNode | LinkNode | ImageNode | HtmlInlineNode;
|
|
136
|
+
export interface FootnoteDefinition {
|
|
137
|
+
label: string;
|
|
138
|
+
blocks: BlockNode[];
|
|
139
|
+
}
|
|
140
|
+
export interface ParseOptions {
|
|
141
|
+
sourcepos?: boolean;
|
|
142
|
+
extensions?: string[];
|
|
143
|
+
}
|
|
144
|
+
export interface RenderOptions {
|
|
145
|
+
softbreak?: string;
|
|
146
|
+
safe?: boolean;
|
|
147
|
+
tablePreferStyleAttributes?: boolean;
|
|
148
|
+
fullInfoString?: boolean;
|
|
149
|
+
smart?: boolean;
|
|
150
|
+
tagfilter?: boolean;
|
|
151
|
+
extensions?: string[];
|
|
152
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared text-processing utilities for the parser, including entity decoding, escaping,
|
|
3
|
+
* whitespace handling, and URI normalization.
|
|
4
|
+
*/
|
|
5
|
+
export declare function decodeHtmlEntities(str: string): string;
|
|
6
|
+
export declare function escapeXml(str: string): string;
|
|
7
|
+
export declare function escapeHtml(str: string, preserveEntities?: boolean): string;
|
|
8
|
+
export declare const ASCII_PUNCTUATION_REGEX: RegExp;
|
|
9
|
+
export declare const ASCII_PUNCTUATION_CHARS = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
|
10
|
+
export declare function isAsciiPunctuation(char: string): boolean;
|
|
11
|
+
export declare function isWhitespace(char: string): boolean;
|
|
12
|
+
export declare function isUnicodeWhitespace(char: string): boolean;
|
|
13
|
+
export declare function isUnicodePunctuation(char: string): boolean;
|
|
14
|
+
export declare function normalizeLineEndings(str: string): string;
|
|
15
|
+
export declare function expandTabs(line: string, tabStop?: number): string;
|
|
16
|
+
export declare function expandTabsPartial(line: string, startColumn: number, tabStop?: number): string;
|
|
17
|
+
export declare function trimLeadingSpaces(line: string, max?: number): {
|
|
18
|
+
trimmed: string;
|
|
19
|
+
count: number;
|
|
20
|
+
};
|
|
21
|
+
export declare function countLeadingSpaces(line: string): number;
|
|
22
|
+
export declare function countLeadingChars(line: string): {
|
|
23
|
+
spaces: number;
|
|
24
|
+
chars: number;
|
|
25
|
+
};
|
|
26
|
+
export declare function removeIndent(line: string, indent: number): string;
|
|
27
|
+
export declare function extractContentAfterMarker(line: string, startColumn: number, charIndex: number): string;
|
|
28
|
+
export declare function normalizeUri(uri: string): string;
|
|
29
|
+
export declare function unescapeString(str: string): string;
|
|
30
|
+
export declare function normalizeLabel(label: string): string;
|
|
31
|
+
export declare function isTagFilterTag(tagName: string): boolean;
|
|
32
|
+
export declare function applyTagFilter(html: string): string;
|