All files index.ts

0% Statements 0/36
0% Branches 0/32
0% Functions 0/5
0% Lines 0/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70                                                                                                                                           
import Prism from 'prismjs'
import parse from 'rehype-parse'
import unifiedTypes, { unified } from 'unified'
import * as mdast from 'mdast'
import * as hast from 'hast'
import { Node } from 'unist'
import { visit } from 'unist-util-visit'
import { select } from 'unist-util-select'
 
 
const getLang = (node: hast.Element): string => {
  const lang: string = node.properties?.className?.[0] || ''
  return lang.replace(/^language-/, '')
}
 
const visitor = (preNode: hast.Element): void => {
  const codeNode = select('[tagName=code]', preNode) as (hast.Element | null)
 
  if (!codeNode) return
  const lang = getLang(codeNode)
 
  if (!lang || !Prism.languages[lang]) return
 
  const textNode = select('text', codeNode) as (mdast.Text | null)
  if (!textNode) return
 
  const className = `language-${lang}`
  const html = Prism.highlight(textNode.value, Prism.languages[lang], lang)
  const tree = unified()
    .use(parse, { fragment: true })
    .parse(html) as hast.Element
 
  if (!preNode.properties) preNode.properties = {}
  if (!preNode.properties.className) preNode.properties.className = []
  if (typeof preNode.properties.className === 'string') preNode.properties.className = [preNode.properties.className]
  if (typeof preNode.properties.className === 'number') preNode.properties.className = [preNode.properties.className]
  if (typeof preNode.properties.className === 'boolean') preNode.properties.className = []
  preNode.properties.className = [...preNode.properties.className, className]
 
  codeNode.children = tree.children
}
 
const selector = (node: hast.Element): boolean => node.tagName === 'pre'
 
 
export interface RehypePrismOptions {
  plugins: (
    'autolinker'| 'autoloader' | 'command-line' | 'copy-to-clipboard' |
    'custom-class' | 'data-uri-highlight' | 'diff-highlight' |
    'download-button' | 'file-highlight' | 'filter-highlight-all' |
    'highlight-keywords' | 'inline-color' | 'jsonp-highlight' |
    'keep-markup' | 'line-highlight' | 'line-numbers' | 'match-braces' |
    'normalize-whitespace' | 'previewers' | 'remove-initial-line-feed' |
    'show-invisibles' | 'show-language' | 'toolbar' | 'treeview' |
    'unescaped-markup' | 'wpd'
  )[]
}
 
const rehypePrism: unifiedTypes.Plugin<[RehypePrismOptions?]> = (options?: RehypePrismOptions) => {
  if (options && options.plugins) {
    for (const plugin of options.plugins) {
      import(`prismjs/plugins/${plugin}/prism-${plugin}.js`)
    }
  }
 
  return (tree: Node) => visit(tree, selector as any, visitor as any)
}
 
export default rehypePrism