refactor: migrate shikijs to shiki

This commit is contained in:
YunYouJun 2024-02-10 11:24:44 +08:00
parent cd475a9c63
commit 485ae82cef
12 changed files with 1192 additions and 798 deletions

View File

@ -37,6 +37,7 @@
"instantsearch",
"jiti",
"lightgallery",
"shiki",
"twikoo",
"unconfig",
"unhead",

View File

@ -22,7 +22,7 @@
"devDependencies": {
"@iconify-json/simple-icons": "^1.1.90",
"nodemon": "^3.0.3",
"vite": "^5.0.12",
"vitepress": "1.0.0-rc.41"
"vite": "^5.1.1",
"vitepress": "1.0.0-rc.42"
}
}

View File

@ -157,14 +157,14 @@ All icon names added to `config.unocss.safelist` will be ready for hot reloading
:::
::: zh-CN
基于 [Shikiji](https://shikiji.netlify.app/) 实现。
基于 [Shiki](https://shiki.style) 实现。
Valaxy 支持 `vue` 等语法高亮,拷贝代码,高亮其中某一行。
譬如:
:::
::: en
Based on [Shikiji](https://shikiji.netlify.app/).
Based on [Shiki](https://shiki.style).
Valaxy supports syntax highlighting for languages like `vue`, and also supports copying code and
highlighting a particular line in the code block.

View File

@ -56,18 +56,18 @@
"prepare": "husky install"
},
"dependencies": {
"c12": "^1.6.1"
"c12": "^1.7.0"
},
"devDependencies": {
"@antfu/eslint-config": "2.6.4",
"@iconify-json/logos": "^1.1.42",
"@iconify-json/vscode-icons": "^1.1.33",
"@microsoft/api-extractor": "^7.39.4",
"@microsoft/api-extractor": "^7.40.1",
"@types/debug": "^4.1.12",
"@types/markdown-it-attrs": "^4.1.3",
"@types/markdown-it-container": "^2.0.9",
"@types/markdown-it-emoji": "^2.0.4",
"@types/node": "^20.11.16",
"@types/node": "^20.11.17",
"@types/prompts": "^2.4.9",
"@types/resolve": "^1.20.6",
"@valaxyjs/devtools": "workspace:*",
@ -78,7 +78,7 @@
"eslint-plugin-cypress": "^2.15.1",
"https-localhost": "^4.7.1",
"husky": "^9.0.10",
"lint-staged": "^15.2.1",
"lint-staged": "^15.2.2",
"npm-run-all": "^4.1.5",
"prompts": "^2.4.2",
"rimraf": "^5.0.5",
@ -86,7 +86,7 @@
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard-scss": "^13.0.0",
"tsup": "^8.0.1",
"tsx": "^4.7.0",
"tsx": "^4.7.1",
"typescript": "^5.3.3",
"unbuild": "^2.0.0",
"valaxy": "workspace:*",

View File

@ -34,6 +34,6 @@
"devDependencies": {
"typescript": "^5.3.3",
"unbuild": "^2.0.0",
"vite": "^5.0.12"
"vite": "^5.1.1"
}
}

View File

@ -58,6 +58,7 @@ export async function setupMarkdownPlugins(
) {
const mdOptions = options?.config.markdown || {}
const theme = mdOptions.theme ?? defaultCodeTheme
const hasSingleTheme = typeof theme === 'string' || 'name' in theme
const siteConfig = options?.config.siteConfig || {}
if (mdOptions.preConfig)
@ -70,8 +71,10 @@ export async function setupMarkdownPlugins(
.use(preWrapperPlugin, { theme, siteConfig })
.use(snippetPlugin, options?.userRoot)
.use(containerPlugin, {
hasSingleTheme,
}, {
...mdOptions.blocks,
theme,
...mdOptions?.container,
})
.use(cssI18nContainer, {
languages: options?.config.siteConfig.languages,
@ -123,7 +126,7 @@ export async function setupMarkdownPlugins(
} as FrontmatterPluginOptions)
.use(headersPlugin, {
slugify,
...mdOptions.headers,
...(typeof mdOptions.headers === 'boolean' ? undefined : mdOptions.headers),
} as HeadersPluginOptions)
.use(sfcPlugin, {
...mdOptions.sfc,

View File

@ -8,15 +8,14 @@ import {
transformerNotationErrorLevel,
transformerNotationFocus,
transformerNotationHighlight,
} from 'shikiji-transformers'
import type { ShikijiTransformer } from 'shikiji'
} from '@shikijs/transformers'
import type { ShikiTransformer } from 'shiki'
import {
addClassToHast,
bundledLanguages,
getHighlighter,
isPlaintext as isPlainLang,
isSpecialLang,
} from 'shikiji'
} from 'shiki'
import type { Logger } from 'vite'
import type { MarkdownOptions, ThemeOptions } from '../types'
@ -67,15 +66,15 @@ export async function highlight(
const highlighter = await getHighlighter({
themes:
(typeof theme === 'string' || 'name' in theme)
? [theme]
: [theme.light, theme.dark],
typeof theme === 'object' && 'light' in theme && 'dark' in theme
? [theme.light, theme.dark]
: [theme],
langs: [...Object.keys(bundledLanguages), ...(options.languages || [])],
langAlias: options.languageAlias,
})
await options?.shikijiSetup?.(highlighter)
const transformers: ShikijiTransformer[] = [
await options?.shikiSetup?.(highlighter)
const transformers: ShikiTransformer[] = [
transformerNotationDiff(),
transformerNotationFocus({
classActiveLine: 'has-focus',
@ -113,7 +112,7 @@ export async function highlight(
if (lang) {
const langLoaded = highlighter.getLoadedLanguages().includes(lang as any)
if (!langLoaded && !isPlainLang(lang) && !isSpecialLang(lang)) {
if (!langLoaded && !isSpecialLang(lang)) {
logger.warn(
c.yellow(
`\nThe language '${lang}' is not loaded, falling back to '${
@ -148,13 +147,6 @@ export async function highlight(
return s
}
const fillEmptyHighlightedLine = (s: string) => {
return s.replace(
/(<span class="line highlighted">)(<\/span>)/g,
'$1<wbr>$2',
)
}
str = removeMustache(str).trimEnd()
const highlighted = highlighter.codeToHtml(str, {
@ -169,19 +161,37 @@ export async function highlight(
node.properties['v-pre'] = ''
},
},
{
name: 'valaxy:empty-line',
code(hast) {
hast.children.forEach((span) => {
if (
span.type === 'element'
&& span.tagName === 'span'
&& Array.isArray(span.properties.class)
&& span.properties.class.includes('line')
&& span.children.length === 0
) {
span.children.push({
type: 'element',
tagName: 'wbr',
properties: {},
children: [],
})
}
})
},
},
...userTransformers,
],
meta: {
__raw: attrs,
},
...(typeof theme === 'string' || 'name' in theme
? { theme }
: {
themes: theme,
defaultColor: false,
}),
...(typeof theme === 'object' && 'light' in theme && 'dark' in theme
? { themes: theme, defaultColor: false }
: { theme }),
})
return fillEmptyHighlightedLine(restoreMustache(highlighted))
return restoreMustache(highlighted)
}
}

View File

@ -111,11 +111,11 @@ const defaultBlocksOptions: ContainerOptions = {
},
}
export function containerPlugin(md: MarkdownIt, options: ContainerOptions = {}) {
export function containerPlugin(md: MarkdownIt, options: Options, containerOptions: ContainerOptions = {}) {
Object.keys(defaultBlocksOptions).forEach((optionKey) => {
const option: BlockItem = {
...defaultBlocksOptions[optionKey as keyof Blocks],
...(options[optionKey as keyof Blocks] || {}),
...(containerOptions[optionKey as keyof Blocks] || {}),
}
md.use(...createContainer(optionKey, option))
@ -135,7 +135,7 @@ export function containerPlugin(md: MarkdownIt, options: ContainerOptions = {})
})
}
function createCodeGroup(options: ContainerOptions): ContainerArgs {
function createCodeGroup(options: Options): ContainerArgs {
return [
container,
'code-group',
@ -177,9 +177,7 @@ function createCodeGroup(options: ContainerOptions): ContainerArgs {
}
return `<div class="vp-code-group${getAdaptiveThemeMarker(
{
theme: options.theme!,
},
options,
)}"><div class="tabs">${tabs}</div><div class="blocks">\n`
}
return `</div></div>\n`

View File

@ -4,6 +4,7 @@ import type { SiteConfig } from 'valaxy/types'
import type { ThemeOptions } from '../../types'
export interface Options {
hasSingleTheme: boolean
theme: ThemeOptions
siteConfig?: SiteConfig
}
@ -19,16 +20,7 @@ export function extractLang(info: string) {
}
export function getAdaptiveThemeMarker(options: Options) {
const { theme } = options
const hasSingleTheme = typeof theme === 'string' || 'name' in theme
let marker = ''
if (hasSingleTheme) {
marker = ' va-adaptive-theme'
const themeName = typeof theme === 'string' ? theme : theme.name
const isDark = themeName.includes('dark') || themeName.includes('night')
marker = isDark ? ' dark' : ' light'
}
return marker
return options.hasSingleTheme ? '' : ' vp-adaptive-theme'
}
export function extractTitle(info: string, html = false) {

View File

@ -4,10 +4,10 @@ import type {
BuiltinTheme,
Highlighter,
LanguageInput,
ShikijiTransformer,
ShikiTransformer,
ThemeRegistration
,
} from 'shikiji'
} from 'shiki'
import type anchorPlugin from 'markdown-it-anchor'
import type { KatexOptions } from 'katex'
@ -26,7 +26,7 @@ import type { TocPluginOptions } from '@mdit-vue/plugin-toc'
import type {
ComponentPluginOptions,
} from '@mdit-vue/plugin-component'
import type { Blocks } from './plugins/markdown-it/container'
import type { Blocks, ContainerOptions } from './plugins/markdown-it/container'
export type ThemeOptions =
| ThemeRegistration
@ -56,6 +56,8 @@ export interface MarkdownOptions {
allowedAttributes?: string[]
disable?: boolean
}
/* ==================== Syntax Highlighting ==================== */
/**
* Custom theme for syntax highlighting.
*
@ -65,21 +67,21 @@ export interface MarkdownOptions {
* @example { theme: { light: 'github-light', dark: 'github-dark' } }
*
* You can use an existing theme.
* @see https://github.com/antfu/shikiji/blob/main/docs/themes.md#all-themes
* @see https://shiki.style/themes
* Or add your own theme.
* @see https://github.com/antfu/shikiji/blob/main/docs/themes.md#load-custom-themes
* @see https://shiki.style/guide/load-theme
*/
theme?: ThemeOptions
/**
* Languages for syntax highlighting.
* @see https://github.com/antfu/shikiji/blob/main/docs/languages.md#all-themes
* @see https://shiki.style/languages
*/
languages?: LanguageInput[]
/**
* Custom language aliases.
*
* @example { 'my-lang': 'js' }
* @see https://github.com/antfu/shikiji/tree/main#custom-language-aliases
* @see https://shiki.style/guide/load-lang#custom-language-aliases
*/
languageAlias?: Record<string, string>
/**
@ -93,23 +95,51 @@ export interface MarkdownOptions {
defaultHighlightLang?: string
/**
* Transformers applied to code blocks
* @see https://github.com/antfu/shikiji#hast-transformers
* @see https://shiki.style/guide/transformers
*/
codeTransformers?: ShikijiTransformer[]
codeTransformers?: ShikiTransformer[]
/**
* Setup Shikiji instance
* Setup Shiki instance
*/
shikijiSetup?: (shikiji: Highlighter) => void | Promise<void>
shikiSetup?: (shiki: Highlighter) => void | Promise<void>
/* ==================== Markdown It Plugins ==================== */
// mdit-vue plugins
/**
* Options for `@mdit-vue/plugin-frontmatter`
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-frontmatter
*/
frontmatter?: FrontmatterPluginOptions
headers?: HeadersPluginOptions
/**
* Options for `@mdit-vue/plugin-headers`
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-headers
*/
headers?: HeadersPluginOptions | boolean
/**
* Options for `@mdit-vue/plugin-sfc`
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-sfc
*/
sfc?: SfcPluginOptions
/**
* Options for `@mdit-vue/plugin-toc`
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-toc
*/
toc?: TocPluginOptions
/**
* Options for `markdown-it-container`
* @see https://github.com/markdown-it/markdown-it-container
*/
container?: ContainerOptions
/**
* Custom block configurations based on `markdown-it-container`
*/
blocks?: Blocks
/**
* Options for `@mdit-vue/plugin-component`
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-component
*/
component?: ComponentPluginOptions
/**
* @see [markdown-it-image-figures](https://www.npmjs.com/package/markdown-it-image-figures)
*/
@ -124,10 +154,6 @@ export interface MarkdownOptions {
* @see https://katex.org/docs/options.html
*/
katex?: KatexOptions
/**
* Custom block configurations
*/
blocks?: Blocks
externalLinks?: Record<string, string>
/* lazyload?: {

View File

@ -65,7 +65,7 @@
"dependencies": {
"@antfu/utils": "^0.7.7",
"@ctrl/tinycolor": "^4.0.3",
"@iconify-json/carbon": "^1.1.28",
"@iconify-json/carbon": "^1.1.29",
"@iconify-json/ri": "^1.1.19",
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@types/body-scroll-lock": "^3.1.2",
@ -74,7 +74,7 @@
"@unhead/schema-org": "^1.8.10",
"@unhead/vue": "^1.8.10",
"@valaxyjs/devtools": "workspace:*",
"@vitejs/plugin-vue": "^5.0.3",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/devtools-api": "^7.0.14",
"@vueuse/core": "^10.7.2",
"@vueuse/integrations": "^10.7.2",
@ -113,20 +113,19 @@
"pinia": "^2.1.7",
"qrcode": "^1.5.3",
"sass": "^1.70.0",
"shikiji": "0.9.7",
"shikiji-transformers": "0.9.7",
"shiki": "^1.1.1",
"star-markdown-css": "^0.4.2",
"unconfig": "^0.3.11",
"unocss": "^0.58.4",
"unocss": "^0.58.5",
"unplugin-vue-components": "^0.26.0",
"unplugin-vue-router": "^0.7.0",
"vanilla-lazyload": "^17.8.5",
"vite": "^5.0.12",
"vanilla-lazyload": "^17.8.8",
"vite": "^5.1.1",
"vite-plugin-vue-devtools": "^7.0.14",
"vite-plugin-vue-layouts": "0.11.0",
"vite-ssg": "0.23.6",
"vite-ssg-sitemap": "0.6.1",
"vue": "^3.4.15",
"vue": "^3.4.18",
"vue-i18n": "^9.9.1",
"vue-router": "^4.2.5",
"yargs": "^17.7.2"

File diff suppressed because it is too large Load Diff