返回 slidev
drag.ts
根目录 / packages / slidev / node / syntax / drag.ts
1 import type MagicString from 'magic-string-stack'
2 import type MarkdownExit from 'markdown-exit'
3 import { SourceMapConsumer } from 'source-map-js'
4
5 type Token = ReturnType<MarkdownExit['parseInline']>[number]
6
7 const dragComponentRegex = /<(v-?drag-?\w*)([\s>])/i
8 const dragDirectiveRegex = /(?<![</\w])v-drag(=".*?")?/i
9
10 export default function MarkdownItVDrag(md: MarkdownExit, markdownTransformMap: Map<string, MagicString>) {
11 const visited = new WeakSet()
12 const sourceMapConsumers = new WeakMap<MagicString, SourceMapConsumer>()
13
14 function getSourceMapConsumer(id: string | undefined) {
15 const s = id && markdownTransformMap.get(id)
16 if (!s)
17 return undefined
18 let smc = sourceMapConsumers.get(s)
19 if (smc)
20 return smc
21 const sourceMap = s.generateMap()
22 smc = new SourceMapConsumer({
23 ...sourceMap,
24 version: sourceMap.version.toString(),
25 sourcesContent: sourceMap.sourcesContent?.map(content => content ?? ''),
26 })
27 sourceMapConsumers.set(s, smc)
28 return smc
29 }
30
31 const _parse = md.parse
32 md.parse = function (src, env) {
33 const smc = getSourceMapConsumer(env?.id)
34 const toOriginalPos = smc
35 ? (line: number) => smc.originalPositionFor({ line: line + 1, column: 0 }).line - 1
36 : (line: number) => line
37 function toMarkdownSource(map: [number, number], idx: number) {
38 const start = toOriginalPos(map[0])
39 const end = toOriginalPos(map[1])
40 return `[${start},${Math.max(start + 1, end)},${idx}]`
41 }
42
43 function replaceChildren(token: Token, regex: RegExp, replacement: string) {
44 for (const child of token.children ?? []) {
45 if (child.type === 'html_block' || child.type === 'html_inline') {
46 child.content = child.content.replace(regex, replacement)
47 }
48 replaceChildren(child, regex, replacement)
49 }
50 }
51
52 return _parse.call(this, src, env)
53 .map((token) => {
54 if (!['html_block', 'html_inline', 'inline'].includes(token.type) || !token.content.includes('drag') || visited.has(token))
55 return token
56
57 // Iterates all html tokens and replaces <v-drag> with <v-drag :markdownSource="..."> to pass the markdown source to the component
58 token.content = token.content
59 .replace(dragComponentRegex, (_, tag, space, idx) => {
60 const replacement = `<${tag} :markdownSource="${toMarkdownSource(token.map!, idx)}"${space}`
61 replaceChildren(token, dragComponentRegex, replacement)
62 return replacement
63 })
64 .replace(dragDirectiveRegex, (_, value, idx) => {
65 const replacement = `v-drag${value ?? ''} :markdownSource="${toMarkdownSource(token.map!, idx)}"`
66 replaceChildren(token, dragDirectiveRegex, replacement)
67 return replacement
68 })
69
70 visited.add(token)
71 return token
72 })
73 }
74 }
75
75 lines TYPESCRIPT