highlight.js 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. /* global hexo */
  2. const Prism = require('prismjs')
  3. const entities = require('he')
  4. const { promisify } = require('util')
  5. const readFile = promisify(require('fs').readFile)
  6. const path = require('path')
  7. // oof
  8. // I think this is the way to add Prism components that it doesn't include
  9. // in the default build?
  10. global.Prism = Prism
  11. // the / is needed to force it to resolve to the directory
  12. require('prismjs/components/')()
  13. delete global.Prism
  14. const rawInlineCodeRx = /<code>([^\n]+?)<\/code>/ig
  15. const unhighlightedCodeRx = /<pre><code class="([^"]*)?">([\s\S]*?)<\/code><\/pre>/igm
  16. function escapeInlineCodeBlocks (data) {
  17. data.content = data.content.replace(rawInlineCodeRx, (_, code) => {
  18. return `<code>${entities.encode(code)}</code>`
  19. })
  20. return data
  21. }
  22. function highlight (lang, code) {
  23. const startTag = `<figure class="highlight ${lang}"><table><tr><td class="code"><pre>`
  24. const endTag = `</pre></td></tr></table></figure>`
  25. let parsedCode = ''
  26. if (Prism.languages[lang]) {
  27. parsedCode = Prism.highlight(code, Prism.languages[lang])
  28. } else {
  29. parsedCode = code
  30. }
  31. return startTag + parsedCode + endTag
  32. }
  33. function prismify (data) {
  34. data.content = data.content.replace(unhighlightedCodeRx,
  35. (_, lang, code) => highlight(lang, entities.decode(code)))
  36. return data
  37. }
  38. function code (args, content) {
  39. let lang = ''
  40. if (args[0].startsWith('lang:')) {
  41. lang = args.shift().replace(/^lang:/, '')
  42. }
  43. return highlight(lang, content)
  44. }
  45. async function includeCode (args) {
  46. let lang = ''
  47. if (args[0].startsWith('lang:')) {
  48. lang = args.shift().replace(/^lang:/, '')
  49. }
  50. const file = path.join(hexo.source_dir, hexo.config.code_dir, args.join(' '))
  51. const content = await readFile(file, 'utf8')
  52. return highlight(lang, content.trim())
  53. }
  54. // Highlight as many things as we possibly can
  55. hexo.extend.tag.register('code', code, true)
  56. hexo.extend.tag.register('codeblock', code, true)
  57. hexo.extend.tag.register('include_code', includeCode, { async: true })
  58. hexo.extend.tag.register('include-code', includeCode, { async: true })
  59. hexo.extend.filter.register('after_post_render', escapeInlineCodeBlocks)
  60. // Hexo includes its own code block handling by default which may
  61. // cause the above to miss some things, so do another pass when the page
  62. // is done rendering to pick up any code blocks we may have missed.
  63. hexo.extend.filter.register('after_post_render', prismify)