markdown-codec 6.4.3 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 +1 -1
package/README.md
CHANGED
|
@@ -142,11 +142,11 @@ Modelled on `pdf-codec`'s own layering, aimed at CommonMark+GFM instead of PDF:
|
|
|
142
142
|
- **`src/scan/`** — CommonMark line/character scanner, plus `entity-table.ts` (generated from `assets/html-entities/entities.json`).
|
|
143
143
|
- **`src/block/`** — CommonMark block-structure algorithm (open-block stack, continuation matching): paragraphs, headings, code blocks, block quotes, lists (incl. GFM task-list-item), thematic breaks, link references, footnote definitions, GFM tables.
|
|
144
144
|
- **`src/inline/`** — emphasis, code spans, links, autolinks, raw HTML, GFM strikethrough, footnote references, line breaks. `link.ts` and `footnote.ts` hold the label grammars the block phase shares.
|
|
145
|
-
- **`src/html/`** — raw HTML recognition (bounded rules, not a general parser) plus `render.ts` (conformance oracle; internal only).
|
|
145
|
+
- **`src/html/`** — raw HTML recognition (bounded rules, not a general parser) plus `render.ts` (conformance oracle; internal only). `html-table.ts` is the one exception to "never parsed as markup": a block-level HTML that is, in full, one well-formed `<table>` recognises straight to a real `ContentTable` instead of opaque text — see [HTML-table fallback](#html-table-fallback).
|
|
146
146
|
- **`src/image/`** — PNG/JPEG dimension reader and base64 codec, shared by `src/lower/` and `src/emit/`.
|
|
147
147
|
- **`src/shared/`** — string-shape conventions `src/lower`/`src/emit` agree on (`style-constants.ts`, `list-id.ts`'s opaque `numId`). Re-exported so `documents.js`'s `MarkdownEditor` reuses the identical grammar.
|
|
148
148
|
- **`src/lower/`** — AST → `ContentDocument` lowering (thin adapter, not a second parser); top-of-file table maps each construct to its diagnostic gap.
|
|
149
|
-
- **`src/emit/`** — `ContentDocument` → markdown text emission, the structural inverse of `src/lower`.
|
|
149
|
+
- **`src/emit/`** — `ContentDocument` → markdown text emission, the structural inverse of `src/lower`. `html-table.ts` is `src/html/html-table.ts`'s own write-side counterpart.
|
|
150
150
|
- **`src/read.ts`** / **`src/write.ts`** / **`src/codec.ts`** — the public entry points at both levels: `readMarkdown`/`writeMarkdown`/`markdownCodec` over `DocumentTree`, and `readMarkdownContent`/`writeMarkdownContent`/`markdownContentCodec` over `ContentDocument`. The tree-form functions are thin compositions of `document-schema.js`'s `assembleTree`/`flattenTree` onto the flat ones; no conversion logic of their own lives here.
|
|
151
151
|
|
|
152
152
|
## Vendored assets
|
|
@@ -195,7 +195,7 @@ Every construct `src/lower`/`src/emit` cannot represent losslessly is a document
|
|
|
195
195
|
- **`md/math-inline-preserved-as-text`** — inline `\( \)` math stays a Cambria-Math-marked raw-LaTeX run; display `$$` math is a real embedded formula carrying the presentation layer.
|
|
196
196
|
- **`md/image-unresolved`** — no resolver, `undefined` return, or non-PNG/JPEG bytes degrades to alt-text run.
|
|
197
197
|
- **`md/raw-html-preserved-as-text` / `md/raw-html-dropped`** — raw HTML kept as literal text (default) or dropped; never interpreted. The preserved text's verbatim original quarantines as markdown residue on its node, and this package's own writer re-emits that residue as-is.
|
|
198
|
-
- **`md/front-matter-key-unmapped`** — no YAML/TOML engine; only
|
|
198
|
+
- **`md/front-matter-key-unmapped`** — no YAML/TOML engine; only `LayoutMetadata`'s own string/array/enum fields are recognised (every one of them except `producer`, which is PDF-writer-only), each as a flat `key: value` (or `keywords: [a, b]`) line. The verbatim original block rides the package-level residue table (`readMarkdown`'s `documentPackage.source.frontmatter`), which `writeMarkdown` re-emits as-is.
|
|
199
199
|
- **`md/heading-level-clamped`** — styleId beyond `Heading6` (from another format) clamps to level 6 via document-schema.js's shared `clampHeadingLevel()`.
|
|
200
200
|
- **`md/heading-line-break-collapsed`** — a hard or soft break embedded in a heading's own runs (most commonly a setext heading read with more than one physical line) is promoted to setext output whenever the level admits one (1 or 2) and the break's own placement is safe to promote, the only markdown grammar that can hold a genuine line break inside heading text; a level 3-6 heading with the same shape has no such fallback and always collapses the break to a single space instead, as does a level 1/2 heading whose break placement is unsafe to promote (see `md/heading-line-break-unsafe-for-setext` below).
|
|
201
201
|
- **`md/heading-line-break-unsafe-for-setext`** — a level 1/2 heading's own embedded break sits where promoting to setext would violate one of the three clauses packed into setext's own grammar sentence (spec 0.31.2, "Setext headings"): "one or more lines of text, not interrupted by a blank line, of which the first line does not have more than 3 spaces of indentation, followed by a setext heading underline. The lines of text must be such that, were they not followed by the setext heading underline, they would be interpreted as a paragraph: they cannot be interpretable as a code fence, ATX heading, block quote, thematic break, list item, or HTML block." Reported when promoting would leave a blank line immediately before the underline (a trailing break) or in the middle of the heading's own text (an interior break between two embedded breaks, or a break immediately followed by incidental whitespace-only content); when the heading's own first content line opens with 4 or more columns of space/tab indentation — CommonMark's own tab-stop rule counts a tab by the same 4-column stop, so a single leading tab is exactly as unsafe as 4 leading spaces — since that line would read back as an indented code block rather than heading text; or when a line AFTER the first would itself be read as one of the spec's six interrupting constructs (a code fence, ATX heading, block quote, thematic break, list item, or HTML block) instead of ordinary paragraph continuation text — reachable from a bold or strikethrough run that becomes empty immediately after a break, whose bare pair of emphasis markers spells as a thematic break (`____`/`****`) or a code-fence opener (`~~~~`). ATX collapses the break to a single space instead of promoting into any of these corrupt reparses. A break at the very START of the heading's own text stays eligible for setext, but that exemption covers only a single leading blank line, and only the blank line itself: it sits before the text-and-underline run even begins, so it reparses as ordinary inter-block whitespace ahead of the heading rather than corrupting anything. A second consecutive leading blank line is not exempt — CommonMark's own list-item grammar allows at most one, so a second closes a list item as empty and spills the heading out with its list membership lost, even though it would be harmless at top level or inside a blockquote — and the indentation clause above still applies in full to the line that follows the leading blank line, the heading's own first real content: a leading break immediately followed by 4 or more columns of indentation is refused exactly as an unbroken heading in the same shape would be, since promoting would read that following line back as an indented code block regardless of the leading break ahead of it.
|
|
@@ -204,9 +204,10 @@ Every construct `src/lower`/`src/emit` cannot represent losslessly is a document
|
|
|
204
204
|
- **`md/paragraph-indent-dropped`** — `indentLeftPt` without a recognised styleId; indent dropped, paragraph renders.
|
|
205
205
|
- **`md/list-numid-fallback`** — a foreign or absent `numId` (depth-only `ContentListMembership`) falls back to a plain bullet list.
|
|
206
206
|
- **`md/raw-html-preserved-as-text`** — see the raw-HTML entry above.
|
|
207
|
-
- **`md/table-
|
|
208
|
-
- **`md/table-cell-
|
|
209
|
-
- **`md/table-cell-
|
|
207
|
+
- **`md/table-html-fallback`** — a cell's own colSpan/rowSpan/background, or a block a plain GFM cell cannot represent at all (most commonly a nested table), sends the WHOLE table through the HTML-table fallback instead of plain pipe syntax — see [HTML-table fallback](#html-table-fallback). Fires once per table that falls back.
|
|
208
|
+
- **`md/table-cell-formatting-dropped`** — the residual gap even inside the HTML-table fallback: a cell's own pattern background fill (only a solid fill maps onto CSS `background-color`), a nested table mixed with sibling content in the same cell (a cell's own nested table must be its entire content), or any other block kind (`embeddedObject`, `pageBreak`) — none of these have a representation this bounded recogniser attempts; the cell still renders, unmerged/unstyled, with that content dropped.
|
|
209
|
+
- **`md/table-cell-multi-paragraph-joined`** — a cell carrying more than one block joins their rendered content with a literal `<br>`, real inline HTML every GFM table renderer treats as a genuine line break — markdown syntax in the plain pipe-syntax writer, a real `<br>` HTML tag in the HTML-table fallback.
|
|
210
|
+
- **`md/table-cell-image-degraded`** — plain pipe-syntax writer only: an `image`-kind cell block emits inline (``) rather than being dropped; on read-back it degrades to a run carrying the alt text with the image's own data URI as that run's hyperlink, the same "nested image" shape an image inside emphasis/a link already degrades to elsewhere in this package. The HTML-table fallback represents an image losslessly instead, as a real `<img>` tag.
|
|
210
211
|
- **`md/duplicate-footnote-definition`** — two definitions share a label; every reference resolves to the first, both are kept as written.
|
|
211
212
|
- **`md/footnote-body-heading-flattened`** — a heading inside a definition body is carried as literal ATX text, since a construct extent may not open or close a heading scope.
|
|
212
213
|
- **`md/construct-unrepresented`** — a construct kind markdown has no syntax for renders transparently: its extent still appears, the construct itself does not.
|
|
@@ -221,10 +222,22 @@ The two halves of a footnote map onto the **same `anchor` construct at two diffe
|
|
|
221
222
|
- **A definition becomes an `anchor` construct.** Lowering emits document-schema.js's construct boundary markers — a `constructStart` carrying `{ kind: 'anchor', anchorType: 'footnote', name }`, the definition's own lowered body blocks, and a `constructEnd` — which is what `readMarkdownContent` returns in its block flow, and what `decompose` promotes to a construct group of its own in the `DocumentTree` `readMarkdown` returns (the descriptor rides the group's `node`, the body blocks its `children`). The body rides the construct's extent rather than `AnchorDescriptor.definition`, which names a key in a package-level definitions table: `DocumentTree` does carry that table as a root (unlike the flat `ContentDocument`), but a table entry there is a flat descriptor record, not a container for block content, so a body that is genuinely several paragraphs, a code block, or a list still has nowhere to live as a table value either way — the construct's own bracketed extent is the one shape in this schema built to hold real block content. A bodyless `[^1]:` lowers to the point anchor the same descriptor describes: a pair with nothing between it.
|
|
222
223
|
- **A reference site becomes a point run-level `anchor` extent.** A reference sits between two runs inside a paragraph, so no block-level boundary marker can bracket it without splitting the paragraph in two — but a run-level construct extent (`RunConstructExtent` on `ContentParagraph.constructs`, document-schema.js 4.5.0) names exactly that shape. Lowering emits an ordinary text run keeping the reference's own `[^label]` spelling (the materialised rendering, so a consumer that ignores constructs still shows `[^1]`) plus a point extent — `{ kind: 'anchor', anchorType: 'footnote', name }` at `startRun === endRun` naming that run, the same wiring ooxml.js's docx reader mints for a `w:footnoteReference` — carried through `decompose`/`flattenTree` verbatim, exactly the way a table cell's own markers are. The writer spells `[^label]` back out from the covering extent rather than from anything about the run's text (which is what still distinguishes a genuine reference from a deliberately-escaped literal `\[^1\]`), gated by the same label grammar as the definition marker: a foreign name that grammar cannot spell degrades to the run's own escaped text plus `md/construct-unrepresented`.
|
|
223
224
|
|
|
224
|
-
Definitions are recognised
|
|
225
|
+
Definitions are recognised at the document's own top level, directly inside a block quote, or directly inside a list item (ExaDev/markdown-codec#957) — a blockquote's own division construct threads its enclosing container's context straight through the definition's anchor pair the same way it already does for a nested blockquote, and a list item whose own first block is the anchor pair picks up the same placeholder-paragraph carry a nested blockquote already relies on. Not recognised inside another definition's own body: a footnote-in-a-footnote has no cross-format shape, so a `[^2]:` line there stays ordinary body text. A heading inside a definition body is still flattened to literal ATX text, and a blockquote containing a heading anywhere in its own subtree still degrades to indent-only structure — both because a construct boundary marker's extent may not open or close a heading-group scope, a document-schema.js-level limitation this package cannot route around from its own side.
|
|
225
226
|
|
|
226
227
|
Emission is the inverse and validates first: a section's markers must pair as balanced brackets (checked through document-schema.js's own `findConstructMarkerImbalance`, the shared definition every codec and `decompose` agree on) or `writeMarkdownContent` throws `MarkdownUnbalancedConstructMarkersError`. A tree already satisfies that balance by construction — `decompose` refuses to build one from an unbalanced stream — so `writeMarkdown` reaches this check only on a hand-built package flattened back to an unbalanced flow. A construct kind with no markdown syntax — a bookmark, a division, a tracked change — renders transparently: its extent still appears in place, only the construct's own identity is lost.
|
|
227
228
|
|
|
229
|
+
## HTML-table fallback
|
|
230
|
+
|
|
231
|
+
GFM's own pipe-table syntax (github.github.com/gfm, "Tables (extension)") holds inline content only — no cell can carry `colSpan`/`rowSpan`/a background, and no cell can hold a block that isn't inlinable (a nested table, most notably). `writeMarkdownContent` detects this per table (`tableNeedsHtmlFallback`, `src/emit/html-table.ts`): the moment ANY cell needs one of these, the WHOLE table renders as a raw HTML `<table>` block instead of the plain `| a | b |` writer — GFM's own cell grammar cannot mix an HTML sub-block into one cell of an otherwise pipe-syntax table, so a partial, per-cell fallback is not an option. A raw HTML `<table>` block is legal CommonMark regardless (spec 0.31.2, "HTML blocks" start condition 6 names `table` directly — <https://spec.commonmark.org/0.31.2/#html-blocks>). A table with none of these needs still emits the existing, human-readable pipe syntax, unaffected.
|
|
232
|
+
|
|
233
|
+
`readMarkdownContent`/`readMarkdown` recognise this same shape on the way back in (`parseHtmlTable`, `src/html/html-table.ts`) — the one exception to this package's own "raw HTML is never parsed as markup" rule (see the raw-HTML gotcha above and `src/html/html.ts`'s own top comment): a block-level HTML that is, in full, one well-formed `<table>` lowers straight to a real `ContentTable`, so a table written through this fallback reads back as an equal (or equivalent) `ContentTable` rather than degrading to opaque preserved text. This recognition is gated on GFM's table extension being enabled (`gfmTables`, default `true`) exactly the way plain pipe-table promotion already is — with it disabled, a `<table>` block stays exactly the opaque raw HTML it always was, since a `ContentTable` is a GFM-table-extension construct either way it is spelled in the source.
|
|
234
|
+
|
|
235
|
+
Both halves are **bounded recognisers, not a general HTML/DOM parser** — matching `src/html/html.ts`'s own deliberate scope. They recognise `<table>`/`<tr>`/`<td>`/`<th>` and, on each cell, `colspan`/`rowspan` (positive integers) and a `style="background-color:#rrggbb"` declaration (a `ContentCellFill`'s `solid` variant only — a `pattern` fill has no plain-CSS equivalent this writer attempts, and degrades with `TABLE_CELL_FORMATTING_DROPPED`), plus `style="text-align:..."` for the cell's own paragraph alignment — read per cell rather than only from the header row, a strictly richer fidelity than plain GFM's single per-column delimiter marker can ever express, so a table already falling back for another reason never silently loses alignment plain GFM would have kept. Attribute values must be double-quoted, matching exactly what the writer itself emits; a single-quoted or unquoted value is a real HTML shape neither side attempts.
|
|
236
|
+
|
|
237
|
+
A cell's own inline formatting renders as real HTML tags — `<strong>`/`<em>`/`<del>`/`<code>`/`<a href>`, the identical bold/italic/strike/hyperlink/Courier-New-as-code-span vocabulary the plain writer already spells in markdown syntax — because raw HTML block content is never reprocessed as markdown, so writing markdown punctuation inside this block would render as its own literal characters rather than formatting. The reader also recognises the common `<b>`/`<i>`/`<s>`/`<strike>` synonyms real hand-authored HTML tends to use, in any nesting order or combination.
|
|
238
|
+
|
|
239
|
+
A cell's own nested-block content is bounded to two shapes: several paragraph/image blocks join with a literal `<br>`, exactly as the plain writer already joins a multi-paragraph cell (`TABLE_CELL_MULTI_PARAGRAPH_JOINED`); or, when a cell's ENTIRE content is one nested table and nothing else, that table recurses through the identical fallback, unconditionally, since a table nested inside another table's cell can never itself be pipe syntax. A nested table mixed with sibling content in the same cell, or any other block kind (`embeddedObject`, `pageBreak`), has no representation either side attempts and is dropped with `TABLE_CELL_FORMATTING_DROPPED`, exactly as it always was before this fallback existed.
|
|
240
|
+
|
|
228
241
|
## Fidelity
|
|
229
242
|
|
|
230
243
|
**Markdown → `ContentDocument` is dominated by target-schema limits, not parsing gaps.** The parser recognises every construct CommonMark and GFM define; the limiting factor is what `ContentDocument` can hold — a cross-format pivot shaped around docx/pptx/odt/odp/ods/odg, not markdown's richer model. Each gap is a permanent structural mismatch.
|
package/dist/block/block.cjs
CHANGED
|
@@ -312,7 +312,7 @@ var BlockParser = class {
|
|
|
312
312
|
footnoteDefinitionMayOpenIn(container) {
|
|
313
313
|
let node = container;
|
|
314
314
|
while (node?.kind === "list") node = node.parent;
|
|
315
|
-
return node?.kind === "document";
|
|
315
|
+
return node?.kind === "document" || node?.kind === "blockquote" || node?.kind === "listItem";
|
|
316
316
|
}
|
|
317
317
|
tryHtmlBlockStart(container) {
|
|
318
318
|
if (this.line.indented || this.line.peekNextNonspace() !== "<") return "none";
|
package/dist/block/block.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as MarkdownDocumentNode } from "../ast-CNfTgS0N.cjs";
|
|
2
2
|
import { r as FootnoteLabelSet } from "../footnote-CKk4JbLk.cjs";
|
|
3
|
-
import { i as MarkdownDiagnosticSink } from "../diagnostics-
|
|
3
|
+
import { i as MarkdownDiagnosticSink } from "../diagnostics-DLum7kY6.cjs";
|
|
4
4
|
import { LinkReferenceMap } from "../inline/link.cjs";
|
|
5
5
|
import { t as InlineParseOptions } from "../inline-CnO86zMw.cjs";
|
|
6
6
|
//#region src/block/block.d.ts
|
package/dist/block/block.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as MarkdownDocumentNode } from "../ast-CNfTgS0N.js";
|
|
2
2
|
import { r as FootnoteLabelSet } from "../footnote-CKk4JbLk.js";
|
|
3
|
-
import { i as MarkdownDiagnosticSink } from "../diagnostics-
|
|
3
|
+
import { i as MarkdownDiagnosticSink } from "../diagnostics-DLum7kY6.js";
|
|
4
4
|
import { LinkReferenceMap } from "../inline/link.js";
|
|
5
5
|
import { t as InlineParseOptions } from "../inline-DItZ-VVw.js";
|
|
6
6
|
//#region src/block/block.d.ts
|
package/dist/block/block.js
CHANGED
|
@@ -311,7 +311,7 @@ var BlockParser = class {
|
|
|
311
311
|
footnoteDefinitionMayOpenIn(container) {
|
|
312
312
|
let node = container;
|
|
313
313
|
while (node?.kind === "list") node = node.parent;
|
|
314
|
-
return node?.kind === "document";
|
|
314
|
+
return node?.kind === "document" || node?.kind === "blockquote" || node?.kind === "listItem";
|
|
315
315
|
}
|
|
316
316
|
tryHtmlBlockStart(container) {
|
|
317
317
|
if (this.line.indented || this.line.peekNextNonspace() !== "<") return "none";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as MarkdownDiagnosticSink } from "../diagnostics-
|
|
1
|
+
import { i as MarkdownDiagnosticSink } from "../diagnostics-DLum7kY6.cjs";
|
|
2
2
|
import { LinkReferenceDefinition } from "../inline/link.cjs";
|
|
3
3
|
//#region src/block/definitions.d.ts
|
|
4
4
|
declare function extractDefinitions(content: string, references: Map<string, LinkReferenceDefinition>, sink?: MarkdownDiagnosticSink, startLine?: number): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as MarkdownDiagnosticSink } from "../diagnostics-
|
|
1
|
+
import { i as MarkdownDiagnosticSink } from "../diagnostics-DLum7kY6.js";
|
|
2
2
|
import { LinkReferenceDefinition } from "../inline/link.js";
|
|
3
3
|
//#region src/block/definitions.d.ts
|
|
4
4
|
declare function extractDefinitions(content: string, references: Map<string, LinkReferenceDefinition>, sink?: MarkdownDiagnosticSink, startLine?: number): string;
|
|
@@ -32,7 +32,8 @@ const MarkdownDiagnosticCodes = {
|
|
|
32
32
|
LIST_NUMID_FALLBACK: "md/list-numid-fallback",
|
|
33
33
|
TABLE_CELL_FORMATTING_DROPPED: "md/table-cell-formatting-dropped",
|
|
34
34
|
TABLE_CELL_MULTI_PARAGRAPH_JOINED: "md/table-cell-multi-paragraph-joined",
|
|
35
|
-
TABLE_CELL_IMAGE_DEGRADED: "md/table-cell-image-degraded"
|
|
35
|
+
TABLE_CELL_IMAGE_DEGRADED: "md/table-cell-image-degraded",
|
|
36
|
+
TABLE_HTML_FALLBACK: "md/table-html-fallback"
|
|
36
37
|
};
|
|
37
38
|
var MarkdownParseError = class extends Error {
|
|
38
39
|
code;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as MarkdownInputTooLargeError, c as MarkdownNestingLimitExceededError, d as MarkdownUnbalancedConstructMarkersError, f as MarkdownUnsupportedDocumentKindError, i as MarkdownDiagnosticSink, l as MarkdownPackageFlattenError, 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, l as MarkdownPackageFlattenError, 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
|
export { MarkdownDiagnostic, MarkdownDiagnosticCodes, MarkdownDiagnosticSeverity, MarkdownDiagnosticSink, MarkdownInputTooLargeError, MarkdownInvalidRunConstructExtentError, MarkdownInvalidUtf8Error, MarkdownNestingLimitExceededError, MarkdownPackageFlattenError, MarkdownParseError, MarkdownUnbalancedConstructMarkersError, MarkdownUnsupportedDocumentKindError, MarkdownWriteError, NOOP_MARKDOWN_DIAGNOSTIC_SINK };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as MarkdownInputTooLargeError, c as MarkdownNestingLimitExceededError, d as MarkdownUnbalancedConstructMarkersError, f as MarkdownUnsupportedDocumentKindError, i as MarkdownDiagnosticSink, l as MarkdownPackageFlattenError, 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, l as MarkdownPackageFlattenError, 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
|
export { MarkdownDiagnostic, MarkdownDiagnosticCodes, MarkdownDiagnosticSeverity, MarkdownDiagnosticSink, MarkdownInputTooLargeError, MarkdownInvalidRunConstructExtentError, MarkdownInvalidUtf8Error, MarkdownNestingLimitExceededError, MarkdownPackageFlattenError, MarkdownParseError, MarkdownUnbalancedConstructMarkersError, MarkdownUnsupportedDocumentKindError, MarkdownWriteError, NOOP_MARKDOWN_DIAGNOSTIC_SINK };
|
|
@@ -31,7 +31,8 @@ const MarkdownDiagnosticCodes = {
|
|
|
31
31
|
LIST_NUMID_FALLBACK: "md/list-numid-fallback",
|
|
32
32
|
TABLE_CELL_FORMATTING_DROPPED: "md/table-cell-formatting-dropped",
|
|
33
33
|
TABLE_CELL_MULTI_PARAGRAPH_JOINED: "md/table-cell-multi-paragraph-joined",
|
|
34
|
-
TABLE_CELL_IMAGE_DEGRADED: "md/table-cell-image-degraded"
|
|
34
|
+
TABLE_CELL_IMAGE_DEGRADED: "md/table-cell-image-degraded",
|
|
35
|
+
TABLE_HTML_FALLBACK: "md/table-html-fallback"
|
|
35
36
|
};
|
|
36
37
|
var MarkdownParseError = class extends Error {
|
|
37
38
|
code;
|
|
@@ -40,6 +40,7 @@ declare const MarkdownDiagnosticCodes: {
|
|
|
40
40
|
readonly TABLE_CELL_FORMATTING_DROPPED: "md/table-cell-formatting-dropped";
|
|
41
41
|
readonly TABLE_CELL_MULTI_PARAGRAPH_JOINED: "md/table-cell-multi-paragraph-joined";
|
|
42
42
|
readonly TABLE_CELL_IMAGE_DEGRADED: "md/table-cell-image-degraded";
|
|
43
|
+
readonly TABLE_HTML_FALLBACK: "md/table-html-fallback";
|
|
43
44
|
};
|
|
44
45
|
declare class MarkdownParseError extends Error {
|
|
45
46
|
readonly code: string;
|
|
@@ -40,6 +40,7 @@ declare const MarkdownDiagnosticCodes: {
|
|
|
40
40
|
readonly TABLE_CELL_FORMATTING_DROPPED: "md/table-cell-formatting-dropped";
|
|
41
41
|
readonly TABLE_CELL_MULTI_PARAGRAPH_JOINED: "md/table-cell-multi-paragraph-joined";
|
|
42
42
|
readonly TABLE_CELL_IMAGE_DEGRADED: "md/table-cell-image-degraded";
|
|
43
|
+
readonly TABLE_HTML_FALLBACK: "md/table-html-fallback";
|
|
43
44
|
};
|
|
44
45
|
declare class MarkdownParseError extends Error {
|
|
45
46
|
readonly code: string;
|
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 };
|