From 3b7ff8d66e9eda3aab1fb126984efc63132bd22d Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Wed, 4 Jan 2023 16:27:19 +0530 Subject: [PATCH] feat(build): support interpolation inside code blocks (#1759) --- docs/guide/using-vue.md | 2 +- src/node/markdown/plugins/highlight.ts | 37 ++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/docs/guide/using-vue.md b/docs/guide/using-vue.md index 502cb829..43c75245 100644 --- a/docs/guide/using-vue.md +++ b/docs/guide/using-vue.md @@ -62,7 +62,7 @@ const { page } = useData() ## Escaping -By default, fenced code blocks are automatically wrapped with `v-pre`. To display raw mustaches or Vue-specific syntax inside inline code snippets or plain text, you need to wrap a paragraph with the `v-pre` custom container: +By default, fenced code blocks are automatically wrapped with `v-pre`, unless you have set some language with `-vue` suffix like `js-vue` (in that case you can use Vue-style interpolation inside fences). To display raw mustaches or Vue-specific syntax inside inline code snippets or plain text, you need to wrap a paragraph with the `v-pre` custom container: **Input** diff --git a/src/node/markdown/plugins/highlight.ts b/src/node/markdown/plugins/highlight.ts index 186d9bc9..291473ec 100644 --- a/src/node/markdown/plugins/highlight.ts +++ b/src/node/markdown/plugins/highlight.ts @@ -10,6 +10,9 @@ import { type Processor } from 'shiki-processor' import type { ThemeOptions } from '../markdown' +import { customAlphabet } from 'nanoid' + +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) /** * 2 steps: @@ -75,6 +78,7 @@ export async function highlight( const preRE = /^/ const vueRE = /-vue$/ const lineNoRE = /:(no-)?line-numbers$/ + const mustacheRE = /\{\{.*?\}\}/g return (str: string, lang: string, attrs: string) => { const vPre = vueRE.test(lang) ? '' : 'v-pre' @@ -87,13 +91,36 @@ export async function highlight( .replace(preRE, (_, attributes) => `
`)
         .replace(styleRE, (_, style) => _.replace(style, ''))
 
+    const mustaches = new Map()
+
+    const removeMustache = (s: string) => {
+      if (vPre) return s
+      return s.replace(mustacheRE, (match) => {
+        let marker = mustaches.get(match)
+        if (!marker) {
+          marker = nanoid()
+          mustaches.set(match, marker)
+        }
+        return marker
+      })
+    }
+
+    const restoreMustache = (s: string) => {
+      mustaches.forEach((marker, match) => {
+        s = s.replaceAll(marker, match)
+      })
+      return s
+    }
+
     if (hasSingleTheme) {
       return cleanup(
-        highlighter.codeToHtml(str, {
-          lang,
-          lineOptions,
-          theme: getThemeName(theme)
-        })
+        restoreMustache(
+          highlighter.codeToHtml(removeMustache(str), {
+            lang,
+            lineOptions,
+            theme: getThemeName(theme)
+          })
+        )
       )
     }