Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Usage — Node.js

Installation

npm install mdka

Basic Conversion

const { htmlToMarkdown } = require('mdka')

const html = `
  <h1>Hello</h1>
  <p>mdka converts <strong>HTML</strong> to <em>Markdown</em>.</p>
`
const md = htmlToMarkdown(html)
console.log(md)
// # Hello
//
// mdka converts **HTML** to *Markdown*.

Async Conversion

htmlToMarkdownAsync offloads work to a Rust thread pool, keeping the Node.js event loop free:

const { htmlToMarkdownAsync } = require('mdka')

const html = '<h1>Hello</h1>'
const pages = [
  { html: '<h1>A</h1>' },
  { html: '<p>B</p>' },
]

async function main() {
  const md = await htmlToMarkdownAsync(html)

  // Concurrent conversion of many pages
  const results = await Promise.all(pages.map(p => htmlToMarkdownAsync(p.html)))
  console.log(md, results)
}
main()

Conversion with Options

Every Node.js function takes its options as an optional last argument — there is no separate …With function (they were removed in 3.0):

const { htmlToMarkdown, htmlToMarkdownAsync } = require('mdka')

const html = '<nav>menu</nav><h1>Title</h1><p>Body</p>'

// Strip nav/header/footer — useful for content extraction
const md = htmlToMarkdown(html, {
  mode: 'minimal',
  dropInteractiveShell: true,
})

// Async version
async function main() {
  const mdAsync = await htmlToMarkdownAsync(html, { mode: 'balanced' })
  console.log(mdAsync)
}
main()

Available mode strings: "balanced" (default) and "minimal". "strict", "semantic" and "preserve" were aliases of "balanced" and were removed in 3.0: passing one throws (or, from htmlToMarkdownAsync and the file functions, rejects) with conversion mode 'strict' was removed in 3.0; it was an alias of 'balanced'. Use 'balanced'. — see Conversion Modes.

Converting Multiple Strings

const { htmlToMarkdownMany } = require('mdka')

const pages = ['<h1>A</h1>', '<p>B</p>', '<ul><li>C</li></ul>']
const results = htmlToMarkdownMany(pages)
// ['# A\n', 'B\n', '- C\n']

// With options — the same shape as htmlToMarkdown's
const withOpts = htmlToMarkdownMany(pages, { mode: 'minimal' })

Each input is converted independently and the results keep the input order.

Single File Conversion

const { htmlFileToMarkdown } = require('mdka')

async function main() {
  // Output to same directory: page.html → page.md
  // Resolves to the path that was written — a string.
  const dest = await htmlFileToMarkdown('page.html')
  console.log(`page.html → ${dest}`)

  // Output to specific directory
  const outDir = await htmlFileToMarkdown('page.html', 'out/')

  // With options (outDir may be null to keep the default)
  const withOpts = await htmlFileToMarkdown('page.html', 'out/', {
    mode: 'minimal',
    dropInteractiveShell: true,
  })
}
main()

A single file fails the call: if it cannot be read or written, the promise rejects with an Error (IO error: …). There is no result object to inspect.

Bulk Parallel Conversion

const { htmlFilesToMarkdown } = require('mdka')

const files = ['a.html', 'b.html', 'c.html']

async function main() {
  const results = await htmlFilesToMarkdown(files, 'out/')

  // One FileOutcome per input, in input order: src, ok, and exactly one of
  // dest (when ok) and error (when not).
  for (const r of results) {
    if (r.ok) console.log(`${r.src} → ${r.dest}`)
    else      console.error(`${r.src}: ${r.error}`)
  }

  // With options
  const withOpts = await htmlFilesToMarkdown(files, 'out/', {
    mode: 'minimal',
  })
}
main()

A failing file does not reject the promise and does not stop the others; its entry has ok: false. The promise rejects only if the call as a whole cannot proceed — the output directory cannot be created. That is the difference from htmlFileToMarkdown, on purpose: with one file, failing the call is the answer; with many, one bad file must not hide the rest.

Two inputs whose output names collide — a/index.html and b/index.html both becoming out/index.md — are not both converted. The first in the array wins and each later one comes back with ok: false and error set, rather than silently overwriting.

Four options were removed in 3.0: preserveClasses, preserveDataAttrs, preserveAriaAttrs and unwrapUnknownWrappers. None of them ever changed the output. In TypeScript, passing one is a compile error:

TS2353: Object literal may only specify known properties, and
'preserveClasses' does not exist in type 'JsConversionOptions'.

In plain JavaScript it is an error too, from 3.0. An option key that is not recognised is rejected with a message that names it — a key removed in 3.0 says option 'preserveClasses' was removed in 3.0; … and a misspelt one says unknown option 'mdoe'. Valid options: mode, preserveIds, dropInteractiveShell. The synchronous functions throw it and the Promise-returning ones reject with it. (Until 3.0 napi silently dropped unknown keys, so a removed option that was still passed told you nothing.)

JsConversionOptions has three fields: mode, preserveIds and dropInteractiveShell.

Package Version

const { version } = require('mdka')
console.log(version()) // the installed version, e.g. "3.0.0"

TypeScript

Type definitions are bundled. No @types/ package is needed:

import {
  htmlToMarkdown,
  htmlToMarkdownAsync,
  htmlToMarkdownMany,
  htmlFileToMarkdown,
  htmlFilesToMarkdown,
  type JsConversionOptions,
  type FileOutcome,
} from 'mdka'

const html: string = '<h1>Title</h1>'

const opts: JsConversionOptions = {
  mode: 'minimal',
  dropInteractiveShell: true,
}
const md: string = htmlToMarkdown(html, opts)

The options type is exported as JsConversionOptions, not ConversionOptions — the name comes from the napi-rs binding rather than from the Rust ConversionOptions it mirrors.