Rails 8.2 compiles templates with the HTML format through Herb when an application runs the 8.2 framework defaults (rails/rails#58721, config.action_view.erb_implementation = :herb). It also ships bin/rails herb:check, "the check to run before adopting the default".
Sidecar templates follow the new default, and the check does not reach them.
Why sidecars are affected
ViewComponent::Template#compiled_source calls the registered handler with a DataWithSource whose format is :html:
handler.call(DataWithSource.new(format:, identifier: @path, short_identifier:, type:), this_source)
ActionView::Template::Handlers::ERB#call picks the implementation from template.format, so under :herb every .html.erb sidecar and inline ERB template compiles through Herb::Engine.
Why the check misses them
ActionView::HerbChecker.check(ActionController::Base.view_paths) asks each resolver for all_unbound_templates. Sidecar files under app/components are not in the view paths, and inline templates are not files at all. An application can pass herb:check and still raise at the first render of a component whose template has an unclosed tag or ERB output in an attribute name.
Reproduction
Rails main at 2cc9c08 (8.2.0.alpha), herb 0.10.4, view_component 4.15.0, with ActionView::Template::Handlers::ERB::Herb set as the ERB implementation:
<%# app/components/broken_component.html.erb %>
<div><%= title %>
ActionView::HerbChecker.check(ActionController::Base.view_paths)
# => []
ActionController::Base.renderer.render(BrokenComponent.new)
# raises Herb::Engine::CompilationError
The checker returns no failures, so bin/rails herb:check reports that every template compiles.
Possible directions
- A task shipped by ViewComponent, for example
view_component:herb_check, that compiles the templates of every ViewComponent::Base descendant the way HerbChecker does: the ERB handler with implementation: ActionView::Template::Handlers::ERB::Herb, validate_ruby: true and the SecurityValidator.
- Or a hook into
herb:check, if Rails is open to checkers registering extra template sources.
- At least a note in the Rails 8.2 upgrade section of the ViewComponent docs.
#2503 added compatibility tests with Herb, so rendering is covered. This is about the check that runs before anything renders. I run this compile in the CI of a component library built on ViewComponent and can share the code.
Rails 8.2 compiles templates with the HTML format through Herb when an application runs the 8.2 framework defaults (rails/rails#58721,
config.action_view.erb_implementation = :herb). It also shipsbin/rails herb:check, "the check to run before adopting the default".Sidecar templates follow the new default, and the check does not reach them.
Why sidecars are affected
ViewComponent::Template#compiled_sourcecalls the registered handler with aDataWithSourcewhoseformatis:html:ActionView::Template::Handlers::ERB#callpicks the implementation fromtemplate.format, so under:herbevery.html.erbsidecar and inline ERB template compiles throughHerb::Engine.Why the check misses them
ActionView::HerbChecker.check(ActionController::Base.view_paths)asks each resolver forall_unbound_templates. Sidecar files underapp/componentsare not in the view paths, and inline templates are not files at all. An application can passherb:checkand still raise at the first render of a component whose template has an unclosed tag or ERB output in an attribute name.Reproduction
Rails main at 2cc9c08 (8.2.0.alpha), herb 0.10.4, view_component 4.15.0, with
ActionView::Template::Handlers::ERB::Herbset as the ERB implementation:The checker returns no failures, so
bin/rails herb:checkreports that every template compiles.Possible directions
view_component:herb_check, that compiles the templates of everyViewComponent::Basedescendant the wayHerbCheckerdoes: the ERB handler withimplementation: ActionView::Template::Handlers::ERB::Herb,validate_ruby: trueand theSecurityValidator.herb:check, if Rails is open to checkers registering extra template sources.#2503 added compatibility tests with Herb, so rendering is covered. This is about the check that runs before anything renders. I run this compile in the CI of a component library built on ViewComponent and can share the code.