markdansi 0.1.0 → 0.1.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 +12 -10
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +151 -0
- package/dist/hyperlink.d.ts +9 -0
- package/{src → dist}/hyperlink.js +8 -7
- package/dist/index.d.ts +8 -45
- package/dist/index.js +15 -0
- package/dist/parser.d.ts +2 -0
- package/{src → dist}/parser.js +4 -5
- package/dist/render.d.ts +9 -0
- package/dist/render.js +424 -0
- package/dist/theme.d.ts +18 -0
- package/dist/theme.js +105 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.js +1 -0
- package/dist/wrap.d.ts +10 -0
- package/dist/wrap.js +48 -0
- package/docs/spec.md +23 -10
- package/package.json +10 -10
- package/tsconfig.json +2 -4
- package/src/cli.js +0 -62
- package/src/index.js +0 -12
- package/src/render.js +0 -342
- package/src/theme.js +0 -53
- package/src/wrap.js +0 -45
- package/types/index.ts +0 -74
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
# Markdansi
|
|
1
|
+
# 🎨 Markdansi: Wraps, colors, links - no baggage.
|
|
2
|
+
   
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
   
|
|
4
|
+
Tiny, dependency-light Markdown → ANSI renderer and CLI for modern Node (>=22). Focuses on readable terminal output with sensible wrapping, GFM support (tables, task lists, strikethrough), optional OSC‑8 hyperlinks, and zero built‑in syntax highlighting (pluggable hook). Written in TypeScript, ships ESM.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Published on npm as `markdansi`.
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
|
-
> Not yet published to npm (name available as of November 16, 2025). Install from git or local path until released.
|
|
11
|
-
|
|
12
10
|
```bash
|
|
13
11
|
pnpm add markdansi
|
|
14
12
|
# or
|
|
@@ -60,21 +58,25 @@ console.log(custom('`inline`\n\n```\nblock code\n```'));
|
|
|
60
58
|
- `width`: used only when `wrap===true`; default TTY columns or 80.
|
|
61
59
|
- `color` (default TTY): `false` removes all ANSI/OSC.
|
|
62
60
|
- `hyperlinks` (default auto): enable/disable OSC‑8 links.
|
|
63
|
-
- `theme`: `default | dim | bright` or custom theme object.
|
|
61
|
+
- `theme`: `default | dim | bright | solarized | monochrome | contrast` or custom theme object.
|
|
64
62
|
- `listIndent`: spaces per nesting level (default 2).
|
|
65
63
|
- `quotePrefix`: blockquote line prefix (default `│ `).
|
|
64
|
+
- `tableBorder`: `unicode` (default) | `ascii` | `none`.
|
|
65
|
+
- `tablePadding`: spaces inside cells (default 1); `tableDense` drops extra separators.
|
|
66
|
+
- `tableTruncate`: cap cells to column width (default `true`, ellipsis `…`).
|
|
67
|
+
- `codeBox`: draw a box around fenced code (default true); `codeGutter` shows line numbers; `codeWrap` wraps code lines by default.
|
|
66
68
|
- `highlighter(code, lang)`: optional hook to recolor code blocks; must not add/remove newlines.
|
|
67
69
|
|
|
68
70
|
## Status
|
|
69
71
|
|
|
70
|
-
Version: `0.1.
|
|
72
|
+
Version: `0.1.1` (released)
|
|
71
73
|
Tests: `pnpm test`
|
|
72
74
|
License: MIT
|
|
73
75
|
|
|
74
76
|
## Notes
|
|
75
77
|
|
|
76
|
-
- Code blocks
|
|
77
|
-
- Tables
|
|
78
|
+
- Code blocks wrap to the render width by default; disable with `codeWrap=false`. If `lang` is present, a faint `[lang]` label is shown and boxes use unicode borders.
|
|
79
|
+
- Tables use unicode borders by default, include padding, respect GFM alignment, and truncate long cells with `…` so layouts stay tidy. Turn off truncation with `tableTruncate=false`.
|
|
78
80
|
- Tight vs loose lists follow GFM; task items render `[ ]` / `[x]`.
|
|
79
81
|
|
|
80
82
|
See `docs/spec.md` for full behavior details.*** End Patch
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import type { RenderOptions } from "./types.js";
|
|
3
|
+
type CliArgs = Partial<RenderOptions> & {
|
|
4
|
+
in?: string;
|
|
5
|
+
out?: string;
|
|
6
|
+
help?: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Parse CLI arguments into RenderOptions-ish object (plus in/out paths).
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseArgs(argv: string[]): CliArgs;
|
|
12
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { render } from "./index.js";
|
|
6
|
+
/**
|
|
7
|
+
* Parse CLI arguments into RenderOptions-ish object (plus in/out paths).
|
|
8
|
+
*/
|
|
9
|
+
export function parseArgs(argv) {
|
|
10
|
+
const args = {};
|
|
11
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
12
|
+
const a = argv[i];
|
|
13
|
+
if (!a)
|
|
14
|
+
continue;
|
|
15
|
+
if (a === "--no-wrap")
|
|
16
|
+
args.wrap = false;
|
|
17
|
+
else if (a === "--no-color")
|
|
18
|
+
args.color = false;
|
|
19
|
+
else if (a === "--no-links")
|
|
20
|
+
args.hyperlinks = false;
|
|
21
|
+
else if (a === "--code-wrap=false")
|
|
22
|
+
args.codeWrap = false;
|
|
23
|
+
else if (a === "--code-wrap=true")
|
|
24
|
+
args.codeWrap = true;
|
|
25
|
+
else if (a === "--code-box=false")
|
|
26
|
+
args.codeBox = false;
|
|
27
|
+
else if (a === "--code-box=true")
|
|
28
|
+
args.codeBox = true;
|
|
29
|
+
else if (a === "--code-gutter=true")
|
|
30
|
+
args.codeGutter = true;
|
|
31
|
+
else if (a === "--code-gutter=false")
|
|
32
|
+
args.codeGutter = false;
|
|
33
|
+
else if (a.startsWith("--table-border=")) {
|
|
34
|
+
const val = a.split("=")[1];
|
|
35
|
+
if (val === "unicode" || val === "ascii" || val === "none")
|
|
36
|
+
args.tableBorder = val;
|
|
37
|
+
}
|
|
38
|
+
else if (a === "--table-dense")
|
|
39
|
+
args.tableDense = true;
|
|
40
|
+
else if (a === "--table-truncate=false")
|
|
41
|
+
args.tableTruncate = false;
|
|
42
|
+
else if (a === "--table-truncate=true")
|
|
43
|
+
args.tableTruncate = true;
|
|
44
|
+
else if (a === "--table-padding") {
|
|
45
|
+
const next = argv[i + 1];
|
|
46
|
+
if (next)
|
|
47
|
+
args.tablePadding = Number(next);
|
|
48
|
+
i += 1;
|
|
49
|
+
}
|
|
50
|
+
else if (a === "--table-ellipsis") {
|
|
51
|
+
const next = argv[i + 1];
|
|
52
|
+
if (next)
|
|
53
|
+
args.tableEllipsis = next;
|
|
54
|
+
i += 1;
|
|
55
|
+
}
|
|
56
|
+
else if (a === "--in") {
|
|
57
|
+
const next = argv[i + 1];
|
|
58
|
+
if (next)
|
|
59
|
+
args.in = next;
|
|
60
|
+
i += 1;
|
|
61
|
+
}
|
|
62
|
+
else if (a === "--out") {
|
|
63
|
+
const next = argv[i + 1];
|
|
64
|
+
if (next)
|
|
65
|
+
args.out = next;
|
|
66
|
+
i += 1;
|
|
67
|
+
}
|
|
68
|
+
else if (a === "--width") {
|
|
69
|
+
const next = argv[i + 1];
|
|
70
|
+
if (next)
|
|
71
|
+
args.width = Number(next);
|
|
72
|
+
i += 1;
|
|
73
|
+
}
|
|
74
|
+
else if (a.startsWith("--theme=")) {
|
|
75
|
+
const themeVal = a.split("=")[1];
|
|
76
|
+
if (themeVal)
|
|
77
|
+
args.theme = themeVal;
|
|
78
|
+
}
|
|
79
|
+
else if (a === "--list-indent") {
|
|
80
|
+
const next = argv[i + 1];
|
|
81
|
+
if (next)
|
|
82
|
+
args.listIndent = Number(next);
|
|
83
|
+
i += 1;
|
|
84
|
+
}
|
|
85
|
+
else if (a === "--quote-prefix") {
|
|
86
|
+
const next = argv[i + 1];
|
|
87
|
+
if (next)
|
|
88
|
+
args.quotePrefix = next;
|
|
89
|
+
i += 1;
|
|
90
|
+
}
|
|
91
|
+
else if (a === "--help" || a === "-h")
|
|
92
|
+
args.help = true;
|
|
93
|
+
}
|
|
94
|
+
return args;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* CLI entrypoint.
|
|
98
|
+
*/
|
|
99
|
+
function main() {
|
|
100
|
+
const args = parseArgs(process.argv);
|
|
101
|
+
if (args.help) {
|
|
102
|
+
process.stdout.write(`markdansi options:
|
|
103
|
+
--in FILE Input file (default: stdin)
|
|
104
|
+
--out FILE Output file (default: stdout)
|
|
105
|
+
--width N Wrap width (default: TTY cols or 80)
|
|
106
|
+
--no-wrap Disable hard wrapping
|
|
107
|
+
--no-color Disable ANSI/OSC output
|
|
108
|
+
--no-links Disable OSC-8 hyperlinks
|
|
109
|
+
--theme NAME Theme (default|dim|bright)
|
|
110
|
+
--list-indent N Spaces per list nesting level (default: 2)
|
|
111
|
+
--quote-prefix STR Prefix for blockquotes (default: "│ ")
|
|
112
|
+
--table-border STR unicode|ascii|none
|
|
113
|
+
--table-padding N Spaces around table cell content
|
|
114
|
+
--table-dense Fewer separator rows
|
|
115
|
+
--table-truncate Default true; pass --table-truncate=false to disable
|
|
116
|
+
--table-ellipsis STR Ellipsis text for truncation
|
|
117
|
+
--code-wrap[=true|false] Wrap code lines (default true)
|
|
118
|
+
--code-box[=true|false] Box code blocks (default true)
|
|
119
|
+
--code-gutter[=true|false] Show code line numbers (default false)
|
|
120
|
+
`);
|
|
121
|
+
process.exit(0);
|
|
122
|
+
}
|
|
123
|
+
const input = args.in && args.in !== "-"
|
|
124
|
+
? fs.readFileSync(path.resolve(args.in), "utf8")
|
|
125
|
+
: fs.readFileSync(0, "utf8");
|
|
126
|
+
const renderOptions = {
|
|
127
|
+
...(args.wrap !== undefined ? { wrap: args.wrap } : {}),
|
|
128
|
+
...(args.width !== undefined ? { width: args.width } : {}),
|
|
129
|
+
...(args.color !== undefined ? { color: args.color } : {}),
|
|
130
|
+
...(args.hyperlinks !== undefined ? { hyperlinks: args.hyperlinks } : {}),
|
|
131
|
+
...(args.theme !== undefined ? { theme: args.theme } : {}),
|
|
132
|
+
...(args.listIndent !== undefined ? { listIndent: args.listIndent } : {}),
|
|
133
|
+
...(args.quotePrefix !== undefined
|
|
134
|
+
? { quotePrefix: args.quotePrefix }
|
|
135
|
+
: {}),
|
|
136
|
+
};
|
|
137
|
+
const output = render(input, renderOptions);
|
|
138
|
+
if (args.out) {
|
|
139
|
+
fs.writeFileSync(path.resolve(args.out), output, "utf8");
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
process.stdout.write(output);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Only run the CLI when executed directly, not when imported for tests.
|
|
146
|
+
const entryHref = process.argv[1]
|
|
147
|
+
? pathToFileURL(process.argv[1]).href
|
|
148
|
+
: undefined;
|
|
149
|
+
if (import.meta.url === entryHref) {
|
|
150
|
+
main();
|
|
151
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WriteStream } from "node:tty";
|
|
2
|
+
/**
|
|
3
|
+
* Detect OSC-8 hyperlink support for a given stream (defaults to stdout).
|
|
4
|
+
*/
|
|
5
|
+
export declare function hyperlinkSupported(stream?: WriteStream): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Build an OSC-8 hyperlink sequence.
|
|
8
|
+
*/
|
|
9
|
+
export declare function osc8(url: string, text: string): string;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import supportsHyperlinks from "supports-hyperlinks";
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* Detect OSC-8 hyperlink support for a given stream (defaults to stdout).
|
|
5
4
|
*/
|
|
6
5
|
export function hyperlinkSupported(stream = process.stdout) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
const helper = supportsHyperlinks;
|
|
7
|
+
if (helper.stdout)
|
|
8
|
+
return helper.stdout(stream);
|
|
9
|
+
return false;
|
|
11
10
|
}
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Build an OSC-8 hyperlink sequence.
|
|
13
|
+
*/
|
|
13
14
|
export function osc8(url, text) {
|
|
14
|
-
|
|
15
|
+
return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`;
|
|
15
16
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,46 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
strike?: boolean;
|
|
10
|
-
};
|
|
11
|
-
export type Theme = {
|
|
12
|
-
heading?: StyleIntent;
|
|
13
|
-
strong?: StyleIntent;
|
|
14
|
-
emph?: StyleIntent;
|
|
15
|
-
inlineCode?: StyleIntent;
|
|
16
|
-
blockCode?: StyleIntent;
|
|
17
|
-
code?: StyleIntent;
|
|
18
|
-
link?: StyleIntent;
|
|
19
|
-
quote?: StyleIntent;
|
|
20
|
-
hr?: StyleIntent;
|
|
21
|
-
listMarker?: StyleIntent;
|
|
22
|
-
tableHeader?: StyleIntent;
|
|
23
|
-
tableCell?: StyleIntent;
|
|
24
|
-
};
|
|
25
|
-
export type ThemeName = "default" | "dim" | "bright";
|
|
26
|
-
export type Highlighter = (code: string, lang?: string) => string;
|
|
27
|
-
export interface RenderOptions {
|
|
28
|
-
wrap?: boolean;
|
|
29
|
-
width?: number;
|
|
30
|
-
hyperlinks?: boolean;
|
|
31
|
-
color?: boolean;
|
|
32
|
-
theme?: ThemeName | Theme;
|
|
33
|
-
/**
|
|
34
|
-
* Spaces per nesting level for lists (default 2).
|
|
35
|
-
*/
|
|
36
|
-
listIndent?: number;
|
|
37
|
-
/**
|
|
38
|
-
* Prefix used for blockquotes (default "│ ").
|
|
39
|
-
*/
|
|
40
|
-
quotePrefix?: string;
|
|
41
|
-
highlighter?: Highlighter;
|
|
42
|
-
}
|
|
43
|
-
export declare function render(markdown: string, options?: RenderOptions): string;
|
|
44
|
-
export declare function createRenderer(options?: RenderOptions): (markdown: string) => string;
|
|
1
|
+
import { createRenderer, render as renderMarkdown } from "./render.js";
|
|
2
|
+
import { themes } from "./theme.js";
|
|
3
|
+
import type { RenderOptions, Theme, ThemeName } from "./types.js";
|
|
4
|
+
export { renderMarkdown as render, createRenderer, themes };
|
|
5
|
+
export type { RenderOptions, Theme, ThemeName };
|
|
6
|
+
/**
|
|
7
|
+
* Render Markdown to plain text (no ANSI, no hyperlinks) while preserving layout/wrapping.
|
|
8
|
+
*/
|
|
45
9
|
export declare function strip(markdown: string, options?: RenderOptions): string;
|
|
46
|
-
export declare const themes: Record<ThemeName | string, Theme>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createRenderer, render as renderMarkdown } from "./render.js";
|
|
2
|
+
import { themes } from "./theme.js";
|
|
3
|
+
export { renderMarkdown as render, createRenderer, themes };
|
|
4
|
+
/**
|
|
5
|
+
* Render Markdown to plain text (no ANSI, no hyperlinks) while preserving layout/wrapping.
|
|
6
|
+
*/
|
|
7
|
+
export function strip(markdown, options = {}) {
|
|
8
|
+
return renderMarkdown(markdown, {
|
|
9
|
+
...options,
|
|
10
|
+
color: false,
|
|
11
|
+
hyperlinks: false,
|
|
12
|
+
// ensure flags like codeWrap/tableTruncate not lost
|
|
13
|
+
wrap: options.wrap ?? true,
|
|
14
|
+
});
|
|
15
|
+
}
|
package/dist/parser.d.ts
ADDED
package/{src → dist}/parser.js
RENAMED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
2
2
|
import { gfmFromMarkdown } from "mdast-util-gfm";
|
|
3
3
|
import { gfm as gfmSyntax } from "micromark-extension-gfm";
|
|
4
|
-
|
|
5
4
|
export function parse(markdown) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
return fromMarkdown(markdown, {
|
|
6
|
+
extensions: [gfmSyntax()],
|
|
7
|
+
mdastExtensions: [gfmFromMarkdown()],
|
|
8
|
+
});
|
|
10
9
|
}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RenderOptions } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Render Markdown input to an ANSI string.
|
|
4
|
+
*/
|
|
5
|
+
export declare function render(markdown: string, userOptions?: RenderOptions): string;
|
|
6
|
+
/**
|
|
7
|
+
* Create a reusable renderer with fixed options.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createRenderer(options?: RenderOptions): (md: string) => string;
|