jekyll-mermaid-prebuild 0.2.0 → 0.2.1

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: 6fb78d9660c4601fc8e1b4b21d4c0f2e0b1c1d120ce09a3b61c6a47900b26a80
4
- data.tar.gz: 4a7458ca917f5814760a6cd6b9dbfb38291b06db65bf325eb537aac64c09fc96
3
+ metadata.gz: e76d120b1e9c96438f6f84dea780e132e1bbaf498fd11d8a2f2e37f2b585c2f6
4
+ data.tar.gz: b7e4e4b1249cf4b64e9cd8ee8d7bd19bd0402117e96ad57289d3291e401567f6
5
5
  SHA512:
6
- metadata.gz: 7aac32a6e29fda736401e36514bb751c4c94ceb01540e15234377fe660df5e8b4e925d6673fd476b2640f4c35e9ece4b5f957d2966448d75c410f92c9c8fee7b
7
- data.tar.gz: 0d9d4ebcd23007a679221aed4f8154c0d1fe00e9a060bef13a4d8c429e0b9bf447c3067ead4b2b82d8bf002269547d6688a2e7f5ca4d60e32f70ed82d6a3fe9c
6
+ metadata.gz: 463dfec0e22358beeb09c7e4f3500c78bcc35d9c97f46352fe967e100bc5a0d422cfd074c3ade807683230f94a9b1cd8b73e338ba20de6b51e7042cfbebb2129
7
+ data.tar.gz: 89b982187ccb12009c02f7ebf8bb8bab5d97a3766b5bdcf368405ccec5167f533e918bc6c11f3767222ed2cc509178095e54d89c108ce452e3af652f0f454ce3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.1](https://github.com/Texarkanine/jekyll-mermaid-prebuild/compare/v0.2.0...v0.2.1) (2026-01-18)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * Allow literal mermaid code examples to remain un-rendered. ([#4](https://github.com/Texarkanine/jekyll-mermaid-prebuild/issues/4)) ([6a4e2e1](https://github.com/Texarkanine/jekyll-mermaid-prebuild/commit/6a4e2e1aa43145fde757c64dc20d7189c4eb4489))
9
+
3
10
  ## [0.2.0](https://github.com/Texarkanine/jekyll-mermaid-prebuild/compare/v0.1.0...v0.2.0) (2026-01-17)
4
11
 
5
12
 
@@ -3,6 +3,9 @@
3
3
  module JekyllMermaidPrebuild
4
4
  # Processes document/page content, replacing mermaid blocks with SVG references
5
5
  class Processor
6
+ # Pattern to detect any fence opener (captures fence chars and optional language)
7
+ FENCE_OPENER = /^(`{3,}|~{3,})(\w*)/
8
+
6
9
  # Initialize processor
7
10
  #
8
11
  # @param config [Configuration] plugin configuration
@@ -13,6 +16,7 @@ module JekyllMermaidPrebuild
13
16
  end
14
17
 
15
18
  # Process content, replacing mermaid code blocks with figure HTML
19
+ # Only processes top-level mermaid blocks (not nested inside other fences)
16
20
  #
17
21
  # @param content [String] markdown content
18
22
  # @param _site [Jekyll::Site] the Jekyll site (unused, kept for API compatibility)
@@ -20,35 +24,116 @@ module JekyllMermaidPrebuild
20
24
  def process_content(content, _site = nil)
21
25
  return [content, 0, {}] unless content
22
26
 
23
- pattern = MmdcWrapper.mermaid_fence_pattern
24
27
  converted_count = 0
25
28
  svgs_to_copy = {}
26
29
 
27
- processed = content.gsub(pattern) do |original_match|
28
- mermaid_source = Regexp.last_match(2)
30
+ # Find top-level mermaid blocks respecting fence nesting
31
+ top_level_blocks = find_top_level_mermaid_blocks(content)
32
+
33
+ # Process blocks in reverse order to preserve string positions
34
+ processed = content.dup
35
+ top_level_blocks.reverse_each do |block|
36
+ result = convert_block(block)
37
+ next unless result
38
+
39
+ converted_count += 1
40
+ svgs_to_copy[result[:cache_key]] = result[:cached_path]
41
+ processed[block[:start]...block[:end]] = result[:html]
42
+ end
43
+
44
+ [processed, converted_count, svgs_to_copy]
45
+ end
46
+
47
+ private
29
48
 
30
- # Generate cache key from content
31
- cache_key = DigestCalculator.content_digest(mermaid_source)
49
+ # Convert a single mermaid block to SVG
50
+ #
51
+ # @param block [Hash] block info with :content key
52
+ # @return [Hash, nil] {cache_key:, cached_path:, html:} or nil if failed
53
+ def convert_block(block)
54
+ mermaid_source = block[:content]
55
+ cache_key = DigestCalculator.content_digest(mermaid_source)
56
+ cached_path = @generator.generate(mermaid_source, cache_key)
32
57
 
33
- # Generate SVG
34
- cached_path = @generator.generate(mermaid_source, cache_key)
58
+ return nil unless cached_path
35
59
 
36
- if cached_path
37
- converted_count += 1
60
+ svg_url = @generator.build_svg_url(cache_key)
61
+ { cache_key: cache_key, cached_path: cached_path, html: @generator.build_figure_html(svg_url) }
62
+ end
38
63
 
39
- # Track SVG for copying to _site later
40
- svgs_to_copy[cache_key] = cached_path
64
+ # Find all top-level mermaid code blocks (not nested inside other fences)
65
+ #
66
+ # @param content [String] markdown content
67
+ # @return [Array<Hash>] array of {start:, end:, content:} for each top-level mermaid block
68
+ def find_top_level_mermaid_blocks(content)
69
+ state = { blocks: [], fence_stack: [], current_mermaid: nil, position: 0 }
41
70
 
42
- # Return replacement HTML
43
- svg_url = @generator.build_svg_url(cache_key)
44
- @generator.build_figure_html(svg_url)
45
- else
46
- # Keep original on failure
47
- original_match
48
- end
71
+ content.lines.each do |line|
72
+ process_line(line, state)
49
73
  end
50
74
 
51
- [processed, converted_count, svgs_to_copy]
75
+ state[:blocks]
76
+ end
77
+
78
+ # Process a single line for fence detection
79
+ def process_line(line, state)
80
+ line_start = state[:position]
81
+ state[:position] += line.length
82
+
83
+ match = line.match(FENCE_OPENER)
84
+ if match
85
+ handle_fence_line(line, line_start, match, state)
86
+ elsif state[:current_mermaid]
87
+ state[:current_mermaid][:content_lines] << line
88
+ end
89
+ end
90
+
91
+ # Handle a line that matches a fence pattern
92
+ def handle_fence_line(line, line_start, match, state)
93
+ fence_chars = match[1]
94
+ language = match[2]
95
+ fence_type = fence_chars[0]
96
+ fence_length = fence_chars.length
97
+
98
+ if state[:current_mermaid]
99
+ handle_line_in_mermaid(line, fence_chars, fence_type, fence_length, state)
100
+ elsif state[:fence_stack].empty?
101
+ handle_line_at_top_level(line_start, language, fence_type, fence_length, state)
102
+ else
103
+ handle_line_in_nested_fence(line, fence_type, fence_length, state)
104
+ end
105
+ end
106
+
107
+ # Handle fence line while inside a mermaid block
108
+ def handle_line_in_mermaid(line, fence_chars, fence_type, fence_length, state)
109
+ cm = state[:current_mermaid]
110
+ if fence_type == cm[:fence_type] && fence_length == cm[:fence_length] && line.strip == fence_chars
111
+ state[:blocks] << { start: cm[:start], end: state[:position], content: cm[:content_lines].join }
112
+ state[:current_mermaid] = nil
113
+ else
114
+ cm[:content_lines] << line
115
+ end
116
+ end
117
+
118
+ # Handle fence line at top level (not inside any fence)
119
+ def handle_line_at_top_level(line_start, language, fence_type, fence_length, state)
120
+ if language == "mermaid"
121
+ state[:current_mermaid] = { start: line_start, fence_type: fence_type,
122
+ fence_length: fence_length, content_lines: [] }
123
+ else
124
+ state[:fence_stack].push([fence_length, fence_type])
125
+ end
126
+ end
127
+
128
+ # Handle fence line while inside a non-mermaid fence
129
+ def handle_line_in_nested_fence(line, fence_type, fence_length, state)
130
+ top_fence_length, top_fence_type = state[:fence_stack].last
131
+
132
+ if fence_type == top_fence_type && fence_length >= top_fence_length && line.strip.match?(/^[`~]+$/)
133
+ state[:fence_stack].pop
134
+ else
135
+ state[:fence_stack].push([fence_length, fence_type])
136
+ end
52
137
  end
53
138
  end
54
139
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllMermaidPrebuild
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
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.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Texarkanine