fix CodePreview shiki dynamic load for frameworks like Next.js

This commit is contained in:
dswbx
2025-10-31 09:24:45 +01:00
parent be39e8a391
commit 2178e0ee8b
2 changed files with 32 additions and 3 deletions

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useTheme } from "ui/client/use-theme"; import { useTheme } from "ui/client/use-theme";
import { cn } from "ui/lib/utils"; import { cn, importDynamicBrowserModule } from "ui/lib/utils";
export type CodePreviewProps = { export type CodePreviewProps = {
code: string; code: string;
@@ -30,8 +30,10 @@ export const CodePreview = ({
async function highlightCode() { async function highlightCode() {
try { try {
// Dynamically import Shiki from CDN // Dynamically import Shiki from CDN
// @ts-expect-error - Dynamic CDN import const { codeToHtml } = await importDynamicBrowserModule(
const { codeToHtml } = await import("https://esm.sh/shiki@3.13.0"); "shiki",
"https://esm.sh/shiki@3.13.0",
);
if (cancelled) return; if (cancelled) return;

View File

@@ -3,3 +3,30 @@ import { type ClassNameValue, twMerge } from "tailwind-merge";
export function cn(...inputs: ClassNameValue[]) { export function cn(...inputs: ClassNameValue[]) {
return twMerge(inputs); return twMerge(inputs);
} }
/**
* Dynamically import a module from a URL in the browser in a way compatible with all react frameworks (nextjs doesn't support dynamic imports)
*/
export async function importDynamicBrowserModule<T = any>(name: string, url: string): Promise<T> {
if (!(window as any)[name]) {
const script = document.createElement("script");
script.type = "module";
script.async = true;
script.textContent = `import * as ${name} from '${url}';window.${name} = ${name};`;
document.head.appendChild(script);
// poll for the module to be available
const maxAttempts = 50; // 5s
let attempts = 0;
while (!(window as any)[name] && attempts < maxAttempts) {
await new Promise((resolve) => setTimeout(resolve, 100));
attempts++;
}
if (!(window as any)[name]) {
throw new Error(`Browser module "${name}" failed to load`);
}
}
return (window as any)[name] as T;
}