ironmark 1.8.1 → 1.10.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
@@ -84,6 +84,37 @@ const ast = JSON.parse(astJson);
84
84
 
85
85
  `parseToAst()` returns a JSON string for portability across JS runtimes and WASM boundaries.
86
86
 
87
+ ### ANSI Terminal Output
88
+
89
+ Use `renderAnsi()` to render Markdown as coloured terminal output (ANSI 256-colour escape codes). Useful for CLI tools, terminal UIs, or any environment with a TTY.
90
+
91
+ ````ts
92
+ import { renderAnsi } from "ironmark";
93
+
94
+ // Render with defaults (width 80, colour enabled)
95
+ const ansi = renderAnsi("# Hello\n\n**bold** and `code`");
96
+ process.stdout.write(ansi);
97
+
98
+ // Custom terminal width and line numbers in code blocks
99
+ const ansi = renderAnsi(
100
+ "# Hello\n\n```rust\nfn main() {}\n```",
101
+ {}, // parse options (same as parse())
102
+ { width: 120, lineNumbers: true },
103
+ );
104
+ process.stdout.write(ansi);
105
+
106
+ // Plain text — strips all ANSI codes (useful for piping to files)
107
+ const plain = renderAnsi("# Hello\n\n> quote", {}, { color: false });
108
+ ````
109
+
110
+ #### ANSI options
111
+
112
+ | Option | Type | Default | Description |
113
+ | ------------- | --------- | ------- | ------------------------------------------------------------------------------- |
114
+ | `width` | `number` | `80` | Column width for word-wrap, heading underlines, rule length. `0` = use default. |
115
+ | `color` | `boolean` | `true` | Emit ANSI colour codes. `false` = plain text output. |
116
+ | `lineNumbers` | `boolean` | `false` | Show line numbers in fenced code blocks. |
117
+
87
118
  ### Browser / Bundler
88
119
 
89
120
  Call `init()` once before using `parse()`. It's idempotent and optionally accepts a custom `.wasm` URL.
@@ -107,6 +138,71 @@ await init(wasmUrl);
107
138
  const html = parse("# Hello\n\nThis is **fast**.");
108
139
  ```
109
140
 
141
+ ## CLI
142
+
143
+ Render Markdown as coloured terminal output. Two ways to install:
144
+
145
+ ### npm
146
+
147
+ ```bash
148
+ npx ironmark --ansi README.md
149
+ ```
150
+
151
+ Or install globally:
152
+
153
+ ```bash
154
+ npm install -g ironmark
155
+ ironmark --ansi README.md
156
+ ```
157
+
158
+ ### Rust
159
+
160
+ Native binary — faster startup, auto-detects terminal width via `$COLUMNS` / `tput cols`.
161
+
162
+ ```bash
163
+ cargo install ironmark --features cli
164
+ ironmark --ansi README.md
165
+ ```
166
+
167
+ ### Options
168
+
169
+ Both CLIs support the same flags (the npm CLI requires `--ansi` as the first flag):
170
+
171
+ ```text
172
+ OPTIONS:
173
+ --width N Terminal column width (default: auto-detect, fallback 80)
174
+ --no-color Disable ANSI escape codes (plain text)
175
+ -n, --line-numbers Show line numbers in fenced code blocks
176
+ --no-hard-breaks Don't turn soft newlines into hard line breaks
177
+ --no-tables Disable pipe table syntax
178
+ --no-highlight Disable ==highlight== syntax
179
+ --no-strikethrough Disable ~~strikethrough~~ syntax
180
+ --no-underline Disable ++underline++ syntax
181
+ --no-autolink Disable bare URL auto-linking
182
+ --no-task-lists Disable - [x] task list syntax
183
+ --math Enable $inline$ and $$display$$ math
184
+ --wiki-links Enable [[wiki link]] syntax
185
+ --max-size N Truncate input to N bytes (Rust only)
186
+ -h, --help Print this help and exit
187
+ -V, --version Print version and exit
188
+ ```
189
+
190
+ ### Examples
191
+
192
+ ```bash
193
+ # npm
194
+ npx ironmark --ansi README.md
195
+ npx ironmark --ansi --width 120 README.md
196
+ echo '# Hello' | npx ironmark --ansi
197
+ npx ironmark --ansi --no-color README.md | less
198
+
199
+ # Rust (after cargo install ironmark --features cli)
200
+ ironmark --ansi README.md
201
+ ironmark --ansi --width 120 README.md
202
+ echo '# Hello' | ironmark --ansi
203
+ cat doc.md | ironmark --ansi --math --wiki-links
204
+ ```
205
+
110
206
  ## Rust
111
207
 
112
208
  ```bash
@@ -162,6 +258,43 @@ Exported AST types:
162
258
  - `TableData`
163
259
  - `TableAlignment`
164
260
 
261
+ ### ANSI Terminal Output
262
+
263
+ `render_ansi()` renders Markdown as ANSI-coloured terminal output. Pass `Some(&AnsiOptions { .. })` to control width, colour, and line numbers, or `None` for defaults.
264
+
265
+ ````rust
266
+ use ironmark::{AnsiOptions, ParseOptions, render_ansi};
267
+
268
+ fn main() {
269
+ // Defaults — width 80, colour enabled
270
+ let out = render_ansi("# Hello\n\n**bold** and `code`", &ParseOptions::default(), None);
271
+ print!("{out}");
272
+
273
+ // Custom options — 120 columns, line numbers in code blocks
274
+ let out = render_ansi(
275
+ "# Hello\n\n```rust\nfn main() {}\n```",
276
+ &ParseOptions::default(),
277
+ Some(&AnsiOptions { width: 120, line_numbers: true, ..AnsiOptions::default() }),
278
+ );
279
+ print!("{out}");
280
+
281
+ // Plain text — no ANSI codes (e.g. for writing to a file)
282
+ let plain = render_ansi(
283
+ "# Hello",
284
+ &ParseOptions::default(),
285
+ Some(&AnsiOptions { color: false, ..AnsiOptions::default() }),
286
+ );
287
+ }
288
+ ````
289
+
290
+ `AnsiOptions` fields:
291
+
292
+ | Field | Type | Default | Description |
293
+ | -------------- | ------- | ------- | --------------------------------------------------------------------------- |
294
+ | `width` | `usize` | `80` | Column width for word-wrap, heading underlines, rule length. `0` = disable. |
295
+ | `color` | `bool` | `true` | Emit ANSI 256-colour escape codes. |
296
+ | `line_numbers` | `bool` | `false` | Show line numbers in fenced code blocks. |
297
+
165
298
  ## C / C++
166
299
 
167
300
  The crate compiles to a static library (`libironmark.a`) that exposes two C functions. A header is provided at `include/ironmark.h`.
@@ -236,4 +369,4 @@ pnpm build
236
369
 
237
370
  ### Troubleshooting
238
371
 
239
- **`wasm32-unknown-unknown target not found`** or **`wasm-bindgen not found`** — run `pnpm setup:wasm` to install all prerequisites.
372
+ **`wasm32-unknown-unknown target not found`** or **`wasm-bindgen not found`** — run `pnpm setup:wasm` to install all prerequisites
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ironmark",
3
- "version": "1.8.1",
3
+ "version": "1.10.0",
4
4
  "description": "Very fast markdown parser in Rust, consumable from JavaScript/TypeScript via WebAssembly",
5
5
  "keywords": [
6
6
  "markdown",
@@ -17,11 +17,15 @@
17
17
  "type": "git",
18
18
  "url": "https://github.com/ph1p/ironmark.git"
19
19
  },
20
+ "bin": {
21
+ "ironmark": "wasm/cli.js"
22
+ },
20
23
  "files": [
21
24
  "wasm/pkg",
22
25
  "wasm/shared.js",
23
26
  "wasm/web.js",
24
27
  "wasm/node.js",
28
+ "wasm/cli.js",
25
29
  "wasm/index.d.ts",
26
30
  "README.md"
27
31
  ],
package/wasm/cli.js ADDED
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { renderAnsi } from "./node.js";
4
+
5
+ const VERSION = JSON.parse(
6
+ readFileSync(new URL("../package.json", import.meta.url), "utf8"),
7
+ ).version;
8
+
9
+ const HELP = `ironmark — render Markdown as coloured terminal output
10
+
11
+ USAGE:
12
+ ironmark --ansi [OPTIONS] [FILE...]
13
+
14
+ When no FILE is given, reads from stdin. Use '-' for stdin explicitly.
15
+
16
+ OPTIONS:
17
+ --ansi Render as ANSI-coloured terminal output (required)
18
+ --width N Terminal column width (default: auto-detect, fallback 80)
19
+ --no-color Disable ANSI escape codes (plain text)
20
+ -n, --line-numbers Show line numbers in fenced code blocks
21
+ --no-hard-breaks Don't turn soft newlines into hard line breaks
22
+ --no-tables Disable pipe table syntax
23
+ --no-highlight Disable ==highlight== syntax
24
+ --no-strikethrough Disable ~~strikethrough~~ syntax
25
+ --no-underline Disable ++underline++ syntax
26
+ --no-autolink Disable bare URL auto-linking
27
+ --no-task-lists Disable - [x] task list syntax
28
+ --math Enable $inline$ and $$display$$ math
29
+ --wiki-links Enable [[wiki link]] syntax
30
+ -h, --help Print this help and exit
31
+ -V, --version Print version and exit
32
+
33
+ EXAMPLES:
34
+ npx ironmark --ansi README.md
35
+ npx ironmark --ansi --width 120 README.md
36
+ npx ironmark --ansi --no-color README.md | less
37
+ echo '# Hello' | npx ironmark --ansi
38
+ cat doc.md | npx ironmark --ansi --math --wiki-links
39
+ `;
40
+
41
+ const args = process.argv.slice(2);
42
+ const files = [];
43
+ const parseOptions = {};
44
+ const ansiOptions = {};
45
+ let ansiMode = false;
46
+
47
+ for (let i = 0; i < args.length; i++) {
48
+ switch (args[i]) {
49
+ case "-h":
50
+ case "--help":
51
+ process.stdout.write(HELP);
52
+ process.exit(0);
53
+ break;
54
+ case "-V":
55
+ case "--version":
56
+ console.log(`ironmark ${VERSION}`);
57
+ process.exit(0);
58
+ break;
59
+ case "--ansi":
60
+ ansiMode = true;
61
+ break;
62
+ case "--no-color":
63
+ case "--no-colour":
64
+ ansiOptions.color = false;
65
+ break;
66
+ case "-n":
67
+ case "--line-numbers":
68
+ ansiOptions.lineNumbers = true;
69
+ break;
70
+ case "--width": {
71
+ const val = args[++i];
72
+ if (val === undefined || Number.isNaN(Number(val))) {
73
+ console.error("error: --width requires a numeric value");
74
+ process.exit(2);
75
+ }
76
+ ansiOptions.width = Number(val);
77
+ break;
78
+ }
79
+ case "--no-hard-breaks":
80
+ parseOptions.hardBreaks = false;
81
+ break;
82
+ case "--no-tables":
83
+ parseOptions.enableTables = false;
84
+ break;
85
+ case "--no-highlight":
86
+ parseOptions.enableHighlight = false;
87
+ break;
88
+ case "--no-strikethrough":
89
+ parseOptions.enableStrikethrough = false;
90
+ break;
91
+ case "--no-underline":
92
+ parseOptions.enableUnderline = false;
93
+ break;
94
+ case "--no-autolink":
95
+ parseOptions.enableAutolink = false;
96
+ break;
97
+ case "--no-task-lists":
98
+ parseOptions.enableTaskLists = false;
99
+ break;
100
+ case "--math":
101
+ parseOptions.enableLatexMath = true;
102
+ break;
103
+ case "--wiki-links":
104
+ parseOptions.enableWikiLinks = true;
105
+ break;
106
+ default:
107
+ if (args[i].startsWith("--width=")) {
108
+ const val = Number(args[i].slice("--width=".length));
109
+ if (Number.isNaN(val)) {
110
+ console.error("error: --width requires a numeric value");
111
+ process.exit(2);
112
+ }
113
+ ansiOptions.width = val;
114
+ } else if (args[i].startsWith("-") && args[i] !== "-") {
115
+ console.error(`error: unknown flag: ${args[i]}`);
116
+ console.error("Run 'ironmark --help' for usage.");
117
+ process.exit(2);
118
+ } else {
119
+ files.push(args[i]);
120
+ }
121
+ }
122
+ }
123
+
124
+ if (!ansiMode) {
125
+ console.error("error: --ansi flag is required");
126
+ console.error("Run 'ironmark --help' for usage.");
127
+ process.exit(2);
128
+ }
129
+
130
+ // Auto-detect terminal width
131
+ if (ansiOptions.width === undefined) {
132
+ ansiOptions.width = process.stdout.columns || 80;
133
+ }
134
+
135
+ let input = "";
136
+
137
+ if (files.length === 0) {
138
+ input = readFileSync(0, "utf8");
139
+ } else {
140
+ for (const file of files) {
141
+ if (file === "-") {
142
+ input += readFileSync(0, "utf8");
143
+ } else {
144
+ try {
145
+ input += readFileSync(file, "utf8");
146
+ } catch (err) {
147
+ console.error(`error: ${file}: ${err.message}`);
148
+ process.exit(1);
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ process.stdout.write(renderAnsi(input, parseOptions, ansiOptions));
package/wasm/index.d.ts CHANGED
@@ -66,3 +66,49 @@ export declare function parse(markdown: MarkdownInput, options?: ParseOptions):
66
66
  * @returns JSON string representing the AST.
67
67
  */
68
68
  export declare function parseToAst(markdown: MarkdownInput, options?: ParseOptions): string;
69
+
70
+ /**
71
+ * Options for the ANSI terminal renderer.
72
+ */
73
+ export interface AnsiOptions {
74
+ /**
75
+ * Terminal column width for word-wrap, heading underlines, and thematic breaks.
76
+ * Set to `0` to disable all width-dependent formatting. Default: `80`.
77
+ */
78
+ width?: number;
79
+ /**
80
+ * Emit ANSI 256-colour escape codes. Set to `false` for plain-text output
81
+ * (e.g. when piping to a file or a non-colour terminal). Default: `true`.
82
+ */
83
+ color?: boolean;
84
+ /**
85
+ * Show line numbers in fenced code blocks, right-aligned to the total line
86
+ * count and separated from the code by a `│` border. Default: `false`.
87
+ */
88
+ lineNumbers?: boolean;
89
+ }
90
+
91
+ /**
92
+ * Render Markdown as ANSI-coloured terminal output.
93
+ *
94
+ * Produces a string containing ANSI 256-colour escape codes suitable for
95
+ * display in a terminal emulator. Headings, code blocks, inline code,
96
+ * blockquotes, tables, and inline formatting are all styled distinctly.
97
+ *
98
+ * @param markdown - Markdown source (string or binary).
99
+ * @param options - Optional parse options (same flags as `parse()`).
100
+ * @param ansiOptions - Optional ANSI rendering options (width, color, lineNumbers).
101
+ * @returns String with ANSI escape codes (or plain text when `color: false`).
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * import { renderAnsi } from "ironmark";
106
+ * const out = renderAnsi("# Hello\n\n**bold** and `code`");
107
+ * process.stdout.write(out);
108
+ * ```
109
+ */
110
+ export declare function renderAnsi(
111
+ markdown: MarkdownInput,
112
+ options?: ParseOptions,
113
+ ansiOptions?: AnsiOptions,
114
+ ): string;