npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mdream/vite

v1.7.2

Published

Vite plugin for HTML to Markdown conversion with on-demand generation

Readme

@mdream/vite

npm version npm downloads License

Vite plugin for HTML to Markdown conversion. Operates at dev time, build time, and preview time.

Installation

# pnpm
pnpm add @mdream/vite@beta

# npm
npm install @mdream/vite@beta

# yarn
yarn add @mdream/vite@beta

Peer Dependencies

  • vite ^4.0.0, ^5.0.0, ^6.0.0, or ^7.0.0

The plugin depends on mdream and @mdream/js (installed automatically as transitive dependencies).

Setup

Add the plugin to your vite.config.ts:

import { viteHtmlToMarkdownPlugin } from '@mdream/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    viteHtmlToMarkdownPlugin()
  ]
})

A default export is also available:

import htmlToMarkdown from '@mdream/vite'

export default defineConfig({
  plugins: [
    htmlToMarkdown()
  ]
})

Behavior by Vite Mode

Development (vite dev)

Registers middleware via configureServer that intercepts incoming requests. When a request matches (see Request Matching), the plugin:

  1. Resolves the corresponding HTML path by trying multiple candidates in order:
    • <basePath>.html
    • <basePath> (as-is)
    • /index.html (SPA fallback)
  2. Runs the HTML through Vite's transformRequest pipeline.
  3. Converts the result to Markdown using mdream.
  4. Responds with Content-Type: text/markdown; charset=utf-8 and Cache-Control: no-cache.

Build (vite build)

Uses the generateBundle hook to process all HTML assets in the output bundle. For each .html file that matches the include patterns and does not match exclude patterns, the plugin:

  1. Converts the HTML source to Markdown.
  2. Emits a corresponding .md file into the bundle (preserving directory structure).
  3. Optionally places the .md file under a custom outputDir.

Preview (vite preview)

Registers middleware via configurePreviewServer that reads built HTML files from the output directory (dist by default). Resolution order:

  1. <outDir>/<basePath>.html
  2. <outDir>/<basePath>/index.html
  3. <outDir>/index.html (SPA fallback)

Responses use Cache-Control: public, max-age=3600.

Request Matching

The middleware intercepts a request when either condition is true:

  1. Explicit .md extension: The URL path ends with .md (e.g., /about.md, /docs/guide.md).
  2. Content negotiation: The client's Accept header indicates a preference for text/markdown or text/plain over text/html, as determined by quality weights and position ordering. A bare */* wildcard alone does not trigger Markdown serving. Requests with Sec-Fetch-Dest: document are always treated as browser navigation and served HTML.

The following paths are always skipped regardless of extension or headers:

  • /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.html or /about or /index.html | | /docs/guide.md | /docs/guide.html or /docs/guide or /index.html | | /index.md | / (special case: /index maps to /) |

API

Plugin Options

All options are optional. Pass them as an object to the plugin function.

interface ViteHtmlToMarkdownOptions {
  include?: string[]
  exclude?: string[]
  outputDir?: string
  cacheEnabled?: boolean
  mdreamOptions?: Partial<MdreamOptions>
  cacheTTL?: number
  verbose?: boolean
}

include

  • Type: string[]
  • Default: ['*.html', '**/*.html']

Glob patterns controlling which HTML files are processed during vite build. Root-level and nested HTML files are included by default. This option does not affect dev or preview middleware (those respond to any matching request).

exclude

  • Type: string[]
  • Default: ['**/node_modules/**']

Glob patterns for HTML files to skip during vite build.

outputDir

  • Type: string
  • Default: '' (same directory as the source HTML file)

A subdirectory prefix for generated .md files in the build output. For example, setting outputDir: 'markdown' would emit markdown/index.md instead of index.md.

cacheEnabled

  • Type: boolean
  • Default: true

Enables in-memory caching of converted Markdown for dev and preview servers. Cache entries are keyed by source mode and base path.

cacheTTL

  • Type: number (milliseconds)
  • Default: 3600000 (1 hour)

Time-to-live for cache entries. After this duration, entries are evicted on next access and the HTML is re-converted.

mdreamOptions

  • Type: Partial<MdreamOptions>
  • Default: {}

Options passed directly to mdream's htmlToMarkdown() function. Common fields:

| Option | Type | Description | |---|---|---| | origin | string | Base URL for resolving relative image/link paths | | minimal | boolean | Enable the minimal preset (frontmatter, isolateMain, tailwind, filter) | | clean | boolean \| CleanOptions | Post-processing cleanup. true enables all cleanup features | | frontmatter | boolean \| FrontmatterConfig \| (fm) => void | Extract metadata from <head> as YAML frontmatter | | isolateMain | boolean | Isolate main content area using semantic HTML elements | | tailwind | boolean | Convert Tailwind utility classes to semantic Markdown | | filter | { include?, exclude?, processChildren? } | Filter out unwanted HTML elements | | extraction | Record<string, (element) => void> | Extract elements matching CSS selectors during conversion | | tagOverrides | Record<string, TagOverride \| string> | Override or alias tag rendering behavior |

See the mdream documentation for the full MdreamOptions reference.

verbose

  • Type: boolean
  • Default: false

Log conversion events, cache hits, errors, and file counts to the console. Messages are prefixed with [vite-html-to-markdown].

Response Headers

The middleware sets the following headers on Markdown responses:

| Header | Value | Description | |---|---|---| | Content-Type | text/markdown; charset=utf-8 | MIME type for Markdown content | | Cache-Control | no-cache (dev) or public, max-age=3600 (preview) | Caching strategy per environment | | X-Markdown-Source | dev, preview, or build | Which mode generated the response | | X-Markdown-Cached | true or false | Whether the response was served from cache |

Programmatic Usage

The package re-exports htmlToMarkdown and streamHtmlToMarkdown from mdream, so you can use them directly without adding mdream as a separate dependency:

import { htmlToMarkdown } from '@mdream/vite'

const markdown = htmlToMarkdown('<h1>Hello</h1>', {
  origin: 'https://example.com',
  clean: true,
})

Streaming conversion:

import { streamHtmlToMarkdown } from '@mdream/vite'

for await (const chunk of streamHtmlToMarkdown(htmlStream)) {
  process.stdout.write(chunk)
}

Exported Functions

| Function | Signature | Description | |----------|-----------|-------------| | viteHtmlToMarkdownPlugin | (options?: ViteHtmlToMarkdownOptions) => Plugin | Vite plugin for automatic HTML to markdown conversion | | htmlToMarkdown | (html: string, options?: Partial<MdreamOptions>) => string | Convert an HTML string to markdown | | streamHtmlToMarkdown | (stream: ReadableStream, options?: Partial<MdreamOptions>) => AsyncIterable<string> | Stream HTML to markdown |

Exported Types

The package exports the following TypeScript types from @mdream/vite:

  • MdreamOptions: Options for htmlToMarkdown and streamHtmlToMarkdown.
  • ViteHtmlToMarkdownOptions: Plugin configuration options.
  • CacheEntry: Internal cache entry shape (content, timestamp, ttl).
  • MarkdownConversionResult: Result object with content, cached, and source fields.
  • ViteHtmlToMarkdownPlugin: The plugin function signature.

Usage Guides

Minimal Preset with Origin

import { viteHtmlToMarkdownPlugin } from '@mdream/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    viteHtmlToMarkdownPlugin({
      mdreamOptions: {
        minimal: true,
        origin: 'https://example.com',
      }
    })
  ]
})

Scoped Build Output

Only convert docs pages and place the generated Markdown in a dedicated directory:

import { viteHtmlToMarkdownPlugin } from '@mdream/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    viteHtmlToMarkdownPlugin({
      include: ['docs/**/*.html'],
      exclude: ['docs/internal/**'],
      outputDir: 'markdown',
      mdreamOptions: {
        minimal: true,
        origin: 'https://docs.example.com',
        clean: true,
      }
    })
  ]
})

Debug Mode

import { viteHtmlToMarkdownPlugin } from '@mdream/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    viteHtmlToMarkdownPlugin({
      verbose: true,
      cacheEnabled: false,
    })
  ]
})

Extracting Metadata During Build

import { viteHtmlToMarkdownPlugin } from '@mdream/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    viteHtmlToMarkdownPlugin({
      mdreamOptions: {
        frontmatter: {
          metaFields: ['description', 'author', 'keywords'],
          onExtract(fm) {
            console.log('Extracted frontmatter:', fm)
          }
        },
        filter: {
          exclude: ['nav', 'footer', 'aside', 'form'],
        },
      }
    })
  ]
})