mirror of https://github.com/vuejs/vitepress
Vendor @mdit-vue/plugin-frontmatter and pad the stripped frontmatter with blank lines before parsing, so every token.map stays in the coordinates of the file instead of shifting up by the frontmatter height. This deletes the contentLineOffset / src.endsWith(content) reconstruction in markdownToVue. Offering the padding upstream as a preserveLines option is a follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>feat/md-sourcemaps
parent
09f9672ee1
commit
0a41ecbe17
@ -0,0 +1,39 @@
|
|||||||
|
import type { FrontmatterPluginOptions } from '@mdit-vue/plugin-frontmatter'
|
||||||
|
import matter from 'gray-matter'
|
||||||
|
import type { MarkdownItAsync } from 'markdown-it-async'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vendored from `@mdit-vue/plugin-frontmatter` (extracts `env.frontmatter`,
|
||||||
|
* `env.content` and `env.excerpt` before parsing), with one behavioral
|
||||||
|
* change: the stripped frontmatter block is replaced with blank lines, so
|
||||||
|
* every `token.map` stays in the coordinates of the full source instead of
|
||||||
|
* shifting up by the frontmatter height. Blank lines produce no tokens, so
|
||||||
|
* the rendered output is unchanged. Offering this upstream as a
|
||||||
|
* `preserveLines` option is tracked as a follow-up.
|
||||||
|
*/
|
||||||
|
export function frontmatterPlugin(
|
||||||
|
md: MarkdownItAsync,
|
||||||
|
{ grayMatterOptions, renderExcerpt = true }: FrontmatterPluginOptions = {}
|
||||||
|
): void {
|
||||||
|
const parse = md.parse.bind(md)
|
||||||
|
md.parse = (src, env: Record<string, unknown> = {}) => {
|
||||||
|
const { data, content, excerpt = '' } = matter(src, grayMatterOptions)
|
||||||
|
|
||||||
|
env.content = content
|
||||||
|
env.frontmatter = { ...(env.frontmatter as object), ...data }
|
||||||
|
env.excerpt =
|
||||||
|
renderExcerpt && excerpt ? md.render(excerpt, { ...env }) : excerpt
|
||||||
|
|
||||||
|
// gray-matter only ever removes lines from the top of the file, so the
|
||||||
|
// difference in line-break counts is exactly the removed line count
|
||||||
|
const removed =
|
||||||
|
src.length === content.length
|
||||||
|
? 0
|
||||||
|
: countLineBreaks(src) - countLineBreaks(content)
|
||||||
|
return parse(removed > 0 ? '\n'.repeat(removed) + content : content, env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function countLineBreaks(str: string): number {
|
||||||
|
return str.match(/\r\n|[\r\n]/g)?.length ?? 0
|
||||||
|
}
|
||||||
Loading…
Reference in new issue