jekyll-mermaid-prebuild 0.5.0 → 0.6.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: f20707d63b254ecd0493fd044b7bb413476f2f5908bfd5e1f8aaeab4a7c4af88
4
- data.tar.gz: c145bf64bedfb029cdb43f8c513dd302c2f1be8216c42b1c1f17d560244e8f5f
3
+ metadata.gz: 1a6f44826b0694bbba4cea5d5b0272301e8909003a9b182eb340042c80454ebb
4
+ data.tar.gz: 9f632d879d948c8030edc5365bc06bc75bd36809da017fd35c6f20ee5b252b0a
5
5
  SHA512:
6
- metadata.gz: c076fb8bbb8c812e83a04cd985226e909d9c9edf0f4d49275abfb357a3f66ca2b3db3c5a1dab7486e3bbb29eb5ca0cf8ec61e6d4d617a28203a2dad8b9c7082d
7
- data.tar.gz: ebe336837f347ed991aeafe75891e6cc4f47005ea8516a344a2e4c55d63144324e866c419db79addf6a2ded2f7749cf762638ec09c9d4fe1f1bef93d7b500709
6
+ metadata.gz: 88b628146a89abbc660bda67b254653a1e77b74b63d55c4ba882a2abe600804129fe29405df0a720457bfbbcbf19b14e7d7841fea322835274deffdcd21c3e67
7
+ data.tar.gz: 3ce40596559ad01778c875cf599b693f42ee1477c70ab178f0f94ff4e735bbe1e1392d68430f2c3985b61ca82b1d8851f9e292715a501a10faf1c640d9dd001e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.0](https://github.com/Texarkanine/jekyll-mermaid-prebuild/compare/v0.5.0...v0.6.0) (2026-07-19)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **test:** add Mutant mutation testing with 100% kill ([#44](https://github.com/Texarkanine/jekyll-mermaid-prebuild/issues/44))
9
+
10
+ ### Features
11
+
12
+ * **test:** add Mutant mutation testing with 100% kill ([#44](https://github.com/Texarkanine/jekyll-mermaid-prebuild/issues/44)) ([e86a82a](https://github.com/Texarkanine/jekyll-mermaid-prebuild/commit/e86a82a6eba5dc0fc037ff0f3451d12708a4f629))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **deps-dev:** migrate SimpleCov add_filter to skip ([#41](https://github.com/Texarkanine/jekyll-mermaid-prebuild/issues/41)) ([#42](https://github.com/Texarkanine/jekyll-mermaid-prebuild/issues/42)) ([bc068f0](https://github.com/Texarkanine/jekyll-mermaid-prebuild/commit/bc068f0268e2d974a061a1474d0973306d860f8e))
18
+
3
19
  ## [0.5.0](https://github.com/Texarkanine/jekyll-mermaid-prebuild/compare/v0.4.0...v0.5.0) (2026-03-22)
4
20
 
5
21
 
@@ -35,11 +35,13 @@ Gem::Specification.new do |spec|
35
35
  spec.add_dependency "jekyll", ">= 4.0", "< 5.0"
36
36
 
37
37
  # Development dependencies
38
+ spec.add_development_dependency "mutant", "~> 0.16"
39
+ spec.add_development_dependency "mutant-rspec", "~> 0.16"
38
40
  spec.add_development_dependency "rake", "~> 13.3"
39
41
  spec.add_development_dependency "rspec", "~> 3.13"
40
42
  spec.add_development_dependency "rubocop", "~> 1.81"
41
43
  spec.add_development_dependency "rubocop-rake", "~> 0.7"
42
44
  spec.add_development_dependency "rubocop-rspec", "~> 3.8"
43
- spec.add_development_dependency "simplecov", "~> 0.22"
44
- spec.add_development_dependency "simplecov-cobertura", "~> 3.1"
45
+ spec.add_development_dependency "simplecov", "~> 1.0"
46
+ spec.add_development_dependency "simplecov-cobertura", "~> 4.0"
45
47
  end
@@ -50,8 +50,6 @@ module JekyllMermaidPrebuild
50
50
  CACHE_DIR
51
51
  end
52
52
 
53
- private
54
-
55
53
  # Parse the prefers-color-scheme block (see PREFERS_COLOR_SCHEME_YAML_KEY) from a Hash only
56
54
  # (mode + optional background-color map). Non-Hash values fall back to :light and default
57
55
  # backgrounds with a warning.
@@ -59,40 +57,27 @@ module JekyllMermaidPrebuild
59
57
  # @param value [Object] raw site config value
60
58
  # @return [void]
61
59
  def parse_prefers_color_scheme(value)
62
- unless value.is_a?(Hash)
63
- @prefers_color_scheme = DEFAULT_PREFERS_COLOR_SCHEME_MODE
64
- @chart_background_light = finalize_background(DEFAULT_CHART_BG_LIGHT)
65
- @chart_background_dark = finalize_background(DEFAULT_CHART_BG_DARK)
66
- unless value.nil?
67
- Jekyll.logger.warn(
68
- "MermaidPrebuild:",
69
- "Invalid #{PREFERS_COLOR_SCHEME_YAML_KEY} (expected a Hash); " \
70
- "using light mode and default backgrounds"
71
- )
72
- end
73
- return
60
+ if !value.nil? && !value.is_a?(Hash)
61
+ Jekyll.logger.warn(
62
+ "MermaidPrebuild:",
63
+ "Invalid #{PREFERS_COLOR_SCHEME_YAML_KEY} (expected a Hash); " \
64
+ "using light mode and default backgrounds"
65
+ )
74
66
  end
75
67
 
76
68
  mode_raw = config_hash_fetch(value, "mode")
77
69
  @prefers_color_scheme = normalize_prefers_mode(mode_raw)
78
70
 
79
71
  bg_container = config_hash_fetch(value, BACKGROUND_COLOR_YAML_KEY)
80
- if bg_container.is_a?(Hash)
81
- light_raw = config_hash_fetch(bg_container, "light")
82
- dark_raw = config_hash_fetch(bg_container, "dark")
83
- @chart_background_light = coerce_chart_background(light_raw, DEFAULT_CHART_BG_LIGHT, "light")
84
- @chart_background_dark = coerce_chart_background(dark_raw, DEFAULT_CHART_BG_DARK, "dark")
85
- else
86
- @chart_background_light = finalize_background(DEFAULT_CHART_BG_LIGHT)
87
- @chart_background_dark = finalize_background(DEFAULT_CHART_BG_DARK)
88
- end
72
+ light_raw = config_hash_fetch(bg_container, "light")
73
+ dark_raw = config_hash_fetch(bg_container, "dark")
74
+ @chart_background_light = coerce_chart_background(light_raw, DEFAULT_CHART_BG_LIGHT, "light")
75
+ @chart_background_dark = coerce_chart_background(dark_raw, DEFAULT_CHART_BG_DARK, "dark")
89
76
  end
90
77
 
91
78
  # @param raw [Object]
92
79
  # @return [Symbol] :light, :dark, or :auto
93
80
  def normalize_prefers_mode(raw)
94
- return DEFAULT_PREFERS_COLOR_SCHEME_MODE if raw.nil?
95
-
96
81
  s = raw.to_s.strip.downcase
97
82
  return DEFAULT_PREFERS_COLOR_SCHEME_MODE if s.empty?
98
83
 
@@ -117,10 +102,7 @@ module JekyllMermaidPrebuild
117
102
  def config_hash_fetch(hash, key)
118
103
  return nil unless hash.is_a?(Hash)
119
104
 
120
- return hash[key] if hash.key?(key)
121
- return hash[key.to_sym] if hash.key?(key.to_sym)
122
-
123
- nil
105
+ hash.fetch(key) { hash.fetch(key.to_sym, nil) }
124
106
  end
125
107
 
126
108
  # @param value [Object] raw color string or nil (use default)
@@ -159,7 +141,7 @@ module JekyllMermaidPrebuild
159
141
  # @param value [String]
160
142
  # @return [String] frozen copy
161
143
  def finalize_background(value)
162
- value.to_s.freeze
144
+ value.to_s.dup.freeze
163
145
  end
164
146
 
165
147
  # Returns a frozen Hash of diagram type (string) => boolean. Non-hash values are rejected → {}.
@@ -169,7 +151,7 @@ module JekyllMermaidPrebuild
169
151
  def parse_emoji_width_compensation(value)
170
152
  return {}.freeze unless value.is_a?(Hash)
171
153
 
172
- result = value.transform_keys(&:to_s).transform_values { |v| [true, false].include?(v) ? v : false }
154
+ result = value.transform_keys(&:to_s).transform_values { |v| v == true }
173
155
  result.freeze
174
156
  end
175
157
 
@@ -179,18 +161,15 @@ module JekyllMermaidPrebuild
179
161
  dir = dir.strip
180
162
  return DEFAULT_OUTPUT_DIR if dir.empty?
181
163
 
182
- dir.gsub(%r{^/+|/+$}, "")
164
+ dir.split("/").reject(&:empty?).join("/")
183
165
  end
184
166
 
185
167
  # @param value [Object] raw config (numeric or off)
186
168
  # @return [Numeric] non-negative padding in SVG user units; 0 means disabled
187
169
  def parse_edge_label_padding(value)
188
- return 0 if value.nil? || value == false
189
-
190
- num = value.is_a?(Numeric) ? value : nil
191
- return 0 unless num
170
+ return 0 unless value.is_a?(Numeric)
192
171
 
193
- num.negative? ? 0 : num
172
+ value.negative? ? 0 : value
194
173
  end
195
174
  end
196
175
  end
@@ -5,13 +5,11 @@ require "digest"
5
5
  module JekyllMermaidPrebuild
6
6
  # Computes content-based digests for caching
7
7
  module DigestCalculator
8
- module_function
9
-
10
8
  # Generate a short digest for mermaid content
11
9
  #
12
10
  # @param content [String] mermaid diagram source
13
11
  # @return [String] 8-character hex digest
14
- def content_digest(content)
12
+ def self.content_digest(content)
15
13
  Digest::MD5.hexdigest(content)[0, 8]
16
14
  end
17
15
  end
@@ -4,8 +4,6 @@ module JekyllMermaidPrebuild
4
4
  # Stateless module: compensates for headless Chromium emoji width undermeasurement
5
5
  # by appending &nbsp; padding to emoji-containing node labels before mmdc renders.
6
6
  module EmojiCompensator
7
- module_function
8
-
9
7
  # Match a single Extended_Pictographic codepoint (emoji).
10
8
  EMOJI_RE = /\p{Extended_Pictographic}/
11
9
 
@@ -15,28 +13,22 @@ module JekyllMermaidPrebuild
15
13
  #
16
14
  # @param mermaid_source [String] raw Mermaid diagram source
17
15
  # @return [String, nil] diagram type keyword (e.g. "flowchart", "sequenceDiagram") or nil
18
- def detect_diagram_type(mermaid_source)
19
- return nil if !mermaid_source || mermaid_source.strip.empty?
16
+ def self.detect_diagram_type(mermaid_source)
17
+ return nil unless mermaid_source
20
18
 
21
19
  in_frontmatter = false
22
- frontmatter_delim_count = 0
23
20
 
24
21
  mermaid_source.each_line do |line|
25
22
  stripped = line.strip
26
- # Track YAML frontmatter
27
23
  if stripped == "---"
28
- frontmatter_delim_count += 1
29
- in_frontmatter = frontmatter_delim_count.odd?
24
+ in_frontmatter = !in_frontmatter
30
25
  next
31
26
  end
32
27
  next if in_frontmatter
33
28
  next if stripped.empty?
34
29
  next if stripped.start_with?("%%")
35
30
 
36
- # First token of first non-skipped line
37
- token = stripped.split(/\s+/, 2).first
38
- return nil if token.nil? || token.empty?
39
-
31
+ token = stripped[/\A\S+/]
40
32
  return token == "graph" ? "flowchart" : token
41
33
  end
42
34
 
@@ -49,10 +41,10 @@ module JekyllMermaidPrebuild
49
41
  # @param mermaid_source [String] Mermaid diagram source
50
42
  # @param diagram_type [String] result of detect_diagram_type
51
43
  # @return [String] possibly modified source
52
- def compensate(mermaid_source, diagram_type)
53
- return mermaid_source if diagram_type != "flowchart"
44
+ def self.compensate(mermaid_source, diagram_type)
45
+ return compensate_flowchart_labels(mermaid_source) if diagram_type == "flowchart"
54
46
 
55
- compensate_flowchart_labels(mermaid_source)
47
+ mermaid_source
56
48
  end
57
49
 
58
50
  BR_RE = %r{(<br\s*/?>)}i
@@ -62,7 +54,7 @@ module JekyllMermaidPrebuild
62
54
  #
63
55
  # @param text [String]
64
56
  # @return [Integer]
65
- def count_emoji(text)
57
+ def self.count_emoji(text)
66
58
  text.scan(EMOJI_RE).length
67
59
  end
68
60
 
@@ -71,46 +63,48 @@ module JekyllMermaidPrebuild
71
63
  #
72
64
  # @param text [String]
73
65
  # @return [Integer]
74
- def visual_length(text)
66
+ def self.visual_length(text)
75
67
  text.length + count_emoji(text)
76
68
  end
77
69
 
78
70
  # Pad the longest line in a label if it contains emoji.
79
71
  # Splits on <br/> variants, finds the visually longest line, and only
80
72
  # pads that line (shorter lines center naturally in the wider container).
81
- # Returns content unchanged if the longest line has no emoji.
73
+ # When no padding is applied, returns the same String object (identity),
74
+ # not merely an equal copy.
82
75
  #
83
76
  # @param content [String] raw label text (may contain <br/> line breaks)
84
77
  # @return [String] possibly padded label text
85
- def pad_label_content(content)
86
- parts = content.split(BR_RE)
87
- line_indices = (0...parts.length).step(2).to_a
88
- return content if line_indices.empty?
89
-
90
- longest_part_idx = line_indices.max_by { |i| visual_length(parts[i]) }
91
- longest_line = parts[longest_part_idx]
92
- n = count_emoji(longest_line)
93
- return content unless n.positive?
94
-
95
- parts[longest_part_idx] = "#{longest_line}#{NBSP * (n * 2)}"
96
- parts.join
78
+ def self.pad_label_content(content)
79
+ segments = content.split(BR_RE)
80
+ # Even indices are text lines; odd indices are the captured <br> delimiters.
81
+ line_pairs = segments.each_with_index.select { |_segment, index| index.even? }
82
+ return content if line_pairs.empty?
83
+
84
+ # max_by keeps the first of equal-length ties — pad that one index only.
85
+ longest_line, longest_idx = line_pairs.max_by { |segment, _index| visual_length(segment) }
86
+ emoji_count = count_emoji(longest_line)
87
+ return content unless emoji_count.positive?
88
+
89
+ segments[longest_idx] = "#{longest_line}#{NBSP * (emoji_count * 2)}"
90
+ segments.join
97
91
  end
98
92
 
99
- def compensate_flowchart_labels(source)
100
- result = source.dup
93
+ def self.compensate_flowchart_labels(source)
94
+ result = source
101
95
 
102
96
  [
103
- [/\["(.*?)"\]/m, '["', '"]'],
104
- [/\['(.*?)'\]/m, "['", "']"],
105
- [/\("(.*?)"\)/m, '("', '")'],
106
- [/\{"(.*?)"\}/m, '{"', '"}']
97
+ [/\["(.+?)"\]/m, '["', '"]'],
98
+ [/\['(.+?)'\]/m, "['", "']"],
99
+ [/\("(.+?)"\)/m, '("', '")'],
100
+ [/\{"(.+?)"\}/m, '{"', '"}']
107
101
  ].each do |regex, open_str, close_str|
108
102
  result = result.gsub(regex) do
109
103
  "#{open_str}#{pad_label_content(Regexp.last_match(1))}#{close_str}"
110
104
  end
111
105
  end
112
106
 
113
- result.gsub(%r{\[/"((?:[^"\\]|\\.)*)"/\]}m) do
107
+ result.gsub(%r{\[/"((?:[^"\\]|\\.)+?)"/\]}) do
114
108
  "[/\"#{pad_label_content(Regexp.last_match(1))}\"/]"
115
109
  end
116
110
  end
@@ -18,16 +18,15 @@ module JekyllMermaidPrebuild
18
18
  #
19
19
  # @param mermaid_source [String] mermaid diagram definition
20
20
  # @param cache_key [String] digest for caching
21
- # @param diagram_type [String, nil] from EmojiCompensator.detect_diagram_type (e.g. "block")
22
21
  # @return [Hash{String => String}, nil] stem (cache filename without .svg) => absolute cache path, or nil on failure
23
- def generate(mermaid_source, cache_key, diagram_type: nil)
22
+ def generate(mermaid_source, cache_key)
24
23
  case @config.prefers_color_scheme
25
24
  when :light
26
- generate_one(mermaid_source, cache_key, diagram_type: diagram_type, theme: :default)
25
+ generate_one(mermaid_source, cache_key, theme: :default)
27
26
  when :dark
28
- generate_one(mermaid_source, cache_key, diagram_type: diagram_type, theme: :dark)
27
+ generate_one(mermaid_source, cache_key, theme: :dark)
29
28
  when :auto
30
- generate_auto(mermaid_source, cache_key, diagram_type: diagram_type)
29
+ generate_auto(mermaid_source, cache_key)
31
30
  end
32
31
  end
33
32
 
@@ -72,7 +71,7 @@ module JekyllMermaidPrebuild
72
71
  private
73
72
 
74
73
  # @return [Hash{String => String}, nil]
75
- def generate_one(mermaid_source, stem, diagram_type:, theme:)
74
+ def generate_one(mermaid_source, stem, theme:)
76
75
  cache_path = File.join(@config.cache_dir, "#{stem}.svg")
77
76
  return { stem => cache_path } if File.exist?(cache_path)
78
77
 
@@ -80,12 +79,12 @@ module JekyllMermaidPrebuild
80
79
  return nil unless MmdcWrapper.render(mermaid_source, cache_path, theme: theme)
81
80
 
82
81
  bg = theme == :dark ? @config.chart_background_dark : @config.chart_background_light
83
- post_process_svg(cache_path, diagram_type, root_background: bg)
82
+ post_process_svg(cache_path, root_background: bg)
84
83
  { stem => cache_path }
85
84
  end
86
85
 
87
86
  # @return [Hash{String => String}, nil]
88
- def generate_auto(mermaid_source, cache_key, diagram_type:)
87
+ def generate_auto(mermaid_source, cache_key)
89
88
  light_stem = cache_key
90
89
  dark_stem = "#{cache_key}-dark"
91
90
  light_path = File.join(@config.cache_dir, "#{light_stem}.svg")
@@ -96,19 +95,19 @@ module JekyllMermaidPrebuild
96
95
  unless File.exist?(light_path)
97
96
  return nil unless MmdcWrapper.render(mermaid_source, light_path, theme: :default)
98
97
 
99
- post_process_svg(light_path, diagram_type, root_background: @config.chart_background_light)
98
+ post_process_svg(light_path, root_background: @config.chart_background_light)
100
99
  end
101
100
 
102
101
  unless File.exist?(dark_path)
103
102
  return nil unless MmdcWrapper.render(mermaid_source, dark_path, theme: :dark)
104
103
 
105
- post_process_svg(dark_path, diagram_type, root_background: @config.chart_background_dark)
104
+ post_process_svg(dark_path, root_background: @config.chart_background_dark)
106
105
  end
107
106
 
108
107
  { light_stem => light_path, dark_stem => dark_path }
109
108
  end
110
109
 
111
- def post_process_svg(cache_path, _diagram_type, root_background:)
110
+ def post_process_svg(cache_path, root_background:)
112
111
  raw = File.read(cache_path)
113
112
  svg = raw
114
113
 
@@ -116,10 +115,9 @@ module JekyllMermaidPrebuild
116
115
  svg = SvgPostProcessor.ensure_foreignobject_overflow(svg) if @config.overflow_protection
117
116
  svg = SvgPostProcessor.apply_root_svg_background(svg, root_background)
118
117
 
119
- pad = @config.edge_label_padding
120
- svg = SvgPostProcessor.apply(svg, padding: pad) if pad.is_a?(Numeric) && pad.positive?
118
+ svg = SvgPostProcessor.apply(svg, padding: @config.edge_label_padding)
121
119
 
122
- File.write(cache_path, svg) if svg != raw
120
+ File.write(cache_path, svg) unless svg.eql?(raw)
123
121
  end
124
122
  end
125
123
  end
@@ -5,15 +5,13 @@ require "fileutils"
5
5
  module JekyllMermaidPrebuild
6
6
  # Jekyll hook integration
7
7
  module Hooks
8
- module_function
9
-
10
8
  # Copy generated SVGs to _site directory
11
9
  #
12
10
  # @param site [Jekyll::Site] the Jekyll site
13
11
  # @param config [Configuration] plugin configuration
14
12
  # @param svgs [Hash] cache_key => cached_path mapping
15
- def copy_svgs_to_site(site, config, svgs)
16
- return unless svgs && !svgs.empty?
13
+ def self.copy_svgs_to_site(site, config, svgs)
14
+ return if svgs.nil? || svgs.empty?
17
15
 
18
16
  dest_dir = File.join(site.dest, config.output_dir)
19
17
  FileUtils.mkdir_p(dest_dir)
@@ -34,110 +32,110 @@ module JekyllMermaidPrebuild
34
32
  end
35
33
 
36
34
  # Log helpful error message for Puppeteer issues
37
- def log_puppeteer_error
38
- Jekyll.logger.error "MermaidPrebuild:", "=" * 60
35
+ def self.log_puppeteer_error
39
36
  Jekyll.logger.error "MermaidPrebuild:", "mmdc failed: Puppeteer cannot launch headless Chrome"
40
- Jekyll.logger.error "MermaidPrebuild:", ""
41
- Jekyll.logger.error "MermaidPrebuild:", "This usually means missing system libraries."
42
- Jekyll.logger.error "MermaidPrebuild:", "On Debian/Ubuntu/WSL, install with:"
43
- Jekyll.logger.error "MermaidPrebuild:", ""
44
- Jekyll.logger.error "MermaidPrebuild:", " sudo apt-get update"
45
- Jekyll.logger.error "MermaidPrebuild:", " sudo apt-get install -y libgbm1 libasound2 libatk1.0-0 \\"
46
- Jekyll.logger.error "MermaidPrebuild:", " libatk-bridge2.0-0 libcups2 libdrm2 libxcomposite1 \\"
47
- Jekyll.logger.error "MermaidPrebuild:", " libxdamage1 libxfixes3 libxrandr2 libxkbcommon0 \\"
48
- Jekyll.logger.error "MermaidPrebuild:", " libpango-1.0-0 libcairo2 libnss3 libnspr4"
49
- Jekyll.logger.error "MermaidPrebuild:", ""
50
- Jekyll.logger.error "MermaidPrebuild:", "See: https://pptr.dev/troubleshooting"
51
- Jekyll.logger.error "MermaidPrebuild:", "=" * 60
37
+ Jekyll.logger.error "MermaidPrebuild:", "Install system libraries (see https://pptr.dev/troubleshooting)"
52
38
  end
53
39
 
54
- # Register Jekyll hooks
55
- def register
56
- # Initialize and check mmdc after site data is loaded
57
- Jekyll::Hooks.register :site, :post_read do |site|
58
- config = Configuration.new(site)
59
- site.data["mermaid_prebuild_config"] = config
40
+ # Initialize plugin state after site data is loaded (:post_read)
41
+ #
42
+ # @param site [Jekyll::Site] the Jekyll site
43
+ def self.initialize_system(site)
44
+ config = Configuration.new(site)
45
+ site.data["mermaid_prebuild_config"] = config
46
+
47
+ unless config.enabled?
48
+ Jekyll.logger.info "MermaidPrebuild:", "Disabled in configuration"
49
+ site.data["mermaid_prebuild_enabled"] = false
50
+ return
51
+ end
60
52
 
61
- unless config.enabled?
62
- Jekyll.logger.info "MermaidPrebuild:", "Disabled in configuration"
63
- site.data["mermaid_prebuild_enabled"] = false
64
- next
65
- end
53
+ status = MmdcWrapper.check_status
54
+
55
+ case status
56
+ when :ok
57
+ Jekyll.logger.info "MermaidPrebuild:", "Initialized (mmdc #{MmdcWrapper.version || "unknown version"})"
58
+ Jekyll.logger.info "MermaidPrebuild:", "Output directory: #{config.output_dir}"
59
+ site.data["mermaid_prebuild_enabled"] = true
60
+ generator = Generator.new(config)
61
+ site.data["mermaid_prebuild_generator"] = generator
62
+ site.data["mermaid_prebuild_processor"] = Processor.new(config, generator)
63
+ site.data["mermaid_prebuild_svgs"] = {}
64
+ when :not_found
65
+ Jekyll.logger.warn "MermaidPrebuild:", "mmdc not found - mermaid diagrams will not be converted"
66
+ Jekyll.logger.warn "MermaidPrebuild:", "Install with: npm install -g @mermaid-js/mermaid-cli"
67
+ site.data["mermaid_prebuild_enabled"] = false
68
+ when :puppeteer_error
69
+ log_puppeteer_error
70
+ site.data["mermaid_prebuild_enabled"] = false
71
+ else
72
+ Jekyll.logger.warn "MermaidPrebuild:", "mmdc check failed with unknown error"
73
+ site.data["mermaid_prebuild_enabled"] = false
74
+ end
75
+ end
76
+
77
+ # Process documents/pages before rendering (:pre_render)
78
+ #
79
+ # @param site [Jekyll::Site] the Jekyll site
80
+ def self.process_site(site)
81
+ return unless site.data["mermaid_prebuild_enabled"]
82
+
83
+ processor = site.data["mermaid_prebuild_processor"]
84
+ total_count = 0
85
+
86
+ site.documents.each do |document|
87
+ next unless document.content
66
88
 
67
- status = MmdcWrapper.check_status
68
-
69
- case status
70
- when :ok
71
- Jekyll.logger.info "MermaidPrebuild:", "Initialized (mmdc #{MmdcWrapper.version || "unknown version"})"
72
- Jekyll.logger.info "MermaidPrebuild:", "Output directory: #{config.output_dir}"
73
- site.data["mermaid_prebuild_enabled"] = true
74
- site.data["mermaid_prebuild_generator"] = Generator.new(config)
75
- site.data["mermaid_prebuild_processor"] = Processor.new(config, site.data["mermaid_prebuild_generator"])
76
- site.data["mermaid_prebuild_svgs"] = {}
77
- when :not_found
78
- Jekyll.logger.warn "MermaidPrebuild:", "mmdc not found - mermaid diagrams will not be converted"
79
- Jekyll.logger.warn "MermaidPrebuild:", "Install with: npm install -g @mermaid-js/mermaid-cli"
80
- site.data["mermaid_prebuild_enabled"] = false
81
- when :puppeteer_error
82
- log_puppeteer_error
83
- site.data["mermaid_prebuild_enabled"] = false
84
- else
85
- Jekyll.logger.warn "MermaidPrebuild:", "mmdc check failed with unknown error"
86
- site.data["mermaid_prebuild_enabled"] = false
89
+ processed, count, svgs = processor.process_content(document.content, site)
90
+ if count.positive?
91
+ document.content = processed
92
+ site.data["mermaid_prebuild_svgs"].merge!(svgs)
93
+ Jekyll.logger.info "MermaidPrebuild:", "Converted #{count} diagram(s) in #{document.relative_path}"
94
+ total_count += count
87
95
  end
96
+ rescue StandardError => e
97
+ Jekyll.logger.error "MermaidPrebuild:", "Error processing #{document.relative_path}: #{e.message}"
88
98
  end
89
99
 
90
- # Process all documents/pages BEFORE rendering
91
- Jekyll::Hooks.register :site, :pre_render do |site|
92
- next unless site.data["mermaid_prebuild_enabled"]
93
-
94
- processor = site.data["mermaid_prebuild_processor"]
95
- total_count = 0
96
-
97
- # Process all documents
98
- site.documents.each do |document|
99
- next unless document.content
100
-
101
- processed, count, svgs = processor.process_content(document.content, site)
102
- if count.positive?
103
- document.content = processed
104
- site.data["mermaid_prebuild_svgs"].merge!(svgs)
105
- Jekyll.logger.info "MermaidPrebuild:", "Converted #{count} diagram(s) in #{document.relative_path}"
106
- total_count += count
107
- end
108
- rescue StandardError => e
109
- Jekyll.logger.error "MermaidPrebuild:", "Error processing #{document.relative_path}: #{e.message}"
110
- end
100
+ site.pages.each do |page|
101
+ next unless page.content
111
102
 
112
- # Process all pages
113
- site.pages.each do |page|
114
- next unless page.content
115
-
116
- processed, count, svgs = processor.process_content(page.content, site)
117
- if count.positive?
118
- page.content = processed
119
- site.data["mermaid_prebuild_svgs"].merge!(svgs)
120
- Jekyll.logger.info "MermaidPrebuild:", "Converted #{count} diagram(s) in #{page.relative_path}"
121
- total_count += count
122
- end
123
- rescue StandardError => e
124
- Jekyll.logger.error "MermaidPrebuild:", "Error processing page: #{e.message}"
103
+ processed, count, svgs = processor.process_content(page.content, site)
104
+ if count.positive?
105
+ page.content = processed
106
+ site.data["mermaid_prebuild_svgs"].merge!(svgs)
107
+ Jekyll.logger.info "MermaidPrebuild:", "Converted #{count} diagram(s) in #{page.relative_path}"
108
+ total_count += count
125
109
  end
126
-
127
- Jekyll.logger.info "MermaidPrebuild:", "Total: #{total_count} diagram(s) converted" if total_count.positive?
110
+ rescue StandardError => e
111
+ Jekyll.logger.error "MermaidPrebuild:", "Error processing #{page.relative_path}: #{e.message}"
128
112
  end
129
113
 
130
- # Copy SVGs to _site after write
131
- Jekyll::Hooks.register :site, :post_write do |site|
132
- next unless site.data["mermaid_prebuild_enabled"]
114
+ Jekyll.logger.info "MermaidPrebuild:", "Total: #{total_count} diagram(s) converted" if total_count.positive?
115
+ end
133
116
 
134
- config = site.data["mermaid_prebuild_config"]
135
- svgs = site.data["mermaid_prebuild_svgs"]
136
- copy_svgs_to_site(site, config, svgs)
137
- end
117
+ # Copy SVGs into _site after write (:post_write)
118
+ #
119
+ # @param site [Jekyll::Site] the Jekyll site
120
+ def self.copy_generated_svgs(site)
121
+ return unless site.data["mermaid_prebuild_enabled"]
122
+
123
+ config = site.data["mermaid_prebuild_config"]
124
+ svgs = site.data["mermaid_prebuild_svgs"]
125
+ copy_svgs_to_site(site, config, svgs)
138
126
  end
139
127
  end
140
128
  end
141
129
 
142
- # Register hooks when loaded
143
- JekyllMermaidPrebuild::Hooks.register
130
+ # Register Jekyll hooks when loaded
131
+ Jekyll::Hooks.register :site, :post_read do |site|
132
+ JekyllMermaidPrebuild::Hooks.initialize_system(site)
133
+ end
134
+
135
+ Jekyll::Hooks.register :site, :pre_render do |site|
136
+ JekyllMermaidPrebuild::Hooks.process_site(site)
137
+ end
138
+
139
+ Jekyll::Hooks.register :site, :post_write do |site|
140
+ JekyllMermaidPrebuild::Hooks.copy_generated_svgs(site)
141
+ end
@@ -6,39 +6,41 @@ require "tempfile"
6
6
  module JekyllMermaidPrebuild
7
7
  # Wrapper for mmdc (mermaid CLI) commands
8
8
  module MmdcWrapper
9
- module_function
10
-
11
9
  # Check if mmdc is available in PATH
12
10
  #
13
11
  # @return [Boolean] true if mmdc is available
14
- def available?
15
- return @available if defined?(@available)
12
+ MMDC_COMMAND = "mmdc"
13
+
14
+ def self.available?
15
+ return @available if instance_variable_defined?(:@available)
16
16
 
17
- @available = command_exists?("mmdc")
17
+ @available = command_exists?(MMDC_COMMAND)
18
18
  end
19
19
 
20
20
  # Check if a command exists in PATH (cross-platform)
21
21
  #
22
22
  # @param cmd [String] command name
23
23
  # @return [Boolean] true if command found
24
- def command_exists?(cmd)
24
+ def self.command_exists?(cmd)
25
+ return false if cmd.nil? || cmd.empty?
26
+
25
27
  cmd_name = Gem.win_platform? ? "#{cmd}.exe" : cmd
26
28
  path_dirs = ENV.fetch("PATH", "").split(File::PATH_SEPARATOR)
27
29
 
28
30
  path_dirs.any? do |dir|
29
31
  executable = File.join(dir, cmd_name)
30
- File.executable?(executable)
32
+ File.file?(executable) && File.executable?(executable)
31
33
  end
32
34
  end
33
35
 
34
36
  # Get mmdc version
35
37
  #
36
38
  # @return [String, nil] version string or nil
37
- def version
38
- return @version if defined?(@version)
39
+ def self.version
40
+ return @version if instance_variable_defined?(:@version)
39
41
 
40
- output, status = Open3.capture2e("mmdc", "--version")
41
- @version = status.success? ? output.strip : nil
42
+ output, status = Open3.capture2e(MMDC_COMMAND, "--version")
43
+ @version = output.strip if status.success?
42
44
  rescue StandardError
43
45
  @version = nil
44
46
  end
@@ -46,8 +48,8 @@ module JekyllMermaidPrebuild
46
48
  # Check mmdc status (availability and Puppeteer)
47
49
  #
48
50
  # @return [Symbol] :ok, :not_found, :puppeteer_error, or :unknown_error
49
- def check_status
50
- return @check_status if defined?(@check_status)
51
+ def self.check_status
52
+ return @check_status if instance_variable_defined?(:@check_status)
51
53
 
52
54
  unless available?
53
55
  @check_status = :not_found
@@ -58,25 +60,26 @@ module JekyllMermaidPrebuild
58
60
  end
59
61
 
60
62
  # Reset cached status (useful for testing)
61
- def reset_cache!
62
- remove_instance_variable(:@available) if defined?(@available)
63
- remove_instance_variable(:@version) if defined?(@version)
64
- remove_instance_variable(:@check_status) if defined?(@check_status)
63
+ def self.reset_cache!
64
+ remove_instance_variable(:@available) if instance_variable_defined?(:@available)
65
+ remove_instance_variable(:@version) if instance_variable_defined?(:@version)
66
+ remove_instance_variable(:@check_status) if instance_variable_defined?(:@check_status)
65
67
  end
66
68
 
67
69
  # Test mmdc with a minimal diagram to verify Puppeteer works
68
70
  #
69
71
  # @return [Symbol] :ok, :puppeteer_error, or :unknown_error
70
- def test_render
71
- input = Tempfile.new(["test", ".mmd"])
72
- output = Tempfile.new(["test", ".svg"])
73
-
72
+ def self.test_render
73
+ input = Tempfile.new(["", ".mmd"])
74
+ output = Tempfile.new(["", ".svg"])
74
75
  begin
75
76
  input.write("graph TD\nA-->B")
76
77
  input.close
77
78
  output.close # Close before mmdc writes (Windows file locking)
78
79
 
79
- _stdout, stderr, status = Open3.capture3("mmdc", "-i", input.path, "-o", output.path, "-e", "svg")
80
+ _stdout, stderr, status = Open3.capture3(
81
+ MMDC_COMMAND, "-i", input.path, "-o", output.path, "-e", "svg"
82
+ )
80
83
 
81
84
  if status.success?
82
85
  :ok
@@ -86,8 +89,8 @@ module JekyllMermaidPrebuild
86
89
  :unknown_error
87
90
  end
88
91
  ensure
89
- input.close! rescue nil # rubocop:disable Style/RescueModifier
90
- output.close! rescue nil # rubocop:disable Style/RescueModifier
92
+ input.unlink
93
+ output.unlink
91
94
  end
92
95
  end
93
96
 
@@ -100,11 +103,8 @@ module JekyllMermaidPrebuild
100
103
  # @param theme [Symbol] :default (mermaid default theme) or :dark (mmdc -t dark)
101
104
  # @return [Boolean] true if successful
102
105
  # @raise [ArgumentError] if theme is not supported
103
- def render(mermaid_source, output_path, theme: :default)
104
- unless ALLOWED_RENDER_THEMES.include?(theme)
105
- raise ArgumentError,
106
- "unsupported mmdc theme #{theme.inspect} (allowed: #{ALLOWED_RENDER_THEMES.map(&:inspect).join(", ")})"
107
- end
106
+ def self.render(mermaid_source, output_path, theme: :default)
107
+ raise ArgumentError, "unsupported mmdc theme #{theme.inspect}" unless ALLOWED_RENDER_THEMES.include?(theme)
108
108
 
109
109
  input_file = Tempfile.new(["mermaid", ".mmd"])
110
110
 
@@ -112,7 +112,7 @@ module JekyllMermaidPrebuild
112
112
  input_file.write(mermaid_source)
113
113
  input_file.close
114
114
 
115
- cmd = ["mmdc", "-i", input_file.path, "-o", output_path, "-e", "svg"]
115
+ cmd = [MMDC_COMMAND, "-i", input_file.path, "-o", output_path, "-e", "svg"]
116
116
  cmd += ["-t", "dark"] if theme == :dark
117
117
  _stdout, _stderr, status = Open3.capture3(*cmd)
118
118
 
@@ -126,7 +126,7 @@ module JekyllMermaidPrebuild
126
126
  # Supports both backtick (```) and tilde (~~~) fences with 3+ characters
127
127
  #
128
128
  # @return [Regexp] pattern matching mermaid code blocks
129
- def mermaid_fence_pattern
129
+ def self.mermaid_fence_pattern
130
130
  /
131
131
  ^(`{3,}|~{3,})mermaid\s*\n # Opening fence: 3+ backticks or tildes, then 'mermaid'
132
132
  (.*?) # Content (captured, non-greedy)
@@ -22,7 +22,7 @@ module JekyllMermaidPrebuild
22
22
  # @param _site [Jekyll::Site] the Jekyll site (unused, kept for API compatibility)
23
23
  # @return [Array<String, Integer, Hash>] [processed_content, count, svgs_to_copy]
24
24
  def process_content(content, _site = nil)
25
- return [content, 0, {}] unless content
25
+ return [nil, 0, {}] unless content
26
26
 
27
27
  converted_count = 0
28
28
  svgs_to_copy = {}
@@ -36,20 +36,19 @@ module JekyllMermaidPrebuild
36
36
  result = convert_block(block)
37
37
  next unless result
38
38
 
39
+ result => { svgs:, html: }
40
+ block => { start:, end: block_end }
39
41
  converted_count += 1
40
- svgs_to_copy.merge!(result[:svgs])
41
- processed[block[:start]...block[:end]] = result[:html]
42
+ svgs_to_copy.merge!(svgs)
43
+ processed[start...block_end] = html
42
44
  end
43
45
 
44
46
  [processed, converted_count, svgs_to_copy]
45
47
  end
46
48
 
47
- private
48
-
49
49
  # @param source [String] mermaid passed to mmdc (after optional emoji compensation)
50
- # @param diagram_type [String, nil]
51
50
  # @return [String] input to MD5 for cache key
52
- def digest_string_for_cache(source, _diagram_type)
51
+ def digest_string_for_cache(source)
53
52
  parts = [source]
54
53
  parts << "pcs=#{@config.prefers_color_scheme}"
55
54
  parts << "bgL=#{@config.chart_background_light}"
@@ -68,14 +67,14 @@ module JekyllMermaidPrebuild
68
67
  def convert_block(block)
69
68
  mermaid_source = block[:content]
70
69
  diagram_type = EmojiCompensator.detect_diagram_type(mermaid_source)
71
- source_for_render = if diagram_type && @config.emoji_width_compensation[diagram_type]
70
+ source_for_render = if @config.emoji_width_compensation[diagram_type]
72
71
  EmojiCompensator.compensate(mermaid_source, diagram_type)
73
72
  else
74
73
  mermaid_source
75
74
  end
76
- digest_input = digest_string_for_cache(source_for_render, diagram_type)
75
+ digest_input = digest_string_for_cache(source_for_render)
77
76
  cache_key = DigestCalculator.content_digest(digest_input)
78
- paths = @generator.generate(source_for_render, cache_key, diagram_type: diagram_type)
77
+ paths = @generator.generate(source_for_render, cache_key)
79
78
 
80
79
  return nil if paths.nil? || paths.empty?
81
80
 
@@ -95,13 +94,14 @@ module JekyllMermaidPrebuild
95
94
  # @param content [String] markdown content
96
95
  # @return [Array<Hash>] array of {start:, end:, content:} for each top-level mermaid block
97
96
  def find_top_level_mermaid_blocks(content)
98
- state = { blocks: [], fence_stack: [], current_mermaid: nil, position: 0 }
97
+ blocks = []
98
+ state = { blocks: blocks, fence_stack: [], position: 0 }
99
99
 
100
- content.lines.each do |line|
100
+ content.lines do |line|
101
101
  process_line(line, state)
102
102
  end
103
103
 
104
- state[:blocks]
104
+ blocks
105
105
  end
106
106
 
107
107
  # Process a single line for fence detection
@@ -112,8 +112,9 @@ module JekyllMermaidPrebuild
112
112
  match = line.match(FENCE_OPENER)
113
113
  if match
114
114
  handle_fence_line(line, line_start, match, state)
115
- elsif state[:current_mermaid]
116
- state[:current_mermaid][:content_lines] << line
115
+ elsif (current_mermaid = state[:current_mermaid])
116
+ content_lines = current_mermaid[:content_lines]
117
+ content_lines << line
117
118
  end
118
119
  end
119
120
 
@@ -121,7 +122,7 @@ module JekyllMermaidPrebuild
121
122
  def handle_fence_line(line, line_start, match, state)
122
123
  fence_chars = match[1]
123
124
  language = match[2]
124
- fence_type = fence_chars[0]
125
+ fence_type = fence_chars.include?("~") ? "~" : "`"
125
126
  fence_length = fence_chars.length
126
127
 
127
128
  if state[:current_mermaid]
@@ -136,11 +137,14 @@ module JekyllMermaidPrebuild
136
137
  # Handle fence line while inside a mermaid block
137
138
  def handle_line_in_mermaid(line, fence_chars, fence_type, fence_length, state)
138
139
  cm = state[:current_mermaid]
140
+ blocks = state[:blocks]
141
+ position = state[:position]
142
+ content_lines = cm[:content_lines]
139
143
  if fence_type == cm[:fence_type] && fence_length == cm[:fence_length] && line.strip == fence_chars
140
- state[:blocks] << { start: cm[:start], end: state[:position], content: cm[:content_lines].join }
144
+ blocks << { start: cm[:start], end: position, content: content_lines.join }
141
145
  state[:current_mermaid] = nil
142
146
  else
143
- cm[:content_lines] << line
147
+ content_lines << line
144
148
  end
145
149
  end
146
150
 
@@ -156,12 +160,13 @@ module JekyllMermaidPrebuild
156
160
 
157
161
  # Handle fence line while inside a non-mermaid fence
158
162
  def handle_line_in_nested_fence(line, fence_type, fence_length, state)
159
- top_fence_length, top_fence_type = state[:fence_stack].last
163
+ fence_stack = state[:fence_stack]
164
+ top_fence_length, top_fence_type = fence_stack.last
160
165
 
161
- if fence_type == top_fence_type && fence_length >= top_fence_length && line.strip.match?(/^[`~]+$/)
162
- state[:fence_stack].pop
166
+ if fence_type == top_fence_type && fence_length >= top_fence_length && line.strip.match?(/\A[`~]+\z/)
167
+ fence_stack.pop
163
168
  else
164
- state[:fence_stack].push([fence_length, fence_type])
169
+ fence_stack.push([fence_length, fence_type])
165
170
  end
166
171
  end
167
172
  end
@@ -18,8 +18,6 @@ module JekyllMermaidPrebuild
18
18
  # of theme. The plugin replaces that token with configurable CSS color values for light and
19
19
  # dark variants (defaults white / black) so charts match page background in both modes.
20
20
  module SvgPostProcessor
21
- module_function
22
-
23
21
  # Opening sequence produced by mmdc for block edge labels (deterministic minified output).
24
22
  EDGE_LABEL_FOREIGN_OBJECT_RE = /
25
23
  (<g\sclass="edgeLabel"[^>]*><g\sclass="label"[^>]*><foreignObject)
@@ -30,9 +28,8 @@ module JekyllMermaidPrebuild
30
28
  # @param svg_string [String] full SVG document from mmdc
31
29
  # @param padding [Numeric] user units to add to each matching foreignObject width (must be positive)
32
30
  # @return [String] possibly widened SVG, or the original string on no-op / error
33
- def apply(svg_string, padding:)
34
- return svg_string unless svg_string.is_a?(String)
35
- return svg_string unless padding.is_a?(Numeric) && padding.positive?
31
+ def self.apply(svg_string, padding:)
32
+ return svg_string unless padding.positive?
36
33
 
37
34
  apply_edge_label_padding(svg_string, padding)
38
35
  rescue StandardError
@@ -49,10 +46,8 @@ module JekyllMermaidPrebuild
49
46
  #
50
47
  # @param svg_string [String] full SVG document from mmdc
51
48
  # @return [String] SVG with overflow rule injected, or original on no-op / error
52
- def ensure_foreignobject_overflow(svg_string)
53
- return svg_string unless svg_string.is_a?(String)
49
+ def self.ensure_foreignobject_overflow(svg_string)
54
50
  return svg_string if svg_string.include?(OVERFLOW_RULE)
55
- return svg_string unless svg_string.include?("</style>")
56
51
 
57
52
  svg_string.sub("</style>", "#{OVERFLOW_RULE}</style>")
58
53
  rescue StandardError
@@ -68,10 +63,8 @@ module JekyllMermaidPrebuild
68
63
  #
69
64
  # @param svg_string [String] full SVG document from mmdc
70
65
  # @return [String] SVG with centering rule injected, or original on no-op / error
71
- def ensure_text_centering(svg_string)
72
- return svg_string unless svg_string.is_a?(String)
66
+ def self.ensure_text_centering(svg_string)
73
67
  return svg_string if svg_string.include?(CENTERING_RULE)
74
- return svg_string unless svg_string.include?("</style>")
75
68
 
76
69
  svg_string.sub("</style>", "#{CENTERING_RULE}</style>")
77
70
  rescue StandardError
@@ -85,19 +78,18 @@ module JekyllMermaidPrebuild
85
78
  # @param svg_string [String] full SVG document from mmdc
86
79
  # @param css_background [String] literal after `background-color:` (e.g. "black", "#fff0aa")
87
80
  # @return [String] SVG with updated root background, or original on no-op / error
88
- def apply_root_svg_background(svg_string, css_background)
89
- return svg_string unless svg_string.is_a?(String)
81
+ def self.apply_root_svg_background(svg_string, css_background)
90
82
  return svg_string unless css_background.is_a?(String) && !css_background.empty?
91
83
 
92
84
  svg_string.sub(
93
- /(<svg\b[^>]*\bstyle="[^"]*?)background-color:\s*white;?/,
85
+ /(<svg\b[^>]+\bstyle="[^"]*?)background-color:\s*white;?/,
94
86
  "\\1background-color: #{css_background};"
95
87
  )
96
88
  rescue StandardError
97
89
  svg_string
98
90
  end
99
91
 
100
- def apply_edge_label_padding(svg_string, padding)
92
+ def self.apply_edge_label_padding(svg_string, padding)
101
93
  svg_string.gsub(EDGE_LABEL_FOREIGN_OBJECT_RE) do
102
94
  prefix = Regexp.last_match(1)
103
95
  attrs = Regexp.last_match(2)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllMermaidPrebuild
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-mermaid-prebuild
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Texarkanine
@@ -29,6 +29,34 @@ dependencies:
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: '5.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: mutant
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '0.16'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '0.16'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mutant-rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '0.16'
53
+ type: :development
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '0.16'
32
60
  - !ruby/object:Gem::Dependency
33
61
  name: rake
34
62
  requirement: !ruby/object:Gem::Requirement
@@ -105,28 +133,28 @@ dependencies:
105
133
  requirements:
106
134
  - - "~>"
107
135
  - !ruby/object:Gem::Version
108
- version: '0.22'
136
+ version: '1.0'
109
137
  type: :development
110
138
  prerelease: false
111
139
  version_requirements: !ruby/object:Gem::Requirement
112
140
  requirements:
113
141
  - - "~>"
114
142
  - !ruby/object:Gem::Version
115
- version: '0.22'
143
+ version: '1.0'
116
144
  - !ruby/object:Gem::Dependency
117
145
  name: simplecov-cobertura
118
146
  requirement: !ruby/object:Gem::Requirement
119
147
  requirements:
120
148
  - - "~>"
121
149
  - !ruby/object:Gem::Version
122
- version: '3.1'
150
+ version: '4.0'
123
151
  type: :development
124
152
  prerelease: false
125
153
  version_requirements: !ruby/object:Gem::Requirement
126
154
  requirements:
127
155
  - - "~>"
128
156
  - !ruby/object:Gem::Version
129
- version: '3.1'
157
+ version: '4.0'
130
158
  description: Jekyll plugin that converts mermaid diagram code blocks to SVG files
131
159
  at build time using the mmdc CLI. Eliminates the need for client-side mermaid.js
132
160
  (~2MB) by generating static SVG files with intelligent caching.