markdansi 0.3.1 → 0.3.2

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.
Files changed (2) hide show
  1. package/dist/cli.js +71 -44
  2. package/package.json +8 -8
package/dist/cli.js CHANGED
@@ -3,6 +3,31 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { render } from "./index.js";
6
+ function readOptionValue(argv, index, option, allowLeadingDashes = false) {
7
+ const value = argv[index + 1];
8
+ if (value === undefined || (!allowLeadingDashes && value.startsWith("--"))) {
9
+ throw new Error(`${option} requires a value`);
10
+ }
11
+ return value;
12
+ }
13
+ function parseIntegerOption(option, value, minimum) {
14
+ const parsed = Number(value);
15
+ if (!/^\d+$/u.test(value) || !Number.isSafeInteger(parsed) || parsed < minimum) {
16
+ const range = minimum === 0 ? "a non-negative integer" : "a positive integer";
17
+ throw new Error(`${option} must be ${range}`);
18
+ }
19
+ return parsed;
20
+ }
21
+ function parseTheme(value) {
22
+ if (value === "default" || value === "dim" || value === "bright")
23
+ return value;
24
+ throw new Error("--theme must be default, dim, or bright");
25
+ }
26
+ function parseTableBorder(value) {
27
+ if (value === "unicode" || value === "ascii" || value === "none")
28
+ return value;
29
+ throw new Error("--table-border must be unicode, ascii, or none");
30
+ }
6
31
  /**
7
32
  * Ignore EPIPE when downstream (e.g., `head`) closes early.
8
33
  */
@@ -32,80 +57,85 @@ export function parseArgs(argv) {
32
57
  args.color = false;
33
58
  else if (a === "--no-links")
34
59
  args.hyperlinks = false;
60
+ else if (a === "--code-wrap")
61
+ args.codeWrap = true;
35
62
  else if (a === "--code-wrap=false")
36
63
  args.codeWrap = false;
37
64
  else if (a === "--code-wrap=true")
38
65
  args.codeWrap = true;
66
+ else if (a === "--code-box")
67
+ args.codeBox = true;
39
68
  else if (a === "--code-box=false")
40
69
  args.codeBox = false;
41
70
  else if (a === "--code-box=true")
42
71
  args.codeBox = true;
72
+ else if (a === "--code-gutter")
73
+ args.codeGutter = true;
43
74
  else if (a === "--code-gutter=true")
44
75
  args.codeGutter = true;
45
76
  else if (a === "--code-gutter=false")
46
77
  args.codeGutter = false;
47
78
  else if (a.startsWith("--table-border=")) {
48
- const val = a.split("=")[1];
49
- if (val === "unicode" || val === "ascii" || val === "none")
50
- args.tableBorder = val;
79
+ args.tableBorder = parseTableBorder(a.slice("--table-border=".length));
80
+ }
81
+ else if (a === "--table-border") {
82
+ args.tableBorder = parseTableBorder(readOptionValue(argv, i, a));
83
+ i += 1;
51
84
  }
52
85
  else if (a === "--table-dense")
53
86
  args.tableDense = true;
87
+ else if (a === "--table-truncate")
88
+ args.tableTruncate = true;
54
89
  else if (a === "--table-truncate=false")
55
90
  args.tableTruncate = false;
56
91
  else if (a === "--table-truncate=true")
57
92
  args.tableTruncate = true;
58
93
  else if (a === "--table-padding") {
59
- const next = argv[i + 1];
60
- if (next)
61
- args.tablePadding = Number(next);
94
+ const next = readOptionValue(argv, i, a);
95
+ args.tablePadding = parseIntegerOption(a, next, 0);
62
96
  i += 1;
63
97
  }
64
98
  else if (a === "--table-ellipsis") {
65
- const next = argv[i + 1];
66
- if (next)
67
- args.tableEllipsis = next;
99
+ args.tableEllipsis = readOptionValue(argv, i, a, true);
68
100
  i += 1;
69
101
  }
70
102
  else if (a === "--in") {
71
- const next = argv[i + 1];
72
- if (next)
73
- args.in = next;
103
+ args.in = readOptionValue(argv, i, a, true);
74
104
  i += 1;
75
105
  }
76
106
  else if (a === "--out") {
77
- const next = argv[i + 1];
78
- if (next)
79
- args.out = next;
107
+ args.out = readOptionValue(argv, i, a, true);
80
108
  i += 1;
81
109
  }
82
110
  else if (a === "--width") {
83
- const next = argv[i + 1];
84
- if (next)
85
- args.width = Number(next);
111
+ const next = readOptionValue(argv, i, a);
112
+ args.width = parseIntegerOption(a, next, 1);
86
113
  i += 1;
87
114
  }
88
115
  else if (a.startsWith("--theme=")) {
89
- const themeVal = a.split("=")[1];
90
- if (themeVal)
91
- args.theme = themeVal;
116
+ args.theme = parseTheme(a.slice("--theme=".length));
117
+ }
118
+ else if (a === "--theme") {
119
+ args.theme = parseTheme(readOptionValue(argv, i, a));
120
+ i += 1;
92
121
  }
93
122
  else if (a === "--list-indent") {
94
- const next = argv[i + 1];
95
- if (next)
96
- args.listIndent = Number(next);
123
+ const next = readOptionValue(argv, i, a);
124
+ args.listIndent = parseIntegerOption(a, next, 0);
97
125
  i += 1;
98
126
  }
99
127
  else if (a === "--quote-prefix") {
100
- const next = argv[i + 1];
101
- if (next)
102
- args.quotePrefix = next;
128
+ args.quotePrefix = readOptionValue(argv, i, a, true);
103
129
  i += 1;
104
130
  }
105
131
  else if (a === "--help" || a === "-h")
106
132
  args.help = true;
107
- else if (!a.startsWith("-") && !args.in)
133
+ else if (a.startsWith("-"))
134
+ throw new Error(`unknown option: ${a}`);
135
+ else if (!args.in)
108
136
  args.in = a;
137
+ else
138
+ throw new Error(`unexpected positional argument: ${a}`);
109
139
  }
110
140
  return args;
111
141
  }
@@ -114,8 +144,8 @@ export function parseArgs(argv) {
114
144
  */
115
145
  function main() {
116
146
  handleStdoutEpipe();
117
- const args = parseArgs(process.argv);
118
- if (args.help) {
147
+ const { in: inputPath, out: outputPath, help, ...renderOptions } = parseArgs(process.argv);
148
+ if (help) {
119
149
  process.stdout.write(`markdansi [FILE] [options]
120
150
 
121
151
  markdansi file.md Render file
@@ -143,21 +173,12 @@ Options:
143
173
  `);
144
174
  process.exit(0);
145
175
  }
146
- const input = args.in && args.in !== "-"
147
- ? fs.readFileSync(path.resolve(args.in), "utf8")
176
+ const input = inputPath && inputPath !== "-"
177
+ ? fs.readFileSync(path.resolve(inputPath), "utf8")
148
178
  : fs.readFileSync(0, "utf8");
149
- const renderOptions = {
150
- ...(args.wrap !== undefined ? { wrap: args.wrap } : {}),
151
- ...(args.width !== undefined ? { width: args.width } : {}),
152
- ...(args.color !== undefined ? { color: args.color } : {}),
153
- ...(args.hyperlinks !== undefined ? { hyperlinks: args.hyperlinks } : {}),
154
- ...(args.theme !== undefined ? { theme: args.theme } : {}),
155
- ...(args.listIndent !== undefined ? { listIndent: args.listIndent } : {}),
156
- ...(args.quotePrefix !== undefined ? { quotePrefix: args.quotePrefix } : {}),
157
- };
158
179
  const output = render(input, renderOptions);
159
- if (args.out) {
160
- fs.writeFileSync(path.resolve(args.out), output, "utf8");
180
+ if (outputPath) {
181
+ fs.writeFileSync(path.resolve(outputPath), output, "utf8");
161
182
  }
162
183
  else {
163
184
  process.stdout.write(output);
@@ -177,5 +198,11 @@ export function isDirectCliInvocation(metaUrl, argv1) {
177
198
  }
178
199
  // Only run the CLI when executed directly, not when imported for tests.
179
200
  if (isDirectCliInvocation(import.meta.url, process.argv[1])) {
180
- main();
201
+ try {
202
+ main();
203
+ }
204
+ catch (error) {
205
+ console.error(`markdansi: ${error instanceof Error ? error.message : String(error)}`);
206
+ process.exitCode = 1;
207
+ }
181
208
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdansi",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Tiny dependency-light markdown to ANSI converter.",
5
5
  "keywords": [
6
6
  "ansi",
@@ -61,16 +61,16 @@
61
61
  "slice-ansi": "^9.0.0",
62
62
  "string-width": "^8.2.1",
63
63
  "strip-ansi": "^7.2.0",
64
- "supports-hyperlinks": "^4.4.0"
64
+ "supports-hyperlinks": "^4.5.0"
65
65
  },
66
66
  "devDependencies": {
67
- "@types/node": "^25.6.0",
68
- "@vitest/coverage-v8": "^4.1.5",
69
- "oxfmt": "^0.47.0",
70
- "oxlint": "^1.62.0",
71
- "tsx": "^4.21.0",
67
+ "@types/node": "^26.0.1",
68
+ "@vitest/coverage-v8": "^4.1.9",
69
+ "oxfmt": "^0.57.0",
70
+ "oxlint": "^1.72.0",
71
+ "tsx": "^4.22.4",
72
72
  "typescript": "^6.0.3",
73
- "vitest": "^4.1.5"
73
+ "vitest": "^4.1.9"
74
74
  },
75
75
  "engines": {
76
76
  "node": ">=22"