jekyll-kroki 0.6.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer/devcontainer.json +1 -1
- data/.vscode/settings.json +5 -1
- data/README.md +3 -3
- data/lib/jekyll/kroki/version.rb +1 -1
- data/lib/jekyll/kroki.rb +42 -75
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b092bf9850f67c8e4bd7bd0d454aeeaa274b372f5ae8976f6fb05cab3f8372a
|
|
4
|
+
data.tar.gz: 1c8b189b173440b3694156281c41915c204d55b0d35d46bc866a51b0287ea73e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bc181d7f3288979cb09d8aeae9f915de5099ecbe2b0d6b3c56f941935e3d7ea264051b7fa1c0c850f476930613bb45a2723f623f3b97867b65283e752466f16a
|
|
7
|
+
data.tar.gz: 9e46a87b36b24fc80e2a9349044b87c9c0251f59a8b1060ac498b00bf7a012dbf70415be23752988684ba234de27865526db15ea7552a764d6b4953e5fd7f975
|
data/.vscode/settings.json
CHANGED
data/README.md
CHANGED
|
@@ -45,11 +45,11 @@ Instead of using Liquid tags, `jekyll-kroki` leverages the same Markdown fenced
|
|
|
45
45
|
|
|
46
46
|
#### Seamless GitLab integration
|
|
47
47
|
|
|
48
|
-
Self-managed GitLab instances can additionally enable the [Kroki integration](https://docs.gitlab.com/ee/administration/integration/kroki.html), which adds support for all the
|
|
48
|
+
Self-managed or dedicated GitLab instances can additionally enable the [Kroki integration](https://docs.gitlab.com/ee/administration/integration/kroki.html), which adds support for all the diagram scripting languages used by `jekyll-kroki`. Furthermore, by pointing both GitLab and `jekyll-kroki` to the same Kroki instance, you can guarantee that diagrams are generated using identical versions of the diagram libraries.
|
|
49
49
|
|
|
50
50
|
#### Speed
|
|
51
51
|
|
|
52
|
-
The server-side nature of Kroki means that you don't have to deal with installing or updating any diagram library dependencies on your machine. Jekyll sites that are generated in CI/CD pipelines can bypass these steps and will thus build faster.
|
|
52
|
+
The server-side nature of Kroki means that you don't have to deal with installing or updating any diagram library dependencies on your machine. Jekyll sites that are generated in CI/CD pipelines can bypass these setup steps and will thus build faster. `jekyll-kroki` is built with performance in mind to minimise the build time of even the largest sites with thousands of diagrams.
|
|
53
53
|
|
|
54
54
|
#### Flexibility
|
|
55
55
|
|
|
@@ -78,7 +78,7 @@ kroki:
|
|
|
78
78
|
|
|
79
79
|
### Security
|
|
80
80
|
|
|
81
|
-
Embedding
|
|
81
|
+
Embedding untrusted SVGs directly in HTML files can introduce security risks. Use a Kroki instance that you trust, or consider hosting your own local/private instance. For added security, you can configure a [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) by adding custom Webrick headers in your Jekyll `_config.yml` file:
|
|
82
82
|
|
|
83
83
|
```yaml
|
|
84
84
|
webrick:
|
data/lib/jekyll/kroki/version.rb
CHANGED
data/lib/jekyll/kroki.rb
CHANGED
|
@@ -18,13 +18,17 @@ require "zlib"
|
|
|
18
18
|
module Jekyll
|
|
19
19
|
# Converts diagram descriptions into images using Kroki.
|
|
20
20
|
class Kroki
|
|
21
|
+
SUPPORTED_LANGUAGES = %w[actdiag blockdiag bpmn bytefield c4plantuml d2 dbml diagramsnet ditaa erd excalidraw
|
|
22
|
+
graphviz mermaid nomnoml nwdiag packetdiag pikchr plantuml rackdiag seqdiag structurizr
|
|
23
|
+
svgbob symbolator tikz umlet vega vegalite wavedrom wireviz].freeze
|
|
21
24
|
EXPECTED_HTML_TAGS = %w[code div].freeze
|
|
25
|
+
DIAGRAM_SELECTOR = SUPPORTED_LANGUAGES.flat_map do |language|
|
|
26
|
+
EXPECTED_HTML_TAGS.map { |tag| "#{tag}[class~='language-#{language}']" }
|
|
27
|
+
end.join(", ").freeze
|
|
28
|
+
|
|
22
29
|
HTTP_RETRY_INTERVAL_BACKOFF_FACTOR = 2
|
|
23
30
|
HTTP_RETRY_INTERVAL_RANDOMNESS = 0.5
|
|
24
31
|
HTTP_RETRY_INTERVAL = 0.1
|
|
25
|
-
SUPPORTED_LANGUAGES = %w[actdiag blockdiag bpmn bytefield c4plantuml d2 dbml diagramsnet ditaa erd excalidraw
|
|
26
|
-
graphviz mermaid nomnoml nwdiag packetdiag pikchr plantuml rackdiag seqdiag structurizr
|
|
27
|
-
svgbob symbolator tikz umlet vega vegalite wavedrom wireviz].freeze
|
|
28
32
|
|
|
29
33
|
@diagram_cache = Concurrent::Map.new
|
|
30
34
|
|
|
@@ -37,11 +41,11 @@ module Jekyll
|
|
|
37
41
|
connection = setup_connection(config.kroki_url, config.http_retries, config.http_timeout)
|
|
38
42
|
|
|
39
43
|
rendered_diag = embed_docs_in_site(site, connection, config.max_concurrent_docs)
|
|
40
|
-
unless rendered_diag.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
return unless rendered_diag.positive?
|
|
45
|
+
|
|
46
|
+
Jekyll.logger.info(
|
|
47
|
+
"[jekyll-kroki] Rendered #{rendered_diag} diagrams using Kroki instance at '#{config.kroki_url}'"
|
|
48
|
+
)
|
|
45
49
|
end
|
|
46
50
|
|
|
47
51
|
# Renders the diagram descriptions in all Jekyll pages and documents in the given Jekyll site. Pages / documents
|
|
@@ -52,63 +56,42 @@ module Jekyll
|
|
|
52
56
|
# @param [Integer] The maximum number of documents to render concurrently.
|
|
53
57
|
# @return [Integer] The number of successfully rendered diagrams.
|
|
54
58
|
def embed_docs_in_site(site, connection, max_concurrent_docs)
|
|
55
|
-
rendered_diag = 0
|
|
56
59
|
semaphore = Async::Semaphore.new(max_concurrent_docs)
|
|
57
60
|
|
|
58
|
-
Async do
|
|
59
|
-
|
|
61
|
+
Async do
|
|
62
|
+
(site.pages + site.documents).filter_map do |doc|
|
|
60
63
|
next unless embeddable?(doc)
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# Renders the supported diagram descriptions in a single document. Multiple documents can be rendered concurrently
|
|
72
|
-
# up to the limit imposed by the given semaphore.
|
|
73
|
-
#
|
|
74
|
-
# @param [Async::Task] The parent async task to spawn a child task from.
|
|
75
|
-
# @param [Async::Semaphore] A semaphore to limit concurrency.
|
|
76
|
-
# @param [Faraday::Connection] The Faraday connection to use.
|
|
77
|
-
# @param [Jekyll::Page, Jekyll::Document] The document to process.
|
|
78
|
-
# @return [Integer] The number of successfully rendered diagrams.
|
|
79
|
-
def async_embed_single_doc(task, semaphore, connection, doc)
|
|
80
|
-
task.async do
|
|
81
|
-
semaphore.async { embed_single_doc(connection, doc) }.wait
|
|
82
|
-
rescue StandardError => e
|
|
83
|
-
warn "[jekyll-kroki] Error rendering diagram: #{e.message}".red
|
|
84
|
-
0
|
|
85
|
-
end
|
|
65
|
+
semaphore.async do
|
|
66
|
+
embed_single_doc(connection, doc)
|
|
67
|
+
rescue StandardError => e
|
|
68
|
+
Jekyll.logger.error "[jekyll-kroki] Failed to render diagram in '#{doc.relative_path}': #{e.message}"
|
|
69
|
+
0
|
|
70
|
+
end
|
|
71
|
+
end.sum(&:wait)
|
|
72
|
+
end.wait
|
|
86
73
|
end
|
|
87
74
|
|
|
88
75
|
# Renders the supported diagram descriptions in a single document sequentially and embeds them as inline SVGs in
|
|
89
|
-
# the HTML source.
|
|
76
|
+
# the HTML source. Returns without modifying the document if no supported diagram descriptions are found.
|
|
90
77
|
#
|
|
91
78
|
# @param [Faraday::Connection] The Faraday connection to use.
|
|
92
79
|
# @param [Jekyll::Page, Jekyll::Document] The document to process.
|
|
93
80
|
# @return [Integer] The number of successfully rendered diagrams.
|
|
94
81
|
def embed_single_doc(connection, doc)
|
|
95
|
-
# Parse the HTML document.
|
|
96
82
|
parsed_doc = Nokogiri::HTML(doc.output)
|
|
83
|
+
nodes = parsed_doc.css(DIAGRAM_SELECTOR)
|
|
84
|
+
return 0 if nodes.empty?
|
|
97
85
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
# Replace the diagram description with the SVG representation rendered by Kroki.
|
|
103
|
-
diagram_desc.replace(render_diagram(connection, diagram_desc, language))
|
|
104
|
-
rendered_diag += 1
|
|
105
|
-
end
|
|
106
|
-
end
|
|
86
|
+
nodes.each do |node|
|
|
87
|
+
# Extract the diagram language from the class list.
|
|
88
|
+
language = node["class"].split.grep(/\Alanguage-/).first.delete_prefix("language-")
|
|
89
|
+
node.replace(render_diagram(connection, node.text, language))
|
|
107
90
|
end
|
|
108
91
|
|
|
109
92
|
# Convert the document back to HTML.
|
|
110
93
|
doc.output = parsed_doc.to_html
|
|
111
|
-
|
|
94
|
+
nodes.size
|
|
112
95
|
end
|
|
113
96
|
|
|
114
97
|
# Renders a single diagram description using Kroki. The rendered diagram is cached to avoid redundant HTTP
|
|
@@ -118,17 +101,15 @@ module Jekyll
|
|
|
118
101
|
# @param [String] The diagram description.
|
|
119
102
|
# @param [String] The language of the diagram description.
|
|
120
103
|
# @return [String] The rendered diagram in SVG format.
|
|
121
|
-
def render_diagram(connection,
|
|
122
|
-
diagram_text = diagram_desc.text
|
|
104
|
+
def render_diagram(connection, diagram_text, language)
|
|
123
105
|
cache_key = "#{language}:#{Digest::SHA1.hexdigest(diagram_text)}"
|
|
124
106
|
@diagram_cache.compute_if_absent(cache_key) do
|
|
125
|
-
|
|
126
|
-
response = connection.get("#{language}/svg/#{encode_diagram(diagram_text)}")
|
|
127
|
-
rescue Faraday::Error => e
|
|
128
|
-
raise e.message
|
|
129
|
-
end
|
|
107
|
+
response = connection.get("#{language}/svg/#{encode_diagram(diagram_text)}")
|
|
130
108
|
validate_content_type(response)
|
|
131
109
|
sanitise_diagram(response.body)
|
|
110
|
+
rescue Faraday::BadRequestError => e
|
|
111
|
+
kroki_message = e.response_body.to_s.strip
|
|
112
|
+
raise e, (kroki_message.empty? ? e.message : kroki_message)
|
|
132
113
|
end
|
|
133
114
|
end
|
|
134
115
|
|
|
@@ -140,14 +121,14 @@ module Jekyll
|
|
|
140
121
|
returned_content_type = response.headers[:content_type]
|
|
141
122
|
return if returned_content_type == expected_content_type
|
|
142
123
|
|
|
143
|
-
raise "Kroki returned an incorrect content type: " \
|
|
124
|
+
raise "[jekyll-kroki] Kroki returned an incorrect content type: " \
|
|
144
125
|
"expected '#{expected_content_type}', received '#{returned_content_type}'"
|
|
145
126
|
end
|
|
146
127
|
|
|
147
128
|
# Sanitises a rendered diagram. Only <script> elements are removed, which is the most minimal / naive
|
|
148
129
|
# implementation possible.
|
|
149
130
|
#
|
|
150
|
-
# @param [String] The diagram to
|
|
131
|
+
# @param [String] The diagram to sanitise in SVG format.
|
|
151
132
|
# @return [String] The sanitised diagram.
|
|
152
133
|
def sanitise_diagram(diagram_svg)
|
|
153
134
|
parsed_svg = Nokogiri::XML(diagram_svg)
|
|
@@ -158,10 +139,10 @@ module Jekyll
|
|
|
158
139
|
# Encodes the diagram into Kroki format using deflate + base64.
|
|
159
140
|
# See https://docs.kroki.io/kroki/setup/encode-diagram/.
|
|
160
141
|
#
|
|
161
|
-
# @param [String
|
|
142
|
+
# @param [String] The diagram description to encode.
|
|
162
143
|
# @return [String] The encoded diagram.
|
|
163
144
|
def encode_diagram(diagram_desc)
|
|
164
|
-
Base64.urlsafe_encode64(Zlib.deflate(diagram_desc))
|
|
145
|
+
Base64.urlsafe_encode64(Zlib.deflate(diagram_desc, Zlib::BEST_COMPRESSION))
|
|
165
146
|
end
|
|
166
147
|
|
|
167
148
|
# Sets up a new Faraday connection.
|
|
@@ -191,27 +172,13 @@ module Jekyll
|
|
|
191
172
|
def embeddable?(doc)
|
|
192
173
|
doc.output_ext == ".html" && (doc.is_a?(Jekyll::Page) || doc.write?)
|
|
193
174
|
end
|
|
194
|
-
|
|
195
|
-
# Exits the Jekyll process without returning a stack trace. This method does not return because the process is
|
|
196
|
-
# abruptly terminated.
|
|
197
|
-
#
|
|
198
|
-
# @param [StandardError] The error to display in the termination message.
|
|
199
|
-
# @param [int] The caller index to display in the termination message. The default index is 1, which means the
|
|
200
|
-
# calling method. To specify the calling method's caller, pass in 2.
|
|
201
|
-
#
|
|
202
|
-
# Source: https://www.mslinn.com/ruby/2200-crash-exit.html
|
|
203
|
-
def fatal_error(error, caller_index = 1)
|
|
204
|
-
raise error
|
|
205
|
-
rescue StandardError => e
|
|
206
|
-
file, line_number, caller = e.backtrace[caller_index].split(":")
|
|
207
|
-
caller = caller.tr("", "'")
|
|
208
|
-
warn %([jekyll-kroki] "#{error.message}" #{caller} on line #{line_number} of #{file}).red
|
|
209
|
-
exec "exit 1"
|
|
210
|
-
end
|
|
211
175
|
end
|
|
212
176
|
end
|
|
213
177
|
end
|
|
214
178
|
|
|
215
179
|
Jekyll::Hooks.register :site, :post_render do |site|
|
|
216
180
|
Jekyll::Kroki.embed_site(site)
|
|
181
|
+
rescue StandardError => e
|
|
182
|
+
Jekyll.logger.error "[jekyll-kroki] #{e.class}: #{e.message}"
|
|
183
|
+
raise
|
|
217
184
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-kroki
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Felix van Oost
|
|
@@ -227,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
227
227
|
- !ruby/object:Gem::Version
|
|
228
228
|
version: '0'
|
|
229
229
|
requirements: []
|
|
230
|
-
rubygems_version:
|
|
230
|
+
rubygems_version: 4.0.10
|
|
231
231
|
specification_version: 4
|
|
232
232
|
summary: A Jekyll plugin to convert diagram descriptions into images using Kroki
|
|
233
233
|
test_files: []
|