Skip to content

Linter Rule: Disallow block arguments that shadow a declared herb:state name

Rule: herb-state-no-shadowed-states

Description

Flags a block argument, a for loop variable, or a render block argument whose name matches a state the template declares.

Rationale

A declared state is a Ruby local the template assigns at the top, so a block argument of the same name shadows it, the way Ruby always resolves names. Inside that block every read reaches the argument, the state becomes unreachable, and nothing the client writes can steer what the block renders. The compiler resolves the shadowing the same way Ruby does, so the template still compiles, which is exactly why the collision is easy to miss.

Examples

✅ Good

erb
<%# herb:slots client %>
<%# herb:state (track: "") %>
The state `track` is never read or written in this template. Remove it, or disable this line when only app code uses it through `stateFor` or `useState`. (herb-state-no-unused-states)
<% songs.each do |entry| %> <p><%= entry %></p> <% end %>

🚫 Bad

erb
<%# herb:slots client %>
<%# herb:state (track: "") %>

<% songs.each do |track| %>
Block argument `track` shadows the state `track`, so reads inside this block reach the argument and never the state. Rename the argument. (herb-state-no-shadowed-states)
<p><%= track %></p> <% end %>

References

-

Released under the MIT License.