diff --git a/example/source/code.html.md b/example/source/code.html.md index 438adfec..f042054c 100644 --- a/example/source/code.html.md +++ b/example/source/code.html.md @@ -62,3 +62,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 e2d4f236..1e9bbe52 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 @@ -30,5 +32,41 @@ def table(header, body) ) end + + def block_code(code, lang) + if lang == "mermaid" + block_diagram(code) + else + super + 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}", 2) + 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) + $?.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 a31cca76..75aad3e6 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "lint": "standard" }, "dependencies": { + "@mermaid-js/mermaid-cli": "^8.4.8", "govuk-frontend": "^3.6.0" }, "devDependencies": { 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 '