markdown-codec 6.4.3 → 6.5.1

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 (54) hide show
  1. package/README.md +20 -7
  2. package/dist/block/block.cjs +1 -1
  3. package/dist/block/block.d.cts +1 -1
  4. package/dist/block/block.d.ts +1 -1
  5. package/dist/block/block.js +1 -1
  6. package/dist/block/definitions.d.cts +1 -1
  7. package/dist/block/definitions.d.ts +1 -1
  8. package/dist/codec.d.cts +170 -0
  9. package/dist/codec.d.ts +170 -0
  10. package/dist/diagnostics/diagnostics.cjs +2 -1
  11. package/dist/diagnostics/diagnostics.d.cts +1 -1
  12. package/dist/diagnostics/diagnostics.d.ts +1 -1
  13. package/dist/diagnostics/diagnostics.js +2 -1
  14. package/dist/{diagnostics-eL5c8OWm.d.cts → diagnostics-DLum7kY6.d.cts} +1 -0
  15. package/dist/{diagnostics-eL5c8OWm.d.ts → diagnostics-DLum7kY6.d.ts} +1 -0
  16. package/dist/emit/emit.cjs +28 -5
  17. package/dist/emit/emit.js +28 -5
  18. package/dist/emit/front-matter.cjs +22 -5
  19. package/dist/emit/front-matter.js +22 -5
  20. package/dist/emit/html-table.cjs +104 -0
  21. package/dist/emit/html-table.d.cts +7 -0
  22. package/dist/emit/html-table.d.ts +7 -0
  23. package/dist/emit/html-table.js +102 -0
  24. package/dist/emit/inline.cjs +30 -6
  25. package/dist/emit/inline.d.cts +1 -13
  26. package/dist/emit/inline.d.ts +1 -13
  27. package/dist/emit/inline.js +30 -6
  28. package/dist/emit/table.cjs +10 -13
  29. package/dist/emit/table.d.cts +2 -2
  30. package/dist/emit/table.d.ts +2 -2
  31. package/dist/emit/table.js +10 -13
  32. package/dist/html/html-table.cjs +243 -0
  33. package/dist/html/html-table.d.cts +5 -0
  34. package/dist/html/html-table.d.ts +5 -0
  35. package/dist/html/html-table.js +242 -0
  36. package/dist/index.cjs +1 -1
  37. package/dist/index.d.cts +1 -1
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.js +1 -1
  40. package/dist/inline-2-lUb0vo.d.ts +14 -0
  41. package/dist/inline-514HW3Pi.d.cts +14 -0
  42. package/dist/lower/front-matter.cjs +64 -20
  43. package/dist/lower/front-matter.d.cts +1 -1
  44. package/dist/lower/front-matter.d.ts +1 -1
  45. package/dist/lower/front-matter.js +64 -20
  46. package/dist/lower/inline.d.cts +1 -1
  47. package/dist/lower/inline.d.ts +1 -1
  48. package/dist/lower/lower.cjs +16 -5
  49. package/dist/lower/lower.js +16 -5
  50. package/dist/options/options.d.cts +1 -1
  51. package/dist/options/options.d.ts +1 -1
  52. package/dist/read.d.cts +1 -1
  53. package/dist/read.d.ts +1 -1
  54. package/package.json +2 -2
@@ -9,8 +9,8 @@ const require_block_line = require("../block/line.cjs");
9
9
  const require_block_list = require("../block/list.cjs");
10
10
  const require_block_table = require("../block/table.cjs");
11
11
  const require_block_block = require("../block/block.cjs");
12
- const require_shared_list_id = require("../shared/list-id.cjs");
13
12
  const require_shared_style_constants = require("../shared/style-constants.cjs");
13
+ const require_shared_list_id = require("../shared/list-id.cjs");
14
14
  const require_emit_front_matter = require("./front-matter.cjs");
15
15
  const require_emit_inline = require("./inline.cjs");
16
16
  const require_emit_image = require("./image.cjs");
@@ -284,22 +284,36 @@ function firstBlockCheckbox(first, taskNumId) {
284
284
  stripGlyph: false
285
285
  };
286
286
  }
287
- function renderListItemMarker(numId, info, checkboxText, context) {
287
+ function renderListItemMarker(numId, info, glyph, checkboxText, context) {
288
288
  if (info?.type === "ordered" && numId !== void 0) {
289
289
  const next = context.orderedCounters.get(numId) ?? info.start ?? 1;
290
290
  context.orderedCounters.set(numId, next + 1);
291
- const bare = `${String(next)}${context.orderedDelimiter} `;
291
+ const bare = `${String(next)}${glyph} `;
292
292
  return {
293
293
  full: `${bare}${checkboxText}`,
294
294
  bareLength: bare.length
295
295
  };
296
296
  }
297
- const bare = `${context.bulletMarker} `;
297
+ const bare = `${glyph} `;
298
298
  return {
299
299
  full: `${bare}${checkboxText}`,
300
300
  bareLength: bare.length
301
301
  };
302
302
  }
303
+ const BULLET_GLYPH_CANDIDATES = [
304
+ "-",
305
+ "+",
306
+ "*"
307
+ ];
308
+ const ORDERED_GLYPH_CANDIDATES = [".", ")"];
309
+ function resolveListGlyph(numId, type, previousSibling, context) {
310
+ const cached = context.resolvedListGlyphs.get(numId);
311
+ if (cached !== void 0) return cached;
312
+ const defaultGlyph = type === "ordered" ? context.orderedDelimiter : context.bulletMarker;
313
+ const glyph = previousSibling?.type === type && previousSibling.glyph === defaultGlyph ? (type === "ordered" ? ORDERED_GLYPH_CANDIDATES : BULLET_GLYPH_CANDIDATES).find((candidate) => candidate !== defaultGlyph) ?? defaultGlyph : defaultGlyph;
314
+ context.resolvedListGlyphs.set(numId, glyph);
315
+ return glyph;
316
+ }
303
317
  function consumeSameItemRun(items, from, level, itemId) {
304
318
  let end = from;
305
319
  while (end < items.length) {
@@ -379,17 +393,25 @@ function requiresBlankLineBefore(next, previousStyleId, context) {
379
393
  function renderListRegion(items, context) {
380
394
  const parts = [];
381
395
  let index = 0;
396
+ let previousSibling;
382
397
  while (index < items.length) {
383
398
  const item = items[index];
384
399
  if (item === void 0) break;
385
400
  const { numId, level, itemId } = item.list;
386
401
  const info = listInfoFor(numId, context);
387
402
  const loose = info?.loose === true;
403
+ const type = info?.type ?? "bullet";
404
+ const glyph = numId === void 0 ? type === "ordered" ? context.orderedDelimiter : context.bulletMarker : resolveListGlyph(numId, type, previousSibling, context);
405
+ if (numId !== void 0) previousSibling = {
406
+ numId,
407
+ type,
408
+ glyph
409
+ };
388
410
  const { segments, next } = collectListItem(items, index, level, itemId);
389
411
  const first = segments[0]?.blocks[0];
390
412
  if (first === void 0) break;
391
413
  const { checkboxText, stripGlyph } = firstBlockCheckbox(first, info?.task === true);
392
- const marker = renderListItemMarker(numId, info, checkboxText, context);
414
+ const marker = renderListItemMarker(numId, info, glyph, checkboxText, context);
393
415
  const indent = " ".repeat(marker.bareLength);
394
416
  let text = "";
395
417
  let renderedFirstLine = false;
@@ -609,6 +631,7 @@ function emitMarkdown(document, options = {}) {
609
631
  headingStyle: options.headingStyle ?? "atx",
610
632
  embedImages: options.images ?? true,
611
633
  orderedCounters: /* @__PURE__ */ new Map(),
634
+ resolvedListGlyphs: /* @__PURE__ */ new Map(),
612
635
  reportedFallbackNumIds: /* @__PURE__ */ new Set(),
613
636
  reportedAbsentNumIdFallback: false,
614
637
  divisionDepth: 0,
package/dist/emit/emit.js CHANGED
@@ -8,8 +8,8 @@ import { LineCursor } from "../block/line.js";
8
8
  import { parseListMarker } from "../block/list.js";
9
9
  import { parseTableDelimiterRow, splitTableRow } from "../block/table.js";
10
10
  import { ATX_MARKER_PATTERN, CODE_FENCE_PATTERN, MATH_BLOCK_MARKER_PATTERN, THEMATIC_BREAK_PATTERN } from "../block/block.js";
11
- import { parseListNumId } from "../shared/list-id.js";
12
11
  import { CODE_BLOCK_STYLE_ID, HORIZONTAL_RULE_STYLE_ID, HTML_PREFORMATTED_STYLE_ID, MATH_BLOCK_STYLE_ID, QUOTE_STYLE_ID, parseHeadingStyleId } from "../shared/style-constants.js";
12
+ import { parseListNumId } from "../shared/list-id.js";
13
13
  import { emitFrontMatter } from "./front-matter.js";
14
14
  import { emitRuns, escapeLinkDestination, escapeMarkdownText, renderLinkTitle } from "./inline.js";
15
15
  import { emitImage } from "./image.js";
@@ -283,22 +283,36 @@ function firstBlockCheckbox(first, taskNumId) {
283
283
  stripGlyph: false
284
284
  };
285
285
  }
286
- function renderListItemMarker(numId, info, checkboxText, context) {
286
+ function renderListItemMarker(numId, info, glyph, checkboxText, context) {
287
287
  if (info?.type === "ordered" && numId !== void 0) {
288
288
  const next = context.orderedCounters.get(numId) ?? info.start ?? 1;
289
289
  context.orderedCounters.set(numId, next + 1);
290
- const bare = `${String(next)}${context.orderedDelimiter} `;
290
+ const bare = `${String(next)}${glyph} `;
291
291
  return {
292
292
  full: `${bare}${checkboxText}`,
293
293
  bareLength: bare.length
294
294
  };
295
295
  }
296
- const bare = `${context.bulletMarker} `;
296
+ const bare = `${glyph} `;
297
297
  return {
298
298
  full: `${bare}${checkboxText}`,
299
299
  bareLength: bare.length
300
300
  };
301
301
  }
302
+ const BULLET_GLYPH_CANDIDATES = [
303
+ "-",
304
+ "+",
305
+ "*"
306
+ ];
307
+ const ORDERED_GLYPH_CANDIDATES = [".", ")"];
308
+ function resolveListGlyph(numId, type, previousSibling, context) {
309
+ const cached = context.resolvedListGlyphs.get(numId);
310
+ if (cached !== void 0) return cached;
311
+ const defaultGlyph = type === "ordered" ? context.orderedDelimiter : context.bulletMarker;
312
+ const glyph = previousSibling?.type === type && previousSibling.glyph === defaultGlyph ? (type === "ordered" ? ORDERED_GLYPH_CANDIDATES : BULLET_GLYPH_CANDIDATES).find((candidate) => candidate !== defaultGlyph) ?? defaultGlyph : defaultGlyph;
313
+ context.resolvedListGlyphs.set(numId, glyph);
314
+ return glyph;
315
+ }
302
316
  function consumeSameItemRun(items, from, level, itemId) {
303
317
  let end = from;
304
318
  while (end < items.length) {
@@ -378,17 +392,25 @@ function requiresBlankLineBefore(next, previousStyleId, context) {
378
392
  function renderListRegion(items, context) {
379
393
  const parts = [];
380
394
  let index = 0;
395
+ let previousSibling;
381
396
  while (index < items.length) {
382
397
  const item = items[index];
383
398
  if (item === void 0) break;
384
399
  const { numId, level, itemId } = item.list;
385
400
  const info = listInfoFor(numId, context);
386
401
  const loose = info?.loose === true;
402
+ const type = info?.type ?? "bullet";
403
+ const glyph = numId === void 0 ? type === "ordered" ? context.orderedDelimiter : context.bulletMarker : resolveListGlyph(numId, type, previousSibling, context);
404
+ if (numId !== void 0) previousSibling = {
405
+ numId,
406
+ type,
407
+ glyph
408
+ };
387
409
  const { segments, next } = collectListItem(items, index, level, itemId);
388
410
  const first = segments[0]?.blocks[0];
389
411
  if (first === void 0) break;
390
412
  const { checkboxText, stripGlyph } = firstBlockCheckbox(first, info?.task === true);
391
- const marker = renderListItemMarker(numId, info, checkboxText, context);
413
+ const marker = renderListItemMarker(numId, info, glyph, checkboxText, context);
392
414
  const indent = " ".repeat(marker.bareLength);
393
415
  let text = "";
394
416
  let renderedFirstLine = false;
@@ -608,6 +630,7 @@ function emitMarkdown(document, options = {}) {
608
630
  headingStyle: options.headingStyle ?? "atx",
609
631
  embedImages: options.images ?? true,
610
632
  orderedCounters: /* @__PURE__ */ new Map(),
633
+ resolvedListGlyphs: /* @__PURE__ */ new Map(),
611
634
  reportedFallbackNumIds: /* @__PURE__ */ new Set(),
612
635
  reportedAbsentNumIdFallback: false,
613
636
  divisionDepth: 0,
@@ -5,13 +5,30 @@ function emitScalar(value) {
5
5
  if (!NEEDS_QUOTING_PATTERN.test(value) && value.trim() === value && value.length > 0) return value;
6
6
  return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
7
7
  }
8
+ const STRING_FIELD_ENTRIES = [
9
+ ["title", (metadata) => metadata.title],
10
+ ["author", (metadata) => metadata.author],
11
+ ["subject", (metadata) => metadata.subject],
12
+ ["creator", (metadata) => metadata.creator],
13
+ ["date", (metadata) => metadata.createdIso],
14
+ ["modified", (metadata) => metadata.modifiedIso],
15
+ ["lastPrinted", (metadata) => metadata.lastPrintedIso],
16
+ ["language", (metadata) => metadata.language],
17
+ ["publisher", (metadata) => metadata.publisher],
18
+ ["contributor", (metadata) => metadata.contributor],
19
+ ["rights", (metadata) => metadata.rights],
20
+ ["identifier", (metadata) => metadata.identifier],
21
+ ["comments", (metadata) => metadata.comments],
22
+ ["company", (metadata) => metadata.company],
23
+ ["manager", (metadata) => metadata.manager]
24
+ ];
8
25
  function emitFrontMatter(metadata) {
9
26
  const lines = [];
10
- if (metadata.title !== void 0) lines.push(`title: ${emitScalar(metadata.title)}`);
11
- if (metadata.author !== void 0) lines.push(`author: ${emitScalar(metadata.author)}`);
12
- if (metadata.subject !== void 0) lines.push(`subject: ${emitScalar(metadata.subject)}`);
13
- if (metadata.creator !== void 0) lines.push(`creator: ${emitScalar(metadata.creator)}`);
14
- if (metadata.createdIso !== void 0) lines.push(`date: ${emitScalar(metadata.createdIso)}`);
27
+ for (const [key, accessor] of STRING_FIELD_ENTRIES) {
28
+ const value = accessor(metadata);
29
+ if (value !== void 0) lines.push(`${key}: ${emitScalar(value)}`);
30
+ }
31
+ if (metadata.direction !== void 0) lines.push(`direction: ${metadata.direction}`);
15
32
  if (metadata.keywords !== void 0 && metadata.keywords.length > 0) lines.push(`keywords: [${metadata.keywords.map((keyword) => emitScalar(keyword)).join(", ")}]`);
16
33
  if (lines.length === 0) return;
17
34
  return [
@@ -4,13 +4,30 @@ function emitScalar(value) {
4
4
  if (!NEEDS_QUOTING_PATTERN.test(value) && value.trim() === value && value.length > 0) return value;
5
5
  return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
6
6
  }
7
+ const STRING_FIELD_ENTRIES = [
8
+ ["title", (metadata) => metadata.title],
9
+ ["author", (metadata) => metadata.author],
10
+ ["subject", (metadata) => metadata.subject],
11
+ ["creator", (metadata) => metadata.creator],
12
+ ["date", (metadata) => metadata.createdIso],
13
+ ["modified", (metadata) => metadata.modifiedIso],
14
+ ["lastPrinted", (metadata) => metadata.lastPrintedIso],
15
+ ["language", (metadata) => metadata.language],
16
+ ["publisher", (metadata) => metadata.publisher],
17
+ ["contributor", (metadata) => metadata.contributor],
18
+ ["rights", (metadata) => metadata.rights],
19
+ ["identifier", (metadata) => metadata.identifier],
20
+ ["comments", (metadata) => metadata.comments],
21
+ ["company", (metadata) => metadata.company],
22
+ ["manager", (metadata) => metadata.manager]
23
+ ];
7
24
  function emitFrontMatter(metadata) {
8
25
  const lines = [];
9
- if (metadata.title !== void 0) lines.push(`title: ${emitScalar(metadata.title)}`);
10
- if (metadata.author !== void 0) lines.push(`author: ${emitScalar(metadata.author)}`);
11
- if (metadata.subject !== void 0) lines.push(`subject: ${emitScalar(metadata.subject)}`);
12
- if (metadata.creator !== void 0) lines.push(`creator: ${emitScalar(metadata.creator)}`);
13
- if (metadata.createdIso !== void 0) lines.push(`date: ${emitScalar(metadata.createdIso)}`);
26
+ for (const [key, accessor] of STRING_FIELD_ENTRIES) {
27
+ const value = accessor(metadata);
28
+ if (value !== void 0) lines.push(`${key}: ${emitScalar(value)}`);
29
+ }
30
+ if (metadata.direction !== void 0) lines.push(`direction: ${metadata.direction}`);
14
31
  if (metadata.keywords !== void 0 && metadata.keywords.length > 0) lines.push(`keywords: [${metadata.keywords.map((keyword) => emitScalar(keyword)).join(", ")}]`);
15
32
  if (lines.length === 0) return;
16
33
  return [
@@ -0,0 +1,104 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_diagnostics_diagnostics = require("../diagnostics/diagnostics.cjs");
3
+ require("../shared/style-constants.cjs");
4
+ let document_schema_js = require("document-schema.js");
5
+ //#region src/emit/html-table.ts
6
+ function escapeHtmlText(text) {
7
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
8
+ }
9
+ function escapeHtmlAttribute(text) {
10
+ return escapeHtmlText(text).replaceAll("\"", "&quot;");
11
+ }
12
+ function blockNeedsHtmlFallback(block) {
13
+ return block.kind !== "paragraph" && block.kind !== "image";
14
+ }
15
+ function tableNeedsHtmlFallback(table) {
16
+ return table.rows.some((row) => row.cells.some((cell) => cell.colSpan !== void 0 || cell.rowSpan !== void 0 || cell.background !== void 0 || cell.blocks.some(blockNeedsHtmlFallback)));
17
+ }
18
+ function emitRunHtml(run) {
19
+ let text = escapeHtmlText(run.text);
20
+ if (run.fontFamily === "Courier New") text = `<code>${text}</code>`;
21
+ if (run.strike === true) text = `<del>${text}</del>`;
22
+ if (run.italic === true) text = `<em>${text}</em>`;
23
+ if (run.bold === true) text = `<strong>${text}</strong>`;
24
+ if (run.hyperlink !== void 0) text = `<a href="${escapeHtmlAttribute(run.hyperlink)}">${text}</a>`;
25
+ return text;
26
+ }
27
+ function emitHtmlImage(block, embedImages) {
28
+ const alt = escapeHtmlAttribute(block.altText ?? "");
29
+ if (!embedImages) return `<img alt="${alt}">`;
30
+ return `<img src="data:image/${block.format};base64,${block.base64}" alt="${alt}">`;
31
+ }
32
+ function backgroundStyleAttr(fill, sink) {
33
+ if (fill.kind === "solid") return `background-color:#${(0, document_schema_js.colorToRgbHex)(fill.color)}`;
34
+ sink({
35
+ code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
36
+ severity: "info",
37
+ message: "a table cell's own pattern background fill has no CSS equivalent this package's bounded HTML-table writer attempts (only a solid fill maps onto a plain background-color); the cell still renders as an ordinary unstyled cell"
38
+ });
39
+ }
40
+ function textAlignStyleAttr(cell) {
41
+ const first = cell.blocks[0];
42
+ if (first?.kind !== "paragraph" || first.alignment === void 0) return;
43
+ return `text-align:${first.alignment}`;
44
+ }
45
+ function emitCellHtml(cell, context) {
46
+ const soleBlock = cell.blocks.length === 1 ? cell.blocks[0] : void 0;
47
+ if (soleBlock?.kind === "table") return emitHtmlTable(soleBlock, context);
48
+ if (cell.blocks.length > 1) context.sink({
49
+ code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.TABLE_CELL_MULTI_PARAGRAPH_JOINED,
50
+ severity: "info",
51
+ message: `a table cell with ${String(cell.blocks.length)} blocks has no multi-block equivalent in an HTML table cell either; their own rendered content is joined with a literal <br> line break`
52
+ });
53
+ const parts = [];
54
+ for (const block of cell.blocks) {
55
+ if (block.kind === "paragraph") {
56
+ parts.push(block.runs.map(emitRunHtml).join(""));
57
+ continue;
58
+ }
59
+ if (block.kind === "image") {
60
+ parts.push(emitHtmlImage(block, context.embedImages));
61
+ continue;
62
+ }
63
+ if (block.kind === "table") {
64
+ context.sink({
65
+ code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
66
+ severity: "info",
67
+ message: "a nested table mixed with other content in the same cell has no HTML representation this package attempts (a cell's own nested table must be its entire content); it is dropped, the cell's other content still renders"
68
+ });
69
+ continue;
70
+ }
71
+ context.sink({
72
+ code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
73
+ severity: "info",
74
+ message: `a table cell containing a "${block.kind}" block has no HTML-table equivalent this package attempts; it is dropped entirely`
75
+ });
76
+ }
77
+ return parts.join("<br>");
78
+ }
79
+ function emitCellTag(cell, isHeader, context) {
80
+ const tag = isHeader ? "th" : "td";
81
+ const attrs = [];
82
+ if (cell.colSpan !== void 0) attrs.push(`colspan="${String(cell.colSpan)}"`);
83
+ if (cell.rowSpan !== void 0) attrs.push(`rowspan="${String(cell.rowSpan)}"`);
84
+ const styleParts = [];
85
+ if (cell.background !== void 0) {
86
+ const style = backgroundStyleAttr(cell.background, context.sink);
87
+ if (style !== void 0) styleParts.push(style);
88
+ }
89
+ const textAlign = textAlignStyleAttr(cell);
90
+ if (textAlign !== void 0) styleParts.push(textAlign);
91
+ if (styleParts.length > 0) attrs.push(`style="${escapeHtmlAttribute(styleParts.join(";"))}"`);
92
+ return `<${tag}${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}>${emitCellHtml(cell, context)}</${tag}>`;
93
+ }
94
+ function emitHtmlTable(table, context) {
95
+ const [header, ...body] = table.rows;
96
+ const lines = ["<table>"];
97
+ if (header !== void 0) lines.push(`<tr>${header.cells.map((cell) => emitCellTag(cell, true, context)).join("")}</tr>`);
98
+ for (const row of body) lines.push(`<tr>${row.cells.map((cell) => emitCellTag(cell, false, context)).join("")}</tr>`);
99
+ lines.push("</table>");
100
+ return lines.join("\n");
101
+ }
102
+ //#endregion
103
+ exports.emitHtmlTable = emitHtmlTable;
104
+ exports.tableNeedsHtmlFallback = tableNeedsHtmlFallback;
@@ -0,0 +1,7 @@
1
+ import { TableEmitContext } from "./table.cjs";
2
+ import { ContentTable } from "document-schema.js";
3
+ //#region src/emit/html-table.d.ts
4
+ declare function tableNeedsHtmlFallback(table: ContentTable): boolean;
5
+ declare function emitHtmlTable(table: ContentTable, context: TableEmitContext): string;
6
+ //#endregion
7
+ export { emitHtmlTable, tableNeedsHtmlFallback };
@@ -0,0 +1,7 @@
1
+ import { TableEmitContext } from "./table.js";
2
+ import { ContentTable } from "document-schema.js";
3
+ //#region src/emit/html-table.d.ts
4
+ declare function tableNeedsHtmlFallback(table: ContentTable): boolean;
5
+ declare function emitHtmlTable(table: ContentTable, context: TableEmitContext): string;
6
+ //#endregion
7
+ export { emitHtmlTable, tableNeedsHtmlFallback };
@@ -0,0 +1,102 @@
1
+ import { MarkdownDiagnosticCodes } from "../diagnostics/diagnostics.js";
2
+ import "../shared/style-constants.js";
3
+ import { colorToRgbHex } from "document-schema.js";
4
+ //#region src/emit/html-table.ts
5
+ function escapeHtmlText(text) {
6
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
7
+ }
8
+ function escapeHtmlAttribute(text) {
9
+ return escapeHtmlText(text).replaceAll("\"", "&quot;");
10
+ }
11
+ function blockNeedsHtmlFallback(block) {
12
+ return block.kind !== "paragraph" && block.kind !== "image";
13
+ }
14
+ function tableNeedsHtmlFallback(table) {
15
+ return table.rows.some((row) => row.cells.some((cell) => cell.colSpan !== void 0 || cell.rowSpan !== void 0 || cell.background !== void 0 || cell.blocks.some(blockNeedsHtmlFallback)));
16
+ }
17
+ function emitRunHtml(run) {
18
+ let text = escapeHtmlText(run.text);
19
+ if (run.fontFamily === "Courier New") text = `<code>${text}</code>`;
20
+ if (run.strike === true) text = `<del>${text}</del>`;
21
+ if (run.italic === true) text = `<em>${text}</em>`;
22
+ if (run.bold === true) text = `<strong>${text}</strong>`;
23
+ if (run.hyperlink !== void 0) text = `<a href="${escapeHtmlAttribute(run.hyperlink)}">${text}</a>`;
24
+ return text;
25
+ }
26
+ function emitHtmlImage(block, embedImages) {
27
+ const alt = escapeHtmlAttribute(block.altText ?? "");
28
+ if (!embedImages) return `<img alt="${alt}">`;
29
+ return `<img src="data:image/${block.format};base64,${block.base64}" alt="${alt}">`;
30
+ }
31
+ function backgroundStyleAttr(fill, sink) {
32
+ if (fill.kind === "solid") return `background-color:#${colorToRgbHex(fill.color)}`;
33
+ sink({
34
+ code: MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
35
+ severity: "info",
36
+ message: "a table cell's own pattern background fill has no CSS equivalent this package's bounded HTML-table writer attempts (only a solid fill maps onto a plain background-color); the cell still renders as an ordinary unstyled cell"
37
+ });
38
+ }
39
+ function textAlignStyleAttr(cell) {
40
+ const first = cell.blocks[0];
41
+ if (first?.kind !== "paragraph" || first.alignment === void 0) return;
42
+ return `text-align:${first.alignment}`;
43
+ }
44
+ function emitCellHtml(cell, context) {
45
+ const soleBlock = cell.blocks.length === 1 ? cell.blocks[0] : void 0;
46
+ if (soleBlock?.kind === "table") return emitHtmlTable(soleBlock, context);
47
+ if (cell.blocks.length > 1) context.sink({
48
+ code: MarkdownDiagnosticCodes.TABLE_CELL_MULTI_PARAGRAPH_JOINED,
49
+ severity: "info",
50
+ message: `a table cell with ${String(cell.blocks.length)} blocks has no multi-block equivalent in an HTML table cell either; their own rendered content is joined with a literal <br> line break`
51
+ });
52
+ const parts = [];
53
+ for (const block of cell.blocks) {
54
+ if (block.kind === "paragraph") {
55
+ parts.push(block.runs.map(emitRunHtml).join(""));
56
+ continue;
57
+ }
58
+ if (block.kind === "image") {
59
+ parts.push(emitHtmlImage(block, context.embedImages));
60
+ continue;
61
+ }
62
+ if (block.kind === "table") {
63
+ context.sink({
64
+ code: MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
65
+ severity: "info",
66
+ message: "a nested table mixed with other content in the same cell has no HTML representation this package attempts (a cell's own nested table must be its entire content); it is dropped, the cell's other content still renders"
67
+ });
68
+ continue;
69
+ }
70
+ context.sink({
71
+ code: MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
72
+ severity: "info",
73
+ message: `a table cell containing a "${block.kind}" block has no HTML-table equivalent this package attempts; it is dropped entirely`
74
+ });
75
+ }
76
+ return parts.join("<br>");
77
+ }
78
+ function emitCellTag(cell, isHeader, context) {
79
+ const tag = isHeader ? "th" : "td";
80
+ const attrs = [];
81
+ if (cell.colSpan !== void 0) attrs.push(`colspan="${String(cell.colSpan)}"`);
82
+ if (cell.rowSpan !== void 0) attrs.push(`rowspan="${String(cell.rowSpan)}"`);
83
+ const styleParts = [];
84
+ if (cell.background !== void 0) {
85
+ const style = backgroundStyleAttr(cell.background, context.sink);
86
+ if (style !== void 0) styleParts.push(style);
87
+ }
88
+ const textAlign = textAlignStyleAttr(cell);
89
+ if (textAlign !== void 0) styleParts.push(textAlign);
90
+ if (styleParts.length > 0) attrs.push(`style="${escapeHtmlAttribute(styleParts.join(";"))}"`);
91
+ return `<${tag}${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}>${emitCellHtml(cell, context)}</${tag}>`;
92
+ }
93
+ function emitHtmlTable(table, context) {
94
+ const [header, ...body] = table.rows;
95
+ const lines = ["<table>"];
96
+ if (header !== void 0) lines.push(`<tr>${header.cells.map((cell) => emitCellTag(cell, true, context)).join("")}</tr>`);
97
+ for (const row of body) lines.push(`<tr>${row.cells.map((cell) => emitCellTag(cell, false, context)).join("")}</tr>`);
98
+ lines.push("</table>");
99
+ return lines.join("\n");
100
+ }
101
+ //#endregion
102
+ export { emitHtmlTable, tableNeedsHtmlFallback };
@@ -121,9 +121,33 @@ function wrapForStyle(body, key, context, precedingText) {
121
121
  const delimiter = key === "bold" ? marker.repeat(2) : marker;
122
122
  return `${delimiter}${body}${delimiter}`;
123
123
  }
124
- function renderNestedStyles(runs, depth, context, base, constructs) {
125
- if (depth >= STYLE_KEYS.length) return runs.map((run, local) => renderLeaf(run, context, base + local, constructs)).join("");
126
- const key = STYLE_KEYS[depth];
124
+ function groupCount(runs, key) {
125
+ let count = 0;
126
+ let index = 0;
127
+ while (index < runs.length) {
128
+ const active = styleActive(runs[index], key);
129
+ count += 1;
130
+ index += 1;
131
+ while (index < runs.length && styleActive(runs[index], key) === active) index += 1;
132
+ }
133
+ return count;
134
+ }
135
+ function pickSplitKey(runs, remaining) {
136
+ let best = remaining[0];
137
+ let bestCount = groupCount(runs, best);
138
+ for (const key of remaining.slice(1)) {
139
+ const count = groupCount(runs, key);
140
+ if (count < bestCount) {
141
+ best = key;
142
+ bestCount = count;
143
+ }
144
+ }
145
+ return best;
146
+ }
147
+ function renderNestedStyles(runs, remainingKeys, context, base, constructs) {
148
+ if (remainingKeys.length === 0) return runs.map((run, local) => renderLeaf(run, context, base + local, constructs)).join("");
149
+ const key = pickSplitKey(runs, remainingKeys);
150
+ const rest = remainingKeys.filter((candidate) => candidate !== key);
127
151
  let out = "";
128
152
  let index = 0;
129
153
  while (index < runs.length) {
@@ -132,7 +156,7 @@ function renderNestedStyles(runs, depth, context, base, constructs) {
132
156
  const active = styleActive(current, key);
133
157
  let end = index + 1;
134
158
  while (end < runs.length && styleActive(runs[end], key) === active) end += 1;
135
- const inner = renderNestedStyles(runs.slice(index, end), depth + 1, context, base + index, constructs);
159
+ const inner = renderNestedStyles(runs.slice(index, end), rest, context, base + index, constructs);
136
160
  out += active ? wrapForStyle(inner, key, context, out) : inner;
137
161
  index = end;
138
162
  }
@@ -168,7 +192,7 @@ function emitRuns(runs, context, constructs) {
168
192
  if (run.hyperlink === void 0) {
169
193
  let end = index + 1;
170
194
  while (end < runs.length && runs[end]?.hyperlink === void 0) end += 1;
171
- out += renderNestedStyles(runs.slice(index, end), 0, context, index, constructs);
195
+ out += renderNestedStyles(runs.slice(index, end), STYLE_KEYS, context, index, constructs);
172
196
  index = end;
173
197
  continue;
174
198
  }
@@ -184,7 +208,7 @@ function emitRuns(runs, context, constructs) {
184
208
  const title = linkTitleCovering(constructs, index, groupEnd);
185
209
  if (title === void 0 && group.length === 1 && isPlainAutolink(group[0], footnoteReferenceAt(constructs, index))) out += `<${group[0].text}>`;
186
210
  else {
187
- const linkText = renderNestedStyles(group, 0, context, index, constructs);
211
+ const linkText = renderNestedStyles(group, STYLE_KEYS, context, index, constructs);
188
212
  out += `[${linkText}](${escapeLinkDestination(hyperlink)}${title === void 0 ? "" : ` "${renderLinkTitle(title)}"`})`;
189
213
  }
190
214
  index = groupEnd;
@@ -1,14 +1,2 @@
1
- import { i as MarkdownDiagnosticSink } from "../diagnostics-eL5c8OWm.cjs";
2
- import { ContentRun, RunConstructExtent } from "document-schema.js";
3
- //#region src/emit/inline.d.ts
4
- interface InlineEmitContext {
5
- readonly sink: MarkdownDiagnosticSink;
6
- readonly emphasisMarker: string;
7
- }
8
- declare function escapeMarkdownText(text: string): string;
9
- declare function escapeLinkDestination(destination: string): string;
10
- declare function renderLinkTitle(title: string): string;
11
- declare function emitRuns(runs: readonly ContentRun[], context: InlineEmitContext, constructs?: readonly RunConstructExtent[]): string;
12
- declare function emitRunsSingleLine(runs: readonly ContentRun[], context: InlineEmitContext, constructs?: readonly RunConstructExtent[]): string;
13
- //#endregion
1
+ import { a as escapeMarkdownText, i as escapeLinkDestination, n as emitRuns, o as renderLinkTitle, r as emitRunsSingleLine, t as InlineEmitContext } from "../inline-514HW3Pi.cjs";
14
2
  export { InlineEmitContext, emitRuns, emitRunsSingleLine, escapeLinkDestination, escapeMarkdownText, renderLinkTitle };
@@ -1,14 +1,2 @@
1
- import { i as MarkdownDiagnosticSink } from "../diagnostics-eL5c8OWm.js";
2
- import { ContentRun, RunConstructExtent } from "document-schema.js";
3
- //#region src/emit/inline.d.ts
4
- interface InlineEmitContext {
5
- readonly sink: MarkdownDiagnosticSink;
6
- readonly emphasisMarker: string;
7
- }
8
- declare function escapeMarkdownText(text: string): string;
9
- declare function escapeLinkDestination(destination: string): string;
10
- declare function renderLinkTitle(title: string): string;
11
- declare function emitRuns(runs: readonly ContentRun[], context: InlineEmitContext, constructs?: readonly RunConstructExtent[]): string;
12
- declare function emitRunsSingleLine(runs: readonly ContentRun[], context: InlineEmitContext, constructs?: readonly RunConstructExtent[]): string;
13
- //#endregion
1
+ import { a as escapeMarkdownText, i as escapeLinkDestination, n as emitRuns, o as renderLinkTitle, r as emitRunsSingleLine, t as InlineEmitContext } from "../inline-2-lUb0vo.js";
14
2
  export { InlineEmitContext, emitRuns, emitRunsSingleLine, escapeLinkDestination, escapeMarkdownText, renderLinkTitle };
@@ -120,9 +120,33 @@ function wrapForStyle(body, key, context, precedingText) {
120
120
  const delimiter = key === "bold" ? marker.repeat(2) : marker;
121
121
  return `${delimiter}${body}${delimiter}`;
122
122
  }
123
- function renderNestedStyles(runs, depth, context, base, constructs) {
124
- if (depth >= STYLE_KEYS.length) return runs.map((run, local) => renderLeaf(run, context, base + local, constructs)).join("");
125
- const key = STYLE_KEYS[depth];
123
+ function groupCount(runs, key) {
124
+ let count = 0;
125
+ let index = 0;
126
+ while (index < runs.length) {
127
+ const active = styleActive(runs[index], key);
128
+ count += 1;
129
+ index += 1;
130
+ while (index < runs.length && styleActive(runs[index], key) === active) index += 1;
131
+ }
132
+ return count;
133
+ }
134
+ function pickSplitKey(runs, remaining) {
135
+ let best = remaining[0];
136
+ let bestCount = groupCount(runs, best);
137
+ for (const key of remaining.slice(1)) {
138
+ const count = groupCount(runs, key);
139
+ if (count < bestCount) {
140
+ best = key;
141
+ bestCount = count;
142
+ }
143
+ }
144
+ return best;
145
+ }
146
+ function renderNestedStyles(runs, remainingKeys, context, base, constructs) {
147
+ if (remainingKeys.length === 0) return runs.map((run, local) => renderLeaf(run, context, base + local, constructs)).join("");
148
+ const key = pickSplitKey(runs, remainingKeys);
149
+ const rest = remainingKeys.filter((candidate) => candidate !== key);
126
150
  let out = "";
127
151
  let index = 0;
128
152
  while (index < runs.length) {
@@ -131,7 +155,7 @@ function renderNestedStyles(runs, depth, context, base, constructs) {
131
155
  const active = styleActive(current, key);
132
156
  let end = index + 1;
133
157
  while (end < runs.length && styleActive(runs[end], key) === active) end += 1;
134
- const inner = renderNestedStyles(runs.slice(index, end), depth + 1, context, base + index, constructs);
158
+ const inner = renderNestedStyles(runs.slice(index, end), rest, context, base + index, constructs);
135
159
  out += active ? wrapForStyle(inner, key, context, out) : inner;
136
160
  index = end;
137
161
  }
@@ -167,7 +191,7 @@ function emitRuns(runs, context, constructs) {
167
191
  if (run.hyperlink === void 0) {
168
192
  let end = index + 1;
169
193
  while (end < runs.length && runs[end]?.hyperlink === void 0) end += 1;
170
- out += renderNestedStyles(runs.slice(index, end), 0, context, index, constructs);
194
+ out += renderNestedStyles(runs.slice(index, end), STYLE_KEYS, context, index, constructs);
171
195
  index = end;
172
196
  continue;
173
197
  }
@@ -183,7 +207,7 @@ function emitRuns(runs, context, constructs) {
183
207
  const title = linkTitleCovering(constructs, index, groupEnd);
184
208
  if (title === void 0 && group.length === 1 && isPlainAutolink(group[0], footnoteReferenceAt(constructs, index))) out += `<${group[0].text}>`;
185
209
  else {
186
- const linkText = renderNestedStyles(group, 0, context, index, constructs);
210
+ const linkText = renderNestedStyles(group, STYLE_KEYS, context, index, constructs);
187
211
  out += `[${linkText}](${escapeLinkDestination(hyperlink)}${title === void 0 ? "" : ` "${renderLinkTitle(title)}"`})`;
188
212
  }
189
213
  index = groupEnd;