diff --git a/lib/herb/engine/slots/visitor.rb b/lib/herb/engine/slots/visitor.rb
index 39cca8491..d7400fe7b 100644
--- a/lib/herb/engine/slots/visitor.rb
+++ b/lib/herb/engine/slots/visitor.rb
@@ -206,6 +206,7 @@ def initialize(markers: Markers.new, mode: :server, identifier: :path, mark: tru
@fragment_nodes = fragment_nodes.compare_by_identity
@fragment_fallbacks = fragment_fallbacks.compare_by_identity
+ @exposed_fallbacks = [] #: Array[untyped]
@deferred_nodes = deferred_nodes.compare_by_identity
@assignment_nodes = assignment_nodes.compare_by_identity
end
@@ -661,6 +662,7 @@ def visit_document_node(node)
@states.apply_states
wrap_bound_renders
+ expose_fallbacks(node)
end
#: () -> Hash[String, String]?
@@ -670,6 +672,7 @@ def statics
#: (untyped) -> void
def finish(node)
+ withdraw_fallbacks(node)
follow_replacements
return unless @mark
@@ -1982,6 +1985,29 @@ def append_statics(document_node)
document_node.children.concat(nodes)
end
+ #: (Herb::AST::DocumentNode) -> void
+ def expose_fallbacks(document_node)
+ return if @fragment_fallbacks.empty?
+
+ @exposed_fallbacks = @fragment_fallbacks.each_value.flat_map { |body| body } #: Array[untyped]
+
+ document_node.children.concat(@exposed_fallbacks)
+
+ nil
+ end
+
+ #: (Herb::AST::DocumentNode) -> void
+ def withdraw_fallbacks(document_node)
+ exposed = @exposed_fallbacks
+
+ return if exposed.empty?
+
+ document_node.children.reject! { |child| exposed.any? { |node| node.equal?(child) } }
+ @exposed_fallbacks = [] #: Array[untyped]
+
+ nil
+ end
+
#: () -> Array[[String, Array[untyped]]]
def fallback_statics_entries
@fragment_fallbacks.filter_map { |node, body|
diff --git a/sig/herb/engine/slots/visitor.rbs b/sig/herb/engine/slots/visitor.rbs
index 84f2cbf2b..109a21241 100644
--- a/sig/herb/engine/slots/visitor.rbs
+++ b/sig/herb/engine/slots/visitor.rbs
@@ -540,6 +540,12 @@ module Herb
# : (Herb::AST::DocumentNode) -> void
def append_statics: (Herb::AST::DocumentNode) -> void
+ # : (Herb::AST::DocumentNode) -> void
+ def expose_fallbacks: (Herb::AST::DocumentNode) -> void
+
+ # : (Herb::AST::DocumentNode) -> void
+ def withdraw_fallbacks: (Herb::AST::DocumentNode) -> void
+
# : () -> Array[[String, Array[untyped]]]
def fallback_statics_entries: () -> Array[[ String, Array[untyped] ]]
diff --git a/test/engine/scoped_style/visitor_test.rb b/test/engine/scoped_style/visitor_test.rb
index 896069db3..93e38f29c 100644
--- a/test/engine/scoped_style/visitor_test.rb
+++ b/test/engine/scoped_style/visitor_test.rb
@@ -394,5 +394,14 @@ def hoisted(source, filename: TEMPLATE)
refute_nil diagnostic
assert_equal "app/views/posts/_dynamic.html.erb", diagnostic.template
end
+
+ test "carries the scope into the fallback a client stands in while content is stale" do
+ source = %(<%# herb:slots client %><%# herb:state (album: "") %>LoadedLoading)
+
+ assert_compiled_snapshot(
+ source,
+ options(visitors: [Herb::Engine::Slots::Visitor.new(mode: :client)], project_path: PROJECT_PATH)
+ )
+ end
end
end
diff --git a/test/snapshots/engine/scoped_style_visitor_test/test_0032_carries_the_scope_into_the_fallback_a_client_stands_in_while_content_is_stale_0cc487a36bea50e642a7087683c1a917.txt b/test/snapshots/engine/scoped_style_visitor_test/test_0032_carries_the_scope_into_the_fallback_a_client_stands_in_while_content_is_stale_0cc487a36bea50e642a7087683c1a917.txt
new file mode 100644
index 000000000..0797dbcc0
--- /dev/null
+++ b/test/snapshots/engine/scoped_style_visitor_test/test_0032_carries_the_scope_into_the_fallback_a_client_stands_in_while_content_is_stale_0cc487a36bea50e642a7087683c1a917.txt
@@ -0,0 +1,6 @@
+---
+source: "Engine::ScopedStyleVisitorTest#test_0032_carries the scope into the fallback a client stands in while content is stale"
+input: "{source: \"<%# herb:slots client %><%# herb:state (album: \\\"\\\") %>LoadedLoading\", options: {filename: \"app/views/posts/index.html.erb\", escape: false, visitors: [#, #], project_path: \"test/fixtures/scoped_styles\"}}"
+---
+_buf = ::String.new; ::Herb::Engine::Runtime::Session.record_compile_diagnostics("app/views/posts/index.html.erb", [{ message: "Nothing inside this `` is derived on the server, so its `` can never appear.", severity: :warning, code: "herb-slots-component", origin: "Herb Compiler", suggestion: "Compute something with a declared state inside the fragment, or unwrap it.", line: 1, column: 93, end_line: 1, end_column: 183 }].freeze); @_herb_covered ||= {} ; _herb_occurrence = ((@_herb_region_occurrences ||= ::Hash.new(0))["app/views/posts/index.html.erb"] += 1) - 1; _buf << ''.freeze; ; _herb_state_overrides = ::Herb::Engine::Slots::StateOverrides.resolve((__herb_state_overrides if defined?(__herb_state_overrides)), "app/views/posts/index.html.erb"); album = ::Herb::Engine::Slots::StateOverrides.fetch(_herb_state_overrides, "album", :string) { "" }; _herb_seeds = { }.select { |_, v| [true, false, ::Integer, ::String, ::Symbol, nil].any? { |t| t === v } }.transform_values { |v| v.is_a?(::Symbol) ? v.to_s : v }.merge({ "album" => album }.select { |_, v| [true, false, ::Integer, ::String, ::Symbol, nil].any? { |t| t === v } }.transform_values { |v| v.is_a?(::Symbol) ? v.to_s : v }.slice(*(_herb_state_overrides || {}).keys)); _buf << ::Herb::Engine.raw("") unless _herb_seeds.empty?; _buf << ''.freeze; if true; _buf << 'Loaded'.freeze; end; _buf << ''.freeze; unless @_herb_covered["app/views/posts/index.html.erb:0:0"] && @_herb_covered["app/views/posts/index.html.erb:0:1"]; _buf << ''.freeze; unless @_herb_covered["app/views/posts/index.html.erb:0:0"]; _buf << 'Loaded'.freeze; @_herb_covered["app/views/posts/index.html.erb:0:0"] = true; end; unless @_herb_covered["app/views/posts/index.html.erb:0:1"]; _buf << 'Loading'.freeze; @_herb_covered["app/views/posts/index.html.erb:0:1"] = true; end; _buf << ''.freeze; end; ::Herb::Engine::Slots::Manifest::Channel.record("app/views/posts/index.html.erb:aabf5393", "{\"file\":\"app\\/views\\/posts\\/index.html.erb\",\"identifier\":\"app\\/views\\/posts\\/index.html.erb\",\"version\":\"aabf5393\",\"names\":{},\"parts\":{},\"bindings\":{},\"states\":{\"version\":\"aabf5393\",\"declarations\":[{\"name\":\"album\",\"kind\":\"string\",\"default\":\"\\\"\\\"\",\"derived\":null,\"line\":1,\"column\":40,\"scope\":\"region\",\"value\":\"\"}],\"reads\":{},\"conditionals\":{},\"presence\":{},\"computed\":{},\"server\":{\"branches\":{},\"reads\":{}},\"fragments\":{}}}");
+_buf.to_s