md4x 0.0.1 → 0.0.3

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/lib/cli.mjs +94 -15
  2. package/package.json +3 -2
package/lib/cli.mjs CHANGED
@@ -22,22 +22,44 @@ const { values, positionals } = parseArgs({
22
22
  },
23
23
  });
24
24
 
25
+ const _tty = process.stderr.isTTY;
26
+ const c = (code) => (s) => _tty ? `\x1b[${code}m${s}\x1b[0m` : s;
27
+ const _b = c(1);
28
+ const _d = c(2);
29
+ const _c = c(36);
30
+ const _g = c(32);
31
+
25
32
  function usage() {
26
33
  process.stderr.write(
27
- `Usage: md4x [OPTION]... [FILE]
28
- Convert input FILE (or standard input) in Markdown format.
29
-
30
- General options:
31
- -o --output=FILE Output file (default is standard output)
32
- -t, --format=FORMAT Output format: html, json, ansi (default: ansi for TTY, html otherwise)
33
- -s, --stat Measure time of input parsing
34
- -h, --help Display this help and exit
35
- -v, --version Display version and exit
36
-
37
- HTML output options:
38
- -f, --full-html Generate full HTML document, including header
39
- --html-title=TITLE Sets the title of the document
40
- --html-css=URL In full HTML mode add a css link
34
+ `${_b("md4x")} Markdown renderer
35
+
36
+ ${_g("Usage:")} ${_b("md4x")} ${_d("[OPTION]... [FILE]")}
37
+
38
+ ${_g("General options:")}
39
+ ${_c("-o")}, ${_c("--output")}=${_d("FILE")} Output file ${_d("(default: stdout)")}
40
+ ${_c("-t")}, ${_c("--format")}=${_d("FORMAT")} Output format: html, json, ansi ${_d("(default: ansi for TTY, html otherwise)")}
41
+ ${_c("-s")}, ${_c("--stat")} Measure parsing time
42
+ ${_c("-h")}, ${_c("--help")} Display this help and exit
43
+ ${_c("-v")}, ${_c("--version")} Display version and exit
44
+
45
+ ${_g("Input:")}
46
+ File path, ${_c("-")} for stdin, HTTP/HTTPS URL, or shorthand:
47
+ ${_c("gh:")}${_d("<owner>/<repo>[/path]")} GitHub raw content
48
+ ${_c("npm:")}${_d("<package>[@version][/path]")} npm package via unpkg
49
+
50
+ ${_g("HTML options:")}
51
+ ${_c("-f")}, ${_c("--full-html")} Full HTML document with header
52
+ ${_c("--html-title")}=${_d("TITLE")} Document title
53
+ ${_c("--html-css")}=${_d("URL")} CSS link
54
+
55
+ ${_g("Examples:")}
56
+ ${_d("$")} ${_b("md4x")} README.md ${_d("# Render to terminal")}
57
+ ${_d("$")} ${_b("md4x")} ${_c("-t html")} doc.md ${_d("# HTML output")}
58
+ ${_d("$")} ${_b("md4x")} ${_c("-t json")} doc.md ${_d("# JSON AST output")}
59
+ ${_d("$")} ${_b("md4x")} ${_c("gh:")}pi0/md4x ${_d("# GitHub repo README")}
60
+ ${_d("$")} ${_b("md4x")} ${_c("npm:")}vue@3 ${_d("# npm package README")}
61
+ ${_d("$")} echo "# Hello" | ${_b("md4x")} ${_d("# Pipe from stdin")}
62
+ ${_d("$")} ${_b("md4x")} ${_c("-f --html-css")}=style.css doc.md ${_d("# Full HTML with CSS")}
41
63
  `,
42
64
  );
43
65
  }
@@ -62,7 +84,23 @@ if (!["html", "json", "ansi"].includes(format)) {
62
84
  process.exit(1);
63
85
  }
64
86
 
65
- const inputPath = positionals[0];
87
+ let inputPath = positionals[0];
88
+ // gh:owner/repo[/path] → https://github.com/owner/repo[/path]
89
+ if (inputPath && /^gh:/i.test(inputPath)) {
90
+ inputPath = `https://github.com/${inputPath.slice(3)}`;
91
+ }
92
+ // npm:package[@version][/path] → https://unpkg.com/package[@version][/path]
93
+ if (inputPath && /^npm:/i.test(inputPath)) {
94
+ const spec = inputPath.slice(4);
95
+ // Check if spec includes a file path (after the package name + optional version)
96
+ // Scoped: @scope/pkg[@ver][/path] — path starts after 2nd slash
97
+ // Unscoped: pkg[@ver][/path] — path starts after 1st slash
98
+ const slashIdx = spec.startsWith("@")
99
+ ? spec.indexOf("/", spec.indexOf("/") + 1)
100
+ : spec.indexOf("/");
101
+ const hasPath = slashIdx > 0;
102
+ inputPath = `https://unpkg.com/${spec}${hasPath ? "" : "/README.md"}`;
103
+ }
66
104
  let input;
67
105
  if (!inputPath || inputPath === "-") {
68
106
  if (process.stdin.isTTY) {
@@ -75,6 +113,23 @@ if (!inputPath || inputPath === "-") {
75
113
  usage();
76
114
  process.exit(1);
77
115
  }
116
+ } else if (/^https?:\/\//i.test(inputPath)) {
117
+ const fetchUrl = toRawUrl(inputPath);
118
+ try {
119
+ const res = await fetch(fetchUrl, {
120
+ headers: { Accept: "text/markdown, text/plain;q=0.9, */*;q=0.1" },
121
+ });
122
+ if (!res.ok) {
123
+ process.stderr.write(
124
+ `Failed to fetch ${inputPath}: ${res.status} ${res.statusText}\n`,
125
+ );
126
+ process.exit(1);
127
+ }
128
+ input = await res.text();
129
+ } catch (err) {
130
+ process.stderr.write(`Failed to fetch ${inputPath}: ${err.message}\n`);
131
+ process.exit(1);
132
+ }
78
133
  } else {
79
134
  try {
80
135
  input = readFileSync(inputPath, "utf8");
@@ -136,3 +191,27 @@ if (values.output && values.output !== "-") {
136
191
  } else {
137
192
  process.stdout.write(result);
138
193
  }
194
+
195
+ // --- internal helpers ---
196
+
197
+ /** Convert GitHub web URLs to raw content URLs */
198
+ function toRawUrl(url) {
199
+ const u = new URL(url);
200
+ if (u.hostname === "github.com" || u.hostname === "www.github.com") {
201
+ // github.com/:owner/:repo/blob/:ref/path → raw.githubusercontent.com/:owner/:repo/:ref/path
202
+ const blob = u.pathname.match(/^\/([^/]+\/[^/]+)\/blob\/(.+)/);
203
+ if (blob) {
204
+ return `https://raw.githubusercontent.com/${blob[1]}/${blob[2]}`;
205
+ }
206
+ // github.com/:owner/:repo → raw.githubusercontent.com/:owner/:repo/HEAD/README.md
207
+ const repo = u.pathname.match(/^\/([^/]+\/[^/]+)\/?$/);
208
+ if (repo) {
209
+ return `https://raw.githubusercontent.com/${repo[1]}/HEAD/README.md`;
210
+ }
211
+ }
212
+ // gist.github.com/:user/:id → gist.githubusercontent.com/:user/:id/raw
213
+ if (u.hostname === "gist.github.com") {
214
+ return `https://gist.githubusercontent.com${u.pathname}/raw`;
215
+ }
216
+ return url;
217
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md4x",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,8 @@
23
23
  },
24
24
  "files": [
25
25
  "build",
26
- "lib"
26
+ "lib",
27
+ "README.md"
27
28
  ],
28
29
  "devDependencies": {
29
30
  "@milkdown/crepe": "^7.18.0",