Skip to content

Commit bf4e2ec

Browse files
author
chrisfarms
committed
add support for rendering diagrams with mermaid
[mermaid][mermaid] generates diagrams and flowcharts from text in a similar manner as markdown. This adds support for rendering diagrams from any markdown code blocks with the language tag `mermaid`. For example the code block: ```mermaid sequenceDiagram Alice->>+John: Hello John, how are you? John-->>-Alice: Great! ``` Will now render an inline SVG diagram of the sequence instead of the raw `<code>` block. Keeping diagrams as code in this way makes it significantly easier to keep diagrams up to date with the documentation, and can make reviewing changes to them easier. The implementation takes advantage of the existing dependecy on node.js to install and execute the mermaid cli tool that translates the various diagram code into SVG. A timeout is added to execution to workaround an issue where the cli tool [fails to terminate on error][fail-exit]. [mermaid]: https://mermaid-js.github.io/mermaid/#/ [fail-exit]: mermaidjs/mermaid.cli#77
1 parent e140155 commit bf4e2ec

File tree

4 files changed

+76
-0
lines changed

4 files changed

+76
-0
lines changed

example/source/code.html.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,14 @@ An example of a code block with a short length
6262
RSpec.describe ContentItem do
6363
end
6464
```
65+
66+
An example of a [mermaid](https://mermaid-js.github.io/mermaid) diagram
67+
68+
```mermaid
69+
graph TD;
70+
A-->B;
71+
A-->C;
72+
B-->D;
73+
C-->D;
74+
```
75+

lib/govuk_tech_docs/tech_docs_html_renderer.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
require "middleman-core/renderers/redcarpet"
2+
require "tmpdir"
3+
require "timeout"
24

35
module GovukTechDocs
46
class TechDocsHTMLRenderer < Middleman::Renderers::MiddlemanRedcarpetHTML
@@ -30,5 +32,41 @@ def table(header, body)
3032
</table>
3133
</div>)
3234
end
35+
36+
def block_code(code, lang)
37+
if lang == "mermaid"
38+
block_diagram(code)
39+
else
40+
super
41+
end
42+
end
43+
44+
def block_diagram(code)
45+
mmdc = "#{__dir__}/../../node_modules/.bin/mmdc"
46+
Dir.mktmpdir do |tmp|
47+
input_path = "#{tmp}/input"
48+
output_path = "#{tmp}/output.svg"
49+
File.open(input_path, "w") { |f| f.write(code) }
50+
ok = exec_with_timeout("#{mmdc} -i #{input_path} -o #{output_path}", 2)
51+
if ok && File.exist?(output_path)
52+
File.read(output_path)
53+
else
54+
"<pre>#{code}</pre>"
55+
end
56+
end
57+
end
58+
59+
def exec_with_timeout(cmd, timeout)
60+
pid = Process.spawn(cmd, { %i[err out] => :close, :pgroup => true })
61+
begin
62+
Timeout.timeout(timeout) do
63+
Process.waitpid(pid, 0)
64+
$?.exitstatus.zero?
65+
end
66+
rescue Timeout::Error
67+
Process.kill(15, -Process.getpgid(pid))
68+
false
69+
end
70+
end
3371
end
3472
end

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"lint": "standard"
77
},
88
"dependencies": {
9+
"@mermaid-js/mermaid-cli": "^8.4.8",
910
"govuk-frontend": "^3.6.0"
1011
},
1112
"devDependencies": {

spec/features/diagrams_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require "rack/file"
2+
require "capybara/rspec"
3+
4+
Capybara.app = Rack::File.new("example/build")
5+
6+
RSpec.describe "Diagrams in code blocks" do
7+
include Capybara::DSL
8+
9+
it "generates static SVG from mermaid code blocks" do
10+
when_the_site_is_created
11+
and_i_visit_the_code_page
12+
then_there_is_an_svg_diagram
13+
end
14+
15+
def when_the_site_is_created
16+
rebuild_site!
17+
end
18+
19+
def and_i_visit_the_code_page
20+
visit "/code.html"
21+
end
22+
23+
def then_there_is_an_svg_diagram
24+
expect(page.body).to match '<svg id="mermaid-'
25+
end
26+
end

0 commit comments

Comments
 (0)