Spaces:
Runtime error
Runtime error
Commit
·
6e2a902
1
Parent(s):
23183ec
Fix DOMPurify SSR issues by using dynamic imports
Browse files
src/lib/components/CodeBlock.svelte
CHANGED
|
@@ -1,9 +1,10 @@
|
|
| 1 |
<script lang="ts">
|
| 2 |
import CopyToClipBoardBtn from "./CopyToClipBoardBtn.svelte";
|
| 3 |
-
import DOMPurify from "isomorphic-dompurify";
|
| 4 |
import HtmlPreviewModal from "./HtmlPreviewModal.svelte";
|
| 5 |
import PlayFilledAlt from "~icons/carbon/play-filled-alt";
|
| 6 |
import EosIconsLoading from "~icons/eos-icons/loading";
|
|
|
|
|
|
|
| 7 |
|
| 8 |
interface Props {
|
| 9 |
code?: string;
|
|
@@ -13,6 +14,9 @@
|
|
| 13 |
|
| 14 |
let { code = "", rawCode = "", loading = false }: Props = $props();
|
| 15 |
|
|
|
|
|
|
|
|
|
|
| 16 |
let previewOpen = $state(false);
|
| 17 |
|
| 18 |
function hasStrictHtml5Doctype(input: string): boolean {
|
|
@@ -29,6 +33,21 @@
|
|
| 29 |
}
|
| 30 |
|
| 31 |
let showPreview = $derived(hasStrictHtml5Doctype(rawCode) || isSvgDocument(rawCode));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
</script>
|
| 33 |
|
| 34 |
<div class="group relative my-4 rounded-lg">
|
|
@@ -64,7 +83,7 @@
|
|
| 64 |
</div>
|
| 65 |
</div>
|
| 66 |
<pre class="scrollbar-custom overflow-auto px-5 font-mono transition-[height]"><code
|
| 67 |
-
><!-- eslint-disable svelte/no-at-html-tags -->{@html
|
| 68 |
></pre>
|
| 69 |
|
| 70 |
{#if previewOpen}
|
|
|
|
| 1 |
<script lang="ts">
|
| 2 |
import CopyToClipBoardBtn from "./CopyToClipBoardBtn.svelte";
|
|
|
|
| 3 |
import HtmlPreviewModal from "./HtmlPreviewModal.svelte";
|
| 4 |
import PlayFilledAlt from "~icons/carbon/play-filled-alt";
|
| 5 |
import EosIconsLoading from "~icons/eos-icons/loading";
|
| 6 |
+
import { browser } from "$app/environment";
|
| 7 |
+
import { onMount } from "svelte";
|
| 8 |
|
| 9 |
interface Props {
|
| 10 |
code?: string;
|
|
|
|
| 14 |
|
| 15 |
let { code = "", rawCode = "", loading = false }: Props = $props();
|
| 16 |
|
| 17 |
+
let DOMPurify: typeof import("isomorphic-dompurify").default | null = null;
|
| 18 |
+
let sanitizedCode = $state(code);
|
| 19 |
+
|
| 20 |
let previewOpen = $state(false);
|
| 21 |
|
| 22 |
function hasStrictHtml5Doctype(input: string): boolean {
|
|
|
|
| 33 |
}
|
| 34 |
|
| 35 |
let showPreview = $derived(hasStrictHtml5Doctype(rawCode) || isSvgDocument(rawCode));
|
| 36 |
+
|
| 37 |
+
onMount(async () => {
|
| 38 |
+
if (browser) {
|
| 39 |
+
const { default: purify } = await import("isomorphic-dompurify");
|
| 40 |
+
DOMPurify = purify;
|
| 41 |
+
}
|
| 42 |
+
});
|
| 43 |
+
|
| 44 |
+
$effect(() => {
|
| 45 |
+
if (DOMPurify) {
|
| 46 |
+
sanitizedCode = DOMPurify.sanitize(code);
|
| 47 |
+
} else {
|
| 48 |
+
sanitizedCode = code;
|
| 49 |
+
}
|
| 50 |
+
});
|
| 51 |
</script>
|
| 52 |
|
| 53 |
<div class="group relative my-4 rounded-lg">
|
|
|
|
| 83 |
</div>
|
| 84 |
</div>
|
| 85 |
<pre class="scrollbar-custom overflow-auto px-5 font-mono transition-[height]"><code
|
| 86 |
+
><!-- eslint-disable svelte/no-at-html-tags -->{@html sanitizedCode}</code
|
| 87 |
></pre>
|
| 88 |
|
| 89 |
{#if previewOpen}
|
src/lib/components/chat/MarkdownRenderer.svelte
CHANGED
|
@@ -5,10 +5,11 @@
|
|
| 5 |
import type { IncomingMessage, OutgoingMessage } from "$lib/workers/markdownWorker";
|
| 6 |
import { browser } from "$app/environment";
|
| 7 |
|
| 8 |
-
import DOMPurify from "isomorphic-dompurify";
|
| 9 |
import { onMount } from "svelte";
|
| 10 |
import { updateDebouncer } from "$lib/utils/updates";
|
| 11 |
|
|
|
|
|
|
|
| 12 |
interface Props {
|
| 13 |
content: string;
|
| 14 |
sources?: { title?: string; link: string }[];
|
|
@@ -55,7 +56,7 @@
|
|
| 55 |
async (tokens) =>
|
| 56 |
await Promise.all(
|
| 57 |
tokens.map(async (token) => {
|
| 58 |
-
if (token.type === "text") {
|
| 59 |
token.html = DOMPurify.sanitize(await token.html);
|
| 60 |
}
|
| 61 |
return token;
|
|
@@ -68,10 +69,14 @@
|
|
| 68 |
}
|
| 69 |
});
|
| 70 |
|
| 71 |
-
onMount(() => {
|
| 72 |
// todo: fix worker, seems to be transmitting a lot of data
|
| 73 |
// worker = browser && window.Worker ? new MarkdownWorker() : null;
|
| 74 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
DOMPurify.addHook("afterSanitizeAttributes", (node) => {
|
| 76 |
if (node.tagName === "A") {
|
| 77 |
node.setAttribute("target", "_blank");
|
|
|
|
| 5 |
import type { IncomingMessage, OutgoingMessage } from "$lib/workers/markdownWorker";
|
| 6 |
import { browser } from "$app/environment";
|
| 7 |
|
|
|
|
| 8 |
import { onMount } from "svelte";
|
| 9 |
import { updateDebouncer } from "$lib/utils/updates";
|
| 10 |
|
| 11 |
+
let DOMPurify: typeof import("isomorphic-dompurify").default | null = null;
|
| 12 |
+
|
| 13 |
interface Props {
|
| 14 |
content: string;
|
| 15 |
sources?: { title?: string; link: string }[];
|
|
|
|
| 56 |
async (tokens) =>
|
| 57 |
await Promise.all(
|
| 58 |
tokens.map(async (token) => {
|
| 59 |
+
if (token.type === "text" && DOMPurify) {
|
| 60 |
token.html = DOMPurify.sanitize(await token.html);
|
| 61 |
}
|
| 62 |
return token;
|
|
|
|
| 69 |
}
|
| 70 |
});
|
| 71 |
|
| 72 |
+
onMount(async () => {
|
| 73 |
// todo: fix worker, seems to be transmitting a lot of data
|
| 74 |
// worker = browser && window.Worker ? new MarkdownWorker() : null;
|
| 75 |
|
| 76 |
+
// Dynamically import DOMPurify only on the client
|
| 77 |
+
const { default: purify } = await import("isomorphic-dompurify");
|
| 78 |
+
DOMPurify = purify;
|
| 79 |
+
|
| 80 |
DOMPurify.addHook("afterSanitizeAttributes", (node) => {
|
| 81 |
if (node.tagName === "A") {
|
| 82 |
node.setAttribute("target", "_blank");
|
vite.config.ts
CHANGED
|
@@ -38,15 +38,6 @@ export default defineConfig({
|
|
| 38 |
optimizeDeps: {
|
| 39 |
include: ["uuid", "sharp", "@gradio/client", "clsx"],
|
| 40 |
},
|
| 41 |
-
ssr: {
|
| 42 |
-
noExternal: [
|
| 43 |
-
"isomorphic-dompurify",
|
| 44 |
-
"dompurify",
|
| 45 |
-
"@asamuzakjp/css-color",
|
| 46 |
-
"@asamuzakjp/dom-selector",
|
| 47 |
-
],
|
| 48 |
-
external: ["jsdom"],
|
| 49 |
-
},
|
| 50 |
test: {
|
| 51 |
workspace: [
|
| 52 |
{
|
|
|
|
| 38 |
optimizeDeps: {
|
| 39 |
include: ["uuid", "sharp", "@gradio/client", "clsx"],
|
| 40 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
test: {
|
| 42 |
workspace: [
|
| 43 |
{
|