@tabnas/markdown 0.4.2 → 0.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 CHANGED
@@ -1,6 +1,17 @@
1
1
  # @tabnas/markdown (TypeScript)
2
2
 
3
- A [Tabnas](https://github.com/tabnas/parser) grammar plugin that parses **CommonMark / GFM Markdown** into a JSON AST. Built on the bare `@tabnas/parser` engine.
3
+ A CommonMark parser for the [Tabnas](https://github.com/tabnas/parser) engine.
4
+
5
+ **This parser is conformant to CommonMark 0.31.2** — all 652 examples, across all 26
6
+ sections of the spec suite, in both runtimes. The suite is vendored in this repository, so
7
+ the claim is checkable: `npm run conformance` reports 652/652, with no build step and no
8
+ engine installed. It runs with the GFM extensions off, which is what measuring CommonMark
9
+ conformance means — with `gfm: true` the extensions deliberately change nine of those
10
+ examples. It also implements **all five GFM extensions** — tables, task list
11
+ items, autolink literals, strikethrough and disallowed raw HTML — 24/24 on the vendored
12
+ GFM corpus, via `npm run conformance-gfm`.
13
+
14
+ This is the canonical implementation; [`go/`](../go/README.md) is a port of it.
4
15
 
5
16
  [![npm version](https://img.shields.io/npm/v/@tabnas/markdown.svg)](https://npmjs.com/package/@tabnas/markdown)
6
17
  [![build](https://github.com/tabnas/markdown/actions/workflows/build.yml/badge.svg)](https://github.com/tabnas/markdown/actions/workflows/build.yml)
@@ -8,6 +19,78 @@ A [Tabnas](https://github.com/tabnas/parser) grammar plugin that parses **Common
8
19
  | ![Voxgig](https://www.voxgig.com/res/img/vgt01r.png) | This open source module is sponsored and supported by [Voxgig](https://www.voxgig.com). |
9
20
  | ---------------------------------------------------- | --------------------------------------------------------------------------------------- |
10
21
 
22
+ ## Two outputs
23
+
24
+ **The AST is the primary output.** `parseDocument()` returns it directly; the renderer
25
+ does not run.
26
+
27
+ ```js
28
+ import { parseDocument } from '@tabnas/markdown'
29
+
30
+ const doc = parseDocument('# Hello\n\nHello *world*')
31
+
32
+ doc.children[0] // => { type: 'heading', depth: 1, children: [ { type: 'text', value: 'Hello' } ] }
33
+ doc.children[1] // => { type: 'paragraph', children: [ { type: 'text', value: 'Hello ' }, { type: 'emphasis', children: [ { type: 'text', value: 'world' } ] } ] }
34
+ ```
35
+
36
+ **HTML is available, on request, via `toHtml()`.** The CommonMark suite scores HTML
37
+ output, so the renderer is what makes the 652/652 claim measurable; that it is useful to
38
+ callers is a consequence.
39
+
40
+ ```js
41
+ import { toHtml } from '@tabnas/markdown'
42
+
43
+ toHtml('# Hello\n\nHello *world*') // => '<h1>Hello</h1>\n<p>Hello <em>world</em></p>\n'
44
+ ```
45
+
46
+ **The HTML is not sanitized.** Raw HTML blocks and inline tags pass through verbatim, as
47
+ CommonMark specifies. Put a sanitizer downstream of any untrusted Markdown.
48
+
49
+ ```js
50
+ import { toHtml } from '@tabnas/markdown'
51
+
52
+ toHtml('<img onerror="alert(1)">') // => '<img onerror="alert(1)">\n'
53
+ ```
54
+
55
+ GFM's disallowed-raw-HTML filter (on with `gfm`, the default) rewrites the leading `<` of
56
+ nine tag names — `title`, `textarea`, `style`, `xmp`, `iframe`, `noembed`, `noframes`,
57
+ `script`, `plaintext` — and touches nothing else:
58
+
59
+ ```js
60
+ import { toHtml } from '@tabnas/markdown'
61
+
62
+ toHtml('<script>alert(1)</script>') // => '&lt;script>alert(1)&lt;/script>\n'
63
+ ```
64
+
65
+ As a Tabnas plugin, `parse()` returns the same AST as `parseDocument()`:
66
+
67
+ ```js
68
+ import { Tabnas } from '@tabnas/parser'
69
+ import { Markdown } from '@tabnas/markdown'
70
+
71
+ const tn = new Tabnas().use(Markdown)
72
+
73
+ tn.parse('# Hello') // => { type: 'document', children: [ { type: 'heading', depth: 1, children: [ { type: 'text', value: 'Hello' } ] } ] }
74
+ ```
75
+
76
+ `parseTree()` returns the native CommonMark node tree instead — it keeps `sourcepos` on
77
+ block nodes, and can be walked or mutated and then rendered. See the
78
+ [reference](doc/reference.md).
79
+
80
+ GFM tables add three node types, in mdast's shape — `table`, `tableRow` and `tableCell`.
81
+ `align` has one entry per column, `null` where the delimiter cell had no colon; there is
82
+ no header flag, so the first row is the header row by convention, and every row has
83
+ exactly as many cells as `align` has entries.
84
+
85
+ ```js
86
+ import { parseDocument } from '@tabnas/markdown'
87
+
88
+ const doc = parseDocument('| a | b |\n| :- | -: |\n| 1 | 2 |')
89
+
90
+ doc.children[0].align // => [ 'left', 'right' ]
91
+ doc.children[0].children[0].children[0] // => { type: 'tableCell', children: [ { type: 'text', value: 'a' } ] }
92
+ ```
93
+
11
94
  ## Install
12
95
 
13
96
  ```bash
@@ -16,21 +99,59 @@ npm install @tabnas/markdown @tabnas/parser
16
99
 
17
100
  `@tabnas/parser` (>=0) is the only peer dependency. Requires Node >=24.
18
101
 
19
- ## Example
102
+ ## Specific to this runtime
103
+
104
+ The parser is **engine-free**: nothing under `src/commonmark.ts` imports `@tabnas/parser`.
105
+ Only `src/markdown.ts`, the plugin wiring, does. That is what lets the conformance runner
106
+ work with no build step and no engine installed — it stages `src/*.ts` in a temp directory
107
+ whose `package.json` says `"type": "module"` and runs them under Node's type stripping:
108
+
109
+ ```bash
110
+ npm run conformance # 652/652, all 26 sections
111
+ npm run conformance -- --failures
112
+ npm run conformance-gfm # 24/24, all five extensions
113
+ ```
114
+
115
+ The `// =>` assertions in this repo's Markdown are executed as tests
116
+ (`test/doc-examples.test.ts`, and `tools/check-doc-examples.mjs` for the engine-free
117
+ check), so a wrong expected value is a failing test.
118
+
119
+ Options are `gfm` (default `true`) and `breaks` (default `false`). `gfm` gates five
120
+ extensions together — tables, strikethrough, task list items, autolink literals (bare
121
+ `www.` / `https://` / `a@b.co`) and the disallowed-raw-HTML filter. Footnotes are not
122
+ implemented. With `gfm:false` the output is plain CommonMark.
123
+
124
+ Footnotes are a GitHub product feature rather than part of the GFM spec suite, and their
125
+ absence is quiet: `[^1]` is a valid CommonMark link label, so a footnote authored on
126
+ GitHub renders as a broken link instead of raising an error.
20
127
 
21
128
  ```js
22
- import { Tabnas } from '@tabnas/parser'
23
- import { Markdown } from '@tabnas/markdown'
129
+ import { toHtml } from '@tabnas/markdown'
24
130
 
25
- const j = new Tabnas().use(Markdown)
131
+ toHtml('Text[^1]\n\n[^1]: note') // => '<p>Text<a href="note">^1</a></p>\n'
132
+ ```
26
133
 
27
- j.parse('# Hello\n\nHello *world*') // => { type: 'document', children: [{ type: 'heading', depth: 1, children: [{ type: 'text', value: 'Hello' }] }, { type: 'paragraph', children: [{ type: 'text', value: 'Hello ' }, { type: 'emphasis', children: [{ type: 'text', value: 'world' }] }] }] }
28
- j.parse('# Hello') // => { type: 'document', children: [{ type: 'heading', depth: 1, children: [{ type: 'text', value: 'Hello' }] }] }
134
+ Nothing outside CommonMark and GFM is implemented — no math, front matter, definition
135
+ lists, heading attributes, admonitions, wiki links, emoji shortcodes, highlight or
136
+ sub/superscript. Each would need its own opt-in flag; `gfm` is not going to grow to mean
137
+ "everything". Note one collision: GFM's single-tilde strikethrough takes the syntax other
138
+ dialects use for subscript, so `H~2~O` is a deletion under the default `gfm:true`.
139
+
140
+ ```js
141
+ import { toHtml } from '@tabnas/markdown'
142
+
143
+ toHtml('H~2~O') // => '<p>H<del>2</del>O</p>\n'
29
144
  ```
30
145
 
31
146
  ## Documentation
32
147
 
33
- - [Tutorial](doc/tutorial.md) — first parse in 5 minutes.
148
+ - [Tutorial](doc/tutorial.md) — first parse, start to finish.
34
149
  - [How-to guide](doc/guide.md) — task recipes.
35
- - [Reference](doc/reference.md) — API + options + AST.
36
- - [Concepts](doc/concepts.md) — how it works.
150
+ - [Reference](doc/reference.md) — API, options, AST node types.
151
+ - [Concepts](doc/concepts.md) — how it works, and why.
152
+
153
+ Top-level [README](../README.md) · Go port: [go/README.md](../go/README.md).
154
+
155
+ ## License
156
+
157
+ MIT. Copyright (c) Richard Rodger and other contributors.
package/dist/ast.d.ts ADDED
@@ -0,0 +1,113 @@
1
+ import { MdNode } from './node.ts';
2
+ import type { TableAlign } from './node.ts';
3
+ import type { ParserOptions } from './options.ts';
4
+ export type DocumentNode = {
5
+ type: 'document';
6
+ children: Block[];
7
+ };
8
+ export type Block = HeadingNode | ParagraphNode | BlockquoteNode | ListNode | ListItemNode | CodeNode | HtmlNode | ThematicBreakNode | TableNode;
9
+ export type HeadingNode = {
10
+ type: 'heading';
11
+ depth: 1 | 2 | 3 | 4 | 5 | 6;
12
+ children: Inline[];
13
+ };
14
+ export type ParagraphNode = {
15
+ type: 'paragraph';
16
+ children: Inline[];
17
+ };
18
+ export type BlockquoteNode = {
19
+ type: 'blockquote';
20
+ children: Block[];
21
+ };
22
+ export type ListNode = {
23
+ type: 'list';
24
+ ordered: boolean;
25
+ start: number | null;
26
+ spread: boolean;
27
+ children: ListItemNode[];
28
+ };
29
+ export type ListItemNode = {
30
+ type: 'listItem';
31
+ spread: boolean;
32
+ /**
33
+ * mdast's task list field: `true`/`false` for a GFM task list item
34
+ * (`- [x] foo`), `null` for an ordinary item — and therefore `null`
35
+ * everywhere when `gfm` is off.
36
+ */
37
+ checked: boolean | null;
38
+ children: Block[];
39
+ };
40
+ export type CodeNode = {
41
+ type: 'code';
42
+ lang: string | null;
43
+ meta: string | null;
44
+ value: string;
45
+ };
46
+ export type HtmlNode = {
47
+ type: 'html';
48
+ value: string;
49
+ };
50
+ export type ThematicBreakNode = {
51
+ type: 'thematicBreak';
52
+ };
53
+ /**
54
+ * A GFM table, in mdast's shape.
55
+ *
56
+ * `align` has one entry per column — `null` where the delimiter row gave no
57
+ * colon — and every row has exactly that many cells, since the block phase
58
+ * pads short rows and truncates long ones.
59
+ *
60
+ * mdast has no header flag: the **first** row is the header row, by
61
+ * convention, and this projection preserves that ordering from the tree.
62
+ */
63
+ export type TableNode = {
64
+ type: 'table';
65
+ align: TableAlign[];
66
+ children: TableRowNode[];
67
+ };
68
+ export type TableRowNode = {
69
+ type: 'tableRow';
70
+ children: TableCellNode[];
71
+ };
72
+ export type TableCellNode = {
73
+ type: 'tableCell';
74
+ children: Inline[];
75
+ };
76
+ export type Inline = TextNode | EmphasisNode | StrongNode | InlineCodeNode | LinkNode | ImageNode | BreakNode | DeleteNode | HtmlNode;
77
+ export type TextNode = {
78
+ type: 'text';
79
+ value: string;
80
+ };
81
+ export type EmphasisNode = {
82
+ type: 'emphasis';
83
+ children: Inline[];
84
+ };
85
+ export type StrongNode = {
86
+ type: 'strong';
87
+ children: Inline[];
88
+ };
89
+ export type InlineCodeNode = {
90
+ type: 'inlineCode';
91
+ value: string;
92
+ };
93
+ export type LinkNode = {
94
+ type: 'link';
95
+ url: string;
96
+ title: string | null;
97
+ children: Inline[];
98
+ };
99
+ export type ImageNode = {
100
+ type: 'image';
101
+ url: string;
102
+ title: string | null;
103
+ alt: string;
104
+ };
105
+ export type BreakNode = {
106
+ type: 'break';
107
+ };
108
+ export type DeleteNode = {
109
+ type: 'delete';
110
+ children: Inline[];
111
+ };
112
+ /** Project a parsed native tree into the public JSON AST. */
113
+ export declare function toAst(doc: MdNode, opts: ParserOptions): DocumentNode;
package/dist/ast.js ADDED
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ /* Copyright (c) 2021-2026 Richard Rodger, MIT License */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.toAst = toAst;
5
+ /** Plain-text flattening of an image's inline children, for `alt`. */
6
+ function collectAltText(node) {
7
+ let out = '';
8
+ let child = node.firstChild;
9
+ while (child) {
10
+ if ('text' === child.type || 'code' === child.type || 'html_inline' === child.type) {
11
+ out += child.literal ?? '';
12
+ }
13
+ else if ('softbreak' === child.type || 'linebreak' === child.type) {
14
+ out += '\n';
15
+ }
16
+ else {
17
+ out += collectAltText(child);
18
+ }
19
+ child = child.next;
20
+ }
21
+ return out;
22
+ }
23
+ /**
24
+ * mdast semantics: an item is spread when two of its children are separated by
25
+ * a *blank line* — not merely when it has more than one child. `- a\n - b`
26
+ * holds a paragraph and a nested list with no blank line between them, and is
27
+ * not spread; `- a\n\n - b` is.
28
+ *
29
+ * Derived from the block parser's `sourcepos` rather than tracked separately:
30
+ * consecutive children whose line ranges are not adjacent must have had a
31
+ * blank line between them.
32
+ */
33
+ function itemIsSpread(item) {
34
+ let child = item.firstChild;
35
+ while (child && child.next) {
36
+ const endLine = child.sourcepos[1][0];
37
+ const nextStartLine = child.next.sourcepos[0][0];
38
+ if (nextStartLine > endLine + 1)
39
+ return true;
40
+ child = child.next;
41
+ }
42
+ return false;
43
+ }
44
+ function inlineChildren(node, opts) {
45
+ const out = [];
46
+ const pushText = (value) => {
47
+ if ('' === value)
48
+ return;
49
+ const last = out[out.length - 1];
50
+ if (last && 'text' === last.type)
51
+ last.value += value;
52
+ else
53
+ out.push({ type: 'text', value });
54
+ };
55
+ let child = node.firstChild;
56
+ while (child) {
57
+ switch (child.type) {
58
+ case 'text':
59
+ pushText(child.literal ?? '');
60
+ break;
61
+ case 'softbreak':
62
+ // Documented behaviour: a soft break reads as a space in the AST.
63
+ if (opts.breaks)
64
+ out.push({ type: 'break' });
65
+ else
66
+ pushText(' ');
67
+ break;
68
+ case 'linebreak':
69
+ out.push({ type: 'break' });
70
+ break;
71
+ case 'code':
72
+ out.push({ type: 'inlineCode', value: child.literal ?? '' });
73
+ break;
74
+ case 'html_inline':
75
+ out.push({ type: 'html', value: child.literal ?? '' });
76
+ break;
77
+ case 'emph':
78
+ out.push({ type: 'emphasis', children: inlineChildren(child, opts) });
79
+ break;
80
+ case 'strong':
81
+ out.push({ type: 'strong', children: inlineChildren(child, opts) });
82
+ break;
83
+ case 'del':
84
+ out.push({ type: 'delete', children: inlineChildren(child, opts) });
85
+ break;
86
+ case 'link':
87
+ out.push({
88
+ type: 'link',
89
+ url: child.destination ?? '',
90
+ title: child.title ?? null,
91
+ children: inlineChildren(child, opts),
92
+ });
93
+ break;
94
+ case 'image':
95
+ out.push({
96
+ type: 'image',
97
+ url: child.destination ?? '',
98
+ title: child.title ?? null,
99
+ alt: collectAltText(child),
100
+ });
101
+ break;
102
+ default:
103
+ // A block node cannot appear here in a well-formed tree; skipping is
104
+ // safer than emitting a node the published types do not allow.
105
+ break;
106
+ }
107
+ child = child.next;
108
+ }
109
+ return out;
110
+ }
111
+ /** Containers whose children are blocks rather than inlines. */
112
+ const BLOCK_CONTAINERS = {
113
+ document: true,
114
+ block_quote: true,
115
+ list: true,
116
+ item: true,
117
+ };
118
+ /**
119
+ * Project a container's block children.
120
+ *
121
+ * Iterative rather than recursive, because the recursion depth here is the
122
+ * document's container nesting depth, which untrusted input controls directly:
123
+ * `'> '.repeat(4000)` is 8 KB of Markdown that the block parser and the HTML
124
+ * renderer both handle, but that overflowed the stack in this projection —
125
+ * crashing the package's primary API on input the rest of the pipeline
126
+ * survives.
127
+ *
128
+ * Two passes. The first walks the block tree with an explicit stack, recording
129
+ * nodes in an order where every parent precedes its descendants. Building in
130
+ * reverse then guarantees a node's children are already built when it is
131
+ * reached, so `buildBlock` can look them up instead of recursing.
132
+ *
133
+ * The Go port keeps the recursive form: goroutine stacks grow on demand and it
134
+ * projects 100k-deep nesting without trouble, so the same defect does not
135
+ * arise there.
136
+ */
137
+ function blockChildren(node, opts) {
138
+ const order = [];
139
+ const stack = [];
140
+ for (let c = node.lastChild; c; c = c.prev)
141
+ stack.push(c);
142
+ while (0 < stack.length) {
143
+ const n = stack.pop();
144
+ order.push(n);
145
+ if (true === BLOCK_CONTAINERS[n.type]) {
146
+ for (let c = n.lastChild; c; c = c.prev)
147
+ stack.push(c);
148
+ }
149
+ }
150
+ const built = new Map();
151
+ for (let i = order.length - 1; 0 <= i; i--) {
152
+ const n = order[i];
153
+ const block = buildBlock(n, opts, built);
154
+ if (null !== block)
155
+ built.set(n, block);
156
+ }
157
+ const out = [];
158
+ for (let c = node.firstChild; c; c = c.next) {
159
+ const b = built.get(c);
160
+ if (undefined !== b)
161
+ out.push(b);
162
+ }
163
+ return out;
164
+ }
165
+ /** Children of `n` that have already been built, in source order. */
166
+ function builtChildren(n, built) {
167
+ const out = [];
168
+ for (let c = n.firstChild; c; c = c.next) {
169
+ const b = built.get(c);
170
+ if (undefined !== b)
171
+ out.push(b);
172
+ }
173
+ return out;
174
+ }
175
+ function buildBlock(node, opts, built) {
176
+ switch (node.type) {
177
+ case 'paragraph':
178
+ return { type: 'paragraph', children: inlineChildren(node, opts) };
179
+ case 'heading':
180
+ return {
181
+ type: 'heading',
182
+ depth: node.level,
183
+ children: inlineChildren(node, opts),
184
+ };
185
+ case 'thematic_break':
186
+ return { type: 'thematicBreak' };
187
+ case 'block_quote':
188
+ return { type: 'blockquote', children: builtChildren(node, built) };
189
+ case 'code_block': {
190
+ const info = (node.info ?? '').trim();
191
+ let lang = null;
192
+ let meta = null;
193
+ if ('' !== info) {
194
+ const sp = info.search(/\s/);
195
+ if (-1 === sp) {
196
+ lang = info;
197
+ }
198
+ else {
199
+ lang = info.slice(0, sp);
200
+ meta = info.slice(sp + 1).trim() || null;
201
+ }
202
+ }
203
+ // The native tree keeps the trailing newline because the spec's HTML
204
+ // for `<pre><code>` includes it; mdast's `code.value` does not. Strip
205
+ // exactly one, not all, so a block that genuinely ends in a blank line
206
+ // keeps it.
207
+ const raw = node.literal ?? '';
208
+ const value = raw.endsWith('\n') ? raw.slice(0, -1) : raw;
209
+ return { type: 'code', lang, meta, value };
210
+ }
211
+ case 'html_block':
212
+ return { type: 'html', value: node.literal ?? '' };
213
+ case 'table': {
214
+ // Built here rather than through `built`, because a table's children are
215
+ // rows and cells rather than blocks — `blockChildren` never descends
216
+ // into one. The nesting is a fixed three levels deep, so unlike the
217
+ // block containers this cannot be driven past the stack by input.
218
+ const rows = [];
219
+ for (let row = node.firstChild; row; row = row.next) {
220
+ if ('table_row' !== row.type)
221
+ continue;
222
+ const cells = [];
223
+ for (let cell = row.firstChild; cell; cell = cell.next) {
224
+ if ('table_cell' !== cell.type)
225
+ continue;
226
+ cells.push({ type: 'tableCell', children: inlineChildren(cell, opts) });
227
+ }
228
+ rows.push({ type: 'tableRow', children: cells });
229
+ }
230
+ return { type: 'table', align: node.tableAlign ?? [], children: rows };
231
+ }
232
+ case 'list': {
233
+ const data = node.listData;
234
+ const ordered = 'ordered' === data?.type;
235
+ const items = [];
236
+ let child = node.firstChild;
237
+ while (child) {
238
+ if ('item' === child.type) {
239
+ items.push({
240
+ type: 'listItem',
241
+ spread: itemIsSpread(child),
242
+ checked: child.checked,
243
+ children: builtChildren(child, built),
244
+ });
245
+ }
246
+ child = child.next;
247
+ }
248
+ return {
249
+ type: 'list',
250
+ ordered,
251
+ start: ordered ? (data?.start ?? 1) : null,
252
+ spread: !(data?.tight ?? true),
253
+ children: items,
254
+ };
255
+ }
256
+ case 'item':
257
+ // Only reachable if an item is somehow detached from its list; a list
258
+ // builds its own items above.
259
+ return {
260
+ type: 'listItem',
261
+ spread: itemIsSpread(node),
262
+ checked: node.checked,
263
+ children: builtChildren(node, built),
264
+ };
265
+ default:
266
+ return null;
267
+ }
268
+ }
269
+ /** Project a parsed native tree into the public JSON AST. */
270
+ function toAst(doc, opts) {
271
+ return { type: 'document', children: blockChildren(doc, opts) };
272
+ }
273
+ //# sourceMappingURL=ast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.js","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { MdNode } from './node.ts';
2
+ import type { ParserOptions, RefMap } from './options.ts';
3
+ /**
4
+ * Phase 1: build the block tree and collect link reference definitions.
5
+ * Paragraph and heading text is left raw in `stringContent` for phase 2.
6
+ */
7
+ export declare function parseBlocks(input: string, options: ParserOptions): {
8
+ doc: MdNode;
9
+ refmap: RefMap;
10
+ };