mdream Documentation
repository·main·Indexed 21 days ago
https://github.com/harlan-zw/mdreamA high-performance HTML to Markdown converter optimized for LLM token efficiency and speed. mdream provides a Rust library API, a CLI tool, and various platform-specific binaries for Node.js (including Android, macOS, Linux, and WASM). It includes a Nuxt module for dynamic and static Markdown generation, and a GitHub Action (@mdream/action) to process prerendered HTML files into llms.txt artifacts for CI/CD workflows.
What's inside mdream
- Mdream is a zero-dependency, LLM-optimized HTML to Markdown converter. It is designed to be faster and more token-efficient than alternatives like Turndown or node-html-markdown, producing output specifically tuned for readability and reduced token costs in Large Language Model (LLM) workflows.
Identify the `@mdream/rust-linux-arm64-musl` package
mainThe@mdream/rust-linux-arm64-muslpackage provides the aarch64-unknown-linux-musl target binary for@mdream/rust. Use this package when you need to run@mdream/ruston Linux systems with ARM64 architecture using themuslC library (common in lightweight or containerized environments like Alpine Linux).Choose the correct mdream Docker image
mainmdream provides two distinct Docker images depending on whether you need to convert existing HTML or crawl live websites:
harlanzw/mdream:core: Use this for high-performance HTML to Markdown conversion. It contains a single native Rust binary (~600 KB). It reads fromstdinand writes tostdout. It cannot fetch URLs or render JavaScript.harlanzw/mdream:crawl: Use this for fetching URLs, crawling entire websites, and generatingllms.txtfiles. It includes Node.js and Playwright Chrome (~1.5 GB) to handle JavaScript-heavy sites.
Note:
harlanzw/mdream:latestis an alias for:crawl. It is recommended to use the explicit:coreor:crawltags.| Image | Use it for | Engine | Size | |-------|-----------|--------|------| | `harlanzw/mdream:core` | Converting HTML you already have to Markdown | Native Rust binary | ~600 KB | | `harlanzw/mdream:crawl` | Fetching/crawling URLs, `llms.txt` generation | Node + Playwright Chrome | ~1.5 GB |Overview of Mdream Packages
mainMdream provides several specialized packages depending on your environment and use case:
Package Description mdreamRust NAPI engine + WASM for edge. Performance-first, declarative config. Includes CLI. @mdream/jsPure JS engine. Full hook access, zero native deps. Tree-shakable via /core.@mdream/crawlSite-wide crawler to generate llms.txtartifacts from entire websites.DockerPre-built images for coreconversion andcrawl(with Playwright Chrome).@mdream/viteAutomatically generate .mdfor Vite sites.@mdream/actionGenerate .mdandllms.txtfrom static.htmloutput.mdream(crate)Native Rust crate with CLI, zero dependencies, and streaming support. Browser CDNUse via unpkg/jsDelivr without build steps. Targeted crawling with Glob Patterns
mainURLs support glob patterns for targeted crawling. When a glob pattern is provided, the crawler uses sitemap discovery to find all matching URLs. Patterns are matched using
picomatchsyntax. A trailing single*(e.g.,/fieldtypes*) expands to match both the path itself and all subdirectories.# Crawl only the /docs/ section npx @mdream/crawl -u "docs.example.com/docs/**" # Crawl pages matching a prefix npx @mdream/crawl -u "example.com/blog/2024*"How @mdream/vite behaves in different Vite modes
mainThe plugin's behavior changes depending on the Vite command being run:
Development (
vite dev)Intercepts incoming requests via middleware. If a request matches the Request Matching criteria, it resolves the HTML path (checking
.html, the base path, or/index.html), converts it to Markdown usingmdream, and responds withContent-Type: text/markdown; charset=utf-8andCache-Control: no-cache.Build (
vite build)Processes all HTML assets in the output bundle using the
generateBundlehook. For files matchingincludeand not matchingexclude, it converts the HTML to Markdown and emits a corresponding.mdfile into the bundle (preserving directory structure). You can useoutputDirto place these files in a specific subdirectory.Preview (
vite preview)Uses middleware to read built HTML files from the output directory (default
dist). It resolves paths in the order:<outDir>/<basePath>.html,<outDir>/<basePath>/index.html, or<outDir>/index.html. Responses useCache-Control: public, max-age=3600.Understand the mdream rendering engines
mainMdream provides two engines that can be used depending on your environment and feature requirements:
- Rust (NAPI): Provided by the
mdreampackage. It is the default for Node.js and uses declarative configuration only. It is highly performant due to native bindings. - Rust (WASM): Provided by the
mdreampackage. Suitable for Edge environments or the browser. - JavaScript: Provided by the
@mdream/jspackage. This engine supports both declarative configuration and hook-based plugins (imperative transforms). It is also used for the Markdown splitter.
Both engines support the same declarative plugin configuration keys:
origin,minimal,frontmatter,isolateMain,tailwind,filter,extraction,tagOverrides, andclean.- Rust (NAPI): Provided by the
Request Matching criteria for @mdream/vite
mainThe middleware intercepts requests when:
- Explicit
.mdextension: The URL ends in.md(e.g.,/about.md). - Content negotiation: The client's
Acceptheader preferstext/markdownortext/plainovertext/html.
Note: A bare
*/*wildcard in theAcceptheader does not trigger Markdown serving. Requests withSec-Fetch-Dest: documentare always served as HTML.Skipped Paths:
/api/*(API routes)/_*(internal routes)/@*(Vite internal routes)- Any path with a file extension other than
.md(e.g.,.js,.css,.html,.json).
URL Mapping Examples:
Request Path Resolved HTML /about.md/about.htmlor/aboutor/index.html/docs/guide.md/docs/guide.htmlor/docs/guideor/index.html/index.md/(special case:/indexmaps to/)- Explicit
Static Generation and llms.txt support
mainWhen using
nuxt generateor configuringnitro.prerender.routes, the module automatically performs the following:- Generates
.mdfiles alongside HTML for all prerendered pages. - Creates
llms.txtwith a page listing (using site name and description fromnuxt-site-config). - Creates
llms-full.txtcontaining the full markdown content of all pages.
These files are written to the Nitro public output directory and served as static assets.
- Generates
Understand the output formats
mainThe crawler generates three types of output:
- Individual Markdown Files: One
.mdfile per crawled page, organized in the output directory following the original URL path structure (e.g.,https://example.com/docs/introbecomesoutput/docs/intro.md). - llms.txt: A site overview file following the llms.txt specification, containing a list of all crawled pages with their titles and links to their respective markdown files.
- llms-full.txt: Similar to
llms.txt, but includes the full markdown content of every page inline.
- Individual Markdown Files: One
Use crawl hooks to transform data
mainThe
@mdream/crawlpackage provides six hooks that allow you to intercept and transform data at different stages of the crawl pipeline. Hooks receive mutable objects; you must mutate them in-place to apply changes. You can use these viadefineConfigor by passing them directly to thecrawlAndGeneratefunction.Available hooks:
crawl:url: Called before fetching. Setctx.skip = trueto skip the network request.crawl:html: Called after fetching, before HTML-to-Markdown conversion. Mutatectx.htmlto transform raw HTML.crawl:page: Called after HTML-to-Markdown conversion. Mutatepage.title,page.metadata, etc. (replaces the legacyonPagecallback).crawl:content: Called before markdown is written to disk. Mutatectx.contentorctx.filePath.crawl:done: Called after all pages are crawled, beforellms.txtgeneration. Usectx.resultsto filter or reorder results.
import { crawlAndGenerate, defineConfig } from '@mdream/crawl' // Example using defineConfig export default defineConfig({ hooks: { 'crawl:url': (ctx) => { if (ctx.url.includes('/assets/')) ctx.skip = true }, 'crawl:page': (page) => { page.title = page.title.replace(/ - Docs$/, '') }, 'crawl:done': (ctx) => { const filtered = ctx.results.filter(r => r.content.length > 100) ctx.results.length = 0 ctx.results.push(...filtered) } }, }) // Example using programmatic API await crawlAndGenerate({ urls: ['https://example.com'], outputDir: './output', hooks: { 'crawl:page': (page) => { page.title = page.title.replace(/ \| Brand$/, '') } }, })Create custom transform logic with Hook-Based Plugins
mainThe JS engine (
@mdream/js) allows you to intercept and modify the conversion pipeline using imperative hook-based plugins. You can create a plugin usingcreatePluginfrom@mdream/js/pluginsand pass it to thehooksoption inhtmlToMarkdown.Available hooks include:
beforeNodeProcess: Intercept before any node processing. Return{ skip: true }to skip the node.onNodeEnter: Prepend a string when entering an element node.onNodeExit: Append a string when exiting an element node.processAttributes: Modify element attributes (e.g., for Tailwind class extraction).processTextNode: Transform text nodes. Return{ content: string, skip: boolean }.
import { htmlToMarkdown } from '@mdream/js' import { createPlugin } from '@mdream/js/plugins' const myPlugin = createPlugin({ onNodeEnter(node) { if (node.name === 'h1') return '** ' }, processTextNode(textNode) { if (textNode.parent?.attributes?.id === 'highlight') { return { content: `**${textNode.value}**`, skip: false } } }, }) const markdown = htmlToMarkdown(html, { hooks: [myPlugin] })