markdown-codec 6.7.4 → 6.7.6

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 (72) hide show
  1. package/README.md +2 -1
  2. package/dist/block/block.cjs +19 -37
  3. package/dist/block/block.js +20 -38
  4. package/dist/block/definitions.cjs +1 -5
  5. package/dist/block/definitions.js +1 -5
  6. package/dist/block/line.cjs +2 -2
  7. package/dist/block/line.js +2 -2
  8. package/dist/block/list.cjs +7 -14
  9. package/dist/block/list.js +7 -14
  10. package/dist/block/node.cjs +3 -3
  11. package/dist/block/node.d.cts +2 -2
  12. package/dist/block/node.d.ts +2 -2
  13. package/dist/block/node.js +3 -3
  14. package/dist/block/table.cjs +3 -4
  15. package/dist/block/table.js +3 -4
  16. package/dist/codec.d.cts +36 -36
  17. package/dist/codec.d.ts +36 -36
  18. package/dist/emit/emit.cjs +58 -62
  19. package/dist/emit/emit.js +58 -62
  20. package/dist/emit/inline.cjs +18 -12
  21. package/dist/emit/inline.js +18 -12
  22. package/dist/emit/table.cjs +2 -2
  23. package/dist/emit/table.js +2 -2
  24. package/dist/html/html-table.cjs +14 -22
  25. package/dist/html/html-table.js +14 -22
  26. package/dist/html/html.cjs +0 -2
  27. package/dist/html/html.js +0 -2
  28. package/dist/html/render.cjs +3 -11
  29. package/dist/html/render.js +3 -11
  30. package/dist/image/image.cjs +8 -57
  31. package/dist/image/image.d.cts +2 -2
  32. package/dist/image/image.d.ts +2 -2
  33. package/dist/image/image.js +9 -56
  34. package/dist/{image-BtmTG4_a.d.cts → image-83yql5ES.d.cts} +1 -3
  35. package/dist/{image-BtmTG4_a.d.ts → image-83yql5ES.d.ts} +1 -3
  36. package/dist/{image-B5L0HpAJ.d.cts → image-B6p0Ovgy.d.cts} +1 -1
  37. package/dist/{image-C390OIB3.d.ts → image-gR1vISI0.d.ts} +1 -1
  38. package/dist/index.d.cts +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/inline/chars.cjs +3 -6
  41. package/dist/inline/chars.js +3 -6
  42. package/dist/inline/delimiter.cjs +5 -16
  43. package/dist/inline/delimiter.d.cts +2 -2
  44. package/dist/inline/delimiter.d.ts +2 -2
  45. package/dist/inline/delimiter.js +5 -16
  46. package/dist/inline/entity.cjs +6 -10
  47. package/dist/inline/entity.js +6 -10
  48. package/dist/inline/gfm-autolink.cjs +20 -19
  49. package/dist/inline/gfm-autolink.js +20 -19
  50. package/dist/inline/inline.cjs +29 -39
  51. package/dist/inline/inline.js +30 -40
  52. package/dist/inline/link.cjs +4 -5
  53. package/dist/inline/link.js +4 -5
  54. package/dist/lower/front-matter.cjs +4 -8
  55. package/dist/lower/front-matter.js +4 -8
  56. package/dist/lower/image.cjs +3 -2
  57. package/dist/lower/image.d.cts +1 -1
  58. package/dist/lower/image.d.ts +1 -1
  59. package/dist/lower/image.js +2 -1
  60. package/dist/lower/inline.cjs +0 -2
  61. package/dist/lower/inline.js +0 -2
  62. package/dist/lower/lower.cjs +7 -10
  63. package/dist/lower/lower.js +7 -10
  64. package/dist/options/options.d.cts +1 -1
  65. package/dist/options/options.d.ts +1 -1
  66. package/dist/read.cjs +2 -2
  67. package/dist/read.js +2 -2
  68. package/dist/scan/scan.cjs +1 -3
  69. package/dist/scan/scan.js +1 -3
  70. package/dist/shared/list-id.cjs +1 -1
  71. package/dist/shared/list-id.js +1 -1
  72. package/package.json +2 -1
package/dist/emit/emit.js CHANGED
@@ -3,7 +3,6 @@ import "../defaults/defaults.js";
3
3
  import { MarkdownDiagnosticCodes, MarkdownInvalidRunConstructExtentError, MarkdownUnbalancedConstructMarkersError, MarkdownUnsupportedDocumentKindError, NOOP_MARKDOWN_DIAGNOSTIC_SINK } from "../diagnostics/diagnostics.js";
4
4
  import { matchHtmlBlockStart } from "../html/html.js";
5
5
  import { ESCAPED_HARD_BREAK_PATTERN, LINE_ENDING_PATTERN } from "../shared/line-ending.js";
6
- import "../scan/scan.js";
7
6
  import { LineCursor } from "../block/line.js";
8
7
  import { parseListMarker } from "../block/list.js";
9
8
  import { parseTableDelimiterRow, splitTableRow } from "../block/table.js";
@@ -23,11 +22,19 @@ const MAX_SETEXT_LEVEL = 2;
23
22
  const SETEXT_LEVEL_1_CHAR = "=";
24
23
  const SETEXT_LEVEL_2_CHAR = "-";
25
24
  const MIN_SETEXT_UNDERLINE_LENGTH = 1;
25
+ function splitLines(text, pattern) {
26
+ const [first, ...rest] = text.split(pattern);
27
+ return [first, ...rest];
28
+ }
26
29
  function renderSetextHeading(level, text) {
27
30
  const underlineChar = level === 1 ? SETEXT_LEVEL_1_CHAR : SETEXT_LEVEL_2_CHAR;
28
- const firstLine = text.split(LINE_ENDING_PATTERN)[0] ?? "";
31
+ const [firstLine] = splitLines(text, LINE_ENDING_PATTERN);
29
32
  return `${text}\n${underlineChar.repeat(Math.max(MIN_SETEXT_UNDERLINE_LENGTH, firstLine.length))}`;
30
33
  }
34
+ function renderAtxHeading(level, text) {
35
+ const collapsed = text.replace(ESCAPED_HARD_BREAK_PATTERN, " ").split(LINE_ENDING_PATTERN).join(" ");
36
+ return `${"#".repeat(level)} ${collapsed}`;
37
+ }
31
38
  const MIN_CODE_FENCE_LENGTH = 3;
32
39
  function longestRunLength(text, char) {
33
40
  let longest = 0;
@@ -53,19 +60,17 @@ function isQuotableStyle(styleId) {
53
60
  return QUOTABLE_STYLE_IDS.has(styleId) || parseHeadingStyleId(styleId) !== void 0;
54
61
  }
55
62
  function terminatesCleanly(styleId) {
56
- if (styleId === void 0 || styleId === "Quote" || styleId === "HTMLPreformatted") return false;
57
- return styleId === "CodeBlock" || styleId === "MathBlock" || styleId === "HorizontalRule" || parseHeadingStyleId(styleId) !== void 0;
63
+ return styleId === "CodeBlock" || styleId === "MathBlock" || styleId === "HorizontalRule" || styleId !== void 0 && parseHeadingStyleId(styleId) !== void 0;
58
64
  }
59
65
  const BLANK_OR_WHITESPACE_ONLY_LINE = /^[ \t]*$/;
60
66
  function firstContentLineIndex(text) {
61
67
  return text.split(LINE_ENDING_PATTERN).findIndex((line) => !BLANK_OR_WHITESPACE_ONLY_LINE.test(line));
62
68
  }
63
- function leadingIndentColumns(line) {
69
+ function leadingIndentReachesCodeThreshold(line) {
64
70
  let column = 0;
65
- for (const char of line) if (char === " ") column += 1;
66
- else if (char === " ") column += 4 - column % 4;
67
- else break;
68
- return column;
71
+ while (line[column] === " ") column += 1;
72
+ if (column >= 4) return true;
73
+ return line[column] === " ";
69
74
  }
70
75
  const MAX_LEADING_BLANK_LINES_FOR_SETEXT = 1;
71
76
  function interruptsSetextParagraph(line, atBlockStart) {
@@ -82,19 +87,17 @@ function tableDelimiterRowPromotesPrecedingLine(line, precedingLine) {
82
87
  return parseTableDelimiterRow(cursor.restFromNextNonspace())?.length === splitTableRow(precedingLine).length;
83
88
  }
84
89
  function unsafeSetextBreakReason(text) {
85
- let sawContentLine = false;
86
90
  let leadingBlankLines = 0;
87
- let precedingLine = "";
91
+ let precedingLine;
88
92
  for (const line of text.split(LINE_ENDING_PATTERN)) {
89
93
  const isBlank = BLANK_OR_WHITESPACE_ONLY_LINE.test(line);
90
- if (!sawContentLine) {
94
+ if (precedingLine === void 0) {
91
95
  if (isBlank) {
92
96
  leadingBlankLines += 1;
93
97
  if (leadingBlankLines > MAX_LEADING_BLANK_LINES_FOR_SETEXT) return "blank-line";
94
98
  continue;
95
99
  }
96
- sawContentLine = true;
97
- if (leadingIndentColumns(line) >= 4) return "leading-indentation";
100
+ if (leadingIndentReachesCodeThreshold(line)) return "leading-indentation";
98
101
  if (interruptsSetextParagraph(line, true)) return "interrupting-line";
99
102
  precedingLine = line;
100
103
  continue;
@@ -103,7 +106,7 @@ function unsafeSetextBreakReason(text) {
103
106
  if (interruptsSetextParagraph(line, false) || tableDelimiterRowPromotesPrecedingLine(line, precedingLine)) return "interrupting-line";
104
107
  precedingLine = line;
105
108
  }
106
- return sawContentLine ? void 0 : "blank-line";
109
+ return precedingLine === void 0 ? "blank-line" : void 0;
107
110
  }
108
111
  function embedsUnsafeBreakForSetext(text) {
109
112
  return unsafeSetextBreakReason(text) !== void 0;
@@ -123,7 +126,7 @@ function willRenderAsSetext(paragraph, level, context) {
123
126
  }
124
127
  function canInterruptOpenParagraph(paragraph, context) {
125
128
  const styleId = paragraph.styleId;
126
- if (styleId === void 0 || styleId === "Quote" || styleId === "HTMLPreformatted") return false;
129
+ if (styleId === void 0) return false;
127
130
  if (styleId === "CodeBlock" || styleId === "MathBlock") return true;
128
131
  if (styleId === "HorizontalRule") return context.thematicBreakChar !== SETEXT_LEVEL_2_CHAR;
129
132
  const headingLevel = parseHeadingStyleId(styleId);
@@ -176,18 +179,14 @@ function renderParagraphBody(paragraph, context) {
176
179
  severity: "info",
177
180
  message: unsafeSetextDiagnosticMessage(level, unsafeSetextReason, embedsLineBreak)
178
181
  });
179
- if (embedsLineBreak) return `${"#".repeat(level)} ${text.replace(ESCAPED_HARD_BREAK_PATTERN, " ").split(LINE_ENDING_PATTERN).join(" ")}`;
180
- return `${"#".repeat(level)} ${text}`;
181
- }
182
- if (embedsLineBreak) {
183
- context.sink({
184
- code: MarkdownDiagnosticCodes.HEADING_LINE_BREAK_COLLAPSED,
185
- severity: "info",
186
- message: `a level ${String(level)} heading's own content contains a line break; only setext's own level-1/2 grammar can hold one, so ATX collapses it to a single space`
187
- });
188
- return `${"#".repeat(level)} ${text.replace(ESCAPED_HARD_BREAK_PATTERN, " ").split(LINE_ENDING_PATTERN).join(" ")}`;
182
+ return renderAtxHeading(level, text);
189
183
  }
190
- return `${"#".repeat(level)} ${text}`;
184
+ if (embedsLineBreak) context.sink({
185
+ code: MarkdownDiagnosticCodes.HEADING_LINE_BREAK_COLLAPSED,
186
+ severity: "info",
187
+ message: `a level ${String(level)} heading's own content contains a line break; only setext's own level-1/2 grammar can hold one, so ATX collapses it to a single space`
188
+ });
189
+ return renderAtxHeading(level, text);
191
190
  }
192
191
  return emitRuns(paragraph.runs, context, paragraph.constructs);
193
192
  }
@@ -263,24 +262,24 @@ function toEmitItem(item) {
263
262
  function firstBlockCheckbox(first, taskNumId) {
264
263
  if (first.list.checked !== void 0) return {
265
264
  checkboxText: first.list.checked ? "[x] " : "[ ] ",
266
- stripGlyph: false
265
+ strippedFirstBlock: void 0
267
266
  };
268
267
  if (!taskNumId || first.kind !== "paragraph") return {
269
268
  checkboxText: "",
270
- stripGlyph: false
269
+ strippedFirstBlock: void 0
271
270
  };
272
- const leading = first.block.runs[0]?.text ?? "";
273
- if (leading.startsWith(`☒ `)) return {
271
+ const leading = first.block.runs[0]?.text;
272
+ if (leading?.startsWith(`☒ `) === true) return {
274
273
  checkboxText: "[x] ",
275
- stripGlyph: true
274
+ strippedFirstBlock: stripCheckboxRun(first.block)
276
275
  };
277
- if (leading.startsWith(`☐ `)) return {
276
+ if (leading?.startsWith(`☐ `) === true) return {
278
277
  checkboxText: "[ ] ",
279
- stripGlyph: true
278
+ strippedFirstBlock: stripCheckboxRun(first.block)
280
279
  };
281
280
  return {
282
281
  checkboxText: "",
283
- stripGlyph: false
282
+ strippedFirstBlock: void 0
284
283
  };
285
284
  }
286
285
  function renderListItemMarker(numId, info, glyph, checkboxText, context) {
@@ -315,7 +314,7 @@ function resolveListGlyph(numId, type, previousSibling, context) {
315
314
  }
316
315
  function consumeSameItemRun(items, from, level, itemId) {
317
316
  let end = from;
318
- while (end < items.length) {
317
+ for (;;) {
319
318
  const candidate = items[end];
320
319
  if (candidate?.list.level !== level || candidate.list.itemId !== itemId) break;
321
320
  end += 1;
@@ -333,7 +332,7 @@ function collectListItem(items, start, level, itemId) {
333
332
  index = ownEnd;
334
333
  for (;;) {
335
334
  let nestedEnd = index;
336
- while (nestedEnd < items.length) {
335
+ for (;;) {
337
336
  const candidateLevel = items[nestedEnd]?.list.level;
338
337
  if (candidateLevel === void 0 || candidateLevel <= level) break;
339
338
  nestedEnd += 1;
@@ -346,7 +345,6 @@ function collectListItem(items, start, level, itemId) {
346
345
  index = nestedEnd;
347
346
  if (itemId === void 0) break;
348
347
  const resumedEnd = consumeSameItemRun(items, index, level, itemId);
349
- if (resumedEnd === index) break;
350
348
  segments.push({
351
349
  kind: "own",
352
350
  blocks: items.slice(index, resumedEnd)
@@ -377,8 +375,8 @@ function regionItemCanInterrupt(item, context) {
377
375
  function lastStyleIdOfRegionItem(item) {
378
376
  return lastStyleIdOf(toEmitItem(item));
379
377
  }
380
- function listRegionItemBody(item, context, stripGlyph) {
381
- if (item.kind === "paragraph") return renderParagraphBody(stripGlyph ? stripCheckboxRun(item.block) : item.block, context);
378
+ function listRegionItemBody(item, context, overrideParagraph) {
379
+ if (item.kind === "paragraph") return renderParagraphBody(overrideParagraph ?? item.block, context);
382
380
  const previousEnclosingItemId = context.enclosingItemId;
383
381
  context.enclosingItemId = item.list.itemId;
384
382
  const body = renderConstruct(item.item, context);
@@ -393,14 +391,14 @@ function renderListRegion(items, context) {
393
391
  const parts = [];
394
392
  let index = 0;
395
393
  let previousSibling;
396
- while (index < items.length) {
394
+ for (;;) {
397
395
  const item = items[index];
398
396
  if (item === void 0) break;
399
397
  const { numId, level, itemId } = item.list;
400
398
  const info = listInfoFor(numId, context);
401
399
  const loose = info?.loose === true;
402
400
  const type = info?.type ?? "bullet";
403
- const glyph = numId === void 0 ? type === "ordered" ? context.orderedDelimiter : context.bulletMarker : resolveListGlyph(numId, type, previousSibling, context);
401
+ const glyph = numId === void 0 ? context.bulletMarker : resolveListGlyph(numId, type, previousSibling, context);
404
402
  if (numId !== void 0) previousSibling = {
405
403
  numId,
406
404
  type,
@@ -409,10 +407,10 @@ function renderListRegion(items, context) {
409
407
  const { segments, next } = collectListItem(items, index, level, itemId);
410
408
  const first = segments[0]?.blocks[0];
411
409
  if (first === void 0) break;
412
- const { checkboxText, stripGlyph } = firstBlockCheckbox(first, info?.task === true);
410
+ const { checkboxText, strippedFirstBlock } = firstBlockCheckbox(first, info?.task === true);
413
411
  const marker = renderListItemMarker(numId, info, glyph, checkboxText, context);
414
412
  const indent = " ".repeat(marker.bareLength);
415
- let text = "";
413
+ let text = marker.full;
416
414
  let renderedFirstLine = false;
417
415
  let previousStyleId;
418
416
  for (const segment of segments) {
@@ -425,13 +423,13 @@ function renderListRegion(items, context) {
425
423
  }
426
424
  for (const block of segment.blocks) {
427
425
  if (!renderedFirstLine) {
428
- const [firstLine = "", ...restLines] = listRegionItemBody(block, context, stripGlyph).split("\n");
429
- text = [`${marker.full}${firstLine}`, ...restLines.map((line) => `${indent}${line}`)].join("\n");
426
+ const [firstLine, ...restLines] = splitLines(listRegionItemBody(block, context, strippedFirstBlock), "\n");
427
+ text += [firstLine, ...restLines.map((line) => `${indent}${line}`)].join("\n");
430
428
  renderedFirstLine = true;
431
429
  previousStyleId = lastStyleIdOfRegionItem(block);
432
430
  continue;
433
431
  }
434
- const rendered = listRegionItemBody(block, context, false).split("\n").map((line) => line.length === 0 ? line : `${indent}${line}`).join("\n");
432
+ const rendered = listRegionItemBody(block, context, void 0).split("\n").map((line) => line.length === 0 ? line : `${indent}${line}`).join("\n");
435
433
  const blank = loose || requiresBlankLineBefore(block, previousStyleId, context);
436
434
  text += blank ? `\n\n${rendered}` : `\n${rendered}`;
437
435
  previousStyleId = lastStyleIdOfRegionItem(block);
@@ -447,9 +445,8 @@ function renderListRegion(items, context) {
447
445
  for (const [partIndex, part] of parts.entries()) {
448
446
  if (partIndex > 0) {
449
447
  const previous = parts[partIndex - 1];
450
- const sameList = previous.numId === part.numId;
451
- const loose = sameList && previous.numId !== void 0 && (parseListNumId(previous.numId)?.loose ?? false);
452
- out += sameList && !loose ? "\n" : "\n\n";
448
+ const continuesTightly = previous.numId === part.numId && (previous.numId === void 0 || parseListNumId(previous.numId)?.loose !== true);
449
+ out += continuesTightly ? "\n" : "\n\n";
453
450
  }
454
451
  out += part.text;
455
452
  }
@@ -458,16 +455,17 @@ function renderListRegion(items, context) {
458
455
  function isConstructItem(item) {
459
456
  return "descriptor" in item;
460
457
  }
458
+ const UNINDENTED_PT = 0;
461
459
  function isMaterialisedDivision(item) {
462
460
  return item.descriptor.kind === "division" && item.children.every((child) => {
463
461
  if (isConstructItem(child)) return true;
464
- return child.block.kind !== "paragraph" || child.block.indentLeftPt !== void 0 && child.block.indentLeftPt >= 36;
462
+ return child.block.kind !== "paragraph" || (child.block.indentLeftPt ?? UNINDENTED_PT) >= 36;
465
463
  });
466
464
  }
467
465
  function groupConstructItems(blocks, start) {
468
466
  const items = [];
469
467
  let index = start;
470
- while (index < blocks.length) {
468
+ for (;;) {
471
469
  const block = blocks[index];
472
470
  if (block === void 0) break;
473
471
  index += 1;
@@ -496,7 +494,7 @@ function renderFootnoteDefinition(name, body) {
496
494
  const marker = `[^${name}]:`;
497
495
  if (body.length === 0) return marker;
498
496
  const indent = " ".repeat(FOOTNOTE_CONTINUATION_INDENT);
499
- const [firstLine = "", ...restLines] = body.split("\n");
497
+ const [firstLine, ...restLines] = splitLines(body, "\n");
500
498
  return [`${marker} ${firstLine}`, ...restLines.map((line) => line.length === 0 ? line : `${indent}${line}`)].join("\n");
501
499
  }
502
500
  function renderConstruct(item, context) {
@@ -511,13 +509,11 @@ function renderConstruct(item, context) {
511
509
  });
512
510
  return body;
513
511
  }
514
- if (descriptor.kind === "division") {
515
- if (isMaterialisedDivision(item)) {
516
- context.divisionDepth += 1;
517
- const body = renderItems(item.children, context);
518
- context.divisionDepth -= 1;
519
- return body.split("\n").map((line) => line.length === 0 ? ">" : `> ${line}`).join("\n");
520
- }
512
+ if (isMaterialisedDivision(item)) {
513
+ context.divisionDepth += 1;
514
+ const body = renderItems(item.children, context);
515
+ context.divisionDepth -= 1;
516
+ return body.split("\n").map((line) => line.length === 0 ? ">" : `> ${line}`).join("\n");
521
517
  }
522
518
  if (descriptor.kind === "link" && descriptor.target.kind === "external") {
523
519
  const onlyChild = item.children.length === 1 && !isConstructItem(item.children[0]) ? item.children[0].block : void 0;
@@ -548,7 +544,7 @@ function isInheritedListMembership(list, context) {
548
544
  function renderItems(items, context) {
549
545
  const parts = [];
550
546
  let index = 0;
551
- while (index < items.length) {
547
+ for (;;) {
552
548
  const item = items[index];
553
549
  if (item === void 0) break;
554
550
  if (isConstructItem(item)) {
@@ -611,7 +607,7 @@ function emitBlocks(blocks, context) {
611
607
  }
612
608
  function validateRunConstructExtents(blocks) {
613
609
  for (const block of blocks) {
614
- if (block.kind === "paragraph" && block.constructs !== void 0) {
610
+ if (block.kind === "paragraph") {
615
611
  const fault = findRunConstructFault(block);
616
612
  if (fault !== void 0) throw new MarkdownInvalidRunConstructExtentError(fault.kind, fault.index);
617
613
  }
@@ -39,11 +39,12 @@ const ESCAPE_CHARS = /* @__PURE__ */ new Set([
39
39
  function escapeMarkdownText(text) {
40
40
  let out = "";
41
41
  let index = 0;
42
- while (index < text.length) {
43
- const char = text.charAt(index);
42
+ for (;;) {
43
+ const char = text[index];
44
+ if (char === void 0) break;
44
45
  if (char === "\n" || char === "\r") {
45
46
  out += "\\\n";
46
- index += char === "\r" && text.charAt(index + 1) === "\n" ? 2 : 1;
47
+ index += char === "\r" && text[index + 1] === "\n" ? 2 : 1;
47
48
  continue;
48
49
  }
49
50
  if (ESCAPE_CHARS.has(char)) {
@@ -64,9 +65,8 @@ function renderCodeSpan(text) {
64
65
  longestBacktickRun = Math.max(longestBacktickRun, current);
65
66
  } else current = 0;
66
67
  const fence = "`".repeat(longestBacktickRun + 1);
67
- const isAllSpaces = text.length > 0 && text.trim().length === 0;
68
68
  const risksFenceCollision = text.startsWith("`") || text.endsWith("`");
69
- const wouldBeStrippedOnReparse = !isAllSpaces && text.startsWith(" ") && text.endsWith(" ");
69
+ const wouldBeStrippedOnReparse = text.startsWith(" ") && text.endsWith(" ") && text.trim().length > 0;
70
70
  return risksFenceCollision || wouldBeStrippedOnReparse ? `${fence} ${text} ${fence}` : `${fence}${text}${fence}`;
71
71
  }
72
72
  function footnoteReferenceAt(constructs, index) {
@@ -102,12 +102,11 @@ function styleActive(run, key) {
102
102
  }
103
103
  const WORD_CHAR_PATTERN = /[\p{L}\p{N}]/u;
104
104
  function isIntrawordRisk(body) {
105
- if (body.length === 0) return false;
106
105
  return WORD_CHAR_PATTERN.test(body.charAt(0)) || WORD_CHAR_PATTERN.test(body.charAt(body.length - 1));
107
106
  }
108
107
  function hasMarkerConflict(body, candidate, precedingText) {
109
108
  if (candidate === "_" && isIntrawordRisk(body)) return true;
110
- if (body.length > 0 && (body.startsWith(candidate) || body.endsWith(candidate))) return true;
109
+ if (body.startsWith(candidate) || body.endsWith(candidate)) return true;
111
110
  return precedingText.endsWith(candidate);
112
111
  }
113
112
  function pickEmphasisMarker(body, configured, precedingText) {
@@ -135,7 +134,7 @@ function groupCount(runs, key) {
135
134
  function pickSplitKey(runs, remaining) {
136
135
  let best = remaining[0];
137
136
  let bestCount = groupCount(runs, best);
138
- for (const key of remaining.slice(1)) {
137
+ for (const key of remaining) {
139
138
  const count = groupCount(runs, key);
140
139
  if (count < bestCount) {
141
140
  best = key;
@@ -150,7 +149,7 @@ function renderNestedStyles(runs, remainingKeys, context, base, constructs) {
150
149
  const rest = remainingKeys.filter((candidate) => candidate !== key);
151
150
  let out = "";
152
151
  let index = 0;
153
- while (index < runs.length) {
152
+ for (;;) {
154
153
  const current = runs[index];
155
154
  if (current === void 0) break;
156
155
  const active = styleActive(current, key);
@@ -186,19 +185,26 @@ function linkTitleCovering(constructs, start, end) {
186
185
  function emitRuns(runs, context, constructs) {
187
186
  let out = "";
188
187
  let index = 0;
189
- while (index < runs.length) {
188
+ for (;;) {
190
189
  const run = runs[index];
191
190
  if (run === void 0) break;
192
191
  if (run.hyperlink === void 0) {
193
192
  let end = index + 1;
194
- while (end < runs.length && runs[end]?.hyperlink === void 0) end += 1;
193
+ for (;;) {
194
+ const next = runs[end];
195
+ if (next === void 0 || next.hyperlink !== void 0) break;
196
+ end += 1;
197
+ }
195
198
  out += renderNestedStyles(runs.slice(index, end), STYLE_KEYS, context, index, constructs);
196
199
  index = end;
197
200
  continue;
198
201
  }
199
202
  const hyperlink = run.hyperlink;
200
203
  let groupEnd = index + 1;
201
- while (groupEnd < runs.length && runs[groupEnd]?.hyperlink === hyperlink) groupEnd += 1;
204
+ for (;;) {
205
+ if (runs[groupEnd]?.hyperlink !== hyperlink) break;
206
+ groupEnd += 1;
207
+ }
202
208
  const group = runs.slice(index, groupEnd);
203
209
  if (group.length > 1) context.sink({
204
210
  code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.ADJACENT_LINKS_MERGED,
@@ -38,11 +38,12 @@ const ESCAPE_CHARS = /* @__PURE__ */ new Set([
38
38
  function escapeMarkdownText(text) {
39
39
  let out = "";
40
40
  let index = 0;
41
- while (index < text.length) {
42
- const char = text.charAt(index);
41
+ for (;;) {
42
+ const char = text[index];
43
+ if (char === void 0) break;
43
44
  if (char === "\n" || char === "\r") {
44
45
  out += "\\\n";
45
- index += char === "\r" && text.charAt(index + 1) === "\n" ? 2 : 1;
46
+ index += char === "\r" && text[index + 1] === "\n" ? 2 : 1;
46
47
  continue;
47
48
  }
48
49
  if (ESCAPE_CHARS.has(char)) {
@@ -63,9 +64,8 @@ function renderCodeSpan(text) {
63
64
  longestBacktickRun = Math.max(longestBacktickRun, current);
64
65
  } else current = 0;
65
66
  const fence = "`".repeat(longestBacktickRun + 1);
66
- const isAllSpaces = text.length > 0 && text.trim().length === 0;
67
67
  const risksFenceCollision = text.startsWith("`") || text.endsWith("`");
68
- const wouldBeStrippedOnReparse = !isAllSpaces && text.startsWith(" ") && text.endsWith(" ");
68
+ const wouldBeStrippedOnReparse = text.startsWith(" ") && text.endsWith(" ") && text.trim().length > 0;
69
69
  return risksFenceCollision || wouldBeStrippedOnReparse ? `${fence} ${text} ${fence}` : `${fence}${text}${fence}`;
70
70
  }
71
71
  function footnoteReferenceAt(constructs, index) {
@@ -101,12 +101,11 @@ function styleActive(run, key) {
101
101
  }
102
102
  const WORD_CHAR_PATTERN = /[\p{L}\p{N}]/u;
103
103
  function isIntrawordRisk(body) {
104
- if (body.length === 0) return false;
105
104
  return WORD_CHAR_PATTERN.test(body.charAt(0)) || WORD_CHAR_PATTERN.test(body.charAt(body.length - 1));
106
105
  }
107
106
  function hasMarkerConflict(body, candidate, precedingText) {
108
107
  if (candidate === "_" && isIntrawordRisk(body)) return true;
109
- if (body.length > 0 && (body.startsWith(candidate) || body.endsWith(candidate))) return true;
108
+ if (body.startsWith(candidate) || body.endsWith(candidate)) return true;
110
109
  return precedingText.endsWith(candidate);
111
110
  }
112
111
  function pickEmphasisMarker(body, configured, precedingText) {
@@ -134,7 +133,7 @@ function groupCount(runs, key) {
134
133
  function pickSplitKey(runs, remaining) {
135
134
  let best = remaining[0];
136
135
  let bestCount = groupCount(runs, best);
137
- for (const key of remaining.slice(1)) {
136
+ for (const key of remaining) {
138
137
  const count = groupCount(runs, key);
139
138
  if (count < bestCount) {
140
139
  best = key;
@@ -149,7 +148,7 @@ function renderNestedStyles(runs, remainingKeys, context, base, constructs) {
149
148
  const rest = remainingKeys.filter((candidate) => candidate !== key);
150
149
  let out = "";
151
150
  let index = 0;
152
- while (index < runs.length) {
151
+ for (;;) {
153
152
  const current = runs[index];
154
153
  if (current === void 0) break;
155
154
  const active = styleActive(current, key);
@@ -185,19 +184,26 @@ function linkTitleCovering(constructs, start, end) {
185
184
  function emitRuns(runs, context, constructs) {
186
185
  let out = "";
187
186
  let index = 0;
188
- while (index < runs.length) {
187
+ for (;;) {
189
188
  const run = runs[index];
190
189
  if (run === void 0) break;
191
190
  if (run.hyperlink === void 0) {
192
191
  let end = index + 1;
193
- while (end < runs.length && runs[end]?.hyperlink === void 0) end += 1;
192
+ for (;;) {
193
+ const next = runs[end];
194
+ if (next === void 0 || next.hyperlink !== void 0) break;
195
+ end += 1;
196
+ }
194
197
  out += renderNestedStyles(runs.slice(index, end), STYLE_KEYS, context, index, constructs);
195
198
  index = end;
196
199
  continue;
197
200
  }
198
201
  const hyperlink = run.hyperlink;
199
202
  let groupEnd = index + 1;
200
- while (groupEnd < runs.length && runs[groupEnd]?.hyperlink === hyperlink) groupEnd += 1;
203
+ for (;;) {
204
+ if (runs[groupEnd]?.hyperlink !== hyperlink) break;
205
+ groupEnd += 1;
206
+ }
201
207
  const group = runs.slice(index, groupEnd);
202
208
  if (group.length > 1) context.sink({
203
209
  code: MarkdownDiagnosticCodes.ADJACENT_LINKS_MERGED,
@@ -18,9 +18,9 @@ function delimiterCell(alignment) {
18
18
  function escapeUnescapedPipes(text) {
19
19
  let out = "";
20
20
  let index = 0;
21
- while (index < text.length) {
21
+ while (text.charAt(index) !== "") {
22
22
  const char = text.charAt(index);
23
- if (char === "\\" && index + 1 < text.length) {
23
+ if (char === "\\") {
24
24
  out += char + text.charAt(index + 1);
25
25
  index += 2;
26
26
  continue;
@@ -17,9 +17,9 @@ function delimiterCell(alignment) {
17
17
  function escapeUnescapedPipes(text) {
18
18
  let out = "";
19
19
  let index = 0;
20
- while (index < text.length) {
20
+ while (text.charAt(index) !== "") {
21
21
  const char = text.charAt(index);
22
- if (char === "\\" && index + 1 < text.length) {
22
+ if (char === "\\") {
23
23
  out += char + text.charAt(index + 1);
24
24
  index += 2;
25
25
  continue;
@@ -5,25 +5,21 @@ let document_schema_js = require("document-schema.js");
5
5
  //#region src/html/html-table.ts
6
6
  function findBalancedClose(text, openEnd, tagNames) {
7
7
  const names = tagNames.join("|");
8
- const openPattern = new RegExp(`<(?:${names})(?=[\\s/>])`, "gi");
9
- const closePattern = new RegExp(`</(?:${names})\\s*>`, "gi");
8
+ const tagPattern = new RegExp(`</(?:${names})\\s*>|<(?:${names})(?=[\\s/>])`, "gi");
10
9
  let depth = 1;
11
10
  let pos = openEnd;
12
- while (depth > 0) {
13
- openPattern.lastIndex = pos;
14
- closePattern.lastIndex = pos;
15
- const openMatch = openPattern.exec(text);
16
- const closeMatch = closePattern.exec(text);
17
- if (closeMatch === null) return;
18
- if (openMatch !== null && openMatch.index < closeMatch.index) {
11
+ for (;;) {
12
+ tagPattern.lastIndex = pos;
13
+ const match = tagPattern.exec(text);
14
+ if (match === null) return;
15
+ pos = match.index + match[0].length;
16
+ if (!match[0].startsWith("</")) {
19
17
  depth += 1;
20
- pos = openMatch.index + openMatch[0].length;
21
18
  continue;
22
19
  }
23
20
  depth -= 1;
24
- pos = closeMatch.index + closeMatch[0].length;
25
21
  if (depth === 0) return {
26
- start: closeMatch.index,
22
+ start: match.index,
27
23
  end: pos
28
24
  };
29
25
  }
@@ -37,7 +33,7 @@ function extractTopLevelElements(text, tagNames) {
37
33
  const match = openPattern.exec(text);
38
34
  if (match === null) break;
39
35
  if (text.slice(pos, match.index).trim().length > 0) return;
40
- const attrs = match[1] ?? "";
36
+ const attrs = match[1];
41
37
  const openEnd = match.index + match[0].length;
42
38
  const close = findBalancedClose(text, openEnd, tagNames);
43
39
  if (close === void 0) return;
@@ -136,15 +132,15 @@ function parseInlineHtml(text, style) {
136
132
  pushPlainRun(runs, rest, style);
137
133
  return runs;
138
134
  }
139
- pushPlainRun(runs, rest.slice(0, match.index), style);
140
135
  const tagName = match[1].toLowerCase();
141
- const attrs = match[2] ?? "";
136
+ const attrs = match[2];
142
137
  const openEnd = pos + match.index + match[0].length;
143
138
  const close = findBalancedClose(text, openEnd, [tagName]);
144
139
  if (close === void 0) {
145
140
  pushPlainRun(runs, rest, style);
146
141
  return runs;
147
142
  }
143
+ pushPlainRun(runs, rest.slice(0, match.index), style);
148
144
  runs.push(...parseInlineHtml(text.slice(openEnd, close.start), applyTagStyle(style, tagName, attrs)));
149
145
  pos = close.end;
150
146
  }
@@ -168,12 +164,10 @@ function parseHtmlImage(piece) {
168
164
  };
169
165
  }
170
166
  function parseCellBlocks(inner, contentWidthPt) {
171
- const trimmed = inner.trim();
172
- if (trimmed.length === 0) return [];
173
- const nestedTable = parseWholeTable(trimmed, contentWidthPt);
167
+ const nestedTable = parseWholeTable(inner, contentWidthPt);
174
168
  if (nestedTable !== void 0) return [nestedTable];
175
169
  const blocks = [];
176
- for (const segment of trimmed.split(/<br\s*\/?>/i)) {
170
+ for (const segment of inner.split(/<br\s*\/?>/i)) {
177
171
  const piece = segment.trim();
178
172
  if (piece.length === 0) continue;
179
173
  const image = parseHtmlImage(piece);
@@ -235,9 +229,7 @@ function parseWholeTable(text, contentWidthPt) {
235
229
  return rows === void 0 ? void 0 : buildContentTable(rows, contentWidthPt);
236
230
  }
237
231
  function parseHtmlTable(literal, contentWidthPt) {
238
- const trimmed = literal.trim();
239
- if (!/^<table\b/i.test(trimmed)) return;
240
- return parseWholeTable(trimmed, contentWidthPt);
232
+ return parseWholeTable(literal, contentWidthPt);
241
233
  }
242
234
  //#endregion
243
235
  exports.parseHtmlTable = parseHtmlTable;