From d7947fc975fdde9c6a1cc286494802afb5fd3302 Mon Sep 17 00:00:00 2001 From: Joel Hawksley Date: Mon, 20 Apr 2026 09:32:44 -0600 Subject: [PATCH 1/3] Add compile.view_component ActiveSupport::Notifications event for eager compilation at boot time. --- docs/CHANGELOG.md | 4 ++++ docs/guide/instrumentation.md | 14 ++++++++++++++ lib/view_component/engine.rb | 6 +++++- test/sandbox/test/instrumentation_test.rb | 17 +++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9a79b86a3..31c1f7ac9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,10 @@ nav_order: 6 ## main +* Add `compile.view_component` ActiveSupport::Notifications event for eager compilation at boot time. + + *Joel Hawksley*, *GitHub Copilot* + ## 4.7.0 * Fix stale content cache when slots are accessed before `render_in`. diff --git a/docs/guide/instrumentation.md b/docs/guide/instrumentation.md index 2a29ed12a..1a0709fc7 100644 --- a/docs/guide/instrumentation.md +++ b/docs/guide/instrumentation.md @@ -28,6 +28,20 @@ end _Note: Enabling instrumentation negatively impacts the performance of ViewComponent._ +## Compile instrumentation + +Since 4.8.0 +{: .label } + +ViewComponent also instruments eager compilation at boot time via the `compile.view_component` event. This event is always emitted (no configuration needed) when `config.eager_load` is `true`: + +```ruby +ActiveSupport::Notifications.subscribe("compile.view_component") do |event| + event.name # => "compile.view_component" + event.duration # => 123.45 (milliseconds) +end +``` + ## Viewing instrumentation sums in the browser developer tools When using `render.view_component` with `config.server_timing = true` (default in development) in Rails 7, the browser developer tools display the sum total timing information in Network > Timing under the key `render.view_component`. diff --git a/lib/view_component/engine.rb b/lib/view_component/engine.rb index ed70c912d..39a30ff34 100644 --- a/lib/view_component/engine.rb +++ b/lib/view_component/engine.rb @@ -79,7 +79,11 @@ class Engine < Rails::Engine # :nodoc: initializer "view_component.eager_load_actions" do ActiveSupport.on_load(:after_initialize) do - ViewComponent::Base.descendants.each(&:__vc_compile) if Rails.application.config.eager_load + if Rails.application.config.eager_load + ActiveSupport::Notifications.instrument("compile.view_component") do + ViewComponent::Base.descendants.each(&:__vc_compile) + end + end end end diff --git a/test/sandbox/test/instrumentation_test.rb b/test/sandbox/test/instrumentation_test.rb index ea8254620..431d7aadb 100644 --- a/test/sandbox/test/instrumentation_test.rb +++ b/test/sandbox/test/instrumentation_test.rb @@ -32,4 +32,21 @@ def test_instrumentation_disabled assert_equal(events.size, 0) end end + + def test_compile_instrumentation + events = [] + ActiveSupport::Notifications.subscribe("compile.view_component") do |*args| + events << ActiveSupport::Notifications::Event.new(*args) + end + + ViewComponent::CompileCache.invalidate! + ActiveSupport::Notifications.instrument("compile.view_component") do + ViewComponent::Base.descendants.each(&:__vc_compile) + end + + assert_equal(1, events.size) + assert_equal("compile.view_component", events[0].name) + ensure + ActiveSupport::Notifications.unsubscribe("compile.view_component") + end end From 3cc6d61b83b34f41aaf49dad5e2fbb1c219756ad Mon Sep 17 00:00:00 2001 From: Joel Hawksley Date: Mon, 20 Apr 2026 09:45:30 -0600 Subject: [PATCH 2/3] simplify test --- test/sandbox/test/instrumentation_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/sandbox/test/instrumentation_test.rb b/test/sandbox/test/instrumentation_test.rb index 431d7aadb..747571bcd 100644 --- a/test/sandbox/test/instrumentation_test.rb +++ b/test/sandbox/test/instrumentation_test.rb @@ -39,9 +39,9 @@ def test_compile_instrumentation events << ActiveSupport::Notifications::Event.new(*args) end - ViewComponent::CompileCache.invalidate! ActiveSupport::Notifications.instrument("compile.view_component") do - ViewComponent::Base.descendants.each(&:__vc_compile) + ViewComponent::CompileCache.invalidate_class!(InstrumentationComponent) + InstrumentationComponent.__vc_compile end assert_equal(1, events.size) From d9209180e40c0ffbe08681fd716f012651d032e0 Mon Sep 17 00:00:00 2001 From: Joel Hawksley Date: Mon, 20 Apr 2026 09:55:44 -0600 Subject: [PATCH 3/3] clean up test --- test/sandbox/test/instrumentation_test.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/sandbox/test/instrumentation_test.rb b/test/sandbox/test/instrumentation_test.rb index 747571bcd..29e60e4ce 100644 --- a/test/sandbox/test/instrumentation_test.rb +++ b/test/sandbox/test/instrumentation_test.rb @@ -35,18 +35,23 @@ def test_instrumentation_disabled def test_compile_instrumentation events = [] - ActiveSupport::Notifications.subscribe("compile.view_component") do |*args| + subscriber = ActiveSupport::Notifications.subscribe("compile.view_component") do |*args| events << ActiveSupport::Notifications::Event.new(*args) end - ActiveSupport::Notifications.instrument("compile.view_component") do - ViewComponent::CompileCache.invalidate_class!(InstrumentationComponent) - InstrumentationComponent.__vc_compile - end + old_eager_load = Rails.application.config.eager_load + Rails.application.config.eager_load = true + + ViewComponent::CompileCache.invalidate! + + # Execute the same block that the view_component.eager_load_actions initializer registers + initializer = ViewComponent::Engine.initializers.find { |i| i.name == "view_component.eager_load_actions" } + initializer.run(Rails.application) assert_equal(1, events.size) assert_equal("compile.view_component", events[0].name) ensure - ActiveSupport::Notifications.unsubscribe("compile.view_component") + Rails.application.config.eager_load = old_eager_load + ActiveSupport::Notifications.unsubscribe(subscriber) end end