Module: VivlioStarter::CLI::PostProcessCommands::HtmlReplacer
- Defined in:
- lib/vivlio_starter/cli/post_process/html_replacer.rb
Overview
組み込み置換ルール適用エンジン
Constant Summary collapse
- PRE_PLACEHOLDER_PREFIX =
退避プレースホルダのマーカー。Unicode 制御文字 (U+0000) を両端に置くので 原稿内やルールパターンに出現することはない。
"\u0000__VS_PRE__"- CODE_PLACEHOLDER_PREFIX =
"\u0000__VS_CODE__"- TAG_PLACEHOLDER_PREFIX =
"\u0000__VS_TAG__"- PLACEHOLDER_SUFFIX =
"__\u0000"
Class Method Summary collapse
-
.apply_rule(content, rule) ⇒ Object
単一のルールを適用する。rule.mode で対象領域を絞り込んでから gsub を実行する。.
-
.process_html_file(html_file, replace_rules) ⇒ Object
ReplacementRules::Rule の配列を適用して HTML ファイルを更新.
- .replace_with_captures(content, regex, replacement_str) ⇒ Object
-
.with_text_scope_protected(content) ⇒ Object
テキスト専用ルール向けに、HTML 構造を全て退避したビューをブロックへ渡す。 以下の順で退避する(後続のマッチが先のプレースホルダを巻き込まないよう 大きい構造から順に退避): 1.
Class Method Details
.apply_rule(content, rule) ⇒ Object
単一のルールを適用する。rule.mode で対象領域を絞り込んでから gsub を実行する。
モード:
-
:text_only … HTML 構造(
/
の本体、および全てのタグ定義<...>)を退避し、テキストノード部分だけに適用する。 これにより@vspaceなどのマクロがdata-heading="..."の ような属性値の中で置換されて HTML が壊れる事故を防ぐ。 -
:tag_aware …
<p><li ...><span ...>など HTML 構造を対象にする ルール。/
内にはそもそもこれらのリテラルタグが 存在しない(実体参照化される)ため、ブロックのみ退避 して全体に適用する。
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/vivlio_starter/cli/post_process/html_replacer.rb', line 70 def apply_rule(content, rule) case rule.mode when :text_only with_text_scope_protected(content) do |stashed| replace_with_captures(stashed, rule.pattern, rule.replacement) end else # :tag_aware # <pre> ブロック内のテキストは置換対象外とする # コードブロック内の ::: 等が <div> に変換されるのを防ぐ pre_blocks = [] protected = content.gsub(%r{<pre\b[^>]*>.*?</pre>}m) do |block| pre_blocks << block "#{PRE_PLACEHOLDER_PREFIX}TA#{pre_blocks.size - 1}#{PLACEHOLDER_SUFFIX}" end result, applied = replace_with_captures(protected, rule.pattern, rule.replacement) result = result.gsub(/#{Regexp.escape(PRE_PLACEHOLDER_PREFIX)}TA(\d+)#{Regexp.escape(PLACEHOLDER_SUFFIX)}/) do pre_blocks[Regexp.last_match(1).to_i] end [result, applied] end end |
.process_html_file(html_file, replace_rules) ⇒ Object
ReplacementRules::Rule の配列を適用して HTML ファイルを更新
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/vivlio_starter/cli/post_process/html_replacer.rb', line 37 def process_html_file(html_file, replace_rules) return { changed: false, replacements: 0 } unless replace_rules&.any? content = File.read(html_file, encoding: 'utf-8') replacements = 0 replace_rules.each do |rule| content, applied = apply_rule(content, rule) replacements += applied end if replacements.positive? File.write(html_file, content, encoding: 'utf-8') { changed: true, replacements: replacements } else { changed: false, replacements: 0 } end rescue StandardError => e Common.log_error("置換処理に失敗: #{html_file} - #{e.}") { changed: false, replacements: 0 } end |
.replace_with_captures(content, regex, replacement_str) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/vivlio_starter/cli/post_process/html_replacer.rb', line 134 def replace_with_captures(content, regex, replacement_str) applied = 0 new_content = content.gsub(regex) do match_data = ::Regexp.last_match result = replacement_str.dup (1..9).each { |i| result.gsub!("$#{i}", match_data[i].to_s) } applied += 1 result end [new_content, applied] end |
.with_text_scope_protected(content) ⇒ Object
テキスト専用ルール向けに、HTML 構造を全て退避したビューをブロックへ渡す。 以下の順で退避する(後続のマッチが先のプレースホルダを巻き込まないよう 大きい構造から順に退避):
1. <pre>...</pre> (フェンス付きコードブロック全体)
2. <code>...</code> (インラインコード/pre 内に残存するものも含む)
3. <...> (開始・終了タグ、自己閉じタグ、コメント)
終了後は逆順で復元する。
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/vivlio_starter/cli/post_process/html_replacer.rb', line 101 def with_text_scope_protected(content) pre_blocks = [] code_blocks = [] = [] stashed = content.gsub(%r{<pre\b[^>]*>.*?</pre>}m) do |block| pre_blocks << block "#{PRE_PLACEHOLDER_PREFIX}#{pre_blocks.size - 1}#{PLACEHOLDER_SUFFIX}" end stashed = stashed.gsub(%r{<code\b[^>]*>.*?</code>}m) do |block| code_blocks << block "#{CODE_PLACEHOLDER_PREFIX}#{code_blocks.size - 1}#{PLACEHOLDER_SUFFIX}" end stashed = stashed.gsub(/<[^>]*>/m) do |tag| << tag "#{TAG_PLACEHOLDER_PREFIX}#{.size - 1}#{PLACEHOLDER_SUFFIX}" end result, applied = yield(stashed) result = result.gsub(/#{Regexp.escape(TAG_PLACEHOLDER_PREFIX)}(\d+)#{Regexp.escape(PLACEHOLDER_SUFFIX)}/) do [Regexp.last_match(1).to_i] end result = result.gsub(/#{Regexp.escape(CODE_PLACEHOLDER_PREFIX)}(\d+)#{Regexp.escape(PLACEHOLDER_SUFFIX)}/) do code_blocks[Regexp.last_match(1).to_i] end result = result.gsub(/#{Regexp.escape(PRE_PLACEHOLDER_PREFIX)}(\d+)#{Regexp.escape(PLACEHOLDER_SUFFIX)}/) do pre_blocks[Regexp.last_match(1).to_i] end [result, applied] end |