| @@ -1,32 +1,68 @@ | ||
| 1 | +import { useEffect, useState } from '@wordpress/element'; | |
| 1 | 2 | import { applyFilters } from '@wordpress/hooks'; |
| 2 | -import { __ } from '@wordpress/i18n'; | |
| 3 | -import { getHighlighter, Lang, setCDN, Theme } from 'shiki'; | |
| 3 | +import { getHighlighter, setCDN, setWasm, type Theme } from 'shiki'; | |
| 4 | 4 | import useSWRImmutable from 'swr/immutable'; |
| 5 | +import defaultThemes from '../defaultThemes.json'; | |
| 6 | +import type { Lang, ThemeOption } from '../types'; | |
| 7 | +import { getEditorLanguage } from '../util/languages'; | |
| 5 | 8 | |
| 6 | -type Params = { theme: Theme; lang: Lang; ready?: boolean }; | |
| 9 | +type Params = { | |
| 10 | + theme: Theme; | |
| 11 | + lang: Lang | 'ansi'; | |
| 12 | + ready?: boolean; | |
| 13 | +}; | |
| 7 | 14 | |
| 8 | 15 | const fetcher = ({ theme, lang, ready }: Params) => { |
| 9 | - if (!ready) throw new Error(__('Loading...', 'code-block-pro')); | |
| 10 | - const themeFiltered = applyFilters( | |
| 11 | - 'blocks.codeBlockPro.theme', | |
| 12 | - theme, | |
| 13 | - ) as Theme; | |
| 14 | - return getHighlighter({ langs: [lang], theme: themeFiltered }); | |
| 16 | + if (!ready) throw new Error(); | |
| 17 | + const themeFiltered = applyFilters( | |
| 18 | + 'blocks.codeBlockPro.theme', | |
| 19 | + theme, | |
| 20 | + ) as Theme; | |
| 21 | + // If the theme has an alias, use that instead | |
| 22 | + // i.e. it was renamed upstream | |
| 23 | + const themeAlias = ( | |
| 24 | + defaultThemes as Record< | |
| 25 | + string, | |
| 26 | + { name: string; priority?: boolean; alias?: string } | |
| 27 | + > | |
| 28 | + )?.[themeFiltered]?.alias as Theme; | |
| 29 | + | |
| 30 | + return getHighlighter({ | |
| 31 | + langs: lang === 'plaintext' ? [] : [getEditorLanguage(lang)], | |
| 32 | + theme: themeAlias ?? themeFiltered, | |
| 33 | + }); | |
| 15 | 34 | }; |
| 16 | 35 | let once = false; |
| 17 | 36 | |
| 18 | 37 | export const useTheme = ({ theme, lang, ready = true }: Params) => { |
| 19 | - if (!once) { | |
| 20 | - once = true; | |
| 21 | - const assetDir = window.codeBlockPro?.pluginUrl + '/build/shiki/'; | |
| 22 | - setCDN( | |
| 23 | - applyFilters('blocks.codeBlockPro.assetDir', assetDir) as string, | |
| 24 | - ); | |
| 25 | - } | |
| 26 | - const { data: highlighter, error } = useSWRImmutable( | |
| 27 | - { theme, lang, ready }, | |
| 28 | - fetcher, | |
| 29 | - ); | |
| 38 | + const [wasmLoaded, setWasmFileLoaded] = useState(false); | |
| 39 | + const themes = applyFilters( | |
| 40 | + 'blocks.codeBlockPro.themes', | |
| 41 | + defaultThemes, | |
| 42 | + ) as ThemeOption; | |
| 43 | + if (!once) { | |
| 44 | + once = true; | |
| 45 | + const assetDir = `${window.codeBlockPro?.pluginUrl}build/shiki/`; | |
| 46 | + setCDN(applyFilters('blocks.codeBlockPro.assetDir', assetDir) as string); | |
| 47 | + } | |
| 48 | + if (themes[theme]?.custom) theme = 'css-variables'; | |
| 49 | + const { data: highlighter, error } = useSWRImmutable( | |
| 50 | + { theme, lang, ready: ready && wasmLoaded }, | |
| 51 | + fetcher, | |
| 52 | + ); | |
| 53 | + useEffect(() => { | |
| 54 | + const assetDir = `${window.codeBlockPro?.pluginUrl}build/shiki/`; | |
| 55 | + fetch(`${assetDir}dist/onig.wasm`) | |
| 56 | + .then((res) => res.arrayBuffer()) | |
| 57 | + .then((wasmBuffer) => { | |
| 58 | + setWasm(wasmBuffer); | |
| 59 | + setWasmFileLoaded(true); | |
| 60 | + }); | |
| 61 | + }, []); | |
| 30 | 62 | |
| 31 | - return { highlighter, error, loading: !highlighter && !error }; | |
| 63 | + return { | |
| 64 | + highlighter, | |
| 65 | + error, | |
| 66 | + loading: (!highlighter && !error) || !wasmLoaded, | |
| 67 | + }; | |
| 32 | 68 | }; |