Class: Rhales::HydrationInjector

Inherits:
Object
  • Object
show all
Defined in:
lib/rhales/hydration_injector.rb

Overview

Handles intelligent hydration script injection with multiple strategies for optimal performance and resource loading.

Supported Injection Strategies

Traditional Strategies

  • :late (default) - Inject before </body> tag (safest, backwards compatible)
  • :early - Inject before detected mount points (#app, #root, etc.)
  • :earliest - Inject in HTML head section for maximum performance
  • :link - Basic link reference to API endpoint
  • :prefetch - Browser prefetch for future page loads
  • :preload - High priority preload for current page
  • :modulepreload - ES module preloading
  • :lazy - Intersection observer-based lazy loading

Strategy Selection Logic

  1. Template Disable Check: Respect disable_early_for_templates configuration
  2. Strategy Routing: Execute strategy-specific injection logic
  3. Fallback Chain: :earliest → :early → :late (when enabled)
  4. Safety Validation: All injection points validated for HTML safety

Link-based strategies generate API calls instead of inline data, enabling better caching, parallel loading, and reduced HTML payload.

Constant Summary collapse

[:link, :prefetch, :preload, :modulepreload, :lazy].freeze

Instance Method Summary collapse

Constructor Details

#initialize(hydration_config, template_name = nil) ⇒ HydrationInjector

Returns a new instance of HydrationInjector.



35
36
37
38
39
40
41
42
43
44
# File 'lib/rhales/hydration_injector.rb', line 35

def initialize(hydration_config, template_name = nil)
  @hydration_config = hydration_config
  @template_name = template_name
  @strategy = hydration_config.injection_strategy
  @fallback_to_late = hydration_config.fallback_to_late
  @fallback_when_unsafe = hydration_config.fallback_when_unsafe
  @disabled_templates = hydration_config.disable_early_for_templates
  @earliest_detector = EarliestInjectionDetector.new
  @link_detector = LinkBasedInjectionDetector.new(hydration_config)
end

Instance Method Details



150
151
152
153
154
155
156
157
158
159
# File 'lib/rhales/hydration_injector.rb', line 150

def generate_all_link_strategies(merged_data, nonce)
  link_parts = []

  merged_data.each do |window_attr, _data|
    link_html = @link_detector.generate_for_strategy(@strategy, @template_name, window_attr, nonce)
    link_parts << link_html
  end

  link_parts.join("\n")
end

#inject(template_html, hydration_html, mount_point_data = nil) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/rhales/hydration_injector.rb', line 46

def inject(template_html, hydration_html, mount_point_data = nil)
  return template_html if hydration_html.nil? || hydration_html.strip.empty?

  # Check if early/earliest injection is disabled for this template
  if [:early, :earliest].include?(@strategy) && template_disabled_for_early?
    return inject_late(template_html, hydration_html)
  end

  case @strategy
  when :early
    inject_early(template_html, hydration_html, mount_point_data)
  when :earliest
    inject_earliest(template_html, hydration_html)
  when :late
    inject_late(template_html, hydration_html)
  when *LINK_BASED_STRATEGIES
    inject_link_based(template_html, hydration_html)
  else
    inject_late(template_html, hydration_html)
  end
end

#inject_earliest(template_html, hydration_html) ⇒ Object (private)



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/rhales/hydration_injector.rb', line 118

def inject_earliest(template_html, hydration_html)
  begin
    injection_position = @earliest_detector.detect(template_html)
  rescue => e
    # Fall back to late injection on detector error
    return @fallback_to_late ? inject_late(template_html, hydration_html) : template_html
  end

  if injection_position
    before = template_html[0...injection_position]
    after = template_html[injection_position..]
    "#{before}#{hydration_html}\n#{after}"
  else
    # Fallback to late injection if earliest fails
    @fallback_to_late ? inject_late(template_html, hydration_html) : template_html
  end
end

#inject_early(template_html, hydration_html, mount_point_data) ⇒ Object (private)



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/rhales/hydration_injector.rb', line 93

def inject_early(template_html, hydration_html, mount_point_data)
  # Fallback to late injection if no mount point found
  if mount_point_data.nil?
    return @fallback_to_late ? inject_late(template_html, hydration_html) : template_html
  end

  # Check if the mount point data indicates an unsafe injection
  # (This would be nil if SafeInjectionValidator found no safe position)
  if mount_point_data[:position].nil?
    return @fallback_when_unsafe ? inject_late(template_html, hydration_html) : template_html
  end

  # Insert hydration script before the mount element
  position = mount_point_data[:position]

  before = template_html[0...position]
  after = template_html[position..]

  "#{before}#{hydration_html}\n#{after}"
end

#inject_late(template_html, hydration_html) ⇒ Object (private)



161
162
163
164
165
166
167
168
169
# File 'lib/rhales/hydration_injector.rb', line 161

def inject_late(template_html, hydration_html)
  # Try to inject before closing </body> tag
  if template_html.include?('</body>')
    template_html.sub('</body>', "#{hydration_html}\n</body>")
  else
    # If no </body> tag, append to end
    "#{template_html}\n#{hydration_html}"
  end
end


136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/rhales/hydration_injector.rb', line 136

def inject_link_based(template_html, hydration_html)
  # For link-based strategies, try earliest injection first, then fallback
  injection_position = @earliest_detector.detect(template_html)

  if injection_position
    before = template_html[0...injection_position]
    after = template_html[injection_position..]
    "#{before}#{hydration_html}\n#{after}"
  else
    # Fallback to late injection
    @fallback_to_late ? inject_late(template_html, hydration_html) : template_html
  end
end

Special method for link-based strategies that need merged data context



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/rhales/hydration_injector.rb', line 69

def inject_link_based_strategy(template_html, merged_data, nonce = nil)
  return template_html if merged_data.nil? || merged_data.empty?

  # Check if early injection is disabled for this template
  if template_disabled_for_early?
    # For link strategies, we still generate the links but fall back to late positioning
    link_html = generate_all_link_strategies(merged_data, nonce)
    return inject_late(template_html, link_html)
  end

  link_html = generate_all_link_strategies(merged_data, nonce)

  case @strategy
  when :earliest
    inject_earliest(template_html, link_html)
  when *LINK_BASED_STRATEGIES
    inject_link_based(template_html, link_html)
  else
    inject_late(template_html, link_html)
  end
end

#template_disabled_for_early?Boolean (private)

Returns:

  • (Boolean)


114
115
116
# File 'lib/rhales/hydration_injector.rb', line 114

def template_disabled_for_early?
  @template_name && @disabled_templates.include?(@template_name)
end