@tabnas/markdown 0.4.2 → 0.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 CHANGED
@@ -1,6 +1,8 @@
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, scoring
4
+ **652/652 on the CommonMark 0.31.2 spec suite**, with one GFM extension (strikethrough).
5
+ This is the canonical implementation; [`go/`](../go/README.md) is a port of it.
4
6
 
5
7
  [![npm version](https://img.shields.io/npm/v/@tabnas/markdown.svg)](https://npmjs.com/package/@tabnas/markdown)
6
8
  [![build](https://github.com/tabnas/markdown/actions/workflows/build.yml/badge.svg)](https://github.com/tabnas/markdown/actions/workflows/build.yml)
@@ -8,29 +10,93 @@ A [Tabnas](https://github.com/tabnas/parser) grammar plugin that parses **Common
8
10
  | ![Voxgig](https://www.voxgig.com/res/img/vgt01r.png) | This open source module is sponsored and supported by [Voxgig](https://www.voxgig.com). |
9
11
  | ---------------------------------------------------- | --------------------------------------------------------------------------------------- |
10
12
 
11
- ## Install
13
+ ## Two outputs
12
14
 
13
- ```bash
14
- npm install @tabnas/markdown @tabnas/parser
15
+ **The AST is the primary output.** `parseDocument()` returns it directly; the renderer
16
+ does not run.
17
+
18
+ ```js
19
+ import { parseDocument } from '@tabnas/markdown'
20
+
21
+ const doc = parseDocument('# Hello\n\nHello *world*')
22
+
23
+ doc.children[0] // => { type: 'heading', depth: 1, children: [ { type: 'text', value: 'Hello' } ] }
24
+ doc.children[1] // => { type: 'paragraph', children: [ { type: 'text', value: 'Hello ' }, { type: 'emphasis', children: [ { type: 'text', value: 'world' } ] } ] }
15
25
  ```
16
26
 
17
- `@tabnas/parser` (>=0) is the only peer dependency. Requires Node >=24.
27
+ **HTML is available, on request, via `toHtml()`.** The CommonMark suite scores HTML
28
+ output, so the renderer is what makes the 652/652 claim measurable; that it is useful to
29
+ callers is a consequence.
30
+
31
+ ```js
32
+ import { toHtml } from '@tabnas/markdown'
33
+
34
+ toHtml('# Hello\n\nHello *world*') // => '<h1>Hello</h1>\n<p>Hello <em>world</em></p>\n'
35
+ ```
18
36
 
19
- ## Example
37
+ **The HTML is not sanitized.** Raw HTML blocks and inline tags pass through verbatim, as
38
+ CommonMark specifies, and GFM's disallowed-raw-HTML filter is not implemented. Put a
39
+ sanitizer downstream of any untrusted Markdown.
40
+
41
+ ```js
42
+ import { toHtml } from '@tabnas/markdown'
43
+
44
+ toHtml('<script>alert(1)</script>') // => '<script>alert(1)</script>\n'
45
+ ```
46
+
47
+ As a Tabnas plugin, `parse()` returns the same AST as `parseDocument()`:
20
48
 
21
49
  ```js
22
50
  import { Tabnas } from '@tabnas/parser'
23
51
  import { Markdown } from '@tabnas/markdown'
24
52
 
25
- const j = new Tabnas().use(Markdown)
53
+ const tn = new Tabnas().use(Markdown)
54
+
55
+ tn.parse('# Hello') // => { type: 'document', children: [ { type: 'heading', depth: 1, children: [ { type: 'text', value: 'Hello' } ] } ] }
56
+ ```
57
+
58
+ `parseTree()` returns the native CommonMark node tree instead — it keeps `sourcepos` on
59
+ block nodes, and can be walked or mutated and then rendered. See the
60
+ [reference](doc/reference.md).
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ npm install @tabnas/markdown @tabnas/parser
66
+ ```
67
+
68
+ `@tabnas/parser` (>=0) is the only peer dependency. Requires Node >=24.
69
+
70
+ ## Specific to this runtime
71
+
72
+ The parser is **engine-free**: nothing under `src/commonmark.ts` imports `@tabnas/parser`.
73
+ Only `src/markdown.ts`, the plugin wiring, does. That is what lets the conformance runner
74
+ work with no build step and no engine installed — it stages `src/*.ts` in a temp directory
75
+ whose `package.json` says `"type": "module"` and runs them under Node's type stripping:
26
76
 
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' }] }] }
77
+ ```bash
78
+ npm run conformance # 652/652
79
+ npm run conformance -- --failures
29
80
  ```
30
81
 
82
+ The `// =>` assertions in this repo's Markdown are executed as tests
83
+ (`test/doc-examples.test.ts`, and `tools/check-doc-examples.mjs` for the engine-free
84
+ check), so a wrong expected value is a failing test.
85
+
86
+ Options are `gfm` (default `true`) and `breaks` (default `false`). `gfm` gates
87
+ strikethrough and nothing else: tables, task list items, autolink literals (bare `www.` /
88
+ `https://` without angle brackets), footnotes and disallowed-raw-HTML filtering are not
89
+ implemented.
90
+
31
91
  ## Documentation
32
92
 
33
- - [Tutorial](doc/tutorial.md) — first parse in 5 minutes.
93
+ - [Tutorial](doc/tutorial.md) — first parse, start to finish.
34
94
  - [How-to guide](doc/guide.md) — task recipes.
35
- - [Reference](doc/reference.md) — API + options + AST.
36
- - [Concepts](doc/concepts.md) — how it works.
95
+ - [Reference](doc/reference.md) — API, options, AST node types.
96
+ - [Concepts](doc/concepts.md) — how it works, and why.
97
+
98
+ Top-level [README](../README.md) · Go port: [go/README.md](../go/README.md).
99
+
100
+ ## License
101
+
102
+ MIT. Copyright (c) Richard Rodger and other contributors.
package/dist/ast.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { MdNode } from './node.ts';
2
+ import type { ParserOptions } from './options.ts';
3
+ export type DocumentNode = {
4
+ type: 'document';
5
+ children: Block[];
6
+ };
7
+ export type Block = HeadingNode | ParagraphNode | BlockquoteNode | ListNode | ListItemNode | CodeNode | HtmlNode | ThematicBreakNode;
8
+ export type HeadingNode = {
9
+ type: 'heading';
10
+ depth: 1 | 2 | 3 | 4 | 5 | 6;
11
+ children: Inline[];
12
+ };
13
+ export type ParagraphNode = {
14
+ type: 'paragraph';
15
+ children: Inline[];
16
+ };
17
+ export type BlockquoteNode = {
18
+ type: 'blockquote';
19
+ children: Block[];
20
+ };
21
+ export type ListNode = {
22
+ type: 'list';
23
+ ordered: boolean;
24
+ start: number | null;
25
+ spread: boolean;
26
+ children: ListItemNode[];
27
+ };
28
+ export type ListItemNode = {
29
+ type: 'listItem';
30
+ spread: boolean;
31
+ children: Block[];
32
+ };
33
+ export type CodeNode = {
34
+ type: 'code';
35
+ lang: string | null;
36
+ meta: string | null;
37
+ value: string;
38
+ };
39
+ export type HtmlNode = {
40
+ type: 'html';
41
+ value: string;
42
+ };
43
+ export type ThematicBreakNode = {
44
+ type: 'thematicBreak';
45
+ };
46
+ export type Inline = TextNode | EmphasisNode | StrongNode | InlineCodeNode | LinkNode | ImageNode | BreakNode | DeleteNode | HtmlNode;
47
+ export type TextNode = {
48
+ type: 'text';
49
+ value: string;
50
+ };
51
+ export type EmphasisNode = {
52
+ type: 'emphasis';
53
+ children: Inline[];
54
+ };
55
+ export type StrongNode = {
56
+ type: 'strong';
57
+ children: Inline[];
58
+ };
59
+ export type InlineCodeNode = {
60
+ type: 'inlineCode';
61
+ value: string;
62
+ };
63
+ export type LinkNode = {
64
+ type: 'link';
65
+ url: string;
66
+ title: string | null;
67
+ children: Inline[];
68
+ };
69
+ export type ImageNode = {
70
+ type: 'image';
71
+ url: string;
72
+ title: string | null;
73
+ alt: string;
74
+ };
75
+ export type BreakNode = {
76
+ type: 'break';
77
+ };
78
+ export type DeleteNode = {
79
+ type: 'delete';
80
+ children: Inline[];
81
+ };
82
+ /** Project a parsed native tree into the public JSON AST. */
83
+ export declare function toAst(doc: MdNode, opts: ParserOptions): DocumentNode;
package/dist/ast.js ADDED
@@ -0,0 +1,248 @@
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 'list': {
214
+ const data = node.listData;
215
+ const ordered = 'ordered' === data?.type;
216
+ const items = [];
217
+ let child = node.firstChild;
218
+ while (child) {
219
+ if ('item' === child.type) {
220
+ items.push({
221
+ type: 'listItem',
222
+ spread: itemIsSpread(child),
223
+ children: builtChildren(child, built),
224
+ });
225
+ }
226
+ child = child.next;
227
+ }
228
+ return {
229
+ type: 'list',
230
+ ordered,
231
+ start: ordered ? (data?.start ?? 1) : null,
232
+ spread: !(data?.tight ?? true),
233
+ children: items,
234
+ };
235
+ }
236
+ case 'item':
237
+ // Only reachable if an item is somehow detached from its list; a list
238
+ // builds its own items above.
239
+ return { type: 'listItem', spread: itemIsSpread(node), children: builtChildren(node, built) };
240
+ default:
241
+ return null;
242
+ }
243
+ }
244
+ /** Project a parsed native tree into the public JSON AST. */
245
+ function toAst(doc, opts) {
246
+ return { type: 'document', children: blockChildren(doc, opts) };
247
+ }
248
+ //# 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
+ };