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.
```html
```vue-html
<ClientOnly>
<NonSSRFriendlyComponent />
</ClientOnly>

@ -198,7 +198,7 @@ console.log('Hello, VitePress!')
## 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**
@ -240,7 +240,7 @@ export default {
</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

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

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

@ -303,7 +303,7 @@
position: relative;
z-index: 1;
margin: 0;
padding: 16px 24px;
padding: 14px 24px;
background: transparent;
overflow-x: auto;
}
@ -372,100 +372,32 @@
transition: color 0.5s;
}
.vp-doc [class~='language-vue']:before { content: 'vue'; }
.vp-doc [class~='language-html']:before { content: 'html'; }
.vp-doc [class~='language-vue-html']:before { content: 'template'; }
.vp-doc [class~='language-css']:before { content: 'css'; }
.vp-doc [class~='language-js']:before { content: 'js'; }
.vp-doc [class~='language-jsx']:before { content: 'jsx'; }
.vp-doc [class~='language-ts']:before { content: 'ts'; }
.vp-doc [class~='language-tsx']:before { content: 'tsx'; }
.vp-doc [class~='language-json']:before { content: 'json'; }
.vp-doc [class~='language-yaml']:before { content: 'yaml'; }
.vp-doc [class~='language-yml']:before { content: 'yaml'; }
.vp-doc [class~='language-sh']:before { content: 'sh'; }
.vp-doc [class~='language-bash']:before { content: 'sh'; }
/**
* Code: Highlight
*
* prism.js tomorrow night eighties theme.
* https://github.com/chriskempson/tomorrow-theme
*
* @author Rose Pritchard
* -------------------------------------------------------------------------- */
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #999;
}
.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;
}
.vp-doc [class~='language-vue']:before { content: 'vue'; }
.vp-doc [class~='language-html']:before { content: 'html'; }
.vp-doc [class~='language-vue-html']:before { content: 'template'; }
.vp-doc [class~='language-md']:before { content: 'md' }
.vp-doc [class~='language-markdown']:before { content: 'md'; }
.vp-doc [class~='language-css']:before { content: 'css'; }
.vp-doc [class~='language-sass']:before { content: 'sass'; }
.vp-doc [class~='language-scss']:before { content: 'scss'; }
.vp-doc [class~='language-less']:before { content: 'less'; }
.vp-doc [class~='language-stylus']:before { content: 'styl'; }
.vp-doc [class~='language-js']:before { content: 'js'; }
.vp-doc [class~='language-javascript']:before { content: 'js'; }
.vp-doc [class~='language-jsx']:before { content: 'jsx'; }
.vp-doc [class~='language-ts']:before { content: 'ts'; }
.vp-doc [class~='language-typescript']:before { content: 'ts'; }
.vp-doc [class~='language-tsx']:before { content: 'tsx'; }
.vp-doc [class~='language-json']:before { content: 'json'; }
.vp-doc [class~='language-rb']:before { content: 'rb'; }
.vp-doc [class~='language-ruby']:before { content: 'rb'; }
.vp-doc [class~='language-py']:before { content: 'py'; }
.vp-doc [class~='language-python']:before { content: 'py'; }
.vp-doc [class~='language-php']:before { content: 'php'; }
.vp-doc [class~='language-go']:before { content: 'go'; }
.vp-doc [class~='language-rust']:before { content: 'rust'; }
.vp-doc [class~='language-java']:before { content: 'java'; }
.vp-doc [class~='language-c']:before { content: 'c'; }
.vp-doc [class~='language-yaml']:before { content: 'yaml'; }
.vp-doc [class~='language-sh']:before { content: 'sh'; }
.vp-doc [class~='language-bash']:before { content: 'sh'; }

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

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

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

@ -1,52 +1,15 @@
import chalk from 'chalk'
import escapeHtml from 'escape-html'
import prism from 'prismjs'
// prism is listed as actual dep so it's ok to require
const loadLanguages = require('prismjs/components/index')
export const highlight = async (theme = 'material-palenight') => {
const highlighter = await require('shiki').getHighlighter({
theme
})
// required to make embedded highlighting work...
loadLanguages(['markup', 'css', 'javascript'])
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 (str: string, lang: string) => {
if (!lang || lang === 'text') {
return `<pre v-pre><code>${escapeHtml(str)}</code></pre>`
}
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
.replace(/<\/div>$/, `${lineNumbersWrapperCode}</div>`)
.replace(/"(language-\w+)"/, '"$1 line-numbers-mode"')
.replace(/"(language-\w*)"/, '"$1 line-numbers-mode"')
return finalCode
}

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

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

@ -23,16 +23,6 @@ declare module 'escape-html' {
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' {
export const remove: (str: string) => string
}

Loading…
Cancel
Save