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,809 @@
1
+ /**
2
+ * GFM inline parser that extends CommonMark with strikethrough and extended autolinks.
3
+ */
4
+ import { parseEscape } from '../inlines/escape.js';
5
+ import { parseEntity } from '../inlines/entity.js';
6
+ import { parseCodeSpan } from '../inlines/code-span.js';
7
+ import { parseDelimiterRun, canDelimitersMatch } from '../inlines/emphasis.js';
8
+ import { parseAutolink } from '../inlines/autolink.js';
9
+ import { parseInlineLink, parseLinkLabel, createLinkNode, createImageNode } from '../inlines/link.js';
10
+ import { createTextNode } from '../inlines/text.js';
11
+ import { normalizeLabel } from '../utils.js';
12
+ import { isTagFilterTag } from '../utils.js';
13
+ import { parseStrikethroughDelimiter, canStrikethroughDelimitersMatch, createStrikethroughNode, } from '../inlines/strikethrough.js';
14
+ import { parseExtendedAutolink } from '../inlines/autolink-extended.js';
15
+ import { parseFootnoteLabel } from '../inlines/footnote.js';
16
+ export class GFMInlineParser {
17
+ linkReferences = new Map();
18
+ footnoteDefinitions = new Map();
19
+ enableAutolink;
20
+ enableStrikethrough;
21
+ enableFootnotes;
22
+ enableTagfilter;
23
+ constructor(extensions) {
24
+ this.enableAutolink = !extensions || extensions.has('autolink');
25
+ this.enableStrikethrough = !extensions || extensions.has('strikethrough');
26
+ this.enableFootnotes = !extensions || extensions.has('footnotes');
27
+ this.enableTagfilter = !extensions || extensions.has('tagfilter');
28
+ }
29
+ setLinkReferences(refs) {
30
+ this.linkReferences = refs;
31
+ }
32
+ setFootnoteDefinitions(defs) {
33
+ this.footnoteDefinitions = defs;
34
+ }
35
+ parse(text) {
36
+ const nodes = [];
37
+ let delimiterStack = null;
38
+ let bracketStack = null;
39
+ let pos = 0;
40
+ let textBuffer = '';
41
+ const flushText = () => {
42
+ if (textBuffer.length > 0) {
43
+ nodes.push(createTextNode(textBuffer));
44
+ textBuffer = '';
45
+ }
46
+ };
47
+ const pushDelimiter = (run, node) => {
48
+ const d = {
49
+ run,
50
+ node,
51
+ active: true,
52
+ prev: delimiterStack,
53
+ next: null,
54
+ };
55
+ if (delimiterStack) {
56
+ delimiterStack.next = d;
57
+ }
58
+ delimiterStack = d;
59
+ };
60
+ while (pos < text.length) {
61
+ const char = text[pos];
62
+ const charBefore = pos > 0 ? text[pos - 1] : undefined;
63
+ if (this.enableAutolink) {
64
+ const extendedAutolink = parseExtendedAutolink(text, pos, charBefore);
65
+ if (extendedAutolink) {
66
+ flushText();
67
+ nodes.push(extendedAutolink.node);
68
+ pos += extendedAutolink.length;
69
+ continue;
70
+ }
71
+ }
72
+ const escapeResult = parseEscape(text, pos);
73
+ if (escapeResult) {
74
+ if (escapeResult.char === '\n') {
75
+ textBuffer = textBuffer.replace(/ *$/, '');
76
+ flushText();
77
+ nodes.push({ type: 'hardbreak' });
78
+ pos += escapeResult.length;
79
+ while (pos < text.length && text[pos] === ' ') {
80
+ pos++;
81
+ }
82
+ }
83
+ else if (escapeResult.char === '*' || escapeResult.char === '_' || escapeResult.char === '~') {
84
+ flushText();
85
+ nodes.push(createTextNode(escapeResult.char, true));
86
+ pos += escapeResult.length;
87
+ }
88
+ else if (escapeResult.char === '"' || escapeResult.char === "'" ||
89
+ escapeResult.char === '-' || escapeResult.char === '.') {
90
+ flushText();
91
+ const node = createTextNode(escapeResult.char);
92
+ node.noSmart = true;
93
+ nodes.push(node);
94
+ pos += escapeResult.length;
95
+ }
96
+ else {
97
+ textBuffer += escapeResult.char;
98
+ pos += escapeResult.length;
99
+ }
100
+ continue;
101
+ }
102
+ const entityResult = parseEntity(text, pos);
103
+ if (entityResult) {
104
+ textBuffer += entityResult.char;
105
+ pos += entityResult.length;
106
+ continue;
107
+ }
108
+ if (char === '`') {
109
+ const codeSpanResult = parseCodeSpan(text, pos);
110
+ if (codeSpanResult) {
111
+ flushText();
112
+ nodes.push(codeSpanResult.node);
113
+ pos += codeSpanResult.length;
114
+ continue;
115
+ }
116
+ else {
117
+ textBuffer += char;
118
+ pos++;
119
+ while (pos < text.length && text[pos] === '`') {
120
+ textBuffer += text[pos];
121
+ pos++;
122
+ }
123
+ continue;
124
+ }
125
+ }
126
+ if (char === '<') {
127
+ const autolinkResult = parseAutolink(text, pos);
128
+ if (autolinkResult) {
129
+ flushText();
130
+ nodes.push(autolinkResult.node);
131
+ pos += autolinkResult.length;
132
+ continue;
133
+ }
134
+ const openingTagMatch = text.slice(pos).match(/^<[a-zA-Z][a-zA-Z0-9-]*(?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:[^\s"'=<>`]+|'[^']*'|"[^"]*"))?)*\s*\/?>/);
135
+ if (openingTagMatch) {
136
+ const tagNameMatch = openingTagMatch[0].match(/^<\s*([a-zA-Z][a-zA-Z0-9-]*)/);
137
+ if (this.enableTagfilter && tagNameMatch && isTagFilterTag(tagNameMatch[1])) {
138
+ flushText();
139
+ nodes.push({
140
+ type: 'html_inline',
141
+ literal: openingTagMatch[0].replace(/^</, '&lt;'),
142
+ });
143
+ pos += openingTagMatch[0].length;
144
+ continue;
145
+ }
146
+ flushText();
147
+ nodes.push({ type: 'html_inline', literal: openingTagMatch[0] });
148
+ pos += openingTagMatch[0].length;
149
+ continue;
150
+ }
151
+ const closingTagMatch = text.slice(pos).match(/^<\/[a-zA-Z][a-zA-Z0-9-]*\s*>/);
152
+ if (closingTagMatch) {
153
+ const tagNameMatch = closingTagMatch[0].match(/^<\/\s*([a-zA-Z][a-zA-Z0-9-]*)/);
154
+ if (this.enableTagfilter && tagNameMatch && isTagFilterTag(tagNameMatch[1])) {
155
+ flushText();
156
+ nodes.push({
157
+ type: 'html_inline',
158
+ literal: closingTagMatch[0].replace(/^</, '&lt;'),
159
+ });
160
+ pos += closingTagMatch[0].length;
161
+ continue;
162
+ }
163
+ flushText();
164
+ nodes.push({ type: 'html_inline', literal: closingTagMatch[0] });
165
+ pos += closingTagMatch[0].length;
166
+ continue;
167
+ }
168
+ const shortCommentMatch = text.slice(pos).match(/^<!---?>/);
169
+ if (shortCommentMatch) {
170
+ flushText();
171
+ nodes.push({ type: 'html_inline', literal: '<!---->' });
172
+ pos += shortCommentMatch[0].length;
173
+ continue;
174
+ }
175
+ const commentMatch = text.slice(pos).match(/^<!--(?!>)(?!-?>)[\s\S]*?-->/);
176
+ if (commentMatch) {
177
+ flushText();
178
+ nodes.push({ type: 'html_inline', literal: commentMatch[0] });
179
+ pos += commentMatch[0].length;
180
+ continue;
181
+ }
182
+ const piMatch = text.slice(pos).match(/^<\?[\s\S]*?\?>/);
183
+ if (piMatch) {
184
+ flushText();
185
+ nodes.push({ type: 'html_inline', literal: piMatch[0] });
186
+ pos += piMatch[0].length;
187
+ continue;
188
+ }
189
+ const cdataMatch = text.slice(pos).match(/^<!\[CDATA\[[\s\S]*?\]\]>/);
190
+ if (cdataMatch) {
191
+ flushText();
192
+ nodes.push({ type: 'html_inline', literal: cdataMatch[0] });
193
+ pos += cdataMatch[0].length;
194
+ continue;
195
+ }
196
+ const declMatch = text.slice(pos).match(/^<![a-zA-Z][\s\S]*?>/);
197
+ if (declMatch) {
198
+ flushText();
199
+ nodes.push({ type: 'html_inline', literal: declMatch[0] });
200
+ pos += declMatch[0].length;
201
+ continue;
202
+ }
203
+ }
204
+ if (char === ' ') {
205
+ let spaceCount = 0;
206
+ let j = pos;
207
+ while (j < text.length && text[j] === ' ') {
208
+ spaceCount++;
209
+ j++;
210
+ }
211
+ if (text[j] === '\n' && spaceCount >= 2) {
212
+ textBuffer = textBuffer.replace(/ *$/, '');
213
+ flushText();
214
+ nodes.push({ type: 'hardbreak' });
215
+ pos = j + 1;
216
+ while (pos < text.length && text[pos] === ' ') {
217
+ pos++;
218
+ }
219
+ continue;
220
+ }
221
+ }
222
+ if (char === '\n') {
223
+ textBuffer = textBuffer.replace(/ *$/, '');
224
+ flushText();
225
+ nodes.push({ type: 'softbreak' });
226
+ pos++;
227
+ while (pos < text.length && text[pos] === ' ') {
228
+ pos++;
229
+ }
230
+ continue;
231
+ }
232
+ if (char === '~' && this.enableStrikethrough) {
233
+ let tildeCount = 0;
234
+ let j = pos;
235
+ while (j < text.length && text[j] === '~') {
236
+ tildeCount++;
237
+ j++;
238
+ }
239
+ if (tildeCount <= 2) {
240
+ const strikeRun = parseStrikethroughDelimiter(text, pos);
241
+ if (strikeRun && (strikeRun.canOpen || strikeRun.canClose)) {
242
+ flushText();
243
+ const node = createTextNode(text.slice(pos, pos + strikeRun.length));
244
+ nodes.push(node);
245
+ pos += strikeRun.length;
246
+ continue;
247
+ }
248
+ }
249
+ else {
250
+ textBuffer += text.slice(pos, j);
251
+ pos = j;
252
+ continue;
253
+ }
254
+ }
255
+ if (char === '!' && text[pos + 1] === '[' && text[pos + 2] === '^') {
256
+ textBuffer += '!';
257
+ pos++;
258
+ continue;
259
+ }
260
+ if (this.enableFootnotes && char === '[' && text[pos + 1] === '^') {
261
+ const footnoteLabel = parseFootnoteLabel(text, pos);
262
+ if (footnoteLabel) {
263
+ const def = this.footnoteDefinitions.get(footnoteLabel.normalized);
264
+ flushText();
265
+ if (def) {
266
+ nodes.push({
267
+ type: 'footnote_ref',
268
+ label: def.label,
269
+ key: footnoteLabel.normalized,
270
+ });
271
+ }
272
+ else {
273
+ nodes.push(createTextNode(text.slice(pos, pos + footnoteLabel.length), true));
274
+ }
275
+ pos += footnoteLabel.length;
276
+ continue;
277
+ }
278
+ }
279
+ if (char === '*' || char === '_') {
280
+ flushText();
281
+ const run = parseDelimiterRun(text, pos);
282
+ if (run) {
283
+ const node = createTextNode(text.slice(pos, pos + run.length));
284
+ node.delimiterOrigLength = run.length;
285
+ nodes.push(node);
286
+ if (run.canOpen || run.canClose) {
287
+ pushDelimiter(run, node);
288
+ }
289
+ pos += run.length;
290
+ continue;
291
+ }
292
+ }
293
+ if (char === '!' && text[pos + 1] === '[') {
294
+ flushText();
295
+ const node = createTextNode('![');
296
+ nodes.push(node);
297
+ const bracket = {
298
+ type: 'image',
299
+ node,
300
+ nodeIndex: nodes.length - 1,
301
+ active: true,
302
+ prev: bracketStack,
303
+ bracketAfter: false,
304
+ delimiterBefore: delimiterStack,
305
+ textPos: pos + 2,
306
+ };
307
+ if (bracketStack)
308
+ bracketStack.bracketAfter = true;
309
+ bracketStack = bracket;
310
+ pos += 2;
311
+ continue;
312
+ }
313
+ if (char === '[') {
314
+ flushText();
315
+ const node = createTextNode('[');
316
+ nodes.push(node);
317
+ const bracket = {
318
+ type: 'link',
319
+ node,
320
+ nodeIndex: nodes.length - 1,
321
+ active: true,
322
+ prev: bracketStack,
323
+ bracketAfter: false,
324
+ delimiterBefore: delimiterStack,
325
+ textPos: pos + 1,
326
+ };
327
+ if (bracketStack)
328
+ bracketStack.bracketAfter = true;
329
+ bracketStack = bracket;
330
+ pos += 1;
331
+ continue;
332
+ }
333
+ if (char === ']' && bracketStack) {
334
+ flushText();
335
+ let opener = bracketStack;
336
+ if (!opener) {
337
+ textBuffer += ']';
338
+ pos++;
339
+ continue;
340
+ }
341
+ if (!opener.active) {
342
+ bracketStack = opener.prev;
343
+ textBuffer += ']';
344
+ pos++;
345
+ continue;
346
+ }
347
+ let matched = false;
348
+ let destination = '';
349
+ let title = '';
350
+ let consumed = 1;
351
+ if (text[pos + 1] === '(') {
352
+ const linkResult = parseInlineLink(text, pos + 1);
353
+ if (linkResult) {
354
+ matched = true;
355
+ destination = linkResult.destination;
356
+ title = linkResult.title;
357
+ consumed = 1 + linkResult.length;
358
+ }
359
+ }
360
+ if (!matched && text[pos + 1] === '[') {
361
+ const labelResult = parseLinkLabel(text, pos + 1);
362
+ if (labelResult && labelResult.label) {
363
+ if (this.linkReferences.has(labelResult.label)) {
364
+ matched = true;
365
+ const ref = this.linkReferences.get(labelResult.label);
366
+ destination = ref.destination;
367
+ title = ref.title;
368
+ consumed = 1 + labelResult.length;
369
+ }
370
+ }
371
+ else if (labelResult && labelResult.label === '') {
372
+ const innerText = this.extractTextFromNodes(nodes.slice(opener.nodeIndex + 1));
373
+ const label = normalizeLabel(innerText);
374
+ if (this.linkReferences.has(label)) {
375
+ matched = true;
376
+ const ref = this.linkReferences.get(label);
377
+ destination = ref.destination;
378
+ title = ref.title;
379
+ consumed = 1 + labelResult.length;
380
+ }
381
+ }
382
+ }
383
+ if (!matched && text[pos + 1] !== '[') {
384
+ const rawLabel = text.slice(opener.textPos, pos);
385
+ const label = normalizeLabel(this.normalizeLabelForMatching(rawLabel));
386
+ if (label && this.linkReferences.has(label)) {
387
+ matched = true;
388
+ const ref = this.linkReferences.get(label);
389
+ destination = ref.destination;
390
+ title = ref.title;
391
+ }
392
+ }
393
+ if (matched) {
394
+ const innerNodes = nodes.splice(opener.nodeIndex + 1);
395
+ nodes.pop();
396
+ this.processEmphasisAndStrikethrough(innerNodes, opener.delimiterBefore);
397
+ if (opener.type === 'image') {
398
+ const alt = this.extractTextFromNodes(innerNodes);
399
+ nodes.push(createImageNode(destination, title, alt));
400
+ }
401
+ else {
402
+ const linkNode = createLinkNode(destination, title);
403
+ linkNode.children = innerNodes;
404
+ nodes.push(linkNode);
405
+ }
406
+ if (opener.type === 'link') {
407
+ let b = opener.prev;
408
+ while (b) {
409
+ if (b.type === 'link')
410
+ b.active = false;
411
+ b = b.prev;
412
+ }
413
+ }
414
+ bracketStack = opener.prev;
415
+ pos += consumed;
416
+ }
417
+ else {
418
+ bracketStack = opener.prev;
419
+ textBuffer += ']';
420
+ pos++;
421
+ }
422
+ continue;
423
+ }
424
+ textBuffer += char;
425
+ pos++;
426
+ }
427
+ flushText();
428
+ this.processEmphasisAndStrikethrough(nodes, null);
429
+ this.flattenNestedEmphasis(nodes);
430
+ return nodes;
431
+ }
432
+ extractTextFromNodes(nodes) {
433
+ let result = '';
434
+ for (const node of nodes) {
435
+ if (node.type === 'text') {
436
+ result += node.literal;
437
+ }
438
+ else if (node.type === 'code_span') {
439
+ result += node.literal;
440
+ }
441
+ else if (node.type === 'image') {
442
+ result += node.alt;
443
+ }
444
+ else if (node.type === 'softbreak' || node.type === 'hardbreak') {
445
+ result += ' ';
446
+ }
447
+ else if ('children' in node && Array.isArray(node.children)) {
448
+ result += this.extractTextFromNodes(node.children);
449
+ }
450
+ }
451
+ return result;
452
+ }
453
+ normalizeLabelForMatching(rawLabel) {
454
+ let result = '';
455
+ let i = 0;
456
+ while (i < rawLabel.length) {
457
+ if (rawLabel[i] === '\\' && i + 1 < rawLabel.length) {
458
+ const next = rawLabel[i + 1];
459
+ if (next === '[' || next === ']' || next === '\\') {
460
+ result += next;
461
+ i += 2;
462
+ continue;
463
+ }
464
+ }
465
+ result += rawLabel[i];
466
+ i++;
467
+ }
468
+ return result;
469
+ }
470
+ processEmphasisAndStrikethrough(nodes, stackBottom) {
471
+ this.processEmphasis(nodes, stackBottom);
472
+ if (this.enableStrikethrough) {
473
+ this.processStrikethrough(nodes);
474
+ }
475
+ }
476
+ processStrikethrough(nodes) {
477
+ const delimiters = [];
478
+ for (let i = 0; i < nodes.length; i++) {
479
+ const node = nodes[i];
480
+ if (node.type === 'text' && node.literal.length > 0 && !node.noDelim) {
481
+ if (node.literal.match(/^~{1,2}$/)) {
482
+ const beforeNode = i > 0 ? nodes[i - 1] : null;
483
+ const afterNode = i < nodes.length - 1 ? nodes[i + 1] : null;
484
+ let charBefore = '\n';
485
+ let charAfter = '\n';
486
+ if (beforeNode) {
487
+ if (beforeNode.type === 'text') {
488
+ charBefore = this.getLastChar(beforeNode.literal);
489
+ }
490
+ else if (beforeNode.type !== 'softbreak' && beforeNode.type !== 'hardbreak') {
491
+ charBefore = 'a';
492
+ }
493
+ }
494
+ if (afterNode) {
495
+ if (afterNode.type === 'text') {
496
+ charAfter = this.getFirstChar(afterNode.literal);
497
+ }
498
+ else if (afterNode.type !== 'softbreak' && afterNode.type !== 'hardbreak') {
499
+ charAfter = 'a';
500
+ }
501
+ }
502
+ const isWhitespace = (c) => /\s/.test(c) || c === '\n';
503
+ const beforeIsWhitespace = isWhitespace(charBefore);
504
+ const afterIsWhitespace = isWhitespace(charAfter);
505
+ const canOpen = !afterIsWhitespace;
506
+ const canClose = !beforeIsWhitespace;
507
+ if (canOpen || canClose) {
508
+ delimiters.push({
509
+ run: {
510
+ char: '~',
511
+ length: node.literal.length,
512
+ canOpen,
513
+ canClose,
514
+ position: 0,
515
+ origLength: node.literal.length,
516
+ },
517
+ node: node,
518
+ active: true,
519
+ nodeIndex: i,
520
+ });
521
+ }
522
+ }
523
+ }
524
+ }
525
+ if (delimiters.length < 2)
526
+ return;
527
+ let closerIdx = 0;
528
+ while (closerIdx < delimiters.length) {
529
+ const closer = delimiters[closerIdx];
530
+ if (!closer.run.canClose || !closer.active) {
531
+ closerIdx++;
532
+ continue;
533
+ }
534
+ let openerIdx = closerIdx - 1;
535
+ let openerFound = false;
536
+ while (openerIdx >= 0) {
537
+ const opener = delimiters[openerIdx];
538
+ if (opener.active && opener.run.canOpen &&
539
+ canStrikethroughDelimitersMatch(opener.run, closer.run)) {
540
+ openerFound = true;
541
+ break;
542
+ }
543
+ openerIdx--;
544
+ }
545
+ if (!openerFound) {
546
+ closerIdx++;
547
+ continue;
548
+ }
549
+ const opener = delimiters[openerIdx];
550
+ const openerNodeIdx = nodes.indexOf(opener.node);
551
+ const closerNodeIdx = nodes.indexOf(closer.node);
552
+ if (openerNodeIdx === -1 || closerNodeIdx === -1) {
553
+ closerIdx++;
554
+ continue;
555
+ }
556
+ const contentNodes = nodes.slice(openerNodeIdx + 1, closerNodeIdx);
557
+ const strikeNode = createStrikethroughNode(contentNodes);
558
+ nodes.splice(openerNodeIdx, closerNodeIdx - openerNodeIdx + 1, strikeNode);
559
+ for (let i = openerIdx + 1; i < closerIdx; i++) {
560
+ delimiters[i].active = false;
561
+ }
562
+ opener.active = false;
563
+ closer.active = false;
564
+ delimiters.length = 0;
565
+ for (let i = 0; i < nodes.length; i++) {
566
+ const node = nodes[i];
567
+ if (node.type === 'text' && node.literal.length > 0 && !node.noDelim) {
568
+ if (node.literal.match(/^~{1,2}$/)) {
569
+ const beforeNode = i > 0 ? nodes[i - 1] : null;
570
+ const afterNode = i < nodes.length - 1 ? nodes[i + 1] : null;
571
+ let charBefore = '\n';
572
+ let charAfter = '\n';
573
+ if (beforeNode) {
574
+ if (beforeNode.type === 'text')
575
+ charBefore = this.getLastChar(beforeNode.literal);
576
+ else if (beforeNode.type !== 'softbreak' && beforeNode.type !== 'hardbreak')
577
+ charBefore = 'a';
578
+ }
579
+ if (afterNode) {
580
+ if (afterNode.type === 'text')
581
+ charAfter = this.getFirstChar(afterNode.literal);
582
+ else if (afterNode.type !== 'softbreak' && afterNode.type !== 'hardbreak')
583
+ charAfter = 'a';
584
+ }
585
+ const isWhitespace = (c) => /\s/.test(c) || c === '\n';
586
+ const canOpen = !isWhitespace(charAfter);
587
+ const canClose = !isWhitespace(charBefore);
588
+ if (canOpen || canClose) {
589
+ delimiters.push({
590
+ run: {
591
+ char: '~',
592
+ length: node.literal.length,
593
+ canOpen,
594
+ canClose,
595
+ position: 0,
596
+ origLength: node.literal.length,
597
+ },
598
+ node: node,
599
+ active: true,
600
+ nodeIndex: i,
601
+ });
602
+ }
603
+ }
604
+ }
605
+ }
606
+ closerIdx = 0;
607
+ }
608
+ for (let i = nodes.length - 1; i >= 0; i--) {
609
+ const node = nodes[i];
610
+ if (node.type === 'text' && node.literal === '') {
611
+ nodes.splice(i, 1);
612
+ }
613
+ }
614
+ }
615
+ processEmphasis(nodes, stackBottom) {
616
+ const nodeDelims = [];
617
+ for (let i = 0; i < nodes.length; i++) {
618
+ const node = nodes[i];
619
+ if (node.type === 'text' && node.literal.length > 0 && !node.noDelim) {
620
+ const firstChar = node.literal[0];
621
+ if ((firstChar === '*' || firstChar === '_') && node.literal.match(/^[*_]+$/)) {
622
+ const beforeNode = i > 0 ? nodes[i - 1] : null;
623
+ const afterNode = i < nodes.length - 1 ? nodes[i + 1] : null;
624
+ let charBefore = '\n';
625
+ if (beforeNode) {
626
+ if (beforeNode.type === 'text') {
627
+ charBefore = this.getLastChar(beforeNode.literal);
628
+ }
629
+ else if (beforeNode.type === 'softbreak' || beforeNode.type === 'hardbreak') {
630
+ charBefore = '\n';
631
+ }
632
+ else {
633
+ charBefore = 'a';
634
+ }
635
+ }
636
+ let charAfter = '\n';
637
+ if (afterNode) {
638
+ if (afterNode.type === 'text') {
639
+ charAfter = this.getFirstChar(afterNode.literal);
640
+ }
641
+ else if (afterNode.type === 'softbreak' || afterNode.type === 'hardbreak') {
642
+ charAfter = '\n';
643
+ }
644
+ else {
645
+ charAfter = 'a';
646
+ }
647
+ }
648
+ const run = this.computeDelimiterRun(node.literal, charBefore, charAfter);
649
+ node.delimiterOrigLength = node.delimiterOrigLength ?? node.literal.length;
650
+ run.origLength = node.delimiterOrigLength;
651
+ if (run.canOpen || run.canClose) {
652
+ nodeDelims.push({ node, index: i, run });
653
+ }
654
+ }
655
+ }
656
+ }
657
+ if (nodeDelims.length === 0)
658
+ return;
659
+ const openerBottomByChar = { '*': -1, '_': -1 };
660
+ let closerIdx = 0;
661
+ while (closerIdx < nodeDelims.length) {
662
+ const closer = nodeDelims[closerIdx];
663
+ if (!closer.run.canClose) {
664
+ closerIdx++;
665
+ continue;
666
+ }
667
+ let openerIdx = closerIdx - 1;
668
+ let openerFound = false;
669
+ while (openerIdx >= 0 && openerIdx > openerBottomByChar[closer.run.char]) {
670
+ const opener = nodeDelims[openerIdx];
671
+ if (opener.run.canOpen && opener.run.char === closer.run.char &&
672
+ canDelimitersMatch(opener.run, closer.run)) {
673
+ openerFound = true;
674
+ break;
675
+ }
676
+ openerIdx--;
677
+ }
678
+ if (!openerFound) {
679
+ if (!closer.run.canOpen) {
680
+ openerBottomByChar[closer.run.char] = closerIdx - 1;
681
+ }
682
+ closerIdx++;
683
+ continue;
684
+ }
685
+ const opener = nodeDelims[openerIdx];
686
+ const strong = opener.run.length >= 2 && closer.run.length >= 2;
687
+ const numDelims = strong ? 2 : 1;
688
+ opener.node.literal = opener.node.literal.slice(0, -numDelims);
689
+ closer.node.literal = closer.node.literal.slice(numDelims);
690
+ opener.run.length -= numDelims;
691
+ closer.run.length -= numDelims;
692
+ const openerNodeIdx = nodes.indexOf(opener.node);
693
+ const closerNodeIdx = nodes.indexOf(closer.node);
694
+ const contentNodes = nodes.slice(openerNodeIdx + 1, closerNodeIdx);
695
+ const emphNode = strong
696
+ ? { type: 'strong', children: contentNodes }
697
+ : { type: 'emphasis', children: contentNodes };
698
+ nodes.splice(openerNodeIdx + 1, closerNodeIdx - openerNodeIdx - 1, emphNode);
699
+ for (let i = openerIdx + 1; i < closerIdx; i++) {
700
+ nodeDelims[i].run.length = 0;
701
+ }
702
+ if (opener.run.length === 0) {
703
+ const idx = nodes.indexOf(opener.node);
704
+ if (idx !== -1 && opener.node.literal === '') {
705
+ nodes.splice(idx, 1);
706
+ }
707
+ }
708
+ if (closer.run.length === 0) {
709
+ const idx = nodes.indexOf(closer.node);
710
+ if (idx !== -1 && closer.node.literal === '') {
711
+ nodes.splice(idx, 1);
712
+ }
713
+ }
714
+ nodeDelims.length = 0;
715
+ for (let i = 0; i < nodes.length; i++) {
716
+ const node = nodes[i];
717
+ if (node.type === 'text' && node.literal.length > 0 && !node.noDelim) {
718
+ const firstChar = node.literal[0];
719
+ if ((firstChar === '*' || firstChar === '_') && node.literal.match(/^[*_]+$/)) {
720
+ const beforeNode = i > 0 ? nodes[i - 1] : null;
721
+ const afterNode = i < nodes.length - 1 ? nodes[i + 1] : null;
722
+ let charBefore = '\n';
723
+ let charAfter = '\n';
724
+ if (beforeNode) {
725
+ if (beforeNode.type === 'text')
726
+ charBefore = this.getLastChar(beforeNode.literal);
727
+ else if (beforeNode.type !== 'softbreak' && beforeNode.type !== 'hardbreak')
728
+ charBefore = 'a';
729
+ }
730
+ if (afterNode) {
731
+ if (afterNode.type === 'text')
732
+ charAfter = this.getFirstChar(afterNode.literal);
733
+ else if (afterNode.type !== 'softbreak' && afterNode.type !== 'hardbreak')
734
+ charAfter = 'a';
735
+ }
736
+ const run = this.computeDelimiterRun(node.literal, charBefore, charAfter);
737
+ node.delimiterOrigLength = node.delimiterOrigLength ?? node.literal.length;
738
+ run.origLength = node.delimiterOrigLength;
739
+ if (run.canOpen || run.canClose) {
740
+ nodeDelims.push({ node, index: i, run });
741
+ }
742
+ }
743
+ }
744
+ }
745
+ closerIdx = 0;
746
+ }
747
+ for (let i = nodes.length - 1; i >= 0; i--) {
748
+ const node = nodes[i];
749
+ if (node.type === 'text' && node.literal === '') {
750
+ nodes.splice(i, 1);
751
+ }
752
+ }
753
+ }
754
+ flattenNestedEmphasis(nodes) {
755
+ for (const node of nodes) {
756
+ if ('children' in node && Array.isArray(node.children)) {
757
+ this.flattenNestedEmphasis(node.children);
758
+ if (node.type === 'strong') {
759
+ const flattened = [];
760
+ for (const child of node.children) {
761
+ if (child.type === node.type) {
762
+ flattened.push(...child.children);
763
+ }
764
+ else {
765
+ flattened.push(child);
766
+ }
767
+ }
768
+ node.children = flattened;
769
+ }
770
+ }
771
+ }
772
+ }
773
+ computeDelimiterRun(literal, charBefore, charAfter) {
774
+ const char = literal[0];
775
+ const length = literal.length;
776
+ const isWhitespace = (c) => /\s/.test(c) || c === '\n';
777
+ const isPunctuation = (c) => /[\p{P}\p{S}]/u.test(c);
778
+ const beforeIsWhitespace = isWhitespace(charBefore);
779
+ const afterIsWhitespace = isWhitespace(charAfter);
780
+ const beforeIsPunctuation = isPunctuation(charBefore);
781
+ const afterIsPunctuation = isPunctuation(charAfter);
782
+ const leftFlanking = !afterIsWhitespace &&
783
+ (!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation);
784
+ const rightFlanking = !beforeIsWhitespace &&
785
+ (!beforeIsPunctuation || afterIsWhitespace || afterIsPunctuation);
786
+ let canOpen;
787
+ let canClose;
788
+ if (char === '*') {
789
+ canOpen = leftFlanking;
790
+ canClose = rightFlanking;
791
+ }
792
+ else {
793
+ canOpen = leftFlanking && (!rightFlanking || beforeIsPunctuation);
794
+ canClose = rightFlanking && (!leftFlanking || afterIsPunctuation);
795
+ }
796
+ return { char, length, canOpen, canClose, position: 0, origLength: length };
797
+ }
798
+ getFirstChar(text) {
799
+ if (!text)
800
+ return '\n';
801
+ return Array.from(text)[0] ?? '\n';
802
+ }
803
+ getLastChar(text) {
804
+ if (!text)
805
+ return '\n';
806
+ const chars = Array.from(text);
807
+ return chars.length > 0 ? chars[chars.length - 1] : '\n';
808
+ }
809
+ }