2025-02-09 00:20:32 +08:00

85 lines
3.0 KiB
TypeScript

import type { TransformerTwoslashOptions } from '@shikijs/twoslash/core'
import type { ShikiTransformer } from 'shiki'
import type { VueSpecificOptions } from 'twoslash-vue'
import type { TwoslashFloatingVueRendererOptions } from './renderer-floating-vue.js'
import process from 'node:process'
import { createTransformerFactory } from '@shikijs/twoslash/core'
import { removeTwoslashNotations } from 'twoslash'
import { createTwoslasher } from 'twoslash-vue'
import { rendererFloatingVue } from './renderer-floating-vue.js'
export * from './renderer-floating-vue.js'
interface TransformerTwoslashVueOptions extends TransformerTwoslashOptions {
twoslashOptions?: TransformerTwoslashOptions['twoslashOptions'] & VueSpecificOptions
}
export interface VuePressTwoslashOptions extends TransformerTwoslashVueOptions, TwoslashFloatingVueRendererOptions {
/**
* Requires adding `twoslash` to the code block explicitly to run twoslash
* @default true
*/
explicitTrigger?: TransformerTwoslashOptions['explicitTrigger']
}
/**
* Create a Shiki transformer for VuePress to enable twoslash integration
*/
export function transformerTwoslash(options: VuePressTwoslashOptions = {}): ShikiTransformer {
const {
explicitTrigger = true,
} = options
const onError = (error: any, code: string) => {
const isCI = typeof process !== 'undefined' && process?.env?.CI
const isDev = typeof process !== 'undefined' && process?.env?.NODE_ENV === 'development'
const shouldThrow = (options.throws || isCI || !isDev) && options.throws !== false
console.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
console.error(error)
return removeTwoslashNotations(code)
}
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, options) {
const cleanup = options.transformers?.find(i => i.name === 'vuepress:clean-up')
if (cleanup)
options.transformers?.splice(options.transformers.indexOf(cleanup), 1)
// Disable v-pre for twoslash, because we need render it with FloatingVue
if (!explicitTrigger || options.meta?.__raw?.match(trigger)) {
const vPre = options.transformers?.find(i => i.name === 'vuepress:v-pre')
if (vPre)
options.transformers?.splice(options.transformers.indexOf(vPre), 1)
}
return twoslash.preprocess!.call(this, code, options)
},
postprocess(html) {
if (this.meta.twoslash)
return html.replace(/\{/g, '{')
return html
},
}
}