jekyll-kroki 0.3.3 → 0.4.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 +2 -1
- data/.rubocop.yml +12 -1
- data/README.md +1 -1
- data/jekyll-kroki.gemspec +10 -5
- data/lib/jekyll/kroki/version.rb +1 -1
- data/lib/jekyll/kroki.rb +81 -40
- metadata +60 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50b7f01538196d5f83bb7d6a53c39ec6f4ea3e341081805564e1bafe3e3aa380
|
4
|
+
data.tar.gz: fad323a88f082cc7bfddb35078246569d1b58e9ffa5a391f1344f4b9f55b5304
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcba83e90f609bddd69e368f1089a0a0fc399647cc8801eee6a021e2c2fd9eb80abdc66805e811d41b26fd3738d595b561a1ae883d0037d6d8ea5e1e5aba383e
|
7
|
+
data.tar.gz: 1a425cdfcfc65488e0f8014a3dcefd7486943ad4d91a71d9e28915b4fe84fef357826614a642acb93d91401b8822d270c2dea20547c14a11c9fb81a648943f5b
|
@@ -1,9 +1,10 @@
|
|
1
1
|
{
|
2
2
|
"name": "Ruby",
|
3
|
-
"image": "mcr.microsoft.com/devcontainers/ruby:3.
|
3
|
+
"image": "mcr.microsoft.com/devcontainers/ruby:3.4-bullseye",
|
4
4
|
"customizations": {
|
5
5
|
"vscode": {
|
6
6
|
"extensions": [
|
7
|
+
"GitHub.vscode-github-actions",
|
7
8
|
"streetsidesoftware.code-spell-checker"
|
8
9
|
]
|
9
10
|
}
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,15 @@
|
|
1
|
+
plugins:
|
2
|
+
- rubocop-minitest
|
3
|
+
- rubocop-performance
|
4
|
+
- rubocop-rake
|
5
|
+
|
1
6
|
AllCops:
|
7
|
+
NewCops: enable
|
2
8
|
TargetRubyVersion: 2.6
|
3
9
|
|
10
|
+
Gemspec/DevelopmentDependencies:
|
11
|
+
EnforcedStyle: gemspec
|
12
|
+
|
4
13
|
Style/StringLiterals:
|
5
14
|
Enabled: true
|
6
15
|
EnforcedStyle: double_quotes
|
@@ -11,5 +20,7 @@ Style/StringLiteralsInInterpolation:
|
|
11
20
|
|
12
21
|
Layout/LineLength:
|
13
22
|
Max: 120
|
23
|
+
Metrics/ClassLength:
|
24
|
+
Max: 120
|
14
25
|
Metrics/MethodLength:
|
15
|
-
Max: 14
|
26
|
+
Max: 14
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ When Jekyll builds your site, the `jekyll-kroki` plugin will encode the diagrams
|
|
32
32
|
|
33
33
|

|
34
34
|
|
35
|
-
The site remains
|
35
|
+
The site remains fully static as the images are directly embedded in the HTML files served by Jekyll. Jekyll only depends on the Kroki server - which can also be run locally - during the build stage, and all of the client-side processing that is normally used to render diagrams into images is eliminated.
|
36
36
|
|
37
37
|
### Advantages
|
38
38
|
|
data/jekyll-kroki.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
18
18
|
spec.metadata["source_code_uri"] = spec.homepage
|
19
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
19
20
|
|
20
21
|
# Load the files that are versioned in Git into the RubyGem.
|
21
22
|
spec.files = Dir.chdir(__dir__) do
|
@@ -28,13 +29,17 @@ Gem::Specification.new do |spec|
|
|
28
29
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
30
|
spec.require_paths = ["lib"]
|
30
31
|
|
31
|
-
spec.
|
32
|
-
spec.
|
33
|
-
spec.
|
34
|
-
spec.
|
35
|
-
spec.
|
32
|
+
spec.add_dependency "async", ["~> 2.25"]
|
33
|
+
spec.add_dependency "faraday", ["~> 2.7"]
|
34
|
+
spec.add_dependency "faraday-retry", ["~> 2.2"]
|
35
|
+
spec.add_dependency "httpx", ["~> 1.1"]
|
36
|
+
spec.add_dependency "jekyll", ["~> 4"]
|
37
|
+
spec.add_dependency "nokogiri", ["~> 1.15"]
|
36
38
|
|
37
39
|
spec.add_development_dependency "minitest", ["~> 5.0"]
|
38
40
|
spec.add_development_dependency "rake", ["~> 13.0"]
|
39
41
|
spec.add_development_dependency "rubocop", ["~> 1.21"]
|
42
|
+
spec.add_development_dependency "rubocop-minitest", ["~> 0.38"]
|
43
|
+
spec.add_development_dependency "rubocop-performance", ["~> 1.25"]
|
44
|
+
spec.add_development_dependency "rubocop-rake", ["~> 0.7"]
|
40
45
|
end
|
data/lib/jekyll/kroki/version.rb
CHANGED
data/lib/jekyll/kroki.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require_relative "kroki/version"
|
4
4
|
|
5
|
+
require "async"
|
6
|
+
require "async/semaphore"
|
5
7
|
require "base64"
|
6
8
|
require "faraday"
|
7
9
|
require "faraday/retry"
|
@@ -11,7 +13,7 @@ require "nokogiri"
|
|
11
13
|
require "zlib"
|
12
14
|
|
13
15
|
module Jekyll
|
14
|
-
# Converts diagram descriptions into images using Kroki
|
16
|
+
# Converts diagram descriptions into images using Kroki.
|
15
17
|
class Kroki
|
16
18
|
KROKI_DEFAULT_URL = "https://kroki.io"
|
17
19
|
SUPPORTED_LANGUAGES = %w[actdiag blockdiag bpmn bytefield c4plantuml d2 dbml diagramsnet ditaa erd excalidraw
|
@@ -19,24 +21,21 @@ module Jekyll
|
|
19
21
|
svgbob symbolator tikz umlet vega vegalite wavedrom wireviz].freeze
|
20
22
|
EXPECTED_HTML_TAGS = %w[code div].freeze
|
21
23
|
HTTP_MAX_RETRIES = 3
|
24
|
+
HTTP_RETRY_INTERVAL_BACKOFF_FACTOR = 2
|
25
|
+
HTTP_RETRY_INTERVAL_RANDOMNESS = 0.5
|
26
|
+
HTTP_RETRY_INTERVAL_SECONDS = 0.1
|
27
|
+
HTTP_TIMEOUT_SECONDS = 15
|
28
|
+
MAX_CONCURRENT_DOCS = 8
|
22
29
|
|
23
30
|
class << self
|
24
|
-
# Renders and embeds all diagram descriptions in
|
31
|
+
# Renders and embeds all diagram descriptions in the given Jekyll site using Kroki.
|
25
32
|
#
|
26
|
-
# @param [Jekyll::Site] The Jekyll site to embed diagrams in
|
33
|
+
# @param [Jekyll::Site] The Jekyll site to embed diagrams in.
|
27
34
|
def embed_site(site)
|
28
|
-
# Get the URL of the Kroki instance
|
29
35
|
kroki_url = kroki_url(site.config)
|
30
36
|
connection = setup_connection(kroki_url)
|
31
37
|
|
32
|
-
rendered_diag =
|
33
|
-
(site.pages + site.documents).each do |doc|
|
34
|
-
next unless embeddable?(doc)
|
35
|
-
|
36
|
-
# Render all supported diagram descriptions in the document
|
37
|
-
rendered_diag += embed_doc(connection, doc)
|
38
|
-
end
|
39
|
-
|
38
|
+
rendered_diag = embed_docs_in_site(site, connection)
|
40
39
|
unless rendered_diag.zero?
|
41
40
|
puts "[jekyll-kroki] Rendered #{rendered_diag} diagrams using Kroki instance at '#{kroki_url}'"
|
42
41
|
end
|
@@ -44,37 +43,78 @@ module Jekyll
|
|
44
43
|
exit(e)
|
45
44
|
end
|
46
45
|
|
47
|
-
# Renders
|
46
|
+
# Renders the diagram descriptions in all Jekyll pages and documents in the given Jekyll site. Pages / documents
|
47
|
+
# are rendered concurrently up to the limit defined by MAX_CONCURRENT_DOCS.
|
48
48
|
#
|
49
|
-
# @param [
|
50
|
-
# @param [
|
51
|
-
# @
|
52
|
-
def
|
53
|
-
|
49
|
+
# @param [Jekyll::Site] The Jekyll site to embed diagrams in.
|
50
|
+
# @param [Faraday::Connection] The Faraday connection to use.
|
51
|
+
# @return [Integer] The number of successfully rendered diagrams.
|
52
|
+
def embed_docs_in_site(site, connection)
|
53
|
+
rendered_diag = 0
|
54
|
+
semaphore = Async::Semaphore.new(MAX_CONCURRENT_DOCS)
|
55
|
+
|
56
|
+
Async do |task|
|
57
|
+
tasks = (site.pages + site.documents).map do |doc|
|
58
|
+
next unless embeddable?(doc)
|
59
|
+
|
60
|
+
async_embed_single_doc(task, semaphore, connection, doc)
|
61
|
+
end.compact
|
62
|
+
|
63
|
+
rendered_diag = tasks.sum(&:wait)
|
64
|
+
end
|
65
|
+
|
66
|
+
rendered_diag
|
67
|
+
end
|
68
|
+
|
69
|
+
# Renders the supported diagram descriptions in a single document asynchronously, respecting the concurrency limit
|
70
|
+
# imposed by the provided semaphore.
|
71
|
+
#
|
72
|
+
# @param [Async::Task] The parent async task to spawn a child task from.
|
73
|
+
# @param [Async::Semaphore] A semaphore to limit concurrency.
|
74
|
+
# @param [Faraday::Connection] The Faraday connection to use.
|
75
|
+
# @param [Jekyll::Page, Jekyll::Document] The document to process.
|
76
|
+
# @return [Integer] The number of successfully rendered diagrams.
|
77
|
+
def async_embed_single_doc(task, semaphore, connection, doc)
|
78
|
+
task.async do
|
79
|
+
semaphore.async { embed_single_doc(connection, doc) }.wait
|
80
|
+
rescue StandardError => e
|
81
|
+
warn "[jekyll-kroki] Error rendering diagram: #{e.message}".red
|
82
|
+
0
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Renders the supported diagram descriptions in a single document and embeds them as inline SVGs in the HTML
|
87
|
+
# source.
|
88
|
+
#
|
89
|
+
# @param [Faraday::Connection] The Faraday connection to use.
|
90
|
+
# @param [Jekyll::Page, Jekyll::Document] The document to process.
|
91
|
+
# @return [Integer] The number of successfully rendered diagrams.
|
92
|
+
def embed_single_doc(connection, doc)
|
93
|
+
# Parse the HTML document.
|
54
94
|
parsed_doc = Nokogiri::HTML(doc.output)
|
55
95
|
|
56
96
|
rendered_diag = 0
|
57
97
|
SUPPORTED_LANGUAGES.each do |language|
|
58
98
|
EXPECTED_HTML_TAGS.each do |tag|
|
59
99
|
parsed_doc.css("#{tag}[class~='language-#{language}']").each do |diagram_desc|
|
60
|
-
# Replace the diagram description with the SVG representation rendered by Kroki
|
100
|
+
# Replace the diagram description with the SVG representation rendered by Kroki.
|
61
101
|
diagram_desc.replace(render_diagram(connection, diagram_desc, language))
|
62
102
|
rendered_diag += 1
|
63
103
|
end
|
64
104
|
end
|
65
105
|
end
|
66
106
|
|
67
|
-
# Convert the document back to HTML
|
107
|
+
# Convert the document back to HTML.
|
68
108
|
doc.output = parsed_doc.to_html
|
69
109
|
rendered_diag
|
70
110
|
end
|
71
111
|
|
72
112
|
# Renders a single diagram description using Kroki.
|
73
113
|
#
|
74
|
-
# @param [Faraday::Connection] The Faraday connection to use
|
75
|
-
# @param [String] The diagram description
|
76
|
-
# @param [String] The language of the diagram description
|
77
|
-
# @return [String] The rendered diagram in SVG format
|
114
|
+
# @param [Faraday::Connection] The Faraday connection to use.
|
115
|
+
# @param [String] The diagram description.
|
116
|
+
# @param [String] The language of the diagram description.
|
117
|
+
# @return [String] The rendered diagram in SVG format.
|
78
118
|
def render_diagram(connection, diagram_desc, language)
|
79
119
|
begin
|
80
120
|
response = connection.get("#{language}/svg/#{encode_diagram(diagram_desc.text)}")
|
@@ -94,8 +134,8 @@ module Jekyll
|
|
94
134
|
# Sanitises a rendered diagram. Only <script> elements are removed, which is the most minimal / naive
|
95
135
|
# implementation possible.
|
96
136
|
#
|
97
|
-
# @param [String] The diagram to santise in SVG format
|
98
|
-
# @return [String] The sanitised diagram
|
137
|
+
# @param [String] The diagram to santise in SVG format.
|
138
|
+
# @return [String] The sanitised diagram.
|
99
139
|
def sanitise_diagram(diagram_svg)
|
100
140
|
parsed_svg = Nokogiri::XML(diagram_svg)
|
101
141
|
parsed_svg.xpath('//*[name()="script"]').each(&:remove)
|
@@ -105,21 +145,23 @@ module Jekyll
|
|
105
145
|
# Encodes the diagram into Kroki format using deflate + base64.
|
106
146
|
# See https://docs.kroki.io/kroki/setup/encode-diagram/.
|
107
147
|
#
|
108
|
-
# @param [String, #read] The diagram description to encode
|
109
|
-
# @return [String] The encoded diagram
|
148
|
+
# @param [String, #read] The diagram description to encode.
|
149
|
+
# @return [String] The encoded diagram.
|
110
150
|
def encode_diagram(diagram_desc)
|
111
151
|
Base64.urlsafe_encode64(Zlib.deflate(diagram_desc))
|
112
152
|
end
|
113
153
|
|
114
154
|
# Sets up a new Faraday connection.
|
115
155
|
#
|
116
|
-
# @param [URI::HTTP] The URL of the Kroki instance
|
117
|
-
# @return [Faraday::Connection] The Faraday connection
|
156
|
+
# @param [URI::HTTP] The URL of the Kroki instance.
|
157
|
+
# @return [Faraday::Connection] The Faraday connection.
|
118
158
|
def setup_connection(kroki_url)
|
119
|
-
retry_options = { max: HTTP_MAX_RETRIES, interval:
|
159
|
+
retry_options = { max: HTTP_MAX_RETRIES, interval: HTTP_RETRY_INTERVAL_SECONDS,
|
160
|
+
interval_randomness: HTTP_RETRY_INTERVAL_RANDOMNESS,
|
161
|
+
backoff_factor: HTTP_RETRY_INTERVAL_BACKOFF_FACTOR,
|
120
162
|
exceptions: [Faraday::RequestTimeoutError, Faraday::ServerError] }
|
121
163
|
|
122
|
-
Faraday.new(url: kroki_url, request: { timeout:
|
164
|
+
Faraday.new(url: kroki_url, request: { timeout: HTTP_TIMEOUT_SECONDS }) do |builder|
|
123
165
|
builder.adapter :httpx, persistent: true
|
124
166
|
builder.request :retry, retry_options
|
125
167
|
builder.response :json, content_type: /\bjson$/
|
@@ -129,23 +171,22 @@ module Jekyll
|
|
129
171
|
|
130
172
|
# Gets the URL of the Kroki instance to use for rendering diagrams.
|
131
173
|
#
|
132
|
-
# @param The Jekyll site configuration
|
133
|
-
# @return [URI::HTTP] The URL of the Kroki instance
|
174
|
+
# @param The Jekyll site configuration.
|
175
|
+
# @return [URI::HTTP] The URL of the Kroki instance.
|
134
176
|
def kroki_url(config)
|
135
177
|
if config.key?("kroki") && config["kroki"].key?("url")
|
136
178
|
url = config["kroki"]["url"]
|
137
179
|
raise TypeError, "'url' is not a valid HTTP URL" unless URI.parse(url).is_a?(URI::HTTP)
|
138
|
-
|
139
|
-
URI(url)
|
140
180
|
else
|
141
|
-
|
181
|
+
url = KROKI_DEFAULT_URL
|
142
182
|
end
|
183
|
+
URI(url)
|
143
184
|
end
|
144
185
|
|
145
186
|
# Determines whether a document may contain embeddable diagram descriptions - it is in HTML format and is either
|
146
187
|
# a Jekyll::Page or writeable Jekyll::Document.
|
147
188
|
#
|
148
|
-
# @param [Jekyll::Page or Jekyll::Document] The document to check for
|
189
|
+
# @param [Jekyll::Page or Jekyll::Document] The document to check for embeddability.
|
149
190
|
def embeddable?(doc)
|
150
191
|
doc.output_ext == ".html" && (doc.is_a?(Jekyll::Page) || doc.write?)
|
151
192
|
end
|
@@ -153,7 +194,7 @@ module Jekyll
|
|
153
194
|
# Exits the Jekyll process without returning a stack trace. This method does not return because the process is
|
154
195
|
# abruptly terminated.
|
155
196
|
#
|
156
|
-
# @param [StandardError] The error to display in the termination message
|
197
|
+
# @param [StandardError] The error to display in the termination message.
|
157
198
|
# @param [int] The caller index to display in the termination message. The default index is 1, which means the
|
158
199
|
# calling method. To specify the calling method's caller, pass in 2.
|
159
200
|
#
|
@@ -162,7 +203,7 @@ module Jekyll
|
|
162
203
|
raise error
|
163
204
|
rescue StandardError => e
|
164
205
|
file, line_number, caller = e.backtrace[caller_index].split(":")
|
165
|
-
caller = caller.tr("
|
206
|
+
caller = caller.tr("", "'")
|
166
207
|
warn %([jekyll-kroki] "#{error.message}" #{caller} on line #{line_number} of #{file}).red
|
167
208
|
exec "exit 1"
|
168
209
|
end
|
metadata
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-kroki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felix van Oost
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: async
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '2.25'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '2.25'
|
13
26
|
- !ruby/object:Gem::Dependency
|
14
27
|
name: faraday
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,10 +135,51 @@ dependencies:
|
|
122
135
|
- - "~>"
|
123
136
|
- !ruby/object:Gem::Version
|
124
137
|
version: '1.21'
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: rubocop-minitest
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - "~>"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0.38'
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0.38'
|
152
|
+
- !ruby/object:Gem::Dependency
|
153
|
+
name: rubocop-performance
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - "~>"
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '1.25'
|
159
|
+
type: :development
|
160
|
+
prerelease: false
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - "~>"
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '1.25'
|
166
|
+
- !ruby/object:Gem::Dependency
|
167
|
+
name: rubocop-rake
|
168
|
+
requirement: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - "~>"
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0.7'
|
173
|
+
type: :development
|
174
|
+
prerelease: false
|
175
|
+
version_requirements: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - "~>"
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0.7'
|
125
180
|
description: |-
|
126
181
|
A Jekyll plugin to convert diagram descriptions written in over 25 popular diagram scripting
|
127
182
|
languages into images using Kroki
|
128
|
-
email:
|
129
183
|
executables: []
|
130
184
|
extensions: []
|
131
185
|
extra_rdoc_files: []
|
@@ -145,7 +199,7 @@ licenses:
|
|
145
199
|
metadata:
|
146
200
|
homepage_uri: https://github.com/felixvanoost/jekyll-kroki
|
147
201
|
source_code_uri: https://github.com/felixvanoost/jekyll-kroki
|
148
|
-
|
202
|
+
rubygems_mfa_required: 'true'
|
149
203
|
rdoc_options: []
|
150
204
|
require_paths:
|
151
205
|
- lib
|
@@ -160,8 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
160
214
|
- !ruby/object:Gem::Version
|
161
215
|
version: '0'
|
162
216
|
requirements: []
|
163
|
-
rubygems_version: 3.
|
164
|
-
signing_key:
|
217
|
+
rubygems_version: 3.6.7
|
165
218
|
specification_version: 4
|
166
219
|
summary: A Jekyll plugin to convert diagram descriptions into images using Kroki
|
167
220
|
test_files: []
|