@rtdui/editor
Version:
React rich text editor based on tiptap
75 lines (72 loc) • 2.57 kB
JavaScript
import { findChildren } from "@tiptap/core";
import { bundledLanguages, bundledThemes, createHighlighter } from "shiki";
//#region packages/editor/src/RichTextEditor/tiptap_extensions/extension-code-block-shiki/shiki-highlighter.ts
let highlighter;
let highlighterPromise;
const loadingLanguages = /* @__PURE__ */ new Set();
const loadingThemes = /* @__PURE__ */ new Set();
function getShiki() {
return highlighter;
}
/**
* Load the highlighter. Makes sure the highlighter is only loaded once.
*/
function loadHighlighter(opts) {
if (!highlighter && !highlighterPromise) {
highlighterPromise = createHighlighter({
themes: opts.themes.filter((theme) => !!theme && theme in bundledThemes),
langs: opts.languages.filter((lang) => !!lang && lang in bundledLanguages)
}).then((h) => {
highlighter = h;
});
return highlighterPromise;
}
if (highlighterPromise) return highlighterPromise;
}
/**
* Loads a theme if it's valid and not yet loaded.
* @returns true or false depending on if it got loaded.
*/
async function loadTheme(theme) {
if (highlighter && !highlighter.getLoadedThemes().includes(theme) && !loadingThemes.has(theme) && theme in bundledThemes) {
loadingThemes.add(theme);
await highlighter.loadTheme(theme);
loadingThemes.delete(theme);
return true;
}
return false;
}
/**
* Loads a language if it's valid and not yet loaded
* @returns true or false depending on if it got loaded.
*/
async function loadLanguage(language) {
if (highlighter && !highlighter.getLoadedLanguages().includes(language) && !loadingLanguages.has(language) && language in bundledLanguages) {
loadingLanguages.add(language);
await highlighter.loadLanguage(language);
loadingLanguages.delete(language);
return true;
}
return false;
}
/**
* Initializes the highlighter based on the prosemirror document,
* with the themes and languages in the document.
*/
async function initHighlighter({ doc, name, defaultTheme, defaultLanguage }) {
const codeBlocks = findChildren(doc, (node) => node.type.name === name);
const themes = [
...codeBlocks.map((block) => block.node.attrs.theme),
defaultTheme,
"one-light",
"one-dark-pro"
];
const languages = [...codeBlocks.map((block) => block.node.attrs.language), defaultLanguage];
if (!highlighter) await loadHighlighter({
languages,
themes
});
else await Promise.all([...themes.flatMap((theme) => loadTheme(theme)), ...languages.flatMap((language) => !!language && loadLanguage(language))]);
}
//#endregion
export { getShiki, initHighlighter, loadHighlighter, loadLanguage, loadTheme };