Skip to content

Commit 7a2f327

Browse files
committed
feat: add addClassToHast to transformer context
1 parent d123b4b commit 7a2f327

File tree

14 files changed

+34
-29
lines changed

14 files changed

+34
-29
lines changed

docs/.vitepress/store/playground.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const usePlayground = defineStore('playground', () => {
3939
}
4040

4141
;(async () => {
42-
const { getHighlighter, addClassToHast } = await import('shiki')
42+
const { getHighlighter } = await import('shiki')
4343
const { bundledLanguagesInfo: bundleFull } = await import('shiki/bundle/full')
4444
const { bundledLanguagesInfo: bundleWeb } = await import('shiki/bundle/web')
4545
const { bundledThemesInfo } = await import('shiki/themes')
@@ -93,7 +93,7 @@ export const usePlayground = defineStore('playground', () => {
9393
transformers: [
9494
{
9595
pre(node) {
96-
addClassToHast(node, 'vp-code')
96+
this.addClassToHast(node, 'vp-code')
9797
preStyle.value = node.properties?.style as string || ''
9898
},
9999
},

docs/guide/transformers.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ Shiki uses [`hast`](https://github.com/syntax-tree/hast), a AST format for HTML,
55
You can provide your own `transformers` to customize the generated HTML by manipulating the hast tree. You can pass custom functions to modify the tree for different types of nodes. For example:
66

77
```ts twoslash
8-
import { addClassToHast, codeToHtml } from 'shiki'
8+
import { codeToHtml } from 'shiki'
99

1010
const code = await codeToHtml('foo\bar', {
1111
lang: 'js',
1212
theme: 'vitesse-light',
1313
transformers: [
1414
{
1515
code(node) {
16-
addClassToHast(node, 'language-js')
16+
this.addClassToHast(node, 'language-js')
1717
},
1818
line(node, line) {
1919
node.properties['data-line'] = line
2020
if ([1, 3, 4].includes(line))
21-
addClassToHast(node, 'highlight')
21+
this.addClassToHast(node, 'highlight')
2222
},
2323
span(node, line, col) {
2424
node.properties['data-token'] = `token:${line}:${col}`

packages/core/src/code-to-hast.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {
99
} from './types'
1010
import { FontStyle } from './types'
1111
import { codeToTokens } from './code-to-tokens'
12-
import { getTokenStyleObject, stringifyTokenStyle } from './utils'
12+
import { addClassToHast, getTokenStyleObject, stringifyTokenStyle } from './utils'
1313

1414
export function codeToHast(
1515
internal: ShikiInternal,
@@ -102,6 +102,7 @@ export function tokensToHast(
102102

103103
const context: ShikiTransformerContext = {
104104
...transformerContext,
105+
addClassToHast,
105106
get tokens() {
106107
return tokens
107108
},

packages/core/src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,13 @@ export interface ShikiTransformerContext extends ShikiTransformerContextCommon {
488488
readonly pre: Element
489489
readonly code: Element
490490
readonly lines: Element[]
491+
492+
/**
493+
* Utility to append class to a hast node
494+
*
495+
* If the `property.class` is a string, it will be splitted by space and converted to an array.
496+
*/
497+
addClassToHast: (hast: Element, className: string | string[]) => Element
491498
}
492499

493500
export interface ShikiTransformer {

packages/core/src/utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export function isSpecialTheme(theme: string | ThemeInput | null | undefined): t
5656
*/
5757
export function addClassToHast(node: Element, className: string | string[]) {
5858
if (!className)
59-
return
59+
return node
6060
node.properties ||= {}
6161
node.properties.class ||= []
6262
if (typeof node.properties.class === 'string')
@@ -69,6 +69,7 @@ export function addClassToHast(node: Element, className: string | string[]) {
6969
if (c && !node.properties.class.includes(c))
7070
node.properties.class.push(c)
7171
}
72+
return node
7273
}
7374

7475
/**

packages/rehype/src/core.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { addClassToHast } from 'shiki/core'
21
import type { CodeOptionsMeta, CodeOptionsThemes, CodeToHastOptions, HighlighterGeneric, TransformerOptions } from 'shiki/core'
32
import type { Element, Root } from 'hast'
43
import type { BuiltinTheme } from 'shiki'
@@ -135,7 +134,7 @@ const rehypeShikiFromHighlighter: Plugin<[HighlighterGeneric<any, any>, RehypeSh
135134
codeOptions.transformers.push({
136135
name: 'rehype-shiki:code-language-class',
137136
code(node) {
138-
addClassToHast(node, language)
137+
this.addClassToHast(node, language)
139138
return node
140139
},
141140
})

packages/transformers/src/shared/highlight-word.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { Element, ElementContent, Text } from 'hast'
2-
import { addClassToHast } from 'shiki/core'
2+
import type { ShikiTransformerContext } from 'shiki/core'
33

4-
export function highlightWordInLine(line: Element, ignoredElement: Element | null, word: string, className: string): void {
4+
export function highlightWordInLine(this: ShikiTransformerContext, line: Element, ignoredElement: Element | null, word: string, className: string): void {
55
const content = getTextContent(line)
66
let index = content.indexOf(word)
77

88
while (index !== -1) {
9-
highlightRange(line.children, ignoredElement, index, word.length, className)
9+
highlightRange.call(this, line.children, ignoredElement, index, word.length, className)
1010
index = content.indexOf(word, index + 1)
1111
}
1212
}
@@ -25,7 +25,7 @@ function getTextContent(element: ElementContent): string {
2525
* @param index highlight beginning index
2626
* @param len highlight length
2727
*/
28-
function highlightRange(elements: ElementContent[], ignoredElement: Element | null, index: number, len: number, className: string) {
28+
function highlightRange(this: ShikiTransformerContext, elements: ElementContent[], ignoredElement: Element | null, index: number, len: number, className: string) {
2929
let currentIdx = 0
3030

3131
for (let i = 0; i < elements.length; i++) {
@@ -45,7 +45,7 @@ function highlightRange(elements: ElementContent[], ignoredElement: Element | nu
4545
continue
4646

4747
const separated = separateToken(element, textNode, start, length)
48-
addClassToHast(separated[1], className)
48+
this.addClassToHast(separated[1], className)
4949

5050
// insert
5151
const output = separated.filter(Boolean) as Element[]

packages/transformers/src/transformers/compact-line-options.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { ShikiTransformer } from 'shiki'
2-
import { addClassToHast } from 'shiki'
32

43
export interface TransformerCompactLineOption {
54
/**
@@ -20,7 +19,7 @@ export function transformerCompactLineOptions(
2019
line(node, line) {
2120
const lineOption = lineOptions.find(o => o.line === line)
2221
if (lineOption?.classes)
23-
addClassToHast(node, lineOption.classes)
22+
this.addClassToHast(node, lineOption.classes)
2423
return node
2524
},
2625
}

packages/transformers/src/transformers/notation-highlight-word.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ShikiTransformer, addClassToHast } from 'shiki'
1+
import type { ShikiTransformer } from 'shiki'
22
import { createCommentNotationTransformer } from '../utils'
33
import { highlightWordInLine } from '../shared/highlight-word'
44

@@ -34,10 +34,10 @@ export function transformerNotationWordHighlight(
3434
lines
3535
// Don't include the comment itself
3636
.slice(index + 1, index + 1 + lineNum)
37-
.forEach(line => highlightWordInLine(line, comment, word, classActiveWord))
37+
.forEach(line => highlightWordInLine.call(this, line, comment, word, classActiveWord))
3838

3939
if (classActivePre)
40-
addClassToHast(this.pre, classActivePre)
40+
this.addClassToHast(this.pre, classActivePre)
4141
return true
4242
},
4343
true, // remove empty lines

packages/transformers/src/transformers/notation-map.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { ShikiTransformer } from 'shiki'
2-
import { addClassToHast } from 'shiki'
32
import { createCommentNotationTransformer } from '../utils'
43

54
export interface TransformerNotationMapOptions {
@@ -31,10 +30,10 @@ export function transformerNotationMap(
3130
lines
3231
.slice(index, index + lineNum)
3332
.forEach((line) => {
34-
addClassToHast(line, classMap[match])
33+
this.addClassToHast(line, classMap[match])
3534
})
3635
if (classActivePre)
37-
addClassToHast(this.pre, classActivePre)
36+
this.addClassToHast(this.pre, classActivePre)
3837
return true
3938
},
4039
)

0 commit comments

Comments
 (0)