ironmark 1.13.0 → 2.0.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 +305 -126
- package/package.json +1 -1
- package/wasm/cli.js +55 -16
- package/wasm/index.d.ts +257 -71
- package/wasm/node.js +18 -9
- package/wasm/pkg/ironmark_bg.wasm +0 -0
- package/wasm/pkg/ironmark_bg.wasm.d.ts +1 -1
- package/wasm/shared.js +201 -80
- package/wasm/web.js +17 -8
package/README.md
CHANGED
|
@@ -6,159 +6,144 @@ Fast Markdown parser written in Rust with **zero third-party** parsing dependenc
|
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
9
|
-
- [
|
|
10
|
-
|
|
11
|
-
- [Security](#security)
|
|
12
|
-
- [Other Options](#other-options)
|
|
9
|
+
- [Quick start](#quick-start)
|
|
10
|
+
- [Which function should I use?](#which-function-should-i-use)
|
|
13
11
|
- [JavaScript / TypeScript](#javascript--typescript)
|
|
14
12
|
- [Node.js](#nodejs)
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [AST
|
|
13
|
+
- [Markdown → HTML](#markdown--html)
|
|
14
|
+
- [Markdown → AST](#markdown--ast)
|
|
15
|
+
- [Markdown → AST → Markdown](#markdown--ast--markdown)
|
|
16
|
+
- [Safe HTML rendering](#safe-html-rendering)
|
|
18
17
|
- [ANSI Terminal Output](#ansi-terminal-output)
|
|
18
|
+
- [HTML to Markdown](#html-to-markdown)
|
|
19
19
|
- [Browser / Bundler](#browser--bundler)
|
|
20
|
+
- [Using ironmark in AI pipelines](#using-ironmark-in-ai-pipelines)
|
|
21
|
+
- [Presets](#presets)
|
|
22
|
+
- [Introspection helpers](#introspection-helpers)
|
|
23
|
+
- [Utility functions](#utility-functions)
|
|
24
|
+
- [Configuration](#configuration)
|
|
25
|
+
- [Extensions](#extensions)
|
|
26
|
+
- [Security](#security)
|
|
27
|
+
- [Other Options](#other-options)
|
|
20
28
|
- [CLI](#cli)
|
|
21
29
|
- [Rust](#rust)
|
|
22
30
|
- [C / C++](#c--c++)
|
|
23
31
|
- [Benchmarks](#benchmarks)
|
|
24
32
|
- [Development](#development)
|
|
25
33
|
|
|
26
|
-
##
|
|
34
|
+
## Quick start
|
|
27
35
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
| Highlight | `enableHighlight` | `enable_highlight` | `==text==` → `<mark>` |
|
|
34
|
-
| Strikethrough | `enableStrikethrough` | `enable_strikethrough` | `~~text~~` → `<del>` |
|
|
35
|
-
| Underline | `enableUnderline` | `enable_underline` | `++text++` → `<u>` |
|
|
36
|
-
| Tables | `enableTables` | `enable_tables` | Pipe table syntax |
|
|
37
|
-
| Autolink | `enableAutolink` | `enable_autolink` | Bare URLs & emails → `<a>` |
|
|
38
|
-
| Task lists | `enableTaskLists` | `enable_task_lists` | `- [ ]` / `- [x]` checkboxes |
|
|
39
|
-
| Indented code | `enableIndentedCodeBlocks` | `enable_indented_code_blocks` | 4-space indent → `<pre><code>` |
|
|
40
|
-
| Wiki links | `enableWikiLinks` | `enable_wiki_links` | `[[page]]` → `<a href="page">` |
|
|
41
|
-
| LaTeX math | `enableLateXMath` | `enable_latex_math` | `$inline$` and `$$display$$` → `<span class="math-…">` |
|
|
42
|
-
| Heading IDs | `enableHeadingIds` | `enable_heading_ids` | Auto `id=` on headings from slugified text |
|
|
43
|
-
| Heading anchors | `enableHeadingAnchors` | `enable_heading_anchors` | `<a class="anchor">` inside each heading (implies IDs) |
|
|
44
|
-
| Permissive headings | `permissiveAtxHeaders` | `permissive_atx_headers` | Allow `#Heading` without space after `#` |
|
|
45
|
-
|
|
46
|
-
### Security
|
|
36
|
+
```bash
|
|
37
|
+
npm install ironmark
|
|
38
|
+
# or
|
|
39
|
+
pnpm add ironmark
|
|
40
|
+
```
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
| Disable raw HTML | `disableRawHtml` | `disable_raw_html` | `false` | Escape **all** HTML blocks and inline HTML |
|
|
51
|
-
| No HTML blocks | `noHtmlBlocks` | `no_html_blocks` | `false` | Escape block-level HTML only (more granular than `disableRawHtml`) |
|
|
52
|
-
| No HTML spans | `noHtmlSpans` | `no_html_spans` | `false` | Escape inline HTML only |
|
|
53
|
-
| Tag filter | `tagFilter` | `tag_filter` | `false` | GFM tag filter: escape `<script>`, `<iframe>`, etc. |
|
|
54
|
-
| Max nesting | — | `max_nesting_depth` | `128` | Limit blockquote/list nesting depth (DoS prevention) |
|
|
55
|
-
| Max input size | — | `max_input_size` | `0` (no limit) | Truncate input beyond this byte count |
|
|
42
|
+
```ts
|
|
43
|
+
import { renderHtml, parseMarkdown } from "ironmark";
|
|
56
44
|
|
|
57
|
-
|
|
45
|
+
// Markdown → HTML
|
|
46
|
+
const html = renderHtml("# Hello\n\n**World**");
|
|
58
47
|
|
|
59
|
-
|
|
48
|
+
// Markdown → AST (parsed JavaScript object)
|
|
49
|
+
const ast = parseMarkdown("# Hello\n\n**World**");
|
|
50
|
+
```
|
|
60
51
|
|
|
61
|
-
|
|
52
|
+
## Which function should I use?
|
|
62
53
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
54
|
+
| Goal | Function |
|
|
55
|
+
| ------------------------------- | --------------------------------------------------- |
|
|
56
|
+
| Render Markdown to HTML | `renderHtml(input, options?)` |
|
|
57
|
+
| Parse Markdown to AST object | `parseMarkdown(input, options?)` |
|
|
58
|
+
| Normalize / round-trip Markdown | `parseMarkdown()` + `renderMarkdown()` |
|
|
59
|
+
| Terminal / CLI output | `renderAnsiTerminal(input, options?, ansiOptions?)` |
|
|
60
|
+
| Convert HTML back to Markdown | `htmlToMarkdown(html)` / `parseHtmlToAst(html)` |
|
|
66
61
|
|
|
67
62
|
## JavaScript / TypeScript
|
|
68
63
|
|
|
69
|
-
```bash
|
|
70
|
-
npm install ironmark
|
|
71
|
-
# or
|
|
72
|
-
pnpm add ironmark
|
|
73
|
-
```
|
|
74
|
-
|
|
75
64
|
### Node.js
|
|
76
65
|
|
|
77
66
|
WASM is embedded and loaded synchronously — no `init()` needed:
|
|
78
67
|
|
|
79
68
|
```ts
|
|
80
|
-
import {
|
|
69
|
+
import { renderHtml, parseMarkdown } from "ironmark";
|
|
81
70
|
|
|
82
|
-
const html =
|
|
83
|
-
|
|
84
|
-
// safe mode for untrusted input
|
|
85
|
-
const safe = parse(userInput, { disableRawHtml: true });
|
|
71
|
+
const html = renderHtml("# Hello\n\nThis is **fast**.");
|
|
72
|
+
const ast = parseMarkdown("# Hello");
|
|
86
73
|
```
|
|
87
74
|
|
|
88
|
-
###
|
|
89
|
-
|
|
90
|
-
Use `parseToAst()` when you need the block-level document structure instead of rendered HTML.
|
|
75
|
+
### Markdown → HTML
|
|
91
76
|
|
|
92
77
|
```ts
|
|
93
|
-
import {
|
|
78
|
+
import { renderHtml } from "ironmark";
|
|
94
79
|
|
|
95
|
-
const
|
|
96
|
-
const ast = JSON.parse(astJson);
|
|
80
|
+
const html = renderHtml("# Hello\n\nThis is **fast**.");
|
|
97
81
|
```
|
|
98
82
|
|
|
99
|
-
|
|
83
|
+
### Markdown → AST
|
|
100
84
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Convert HTML back to Markdown syntax using `htmlToMarkdown()`. Useful for importing content from HTML sources or round-trip conversion.
|
|
85
|
+
`parseMarkdown()` returns a parsed JavaScript array — no `JSON.parse()` needed.
|
|
104
86
|
|
|
105
87
|
```ts
|
|
106
|
-
import {
|
|
88
|
+
import { parseMarkdown } from "ironmark";
|
|
107
89
|
|
|
108
|
-
const
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
// Preserve unknown HTML tags (e.g., <sup>, <sub>) as raw HTML in output
|
|
112
|
-
const md = htmlToMarkdown("<p>H<sub>2</sub>O</p>", true);
|
|
113
|
-
// Returns: "H<sub>2</sub>O"
|
|
90
|
+
const ast = parseMarkdown("# Hello\n\n- [x] done");
|
|
91
|
+
// ast is a JavaScript array of block nodes
|
|
92
|
+
console.log(ast[0]); // { t: "Heading", level: 1, ... }
|
|
114
93
|
```
|
|
115
94
|
|
|
116
|
-
|
|
95
|
+
### Markdown → AST → Markdown
|
|
117
96
|
|
|
118
97
|
```ts
|
|
119
|
-
import {
|
|
98
|
+
import { parseMarkdown, renderMarkdown } from "ironmark";
|
|
120
99
|
|
|
121
|
-
const
|
|
122
|
-
const
|
|
100
|
+
const ast = parseMarkdown("**Hello**");
|
|
101
|
+
const md = renderMarkdown(ast);
|
|
102
|
+
// Returns: "**Hello**"
|
|
123
103
|
```
|
|
124
104
|
|
|
125
|
-
###
|
|
105
|
+
### Safe HTML rendering
|
|
126
106
|
|
|
127
|
-
|
|
107
|
+
Use `safe: true` for **any untrusted input** — it disables raw HTML passthrough and enables the GFM tag filter.
|
|
128
108
|
|
|
129
109
|
```ts
|
|
130
|
-
import {
|
|
110
|
+
import { renderHtml } from "ironmark";
|
|
131
111
|
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
112
|
+
// Simple safe flag
|
|
113
|
+
const html = renderHtml(userInput, { safe: true });
|
|
114
|
+
|
|
115
|
+
// Or use the built-in preset
|
|
116
|
+
const html = renderHtml(userInput, { preset: "safe" });
|
|
135
117
|
```
|
|
136
118
|
|
|
119
|
+
What `safe: true` does:
|
|
120
|
+
|
|
121
|
+
- Sets `disableRawHtml: true` — all inline and block HTML is escaped
|
|
122
|
+
- Sets `tagFilter: true` — dangerous tags (`<script>`, `<iframe>`, etc.) are escaped
|
|
123
|
+
|
|
124
|
+
Dangerous URI schemes (`javascript:`, `vbscript:`, `data:` except `data:image/…`) are **always** stripped from link destinations regardless of options.
|
|
125
|
+
|
|
137
126
|
### ANSI Terminal Output
|
|
138
127
|
|
|
139
|
-
Use `
|
|
128
|
+
Use `renderAnsiTerminal()` to render Markdown as coloured terminal output (ANSI 256-colour escape codes).
|
|
140
129
|
|
|
141
130
|
````ts
|
|
142
|
-
import {
|
|
131
|
+
import { renderAnsiTerminal } from "ironmark";
|
|
143
132
|
|
|
144
|
-
//
|
|
145
|
-
const ansi =
|
|
133
|
+
// Defaults: width 80, color enabled
|
|
134
|
+
const ansi = renderAnsiTerminal("# Hello\n\n**bold** and `code`");
|
|
146
135
|
process.stdout.write(ansi);
|
|
147
136
|
|
|
148
|
-
// Custom
|
|
149
|
-
const ansi =
|
|
137
|
+
// Custom options
|
|
138
|
+
const ansi = renderAnsiTerminal(
|
|
150
139
|
"# Hello\n\n```rust\nfn main() {}\n```",
|
|
151
|
-
{},
|
|
140
|
+
{},
|
|
152
141
|
{ width: 120, lineNumbers: true },
|
|
153
142
|
);
|
|
154
143
|
process.stdout.write(ansi);
|
|
155
144
|
|
|
156
|
-
//
|
|
157
|
-
const
|
|
158
|
-
process.stdout.write(ansi);
|
|
159
|
-
|
|
160
|
-
// Plain text — strips all ANSI codes (useful for piping to files)
|
|
161
|
-
const plain = renderAnsi("# Hello\n\n> quote", {}, { color: false });
|
|
145
|
+
// Plain text — no ANSI codes (useful for piping to files)
|
|
146
|
+
const plain = renderAnsiTerminal("# Hello", {}, { color: false });
|
|
162
147
|
````
|
|
163
148
|
|
|
164
149
|
#### ANSI options
|
|
@@ -170,29 +155,207 @@ const plain = renderAnsi("# Hello\n\n> quote", {}, { color: false });
|
|
|
170
155
|
| `lineNumbers` | `boolean` | `false` | Show line numbers in fenced code blocks. |
|
|
171
156
|
| `padding` | `number` | `0` | Horizontal padding added to both sides of each line, plus ⌈padding/2⌉ blank lines at the top. |
|
|
172
157
|
|
|
158
|
+
### HTML to Markdown
|
|
159
|
+
|
|
160
|
+
Convert HTML back to Markdown syntax using `htmlToMarkdown()`.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { htmlToMarkdown } from "ironmark";
|
|
164
|
+
|
|
165
|
+
const md = htmlToMarkdown("<h1>Hello</h1><p><strong>Bold</strong> text</p>");
|
|
166
|
+
// Returns: "# Hello\n\n**Bold** text"
|
|
167
|
+
|
|
168
|
+
// Preserve unknown HTML tags (e.g. <sup>, <sub>) as raw HTML in output
|
|
169
|
+
const md = htmlToMarkdown("<p>H<sub>2</sub>O</p>", true);
|
|
170
|
+
// Returns: "H<sub>2</sub>O"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
For AST access, use `parseHtmlToAst()`:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { parseHtmlToAst, renderMarkdown } from "ironmark";
|
|
177
|
+
|
|
178
|
+
const ast = parseHtmlToAst("<h1>Hello</h1><p>World</p>");
|
|
179
|
+
const md = renderMarkdown(ast);
|
|
180
|
+
```
|
|
181
|
+
|
|
173
182
|
### Browser / Bundler
|
|
174
183
|
|
|
175
|
-
Call `init()` once before using
|
|
184
|
+
Call `init()` once before using any function. It's idempotent and optionally accepts a custom `.wasm` URL.
|
|
176
185
|
|
|
177
186
|
```ts
|
|
178
|
-
import { init,
|
|
187
|
+
import { init, renderHtml, parseMarkdown } from "ironmark";
|
|
179
188
|
|
|
180
189
|
await init();
|
|
181
190
|
|
|
182
|
-
const html =
|
|
191
|
+
const html = renderHtml("# Hello\n\nThis is **fast**.");
|
|
192
|
+
const ast = parseMarkdown("# Hello");
|
|
183
193
|
```
|
|
184
194
|
|
|
185
195
|
#### Vite
|
|
186
196
|
|
|
187
197
|
```ts
|
|
188
|
-
import { init,
|
|
198
|
+
import { init, renderHtml } from "ironmark";
|
|
189
199
|
import wasmUrl from "ironmark/ironmark.wasm?url";
|
|
190
200
|
|
|
191
201
|
await init(wasmUrl);
|
|
192
202
|
|
|
193
|
-
const html =
|
|
203
|
+
const html = renderHtml("# Hello\n\nThis is **fast**.");
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Using ironmark in AI pipelines
|
|
207
|
+
|
|
208
|
+
ironmark is designed to be a reliable, deterministic parsing layer for AI agents and code generation tools.
|
|
209
|
+
|
|
210
|
+
**Recommended pattern for agents:**
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import { parseMarkdown, renderHtml, renderMarkdown, getCapabilities } from "ironmark";
|
|
214
|
+
|
|
215
|
+
// 1. Inspect capabilities at startup
|
|
216
|
+
const caps = getCapabilities();
|
|
217
|
+
// { astSchemaVersion: "2", formats: [...], presets: [...] }
|
|
218
|
+
|
|
219
|
+
// 2. Parse with deterministic options
|
|
220
|
+
const ast = parseMarkdown(content, { preset: "llm" });
|
|
221
|
+
|
|
222
|
+
// 3. Render or transform
|
|
223
|
+
const html = renderHtml(content, { preset: "llm" });
|
|
224
|
+
const normalized = renderMarkdown(ast);
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**The `llm` preset** produces deterministic, structure-first output:
|
|
228
|
+
|
|
229
|
+
- Disables autolinks, wiki links, math, hard breaks (ambiguous in AI-generated text)
|
|
230
|
+
- Enables heading IDs and whitespace normalization
|
|
231
|
+
- Disables raw HTML passthrough (safe by default)
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
import { parseMarkdown, extractHeadings, summarizeAst } from "ironmark";
|
|
235
|
+
|
|
236
|
+
const ast = parseMarkdown(content, { preset: "llm" });
|
|
237
|
+
|
|
238
|
+
// Extract document structure
|
|
239
|
+
const headings = extractHeadings(ast);
|
|
240
|
+
// [{ level: 1, text: "Introduction", id: "introduction" }, ...]
|
|
241
|
+
|
|
242
|
+
// Summarize node types
|
|
243
|
+
const summary = summarizeAst(ast);
|
|
244
|
+
// { blockCount: 5, nodeCounts: { Heading: 2, Paragraph: 3, ... } }
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Presets
|
|
248
|
+
|
|
249
|
+
Named presets configure multiple options at once. Explicit options always override the preset.
|
|
250
|
+
|
|
251
|
+
| Preset | Description |
|
|
252
|
+
| ----------- | -------------------------------------------------------------------------------------------------- |
|
|
253
|
+
| `"default"` | All extensions enabled. Current default behavior. |
|
|
254
|
+
| `"safe"` | Disables raw HTML, enables GFM tag filter. Use for untrusted input. |
|
|
255
|
+
| `"strict"` | CommonMark-only: disables extensions and permissive behaviors. |
|
|
256
|
+
| `"llm"` | Deterministic, stable output for AI pipelines. Disables ambiguous extensions, enables heading IDs. |
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
import { renderHtml, parseMarkdown } from "ironmark";
|
|
260
|
+
|
|
261
|
+
// Use a preset
|
|
262
|
+
const html = renderHtml(content, { preset: "safe" });
|
|
263
|
+
|
|
264
|
+
// Presets compose with explicit options (explicit wins)
|
|
265
|
+
const html = renderHtml(content, { preset: "llm", enableTables: false });
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Introspection helpers
|
|
269
|
+
|
|
270
|
+
These functions return stable machine-readable metadata. Useful for agents and tooling to discover available features at runtime.
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { getCapabilities, getAstSchemaVersion, getDefaultOptions, getPresets } from "ironmark";
|
|
274
|
+
|
|
275
|
+
// What does this build support?
|
|
276
|
+
const caps = getCapabilities();
|
|
277
|
+
// {
|
|
278
|
+
// astSchemaVersion: "1",
|
|
279
|
+
// formats: ["html", "ast", "markdown", "ansi"],
|
|
280
|
+
// presets: ["default", "safe", "strict", "llm"],
|
|
281
|
+
// extensions: [...],
|
|
282
|
+
// security: [...]
|
|
283
|
+
// }
|
|
284
|
+
|
|
285
|
+
// What is the current AST schema version?
|
|
286
|
+
const version = getAstSchemaVersion(); // "1"
|
|
287
|
+
|
|
288
|
+
// What are the resolved defaults for every option?
|
|
289
|
+
const defaults = getDefaultOptions();
|
|
290
|
+
|
|
291
|
+
// What options does each preset set?
|
|
292
|
+
const presets = getPresets();
|
|
293
|
+
console.log(presets.llm);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Utility functions
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
import { parseMarkdown, extractHeadings, summarizeAst } from "ironmark";
|
|
300
|
+
|
|
301
|
+
const ast = parseMarkdown("# Hello\n\n## World\n\nA paragraph.");
|
|
302
|
+
|
|
303
|
+
// Extract heading structure
|
|
304
|
+
const headings = extractHeadings(ast);
|
|
305
|
+
// [
|
|
306
|
+
// { level: 1, text: "Hello", id: "hello" },
|
|
307
|
+
// { level: 2, text: "World", id: "world" },
|
|
308
|
+
// ]
|
|
309
|
+
|
|
310
|
+
// Count node types
|
|
311
|
+
const summary = summarizeAst(ast);
|
|
312
|
+
// { blockCount: 3, nodeCounts: { Heading: 2, Paragraph: 1 } }
|
|
194
313
|
```
|
|
195
314
|
|
|
315
|
+
## Configuration
|
|
316
|
+
|
|
317
|
+
### Extensions (default `true`)
|
|
318
|
+
|
|
319
|
+
| Option | JS (`camelCase`) | Rust (`snake_case`) | Description |
|
|
320
|
+
| ------------------- | -------------------------- | ----------------------------- | ------------------------------------------------------ |
|
|
321
|
+
| Hard breaks | `hardBreaks` | `hard_breaks` | Every newline becomes `<br />` |
|
|
322
|
+
| Highlight | `enableHighlight` | `enable_highlight` | `==text==` → `<mark>` |
|
|
323
|
+
| Strikethrough | `enableStrikethrough` | `enable_strikethrough` | `~~text~~` → `<del>` |
|
|
324
|
+
| Underline | `enableUnderline` | `enable_underline` | `++text++` → `<u>` |
|
|
325
|
+
| Tables | `enableTables` | `enable_tables` | Pipe table syntax |
|
|
326
|
+
| Autolink | `enableAutolink` | `enable_autolink` | Bare URLs & emails → `<a>` |
|
|
327
|
+
| Task lists | `enableTaskLists` | `enable_task_lists` | `- [ ]` / `- [x]` checkboxes |
|
|
328
|
+
| Indented code | `enableIndentedCodeBlocks` | `enable_indented_code_blocks` | 4-space indent → `<pre><code>` |
|
|
329
|
+
| Wiki links | `enableWikiLinks` | `enable_wiki_links` | `[[page]]` → `<a href="page">` |
|
|
330
|
+
| LaTeX math | `enableLatexMath` | `enable_latex_math` | `$inline$` and `$$display$$` → `<span class="math-…">` |
|
|
331
|
+
| Heading IDs | `enableHeadingIds` | `enable_heading_ids` | Auto `id=` on headings from slugified text |
|
|
332
|
+
| Heading anchors | `enableHeadingAnchors` | `enable_heading_anchors` | `<a class="anchor">` inside each heading (implies IDs) |
|
|
333
|
+
| Permissive headings | `permissiveAtxHeaders` | `permissive_atx_headers` | Allow `#Heading` without space after `#` |
|
|
334
|
+
|
|
335
|
+
### Security
|
|
336
|
+
|
|
337
|
+
| Option | JS (`camelCase`) | Rust (`snake_case`) | Default | Description |
|
|
338
|
+
| ---------------- | ---------------- | ------------------- | -------------- | ------------------------------------------------------------------ |
|
|
339
|
+
| Safe mode | `safe` | — | `false` | Shorthand: sets `disableRawHtml: true` and `tagFilter: true` |
|
|
340
|
+
| Disable raw HTML | `disableRawHtml` | `disable_raw_html` | `false` | Escape **all** HTML blocks and inline HTML |
|
|
341
|
+
| No HTML blocks | `noHtmlBlocks` | `no_html_blocks` | `false` | Escape block-level HTML only (more granular than `disableRawHtml`) |
|
|
342
|
+
| No HTML spans | `noHtmlSpans` | `no_html_spans` | `false` | Escape inline HTML only |
|
|
343
|
+
| Tag filter | `tagFilter` | `tag_filter` | `false` | GFM tag filter: escape `<script>`, `<iframe>`, etc. |
|
|
344
|
+
| Max nesting | — | `max_nesting_depth` | `128` | Limit blockquote/list nesting depth (DoS prevention) |
|
|
345
|
+
| Max input size | — | `max_input_size` | `0` (no limit) | Truncate input beyond this byte count |
|
|
346
|
+
|
|
347
|
+
> In the WASM build, `max_nesting_depth` is fixed at `128` and `max_input_size` at `10 MB`.
|
|
348
|
+
|
|
349
|
+
Dangerous URI schemes (`javascript:`, `vbscript:`, `data:` except `data:image/…`) are **always** stripped from link and image destinations, regardless of options.
|
|
350
|
+
|
|
351
|
+
### Other Options
|
|
352
|
+
|
|
353
|
+
| Option | JS (`camelCase`) | Rust (`snake_case`) | Default | Description |
|
|
354
|
+
| ------------------- | -------------------- | --------------------- | ------- | ------------------------------------------------- |
|
|
355
|
+
| Collapse whitespace | `collapseWhitespace` | `collapse_whitespace` | `false` | Collapse runs of spaces/tabs in text to one space |
|
|
356
|
+
| Preset | `preset` | — | — | Apply a named option preset (JS only) |
|
|
357
|
+
| Deterministic | `deterministic` | — | `false` | Normalize whitespace for stable output (JS only) |
|
|
358
|
+
|
|
196
359
|
## CLI
|
|
197
360
|
|
|
198
361
|
Render Markdown as HTML, ANSI terminal output, or an AST. Default format is `html`.
|
|
@@ -225,19 +388,24 @@ Both CLIs support the same flags:
|
|
|
225
388
|
|
|
226
389
|
```text
|
|
227
390
|
OPTIONS (all formats):
|
|
228
|
-
--format <html|ansi|ast>
|
|
229
|
-
--
|
|
230
|
-
--
|
|
231
|
-
--no-
|
|
232
|
-
--no-
|
|
233
|
-
--no-
|
|
234
|
-
--no-
|
|
235
|
-
--no-
|
|
236
|
-
--
|
|
237
|
-
--
|
|
238
|
-
--
|
|
239
|
-
-
|
|
240
|
-
|
|
391
|
+
--format <html|ansi|ast> Output format; ast also accepts 'json' (default: html)
|
|
392
|
+
--preset <name> Apply a named option preset: default, safe, strict, llm
|
|
393
|
+
--safe Alias for --preset safe
|
|
394
|
+
--no-hard-breaks Don't turn soft newlines into hard line breaks
|
|
395
|
+
--no-tables Disable pipe table syntax
|
|
396
|
+
--no-highlight Disable ==highlight== syntax
|
|
397
|
+
--no-strikethrough Disable ~~strikethrough~~ syntax
|
|
398
|
+
--no-underline Disable ++underline++ syntax
|
|
399
|
+
--no-autolink Disable bare URL auto-linking
|
|
400
|
+
--no-task-lists Disable - [x] task list syntax
|
|
401
|
+
--math Enable $inline$ and $$display$$ math
|
|
402
|
+
--wiki-links Enable [[wiki link]] syntax
|
|
403
|
+
--capabilities Print machine-readable capabilities JSON and exit
|
|
404
|
+
--default-options Print resolved default options JSON and exit
|
|
405
|
+
--list-presets Print all presets and exit
|
|
406
|
+
--max-size N Truncate input to N bytes (Rust only)
|
|
407
|
+
-h, --help Print this help and exit
|
|
408
|
+
-V, --version Print version and exit
|
|
241
409
|
|
|
242
410
|
OPTIONS (ansi format only):
|
|
243
411
|
--width N Terminal column width (default: auto-detect, fallback 80)
|
|
@@ -253,6 +421,13 @@ OPTIONS (ansi format only):
|
|
|
253
421
|
echo '# Hello' | npx ironmark
|
|
254
422
|
npx ironmark README.md
|
|
255
423
|
|
|
424
|
+
# Safe mode for untrusted content
|
|
425
|
+
npx ironmark --safe README.md
|
|
426
|
+
npx ironmark --preset safe README.md
|
|
427
|
+
|
|
428
|
+
# LLM-optimized output
|
|
429
|
+
npx ironmark --preset llm README.md
|
|
430
|
+
|
|
256
431
|
# Render as ANSI terminal output
|
|
257
432
|
npx ironmark --format ansi README.md
|
|
258
433
|
npx ironmark --format ansi --width 120 README.md
|
|
@@ -261,6 +436,10 @@ npx ironmark --format ansi --no-color README.md | less
|
|
|
261
436
|
# Render as AST (JSON)
|
|
262
437
|
npx ironmark --format ast README.md
|
|
263
438
|
|
|
439
|
+
# Inspect capabilities
|
|
440
|
+
npx ironmark --capabilities
|
|
441
|
+
npx ironmark --list-presets
|
|
442
|
+
|
|
264
443
|
# Rust (after cargo install ironmark --features cli)
|
|
265
444
|
echo '# Hello' | ironmark
|
|
266
445
|
ironmark --format ansi README.md
|
|
@@ -275,21 +454,21 @@ cargo add ironmark
|
|
|
275
454
|
```
|
|
276
455
|
|
|
277
456
|
```rust
|
|
278
|
-
use ironmark::{
|
|
457
|
+
use ironmark::{render_html, ParseOptions};
|
|
279
458
|
|
|
280
459
|
fn main() {
|
|
281
460
|
// with defaults
|
|
282
|
-
let html =
|
|
461
|
+
let html = render_html("# Hello\n\nThis is **fast**.", &ParseOptions::default());
|
|
283
462
|
|
|
284
463
|
// with custom options
|
|
285
|
-
let html =
|
|
464
|
+
let html = render_html("line one\nline two", &ParseOptions {
|
|
286
465
|
hard_breaks: false,
|
|
287
466
|
enable_strikethrough: false,
|
|
288
467
|
..Default::default()
|
|
289
468
|
});
|
|
290
469
|
|
|
291
470
|
// safe mode for untrusted input
|
|
292
|
-
let html =
|
|
471
|
+
let html = render_html("<script>alert(1)</script>", &ParseOptions {
|
|
293
472
|
disable_raw_html: true,
|
|
294
473
|
max_input_size: 1_000_000, // 1 MB
|
|
295
474
|
..Default::default()
|
|
@@ -302,10 +481,10 @@ fn main() {
|
|
|
302
481
|
`parse_to_ast()` returns the typed Rust AST (`Block`) directly:
|
|
303
482
|
|
|
304
483
|
```rust
|
|
305
|
-
use ironmark::{Block, ParseOptions,
|
|
484
|
+
use ironmark::{Block, ParseOptions, parse_markdown};
|
|
306
485
|
|
|
307
486
|
fn main() {
|
|
308
|
-
let ast =
|
|
487
|
+
let ast = parse_markdown("# Hello", &ParseOptions::default());
|
|
309
488
|
|
|
310
489
|
match ast {
|
|
311
490
|
Block::Document { children } => {
|
|
@@ -377,10 +556,10 @@ fn main() {
|
|
|
377
556
|
Render an AST back to Markdown syntax:
|
|
378
557
|
|
|
379
558
|
```rust
|
|
380
|
-
use ironmark::{
|
|
559
|
+
use ironmark::{parse_markdown, render_markdown, ParseOptions};
|
|
381
560
|
|
|
382
561
|
fn main() {
|
|
383
|
-
let ast =
|
|
562
|
+
let ast = parse_markdown("# Hello\n\n**World**", &ParseOptions::default());
|
|
384
563
|
let md = render_markdown(&ast);
|
|
385
564
|
// Returns: "# Hello\n\n**World**"
|
|
386
565
|
}
|
|
@@ -388,18 +567,18 @@ fn main() {
|
|
|
388
567
|
|
|
389
568
|
### ANSI Terminal Output
|
|
390
569
|
|
|
391
|
-
`
|
|
570
|
+
`render_ansi_terminal()` renders Markdown as ANSI-coloured terminal output. Pass `Some(&AnsiOptions { .. })` to control width, colour, and line numbers, or `None` for defaults.
|
|
392
571
|
|
|
393
572
|
````rust
|
|
394
|
-
use ironmark::{AnsiOptions, ParseOptions,
|
|
573
|
+
use ironmark::{AnsiOptions, ParseOptions, render_ansi_terminal};
|
|
395
574
|
|
|
396
575
|
fn main() {
|
|
397
576
|
// Defaults — width 80, colour enabled
|
|
398
|
-
let out =
|
|
577
|
+
let out = render_ansi_terminal("# Hello\n\n**bold** and `code`", &ParseOptions::default(), None);
|
|
399
578
|
print!("{out}");
|
|
400
579
|
|
|
401
580
|
// Custom options — 120 columns, line numbers in code blocks
|
|
402
|
-
let out =
|
|
581
|
+
let out = render_ansi_terminal(
|
|
403
582
|
"# Hello\n\n```rust\nfn main() {}\n```",
|
|
404
583
|
&ParseOptions::default(),
|
|
405
584
|
Some(&AnsiOptions { width: 120, line_numbers: true, ..AnsiOptions::default() }),
|
|
@@ -407,7 +586,7 @@ fn main() {
|
|
|
407
586
|
print!("{out}");
|
|
408
587
|
|
|
409
588
|
// With padding — adds horizontal spacing on both sides
|
|
410
|
-
let out =
|
|
589
|
+
let out = render_ansi_terminal(
|
|
411
590
|
"# Hello\n\n> A quote",
|
|
412
591
|
&ParseOptions::default(),
|
|
413
592
|
Some(&AnsiOptions { padding: 2, ..AnsiOptions::default() }),
|
|
@@ -415,7 +594,7 @@ fn main() {
|
|
|
415
594
|
print!("{out}");
|
|
416
595
|
|
|
417
596
|
// Plain text — no ANSI codes (e.g. for writing to a file)
|
|
418
|
-
let plain =
|
|
597
|
+
let plain = render_ansi_terminal(
|
|
419
598
|
"# Hello",
|
|
420
599
|
&ParseOptions::default(),
|
|
421
600
|
Some(&AnsiOptions { color: false, ..AnsiOptions::default() }),
|
|
@@ -461,7 +640,7 @@ cc -o example example.c -L target/release -l ironmark \
|
|
|
461
640
|
#include <stdio.h>
|
|
462
641
|
|
|
463
642
|
int main(void) {
|
|
464
|
-
char *html =
|
|
643
|
+
char *html = ironmark_render_html("# Hello\n\nThis is **fast**.");
|
|
465
644
|
if (html) {
|
|
466
645
|
printf("%s\n", html);
|
|
467
646
|
ironmark_free(html);
|
|
@@ -470,7 +649,7 @@ int main(void) {
|
|
|
470
649
|
}
|
|
471
650
|
```
|
|
472
651
|
|
|
473
|
-
**Memory contract**: `
|
|
652
|
+
**Memory contract**: `ironmark_render_html` returns a heap-allocated string. You **must** free it with `ironmark_free`. Passing any other pointer to `ironmark_free` is undefined behaviour. Both functions are null-safe: `ironmark_render_html(NULL)` returns `NULL`; `ironmark_free(NULL)` is a no-op.
|
|
474
653
|
|
|
475
654
|
Parsing always uses the default `ParseOptions` (all extensions enabled, `disable_raw_html` off). Options are not yet configurable through the C API.
|
|
476
655
|
|