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,52 @@
1
+ /**
2
+ * Parser for fenced code blocks with optional info strings.
3
+ */
4
+ const BACKTICK_FENCE_OPEN_REGEX = /^( {0,3})(`{3,})([^`]*)$/;
5
+ const TILDE_FENCE_OPEN_REGEX = /^( {0,3})(~{3,})(.*)$/;
6
+ export function parseFenceOpen(line) {
7
+ let match = line.match(BACKTICK_FENCE_OPEN_REGEX);
8
+ if (!match) {
9
+ match = line.match(TILDE_FENCE_OPEN_REGEX);
10
+ }
11
+ if (!match)
12
+ return null;
13
+ const indent = match[1].length;
14
+ const fence = match[2];
15
+ const char = fence[0];
16
+ const length = fence.length;
17
+ const info = match[3].trim();
18
+ return { indent, char, length, info };
19
+ }
20
+ export function isFenceClose(line, fence) {
21
+ const trimmed = line.replace(/^( {0,3})/, '');
22
+ const closeRegex = new RegExp(`^${fence.char}{${fence.length},}[ \\t]*$`);
23
+ return closeRegex.test(trimmed);
24
+ }
25
+ export function isFencedCodeStart(line) {
26
+ return BACKTICK_FENCE_OPEN_REGEX.test(line) || TILDE_FENCE_OPEN_REGEX.test(line);
27
+ }
28
+ export function createFencedCodeBlock(info, lines, indent) {
29
+ const content = lines
30
+ .map(line => {
31
+ let removed = 0;
32
+ let i = 0;
33
+ while (i < line.length && removed < indent) {
34
+ if (line[i] === ' ') {
35
+ removed++;
36
+ i++;
37
+ }
38
+ else {
39
+ break;
40
+ }
41
+ }
42
+ return line.slice(i);
43
+ })
44
+ .join('\n');
45
+ const hasContent = lines.length > 0 && (lines.length > 1 || lines[0] !== '');
46
+ return {
47
+ type: 'code_block',
48
+ info,
49
+ literal: hasContent ? content + '\n' : '',
50
+ fenced: true,
51
+ };
52
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Parser for indented code blocks.
3
+ */
4
+ import { countLeadingSpaces } from '../utils.js';
5
+ export function isIndentedCodeLine(line) {
6
+ if (line.trim() === '')
7
+ return false;
8
+ return countLeadingSpaces(line) >= 4;
9
+ }
10
+ export function removeIndentation(line, amount = 4) {
11
+ let removed = 0;
12
+ let i = 0;
13
+ while (i < line.length && removed < amount) {
14
+ if (line[i] === ' ') {
15
+ removed++;
16
+ i++;
17
+ }
18
+ else if (line[i] === '\t') {
19
+ const tabWidth = 4 - (removed % 4);
20
+ if (removed + tabWidth <= amount) {
21
+ removed += tabWidth;
22
+ i++;
23
+ }
24
+ else {
25
+ break;
26
+ }
27
+ }
28
+ else {
29
+ break;
30
+ }
31
+ }
32
+ return line.slice(i);
33
+ }
34
+ export function createIndentedCodeBlock(lines) {
35
+ const content = lines.join('\n');
36
+ return {
37
+ type: 'code_block',
38
+ info: '',
39
+ literal: content + '\n',
40
+ fenced: false,
41
+ };
42
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Parser for ATX headings using leading # markers.
3
+ */
4
+ const ATX_HEADING_REGEX = /^( {0,3})(#{1,6})([ \t].*|[ \t]*)$/;
5
+ export function parseAtxHeading(line) {
6
+ const match = line.match(ATX_HEADING_REGEX);
7
+ if (!match)
8
+ return null;
9
+ const level = match[2].length;
10
+ let content = match[3];
11
+ content = content.replace(/[ \t]+#+[ \t]*$/, '');
12
+ content = content.trim();
13
+ return { level, content };
14
+ }
15
+ export function isAtxHeading(line) {
16
+ return ATX_HEADING_REGEX.test(line);
17
+ }
18
+ export function createHeadingNode(level) {
19
+ return {
20
+ type: 'heading',
21
+ level,
22
+ children: [],
23
+ };
24
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Parser for setext headings underlined with = or -.
3
+ */
4
+ const SETEXT_HEADING_REGEX = /^( {0,3})(=+|-+)[ \t]*$/;
5
+ export function parseSetextHeading(line) {
6
+ const match = line.match(SETEXT_HEADING_REGEX);
7
+ if (!match)
8
+ return null;
9
+ const char = match[2][0];
10
+ const level = char === '=' ? 1 : 2;
11
+ return { level: level };
12
+ }
13
+ export function isSetextHeadingUnderline(line) {
14
+ return SETEXT_HEADING_REGEX.test(line);
15
+ }
16
+ export function getSetextLevel(line) {
17
+ const result = parseSetextHeading(line);
18
+ return result?.level ?? null;
19
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Parser for raw HTML blocks defined by the CommonMark spec.
3
+ */
4
+ const TYPE_6_TAGS = new Set([
5
+ 'address', 'article', 'aside', 'base', 'basefont', 'blockquote', 'body',
6
+ 'caption', 'center', 'col', 'colgroup', 'dd', 'details', 'dialog', 'dir',
7
+ 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form',
8
+ 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header',
9
+ 'hr', 'html', 'iframe', 'legend', 'li', 'link', 'main', 'menu', 'menuitem',
10
+ 'nav', 'noframes', 'ol', 'optgroup', 'option', 'p', 'param', 'search',
11
+ 'section', 'summary', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
12
+ 'title', 'tr', 'track', 'ul'
13
+ ]);
14
+ const TYPE_1_OPEN_REGEX = /^<(?:pre|script|style|textarea)(?:\s|>|$)/i;
15
+ const TYPE_1_CLOSE_REGEX = /<\/(?:pre|script|style|textarea)>/i;
16
+ const TYPE_2_OPEN_REGEX = /^<!--/;
17
+ const TYPE_2_CLOSE_REGEX = /-->/;
18
+ const TYPE_3_OPEN_REGEX = /^<\?/;
19
+ const TYPE_3_CLOSE_REGEX = /\?>/;
20
+ const TYPE_4_OPEN_REGEX = /^<![a-zA-Z]/;
21
+ const TYPE_4_CLOSE_REGEX = />/;
22
+ const TYPE_5_OPEN_REGEX = /^<!\[CDATA\[/i;
23
+ const TYPE_5_CLOSE_REGEX = /\]\]>/;
24
+ const TYPE_6_OPEN_REGEX = /^<\/?([a-zA-Z][a-zA-Z0-9-]*)(?:\s|\/?>|$)/i;
25
+ const TYPE_7_OPEN_TAG_REGEX = /^<([a-zA-Z][a-zA-Z0-9-]*)(?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:[^\s"'=<>`]+|'[^']*'|"[^"]*"))?)*\s*\/?>\s*$/i;
26
+ const TYPE_7_CLOSE_TAG_REGEX = /^<\/([a-zA-Z][a-zA-Z0-9-]*)\s*>\s*$/i;
27
+ export function getHtmlBlockType(line, canInterruptParagraph) {
28
+ const trimmed = line.replace(/^ {0,3}/, '');
29
+ if (TYPE_1_OPEN_REGEX.test(trimmed))
30
+ return 1;
31
+ if (TYPE_2_OPEN_REGEX.test(trimmed))
32
+ return 2;
33
+ if (TYPE_3_OPEN_REGEX.test(trimmed))
34
+ return 3;
35
+ if (TYPE_4_OPEN_REGEX.test(trimmed))
36
+ return 4;
37
+ if (TYPE_5_OPEN_REGEX.test(trimmed))
38
+ return 5;
39
+ const type6Match = trimmed.match(TYPE_6_OPEN_REGEX);
40
+ if (type6Match && TYPE_6_TAGS.has(type6Match[1].toLowerCase())) {
41
+ return 6;
42
+ }
43
+ if (canInterruptParagraph) {
44
+ if (TYPE_7_OPEN_TAG_REGEX.test(trimmed) || TYPE_7_CLOSE_TAG_REGEX.test(trimmed)) {
45
+ return 7;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ export function isHtmlBlockClose(line, type) {
51
+ switch (type) {
52
+ case 1:
53
+ return TYPE_1_CLOSE_REGEX.test(line);
54
+ case 2:
55
+ return TYPE_2_CLOSE_REGEX.test(line);
56
+ case 3:
57
+ return TYPE_3_CLOSE_REGEX.test(line);
58
+ case 4:
59
+ return TYPE_4_CLOSE_REGEX.test(line);
60
+ case 5:
61
+ return TYPE_5_CLOSE_REGEX.test(line);
62
+ case 6:
63
+ case 7:
64
+ return line.trim() === '';
65
+ default:
66
+ return false;
67
+ }
68
+ }
69
+ export function isHtmlBlockStart(line, canInterruptParagraph = true) {
70
+ return getHtmlBlockType(line, canInterruptParagraph) !== null;
71
+ }
72
+ export function createHtmlBlockNode(content) {
73
+ return {
74
+ type: 'html_block',
75
+ literal: content,
76
+ };
77
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Parser for link reference definitions used by reference-style links.
3
+ */
4
+ import { normalizeLabel, unescapeString, decodeHtmlEntities } from '../utils.js';
5
+ const LINK_LABEL_REGEX = /^\[((?:[^\[\]\\]|\\.){0,999})\]/;
6
+ const LINK_DESTINATION_ANGLE_REGEX = /^<([^<>\n\\]|\\.)*>/;
7
+ const LINK_DESTINATION_BARE_REGEX = /^[^\s<\[\]]*(?:\([^\s<\[\]]*\)[^\s<\[\]]*)*[^\s<\[\]]*/;
8
+ const LINK_TITLE_DOUBLE_REGEX = /^"((?:[^"\\]|\\[\s\S])*)"/;
9
+ const LINK_TITLE_SINGLE_REGEX = /^'((?:[^'\\]|\\[\s\S])*)'/;
10
+ const LINK_TITLE_PAREN_REGEX = /^\(((?:[^()\\]|\\[\s\S])*)\)/;
11
+ export function parseLinkReferenceDefinition(text) {
12
+ let pos = 0;
13
+ const indentMatch = text.match(/^( {0,3})/);
14
+ if (indentMatch)
15
+ pos += indentMatch[1].length;
16
+ const labelMatch = text.slice(pos).match(LINK_LABEL_REGEX);
17
+ if (!labelMatch)
18
+ return null;
19
+ const rawLabel = labelMatch[1];
20
+ if (rawLabel.trim() === '')
21
+ return null;
22
+ pos += labelMatch[0].length;
23
+ if (text[pos] !== ':')
24
+ return null;
25
+ pos++;
26
+ const beforeDest = text.slice(pos).match(/^[ \t]*\n?[ \t]*/);
27
+ if (beforeDest)
28
+ pos += beforeDest[0].length;
29
+ let destination;
30
+ const angleMatch = text.slice(pos).match(LINK_DESTINATION_ANGLE_REGEX);
31
+ if (angleMatch) {
32
+ destination = angleMatch[0].slice(1, -1);
33
+ pos += angleMatch[0].length;
34
+ }
35
+ else {
36
+ const bareMatch = text.slice(pos).match(LINK_DESTINATION_BARE_REGEX);
37
+ if (!bareMatch || bareMatch[0] === '')
38
+ return null;
39
+ destination = bareMatch[0];
40
+ pos += bareMatch[0].length;
41
+ }
42
+ let title = '';
43
+ let titleFound = false;
44
+ const tryMatchTitle = (titlePos) => {
45
+ const remaining = text.slice(titlePos);
46
+ const doubleMatch = remaining.match(LINK_TITLE_DOUBLE_REGEX);
47
+ const singleMatch = remaining.match(LINK_TITLE_SINGLE_REGEX);
48
+ const parenMatch = remaining.match(LINK_TITLE_PAREN_REGEX);
49
+ const titleMatch = doubleMatch || singleMatch || parenMatch;
50
+ if (titleMatch) {
51
+ const afterTitlePos = titlePos + titleMatch[0].length;
52
+ const afterTitle = text.slice(afterTitlePos).match(/^[ \t]*(?:\n|$)/);
53
+ if (afterTitle) {
54
+ return { title: titleMatch[1], endPos: afterTitlePos + afterTitle[0].length };
55
+ }
56
+ }
57
+ return null;
58
+ };
59
+ const spaceBeforeTitle = text.slice(pos).match(/^[ \t]+/);
60
+ if (spaceBeforeTitle) {
61
+ const result = tryMatchTitle(pos + spaceBeforeTitle[0].length);
62
+ if (result) {
63
+ title = result.title;
64
+ pos = result.endPos;
65
+ titleFound = true;
66
+ }
67
+ }
68
+ if (!titleFound) {
69
+ const newlineBeforeTitle = text.slice(pos).match(/^[ \t]*\n[ \t]*/);
70
+ if (newlineBeforeTitle) {
71
+ const result = tryMatchTitle(pos + newlineBeforeTitle[0].length);
72
+ if (result) {
73
+ title = result.title;
74
+ pos = result.endPos;
75
+ titleFound = true;
76
+ }
77
+ }
78
+ }
79
+ if (!titleFound) {
80
+ const endAfterDest = text.slice(pos).match(/^[ \t]*(?:\n|$)/);
81
+ if (endAfterDest) {
82
+ pos += endAfterDest[0].length;
83
+ return {
84
+ label: normalizeLabel(unescapeString(rawLabel)),
85
+ definition: {
86
+ destination: decodeHtmlEntities(unescapeString(destination)),
87
+ title: '',
88
+ },
89
+ consumed: pos,
90
+ };
91
+ }
92
+ return null;
93
+ }
94
+ return {
95
+ label: normalizeLabel(unescapeString(rawLabel)),
96
+ definition: {
97
+ destination: decodeHtmlEntities(unescapeString(destination)),
98
+ title: decodeHtmlEntities(unescapeString(title)),
99
+ },
100
+ consumed: pos,
101
+ };
102
+ }
103
+ export function isLinkReferenceStart(line) {
104
+ return /^ {0,3}\[/.test(line);
105
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Parser for ordered and unordered lists with nesting and tight/loose detection.
3
+ */
4
+ const BULLET_LIST_REGEX = /^( {0,3})([-+*])( +|\t|$)/;
5
+ const ORDERED_LIST_REGEX = /^( {0,3})(\d{1,9})([.)])( +|\t|$)/;
6
+ export function parseListMarker(line) {
7
+ const bulletMatch = line.match(BULLET_LIST_REGEX);
8
+ if (bulletMatch) {
9
+ const indentChars = bulletMatch[1].length;
10
+ const indent = indentChars;
11
+ const marker = bulletMatch[2];
12
+ const spacesAfter = bulletMatch[3];
13
+ let paddingChars = spacesAfter.length;
14
+ const columnAfterMarker = indent + 1;
15
+ let padding;
16
+ if (spacesAfter === '\t') {
17
+ padding = 4 - (columnAfterMarker % 4);
18
+ }
19
+ else if (spacesAfter === '') {
20
+ padding = 1;
21
+ }
22
+ else {
23
+ padding = spacesAfter.length;
24
+ }
25
+ let paddingToConsume = padding;
26
+ if (spacesAfter === '\t') {
27
+ paddingToConsume = 1;
28
+ paddingChars = 1;
29
+ }
30
+ else if (padding >= 5) {
31
+ paddingToConsume = 1;
32
+ paddingChars = 1;
33
+ }
34
+ const contentIndent = indent + 1 + paddingToConsume;
35
+ const contentCharIndex = indentChars + 1 + paddingChars;
36
+ return {
37
+ type: 'bullet',
38
+ indent,
39
+ indentChars,
40
+ marker,
41
+ bulletChar: marker,
42
+ padding: Math.min(paddingToConsume, 4),
43
+ paddingChars,
44
+ contentIndent,
45
+ contentCharIndex,
46
+ };
47
+ }
48
+ const orderedMatch = line.match(ORDERED_LIST_REGEX);
49
+ if (orderedMatch) {
50
+ const indentChars = orderedMatch[1].length;
51
+ const indent = indentChars;
52
+ const start = parseInt(orderedMatch[2], 10);
53
+ const delimiter = orderedMatch[3];
54
+ const marker = orderedMatch[2] + delimiter;
55
+ const spacesAfter = orderedMatch[4];
56
+ let paddingChars = spacesAfter.length;
57
+ const columnAfterMarker = indent + marker.length;
58
+ let padding;
59
+ if (spacesAfter === '\t') {
60
+ padding = 4 - (columnAfterMarker % 4);
61
+ }
62
+ else if (spacesAfter === '') {
63
+ padding = 1;
64
+ }
65
+ else {
66
+ padding = spacesAfter.length;
67
+ }
68
+ let paddingToConsume = padding;
69
+ if (spacesAfter === '\t') {
70
+ paddingToConsume = 1;
71
+ paddingChars = 1;
72
+ }
73
+ else if (padding >= 5) {
74
+ paddingToConsume = 1;
75
+ paddingChars = 1;
76
+ }
77
+ const contentIndent = indent + marker.length + paddingToConsume;
78
+ const contentCharIndex = indentChars + marker.length + paddingChars;
79
+ return {
80
+ type: 'ordered',
81
+ indent,
82
+ indentChars,
83
+ marker,
84
+ start,
85
+ delimiter,
86
+ padding: Math.min(paddingToConsume, 4),
87
+ paddingChars,
88
+ contentIndent,
89
+ contentCharIndex,
90
+ };
91
+ }
92
+ return null;
93
+ }
94
+ export function isListItemStart(line) {
95
+ return BULLET_LIST_REGEX.test(line) || ORDERED_LIST_REGEX.test(line);
96
+ }
97
+ export function isBulletListStart(line) {
98
+ return BULLET_LIST_REGEX.test(line);
99
+ }
100
+ export function isOrderedListStart(line) {
101
+ return ORDERED_LIST_REGEX.test(line);
102
+ }
103
+ export function canStartList(line, interruptingParagraph) {
104
+ if (!interruptingParagraph)
105
+ return isListItemStart(line);
106
+ const marker = parseListMarker(line);
107
+ if (!marker)
108
+ return false;
109
+ if (marker.type === 'ordered' && marker.start !== 1) {
110
+ return false;
111
+ }
112
+ const content = line.slice(marker.indent + marker.marker.length + marker.padding);
113
+ if (content.trim() === '') {
114
+ return false;
115
+ }
116
+ return true;
117
+ }
118
+ export function createListNode(marker) {
119
+ return {
120
+ type: 'list',
121
+ listType: marker.type,
122
+ start: marker.start ?? 1,
123
+ tight: true,
124
+ delimiter: marker.delimiter ?? null,
125
+ bulletChar: marker.bulletChar ?? null,
126
+ children: [],
127
+ };
128
+ }
129
+ export function createListItemNode() {
130
+ return {
131
+ type: 'list_item',
132
+ children: [],
133
+ };
134
+ }
135
+ export function listsMatch(a, b) {
136
+ if (a.type !== b.type)
137
+ return false;
138
+ if (a.type === 'bullet' && b.type === 'bullet') {
139
+ return a.bulletChar === b.bulletChar;
140
+ }
141
+ if (a.type === 'ordered' && b.type === 'ordered') {
142
+ return a.delimiter === b.delimiter;
143
+ }
144
+ return false;
145
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Parser for paragraph blocks as the default block type.
3
+ */
4
+ export function createParagraphNode() {
5
+ return {
6
+ type: 'paragraph',
7
+ children: [],
8
+ };
9
+ }
10
+ export function isParagraphContinuation(line) {
11
+ return line.trim() !== '';
12
+ }
13
+ export function trimParagraphLine(line) {
14
+ return line.replace(/^ {0,3}/, '').replace(/[ \t]+$/, '');
15
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Parser for GFM tables built from pipe-delimited rows.
3
+ */
4
+ export function parseDelimiterRow(line) {
5
+ const trimmed = line.trim();
6
+ let cells = splitTableRow(trimmed);
7
+ if (cells.length === 0)
8
+ return null;
9
+ const alignments = [];
10
+ for (const cell of cells) {
11
+ const content = cell.trim();
12
+ if (!/^:?-+:?$/.test(content)) {
13
+ return null;
14
+ }
15
+ const hasLeftColon = content.startsWith(':');
16
+ const hasRightColon = content.endsWith(':');
17
+ if (hasLeftColon && hasRightColon) {
18
+ alignments.push('center');
19
+ }
20
+ else if (hasRightColon) {
21
+ alignments.push('right');
22
+ }
23
+ else if (hasLeftColon) {
24
+ alignments.push('left');
25
+ }
26
+ else {
27
+ alignments.push(null);
28
+ }
29
+ }
30
+ return alignments;
31
+ }
32
+ export function splitTableRow(line) {
33
+ const cells = [];
34
+ let current = '';
35
+ let i = 0;
36
+ let inBackticks = false;
37
+ let backtickCount = 0;
38
+ let trimmed = line.trim();
39
+ if (trimmed.startsWith('|')) {
40
+ trimmed = trimmed.slice(1);
41
+ }
42
+ if (trimmed.endsWith('|') && !trimmed.endsWith('\\|')) {
43
+ trimmed = trimmed.slice(0, -1);
44
+ }
45
+ while (i < trimmed.length) {
46
+ const char = trimmed[i];
47
+ if (char === '`') {
48
+ if (!inBackticks) {
49
+ inBackticks = true;
50
+ backtickCount = 1;
51
+ let j = i + 1;
52
+ while (j < trimmed.length && trimmed[j] === '`') {
53
+ backtickCount++;
54
+ j++;
55
+ }
56
+ current += trimmed.slice(i, j);
57
+ i = j;
58
+ continue;
59
+ }
60
+ else {
61
+ let closeCount = 1;
62
+ let j = i + 1;
63
+ while (j < trimmed.length && trimmed[j] === '`') {
64
+ closeCount++;
65
+ j++;
66
+ }
67
+ if (closeCount === backtickCount) {
68
+ inBackticks = false;
69
+ }
70
+ current += trimmed.slice(i, j);
71
+ i = j;
72
+ continue;
73
+ }
74
+ }
75
+ if (char === '\\' && i + 1 < trimmed.length && trimmed[i + 1] === '|') {
76
+ current += '|';
77
+ i += 2;
78
+ continue;
79
+ }
80
+ if (char === '|' && !inBackticks) {
81
+ cells.push(current);
82
+ current = '';
83
+ i++;
84
+ continue;
85
+ }
86
+ current += char;
87
+ i++;
88
+ }
89
+ cells.push(current);
90
+ return cells;
91
+ }
92
+ export function isTableRow(line) {
93
+ const trimmed = line.trim();
94
+ for (let i = 0; i < trimmed.length; i++) {
95
+ if (trimmed[i] === '|') {
96
+ if (i === 0 || trimmed[i - 1] !== '\\') {
97
+ return true;
98
+ }
99
+ }
100
+ }
101
+ return false;
102
+ }
103
+ export function couldBeTableHeader(line) {
104
+ return isTableRow(line);
105
+ }
106
+ export function isTableStart(headerLine, delimiterLine) {
107
+ if (!isTableRow(headerLine))
108
+ return null;
109
+ const headerCells = splitTableRow(headerLine);
110
+ const hasContent = headerCells.some(cell => cell.trim() !== '');
111
+ if (!hasContent)
112
+ return null;
113
+ const alignments = parseDelimiterRow(delimiterLine);
114
+ if (!alignments)
115
+ return null;
116
+ if (headerCells.length !== alignments.length)
117
+ return null;
118
+ return alignments;
119
+ }
120
+ export function createTableCellNode(align, isHeader, children = []) {
121
+ return {
122
+ type: 'table_cell',
123
+ children,
124
+ align,
125
+ isHeader,
126
+ };
127
+ }
128
+ export function createTableRowNode(isHeader, cells = []) {
129
+ return {
130
+ type: 'table_row',
131
+ children: cells,
132
+ isHeader,
133
+ };
134
+ }
135
+ export function createTableNode(alignments) {
136
+ return {
137
+ type: 'table',
138
+ alignments,
139
+ children: [],
140
+ };
141
+ }
142
+ export function parseTableRowCells(line, alignments, isHeader) {
143
+ const cellContents = splitTableRow(line);
144
+ const cells = [];
145
+ for (let i = 0; i < alignments.length; i++) {
146
+ const content = cellContents[i]?.trim() ?? '';
147
+ const cell = createTableCellNode(alignments[i], isHeader);
148
+ cell.rawContent = content;
149
+ cells.push(cell);
150
+ }
151
+ return cells;
152
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Parser for GFM task list items that recognize [ ] and [x] markers.
3
+ */
4
+ const TASK_LIST_MARKER = /^\[([ xX])\](?=\s)/;
5
+ export function parseTaskListMarker(text) {
6
+ const match = text.match(TASK_LIST_MARKER);
7
+ if (!match)
8
+ return null;
9
+ const char = match[1];
10
+ const checked = char === 'x' || char === 'X';
11
+ return {
12
+ checked,
13
+ length: 3,
14
+ };
15
+ }
16
+ export function processTaskListItem(item) {
17
+ if (item.children.length === 0)
18
+ return;
19
+ const firstChild = item.children[0];
20
+ if (firstChild.type !== 'paragraph')
21
+ return;
22
+ const para = firstChild;
23
+ const rawContent = para.rawContent;
24
+ if (typeof rawContent === 'string') {
25
+ const result = parseTaskListMarker(rawContent);
26
+ if (result) {
27
+ item.checked = result.checked;
28
+ para.rawContent = rawContent.slice(result.length).replace(/^\s/, '');
29
+ }
30
+ return;
31
+ }
32
+ if (para.children && para.children.length > 0) {
33
+ const firstInline = para.children[0];
34
+ if (firstInline.type === 'text') {
35
+ const textNode = firstInline;
36
+ const result = parseTaskListMarker(textNode.literal);
37
+ if (result) {
38
+ item.checked = result.checked;
39
+ textNode.literal = textNode.literal.slice(result.length).replace(/^\s/, '');
40
+ if (textNode.literal === '') {
41
+ para.children.shift();
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ export function processTaskLists(blocks) {
48
+ for (const block of blocks) {
49
+ if (block.type === 'list') {
50
+ for (const item of block.children) {
51
+ processTaskListItem(item);
52
+ if (item.children) {
53
+ processTaskLists(item.children);
54
+ }
55
+ }
56
+ }
57
+ else if (block.type === 'blockquote' && block.children) {
58
+ processTaskLists(block.children);
59
+ }
60
+ }
61
+ }