From 379d027d66087a95f258f4778380840309a93d20 Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Fri, 31 Jul 2026 21:38:33 -0400 Subject: [PATCH 1/7] Debug mode: Never wrap head-emitting helper output in debug spans A partial rendered into a layout's has no node in its own AST, so the element-stack exclusion in `in_excluded_context?` never fires and helpers like `csrf_meta_tags` or `javascript_include_tag` got wrapped in a ``. Since is invalid head content, browsers implicitly close there and push the remaining scripts/stylesheets into , breaking Turbo apps. Add a name-based `head_content_helper?` skip (alongside the existing `complex_rails_helper?` guard) covering the standard head helpers and `tag.meta`/`tag.link`/`tag.title`/`tag.base`, and add `meta`, `link`, and `base` to the excluded tags for the inline-in-layout case. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01E16jrPfFSya7LHfBXMd5yg --- lib/herb/engine/debug_visitor.rb | 34 +++++++++++-- test/engine/debug_mode_test.rb | 51 +++++++++++++++++++ ...spans_ac8642523a9d8d78192fdd1760a2c8da.txt | 9 ++++ ...spans_9f677a05c60df1b58d547b49072d5733.txt | 13 +++++ ...spans_7358357688bbe51179504e5553468b2e.txt | 10 ++++ ...spans_89fb0c6b52349ca087eecfe6f2f5b627.txt | 9 ++++ ..._span_7bc38ba24da939d9e31615df69067289.txt | 6 +++ 7 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 test/snapshots/engine/debug_mode_test/test_0071_head_helpers_in_a_partial_do_NOT_get_debug_spans_ac8642523a9d8d78192fdd1760a2c8da.txt create mode 100644 test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_9f677a05c60df1b58d547b49072d5733.txt create mode 100644 test/snapshots/engine/debug_mode_test/test_0073_tag_helper_head_elements_in_a_partial_do_NOT_get_debug_spans_7358357688bbe51179504e5553468b2e.txt create mode 100644 test/snapshots/engine/debug_mode_test/test_0074_standalone_meta_link_and_base_content_erb_expressions_do_NOT_get_debug_spans_89fb0c6b52349ca087eecfe6f2f5b627.txt create mode 100644 test/snapshots/engine/debug_mode_test/test_0075_ordinary_body_output_in_a_partial_still_gets_debug_span_7bc38ba24da939d9e31615df69067289.txt diff --git a/lib/herb/engine/debug_visitor.rb b/lib/herb/engine/debug_visitor.rb index 6feb512c9..fceeaf947 100644 --- a/lib/herb/engine/debug_visitor.rb +++ b/lib/herb/engine/debug_visitor.rb @@ -76,7 +76,7 @@ def visit_erb_content_node(node) if !@in_attribute && !@in_html_comment && !@in_html_doctype && !in_excluded_context? && erb_output?(node.tag_opening.value) code = node.content.value.strip - @erb_nodes_to_wrap << node unless complex_rails_helper?(code) + @erb_nodes_to_wrap << node unless complex_rails_helper?(code) || head_content_helper?(code) end super @@ -214,7 +214,7 @@ def create_debug_span_for_erb(erb_node) code = erb_node.content.value.strip erb_code = "#{opening} #{code} %>" - return erb_node if complex_rails_helper?(code) + return erb_node if complex_rails_helper?(code) || head_content_helper?(code) line = erb_node.location&.start&.line column = erb_node.location&.start&.column @@ -336,7 +336,7 @@ def in_script_or_style_context? end def in_excluded_context? - excluded_tags = ["script", "style", "head", "title", "textarea", "pre", "svg", "math"] + excluded_tags = ["script", "style", "head", "title", "meta", "link", "base", "textarea", "pre", "svg", "math"] return true if excluded_tags.any? { |tag| @element_stack.include?(tag) } if @erb_block_stack.any? { |node| javascript_tag?(node.content.value.strip) || include_debug_disable_comment?(node.content.value.strip) } @@ -393,6 +393,34 @@ def javascript_tag?(code) false end + # Helpers whose output belongs in . Wrapping them in a + # would implicitly close in the browser, so they are never + # wrapped—even when rendered from a partial whose own AST has no + # element for in_excluded_context? to detect. + # TODO: Rewrite using Prism Nodes once available + def head_content_helper?(code) + cleaned_code = code.strip.gsub(/\s+/, " ") + + head_helpers = [ + "csrf_meta_tags", + "csp_meta_tag", + "viewport_meta_tag", + "javascript_include_tag", + "javascript_importmap_tags", + "stylesheet_link_tag", + "stylesheet_import_tag", + "favicon_link_tag", + "auto_discovery_link_tag", + "preload_link_tag" + ] + + return true if cleaned_code.match?(/\b(?:#{head_helpers.join("|")})\b/) + + return true if cleaned_code.match?(/\btag\.(?:meta|link|title|base)\b/) + + false + end + def include_debug_disable_comment?(code) cleaned_code = code.strip.gsub(/\s+/, " ") diff --git a/test/engine/debug_mode_test.rb b/test/engine/debug_mode_test.rb index 4aa8bfab5..ca9aa9b93 100644 --- a/test/engine/debug_mode_test.rb +++ b/test/engine/debug_mode_test.rb @@ -616,5 +616,56 @@ class DebugModeTest < Minitest::Spec <%= @page_title %> ERB end + + test "head helpers in a partial do NOT get debug spans" do + template = <<~ERB + <%= csrf_meta_tags %> + <%= javascript_include_tag "application", defer: true %> + <%= stylesheet_link_tag "application" %> + ERB + + assert_compiled_snapshot(template, debug: true, filename: "_head.html.erb") + end + + test "more head helpers in a partial do NOT get debug spans" do + template = <<~ERB + <%= csp_meta_tag %> + <%= viewport_meta_tag %> + <%= javascript_importmap_tags %> + <%= stylesheet_import_tag "application" %> + <%= favicon_link_tag %> + <%= auto_discovery_link_tag(:rss, articles_url) %> + <%= preload_link_tag "fonts/inter.woff2" %> + ERB + + assert_compiled_snapshot(template, debug: true, filename: "_head.html.erb") + end + + test "tag helper head elements in a partial do NOT get debug spans" do + template = <<~ERB + <%= tag.meta charset: "utf-8" %> + <%= tag.title %> + <%= tag.link rel: "canonical", href: canonical_url %> + <%= tag.base href: "/" %> + ERB + + assert_compiled_snapshot(template, debug: true, filename: "_head.html.erb") + end + + test "standalone meta link and base content erb expressions do NOT get debug spans" do + template = <<~ERB + + + + ERB + + assert_compiled_snapshot(template, debug: true) + end + + test "ordinary body output in a partial still gets debug span" do + template = "
<%= @name %>
" + + assert_compiled_snapshot(template, debug: true, filename: "_card.html.erb") + end end end diff --git a/test/snapshots/engine/debug_mode_test/test_0071_head_helpers_in_a_partial_do_NOT_get_debug_spans_ac8642523a9d8d78192fdd1760a2c8da.txt b/test/snapshots/engine/debug_mode_test/test_0071_head_helpers_in_a_partial_do_NOT_get_debug_spans_ac8642523a9d8d78192fdd1760a2c8da.txt new file mode 100644 index 000000000..f2fe2e1db --- /dev/null +++ b/test/snapshots/engine/debug_mode_test/test_0071_head_helpers_in_a_partial_do_NOT_get_debug_spans_ac8642523a9d8d78192fdd1760a2c8da.txt @@ -0,0 +1,9 @@ +--- +source: "Engine::DebugModeTest#test_0071_head helpers in a partial do NOT get debug spans" +input: "{source: \"<%= csrf_meta_tags %>\\n<%= javascript_include_tag \\\"application\\\", defer: true %>\\n<%= stylesheet_link_tag \\\"application\\\" %>\\n\", options: {debug: true, filename: \"_head.html.erb\"}}" +--- +_buf = ::String.new; _buf << (csrf_meta_tags).to_s; _buf << ' +'.freeze; _buf << (javascript_include_tag "application", defer: true).to_s; _buf << ' +'.freeze; _buf << (stylesheet_link_tag "application").to_s; _buf << ' +'.freeze; +_buf.to_s diff --git a/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_9f677a05c60df1b58d547b49072d5733.txt b/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_9f677a05c60df1b58d547b49072d5733.txt new file mode 100644 index 000000000..67c37cfb0 --- /dev/null +++ b/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_9f677a05c60df1b58d547b49072d5733.txt @@ -0,0 +1,13 @@ +--- +source: "Engine::DebugModeTest#test_0072_more head helpers in a partial do NOT get debug spans" +input: "{source: \"<%= csp_meta_tag %>\\n<%= viewport_meta_tag %>\\n<%= javascript_importmap_tags %>\\n<%= stylesheet_import_tag \\\"application\\\" %>\\n<%= favicon_link_tag %>\\n<%= auto_discovery_link_tag(:rss, articles_url) %>\\n<%= preload_link_tag \\\"fonts/inter.woff2\\\" %>\\n\", options: {debug: true, filename: \"_head.html.erb\"}}" +--- +_buf = ::String.new; _buf << (csp_meta_tag).to_s; _buf << ' +'.freeze; _buf << (viewport_meta_tag).to_s; _buf << ' +'.freeze; _buf << (javascript_importmap_tags).to_s; _buf << ' +'.freeze; _buf << (stylesheet_import_tag "application").to_s; _buf << ' +'.freeze; _buf << (favicon_link_tag).to_s; _buf << ' +'.freeze; _buf << (auto_discovery_link_tag(:rss, articles_url)).to_s; _buf << ' +'.freeze; _buf << (preload_link_tag "fonts/inter.woff2").to_s; _buf << ' +'.freeze; +_buf.to_s diff --git a/test/snapshots/engine/debug_mode_test/test_0073_tag_helper_head_elements_in_a_partial_do_NOT_get_debug_spans_7358357688bbe51179504e5553468b2e.txt b/test/snapshots/engine/debug_mode_test/test_0073_tag_helper_head_elements_in_a_partial_do_NOT_get_debug_spans_7358357688bbe51179504e5553468b2e.txt new file mode 100644 index 000000000..2745238cd --- /dev/null +++ b/test/snapshots/engine/debug_mode_test/test_0073_tag_helper_head_elements_in_a_partial_do_NOT_get_debug_spans_7358357688bbe51179504e5553468b2e.txt @@ -0,0 +1,10 @@ +--- +source: "Engine::DebugModeTest#test_0073_tag helper head elements in a partial do NOT get debug spans" +input: "{source: \"<%= tag.meta charset: \\\"utf-8\\\" %>\\n<%= tag.title %>\\n<%= tag.link rel: \\\"canonical\\\", href: canonical_url %>\\n<%= tag.base href: \\\"/\\\" %>\\n\", options: {debug: true, filename: \"_head.html.erb\"}}" +--- +_buf = ::String.new; _buf << (tag.meta charset: "utf-8").to_s; _buf << ' +'.freeze; _buf << (tag.title).to_s; _buf << ' +'.freeze; _buf << (tag.link rel: "canonical", href: canonical_url).to_s; _buf << ' +'.freeze; _buf << (tag.base href: "/").to_s; _buf << ' +'.freeze; +_buf.to_s diff --git a/test/snapshots/engine/debug_mode_test/test_0074_standalone_meta_link_and_base_content_erb_expressions_do_NOT_get_debug_spans_89fb0c6b52349ca087eecfe6f2f5b627.txt b/test/snapshots/engine/debug_mode_test/test_0074_standalone_meta_link_and_base_content_erb_expressions_do_NOT_get_debug_spans_89fb0c6b52349ca087eecfe6f2f5b627.txt new file mode 100644 index 000000000..9dff3c3b6 --- /dev/null +++ b/test/snapshots/engine/debug_mode_test/test_0074_standalone_meta_link_and_base_content_erb_expressions_do_NOT_get_debug_spans_89fb0c6b52349ca087eecfe6f2f5b627.txt @@ -0,0 +1,9 @@ +--- +source: "Engine::DebugModeTest#test_0074_standalone meta link and base content erb expressions do NOT get debug spans" +input: "{source: \"\\\">\\n\\\">\\n\\\">\\n\", options: {debug: true}}" +--- +_buf = ::String.new; _buf << ' + + +'.freeze; +_buf.to_s diff --git a/test/snapshots/engine/debug_mode_test/test_0075_ordinary_body_output_in_a_partial_still_gets_debug_span_7bc38ba24da939d9e31615df69067289.txt b/test/snapshots/engine/debug_mode_test/test_0075_ordinary_body_output_in_a_partial_still_gets_debug_span_7bc38ba24da939d9e31615df69067289.txt new file mode 100644 index 000000000..6716d360d --- /dev/null +++ b/test/snapshots/engine/debug_mode_test/test_0075_ordinary_body_output_in_a_partial_still_gets_debug_span_7bc38ba24da939d9e31615df69067289.txt @@ -0,0 +1,6 @@ +--- +source: "Engine::DebugModeTest#test_0075_ordinary body output in a partial still gets debug span" +input: "{source: \"
<%= @name %>
\", options: {debug: true, filename: \"_card.html.erb\"}}" +--- +_buf = ::String.new; _buf << '
'.freeze; _buf << (@name).to_s; _buf << '
'.freeze; +_buf.to_s From 36c27e9a28000f87d4126b859cb1b3161f161623 Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Sat, 1 Aug 2026 17:42:39 -0400 Subject: [PATCH 2/7] Remove explanatory comment from head_content_helper? Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01E16jrPfFSya7LHfBXMd5yg --- lib/herb/engine/debug_visitor.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/herb/engine/debug_visitor.rb b/lib/herb/engine/debug_visitor.rb index fceeaf947..b202abf3a 100644 --- a/lib/herb/engine/debug_visitor.rb +++ b/lib/herb/engine/debug_visitor.rb @@ -393,10 +393,6 @@ def javascript_tag?(code) false end - # Helpers whose output belongs in . Wrapping them in a - # would implicitly close in the browser, so they are never - # wrapped—even when rendered from a partial whose own AST has no - # element for in_excluded_context? to detect. # TODO: Rewrite using Prism Nodes once available def head_content_helper?(code) cleaned_code = code.strip.gsub(/\s+/, " ") From 5ce00a613a5ff2a63ee142c1d7effdcf2ed028af Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Sat, 1 Aug 2026 17:42:39 -0400 Subject: [PATCH 3/7] Add RBS signature for head_content_helper? Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01E16jrPfFSya7LHfBXMd5yg --- sig/herb/engine/debug_visitor.rbs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sig/herb/engine/debug_visitor.rbs b/sig/herb/engine/debug_visitor.rbs index cb70203d7..7cd309989 100644 --- a/sig/herb/engine/debug_visitor.rbs +++ b/sig/herb/engine/debug_visitor.rbs @@ -77,6 +77,9 @@ module Herb # TODO: Rewrite using Prism Nodes once available def javascript_tag?: (untyped code) -> untyped + # TODO: Rewrite using Prism Nodes once available + def head_content_helper?: (untyped code) -> untyped + def include_debug_disable_comment?: (untyped code) -> untyped end end From 53811bd5f9e8afabae6e6c217e6cf24046b3d8b1 Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Thu, 6 Aug 2026 22:22:35 -0400 Subject: [PATCH 4/7] Registry: Add head_content metadata to Action View helpers Flag helpers whose output belongs in (meta/link/script includes from actionview, actioncable, importmap-rails, and turbo-rails) with head_content: true in their registry YAMLs, and expose them through HelperRegistry.head_content_helpers / head_content?(name) so consumers can query the list instead of hardcoding helper names. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01E16jrPfFSya7LHfBXMd5yg --- .../action_cable_meta_tag.yml | 1 + .../auto_discovery_link_tag.yml | 1 + .../asset_tag_helper/favicon_link_tag.yml | 1 + .../javascript_include_tag.yml | 1 + .../asset_tag_helper/preload_link_tag.yml | 1 + .../asset_tag_helper/stylesheet_link_tag.yml | 1 + .../actionview/csp_helper/csp_meta_tag.yml | 1 + .../actionview/csrf_helper/csrf_meta_tags.yml | 1 + .../javascript_import_module_tag.yml | 1 + ...vascript_importmap_module_preload_tags.yml | 1 + .../javascript_importmap_tags.yml | 1 + .../javascript_inline_importmap_tag.yml | 1 + .../javascript_module_preload_tag.yml | 1 + .../turbo_exempts_page_from_cache_tag.yml | 1 + .../turbo_exempts_page_from_preview_tag.yml | 1 + .../turbo_page_requires_reload_tag.yml | 1 + .../drive_helper/turbo_refresh_method_tag.yml | 1 + .../drive_helper/turbo_refresh_scroll_tag.yml | 1 + .../drive_helper/turbo_refreshes_with.yml | 1 + .../includes_helper/turbo_include_tags.yml | 1 + sig/herb/action_view/helper_registry.rbs | 15 +++++++++++-- .../herb/action_view/helper_registry.rb.erb | 22 +++++++++++++++++-- templates/template.rb | 5 +++++ ...spans_9f677a05c60df1b58d547b49072d5733.txt | 13 ----------- 24 files changed, 58 insertions(+), 17 deletions(-) delete mode 100644 test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_9f677a05c60df1b58d547b49072d5733.txt diff --git a/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml b/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml index 5560620b5..c2f1a0b17 100644 --- a/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml +++ b/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml @@ -4,6 +4,7 @@ source: "ActionCable::Helpers::ActionCableHelper#action_cable_meta_tag" gem: "actioncable" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "action_cable_meta_tag" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml index a74f85a84..ee906b6bb 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml @@ -4,6 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#auto_discovery_link_tag" gem: "actionview" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml index fcd7dac25..6d4984b46 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml @@ -4,6 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#favicon_link_tag" gem: "actionview" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "favicon_link_tag(source = \"favicon.ico\", options = {})" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml index 0dd7d13ee..97dff517e 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml @@ -4,6 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#javascript_include_tag" gem: "actionview" output: "html" visibility: "public" +head_content: true supported: true supports_block: false signature: "javascript_include_tag(*sources)" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml index 965382dee..95628c150 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml @@ -4,6 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#preload_link_tag" gem: "actionview" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "preload_link_tag(source, options = {})" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml index ca9a0ade9..ad8918a70 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml @@ -4,6 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#stylesheet_link_tag" gem: "actionview" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "stylesheet_link_tag(*sources)" diff --git a/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml b/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml index 79737e02f..6e6eb03a3 100644 --- a/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml +++ b/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml @@ -4,6 +4,7 @@ source: "ActionView::Helpers::CspHelper#csp_meta_tag" gem: "actionview" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "csp_meta_tag(**options)" diff --git a/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml b/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml index f8ab958a8..f7c0ae5be 100644 --- a/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml +++ b/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml @@ -4,6 +4,7 @@ source: "ActionView::Helpers::CsrfHelper#csrf_meta_tags" gem: "actionview" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "csrf_meta_tags" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml index 40d9a4535..09170c47a 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml @@ -4,6 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_import_module_tag" gem: "importmap-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "javascript_import_module_tag(*module_names)" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml index 7c8ef9d94..ba6b3173e 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml @@ -4,6 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_importmap_module_preload_tags gem: "importmap-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "javascript_importmap_module_preload_tags(importmap = Rails.application.importmap, entry_point: \"application\")" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml index 5544fcbbe..126c20f59 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml @@ -4,6 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_importmap_tags" gem: "importmap-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "javascript_importmap_tags(entry_point = \"application\", importmap: Rails.application.importmap)" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml index e9164e15b..80a428a96 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml @@ -4,6 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_inline_importmap_tag" gem: "importmap-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "javascript_inline_importmap_tag(importmap_json = Rails.application.importmap.to_json(resolver: self))" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml index 508e33145..2f0edfc4e 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml @@ -4,6 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_module_preload_tag" gem: "importmap-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "javascript_module_preload_tag(*paths)" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml index c3e415521..defaf48c9 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml @@ -4,6 +4,7 @@ source: "Turbo::DriveHelper#turbo_exempts_page_from_cache_tag" gem: "turbo-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "turbo_exempts_page_from_cache_tag" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml index a3e01e2e5..28b77f83d 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml @@ -4,6 +4,7 @@ source: "Turbo::DriveHelper#turbo_exempts_page_from_preview_tag" gem: "turbo-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "turbo_exempts_page_from_preview_tag" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml index cd214305c..89b2f6ebf 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml @@ -4,6 +4,7 @@ source: "Turbo::DriveHelper#turbo_page_requires_reload_tag" gem: "turbo-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "turbo_page_requires_reload_tag" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml index 29bb9663e..2e5ed750d 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml @@ -4,6 +4,7 @@ source: "Turbo::DriveHelper#turbo_refresh_method_tag" gem: "turbo-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "turbo_refresh_method_tag(method = :replace)" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml index d45f3b5e4..7e94962e2 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml @@ -4,6 +4,7 @@ source: "Turbo::DriveHelper#turbo_refresh_scroll_tag" gem: "turbo-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "turbo_refresh_scroll_tag(scroll = :reset)" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml index efc4db805..0dfdd417c 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml @@ -4,6 +4,7 @@ source: "Turbo::DriveHelper#turbo_refreshes_with" gem: "turbo-rails" output: "void" visibility: "public" +head_content: true supported: false supports_block: false signature: "turbo_refreshes_with(method: :replace, scroll: :reset)" diff --git a/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml b/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml index 595aa9311..1aa9e006b 100644 --- a/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml +++ b/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml @@ -4,6 +4,7 @@ source: "Turbo::IncludesHelper#turbo_include_tags" gem: "turbo-rails" output: "html" visibility: "public" +head_content: true supported: false supports_block: false signature: "turbo_include_tags" diff --git a/sig/herb/action_view/helper_registry.rbs b/sig/herb/action_view/helper_registry.rbs index 92e49780c..c24a2673b 100644 --- a/sig/herb/action_view/helper_registry.rbs +++ b/sig/herb/action_view/helper_registry.rbs @@ -608,8 +608,8 @@ module Herb attr_reader aliases: Array[String] - # : (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void - def initialize: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void + # : (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, head_content: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void + def initialize: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, head_content: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void # : () -> bool def void?: () -> bool @@ -623,6 +623,9 @@ module Herb # : () -> bool def supported?: () -> bool + # : () -> bool + def head_content?: () -> bool + # : () -> bool def static_tag_name?: () -> bool @@ -1139,6 +1142,8 @@ module Herb BY_TAG_NAME: Hash[String, Array[HelperEntry]] + HEAD_CONTENT: Array[HelperEntry] + # : (String) -> HelperEntry? def self.get: (String) -> HelperEntry? @@ -1169,6 +1174,12 @@ module Herb # : () -> Array[HelperEntry] def self.supported: () -> Array[HelperEntry] + # : () -> Array[HelperEntry] + def self.head_content_helpers: () -> Array[HelperEntry] + + # : (String) -> bool + def self.head_content?: (String) -> bool + # : (String) -> Array[HelperEntry] def self.by_gem: (String) -> Array[HelperEntry] diff --git a/templates/lib/herb/action_view/helper_registry.rb.erb b/templates/lib/herb/action_view/helper_registry.rb.erb index 136eccff5..a9309eb34 100644 --- a/templates/lib/herb/action_view/helper_registry.rb.erb +++ b/templates/lib/herb/action_view/helper_registry.rb.erb @@ -113,10 +113,10 @@ module Herb attr_reader :special_behaviors #: Array[Symbol] attr_reader :aliases #: Array[String] - #: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void + #: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, head_content: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void def initialize( # rubocop:disable Metrics/ParameterLists name:, type:, source:, gem:, output:, visibility:, tag_name:, is_void:, supports_block:, - preferred_for_tag:, supported:, detect_style:, description:, signature:, documentation_url:, + preferred_for_tag:, supported:, head_content:, detect_style:, description:, signature:, documentation_url:, implicit_attribute:, arguments:, options:, block_arguments:, special_behaviors:, aliases: ) @name = name @@ -130,6 +130,7 @@ module Herb @supports_block = supports_block @preferred_for_tag = preferred_for_tag @supported = supported + @head_content = head_content @detect_style = detect_style @description = description @signature = signature @@ -154,6 +155,9 @@ module Herb #: () -> bool def supported? = @supported + #: () -> bool + def head_content? = @head_content + #: () -> bool def static_tag_name? = !@tag_name.nil? @@ -181,6 +185,7 @@ module Herb supports_block: <%= helper.supports_block %>, preferred_for_tag: <%= helper.preferred_for_tag %>, supported: <%= helper.supported %>, + head_content: <%= helper.head_content? %>, detect_style: :<%= helper.detect_style || "call_name" %>, description: "<%= helper.escaped_description %>", signature: "<%= helper.escaped_signature %>", @@ -239,6 +244,8 @@ module Herb <%- end -%> }.freeze #: Hash[String, Array[HelperEntry]] + HEAD_CONTENT = [<%= helpers.select(&:head_content?).map(&:constant_name).join(", ") %>].freeze #: Array[HelperEntry] + class << self #: (String) -> HelperEntry? def get(name) @@ -294,6 +301,17 @@ module Herb entries.select(&:supported?) end + #: () -> Array[HelperEntry] + def head_content_helpers + HEAD_CONTENT + end + + #: (String) -> bool + def head_content?(name) + entry = get(name) + entry&.head_content? || false + end + #: (String) -> Array[HelperEntry] def by_gem(gem_name) entries.select { |entry| entry.gem == gem_name } diff --git a/templates/template.rb b/templates/template.rb index 37fe1ec96..19418a4fd 100755 --- a/templates/template.rb +++ b/templates/template.rb @@ -684,6 +684,7 @@ def initialize(config) @visibility = config.fetch("visibility", "public") @supports_block = config.fetch("supports_block", false) @supported = config.fetch("supported", false) + @head_content = config.fetch("head_content", false) @description = config.fetch("description", "").strip @signature = config.fetch("signature") @documentation_url = config.fetch("documentation_url") @@ -788,6 +789,10 @@ def supported? @supported end + def head_content? + @head_content + end + def static_tag_name? !tag_name.nil? end diff --git a/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_9f677a05c60df1b58d547b49072d5733.txt b/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_9f677a05c60df1b58d547b49072d5733.txt deleted file mode 100644 index 67c37cfb0..000000000 --- a/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_9f677a05c60df1b58d547b49072d5733.txt +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: "Engine::DebugModeTest#test_0072_more head helpers in a partial do NOT get debug spans" -input: "{source: \"<%= csp_meta_tag %>\\n<%= viewport_meta_tag %>\\n<%= javascript_importmap_tags %>\\n<%= stylesheet_import_tag \\\"application\\\" %>\\n<%= favicon_link_tag %>\\n<%= auto_discovery_link_tag(:rss, articles_url) %>\\n<%= preload_link_tag \\\"fonts/inter.woff2\\\" %>\\n\", options: {debug: true, filename: \"_head.html.erb\"}}" ---- -_buf = ::String.new; _buf << (csp_meta_tag).to_s; _buf << ' -'.freeze; _buf << (viewport_meta_tag).to_s; _buf << ' -'.freeze; _buf << (javascript_importmap_tags).to_s; _buf << ' -'.freeze; _buf << (stylesheet_import_tag "application").to_s; _buf << ' -'.freeze; _buf << (favicon_link_tag).to_s; _buf << ' -'.freeze; _buf << (auto_discovery_link_tag(:rss, articles_url)).to_s; _buf << ' -'.freeze; _buf << (preload_link_tag "fonts/inter.woff2").to_s; _buf << ' -'.freeze; -_buf.to_s From a27c27b53a804645fd3bbc1d7a63be0fe7e4e61e Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Thu, 6 Aug 2026 22:22:35 -0400 Subject: [PATCH 5/7] Debug mode: Derive head-content helpers from the helper registry Replace the hardcoded helper name list in head_content_helper? with a pattern built from HelperRegistry.head_content_helpers. This picks up helpers the list missed (action_cable_meta_tag, the turbo-rails meta tag helpers, turbo_include_tags, and the remaining importmap-rails helpers) and drops viewport_meta_tag / stylesheet_import_tag, which don't exist in any registered gem. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01E16jrPfFSya7LHfBXMd5yg --- lib/herb/engine/debug_visitor.rb | 19 +++++-------------- sig/herb/engine/debug_visitor.rbs | 2 ++ test/engine/debug_mode_test.rb | 16 ++++++++++++++-- ...spans_55be974fcc401ea8fb347cda7cddbf76.txt | 13 +++++++++++++ ...spans_b7b6cba73c2f44ebb55c70cca71cef03.txt | 11 +++++++++++ 5 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_55be974fcc401ea8fb347cda7cddbf76.txt create mode 100644 test/snapshots/engine/debug_mode_test/test_0076_turbo_and_action_cable_head_helpers_in_a_partial_do_NOT_get_debug_spans_b7b6cba73c2f44ebb55c70cca71cef03.txt diff --git a/lib/herb/engine/debug_visitor.rb b/lib/herb/engine/debug_visitor.rb index fd2430f36..86ea8c10a 100644 --- a/lib/herb/engine/debug_visitor.rb +++ b/lib/herb/engine/debug_visitor.rb @@ -1,9 +1,13 @@ # frozen_string_literal: true # typed: false +require_relative "../action_view/helper_registry" + module Herb class Engine class DebugVisitor < Herb::Visitor + HEAD_CONTENT_HELPER_PATTERN = /\b(?:#{Herb::ActionView::HelperRegistry.head_content_helpers.map { |helper| Regexp.escape(helper.name) }.join("|")})\b/ #: Regexp + def initialize(file_path: nil, project_path: nil) super() @@ -390,20 +394,7 @@ def javascript_tag?(code) def head_content_helper?(code) cleaned_code = code.strip.gsub(/\s+/, " ") - head_helpers = [ - "csrf_meta_tags", - "csp_meta_tag", - "viewport_meta_tag", - "javascript_include_tag", - "javascript_importmap_tags", - "stylesheet_link_tag", - "stylesheet_import_tag", - "favicon_link_tag", - "auto_discovery_link_tag", - "preload_link_tag" - ] - - return true if cleaned_code.match?(/\b(?:#{head_helpers.join("|")})\b/) + return true if cleaned_code.match?(HEAD_CONTENT_HELPER_PATTERN) return true if cleaned_code.match?(/\btag\.(?:meta|link|title|base)\b/) diff --git a/sig/herb/engine/debug_visitor.rbs b/sig/herb/engine/debug_visitor.rbs index 9e1412ff2..45abc3294 100644 --- a/sig/herb/engine/debug_visitor.rbs +++ b/sig/herb/engine/debug_visitor.rbs @@ -3,6 +3,8 @@ module Herb class Engine class DebugVisitor < Herb::Visitor + HEAD_CONTENT_HELPER_PATTERN: Regexp + def initialize: (?file_path: untyped, ?project_path: untyped) -> untyped def visit_document_node: (untyped node) -> untyped diff --git a/test/engine/debug_mode_test.rb b/test/engine/debug_mode_test.rb index ca9aa9b93..1cac3d170 100644 --- a/test/engine/debug_mode_test.rb +++ b/test/engine/debug_mode_test.rb @@ -630,9 +630,9 @@ class DebugModeTest < Minitest::Spec test "more head helpers in a partial do NOT get debug spans" do template = <<~ERB <%= csp_meta_tag %> - <%= viewport_meta_tag %> <%= javascript_importmap_tags %> - <%= stylesheet_import_tag "application" %> + <%= javascript_inline_importmap_tag %> + <%= javascript_import_module_tag "application" %> <%= favicon_link_tag %> <%= auto_discovery_link_tag(:rss, articles_url) %> <%= preload_link_tag "fonts/inter.woff2" %> @@ -667,5 +667,17 @@ class DebugModeTest < Minitest::Spec assert_compiled_snapshot(template, debug: true, filename: "_card.html.erb") end + + test "turbo and action cable head helpers in a partial do NOT get debug spans" do + template = <<~ERB + <%= action_cable_meta_tag %> + <%= turbo_refreshes_with method: :morph, scroll: :preserve %> + <%= turbo_refresh_method_tag :morph %> + <%= turbo_page_requires_reload_tag %> + <%= turbo_include_tags %> + ERB + + assert_compiled_snapshot(template, debug: true, filename: "_head.html.erb") + end end end diff --git a/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_55be974fcc401ea8fb347cda7cddbf76.txt b/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_55be974fcc401ea8fb347cda7cddbf76.txt new file mode 100644 index 000000000..e61096621 --- /dev/null +++ b/test/snapshots/engine/debug_mode_test/test_0072_more_head_helpers_in_a_partial_do_NOT_get_debug_spans_55be974fcc401ea8fb347cda7cddbf76.txt @@ -0,0 +1,13 @@ +--- +source: "Engine::DebugModeTest#test_0072_more head helpers in a partial do NOT get debug spans" +input: "{source: \"<%= csp_meta_tag %>\\n<%= javascript_importmap_tags %>\\n<%= javascript_inline_importmap_tag %>\\n<%= javascript_import_module_tag \\\"application\\\" %>\\n<%= favicon_link_tag %>\\n<%= auto_discovery_link_tag(:rss, articles_url) %>\\n<%= preload_link_tag \\\"fonts/inter.woff2\\\" %>\\n\", options: {debug: true, filename: \"_head.html.erb\"}}" +--- +_buf = ::String.new; _buf << (csp_meta_tag).to_s; _buf << ' +'.freeze; _buf << (javascript_importmap_tags).to_s; _buf << ' +'.freeze; _buf << (javascript_inline_importmap_tag).to_s; _buf << ' +'.freeze; _buf << (javascript_import_module_tag "application").to_s; _buf << ' +'.freeze; _buf << (favicon_link_tag).to_s; _buf << ' +'.freeze; _buf << (auto_discovery_link_tag(:rss, articles_url)).to_s; _buf << ' +'.freeze; _buf << (preload_link_tag "fonts/inter.woff2").to_s; _buf << ' +'.freeze; +_buf.to_s diff --git a/test/snapshots/engine/debug_mode_test/test_0076_turbo_and_action_cable_head_helpers_in_a_partial_do_NOT_get_debug_spans_b7b6cba73c2f44ebb55c70cca71cef03.txt b/test/snapshots/engine/debug_mode_test/test_0076_turbo_and_action_cable_head_helpers_in_a_partial_do_NOT_get_debug_spans_b7b6cba73c2f44ebb55c70cca71cef03.txt new file mode 100644 index 000000000..024aecadb --- /dev/null +++ b/test/snapshots/engine/debug_mode_test/test_0076_turbo_and_action_cable_head_helpers_in_a_partial_do_NOT_get_debug_spans_b7b6cba73c2f44ebb55c70cca71cef03.txt @@ -0,0 +1,11 @@ +--- +source: "Engine::DebugModeTest#test_0076_turbo and action cable head helpers in a partial do NOT get debug spans" +input: "{source: \"<%= action_cable_meta_tag %>\\n<%= turbo_refreshes_with method: :morph, scroll: :preserve %>\\n<%= turbo_refresh_method_tag :morph %>\\n<%= turbo_page_requires_reload_tag %>\\n<%= turbo_include_tags %>\\n\", options: {debug: true, filename: \"_head.html.erb\"}}" +--- +_buf = ::String.new; _buf << (action_cable_meta_tag).to_s; _buf << ' +'.freeze; _buf << (turbo_refreshes_with method: :morph, scroll: :preserve).to_s; _buf << ' +'.freeze; _buf << (turbo_refresh_method_tag :morph).to_s; _buf << ' +'.freeze; _buf << (turbo_page_requires_reload_tag).to_s; _buf << ' +'.freeze; _buf << (turbo_include_tags).to_s; _buf << ' +'.freeze; +_buf.to_s From d949a5be233d7a1f1de3c6776cc825efe3fb0f37 Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Thu, 6 Aug 2026 22:49:31 -0400 Subject: [PATCH 6/7] Make HEAD_CONTENT_HELPER_PATTERN a private constant Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01E16jrPfFSya7LHfBXMd5yg --- lib/herb/engine/debug_visitor.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/herb/engine/debug_visitor.rb b/lib/herb/engine/debug_visitor.rb index 86ea8c10a..a85210b73 100644 --- a/lib/herb/engine/debug_visitor.rb +++ b/lib/herb/engine/debug_visitor.rb @@ -7,6 +7,7 @@ module Herb class Engine class DebugVisitor < Herb::Visitor HEAD_CONTENT_HELPER_PATTERN = /\b(?:#{Herb::ActionView::HelperRegistry.head_content_helpers.map { |helper| Regexp.escape(helper.name) }.join("|")})\b/ #: Regexp + private_constant :HEAD_CONTENT_HELPER_PATTERN def initialize(file_path: nil, project_path: nil) super() From a2b2588a8bbb289d887dc57439baff1e29b351a9 Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Fri, 7 Aug 2026 18:02:46 -0400 Subject: [PATCH 7/7] Registry: Generalize head_content flag into a context field Replace the head_content: true boolean with context: "head" in the helper YAMLs so other helpers can declare other contexts (e.g. "body", "html", "table") without adding a new flag per placement. The registry now exposes BY_CONTEXT and HelperRegistry.by_context(context) in place of HEAD_CONTENT and head_content_helpers. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01E16jrPfFSya7LHfBXMd5yg --- .../action_cable_meta_tag.yml | 2 +- .../auto_discovery_link_tag.yml | 2 +- .../asset_tag_helper/favicon_link_tag.yml | 2 +- .../javascript_include_tag.yml | 2 +- .../asset_tag_helper/preload_link_tag.yml | 2 +- .../asset_tag_helper/stylesheet_link_tag.yml | 2 +- .../actionview/csp_helper/csp_meta_tag.yml | 2 +- .../actionview/csrf_helper/csrf_meta_tags.yml | 2 +- .../javascript_import_module_tag.yml | 2 +- ...vascript_importmap_module_preload_tags.yml | 2 +- .../javascript_importmap_tags.yml | 2 +- .../javascript_inline_importmap_tag.yml | 2 +- .../javascript_module_preload_tag.yml | 2 +- .../turbo_exempts_page_from_cache_tag.yml | 2 +- .../turbo_exempts_page_from_preview_tag.yml | 2 +- .../turbo_page_requires_reload_tag.yml | 2 +- .../drive_helper/turbo_refresh_method_tag.yml | 2 +- .../drive_helper/turbo_refresh_scroll_tag.yml | 2 +- .../drive_helper/turbo_refreshes_with.yml | 2 +- .../includes_helper/turbo_include_tags.yml | 2 +- lib/herb/engine/debug_visitor.rb | 2 +- sig/herb/action_view/helper_registry.rbs | 18 ++++------ .../herb/action_view/helper_registry.rb.erb | 34 +++++++++---------- templates/template.rb | 8 ++--- 24 files changed, 46 insertions(+), 56 deletions(-) diff --git a/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml b/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml index c2f1a0b17..c03e43a12 100644 --- a/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml +++ b/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml @@ -4,7 +4,7 @@ source: "ActionCable::Helpers::ActionCableHelper#action_cable_meta_tag" gem: "actioncable" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "action_cable_meta_tag" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml index ee906b6bb..30a7b5828 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml @@ -4,7 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#auto_discovery_link_tag" gem: "actionview" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml index 6d4984b46..921a0780b 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml @@ -4,7 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#favicon_link_tag" gem: "actionview" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "favicon_link_tag(source = \"favicon.ico\", options = {})" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml index 97dff517e..9515c6e01 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml @@ -4,7 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#javascript_include_tag" gem: "actionview" output: "html" visibility: "public" -head_content: true +context: "head" supported: true supports_block: false signature: "javascript_include_tag(*sources)" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml index 95628c150..4de6f3004 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml @@ -4,7 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#preload_link_tag" gem: "actionview" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "preload_link_tag(source, options = {})" diff --git a/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml b/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml index ad8918a70..2e37e6d8b 100644 --- a/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml +++ b/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml @@ -4,7 +4,7 @@ source: "ActionView::Helpers::AssetTagHelper#stylesheet_link_tag" gem: "actionview" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "stylesheet_link_tag(*sources)" diff --git a/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml b/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml index 6e6eb03a3..689d81bd5 100644 --- a/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml +++ b/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml @@ -4,7 +4,7 @@ source: "ActionView::Helpers::CspHelper#csp_meta_tag" gem: "actionview" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "csp_meta_tag(**options)" diff --git a/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml b/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml index f7c0ae5be..e7569d57d 100644 --- a/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml +++ b/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml @@ -4,7 +4,7 @@ source: "ActionView::Helpers::CsrfHelper#csrf_meta_tags" gem: "actionview" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "csrf_meta_tags" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml index 09170c47a..5b315af46 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml @@ -4,7 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_import_module_tag" gem: "importmap-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "javascript_import_module_tag(*module_names)" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml index ba6b3173e..598e63c92 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml @@ -4,7 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_importmap_module_preload_tags gem: "importmap-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "javascript_importmap_module_preload_tags(importmap = Rails.application.importmap, entry_point: \"application\")" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml index 126c20f59..b85054b5e 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml @@ -4,7 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_importmap_tags" gem: "importmap-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "javascript_importmap_tags(entry_point = \"application\", importmap: Rails.application.importmap)" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml index 80a428a96..99d1cc0df 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml @@ -4,7 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_inline_importmap_tag" gem: "importmap-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "javascript_inline_importmap_tag(importmap_json = Rails.application.importmap.to_json(resolver: self))" diff --git a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml index 2f0edfc4e..d43bc60e0 100644 --- a/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml +++ b/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml @@ -4,7 +4,7 @@ source: "Importmap::ImportmapTagsHelper#javascript_module_preload_tag" gem: "importmap-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "javascript_module_preload_tag(*paths)" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml index defaf48c9..27e6b5724 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml @@ -4,7 +4,7 @@ source: "Turbo::DriveHelper#turbo_exempts_page_from_cache_tag" gem: "turbo-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "turbo_exempts_page_from_cache_tag" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml index 28b77f83d..3e5bd5dd7 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml @@ -4,7 +4,7 @@ source: "Turbo::DriveHelper#turbo_exempts_page_from_preview_tag" gem: "turbo-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "turbo_exempts_page_from_preview_tag" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml index 89b2f6ebf..7c83b7bf7 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml @@ -4,7 +4,7 @@ source: "Turbo::DriveHelper#turbo_page_requires_reload_tag" gem: "turbo-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "turbo_page_requires_reload_tag" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml index 2e5ed750d..4b41f7dd6 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml @@ -4,7 +4,7 @@ source: "Turbo::DriveHelper#turbo_refresh_method_tag" gem: "turbo-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "turbo_refresh_method_tag(method = :replace)" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml index 7e94962e2..1bc5de585 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml @@ -4,7 +4,7 @@ source: "Turbo::DriveHelper#turbo_refresh_scroll_tag" gem: "turbo-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "turbo_refresh_scroll_tag(scroll = :reset)" diff --git a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml index 0dfdd417c..ab9a2012a 100644 --- a/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml +++ b/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml @@ -4,7 +4,7 @@ source: "Turbo::DriveHelper#turbo_refreshes_with" gem: "turbo-rails" output: "void" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "turbo_refreshes_with(method: :replace, scroll: :reset)" diff --git a/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml b/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml index 1aa9e006b..892a08979 100644 --- a/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml +++ b/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml @@ -4,7 +4,7 @@ source: "Turbo::IncludesHelper#turbo_include_tags" gem: "turbo-rails" output: "html" visibility: "public" -head_content: true +context: "head" supported: false supports_block: false signature: "turbo_include_tags" diff --git a/lib/herb/engine/debug_visitor.rb b/lib/herb/engine/debug_visitor.rb index a85210b73..79c4c9240 100644 --- a/lib/herb/engine/debug_visitor.rb +++ b/lib/herb/engine/debug_visitor.rb @@ -6,7 +6,7 @@ module Herb class Engine class DebugVisitor < Herb::Visitor - HEAD_CONTENT_HELPER_PATTERN = /\b(?:#{Herb::ActionView::HelperRegistry.head_content_helpers.map { |helper| Regexp.escape(helper.name) }.join("|")})\b/ #: Regexp + HEAD_CONTENT_HELPER_PATTERN = /\b(?:#{Herb::ActionView::HelperRegistry.by_context("head").map { |helper| Regexp.escape(helper.name) }.join("|")})\b/ #: Regexp private_constant :HEAD_CONTENT_HELPER_PATTERN def initialize(file_path: nil, project_path: nil) diff --git a/sig/herb/action_view/helper_registry.rbs b/sig/herb/action_view/helper_registry.rbs index c24a2673b..3d95fe1dd 100644 --- a/sig/herb/action_view/helper_registry.rbs +++ b/sig/herb/action_view/helper_registry.rbs @@ -586,6 +586,8 @@ module Herb attr_reader visibility: String + attr_reader context: String? + attr_reader tag_name: String? attr_reader detect_style: Symbol @@ -608,8 +610,8 @@ module Herb attr_reader aliases: Array[String] - # : (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, head_content: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void - def initialize: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, head_content: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void + # : (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, context: String?, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void + def initialize: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, context: String?, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void # : () -> bool def void?: () -> bool @@ -623,9 +625,6 @@ module Herb # : () -> bool def supported?: () -> bool - # : () -> bool - def head_content?: () -> bool - # : () -> bool def static_tag_name?: () -> bool @@ -1142,7 +1141,7 @@ module Herb BY_TAG_NAME: Hash[String, Array[HelperEntry]] - HEAD_CONTENT: Array[HelperEntry] + BY_CONTEXT: Hash[String, Array[HelperEntry]] # : (String) -> HelperEntry? def self.get: (String) -> HelperEntry? @@ -1174,11 +1173,8 @@ module Herb # : () -> Array[HelperEntry] def self.supported: () -> Array[HelperEntry] - # : () -> Array[HelperEntry] - def self.head_content_helpers: () -> Array[HelperEntry] - - # : (String) -> bool - def self.head_content?: (String) -> bool + # : (String) -> Array[HelperEntry] + def self.by_context: (String) -> Array[HelperEntry] # : (String) -> Array[HelperEntry] def self.by_gem: (String) -> Array[HelperEntry] diff --git a/templates/lib/herb/action_view/helper_registry.rb.erb b/templates/lib/herb/action_view/helper_registry.rb.erb index a9309eb34..2b5726df5 100644 --- a/templates/lib/herb/action_view/helper_registry.rb.erb +++ b/templates/lib/herb/action_view/helper_registry.rb.erb @@ -101,6 +101,7 @@ module Herb attr_reader :gem #: String attr_reader :output #: Symbol attr_reader :visibility #: String + attr_reader :context #: String? attr_reader :tag_name #: String? attr_reader :detect_style #: Symbol attr_reader :description #: String @@ -113,10 +114,10 @@ module Herb attr_reader :special_behaviors #: Array[Symbol] attr_reader :aliases #: Array[String] - #: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, head_content: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void + #: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, context: String?, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], block_arguments: Array[HelperBlockArgument], special_behaviors: Array[Symbol], aliases: Array[String]) -> void def initialize( # rubocop:disable Metrics/ParameterLists - name:, type:, source:, gem:, output:, visibility:, tag_name:, is_void:, supports_block:, - preferred_for_tag:, supported:, head_content:, detect_style:, description:, signature:, documentation_url:, + name:, type:, source:, gem:, output:, visibility:, context:, tag_name:, is_void:, supports_block:, + preferred_for_tag:, supported:, detect_style:, description:, signature:, documentation_url:, implicit_attribute:, arguments:, options:, block_arguments:, special_behaviors:, aliases: ) @name = name @@ -125,12 +126,12 @@ module Herb @gem = gem @output = output @visibility = visibility + @context = context @tag_name = tag_name @is_void = is_void @supports_block = supports_block @preferred_for_tag = preferred_for_tag @supported = supported - @head_content = head_content @detect_style = detect_style @description = description @signature = signature @@ -155,9 +156,6 @@ module Herb #: () -> bool def supported? = @supported - #: () -> bool - def head_content? = @head_content - #: () -> bool def static_tag_name? = !@tag_name.nil? @@ -180,12 +178,12 @@ module Herb gem: "<%= helper.gem %>", output: :<%= helper.output %>, visibility: "<%= helper.visibility %>", + context: <%= helper.context ? "\"#{helper.context}\"" : "nil" %>, tag_name: <%= helper.tag_name ? "\"#{helper.tag_name}\"" : "nil" %>, is_void: <%= helper.void? %>, supports_block: <%= helper.supports_block %>, preferred_for_tag: <%= helper.preferred_for_tag %>, supported: <%= helper.supported %>, - head_content: <%= helper.head_content? %>, detect_style: :<%= helper.detect_style || "call_name" %>, description: "<%= helper.escaped_description %>", signature: "<%= helper.escaped_signature %>", @@ -244,7 +242,13 @@ module Herb <%- end -%> }.freeze #: Hash[String, Array[HelperEntry]] - HEAD_CONTENT = [<%= helpers.select(&:head_content?).map(&:constant_name).join(", ") %>].freeze #: Array[HelperEntry] + <%- helpers_by_context = helpers.select(&:context).group_by(&:context) -%> + + BY_CONTEXT = { + <%- helpers_by_context.each do |context, helpers| -%> + "<%= context %>" => [<%= helpers.map(&:constant_name).join(", ") %>].freeze, + <%- end -%> + }.freeze #: Hash[String, Array[HelperEntry]] class << self #: (String) -> HelperEntry? @@ -301,15 +305,9 @@ module Herb entries.select(&:supported?) end - #: () -> Array[HelperEntry] - def head_content_helpers - HEAD_CONTENT - end - - #: (String) -> bool - def head_content?(name) - entry = get(name) - entry&.head_content? || false + #: (String) -> Array[HelperEntry] + def by_context(context) + BY_CONTEXT[context] || [] end #: (String) -> Array[HelperEntry] diff --git a/templates/template.rb b/templates/template.rb index 19418a4fd..88d1ca59c 100755 --- a/templates/template.rb +++ b/templates/template.rb @@ -670,7 +670,7 @@ def receiver_call_detect? end class HelperType - attr_reader :name, :source, :gem, :output, :visibility, :supports_block, + attr_reader :name, :source, :gem, :output, :visibility, :context, :supports_block, :supported, :description, :signature, :documentation_url, :tag, :content, :attributes_arg, :attributes_arg_with_block, :transform_style, :custom_transform, @@ -684,7 +684,7 @@ def initialize(config) @visibility = config.fetch("visibility", "public") @supports_block = config.fetch("supports_block", false) @supported = config.fetch("supported", false) - @head_content = config.fetch("head_content", false) + @context = config.fetch("context", nil) @description = config.fetch("description", "").strip @signature = config.fetch("signature") @documentation_url = config.fetch("documentation_url") @@ -789,10 +789,6 @@ def supported? @supported end - def head_content? - @head_content - end - def static_tag_name? !tag_name.nil? end