Skip to content

Commit 724b410

Browse files
fuma-namaantfu
andauthoredJun 7, 2024··
feat(rehype): Support default language option (#692)
Co-authored-by: Anthony Fu <github@antfu.me>
1 parent a6067e6 commit 724b410

File tree

7 files changed

+55
-30
lines changed

7 files changed

+55
-30
lines changed
 

‎packages/rehype/src/core.ts

+35-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CodeOptionsMeta, CodeOptionsThemes, CodeToHastOptions, HighlighterGeneric, TransformerOptions } from 'shiki/core'
22
import type { Element, Root } from 'hast'
33
import type { BuiltinTheme } from 'shiki'
4-
import type { Plugin } from 'unified'
4+
import type { Transformer } from 'unified'
55
import { toString } from 'hast-util-to-string'
66
import { visit } from 'unist-util-visit'
77

@@ -18,6 +18,16 @@ export interface RehypeShikiExtraOptions {
1818
*/
1919
addLanguageClass?: boolean
2020

21+
/**
22+
* The default language to use when is not specified
23+
*/
24+
defaultLanguage?: string
25+
26+
/**
27+
* The fallback language to use when specified language is not loaded
28+
*/
29+
fallbackLanguage?: string
30+
2131
/**
2232
* Custom meta string parser
2333
* Return an object to merge with `meta`
@@ -57,19 +67,23 @@ declare module 'hast' {
5767
}
5868
}
5969

60-
const rehypeShikiFromHighlighter: Plugin<[HighlighterGeneric<any, any>, RehypeShikiCoreOptions], Root> = function (
61-
highlighter,
62-
options,
63-
) {
70+
const languagePrefix = 'language-'
71+
72+
function rehypeShikiFromHighlighter(
73+
highlighter: HighlighterGeneric<any, any>,
74+
options: RehypeShikiCoreOptions,
75+
): Transformer<Root, Root> {
76+
const langs = highlighter.getLoadedLanguages()
6477
const {
6578
addLanguageClass = false,
6679
parseMetaString,
6780
cache,
81+
defaultLanguage,
82+
fallbackLanguage,
83+
onError,
6884
...rest
6985
} = options
7086

71-
const prefix = 'language-'
72-
7387
return function (tree) {
7488
visit(tree, 'element', (node, index, parent) => {
7589
if (!parent || index == null || node.tagName !== 'pre')
@@ -87,19 +101,21 @@ const rehypeShikiFromHighlighter: Plugin<[HighlighterGeneric<any, any>, RehypeSh
87101
}
88102

89103
const classes = head.properties.className
104+
const languageClass = Array.isArray(classes)
105+
? classes.find(
106+
d => typeof d === 'string' && d.startsWith(languagePrefix),
107+
)
108+
: undefined
90109

91-
if (!Array.isArray(classes))
92-
return
93-
94-
const language = classes.find(
95-
d => typeof d === 'string' && d.startsWith(prefix),
96-
)
110+
let lang = typeof languageClass === 'string' ? languageClass.slice(languagePrefix.length) : defaultLanguage
97111

98-
if (typeof language !== 'string')
112+
if (!lang)
99113
return
100114

101-
const code = toString(head as any)
115+
if (fallbackLanguage && !langs.includes(lang))
116+
lang = fallbackLanguage
102117

118+
const code = toString(head)
103119
const cachedValue = cache?.get(code)
104120

105121
if (cachedValue) {
@@ -112,7 +128,7 @@ const rehypeShikiFromHighlighter: Plugin<[HighlighterGeneric<any, any>, RehypeSh
112128

113129
const codeOptions: CodeToHastOptions = {
114130
...rest,
115-
lang: language.slice(prefix.length),
131+
lang,
116132
meta: {
117133
...rest.meta,
118134
...meta,
@@ -125,7 +141,7 @@ const rehypeShikiFromHighlighter: Plugin<[HighlighterGeneric<any, any>, RehypeSh
125141
codeOptions.transformers.push({
126142
name: 'rehype-shiki:code-language-class',
127143
code(node) {
128-
this.addClassToHast(node, language)
144+
this.addClassToHast(node, `${languagePrefix}${lang}`)
129145
return node
130146
},
131147
})
@@ -137,8 +153,8 @@ const rehypeShikiFromHighlighter: Plugin<[HighlighterGeneric<any, any>, RehypeSh
137153
parent.children.splice(index, 1, ...fragment.children)
138154
}
139155
catch (error) {
140-
if (options.onError)
141-
options.onError(error)
156+
if (onError)
157+
onError(error)
142158
else
143159
throw error
144160
}

‎packages/rehype/src/index.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,22 @@ export type RehypeShikiOptions = RehypeShikiCoreOptions
1717
}
1818

1919
const rehypeShiki: Plugin<[RehypeShikiOptions], Root> = function (
20-
options = {} as any,
20+
options = {} as RehypeShikiOptions,
2121
) {
2222
const themeNames = ('themes' in options ? Object.values(options.themes) : [options.theme]).filter(Boolean) as BuiltinTheme[]
2323
const langs = options.langs || Object.keys(bundledLanguages)
2424

25-
// eslint-disable-next-line ts/no-this-alias
26-
const ctx = this
27-
let promise: Promise<any>
25+
let getHandler: Promise<any>
2826

29-
return async function (tree) {
30-
if (!promise) {
31-
promise = getHighlighter({
27+
return async (tree) => {
28+
if (!getHandler) {
29+
getHandler = getHighlighter({
3230
themes: themeNames,
3331
langs,
3432
})
35-
.then(highlighter => rehypeShikiFromHighlighter.call(ctx, highlighter, options))
33+
.then(highlighter => rehypeShikiFromHighlighter.call(this, highlighter, options))
3634
}
37-
const handler = await promise
35+
const handler = await getHandler
3836
return handler!(tree) as Root
3937
}
4038
}

‎packages/rehype/test/core.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ it('run', async () => {
2727
.use(remarkRehype)
2828
.use(rehypeShikiFromHighlighter, highlighter, {
2929
theme: 'vitesse-light',
30+
defaultLanguage: 'text',
3031
transformers: [
3132
transformerMetaHighlight(),
3233
],
@@ -63,6 +64,7 @@ it('run with rehype-raw', async () => {
6364
.use(rehypeRaw)
6465
.use(rehypeShikiFromHighlighter, highlighter, {
6566
theme: 'vitesse-light',
67+
defaultLanguage: 'text',
6668
transformers: [
6769
transformerMetaHighlight(),
6870
],

‎packages/rehype/test/fixtures/a.core.out.html

+3-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/rehype/test/fixtures/a.md

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/rehype/test/fixtures/a.out.html

+3-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/rehype/test/index.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ it('run', async () => {
1313
.use(remarkRehype)
1414
.use(rehypeShiki, {
1515
theme: 'vitesse-light',
16+
defaultLanguage: 'text',
1617
transformers: [
1718
transformerMetaWordHighlight(),
1819
transformerMetaHighlight(),

0 commit comments

Comments
 (0)
Please sign in to comment.