md4x 0.0.9 → 0.0.10

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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/build/md4x.wasm CHANGED
Binary file
Binary file
Binary file
package/lib/cli.mjs CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  import { parseArgs } from "node:util";
4
4
  import { readFileSync, writeFileSync } from "node:fs";
5
- import { renderToHtml, renderToAnsi, renderToAST } from "./napi.mjs";
5
+ import {
6
+ renderToHtml,
7
+ renderToAnsi,
8
+ renderToText,
9
+ parseAST,
10
+ parseMeta,
11
+ } from "./napi.mjs";
6
12
 
7
13
  const { values, positionals } = parseArgs({
8
14
  allowPositionals: true,
@@ -11,7 +17,7 @@ const { values, positionals } = parseArgs({
11
17
  format: {
12
18
  type: "string",
13
19
  short: "t",
14
- default: process.stdout.isTTY ? "ansi" : "html",
20
+ default: process.stdout.isTTY ? "ansi" : "text",
15
21
  },
16
22
  "full-html": { type: "boolean", short: "f", default: false },
17
23
  "html-title": { type: "string" },
@@ -37,7 +43,7 @@ ${_g("Usage:")} ${_b("md4x")} ${_d("[OPTION]... [FILE]")}
37
43
 
38
44
  ${_g("General options:")}
39
45
  ${_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)")}
46
+ ${_c("-t")}, ${_c("--format")}=${_d("FORMAT")} Output format: ${_c("html")}, ${_c("text")}, ${_c("ast")}, ${_c("ansi")}, ${_c("meta")} ${_d("(default: ansi for TTY, text otherwise)")}
41
47
  ${_c("-s")}, ${_c("--stat")} Measure parsing time
42
48
  ${_c("-h")}, ${_c("--help")} Display this help and exit
43
49
  ${_c("-v")}, ${_c("--version")} Display version and exit
@@ -53,13 +59,19 @@ ${_g("HTML options:")}
53
59
  ${_c("--html-css")}=${_d("URL")} CSS link
54
60
 
55
61
  ${_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")}
62
+ ${_d("$")} ${_b("md4x")} README.md ${_d("# ANSI output")}
63
+ ${_d("$")} ${_b("md4x")} README.md ${_c("-t html")} ${_d("# HTML output")}
64
+ ${_d("$")} ${_b("md4x")} README.md ${_c("-t text")} ${_d("# Plain text output (strip markdown)")}
65
+ ${_d("$")} ${_b("md4x")} README.md ${_c("-t ast")} ${_d("# JSON AST output (comark)")}
66
+ ${_d("$")} ${_b("md4x")} README.md ${_c("-t meta")} ${_d("# Metadata JSON output")}
67
+ ${_d("$")} ${_b("md4x")} ${_c("https://nitro.build/guide")} ${_d("# Fetch and render any URL")}
68
+ ${_d("$")} ${_b("md4x")} ${_c("gh:")}nitrojs/nitro ${_d("# GitHub repo README.md")}
69
+ ${_d("$")} ${_b("md4x")} ${_c("npm:")}vue@3 ${_d("# npm package at specific version")}
70
+ ${_d("$")} echo "# Hello" | ${_b("md4x")} ${_c("-t text")}
71
+ ${_d("$")} cat README.md | ${_b("md4x")} ${_c("-t html")}
72
+ ${_d("$")} ${_b("md4x")} README.md ${_c("-t meta -o")} README.json ${_d("# Write to file")}
73
+ ${_d("$")} ${_b("md4x")} README.md ${_c("-t html -f --html-title")}="My Docs" ${_d("# Full HTML with <head>")}
74
+ ${_d("$")} ${_b("md4x")} README.md ${_c("-t html -f --html-css")}=style.css ${_d("# Add CSS link")}
63
75
  `,
64
76
  );
65
77
  }
@@ -77,10 +89,11 @@ if (values.version) {
77
89
  process.exit(0);
78
90
  }
79
91
 
92
+ const supportedFormats = ["html", "ast", "ansi", "text", "meta"];
80
93
  const format = values.format;
81
- if (!["html", "json", "ansi"].includes(format)) {
94
+ if (!supportedFormats.includes(format)) {
82
95
  process.stderr.write(`Unknown format: ${format}\n`);
83
- process.stderr.write("Supported formats: html, json, ansi\n");
96
+ process.stderr.write(`Supported formats: ${supportedFormats.join(", ")}\n`);
84
97
  process.exit(1);
85
98
  }
86
99
 
@@ -146,12 +159,18 @@ switch (format) {
146
159
  case "html":
147
160
  output = renderToHtml(input);
148
161
  break;
149
- case "json":
150
- output = JSON.stringify(renderToAST(input));
151
- break;
152
162
  case "ansi":
153
163
  output = renderToAnsi(input);
154
164
  break;
165
+ case "text":
166
+ output = renderToText(input);
167
+ break;
168
+ case "ast":
169
+ output = JSON.stringify(parseAST(input), null, 2);
170
+ break;
171
+ case "meta":
172
+ output = JSON.stringify(parseMeta(input), null, 2);
173
+ break;
155
174
  }
156
175
 
157
176
  if (values.stat) {
package/lib/napi.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ComarkTree } from "./types.js";
1
+ import type { ComarkTree, ComarkMeta, HtmlOptions } from "./types.mjs";
2
2
 
3
3
  export type {
4
4
  ComarkTree,
@@ -6,9 +6,28 @@ export type {
6
6
  ComarkElement,
7
7
  ComarkText,
8
8
  ComarkElementAttributes,
9
- } from "./types.js";
9
+ ComarkHeading,
10
+ ComarkMeta,
11
+ HtmlOptions,
12
+ } from "./types.mjs";
10
13
 
11
- export declare function renderToHtml(input: string): string;
14
+ export interface NAPIBinding {
15
+ renderToHtml(input: string, flags?: number): string;
16
+ renderToAST(input: string): string;
17
+ renderToAnsi(input: string): string;
18
+ renderToMeta(input: string): string;
19
+ renderToText(input: string): string;
20
+ }
21
+
22
+ export interface InitOptions {
23
+ binding?: NAPIBinding;
24
+ }
25
+
26
+ export declare function init(opts?: InitOptions): Promise<void>;
27
+ export declare function renderToHtml(input: string, opts?: HtmlOptions): string;
12
28
  export declare function renderToAST(input: string): string;
13
29
  export declare function parseAST(input: string): ComarkTree;
14
30
  export declare function renderToAnsi(input: string): string;
31
+ export declare function renderToMeta(input: string): string;
32
+ export declare function parseMeta(input: string): ComarkMeta;
33
+ export declare function renderToText(input: string): string;
package/lib/napi.mjs CHANGED
@@ -1,31 +1,40 @@
1
- import { createRequire } from "node:module";
2
- import { arch, platform } from "node:process";
1
+ // --- internal ---
3
2
 
4
- const require = createRequire(import.meta.url);
3
+ let binding;
5
4
 
6
- function isMusl() {
7
- if (platform !== "linux") return false;
8
- try {
9
- // glibc sets glibcVersionRuntime; musl does not
10
- return !process.report?.getReport?.()?.header?.glibcVersionRuntime;
11
- } catch {
12
- return false;
5
+ function getBinding(opts) {
6
+ if (binding) return binding;
7
+ if (opts?.binding) {
8
+ return (binding = opts.binding);
13
9
  }
10
+ const { arch, platform } = globalThis.process || {};
11
+ const { createRequire } = globalThis.process?.getBuiltinModule?.("module");
12
+ const isMusl =
13
+ platform === "linux" &&
14
+ !process.report?.getReport?.()?.header?.glibcVersionRuntime;
15
+ return (binding = createRequire(import.meta.url)(
16
+ `../build/md4x.${platform}-${arch}${isMusl ? "-musl" : ""}.node`,
17
+ ));
14
18
  }
15
19
 
16
- function loadBinding() {
17
- const suffix = isMusl() ? "-musl" : "";
18
- return require(`../build/md4x.${platform}-${arch}${suffix}.node`);
19
- }
20
+ // --- exports ----
20
21
 
21
- const binding = loadBinding();
22
+ export function init(opts) {
23
+ try {
24
+ getBinding(opts);
25
+ return Promise.resolve(binding);
26
+ } catch (err) {
27
+ return Promise.reject(err);
28
+ }
29
+ }
22
30
 
23
- export function renderToHtml(input) {
24
- return binding.renderToHtml(input);
31
+ export function renderToHtml(input, opts) {
32
+ const flags = opts?.full ? 0x0008 : 0;
33
+ return getBinding().renderToHtml(input, flags);
25
34
  }
26
35
 
27
36
  export function renderToAST(input) {
28
- return binding.renderToAST(input);
37
+ return getBinding().renderToAST(input);
29
38
  }
30
39
 
31
40
  export function parseAST(input) {
@@ -33,5 +42,21 @@ export function parseAST(input) {
33
42
  }
34
43
 
35
44
  export function renderToAnsi(input) {
36
- return binding.renderToAnsi(input);
45
+ return getBinding().renderToAnsi(input);
46
+ }
47
+
48
+ export function renderToMeta(input) {
49
+ return getBinding().renderToMeta(input);
50
+ }
51
+
52
+ export function renderToText(input) {
53
+ return getBinding().renderToText(input);
54
+ }
55
+
56
+ export function parseMeta(input) {
57
+ const meta = JSON.parse(renderToMeta(input));
58
+ if (!meta.title && meta.headings?.[0]) {
59
+ meta.title = meta.headings[0].text;
60
+ }
61
+ return meta;
37
62
  }
@@ -16,3 +16,19 @@ export type ComarkElement = [
16
16
  export type ComarkElementAttributes = {
17
17
  [key: string]: unknown;
18
18
  };
19
+
20
+ export type ComarkHeading = {
21
+ level: number;
22
+ text: string;
23
+ };
24
+
25
+ export type ComarkMeta = {
26
+ title?: string;
27
+ headings: ComarkHeading[];
28
+ [key: string]: unknown;
29
+ };
30
+
31
+ export interface HtmlOptions {
32
+ /** Generate a full HTML document with `<!DOCTYPE html>`, `<head>`, and `<body>`. */
33
+ full?: boolean;
34
+ }
package/lib/wasm.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ComarkTree } from "./types.js";
1
+ import type { ComarkTree, ComarkMeta, HtmlOptions } from "./types.mjs";
2
2
 
3
3
  export type {
4
4
  ComarkTree,
@@ -6,17 +6,25 @@ export type {
6
6
  ComarkElement,
7
7
  ComarkText,
8
8
  ComarkElementAttributes,
9
- } from "./types.js";
9
+ ComarkHeading,
10
+ ComarkMeta,
11
+ HtmlOptions,
12
+ } from "./types.mjs";
10
13
 
11
- export declare function initWasm(
12
- input?:
14
+ export interface InitOptions {
15
+ wasm?:
13
16
  | ArrayBuffer
14
17
  | Uint8Array
15
18
  | WebAssembly.Module
16
19
  | Response
17
- | Promise<Response>,
18
- ): Promise<void>;
19
- export declare function renderToHtml(input: string): string;
20
+ | Promise<Response>;
21
+ }
22
+
23
+ export declare function init(opts?: InitOptions): Promise<void>;
24
+ export declare function renderToHtml(input: string, opts?: HtmlOptions): string;
20
25
  export declare function renderToAST(input: string): string;
21
26
  export declare function parseAST(input: string): ComarkTree;
22
27
  export declare function renderToAnsi(input: string): string;
28
+ export declare function renderToMeta(input: string): string;
29
+ export declare function parseMeta(input: string): ComarkMeta;
30
+ export declare function renderToText(input: string): string;
package/lib/wasm.mjs CHANGED
@@ -1,16 +1,46 @@
1
+ // --- internal ---
2
+
1
3
  let _instance;
2
4
 
3
5
  function getExports() {
4
6
  if (!_instance) {
5
- throw new Error(
6
- "md4x: WASM not initialized. Call `await initWasm()` first.",
7
- );
7
+ throw new Error("md4x: WASM not initialized. Call `await init()` first.");
8
8
  }
9
9
  return _instance.exports;
10
10
  }
11
11
 
12
- export async function initWasm(input) {
12
+ const wasiStub = {
13
+ fd_close: () => 0,
14
+ fd_seek: () => 0,
15
+ fd_write: () => 0,
16
+ proc_exit: () => {},
17
+ };
18
+
19
+ function render(exports, fn, input, ...extra) {
20
+ const { memory, md4x_alloc, md4x_free, md4x_result_ptr, md4x_result_size } =
21
+ exports;
22
+ const encoded = new TextEncoder().encode(input);
23
+ const ptr = md4x_alloc(encoded.length);
24
+ new Uint8Array(memory.buffer).set(encoded, ptr);
25
+ const ret = fn(ptr, encoded.length, ...extra);
26
+ md4x_free(ptr);
27
+ if (ret !== 0) {
28
+ throw new Error("md4x: render failed");
29
+ }
30
+ const outPtr = md4x_result_ptr();
31
+ const outSize = md4x_result_size();
32
+ const result = new TextDecoder().decode(
33
+ new Uint8Array(memory.buffer, outPtr, outSize),
34
+ );
35
+ md4x_free(outPtr);
36
+ return result;
37
+ }
38
+
39
+ // --- exports ----
40
+
41
+ export async function init(opts) {
13
42
  if (_instance) return;
43
+ const input = opts?.wasm;
14
44
  let bytes;
15
45
  if (input instanceof ArrayBuffer || input instanceof Uint8Array) {
16
46
  bytes = input;
@@ -47,8 +77,9 @@ export async function initWasm(input) {
47
77
  _instance = instance;
48
78
  }
49
79
 
50
- export function renderToHtml(input) {
51
- return render(getExports(), getExports().md4x_to_html, input);
80
+ export function renderToHtml(input, opts) {
81
+ const flags = opts?.full ? 0x0008 : 0;
82
+ return render(getExports(), getExports().md4x_to_html, input, flags);
52
83
  }
53
84
 
54
85
  export function renderToAST(input) {
@@ -63,31 +94,18 @@ export function renderToAnsi(input) {
63
94
  return render(getExports(), getExports().md4x_to_ansi, input);
64
95
  }
65
96
 
66
- // --- internals ---
97
+ export function renderToMeta(input) {
98
+ return render(getExports(), getExports().md4x_to_meta, input);
99
+ }
67
100
 
68
- const wasiStub = {
69
- fd_close: () => 0,
70
- fd_seek: () => 0,
71
- fd_write: () => 0,
72
- proc_exit: () => {},
73
- };
101
+ export function renderToText(input) {
102
+ return render(getExports(), getExports().md4x_to_text, input);
103
+ }
74
104
 
75
- function render(exports, fn, input) {
76
- const { memory, md4x_alloc, md4x_free, md4x_result_ptr, md4x_result_size } =
77
- exports;
78
- const encoded = new TextEncoder().encode(input);
79
- const ptr = md4x_alloc(encoded.length);
80
- new Uint8Array(memory.buffer).set(encoded, ptr);
81
- const ret = fn(ptr, encoded.length);
82
- md4x_free(ptr);
83
- if (ret !== 0) {
84
- throw new Error("md4x: render failed");
105
+ export function parseMeta(input) {
106
+ const meta = JSON.parse(renderToMeta(input));
107
+ if (!meta.title && meta.headings?.[0]) {
108
+ meta.title = meta.headings[0].text;
85
109
  }
86
- const outPtr = md4x_result_ptr();
87
- const outSize = md4x_result_size();
88
- const result = new TextDecoder().decode(
89
- new Uint8Array(memory.buffer, outPtr, outSize),
90
- );
91
- md4x_free(outPtr);
92
- return result;
110
+ return meta;
93
111
  }
package/package.json CHANGED
@@ -1,35 +1,29 @@
1
1
  {
2
2
  "name": "md4x",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
+ "repository": "pi0/md4x",
4
5
  "license": "MIT",
5
6
  "type": "module",
6
- "repository": "pi0/md4x",
7
- "bin": {
8
- "md4x": "./lib/cli.mjs"
9
- },
10
7
  "exports": {
8
+ "./wasm": "./lib/wasm.mjs",
9
+ "./napi": "./lib/napi.mjs",
11
10
  ".": {
12
- "types": "./lib/napi.d.mts",
13
11
  "node": "./lib/napi.mjs",
14
12
  "default": "./lib/wasm.mjs"
15
- },
16
- "./wasm": {
17
- "types": "./lib/wasm.d.mts",
18
- "default": "./lib/wasm.mjs"
19
- },
20
- "./napi": {
21
- "types": "./lib/napi.d.mts",
22
- "default": "./lib/napi.mjs"
23
13
  }
24
14
  },
25
- "scripts": {
26
- "prepack": "cd ../.. && zig build --verbose"
15
+ "types": "./lib/types.d.mts",
16
+ "bin": {
17
+ "md4x": "./lib/cli.mjs"
27
18
  },
28
19
  "files": [
29
20
  "build",
30
21
  "lib",
31
22
  "README.md"
32
23
  ],
24
+ "scripts": {
25
+ "prepack": "cd ../.. && zig build --verbose"
26
+ },
33
27
  "devDependencies": {
34
28
  "markdown-it": "^14.1.1",
35
29
  "markdown-it-mdc": "^0.2.12",