From af8c80c95124bd784bbe93f9f82cd04e46ddd8f3 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 24 Jan 2025 22:26:09 +0800 Subject: [PATCH 01/16] perf(plugin-shiki): lazy load languages --- docs/plugins/markdown/shiki.md | 8 +-- docs/zh/plugins/markdown/shiki.md | 8 +-- plugins/markdown/plugin-shiki/package.json | 4 +- .../markdown/plugin-shiki/rollup.config.ts | 9 ++- .../markdown/plugin-shiki/src/node/index.ts | 1 - .../highlighter/createShikiHighlighter.ts | 65 +++++++++++++++---- .../highlighter/getHighLightFunction.ts | 10 +-- .../node/markdown/highlighter/getLanguage.ts | 6 +- .../plugin-shiki/src/node/resolveLang.ts | 23 +++++++ .../markdown/plugin-shiki/src/node/shiki.ts | 5 -- .../plugin-shiki/src/node/shikiPlugin.ts | 5 +- .../src/node/transformers/getTransformers.ts | 25 +++++-- .../tests/shiki-preWrapper.spec.ts | 7 +- pnpm-lock.yaml | 38 +++++++++++ 14 files changed, 164 insertions(+), 50 deletions(-) create mode 100644 plugins/markdown/plugin-shiki/src/node/resolveLang.ts delete mode 100644 plugins/markdown/plugin-shiki/src/node/shiki.ts diff --git a/docs/plugins/markdown/shiki.md b/docs/plugins/markdown/shiki.md index c4ac32bd11..0628c4ebf7 100644 --- a/docs/plugins/markdown/shiki.md +++ b/docs/plugins/markdown/shiki.md @@ -41,13 +41,11 @@ export default { - Details: - Languages of code blocks to be parsed by Shiki. + Additional languages to be parsed by Shiki. - This option will be forwarded to `createHighlighter()` method of Shiki. + ::: tip - ::: warning - - We recommend you to provide the languages list you are using explicitly, otherwise Shiki will load all languages and can affect performance. + The plugin now automatically loads the languages used in your markdown files, so you don't need to specify them manually. ::: diff --git a/docs/zh/plugins/markdown/shiki.md b/docs/zh/plugins/markdown/shiki.md index 413af3d65d..e7db0f8e58 100644 --- a/docs/zh/plugins/markdown/shiki.md +++ b/docs/zh/plugins/markdown/shiki.md @@ -41,13 +41,11 @@ export default { - 详情: - Shiki 要解析的代码块的语言。 + 被 Shiki 解析的额外语言。 - 该配置项会被传递到 Shiki 的 `createHighlighter()` 方法中。 + ::: tip - ::: warning - - 我们建议明确传入所有你使用的语言列表,否则 Shiki 会加载所有语言,并可能影响性能。 + 插件现在会自动加载你的 markdown 文件中使用的语言,所以你不需要手动指定它们。 ::: diff --git a/plugins/markdown/plugin-shiki/package.json b/plugins/markdown/plugin-shiki/package.json index 450edd4857..d2f9f2a80c 100644 --- a/plugins/markdown/plugin-shiki/package.json +++ b/plugins/markdown/plugin-shiki/package.json @@ -23,6 +23,7 @@ "type": "module", "exports": { ".": "./lib/node/index.js", + "./resolveLang": "./lib/node/resolveLang.js", "./styles/*": "./lib/client/styles/*", "./package.json": "./package.json" }, @@ -42,7 +43,8 @@ "@vuepress/helper": "workspace:*", "@vuepress/highlighter-helper": "workspace:*", "nanoid": "^5.0.9", - "shiki": "^2.1.0" + "shiki": "^2.1.0", + "synckit": "^0.9.2" }, "peerDependencies": { "vuepress": "2.0.0-rc.19" diff --git a/plugins/markdown/plugin-shiki/rollup.config.ts b/plugins/markdown/plugin-shiki/rollup.config.ts index cb16101b70..a85286f1c1 100644 --- a/plugins/markdown/plugin-shiki/rollup.config.ts +++ b/plugins/markdown/plugin-shiki/rollup.config.ts @@ -1,5 +1,8 @@ import { rollupBundle } from '../../../scripts/rollup.js' -export default rollupBundle('node/index', { - external: ['@shikijs/transformers', 'nanoid', 'shiki'], -}) +export default rollupBundle( + { base: 'node', files: ['index', 'resolveLang'] }, + { + external: ['@shikijs/transformers', 'nanoid', 'shiki', 'synckit'], + }, +) diff --git a/plugins/markdown/plugin-shiki/src/node/index.ts b/plugins/markdown/plugin-shiki/src/node/index.ts index 0183710c3d..a6bd7d475b 100644 --- a/plugins/markdown/plugin-shiki/src/node/index.ts +++ b/plugins/markdown/plugin-shiki/src/node/index.ts @@ -1,4 +1,3 @@ export type * from './options.js' -export * from './shiki.js' export * from './shikiPlugin.js' export type * from './types.js' diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts index 5b631b591f..09fbaf4d28 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts @@ -1,19 +1,35 @@ -import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki' -import { createHighlighter } from 'shiki' -import { bundledLanguageNames } from '../../shiki.js' +import { createRequire } from 'node:module' +import type { + BundledLanguage, + BundledTheme, + HighlighterGeneric, + LanguageRegistration, +} from 'shiki' +import { createHighlighter, isSpecialLang } from 'shiki' +import { createSyncFn } from 'synckit' +import type { ShikiResolveLang } from '../../resolveLang.js' import type { ShikiHighlightOptions } from '../../types.js' +const require = createRequire(import.meta.url) + +const resolveLangSync = createSyncFn( + require.resolve('@vuepress/plugin-shiki/resolveLang'), +) + +export type ShikiLoadLang = (lang: LanguageRegistration | string) => boolean + export const createShikiHighlighter = async ({ - langs = bundledLanguageNames, + langs = [], langAlias = {}, defaultLang, shikiSetup, ...options -}: ShikiHighlightOptions = {}): Promise< - HighlighterGeneric -> => { - const shikiHighlighter = await createHighlighter({ - langs, +}: ShikiHighlightOptions = {}): Promise<{ + highlighter: HighlighterGeneric + loadLang: (lang: LanguageRegistration | string) => boolean +}> => { + const highlighter = await createHighlighter({ + langs: [...langs, ...Object.values(langAlias)], langAlias, themes: 'themes' in options @@ -21,7 +37,34 @@ export const createShikiHighlighter = async ({ : [options.theme ?? 'nord'], }) - await shikiSetup?.(shikiHighlighter) + const loadLang = (langConfig: LanguageRegistration | string): boolean => { + const lang = typeof langConfig === 'string' ? langConfig : langConfig.name + + if ( + !isSpecialLang(lang) && + !highlighter.getLoadedLanguages().includes(lang) + ) { + const resolvedLang = resolveLangSync(lang) + + if (!resolvedLang.length) return false + + console.log('loading lang', lang) + + highlighter.loadLanguageSync(resolvedLang) + } + return true + } + + // patch for twoslash - https://github.com/vuejs/vitepress/issues/4334 + const rawGetLanguage = highlighter.getLanguage + + highlighter.getLanguage = (name) => { + loadLang(name) + + return rawGetLanguage.call(highlighter, name) + } + + await shikiSetup?.(highlighter) - return shikiHighlighter + return { highlighter, loadLang } } diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts index ff73439602..54efec5aac 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts @@ -7,6 +7,7 @@ import { import type { ShikiHighlightOptions } from '../../types.js' import { attrsToLines } from '../../utils.js' import type { MarkdownFilePathGetter } from './createMarkdownFilePathGetter.js' +import type { ShikiLoadLang } from './createShikiHighlighter.js' import { getLanguage } from './getLanguage.js' import { handleMustache } from './handleMustache.js' @@ -19,20 +20,15 @@ type MarkdownItHighlight = ( export const getHighLightFunction = ( highlighter: HighlighterGeneric, options: ShikiHighlightOptions, + loadLang: ShikiLoadLang, markdownFilePathGetter: MarkdownFilePathGetter, ): MarkdownItHighlight => { const transformers = getTransformers(options) - const loadedLanguages = highlighter.getLoadedLanguages() return (content, language, attrs) => handleMustache(content, (str) => highlighter.codeToHtml(str, { - lang: getLanguage( - language, - loadedLanguages, - options, - markdownFilePathGetter, - ), + lang: getLanguage(language, options, loadLang, markdownFilePathGetter), meta: { /** * Custom `transformers` passed by users may require `attrs`. diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getLanguage.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getLanguage.ts index 36856e1f41..35ac380b68 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getLanguage.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getLanguage.ts @@ -1,20 +1,20 @@ -import { isSpecialLang } from 'shiki' import { colors } from 'vuepress/utils' import type { ShikiHighlightOptions } from '../../types.js' import { logger, resolveLanguage } from '../../utils.js' import type { MarkdownFilePathGetter } from './createMarkdownFilePathGetter.js' +import type { ShikiLoadLang } from './createShikiHighlighter.js' const WARNED_LANGS = new Set() export const getLanguage = ( lang: string, - loadedLanguages: string[], { defaultLang, logLevel }: ShikiHighlightOptions, + loadLang: ShikiLoadLang, markdownFilePathGetter: MarkdownFilePathGetter, ): string => { let result = resolveLanguage(lang) - if (result && !loadedLanguages.includes(result) && !isSpecialLang(result)) { + if (result && !loadLang(lang)) { // warn for unknown languages only once if (logLevel !== 'silent' && !WARNED_LANGS.has(result)) { logger.warn( diff --git a/plugins/markdown/plugin-shiki/src/node/resolveLang.ts b/plugins/markdown/plugin-shiki/src/node/resolveLang.ts new file mode 100644 index 0000000000..86c140a3d2 --- /dev/null +++ b/plugins/markdown/plugin-shiki/src/node/resolveLang.ts @@ -0,0 +1,23 @@ +import type { + DynamicImportLanguageRegistration, + LanguageRegistration, +} from 'shiki' +import { bundledLanguages } from 'shiki' +import { runAsWorker } from 'synckit' + +async function resolveLang(lang: string): Promise { + return ( + ( + bundledLanguages as Record< + string, + DynamicImportLanguageRegistration | undefined + > + ) + [lang]?.() + .then((m) => m.default) ?? [] + ) +} + +runAsWorker(resolveLang) + +export type ShikiResolveLang = typeof resolveLang diff --git a/plugins/markdown/plugin-shiki/src/node/shiki.ts b/plugins/markdown/plugin-shiki/src/node/shiki.ts deleted file mode 100644 index c6ab41daa3..0000000000 --- a/plugins/markdown/plugin-shiki/src/node/shiki.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { bundledLanguages } from 'shiki' - -export { bundledLanguages } from 'shiki' - -export const bundledLanguageNames = Object.keys(bundledLanguages) diff --git a/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts b/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts index 6173fbc088..dc81243466 100644 --- a/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts +++ b/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts @@ -41,11 +41,12 @@ export const shikiPlugin = (_options: ShikiPluginOptions = {}): Plugin => { const { preWrapper, lineNumbers, collapsedLines } = options const markdownFilePathGetter = createMarkdownFilePathGetter(md) - const shikiHighlighter = await createShikiHighlighter(options) + const { highlighter, loadLang } = await createShikiHighlighter(options) md.options.highlight = getHighLightFunction( - shikiHighlighter, + highlighter, options, + loadLang, markdownFilePathGetter, ) diff --git a/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts b/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts index cecc2c675f..51c62f5647 100644 --- a/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts +++ b/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts @@ -24,7 +24,11 @@ export const getTransformers = ( const transformers: ShikiTransformer[] = [] if (options.notationDiff) { - transformers.push(transformerNotationDiff()) + transformers.push( + transformerNotationDiff({ + matchAlgorithm: 'v3', + }), + ) } if (options.notationFocus) { @@ -32,20 +36,33 @@ export const getTransformers = ( transformerNotationFocus({ classActiveLine: 'has-focus', classActivePre: 'has-focused-lines', + matchAlgorithm: 'v3', }), ) } if (options.notationHighlight) { - transformers.push(transformerNotationHighlight()) + transformers.push( + transformerNotationHighlight({ + matchAlgorithm: 'v3', + }), + ) } if (options.notationErrorLevel) { - transformers.push(transformerNotationErrorLevel()) + transformers.push( + transformerNotationErrorLevel({ + matchAlgorithm: 'v3', + }), + ) } if (options.notationWordHighlight) { - transformers.push(transformerNotationWordHighlight()) + transformers.push( + transformerNotationWordHighlight({ + matchAlgorithm: 'v3', + }), + ) transformers.push(transformerMetaWordHighlight()) } diff --git a/plugins/markdown/plugin-shiki/tests/shiki-preWrapper.spec.ts b/plugins/markdown/plugin-shiki/tests/shiki-preWrapper.spec.ts index 19c1fa367e..5f239298a5 100644 --- a/plugins/markdown/plugin-shiki/tests/shiki-preWrapper.spec.ts +++ b/plugins/markdown/plugin-shiki/tests/shiki-preWrapper.spec.ts @@ -18,7 +18,7 @@ import { } from '../src/node/markdown/index.js' import type { ShikiPluginOptions } from '../src/node/options.js' -const shikiHighlighter = await createShikiHighlighter() +const { highlighter, loadLang } = await createShikiHighlighter() const createMarkdown = ({ preWrapper = true, @@ -26,13 +26,14 @@ const createMarkdown = ({ collapsedLines = false, ...options }: ShikiPluginOptions = {}): MarkdownIt => { - const md = MarkdownIt() + const md = new MarkdownIt() const markdownFilePathGetter = createMarkdownFilePathGetter(md) md.options.highlight = getHighLightFunction( - shikiHighlighter, + highlighter, options, + loadLang, markdownFilePathGetter, ) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83aaf15d4f..91a4c0cfb2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -942,6 +942,9 @@ importers: shiki: specifier: ^2.1.0 version: 2.1.0 + synckit: + specifier: ^0.9.2 + version: 0.9.2 vuepress: specifier: 2.0.0-rc.19 version: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(sass-embedded@1.83.4)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0))(@vuepress/bundler-webpack@2.0.0-rc.19(esbuild@0.24.2)(typescript@5.7.3))(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)) @@ -2794,36 +2797,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.0': resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.0': resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.0': resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.0': resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.0': resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.5.0': resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==} @@ -2851,6 +2860,10 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@pkgr/core@0.1.1': + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@playwright/test@1.50.0': resolution: {integrity: sha512-ZGNXbt+d65EGjBORQHuYKj+XhCewlwpnSd/EDuLPZGSiEWmgOJB5RmMCCYGy5aMfTs9wx61RivfDKi8H/hcMvw==} engines: {node: '>=18'} @@ -2957,51 +2970,61 @@ packages: resolution: {integrity: sha512-3pA7xecItbgOs1A5H58dDvOUEboG5UfpTq3WzAdF54acBbUM+olDJAPkgj1GRJ4ZqE12DZ9/hNS2QZk166v92A==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.32.0': resolution: {integrity: sha512-Y7XUZEVISGyge51QbYyYAEHwpGgmRrAxQXO3siyYo2kmaj72USSG8LtlQQgAtlGfxYiOwu+2BdbPjzEpcOpRmQ==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.32.0': resolution: {integrity: sha512-r7/OTF5MqeBrZo5omPXcTnjvv1GsrdH8a8RerARvDFiDwFpDVDnJyByYM/nX+mvks8XXsgPUxkwe/ltaX2VH7w==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.32.0': resolution: {integrity: sha512-HJbifC9vex9NqnlodV2BHVFNuzKL5OnsV2dvTw6e1dpZKkNjPG6WUq+nhEYV6Hv2Bv++BXkwcyoGlXnPrjAKXw==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.32.0': resolution: {integrity: sha512-VAEzZTD63YglFlWwRj3taofmkV1V3xhebDXffon7msNz4b14xKsz7utO6F8F4cqt8K/ktTl9rm88yryvDpsfOw==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.32.0': resolution: {integrity: sha512-Sts5DST1jXAc9YH/iik1C9QRsLcCoOScf3dfbY5i4kH9RJpKxiTBXqm7qU5O6zTXBTEZry69bGszr3SMgYmMcQ==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.32.0': resolution: {integrity: sha512-qhlXeV9AqxIyY9/R1h1hBD6eMvQCO34ZmdYvry/K+/MBs6d1nRFLm6BOiITLVI+nFAAB9kUB6sdJRKyVHXnqZw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.32.0': resolution: {integrity: sha512-8ZGN7ExnV0qjXa155Rsfi6H8M4iBBwNLBM9lcVS+4NcSzOFaNqmt7djlox8pN1lWrRPMRRQ8NeDlozIGx3Omsw==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.32.0': resolution: {integrity: sha512-VDzNHtLLI5s7xd/VubyS10mq6TxvZBp+4NRWoW+Hi3tgV05RtVm4qK99+dClwTN1McA6PHwob6DEJ6PlXbY83A==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.32.0': resolution: {integrity: sha512-qcb9qYDlkxz9DxJo7SDhWxTWV1gFuwznjbTiov289pASxlfGbaOD54mgbs9+z94VwrXtKTu+2RqwlSTbiOqxGg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.32.0': resolution: {integrity: sha512-pFDdotFDMXW2AXVbfdUEfidPAk/OtwE/Hd4eYMTNVVaCQ6Yl8et0meDaKNL63L44Haxv4UExpv9ydSf3aSayDg==} @@ -5758,24 +5781,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.29.1: resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.29.1: resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.29.1: resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.29.1: resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} @@ -7728,6 +7755,10 @@ packages: resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==} engines: {node: '>=16.0.0'} + synckit@0.9.2: + resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} + engines: {node: ^14.18.0 || >=16.0.0} + table@6.9.0: resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} engines: {node: '>=10.0.0'} @@ -10372,6 +10403,8 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@pkgr/core@0.1.1': {} + '@playwright/test@1.50.0': dependencies: playwright: 1.50.0 @@ -15808,6 +15841,11 @@ snapshots: sync-message-port@1.1.3: {} + synckit@0.9.2: + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.8.1 + table@6.9.0: dependencies: ajv: 8.17.1 From 2eeec6ee04aafc07f557cd679ee2ec0c1fe53a57 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 24 Jan 2025 22:51:21 +0800 Subject: [PATCH 02/16] fix: fix lang --- .../highlighter/createShikiHighlighter.ts | 21 ++++++++++--------- .../node/markdown/highlighter/getLanguage.ts | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts index 09fbaf4d28..c78c31c530 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts @@ -9,6 +9,7 @@ import { createHighlighter, isSpecialLang } from 'shiki' import { createSyncFn } from 'synckit' import type { ShikiResolveLang } from '../../resolveLang.js' import type { ShikiHighlightOptions } from '../../types.js' +import { resolveLanguage } from '../../utils.js' const require = createRequire(import.meta.url) @@ -26,7 +27,7 @@ export const createShikiHighlighter = async ({ ...options }: ShikiHighlightOptions = {}): Promise<{ highlighter: HighlighterGeneric - loadLang: (lang: LanguageRegistration | string) => boolean + loadLang: (lang: string) => boolean }> => { const highlighter = await createHighlighter({ langs: [...langs, ...Object.values(langAlias)], @@ -37,21 +38,19 @@ export const createShikiHighlighter = async ({ : [options.theme ?? 'nord'], }) - const loadLang = (langConfig: LanguageRegistration | string): boolean => { - const lang = typeof langConfig === 'string' ? langConfig : langConfig.name + const loadLang = (lang: string): boolean => { + if (isSpecialLang(lang)) return true - if ( - !isSpecialLang(lang) && - !highlighter.getLoadedLanguages().includes(lang) - ) { + const loadedLangs = highlighter.getLoadedLanguages() + + if (!loadedLangs.includes(lang)) { const resolvedLang = resolveLangSync(lang) if (!resolvedLang.length) return false - console.log('loading lang', lang) - highlighter.loadLanguageSync(resolvedLang) } + return true } @@ -59,7 +58,9 @@ export const createShikiHighlighter = async ({ const rawGetLanguage = highlighter.getLanguage highlighter.getLanguage = (name) => { - loadLang(name) + const lang = typeof name === 'string' ? name : name.name + + loadLang(resolveLanguage(lang)) return rawGetLanguage.call(highlighter, name) } diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getLanguage.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getLanguage.ts index 35ac380b68..b81001fc5b 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getLanguage.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getLanguage.ts @@ -14,7 +14,7 @@ export const getLanguage = ( ): string => { let result = resolveLanguage(lang) - if (result && !loadLang(lang)) { + if (result && !loadLang(result)) { // warn for unknown languages only once if (logLevel !== 'silent' && !WARNED_LANGS.has(result)) { logger.warn( From dc4abbbdd0a8d9dc204989f69dad6601591f26f2 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 24 Jan 2025 22:57:54 +0800 Subject: [PATCH 03/16] chore: fix types --- .../markdown/highlighter/createShikiHighlighter.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts index c78c31c530..557fc9809f 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts @@ -1,10 +1,5 @@ import { createRequire } from 'node:module' -import type { - BundledLanguage, - BundledTheme, - HighlighterGeneric, - LanguageRegistration, -} from 'shiki' +import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki' import { createHighlighter, isSpecialLang } from 'shiki' import { createSyncFn } from 'synckit' import type { ShikiResolveLang } from '../../resolveLang.js' @@ -17,7 +12,7 @@ const resolveLangSync = createSyncFn( require.resolve('@vuepress/plugin-shiki/resolveLang'), ) -export type ShikiLoadLang = (lang: LanguageRegistration | string) => boolean +export type ShikiLoadLang = (lang: string) => boolean export const createShikiHighlighter = async ({ langs = [], @@ -27,7 +22,7 @@ export const createShikiHighlighter = async ({ ...options }: ShikiHighlightOptions = {}): Promise<{ highlighter: HighlighterGeneric - loadLang: (lang: string) => boolean + loadLang: ShikiLoadLang }> => { const highlighter = await createHighlighter({ langs: [...langs, ...Object.values(langAlias)], From bb501521489c0030ee8175ba87efdb34c4fe69bd Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Fri, 24 Jan 2025 23:21:50 +0800 Subject: [PATCH 04/16] feat(plugin-shiki): add built-in twoslash support --- .vscode/settings.json | 1 + docs/.vuepress/config.ts | 49 +++++-------- docs/.vuepress/theme.ts | 17 +---- docs/package.json | 1 + docs/plugins/markdown/shiki.md | 38 +++++++++++ plugins/markdown/plugin-shiki/package.json | 6 ++ .../src/client/styles/twoslash.scss | 9 +++ .../markdown/plugin-shiki/src/node/options.ts | 11 ++- .../src/node/prepareClientConfigFile.ts | 8 +++ .../src/node/transformers/getTransformers.ts | 31 ++++++++- pnpm-lock.yaml | 68 +++++++++++++------ 11 files changed, 168 insertions(+), 71 deletions(-) create mode 100644 plugins/markdown/plugin-shiki/src/client/styles/twoslash.scss diff --git a/.vscode/settings.json b/.vscode/settings.json index fa4846d636..ff286c4f93 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -77,6 +77,7 @@ "trackpad", "tsbuildinfo", "twikoo", + "twoslash", "umami", "unmount", "vuejs", diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index 2920c98557..38b3064c6b 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -1,4 +1,5 @@ import process from 'node:process' +import { transformerTwoslash } from '@shikijs/twoslash' import { viteBundler } from '@vuepress/bundler-vite' import { webpackBundler } from '@vuepress/bundler-webpack' import { getModulePath } from '@vuepress/helper' @@ -155,39 +156,21 @@ export default defineUserConfig({ NpmBadge: path.resolve(__dirname, './components/NpmBadge.vue'), }, }), - // only enable shiki plugin in production mode - IS_PROD - ? shikiPlugin({ - langs: [ - 'bash', - 'diff', - 'json', - 'md', - 'scss', - 'ts', - 'vue', - 'less', - 'java', - 'py', - 'vb', - 'bat', - 'cs', - 'cpp', - ], - themes: { - light: 'one-light', - dark: 'one-dark-pro', - }, - lineNumbers: 10, - notationDiff: true, - notationErrorLevel: true, - notationFocus: true, - notationHighlight: true, - notationWordHighlight: true, - whitespace: true, - collapsedLines: false, - }) - : [], + shikiPlugin({ + themes: { + light: 'one-light', + dark: 'one-dark-pro', + }, + lineNumbers: 10, + notationDiff: true, + notationErrorLevel: true, + notationFocus: true, + notationHighlight: true, + notationWordHighlight: true, + whitespace: true, + collapsedLines: false, + twoslash: true, + }), cachePlugin(), ], diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts index d3244a2f84..95a4b0754f 100644 --- a/docs/.vuepress/theme.ts +++ b/docs/.vuepress/theme.ts @@ -67,21 +67,6 @@ export default defaultTheme({ alert: true, }, // use shiki plugin in production mode instead - prismjs: IS_PROD - ? false - : { - themes: { - light: 'one-light', - dark: 'one-dark', - }, - lineNumbers: 10, - notationDiff: true, - notationErrorLevel: true, - notationFocus: true, - notationHighlight: true, - notationWordHighlight: true, - whitespace: true, - collapsedLines: false, - }, + prismjs: false, }, }) diff --git a/docs/package.json b/docs/package.json index 3c7dc223df..01e02e180d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,6 +9,7 @@ "docs:serve": "http-server -a localhost .vuepress/dist" }, "dependencies": { + "@shikijs/twoslash": "^2.1.0", "@vuepress/bundler-vite": "2.0.0-rc.19", "@vuepress/bundler-webpack": "2.0.0-rc.19", "@vuepress/helper": "workspace:*", diff --git a/docs/plugins/markdown/shiki.md b/docs/plugins/markdown/shiki.md index 0628c4ebf7..613009005d 100644 --- a/docs/plugins/markdown/shiki.md +++ b/docs/plugins/markdown/shiki.md @@ -671,6 +671,44 @@ body > div { - Also see: - [Shiki > Render Whitespace](https://shiki.style/packages/transformers#transformerrenderwhitespace) +### twoslash + +- Type: `boolean` + +- Default: `false` + +- Details: Whether enable [twoslash](https://github.com/twoslashes/twoslash). + + ::: tip + + For size reasons, the plugin does not include the `@shiki/twoslash` package by default. If you want to use it, you need to install it manually. + + ::: + +- Also see: + + - [Shiki > Twosplash](https://shiki.style/packages/twoslash) + +- Example: + + **Input:** + + ````md + ```ts twoslash + const a = 1 + const b = 2 + console.log(a + b) + ``` + ```` + + **Output:** + + ```ts twoslash + const a = 1 + const b = 2 + console.log(a + b) + ``` + ## Advanced Options ### defaultLang diff --git a/plugins/markdown/plugin-shiki/package.json b/plugins/markdown/plugin-shiki/package.json index d2f9f2a80c..35be403e20 100644 --- a/plugins/markdown/plugin-shiki/package.json +++ b/plugins/markdown/plugin-shiki/package.json @@ -47,8 +47,14 @@ "synckit": "^0.9.2" }, "peerDependencies": { + "@shikijs/twoslash": "^2.1.0", "vuepress": "2.0.0-rc.19" }, + "peerDependenciesMeta": { + "@shikijs/twoslash": { + "optional": true + } + }, "publishConfig": { "access": "public" }, diff --git a/plugins/markdown/plugin-shiki/src/client/styles/twoslash.scss b/plugins/markdown/plugin-shiki/src/client/styles/twoslash.scss new file mode 100644 index 0000000000..5bfbd269e3 --- /dev/null +++ b/plugins/markdown/plugin-shiki/src/client/styles/twoslash.scss @@ -0,0 +1,9 @@ +div[data-highlighter='shiki'] { + pre.twoslash { + --twoslash-popup-bg: var(--code-c-bg, var(--shiki-light-bg)); + + [data-theme='dark'] & { + --twoslash-popup-bg: var(--code-c-bg, var(--shiki-dark-bg)); + } + } +} diff --git a/plugins/markdown/plugin-shiki/src/node/options.ts b/plugins/markdown/plugin-shiki/src/node/options.ts index b1d1f4902c..8ac8420d32 100644 --- a/plugins/markdown/plugin-shiki/src/node/options.ts +++ b/plugins/markdown/plugin-shiki/src/node/options.ts @@ -11,4 +11,13 @@ import type { ShikiHighlightOptions } from './types.js' export type ShikiPluginOptions = MarkdownItLineNumbersOptions & MarkdownItPreWrapperOptions & Pick & - ShikiHighlightOptions + ShikiHighlightOptions & { + /** + * Enable twoslash + * + * @description You should install `@shikijs/twoslash` manually. + * + * @default false + */ + twoslash?: boolean + } diff --git a/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts b/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts index 63ca6113c8..f608d6880b 100644 --- a/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts +++ b/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts @@ -15,6 +15,7 @@ export const prepareClientConfigFile = ( notationHighlight, notationWordHighlight, whitespace, + twoslash, }: ShikiPluginOptions, ): Promise => { const imports: string[] = [ @@ -80,6 +81,13 @@ export const prepareClientConfigFile = ( setups.push('setupCollapsedLines()') } + if (twoslash) { + imports.push( + `import "${getModulePath('@shikijs/twoslash/style-rich.css', import.meta)}"`, + `import "${getModulePath(`${PLUGIN_NAME}/styles/twoslash.css`, import.meta)}"`, + ) + } + let code = imports.join('\n') if (setups.length) { diff --git a/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts b/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts index 51c62f5647..34f94c5090 100644 --- a/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts +++ b/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts @@ -7,10 +7,13 @@ import { transformerNotationWordHighlight, transformerRenderWhitespace, } from '@shikijs/transformers' +import type { TransformerTwoslashIndexOptions } from '@shikijs/twoslash' import type { WhitespacePosition } from '@vuepress/highlighter-helper' import { resolveWhitespacePosition } from '@vuepress/highlighter-helper' import type { ShikiTransformer } from 'shiki' +import { colors } from 'vuepress/utils' import type { ShikiHighlightOptions } from '../types.js' +import { logger } from '../utils.js' import { addClassTransformer, cleanupTransformer, @@ -18,8 +21,20 @@ import { removeEscapeTransformer, } from './vuepressTransformers.js' +let transformerTwoslash: + | ((options?: TransformerTwoslashIndexOptions) => ShikiTransformer) + | null + +try { + ;({ transformerTwoslash } = await import('@shikijs/twoslash')) +} catch { + transformerTwoslash = null +} + export const getTransformers = ( - options: ShikiHighlightOptions, + options: ShikiHighlightOptions & { + twoslash?: boolean + }, ): ShikiTransformer[] => { const transformers: ShikiTransformer[] = [] @@ -66,6 +81,20 @@ export const getTransformers = ( transformers.push(transformerMetaWordHighlight()) } + if (options.twoslash) { + if (transformerTwoslash) + transformers.push( + transformerTwoslash({ + explicitTrigger: true, + }), + ) + else { + logger.error( + `${colors.cyan('twoslash')} is enabled, but ${colors.magenta('@shikijs/twoslash')} is not installed, please install it manually`, + ) + } + } + transformers.push( addClassTransformer, cleanupTransformer, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91a4c0cfb2..021760edc1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,6 +124,9 @@ importers: docs: dependencies: + '@shikijs/twoslash': + specifier: ^2.1.0 + version: 2.1.0(typescript@5.7.3) '@vuepress/bundler-vite': specifier: 2.0.0-rc.19 version: 2.0.0-rc.19(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(sass-embedded@1.83.4)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) @@ -930,6 +933,9 @@ importers: '@shikijs/transformers': specifier: ^2.1.0 version: 2.1.0 + '@shikijs/twoslash': + specifier: ^2.1.0 + version: 2.1.0(typescript@5.7.3) '@vuepress/helper': specifier: workspace:* version: link:../../../tools/helper @@ -2797,42 +2803,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.0': resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.0': resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.0': resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.0': resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.0': resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.0': resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==} @@ -2970,61 +2970,51 @@ packages: resolution: {integrity: sha512-3pA7xecItbgOs1A5H58dDvOUEboG5UfpTq3WzAdF54acBbUM+olDJAPkgj1GRJ4ZqE12DZ9/hNS2QZk166v92A==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.32.0': resolution: {integrity: sha512-Y7XUZEVISGyge51QbYyYAEHwpGgmRrAxQXO3siyYo2kmaj72USSG8LtlQQgAtlGfxYiOwu+2BdbPjzEpcOpRmQ==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.32.0': resolution: {integrity: sha512-r7/OTF5MqeBrZo5omPXcTnjvv1GsrdH8a8RerARvDFiDwFpDVDnJyByYM/nX+mvks8XXsgPUxkwe/ltaX2VH7w==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.32.0': resolution: {integrity: sha512-HJbifC9vex9NqnlodV2BHVFNuzKL5OnsV2dvTw6e1dpZKkNjPG6WUq+nhEYV6Hv2Bv++BXkwcyoGlXnPrjAKXw==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.32.0': resolution: {integrity: sha512-VAEzZTD63YglFlWwRj3taofmkV1V3xhebDXffon7msNz4b14xKsz7utO6F8F4cqt8K/ktTl9rm88yryvDpsfOw==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.32.0': resolution: {integrity: sha512-Sts5DST1jXAc9YH/iik1C9QRsLcCoOScf3dfbY5i4kH9RJpKxiTBXqm7qU5O6zTXBTEZry69bGszr3SMgYmMcQ==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.32.0': resolution: {integrity: sha512-qhlXeV9AqxIyY9/R1h1hBD6eMvQCO34ZmdYvry/K+/MBs6d1nRFLm6BOiITLVI+nFAAB9kUB6sdJRKyVHXnqZw==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.32.0': resolution: {integrity: sha512-8ZGN7ExnV0qjXa155Rsfi6H8M4iBBwNLBM9lcVS+4NcSzOFaNqmt7djlox8pN1lWrRPMRRQ8NeDlozIGx3Omsw==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.32.0': resolution: {integrity: sha512-VDzNHtLLI5s7xd/VubyS10mq6TxvZBp+4NRWoW+Hi3tgV05RtVm4qK99+dClwTN1McA6PHwob6DEJ6PlXbY83A==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.32.0': resolution: {integrity: sha512-qcb9qYDlkxz9DxJo7SDhWxTWV1gFuwznjbTiov289pASxlfGbaOD54mgbs9+z94VwrXtKTu+2RqwlSTbiOqxGg==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.32.0': resolution: {integrity: sha512-pFDdotFDMXW2AXVbfdUEfidPAk/OtwE/Hd4eYMTNVVaCQ6Yl8et0meDaKNL63L44Haxv4UExpv9ydSf3aSayDg==} @@ -3062,6 +3052,9 @@ packages: '@shikijs/transformers@2.1.0': resolution: {integrity: sha512-3sfvh6OKUVkT5wZFU1xxiq1qqNIuCwUY3yOb9ZGm19y80UZ/eoroLE2orGNzfivyTxR93GfXXZC/ghPR0/SBow==} + '@shikijs/twoslash@2.1.0': + resolution: {integrity: sha512-tgZEk78/g1ceC/mS3xA50aIc2rArl+oiphZEdAXaoioLVNebDChhV93NzcXu4NAq4pCogfBbD5HV8qO38+fQyQ==} + '@shikijs/types@2.1.0': resolution: {integrity: sha512-OFOdHA6VEVbiQvepJ8yqicC6VmBrKxFFhM2EsHHrZESqLVAXOSeRDiuSYV185lIgp15TVic5vYBYNhTsk1xHLg==} @@ -3342,6 +3335,11 @@ packages: resolution: {integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript/vfs@1.6.0': + resolution: {integrity: sha512-hvJUjNVeBMp77qPINuUvYXj4FyWeeMMKZkxEATEU3hqBAQ7qdTBCUFT7Sp0Zu0faeEtFf+ldXxMEDr/bk73ISg==} + peerDependencies: + typescript: '*' + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -5781,28 +5779,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.29.1: resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.29.1: resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.29.1: resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.29.1: resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} @@ -7909,6 +7903,14 @@ packages: twikoo@1.6.41: resolution: {integrity: sha512-Hrp/fvk2N5oqin+9bkFVULstL+YMwQjn/vVL8r5FPIhyRBA0zWAgUri4hhCji4FsXegKR6+ihyMPTXBRENjnnA==} + twoslash-protocol@0.2.12: + resolution: {integrity: sha512-5qZLXVYfZ9ABdjqbvPc4RWMr7PrpPaaDSeaYY55vl/w1j6H6kzsWK/urAEIXlzYlyrFmyz1UbwIt+AA0ck+wbg==} + + twoslash@0.2.12: + resolution: {integrity: sha512-tEHPASMqi7kqwfJbkk7hc/4EhlrKCSLcur+TcvYki3vhIfaRMXnXjaYFgXpoZRbT6GdprD4tGuVBEmTpUgLBsw==} + peerDependencies: + typescript: '*' + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -10579,6 +10581,15 @@ snapshots: '@shikijs/core': 2.1.0 '@shikijs/types': 2.1.0 + '@shikijs/twoslash@2.1.0(typescript@5.7.3)': + dependencies: + '@shikijs/core': 2.1.0 + '@shikijs/types': 2.1.0 + twoslash: 0.2.12(typescript@5.7.3) + transitivePeerDependencies: + - supports-color + - typescript + '@shikijs/types@2.1.0': dependencies: '@shikijs/vscode-textmate': 10.0.1 @@ -10913,6 +10924,13 @@ snapshots: '@typescript-eslint/types': 8.21.0 eslint-visitor-keys: 4.2.0 + '@typescript/vfs@1.6.0(typescript@5.7.3)': + dependencies: + debug: 4.4.0 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + '@ungap/structured-clone@1.3.0': {} '@vitejs/plugin-vue@5.2.1(vite@6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(sass-embedded@1.83.4)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))': @@ -15980,6 +15998,16 @@ snapshots: twikoo@1.6.41: {} + twoslash-protocol@0.2.12: {} + + twoslash@0.2.12(typescript@5.7.3): + dependencies: + '@typescript/vfs': 1.6.0(typescript@5.7.3) + twoslash-protocol: 0.2.12 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 From 820c8a4b3ca02511b51f426fdab4327cac71809f Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 12:50:08 +0800 Subject: [PATCH 05/16] refactor: refactor shiki twoslash --- docs/plugins/markdown/shiki.md | 9 +- docs/zh/plugins/markdown/shiki.md | 39 ++ plugins/markdown/plugin-shiki/package.json | 8 +- .../src/client/styles/twoslash.scss | 9 - .../highlighter/createShikiHighlighter.ts | 29 +- .../highlighter/getHighLightFunction.ts | 10 +- .../markdown/plugin-shiki/src/node/options.ts | 3 +- .../src/node/prepareClientConfigFile.ts | 24 +- .../plugin-shiki/src/node/shikiPlugin.ts | 31 +- .../src/node/transformers/getTransformers.ts | 40 +- .../node/transformers/vuepressTransformers.ts | 7 + pnpm-lock.yaml | 612 +++++++++++++++++- tools/shiki-twoslash/package.json | 68 ++ tools/shiki-twoslash/rollup.config.ts | 3 + tools/shiki-twoslash/src/client/index.ts | 1 + tools/shiki-twoslash/src/client/shim.d.ts | 5 + .../src/client/styles/twoslash.scss | 378 +++++++++++ tools/shiki-twoslash/src/client/twoslash.ts | 62 ++ tools/shiki-twoslash/src/node/index.ts | 4 + .../src/node/rendererFloatingVue.ts | 220 +++++++ .../shiki-twoslash/src/node/resolveTsPaths.ts | 31 + .../src/node/transformerTwoslash.ts | 97 +++ tools/shiki-twoslash/src/node/types.ts | 39 ++ tools/shiki-twoslash/tsconfig.build.json | 9 + 24 files changed, 1648 insertions(+), 90 deletions(-) delete mode 100644 plugins/markdown/plugin-shiki/src/client/styles/twoslash.scss create mode 100644 tools/shiki-twoslash/package.json create mode 100644 tools/shiki-twoslash/rollup.config.ts create mode 100644 tools/shiki-twoslash/src/client/index.ts create mode 100644 tools/shiki-twoslash/src/client/shim.d.ts create mode 100644 tools/shiki-twoslash/src/client/styles/twoslash.scss create mode 100644 tools/shiki-twoslash/src/client/twoslash.ts create mode 100644 tools/shiki-twoslash/src/node/index.ts create mode 100644 tools/shiki-twoslash/src/node/rendererFloatingVue.ts create mode 100644 tools/shiki-twoslash/src/node/resolveTsPaths.ts create mode 100644 tools/shiki-twoslash/src/node/transformerTwoslash.ts create mode 100644 tools/shiki-twoslash/src/node/types.ts create mode 100644 tools/shiki-twoslash/tsconfig.build.json diff --git a/docs/plugins/markdown/shiki.md b/docs/plugins/markdown/shiki.md index 613009005d..0de39e20d6 100644 --- a/docs/plugins/markdown/shiki.md +++ b/docs/plugins/markdown/shiki.md @@ -673,7 +673,7 @@ body > div { ### twoslash -- Type: `boolean` +- Type: `boolean | TwoslashOptions` - Default: `false` @@ -681,13 +681,14 @@ body > div { ::: tip - For size reasons, the plugin does not include the `@shiki/twoslash` package by default. If you want to use it, you need to install it manually. + For size reasons, the plugin does not include the `@vuepress/shiki-twoslash` package by default. If you want to use it, you need to install it manually. ::: - Also see: - - [Shiki > Twosplash](https://shiki.style/packages/twoslash) + - [Shiki > Twoslash](https://shiki.style/packages/twoslash) + - [Twoslash > TwoslashOptions](https://github.com/twoslashes/twoslash/blob/main/packages/twoslash/src/types/options.ts#L18) - Example: @@ -705,7 +706,7 @@ body > div { ```ts twoslash const a = 1 - const b = 2 + const b = 23 console.log(a + b) ``` diff --git a/docs/zh/plugins/markdown/shiki.md b/docs/zh/plugins/markdown/shiki.md index e7db0f8e58..e2cfc10d82 100644 --- a/docs/zh/plugins/markdown/shiki.md +++ b/docs/zh/plugins/markdown/shiki.md @@ -673,6 +673,45 @@ body > div { - 参考: - [Shiki > 空白符渲染](https://shiki.tmrs.site/packages/transformers#transformerrenderwhitespace) +### twoslash + +- 类型: `boolean | TwoslashOptions` + +- 默认值: `false` + +- 详情: 是否启用 [twoslash](https://github.com/twoslashes/twoslash). + + ::: tip + + 出于体积考虑,该插件默认不包含`@vuepress/shiki-twoslash`包。如需使用,需手动安装。 + + ::: + +- 参考: + + - [Shiki > Twoslash](https://shiki.style/packages/twoslash) + - [Twoslash > TwoslashOptions](https://github.com/twoslashes/twoslash/blob/main/packages/twoslash/src/types/options.ts#L18) + +- 示例: + + **输入:** + + ````md + ```ts twoslash + const a = 1 + const b = 2 + console.log(a + b) + ``` + ```` + + **输出:** + + ```ts twoslash + const a = 1 + const b = 23 + console.log(a + b) + ``` + ## 高级选项 ### defaultLang diff --git a/plugins/markdown/plugin-shiki/package.json b/plugins/markdown/plugin-shiki/package.json index 6a3a6ca768..9ff5ef2999 100644 --- a/plugins/markdown/plugin-shiki/package.json +++ b/plugins/markdown/plugin-shiki/package.json @@ -39,19 +39,19 @@ "style": "sass src:lib --embed-sources --style=compressed --pkg-importer=node" }, "dependencies": { - "@shikijs/transformers": "^2.1.0", + "@shikijs/transformers": "^2.3.2", "@vuepress/helper": "workspace:*", "@vuepress/highlighter-helper": "workspace:*", "nanoid": "^5.0.9", - "shiki": "^2.1.0", + "shiki": "^2.3.2", "synckit": "^0.9.2" }, "peerDependencies": { - "@shikijs/twoslash": "^2.1.0", + "@vuepress/shiki-twoslash": "workspace:*", "vuepress": "2.0.0-rc.19" }, "peerDependenciesMeta": { - "@shikijs/twoslash": { + "@vuepress/shiki-twoslash": { "optional": true } }, diff --git a/plugins/markdown/plugin-shiki/src/client/styles/twoslash.scss b/plugins/markdown/plugin-shiki/src/client/styles/twoslash.scss deleted file mode 100644 index 5bfbd269e3..0000000000 --- a/plugins/markdown/plugin-shiki/src/client/styles/twoslash.scss +++ /dev/null @@ -1,9 +0,0 @@ -div[data-highlighter='shiki'] { - pre.twoslash { - --twoslash-popup-bg: var(--code-c-bg, var(--shiki-light-bg)); - - [data-theme='dark'] & { - --twoslash-popup-bg: var(--code-c-bg, var(--shiki-dark-bg)); - } - } -} diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts index 557fc9809f..55375fa091 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts @@ -1,9 +1,15 @@ import { createRequire } from 'node:module' -import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki' +import type { + BundledLanguage, + BundledTheme, + HighlighterGeneric, + ShikiTransformer, +} from 'shiki' import { createHighlighter, isSpecialLang } from 'shiki' import { createSyncFn } from 'synckit' +import { isPlainObject } from 'vuepress/shared' +import type { ShikiPluginOptions } from '../../options.js' import type { ShikiResolveLang } from '../../resolveLang.js' -import type { ShikiHighlightOptions } from '../../types.js' import { resolveLanguage } from '../../utils.js' const require = createRequire(import.meta.url) @@ -20,9 +26,10 @@ export const createShikiHighlighter = async ({ defaultLang, shikiSetup, ...options -}: ShikiHighlightOptions = {}): Promise<{ +}: ShikiPluginOptions = {}): Promise<{ highlighter: HighlighterGeneric loadLang: ShikiLoadLang + transformerTwoslash: ShikiTransformer | null }> => { const highlighter = await createHighlighter({ langs: [...langs, ...Object.values(langAlias)], @@ -60,7 +67,19 @@ export const createShikiHighlighter = async ({ return rawGetLanguage.call(highlighter, name) } - await shikiSetup?.(highlighter) + let transformerTwoslash: ShikiTransformer | null = null - return { highlighter, loadLang } + if (options.twoslash) { + const { transformerTwoslash: transformer } = await import( + '@vuepress/shiki-twoslash' + ) + + transformerTwoslash = await transformer({ + twoslashOptions: isPlainObject(options.twoslash) ? options.twoslash : {}, + }) + + await shikiSetup?.(highlighter) + } + + return { highlighter, loadLang, transformerTwoslash } } diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts index 54efec5aac..14fa3073ce 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts @@ -1,7 +1,13 @@ import { transformerCompactLineOptions } from '@shikijs/transformers' -import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki' +import type { + BundledLanguage, + BundledTheme, + HighlighterGeneric, + ShikiTransformer, +} from 'shiki' import { getTransformers, + twoslashTransformer, whitespaceTransformer, } from '../../transformers/getTransformers.js' import type { ShikiHighlightOptions } from '../../types.js' @@ -22,6 +28,7 @@ export const getHighLightFunction = ( options: ShikiHighlightOptions, loadLang: ShikiLoadLang, markdownFilePathGetter: MarkdownFilePathGetter, + transformerTwoslash: ShikiTransformer | null, ): MarkdownItHighlight => { const transformers = getTransformers(options) @@ -42,6 +49,7 @@ export const getHighLightFunction = ( ? [transformerCompactLineOptions(attrsToLines(attrs))] : []), ...whitespaceTransformer(attrs, options.whitespace), + ...twoslashTransformer(attrs, transformerTwoslash), ...(options.transformers ?? []), ], ...('themes' in options diff --git a/plugins/markdown/plugin-shiki/src/node/options.ts b/plugins/markdown/plugin-shiki/src/node/options.ts index 8ac8420d32..a95e2d2e08 100644 --- a/plugins/markdown/plugin-shiki/src/node/options.ts +++ b/plugins/markdown/plugin-shiki/src/node/options.ts @@ -2,6 +2,7 @@ import type { MarkdownItCollapsedLinesOptions, MarkdownItLineNumbersOptions, } from '@vuepress/highlighter-helper' +import type { TwoslashOptions } from '@vuepress/shiki-twoslash' import type { MarkdownItPreWrapperOptions } from './markdown/index.js' import type { ShikiHighlightOptions } from './types.js' @@ -19,5 +20,5 @@ export type ShikiPluginOptions = MarkdownItLineNumbersOptions & * * @default false */ - twoslash?: boolean + twoslash?: TwoslashOptions | boolean } diff --git a/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts b/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts index f608d6880b..432f3f781a 100644 --- a/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts +++ b/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts @@ -24,6 +24,7 @@ export const prepareClientConfigFile = ( ] const setups: string[] = [] + const enhances: string[] = [] if (lineNumbers !== 'disable') { imports.push( @@ -83,20 +84,25 @@ export const prepareClientConfigFile = ( if (twoslash) { imports.push( - `import "${getModulePath('@shikijs/twoslash/style-rich.css', import.meta)}"`, - `import "${getModulePath(`${PLUGIN_NAME}/styles/twoslash.css`, import.meta)}"`, + `import { enhanceTwoslash } from "${getModulePath('@vuepress/shiki-twoslash/client', import.meta)}"`, ) + imports.push( + `import "${getModulePath('@vuepress/shiki-twoslash/styles/twoslash.css', import.meta)}"`, + ) + enhances.push('enhanceTwoslash(app)') } let code = imports.join('\n') - if (setups.length) { - code += `\n -export default { - setup() { - ${setups.join('\n ')} - } -}\n` + if (setups.length || enhances.length) { + code += '\nexport default {\n' + if (setups.length) { + code += ` setup() {\n ${setups.join('\n ')}\n },` + } + if (enhances.length) { + code += `\n enhance({ app }) {\n ${enhances.join('\n ')}\n },` + } + code += '\n}\n' } return app.writeTemp('shiki/config.js', code) diff --git a/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts b/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts index dc81243466..b989515e01 100644 --- a/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts +++ b/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts @@ -1,3 +1,4 @@ +import { isModuleAvailable } from '@vuepress/helper' import type { MarkdownItCollapsedLinesOptions, MarkdownItLineNumbersOptions, @@ -8,6 +9,7 @@ import { } from '@vuepress/highlighter-helper' import type { Plugin } from 'vuepress/core' import { isPlainObject } from 'vuepress/shared' +import { colors } from 'vuepress/utils' import { createMarkdownFilePathGetter } from './markdown/highlighter/createMarkdownFilePathGetter.js' import type { MarkdownItPreWrapperOptions } from './markdown/index.js' import { @@ -18,6 +20,7 @@ import { } from './markdown/index.js' import type { ShikiPluginOptions } from './options.js' import { prepareClientConfigFile } from './prepareClientConfigFile.js' +import { logger } from './utils.js' export const shikiPlugin = (_options: ShikiPluginOptions = {}): Plugin => { return (app) => { @@ -34,6 +37,16 @@ export const shikiPlugin = (_options: ShikiPluginOptions = {}): Plugin => { options.lineNumbers ??= true options.collapsedLines ??= 'disable' + if ( + options.twoslash && + !isModuleAvailable('@vuepress/shiki-twoslash', import.meta) + ) { + logger.error( + `${colors.cyan('twoslash')} is enabled, but ${colors.magenta('@vuepress/shiki-twoslash')} is not installed, please install it manually`, + ) + options.twoslash = false + } + return { name: '@vuepress/plugin-shiki', @@ -41,13 +54,15 @@ export const shikiPlugin = (_options: ShikiPluginOptions = {}): Plugin => { const { preWrapper, lineNumbers, collapsedLines } = options const markdownFilePathGetter = createMarkdownFilePathGetter(md) - const { highlighter, loadLang } = await createShikiHighlighter(options) + const { highlighter, loadLang, transformerTwoslash } = + await createShikiHighlighter(options) md.options.highlight = getHighLightFunction( highlighter, options, loadLang, markdownFilePathGetter, + transformerTwoslash, ) md.use(highlightLinesPlugin) @@ -62,6 +77,20 @@ export const shikiPlugin = (_options: ShikiPluginOptions = {}): Plugin => { } }, + extendsMarkdownOptions: (opts) => { + /** + * After injecting twoslash & floating-vue, + * it is necessary to turn off the `v-pre` configuration of the code block. + */ + if (options.twoslash && opts.vPre !== false) { + const vPre = isPlainObject(opts.vPre) ? opts.vPre : { block: true } + if (vPre.block) { + opts.vPre ??= {} + opts.vPre.block = false + } + } + }, + clientConfigFile: () => prepareClientConfigFile(app, options), } } diff --git a/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts b/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts index 34f94c5090..dee4cf5892 100644 --- a/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts +++ b/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts @@ -7,30 +7,18 @@ import { transformerNotationWordHighlight, transformerRenderWhitespace, } from '@shikijs/transformers' -import type { TransformerTwoslashIndexOptions } from '@shikijs/twoslash' import type { WhitespacePosition } from '@vuepress/highlighter-helper' import { resolveWhitespacePosition } from '@vuepress/highlighter-helper' import type { ShikiTransformer } from 'shiki' -import { colors } from 'vuepress/utils' import type { ShikiHighlightOptions } from '../types.js' -import { logger } from '../utils.js' import { addClassTransformer, cleanupTransformer, emptyLineTransformer, removeEscapeTransformer, + vPreTransformer, } from './vuepressTransformers.js' -let transformerTwoslash: - | ((options?: TransformerTwoslashIndexOptions) => ShikiTransformer) - | null - -try { - ;({ transformerTwoslash } = await import('@shikijs/twoslash')) -} catch { - transformerTwoslash = null -} - export const getTransformers = ( options: ShikiHighlightOptions & { twoslash?: boolean @@ -81,20 +69,6 @@ export const getTransformers = ( transformers.push(transformerMetaWordHighlight()) } - if (options.twoslash) { - if (transformerTwoslash) - transformers.push( - transformerTwoslash({ - explicitTrigger: true, - }), - ) - else { - logger.error( - `${colors.cyan('twoslash')} is enabled, but ${colors.magenta('@shikijs/twoslash')} is not installed, please install it manually`, - ) - } - } - transformers.push( addClassTransformer, cleanupTransformer, @@ -115,3 +89,15 @@ export const whitespaceTransformer = ( return [transformerRenderWhitespace({ position })] } + +const TWOSLASH_REGEXP = /\btwoslash\b/ +export const twoslashTransformer = ( + meta: string, + transformer: ShikiTransformer | null, +): ShikiTransformer[] => { + if (transformer) { + if (TWOSLASH_REGEXP.test(meta)) return [transformer] + return [vPreTransformer] + } + return [] +} diff --git a/plugins/markdown/plugin-shiki/src/node/transformers/vuepressTransformers.ts b/plugins/markdown/plugin-shiki/src/node/transformers/vuepressTransformers.ts index 3ceb435237..b2b873fc20 100644 --- a/plugins/markdown/plugin-shiki/src/node/transformers/vuepressTransformers.ts +++ b/plugins/markdown/plugin-shiki/src/node/transformers/vuepressTransformers.ts @@ -51,3 +51,10 @@ export const emptyLineTransformer: ShikiTransformer = { }) }, } + +export const vPreTransformer: ShikiTransformer = { + name: 'vuepress:v-pre', + pre(node) { + node.properties['v-pre'] = '' + }, +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 436ffa1b79..01602f0a16 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -931,23 +931,23 @@ importers: plugins/markdown/plugin-shiki: dependencies: '@shikijs/transformers': - specifier: ^2.1.0 - version: 2.1.0 - '@shikijs/twoslash': - specifier: ^2.1.0 - version: 2.1.0(typescript@5.7.3) + specifier: ^2.3.2 + version: 2.3.2 '@vuepress/helper': specifier: workspace:* version: link:../../../tools/helper '@vuepress/highlighter-helper': specifier: workspace:* version: link:../../../tools/highlighter-helper + '@vuepress/shiki-twoslash': + specifier: workspace:* + version: link:../../../tools/shiki-twoslash nanoid: specifier: ^5.0.9 version: 5.0.9 shiki: - specifier: ^2.1.0 - version: 2.1.0 + specifier: ^2.3.2 + version: 2.3.2 synckit: specifier: ^0.9.2 version: 0.9.2 @@ -1281,6 +1281,43 @@ importers: specifier: 2.0.0-rc.19 version: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(sass-embedded@1.83.4)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0))(@vuepress/bundler-webpack@2.0.0-rc.19(esbuild@0.24.2)(typescript@5.7.3))(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)) + tools/shiki-twoslash: + dependencies: + '@shikijs/twoslash': + specifier: ^2.3.2 + version: 2.3.2(typescript@5.7.3) + floating-vue: + specifier: ^5.2.2 + version: 5.2.2(vue@3.5.13(typescript@5.7.3)) + mdast-util-from-markdown: + specifier: ^2.0.2 + version: 2.0.2 + mdast-util-gfm: + specifier: ^3.0.0 + version: 3.0.0 + mdast-util-to-hast: + specifier: ^13.2.0 + version: 13.2.0 + shiki: + specifier: ^2.3.2 + version: 2.3.2 + twoslash: + specifier: ^0.2.12 + version: 0.2.12(typescript@5.7.3) + twoslash-vue: + specifier: ^0.2.12 + version: 0.2.12(typescript@5.7.3) + vuepress: + specifier: 2.0.0-rc.19 + version: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(sass-embedded@1.83.4)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0))(@vuepress/bundler-webpack@2.0.0-rc.19(esbuild@0.24.2)(typescript@5.7.3))(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)) + devDependencies: + '@types/hast': + specifier: ^3.0.4 + version: 3.0.4 + vue: + specifier: ^3.5.13 + version: 3.5.13(typescript@5.7.3) + tools/vp-update: dependencies: commander: @@ -2180,6 +2217,15 @@ packages: resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.6.9': + resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} + + '@floating-ui/dom@1.1.1': + resolution: {integrity: sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==} + + '@floating-ui/utils@0.2.9': + resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -3037,27 +3083,42 @@ packages: '@shikijs/core@2.1.0': resolution: {integrity: sha512-v795KDmvs+4oV0XD05YLzfDMe9ISBgNjtFxP4PAEv5DqyeghO1/TwDqs9ca5/E6fuO95IcAcWqR6cCX9TnqLZA==} + '@shikijs/core@2.3.2': + resolution: {integrity: sha512-s7vyL3LzUKm3Qwf36zRWlavX9BQMZTIq9B1almM63M5xBuSldnsTHCmsXzoF/Kyw4k7Xgas7yAyJz9VR/vcP1A==} + '@shikijs/engine-javascript@2.1.0': resolution: {integrity: sha512-cgIUdAliOsoaa0rJz/z+jvhrpRd+fVAoixVFEVxUq5FA+tHgBZAIfVJSgJNVRj2hs/wZ1+4hMe82eKAThVh0nQ==} + '@shikijs/engine-javascript@2.3.2': + resolution: {integrity: sha512-w3IEMu5HfL/OaJTsMbIfZ1HRPnWVYRANeDtmsdIIEgUOcLjzFJFQwlnkckGjKHekEzNqlMLbgB/twnfZ/EEAGg==} + '@shikijs/engine-oniguruma@2.1.0': resolution: {integrity: sha512-Ujik33wEDqgqY2WpjRDUBECGcKPv3eGGkoXPujIXvokLaRmGky8NisSk8lHUGeSFxo/Cz5sgFej9sJmA9yeepg==} - '@shikijs/langs@2.1.0': - resolution: {integrity: sha512-Jn0gS4rPgerMDPj1ydjgFzZr5fAIoMYz4k7ZT3LJxWWBWA6lokK0pumUwVtb+MzXtlpjxOaQejLprmLbvMZyww==} + '@shikijs/engine-oniguruma@2.3.2': + resolution: {integrity: sha512-vikMY1TroyZXUHIXbMnvY/mjtOxMn+tavcfAeQPgWS9FHcgFSUoEtywF5B5sOLb9NXb8P2vb7odkh3nj15/00A==} - '@shikijs/themes@2.1.0': - resolution: {integrity: sha512-oS2mU6+bz+8TKutsjBxBA7Z3vrQk21RCmADLpnu8cy3tZD6Rw0FKqDyXNtwX52BuIDKHxZNmRlTdG3vtcYv3NQ==} + '@shikijs/langs@2.3.2': + resolution: {integrity: sha512-UqI6bSxFzhexIJficZLKeB1L2Sc3xoNiAV0yHpfbg5meck93du+EKQtsGbBv66Ki53XZPhnR/kYkOr85elIuFw==} - '@shikijs/transformers@2.1.0': - resolution: {integrity: sha512-3sfvh6OKUVkT5wZFU1xxiq1qqNIuCwUY3yOb9ZGm19y80UZ/eoroLE2orGNzfivyTxR93GfXXZC/ghPR0/SBow==} + '@shikijs/themes@2.3.2': + resolution: {integrity: sha512-QAh7D/hhfYKHibkG2tti8vxNt3ekAH5EqkXJeJbTh7FGvTCWEI7BHqNCtMdjFvZ0vav5nvUgdvA7/HI7pfsB4w==} + + '@shikijs/transformers@2.3.2': + resolution: {integrity: sha512-2HDnJumw8A/9GecRpTgvfqSbPjEbJ4DPWq5J++OVP1gNMLvbV0MqFsP4canqRNM1LqB7VmWY45Stipb0ZIJ+0A==} '@shikijs/twoslash@2.1.0': resolution: {integrity: sha512-tgZEk78/g1ceC/mS3xA50aIc2rArl+oiphZEdAXaoioLVNebDChhV93NzcXu4NAq4pCogfBbD5HV8qO38+fQyQ==} + '@shikijs/twoslash@2.3.2': + resolution: {integrity: sha512-eYLSPNKH7qWpoStesZlDix+Mdppb/VUBc7LFZyOwTvTzZ6H+DS3OMUDH0wndc6ZWYUR27cyDMtFZoBjyzxRL0A==} + '@shikijs/types@2.1.0': resolution: {integrity: sha512-OFOdHA6VEVbiQvepJ8yqicC6VmBrKxFFhM2EsHHrZESqLVAXOSeRDiuSYV185lIgp15TVic5vYBYNhTsk1xHLg==} + '@shikijs/types@2.3.2': + resolution: {integrity: sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==} + '@shikijs/vscode-textmate@10.0.1': resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} @@ -3387,6 +3448,12 @@ packages: '@vitest/utils@3.0.4': resolution: {integrity: sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==} + '@volar/language-core@2.4.11': + resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} + + '@volar/source-map@2.4.11': + resolution: {integrity: sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==} + '@vue/compiler-core@3.5.13': resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} @@ -3399,6 +3466,9 @@ packages: '@vue/compiler-ssr@3.5.13': resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} @@ -3411,6 +3481,14 @@ packages: '@vue/devtools-shared@7.7.1': resolution: {integrity: sha512-BtgF7kHq4BHG23Lezc/3W2UhK2ga7a8ohAIAGJMBr4BkxUFzhqntQtCiuL1ijo2ztWnmusymkirgqUrXoQKumA==} + '@vue/language-core@2.1.10': + resolution: {integrity: sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@vue/reactivity@3.5.13': resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} @@ -3585,6 +3663,9 @@ packages: resolution: {integrity: sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==} engines: {node: '>= 14.0.0'} + alien-signals@0.2.2: + resolution: {integrity: sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==} + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -3855,6 +3936,9 @@ packages: character-entities@1.2.4: resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + character-reference-invalid@1.1.4: resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} @@ -4309,6 +4393,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + debounce@2.2.0: resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==} engines: {node: '>=18'} @@ -4338,6 +4425,9 @@ packages: supports-color: optional: true + decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + dedent@1.5.3: resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: @@ -4601,6 +4691,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + eslint-config-prettier@10.0.1: resolution: {integrity: sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==} hasBin: true @@ -4858,6 +4952,15 @@ packages: flatted@3.3.2: resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + floating-vue@5.2.2: + resolution: {integrity: sha512-afW+h2CFafo+7Y9Lvw/xsqjaQlKLdJV7h1fCHfcYQ1C4SVMlu7OAekqWgu5d4SgvkBVU0pVpLlVsrSTBURFRkg==} + peerDependencies: + '@nuxt/kit': ^3.2.0 + vue: ^3.2.0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + follow-redirects@1.15.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} @@ -5910,6 +6013,9 @@ packages: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loupe@3.1.2: resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} @@ -5963,6 +6069,9 @@ packages: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked-highlight@2.2.1: resolution: {integrity: sha512-SiCIeEiQbs9TxGwle9/OwbOejHCZsohQRaNTY2u8euEXYt2rYUFoiImUirThU3Gd/o6Q1gHGtH9qloHlbJpNIA==} peerDependencies: @@ -5983,15 +6092,48 @@ packages: mathml-tag-names@2.1.3: resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + mdast-util-from-markdown@0.8.5: resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.0.0: + resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.0.0: + resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + mdast-util-to-hast@13.2.0: resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + mdast-util-to-string@2.0.0: resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} @@ -6043,15 +6185,60 @@ packages: mhchemparser@4.2.1: resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} + micromark-core-commonmark@2.0.2: + resolution: {integrity: sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + micromark-util-character@2.1.1: resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + micromark-util-encode@2.0.1: resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + micromark-util-sanitize-uri@2.0.1: resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + micromark-util-subtokenize@2.0.4: + resolution: {integrity: sha512-N6hXjrin2GTJDe3MVjf5FuXpm12PGm80BrUAeub9XFXca8JZbP+oIwY4LJSVwFUCL1IPm/WwSVUN7goFHmSGGQ==} + micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} @@ -6061,6 +6248,9 @@ packages: micromark@2.11.4: resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} + micromark@4.0.1: + resolution: {integrity: sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -6174,6 +6364,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + multicast-dns@7.2.5: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true @@ -6338,6 +6531,9 @@ packages: oniguruma-to-es@2.3.0: resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==} + oniguruma-to-es@3.1.0: + resolution: {integrity: sha512-BJ3Jy22YlgejHSO7Fvmz1kKazlaPmRSUH+4adTDUS/dKQ4wLxI+gALZ8updbaux7/m7fIlpgOZ5fp/Inq5jUAw==} + open@10.1.0: resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} engines: {node: '>=18'} @@ -6483,6 +6679,9 @@ packages: pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -7005,12 +7204,18 @@ packages: regex-recursion@5.1.1: resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + regex-utilities@2.3.0: resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} regex@5.1.1: resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==} + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} @@ -7396,8 +7601,8 @@ packages: resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} engines: {node: '>= 0.4'} - shiki@2.1.0: - resolution: {integrity: sha512-yvKPdNGLXZv7WC4bl7JBbU3CEcUxnBanvMez8MG3gZXKpClGL4bHqFyLhTx+2zUvbjClUANs/S22HXb7aeOgmA==} + shiki@2.3.2: + resolution: {integrity: sha512-UZhz/gsUz7DHFbQBOJP7eXqvKyYvMGramxQiSDc83M/7OkWm6OdVHAReEc3vMLh6L6TRhgL9dvhXz9XDkCDaaw==} side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} @@ -7909,6 +8114,11 @@ packages: twoslash-protocol@0.2.12: resolution: {integrity: sha512-5qZLXVYfZ9ABdjqbvPc4RWMr7PrpPaaDSeaYY55vl/w1j6H6kzsWK/urAEIXlzYlyrFmyz1UbwIt+AA0ck+wbg==} + twoslash-vue@0.2.12: + resolution: {integrity: sha512-kxH60DLn2QBcN2wjqxgMDkyRgmPXsytv7fJIlsyFMDPSkm1/lMrI/UMrNAshNaRHcI+hv8x3h/WBgcvlb2RNAQ==} + peerDependencies: + typescript: '*' + twoslash@0.2.12: resolution: {integrity: sha512-tEHPASMqi7kqwfJbkk7hc/4EhlrKCSLcur+TcvYki3vhIfaRMXnXjaYFgXpoZRbT6GdprD4tGuVBEmTpUgLBsw==} peerDependencies: @@ -8204,6 +8414,11 @@ packages: vue: optional: true + vue-resize@2.0.0-alpha.1: + resolution: {integrity: sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==} + peerDependencies: + vue: ^3.0.0 + vue-router@4.5.0: resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} peerDependencies: @@ -9554,6 +9769,16 @@ snapshots: '@eslint/core': 0.10.0 levn: 0.4.1 + '@floating-ui/core@1.6.9': + dependencies: + '@floating-ui/utils': 0.2.9 + + '@floating-ui/dom@1.1.1': + dependencies: + '@floating-ui/core': 1.6.9 + + '@floating-ui/utils@0.2.9': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -10560,29 +10785,49 @@ snapshots: '@types/hast': 3.0.4 hast-util-to-html: 9.0.4 + '@shikijs/core@2.3.2': + dependencies: + '@shikijs/engine-javascript': 2.3.2 + '@shikijs/engine-oniguruma': 2.3.2 + '@shikijs/types': 2.3.2 + '@shikijs/vscode-textmate': 10.0.1 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.4 + '@shikijs/engine-javascript@2.1.0': dependencies: '@shikijs/types': 2.1.0 '@shikijs/vscode-textmate': 10.0.1 oniguruma-to-es: 2.3.0 + '@shikijs/engine-javascript@2.3.2': + dependencies: + '@shikijs/types': 2.3.2 + '@shikijs/vscode-textmate': 10.0.1 + oniguruma-to-es: 3.1.0 + '@shikijs/engine-oniguruma@2.1.0': dependencies: '@shikijs/types': 2.1.0 '@shikijs/vscode-textmate': 10.0.1 - '@shikijs/langs@2.1.0': + '@shikijs/engine-oniguruma@2.3.2': dependencies: - '@shikijs/types': 2.1.0 + '@shikijs/types': 2.3.2 + '@shikijs/vscode-textmate': 10.0.1 - '@shikijs/themes@2.1.0': + '@shikijs/langs@2.3.2': dependencies: - '@shikijs/types': 2.1.0 + '@shikijs/types': 2.3.2 - '@shikijs/transformers@2.1.0': + '@shikijs/themes@2.3.2': dependencies: - '@shikijs/core': 2.1.0 - '@shikijs/types': 2.1.0 + '@shikijs/types': 2.3.2 + + '@shikijs/transformers@2.3.2': + dependencies: + '@shikijs/core': 2.3.2 + '@shikijs/types': 2.3.2 '@shikijs/twoslash@2.1.0(typescript@5.7.3)': dependencies: @@ -10593,11 +10838,25 @@ snapshots: - supports-color - typescript + '@shikijs/twoslash@2.3.2(typescript@5.7.3)': + dependencies: + '@shikijs/core': 2.3.2 + '@shikijs/types': 2.3.2 + twoslash: 0.2.12(typescript@5.7.3) + transitivePeerDependencies: + - supports-color + - typescript + '@shikijs/types@2.1.0': dependencies: '@shikijs/vscode-textmate': 10.0.1 '@types/hast': 3.0.4 + '@shikijs/types@2.3.2': + dependencies: + '@shikijs/vscode-textmate': 10.0.1 + '@types/hast': 3.0.4 + '@shikijs/vscode-textmate@10.0.1': {} '@sigstore/bundle@2.3.2': @@ -10999,6 +11258,12 @@ snapshots: loupe: 3.1.2 tinyrainbow: 2.0.0 + '@volar/language-core@2.4.11': + dependencies: + '@volar/source-map': 2.4.11 + + '@volar/source-map@2.4.11': {} + '@vue/compiler-core@3.5.13': dependencies: '@babel/parser': 7.26.5 @@ -11029,6 +11294,11 @@ snapshots: '@vue/compiler-dom': 3.5.13 '@vue/shared': 3.5.13 + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + '@vue/devtools-api@6.6.4': {} '@vue/devtools-api@7.7.1': @@ -11049,6 +11319,19 @@ snapshots: dependencies: rfdc: 1.4.1 + '@vue/language-core@2.1.10(typescript@5.7.3)': + dependencies: + '@volar/language-core': 2.4.11 + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.13 + alien-signals: 0.2.2 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.7.3 + '@vue/reactivity@3.5.13': dependencies: '@vue/shared': 3.5.13 @@ -11417,6 +11700,8 @@ snapshots: '@algolia/requester-fetch': 5.20.0 '@algolia/requester-node-http': 5.20.0 + alien-signals@0.2.2: {} + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -11699,6 +11984,8 @@ snapshots: character-entities@1.2.4: {} + character-entities@2.0.2: {} + character-reference-invalid@1.1.4: {} chardet@0.7.0: {} @@ -12202,6 +12489,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + de-indent@1.0.2: {} + debounce@2.2.0: {} debug@2.6.9: @@ -12216,6 +12505,10 @@ snapshots: dependencies: ms: 2.1.3 + decode-named-character-reference@1.0.2: + dependencies: + character-entities: 2.0.2 + dedent@1.5.3: {} deep-eql@5.0.2: {} @@ -12516,6 +12809,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-config-prettier@10.0.1(eslint@9.19.0(jiti@2.4.2)): dependencies: eslint: 9.19.0(jiti@2.4.2) @@ -12883,6 +13178,12 @@ snapshots: flatted@3.3.2: {} + floating-vue@5.2.2(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@floating-ui/dom': 1.1.1 + vue: 3.5.13(typescript@5.7.3) + vue-resize: 2.0.0-alpha.1(vue@3.5.13(typescript@5.7.3)) + follow-redirects@1.15.9: {} for-each@0.3.3: @@ -13918,6 +14219,8 @@ snapshots: chalk: 5.4.1 is-unicode-supported: 1.3.0 + longest-streak@3.1.0: {} + loupe@3.1.2: {} lower-case@2.0.2: @@ -13987,6 +14290,8 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 + markdown-table@3.0.4: {} + marked-highlight@2.2.1(marked@15.0.6): dependencies: marked: 15.0.6 @@ -14004,6 +14309,13 @@ snapshots: mathml-tag-names@2.1.3: {} + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + mdast-util-from-markdown@0.8.5: dependencies: '@types/mdast': 3.0.15 @@ -14014,6 +14326,85 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + mdast-util-to-hast@13.2.0: dependencies: '@types/hast': 3.0.4 @@ -14026,8 +14417,24 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.3 + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + mdast-util-to-string@2.0.0: {} + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdn-data@2.0.28: {} mdn-data@2.0.30: {} @@ -14063,19 +14470,113 @@ snapshots: mhchemparser@4.2.1: {} + micromark-core-commonmark@2.0.2: + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.0.4 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.1 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + micromark-util-character@2.1.1: dependencies: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.1 + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + micromark-util-encode@2.0.1: {} + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.1 + micromark-util-sanitize-uri@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-encode: 2.0.1 micromark-util-symbol: 2.0.1 + micromark-util-subtokenize@2.0.4: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + micromark-util-symbol@2.0.1: {} micromark-util-types@2.0.1: {} @@ -14087,6 +14588,28 @@ snapshots: transitivePeerDependencies: - supports-color + micromark@4.0.1: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.0 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.0.4 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + transitivePeerDependencies: + - supports-color + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -14183,6 +14706,8 @@ snapshots: ms@2.1.3: {} + muggle-string@0.4.1: {} + multicast-dns@7.2.5: dependencies: dns-packet: 5.6.1 @@ -14358,6 +14883,12 @@ snapshots: regex: 5.1.1 regex-recursion: 5.1.1 + oniguruma-to-es@3.1.0: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.0.1 + regex-recursion: 6.0.2 + open@10.1.0: dependencies: default-browser: 5.2.1 @@ -14550,6 +15081,8 @@ snapshots: no-case: 3.0.4 tslib: 2.8.1 + path-browserify@1.0.1: {} + path-exists@4.0.0: {} path-exists@5.0.0: {} @@ -15013,12 +15546,20 @@ snapshots: regex: 5.1.1 regex-utilities: 2.3.0 + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + regex-utilities@2.3.0: {} regex@5.1.1: dependencies: regex-utilities: 2.3.0 + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -15411,14 +15952,14 @@ snapshots: shell-quote@1.8.2: {} - shiki@2.1.0: + shiki@2.3.2: dependencies: - '@shikijs/core': 2.1.0 - '@shikijs/engine-javascript': 2.1.0 - '@shikijs/engine-oniguruma': 2.1.0 - '@shikijs/langs': 2.1.0 - '@shikijs/themes': 2.1.0 - '@shikijs/types': 2.1.0 + '@shikijs/core': 2.3.2 + '@shikijs/engine-javascript': 2.3.2 + '@shikijs/engine-oniguruma': 2.3.2 + '@shikijs/langs': 2.3.2 + '@shikijs/themes': 2.3.2 + '@shikijs/types': 2.3.2 '@shikijs/vscode-textmate': 10.0.1 '@types/hast': 3.0.4 @@ -16005,6 +16546,15 @@ snapshots: twoslash-protocol@0.2.12: {} + twoslash-vue@0.2.12(typescript@5.7.3): + dependencies: + '@vue/language-core': 2.1.10(typescript@5.7.3) + twoslash: 0.2.12(typescript@5.7.3) + twoslash-protocol: 0.2.12 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + twoslash@0.2.12(typescript@5.7.3): dependencies: '@typescript/vfs': 1.6.0(typescript@5.7.3) @@ -16294,6 +16844,10 @@ snapshots: optionalDependencies: vue: 3.5.13(typescript@5.7.3) + vue-resize@2.0.0-alpha.1(vue@3.5.13(typescript@5.7.3)): + dependencies: + vue: 3.5.13(typescript@5.7.3) + vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)): dependencies: '@vue/devtools-api': 6.6.4 diff --git a/tools/shiki-twoslash/package.json b/tools/shiki-twoslash/package.json new file mode 100644 index 0000000000..3cf936578f --- /dev/null +++ b/tools/shiki-twoslash/package.json @@ -0,0 +1,68 @@ +{ + "name": "@vuepress/shiki-twoslash", + "version": "2.0.0-rc.74", + "description": "VuePress shiki twoslash", + "keywords": [ + "vuepress", + "shiki", + "twoslash" + ], + "homepage": "https://ecosystem.vuejs.press/tools/shiki-twoslash/", + "bugs": { + "url": "https://github.com/vuepress/ecosystem/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vuepress/ecosystem.git", + "directory": "tools/tools/shiki-twoslash" + }, + "license": "MIT", + "author": { + "name": "pengzhanbo", + "email": "volodymyr@foxmail.com", + "url": "https://github.com/pengzhanbo" + }, + "type": "module", + "exports": { + ".": "./lib/node/index.js", + "./client": "./lib/client/index.js", + "./styles/*": "./lib/client/styles/*", + "./package.json": "./package.json" + }, + "main": "./lib/node/index.js", + "types": "./lib/node/index.d.ts", + "files": [ + "lib" + ], + "scripts": { + "build": "tsc -b tsconfig.build.json", + "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", + "clean": "rimraf --glob ./lib ./*.tsbuildinfo", + "style": "sass src:lib --embed-sources --style=compressed --pkg-importer=node" + }, + "dependencies": { + "@shikijs/twoslash": "^2.3.2", + "floating-vue": "^5.2.2", + "mdast-util-from-markdown": "^2.0.2", + "mdast-util-gfm": "^3.0.0", + "mdast-util-to-hast": "^13.2.0", + "twoslash": "^0.2.12", + "twoslash-vue": "^0.2.12" + }, + "devDependencies": { + "@types/hast": "^3.0.4", + "vue": "^3.5.13" + }, + "peerDependencies": { + "vuepress": "2.0.0-rc.19", + "shiki": "^2.3.2" + }, + "peerDependenciesMeta": { + "shiki": { + "optional": true + } + }, + "publishConfig": { + "access": "public" + } +} diff --git a/tools/shiki-twoslash/rollup.config.ts b/tools/shiki-twoslash/rollup.config.ts new file mode 100644 index 0000000000..b54d83bbcd --- /dev/null +++ b/tools/shiki-twoslash/rollup.config.ts @@ -0,0 +1,3 @@ +import { rollupBundle } from '../../scripts/rollup.js' + +export default [...rollupBundle('node/index'), ...rollupBundle('client/index')] diff --git a/tools/shiki-twoslash/src/client/index.ts b/tools/shiki-twoslash/src/client/index.ts new file mode 100644 index 0000000000..876e5b619a --- /dev/null +++ b/tools/shiki-twoslash/src/client/index.ts @@ -0,0 +1 @@ +export * from './twoslash.js' diff --git a/tools/shiki-twoslash/src/client/shim.d.ts b/tools/shiki-twoslash/src/client/shim.d.ts new file mode 100644 index 0000000000..c6bbb234cb --- /dev/null +++ b/tools/shiki-twoslash/src/client/shim.d.ts @@ -0,0 +1,5 @@ +declare module 'floating-vue/dist/style.css' { + const styles: string + + export default styles +} diff --git a/tools/shiki-twoslash/src/client/styles/twoslash.scss b/tools/shiki-twoslash/src/client/styles/twoslash.scss new file mode 100644 index 0000000000..8f28fdff04 --- /dev/null +++ b/tools/shiki-twoslash/src/client/styles/twoslash.scss @@ -0,0 +1,378 @@ +/* stylelint-disable selector-class-pattern */ +/* stylelint-disable no-descending-specificity */ + +/* ===== Basic ===== */ +:root { + --twoslash-underline-color: var(--vp-c-border-hard); + --twoslash-border-color: var(--vp-c-border); + --twoslash-cursor-color: var(--vp-c-accent); + --twoslash-matched-color: var(--vp-c-accent); + --twoslash-unmatched-color: var(--vp-c-text-mute); + --twoslash-error-color: var(--vp-c-red-text); + --twoslash-error-bg: var(--vp-c-red-soft); + --twoslash-tag-color: var(--vp-c-green-text); + --twoslash-tag-bg: var(--vp-c-green-soft); + --twoslash-tag-warn-color: var(--vp-c-yellow-text); + --twoslash-tag-warn-bg: var(--vp-c-yellow-soft); + --twoslash-tag-annotate-color: var(--vp-c-blue-text); + --twoslash-tag-annotate-bg: var(--vp-c-blue-soft); + --twoslash-highlighted-bg: var(--vp-c-gray-soft); + --twoslash-highlighted-border: var(--vp-c-border); + --twoslash-popup-bg: var(--vp-c-bg, inherit); + --twoslash-popup-color: var(--vp-c-text); + --twoslash-popup-shadow: var(--vp-c-shadow); + --twoslash-docs-color: var(--vp-c-text); + --twoslash-docs-font: var(--font-family); + --twoslash-jsdoc-color: var(--vp-c-text-mute); + --twoslash-code-font: var(--code-font-family); + --twoslash-code-size: var(--code-font-size); + --twoslash-code-line-height: var(--code-line-height); + --twoslash-z-index: 10; +} + +/* Respect people's wishes to not have animations */ +@media (prefers-reduced-motion: reduce) { + .twoslash * { + transition: none !important; + } +} + +/* ===== Hover Info ===== */ +.twoslash:hover .twoslash-hover { + border-color: var(--twoslash-underline-color); +} + +.twoslash .twoslash-hover { + position: relative; + border-bottom: 1px dotted transparent; + transition: border-color var(--vp-t-color); + transition-timing-function: ease; +} + +/* ===== Error Line ===== */ +.twoslash .twoslash-error-line { + position: relative; + + margin: 0.2em 0; + padding: 6px; + border-left: 3px solid var(--twoslash-error-color); + + background-color: var(--twoslash-error-bg); + color: var(--twoslash-error-color); + + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); +} + +.twoslash .twoslash-error { + padding-bottom: 2px; + background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23c94824'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") + repeat-x bottom left; +} + +/* ===== Completeions ===== */ +.twoslash .twoslash-completion-cursor { + position: relative; +} + +.twoslash .twoslash-completion-cursor .twoslash-completion-list { + position: absolute; + top: 0; + left: 0; + z-index: 8; + + display: inline-block; + + margin: 3px 0 0 -1px; + border: 1px solid var(--twoslash-border-color); + + background: var(--twoslash-popup-bg); + box-shadow: var(--twoslash-popup-shadow); + + user-select: none; + + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); + + transform: translate(0, 1.2em); +} + +.twoslash-completion-list { + display: flex; + flex-direction: column; + gap: 4px; + + width: 240px; + padding: 4px; + + font-size: 0.8rem; +} + +.twoslash-completion-list:hover { + user-select: auto; +} + +.twoslash-completion-list::before { + content: ' '; + + position: absolute; + top: -1.6em; + left: -1px; + + width: 2px; + height: 1.4em; + + background-color: var(--twoslash-cursor-color); +} + +.twoslash-completion-list li { + display: flex; + gap: 0.25em; + align-items: center; + + overflow: hidden; + + line-height: 1em; +} + +.twoslash-completion-list li span.twoslash-completions-unmatched { + color: var(--twoslash-unmatched-color); +} + +.twoslash-completion-list .deprecated { + text-decoration: line-through; + opacity: 0.5; +} + +.twoslash-completion-list li span.twoslash-completions-matched { + color: var(--twoslash-matched-color); +} + +/* Highlights */ +.twoslash-highlighted { + margin: -1px -3px; + padding: 1px 2px; + border: 1px solid var(--twoslash-highlighted-border); + border-radius: 4px; + + background-color: var(--twoslash-highlighted-bg); + + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); +} + +/* Icons */ +.twoslash-completion-list .twoslash-completions-icon { + flex: none; + width: 1em; + color: var(--twoslash-unmatched-color); +} + +/* Custom Tags */ +.twoslash .twoslash-tag-line { + position: relative; + + display: flex; + gap: 0.3em; + align-items: center; + + margin: 0.2em 0; + padding: 6px 10px; + border-left: 3px solid var(--twoslash-tag-color); + + background-color: var(--twoslash-tag-bg); + color: var(--twoslash-tag-color); + + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); +} + +.twoslash .twoslash-tag-line .twoslash-tag-icon { + width: 1.1em; + color: inherit; +} + +.twoslash .twoslash-tag-line.twoslash-tag-error-line { + border-left: 3px solid var(--twoslash-error-color); + background-color: var(--twoslash-error-bg); + color: var(--twoslash-error-color); +} + +.twoslash .twoslash-tag-line.twoslash-tag-warn-line { + border-left: 3px solid var(--twoslash-tag-warn-color); + background-color: var(--twoslash-tag-warn-bg); + color: var(--twoslash-tag-warn-color); +} + +.twoslash .twoslash-tag-line.twoslash-tag-annotate-line { + border-left: 3px solid var(--twoslash-tag-annotate-color); + background-color: var(--twoslash-tag-annotate-bg); + color: var(--twoslash-tag-annotate-color); +} + +/* ========== floating vue ================== */ +.v-popper--theme-twoslash { + z-index: var(--twoslash-z-index); +} + +.v-popper--theme-twoslash .v-popper__inner { + border-color: var(--twoslash-border-color); + background: var(--twoslash-popup-bg); + color: var(--twoslash-popup-color); + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); +} + +.v-popper--theme-twoslash .v-popper__arrow-outer { + border-color: var(--twoslash-border-color); + transition: border-color var(--vp-t-color); +} + +.v-popper--theme-twoslash .v-popper__arrow-inner { + border-color: var(--twoslash-popup-bg); + transition: border-color var(--vp-t-color); +} + +.twoslash-popup-container { + transform: translateY(1.5em); +} + +.twoslash-query-presisted .twoslash-popup-container { + transform: translateY(1.8em); +} + +.twoslash .v-popper { + display: inline-block; +} + +.twoslash-completion-list .twoslash-completions-icon { + color: var(--twoslash-unmatched-color) !important; +} + +.twoslash-floating .twoslash-popup-code { + display: block; + + box-sizing: border-box; + width: fit-content; + min-width: 100%; + max-width: 600px; + padding: 6px 12px; + border-radius: 0; + + font-size: var(--twoslash-code-size); + line-height: var(--twoslash-code-line-height); + white-space: pre-wrap; +} + +.twoslash-floating .twoslash-popup-code, +.twoslash-floating .twoslash-popup-docs { + background-color: var(--twoslash-popup-bg); + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); +} + +.twoslash-floating .twoslash-popup-docs, +.twoslash-floating .twoslash-popup-error { + overflow: hidden auto; + + max-width: 700px; + max-height: 500px; + padding: 12px !important; + + font-size: 0.9em; + font-family: var(--twoslash-docs-font); + text-wrap: balance; +} + +.twoslash-floating .twoslash-popup-docs p:first-child, +.twoslash-floating .twoslash-popup-error p:first-child { + margin-top: 0; +} + +.twoslash-floating .twoslash-popup-docs p:last-child, +.twoslash-floating .twoslash-popup-error p:last-child { + margin-bottom: 0; +} + +.twoslash-floating .twoslash-popup-docs { + border-top: 1px solid var(--twoslash-border-color); + color: var(--twoslash-docs-color); +} + +.twoslash-floating .twoslash-popup-error { + color: var(--twoslash-error-color); +} + +.twoslash-floating .twoslash-popup-error.twoslash-error-level-warning { + color: var(--twoslash-warn-color); +} + +.twoslash-floating .twoslash-popup-docs p, +.twoslash-floating .twoslash-popup-error p { + margin: 6px 0; + text-wrap: balance; +} + +/* stylelint-disable-next-line selector-max-compound-selectors */ +.twoslash-floating + .twoslash-popup-docs + pre + .twoslash-floating + .twoslash-popup-error + pre { + overflow-x: auto; + + margin: 6px -2px; + padding: 12px; + border-radius: 8px; + + background-color: var(--code-c-bg); + + transition: background-color var(--vp-t-color); +} + +.twoslash-floating .twoslash-popup-docs-tags { + display: flex; + flex-direction: column; + padding: 8px 12px !important; +} + +.twoslash-floating .twoslash-popup-docs-tags .twoslash-popup-docs-tag-name { + margin-right: 0.5em; + color: var(--twoslash-unmatched-color); + font-family: var(--twoslash-code-font); +} + +.twoslash-completion-cursor { + display: inline-block; + + width: 2px; + height: 1.2em; + margin-bottom: -0.2em; + + background: var(--twoslash-cursor-color); + + user-select: none; + + transition: background-color var(--vp-t-color); +} + +.twoslash-floating.twoslash-completion .v-popper__arrow-container { + display: none; +} + +.twoslash-floating.twoslash-completion .twoslash-completion-list { + padding: 6px; + font-size: var(--twoslash-code-size) !important; + font-family: var(--twoslash-code-font); +} + +.twoslash-floating.twoslash-completion .twoslash-completion-list li { + padding: 3px 0; +} diff --git a/tools/shiki-twoslash/src/client/twoslash.ts b/tools/shiki-twoslash/src/client/twoslash.ts new file mode 100644 index 0000000000..c82563b4ee --- /dev/null +++ b/tools/shiki-twoslash/src/client/twoslash.ts @@ -0,0 +1,62 @@ +import FloatingVue, { recomputeAllPoppers } from 'floating-vue' +import type { App } from 'vue' +import 'floating-vue/dist/style.css' + +const isMobile = + typeof navigator !== 'undefined' && + /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent, + ) + +export const enhanceTwoslash = (app: App): void => { + if (typeof window !== 'undefined') { + // Recompute poppers when clicking on a tab + window.addEventListener( + 'click', + (e) => { + const path = e.composedPath() + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if ( + path.some((el) => + (el as HTMLElement)?.classList?.contains('vp-code-tab'), + ) + ) + recomputeAllPoppers() + }, + { passive: true }, + ) + } + + app.use(FloatingVue, { + themes: { + 'twoslash': { + $extend: 'dropdown', + triggers: isMobile ? ['touch'] : ['hover', 'touch'], + popperTriggers: isMobile ? ['touch'] : ['hover', 'touch'], + placement: 'bottom-start', + overflowPadding: 10, + delay: 0, + handleResize: false, + autoHide: true, + instantMove: true, + flip: false, + arrowPadding: 8, + autoBoundaryMaxSize: true, + }, + 'twoslash-query': { + $extend: 'twoslash', + triggers: ['click'], + popperTriggers: ['click'], + autoHide: false, + }, + 'twoslash-completion': { + $extend: 'twoslash-query', + triggers: ['click'], + popperTriggers: ['click'], + autoHide: false, + distance: 0, + arrowOverflow: true, + }, + }, + }) +} diff --git a/tools/shiki-twoslash/src/node/index.ts b/tools/shiki-twoslash/src/node/index.ts new file mode 100644 index 0000000000..19b4ca9cf8 --- /dev/null +++ b/tools/shiki-twoslash/src/node/index.ts @@ -0,0 +1,4 @@ +export * from './rendererFloatingVue.js' +export * from './transformerTwoslash.js' +export * from './resolveTsPaths.js' +export type * from './types.js' diff --git a/tools/shiki-twoslash/src/node/rendererFloatingVue.ts b/tools/shiki-twoslash/src/node/rendererFloatingVue.ts new file mode 100644 index 0000000000..d5e627682f --- /dev/null +++ b/tools/shiki-twoslash/src/node/rendererFloatingVue.ts @@ -0,0 +1,220 @@ +import type { TwoslashRenderer } from '@shikijs/twoslash' +import { defaultHoverInfoProcessor, rendererRich } from '@shikijs/twoslash' +import type { Element, ElementContent, Text } from 'hast' +import { fromMarkdown } from 'mdast-util-from-markdown' +import { gfmFromMarkdown } from 'mdast-util-gfm' +import { defaultHandlers, toHast } from 'mdast-util-to-hast' +import type { ShikiTransformerContextCommon } from 'shiki' +import type { TwoslashFloatingVueRendererOptions } from './types.js' + +export { defaultHoverInfoProcessor } + +const vPre = (el: T): T => { + if (el.type === 'element') { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + el.properties ??= {} + el.properties['v-pre'] = '' + } + return el +} + +const compose = (parts: { + token: Element | Text + popup: Element +}): Element[] => { + return [ + { + type: 'element', + tagName: 'span', + properties: {}, + children: [parts.token], + }, + { + type: 'element', + tagName: 'template', + properties: { + 'v-slot:popper': '{}', + }, + content: { + type: 'root', + children: [vPre(parts.popup)], + }, + children: [], + }, + ] +} + +function renderMarkdown( + this: ShikiTransformerContextCommon, + md: string, +): ElementContent[] { + const mdast = fromMarkdown( + md.replace(/\{@link ([^}]*)\}/g, '$1'), // replace jsdoc links + { mdastExtensions: [gfmFromMarkdown()] }, + ) + + return ( + toHast(mdast, { + handlers: { + code: (state, node: Parameters[1]) => { + const _node = node as { lang?: string; value: string } + const lang = _node.lang ?? '' + const { children } = this.codeToHast(_node.value, { + ...this.options, + transformers: [], + lang, + structure: _node.value.trim().includes('\n') ? 'classic' : 'inline', + }) + if (lang) { + return { + type: 'element', + tagName: 'div', + properties: { + 'class': `language-${lang}`, + 'data-ext': lang, + 'data-highlighter': 'shiki', + 'style': + children[0]?.type === 'element' && + children[0].tagName === 'pre' + ? children[0].properties.style + : '', + }, + children, + } as Element + } + return defaultHandlers.code(state, node) + }, + }, + }) as Element + ).children +} + +function renderMarkdownInline( + this: ShikiTransformerContextCommon, + md: string, + context?: string, +): ElementContent[] { + let str = md + if (context === 'tag:param') str = md.replace(/^([\w$-]+)/, '`$1` ') + + const children = renderMarkdown.call(this, str) + if ( + children.length === 1 && + children[0].type === 'element' && + children[0].tagName === 'p' + ) + return children[0].children + return children +} + +export const rendererFloatingVue = ( + options: TwoslashFloatingVueRendererOptions = {}, +): TwoslashRenderer => { + const { + classCopyIgnore = 'vp-copy-ignore', + classFloatingPanel = 'twoslash-floating', + classCode = 'vp-code', + attrMarkdown = 'vp-content', + floatingVueTheme = 'twoslash', + floatingVueThemeQuery = 'twoslash-query', + floatingVueThemeCompletion = 'twoslash-completion', + } = options.floatingVue ?? {} + + const { errorRendering = 'line' } = options + + const hoverBasicProps = { + 'class': 'twoslash-hover', + 'popper-class': [ + 'shiki', + classFloatingPanel, + classCopyIgnore, + classCode, + ].join(' '), + 'theme': floatingVueTheme, + } + + const rich = rendererRich({ + classExtra: classCopyIgnore, + ...options, + renderMarkdown, + renderMarkdownInline, + hast: { + hoverToken: { + tagName: 'v-menu', + properties: hoverBasicProps, + }, + hoverCompose: compose, + queryToken: { + tagName: 'v-menu', + properties: { + ...hoverBasicProps, + ':shown': 'true', + 'theme': floatingVueThemeQuery, + }, + }, + queryCompose: compose, + popupDocs: { + class: 'twoslash-popup-docs', + properties: { + [attrMarkdown]: '', + }, + }, + popupDocsTags: { + class: 'twoslash-popup-docs twoslash-popup-docs-tags', + properties: { + [attrMarkdown]: '', + }, + }, + popupError: { + class: 'twoslash-popup-error', + properties: { + [attrMarkdown]: '', + }, + }, + errorToken: + errorRendering === 'line' + ? undefined + : { + tagName: 'v-menu', + properties: { + ...hoverBasicProps, + class: 'twoslash-error twoslash-error-hover', + }, + }, + errorCompose: compose, + completionCompose({ popup, cursor }) { + return [ + { + type: 'element', + tagName: 'v-menu', + properties: { + 'popper-class': [ + 'shiki twoslash-completion', + classCopyIgnore, + classFloatingPanel, + ], + 'theme': floatingVueThemeCompletion, + ':shown': 'true', + }, + children: [ + cursor, + { + type: 'element', + tagName: 'template', + properties: { + 'v-slot:popper': '{}', + }, + content: { + type: 'root', + children: [vPre(popup)], + }, + }, + ], + } as Element, + ] + }, + }, + }) + + return rich +} diff --git a/tools/shiki-twoslash/src/node/resolveTsPaths.ts b/tools/shiki-twoslash/src/node/resolveTsPaths.ts new file mode 100644 index 0000000000..a3a66610f1 --- /dev/null +++ b/tools/shiki-twoslash/src/node/resolveTsPaths.ts @@ -0,0 +1,31 @@ +import fs from 'node:fs/promises' +import path from 'node:path' +import process from 'node:process' + +export async function resolveTsPaths(): Promise< + Record | undefined +> { + const tsconfigPath = path.join(process.cwd(), 'tsconfig.json') + try { + const tsconfig = JSON.parse(await fs.readFile(tsconfigPath, 'utf-8')) as { + compilerOptions?: { paths?: Record; baseUrl?: string } + } + + const paths = tsconfig.compilerOptions?.paths ?? undefined + const baseUrl = tsconfig.compilerOptions?.baseUrl + + if (baseUrl && paths) { + const dirname = path.join(process.cwd(), baseUrl) + for (const key in paths) { + const value = paths[key] + if (Array.isArray(value)) + paths[key] = value.map((v) => path.resolve(dirname, v)) + else paths[key] = [path.resolve(dirname, value)] + } + } + + return paths + } catch { + return undefined + } +} diff --git a/tools/shiki-twoslash/src/node/transformerTwoslash.ts b/tools/shiki-twoslash/src/node/transformerTwoslash.ts new file mode 100644 index 0000000000..b7b4ef90d1 --- /dev/null +++ b/tools/shiki-twoslash/src/node/transformerTwoslash.ts @@ -0,0 +1,97 @@ +import process from 'node:process' +import { + createTransformerFactory, + defaultHoverInfoProcessor, + defaultTwoslashOptions, +} from '@shikijs/twoslash/core' +import type { ShikiTransformer } from 'shiki' +import { removeTwoslashNotations } from 'twoslash' +import { createTwoslasher } from 'twoslash-vue' +import { logger } from 'vuepress/utils' +import { rendererFloatingVue } from './rendererFloatingVue.js' +import { resolveTsPaths } from './resolveTsPaths.js' +import type { VuePressTwoslashOptions } from './types.js' + +/** + * Create a Shiki transformer for VitePress to enable twoslash integration + */ +export const transformerTwoslash = async ( + options: VuePressTwoslashOptions = {}, +): Promise => { + const { explicitTrigger = true } = options + + const onError = (error: unknown, code: string): string => { + const isCI = process.env.CI + const isDev = + typeof process !== 'undefined' && process.env.NODE_ENV === 'development' + const shouldThrow = + (options.throws ?? isCI ?? !isDev) && options.throws !== false + logger.error( + `\n\n--------\nTwoslash error in code:\n--------\n${code.split(/\n/g).slice(0, 15).join('\n').trim()}\n--------\n`, + ) + if (shouldThrow) { + throw error + } else { + logger.error(error) + } + return removeTwoslashNotations(code) + } + options.processHoverInfo ??= defaultHoverInfoProcessor + + const paths = await resolveTsPaths() + const { compilerOptions = {}, ...twoslashOptions } = + options.twoslashOptions ?? {} + if (paths) { + compilerOptions.paths = { + ...compilerOptions.paths, + ...paths, + } + } + options.twoslashOptions = { + ...defaultTwoslashOptions(), + ...twoslashOptions, + compilerOptions: { + baseUrl: process.cwd(), + ...compilerOptions, + }, + } + + const twoslash = createTransformerFactory( + createTwoslasher(options.twoslashOptions), + )({ + langs: ['ts', 'tsx', 'js', 'jsx', 'json', 'vue'], + renderer: rendererFloatingVue(options), + onTwoslashError: onError, + onShikiError: onError, + ...options, + explicitTrigger, + }) + + const trigger = + explicitTrigger instanceof RegExp ? explicitTrigger : /\btwoslash\b/ + + return { + ...twoslash, + name: '@shiki/vuepress-twoslash', + preprocess(code, opt) { + const cleanup = opt.transformers?.find( + (i) => i.name === 'vuepress:clean-up', + ) + if (cleanup) + opt.transformers?.splice(opt.transformers.indexOf(cleanup), 1) + + // Disable v-pre for twoslash, because we need render it with FloatingVue + if (!explicitTrigger || opt.meta?.__raw?.match(trigger)) { + const vPre = opt.transformers?.find((i) => i.name === 'vuepress:v-pre') + if (vPre) opt.transformers?.splice(opt.transformers.indexOf(vPre), 1) + } + + return twoslash.preprocess!.call(this, code, opt) + }, + postprocess(html) { + if (this.meta.twoslash) return html.replace(/\{/g, '{') + + return html + }, + } +} diff --git a/tools/shiki-twoslash/src/node/types.ts b/tools/shiki-twoslash/src/node/types.ts new file mode 100644 index 0000000000..82fac667df --- /dev/null +++ b/tools/shiki-twoslash/src/node/types.ts @@ -0,0 +1,39 @@ +import type { RendererRichOptions } from '@shikijs/twoslash' +import type { TransformerTwoslashOptions } from '@shikijs/twoslash/core' +import type { VueSpecificOptions } from 'twoslash-vue' + +export interface TwoslashFloatingVueOptions { + classCopyIgnore?: string + classFloatingPanel?: string + classCode?: string + attrMarkdown?: string + + floatingVueTheme?: string + floatingVueThemeQuery?: string + floatingVueThemeCompletion?: string +} + +export interface TwoslashFloatingVueRendererOptions + extends RendererRichOptions { + /** + * Class and themes for floating-vue specific nodes + */ + floatingVue?: TwoslashFloatingVueOptions +} + +export type TwoslashOptions = TransformerTwoslashOptions['twoslashOptions'] & + VueSpecificOptions + +export interface VuePressTwoslashOptions + extends TransformerTwoslashOptions, + TwoslashFloatingVueRendererOptions { + /** + * Twoslash options + */ + twoslashOptions?: TwoslashOptions + /** + * Requires adding `twoslash` to the code block explicitly to run twoslash + * @default true + */ + explicitTrigger?: TransformerTwoslashOptions['explicitTrigger'] +} diff --git a/tools/shiki-twoslash/tsconfig.build.json b/tools/shiki-twoslash/tsconfig.build.json new file mode 100644 index 0000000000..78ad610eea --- /dev/null +++ b/tools/shiki-twoslash/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "types": ["vuepress/client-types"] + }, + "include": ["./src"] +} From f0a349af2f458cc28e8af934a3e5a05dcd419bf1 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 12:56:58 +0800 Subject: [PATCH 06/16] chore: tweak --- docs/.vuepress/config.ts | 3 +- docs/package.json | 2 +- pnpm-lock.yaml | 79 ++-------------------------------------- tsconfig.build.json | 1 + 4 files changed, 6 insertions(+), 79 deletions(-) diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index 38b3064c6b..d1cfc5787c 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -1,5 +1,4 @@ import process from 'node:process' -import { transformerTwoslash } from '@shikijs/twoslash' import { viteBundler } from '@vuepress/bundler-vite' import { webpackBundler } from '@vuepress/bundler-webpack' import { getModulePath } from '@vuepress/helper' @@ -27,7 +26,7 @@ import theme from './theme.js' const __dirname = import.meta.dirname || getDirname(import.meta.url) -const IS_PROD = process.env.NODE_ENV === 'production' +// const IS_PROD = process.env.NODE_ENV === 'production' export default defineUserConfig({ // set site base to default value diff --git a/docs/package.json b/docs/package.json index 01e02e180d..b5ccc74a84 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,7 +9,7 @@ "docs:serve": "http-server -a localhost .vuepress/dist" }, "dependencies": { - "@shikijs/twoslash": "^2.1.0", + "@vuepress/shiki-twoslash": "workspace:*", "@vuepress/bundler-vite": "2.0.0-rc.19", "@vuepress/bundler-webpack": "2.0.0-rc.19", "@vuepress/helper": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 01602f0a16..1aa8a55fce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,9 +124,6 @@ importers: docs: dependencies: - '@shikijs/twoslash': - specifier: ^2.1.0 - version: 2.1.0(typescript@5.7.3) '@vuepress/bundler-vite': specifier: 2.0.0-rc.19 version: 2.0.0-rc.19(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(sass-embedded@1.83.4)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) @@ -205,6 +202,9 @@ importers: '@vuepress/plugin-shiki': specifier: workspace:* version: link:../plugins/markdown/plugin-shiki + '@vuepress/shiki-twoslash': + specifier: workspace:* + version: link:../tools/shiki-twoslash '@vuepress/theme-default': specifier: workspace:* version: link:../themes/theme-default @@ -3080,21 +3080,12 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@shikijs/core@2.1.0': - resolution: {integrity: sha512-v795KDmvs+4oV0XD05YLzfDMe9ISBgNjtFxP4PAEv5DqyeghO1/TwDqs9ca5/E6fuO95IcAcWqR6cCX9TnqLZA==} - '@shikijs/core@2.3.2': resolution: {integrity: sha512-s7vyL3LzUKm3Qwf36zRWlavX9BQMZTIq9B1almM63M5xBuSldnsTHCmsXzoF/Kyw4k7Xgas7yAyJz9VR/vcP1A==} - '@shikijs/engine-javascript@2.1.0': - resolution: {integrity: sha512-cgIUdAliOsoaa0rJz/z+jvhrpRd+fVAoixVFEVxUq5FA+tHgBZAIfVJSgJNVRj2hs/wZ1+4hMe82eKAThVh0nQ==} - '@shikijs/engine-javascript@2.3.2': resolution: {integrity: sha512-w3IEMu5HfL/OaJTsMbIfZ1HRPnWVYRANeDtmsdIIEgUOcLjzFJFQwlnkckGjKHekEzNqlMLbgB/twnfZ/EEAGg==} - '@shikijs/engine-oniguruma@2.1.0': - resolution: {integrity: sha512-Ujik33wEDqgqY2WpjRDUBECGcKPv3eGGkoXPujIXvokLaRmGky8NisSk8lHUGeSFxo/Cz5sgFej9sJmA9yeepg==} - '@shikijs/engine-oniguruma@2.3.2': resolution: {integrity: sha512-vikMY1TroyZXUHIXbMnvY/mjtOxMn+tavcfAeQPgWS9FHcgFSUoEtywF5B5sOLb9NXb8P2vb7odkh3nj15/00A==} @@ -3107,15 +3098,9 @@ packages: '@shikijs/transformers@2.3.2': resolution: {integrity: sha512-2HDnJumw8A/9GecRpTgvfqSbPjEbJ4DPWq5J++OVP1gNMLvbV0MqFsP4canqRNM1LqB7VmWY45Stipb0ZIJ+0A==} - '@shikijs/twoslash@2.1.0': - resolution: {integrity: sha512-tgZEk78/g1ceC/mS3xA50aIc2rArl+oiphZEdAXaoioLVNebDChhV93NzcXu4NAq4pCogfBbD5HV8qO38+fQyQ==} - '@shikijs/twoslash@2.3.2': resolution: {integrity: sha512-eYLSPNKH7qWpoStesZlDix+Mdppb/VUBc7LFZyOwTvTzZ6H+DS3OMUDH0wndc6ZWYUR27cyDMtFZoBjyzxRL0A==} - '@shikijs/types@2.1.0': - resolution: {integrity: sha512-OFOdHA6VEVbiQvepJ8yqicC6VmBrKxFFhM2EsHHrZESqLVAXOSeRDiuSYV185lIgp15TVic5vYBYNhTsk1xHLg==} - '@shikijs/types@2.3.2': resolution: {integrity: sha512-CBaMY+a3pepyC4SETi7+bSzO0f6hxEQJUUuS4uD7zppzjmrN4ZRtBqxaT+wOan26CR9eeJ5iBhc4qvWEwn7Eeg==} @@ -6528,9 +6513,6 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - oniguruma-to-es@2.3.0: - resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==} - oniguruma-to-es@3.1.0: resolution: {integrity: sha512-BJ3Jy22YlgejHSO7Fvmz1kKazlaPmRSUH+4adTDUS/dKQ4wLxI+gALZ8updbaux7/m7fIlpgOZ5fp/Inq5jUAw==} @@ -7201,18 +7183,12 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} - regex-recursion@5.1.1: - resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==} - regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} regex-utilities@2.3.0: resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - regex@5.1.1: - resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==} - regex@6.0.1: resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} @@ -10776,15 +10752,6 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@shikijs/core@2.1.0': - dependencies: - '@shikijs/engine-javascript': 2.1.0 - '@shikijs/engine-oniguruma': 2.1.0 - '@shikijs/types': 2.1.0 - '@shikijs/vscode-textmate': 10.0.1 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.4 - '@shikijs/core@2.3.2': dependencies: '@shikijs/engine-javascript': 2.3.2 @@ -10794,23 +10761,12 @@ snapshots: '@types/hast': 3.0.4 hast-util-to-html: 9.0.4 - '@shikijs/engine-javascript@2.1.0': - dependencies: - '@shikijs/types': 2.1.0 - '@shikijs/vscode-textmate': 10.0.1 - oniguruma-to-es: 2.3.0 - '@shikijs/engine-javascript@2.3.2': dependencies: '@shikijs/types': 2.3.2 '@shikijs/vscode-textmate': 10.0.1 oniguruma-to-es: 3.1.0 - '@shikijs/engine-oniguruma@2.1.0': - dependencies: - '@shikijs/types': 2.1.0 - '@shikijs/vscode-textmate': 10.0.1 - '@shikijs/engine-oniguruma@2.3.2': dependencies: '@shikijs/types': 2.3.2 @@ -10829,15 +10785,6 @@ snapshots: '@shikijs/core': 2.3.2 '@shikijs/types': 2.3.2 - '@shikijs/twoslash@2.1.0(typescript@5.7.3)': - dependencies: - '@shikijs/core': 2.1.0 - '@shikijs/types': 2.1.0 - twoslash: 0.2.12(typescript@5.7.3) - transitivePeerDependencies: - - supports-color - - typescript - '@shikijs/twoslash@2.3.2(typescript@5.7.3)': dependencies: '@shikijs/core': 2.3.2 @@ -10847,11 +10794,6 @@ snapshots: - supports-color - typescript - '@shikijs/types@2.1.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.1 - '@types/hast': 3.0.4 - '@shikijs/types@2.3.2': dependencies: '@shikijs/vscode-textmate': 10.0.1 @@ -14877,12 +14819,6 @@ snapshots: dependencies: mimic-function: 5.0.1 - oniguruma-to-es@2.3.0: - dependencies: - emoji-regex-xs: 1.0.0 - regex: 5.1.1 - regex-recursion: 5.1.1 - oniguruma-to-es@3.1.0: dependencies: emoji-regex-xs: 1.0.0 @@ -15541,21 +15477,12 @@ snapshots: dependencies: '@babel/runtime': 7.26.0 - regex-recursion@5.1.1: - dependencies: - regex: 5.1.1 - regex-utilities: 2.3.0 - regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 regex-utilities@2.3.0: {} - regex@5.1.1: - dependencies: - regex-utilities: 2.3.0 - regex@6.0.1: dependencies: regex-utilities: 2.3.0 diff --git a/tsconfig.build.json b/tsconfig.build.json index 489a6b6f26..1726530867 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -109,6 +109,7 @@ { "path": "./tools/create-vuepress/tsconfig.build.json" }, { "path": "./tools/helper/tsconfig.build.json" }, { "path": "./tools/highlighter-helper/tsconfig.build.json" }, + { "path": "./tools/shiki-twoslash/tsconfig.build.json" }, { "path": "./tools/vp-update/tsconfig.build.json" } ], "files": [] From 28f18b178612cd3e0e6d8be2b02be34232c1b62c Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 13:00:09 +0800 Subject: [PATCH 07/16] chore: tweak --- plugins/markdown/plugin-shiki/tsconfig.build.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/markdown/plugin-shiki/tsconfig.build.json b/plugins/markdown/plugin-shiki/tsconfig.build.json index a99fd7b273..52c53233b6 100644 --- a/plugins/markdown/plugin-shiki/tsconfig.build.json +++ b/plugins/markdown/plugin-shiki/tsconfig.build.json @@ -7,6 +7,7 @@ "include": ["./src"], "references": [ { "path": "../../../tools/highlighter-helper/tsconfig.build.json" }, - { "path": "../../../tools/helper/tsconfig.build.json" } + { "path": "../../../tools/helper/tsconfig.build.json" }, + { "path": "../../../tools/shiki-twoslash/tsconfig.build.json" } ] } From ad22c8fc9c0e1abfd4ac96e4a3a547a0d79bf7e8 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 13:11:34 +0800 Subject: [PATCH 08/16] style: lint fix --- tools/shiki-twoslash/src/client/twoslash.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/shiki-twoslash/src/client/twoslash.ts b/tools/shiki-twoslash/src/client/twoslash.ts index c82563b4ee..2113655e75 100644 --- a/tools/shiki-twoslash/src/client/twoslash.ts +++ b/tools/shiki-twoslash/src/client/twoslash.ts @@ -1,4 +1,4 @@ -import FloatingVue, { recomputeAllPoppers } from 'floating-vue' +import Gt, { recomputeAllPoppers } from 'floating-vue' import type { App } from 'vue' import 'floating-vue/dist/style.css' @@ -15,9 +15,9 @@ export const enhanceTwoslash = (app: App): void => { 'click', (e) => { const path = e.composedPath() - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if ( path.some((el) => + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition (el as HTMLElement)?.classList?.contains('vp-code-tab'), ) ) @@ -27,7 +27,7 @@ export const enhanceTwoslash = (app: App): void => { ) } - app.use(FloatingVue, { + app.use(Gt, { themes: { 'twoslash': { $extend: 'dropdown', From 7656a719ea051f922194fa07a1e06de1ad6b2fb0 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 17:23:56 +0800 Subject: [PATCH 09/16] chore: tweak --- .../highlighter/createShikiHighlighter.ts | 22 +++++++--------- .../highlighter/getHighLightFunction.ts | 13 +++------- .../plugin-shiki/src/node/shikiPlugin.ts | 4 +-- .../src/node/transformers/getTransformers.ts | 13 ---------- .../node/transformers/vuepressTransformers.ts | 7 ------ pnpm-lock.yaml | 6 ++--- tools/shiki-twoslash/package.json | 9 ++----- .../src/node/createTwoslashTransformer.ts | 25 +++++++++++++++++++ tools/shiki-twoslash/src/node/index.ts | 3 ++- .../src/node/rendererFloatingVue.ts | 4 +-- ...slash.ts => transformerTwoslashFactory.ts} | 4 +-- 11 files changed, 50 insertions(+), 60 deletions(-) create mode 100644 tools/shiki-twoslash/src/node/createTwoslashTransformer.ts rename tools/shiki-twoslash/src/node/{transformerTwoslash.ts => transformerTwoslashFactory.ts} (95%) diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts index 55375fa091..ba9915d3ea 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/createShikiHighlighter.ts @@ -1,10 +1,6 @@ import { createRequire } from 'node:module' -import type { - BundledLanguage, - BundledTheme, - HighlighterGeneric, - ShikiTransformer, -} from 'shiki' +import type { TwoslashTransformer } from '@vuepress/shiki-twoslash' +import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki' import { createHighlighter, isSpecialLang } from 'shiki' import { createSyncFn } from 'synckit' import { isPlainObject } from 'vuepress/shared' @@ -29,7 +25,7 @@ export const createShikiHighlighter = async ({ }: ShikiPluginOptions = {}): Promise<{ highlighter: HighlighterGeneric loadLang: ShikiLoadLang - transformerTwoslash: ShikiTransformer | null + twoslashTransformer: TwoslashTransformer }> => { const highlighter = await createHighlighter({ langs: [...langs, ...Object.values(langAlias)], @@ -67,19 +63,19 @@ export const createShikiHighlighter = async ({ return rawGetLanguage.call(highlighter, name) } - let transformerTwoslash: ShikiTransformer | null = null + let twoslashTransformer: TwoslashTransformer = () => [] if (options.twoslash) { - const { transformerTwoslash: transformer } = await import( + const { createTwoslashTransformer } = await import( '@vuepress/shiki-twoslash' ) - transformerTwoslash = await transformer({ + twoslashTransformer = await createTwoslashTransformer({ twoslashOptions: isPlainObject(options.twoslash) ? options.twoslash : {}, }) - - await shikiSetup?.(highlighter) } - return { highlighter, loadLang, transformerTwoslash } + await shikiSetup?.(highlighter) + + return { highlighter, loadLang, twoslashTransformer } } diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts index 14fa3073ce..e3e8d14ca1 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts @@ -1,13 +1,8 @@ import { transformerCompactLineOptions } from '@shikijs/transformers' -import type { - BundledLanguage, - BundledTheme, - HighlighterGeneric, - ShikiTransformer, -} from 'shiki' +import type { TwoslashTransformer } from '@vuepress/shiki-twoslash' +import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki' import { getTransformers, - twoslashTransformer, whitespaceTransformer, } from '../../transformers/getTransformers.js' import type { ShikiHighlightOptions } from '../../types.js' @@ -28,7 +23,7 @@ export const getHighLightFunction = ( options: ShikiHighlightOptions, loadLang: ShikiLoadLang, markdownFilePathGetter: MarkdownFilePathGetter, - transformerTwoslash: ShikiTransformer | null, + twoslashTransformer: TwoslashTransformer, ): MarkdownItHighlight => { const transformers = getTransformers(options) @@ -49,7 +44,7 @@ export const getHighLightFunction = ( ? [transformerCompactLineOptions(attrsToLines(attrs))] : []), ...whitespaceTransformer(attrs, options.whitespace), - ...twoslashTransformer(attrs, transformerTwoslash), + ...twoslashTransformer(attrs), ...(options.transformers ?? []), ], ...('themes' in options diff --git a/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts b/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts index b989515e01..2bf6c40af8 100644 --- a/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts +++ b/plugins/markdown/plugin-shiki/src/node/shikiPlugin.ts @@ -54,7 +54,7 @@ export const shikiPlugin = (_options: ShikiPluginOptions = {}): Plugin => { const { preWrapper, lineNumbers, collapsedLines } = options const markdownFilePathGetter = createMarkdownFilePathGetter(md) - const { highlighter, loadLang, transformerTwoslash } = + const { highlighter, loadLang, twoslashTransformer } = await createShikiHighlighter(options) md.options.highlight = getHighLightFunction( @@ -62,7 +62,7 @@ export const shikiPlugin = (_options: ShikiPluginOptions = {}): Plugin => { options, loadLang, markdownFilePathGetter, - transformerTwoslash, + twoslashTransformer, ) md.use(highlightLinesPlugin) diff --git a/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts b/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts index dee4cf5892..7013936fc8 100644 --- a/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts +++ b/plugins/markdown/plugin-shiki/src/node/transformers/getTransformers.ts @@ -16,7 +16,6 @@ import { cleanupTransformer, emptyLineTransformer, removeEscapeTransformer, - vPreTransformer, } from './vuepressTransformers.js' export const getTransformers = ( @@ -89,15 +88,3 @@ export const whitespaceTransformer = ( return [transformerRenderWhitespace({ position })] } - -const TWOSLASH_REGEXP = /\btwoslash\b/ -export const twoslashTransformer = ( - meta: string, - transformer: ShikiTransformer | null, -): ShikiTransformer[] => { - if (transformer) { - if (TWOSLASH_REGEXP.test(meta)) return [transformer] - return [vPreTransformer] - } - return [] -} diff --git a/plugins/markdown/plugin-shiki/src/node/transformers/vuepressTransformers.ts b/plugins/markdown/plugin-shiki/src/node/transformers/vuepressTransformers.ts index b2b873fc20..3ceb435237 100644 --- a/plugins/markdown/plugin-shiki/src/node/transformers/vuepressTransformers.ts +++ b/plugins/markdown/plugin-shiki/src/node/transformers/vuepressTransformers.ts @@ -51,10 +51,3 @@ export const emptyLineTransformer: ShikiTransformer = { }) }, } - -export const vPreTransformer: ShikiTransformer = { - name: 'vuepress:v-pre', - pre(node) { - node.properties['v-pre'] = '' - }, -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1aa8a55fce..5a39b95f35 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1298,9 +1298,6 @@ importers: mdast-util-to-hast: specifier: ^13.2.0 version: 13.2.0 - shiki: - specifier: ^2.3.2 - version: 2.3.2 twoslash: specifier: ^0.2.12 version: 0.2.12(typescript@5.7.3) @@ -1314,6 +1311,9 @@ importers: '@types/hast': specifier: ^3.0.4 version: 3.0.4 + shiki: + specifier: ^2.3.2 + version: 2.3.2 vue: specifier: ^3.5.13 version: 3.5.13(typescript@5.7.3) diff --git a/tools/shiki-twoslash/package.json b/tools/shiki-twoslash/package.json index 3cf936578f..3ba0351200 100644 --- a/tools/shiki-twoslash/package.json +++ b/tools/shiki-twoslash/package.json @@ -51,16 +51,11 @@ }, "devDependencies": { "@types/hast": "^3.0.4", + "shiki": "^2.3.2", "vue": "^3.5.13" }, "peerDependencies": { - "vuepress": "2.0.0-rc.19", - "shiki": "^2.3.2" - }, - "peerDependenciesMeta": { - "shiki": { - "optional": true - } + "vuepress": "2.0.0-rc.19" }, "publishConfig": { "access": "public" diff --git a/tools/shiki-twoslash/src/node/createTwoslashTransformer.ts b/tools/shiki-twoslash/src/node/createTwoslashTransformer.ts new file mode 100644 index 0000000000..bda8c0aa52 --- /dev/null +++ b/tools/shiki-twoslash/src/node/createTwoslashTransformer.ts @@ -0,0 +1,25 @@ +import type { ShikiTransformer } from 'shiki' +import { transformerTwoslashFactory } from './transformerTwoslashFactory.js' +import type { VuePressTwoslashOptions } from './types.js' + +const TWOSLASH_REGEXP = /\btwoslash\b/ + +const vPreTransformer: ShikiTransformer = { + name: 'vuepress:v-pre', + pre(node) { + node.properties['v-pre'] = '' + }, +} + +export type TwoslashTransformer = (meta: string) => ShikiTransformer[] + +export const createTwoslashTransformer = async ( + options: VuePressTwoslashOptions = {}, +): Promise => { + const transformer = await transformerTwoslashFactory(options) + + return (meta = ''): ShikiTransformer[] => { + if (TWOSLASH_REGEXP.test(meta)) return [transformer] + return [vPreTransformer] + } +} diff --git a/tools/shiki-twoslash/src/node/index.ts b/tools/shiki-twoslash/src/node/index.ts index 19b4ca9cf8..efbb1f2a68 100644 --- a/tools/shiki-twoslash/src/node/index.ts +++ b/tools/shiki-twoslash/src/node/index.ts @@ -1,4 +1,5 @@ export * from './rendererFloatingVue.js' -export * from './transformerTwoslash.js' +export * from './transformerTwoslashFactory.js' +export * from './createTwoslashTransformer.js' export * from './resolveTsPaths.js' export type * from './types.js' diff --git a/tools/shiki-twoslash/src/node/rendererFloatingVue.ts b/tools/shiki-twoslash/src/node/rendererFloatingVue.ts index d5e627682f..d7ddc04af4 100644 --- a/tools/shiki-twoslash/src/node/rendererFloatingVue.ts +++ b/tools/shiki-twoslash/src/node/rendererFloatingVue.ts @@ -1,5 +1,5 @@ import type { TwoslashRenderer } from '@shikijs/twoslash' -import { defaultHoverInfoProcessor, rendererRich } from '@shikijs/twoslash' +import { rendererRich } from '@shikijs/twoslash' import type { Element, ElementContent, Text } from 'hast' import { fromMarkdown } from 'mdast-util-from-markdown' import { gfmFromMarkdown } from 'mdast-util-gfm' @@ -7,8 +7,6 @@ import { defaultHandlers, toHast } from 'mdast-util-to-hast' import type { ShikiTransformerContextCommon } from 'shiki' import type { TwoslashFloatingVueRendererOptions } from './types.js' -export { defaultHoverInfoProcessor } - const vPre = (el: T): T => { if (el.type === 'element') { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition diff --git a/tools/shiki-twoslash/src/node/transformerTwoslash.ts b/tools/shiki-twoslash/src/node/transformerTwoslashFactory.ts similarity index 95% rename from tools/shiki-twoslash/src/node/transformerTwoslash.ts rename to tools/shiki-twoslash/src/node/transformerTwoslashFactory.ts index b7b4ef90d1..f23b05c5e6 100644 --- a/tools/shiki-twoslash/src/node/transformerTwoslash.ts +++ b/tools/shiki-twoslash/src/node/transformerTwoslashFactory.ts @@ -13,9 +13,9 @@ import { resolveTsPaths } from './resolveTsPaths.js' import type { VuePressTwoslashOptions } from './types.js' /** - * Create a Shiki transformer for VitePress to enable twoslash integration + * Create a Shiki transformer for VuePress to enable twoslash integration */ -export const transformerTwoslash = async ( +export const transformerTwoslashFactory = async ( options: VuePressTwoslashOptions = {}, ): Promise => { const { explicitTrigger = true } = options From 061d090cee11e4667a6f65dfbc43808217e8532f Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 17:33:37 +0800 Subject: [PATCH 10/16] chore: tweak --- .../src/node/markdown/highlighter/getHighLightFunction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts index e3e8d14ca1..f8f090daf3 100644 --- a/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts +++ b/plugins/markdown/plugin-shiki/src/node/markdown/highlighter/getHighLightFunction.ts @@ -23,7 +23,7 @@ export const getHighLightFunction = ( options: ShikiHighlightOptions, loadLang: ShikiLoadLang, markdownFilePathGetter: MarkdownFilePathGetter, - twoslashTransformer: TwoslashTransformer, + twoslashTransformer: TwoslashTransformer = () => [], ): MarkdownItHighlight => { const transformers = getTransformers(options) From 389ef37bd21133c727cb5eaec8db98c76059d578 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 17:35:22 +0800 Subject: [PATCH 11/16] chore: tweak --- tools/shiki-twoslash/src/client/twoslash.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/shiki-twoslash/src/client/twoslash.ts b/tools/shiki-twoslash/src/client/twoslash.ts index 2113655e75..6a73ccc633 100644 --- a/tools/shiki-twoslash/src/client/twoslash.ts +++ b/tools/shiki-twoslash/src/client/twoslash.ts @@ -1,4 +1,5 @@ -import Gt, { recomputeAllPoppers } from 'floating-vue' +// eslint-disable-next-line import/no-rename-default +import FloatingVue, { recomputeAllPoppers } from 'floating-vue' import type { App } from 'vue' import 'floating-vue/dist/style.css' @@ -27,7 +28,7 @@ export const enhanceTwoslash = (app: App): void => { ) } - app.use(Gt, { + app.use(FloatingVue, { themes: { 'twoslash': { $extend: 'dropdown', From f6c355ed9d4df187345765656a8ce28b69f115cd Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 17:47:30 +0800 Subject: [PATCH 12/16] chore: tweak --- .../plugin-shiki/src/node/prepareClientConfigFile.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts b/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts index 432f3f781a..a863a6432f 100644 --- a/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts +++ b/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts @@ -95,12 +95,16 @@ export const prepareClientConfigFile = ( let code = imports.join('\n') if (setups.length || enhances.length) { - code += '\nexport default {\n' + code += '\nexport default {' if (setups.length) { - code += ` setup() {\n ${setups.join('\n ')}\n },` + code += `\n setup() { + ${setups.join('\n ')} + },` } if (enhances.length) { - code += `\n enhance({ app }) {\n ${enhances.join('\n ')}\n },` + code += `\n enhance({ app }) { + ${enhances.join('\n ')} + },` } code += '\n}\n' } From aae212c40fefeacca0a34bc113fe21ce01f98882 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 18:26:10 +0800 Subject: [PATCH 13/16] chore: tweak --- .../src/client/styles/twoslash.scss | 64 +++++++++---------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/tools/shiki-twoslash/src/client/styles/twoslash.scss b/tools/shiki-twoslash/src/client/styles/twoslash.scss index 8f28fdff04..e4f0f74a7d 100644 --- a/tools/shiki-twoslash/src/client/styles/twoslash.scss +++ b/tools/shiki-twoslash/src/client/styles/twoslash.scss @@ -1,6 +1,3 @@ -/* stylelint-disable selector-class-pattern */ -/* stylelint-disable no-descending-specificity */ - /* ===== Basic ===== */ :root { --twoslash-underline-color: var(--vp-c-border-hard); @@ -215,27 +212,29 @@ } /* ========== floating vue ================== */ -.v-popper--theme-twoslash { +$popper: v-popper; + +.#{$popper}--theme-twoslash { z-index: var(--twoslash-z-index); -} -.v-popper--theme-twoslash .v-popper__inner { - border-color: var(--twoslash-border-color); - background: var(--twoslash-popup-bg); - color: var(--twoslash-popup-color); - transition: - background-color var(--vp-t-color), - border-color var(--vp-t-color); -} + .#{$popper}__inner { + border-color: var(--twoslash-border-color); + background: var(--twoslash-popup-bg); + color: var(--twoslash-popup-color); + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); + } -.v-popper--theme-twoslash .v-popper__arrow-outer { - border-color: var(--twoslash-border-color); - transition: border-color var(--vp-t-color); -} + .#{$popper}__arrow-outer { + border-color: var(--twoslash-border-color); + transition: border-color var(--vp-t-color); + } -.v-popper--theme-twoslash .v-popper__arrow-inner { - border-color: var(--twoslash-popup-bg); - transition: border-color var(--vp-t-color); + .#{$popper}__arrow-inner { + border-color: var(--twoslash-popup-bg); + transition: border-color var(--vp-t-color); + } } .twoslash-popup-container { @@ -319,22 +318,19 @@ text-wrap: balance; } -/* stylelint-disable-next-line selector-max-compound-selectors */ -.twoslash-floating - .twoslash-popup-docs - pre - .twoslash-floating - .twoslash-popup-error - pre { - overflow-x: auto; +.twoslash-floating .twoslash-popup-docs pre { + /* stylelint-disable-next-line selector-max-compound-selectors */ + .twoslash-floating .twoslash-popup-error pre { + overflow-x: auto; - margin: 6px -2px; - padding: 12px; - border-radius: 8px; + margin: 6px -2px; + padding: 12px; + border-radius: 8px; - background-color: var(--code-c-bg); + background-color: var(--code-c-bg); - transition: background-color var(--vp-t-color); + transition: background-color var(--vp-t-color); + } } .twoslash-floating .twoslash-popup-docs-tags { @@ -363,7 +359,7 @@ transition: background-color var(--vp-t-color); } -.twoslash-floating.twoslash-completion .v-popper__arrow-container { +.twoslash-floating.twoslash-completion .#{$popper}__arrow-container { display: none; } From ec7ca64d96d08346b07c1314f54ef13c63ec86e4 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sat, 8 Feb 2025 18:56:50 +0800 Subject: [PATCH 14/16] chore: tweak --- pnpm-lock.yaml | 3 +++ tools/shiki-twoslash/package.json | 1 + tools/shiki-twoslash/src/client/twoslash.ts | 9 ++++----- tools/shiki-twoslash/tsconfig.build.json | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a39b95f35..d736e47ddc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1286,6 +1286,9 @@ importers: '@shikijs/twoslash': specifier: ^2.3.2 version: 2.3.2(typescript@5.7.3) + '@vuepress/helper': + specifier: workspace:* + version: link:../helper floating-vue: specifier: ^5.2.2 version: 5.2.2(vue@3.5.13(typescript@5.7.3)) diff --git a/tools/shiki-twoslash/package.json b/tools/shiki-twoslash/package.json index 3ba0351200..ee5a2430df 100644 --- a/tools/shiki-twoslash/package.json +++ b/tools/shiki-twoslash/package.json @@ -42,6 +42,7 @@ }, "dependencies": { "@shikijs/twoslash": "^2.3.2", + "@vuepress/helper": "workspace:*", "floating-vue": "^5.2.2", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.0.0", diff --git a/tools/shiki-twoslash/src/client/twoslash.ts b/tools/shiki-twoslash/src/client/twoslash.ts index 6a73ccc633..730aa77cab 100644 --- a/tools/shiki-twoslash/src/client/twoslash.ts +++ b/tools/shiki-twoslash/src/client/twoslash.ts @@ -1,13 +1,12 @@ +import { checkIsMobile } from '@vuepress/helper/client' // eslint-disable-next-line import/no-rename-default import FloatingVue, { recomputeAllPoppers } from 'floating-vue' import type { App } from 'vue' import 'floating-vue/dist/style.css' -const isMobile = - typeof navigator !== 'undefined' && - /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( - navigator.userAgent, - ) +const isMobile = checkIsMobile( + typeof navigator !== 'undefined' ? navigator.userAgent : '', +) export const enhanceTwoslash = (app: App): void => { if (typeof window !== 'undefined') { diff --git a/tools/shiki-twoslash/tsconfig.build.json b/tools/shiki-twoslash/tsconfig.build.json index 78ad610eea..e7e3745605 100644 --- a/tools/shiki-twoslash/tsconfig.build.json +++ b/tools/shiki-twoslash/tsconfig.build.json @@ -5,5 +5,6 @@ "outDir": "./lib", "types": ["vuepress/client-types"] }, - "include": ["./src"] + "include": ["./src"], + "references": [{ "path": "../helper/tsconfig.build.json" }] } From c393c92822f2628acd75fff7776cc1fdda7e5288 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Mon, 10 Feb 2025 18:08:45 +0800 Subject: [PATCH 15/16] chore: tweaks --- .vscode/settings.json | 1 + .../src/node/prepareClientConfigFile.ts | 31 +- .../src/client/styles/twoslash.scss | 297 +++++++++--------- .../src/node/rendererFloatingVue.ts | 1 + tools/shiki-twoslash/src/node/types.ts | 2 + 5 files changed, 173 insertions(+), 159 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index bb3b61a8bc..45ab58a9cf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,6 +50,7 @@ "lightmode", "linkify", "mathjax", + "mdast", "mdit", "meteorlxy", "mhchem", diff --git a/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts b/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts index a863a6432f..cf23fa8e6b 100644 --- a/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts +++ b/plugins/markdown/plugin-shiki/src/node/prepareClientConfigFile.ts @@ -23,8 +23,8 @@ export const prepareClientConfigFile = ( `import "${getModulePath(`${PLUGIN_NAME}/styles/shiki.css`, import.meta)}"`, ] - const setups: string[] = [] const enhances: string[] = [] + const setups: string[] = [] if (lineNumbers !== 'disable') { imports.push( @@ -95,18 +95,29 @@ export const prepareClientConfigFile = ( let code = imports.join('\n') if (setups.length || enhances.length) { - code += '\nexport default {' - if (setups.length) { - code += `\n setup() { - ${setups.join('\n ')} - },` - } + code += ` +export default { +` + if (enhances.length) { - code += `\n enhance({ app }) { + code += `\ + enhance({ app }) { ${enhances.join('\n ')} - },` + }, +` } - code += '\n}\n' + + if (setups.length) { + code += `\ + setup() { + ${setups.join('\n ')} + }, +` + } + + code += `\ +} +` } return app.writeTemp('shiki/config.js', code) diff --git a/tools/shiki-twoslash/src/client/styles/twoslash.scss b/tools/shiki-twoslash/src/client/styles/twoslash.scss index e4f0f74a7d..917cc110ad 100644 --- a/tools/shiki-twoslash/src/client/styles/twoslash.scss +++ b/tools/shiki-twoslash/src/client/styles/twoslash.scss @@ -28,8 +28,8 @@ } /* Respect people's wishes to not have animations */ -@media (prefers-reduced-motion: reduce) { - .twoslash * { +.twoslash * { + @media (prefers-reduced-motion: reduce) { transition: none !important; } } @@ -64,36 +64,36 @@ .twoslash .twoslash-error { padding-bottom: 2px; - background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23c94824'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 3' enable-background='new 0 0 6 3' height='3' width='6'%3E%3Cg fill='%23c94824'%3E%3Cpolygon points='5.5,0 2.5,3 1.1,3 4.1,0'/%3E%3Cpolygon points='4,0 6,2 6,0.6 5.4,0'/%3E%3Cpolygon points='0,2 1,3 2.4,3 0,0.6'/%3E%3C/g%3E%3C/svg%3E") repeat-x bottom left; } -/* ===== Completeions ===== */ +/* ===== Completions ===== */ .twoslash .twoslash-completion-cursor { position: relative; -} -.twoslash .twoslash-completion-cursor .twoslash-completion-list { - position: absolute; - top: 0; - left: 0; - z-index: 8; + .twoslash-completion-list { + position: absolute; + top: 0; + left: 0; + z-index: 8; - display: inline-block; + display: inline-block; - margin: 3px 0 0 -1px; - border: 1px solid var(--twoslash-border-color); + margin: 3px 0 0 -1px; + border: 1px solid var(--twoslash-border-color); - background: var(--twoslash-popup-bg); - box-shadow: var(--twoslash-popup-shadow); + background: var(--twoslash-popup-bg); + box-shadow: var(--twoslash-popup-shadow); - user-select: none; + user-select: none; - transition: - background-color var(--vp-t-color), - border-color var(--vp-t-color); + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); - transform: translate(0, 1.2em); + transform: translate(0, 1.2em); + } } .twoslash-completion-list { @@ -105,46 +105,46 @@ padding: 4px; font-size: 0.8rem; -} -.twoslash-completion-list:hover { - user-select: auto; -} + &:hover { + user-select: auto; + } -.twoslash-completion-list::before { - content: ' '; + &::before { + content: ' '; - position: absolute; - top: -1.6em; - left: -1px; + position: absolute; + top: -1.6em; + left: -1px; - width: 2px; - height: 1.4em; + width: 2px; + height: 1.4em; - background-color: var(--twoslash-cursor-color); -} + background-color: var(--twoslash-cursor-color); + } -.twoslash-completion-list li { - display: flex; - gap: 0.25em; - align-items: center; + li { + display: flex; + gap: 0.25em; + align-items: center; - overflow: hidden; + overflow: hidden; - line-height: 1em; -} + line-height: 1em; -.twoslash-completion-list li span.twoslash-completions-unmatched { - color: var(--twoslash-unmatched-color); -} + span.twoslash-completions-unmatched { + color: var(--twoslash-unmatched-color); + } -.twoslash-completion-list .deprecated { - text-decoration: line-through; - opacity: 0.5; -} + span.twoslash-completions-matched { + color: var(--twoslash-matched-color); + } + } -.twoslash-completion-list li span.twoslash-completions-matched { - color: var(--twoslash-matched-color); + .deprecated { + text-decoration: line-through; + opacity: 0.5; + } } /* Highlights */ @@ -186,34 +186,38 @@ transition: background-color var(--vp-t-color), border-color var(--vp-t-color); -} -.twoslash .twoslash-tag-line .twoslash-tag-icon { - width: 1.1em; - color: inherit; -} + .twoslash-tag-icon { + width: 1.1em; + color: inherit; + } -.twoslash .twoslash-tag-line.twoslash-tag-error-line { - border-left: 3px solid var(--twoslash-error-color); - background-color: var(--twoslash-error-bg); - color: var(--twoslash-error-color); -} + &.twoslash-tag-error-line { + border-left: 3px solid var(--twoslash-error-color); + background-color: var(--twoslash-error-bg); + color: var(--twoslash-error-color); + } -.twoslash .twoslash-tag-line.twoslash-tag-warn-line { - border-left: 3px solid var(--twoslash-tag-warn-color); - background-color: var(--twoslash-tag-warn-bg); - color: var(--twoslash-tag-warn-color); -} + &.twoslash-tag-warn-line { + border-left: 3px solid var(--twoslash-tag-warn-color); + background-color: var(--twoslash-tag-warn-bg); + color: var(--twoslash-tag-warn-color); + } -.twoslash .twoslash-tag-line.twoslash-tag-annotate-line { - border-left: 3px solid var(--twoslash-tag-annotate-color); - background-color: var(--twoslash-tag-annotate-bg); - color: var(--twoslash-tag-annotate-color); + &.twoslash-tag-annotate-line { + border-left: 3px solid var(--twoslash-tag-annotate-color); + background-color: var(--twoslash-tag-annotate-bg); + color: var(--twoslash-tag-annotate-color); + } } /* ========== floating vue ================== */ $popper: v-popper; +.twoslash .#{$popper} { + display: inline-block; +} + .#{$popper}--theme-twoslash { z-index: var(--twoslash-z-index); @@ -239,88 +243,81 @@ $popper: v-popper; .twoslash-popup-container { transform: translateY(1.5em); -} -.twoslash-query-presisted .twoslash-popup-container { - transform: translateY(1.8em); -} - -.twoslash .v-popper { - display: inline-block; + .twoslash-query-presisted & { + transform: translateY(1.8em); + } } .twoslash-completion-list .twoslash-completions-icon { color: var(--twoslash-unmatched-color) !important; } -.twoslash-floating .twoslash-popup-code { - display: block; +.twoslash-floating { + .twoslash-popup-code { + display: block; - box-sizing: border-box; - width: fit-content; - min-width: 100%; - max-width: 600px; - padding: 6px 12px; - border-radius: 0; + box-sizing: border-box; + width: fit-content; + min-width: 100%; + max-width: 600px; + padding: 6px 12px; + border-radius: 0; - font-size: var(--twoslash-code-size); - line-height: var(--twoslash-code-line-height); - white-space: pre-wrap; -} + font-size: var(--twoslash-code-size); + line-height: var(--twoslash-code-line-height); + white-space: pre-wrap; + } -.twoslash-floating .twoslash-popup-code, -.twoslash-floating .twoslash-popup-docs { - background-color: var(--twoslash-popup-bg); - transition: - background-color var(--vp-t-color), - border-color var(--vp-t-color); -} + .twoslash-popup-code, + .twoslash-popup-docs { + background-color: var(--twoslash-popup-bg); + transition: + background-color var(--vp-t-color), + border-color var(--vp-t-color); + } -.twoslash-floating .twoslash-popup-docs, -.twoslash-floating .twoslash-popup-error { - overflow: hidden auto; + .twoslash-popup-docs, + .twoslash-popup-error { + overflow: hidden auto; - max-width: 700px; - max-height: 500px; - padding: 12px !important; + max-width: 700px; + max-height: 500px; + padding: 12px !important; - font-size: 0.9em; - font-family: var(--twoslash-docs-font); - text-wrap: balance; -} + font-size: 0.9em; + font-family: var(--twoslash-docs-font); + text-wrap: balance; -.twoslash-floating .twoslash-popup-docs p:first-child, -.twoslash-floating .twoslash-popup-error p:first-child { - margin-top: 0; -} + p { + margin: 6px 0; + text-wrap: balance; -.twoslash-floating .twoslash-popup-docs p:last-child, -.twoslash-floating .twoslash-popup-error p:last-child { - margin-bottom: 0; -} + &:first-child { + margin-top: 0; + } -.twoslash-floating .twoslash-popup-docs { - border-top: 1px solid var(--twoslash-border-color); - color: var(--twoslash-docs-color); -} + &:last-child { + margin-bottom: 0; + } + } + } -.twoslash-floating .twoslash-popup-error { - color: var(--twoslash-error-color); -} + .twoslash-popup-docs { + border-top: 1px solid var(--twoslash-border-color); + color: var(--twoslash-docs-color); + } -.twoslash-floating .twoslash-popup-error.twoslash-error-level-warning { - color: var(--twoslash-warn-color); -} + .twoslash-popup-error { + color: var(--twoslash-error-color); -.twoslash-floating .twoslash-popup-docs p, -.twoslash-floating .twoslash-popup-error p { - margin: 6px 0; - text-wrap: balance; -} + &.twoslash-error-level-warning { + color: var(--twoslash-warn-color); + } + } -.twoslash-floating .twoslash-popup-docs pre { /* stylelint-disable-next-line selector-max-compound-selectors */ - .twoslash-floating .twoslash-popup-error pre { + .twoslash-popup-docs pre .twoslash-floating .twoslash-popup-error pre { overflow-x: auto; margin: 6px -2px; @@ -331,18 +328,18 @@ $popper: v-popper; transition: background-color var(--vp-t-color); } -} -.twoslash-floating .twoslash-popup-docs-tags { - display: flex; - flex-direction: column; - padding: 8px 12px !important; -} + .twoslash-popup-docs-tags { + display: flex; + flex-direction: column; + padding: 8px 12px !important; -.twoslash-floating .twoslash-popup-docs-tags .twoslash-popup-docs-tag-name { - margin-right: 0.5em; - color: var(--twoslash-unmatched-color); - font-family: var(--twoslash-code-font); + .twoslash-popup-docs-tag-name { + margin-right: 0.5em; + color: var(--twoslash-unmatched-color); + font-family: var(--twoslash-code-font); + } + } } .twoslash-completion-cursor { @@ -359,16 +356,18 @@ $popper: v-popper; transition: background-color var(--vp-t-color); } -.twoslash-floating.twoslash-completion .#{$popper}__arrow-container { - display: none; -} +.twoslash-floating.twoslash-completion { + .#{$popper}__arrow-container { + display: none; + } -.twoslash-floating.twoslash-completion .twoslash-completion-list { - padding: 6px; - font-size: var(--twoslash-code-size) !important; - font-family: var(--twoslash-code-font); -} + .twoslash-completion-list { + padding: 6px; + font-size: var(--twoslash-code-size) !important; + font-family: var(--twoslash-code-font); -.twoslash-floating.twoslash-completion .twoslash-completion-list li { - padding: 3px 0; + li { + padding: 3px 0; + } + } } diff --git a/tools/shiki-twoslash/src/node/rendererFloatingVue.ts b/tools/shiki-twoslash/src/node/rendererFloatingVue.ts index d7ddc04af4..6774201ecd 100644 --- a/tools/shiki-twoslash/src/node/rendererFloatingVue.ts +++ b/tools/shiki-twoslash/src/node/rendererFloatingVue.ts @@ -63,6 +63,7 @@ function renderMarkdown( lang, structure: _node.value.trim().includes('\n') ? 'classic' : 'inline', }) + if (lang) { return { type: 'element', diff --git a/tools/shiki-twoslash/src/node/types.ts b/tools/shiki-twoslash/src/node/types.ts index 82fac667df..a09ae261ef 100644 --- a/tools/shiki-twoslash/src/node/types.ts +++ b/tools/shiki-twoslash/src/node/types.ts @@ -31,8 +31,10 @@ export interface VuePressTwoslashOptions * Twoslash options */ twoslashOptions?: TwoslashOptions + /** * Requires adding `twoslash` to the code block explicitly to run twoslash + * * @default true */ explicitTrigger?: TransformerTwoslashOptions['explicitTrigger'] From 846eac7b45e96e56a9e09cbd43f298d23292c583 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Mon, 10 Feb 2025 18:16:08 +0800 Subject: [PATCH 16/16] chore: tweaks --- docs/.vuepress/config.ts | 2 -- docs/.vuepress/theme.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index d1cfc5787c..e2b41904d3 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -26,8 +26,6 @@ import theme from './theme.js' const __dirname = import.meta.dirname || getDirname(import.meta.url) -// const IS_PROD = process.env.NODE_ENV === 'production' - export default defineUserConfig({ // set site base to default value base: (process.env.BASE as '/' | `/${string}/` | undefined) || '/', diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts index 95a4b0754f..9038acfb75 100644 --- a/docs/.vuepress/theme.ts +++ b/docs/.vuepress/theme.ts @@ -66,7 +66,7 @@ export default defaultTheme({ hint: { alert: true, }, - // use shiki plugin in production mode instead + // use shiki plugin instead prismjs: false, }, })