mirror of https://github.com/vuejs/vitepress
parent
96c30a6d83
commit
9cbfa9529d
@ -1,35 +0,0 @@
|
||||
import path from 'path'
|
||||
import { createMarkdownRenderer, MarkdownOpitons } from './markdown'
|
||||
import LRUCache from 'lru-cache'
|
||||
|
||||
const matter = require('gray-matter')
|
||||
const debug = require('debug')('vitepress:md')
|
||||
const cache = new LRUCache<string, string>({ max: 1024 })
|
||||
|
||||
export function createMarkdownToVueRenderFn(
|
||||
root: string,
|
||||
options: MarkdownOpitons = {}
|
||||
) {
|
||||
const md = createMarkdownRenderer(options)
|
||||
|
||||
return (src: string, file: string) => {
|
||||
file = path.relative(root, file)
|
||||
const cached = cache.get(src)
|
||||
if (cached) {
|
||||
debug(`[cache hit] ${file}`)
|
||||
return cached
|
||||
}
|
||||
const start = Date.now()
|
||||
|
||||
const { content, data } = matter(src)
|
||||
const { html } = md.render(content)
|
||||
|
||||
// TODO validate links?
|
||||
|
||||
const vueSrc =
|
||||
`<template>${html}</template>` + (data.hoistedTags || []).join('\n')
|
||||
debug(`[render] ${file} in ${Date.now() - start}ms.`, data)
|
||||
cache.set(src, vueSrc)
|
||||
return vueSrc
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { MarkdownParsedData } from '../markdown'
|
||||
import { deeplyParseHeader } from '../../utils/parseHeader'
|
||||
import { slugify } from './slugify'
|
||||
|
||||
export interface Header {
|
||||
level: number
|
||||
title: string
|
||||
slug: string
|
||||
}
|
||||
|
||||
export const extractHeaderPlugin = (
|
||||
md: MarkdownIt & { __data: MarkdownParsedData },
|
||||
include = ['h2', 'h3']
|
||||
) => {
|
||||
md.renderer.rules.heading_open = (tokens, i, options, env, self) => {
|
||||
const token = tokens[i]
|
||||
if (include.includes(token.tag)) {
|
||||
const title = tokens[i + 1].content
|
||||
const idAttr = token.attrs!.find(([name]) => name === 'id')
|
||||
const slug = idAttr && idAttr[1]
|
||||
const data = md.__data
|
||||
const headers = data.headers || (data.headers = [])
|
||||
headers.push({
|
||||
level: parseInt(token.tag.slice(1), 10),
|
||||
title: deeplyParseHeader(title),
|
||||
slug: slug || slugify(title)
|
||||
})
|
||||
}
|
||||
return self.renderToken(tokens, i, options)
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
import path from 'path'
|
||||
import matter from 'gray-matter'
|
||||
import LRUCache from 'lru-cache'
|
||||
import { createMarkdownRenderer, MarkdownOpitons } from './markdown/markdown'
|
||||
import { Header } from './markdown/plugins/header'
|
||||
import { deeplyParseHeader } from './utils/parseHeader'
|
||||
|
||||
const debug = require('debug')('vitepress:md')
|
||||
const cache = new LRUCache<string, string>({ max: 1024 })
|
||||
|
||||
export function createMarkdownToVueRenderFn(
|
||||
root: string,
|
||||
options: MarkdownOpitons = {}
|
||||
) {
|
||||
const md = createMarkdownRenderer(options)
|
||||
|
||||
return (src: string, file: string, lastUpdated: number) => {
|
||||
file = path.relative(root, file)
|
||||
const cached = cache.get(src)
|
||||
if (cached) {
|
||||
debug(`[cache hit] ${file}`)
|
||||
return cached
|
||||
}
|
||||
const start = Date.now()
|
||||
|
||||
const { content, data: frontmatter } = matter(src)
|
||||
const { html, data } = md.render(content)
|
||||
|
||||
// TODO validate data.links?
|
||||
|
||||
// inject page data
|
||||
const additionalBlocks = injectPageData(
|
||||
data.hoistedTags || [],
|
||||
content,
|
||||
frontmatter,
|
||||
data.headers || [],
|
||||
lastUpdated
|
||||
)
|
||||
|
||||
const vueSrc =
|
||||
`<template><div class="vitepress-content">${html}</div></template>\n` +
|
||||
additionalBlocks.join('\n')
|
||||
debug(`[render] ${file} in ${Date.now() - start}ms.`)
|
||||
cache.set(src, vueSrc)
|
||||
return vueSrc
|
||||
}
|
||||
}
|
||||
|
||||
const scriptRE = /<\/script>/
|
||||
function injectPageData(
|
||||
tags: string[],
|
||||
content: string,
|
||||
frontmatter: object,
|
||||
headers: Header[],
|
||||
lastUpdated: number
|
||||
) {
|
||||
const code = `\nexport const __pageData = ${JSON.stringify({
|
||||
title: inferTitle(frontmatter, content),
|
||||
frontmatter,
|
||||
headers,
|
||||
lastUpdated
|
||||
})}`
|
||||
|
||||
const existingScriptIndex = tags.findIndex((tag) => scriptRE.test(tag))
|
||||
if (existingScriptIndex > -1) {
|
||||
tags[existingScriptIndex] = tags[existingScriptIndex].replace(
|
||||
scriptRE,
|
||||
code + `</script>`
|
||||
)
|
||||
} else {
|
||||
tags.push(`<script>${code}\nexport default {}</script>`)
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
const inferTitle = (frontmatter: any, content: string) => {
|
||||
if (frontmatter.home) {
|
||||
return 'Home'
|
||||
}
|
||||
if (frontmatter.title) {
|
||||
return deeplyParseHeader(frontmatter.title)
|
||||
}
|
||||
const match = content.match(/^\s*#+\s+(.*)/m)
|
||||
if (match) {
|
||||
return deeplyParseHeader(match[1].trim())
|
||||
}
|
||||
}
|
Loading…
Reference in new issue