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/table.cjs
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_diagnostics_diagnostics = require("../diagnostics/diagnostics.cjs");
|
|
3
3
|
const require_emit_inline = require("./inline.cjs");
|
|
4
4
|
const require_emit_image = require("./image.cjs");
|
|
5
|
+
const require_emit_html_table = require("./html-table.cjs");
|
|
5
6
|
//#region src/emit/table.ts
|
|
6
7
|
function toMarkdownAlignment(alignment) {
|
|
7
8
|
return alignment === void 0 || alignment === "justify" ? "none" : alignment;
|
|
@@ -35,11 +36,6 @@ function escapeUnescapedPipes(text) {
|
|
|
35
36
|
return out;
|
|
36
37
|
}
|
|
37
38
|
function renderCellText(cell, context) {
|
|
38
|
-
if (cell.colSpan !== void 0 || cell.rowSpan !== void 0 || cell.background !== void 0) context.sink({
|
|
39
|
-
code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
|
|
40
|
-
severity: "info",
|
|
41
|
-
message: "a table cell's own colSpan/rowSpan/background has no GFM table equivalent; the cell renders as an ordinary unmerged, unstyled cell"
|
|
42
|
-
});
|
|
43
39
|
if (cell.blocks.length > 1) context.sink({
|
|
44
40
|
code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.TABLE_CELL_MULTI_PARAGRAPH_JOINED,
|
|
45
41
|
severity: "info",
|
|
@@ -56,20 +52,21 @@ function renderCellText(cell, context) {
|
|
|
56
52
|
parts.push(require_emit_image.emitImage(block, context.embedImages));
|
|
57
53
|
continue;
|
|
58
54
|
}
|
|
59
|
-
if (block.kind !== "paragraph")
|
|
60
|
-
context.sink({
|
|
61
|
-
code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
|
|
62
|
-
severity: "info",
|
|
63
|
-
message: `a table cell containing a "${block.kind}" block has no GFM table equivalent; it is dropped entirely`
|
|
64
|
-
});
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
55
|
+
if (block.kind !== "paragraph") continue;
|
|
67
56
|
const text = require_emit_inline.emitRunsSingleLine(block.runs, context, block.constructs);
|
|
68
57
|
if (text.length > 0) parts.push(text);
|
|
69
58
|
}
|
|
70
59
|
return escapeUnescapedPipes(parts.join("<br>"));
|
|
71
60
|
}
|
|
72
61
|
function emitTable(table, context) {
|
|
62
|
+
if (require_emit_html_table.tableNeedsHtmlFallback(table)) {
|
|
63
|
+
context.sink({
|
|
64
|
+
code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.TABLE_HTML_FALLBACK,
|
|
65
|
+
severity: "info",
|
|
66
|
+
message: "a cell in this table needs colSpan/rowSpan/background, or holds a block a GFM table cell cannot represent at all (most commonly a nested table); GFM's own table extension holds inline content only (github.github.com/gfm, \"Tables (extension)\"), so no single cell can carry an HTML sub-block inside an otherwise pipe-syntax table -- the whole table is rendered as a raw HTML <table> block instead (CommonMark spec 0.31.2, HTML blocks condition 6, https://spec.commonmark.org/0.31.2/#html-blocks), which src/html/html-table.ts's own reader recognises back into an equal ContentTable"
|
|
67
|
+
});
|
|
68
|
+
return require_emit_html_table.emitHtmlTable(table, context);
|
|
69
|
+
}
|
|
73
70
|
const [header, ...body] = table.rows;
|
|
74
71
|
if (header === void 0) return "";
|
|
75
72
|
const alignments = header.cells.map((cell) => toMarkdownAlignment(cell.blocks[0]?.kind === "paragraph" ? cell.blocks[0].alignment : void 0));
|
package/dist/emit/table.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as MarkdownDiagnosticSink } from "../diagnostics-
|
|
2
|
-
import { InlineEmitContext } from "
|
|
1
|
+
import { i as MarkdownDiagnosticSink } from "../diagnostics-DLum7kY6.cjs";
|
|
2
|
+
import { t as InlineEmitContext } from "../inline-514HW3Pi.cjs";
|
|
3
3
|
import { ContentTable } from "document-schema.js";
|
|
4
4
|
//#region src/emit/table.d.ts
|
|
5
5
|
interface TableEmitContext extends InlineEmitContext {
|
package/dist/emit/table.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as MarkdownDiagnosticSink } from "../diagnostics-
|
|
2
|
-
import { InlineEmitContext } from "
|
|
1
|
+
import { i as MarkdownDiagnosticSink } from "../diagnostics-DLum7kY6.js";
|
|
2
|
+
import { t as InlineEmitContext } from "../inline-2-lUb0vo.js";
|
|
3
3
|
import { ContentTable } from "document-schema.js";
|
|
4
4
|
//#region src/emit/table.d.ts
|
|
5
5
|
interface TableEmitContext extends InlineEmitContext {
|
package/dist/emit/table.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MarkdownDiagnosticCodes } from "../diagnostics/diagnostics.js";
|
|
2
2
|
import { emitRunsSingleLine } from "./inline.js";
|
|
3
3
|
import { emitImage } from "./image.js";
|
|
4
|
+
import { emitHtmlTable, tableNeedsHtmlFallback } from "./html-table.js";
|
|
4
5
|
//#region src/emit/table.ts
|
|
5
6
|
function toMarkdownAlignment(alignment) {
|
|
6
7
|
return alignment === void 0 || alignment === "justify" ? "none" : alignment;
|
|
@@ -34,11 +35,6 @@ function escapeUnescapedPipes(text) {
|
|
|
34
35
|
return out;
|
|
35
36
|
}
|
|
36
37
|
function renderCellText(cell, context) {
|
|
37
|
-
if (cell.colSpan !== void 0 || cell.rowSpan !== void 0 || cell.background !== void 0) context.sink({
|
|
38
|
-
code: MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
|
|
39
|
-
severity: "info",
|
|
40
|
-
message: "a table cell's own colSpan/rowSpan/background has no GFM table equivalent; the cell renders as an ordinary unmerged, unstyled cell"
|
|
41
|
-
});
|
|
42
38
|
if (cell.blocks.length > 1) context.sink({
|
|
43
39
|
code: MarkdownDiagnosticCodes.TABLE_CELL_MULTI_PARAGRAPH_JOINED,
|
|
44
40
|
severity: "info",
|
|
@@ -55,20 +51,21 @@ function renderCellText(cell, context) {
|
|
|
55
51
|
parts.push(emitImage(block, context.embedImages));
|
|
56
52
|
continue;
|
|
57
53
|
}
|
|
58
|
-
if (block.kind !== "paragraph")
|
|
59
|
-
context.sink({
|
|
60
|
-
code: MarkdownDiagnosticCodes.TABLE_CELL_FORMATTING_DROPPED,
|
|
61
|
-
severity: "info",
|
|
62
|
-
message: `a table cell containing a "${block.kind}" block has no GFM table equivalent; it is dropped entirely`
|
|
63
|
-
});
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
54
|
+
if (block.kind !== "paragraph") continue;
|
|
66
55
|
const text = emitRunsSingleLine(block.runs, context, block.constructs);
|
|
67
56
|
if (text.length > 0) parts.push(text);
|
|
68
57
|
}
|
|
69
58
|
return escapeUnescapedPipes(parts.join("<br>"));
|
|
70
59
|
}
|
|
71
60
|
function emitTable(table, context) {
|
|
61
|
+
if (tableNeedsHtmlFallback(table)) {
|
|
62
|
+
context.sink({
|
|
63
|
+
code: MarkdownDiagnosticCodes.TABLE_HTML_FALLBACK,
|
|
64
|
+
severity: "info",
|
|
65
|
+
message: "a cell in this table needs colSpan/rowSpan/background, or holds a block a GFM table cell cannot represent at all (most commonly a nested table); GFM's own table extension holds inline content only (github.github.com/gfm, \"Tables (extension)\"), so no single cell can carry an HTML sub-block inside an otherwise pipe-syntax table -- the whole table is rendered as a raw HTML <table> block instead (CommonMark spec 0.31.2, HTML blocks condition 6, https://spec.commonmark.org/0.31.2/#html-blocks), which src/html/html-table.ts's own reader recognises back into an equal ContentTable"
|
|
66
|
+
});
|
|
67
|
+
return emitHtmlTable(table, context);
|
|
68
|
+
}
|
|
72
69
|
const [header, ...body] = table.rows;
|
|
73
70
|
if (header === void 0) return "";
|
|
74
71
|
const alignments = header.cells.map((cell) => toMarkdownAlignment(cell.blocks[0]?.kind === "paragraph" ? cell.blocks[0].alignment : void 0));
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_lower_image = require("../lower/image.cjs");
|
|
3
|
+
const require_shared_style_constants = require("../shared/style-constants.cjs");
|
|
4
|
+
let document_schema_js = require("document-schema.js");
|
|
5
|
+
//#region src/html/html-table.ts
|
|
6
|
+
function findBalancedClose(text, openEnd, tagNames) {
|
|
7
|
+
const names = tagNames.join("|");
|
|
8
|
+
const openPattern = new RegExp(`<(?:${names})(?=[\\s/>])`, "gi");
|
|
9
|
+
const closePattern = new RegExp(`</(?:${names})\\s*>`, "gi");
|
|
10
|
+
let depth = 1;
|
|
11
|
+
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) {
|
|
19
|
+
depth += 1;
|
|
20
|
+
pos = openMatch.index + openMatch[0].length;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
depth -= 1;
|
|
24
|
+
pos = closeMatch.index + closeMatch[0].length;
|
|
25
|
+
if (depth === 0) return {
|
|
26
|
+
start: closeMatch.index,
|
|
27
|
+
end: pos
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function extractTopLevelElements(text, tagNames) {
|
|
32
|
+
const openPattern = new RegExp(`<(?:${tagNames.join("|")})\\b([^>]*)>`, "gi");
|
|
33
|
+
const elements = [];
|
|
34
|
+
let pos = 0;
|
|
35
|
+
for (;;) {
|
|
36
|
+
openPattern.lastIndex = pos;
|
|
37
|
+
const match = openPattern.exec(text);
|
|
38
|
+
if (match === null) break;
|
|
39
|
+
if (text.slice(pos, match.index).trim().length > 0) return;
|
|
40
|
+
const attrs = match[1] ?? "";
|
|
41
|
+
const openEnd = match.index + match[0].length;
|
|
42
|
+
const close = findBalancedClose(text, openEnd, tagNames);
|
|
43
|
+
if (close === void 0) return;
|
|
44
|
+
elements.push({
|
|
45
|
+
attrs,
|
|
46
|
+
inner: text.slice(openEnd, close.start)
|
|
47
|
+
});
|
|
48
|
+
pos = close.end;
|
|
49
|
+
}
|
|
50
|
+
if (text.slice(pos).trim().length > 0) return;
|
|
51
|
+
return elements;
|
|
52
|
+
}
|
|
53
|
+
function readAttr(attrs, name) {
|
|
54
|
+
return new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`, "i").exec(attrs)?.[1];
|
|
55
|
+
}
|
|
56
|
+
function readPositiveIntAttr(attrs, name) {
|
|
57
|
+
const raw = readAttr(attrs, name);
|
|
58
|
+
if (raw === void 0) return;
|
|
59
|
+
const value = Number.parseInt(raw, 10);
|
|
60
|
+
return Number.isFinite(value) && value > 0 ? value : void 0;
|
|
61
|
+
}
|
|
62
|
+
const BACKGROUND_COLOR_PATTERN = /background-color\s*:\s*#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b/i;
|
|
63
|
+
function readBackgroundFill(attrs) {
|
|
64
|
+
const style = readAttr(attrs, "style");
|
|
65
|
+
if (style === void 0) return;
|
|
66
|
+
const hex = BACKGROUND_COLOR_PATTERN.exec(style)?.[1];
|
|
67
|
+
if (hex === void 0) return;
|
|
68
|
+
const normalised = hex.length === 3 ? hex.split("").map((digit) => digit + digit).join("") : hex;
|
|
69
|
+
return {
|
|
70
|
+
kind: "solid",
|
|
71
|
+
color: (0, document_schema_js.rgbHexToColor)(normalised)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const TEXT_ALIGN_PATTERN = /text-align\s*:\s*(left|right|center|justify)\b/i;
|
|
75
|
+
function readTextAlign(attrs) {
|
|
76
|
+
const style = readAttr(attrs, "style");
|
|
77
|
+
if (style === void 0) return;
|
|
78
|
+
switch (TEXT_ALIGN_PATTERN.exec(style)?.[1]?.toLowerCase()) {
|
|
79
|
+
case "left": return "left";
|
|
80
|
+
case "right": return "right";
|
|
81
|
+
case "center": return "center";
|
|
82
|
+
case "justify": return "justify";
|
|
83
|
+
default: return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function unescapeHtml(text) {
|
|
87
|
+
return text.replaceAll("<", "<").replaceAll(">", ">").replaceAll(""", "\"").replaceAll("'", "'").replaceAll("&", "&");
|
|
88
|
+
}
|
|
89
|
+
const INLINE_TAG_PATTERN = /<(a|strong|b|em|i|del|s|strike|code)\b([^>]*)>/i;
|
|
90
|
+
function applyTagStyle(style, tagName, attrs) {
|
|
91
|
+
switch (tagName) {
|
|
92
|
+
case "strong":
|
|
93
|
+
case "b": return {
|
|
94
|
+
...style,
|
|
95
|
+
bold: true
|
|
96
|
+
};
|
|
97
|
+
case "em":
|
|
98
|
+
case "i": return {
|
|
99
|
+
...style,
|
|
100
|
+
italic: true
|
|
101
|
+
};
|
|
102
|
+
case "del":
|
|
103
|
+
case "s":
|
|
104
|
+
case "strike": return {
|
|
105
|
+
...style,
|
|
106
|
+
strike: true
|
|
107
|
+
};
|
|
108
|
+
case "code": return {
|
|
109
|
+
...style,
|
|
110
|
+
fontFamily: require_shared_style_constants.MONOSPACE_FONT_FAMILY
|
|
111
|
+
};
|
|
112
|
+
case "a": {
|
|
113
|
+
const href = readAttr(attrs, "href");
|
|
114
|
+
return href === void 0 ? style : {
|
|
115
|
+
...style,
|
|
116
|
+
hyperlink: unescapeHtml(href)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
default: return style;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function pushPlainRun(runs, text, style) {
|
|
123
|
+
if (text.length === 0) return;
|
|
124
|
+
runs.push({
|
|
125
|
+
text: unescapeHtml(text),
|
|
126
|
+
...style
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function parseInlineHtml(text, style) {
|
|
130
|
+
const runs = [];
|
|
131
|
+
let pos = 0;
|
|
132
|
+
for (;;) {
|
|
133
|
+
const rest = text.slice(pos);
|
|
134
|
+
const match = INLINE_TAG_PATTERN.exec(rest);
|
|
135
|
+
if (match === null) {
|
|
136
|
+
pushPlainRun(runs, rest, style);
|
|
137
|
+
return runs;
|
|
138
|
+
}
|
|
139
|
+
pushPlainRun(runs, rest.slice(0, match.index), style);
|
|
140
|
+
const tagName = match[1].toLowerCase();
|
|
141
|
+
const attrs = match[2] ?? "";
|
|
142
|
+
const openEnd = pos + match.index + match[0].length;
|
|
143
|
+
const close = findBalancedClose(text, openEnd, [tagName]);
|
|
144
|
+
if (close === void 0) {
|
|
145
|
+
pushPlainRun(runs, rest, style);
|
|
146
|
+
return runs;
|
|
147
|
+
}
|
|
148
|
+
runs.push(...parseInlineHtml(text.slice(openEnd, close.start), applyTagStyle(style, tagName, attrs)));
|
|
149
|
+
pos = close.end;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const IMG_TAG_PATTERN = /^<img\b([^>]*?)\/?>$/i;
|
|
153
|
+
function parseHtmlImage(piece) {
|
|
154
|
+
const attrs = IMG_TAG_PATTERN.exec(piece)?.[1];
|
|
155
|
+
if (attrs === void 0) return;
|
|
156
|
+
const src = readAttr(attrs, "src");
|
|
157
|
+
if (src === void 0) return;
|
|
158
|
+
const alt = unescapeHtml(readAttr(attrs, "alt") ?? "");
|
|
159
|
+
const resolved = require_lower_image.resolveMarkdownImage(src, { alt }, void 0);
|
|
160
|
+
if (resolved === void 0) return;
|
|
161
|
+
return {
|
|
162
|
+
kind: "image",
|
|
163
|
+
format: resolved.format,
|
|
164
|
+
base64: resolved.base64,
|
|
165
|
+
widthPt: resolved.widthPt,
|
|
166
|
+
heightPt: resolved.heightPt,
|
|
167
|
+
altText: alt
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function parseCellBlocks(inner, contentWidthPt) {
|
|
171
|
+
const trimmed = inner.trim();
|
|
172
|
+
if (trimmed.length === 0) return [];
|
|
173
|
+
const nestedTable = parseWholeTable(trimmed, contentWidthPt);
|
|
174
|
+
if (nestedTable !== void 0) return [nestedTable];
|
|
175
|
+
const blocks = [];
|
|
176
|
+
for (const segment of trimmed.split(/<br\s*\/?>/i)) {
|
|
177
|
+
const piece = segment.trim();
|
|
178
|
+
if (piece.length === 0) continue;
|
|
179
|
+
const image = parseHtmlImage(piece);
|
|
180
|
+
blocks.push(image ?? {
|
|
181
|
+
kind: "paragraph",
|
|
182
|
+
runs: parseInlineHtml(piece, {})
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return blocks;
|
|
186
|
+
}
|
|
187
|
+
function applyTextAlign(blocks, alignment) {
|
|
188
|
+
const first = blocks[0];
|
|
189
|
+
if (alignment === void 0 || first?.kind !== "paragraph") return blocks;
|
|
190
|
+
return [{
|
|
191
|
+
...first,
|
|
192
|
+
alignment
|
|
193
|
+
}, ...blocks.slice(1)];
|
|
194
|
+
}
|
|
195
|
+
function buildCell(cell, contentWidthPt) {
|
|
196
|
+
const colSpan = readPositiveIntAttr(cell.attrs, "colspan");
|
|
197
|
+
const rowSpan = readPositiveIntAttr(cell.attrs, "rowspan");
|
|
198
|
+
const background = readBackgroundFill(cell.attrs);
|
|
199
|
+
const textAlign = readTextAlign(cell.attrs);
|
|
200
|
+
const parsedBlocks = parseCellBlocks(cell.inner, contentWidthPt);
|
|
201
|
+
return {
|
|
202
|
+
blocks: applyTextAlign(parsedBlocks.length > 0 ? parsedBlocks : [{
|
|
203
|
+
kind: "paragraph",
|
|
204
|
+
runs: []
|
|
205
|
+
}], textAlign),
|
|
206
|
+
...colSpan === void 0 ? {} : { colSpan },
|
|
207
|
+
...rowSpan === void 0 ? {} : { rowSpan },
|
|
208
|
+
...background === void 0 ? {} : { background }
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function parseTableRows(inner, contentWidthPt) {
|
|
212
|
+
const rowElements = extractTopLevelElements(inner, ["tr"]);
|
|
213
|
+
if (rowElements === void 0 || rowElements.length === 0) return;
|
|
214
|
+
const rows = [];
|
|
215
|
+
for (const row of rowElements) {
|
|
216
|
+
const cellElements = extractTopLevelElements(row.inner, ["td", "th"]);
|
|
217
|
+
if (cellElements === void 0 || cellElements.length === 0) return;
|
|
218
|
+
rows.push({ cells: cellElements.map((cell) => buildCell(cell, contentWidthPt)) });
|
|
219
|
+
}
|
|
220
|
+
return rows;
|
|
221
|
+
}
|
|
222
|
+
function buildContentTable(rows, contentWidthPt) {
|
|
223
|
+
const header = rows[0];
|
|
224
|
+
const columnCount = Math.max(1, header.cells.reduce((sum, cell) => sum + (cell.colSpan ?? 1), 0));
|
|
225
|
+
return {
|
|
226
|
+
kind: "table",
|
|
227
|
+
rows,
|
|
228
|
+
columnWidthsPt: Array.from({ length: columnCount }, () => contentWidthPt / columnCount)
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function parseWholeTable(text, contentWidthPt) {
|
|
232
|
+
const tableElements = extractTopLevelElements(text, ["table"]);
|
|
233
|
+
if (tableElements?.length !== 1) return;
|
|
234
|
+
const rows = parseTableRows(tableElements[0].inner, contentWidthPt);
|
|
235
|
+
return rows === void 0 ? void 0 : buildContentTable(rows, contentWidthPt);
|
|
236
|
+
}
|
|
237
|
+
function parseHtmlTable(literal, contentWidthPt) {
|
|
238
|
+
const trimmed = literal.trim();
|
|
239
|
+
if (!/^<table\b/i.test(trimmed)) return;
|
|
240
|
+
return parseWholeTable(trimmed, contentWidthPt);
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
243
|
+
exports.parseHtmlTable = parseHtmlTable;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { resolveMarkdownImage } from "../lower/image.js";
|
|
2
|
+
import { MONOSPACE_FONT_FAMILY } from "../shared/style-constants.js";
|
|
3
|
+
import { rgbHexToColor } from "document-schema.js";
|
|
4
|
+
//#region src/html/html-table.ts
|
|
5
|
+
function findBalancedClose(text, openEnd, tagNames) {
|
|
6
|
+
const names = tagNames.join("|");
|
|
7
|
+
const openPattern = new RegExp(`<(?:${names})(?=[\\s/>])`, "gi");
|
|
8
|
+
const closePattern = new RegExp(`</(?:${names})\\s*>`, "gi");
|
|
9
|
+
let depth = 1;
|
|
10
|
+
let pos = openEnd;
|
|
11
|
+
while (depth > 0) {
|
|
12
|
+
openPattern.lastIndex = pos;
|
|
13
|
+
closePattern.lastIndex = pos;
|
|
14
|
+
const openMatch = openPattern.exec(text);
|
|
15
|
+
const closeMatch = closePattern.exec(text);
|
|
16
|
+
if (closeMatch === null) return;
|
|
17
|
+
if (openMatch !== null && openMatch.index < closeMatch.index) {
|
|
18
|
+
depth += 1;
|
|
19
|
+
pos = openMatch.index + openMatch[0].length;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
depth -= 1;
|
|
23
|
+
pos = closeMatch.index + closeMatch[0].length;
|
|
24
|
+
if (depth === 0) return {
|
|
25
|
+
start: closeMatch.index,
|
|
26
|
+
end: pos
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function extractTopLevelElements(text, tagNames) {
|
|
31
|
+
const openPattern = new RegExp(`<(?:${tagNames.join("|")})\\b([^>]*)>`, "gi");
|
|
32
|
+
const elements = [];
|
|
33
|
+
let pos = 0;
|
|
34
|
+
for (;;) {
|
|
35
|
+
openPattern.lastIndex = pos;
|
|
36
|
+
const match = openPattern.exec(text);
|
|
37
|
+
if (match === null) break;
|
|
38
|
+
if (text.slice(pos, match.index).trim().length > 0) return;
|
|
39
|
+
const attrs = match[1] ?? "";
|
|
40
|
+
const openEnd = match.index + match[0].length;
|
|
41
|
+
const close = findBalancedClose(text, openEnd, tagNames);
|
|
42
|
+
if (close === void 0) return;
|
|
43
|
+
elements.push({
|
|
44
|
+
attrs,
|
|
45
|
+
inner: text.slice(openEnd, close.start)
|
|
46
|
+
});
|
|
47
|
+
pos = close.end;
|
|
48
|
+
}
|
|
49
|
+
if (text.slice(pos).trim().length > 0) return;
|
|
50
|
+
return elements;
|
|
51
|
+
}
|
|
52
|
+
function readAttr(attrs, name) {
|
|
53
|
+
return new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`, "i").exec(attrs)?.[1];
|
|
54
|
+
}
|
|
55
|
+
function readPositiveIntAttr(attrs, name) {
|
|
56
|
+
const raw = readAttr(attrs, name);
|
|
57
|
+
if (raw === void 0) return;
|
|
58
|
+
const value = Number.parseInt(raw, 10);
|
|
59
|
+
return Number.isFinite(value) && value > 0 ? value : void 0;
|
|
60
|
+
}
|
|
61
|
+
const BACKGROUND_COLOR_PATTERN = /background-color\s*:\s*#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b/i;
|
|
62
|
+
function readBackgroundFill(attrs) {
|
|
63
|
+
const style = readAttr(attrs, "style");
|
|
64
|
+
if (style === void 0) return;
|
|
65
|
+
const hex = BACKGROUND_COLOR_PATTERN.exec(style)?.[1];
|
|
66
|
+
if (hex === void 0) return;
|
|
67
|
+
const normalised = hex.length === 3 ? hex.split("").map((digit) => digit + digit).join("") : hex;
|
|
68
|
+
return {
|
|
69
|
+
kind: "solid",
|
|
70
|
+
color: rgbHexToColor(normalised)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const TEXT_ALIGN_PATTERN = /text-align\s*:\s*(left|right|center|justify)\b/i;
|
|
74
|
+
function readTextAlign(attrs) {
|
|
75
|
+
const style = readAttr(attrs, "style");
|
|
76
|
+
if (style === void 0) return;
|
|
77
|
+
switch (TEXT_ALIGN_PATTERN.exec(style)?.[1]?.toLowerCase()) {
|
|
78
|
+
case "left": return "left";
|
|
79
|
+
case "right": return "right";
|
|
80
|
+
case "center": return "center";
|
|
81
|
+
case "justify": return "justify";
|
|
82
|
+
default: return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function unescapeHtml(text) {
|
|
86
|
+
return text.replaceAll("<", "<").replaceAll(">", ">").replaceAll(""", "\"").replaceAll("'", "'").replaceAll("&", "&");
|
|
87
|
+
}
|
|
88
|
+
const INLINE_TAG_PATTERN = /<(a|strong|b|em|i|del|s|strike|code)\b([^>]*)>/i;
|
|
89
|
+
function applyTagStyle(style, tagName, attrs) {
|
|
90
|
+
switch (tagName) {
|
|
91
|
+
case "strong":
|
|
92
|
+
case "b": return {
|
|
93
|
+
...style,
|
|
94
|
+
bold: true
|
|
95
|
+
};
|
|
96
|
+
case "em":
|
|
97
|
+
case "i": return {
|
|
98
|
+
...style,
|
|
99
|
+
italic: true
|
|
100
|
+
};
|
|
101
|
+
case "del":
|
|
102
|
+
case "s":
|
|
103
|
+
case "strike": return {
|
|
104
|
+
...style,
|
|
105
|
+
strike: true
|
|
106
|
+
};
|
|
107
|
+
case "code": return {
|
|
108
|
+
...style,
|
|
109
|
+
fontFamily: MONOSPACE_FONT_FAMILY
|
|
110
|
+
};
|
|
111
|
+
case "a": {
|
|
112
|
+
const href = readAttr(attrs, "href");
|
|
113
|
+
return href === void 0 ? style : {
|
|
114
|
+
...style,
|
|
115
|
+
hyperlink: unescapeHtml(href)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
default: return style;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function pushPlainRun(runs, text, style) {
|
|
122
|
+
if (text.length === 0) return;
|
|
123
|
+
runs.push({
|
|
124
|
+
text: unescapeHtml(text),
|
|
125
|
+
...style
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function parseInlineHtml(text, style) {
|
|
129
|
+
const runs = [];
|
|
130
|
+
let pos = 0;
|
|
131
|
+
for (;;) {
|
|
132
|
+
const rest = text.slice(pos);
|
|
133
|
+
const match = INLINE_TAG_PATTERN.exec(rest);
|
|
134
|
+
if (match === null) {
|
|
135
|
+
pushPlainRun(runs, rest, style);
|
|
136
|
+
return runs;
|
|
137
|
+
}
|
|
138
|
+
pushPlainRun(runs, rest.slice(0, match.index), style);
|
|
139
|
+
const tagName = match[1].toLowerCase();
|
|
140
|
+
const attrs = match[2] ?? "";
|
|
141
|
+
const openEnd = pos + match.index + match[0].length;
|
|
142
|
+
const close = findBalancedClose(text, openEnd, [tagName]);
|
|
143
|
+
if (close === void 0) {
|
|
144
|
+
pushPlainRun(runs, rest, style);
|
|
145
|
+
return runs;
|
|
146
|
+
}
|
|
147
|
+
runs.push(...parseInlineHtml(text.slice(openEnd, close.start), applyTagStyle(style, tagName, attrs)));
|
|
148
|
+
pos = close.end;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const IMG_TAG_PATTERN = /^<img\b([^>]*?)\/?>$/i;
|
|
152
|
+
function parseHtmlImage(piece) {
|
|
153
|
+
const attrs = IMG_TAG_PATTERN.exec(piece)?.[1];
|
|
154
|
+
if (attrs === void 0) return;
|
|
155
|
+
const src = readAttr(attrs, "src");
|
|
156
|
+
if (src === void 0) return;
|
|
157
|
+
const alt = unescapeHtml(readAttr(attrs, "alt") ?? "");
|
|
158
|
+
const resolved = resolveMarkdownImage(src, { alt }, void 0);
|
|
159
|
+
if (resolved === void 0) return;
|
|
160
|
+
return {
|
|
161
|
+
kind: "image",
|
|
162
|
+
format: resolved.format,
|
|
163
|
+
base64: resolved.base64,
|
|
164
|
+
widthPt: resolved.widthPt,
|
|
165
|
+
heightPt: resolved.heightPt,
|
|
166
|
+
altText: alt
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function parseCellBlocks(inner, contentWidthPt) {
|
|
170
|
+
const trimmed = inner.trim();
|
|
171
|
+
if (trimmed.length === 0) return [];
|
|
172
|
+
const nestedTable = parseWholeTable(trimmed, contentWidthPt);
|
|
173
|
+
if (nestedTable !== void 0) return [nestedTable];
|
|
174
|
+
const blocks = [];
|
|
175
|
+
for (const segment of trimmed.split(/<br\s*\/?>/i)) {
|
|
176
|
+
const piece = segment.trim();
|
|
177
|
+
if (piece.length === 0) continue;
|
|
178
|
+
const image = parseHtmlImage(piece);
|
|
179
|
+
blocks.push(image ?? {
|
|
180
|
+
kind: "paragraph",
|
|
181
|
+
runs: parseInlineHtml(piece, {})
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return blocks;
|
|
185
|
+
}
|
|
186
|
+
function applyTextAlign(blocks, alignment) {
|
|
187
|
+
const first = blocks[0];
|
|
188
|
+
if (alignment === void 0 || first?.kind !== "paragraph") return blocks;
|
|
189
|
+
return [{
|
|
190
|
+
...first,
|
|
191
|
+
alignment
|
|
192
|
+
}, ...blocks.slice(1)];
|
|
193
|
+
}
|
|
194
|
+
function buildCell(cell, contentWidthPt) {
|
|
195
|
+
const colSpan = readPositiveIntAttr(cell.attrs, "colspan");
|
|
196
|
+
const rowSpan = readPositiveIntAttr(cell.attrs, "rowspan");
|
|
197
|
+
const background = readBackgroundFill(cell.attrs);
|
|
198
|
+
const textAlign = readTextAlign(cell.attrs);
|
|
199
|
+
const parsedBlocks = parseCellBlocks(cell.inner, contentWidthPt);
|
|
200
|
+
return {
|
|
201
|
+
blocks: applyTextAlign(parsedBlocks.length > 0 ? parsedBlocks : [{
|
|
202
|
+
kind: "paragraph",
|
|
203
|
+
runs: []
|
|
204
|
+
}], textAlign),
|
|
205
|
+
...colSpan === void 0 ? {} : { colSpan },
|
|
206
|
+
...rowSpan === void 0 ? {} : { rowSpan },
|
|
207
|
+
...background === void 0 ? {} : { background }
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function parseTableRows(inner, contentWidthPt) {
|
|
211
|
+
const rowElements = extractTopLevelElements(inner, ["tr"]);
|
|
212
|
+
if (rowElements === void 0 || rowElements.length === 0) return;
|
|
213
|
+
const rows = [];
|
|
214
|
+
for (const row of rowElements) {
|
|
215
|
+
const cellElements = extractTopLevelElements(row.inner, ["td", "th"]);
|
|
216
|
+
if (cellElements === void 0 || cellElements.length === 0) return;
|
|
217
|
+
rows.push({ cells: cellElements.map((cell) => buildCell(cell, contentWidthPt)) });
|
|
218
|
+
}
|
|
219
|
+
return rows;
|
|
220
|
+
}
|
|
221
|
+
function buildContentTable(rows, contentWidthPt) {
|
|
222
|
+
const header = rows[0];
|
|
223
|
+
const columnCount = Math.max(1, header.cells.reduce((sum, cell) => sum + (cell.colSpan ?? 1), 0));
|
|
224
|
+
return {
|
|
225
|
+
kind: "table",
|
|
226
|
+
rows,
|
|
227
|
+
columnWidthsPt: Array.from({ length: columnCount }, () => contentWidthPt / columnCount)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function parseWholeTable(text, contentWidthPt) {
|
|
231
|
+
const tableElements = extractTopLevelElements(text, ["table"]);
|
|
232
|
+
if (tableElements?.length !== 1) return;
|
|
233
|
+
const rows = parseTableRows(tableElements[0].inner, contentWidthPt);
|
|
234
|
+
return rows === void 0 ? void 0 : buildContentTable(rows, contentWidthPt);
|
|
235
|
+
}
|
|
236
|
+
function parseHtmlTable(literal, contentWidthPt) {
|
|
237
|
+
const trimmed = literal.trim();
|
|
238
|
+
if (!/^<table\b/i.test(trimmed)) return;
|
|
239
|
+
return parseWholeTable(trimmed, contentWidthPt);
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
export { parseHtmlTable };
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_diagnostics_diagnostics = require("./diagnostics/diagnostics.cjs");
|
|
3
|
-
const require_shared_list_id = require("./shared/list-id.cjs");
|
|
4
3
|
const require_shared_style_constants = require("./shared/style-constants.cjs");
|
|
4
|
+
const require_shared_list_id = require("./shared/list-id.cjs");
|
|
5
5
|
const require_read = require("./read.cjs");
|
|
6
6
|
const require_write = require("./write.cjs");
|
|
7
7
|
const require_codec = require("./codec.cjs");
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as MarkdownInputTooLargeError, c as MarkdownNestingLimitExceededError, d as MarkdownUnbalancedConstructMarkersError, f as MarkdownUnsupportedDocumentKindError, i as MarkdownDiagnosticSink, m as NOOP_MARKDOWN_DIAGNOSTIC_SINK, n as MarkdownDiagnosticCodes, o as MarkdownInvalidRunConstructExtentError, p as MarkdownWriteError, r as MarkdownDiagnosticSeverity, s as MarkdownInvalidUtf8Error, t as MarkdownDiagnostic, u as MarkdownParseError } from "./diagnostics-
|
|
1
|
+
import { a as MarkdownInputTooLargeError, c as MarkdownNestingLimitExceededError, d as MarkdownUnbalancedConstructMarkersError, f as MarkdownUnsupportedDocumentKindError, i as MarkdownDiagnosticSink, m as NOOP_MARKDOWN_DIAGNOSTIC_SINK, n as MarkdownDiagnosticCodes, o as MarkdownInvalidRunConstructExtentError, p as MarkdownWriteError, r as MarkdownDiagnosticSeverity, s as MarkdownInvalidUtf8Error, t as MarkdownDiagnostic, u as MarkdownParseError } from "./diagnostics-DLum7kY6.cjs";
|
|
2
2
|
import { MarkdownBytesSchema, markdownCodec, markdownContentCodec } from "./codec.cjs";
|
|
3
3
|
import { n as MarkdownImageResolver, r as MarkdownResolvedImageBytes, t as MarkdownImageResolveContext } from "./image-B5L0HpAJ.cjs";
|
|
4
4
|
import { MarkdownBulletListMarker, MarkdownCodeFenceChar, MarkdownEmphasisMarker, MarkdownHeadingStyle, MarkdownLineEnding, MarkdownOrderedListDelimiter, MarkdownThematicBreakChar, ReadMarkdownOptions, WriteMarkdownOptions, WriteMarkdownStyleOptions } from "./options/options.cjs";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as MarkdownInputTooLargeError, c as MarkdownNestingLimitExceededError, d as MarkdownUnbalancedConstructMarkersError, f as MarkdownUnsupportedDocumentKindError, i as MarkdownDiagnosticSink, m as NOOP_MARKDOWN_DIAGNOSTIC_SINK, n as MarkdownDiagnosticCodes, o as MarkdownInvalidRunConstructExtentError, p as MarkdownWriteError, r as MarkdownDiagnosticSeverity, s as MarkdownInvalidUtf8Error, t as MarkdownDiagnostic, u as MarkdownParseError } from "./diagnostics-
|
|
1
|
+
import { a as MarkdownInputTooLargeError, c as MarkdownNestingLimitExceededError, d as MarkdownUnbalancedConstructMarkersError, f as MarkdownUnsupportedDocumentKindError, i as MarkdownDiagnosticSink, m as NOOP_MARKDOWN_DIAGNOSTIC_SINK, n as MarkdownDiagnosticCodes, o as MarkdownInvalidRunConstructExtentError, p as MarkdownWriteError, r as MarkdownDiagnosticSeverity, s as MarkdownInvalidUtf8Error, t as MarkdownDiagnostic, u as MarkdownParseError } from "./diagnostics-DLum7kY6.js";
|
|
2
2
|
import { MarkdownBytesSchema, markdownCodec, markdownContentCodec } from "./codec.js";
|
|
3
3
|
import { n as MarkdownImageResolver, r as MarkdownResolvedImageBytes, t as MarkdownImageResolveContext } from "./image-C390OIB3.js";
|
|
4
4
|
import { MarkdownBulletListMarker, MarkdownCodeFenceChar, MarkdownEmphasisMarker, MarkdownHeadingStyle, MarkdownLineEnding, MarkdownOrderedListDelimiter, MarkdownThematicBreakChar, ReadMarkdownOptions, WriteMarkdownOptions, WriteMarkdownStyleOptions } from "./options/options.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MarkdownDiagnosticCodes, MarkdownInputTooLargeError, MarkdownInvalidRunConstructExtentError, MarkdownInvalidUtf8Error, MarkdownNestingLimitExceededError, MarkdownParseError, MarkdownUnbalancedConstructMarkersError, MarkdownUnsupportedDocumentKindError, MarkdownWriteError, NOOP_MARKDOWN_DIAGNOSTIC_SINK } from "./diagnostics/diagnostics.js";
|
|
2
|
-
import { createNumIdMintState, mintListNumId, mintedListType, parseListNumId } from "./shared/list-id.js";
|
|
3
2
|
import { CODE_BLOCK_STYLE_ID, HORIZONTAL_RULE_STYLE_ID, HTML_PREFORMATTED_STYLE_ID, MAX_HEADING_STYLE_LEVEL, MONOSPACE_FONT_FAMILY, QUOTE_INDENT_PT, QUOTE_STYLE_ID, headingStyleId, parseHeadingStyleId } from "./shared/style-constants.js";
|
|
3
|
+
import { createNumIdMintState, mintListNumId, mintedListType, parseListNumId } from "./shared/list-id.js";
|
|
4
4
|
import { readMarkdown, readMarkdownContent } from "./read.js";
|
|
5
5
|
import { writeMarkdown, writeMarkdownContent } from "./write.js";
|
|
6
6
|
import { MarkdownBytesSchema, markdownCodec, markdownContentCodec } from "./codec.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { i as MarkdownDiagnosticSink } from "./diagnostics-DLum7kY6.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
|
|
14
|
+
export { escapeMarkdownText as a, escapeLinkDestination as i, emitRuns as n, renderLinkTitle as o, emitRunsSingleLine as r, InlineEmitContext as t };
|