Skip to content

Commit 2218ae5

Browse files
committed
implement remark-youtube
1 parent 4201b7e commit 2218ae5

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

MyApp.Client/lib/markdownToHtml.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ import remarkParse from "remark-parse"
33
import remarkGfm from "remark-gfm"
44
import remarkRehype from "remark-rehype"
55
import rehypeStringify from "rehype-stringify"
6+
import rehypePrism from "rehype-prism-plus"
7+
import remarkYoutube from "./remark-youtube"
68

79

810
export default async function markdownToHtml(markdown: string) {
911
const result = await unified()
1012
.use(remarkParse)
1113
.use(remarkGfm)
14+
.use(remarkYoutube)
1215
.use(remarkRehype, { allowDangerousHtml: true })
16+
.use(rehypePrism)
1317
.use(rehypeStringify, { allowDangerousHtml: true })
1418
.process(markdown)
1519
return result.toString()

MyApp.Client/lib/remark-youtube.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { visit } from 'unist-util-visit'
2+
import type { Root, Paragraph, Text } from 'mdast'
3+
4+
/**
5+
* Remark plugin to transform :::youtube directives into embedded YouTube iframes
6+
*
7+
* Syntax:
8+
* :::youtube VIDEO_ID
9+
* Video Title
10+
* :::
11+
*/
12+
export default function remarkYoutube() {
13+
return (tree: Root) => {
14+
visit(tree, 'paragraph', (node: Paragraph, index, parent) => {
15+
if (!parent || index === undefined) return
16+
17+
// Check if this paragraph contains a youtube directive
18+
const firstChild = node.children[0]
19+
if (firstChild?.type !== 'text') return
20+
21+
const text = (firstChild as Text).value
22+
23+
// Match the pattern: :::youtube VIDEO_ID\nTitle\n:::
24+
const youtubePattern = /^:::youtube\s+([^\s\n]+)\s*\n(.+?)\n:::$/s
25+
const match = text.match(youtubePattern)
26+
27+
if (match) {
28+
const videoId = match[1].trim()
29+
const title = match[2].trim()
30+
31+
// Create the HTML node for the YouTube embed
32+
const htmlNode = {
33+
type: 'html',
34+
value: `<iframe class="youtube" src="https://www.youtube.com/embed/${videoId}" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen title="${title}"></iframe>`
35+
}
36+
37+
// Replace the paragraph with the HTML node
38+
parent.children[index] = htmlNode as any
39+
}
40+
})
41+
}
42+
}
43+

0 commit comments

Comments
 (0)