ironmark 1.12.3 → 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 +329 -141
- package/package.json +1 -1
- package/wasm/cli.js +93 -29
- 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 {
|
|
81
|
-
|
|
82
|
-
const html = parse("# Hello\n\nThis is **fast**.");
|
|
69
|
+
import { renderHtml, parseMarkdown } from "ironmark";
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
const
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
### HTML to Markdown
|
|
83
|
+
### Markdown → AST
|
|
102
84
|
|
|
103
|
-
|
|
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,44 +155,222 @@ 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**.");
|
|
194
204
|
```
|
|
195
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 } }
|
|
313
|
+
```
|
|
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
|
-
Render Markdown as
|
|
361
|
+
Render Markdown as HTML, ANSI terminal output, or an AST. Default format is `html`.
|
|
199
362
|
|
|
200
363
|
### npm
|
|
201
364
|
|
|
202
365
|
```bash
|
|
203
|
-
npx ironmark
|
|
366
|
+
npx ironmark README.md
|
|
204
367
|
```
|
|
205
368
|
|
|
206
369
|
Or install globally:
|
|
207
370
|
|
|
208
371
|
```bash
|
|
209
372
|
npm install -g ironmark
|
|
210
|
-
ironmark
|
|
373
|
+
ironmark README.md
|
|
211
374
|
```
|
|
212
375
|
|
|
213
376
|
### Rust
|
|
@@ -216,47 +379,72 @@ Native binary — faster startup, auto-detects terminal width via `$COLUMNS` / `
|
|
|
216
379
|
|
|
217
380
|
```bash
|
|
218
381
|
cargo install ironmark --features cli
|
|
219
|
-
ironmark
|
|
382
|
+
ironmark README.md
|
|
220
383
|
```
|
|
221
384
|
|
|
222
385
|
### Options
|
|
223
386
|
|
|
224
|
-
Both CLIs support the same flags
|
|
387
|
+
Both CLIs support the same flags:
|
|
225
388
|
|
|
226
389
|
```text
|
|
227
|
-
OPTIONS:
|
|
390
|
+
OPTIONS (all formats):
|
|
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
|
|
409
|
+
|
|
410
|
+
OPTIONS (ansi format only):
|
|
228
411
|
--width N Terminal column width (default: auto-detect, fallback 80)
|
|
229
|
-
--padding N Horizontal padding added to both sides of each line
|
|
412
|
+
--padding N Horizontal padding added to both sides of each line (default: 0)
|
|
230
413
|
--no-color Disable ANSI escape codes (plain text)
|
|
231
414
|
-n, --line-numbers Show line numbers in fenced code blocks
|
|
232
|
-
--no-hard-breaks Don't turn soft newlines into hard line breaks
|
|
233
|
-
--no-tables Disable pipe table syntax
|
|
234
|
-
--no-highlight Disable ==highlight== syntax
|
|
235
|
-
--no-strikethrough Disable ~~strikethrough~~ syntax
|
|
236
|
-
--no-underline Disable ++underline++ syntax
|
|
237
|
-
--no-autolink Disable bare URL auto-linking
|
|
238
|
-
--no-task-lists Disable - [x] task list syntax
|
|
239
|
-
--math Enable $inline$ and $$display$$ math
|
|
240
|
-
--wiki-links Enable [[wiki link]] syntax
|
|
241
|
-
--max-size N Truncate input to N bytes (Rust only)
|
|
242
|
-
-h, --help Print this help and exit
|
|
243
|
-
-V, --version Print version and exit
|
|
244
415
|
```
|
|
245
416
|
|
|
246
417
|
### Examples
|
|
247
418
|
|
|
248
419
|
```bash
|
|
249
|
-
#
|
|
250
|
-
npx ironmark
|
|
251
|
-
npx ironmark
|
|
252
|
-
|
|
253
|
-
|
|
420
|
+
# Render as HTML (default)
|
|
421
|
+
echo '# Hello' | npx ironmark
|
|
422
|
+
npx ironmark README.md
|
|
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
|
+
|
|
431
|
+
# Render as ANSI terminal output
|
|
432
|
+
npx ironmark --format ansi README.md
|
|
433
|
+
npx ironmark --format ansi --width 120 README.md
|
|
434
|
+
npx ironmark --format ansi --no-color README.md | less
|
|
435
|
+
|
|
436
|
+
# Render as AST (JSON)
|
|
437
|
+
npx ironmark --format ast README.md
|
|
438
|
+
|
|
439
|
+
# Inspect capabilities
|
|
440
|
+
npx ironmark --capabilities
|
|
441
|
+
npx ironmark --list-presets
|
|
254
442
|
|
|
255
443
|
# Rust (after cargo install ironmark --features cli)
|
|
256
|
-
|
|
257
|
-
ironmark --ansi
|
|
258
|
-
|
|
259
|
-
cat doc.md | ironmark --ansi --math --wiki-links
|
|
444
|
+
echo '# Hello' | ironmark
|
|
445
|
+
ironmark --format ansi README.md
|
|
446
|
+
ironmark --format ansi --width 120 README.md
|
|
447
|
+
cat doc.md | ironmark --format ansi --math --wiki-links
|
|
260
448
|
```
|
|
261
449
|
|
|
262
450
|
## Rust
|
|
@@ -266,21 +454,21 @@ cargo add ironmark
|
|
|
266
454
|
```
|
|
267
455
|
|
|
268
456
|
```rust
|
|
269
|
-
use ironmark::{
|
|
457
|
+
use ironmark::{render_html, ParseOptions};
|
|
270
458
|
|
|
271
459
|
fn main() {
|
|
272
460
|
// with defaults
|
|
273
|
-
let html =
|
|
461
|
+
let html = render_html("# Hello\n\nThis is **fast**.", &ParseOptions::default());
|
|
274
462
|
|
|
275
463
|
// with custom options
|
|
276
|
-
let html =
|
|
464
|
+
let html = render_html("line one\nline two", &ParseOptions {
|
|
277
465
|
hard_breaks: false,
|
|
278
466
|
enable_strikethrough: false,
|
|
279
467
|
..Default::default()
|
|
280
468
|
});
|
|
281
469
|
|
|
282
470
|
// safe mode for untrusted input
|
|
283
|
-
let html =
|
|
471
|
+
let html = render_html("<script>alert(1)</script>", &ParseOptions {
|
|
284
472
|
disable_raw_html: true,
|
|
285
473
|
max_input_size: 1_000_000, // 1 MB
|
|
286
474
|
..Default::default()
|
|
@@ -293,10 +481,10 @@ fn main() {
|
|
|
293
481
|
`parse_to_ast()` returns the typed Rust AST (`Block`) directly:
|
|
294
482
|
|
|
295
483
|
```rust
|
|
296
|
-
use ironmark::{Block, ParseOptions,
|
|
484
|
+
use ironmark::{Block, ParseOptions, parse_markdown};
|
|
297
485
|
|
|
298
486
|
fn main() {
|
|
299
|
-
let ast =
|
|
487
|
+
let ast = parse_markdown("# Hello", &ParseOptions::default());
|
|
300
488
|
|
|
301
489
|
match ast {
|
|
302
490
|
Block::Document { children } => {
|
|
@@ -368,10 +556,10 @@ fn main() {
|
|
|
368
556
|
Render an AST back to Markdown syntax:
|
|
369
557
|
|
|
370
558
|
```rust
|
|
371
|
-
use ironmark::{
|
|
559
|
+
use ironmark::{parse_markdown, render_markdown, ParseOptions};
|
|
372
560
|
|
|
373
561
|
fn main() {
|
|
374
|
-
let ast =
|
|
562
|
+
let ast = parse_markdown("# Hello\n\n**World**", &ParseOptions::default());
|
|
375
563
|
let md = render_markdown(&ast);
|
|
376
564
|
// Returns: "# Hello\n\n**World**"
|
|
377
565
|
}
|
|
@@ -379,18 +567,18 @@ fn main() {
|
|
|
379
567
|
|
|
380
568
|
### ANSI Terminal Output
|
|
381
569
|
|
|
382
|
-
`
|
|
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.
|
|
383
571
|
|
|
384
572
|
````rust
|
|
385
|
-
use ironmark::{AnsiOptions, ParseOptions,
|
|
573
|
+
use ironmark::{AnsiOptions, ParseOptions, render_ansi_terminal};
|
|
386
574
|
|
|
387
575
|
fn main() {
|
|
388
576
|
// Defaults — width 80, colour enabled
|
|
389
|
-
let out =
|
|
577
|
+
let out = render_ansi_terminal("# Hello\n\n**bold** and `code`", &ParseOptions::default(), None);
|
|
390
578
|
print!("{out}");
|
|
391
579
|
|
|
392
580
|
// Custom options — 120 columns, line numbers in code blocks
|
|
393
|
-
let out =
|
|
581
|
+
let out = render_ansi_terminal(
|
|
394
582
|
"# Hello\n\n```rust\nfn main() {}\n```",
|
|
395
583
|
&ParseOptions::default(),
|
|
396
584
|
Some(&AnsiOptions { width: 120, line_numbers: true, ..AnsiOptions::default() }),
|
|
@@ -398,7 +586,7 @@ fn main() {
|
|
|
398
586
|
print!("{out}");
|
|
399
587
|
|
|
400
588
|
// With padding — adds horizontal spacing on both sides
|
|
401
|
-
let out =
|
|
589
|
+
let out = render_ansi_terminal(
|
|
402
590
|
"# Hello\n\n> A quote",
|
|
403
591
|
&ParseOptions::default(),
|
|
404
592
|
Some(&AnsiOptions { padding: 2, ..AnsiOptions::default() }),
|
|
@@ -406,7 +594,7 @@ fn main() {
|
|
|
406
594
|
print!("{out}");
|
|
407
595
|
|
|
408
596
|
// Plain text — no ANSI codes (e.g. for writing to a file)
|
|
409
|
-
let plain =
|
|
597
|
+
let plain = render_ansi_terminal(
|
|
410
598
|
"# Hello",
|
|
411
599
|
&ParseOptions::default(),
|
|
412
600
|
Some(&AnsiOptions { color: false, ..AnsiOptions::default() }),
|
|
@@ -452,7 +640,7 @@ cc -o example example.c -L target/release -l ironmark \
|
|
|
452
640
|
#include <stdio.h>
|
|
453
641
|
|
|
454
642
|
int main(void) {
|
|
455
|
-
char *html =
|
|
643
|
+
char *html = ironmark_render_html("# Hello\n\nThis is **fast**.");
|
|
456
644
|
if (html) {
|
|
457
645
|
printf("%s\n", html);
|
|
458
646
|
ironmark_free(html);
|
|
@@ -461,7 +649,7 @@ int main(void) {
|
|
|
461
649
|
}
|
|
462
650
|
```
|
|
463
651
|
|
|
464
|
-
**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.
|
|
465
653
|
|
|
466
654
|
Parsing always uses the default `ParseOptions` (all extensions enabled, `disable_raw_html` off). Options are not yet configurable through the C API.
|
|
467
655
|
|