use shiki instead of prismjs for syntax highlight

pull/654/head
Ryo_gk 3 years ago
parent 02e9cb5e71
commit 6d9e697e0b

@ -85,7 +85,7 @@ Because VitePress applications are server-rendered in Node.js when generating st
If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the `ClientOnly` component. If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the `ClientOnly` component.
```html ```vue-html
<ClientOnly> <ClientOnly>
<NonSSRFriendlyComponent /> <NonSSRFriendlyComponent />
</ClientOnly> </ClientOnly>

@ -198,7 +198,7 @@ console.log('Hello, VitePress!')
## Syntax Highlighting in Code Blocks ## Syntax Highlighting in Code Blocks
VitePress uses [Prism](https://prismjs.com) to highlight language syntax in Markdown code blocks, using coloured text. Prism supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block: VitePress uses [Shiki](https://shiki.matsu.io/) to highlight language syntax in Markdown code blocks, using coloured text. Shiki supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block:
**Input** **Input**
@ -240,7 +240,7 @@ export default {
</ul> </ul>
``` ```
A [list of valid languages](https://prismjs.com/#languages-list) is available on Prisms site. A [list of valid languages](https://github.com/shikijs/shiki/blob/main/docs/languages.md) is available on Shikis repository.
## Line Highlighting in Code Blocks ## Line Highlighting in Code Blocks

@ -79,7 +79,7 @@
"@vitejs/plugin-vue": "^2.3.2", "@vitejs/plugin-vue": "^2.3.2",
"@vueuse/core": "^8.5.0", "@vueuse/core": "^8.5.0",
"body-scroll-lock": "^4.0.0-beta.0", "body-scroll-lock": "^4.0.0-beta.0",
"prismjs": "^1.25.0", "shiki": "^0.10.1",
"vite": "^2.9.7", "vite": "^2.9.7",
"vue": "3.2.33" "vue": "3.2.33"
}, },

@ -56,11 +56,11 @@ importers:
ora: ^5.4.0 ora: ^5.4.0
polka: ^0.5.2 polka: ^0.5.2
prettier: ^2.3.0 prettier: ^2.3.0
prismjs: ^1.25.0
rimraf: ^3.0.2 rimraf: ^3.0.2
rollup: ^2.56.3 rollup: ^2.56.3
rollup-plugin-esbuild: ^4.8.2 rollup-plugin-esbuild: ^4.8.2
semver: ^7.3.5 semver: ^7.3.5
shiki: ^0.10.1
sirv: ^1.0.12 sirv: ^1.0.12
typescript: ^4.6.4 typescript: ^4.6.4
vite: ^2.9.7 vite: ^2.9.7
@ -73,7 +73,7 @@ importers:
'@vitejs/plugin-vue': 2.3.3_vite@2.9.9+vue@3.2.33 '@vitejs/plugin-vue': 2.3.3_vite@2.9.9+vue@3.2.33
'@vueuse/core': 8.5.0_vue@3.2.33 '@vueuse/core': 8.5.0_vue@3.2.33
body-scroll-lock: 4.0.0-beta.0 body-scroll-lock: 4.0.0-beta.0
prismjs: 1.25.0 shiki: 0.10.1
vite: 2.9.9 vite: 2.9.9
vue: 3.2.33 vue: 3.2.33
devDependencies: devDependencies:
@ -2557,7 +2557,6 @@ packages:
/jsonc-parser/3.0.0: /jsonc-parser/3.0.0:
resolution: {integrity: sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==} resolution: {integrity: sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==}
dev: true
/jsonfile/4.0.0: /jsonfile/4.0.0:
resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=} resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=}
@ -3191,10 +3190,6 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/prismjs/1.25.0:
resolution: {integrity: sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==}
dev: false
/process-nextick-args/2.0.1: /process-nextick-args/2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: true dev: true
@ -3464,6 +3459,14 @@ packages:
resolution: {integrity: sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==} resolution: {integrity: sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==}
dev: true dev: true
/shiki/0.10.1:
resolution: {integrity: sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==}
dependencies:
jsonc-parser: 3.0.0
vscode-oniguruma: 1.6.2
vscode-textmate: 5.2.0
dev: false
/side-channel/1.0.4: /side-channel/1.0.4:
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
dependencies: dependencies:
@ -3909,6 +3912,14 @@ packages:
- stylus - stylus
dev: true dev: true
/vscode-oniguruma/1.6.2:
resolution: {integrity: sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==}
dev: false
/vscode-textmate/5.2.0:
resolution: {integrity: sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==}
dev: false
/vue-demi/0.12.5_vue@3.2.33: /vue-demi/0.12.5_vue@3.2.33:
resolution: {integrity: sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==} resolution: {integrity: sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==}
engines: {node: '>=12'} engines: {node: '>=12'}

@ -303,7 +303,7 @@
position: relative; position: relative;
z-index: 1; z-index: 1;
margin: 0; margin: 0;
padding: 16px 24px; padding: 14px 24px;
background: transparent; background: transparent;
overflow-x: auto; overflow-x: auto;
} }
@ -372,100 +372,32 @@
transition: color 0.5s; transition: color 0.5s;
} }
.vp-doc [class~='language-vue']:before { content: 'vue'; } .vp-doc [class~='language-vue']:before { content: 'vue'; }
.vp-doc [class~='language-html']:before { content: 'html'; } .vp-doc [class~='language-html']:before { content: 'html'; }
.vp-doc [class~='language-vue-html']:before { content: 'template'; } .vp-doc [class~='language-vue-html']:before { content: 'template'; }
.vp-doc [class~='language-css']:before { content: 'css'; } .vp-doc [class~='language-md']:before { content: 'md' }
.vp-doc [class~='language-js']:before { content: 'js'; } .vp-doc [class~='language-markdown']:before { content: 'md'; }
.vp-doc [class~='language-jsx']:before { content: 'jsx'; } .vp-doc [class~='language-css']:before { content: 'css'; }
.vp-doc [class~='language-ts']:before { content: 'ts'; } .vp-doc [class~='language-sass']:before { content: 'sass'; }
.vp-doc [class~='language-tsx']:before { content: 'tsx'; } .vp-doc [class~='language-scss']:before { content: 'scss'; }
.vp-doc [class~='language-json']:before { content: 'json'; } .vp-doc [class~='language-less']:before { content: 'less'; }
.vp-doc [class~='language-yaml']:before { content: 'yaml'; } .vp-doc [class~='language-stylus']:before { content: 'styl'; }
.vp-doc [class~='language-yml']:before { content: 'yaml'; } .vp-doc [class~='language-js']:before { content: 'js'; }
.vp-doc [class~='language-sh']:before { content: 'sh'; } .vp-doc [class~='language-javascript']:before { content: 'js'; }
.vp-doc [class~='language-bash']:before { content: 'sh'; } .vp-doc [class~='language-jsx']:before { content: 'jsx'; }
.vp-doc [class~='language-ts']:before { content: 'ts'; }
/** .vp-doc [class~='language-typescript']:before { content: 'ts'; }
* Code: Highlight .vp-doc [class~='language-tsx']:before { content: 'tsx'; }
* .vp-doc [class~='language-json']:before { content: 'json'; }
* prism.js tomorrow night eighties theme. .vp-doc [class~='language-rb']:before { content: 'rb'; }
* https://github.com/chriskempson/tomorrow-theme .vp-doc [class~='language-ruby']:before { content: 'rb'; }
* .vp-doc [class~='language-py']:before { content: 'py'; }
* @author Rose Pritchard .vp-doc [class~='language-python']:before { content: 'py'; }
* -------------------------------------------------------------------------- */ .vp-doc [class~='language-php']:before { content: 'php'; }
.vp-doc [class~='language-go']:before { content: 'go'; }
.token.comment, .vp-doc [class~='language-rust']:before { content: 'rust'; }
.token.block-comment, .vp-doc [class~='language-java']:before { content: 'java'; }
.token.prolog, .vp-doc [class~='language-c']:before { content: 'c'; }
.token.doctype, .vp-doc [class~='language-yaml']:before { content: 'yaml'; }
.token.cdata { .vp-doc [class~='language-sh']:before { content: 'sh'; }
color: #999; .vp-doc [class~='language-bash']:before { content: 'sh'; }
}
.token.punctuation {
color: #ccc;
}
.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
color: #e2777a;
}
.token.function-name {
color: #6196cc;
}
.token.boolean,
.token.number,
.token.function {
color: #f08d49;
}
.token.property,
.token.class-name,
.token.constant,
.token.symbol {
color: #f8c555;
}
.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
color: #cc99cd;
}
.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
color: #7ec699;
}
.token.operator,
.token.entity,
.token.url {
color: #67cdcc;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.inserted {
color: green;
}

@ -182,7 +182,7 @@
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
:root { :root {
--vp-code-line-height: 24px; --vp-code-line-height: 1.5;
--vp-code-font-size: 14px; --vp-code-font-size: 14px;
--vp-code-block-color: var(--vp-c-text-dark-1); --vp-code-block-color: var(--vp-c-text-dark-1);

@ -41,11 +41,11 @@ export async function bundle(
// resolve options to pass to vite // resolve options to pass to vite
const { rollupOptions } = options const { rollupOptions } = options
const resolveViteConfig = (ssr: boolean): ViteUserConfig => ({ const resolveViteConfig = async (ssr: boolean): Promise<ViteUserConfig> => ({
root: srcDir, root: srcDir,
base: config.site.base, base: config.site.base,
logLevel: 'warn', logLevel: 'warn',
plugins: createVitePressPlugin( plugins: await createVitePressPlugin(
root, root,
config, config,
ssr, ssr,
@ -108,8 +108,8 @@ export async function bundle(
spinner.start('building client + server bundles...') spinner.start('building client + server bundles...')
try { try {
;[clientResult, serverResult] = await (Promise.all([ ;[clientResult, serverResult] = await (Promise.all([
config.mpa ? null : build(resolveViteConfig(false)), config.mpa ? null : build(await resolveViteConfig(false)),
build(resolveViteConfig(true)) build(await resolveViteConfig(true))
]) as Promise<[RollupOutput, RollupOutput]>) ]) as Promise<[RollupOutput, RollupOutput]>)
} catch (e) { } catch (e) {
spinner.stopAndPersist({ spinner.stopAndPersist({

@ -1,4 +1,5 @@
import MarkdownIt from 'markdown-it' import MarkdownIt from 'markdown-it'
import { Theme } from 'shiki'
import { parseHeader } from '../utils/parseHeader' import { parseHeader } from '../utils/parseHeader'
import { highlight } from './plugins/highlight' import { highlight } from './plugins/highlight'
import { slugify } from './plugins/slugify' import { slugify } from './plugins/slugify'
@ -29,6 +30,7 @@ export interface MarkdownOptions extends MarkdownIt.Options {
rightDelimiter?: string rightDelimiter?: string
allowedAttributes?: string[] allowedAttributes?: string[]
} }
theme?: Theme
// https://github.com/Oktavilla/markdown-it-table-of-contents // https://github.com/Oktavilla/markdown-it-table-of-contents
toc?: any toc?: any
externalLinks?: Record<string, string> externalLinks?: Record<string, string>
@ -48,15 +50,15 @@ export interface MarkdownRenderer extends MarkdownIt {
export type { Header } export type { Header }
export const createMarkdownRenderer = ( export const createMarkdownRenderer = async (
srcDir: string, srcDir: string,
options: MarkdownOptions = {}, options: MarkdownOptions = {},
base: string base: string
): MarkdownRenderer => { ): Promise<MarkdownRenderer> => {
const md = MarkdownIt({ const md = MarkdownIt({
html: true, html: true,
linkify: true, linkify: true,
highlight, highlight: await highlight(options.theme),
...options ...options
}) as MarkdownRenderer }) as MarkdownRenderer

@ -1,52 +1,15 @@
import chalk from 'chalk'
import escapeHtml from 'escape-html' import escapeHtml from 'escape-html'
import prism from 'prismjs'
// prism is listed as actual dep so it's ok to require export const highlight = async (theme = 'material-palenight') => {
const loadLanguages = require('prismjs/components/index') const highlighter = await require('shiki').getHighlighter({
theme
})
// required to make embedded highlighting work... return (str: string, lang: string) => {
loadLanguages(['markup', 'css', 'javascript']) if (!lang || lang === 'text') {
return `<pre v-pre><code>${escapeHtml(str)}</code></pre>`
function wrap(code: string, lang: string): string {
if (lang === 'text') {
code = escapeHtml(code)
}
return `<pre v-pre><code>${code}</code></pre>`
}
export const highlight = (str: string, lang: string) => {
if (!lang) {
return wrap(str, 'text')
}
lang = lang.toLowerCase()
const rawLang = lang
if (lang === 'vue' || lang === 'html') {
lang = 'markup'
}
if (lang === 'md') {
lang = 'markdown'
}
if (lang === 'ts') {
lang = 'typescript'
}
if (lang === 'py') {
lang = 'python'
}
if (!prism.languages[lang]) {
try {
loadLanguages([lang])
} catch (e) {
console.warn(
chalk.yellow(
`[vitepress] Syntax highlight for language "${lang}" is not supported.`
)
)
} }
return highlighter.codeToHtml(str, lang).replace(/^<pre.*?>/, '<pre v-pre>')
} }
if (prism.languages[lang]) {
const code = prism.highlight(str, prism.languages[lang], lang)
return wrap(code, rawLang)
}
return wrap(str, 'text')
} }

@ -21,7 +21,7 @@ export const lineNumberPlugin = (md: MarkdownIt) => {
const finalCode = rawCode const finalCode = rawCode
.replace(/<\/div>$/, `${lineNumbersWrapperCode}</div>`) .replace(/<\/div>$/, `${lineNumbersWrapperCode}</div>`)
.replace(/"(language-\w+)"/, '"$1 line-numbers-mode"') .replace(/"(language-\w*)"/, '"$1 line-numbers-mode"')
return finalCode return finalCode
} }

@ -21,7 +21,7 @@ export interface MarkdownCompileResult {
includes: string[] includes: string[]
} }
export function createMarkdownToVueRenderFn( export async function createMarkdownToVueRenderFn(
srcDir: string, srcDir: string,
options: MarkdownOptions = {}, options: MarkdownOptions = {},
pages: string[], pages: string[],
@ -30,7 +30,7 @@ export function createMarkdownToVueRenderFn(
base: string, base: string,
includeLastUpdatedData = false includeLastUpdatedData = false
) { ) {
const md = createMarkdownRenderer(srcDir, options, base) const md = await createMarkdownRenderer(srcDir, options, base)
pages = pages.map((p) => slash(p.replace(/\.md$/, ''))) pages = pages.map((p) => slash(p.replace(/\.md$/, '')))
const userDefineRegex = userDefines const userDefineRegex = userDefines

@ -7,6 +7,8 @@ import { slash } from './utils/slash'
import { OutputAsset, OutputChunk } from 'rollup' import { OutputAsset, OutputChunk } from 'rollup'
import { staticDataPlugin } from './staticDataPlugin' import { staticDataPlugin } from './staticDataPlugin'
type Awaited<T> = T extends Promise<infer P> ? P : never
const hashRE = /\.(\w+)\.js$/ const hashRE = /\.(\w+)\.js$/
const staticInjectMarkerRE = const staticInjectMarkerRE =
/\b(const _hoisted_\d+ = \/\*(?:#|@)__PURE__\*\/\s*createStaticVNode)\("(.*)", (\d+)\)/g /\b(const _hoisted_\d+ = \/\*(?:#|@)__PURE__\*\/\s*createStaticVNode)\("(.*)", (\d+)\)/g
@ -46,7 +48,7 @@ export function createVitePressPlugin(
pages pages
} = siteConfig } = siteConfig
let markdownToVue: ReturnType<typeof createMarkdownToVueRenderFn> let markdownToVue: Awaited<ReturnType<typeof createMarkdownToVueRenderFn>>
// lazy require plugin-vue to respect NODE_ENV in @vue/compiler-x // lazy require plugin-vue to respect NODE_ENV in @vue/compiler-x
const vuePlugin = require('@vitejs/plugin-vue')({ const vuePlugin = require('@vitejs/plugin-vue')({
@ -70,9 +72,9 @@ export function createVitePressPlugin(
const vitePressPlugin: Plugin = { const vitePressPlugin: Plugin = {
name: 'vitepress', name: 'vitepress',
configResolved(resolvedConfig) { async configResolved(resolvedConfig) {
config = resolvedConfig config = resolvedConfig
markdownToVue = createMarkdownToVueRenderFn( markdownToVue = await createMarkdownToVueRenderFn(
srcDir, srcDir,
markdown, markdown,
pages, pages,

@ -23,16 +23,6 @@ declare module 'escape-html' {
export default def export default def
} }
declare module 'prismjs' {
const def: any
export default def
}
declare module 'prismjs/components/index' {
const def: any
export default def
}
declare module 'diacritics' { declare module 'diacritics' {
export const remove: (str: string) => string export const remove: (str: string) => string
} }

Loading…
Cancel
Save