renderer.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. const zlib = require('zlib')
  2. // ------------------------------------
  3. // Markdown - Kroki Preprocessor
  4. // ------------------------------------
  5. module.exports = {
  6. init (mdinst, conf) {
  7. mdinst.use((md, opts) => {
  8. const openMarker = opts.openMarker || '```kroki'
  9. const openChar = openMarker.charCodeAt(0)
  10. const closeMarker = opts.closeMarker || '```'
  11. const closeChar = closeMarker.charCodeAt(0)
  12. const server = opts.server || 'https://kroki.io'
  13. md.block.ruler.before('fence', 'kroki', (state, startLine, endLine, silent) => {
  14. let nextLine
  15. let markup
  16. let params
  17. let token
  18. let i
  19. let autoClosed = false
  20. let start = state.bMarks[startLine] + state.tShift[startLine]
  21. let max = state.eMarks[startLine]
  22. // Check out the first character quickly,
  23. // this should filter out most of non-uml blocks
  24. //
  25. if (openChar !== state.src.charCodeAt(start)) { return false }
  26. // Check out the rest of the marker string
  27. //
  28. for (i = 0; i < openMarker.length; ++i) {
  29. if (openMarker[i] !== state.src[start + i]) { return false }
  30. }
  31. markup = state.src.slice(start, start + i)
  32. params = state.src.slice(start + i, max)
  33. // Since start is found, we can report success here in validation mode
  34. //
  35. if (silent) { return true }
  36. // Search for the end of the block
  37. //
  38. nextLine = startLine
  39. for (;;) {
  40. nextLine++
  41. if (nextLine >= endLine) {
  42. // unclosed block should be autoclosed by end of document.
  43. // also block seems to be autoclosed by end of parent
  44. break
  45. }
  46. start = state.bMarks[nextLine] + state.tShift[nextLine]
  47. max = state.eMarks[nextLine]
  48. if (start < max && state.sCount[nextLine] < state.blkIndent) {
  49. // non-empty line with negative indent should stop the list:
  50. // - ```
  51. // test
  52. break
  53. }
  54. if (closeChar !== state.src.charCodeAt(start)) {
  55. // didn't find the closing fence
  56. continue
  57. }
  58. if (state.sCount[nextLine] > state.sCount[startLine]) {
  59. // closing fence should not be indented with respect of opening fence
  60. continue
  61. }
  62. const i = closeMarker.findIndex(item => item !== state.src[start + i])
  63. const closeMarkerMatched = i !== -1
  64. if (!closeMarkerMatched) {
  65. continue
  66. }
  67. // make sure tail has spaces only
  68. if (state.skipSpaces(start + i) < max) {
  69. continue
  70. }
  71. // found!
  72. autoClosed = true
  73. break
  74. }
  75. let contents = state.src
  76. .split('\n')
  77. .slice(startLine + 1, nextLine)
  78. .join('\n')
  79. // We generate a token list for the alt property, to mimic what the image parser does.
  80. let altToken = []
  81. // Remove leading space if any.
  82. let alt = params ? params.slice(1) : 'uml diagram'
  83. state.md.inline.parse(
  84. alt,
  85. state.md,
  86. state.env,
  87. altToken
  88. )
  89. let firstlf = contents.indexOf('\n')
  90. if (firstlf === -1) firstlf = undefined
  91. let diagramType = contents.substring(0, firstlf)
  92. contents = contents.substring(firstlf + 1)
  93. let result = zlib.deflateSync(contents).toString('base64').replace(/\+/g, '-').replace(/\//g, '_')
  94. token = state.push('kroki', 'img', 0)
  95. // alt is constructed from children. No point in populating it here.
  96. token.attrs = [ [ 'src', `${server}/${diagramType}/svg/${result}` ], [ 'alt', '' ], ['class', 'uml-diagram prefetch-candidate'] ]
  97. token.block = true
  98. token.children = altToken
  99. token.info = params
  100. token.map = [ startLine, nextLine ]
  101. token.markup = markup
  102. state.line = nextLine + (autoClosed ? 1 : 0)
  103. return true
  104. }, {
  105. alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
  106. })
  107. md.renderer.rules.kroki = md.renderer.rules.image
  108. }, {
  109. openMarker: conf.openMarker,
  110. closeMarker: conf.closeMarker,
  111. server: conf.server
  112. })
  113. }
  114. }