jekyll-kroki 0.3.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 660d6cc7decf15b06f8b0e524f69114e2ac65af4fe98d3b4cf77a7fb57412a80
4
- data.tar.gz: b8b7aecf4b9ca6739f92317e9a9615be2b4cf2a81e19c45300ca4046f25829f7
3
+ metadata.gz: 50b7f01538196d5f83bb7d6a53c39ec6f4ea3e341081805564e1bafe3e3aa380
4
+ data.tar.gz: fad323a88f082cc7bfddb35078246569d1b58e9ffa5a391f1344f4b9f55b5304
5
5
  SHA512:
6
- metadata.gz: 493bf16248a87c91ea3cc6281a384b0fecdef5bbe39f1d7f9cc3e9b2b65a4b897069470a2ce18f6f8654d237377bc9be3a59beaa6c8f67d92a1d78aa4e749e14
7
- data.tar.gz: 6819c03ec7107754bad0c4d55fe355a9b209fee2f33bca863a8aacd3bd9a7ab8bf1f6b476759fb3cef61a33590d24fa39f2f8f5ee32dec1a3290d0e9594b47f5
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.0-bullseye",
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
  ![sample-diagram](https://github.com/felixvanoost/jekyll-kroki/assets/10233016/244d2ec4-b09b-4a5f-8164-3851574c3dd2)
34
34
 
35
- The site remains truly 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.
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.add_runtime_dependency "faraday", ["~> 2.7"]
32
- spec.add_runtime_dependency "faraday-retry", ["~> 2.2"]
33
- spec.add_runtime_dependency "httpx", ["~> 1.1"]
34
- spec.add_runtime_dependency "jekyll", ["~> 4"]
35
- spec.add_runtime_dependency "nokogiri", ["~> 1.15"]
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  class Kroki
5
- VERSION = "0.3.2"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
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 a Jekyll site using Kroki.
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 = 0
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 all supported diagram descriptions in a document and embeds them as inline SVGs in the HTML source.
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 [Faraday::Connection] The Faraday connection to use
50
- # @param [Nokogiri::HTML4::Document] The parsed HTML document
51
- # @param [Integer] The number of rendered diagrams
52
- def embed_doc(connection, doc)
53
- # Parse the HTML document
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: 0.1, interval_randomness: 0.5, backoff_factor: 2,
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: 5 }) do |builder|
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
- URI(KROKI_DEFAULT_URL)
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 embedability
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.3.2
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: 2024-02-27 00:00:00.000000000 Z
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
- post_install_message:
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.2.33
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: []