Skip to content

Check component templates with Herb in herb:check - #2779

Closed
czj wants to merge 2 commits into
ViewComponent:mainfrom
czj:herb-check
Closed

czj wants to merge 2 commits into
ViewComponent:mainfrom
czj:herb-check

Conversation

@czj

@czj czj commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

What are you trying to accomplish?

Fixes #2717

Rails 8.2 can compile HTML+ERB templates through Herb (config.action_view.erb_implementation = :herb) and ships bin/rails herb:check to find templates that won't compile before you adopt it. Component templates compile through Herb too: Template#compiled_source hands the ERB handler an :html template. But herb:check reads the view paths, which don't include sidecar templates, and inline templates aren't files at all. So an app can pass herb:check and still raise Herb::Engine::CompilationError the first time a component renders.

What approach did you choose and why?

  • ViewComponent::HerbChecker works like ActionView::HerbChecker. It compiles each component's HTML+ERB templates with ActionView::Template::Handlers::ERB, using implementation: ERB::Herb, validate_ruby: true and the SecurityValidator. It returns Failures for Herb::Engine::CompilationError and SecurityError. It checks each component's own sidecar and inline templates. It skips #call methods and inherited templates, which the parent's check already covers.
  • bin/rails view_component:herb:check eager loads the app, runs the checker and prints failures in the same format as Rails' task.
  • herb:check now checks components too. A prerequisite prints component failures without stopping, so Rails still checks the views, and an at_exit makes the command fail once it has finished. People already run that command before switching to Herb. API-only apps don't get herb:check from Rails, so the hook is skipped there.
  • Both tasks are only defined when Action View has ERB::Herb, which means Rails 8.2 and later. supported? checks this without loading the herb gem.
  • To feed the handler exactly what the compiler feeds it, I pulled handler_template and handler_source out of Template#compiled_source, and added Compiler#source_templates. Compilation itself doesn't change.

Alternatives I considered:

  • Hooking into Rails' checker: it only accepts view-path resolvers, and adding a registration API would need a change in Rails.
  • A docs-only note: it wouldn't actually catch anything.

Anything you want to highlight for special attention from reviewers?

  • Tests that need Herb are skipped in CI for now. Both rails_main and rails_main_head are locked to Rails revisions from before Herb support (9cc62ec, 11417ba), so HerbChecker.supported? is false everywhere CI runs. The tests for source_templates, handler_template/handler_source and the rake wiring run on every Rails version. The Herb tests will start running once the rails_main lock is bumped. I couldn't run the suite locally, so I'd appreciate a run against current Rails main.
  • The at_exit in the prerequisite is a little unusual. I couldn't find another way for a prerequisite to report failures and still let Rails check the views. Happy to drop the hook and keep view_component:herb:check on its own if you'd rather.
  • It checks every loaded component, including those that come from gems (as Rails checks engine view paths). Should it be limited to components under Rails.root?

Rails 8.2 compiles HTML+ERB templates through Herb when an application
opts in, and ships `bin/rails herb:check` to find the templates that
won't compile. That task reads the view paths, which don't hold sidecar
templates, and inline templates aren't files at all, so an application
can pass the check and still raise at the first render of a component.

Add `bin/rails view_component:herb:check`, which compiles the HTML+ERB
templates of every component the way `ActionView::HerbChecker` does,
and make it a prerequisite of `herb:check`. Both are defined only when
Action View can compile ERB through Herb.

Fixes ViewComponent#2717.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLwjZw7FEdBpwALzxXxXnJ
As a prerequisite of `herb:check`, the component check aborted on the
first failing component, so Rails never checked the views. Report
component failures there without stopping, and fail once `herb:check`
has finished. `view_component:herb:check` still aborts on its own.

Also share sidecar lookups across components while checking, and print
the line an inline template starts on, since Herb counts lines from the
start of the template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLwjZw7FEdBpwALzxXxXnJ
@joelhawksley

Copy link
Copy Markdown
Member

@czj thank you for the PR, but I think we should just do this in Rails: rails/rails#59137

@czj

czj commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

That's better, indeed ! Thanks :)

@czj
czj deleted the herb-check branch November 9, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rails 8.2: herb:check does not reach sidecar templates

2 participants