Resolve declared component dependencies that never opted in - #2711
Draft
erikaxel wants to merge 1 commit into
Draft
Resolve declared component dependencies that never opted in#2711erikaxel wants to merge 1 commit into
erikaxel wants to merge 1 commit into
Conversation
`# Template Dependency: SomeComponent` is the escape hatch for component renders static analysis can't see -- a class held in a local variable, or reached through a helper module. `explicit_component_dependencies` translated the declared class name into the synthetic path the component is digested under, but only for classes `constantize_component` accepts, which means only ones that already include `ExperimentallyCacheable`. For anything else the raw constant name stayed in the dependency list and was handed to the Digestor as a template path. Nothing resolves there, so the declaration silently did nothing and the only signal was `Couldn't find template for digesting: SomeComponent` in the log. That defeats the point of the hatch: the dependencies you need it for are exactly the ones whose target you may not control or want to modify. Resolve any `ViewComponent::Base` descendant instead, and register it so the Resolver can synthesize a template for it -- being named by a declaration is itself the opt-in. `component_for` resolves through the same path, since the registry can now hold components that never included the module. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
erikaxel
force-pushed
the
resolve-explicit-component-dependencies-without-opt-in
branch
from
September 3, 2026 11:53
5d8ec5b to
b085ece
Compare
This was referenced Sep 3, 2026
erikaxel
marked this pull request as draft
September 3, 2026 12:24
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
# Template Dependency: SomeComponentis the escape hatch for component renders static analysis can't see — a class held in a local variable, or reached through a helper module. The caching guide documents naming components this way.CacheDigest.explicit_component_dependenciestranslates the declared class name into the synthetic path the component is digested under, but it resolves the name throughconstantize_component, which only accepts classes that already includeViewComponent::ExperimentallyCacheable. For anything else the raw constant name stays in the dependency list and is handed to the Digestor as a template path. Nothing resolves there, so the declaration silently does nothing and the only signal is a confusing log line naming a class where a path is expected:That defeats the point of the hatch: the dependencies you need it for are exactly the ones whose target you may not control or want to modify.
Fix
Resolve any
ViewComponent::Basedescendant inexplicit_component_dependencies, and register it soResolvercan synthesize a template for it — being named by a declaration is itself the opt-in.component_forresolves through the same path, since the registry can now hold components that never included the module.The declared component's template, Ruby class, sidecar files, and superclasses are digested, so editing any of them busts caches that depend on it. Rendering the declared component is still uncached; only components that include the module and declare
cache_oncache their own output.Constants that aren't components (and names that aren't constants at all) are left in the dependency list untouched, as before.
Tests
test_declared_components_resolve_without_opting_into_caching— a declaration naming a component with no__vc_cacheable?now resolves to its synthetic path.test_cache_digest_changes_when_a_component_declared_without_opting_in_changes— end to end through the Digestor: editingErbComponent's template changes the digest of a component that declares it. This fails onmain.test_declared_names_that_are_not_components_are_left_alone— replaces the old assertion that a non-opted-in component resolved to nothing, and covers a plain class and an unknown constant instead.Full suite passes on the default Gemfile and on
gemfiles/rails_7.1.gemfile(the two failures there are the pre-existingRenderingAllocationsTestentries with no baseline for the Ruby version I ran locally).Not covered here
The guide also suggests
# Template Dependency:for modules included into a component. A module still resolves to nothing, since it has novirtual_pathand no template or sidecar files to hash. That needs a separate mechanism and seemed out of scope for this fix.🤖 Generated with Claude Code