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.
Files changed (199) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/dist/cjs/cli.js +36 -0
  4. package/dist/cjs/gfm.js +26 -0
  5. package/dist/cjs/index.js +26 -0
  6. package/dist/cjs/parser/block-parser.js +644 -0
  7. package/dist/cjs/parser/blocks/blockquote.js +28 -0
  8. package/dist/cjs/parser/blocks/code-block-fenced.js +58 -0
  9. package/dist/cjs/parser/blocks/code-block-indented.js +47 -0
  10. package/dist/cjs/parser/blocks/heading-atx.js +29 -0
  11. package/dist/cjs/parser/blocks/heading-setext.js +24 -0
  12. package/dist/cjs/parser/blocks/html-block.js +83 -0
  13. package/dist/cjs/parser/blocks/link-reference.js +109 -0
  14. package/dist/cjs/parser/blocks/list.js +155 -0
  15. package/dist/cjs/parser/blocks/paragraph.js +20 -0
  16. package/dist/cjs/parser/blocks/table.js +163 -0
  17. package/dist/cjs/parser/blocks/task-list.js +66 -0
  18. package/dist/cjs/parser/blocks/thematic-break.js +17 -0
  19. package/dist/cjs/parser/entities.js +2133 -0
  20. package/dist/cjs/parser/gfm/block-parser.js +773 -0
  21. package/dist/cjs/parser/gfm/index.js +125 -0
  22. package/dist/cjs/parser/gfm/inline-parser.js +813 -0
  23. package/dist/cjs/parser/gfm/renderer.js +513 -0
  24. package/dist/cjs/parser/index.js +104 -0
  25. package/dist/cjs/parser/inline-parser.js +564 -0
  26. package/dist/cjs/parser/inlines/autolink-extended.js +364 -0
  27. package/dist/cjs/parser/inlines/autolink.js +44 -0
  28. package/dist/cjs/parser/inlines/code-span.js +48 -0
  29. package/dist/cjs/parser/inlines/emphasis.js +64 -0
  30. package/dist/cjs/parser/inlines/entity.js +25 -0
  31. package/dist/cjs/parser/inlines/escape.js +25 -0
  32. package/dist/cjs/parser/inlines/footnote.js +41 -0
  33. package/dist/cjs/parser/inlines/hard-break.js +45 -0
  34. package/dist/cjs/parser/inlines/image.js +9 -0
  35. package/dist/cjs/parser/inlines/link.js +166 -0
  36. package/dist/cjs/parser/inlines/soft-break.js +18 -0
  37. package/dist/cjs/parser/inlines/strikethrough.js +48 -0
  38. package/dist/cjs/parser/inlines/text.js +20 -0
  39. package/dist/cjs/parser/renderer.js +345 -0
  40. package/dist/cjs/parser/types.js +5 -0
  41. package/dist/cjs/parser/utils.js +277 -0
  42. package/dist/cli.d.ts +6 -0
  43. package/dist/cli.js +36 -0
  44. package/dist/esm/cli.js +34 -0
  45. package/dist/esm/gfm.js +5 -0
  46. package/dist/esm/index.js +5 -0
  47. package/dist/esm/package.json +3 -0
  48. package/dist/esm/parser/block-parser.js +640 -0
  49. package/dist/esm/parser/blocks/blockquote.js +22 -0
  50. package/dist/esm/parser/blocks/code-block-fenced.js +52 -0
  51. package/dist/esm/parser/blocks/code-block-indented.js +42 -0
  52. package/dist/esm/parser/blocks/heading-atx.js +24 -0
  53. package/dist/esm/parser/blocks/heading-setext.js +19 -0
  54. package/dist/esm/parser/blocks/html-block.js +77 -0
  55. package/dist/esm/parser/blocks/link-reference.js +105 -0
  56. package/dist/esm/parser/blocks/list.js +145 -0
  57. package/dist/esm/parser/blocks/paragraph.js +15 -0
  58. package/dist/esm/parser/blocks/table.js +152 -0
  59. package/dist/esm/parser/blocks/task-list.js +61 -0
  60. package/dist/esm/parser/blocks/thematic-break.js +13 -0
  61. package/dist/esm/parser/entities.js +2130 -0
  62. package/dist/esm/parser/gfm/block-parser.js +769 -0
  63. package/dist/esm/parser/gfm/index.js +115 -0
  64. package/dist/esm/parser/gfm/inline-parser.js +809 -0
  65. package/dist/esm/parser/gfm/renderer.js +509 -0
  66. package/dist/esm/parser/index.js +80 -0
  67. package/dist/esm/parser/inline-parser.js +560 -0
  68. package/dist/esm/parser/inlines/autolink-extended.js +357 -0
  69. package/dist/esm/parser/inlines/autolink.js +40 -0
  70. package/dist/esm/parser/inlines/code-span.js +44 -0
  71. package/dist/esm/parser/inlines/emphasis.js +59 -0
  72. package/dist/esm/parser/inlines/entity.js +21 -0
  73. package/dist/esm/parser/inlines/escape.js +21 -0
  74. package/dist/esm/parser/inlines/footnote.js +38 -0
  75. package/dist/esm/parser/inlines/hard-break.js +41 -0
  76. package/dist/esm/parser/inlines/image.js +4 -0
  77. package/dist/esm/parser/inlines/link.js +156 -0
  78. package/dist/esm/parser/inlines/soft-break.js +14 -0
  79. package/dist/esm/parser/inlines/strikethrough.js +42 -0
  80. package/dist/esm/parser/inlines/text.js +16 -0
  81. package/dist/esm/parser/renderer.js +341 -0
  82. package/dist/esm/parser/types.js +4 -0
  83. package/dist/esm/parser/utils.js +254 -0
  84. package/dist/gfm.d.ts +6 -0
  85. package/dist/gfm.js +26 -0
  86. package/dist/index.d.ts +5 -0
  87. package/dist/index.js +26 -0
  88. package/dist/parser/block-parser.d.ts +25 -0
  89. package/dist/parser/block-parser.js +644 -0
  90. package/dist/parser/blocks/blockquote.d.ts +8 -0
  91. package/dist/parser/blocks/blockquote.js +28 -0
  92. package/dist/parser/blocks/code-block-fenced.d.ts +14 -0
  93. package/dist/parser/blocks/code-block-fenced.js +58 -0
  94. package/dist/parser/blocks/code-block-indented.d.ts +7 -0
  95. package/dist/parser/blocks/code-block-indented.js +47 -0
  96. package/dist/parser/blocks/heading-atx.d.ts +10 -0
  97. package/dist/parser/blocks/heading-atx.js +29 -0
  98. package/dist/parser/blocks/heading-setext.d.ts +8 -0
  99. package/dist/parser/blocks/heading-setext.js +24 -0
  100. package/dist/parser/blocks/html-block.d.ts +9 -0
  101. package/dist/parser/blocks/html-block.js +83 -0
  102. package/dist/parser/blocks/link-reference.d.ts +11 -0
  103. package/dist/parser/blocks/link-reference.js +109 -0
  104. package/dist/parser/blocks/list.d.ts +25 -0
  105. package/dist/parser/blocks/list.js +155 -0
  106. package/dist/parser/blocks/paragraph.d.ts +7 -0
  107. package/dist/parser/blocks/paragraph.js +20 -0
  108. package/dist/parser/blocks/table.d.ts +13 -0
  109. package/dist/parser/blocks/table.js +163 -0
  110. package/dist/parser/blocks/task-list.d.ts +10 -0
  111. package/dist/parser/blocks/task-list.js +66 -0
  112. package/dist/parser/blocks/thematic-break.d.ts +6 -0
  113. package/dist/parser/blocks/thematic-break.js +17 -0
  114. package/dist/parser/entities.d.ts +4 -0
  115. package/dist/parser/entities.js +2133 -0
  116. package/dist/parser/gfm/block-parser.d.ts +32 -0
  117. package/dist/parser/gfm/block-parser.js +773 -0
  118. package/dist/parser/gfm/index.d.ts +31 -0
  119. package/dist/parser/gfm/index.js +125 -0
  120. package/dist/parser/gfm/inline-parser.d.ts +25 -0
  121. package/dist/parser/gfm/inline-parser.js +813 -0
  122. package/dist/parser/gfm/renderer.d.ts +43 -0
  123. package/dist/parser/gfm/renderer.js +513 -0
  124. package/dist/parser/index.d.ts +33 -0
  125. package/dist/parser/index.js +104 -0
  126. package/dist/parser/inline-parser.d.ts +16 -0
  127. package/dist/parser/inline-parser.js +564 -0
  128. package/dist/parser/inlines/autolink-extended.d.ts +24 -0
  129. package/dist/parser/inlines/autolink-extended.js +364 -0
  130. package/dist/parser/inlines/autolink.d.ts +9 -0
  131. package/dist/parser/inlines/autolink.js +44 -0
  132. package/dist/parser/inlines/code-span.d.ts +9 -0
  133. package/dist/parser/inlines/code-span.js +48 -0
  134. package/dist/parser/inlines/emphasis.d.ts +14 -0
  135. package/dist/parser/inlines/emphasis.js +64 -0
  136. package/dist/parser/inlines/entity.d.ts +8 -0
  137. package/dist/parser/inlines/entity.js +25 -0
  138. package/dist/parser/inlines/escape.d.ts +8 -0
  139. package/dist/parser/inlines/escape.js +25 -0
  140. package/dist/parser/inlines/footnote.d.ts +9 -0
  141. package/dist/parser/inlines/footnote.js +41 -0
  142. package/dist/parser/inlines/hard-break.d.ts +9 -0
  143. package/dist/parser/inlines/hard-break.js +45 -0
  144. package/dist/parser/inlines/image.d.ts +4 -0
  145. package/dist/parser/inlines/image.js +9 -0
  146. package/dist/parser/inlines/link.d.ts +33 -0
  147. package/dist/parser/inlines/link.js +166 -0
  148. package/dist/parser/inlines/soft-break.d.ts +9 -0
  149. package/dist/parser/inlines/soft-break.js +18 -0
  150. package/dist/parser/inlines/strikethrough.d.ts +16 -0
  151. package/dist/parser/inlines/strikethrough.js +48 -0
  152. package/dist/parser/inlines/text.d.ts +6 -0
  153. package/dist/parser/inlines/text.js +20 -0
  154. package/dist/parser/renderer.d.ts +33 -0
  155. package/dist/parser/renderer.js +345 -0
  156. package/dist/parser/types.d.ts +152 -0
  157. package/dist/parser/types.js +5 -0
  158. package/dist/parser/utils.d.ts +32 -0
  159. package/dist/parser/utils.js +277 -0
  160. package/dist/types/cli.d.ts +6 -0
  161. package/dist/types/gfm.d.ts +6 -0
  162. package/dist/types/index.d.ts +5 -0
  163. package/dist/types/parser/block-parser.d.ts +25 -0
  164. package/dist/types/parser/blocks/blockquote.d.ts +8 -0
  165. package/dist/types/parser/blocks/code-block-fenced.d.ts +14 -0
  166. package/dist/types/parser/blocks/code-block-indented.d.ts +7 -0
  167. package/dist/types/parser/blocks/heading-atx.d.ts +10 -0
  168. package/dist/types/parser/blocks/heading-setext.d.ts +8 -0
  169. package/dist/types/parser/blocks/html-block.d.ts +9 -0
  170. package/dist/types/parser/blocks/link-reference.d.ts +11 -0
  171. package/dist/types/parser/blocks/list.d.ts +25 -0
  172. package/dist/types/parser/blocks/paragraph.d.ts +7 -0
  173. package/dist/types/parser/blocks/table.d.ts +13 -0
  174. package/dist/types/parser/blocks/task-list.d.ts +10 -0
  175. package/dist/types/parser/blocks/thematic-break.d.ts +6 -0
  176. package/dist/types/parser/entities.d.ts +4 -0
  177. package/dist/types/parser/gfm/block-parser.d.ts +32 -0
  178. package/dist/types/parser/gfm/index.d.ts +31 -0
  179. package/dist/types/parser/gfm/inline-parser.d.ts +25 -0
  180. package/dist/types/parser/gfm/renderer.d.ts +43 -0
  181. package/dist/types/parser/index.d.ts +33 -0
  182. package/dist/types/parser/inline-parser.d.ts +16 -0
  183. package/dist/types/parser/inlines/autolink-extended.d.ts +24 -0
  184. package/dist/types/parser/inlines/autolink.d.ts +9 -0
  185. package/dist/types/parser/inlines/code-span.d.ts +9 -0
  186. package/dist/types/parser/inlines/emphasis.d.ts +14 -0
  187. package/dist/types/parser/inlines/entity.d.ts +8 -0
  188. package/dist/types/parser/inlines/escape.d.ts +8 -0
  189. package/dist/types/parser/inlines/footnote.d.ts +9 -0
  190. package/dist/types/parser/inlines/hard-break.d.ts +9 -0
  191. package/dist/types/parser/inlines/image.d.ts +4 -0
  192. package/dist/types/parser/inlines/link.d.ts +33 -0
  193. package/dist/types/parser/inlines/soft-break.d.ts +9 -0
  194. package/dist/types/parser/inlines/strikethrough.d.ts +16 -0
  195. package/dist/types/parser/inlines/text.d.ts +6 -0
  196. package/dist/types/parser/renderer.d.ts +33 -0
  197. package/dist/types/parser/types.d.ts +152 -0
  198. package/dist/types/parser/utils.d.ts +32 -0
  199. package/package.json +54 -0
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ /**
3
+ * Parser for inline links and images, including destinations, titles, and reference labels.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseLinkDestination = parseLinkDestination;
7
+ exports.parseLinkTitle = parseLinkTitle;
8
+ exports.parseInlineLink = parseInlineLink;
9
+ exports.parseLinkLabel = parseLinkLabel;
10
+ exports.createLinkNode = createLinkNode;
11
+ exports.createImageNode = createImageNode;
12
+ exports.isLinkStart = isLinkStart;
13
+ exports.isImageStart = isImageStart;
14
+ const utils_js_1 = require("../utils.js");
15
+ const LINK_DESTINATION_ANGLE_REGEX = /^<([^<>\n\\]|\\.)*>/;
16
+ const LINK_DESTINATION_BARE_REGEX = /^[^\s\x00-\x1f]*?(?:\([^\s\x00-\x1f]*?\)[^\s\x00-\x1f]*?)*[^\s\x00-\x1f)]/;
17
+ const LINK_TITLE_REGEX = /^(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|\(([^()\\]*(?:\\.[^()\\]*)*)\))/;
18
+ function parseLinkDestination(text) {
19
+ if (text[0] === '<') {
20
+ const match = text.match(LINK_DESTINATION_ANGLE_REGEX);
21
+ if (match) {
22
+ return {
23
+ destination: (0, utils_js_1.decodeHtmlEntities)((0, utils_js_1.unescapeString)(match[0].slice(1, -1))),
24
+ length: match[0].length,
25
+ };
26
+ }
27
+ return null;
28
+ }
29
+ let parenDepth = 0;
30
+ let i = 0;
31
+ while (i < text.length) {
32
+ const char = text[i];
33
+ if (char === '\\' && i + 1 < text.length) {
34
+ const nextChar = text[i + 1];
35
+ if (nextChar === ' ' || nextChar === '\t' || nextChar === '\n' || nextChar === '\r') {
36
+ return null;
37
+ }
38
+ i += 2;
39
+ continue;
40
+ }
41
+ if (char === '(') {
42
+ parenDepth++;
43
+ }
44
+ else if (char === ')') {
45
+ if (parenDepth === 0)
46
+ break;
47
+ parenDepth--;
48
+ }
49
+ else if (char === ' ' || char === '\t' || char === '\n' || char === '\r' || char.charCodeAt(0) < 0x20) {
50
+ break;
51
+ }
52
+ i++;
53
+ }
54
+ if (i === 0)
55
+ return null;
56
+ return {
57
+ destination: (0, utils_js_1.decodeHtmlEntities)((0, utils_js_1.unescapeString)(text.slice(0, i))),
58
+ length: i,
59
+ };
60
+ }
61
+ function parseLinkTitle(text) {
62
+ const match = text.match(LINK_TITLE_REGEX);
63
+ if (!match)
64
+ return null;
65
+ const title = match[1] ?? match[2] ?? match[3] ?? '';
66
+ return {
67
+ title: (0, utils_js_1.decodeHtmlEntities)((0, utils_js_1.unescapeString)(title)),
68
+ length: match[0].length,
69
+ };
70
+ }
71
+ function parseInlineLink(text, pos) {
72
+ if (text[pos] !== '(')
73
+ return null;
74
+ let i = pos + 1;
75
+ while (i < text.length && /[ \t\n]/.test(text[i]))
76
+ i++;
77
+ if (text[i] === ')') {
78
+ return { destination: '', title: '', length: i - pos + 1 };
79
+ }
80
+ const destResult = parseLinkDestination(text.slice(i));
81
+ if (!destResult)
82
+ return null;
83
+ const destination = destResult.destination;
84
+ i += destResult.length;
85
+ while (i < text.length && /[ \t]/.test(text[i]))
86
+ i++;
87
+ let title = '';
88
+ if (text[i] === '\n') {
89
+ i++;
90
+ while (i < text.length && /[ \t]/.test(text[i]))
91
+ i++;
92
+ }
93
+ if (text[i] === '"' || text[i] === "'" || text[i] === '(') {
94
+ const titleResult = parseLinkTitle(text.slice(i));
95
+ if (titleResult) {
96
+ title = titleResult.title;
97
+ i += titleResult.length;
98
+ }
99
+ }
100
+ while (i < text.length && /[ \t\n]/.test(text[i]))
101
+ i++;
102
+ if (text[i] !== ')')
103
+ return null;
104
+ return { destination, title, length: i - pos + 1 };
105
+ }
106
+ function parseLinkLabel(text, pos) {
107
+ if (text[pos] !== '[')
108
+ return null;
109
+ let i = pos + 1;
110
+ let depth = 1;
111
+ let label = '';
112
+ while (i < text.length && depth > 0) {
113
+ const char = text[i];
114
+ if (char === '\\' && i + 1 < text.length) {
115
+ const next = text[i + 1];
116
+ if (next === '[' || next === ']' || next === '\\') {
117
+ label += next;
118
+ i += 2;
119
+ continue;
120
+ }
121
+ label += '\\';
122
+ i++;
123
+ continue;
124
+ }
125
+ if (char === '[') {
126
+ return null;
127
+ }
128
+ if (char === ']') {
129
+ depth--;
130
+ if (depth === 0)
131
+ break;
132
+ }
133
+ label += char;
134
+ i++;
135
+ }
136
+ if (depth !== 0)
137
+ return null;
138
+ if (label.length > 999)
139
+ return null;
140
+ if (label.trim() === '') {
141
+ return { label: '', length: i - pos + 1 };
142
+ }
143
+ return { label: (0, utils_js_1.normalizeLabel)(label), length: i - pos + 1 };
144
+ }
145
+ function createLinkNode(destination, title) {
146
+ return {
147
+ type: 'link',
148
+ destination,
149
+ title,
150
+ children: [],
151
+ };
152
+ }
153
+ function createImageNode(destination, title, alt) {
154
+ return {
155
+ type: 'image',
156
+ destination,
157
+ title,
158
+ alt,
159
+ };
160
+ }
161
+ function isLinkStart(text, pos) {
162
+ return text[pos] === '[';
163
+ }
164
+ function isImageStart(text, pos) {
165
+ return text[pos] === '!' && text[pos + 1] === '[';
166
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Parser for soft line breaks created by single newlines.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseSoftBreak = parseSoftBreak;
7
+ exports.isSoftBreak = isSoftBreak;
8
+ function parseSoftBreak(text, pos) {
9
+ if (text[pos] !== '\n')
10
+ return null;
11
+ return {
12
+ node: { type: 'softbreak' },
13
+ length: 1,
14
+ };
15
+ }
16
+ function isSoftBreak(text, pos) {
17
+ return text[pos] === '\n';
18
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * Parser for GFM strikethrough using ~ delimiters.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseStrikethroughDelimiter = parseStrikethroughDelimiter;
7
+ exports.isStrikethroughDelimiter = isStrikethroughDelimiter;
8
+ exports.createStrikethroughNode = createStrikethroughNode;
9
+ exports.canStrikethroughDelimitersMatch = canStrikethroughDelimitersMatch;
10
+ function parseStrikethroughDelimiter(text, pos) {
11
+ if (text[pos] !== '~')
12
+ return null;
13
+ let length = 0;
14
+ let i = pos;
15
+ while (i < text.length && text[i] === '~') {
16
+ length++;
17
+ i++;
18
+ }
19
+ if (length > 2)
20
+ return null;
21
+ const charBefore = pos > 0 ? text[pos - 1] : '\n';
22
+ const charAfter = i < text.length ? text[i] : '\n';
23
+ const isWhitespace = (c) => /\s/.test(c) || c === '\n';
24
+ const beforeIsWhitespace = isWhitespace(charBefore);
25
+ const afterIsWhitespace = isWhitespace(charAfter);
26
+ const canOpen = !afterIsWhitespace;
27
+ const canClose = !beforeIsWhitespace;
28
+ return {
29
+ char: '~',
30
+ length,
31
+ canOpen,
32
+ canClose,
33
+ position: pos,
34
+ origLength: length,
35
+ };
36
+ }
37
+ function isStrikethroughDelimiter(text, pos) {
38
+ return text[pos] === '~';
39
+ }
40
+ function createStrikethroughNode(children = []) {
41
+ return {
42
+ type: 'strikethrough',
43
+ children,
44
+ };
45
+ }
46
+ function canStrikethroughDelimitersMatch(opener, closer) {
47
+ return opener.length === closer.length;
48
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * Text node factories for literal inline content.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createTextNode = createTextNode;
7
+ exports.mergeTextNodes = mergeTextNodes;
8
+ function createTextNode(literal, noDelim = false) {
9
+ return {
10
+ type: 'text',
11
+ literal,
12
+ ...(noDelim ? { noDelim: true } : {}),
13
+ };
14
+ }
15
+ function mergeTextNodes(nodes) {
16
+ return {
17
+ type: 'text',
18
+ literal: nodes.map(n => n.literal).join(''),
19
+ };
20
+ }
@@ -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,5 @@
1
+ "use strict";
2
+ /**
3
+ * Type definitions for the Markdown AST nodes and parser/render options.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });