markdown-codec 3.0.1 → 3.1.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 +21 -3
- package/dist/ast/ast.cjs +2 -1
- package/dist/ast/ast.d.cts +2 -2
- package/dist/ast/ast.d.ts +2 -2
- package/dist/ast/ast.js +2 -1
- package/dist/{ast-DbjiuYr8.d.cts → ast-8XCbjRQT.d.cts} +14 -3
- package/dist/{ast-DbjiuYr8.d.ts → ast-8XCbjRQT.d.ts} +14 -3
- package/dist/block/block.cjs +79 -28
- package/dist/block/block.d.cts +6 -3
- package/dist/block/block.d.ts +6 -3
- package/dist/block/block.js +79 -28
- package/dist/block/definitions.d.cts +1 -1
- package/dist/block/definitions.d.ts +1 -1
- package/dist/block/node.cjs +2 -0
- package/dist/block/node.d.cts +3 -2
- package/dist/block/node.d.ts +3 -2
- package/dist/block/node.js +2 -0
- package/dist/block/table.d.cts +1 -1
- package/dist/block/table.d.ts +1 -1
- package/dist/diagnostics/diagnostics.cjs +15 -0
- package/dist/diagnostics/diagnostics.d.cts +2 -2
- package/dist/diagnostics/diagnostics.d.ts +2 -2
- package/dist/diagnostics/diagnostics.js +15 -1
- package/dist/{diagnostics-B72W0P_E.d.cts → diagnostics-BuO5-SW1.d.cts} +10 -1
- package/dist/{diagnostics-B72W0P_E.d.ts → diagnostics-BuO5-SW1.d.ts} +10 -1
- package/dist/emit/emit.cjs +77 -7
- package/dist/emit/emit.js +79 -9
- package/dist/emit/inline.cjs +2 -1
- package/dist/emit/inline.d.cts +1 -1
- package/dist/emit/inline.d.ts +1 -1
- package/dist/emit/inline.js +2 -1
- package/dist/emit/table.d.cts +1 -1
- package/dist/emit/table.d.ts +1 -1
- package/dist/footnote-CKk4JbLk.d.cts +15 -0
- package/dist/footnote-CKk4JbLk.d.ts +15 -0
- package/dist/html/render.cjs +7 -0
- package/dist/html/render.d.cts +1 -1
- package/dist/html/render.d.ts +1 -1
- package/dist/html/render.js +7 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/inline/footnote.cjs +30 -0
- package/dist/inline/footnote.d.cts +2 -0
- package/dist/inline/footnote.d.ts +2 -0
- package/dist/inline/footnote.js +27 -0
- package/dist/inline/inline.cjs +28 -3
- package/dist/inline/inline.d.cts +1 -1
- package/dist/inline/inline.d.ts +1 -1
- package/dist/inline/inline.js +28 -3
- package/dist/inline/node.cjs +1 -0
- package/dist/inline/node.d.cts +2 -1
- package/dist/inline/node.d.ts +2 -1
- package/dist/inline/node.js +1 -0
- package/dist/{inline-TuBQ2TUr.d.ts → inline-B_V7bs5j.d.ts} +3 -2
- package/dist/{inline-uVHJ5xzT.d.cts → inline-CXVQWQnW.d.cts} +3 -2
- package/dist/lower/front-matter.d.cts +1 -1
- package/dist/lower/front-matter.d.ts +1 -1
- package/dist/lower/inline.cjs +7 -0
- package/dist/lower/inline.d.cts +2 -2
- package/dist/lower/inline.d.ts +2 -2
- package/dist/lower/inline.js +8 -1
- package/dist/lower/lower.cjs +51 -0
- package/dist/lower/lower.js +51 -0
- package/dist/lower/table.d.cts +1 -1
- package/dist/lower/table.d.ts +1 -1
- package/dist/options/options.d.cts +2 -1
- package/dist/options/options.d.ts +2 -1
- package/dist/read.d.cts +1 -1
- package/dist/read.d.ts +1 -1
- package/dist/shared/style-constants.cjs +2 -0
- package/dist/shared/style-constants.d.cts +2 -1
- package/dist/shared/style-constants.d.ts +2 -1
- package/dist/shared/style-constants.js +2 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ graph TD
|
|
|
51
51
|
|
|
52
52
|
## Status
|
|
53
53
|
|
|
54
|
-
The scanner, block parser, and inline parser are complete hand-written implementations of CommonMark 0.31.2's two-phase algorithm plus GFM's table/strikethrough/autolink/task-list-item extensions. `readMarkdown`/`writeMarkdown`/`markdownCodec` are wired and real. Conformance suites measure the full public surface (`readMarkdown` → `writeMarkdown` → reparse → render to HTML) against the vendored CommonMark/GFM corpora — see [Fidelity](#fidelity) for why the rate is below 100% (dominated by what `ContentDocument` can represent, not parsing gaps).
|
|
54
|
+
The scanner, block parser, and inline parser are complete hand-written implementations of CommonMark 0.31.2's two-phase algorithm plus GFM's table/strikethrough/autolink/task-list-item extensions and GitHub's footnotes (see [Footnotes](#footnotes)). `readMarkdown`/`writeMarkdown`/`markdownCodec` are wired and real. Conformance suites measure the full public surface (`readMarkdown` → `writeMarkdown` → reparse → render to HTML) against the vendored CommonMark/GFM corpora — see [Fidelity](#fidelity) for why the rate is below 100% (dominated by what `ContentDocument` can represent, not parsing gaps).
|
|
55
55
|
|
|
56
56
|
## Getting started
|
|
57
57
|
|
|
@@ -80,6 +80,7 @@ import { readMarkdown, writeMarkdown } from 'markdown-codec';
|
|
|
80
80
|
|
|
81
81
|
const { document, diagnostics } = readMarkdown('# Title\n\nSome **bold** text with a [link](https://example.com).', {
|
|
82
82
|
frontMatter: true, // parse a leading YAML front matter block into ContentDocument.metadata
|
|
83
|
+
footnotes: true, // recognise [^label] markers and [^label]: definitions (default; see Footnotes)
|
|
83
84
|
images: (destination) => undefined, // a synchronous MarkdownImageResolver port for non-data: URI images
|
|
84
85
|
});
|
|
85
86
|
|
|
@@ -112,8 +113,8 @@ Modelled on `pdf-codec`'s own layering, aimed at CommonMark+GFM instead of PDF:
|
|
|
112
113
|
- **`src/ast/`** — markdown AST node types (document/block/inline union), Zod-first.
|
|
113
114
|
- **`src/options/`** / **`src/defaults/`** — read/write options (GFM toggles, sink, `AbortSignal`, write-side style) and defaults.
|
|
114
115
|
- **`src/scan/`** — CommonMark line/character scanner, plus `entity-table.ts` (generated from `assets/html-entities/entities.json`).
|
|
115
|
-
- **`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, GFM tables.
|
|
116
|
-
- **`src/inline/`** — emphasis, code spans, links, autolinks, raw HTML, GFM strikethrough, line breaks.
|
|
116
|
+
- **`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.
|
|
117
|
+
- **`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.
|
|
117
118
|
- **`src/html/`** — raw HTML recognition (bounded rules, not a general parser) plus `render.ts` (conformance oracle; internal only).
|
|
118
119
|
- **`src/image/`** — PNG/JPEG dimension reader and base64 codec, shared by `src/lower/` and `src/emit/`.
|
|
119
120
|
- **`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.
|
|
@@ -173,6 +174,23 @@ Every construct `src/lower`/`src/emit` cannot represent losslessly is a document
|
|
|
173
174
|
- **`md/paragraph-indent-dropped`** — `indentLeftPt` without a recognised styleId; indent dropped, paragraph renders.
|
|
174
175
|
- **`md/list-numid-fallback`** — a foreign or absent `numId` (depth-only `ContentListMembership`) falls back to a plain bullet list.
|
|
175
176
|
- **`md/table-cell-formatting-dropped`** / **`md/table-cell-multi-paragraph-joined`** — GFM cells have no rich-formatting or multi-paragraph representation.
|
|
177
|
+
- **`md/duplicate-footnote-definition`** — two definitions share a label; every reference resolves to the first, both are kept as written.
|
|
178
|
+
- **`md/footnote-reference-preserved-as-text`** — a reference site is a marked run, not an `anchor` construct; see [Footnotes](#footnotes).
|
|
179
|
+
- **`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.
|
|
180
|
+
- **`md/construct-unrepresented`** — a construct kind markdown has no syntax for renders transparently: its extent still appears, the construct itself does not.
|
|
181
|
+
|
|
182
|
+
## Footnotes
|
|
183
|
+
|
|
184
|
+
GitHub's footnote extension (`[^label]` markers, `[^label]: body` definitions) is on by default, alongside the four GFM toggles — switch it off with `footnotes: false`. Neither CommonMark nor the GFM spec document defines footnotes, so both spellings are ordinary text with it off.
|
|
185
|
+
|
|
186
|
+
The two halves of a footnote map onto **two different mechanisms**, and that split is structural rather than a choice:
|
|
187
|
+
|
|
188
|
+
- **A definition becomes an `anchor` construct.** `readMarkdown` emits document-schema.js 4.2.0's construct boundary markers — a `constructStart` carrying `{ kind: 'anchor', anchorType: 'footnote', name }`, the definition's own lowered body blocks, and a `constructEnd`. The body rides the construct's extent rather than `AnchorDescriptor.definition`, which names a key in a package-level definitions table a flat `ContentDocument` has no root to carry; a body is genuinely block content (several paragraphs, a code block, a list) that a string field could not have held either way. A bodyless `[^1]:` lowers to the point anchor the same descriptor describes: a pair with nothing between it.
|
|
189
|
+
- **A reference site stays a marked run.** A construct's extent is block-scoped by document-schema.js's own definition, and a reference sits between two runs inside a paragraph, so no block-level boundary marker can bracket it without splitting the paragraph in two. The schema names this gap itself and parks the inline-anchor case on a run-level extent mechanism it has not shipped. Until it does, the reference is a `ContentRun` keeping its own `[^label]` spelling and carrying `FOOTNOTE_REFERENCE_FONT_MARKER`, reported through `md/footnote-reference-preserved-as-text`.
|
|
190
|
+
|
|
191
|
+
Definitions are recognised only at the document's own top level. Inside a block quote or a list item, the pair's extent would sit inside a scope the enclosing container had already opened, which the marker contract forbids a producer from emitting — so the text stays an ordinary paragraph there. A heading inside a definition body is flattened to literal ATX text for the same reason.
|
|
192
|
+
|
|
193
|
+
`writeMarkdown` 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 it throws `MarkdownUnbalancedConstructMarkersError`. 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.
|
|
176
194
|
|
|
177
195
|
## Fidelity
|
|
178
196
|
|
package/dist/ast/ast.cjs
CHANGED
package/dist/ast/ast.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { MarkdownAutolinkNode, MarkdownBlockNode, MarkdownBlockquoteNode, MarkdownBulletMarker, MarkdownCodeBlockNode, MarkdownCodeSpanNode, MarkdownDocumentNode, MarkdownEmphasisMarker, MarkdownEmphasisNode, MarkdownEntityNode, MarkdownHardBreakNode, MarkdownHeadingNode, MarkdownHeadingStyle, MarkdownHtmlBlockNode, MarkdownImageNode, MarkdownInlineNode, MarkdownLinkNode, MarkdownListItemNode, MarkdownListMarkerType, MarkdownListNode, MarkdownMathBlockNode, MarkdownMathInlineNode, MarkdownNode, MarkdownOrderedListDelimiter, MarkdownParagraphNode, MarkdownPosition, MarkdownRawHtmlNode, MarkdownSoftBreakNode, MarkdownStrikethroughNode, MarkdownStrongNode, MarkdownTableAlignment, MarkdownTableCellNode, MarkdownTableNode, MarkdownTableRowNode, MarkdownTextNode, MarkdownThematicBreakNode, isMarkdownBlockNode, isMarkdownInlineNode };
|
|
1
|
+
import { A as MarkdownSoftBreakNode, B as isMarkdownInlineNode, C as MarkdownMathBlockNode, D as MarkdownParagraphNode, E as MarkdownOrderedListDelimiter, F as MarkdownTableNode, I as MarkdownTableRowNode, L as MarkdownTextNode, M as MarkdownStrongNode, N as MarkdownTableAlignment, O as MarkdownPosition, P as MarkdownTableCellNode, R as MarkdownThematicBreakNode, S as MarkdownListNode, T as MarkdownNode, _ as MarkdownImageNode, a as MarkdownCodeBlockNode, b as MarkdownListItemNode, c as MarkdownEmphasisMarker, d as MarkdownFootnoteDefinitionNode, f as MarkdownFootnoteReferenceNode, g as MarkdownHtmlBlockNode, h as MarkdownHeadingStyle, i as MarkdownBulletMarker, j as MarkdownStrikethroughNode, k as MarkdownRawHtmlNode, l as MarkdownEmphasisNode, m as MarkdownHeadingNode, n as MarkdownBlockNode, o as MarkdownCodeSpanNode, p as MarkdownHardBreakNode, r as MarkdownBlockquoteNode, s as MarkdownDocumentNode, t as MarkdownAutolinkNode, u as MarkdownEntityNode, v as MarkdownInlineNode, w as MarkdownMathInlineNode, x as MarkdownListMarkerType, y as MarkdownLinkNode, z as isMarkdownBlockNode } from "../ast-8XCbjRQT.cjs";
|
|
2
|
+
export { MarkdownAutolinkNode, MarkdownBlockNode, MarkdownBlockquoteNode, MarkdownBulletMarker, MarkdownCodeBlockNode, MarkdownCodeSpanNode, MarkdownDocumentNode, MarkdownEmphasisMarker, MarkdownEmphasisNode, MarkdownEntityNode, MarkdownFootnoteDefinitionNode, MarkdownFootnoteReferenceNode, MarkdownHardBreakNode, MarkdownHeadingNode, MarkdownHeadingStyle, MarkdownHtmlBlockNode, MarkdownImageNode, MarkdownInlineNode, MarkdownLinkNode, MarkdownListItemNode, MarkdownListMarkerType, MarkdownListNode, MarkdownMathBlockNode, MarkdownMathInlineNode, MarkdownNode, MarkdownOrderedListDelimiter, MarkdownParagraphNode, MarkdownPosition, MarkdownRawHtmlNode, MarkdownSoftBreakNode, MarkdownStrikethroughNode, MarkdownStrongNode, MarkdownTableAlignment, MarkdownTableCellNode, MarkdownTableNode, MarkdownTableRowNode, MarkdownTextNode, MarkdownThematicBreakNode, isMarkdownBlockNode, isMarkdownInlineNode };
|
package/dist/ast/ast.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { MarkdownAutolinkNode, MarkdownBlockNode, MarkdownBlockquoteNode, MarkdownBulletMarker, MarkdownCodeBlockNode, MarkdownCodeSpanNode, MarkdownDocumentNode, MarkdownEmphasisMarker, MarkdownEmphasisNode, MarkdownEntityNode, MarkdownHardBreakNode, MarkdownHeadingNode, MarkdownHeadingStyle, MarkdownHtmlBlockNode, MarkdownImageNode, MarkdownInlineNode, MarkdownLinkNode, MarkdownListItemNode, MarkdownListMarkerType, MarkdownListNode, MarkdownMathBlockNode, MarkdownMathInlineNode, MarkdownNode, MarkdownOrderedListDelimiter, MarkdownParagraphNode, MarkdownPosition, MarkdownRawHtmlNode, MarkdownSoftBreakNode, MarkdownStrikethroughNode, MarkdownStrongNode, MarkdownTableAlignment, MarkdownTableCellNode, MarkdownTableNode, MarkdownTableRowNode, MarkdownTextNode, MarkdownThematicBreakNode, isMarkdownBlockNode, isMarkdownInlineNode };
|
|
1
|
+
import { A as MarkdownSoftBreakNode, B as isMarkdownInlineNode, C as MarkdownMathBlockNode, D as MarkdownParagraphNode, E as MarkdownOrderedListDelimiter, F as MarkdownTableNode, I as MarkdownTableRowNode, L as MarkdownTextNode, M as MarkdownStrongNode, N as MarkdownTableAlignment, O as MarkdownPosition, P as MarkdownTableCellNode, R as MarkdownThematicBreakNode, S as MarkdownListNode, T as MarkdownNode, _ as MarkdownImageNode, a as MarkdownCodeBlockNode, b as MarkdownListItemNode, c as MarkdownEmphasisMarker, d as MarkdownFootnoteDefinitionNode, f as MarkdownFootnoteReferenceNode, g as MarkdownHtmlBlockNode, h as MarkdownHeadingStyle, i as MarkdownBulletMarker, j as MarkdownStrikethroughNode, k as MarkdownRawHtmlNode, l as MarkdownEmphasisNode, m as MarkdownHeadingNode, n as MarkdownBlockNode, o as MarkdownCodeSpanNode, p as MarkdownHardBreakNode, r as MarkdownBlockquoteNode, s as MarkdownDocumentNode, t as MarkdownAutolinkNode, u as MarkdownEntityNode, v as MarkdownInlineNode, w as MarkdownMathInlineNode, x as MarkdownListMarkerType, y as MarkdownLinkNode, z as isMarkdownBlockNode } from "../ast-8XCbjRQT.js";
|
|
2
|
+
export { MarkdownAutolinkNode, MarkdownBlockNode, MarkdownBlockquoteNode, MarkdownBulletMarker, MarkdownCodeBlockNode, MarkdownCodeSpanNode, MarkdownDocumentNode, MarkdownEmphasisMarker, MarkdownEmphasisNode, MarkdownEntityNode, MarkdownFootnoteDefinitionNode, MarkdownFootnoteReferenceNode, MarkdownHardBreakNode, MarkdownHeadingNode, MarkdownHeadingStyle, MarkdownHtmlBlockNode, MarkdownImageNode, MarkdownInlineNode, MarkdownLinkNode, MarkdownListItemNode, MarkdownListMarkerType, MarkdownListNode, MarkdownMathBlockNode, MarkdownMathInlineNode, MarkdownNode, MarkdownOrderedListDelimiter, MarkdownParagraphNode, MarkdownPosition, MarkdownRawHtmlNode, MarkdownSoftBreakNode, MarkdownStrikethroughNode, MarkdownStrongNode, MarkdownTableAlignment, MarkdownTableCellNode, MarkdownTableNode, MarkdownTableRowNode, MarkdownTextNode, MarkdownThematicBreakNode, isMarkdownBlockNode, isMarkdownInlineNode };
|
package/dist/ast/ast.js
CHANGED
|
@@ -6,7 +6,7 @@ interface MarkdownPosition {
|
|
|
6
6
|
readonly endColumn: number;
|
|
7
7
|
}
|
|
8
8
|
type MarkdownNode = MarkdownBlockNode | MarkdownInlineNode;
|
|
9
|
-
type MarkdownBlockNode = MarkdownDocumentNode | MarkdownParagraphNode | MarkdownHeadingNode | MarkdownBlockquoteNode | MarkdownListNode | MarkdownListItemNode | MarkdownCodeBlockNode | MarkdownThematicBreakNode | MarkdownHtmlBlockNode | MarkdownTableNode | MarkdownTableRowNode | MarkdownTableCellNode | MarkdownMathBlockNode;
|
|
9
|
+
type MarkdownBlockNode = MarkdownDocumentNode | MarkdownParagraphNode | MarkdownHeadingNode | MarkdownBlockquoteNode | MarkdownListNode | MarkdownListItemNode | MarkdownCodeBlockNode | MarkdownThematicBreakNode | MarkdownHtmlBlockNode | MarkdownTableNode | MarkdownTableRowNode | MarkdownTableCellNode | MarkdownMathBlockNode | MarkdownFootnoteDefinitionNode;
|
|
10
10
|
interface MarkdownDocumentNode {
|
|
11
11
|
readonly type: 'document';
|
|
12
12
|
readonly children: MarkdownBlockNode[];
|
|
@@ -89,7 +89,13 @@ interface MarkdownMathBlockNode {
|
|
|
89
89
|
readonly literal: string;
|
|
90
90
|
readonly position?: MarkdownPosition;
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
interface MarkdownFootnoteDefinitionNode {
|
|
93
|
+
readonly type: 'footnoteDefinition';
|
|
94
|
+
readonly label: string;
|
|
95
|
+
readonly children: MarkdownBlockNode[];
|
|
96
|
+
readonly position?: MarkdownPosition;
|
|
97
|
+
}
|
|
98
|
+
type MarkdownInlineNode = MarkdownTextNode | MarkdownEmphasisNode | MarkdownStrongNode | MarkdownStrikethroughNode | MarkdownCodeSpanNode | MarkdownLinkNode | MarkdownImageNode | MarkdownAutolinkNode | MarkdownHardBreakNode | MarkdownSoftBreakNode | MarkdownRawHtmlNode | MarkdownEntityNode | MarkdownMathInlineNode | MarkdownFootnoteReferenceNode;
|
|
93
99
|
type MarkdownEmphasisMarker = '_' | '*';
|
|
94
100
|
interface MarkdownTextNode {
|
|
95
101
|
readonly type: 'text';
|
|
@@ -164,7 +170,12 @@ interface MarkdownMathInlineNode {
|
|
|
164
170
|
readonly literal: string;
|
|
165
171
|
readonly position?: MarkdownPosition;
|
|
166
172
|
}
|
|
173
|
+
interface MarkdownFootnoteReferenceNode {
|
|
174
|
+
readonly type: 'footnoteReference';
|
|
175
|
+
readonly label: string;
|
|
176
|
+
readonly position?: MarkdownPosition;
|
|
177
|
+
}
|
|
167
178
|
declare function isMarkdownBlockNode(node: MarkdownNode): node is MarkdownBlockNode;
|
|
168
179
|
declare function isMarkdownInlineNode(node: MarkdownNode): node is MarkdownInlineNode;
|
|
169
180
|
//#endregion
|
|
170
|
-
export {
|
|
181
|
+
export { MarkdownSoftBreakNode as A, isMarkdownInlineNode as B, MarkdownMathBlockNode as C, MarkdownParagraphNode as D, MarkdownOrderedListDelimiter as E, MarkdownTableNode as F, MarkdownTableRowNode as I, MarkdownTextNode as L, MarkdownStrongNode as M, MarkdownTableAlignment as N, MarkdownPosition as O, MarkdownTableCellNode as P, MarkdownThematicBreakNode as R, MarkdownListNode as S, MarkdownNode as T, MarkdownImageNode as _, MarkdownCodeBlockNode as a, MarkdownListItemNode as b, MarkdownEmphasisMarker as c, MarkdownFootnoteDefinitionNode as d, MarkdownFootnoteReferenceNode as f, MarkdownHtmlBlockNode as g, MarkdownHeadingStyle as h, MarkdownBulletMarker as i, MarkdownStrikethroughNode as j, MarkdownRawHtmlNode as k, MarkdownEmphasisNode as l, MarkdownHeadingNode as m, MarkdownBlockNode as n, MarkdownCodeSpanNode as o, MarkdownHardBreakNode as p, MarkdownBlockquoteNode as r, MarkdownDocumentNode as s, MarkdownAutolinkNode as t, MarkdownEntityNode as u, MarkdownInlineNode as v, MarkdownMathInlineNode as w, MarkdownListMarkerType as x, MarkdownLinkNode as y, isMarkdownBlockNode as z };
|
|
@@ -6,7 +6,7 @@ interface MarkdownPosition {
|
|
|
6
6
|
readonly endColumn: number;
|
|
7
7
|
}
|
|
8
8
|
type MarkdownNode = MarkdownBlockNode | MarkdownInlineNode;
|
|
9
|
-
type MarkdownBlockNode = MarkdownDocumentNode | MarkdownParagraphNode | MarkdownHeadingNode | MarkdownBlockquoteNode | MarkdownListNode | MarkdownListItemNode | MarkdownCodeBlockNode | MarkdownThematicBreakNode | MarkdownHtmlBlockNode | MarkdownTableNode | MarkdownTableRowNode | MarkdownTableCellNode | MarkdownMathBlockNode;
|
|
9
|
+
type MarkdownBlockNode = MarkdownDocumentNode | MarkdownParagraphNode | MarkdownHeadingNode | MarkdownBlockquoteNode | MarkdownListNode | MarkdownListItemNode | MarkdownCodeBlockNode | MarkdownThematicBreakNode | MarkdownHtmlBlockNode | MarkdownTableNode | MarkdownTableRowNode | MarkdownTableCellNode | MarkdownMathBlockNode | MarkdownFootnoteDefinitionNode;
|
|
10
10
|
interface MarkdownDocumentNode {
|
|
11
11
|
readonly type: 'document';
|
|
12
12
|
readonly children: MarkdownBlockNode[];
|
|
@@ -89,7 +89,13 @@ interface MarkdownMathBlockNode {
|
|
|
89
89
|
readonly literal: string;
|
|
90
90
|
readonly position?: MarkdownPosition;
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
interface MarkdownFootnoteDefinitionNode {
|
|
93
|
+
readonly type: 'footnoteDefinition';
|
|
94
|
+
readonly label: string;
|
|
95
|
+
readonly children: MarkdownBlockNode[];
|
|
96
|
+
readonly position?: MarkdownPosition;
|
|
97
|
+
}
|
|
98
|
+
type MarkdownInlineNode = MarkdownTextNode | MarkdownEmphasisNode | MarkdownStrongNode | MarkdownStrikethroughNode | MarkdownCodeSpanNode | MarkdownLinkNode | MarkdownImageNode | MarkdownAutolinkNode | MarkdownHardBreakNode | MarkdownSoftBreakNode | MarkdownRawHtmlNode | MarkdownEntityNode | MarkdownMathInlineNode | MarkdownFootnoteReferenceNode;
|
|
93
99
|
type MarkdownEmphasisMarker = '_' | '*';
|
|
94
100
|
interface MarkdownTextNode {
|
|
95
101
|
readonly type: 'text';
|
|
@@ -164,7 +170,12 @@ interface MarkdownMathInlineNode {
|
|
|
164
170
|
readonly literal: string;
|
|
165
171
|
readonly position?: MarkdownPosition;
|
|
166
172
|
}
|
|
173
|
+
interface MarkdownFootnoteReferenceNode {
|
|
174
|
+
readonly type: 'footnoteReference';
|
|
175
|
+
readonly label: string;
|
|
176
|
+
readonly position?: MarkdownPosition;
|
|
177
|
+
}
|
|
167
178
|
declare function isMarkdownBlockNode(node: MarkdownNode): node is MarkdownBlockNode;
|
|
168
179
|
declare function isMarkdownInlineNode(node: MarkdownNode): node is MarkdownInlineNode;
|
|
169
180
|
//#endregion
|
|
170
|
-
export {
|
|
181
|
+
export { MarkdownSoftBreakNode as A, isMarkdownInlineNode as B, MarkdownMathBlockNode as C, MarkdownParagraphNode as D, MarkdownOrderedListDelimiter as E, MarkdownTableNode as F, MarkdownTableRowNode as I, MarkdownTextNode as L, MarkdownStrongNode as M, MarkdownTableAlignment as N, MarkdownPosition as O, MarkdownTableCellNode as P, MarkdownThematicBreakNode as R, MarkdownListNode as S, MarkdownNode as T, MarkdownImageNode as _, MarkdownCodeBlockNode as a, MarkdownListItemNode as b, MarkdownEmphasisMarker as c, MarkdownFootnoteDefinitionNode as d, MarkdownFootnoteReferenceNode as f, MarkdownHtmlBlockNode as g, MarkdownHeadingStyle as h, MarkdownBulletMarker as i, MarkdownStrikethroughNode as j, MarkdownRawHtmlNode as k, MarkdownEmphasisNode as l, MarkdownHeadingNode as m, MarkdownBlockNode as n, MarkdownCodeSpanNode as o, MarkdownHardBreakNode as p, MarkdownBlockquoteNode as r, MarkdownDocumentNode as s, MarkdownAutolinkNode as t, MarkdownEntityNode as u, MarkdownInlineNode as v, MarkdownMathInlineNode as w, MarkdownListMarkerType as x, MarkdownLinkNode as y, isMarkdownBlockNode as z };
|
package/dist/block/block.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_inline_footnote = require("../inline/footnote.cjs");
|
|
2
3
|
require("../defaults/defaults.cjs");
|
|
3
4
|
const require_diagnostics_diagnostics = require("../diagnostics/diagnostics.cjs");
|
|
4
5
|
const require_html_html = require("../html/html.cjs");
|
|
@@ -14,7 +15,7 @@ const TASK_LIST_MARKER_PATTERN = /^\[([ xX])\][ \t]/;
|
|
|
14
15
|
const NUL_REPLACEMENT = "�";
|
|
15
16
|
const NUL_PATTERN = /\0/g;
|
|
16
17
|
const LINE_ENDING_PATTERN = /\r\n|\n|\r/;
|
|
17
|
-
const MAYBE_SPECIAL_PATTERN = /^[#$`~*+_=<>0-9|:-]/;
|
|
18
|
+
const MAYBE_SPECIAL_PATTERN = /^[#$`~*+_=<>[0-9|:-]/;
|
|
18
19
|
const ATX_MARKER_PATTERN = /^#{1,6}(?:[ \t]+|$)/;
|
|
19
20
|
const ATX_ONLY_CLOSING_SEQUENCE_PATTERN = /^[ \t]*#+[ \t]*$/;
|
|
20
21
|
const ATX_TRAILING_CLOSING_SEQUENCE_PATTERN = /[ \t]+#+[ \t]*$/;
|
|
@@ -46,8 +47,10 @@ function headingLevelOf(hashes) {
|
|
|
46
47
|
}
|
|
47
48
|
var BlockParser = class {
|
|
48
49
|
references = /* @__PURE__ */ new Map();
|
|
50
|
+
footnotes = /* @__PURE__ */ new Set();
|
|
49
51
|
document = new require_block_node.BlockNode("document", 1);
|
|
50
52
|
tables;
|
|
53
|
+
footnotesEnabled;
|
|
51
54
|
sink;
|
|
52
55
|
maxNesting;
|
|
53
56
|
tip = this.document;
|
|
@@ -59,6 +62,7 @@ var BlockParser = class {
|
|
|
59
62
|
nestingDepth = 0;
|
|
60
63
|
constructor(options) {
|
|
61
64
|
this.tables = options.gfmTables ?? true;
|
|
65
|
+
this.footnotesEnabled = options.footnotes ?? true;
|
|
62
66
|
this.sink = options.sink ?? require_diagnostics_diagnostics.NOOP_MARKDOWN_DIAGNOSTIC_SINK;
|
|
63
67
|
this.maxNesting = options.maxNesting ?? 250;
|
|
64
68
|
}
|
|
@@ -129,6 +133,7 @@ var BlockParser = class {
|
|
|
129
133
|
case "list": return "matched";
|
|
130
134
|
case "blockquote": return this.continueBlockquote();
|
|
131
135
|
case "listItem": return this.continueListItem(node);
|
|
136
|
+
case "footnoteDefinition": return this.continueFootnoteDefinition(node);
|
|
132
137
|
case "codeBlock": return this.continueCodeBlock(node);
|
|
133
138
|
case "mathBlock": return this.continueMathBlock(node);
|
|
134
139
|
case "htmlBlock": return this.line.blank && HTML_BLOCK_BLANK_LINE_END_TYPES.includes(node.htmlBlockType) ? "not-matched" : "matched";
|
|
@@ -163,6 +168,18 @@ var BlockParser = class {
|
|
|
163
168
|
}
|
|
164
169
|
return "not-matched";
|
|
165
170
|
}
|
|
171
|
+
continueFootnoteDefinition(node) {
|
|
172
|
+
if (this.line.blank) {
|
|
173
|
+
if (node.children.length === 0) return "not-matched";
|
|
174
|
+
this.line.advanceToNextNonspace();
|
|
175
|
+
return "matched";
|
|
176
|
+
}
|
|
177
|
+
if (this.line.indent >= 4) {
|
|
178
|
+
this.line.advance(4);
|
|
179
|
+
return "matched";
|
|
180
|
+
}
|
|
181
|
+
return "not-matched";
|
|
182
|
+
}
|
|
166
183
|
continueCodeBlock(node) {
|
|
167
184
|
if (!node.fenced) {
|
|
168
185
|
if (this.line.indent >= 4) {
|
|
@@ -216,6 +233,7 @@ var BlockParser = class {
|
|
|
216
233
|
() => this.tryAtxHeadingStart(),
|
|
217
234
|
() => this.tryCodeFenceStart(),
|
|
218
235
|
() => this.tryMathBlockStart(),
|
|
236
|
+
() => this.tryFootnoteDefinitionStart(container),
|
|
219
237
|
() => this.tryHtmlBlockStart(container),
|
|
220
238
|
() => this.tryPromoteParagraph(container),
|
|
221
239
|
() => this.tryThematicBreakStart(),
|
|
@@ -273,6 +291,29 @@ var BlockParser = class {
|
|
|
273
291
|
this.line.advance(MATH_BLOCK_MARKER_LENGTH);
|
|
274
292
|
return "leaf";
|
|
275
293
|
}
|
|
294
|
+
tryFootnoteDefinitionStart(container) {
|
|
295
|
+
if (!this.footnotesEnabled || this.line.indented || !this.footnoteDefinitionMayOpenIn(container)) return "none";
|
|
296
|
+
const marker = require_inline_footnote.matchFootnoteDefinitionMarker(this.line.restFromNextNonspace());
|
|
297
|
+
if (marker === void 0) return "none";
|
|
298
|
+
if (this.footnotes.has(marker.label)) this.sink({
|
|
299
|
+
code: require_diagnostics_diagnostics.MarkdownDiagnosticCodes.DUPLICATE_FOOTNOTE_DEFINITION,
|
|
300
|
+
severity: "warning",
|
|
301
|
+
message: `footnote "${marker.label}" was already defined earlier in the document; every reference resolves to the first definition, and both definitions are kept as written`,
|
|
302
|
+
line: this.lineNumber
|
|
303
|
+
});
|
|
304
|
+
this.footnotes.add(marker.label);
|
|
305
|
+
this.line.advanceToNextNonspace();
|
|
306
|
+
this.closeUnmatchedBlocks();
|
|
307
|
+
const node = this.addChild("footnoteDefinition");
|
|
308
|
+
node.footnoteLabel = marker.label;
|
|
309
|
+
this.line.advance(marker.markerLength);
|
|
310
|
+
return "container";
|
|
311
|
+
}
|
|
312
|
+
footnoteDefinitionMayOpenIn(container) {
|
|
313
|
+
let node = container;
|
|
314
|
+
while (node?.kind === "list") node = node.parent;
|
|
315
|
+
return node?.kind === "document";
|
|
316
|
+
}
|
|
276
317
|
tryHtmlBlockStart(container) {
|
|
277
318
|
if (this.line.indented || this.line.peekNextNonspace() !== "<") return "none";
|
|
278
319
|
const interruptsParagraph = container.kind === "paragraph" || !this.allClosed && !this.line.blank && this.tip.kind === "paragraph";
|
|
@@ -429,15 +470,15 @@ var BlockParser = class {
|
|
|
429
470
|
node.literal = breakIndex === -1 ? "" : node.content.slice(breakIndex + 1);
|
|
430
471
|
}
|
|
431
472
|
};
|
|
432
|
-
function toInlineChildren(content,
|
|
433
|
-
return require_inline_inline.parseInlines(content.trim(), references, options);
|
|
473
|
+
function toInlineChildren(content, context) {
|
|
474
|
+
return require_inline_inline.parseInlines(content.trim(), context.references, context.footnotes, context.options);
|
|
434
475
|
}
|
|
435
|
-
function toHeadingNode(node,
|
|
476
|
+
function toHeadingNode(node, context) {
|
|
436
477
|
return {
|
|
437
478
|
type: "heading",
|
|
438
479
|
level: node.level,
|
|
439
480
|
style: node.setext ? "setext" : "atx",
|
|
440
|
-
children: toInlineChildren(node.content,
|
|
481
|
+
children: toInlineChildren(node.content, context)
|
|
441
482
|
};
|
|
442
483
|
}
|
|
443
484
|
function extractTaskListMarker(itemChildren) {
|
|
@@ -448,19 +489,19 @@ function extractTaskListMarker(itemChildren) {
|
|
|
448
489
|
first.content = first.content.slice(match[0].length);
|
|
449
490
|
return match[1] !== " ";
|
|
450
491
|
}
|
|
451
|
-
function toListItemNode(item,
|
|
452
|
-
const checked = options.gfmTaskLists ?? true ? extractTaskListMarker(item.children) : void 0;
|
|
492
|
+
function toListItemNode(item, context) {
|
|
493
|
+
const checked = context.options.gfmTaskLists ?? true ? extractTaskListMarker(item.children) : void 0;
|
|
453
494
|
return checked === void 0 ? {
|
|
454
495
|
type: "listItem",
|
|
455
|
-
children: toAstBlocks(item.children,
|
|
496
|
+
children: toAstBlocks(item.children, context)
|
|
456
497
|
} : {
|
|
457
498
|
type: "listItem",
|
|
458
499
|
checked,
|
|
459
|
-
children: toAstBlocks(item.children,
|
|
500
|
+
children: toAstBlocks(item.children, context)
|
|
460
501
|
};
|
|
461
502
|
}
|
|
462
|
-
function toListNode(node,
|
|
463
|
-
const children = node.children.map((item) => toListItemNode(item,
|
|
503
|
+
function toListNode(node, context) {
|
|
504
|
+
const children = node.children.map((item) => toListItemNode(item, context));
|
|
464
505
|
const data = node.listData;
|
|
465
506
|
if (data?.type === "ordered") return {
|
|
466
507
|
type: "list",
|
|
@@ -478,20 +519,20 @@ function toListNode(node, references, options) {
|
|
|
478
519
|
children
|
|
479
520
|
};
|
|
480
521
|
}
|
|
481
|
-
function toTableRow(cells, header,
|
|
522
|
+
function toTableRow(cells, header, context) {
|
|
482
523
|
return {
|
|
483
524
|
type: "tableRow",
|
|
484
525
|
header,
|
|
485
526
|
children: cells.map((cell) => ({
|
|
486
527
|
type: "tableCell",
|
|
487
|
-
children: toInlineChildren(cell,
|
|
528
|
+
children: toInlineChildren(cell, context)
|
|
488
529
|
}))
|
|
489
530
|
};
|
|
490
531
|
}
|
|
491
|
-
function toTableNode(node,
|
|
492
|
-
const sink = options.sink ?? require_diagnostics_diagnostics.NOOP_MARKDOWN_DIAGNOSTIC_SINK;
|
|
532
|
+
function toTableNode(node, context) {
|
|
533
|
+
const sink = context.options.sink ?? require_diagnostics_diagnostics.NOOP_MARKDOWN_DIAGNOSTIC_SINK;
|
|
493
534
|
const columnCount = node.alignments.length;
|
|
494
|
-
const rows = [toTableRow(require_block_table.fitRowToColumns(require_block_table.splitTableRow(node.headerLine), columnCount), true,
|
|
535
|
+
const rows = [toTableRow(require_block_table.fitRowToColumns(require_block_table.splitTableRow(node.headerLine), columnCount), true, context)];
|
|
495
536
|
for (const rowLine of node.content.split("\n")) {
|
|
496
537
|
if (rowLine.trim().length === 0) continue;
|
|
497
538
|
const cells = require_block_table.splitTableRow(rowLine);
|
|
@@ -501,7 +542,7 @@ function toTableNode(node, references, options) {
|
|
|
501
542
|
message: `table row has ${String(cells.length)} cell(s), but the header row declares ${String(columnCount)}; the row is padded with empty cells or truncated to fit`,
|
|
502
543
|
line: node.startLine
|
|
503
544
|
});
|
|
504
|
-
rows.push(toTableRow(require_block_table.fitRowToColumns(cells, columnCount), false,
|
|
545
|
+
rows.push(toTableRow(require_block_table.fitRowToColumns(cells, columnCount), false, context));
|
|
505
546
|
}
|
|
506
547
|
return {
|
|
507
548
|
type: "table",
|
|
@@ -509,18 +550,23 @@ function toTableNode(node, references, options) {
|
|
|
509
550
|
children: rows
|
|
510
551
|
};
|
|
511
552
|
}
|
|
512
|
-
function toAstBlock(node,
|
|
553
|
+
function toAstBlock(node, context) {
|
|
513
554
|
switch (node.kind) {
|
|
514
555
|
case "paragraph": return {
|
|
515
556
|
type: "paragraph",
|
|
516
|
-
children: toInlineChildren(node.content,
|
|
557
|
+
children: toInlineChildren(node.content, context)
|
|
517
558
|
};
|
|
518
|
-
case "heading": return toHeadingNode(node,
|
|
559
|
+
case "heading": return toHeadingNode(node, context);
|
|
519
560
|
case "blockquote": return {
|
|
520
561
|
type: "blockquote",
|
|
521
|
-
children: toAstBlocks(node.children,
|
|
562
|
+
children: toAstBlocks(node.children, context)
|
|
563
|
+
};
|
|
564
|
+
case "list": return toListNode(node, context);
|
|
565
|
+
case "footnoteDefinition": return {
|
|
566
|
+
type: "footnoteDefinition",
|
|
567
|
+
label: node.footnoteLabel,
|
|
568
|
+
children: toAstBlocks(node.children, context)
|
|
522
569
|
};
|
|
523
|
-
case "list": return toListNode(node, references, options);
|
|
524
570
|
case "codeBlock": return node.fenced ? {
|
|
525
571
|
type: "codeBlock",
|
|
526
572
|
fenced: true,
|
|
@@ -541,15 +587,15 @@ function toAstBlock(node, references, options) {
|
|
|
541
587
|
type: "mathBlock",
|
|
542
588
|
literal: node.literal
|
|
543
589
|
};
|
|
544
|
-
case "table": return toTableNode(node,
|
|
590
|
+
case "table": return toTableNode(node, context);
|
|
545
591
|
case "document":
|
|
546
592
|
case "listItem": return;
|
|
547
593
|
}
|
|
548
594
|
}
|
|
549
|
-
function toAstBlocks(nodes,
|
|
595
|
+
function toAstBlocks(nodes, context) {
|
|
550
596
|
const blocks = [];
|
|
551
597
|
for (const node of nodes) {
|
|
552
|
-
const converted = toAstBlock(node,
|
|
598
|
+
const converted = toAstBlock(node, context);
|
|
553
599
|
if (converted !== void 0) blocks.push(converted);
|
|
554
600
|
}
|
|
555
601
|
return blocks;
|
|
@@ -557,13 +603,18 @@ function toAstBlocks(nodes, references, options) {
|
|
|
557
603
|
function parseMarkdown(source, options = {}) {
|
|
558
604
|
const parser = new BlockParser(options);
|
|
559
605
|
const root = parser.parse(source);
|
|
560
|
-
const
|
|
606
|
+
const context = {
|
|
607
|
+
references: parser.references,
|
|
608
|
+
footnotes: parser.footnotes,
|
|
609
|
+
options
|
|
610
|
+
};
|
|
561
611
|
return {
|
|
562
612
|
document: {
|
|
563
613
|
type: "document",
|
|
564
|
-
children: toAstBlocks(root.children,
|
|
614
|
+
children: toAstBlocks(root.children, context)
|
|
565
615
|
},
|
|
566
|
-
references
|
|
616
|
+
references: context.references,
|
|
617
|
+
footnotes: context.footnotes
|
|
567
618
|
};
|
|
568
619
|
}
|
|
569
620
|
//#endregion
|
package/dist/block/block.d.cts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { s as MarkdownDocumentNode } from "../ast-
|
|
2
|
-
import {
|
|
1
|
+
import { s as MarkdownDocumentNode } from "../ast-8XCbjRQT.cjs";
|
|
2
|
+
import { r as FootnoteLabelSet } from "../footnote-CKk4JbLk.cjs";
|
|
3
|
+
import { i as MarkdownDiagnosticSink } from "../diagnostics-BuO5-SW1.cjs";
|
|
3
4
|
import { n as LinkReferenceMap } from "../link-Dv4kxVjk.cjs";
|
|
4
|
-
import { t as InlineParseOptions } from "../inline-
|
|
5
|
+
import { t as InlineParseOptions } from "../inline-CXVQWQnW.cjs";
|
|
5
6
|
//#region src/block/block.d.ts
|
|
6
7
|
interface MarkdownParseOptions extends InlineParseOptions {
|
|
7
8
|
readonly gfmTables?: boolean;
|
|
8
9
|
readonly gfmTaskLists?: boolean;
|
|
10
|
+
readonly footnotes?: boolean;
|
|
9
11
|
readonly maxNesting?: number;
|
|
10
12
|
readonly sink?: MarkdownDiagnosticSink;
|
|
11
13
|
}
|
|
12
14
|
interface ParsedMarkdown {
|
|
13
15
|
readonly document: MarkdownDocumentNode;
|
|
14
16
|
readonly references: LinkReferenceMap;
|
|
17
|
+
readonly footnotes: FootnoteLabelSet;
|
|
15
18
|
}
|
|
16
19
|
declare function parseMarkdown(source: string, options?: MarkdownParseOptions): ParsedMarkdown;
|
|
17
20
|
//#endregion
|
package/dist/block/block.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { s as MarkdownDocumentNode } from "../ast-
|
|
2
|
-
import {
|
|
1
|
+
import { s as MarkdownDocumentNode } from "../ast-8XCbjRQT.js";
|
|
2
|
+
import { r as FootnoteLabelSet } from "../footnote-CKk4JbLk.js";
|
|
3
|
+
import { i as MarkdownDiagnosticSink } from "../diagnostics-BuO5-SW1.js";
|
|
3
4
|
import { n as LinkReferenceMap } from "../link-Dv4kxVjk.js";
|
|
4
|
-
import { t as InlineParseOptions } from "../inline-
|
|
5
|
+
import { t as InlineParseOptions } from "../inline-B_V7bs5j.js";
|
|
5
6
|
//#region src/block/block.d.ts
|
|
6
7
|
interface MarkdownParseOptions extends InlineParseOptions {
|
|
7
8
|
readonly gfmTables?: boolean;
|
|
8
9
|
readonly gfmTaskLists?: boolean;
|
|
10
|
+
readonly footnotes?: boolean;
|
|
9
11
|
readonly maxNesting?: number;
|
|
10
12
|
readonly sink?: MarkdownDiagnosticSink;
|
|
11
13
|
}
|
|
12
14
|
interface ParsedMarkdown {
|
|
13
15
|
readonly document: MarkdownDocumentNode;
|
|
14
16
|
readonly references: LinkReferenceMap;
|
|
17
|
+
readonly footnotes: FootnoteLabelSet;
|
|
15
18
|
}
|
|
16
19
|
declare function parseMarkdown(source: string, options?: MarkdownParseOptions): ParsedMarkdown;
|
|
17
20
|
//#endregion
|