Skip to content

Commit

Permalink
refactor: migrate from shiki to shikiji (#2520)
Browse files Browse the repository at this point in the history
  • Loading branch information
ocavue authored Dec 20, 2023
1 parent e63473a commit 74138a9
Show file tree
Hide file tree
Showing 17 changed files with 124 additions and 223 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ dist
.netlify/
.eslintcache

public/shiki
public/emojis

*~
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ dist
.eslintcache
elk-translation-status.json

public/shiki
public/emojis

*~
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ You can consult the [PWA documentation](https://docs.elk.zone/pwa) to learn more
- [UnoCSS](https://uno.antfu.me/) - The instant on-demand atomic CSS engine
- [Iconify](https://github.com/iconify/icon-sets#iconify-icon-sets-in-json-format) - Iconify icon sets in JSON format
- [Masto.js](https://neet.github.io/masto.js) - Mastodon API client in TypeScript
- [shiki](https://shiki.matsu.io/) - A beautiful Syntax Highlighter
- [shikiji](https://shikiji.netlify.app/) - A beautiful and powerful syntax highlighter
- [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) - Prompt for update, Web Push Notifications and Web Share Target API

## 👨‍💻 Contributors
Expand Down
37 changes: 16 additions & 21 deletions composables/shiki.ts → composables/shikiji.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import type { Highlighter, Lang } from 'shiki-es'
import { type Highlighter, type BuiltinLanguage as Lang } from 'shikiji'

const shiki = ref<Highlighter>()
const highlighter = ref<Highlighter>()

const registeredLang = ref(new Map<string, boolean>())
let shikiImport: Promise<void> | undefined
let shikijiImport: Promise<void> | undefined

export function useHighlighter(lang: Lang) {
if (!shikiImport) {
shikiImport = import('shiki-es')
.then(async (r) => {
r.setCDN('/shiki/')
shiki.value = await r.getHighlighter({
if (!shikijiImport) {
shikijiImport = import('shikiji')
.then(async ({ getHighlighter }) => {
highlighter.value = await getHighlighter({
themes: [
'vitesse-dark',
'vitesse-light',
Expand All @@ -24,27 +23,27 @@ export function useHighlighter(lang: Lang) {
})
}

if (!shiki.value)
if (!highlighter.value)
return undefined

if (!registeredLang.value.get(lang)) {
shiki.value.loadLanguage(lang)
highlighter.value.loadLanguage(lang)
.then(() => {
registeredLang.value.set(lang, true)
})
.catch(() => {
const fallbackLang = 'md'
shiki.value?.loadLanguage(fallbackLang).then(() => {
highlighter.value?.loadLanguage(fallbackLang).then(() => {
registeredLang.value.set(fallbackLang, true)
})
})
return undefined
}

return shiki.value
return highlighter.value
}

export function useShikiTheme() {
function useShikijiTheme() {
return useColorMode().value === 'dark' ? 'vitesse-dark' : 'vitesse-light'
}

Expand All @@ -61,16 +60,12 @@ function escapeHtml(text: string) {
}

export function highlightCode(code: string, lang: Lang) {
const shiki = useHighlighter(lang)
if (!shiki)
const highlighter = useHighlighter(lang)
if (!highlighter)
return escapeHtml(code)

return shiki.codeToHtml(code, {
return highlighter.codeToHtml(code, {
lang,
theme: useShikiTheme(),
theme: useShikijiTheme(),
})
}

export function useShiki() {
return shiki
}
4 changes: 2 additions & 2 deletions composables/tiptap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Plugin } from 'prosemirror-state'

import type { Ref } from 'vue'
import { TiptapEmojiSuggestion, TiptapHashtagSuggestion, TiptapMentionSuggestion } from './tiptap/suggestion'
import { TiptapPluginCodeBlockShiki } from './tiptap/shiki'
import { TiptapPluginCodeBlockShikiji } from './tiptap/shikiji'
import { TiptapPluginCustomEmoji } from './tiptap/custom-emoji'
import { TiptapPluginEmoji } from './tiptap/emoji'

Expand Down Expand Up @@ -70,7 +70,7 @@ export function useTiptap(options: UseTiptapOptions) {
Placeholder.configure({
placeholder: () => placeholder.value!,
}),
TiptapPluginCodeBlockShiki,
TiptapPluginCodeBlockShikiji,
History.configure({
depth: 10,
}),
Expand Down
129 changes: 0 additions & 129 deletions composables/tiptap/shiki.ts

This file was deleted.

20 changes: 20 additions & 0 deletions composables/tiptap/shikiji-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type Parser, createParser } from 'prosemirror-highlight/shikiji'
import type { BuiltinLanguage } from 'shikiji/langs'

let parser: Parser | undefined

export const shikijiParser: Parser = (options) => {
const lang = options.language ?? 'text'

// Register the language if it's not yet registered
const highlighter = useHighlighter(lang as BuiltinLanguage)

// If the language is not loaded, we return an empty set of decorations
if (!highlighter)
return []

if (!parser)
parser = createParser(highlighter)

return parser(options)
}
25 changes: 25 additions & 0 deletions composables/tiptap/shikiji.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import CodeBlock from '@tiptap/extension-code-block'
import { VueNodeViewRenderer } from '@tiptap/vue-3'

import { createHighlightPlugin } from 'prosemirror-highlight'
import { shikijiParser } from './shikiji-parser'
import TiptapCodeBlock from '~/components/tiptap/TiptapCodeBlock.vue'

export const TiptapPluginCodeBlockShikiji = CodeBlock.extend({
addOptions() {
return {
...this.parent?.(),
defaultLanguage: null,
}
},

addProseMirrorPlugins() {
return [
createHighlightPlugin({ parser: shikijiParser, nodeTypes: ['codeBlock'] }),
]
},

addNodeView() {
return VueNodeViewRenderer(TiptapCodeBlock)
},
})
2 changes: 1 addition & 1 deletion config/pwa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const pwa: VitePWANuxtOptions = {
manifest: false,
injectManifest: {
globPatterns: ['**/*.{js,json,css,html,txt,svg,png,ico,webp,woff,woff2,ttf,eot,otf,wasm}'],
globIgnores: ['emojis/**', 'shiki/**', 'manifest**.webmanifest'],
globIgnores: ['emojis/**', 'manifest**.webmanifest'],
},
devOptions: {
enabled: process.env.VITE_DEV_PWA === 'true',
Expand Down
2 changes: 1 addition & 1 deletion docs/content/1.guide/3.contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ nr test
- [UnoCSS](https://uno.antfu.me/) - The instant on-demand atomic CSS engine
- [Iconify](https://github.com/iconify/icon-sets#iconify-icon-sets-in-json-format) - Iconify icon sets in JSON format
- [Masto.js](https://neet.github.io/masto.js) - Mastodon API client in TypeScript
- [shiki](https://shiki.matsu.io/) - A beautiful Syntax Highlighter
- [shikiji](https://shikiji.netlify.app/) - A beautiful and powerful syntax highlighter
- [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) - Prompt for update, Web Push Notifications and Web Share Target API
4 changes: 2 additions & 2 deletions mocks/prosemirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import proxy from 'unenv/runtime/mock/proxy'

export const Plugin = proxy
export const PluginKey = proxy
export const Decoration = proxy
export const DecorationSet = proxy
export const createParser = proxy
export const createHighlightPlugin = proxy

export { proxy as default }
7 changes: 1 addition & 6 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,6 @@ export default defineNuxtConfig({
maxAge: 24 * 60 * 60 * 365, // 1 year (versioned)
baseURL: '/fonts',
},
{
dir: '~/public/shiki',
maxAge: 24 * 60 * 60 * 365, // 1 year, matching service worker
baseURL: '/shiki',
},
],
},
sourcemap: isDevelopment,
Expand All @@ -179,7 +174,7 @@ export default defineNuxtConfig({
const alias = config.resolve!.alias as Record<string, string>
for (const dep of ['eventemitter3', 'isomorphic-ws'])
alias[dep] = resolve('./mocks/class')
for (const dep of ['shiki-es', 'fuse.js'])
for (const dep of ['fuse.js'])
alias[dep] = 'unenv/runtime/mock/proxy'
const resolver = createResolver(import.meta.url)

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@
"page-lifecycle": "^0.1.2",
"pinia": "^2.1.4",
"postcss-nested": "^6.0.1",
"prosemirror-highlight": "^0.3.3",
"rollup-plugin-node-polyfills": "^0.2.1",
"shiki": "^0.14.3",
"shiki-es": "^0.2.0",
"shikiji": "^0.9.9",
"simple-git": "^3.19.1",
"slimeform": "^0.9.1",
"stale-dep": "^0.7.0",
Expand Down
Loading

0 comments on commit 74138a9

Please sign in to comment.