Skip to content

Commit

Permalink
feat(shiki): highlight excerpt
Browse files Browse the repository at this point in the history
  • Loading branch information
Qwertovsky committed Jan 9, 2023
1 parent 30c26e3 commit 7329679
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 27 deletions.
68 changes: 41 additions & 27 deletions src/runtime/transformers/shiki/shiki.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { visit } from 'unist-util-visit'
import type { MarkdownRoot } from '../../types'
import { defineTransformer } from '../utils'
import { useShikiHighlighter } from './highlighter'
import type { TokenColorMap, MarkdownNode } from './types'
Expand All @@ -8,39 +9,52 @@ export default defineTransformer({
extensions: ['.md'],
transform: async (content, options = {}) => {
const shikiHighlighter = useShikiHighlighter(options)
const colorMap: TokenColorMap = {}
const codeBlocks: any[] = []
const inlineCodes: any = []
visit(
content.body,
(node: any) => (node.tag === 'code' && node?.props.code) || (node.tag === 'code-inline' && (node.props?.lang || node.props?.language)),
(node) => {
if (node.tag === 'code') {
codeBlocks.push(node)
} else if (node.tag === 'code-inline') {
inlineCodes.push(node)
}
}
)

await Promise.all(codeBlocks.map(highlightBlock))
await Promise.all(inlineCodes.map(highlightInline))

// Inject token colors at the end of the document
if (Object.values(colorMap).length) {
content.body.children.push({
type: 'element',
tag: 'style',
children: [{ type: 'text', value: shikiHighlighter.generateStyles(colorMap) }]
})
}
await Promise.all([
highlight(content.body),
highlight(content.excerpt)
])

return content

/**
* Highlight document with code nodes
* @param document tree
*/
async function highlight(document: MarkdownRoot) {
if (!document) return
const colorMap: TokenColorMap = {}
const codeBlocks: any[] = []
const inlineCodes: any = []
visit(
document,
(node: any) => (node?.tag === 'code' && node?.props.code) || (node?.tag === 'code-inline' && (node.props?.lang || node.props?.language)),
(node: MarkdownNode) => {
if (node?.tag === 'code') {
codeBlocks.push(node)
} else if (node?.tag === 'code-inline') {
inlineCodes.push(node)
}
}
)

await Promise.all(codeBlocks.map((node: MarkdownNode) => highlightBlock(node, colorMap)))
await Promise.all(inlineCodes.map((node: MarkdownNode) => highlightInline(node, colorMap)))

// Inject token colors at the end of the document
if (Object.values(colorMap).length) {
document?.children.push({
type: 'element',
tag: 'style',
children: [{ type: 'text', value: shikiHighlighter.generateStyles(colorMap) }]
})
}
}

/**
* Highlight inline code
*/
async function highlightInline (node: MarkdownNode) {
async function highlightInline (node: MarkdownNode, colorMap: TokenColorMap) {
const code = node.children![0].value!

// Fetch highlighted tokens
Expand All @@ -56,7 +70,7 @@ export default defineTransformer({
/**
* Highlight a code block
*/
async function highlightBlock (node: MarkdownNode) {
async function highlightBlock (node: MarkdownNode, colorMap: TokenColorMap) {
const { code, language: lang, highlights = [] } = node.props!

const innerCodeNode = node.children![0].children![0]
Expand Down
21 changes: 21 additions & 0 deletions test/features/highlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,26 @@ export const testHighlighter = () => {
]
`)
})

test('highlight excerpt', async () => {
const parsed = await $fetch('/api/parse', {
method: 'POST',
body: {
id: 'content:index.md',
content: [
'```ts',
'const a: number = 1',
'```',
'<!--more-->',
'Second block'
].join('\n')
}
})

const styleExcerpt = parsed.excerpt.children.pop()
expect(styleExcerpt.tag).toBe('style')
const styleBody = parsed.body.children.pop()
expect(styleBody.tag).toBe('style')
})
})
}

0 comments on commit 7329679

Please sign in to comment.