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.
- package/README.md +20 -7
- package/dist/block/block.cjs +1 -1
- package/dist/block/block.d.cts +1 -1
- package/dist/block/block.d.ts +1 -1
- package/dist/block/block.js +1 -1
- package/dist/block/definitions.d.cts +1 -1
- package/dist/block/definitions.d.ts +1 -1
- package/dist/codec.d.cts +170 -0
- package/dist/codec.d.ts +170 -0
- package/dist/diagnostics/diagnostics.cjs +2 -1
- package/dist/diagnostics/diagnostics.d.cts +1 -1
- package/dist/diagnostics/diagnostics.d.ts +1 -1
- package/dist/diagnostics/diagnostics.js +2 -1
- package/dist/{diagnostics-eL5c8OWm.d.cts → diagnostics-DLum7kY6.d.cts} +1 -0
- package/dist/{diagnostics-eL5c8OWm.d.ts → diagnostics-DLum7kY6.d.ts} +1 -0
- package/dist/emit/emit.cjs +28 -5
- package/dist/emit/emit.js +28 -5
- package/dist/emit/front-matter.cjs +22 -5
- package/dist/emit/front-matter.js +22 -5
- package/dist/emit/html-table.cjs +104 -0
- package/dist/emit/html-table.d.cts +7 -0
- package/dist/emit/html-table.d.ts +7 -0
- package/dist/emit/html-table.js +102 -0
- package/dist/emit/inline.cjs +30 -6
- package/dist/emit/inline.d.cts +1 -13
- package/dist/emit/inline.d.ts +1 -13
- package/dist/emit/inline.js +30 -6
- package/dist/emit/table.cjs +10 -13
- package/dist/emit/table.d.cts +2 -2
- package/dist/emit/table.d.ts +2 -2
- package/dist/emit/table.js +10 -13
- package/dist/html/html-table.cjs +243 -0
- package/dist/html/html-table.d.cts +5 -0
- package/dist/html/html-table.d.ts +5 -0
- package/dist/html/html-table.js +242 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/inline-2-lUb0vo.d.ts +14 -0
- package/dist/inline-514HW3Pi.d.cts +14 -0
- package/dist/lower/front-matter.cjs +64 -20
- package/dist/lower/front-matter.d.cts +1 -1
- package/dist/lower/front-matter.d.ts +1 -1
- package/dist/lower/front-matter.js +64 -20
- package/dist/lower/inline.d.cts +1 -1
- package/dist/lower/inline.d.ts +1 -1
- package/dist/lower/lower.cjs +16 -5
- package/dist/lower/lower.js +16 -5
- package/dist/options/options.d.cts +1 -1
- package/dist/options/options.d.ts +1 -1
- package/dist/read.d.cts +1 -1
- package/dist/read.d.ts +1 -1
- package/package.json +2 -2
package/dist/emit/emit.cjs
CHANGED
|
@@ -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)}${
|
|
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 = `${
|
|
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)}${
|
|
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 = `${
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (metadata.
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (metadata.
|
|
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("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
8
|
+
}
|
|
9
|
+
function escapeHtmlAttribute(text) {
|
|
10
|
+
return escapeHtmlText(text).replaceAll("\"", """);
|
|
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("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
7
|
+
}
|
|
8
|
+
function escapeHtmlAttribute(text) {
|
|
9
|
+
return escapeHtmlText(text).replaceAll("\"", """);
|
|
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 };
|
package/dist/emit/inline.cjs
CHANGED
|
@@ -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
|
|
125
|
-
|
|
126
|
-
|
|
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),
|
|
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),
|
|
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,
|
|
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;
|
package/dist/emit/inline.d.cts
CHANGED
|
@@ -1,14 +1,2 @@
|
|
|
1
|
-
import { i as
|
|
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 };
|
package/dist/emit/inline.d.ts
CHANGED
|
@@ -1,14 +1,2 @@
|
|
|
1
|
-
import { i as
|
|
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 };
|
package/dist/emit/inline.js
CHANGED
|
@@ -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
|
|
124
|
-
|
|
125
|
-
|
|
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),
|
|
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),
|
|
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,
|
|
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;
|