@mdream/vite
v1.7.2
Published
Vite plugin for HTML to Markdown conversion with on-demand generation
Maintainers
Readme
@mdream/vite
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@betaPeer 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:
- Resolves the corresponding HTML path by trying multiple candidates in order:
<basePath>.html<basePath>(as-is)/index.html(SPA fallback)
- Runs the HTML through Vite's
transformRequestpipeline. - Converts the result to Markdown using
mdream. - Responds with
Content-Type: text/markdown; charset=utf-8andCache-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:
- Converts the HTML source to Markdown.
- Emits a corresponding
.mdfile into the bundle (preserving directory structure). - Optionally places the
.mdfile under a customoutputDir.
Preview (vite preview)
Registers middleware via configurePreviewServer that reads built HTML files from the output directory (dist by default). Resolution order:
<outDir>/<basePath>.html<outDir>/<basePath>/index.html<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:
- Explicit
.mdextension: The URL path ends with.md(e.g.,/about.md,/docs/guide.md). - Content negotiation: The client's
Acceptheader indicates a preference fortext/markdownortext/plainovertext/html, as determined by quality weights and position ordering. A bare*/*wildcard alone does not trigger Markdown serving. Requests withSec-Fetch-Dest: documentare 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 forhtmlToMarkdownandstreamHtmlToMarkdown.ViteHtmlToMarkdownOptions: Plugin configuration options.CacheEntry: Internal cache entry shape (content,timestamp,ttl).MarkdownConversionResult: Result object withcontent,cached, andsourcefields.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'],
},
}
})
]
})