diff --git a/packages/console/src/mdx-components/Mermaid/index.tsx b/packages/console/src/mdx-components/Mermaid/index.tsx index e7f7180cb..1cc8c0ed5 100644 --- a/packages/console/src/mdx-components/Mermaid/index.tsx +++ b/packages/console/src/mdx-components/Mermaid/index.tsx @@ -1,15 +1,19 @@ import { Theme } from '@logto/schemas'; -import mermaid from 'mermaid'; +import { type Mermaid as MermaidType } from 'mermaid'; import { useEffect } from 'react'; import useTheme from '@/hooks/use-theme'; -mermaid.initialize({ - startOnLoad: true, - theme: 'default', - securityLevel: 'loose', - fontFamily: 'Fira Code', -}); +const loadMermaid = async () => { + // Define this variable to "outsmart" the detection of the dynamic import by Parcel: + // https://github.com/parcel-bundler/parcel/issues/7064#issuecomment-942441649 + const uri = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const imported: { default: MermaidType } = await import(uri); + return imported.default; +}; + +const mermaidPromise = loadMermaid(); type Props = { readonly children: string; @@ -24,14 +28,16 @@ export default function Mermaid({ children }: Props) { const theme = useTheme(); useEffect(() => { - mermaid.initialize({ - theme: themeToMermaidTheme[theme], - }); + (async () => { + const mermaid = await mermaidPromise; + mermaid.initialize({ + startOnLoad: false, + theme: themeToMermaidTheme[theme], + securityLevel: 'loose', + }); + await mermaid.run(); + })(); }, [theme]); - useEffect(() => { - mermaid.contentLoaded(); - }, []); - return
{children}
; } diff --git a/packages/core/src/middleware/koa-security-headers.ts b/packages/core/src/middleware/koa-security-headers.ts index 7ae1f601e..cde61c93d 100644 --- a/packages/core/src/middleware/koa-security-headers.ts +++ b/packages/core/src/middleware/koa-security-headers.ts @@ -41,13 +41,16 @@ export default function koaSecurityHeaders( /** Google Sign-In (GSI) origin for Google One Tap. */ const gsiOrigin = 'https://accounts.google.com/gsi/'; - // We use react-monaco-editor for code editing in the admin console. It loads the monaco editor asynchronously from a CDN. + // We have the following use cases: + // + // 1. We use `react-monaco-editor` for code editing in the admin console. It loads the monaco + // editor asynchronously from jsDelivr. + // 2. We use `mermaid` for rendering diagrams in the admin console. It loads the mermaid library + // asynchronously from jsDelivr since Parcel has issues with loading it directly in production. + // // Allow the CDN src in the CSP. // Allow blob: for monaco editor to load worker scripts - const monacoEditorCDNSource = [ - 'https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/vs/', - 'blob:', - ]; + const cdnSources = ['https://cdn.jsdelivr.net/', 'blob:']; /** * Default Applied rules: @@ -122,7 +125,7 @@ export default function koaSecurityHeaders( scriptSrc: [ "'self'", ...conditionalArray(!isProduction && ["'unsafe-eval'", "'unsafe-inline'"]), - ...monacoEditorCDNSource, + ...cdnSources, ], connectSrc: ["'self'", logtoOrigin, ...adminOrigins, ...coreOrigins, ...developmentOrigins], // Allow Main Flow origin loaded in preview iframe