Repository: unplugin/unplugin-vue-markdown
Branch: main
Commit: 5dbb5be2a296
Files: 49
Total size: 55.7 KB
Directory structure:
gitextract_xl89wua4/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .vscode/
│ └── settings.json
├── LICENSE
├── README.md
├── eslint.config.js
├── examples/
│ ├── vite/
│ │ ├── App.vue
│ │ ├── Counter.vue
│ │ ├── Counter2.vue
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── package.json
│ │ ├── pages/
│ │ │ ├── index.md
│ │ │ └── route.vue
│ │ ├── vite.config.ts
│ │ └── vue-shim.d.ts
│ └── vue-cli/
│ ├── babel.config.js
│ ├── jsconfig.json
│ ├── package.json
│ ├── public/
│ │ └── index.html
│ ├── src/
│ │ ├── App.vue
│ │ ├── Hi.md
│ │ └── main.js
│ └── vue.config.js
├── package.json
├── pnpm-workspace.yaml
├── scripts/
│ └── postbuild.ts
├── src/
│ ├── core/
│ │ ├── head.ts
│ │ ├── markdown.ts
│ │ ├── options.ts
│ │ └── utils.ts
│ ├── esbuild.ts
│ ├── index.ts
│ ├── rollup.ts
│ ├── rspack.ts
│ ├── types.ts
│ ├── vite.ts
│ └── webpack.ts
├── test/
│ ├── __snapshots__/
│ │ ├── excerpt.test.ts.snap
│ │ └── transform.test.ts.snap
│ ├── excerpt.test.ts
│ ├── fixtures/
│ │ └── simple.md
│ ├── frontmatterPreprocessor.test.ts
│ └── transform.test.ts
├── tsconfig.json
└── tsup.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
open_collective: antfu
github: [antfu]
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
uses: sxzz/workflows/.github/workflows/release.yml@v1
with:
publish: true
permissions:
contents: write
id-token: write
================================================
FILE: .github/workflows/test.yml
================================================
name: Test
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [20.x, lts/*]
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Setup
run: npm i -g pnpm @antfu/ni
- name: Install
run: nci
- name: Build
run: nr build
- name: Test
run: nr test --if-present
================================================
FILE: .gitignore
================================================
node_modules
.DS_Store
dist
example/README.md
.idea/
================================================
FILE: .vscode/settings.json
================================================
{
// Enable the ESlint flat config support
"eslint.experimental.useFlatConfig": true,
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
]
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020-PRESENT Anthony Fu<https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# unplugin-vue-markdown
[](https://www.npmjs.com/package/unplugin-vue-markdown)
Compile Markdown to Vue component.
- 📚 Use Markdown as Vue components.
- 💚 Use Vue components in Markdown.
- 🔌 Supports Vite, Webpack, Vue CLI and more, powered by [unplugin](https://github.com/unjs/unplugin).
- ⚡️ The same transformation as [VitePress](https://vitepress.vuejs.org/).
## Install
```bash
npm i unplugin-vue-markdown
```
<details>
<summary>Vite</summary><br>
```ts
// vite.config.ts
import Vue from '@vitejs/plugin-vue'
import Markdown from 'unplugin-vue-markdown/vite'
export default defineConfig({
plugins: [
Vue({
include: [/\.vue$/, /\.md$/], // <-- allows Vue to compile Markdown files
}),
Markdown({ /* options */ }),
],
})
```
Example: [`examples/vite`](./examples/vite/)
<br>
</details>
<details>
<summary>Webpack</summary><br>
```ts
// webpack.config.js
const Markdown = require('unplugin-vue-markdown/webpack')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
/* ... */
module: {
rules: [
// ... other rules
{
test: /\.(vue|md)$/,
loader: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin(),
Markdown({ /* options */ })
]
}
```
<br>
</details>
<details>
<summary>Vue CLI</summary><br>
```ts
// vue.config.js
const Markdown = require('unplugin-vue-markdown/webpack')
module.exports = {
parallel: false, // Disable thread-loader which will cause errors, we are still investigating the root cause
chainWebpack: (config) => {
config.module
.rule('vue')
.test(/\.(vue|md)$/) // <-- allows Vue to compile Markdown files
config
.plugin('markdown')
.use(Markdown({
markdownUses: [
prism,
],
}))
},
}
```
Example: [`examples/vue-cli`](./examples/vue-cli/)
<br>
</details>
## Import Markdown as Vue components
```html
<template>
<HelloWorld />
</template>
<script>
import HelloWorld from './README.md'
export default {
components: {
HelloWorld,
},
}
</script>
```
## Use Vue Components inside Markdown
You can even use Vue components inside your markdown, for example
```html
<Counter :init='5'/>
```
<Counter :init='5'/>
Note you can either register the components globally, or use the `<script setup>` tag to register them locally.
```ts
import { createApp } from 'vue'
import App from './App.vue'
import Counter from './Counter.vue'
const app = createApp(App)
// register global
app.component('Counter', Counter) // <--
app.mount()
```
```html
<script setup>
import { Counter } from './Counter.vue'
</script>
<Counter :init='5'/>
```
Or you can use [`unplugin-vue-components`](#work-with-unplugin-vue-components) for auto components registration.
## Frontmatter
Frontmatter will be parsed and inject into Vue's instance data `frontmatter` field.
For example:
```md
---
name: My Cool App
---
# Hello World
This is {{frontmatter.name}}
```
Will be rendered as
```html
<h1>Hello World</h1>
<p>This is My Cool App</p>
```
It will also be passed to the wrapper component's props if you have set `wrapperComponent` option.
## Document head and meta
To manage document head and meta, you would need to install [`@unhead/vue`](https://unhead.harlanzw.com/integrations/vue/setup) and do some setup.
```bash
npm i @unhead/vue
```
```js
// vite.config.js
import Vue from '@vitejs/plugin-vue'
import Markdown from 'unplugin-vue-markdown/vite'
export default {
plugins: [
Vue({
include: [/\.vue$/, /\.md$/],
}),
Markdown({
headEnabled: true // <--
})
]
}
```
```js
// src/main.js
import { createHead } from '@unhead/vue/client' // <--
import { createApp } from 'vue'
const app = createApp(App)
const head = createHead() // <--
app.use(head) // <--
```
Then you can use frontmatter to control the head. For example:
```yaml
---
title: My Cool App
meta:
- name: description
content: Hello World
---
```
For more options available, please refer to [`@unhead/vue`'s docs](https://unhead.harlanzw.com/integrations/vue/setup).
## Options
`unplugin-vue-markdown` uses [`markdown-exit`](https://github.com/nicepkg/markdown-exit) under the hood, a TypeScript rewrite of markdown-it with built-in async rendering. markdown-it plugins are compatible.
> **Note:** The old option names (`markdownItOptions`, `markdownItUses`, `markdownItSetup`) are deprecated but still functional.
```ts
// vite.config.js
import MarkdownItAnchor from 'markdown-it-anchor'
import MarkdownItPrism from 'markdown-it-prism'
import Markdown from 'unplugin-vue-markdown/vite'
export default {
plugins: [
Markdown({
// default options passed to markdown-exit
markdownOptions: {
html: true,
linkify: true,
typographer: true,
},
// A function providing the markdown-exit instance gets the ability to apply custom settings/plugins
markdownSetup(md) {
// for example
md.use(MarkdownItAnchor)
md.use(MarkdownItPrism)
},
// Class names for the wrapper div
wrapperClasses: 'markdown-body'
})
],
}
```
See [the tsdoc](./src/types.ts) for more advanced options
## Example
See the [/examples](./examples).
Or the pre-configured Markdown template [Vitesse](https://github.com/antfu/vitesse).
## Integrations
### Work with [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages)
```ts
import Vue from '@vitejs/plugin-vue'
import Markdown from 'unplugin-vue-markdown/vite'
import Pages from 'vite-plugin-pages'
export default {
plugins: [
Vue({
include: [/\.vue$/, /\.md$/],
}),
Pages({
extensions: ['vue', 'md'],
}),
Markdown()
],
}
```
Put your markdown under `./src/pages/xx.md`, then you can access the page via route `/xx`.
### Work with [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components)
`unplugin-vue-components` allows you to do on-demand components auto-importing without worrying about registration.
```ts
import Vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import Markdown from 'unplugin-vue-markdown/vite'
export default {
plugins: [
Vue({
include: [/\.vue$/, /\.md$/],
}),
Markdown(),
// should be placed after `Markdown()`
Components({
// allow auto load markdown components under `./src/components/`
extensions: ['vue', 'md'],
// allow auto import and register components used in markdown
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
})
],
}
```
Components under `./src/components` can be directly used in markdown components, and markdown components can also be put under `./src/components` to be auto imported.
## TypeScript Shim
```ts
declare module '*.vue' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}
declare module '*.md' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}
```
## License
MIT License © 2020-PRESENT [Anthony Fu](https://github.com/antfu)
================================================
FILE: eslint.config.js
================================================
// @ts-check
import antfu from '@antfu/eslint-config'
export default antfu()
================================================
FILE: examples/vite/App.vue
================================================
<script setup lang='ts'>
import { ref } from 'vue'
import ComponentA from './README.md'
const current = ref()
</script>
<template>
<router-view v-slot="{ Component }">
<component :is="Component" ref="current" />
</router-view>
<div v-if="current">
accessing frontmatter from outside via expose API:
{{ current.frontmatter }}
</div>
<ComponentA style="padding: 20px 40px" />
</template>
================================================
FILE: examples/vite/Counter.vue
================================================
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps<{ init?: number }>()
const counter = ref(props.init || 0)
</script>
<template>
<div class="counter">
<div>Counter: {{ counter }}</div>
<button @click="counter += 1">
Inc
</button>
<button @click="counter -= 1">
Dec
</button>
</div>
</template>
<style scoped>
.counter {
border: 2px dashed #3eaf7c;
padding: 5px 10px 10px 10px;
}
</style>
================================================
FILE: examples/vite/Counter2.vue
================================================
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps<{ init?: number }>()
const counter = ref(props.init || 0)
</script>
<template>
<div class="counter">
<div>Counter 2: {{ counter }}</div>
<button @click="counter += 1">
Inc
</button>
<button @click="counter -= 1">
Dec
</button>
</div>
</template>
<style scoped>
.counter {
border: 2px dashed skyblue;
padding: 5px 10px 10px 10px;
}
</style>
================================================
FILE: examples/vite/README.md
================================================
# unplugin-vue-markdown
[](https://www.npmjs.com/package/unplugin-vue-markdown)
Compile Markdown to Vue component.
- 📚 Use Markdown as Vue components.
- 💚 Use Vue components in Markdown.
- 🔌 Supports Vite, Webpack, Vue CLI and more, powered by [unplugin](https://github.com/unjs/unplugin).
- ⚡️ The same transformation as [VitePress](https://vitepress.vuejs.org/).
## Install
```bash
npm i unplugin-vue-markdown
```
<details>
<summary>Vite</summary><br>
```ts
// vite.config.ts
import Vue from '@vitejs/plugin-vue'
import Markdown from 'unplugin-vue-markdown/vite'
export default defineConfig({
plugins: [
Vue({
include: [/\.vue$/, /\.md$/], // <-- allows Vue to compile Markdown files
}),
Markdown({ /* options */ }),
],
})
```
Example: [`examples/vite`](./examples/vite/)
<br>
</details>
<details>
<summary>Webpack</summary><br>
```ts
// webpack.config.js
const Markdown = require('unplugin-vue-markdown/webpack')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
/* ... */
module: {
rules: [
// ... other rules
{
test: /\.(vue|md)$/,
loader: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin(),
Markdown({ /* options */ })
]
}
```
<br>
</details>
<details>
<summary>Vue CLI</summary><br>
```ts
// vue.config.js
const Markdown = require('unplugin-vue-markdown/webpack')
module.exports = {
parallel: false, // Disable thread-loader which will cause errors, we are still investigating the root cause
chainWebpack: (config) => {
config.module
.rule('vue')
.test(/\.(vue|md)$/) // <-- allows Vue to compile Markdown files
config
.plugin('markdown')
.use(Markdown({
markdownItUses: [
prism,
],
}))
},
}
```
Example: [`examples/vue-cli`](./examples/vue-cli/)
<br>
</details>
## Import Markdown as Vue components
```html
<template>
<HelloWorld />
</template>
<script>
import HelloWorld from './README.md'
export default {
components: {
HelloWorld,
},
}
</script>
```
## Use Vue Components inside Markdown
You can even use Vue components inside your markdown, for example
```html
<Counter :init='5'/>
```
<Counter :init='5'/>
Note you can either register the components globally, or use the `<script setup>` tag to register them locally.
```ts
import { createApp } from 'vue'
import App from './App.vue'
import Counter from './Counter.vue'
const app = createApp(App)
// register global
app.component('Counter', Counter) // <--
app.mount()
```
```html
<script setup>
import { Counter } from './Counter.vue'
</script>
<Counter :init='5'/>
```
Or you can use [`unplugin-vue-components`](#work-with-unplugin-vue-components) for auto components registration.
## Frontmatter
Frontmatter will be parsed and inject into Vue's instance data `frontmatter` field.
For example:
```md
---
name: My Cool App
---
# Hello World
This is {{frontmatter.name}}
```
Will be rendered as
```html
<h1>Hello World</h1>
<p>This is My Cool App</p>
```
It will also be passed to the wrapper component's props if you have set `wrapperComponent` option.
## Document head and meta
To manage document head and meta, you would need to install [`@unhead/vue`](https://unhead.harlanzw.com/integrations/vue/setup) and do some setup.
```bash
npm i @unhead/vue
```
```js
// vite.config.js
import Vue from '@vitejs/plugin-vue'
import Markdown from 'unplugin-vue-markdown/vite'
export default {
plugins: [
Vue({
include: [/\.vue$/, /\.md$/],
}),
Markdown({
headEnabled: true // <--
})
]
}
```
```js
// src/main.js
import { createHead } from '@unhead/vue/client' // <--
import { createApp } from 'vue'
const app = createApp(App)
const head = createHead() // <--
app.use(head) // <--
```
Then you can use frontmatter to control the head. For example:
```yaml
---
title: My Cool App
meta:
- name: description
content: Hello World
---
```
For more options available, please refer to [`@unhead/vue`'s docs](https://unhead.harlanzw.com/integrations/vue/setup).
## Options
`unplugin-vue-markdown` uses [`markdown-it`](https://github.com/markdown-it/markdown-it) under the hood, see [`markdown-it`'s docs](https://markdown-it.github.io/markdown-it/) for more details
```ts
// vite.config.js
import MarkdownItAnchor from 'markdown-it-anchor'
import MarkdownItPrism from 'markdown-it-prism'
import Markdown from 'unplugin-vue-markdown/vite'
export default {
plugins: [
Markdown({
// default options passed to markdown-it
// see: https://markdown-it.github.io/markdown-it/
markdownItOptions: {
html: true,
linkify: true,
typographer: true,
},
// A function providing the Markdown It instance gets the ability to apply custom settings/plugins
markdownItSetup(md) {
// for example
md.use(MarkdownItAnchor)
md.use(MarkdownItPrism)
},
// Class names for the wrapper div
wrapperClasses: 'markdown-body'
})
],
}
```
See [the tsdoc](./src/types.ts) for more advanced options
## Example
See the [/examples](./examples).
Or the pre-configured Markdown template [Vitesse](https://github.com/antfu/vitesse).
## Integrations
### Work with [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages)
```ts
import Vue from '@vitejs/plugin-vue'
import Markdown from 'unplugin-vue-markdown/vite'
import Pages from 'vite-plugin-pages'
export default {
plugins: [
Vue({
include: [/\.vue$/, /\.md$/],
}),
Pages({
extensions: ['vue', 'md'],
}),
Markdown()
],
}
```
Put your markdown under `./src/pages/xx.md`, then you can access the page via route `/xx`.
### Work with [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components)
`unplugin-vue-components` allows you to do on-demand components auto-importing without worrying about registration.
```ts
import Vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import Markdown from 'unplugin-vue-markdown/vite'
export default {
plugins: [
Vue({
include: [/\.vue$/, /\.md$/],
}),
Markdown(),
// should be placed after `Markdown()`
Components({
// allow auto load markdown components under `./src/components/`
extensions: ['vue', 'md'],
// allow auto import and register components used in markdown
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
})
],
}
```
Components under `./src/components` can be directly used in markdown components, and markdown components can also be put under `./src/components` to be auto imported.
## TypeScript Shim
```ts
declare module '*.vue' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}
declare module '*.md' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}
```
## License
MIT License © 2020-PRESENT [Anthony Fu](https://github.com/antfu)
================================================
FILE: examples/vite/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite MD</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/4.0.0/github-markdown.min.css" integrity="sha512-Oy18vBnbSJkXTndr2n6lDMO5NN31UljR8e/ICzVPrGpSud4Gkckb8yUpqhKuUNoE+o9gAb4O/rAxxw1ojyUVzg==" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/LukeAskew/prism-github@master/prism-github.css" />
</head>
<body>
<div id="app"></div>
<script type="module" src="./main.ts"></script>
</body>
</html>
================================================
FILE: examples/vite/main.ts
================================================
import { createHead } from '@unhead/vue/client'
import routes from 'pages-generated'
import { createApp } from 'vue'
import {
createRouter,
createWebHistory,
} from 'vue-router'
import App from './App.vue'
import Counter from './Counter.vue'
import 'prismjs'
import 'prismjs/components/prism-bash'
import 'prismjs/components/prism-typescript'
import 'prismjs/components/prism-javascript'
import 'prismjs/components/prism-markup'
import 'prismjs/components/prism-markup-templating'
const app = createApp(App)
const head = createHead()
const router = createRouter({
history: createWebHistory(),
routes,
})
app.use(head)
app.use(router)
app.component('Counter', Counter)
app.mount('#app')
================================================
FILE: examples/vite/package.json
================================================
{
"type": "module",
"private": true,
"scripts": {
"dev": "DEBUG=\"unplugin-vue-markdown:*\" vite",
"build": "DEBUG=\"unplugin-vue-markdown:*\" vite build"
},
"dependencies": {
"@unhead/vue": "^2.0.17",
"vue": "^3.5.21",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"markdown-it-prism": "^3.0.0",
"prismjs": "^1.30.0",
"typescript": "^5.9.2",
"unplugin-vue-markdown": "workspace:*",
"vite": "^7.1.7",
"vite-plugin-inspect": "^11.3.3",
"vite-plugin-pages": "^0.33.1"
}
}
================================================
FILE: examples/vite/pages/index.md
================================================
---
title: Hello
meta:
- name: description
content: Hello World
test: test
---
# Hello world!
Front matter: {{ frontmatter }}
<style>
h1 {
color: cadetblue;
}
</style>
<script setup>
import Counter2 from '../Counter2.vue'
</script>
<Counter />
<br>
<Counter2 />
<router-link to="/">Home</router-link>
<route>
{
meta: {
layout: 'home'
}
}
</route>
================================================
FILE: examples/vite/pages/route.vue
================================================
<template>
route test
</template>
<route>
{
meta: {
layout: 'home'
}
}
</route>
================================================
FILE: examples/vite/vite.config.ts
================================================
import Vue from '@vitejs/plugin-vue'
import prism from 'markdown-it-prism'
import Markdown from 'unplugin-vue-markdown/vite'
import { defineConfig } from 'vite'
import Inspect from 'vite-plugin-inspect'
import Pages from 'vite-plugin-pages'
export default defineConfig({
plugins: [
Vue({
include: [/\.vue$/, /\.md$/],
}),
Markdown({
markdownOptions: {
},
headEnabled: true,
markdownUses: [
prism,
],
}),
Pages({
pagesDir: 'pages',
extensions: ['vue', 'md'],
}),
Inspect(),
],
build: {
sourcemap: true,
},
})
================================================
FILE: examples/vite/vue-shim.d.ts
================================================
declare module '*.vue' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}
declare module '*.md' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}
================================================
FILE: examples/vue-cli/babel.config.js
================================================
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
],
}
================================================
FILE: examples/vue-cli/jsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
],
"baseUrl": "./",
"module": "esnext",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
}
}
}
================================================
FILE: examples/vue-cli/package.json
================================================
{
"name": "example-vue-cli",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.45.1",
"vue": "^3.5.21"
},
"devDependencies": {
"@babel/core": "^7.28.4",
"@vue/cli-plugin-babel": "~5.0.9",
"@vue/cli-service": "~5.0.9",
"unplugin-vue-markdown": "workspace:*"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}
================================================
FILE: examples/vue-cli/public/index.html
================================================
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
================================================
FILE: examples/vue-cli/src/App.vue
================================================
<script>
import Markdown from './Hi.md'
export default {
name: 'App',
components: {
Markdown,
},
}
</script>
<template>
<Markdown />
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
================================================
FILE: examples/vue-cli/src/Hi.md
================================================
# Hello
Hey
================================================
FILE: examples/vue-cli/src/main.js
================================================
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
================================================
FILE: examples/vue-cli/vue.config.js
================================================
const { defineConfig } = require('@vue/cli-service')
const prism = require('markdown-it-prism')
const Markdown = require('unplugin-vue-markdown/webpack')
module.exports = defineConfig({
transpileDependencies: true,
parallel: false,
chainWebpack: (config) => {
config.module
.rule('vue')
.test(/\.(vue|md)$/)
config
.plugin('markdown')
.use(Markdown({
markdownUses: [
prism,
],
}))
},
})
================================================
FILE: package.json
================================================
{
"name": "unplugin-vue-markdown",
"type": "module",
"version": "30.0.0",
"packageManager": "pnpm@10.17.1",
"description": "Compile Markdown to Vue component",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/unplugin/unplugin-vue-markdown",
"repository": {
"type": "git",
"url": "https://github.com/unplugin/unplugin-vue-markdown"
},
"bugs": "https://github.com/unplugin/unplugin-vue-markdown/issues",
"keywords": [
"vite",
"unplugin",
"markdown-exit"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./vite": {
"types": "./dist/vite.d.ts",
"import": "./dist/vite.js",
"require": "./dist/vite.cjs"
},
"./webpack": {
"types": "./dist/webpack.d.ts",
"import": "./dist/webpack.js",
"require": "./dist/webpack.cjs"
},
"./rollup": {
"types": "./dist/rollup.d.ts",
"import": "./dist/rollup.js",
"require": "./dist/rollup.cjs"
},
"./esbuild": {
"types": "./dist/esbuild.d.ts",
"import": "./dist/esbuild.js",
"require": "./dist/esbuild.cjs"
},
"./types": {
"types": "./dist/types.d.ts",
"import": "./dist/types.js",
"require": "./dist/types.cjs"
},
"./*": "./*"
},
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"typesVersions": {
"*": {
"*": [
"./dist/*",
"./*"
]
}
},
"engines": {
"node": ">=20"
},
"scripts": {
"dev": "tsup --watch",
"example:dev": "pnpm -C examples/vite run dev",
"example:build": "pnpm -C examples/vite run build",
"build": "tsup",
"build:fix": "tsx scripts/postbuild.ts",
"test": "vitest",
"test:update": "vitest -u",
"lint": "eslint .",
"prepublishOnly": "pnpm run build",
"release": "bumpp"
},
"peerDependencies": {
"vite": "^2.0.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0"
},
"dependencies": {
"@mdit-vue/plugin-component": "^3.0.2",
"@mdit-vue/plugin-frontmatter": "^3.0.2",
"@mdit-vue/types": "^3.0.2",
"markdown-exit": "^1.0.0-beta.8",
"unplugin": "^2.3.10",
"unplugin-utils": "^0.3.0"
},
"devDependencies": {
"@antfu/eslint-config": "^5.4.1",
"@antfu/ni": "^26.0.1",
"@antfu/utils": "^9.2.1",
"@types/node": "^24.5.2",
"@unhead/vue": "^2.0.17",
"@vue/test-utils": "^2.4.6",
"ansis": "^4.1.0",
"bumpp": "^10.2.3",
"eslint": "^9.36.0",
"fast-glob": "^3.3.3",
"rollup": "^4.52.2",
"tsup": "^8.5.0",
"tsx": "^4.20.5",
"typescript": "^5.9.2",
"vite": "^7.1.7",
"vitest": "^3.2.4",
"vue": "^3.5.21",
"webpack": "^5.101.3"
},
"pnpm": {
"onlyBuiltDependencies": [
"core-js",
"esbuild"
]
}
}
================================================
FILE: pnpm-workspace.yaml
================================================
packages:
- examples/*
ignoreWorkspaceRootCheck: true
strictPeerDependencies: false
shamefullyHoist: true
shellEmulator: true
================================================
FILE: scripts/postbuild.ts
================================================
import { promises as fs } from 'node:fs'
import { basename, dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import ansis from 'ansis'
import fg from 'fast-glob'
async function run() {
// fix cjs exports
const files = await fg('*.cjs', {
ignore: ['chunk-*'],
absolute: true,
cwd: resolve(dirname(fileURLToPath(import.meta.url)), '../dist'),
})
for (const file of files) {
console.log(ansis.cyan.inverse(' POST '), `Fix ${basename(file)}`)
let code = await fs.readFile(file, 'utf8')
code = code.replace('exports.default =', 'module.exports =')
code += 'exports.default = module.exports;'
await fs.writeFile(file, code)
}
}
run()
================================================
FILE: src/core/head.ts
================================================
import type { Frontmatter, MetaProperty, ResolvedOptions } from '../types'
const headProperties = [
'title',
'meta',
'link',
'base',
'style',
'script',
'htmlAttrs',
'bodyAttrs',
]
export function preprocessHead<T extends Frontmatter>(frontmatter: T, options: ResolvedOptions) {
if (!options.headEnabled)
return frontmatter
const head: Frontmatter = options.headField
? (frontmatter[options.headField] as Frontmatter || {})
: frontmatter
const meta = (head.meta = head.meta || []) as MetaProperty[]
if (head.title) {
if (!meta.find((i: any) => i.property === 'og:title'))
meta.push({ property: 'og:title', content: head.title })
if (!meta.find((i: any) => i.name === 'twitter:title'))
meta.push({ name: 'twitter:title', content: head.title })
}
if (head.description) {
if (!meta.find((i: any) => i.name === 'description'))
meta.push({ name: 'description', content: head.description })
if (!meta.find((i: any) => i.property === 'og:description'))
meta.push({ property: 'og:description', content: head.description })
if (!meta.find((i: any) => i.name === 'twitter:description'))
meta.push({ name: 'twitter:description', content: head.description })
}
if (head.image) {
if (!meta.find((i: any) => i.property === 'og:image'))
meta.push({ property: 'og:image', content: head.image })
if (!meta.find((i: any) => i.name === 'twitter:image'))
meta.push({ name: 'twitter:image', content: head.image })
if (!meta.find((i: any) => i.property === 'twitter:card'))
meta.push({ name: 'twitter:card', content: 'summary_large_image' })
}
const result: any = {}
for (const [key, value] of Object.entries(head)) {
if (headProperties.includes(key))
result[key] = value
}
return Object.entries(result).length === 0 ? null : result
}
================================================
FILE: src/core/markdown.ts
================================================
import type { TransformResult } from 'vite'
import type { MarkdownEnv, ResolvedOptions } from '../types'
import { toArray, uniq } from '@antfu/utils'
import { componentPlugin } from '@mdit-vue/plugin-component'
import { frontmatterPlugin } from '@mdit-vue/plugin-frontmatter'
import { createMarkdownExit } from 'markdown-exit'
import { preprocessHead } from './head'
const scriptSetupRE = /<\s*script([^>]*)\bsetup\b([^>]*)>([\s\S]*)<\/script>/g
const defineExposeRE = /defineExpose\s*\(/g
const EXPORTS_KEYWORDS = [
'class',
'default',
'export',
'function',
'import',
'let',
'var',
'const',
'from',
'as',
'return',
'if',
'else',
'switch',
'case',
'break',
'for',
'while',
'do',
]
interface ScriptMeta {
code: string
attr: string
}
function extractScriptSetup(html: string) {
const scripts: ScriptMeta[] = []
html = html.replace(scriptSetupRE, (_, attr1, attr2, code) => {
scripts.push({
code,
attr: `${attr1} ${attr2}`.trim(),
})
return ''
})
return { html, scripts }
}
function extractCustomBlock(html: string, options: ResolvedOptions) {
const blocks: string[] = []
for (const tag of options.customSfcBlocks) {
html = html.replace(new RegExp(`<${tag}[^>]*\\b[^>]*>[^<]*<\\/${tag}>`, 'gm'), (code) => {
blocks.push(code)
return ''
})
}
return { html, blocks }
}
export function createMarkdown(options: ResolvedOptions) {
const isVue2 = options.vueVersion.startsWith('2.')
const setupPromise = (async () => {
const md = createMarkdownExit({
html: true,
linkify: true,
typographer: true,
...options.markdownOptions,
})
md.use(componentPlugin as any, options.componentOptions)
if (options.frontmatter || options.excerpt) {
md.use(frontmatterPlugin as any, {
...options.frontmatterOptions,
grayMatterOptions: {
excerpt: options.excerpt,
...options.frontmatterOptions.grayMatterOptions,
},
})
}
md.linkify.set({ fuzzyLink: false })
options.markdownUses.forEach((e) => {
const [plugin, options] = toArray(e)
md.use(plugin, options)
})
await options.markdownSetup(md)
return md
})()
return async (id: string, raw: string): Promise<TransformResult> => {
const md = await setupPromise
const {
wrapperDiv,
wrapperClasses,
wrapperComponent,
transforms,
headEnabled,
frontmatterPreprocess,
} = options
raw = raw.trimStart()
raw = await transforms.before?.(raw, id) ?? raw
const env: MarkdownEnv = { id }
let html = await md.renderAsync(raw, env)
const { excerpt = '', frontmatter: data = null } = env
if (wrapperDiv) {
const wrapperClassesResolved = toArray(
typeof wrapperClasses === 'function'
? wrapperClasses(id, raw)
: wrapperClasses,
)
.filter(Boolean)
.join(' ')
if (wrapperClassesResolved)
html = `<div class="${wrapperClassesResolved}">${html}</div>`
else
html = `<div>${html}</div>`
}
const wrapperComponentName = typeof wrapperComponent === 'function'
? wrapperComponent(id, raw)
: wrapperComponent
if (wrapperComponentName) {
const attrs = [
options.frontmatter && ':frontmatter="frontmatter"',
options.excerpt && ':excerpt="excerpt"',
].filter(Boolean).join(' ')
html = `<${wrapperComponentName} ${attrs}>${html}</${wrapperComponentName}>`
}
html = await transforms.after?.(html, id) ?? html
if (options.escapeCodeTagInterpolation) {
// escape curly brackets interpolation in <code>, #14
html = html.replace(/<code(.*?)>/g, '<code$1 v-pre>')
}
const hoistScripts = extractScriptSetup(html)
html = hoistScripts.html
const customBlocks = extractCustomBlock(html, options)
html = customBlocks.html
const scriptLines: string[] = []
let frontmatterExportsLines: string[] = []
let excerptExportsLine = ''
let excerptKeyOverlapping = false
function hasExplicitExports() {
return defineExposeRE.test(hoistScripts.scripts.map(i => i.code).join(''))
}
if (options.frontmatter) {
if (options.excerpt && data) {
if (data.excerpt !== undefined)
excerptKeyOverlapping = true
data.excerpt = excerpt
}
const { head, frontmatter } = frontmatterPreprocess(data || {}, options, id, preprocessHead)
if (options.excerpt && !excerptKeyOverlapping && frontmatter.excerpt !== undefined)
delete frontmatter.excerpt
scriptLines.push(`const frontmatter = ${JSON.stringify(frontmatter)}`)
if (options.exportFrontmatter) {
frontmatterExportsLines = Object.entries(frontmatter)
.map(([key, value]) => {
if (EXPORTS_KEYWORDS.includes(key))
key = `_${key}`
return `export const ${key} = ${JSON.stringify(value)}`
})
}
if (!isVue2 && options.exposeFrontmatter && !hasExplicitExports())
scriptLines.push('defineExpose({ frontmatter })')
if (!isVue2 && headEnabled && head) {
// @ts-expect-error legacy option
if (headEnabled === 'vueuse')
throw new Error('unplugin-vue-markdown no longer supports @vueuse/head. Change `headEnabled` to `true` and install `@unhead/vue` instead.')
scriptLines.push(`const head = ${JSON.stringify(head)}`)
scriptLines.unshift(`import { useHead } from "@unhead/vue"`)
scriptLines.push('useHead(head)')
}
scriptLines.push(...await transforms.extraScripts?.(frontmatter, id) || [])
}
if (options.excerpt) {
scriptLines.push(`const excerpt = ${JSON.stringify(excerpt)}`)
if (!excerptKeyOverlapping)
excerptExportsLine = `export const excerpt = ${JSON.stringify(excerpt)}\n`
if (!isVue2 && options.exposeExcerpt && !hasExplicitExports())
scriptLines.push('defineExpose({ excerpt })')
}
scriptLines.push(...hoistScripts.scripts.map(i => i.code))
let attrs = uniq(hoistScripts.scripts.map(i => i.attr)).join(' ').trim()
if (attrs)
attrs = ` ${attrs}`
const scripts = isVue2
? [
`<script${attrs}>`,
...scriptLines,
...frontmatterExportsLines,
excerptExportsLine,
'export default { data() { return { frontmatter } } }',
'</script>',
]
: [
`<script setup${attrs}>`,
...scriptLines,
'</script>',
...((frontmatterExportsLines.length || excerptExportsLine)
? [
`<script${attrs}>`,
...frontmatterExportsLines,
excerptExportsLine,
'</script>',
]
: []),
]
const code = [
`<template>${html}</template>`,
...scripts.map(i => i.trim()).filter(Boolean),
...customBlocks.blocks,
].join('\n')
return {
code,
map: { mappings: '' } as any,
}
}
}
================================================
FILE: src/core/options.ts
================================================
import type { Options, ResolvedOptions } from '../types'
import { getVueVersion } from './utils'
export function resolveOptions(userOptions: Options): ResolvedOptions {
const noop = () => {}
// Resolve with new names taking precedence over deprecated old names
const markdownOptions = userOptions.markdownOptions ?? userOptions.markdownItOptions ?? {}
const markdownUses = userOptions.markdownUses ?? userOptions.markdownItUses ?? []
const markdownSetup = userOptions.markdownSetup ?? userOptions.markdownItSetup ?? noop
const defaultOptions: ResolvedOptions = {
headEnabled: false,
headField: '',
frontmatter: true,
excerpt: false,
exposeFrontmatter: true,
exposeExcerpt: false,
exportFrontmatter: true,
escapeCodeTagInterpolation: true,
customSfcBlocks: ['route', 'i18n', 'style'],
componentOptions: {},
frontmatterOptions: {},
markdownOptions: {},
markdownUses: [],
markdownSetup: noop,
markdownItOptions: {},
markdownItUses: [],
markdownItSetup: noop,
wrapperDiv: true,
wrapperComponent: null,
transforms: {},
vueVersion: userOptions.vueVersion || getVueVersion(),
wrapperClasses: 'markdown-body',
include: null,
exclude: null,
frontmatterPreprocess: (frontmatter, options, _id, defaults) => {
return {
head: defaults(frontmatter, options),
frontmatter,
}
},
}
const options = {
...defaultOptions,
...userOptions,
// Set both old and new keys to the resolved value
markdownOptions,
markdownUses,
markdownSetup,
markdownItOptions: markdownOptions,
markdownItUses: markdownUses,
markdownItSetup: markdownSetup,
}
return options as ResolvedOptions
}
================================================
FILE: src/core/utils.ts
================================================
import { createRequire } from 'node:module'
const _require = typeof require === 'undefined'
? createRequire(import.meta.url)
: require
export function getVueVersion(defaultVersion = '3.2.0') {
try {
let v = _require('vue')
if (v.default)
v = v.default
return v.version || defaultVersion
}
catch {
return defaultVersion
}
}
================================================
FILE: src/esbuild.ts
================================================
import { createEsbuildPlugin } from 'unplugin'
import { unpluginFactory } from '.'
export default createEsbuildPlugin(unpluginFactory)
================================================
FILE: src/index.ts
================================================
import type { UnpluginFactory } from 'unplugin'
import type { Options } from './types'
import { createUnplugin } from 'unplugin'
import { createFilter } from 'unplugin-utils'
import { createMarkdown } from './core/markdown'
import { resolveOptions } from './core/options'
const cssIdRE = /\.(css|postcss|sass|scss|less|stylus|styl)($|\?)/
export const unpluginFactory: UnpluginFactory<Options> = (userOptions = {}) => {
const options = resolveOptions(userOptions)
const markdownToVue = createMarkdown(options)
const filter = createFilter(
userOptions.include || /\.md$|\.md\?vue/,
userOptions.exclude || cssIdRE,
)
return {
name: 'unplugin-vue-markdown',
enforce: 'pre',
transformInclude(id) {
return filter(id)
},
async transform(raw, id) {
try {
return await markdownToVue(id, raw)
}
catch (e: any) {
this.error(e)
}
},
vite: {
async handleHotUpdate(ctx) {
if (!filter(ctx.file))
return
const defaultRead = ctx.read
ctx.read = async function () {
return (await markdownToVue(ctx.file, await defaultRead())).code
}
},
},
}
}
export default /* #__PURE__ */ createUnplugin(unpluginFactory)
================================================
FILE: src/rollup.ts
================================================
import { createRollupPlugin } from 'unplugin'
import { unpluginFactory } from '.'
export default createRollupPlugin(unpluginFactory)
================================================
FILE: src/rspack.ts
================================================
import { createRspackPlugin } from 'unplugin'
import { unpluginFactory } from '.'
export default createRspackPlugin(unpluginFactory)
================================================
FILE: src/types.ts
================================================
import type { ComponentPluginOptions } from '@mdit-vue/plugin-component'
import type { FrontmatterPluginOptions } from '@mdit-vue/plugin-frontmatter'
import type { MarkdownItEnv } from '@mdit-vue/types'
import type MarkdownExit from 'markdown-exit'
import type {
MarkdownExitOptions,
PluginSimple,
PluginWithOptions,
} from 'markdown-exit'
import type { FilterPattern } from 'unplugin-utils'
import type { preprocessHead } from './core/head'
/** a `<meta />` property in HTML is defined with the following name/values */
export interface MetaProperty {
key?: string
/**
* the "name" property used by Facebook and other providers who
* use the Opengraph standards
*/
property?: string
/**
* used by google to identify the "name" of the name/value pair
*/
itemprop?: string
/**
* used by Twitter to indicate the "name" field in a meta properties
* name/value pairing
*/
name?: string
/**
* The value of the meta property
*/
content?: any
[key: string]: unknown
}
/**
* Frontmatter content is represented as key/value dictionary
*/
export interface Frontmatter {
title?: string
name?: string
description?: string
meta?: MetaProperty[]
[key: string]: unknown
}
export interface Options {
/**
* Explicitly set the Vue version
*
* @default auto detected
*/
vueVersion?: string
/**
* Enable head support, need to install @unhead/vue and register to App in main.js
*
* @default true
*/
headEnabled?: boolean
/**
* The head field in frontmatter used to be used for @unhead/vue
*
* When an empty string is passed, it will use the root properties of the frontmatter
*
* @default ''
*/
headField?: string
/**
* Parse for frontmatter
*
* @default true
*/
frontmatter?: boolean
/**
* Parse for excerpt
*
* If `true`, it will be passed to `frontmatterPreprocess` as `frontmatter.excerpt`, replacing the `excerpt` key in frontmatter, if there's any
*
* @default false
*/
excerpt?: boolean
/**
* Remove custom SFC block
*
* @default ['route', 'i18n']
*/
customSfcBlocks?: string[]
/**
* Options passed to [@mdit-vue/plugin-component](https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-component)
*/
componentOptions?: ComponentPluginOptions
/**
* Options passed to [@mdit-vue/plugin-frontmatter](https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-frontmatter)
*/
frontmatterOptions?: FrontmatterPluginOptions
/**
* Custom function to provide defaults to the frontmatter and
* move certain attributes into the "meta" category.
*
* Note: _overriding this will remove built-in functionality setting
* "meta" properties and the built-in "head" support. Do this only
* if you know what you're doing._
*/
frontmatterPreprocess?: (
frontmatter: Frontmatter,
options: ResolvedOptions,
id: string,
defaultHeadProcess: typeof preprocessHead,
) => {
head: Record<string, any>
frontmatter: Frontmatter
}
/**
* Expose frontmatter via expose API
*
* @default true
*/
exposeFrontmatter?: boolean
/**
* Expose excerpt via expose API
*
* @default false
*/
exposeExcerpt?: boolean
/**
* Export frontmatter in component module
*
* @default true
*/
exportFrontmatter?: boolean
/**
* Add `v-pre` to `<code>` tag to escape curly brackets interpolation
*
* @see https://github.com/unplugin/unplugin-vue-markdown/issues/14
* @default true
*/
escapeCodeTagInterpolation?: boolean
/**
* Options passed to markdown-exit
*/
markdownOptions?: MarkdownExitOptions
/**
* Plugins for markdown-exit
*/
markdownUses?: (
| PluginSimple
| [PluginSimple | PluginWithOptions<any>, any]
| any
)[]
/**
* A function providing the markdown-exit instance gets the ability to apply custom
* settings/plugins
*/
markdownSetup?: (md: MarkdownExit) => void | Promise<void>
/**
* @deprecated Use `markdownOptions` instead
*/
markdownItOptions?: MarkdownExitOptions
/**
* @deprecated Use `markdownUses` instead
*/
markdownItUses?: (
| PluginSimple
| [PluginSimple | PluginWithOptions<any>, any]
| any
)[]
/**
* @deprecated Use `markdownSetup` instead
*/
markdownItSetup?: (md: MarkdownExit) => void | Promise<void>
/**
* Wrap the rendered html in a div
*
* @default true
*/
wrapperDiv?: boolean
/**
* Class names for wrapper div
*
* This option will be ignored if `wrapperDiv` is set to `false`
*
* @default 'markdown-body'
*/
wrapperClasses?: string | string[] | undefined | null | ((id: string, code: string) => string | string[] | undefined | null)
/**
* Component name to wrapper with
*
* @default undefined
*/
wrapperComponent?: string | undefined | null | ((id: string, code: string) => string | undefined | null)
/**
* Custom tranformations apply before and after the markdown transformation
*/
transforms?: {
before?: (code: string, id: string) => string | Promise<string>
after?: (code: string, id: string) => string | Promise<string>
/**
* Return extra code to be injected into the `<script>` tag
*/
extraScripts?: (frontmatter: Record<string, any>, id: string) => string[] | Promise<string[]>
}
include?: FilterPattern
exclude?: FilterPattern
}
export interface ResolvedOptions extends Required<Options> {}
export type { MarkdownExit, MarkdownExitOptions }
export interface MarkdownEnv extends MarkdownItEnv {
id: string
}
================================================
FILE: src/vite.ts
================================================
import { createVitePlugin } from 'unplugin'
import { unpluginFactory } from '.'
export default createVitePlugin(unpluginFactory)
================================================
FILE: src/webpack.ts
================================================
import { createWebpackPlugin } from 'unplugin'
import { unpluginFactory } from '.'
export default createWebpackPlugin(unpluginFactory) as any
================================================
FILE: test/__snapshots__/excerpt.test.ts.snap
================================================
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`excerpt > raw excerpt 1`] = `
"<template><div class="markdown-body"><p>This is an excerpt which is kept as <strong>raw Markdown</strong>.</p>
<!-- more -->
<h1>Hello</h1>
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ frontmatter })
const excerpt = "\\nThis is an excerpt which is kept as **raw Markdown**.\\n\\n"
</script>
<script>
export const title = "Hey"
export const excerpt = "\\nThis is an excerpt which is kept as **raw Markdown**.\\n\\n"
</script>"
`;
exports[`excerpt > rendered excerpt 1`] = `
"<template><div class="markdown-body"><p>This is an excerpt which has been rendered to <strong>HTML</strong>.</p>
<!-- more -->
<h1>Hello</h1>
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ frontmatter })
const excerpt = "<p>This is an excerpt which has been rendered to <strong>HTML</strong>.</p>\\n"
</script>
<script>
export const title = "Hey"
export const excerpt = "<p>This is an excerpt which has been rendered to <strong>HTML</strong>.</p>\\n"
</script>"
`;
================================================
FILE: test/__snapshots__/transform.test.ts.snap
================================================
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`transform > basic 1`] = `
"<template><div class="markdown-body"><h1>Hello</h1>
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ frontmatter })
</script>
<script>
export const title = "Hey"
</script>"
`;
exports[`transform > code escape 1`] = `
"<template><div class="markdown-body"><p>Hello <code v-pre>{{ world }}</code></p>
<pre><code class="language-js" v-pre>console.log(\`{{ world }}\`)
</code></pre>
</div></template>
<script setup>
const frontmatter = {}
defineExpose({ frontmatter })
</script>"
`;
exports[`transform > couldn't expose frontmatter 1`] = `
"<template><div class="markdown-body">
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ test: 'test'})
</script>
<script>
export const title = "Hey"
</script>"
`;
exports[`transform > escapeCodeTagInterpolation 1`] = `
"<template><div class="markdown-body"><div>{{hello}}</div>
<pre><code class="language-ts" v-pre><div>{{hello}}</div>
</code></pre>
</div></template>
<script setup>
const frontmatter = {}
defineExpose({ frontmatter })
</script>"
`;
exports[`transform > export keyword frontmatters 1`] = `
"<template><div class="markdown-body"><p>Hello</p>
</div></template>
<script setup>
const frontmatter = {"class":"text","default":"foo"}
defineExpose({ frontmatter })
</script>
<script>
export const _class = "text"
export const _default = "foo"
</script>"
`;
exports[`transform > exposes frontmatter 1`] = `
"<template><div class="markdown-body"><h1>Hello</h1>
</div></template>
<script setup>
const frontmatter = {"title":"Hey"}
defineExpose({ frontmatter })
</script>
<script>
export const title = "Hey"
</script>"
`;
exports[`transform > frontmatter interpolation 1`] = `
"<template><div class="markdown-body"><h1>Hello World</h1>
<p>This is {{frontmatter.name}}</p>
</div></template>
<script setup>
const frontmatter = {"name":"My Cool App"}
defineExpose({ frontmatter })
</script>
<script>
export const name = "My Cool App"
</script>"
`;
exports[`transform > script setup 1`] = `
"<template><div class="markdown-body"><h1>Hello</h1>
</div></template>
<script setup lang="ts">
const frontmatter = {}
defineExpose({ frontmatter })
import Foo from './Foo.vue'
</script>"
`;
exports[`transform > style 1`] = `
"<template><div class="markdown-body"><h1>Hello</h1>
</div></template>
<script setup>
const frontmatter = {}
defineExpose({ frontmatter })
</script>
<style>h1 { color: red }</style>"
`;
exports[`transform > vue directives 1`] = `
"<template><div class="markdown-body">
<p><button @click="onClick"></button></p>
</div></template>
<script setup lang="ts">
const frontmatter = {"name":"My Cool App"}
defineExpose({ frontmatter })
function onClick() {
// ...
}
</script>
<script lang="ts">
export const name = "My Cool App"
</script>"
`;
================================================
FILE: test/excerpt.test.ts
================================================
import { describe, expect, it } from 'vitest'
import { createMarkdown } from '../src/core/markdown'
import { resolveOptions } from '../src/core/options'
describe('excerpt', () => {
it('rendered excerpt', async () => {
const options = resolveOptions({
excerpt: true,
frontmatterOptions: {
grayMatterOptions: {
excerpt: true,
excerpt_separator: '<!-- more -->',
},
},
})
const markdownToVue = createMarkdown(options)
const md = `---
title: Hey
---
This is an excerpt which has been rendered to **HTML**.
<!-- more -->
# Hello
- A
- B
- C`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('raw excerpt', async () => {
const options = resolveOptions({
excerpt: true,
frontmatterOptions: {
renderExcerpt: false,
grayMatterOptions: {
excerpt: true,
excerpt_separator: '<!-- more -->',
},
},
})
const markdownToVue = createMarkdown(options)
const md = `---
title: Hey
---
This is an excerpt which is kept as **raw Markdown**.
<!-- more -->
# Hello
- A
- B
- C`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
})
================================================
FILE: test/fixtures/simple.md
================================================
---
title: Hello World
description: testing is the path to true happiness
---
# My H1
lorem ipsum
## Transformed
base: {{ title }}
================================================
FILE: test/frontmatterPreprocessor.test.ts
================================================
import type { MetaProperty, ResolvedOptions } from '../src/types'
import { readFile } from 'node:fs/promises'
import { describe, expect, it } from 'vitest'
import { createMarkdown } from '../src/core/markdown'
import { resolveOptions } from '../src/core/options'
const frontmatterPreprocess: ResolvedOptions['frontmatterPreprocess'] = (fm) => {
const frontmatter = {
title: 'default title',
description: 'default description',
...fm,
}
const meta: MetaProperty[] = [
{ property: 'og:title', name: 'twitter:title', itemprop: 'title', content: frontmatter.title },
{
property: 'og:description',
name: 'twitter:description',
itemprop: 'description',
content: frontmatter.description,
},
]
return {
head: { ...frontmatter, meta },
frontmatter: { ...frontmatter, meta },
}
}
describe('provide bespoke frontmatter processor', () => {
it('inline markdown is used over default properties', async () => {
const markdownToVue = createMarkdown(resolveOptions({ frontmatterPreprocess }))
const md = (await markdownToVue('', await readFile('test/fixtures/simple.md', 'utf-8'))).code
// Positive tests
expect(
md.includes('Hello World'),
'the title attribute is retained over the default \'title\' value',
).toBeTruthy()
expect(
md.includes('testing is the path to true happiness'),
'description property is also retained',
).toBeTruthy()
// Negative tests
expect(
md.includes('default title'),
'the title attribute is retained over the default \'title\' value',
).toBeFalsy()
expect(md.includes('default description'), 'default description is ignored').toBeFalsy()
// Meta props
expect(md.includes('og:title')).toBeTruthy()
expect(md.includes('og:description')).toBeTruthy()
})
})
================================================
FILE: test/transform.test.ts
================================================
import { describe, expect, it } from 'vitest'
import { createMarkdown } from '../src/core/markdown'
import { resolveOptions } from '../src/core/options'
describe('transform', async () => {
const options = resolveOptions({})
const markdownToVue = createMarkdown(options)
it('basic', async () => {
const md = `---
title: Hey
---
# Hello
- A
- B
- C
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('style', async () => {
const md = `
# Hello
<style>h1 { color: red }</style>
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('script setup', async () => {
const md = `
# Hello
<script setup lang="ts">
import Foo from './Foo.vue'
</script>
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('exposes frontmatter', async () => {
const md = `---
title: Hey
---
# Hello`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('couldn\'t expose frontmatter', async () => {
const md = `---
title: Hey
---
<script setup>
defineExpose({ test: 'test'})
</script>
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('escapeCodeTagInterpolation', async () => {
const md = `
<div>{{hello}}</div>
\`\`\`ts
<div>{{hello}}</div>
\`\`\`
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('frontmatter interpolation', async () => {
const md = `
---
name: 'My Cool App'
---
# Hello World
This is {{frontmatter.name}}
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('vue directives', async () => {
const md = `
---
name: 'My Cool App'
---
<script setup lang="ts">
function onClick() {
// ...
}
</script>
<button @click="onClick"></button>
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('export keyword frontmatters', async () => {
const md = `
---
class: 'text'
default: 'foo'
---
Hello
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
it('code escape', async () => {
const md = `
Hello \`{{ world }}\`
\`\`\`js
console.log(\`{{ world }}\`)
\`\`\`
`
expect((await markdownToVue('', md)).code).toMatchSnapshot()
})
})
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "es2020",
"jsx": "preserve",
"lib": ["esnext", "DOM"],
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"exclude": [
"examples/**",
"dist/**"
]
}
================================================
FILE: tsup.config.ts
================================================
import type { Options } from 'tsup'
export default <Options>{
entryPoints: [
'src/*.ts',
],
clean: true,
format: ['cjs', 'esm'],
dts: true,
shims: true,
splitting: true,
onSuccess: 'npm run build:fix',
}
gitextract_xl89wua4/ ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── release.yml │ └── test.yml ├── .gitignore ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.md ├── eslint.config.js ├── examples/ │ ├── vite/ │ │ ├── App.vue │ │ ├── Counter.vue │ │ ├── Counter2.vue │ │ ├── README.md │ │ ├── index.html │ │ ├── main.ts │ │ ├── package.json │ │ ├── pages/ │ │ │ ├── index.md │ │ │ └── route.vue │ │ ├── vite.config.ts │ │ └── vue-shim.d.ts │ └── vue-cli/ │ ├── babel.config.js │ ├── jsconfig.json │ ├── package.json │ ├── public/ │ │ └── index.html │ ├── src/ │ │ ├── App.vue │ │ ├── Hi.md │ │ └── main.js │ └── vue.config.js ├── package.json ├── pnpm-workspace.yaml ├── scripts/ │ └── postbuild.ts ├── src/ │ ├── core/ │ │ ├── head.ts │ │ ├── markdown.ts │ │ ├── options.ts │ │ └── utils.ts │ ├── esbuild.ts │ ├── index.ts │ ├── rollup.ts │ ├── rspack.ts │ ├── types.ts │ ├── vite.ts │ └── webpack.ts ├── test/ │ ├── __snapshots__/ │ │ ├── excerpt.test.ts.snap │ │ └── transform.test.ts.snap │ ├── excerpt.test.ts │ ├── fixtures/ │ │ └── simple.md │ ├── frontmatterPreprocessor.test.ts │ └── transform.test.ts ├── tsconfig.json └── tsup.config.ts
SYMBOL INDEX (17 symbols across 7 files)
FILE: scripts/postbuild.ts
function run (line 7) | async function run() {
FILE: src/core/head.ts
function preprocessHead (line 14) | function preprocessHead<T extends Frontmatter>(frontmatter: T, options: ...
FILE: src/core/markdown.ts
constant EXPORTS_KEYWORDS (line 12) | const EXPORTS_KEYWORDS = [
type ScriptMeta (line 34) | interface ScriptMeta {
function extractScriptSetup (line 39) | function extractScriptSetup(html: string) {
function extractCustomBlock (line 52) | function extractCustomBlock(html: string, options: ResolvedOptions) {
function createMarkdown (line 64) | function createMarkdown(options: ResolvedOptions) {
FILE: src/core/options.ts
function resolveOptions (line 4) | function resolveOptions(userOptions: Options): ResolvedOptions {
FILE: src/core/utils.ts
function getVueVersion (line 7) | function getVueVersion(defaultVersion = '3.2.0') {
FILE: src/index.ts
method transformInclude (line 22) | transformInclude(id) {
method transform (line 25) | async transform(raw, id) {
method handleHotUpdate (line 34) | async handleHotUpdate(ctx) {
FILE: src/types.ts
type MetaProperty (line 14) | interface MetaProperty {
type Frontmatter (line 40) | interface Frontmatter {
type Options (line 48) | interface Options {
type ResolvedOptions (line 230) | interface ResolvedOptions extends Required<Options> {}
type MarkdownEnv (line 234) | interface MarkdownEnv extends MarkdownItEnv {
Condensed preview — 49 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (63K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 39,
"preview": "open_collective: antfu\ngithub: [antfu]\n"
},
{
"path": ".github/workflows/release.yml",
"chars": 217,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\njobs:\n release:\n uses: sxzz/workflows/.github/workflows/release.y"
},
{
"path": ".github/workflows/test.yml",
"chars": 713,
"preview": "name: Test\n\non:\n push:\n branches:\n - main\n - master\n\n pull_request:\n branches:\n - main\n - ma"
},
{
"path": ".gitignore",
"chars": 53,
"preview": "node_modules\n.DS_Store\ndist\nexample/README.md\n.idea/\n"
},
{
"path": ".vscode/settings.json",
"chars": 1090,
"preview": "{\n // Enable the ESlint flat config support\n \"eslint.experimental.useFlatConfig\": true,\n\n // Disable the default form"
},
{
"path": "LICENSE",
"chars": 1101,
"preview": "MIT License\n\nCopyright (c) 2020-PRESENT Anthony Fu<https://github.com/antfu>\n\nPermission is hereby granted, free of char"
},
{
"path": "README.md",
"chars": 7200,
"preview": "# unplugin-vue-markdown\n\n[](https://www.n"
},
{
"path": "eslint.config.js",
"chars": 78,
"preview": "// @ts-check\nimport antfu from '@antfu/eslint-config'\n\nexport default antfu()\n"
},
{
"path": "examples/vite/App.vue",
"chars": 410,
"preview": "<script setup lang='ts'>\nimport { ref } from 'vue'\nimport ComponentA from './README.md'\n\nconst current = ref()\n</script>"
},
{
"path": "examples/vite/Counter.vue",
"chars": 459,
"preview": "<script setup lang=\"ts\">\nimport { ref } from 'vue'\n\nconst props = defineProps<{ init?: number }>()\n\nconst counter = ref("
},
{
"path": "examples/vite/Counter2.vue",
"chars": 461,
"preview": "<script setup lang=\"ts\">\nimport { ref } from 'vue'\n\nconst props = defineProps<{ init?: number }>()\n\nconst counter = ref("
},
{
"path": "examples/vite/README.md",
"chars": 7114,
"preview": "# unplugin-vue-markdown\n\n[](https://www.n"
},
{
"path": "examples/vite/index.html",
"chars": 633,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, in"
},
{
"path": "examples/vite/main.ts",
"chars": 698,
"preview": "import { createHead } from '@unhead/vue/client'\nimport routes from 'pages-generated'\nimport { createApp } from 'vue'\nimp"
},
{
"path": "examples/vite/package.json",
"chars": 571,
"preview": "{\n \"type\": \"module\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"DEBUG=\\\"unplugin-vue-markdown:*\\\" vite\",\n \"build\""
},
{
"path": "examples/vite/pages/index.md",
"chars": 370,
"preview": "---\ntitle: Hello\nmeta:\n - name: description\n content: Hello World\ntest: test\n---\n\n# Hello world!\n\nFront matter: {{ f"
},
{
"path": "examples/vite/pages/route.vue",
"chars": 91,
"preview": "<template>\n route test\n</template>\n\n<route>\n{\n meta: {\n layout: 'home'\n }\n}\n</route>\n"
},
{
"path": "examples/vite/vite.config.ts",
"chars": 606,
"preview": "import Vue from '@vitejs/plugin-vue'\nimport prism from 'markdown-it-prism'\nimport Markdown from 'unplugin-vue-markdown/v"
},
{
"path": "examples/vite/vue-shim.d.ts",
"chars": 274,
"preview": "declare module '*.vue' {\n import type { ComponentOptions } from 'vue'\n\n const Component: ComponentOptions\n export def"
},
{
"path": "examples/vue-cli/babel.config.js",
"chars": 75,
"preview": "module.exports = {\n presets: [\n '@vue/cli-plugin-babel/preset',\n ],\n}\n"
},
{
"path": "examples/vue-cli/jsconfig.json",
"chars": 279,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\n \"esnext\",\n \"dom\",\n \"dom.iterable\",\n \"scr"
},
{
"path": "examples/vue-cli/package.json",
"chars": 540,
"preview": "{\n \"name\": \"example-vue-cli\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"vue-cli-service serve"
},
{
"path": "examples/vue-cli/public/index.html",
"chars": 555,
"preview": "<!DOCTYPE html>\n<html lang=\"\">\n <head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=ed"
},
{
"path": "examples/vue-cli/src/App.vue",
"chars": 376,
"preview": "<script>\nimport Markdown from './Hi.md'\n\nexport default {\n name: 'App',\n components: {\n Markdown,\n },\n}\n</script>\n"
},
{
"path": "examples/vue-cli/src/Hi.md",
"chars": 13,
"preview": "# Hello\n\nHey\n"
},
{
"path": "examples/vue-cli/src/main.js",
"chars": 90,
"preview": "import { createApp } from 'vue'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
},
{
"path": "examples/vue-cli/vue.config.js",
"chars": 461,
"preview": "const { defineConfig } = require('@vue/cli-service')\nconst prism = require('markdown-it-prism')\nconst Markdown = require"
},
{
"path": "package.json",
"chars": 2968,
"preview": "{\n \"name\": \"unplugin-vue-markdown\",\n \"type\": \"module\",\n \"version\": \"30.0.0\",\n \"packageManager\": \"pnpm@10.17.1\",\n \"d"
},
{
"path": "pnpm-workspace.yaml",
"chars": 129,
"preview": "packages:\n - examples/*\n\nignoreWorkspaceRootCheck: true\nstrictPeerDependencies: false\nshamefullyHoist: true\nshellEmulat"
},
{
"path": "scripts/postbuild.ts",
"chars": 700,
"preview": "import { promises as fs } from 'node:fs'\nimport { basename, dirname, resolve } from 'node:path'\nimport { fileURLToPath }"
},
{
"path": "src/core/head.ts",
"chars": 1871,
"preview": "import type { Frontmatter, MetaProperty, ResolvedOptions } from '../types'\n\nconst headProperties = [\n 'title',\n 'meta'"
},
{
"path": "src/core/markdown.ts",
"chars": 7070,
"preview": "import type { TransformResult } from 'vite'\nimport type { MarkdownEnv, ResolvedOptions } from '../types'\nimport { toArra"
},
{
"path": "src/core/options.ts",
"chars": 1744,
"preview": "import type { Options, ResolvedOptions } from '../types'\nimport { getVueVersion } from './utils'\n\nexport function resolv"
},
{
"path": "src/core/utils.ts",
"chars": 359,
"preview": "import { createRequire } from 'node:module'\n\nconst _require = typeof require === 'undefined'\n ? createRequire(import.me"
},
{
"path": "src/esbuild.ts",
"chars": 136,
"preview": "import { createEsbuildPlugin } from 'unplugin'\nimport { unpluginFactory } from '.'\n\nexport default createEsbuildPlugin(u"
},
{
"path": "src/index.ts",
"chars": 1257,
"preview": "import type { UnpluginFactory } from 'unplugin'\nimport type { Options } from './types'\nimport { createUnplugin } from 'u"
},
{
"path": "src/rollup.ts",
"chars": 134,
"preview": "import { createRollupPlugin } from 'unplugin'\nimport { unpluginFactory } from '.'\n\nexport default createRollupPlugin(unp"
},
{
"path": "src/rspack.ts",
"chars": 134,
"preview": "import { createRspackPlugin } from 'unplugin'\nimport { unpluginFactory } from '.'\n\nexport default createRspackPlugin(unp"
},
{
"path": "src/types.ts",
"chars": 5626,
"preview": "import type { ComponentPluginOptions } from '@mdit-vue/plugin-component'\nimport type { FrontmatterPluginOptions } from '"
},
{
"path": "src/vite.ts",
"chars": 130,
"preview": "import { createVitePlugin } from 'unplugin'\nimport { unpluginFactory } from '.'\n\nexport default createVitePlugin(unplugi"
},
{
"path": "src/webpack.ts",
"chars": 143,
"preview": "import { createWebpackPlugin } from 'unplugin'\nimport { unpluginFactory } from '.'\n\nexport default createWebpackPlugin(u"
},
{
"path": "test/__snapshots__/excerpt.test.ts.snap",
"chars": 1207,
"preview": "// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html\n\nexports[`excerpt > raw excerpt 1`] = `\n\"<template><div cl"
},
{
"path": "test/__snapshots__/transform.test.ts.snap",
"chars": 2926,
"preview": "// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html\n\nexports[`transform > basic 1`] = `\n\"<template><div class="
},
{
"path": "test/excerpt.test.ts",
"chars": 1213,
"preview": "import { describe, expect, it } from 'vitest'\nimport { createMarkdown } from '../src/core/markdown'\nimport { resolveOpti"
},
{
"path": "test/fixtures/simple.md",
"chars": 135,
"preview": "---\ntitle: Hello World\ndescription: testing is the path to true happiness\n---\n\n# My H1\n\nlorem ipsum\n\n## Transformed\n\nbas"
},
{
"path": "test/frontmatterPreprocessor.test.ts",
"chars": 1836,
"preview": "import type { MetaProperty, ResolvedOptions } from '../src/types'\nimport { readFile } from 'node:fs/promises'\nimport { d"
},
{
"path": "test/transform.test.ts",
"chars": 2210,
"preview": "import { describe, expect, it } from 'vitest'\nimport { createMarkdown } from '../src/core/markdown'\nimport { resolveOpti"
},
{
"path": "tsconfig.json",
"chars": 352,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2020\",\n \"jsx\": \"preserve\",\n \"lib\": [\"esnext\", \"DOM\"],\n \"module\": \"esn"
},
{
"path": "tsup.config.ts",
"chars": 225,
"preview": "import type { Options } from 'tsup'\n\nexport default <Options>{\n entryPoints: [\n 'src/*.ts',\n ],\n clean: true,\n fo"
}
]
About this extraction
This page contains the full source code of the unplugin/unplugin-vue-markdown GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 49 files (55.7 KB), approximately 16.2k tokens, and a symbol index with 17 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.