diff --git a/example/source/code.html.md b/example/source/code.html.md index 5b2a4aaa..9cbd2327 100644 --- a/example/source/code.html.md +++ b/example/source/code.html.md @@ -39,3 +39,14 @@ An example of a code block with a short length RSpec.describe ContentItem do end ``` + +An example of a [mermaid](https://mermaid-js.github.io/mermaid) diagram + +```mermaid +graph TD; + A-->B; + A-->C; + B-->D; + C-->D; +``` + diff --git a/lib/govuk_tech_docs/tech_docs_html_renderer.rb b/lib/govuk_tech_docs/tech_docs_html_renderer.rb index 31b47c4d..d137b0e7 100644 --- a/lib/govuk_tech_docs/tech_docs_html_renderer.rb +++ b/lib/govuk_tech_docs/tech_docs_html_renderer.rb @@ -1,4 +1,6 @@ require "middleman-core/renderers/redcarpet" +require "tmpdir" +require "timeout" module GovukTechDocs class TechDocsHTMLRenderer < Middleman::Renderers::MiddlemanRedcarpetHTML @@ -80,7 +82,9 @@ def table_row(body) end def block_code(text, lang) - if defined?(super) + if lang == "mermaid" + block_diagram(text) + elsif defined?(super) # Post-processing the block_code HTML to implement tabbable code blocks. # # Middleman monkey patches the Middleman::Renderers::MiddlemanRedcarpetHTML @@ -108,5 +112,33 @@ def block_code(text, lang) pre.to_html end end + + def block_diagram(code) + mmdc = "#{__dir__}/../../node_modules/.bin/mmdc" + Dir.mktmpdir do |tmp| + input_path = "#{tmp}/input" + output_path = "#{tmp}/output.svg" + File.open(input_path, "w") { |f| f.write(code) } + ok = exec_with_timeout("#{mmdc} -i #{input_path} -o #{output_path} --theme neutral", 10) + if ok && File.exist?(output_path) + File.read(output_path) + else + %({
#{code})
+ end
+ end
+ end
+
+ def exec_with_timeout(cmd, timeout)
+ pid = Process.spawn(cmd, { %i[err out] => :close, :pgroup => true })
+ begin
+ Timeout.timeout(timeout) do
+ Process.waitpid(pid, 0)
+ $CHILD_STATUS.exitstatus.zero?
+ end
+ rescue Timeout::Error
+ Process.kill(15, -Process.getpgid(pid))
+ false
+ end
+ end
end
end
diff --git a/package.json b/package.json
index dcdc429e..f9b39bdb 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,8 @@
"lint": "standard"
},
"dependencies": {
- "govuk-frontend": "~5.7.1"
+ "govuk-frontend": "~5.7.1",
+ "@mermaid-js/mermaid-cli": "^8.4.8"
},
"devDependencies": {
"standard": "^14.3.4"
diff --git a/spec/features/diagrams_spec.rb b/spec/features/diagrams_spec.rb
new file mode 100644
index 00000000..b1ceeb08
--- /dev/null
+++ b/spec/features/diagrams_spec.rb
@@ -0,0 +1,26 @@
+require "rack/file"
+require "capybara/rspec"
+
+Capybara.app = Rack::File.new("example/build")
+
+RSpec.describe "Diagrams in code blocks" do
+ include Capybara::DSL
+
+ it "generates static SVG from mermaid code blocks" do
+ when_the_site_is_created
+ and_i_visit_the_code_page
+ then_there_is_an_svg_diagram
+ end
+
+ def when_the_site_is_created
+ rebuild_site!
+ end
+
+ def and_i_visit_the_code_page
+ visit "/code.html"
+ end
+
+ def then_there_is_an_svg_diagram
+ expect(page.body).to match '