Module: Controllers::Webpackable

Extended by:
ActiveSupport::Concern
Included in:
ApplicationController
Defined in:
app/concerns/controllers/webpackable.rb

Overview

Webpackable - Rails concern for webpack asset management

This module provides helpers for including webpack assets (JS/CSS) in Rails views.
It uses WebpackManifestLoader to get fresh asset URLs from the webpack manifest,
avoiding stale cache issues that can occur with traditional Rails caching.

Key Features:

  • Fresh manifest loading (no stale cache issues)
  • Development webpack dev server support
  • Font preloading for custom typography
  • Error handling and fallbacks

Usage in Controllers:
include Controllers::Webpackable

Usage in Views:
<%= webpack_js_include 'www' %>
<%= webpack_css_include 'crm' %>
<%= preload_webpack_fonts %>
<% if wpd_is_running? %>...<% end %>

Dependencies:

  • WebpackManifestLoader (config/initializers/webpack_manifest.rb)
  • Webpack assets built to public/javascripts/webpack/

Instance Method Summary collapse

Instance Method Details

#preload_webpack_fontsvoid

This method returns an undefined value.

Preloads custom typography fonts for better performance

Preloads Sofia Pro fonts (light, regular, semibold) and Orpheus Pro fonts
(normal, medium, bold) to prevent font loading delays and improve perceived
performance. Font Awesome 7 Pro+ uses SVG+JS so no font preloading is needed for icons.

Example:
<%= preload_webpack_fonts %>



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'app/concerns/controllers/webpackable.rb', line 126

def preload_webpack_fonts
  # Font Awesome 7 Pro+ now uses SVG+JS - no font preloading needed
  # Only preload custom typography fonts (Sofia Pro, Orpheus Pro, Inter, Playfair Display)
  # Skip preloading in development to avoid warnings
  return if Rails.env.development? && wpd_is_running?

  # Only preload the two fonts needed above the fold. The remaining fonts
  # (semibold, Orpheus Pro, Inter, Playfair Display) load via @font-face
  # with font-display:swap and no longer compete with the LCP image for
  # bandwidth on the critical path (~200 KB freed from the Link header).
  custom_fonts = %w[
    sofiapro-light-webfont sofiapro-regular-webfont
  ]

  # Preload custom typography fonts
  custom_fonts.each do |font|
    url = Array(get_hashed_name_from_manifest(font, 'woff2')).first
    next if url.blank?

    helpers.preload_link_tag(
      ensure_absolute_wpd_url(url, wpd_is_running? ? dev_server_origin : nil),
      as: :font,
      type: 'font/woff2',
      crossorigin: :anonymous,
      nopush: false,
      skip_pipeline: true
    )
  rescue StandardError => e
    Rails.logger.warn "Custom font preload failed for #{font}: #{e.message}"
  end
end

#webpack_css_include(webpack_file, track: false) ⇒ String

Generates link tags for webpack CSS bundles

Example:
<%= webpack_css_include 'crm', track: true %> # layout main bundle
<%= webpack_css_include 'rpStyles' %> # per-page bundle

Parameters:

  • webpack_file (String)

    The webpack entrypoint name (e.g., 'www', 'crm')

  • track (Boolean) (defaults to: false)

    Whether to emit data-turbo-track="reload". Default
    is false; see #webpack_js_include for rationale.

Returns:

  • (String)

    HTML link tags for the webpack entrypoint CSS



85
86
87
88
89
90
91
92
93
94
95
96
# File 'app/concerns/controllers/webpackable.rb', line 85

def webpack_css_include(webpack_file, track: false)
  names = get_hashed_name_from_manifest(webpack_file, 'css')
  origin = wpd_is_running? ? dev_server_origin : nil
  tags = names.map do |url|
    full_url = ensure_absolute_wpd_url(url, origin)
    helpers.tag.link(rel: 'stylesheet', href: full_url, media: 'all', **turbo_track_attr(track))
  end
  safe_join(tags, "\n")
rescue StandardError => e
  Rails.logger.error "webpack_css_include error: #{e.message}"
  ''
end

#webpack_css_url(webpack_file) ⇒ Array<String>

Returns the URL(s) for a webpack CSS bundle

Example:
webpack_css_url('www') # => ["/javascripts/webpack/www-abc123.css"]

Parameters:

  • webpack_file (String)

    The webpack entrypoint name (e.g., 'www', 'crm')

Returns:

  • (Array<String>)

    Array of CSS URLs for the webpack entrypoint



106
107
108
109
110
111
112
113
# File 'app/concerns/controllers/webpackable.rb', line 106

def webpack_css_url(webpack_file)
  names = get_hashed_name_from_manifest(webpack_file, 'css')
  origin = wpd_is_running? ? dev_server_origin : nil
  names.map { |url| ensure_absolute_wpd_url(url, origin) }
rescue StandardError => e
  Rails.logger.error "webpack_css_url error: #{e.message}"
  []
end

#webpack_js_include(webpack_file, async: false, defer: false, track: false) ⇒ String

Generates script tags for webpack JavaScript bundles

Example:
<%= webpack_js_include 'crm', track: true %> # layout main bundle
<%= webpack_js_include 'reports' %> # per-page bundle

Parameters:

  • webpack_file (String)

    The webpack entrypoint name (e.g., 'www', 'crm')

  • async (Boolean) (defaults to: false)

    Whether to load scripts asynchronously

  • defer (Boolean) (defaults to: false)

    Whether to defer script execution

  • track (Boolean) (defaults to: false)

    Whether to emit data-turbo-track="reload". Default
    is false so that per-page bundles (e.g., 'rpStyles', 'reports') do not
    force a full page reload when navigating between pages whose tracked
    asset sets differ. Pass track: true for layout-level main bundles
    ('crm', 'www') so a deploy that swaps their hashed URL still forces a
    reload and users pick up new code. See _crm_header_stack.html.erb and
    _cms_header.html.erb for the layout-level call sites.

Returns:

  • (String)

    HTML script tags for the webpack entrypoint



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'app/concerns/controllers/webpackable.rb', line 60

def webpack_js_include(webpack_file, async: false, defer: false, track: false)
  names = get_hashed_name_from_manifest(webpack_file, 'js')
  # Prefix dev-server origin in development when WDS is running
  origin = wpd_is_running? ? dev_server_origin : nil
  tags = names.map do |url|
    full_url = ensure_absolute_wpd_url(url, origin)
    helpers.javascript_include_tag(full_url, async: async, defer: defer, **turbo_track_attr(track))
  end
  safe_join(tags, "\n")
rescue StandardError => e
  Rails.logger.error "webpack_js_include error: #{e.message}"
  ''
end

#wpd_is_running?Boolean

Checks if webpack dev server is running in development

Example:
<% if wpd_is_running? %>

<% end %>

Returns:

  • (Boolean)

    True if webpack dev server is accessible



167
168
169
# File 'app/concerns/controllers/webpackable.rb', line 167

def wpd_is_running?
  webpack_dev_server_running?
end