diff --git a/components/content/ContentCode.vue b/components/content/ContentCode.vue
index 7b034b3c..b4d2f61c 100644
--- a/components/content/ContentCode.vue
+++ b/components/content/ContentCode.vue
@@ -18,5 +18,6 @@ const highlighted = computed(() => {
-
+
+ {{ raw }}
diff --git a/composables/shiki.ts b/composables/shiki.ts
index a35e55d5..f316d24c 100644
--- a/composables/shiki.ts
+++ b/composables/shiki.ts
@@ -48,10 +48,22 @@ export function useShikiTheme() {
return useColorMode().value === 'dark' ? 'vitesse-dark' : 'vitesse-light'
}
+const HTML_ENTITIES = {
+ '<': '<',
+ '>': '>',
+ '&': '&',
+ '\'': ''',
+ '"': '"',
+} as Record[(\`number string) (\`tag string)]
[(\`number string) (\`tag string)]"`; + +exports[`content-rich > block with injected html, with a known language 1`] = ` +"
+
+ <a href="javascript:alert(1)">click me</a>
+
+
+"
+`;
+
+exports[`content-rich > block with injected html, with an unknown language 1`] = `
+"
+
+ <a href="javascript:alert(1)">click me</a>
+
+
+"
+`;
+
+exports[`content-rich > block with injected html, without language 1`] = `
+"
+
+ <a href="javascript:alert(1)">click me</a>
+
+
+"
+`;
exports[`content-rich > code frame 1`] = `
-"Testing code block
import { useMouse, usePreferredDark } from '@vueuse/core'
+"Testing code block
import { useMouse, usePreferredDark } from '@vueuse/core'
// tracks mouse position
const { x, y } = useMouse()
// is the user prefers dark theme
@@ -20,14 +47,14 @@ exports[`content-rich > code frame 2 1`] = `
>
Testing
- const a = hello
+ const a = hello
"
`;
-exports[`content-rich > code frame empty 1`] = `"
"`;
+exports[`content-rich > code frame empty 1`] = `"
"`;
-exports[`content-rich > code frame no lang 1`] = `"hello world
no lang"`;
+exports[`content-rich > code frame no lang 1`] = `"hello world
no lang"`;
exports[`content-rich > custom emoji 1`] = `
"Daniel Roe
@@ -75,7 +102,7 @@ exports[`content-rich > handles formatting from servers 1`] = `
exports[`content-rich > handles html within code blocks 1`] = `
"
HTML block code:
-
+
<span class="icon--noto icon--noto--1st-place-medal"></span>
<span class="icon--noto icon--noto--2nd-place-medal-medal"></span>
diff --git a/tests/content-rich.test.ts b/tests/content-rich.test.ts
index 97d2b8f0..6b4a94e3 100644
--- a/tests/content-rich.test.ts
+++ b/tests/content-rich.test.ts
@@ -136,6 +136,39 @@ describe('content-rich', () => {
"
`)
})
+
+ it ('block with injected html, without language', async () => {
+ const { formatted } = await render(`
+
+
+ <a href="javascript:alert(1)">click me</a>
+
+
+ `)
+ expect(formatted).toMatchSnapshot()
+ })
+
+ it ('block with injected html, with an unknown language', async () => {
+ const { formatted } = await render(`
+
+
+ <a href="javascript:alert(1)">click me</a>
+
+
+ `)
+ expect(formatted).toMatchSnapshot()
+ })
+
+ it ('block with injected html, with a known language', async () => {
+ const { formatted } = await render(`
+
+
+ <a href="javascript:alert(1)">click me</a>
+
+
+ `)
+ expect(formatted).toMatchSnapshot()
+ })
})
async function render(content: string, options?: ContentParseOptions) {
@@ -173,23 +206,11 @@ vi.mock('~/composables/dialog.ts', () => {
return {}
})
-vi.mock('~/components/content/ContentCode.vue', () => {
+vi.mock('shiki-es', async (importOriginal) => {
+ const mod = await importOriginal()
return {
- default: defineComponent({
- props: {
- code: {
- type: String,
- required: true,
- },
- lang: {
- type: String,
- },
- },
- setup(props) {
- const raw = computed(() => decodeURIComponent(props.code).replace(/'/g, '\''))
- return () => h('pre', { lang: props.lang }, raw.value)
- },
- }),
+ ...(mod as any),
+ setCDN() {},
}
})
diff --git a/vitest.config.ts b/vitest.config.ts
index 413bb3f7..ac4388d7 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -14,7 +14,9 @@ export default defineConfig({
'process.client': 'true',
},
plugins: [
- Vue(),
+ Vue({
+ reactivityTransform: true,
+ }),
AutoImport({
dts: false,
imports: [