From fc98487a5336549abb733b2b367b653d272574b8 Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 25 Jul 2024 04:33:59 +0800 Subject: [PATCH] ui: update shiki bundle --- .../components/highlighter/bundle.ts | 266 ++++++++++++++++++ .../components/highlighter/shiki.ts | 36 ++- 2 files changed, 287 insertions(+), 15 deletions(-) create mode 100644 packages/ui-default/components/highlighter/bundle.ts diff --git a/packages/ui-default/components/highlighter/bundle.ts b/packages/ui-default/components/highlighter/bundle.ts new file mode 100644 index 000000000..e04898121 --- /dev/null +++ b/packages/ui-default/components/highlighter/bundle.ts @@ -0,0 +1,266 @@ +import { createdBundledHighlighter } from '@shikijs/core'; +import { bundledThemes } from 'shiki/themes'; +import getWasmInlined from 'shiki/wasm'; +export * from '@shikijs/core'; +export { bundledThemesInfo } from 'shiki/themes'; +export { createCssVariablesTheme } from 'shiki/theme-css-variables'; + +const bundledLanguagesInfo = [ + { + 'id': 'c', + 'name': 'C', + 'import': () => import('shiki/langs/c.mjs'), + }, + { + 'id': 'cpp', + 'name': 'C++', + 'aliases': [ + 'c++', + ], + 'import': () => import('shiki/langs/cpp.mjs'), + }, + { + 'id': 'css', + 'name': 'CSS', + 'import': () => import('shiki/langs/css.mjs'), + }, + { + 'id': 'html', + 'name': 'HTML', + 'import': () => import('shiki/langs/html.mjs'), + }, + { + 'id': 'html-derivative', + 'name': 'HTML (Derivative)', + 'import': () => import('shiki/langs/html-derivative.mjs'), + }, + { + 'id': 'java', + 'name': 'Java', + 'import': () => import('shiki/langs/java.mjs'), + }, + { + id: 'kotlin', + name: 'Kotlin', + import: () => import('shiki/langs/kotlin.mjs'), + aliases: ['kt'], + }, + { + 'id': 'javascript', + 'name': 'JavaScript', + 'aliases': [ + 'js', + ], + 'import': () => import('shiki/langs/javascript.mjs'), + }, + { + 'id': 'jinja', + 'name': 'Jinja', + 'aliases': [ + 'nunjucks', + 'njk', + ], + 'import': () => import('shiki/langs/jinja.mjs'), + }, + { + 'id': 'json', + 'name': 'JSON', + 'import': () => import('shiki/langs/json.mjs'), + }, + { + 'id': 'json5', + 'name': 'JSON5', + 'import': () => import('shiki/langs/json5.mjs'), + }, + { + 'id': 'jsonc', + 'name': 'JSON with Comments', + 'import': () => import('shiki/langs/jsonc.mjs'), + }, + { + 'id': 'jsonl', + 'name': 'JSON Lines', + 'import': () => import('shiki/langs/jsonl.mjs'), + }, + { + 'id': 'jsx', + 'name': 'JSX', + 'import': () => import('shiki/langs/jsx.mjs'), + }, + { + 'id': 'julia', + 'name': 'Julia', + 'aliases': [ + 'jl', + ], + 'import': () => import('shiki/langs/julia.mjs'), + }, + { + 'id': 'lua', + 'name': 'Lua', + 'import': () => import('shiki/langs/lua.mjs'), + }, + { + 'id': 'markdown', + 'name': 'Markdown', + 'aliases': [ + 'md', + ], + 'import': () => import('shiki/langs/markdown.mjs'), + }, + { + 'id': 'mdc', + 'name': 'MDC', + 'import': () => import('shiki/langs/mdc.mjs'), + }, + { + 'id': 'mdx', + 'name': 'MDX', + 'import': () => import('shiki/langs/mdx.mjs'), + }, + { + 'id': 'php', + 'name': 'PHP', + 'import': () => import('shiki/langs/php.mjs'), + }, + { + 'id': 'python', + 'name': 'Python', + 'aliases': [ + 'py', + ], + 'import': () => import('shiki/langs/python.mjs'), + }, + { + 'id': 'r', + 'name': 'R', + 'import': () => import('shiki/langs/r.mjs'), + }, + { + 'id': 'go', + 'name': 'Go', + import: () => import('shiki/langs/go.mjs'), + }, + { + id: 'rust', + name: 'Rust', + import: () => import('shiki/langs/rust.mjs'), + aliases: ['rs'], + }, + { + id: 'pascal', + name: 'Pascal', + import: () => import('shiki/langs/pascal.mjs'), + aliases: ['pas'], + }, + { + 'id': 'regexp', + 'name': 'RegExp', + 'aliases': [ + 'regex', + ], + 'import': () => import('shiki/langs/regexp.mjs'), + }, + { + 'id': 'ruby', + 'name': 'Ruby', + 'aliases': [ + 'rb', + ], + 'import': () => import('shiki/langs/ruby.mjs'), + }, + { + id: 'csharp', + name: 'C#', + import: () => import('shiki/langs/csharp.mjs'), + aliases: ['cs'], + }, + { + 'id': 'shellscript', + 'name': 'Shell', + 'aliases': [ + 'bash', + 'sh', + 'shell', + 'zsh', + ], + 'import': () => import('shiki/langs/shellscript.mjs'), + }, + { + 'id': 'sql', + 'name': 'SQL', + 'import': () => import('shiki/langs/sql.mjs'), + }, + { + 'id': 'toml', + 'name': 'TOML', + 'import': () => import('shiki/langs/toml.mjs'), + }, + { + id: 'haskell', + name: 'Haskell', + import: () => import('shiki/langs/haskell.mjs'), + aliases: ['hs'], + }, + { + 'id': 'tsx', + 'name': 'TSX', + 'import': () => import('shiki/langs/tsx.mjs'), + }, + { + 'id': 'typescript', + 'name': 'TypeScript', + 'aliases': [ + 'ts', + ], + 'import': () => import('shiki/langs/typescript.mjs'), + }, + { + 'id': 'wasm', + 'name': 'WebAssembly', + 'import': () => import('shiki/langs/wasm.mjs'), + }, + { + 'id': 'xml', + 'name': 'XML', + 'import': () => import('shiki/langs/xml.mjs'), + }, + { + 'id': 'yaml', + 'name': 'YAML', + 'aliases': [ + 'yml', + ], + 'import': () => import('shiki/langs/yaml.mjs'), + }, +]; +const bundledLanguagesBase = Object.fromEntries(bundledLanguagesInfo.map((i) => [i.id, i.import])); +const bundledLanguagesAlias = Object.fromEntries(bundledLanguagesInfo.flatMap((i) => i.aliases?.map((a) => [a, i.import]) || [])); +const bundledLanguages = { + ...bundledLanguagesBase, + ...bundledLanguagesAlias, +}; + +const createHighlighter = (args) => createdBundledHighlighter( + bundledLanguages, + bundledThemes, + getWasmInlined, +)(args); + +function addLanguage(id: string, name: string, importFunc: () => Promise, aliases?: string[]) { + bundledLanguagesInfo.push({ + id, name, import: importFunc, aliases, + }); + bundledLanguagesBase[id] = importFunc; + bundledLanguages[id] = importFunc; + for (const alias of aliases || []) { + bundledLanguagesAlias[alias] = importFunc; + bundledLanguages[id] = importFunc; + } +} + +export { + bundledLanguages, bundledLanguagesAlias, + bundledLanguagesInfo, bundledThemes, + createHighlighter, addLanguage, +}; diff --git a/packages/ui-default/components/highlighter/shiki.ts b/packages/ui-default/components/highlighter/shiki.ts index 622a93e8d..c12b41403 100644 --- a/packages/ui-default/components/highlighter/shiki.ts +++ b/packages/ui-default/components/highlighter/shiki.ts @@ -3,13 +3,10 @@ import { transformerNotationWordHighlight, transformerRenderWhitespace, } from '@shikijs/transformers'; import $ from 'jquery'; -import { - bundledLanguages, - bundledLanguagesAlias, - createHighlighter, -} from 'shiki/bundle/web'; import Notification from 'vj/components/notification/index'; +import { getFeatures, load as loadModule } from 'vj/lazyload'; import { i18n } from 'vj/utils'; +import { addLanguage, bundledLanguages, createHighlighter } from './bundle'; $(document).on('click', '[data-clipboard-text]', function (ev) { const content = $(this).attr('data-clipboard-text'); @@ -34,24 +31,33 @@ if (UserContext.showInvisibleChar) { } async function highlightBlocks($dom) { - const eles = $dom.find('pre code').get(); - const langs = Array.from(new Set(eles.map((code) => { - const language = ($(code).attr('class') || '').trim(); - const m = language.match(/language-([a-z0-9]+)/); - return m ? m[1] : null; - }))).filter((i: string) => i && (bundledLanguages[i] || bundledLanguagesAlias[i])) as string[]; - const shiki = await createHighlighter({ langs, themes: ['github-dark', 'light-plus'] }); - for (const code of eles) { + for (const code of $dom.find('pre code').get()) { const $code = $(code); const $pre = $code.parent(); if ($pre.hasClass('syntax-hl')) continue; const language = ($(code).attr('class') || '').trim(); - console.log(language); // try to map the language name const m = language.match(/language-([a-z0-9]+)(?=\.line-numbers)?(?=\|([\d,-]+))?/); if (!m) continue; + if (!bundledLanguages[m[1]]) { + const features = await getFeatures(`shiki-${m[1]}`); + for (const item of features) { + let apply = typeof item === 'function' + ? item + : (item.startsWith('http') || item.startsWith('/')) + ? await loadModule(item) + : (await loadModule(item)).apply; + if (typeof apply !== 'function') apply = apply.default || apply.apply; + if (typeof apply === 'function') await apply(addLanguage); + } + } + const highlightLang = bundledLanguages[m[1]] ? m[1] : 'none'; + const shiki = await createHighlighter({ + langs: [highlightLang], + themes: ['github-dark', 'light-plus'], + }); $pre.replaceWith($(shiki.codeToHtml($code.text(), { - lang: m[1], + lang: highlightLang, theme: 'light-plus', meta: { __raw: m[2] ? `{${m[2]}}` : null,