Skip to content

Commit d2ed7ea

Browse files
chrisfarmstimblair
authored andcommitted
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]. The deafult mermaid themes for diagrams don't quite fit the GOV.UK "brand" colors, and creating the CSS to get the colors pretty is out of scope, so this just sets it to use the "neutral" (grayscale) theme, which at least doesn't clash with anything. [mermaid]: https://mermaid-js.github.io/mermaid/#/ [fail-exit]: mermaidjs/mermaid.cli#77
1 parent 9aa4b31 commit d2ed7ea

File tree

4 files changed

+72
-2
lines changed

4 files changed

+72
-2
lines changed

example/source/code.html.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,14 @@ An example of a code block with a short length
3939
RSpec.describe ContentItem do
4040
end
4141
```
42+
43+
An example of a [mermaid](https://mermaid-js.github.io/mermaid) diagram
44+
45+
```mermaid
46+
graph TD;
47+
A-->B;
48+
A-->C;
49+
B-->D;
50+
C-->D;
51+
```
52+

lib/govuk_tech_docs/tech_docs_html_renderer.rb

Lines changed: 33 additions & 1 deletion
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
@@ -80,7 +82,9 @@ def table_row(body)
8082
end
8183

8284
def block_code(text, lang)
83-
if defined?(super)
85+
if lang == "mermaid"
86+
block_diagram(text)
87+
elsif defined?(super)
8488
# Post-processing the block_code HTML to implement tabbable code blocks.
8589
#
8690
# Middleman monkey patches the Middleman::Renderers::MiddlemanRedcarpetHTML
@@ -108,5 +112,33 @@ def block_code(text, lang)
108112
pre.to_html
109113
end
110114
end
115+
116+
def block_diagram(code)
117+
mmdc = "#{__dir__}/../../node_modules/.bin/mmdc"
118+
Dir.mktmpdir do |tmp|
119+
input_path = "#{tmp}/input"
120+
output_path = "#{tmp}/output.svg"
121+
File.open(input_path, "w") { |f| f.write(code) }
122+
ok = exec_with_timeout("#{mmdc} -i #{input_path} -o #{output_path} --theme neutral", 10)
123+
if ok && File.exist?(output_path)
124+
File.read(output_path)
125+
else
126+
"<pre>#{code}</pre>"
127+
end
128+
end
129+
end
130+
131+
def exec_with_timeout(cmd, timeout)
132+
pid = Process.spawn(cmd, { %i[err out] => :close, :pgroup => true })
133+
begin
134+
Timeout.timeout(timeout) do
135+
Process.waitpid(pid, 0)
136+
$?.exitstatus.zero?
137+
end
138+
rescue Timeout::Error
139+
Process.kill(15, -Process.getpgid(pid))
140+
false
141+
end
142+
end
111143
end
112144
end

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"lint": "standard"
77
},
88
"dependencies": {
9-
"govuk-frontend": "~5.7.1"
9+
"govuk-frontend": "~5.7.1",
10+
"@mermaid-js/mermaid-cli": "^8.4.8"
1011
},
1112
"devDependencies": {
1213
"standard": "^14.3.4"

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)