Skip to content

#tech/vitepress

vitepress

config.js 中增加块的转义

js
//markdown配置  
markdown: {  
    linkify: false,  
    lineNumbers: true,  
    config: (md) => {  
        /**
         * ::: demo
         * aaa
         * :::
         * 
         * ==>
         *
         * <yv-button>
         *     aaa
         * </yv-button>
         */
        md.use(mdContainer, 'demo', {
            validate(params) {
                // console.log('demo:' + params.trim())
                // debugger
                return params.trim() === 'demo'
            },
            render(tokens, idx) {
                if (tokens[idx].type === 'container_demo_open') {
                    return '<yv-button>'
                }
                if (tokens[idx].type === 'container_demo_close') {
                    return '</yv-button>'
                }
            },
        });
    }
}
typescript
/**
 * 解析 Obsidian 的 CallOut 语法
 * > [!CallOutType] CallOutTitle
 * > CallOutContent1
 * > CallOutContent2
 * > CallOutContent3
 *
 * 解析至成
 *
 * <YvCallOut type="CallOutType" title="CallOutTitle">
 *     <slot />
 * </YvCallOut>
 */
function CallOutFn(md) {
    const RE = new RegExp(`^\\[\\!(\\w+)\\]([^\\n\\r]*)`, 'i')

    md.core.ruler.after('block', 'github-alerts', (state) => {
        const tokens = state.tokens
        for (let i = 0; i < tokens.length; i++) {
            if (tokens[i].type === 'blockquote_open') {
                const open = tokens[i]
                const startIndex = i
                while (tokens[i]?.type !== 'blockquote_close' && i <= tokens.length)
                    i += 1
                const close = tokens[i]
                const endIndex = i
                const firstContent = tokens.slice(startIndex, endIndex + 1).find(token => token.type === 'inline')
                if (!firstContent)
                    continue
                const match = firstContent.content.match(RE)
                if (!match)
                    continue
                const type = match[1].toLowerCase()
                const title = match[2].trim() || capitalize(type)
                firstContent.content = firstContent.content.slice(match[0].length).trimStart()
                open.type = 'alert_open'
                open.tag = 'YvCallOut'
                open.meta = {
                    title,
                    type,
                }
                close.type = 'alert_close'
                close.tag = 'YvCallOut'
            }
        }
    })
    md.renderer.rules.alert_open = function (tokens, idx) {
        const {title, type} = tokens[idx].meta
        console.log('alertOpen', title, type)
        return `<YvCallOut type="${type}" title="${title}">`
    }
}

function capitalize(str: string) {
    return str.charAt(0).toUpperCase() + str.slice(1)
}

export default CallOutFn

注册全局组件

config 同级文件夹,建立 theme/index.ts 文件 在 enhanceApp 中,注册全局组件

js
import {h, onMounted, watch, nextTick} from 'vue'
import {useData, useRoute} from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import './style.css'
import "vitepress-markdown-timeline/dist/theme/index.css"
import YvButton from './components/YvButton.vue'

console.log('DefaultTheme', DefaultTheme)

export default {
    extends: DefaultTheme,

    enhanceApp({app}) {
        // 注册全局组件
        app.component('YvButton', YvButton)
    },

    Layout: () => {
        const props: Record<string, any> = {}
        // 获取 frontmatter
        const {frontmatter} = useData()

        /* 添加自定义 class */
        if (frontmatter.value?.layoutClass) {
            console.log(' frontmatter.value.layoutClass', frontmatter.value.layoutClass)
            props.class = frontmatter.value.layoutClass
        }

        // console.log('props', props)
        return h(DefaultTheme.Layout, props)
    },

    setup() {
        const route = useRoute();

        // Get frontmatter and route
        const {frontmatter} = useData();

        // console.log('setup')
    }
}