module Webpacker::Helper
# Returns the current Webpacker instance.
# Could be overridden to use multiple Webpacker
# configurations within the same app (e.g. with engines).
def current_webpacker_instance
Webpacker.instance
end
# Computes the relative path for a given Webpacker asset.
# Returns the relative path using manifest.json and passes it to path_to_asset helper.
# This will use path_to_asset internally, so most of their behaviors will be the same.
#
# Example:
#
# <%= asset_pack_path 'calendar.css' %> # => "/packs/calendar-1016838bab065ae1e122.css"
def asset_pack_path(name, **options)
path_to_asset(current_webpacker_instance.manifest.lookup!(name), options)
end
# Computes the absolute path for a given Webpacker asset.
# Returns the absolute path using manifest.json and passes it to url_to_asset helper.
# This will use url_to_asset internally, so most of their behaviors will be the same.
#
# Example:
#
# <%= asset_pack_url 'calendar.css' %> # => "http://example.com/packs/calendar-1016838bab065ae1e122.css"
def asset_pack_url(name, **options)
url_to_asset(current_webpacker_instance.manifest.lookup!(name), options)
end
# Computes the relative path for a given Webpacker image with the same automated processing as image_pack_tag.
# Returns the relative path using manifest.json and passes it to path_to_asset helper.
# This will use path_to_asset internally, so most of their behaviors will be the same.
def image_pack_path(name, **options)
resolve_path_to_image(name, **options)
end
# Computes the absolute path for a given Webpacker image with the same automated
# processing as image_pack_tag. Returns the relative path using manifest.json
# and passes it to path_to_asset helper. This will use path_to_asset internally,
# so most of their behaviors will be the same.
def image_pack_url(name, **options)
resolve_path_to_image(name, **options.merge(protocol: :request))
end
# Creates an image tag that references the named pack file.
#
# Example:
#
# <%= image_pack_tag 'application.png', size: '16x10', alt: 'Edit Entry' %>
#
#
# <%= image_pack_tag 'picture.png', srcset: { 'picture-2x.png' => '2x' } %>
#
def image_pack_tag(name, **options)
if options[:srcset] && !options[:srcset].is_a?(String)
options[:srcset] = options[:srcset].map do |src_name, size|
"#{resolve_path_to_image(src_name)} #{size}"
end.join(", ")
end
image_tag(resolve_path_to_image(name), options)
end
# Creates a link tag for a favicon that references the named pack file.
#
# Example:
#
# <%= favicon_pack_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png' %>
#
def favicon_pack_tag(name, **options)
favicon_link_tag(resolve_path_to_image(name), options)
end
# Creates script tags that reference the js chunks from entrypoints when using split chunks API,
# as compiled by webpack per the entries list in package/environments/base.js.
# By default, this list is auto-generated to match everything in
# app/packs/entrypoints/*.js and all the dependent chunks. In production mode, the digested reference is automatically looked up.
# See: https://webpack.js.org/plugins/split-chunks-plugin/
#
# Example:
#
# <%= javascript_pack_tag 'calendar', 'map', 'data-turbolinks-track': 'reload' %> # =>
#
#
#
#
#
#
# DO:
#
# <%= javascript_pack_tag 'calendar', 'map' %>
#
# DON'T:
#
# <%= javascript_pack_tag 'calendar' %>
# <%= javascript_pack_tag 'map' %>
def javascript_pack_tag(*names, defer: true, **options)
if @javascript_pack_tag_loaded
raise "To prevent duplicated chunks on the page, you should call javascript_pack_tag only once on the page. " \
"Please refer to https://github.com/shakacode/shakapacker/blob/master/README.md#view-helpers-javascript_pack_tag-and-stylesheet_pack_tag for the usage guide"
end
append_javascript_pack_tag(*names, defer: defer)
non_deferred = sources_from_manifest_entrypoints(javascript_pack_tag_queue[:non_deferred], type: :javascript)
deferred = sources_from_manifest_entrypoints(javascript_pack_tag_queue[:deferred], type: :javascript) - non_deferred
@javascript_pack_tag_loaded = true
capture do
concat javascript_include_tag(*deferred, **options.tap { |o| o[:defer] = true })
concat "\n" if non_deferred.any? && deferred.any?
concat javascript_include_tag(*non_deferred, **options.tap { |o| o[:defer] = false })
end
end
# Creates a link tag, for preloading, that references a given Webpacker asset.
# In production mode, the digested reference is automatically looked up.
# See: https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content
#
# Example:
#
# <%= preload_pack_asset 'fonts/fa-regular-400.woff2' %> # =>
#
def preload_pack_asset(name, **options)
if self.class.method_defined?(:preload_link_tag)
preload_link_tag(current_webpacker_instance.manifest.lookup!(name), options)
else
raise "You need Rails >= 5.2 to use this tag."
end
end
# Creates link tags that reference the css chunks from entrypoints when using split chunks API,
# as compiled by webpack per the entries list in package/environments/base.js.
# By default, this list is auto-generated to match everything in
# app/packs/entrypoints/*.js and all the dependent chunks. In production mode, the digested reference is automatically looked up.
# See: https://webpack.js.org/plugins/split-chunks-plugin/
#
# Examples:
#
# <%= stylesheet_pack_tag 'calendar', 'map' %> # =>
#
#
#
#
# When using the webpack-dev-server, CSS is inlined so HMR can be turned on for CSS,
# including CSS modules
# <%= stylesheet_pack_tag 'calendar', 'map' %> # => nil
#
# DO:
#
# <%= stylesheet_pack_tag 'calendar', 'map' %>
#
# DON'T:
#
# <%= stylesheet_pack_tag 'calendar' %>
# <%= stylesheet_pack_tag 'map' %>
def stylesheet_pack_tag(*names, **options)
return "" if Webpacker.inlining_css?
requested_packs = sources_from_manifest_entrypoints(names, type: :stylesheet)
appended_packs = available_sources_from_manifest_entrypoints(@stylesheet_pack_tag_queue || [], type: :stylesheet)
@stylesheet_pack_tag_loaded = true
stylesheet_link_tag(*(requested_packs | appended_packs), **options)
end
def append_stylesheet_pack_tag(*names)
if @stylesheet_pack_tag_loaded
raise "You can only call append_stylesheet_pack_tag before stylesheet_pack_tag helper. " \
"Please refer to https://github.com/shakacode/shakapacker/blob/master/README.md#view-helper-append_javascript_pack_tag-and-append_stylesheet_pack_tag for the usage guide"
end
@stylesheet_pack_tag_queue ||= []
@stylesheet_pack_tag_queue.concat names
# prevent rendering Array#to_s representation when used with <%= … %> syntax
nil
end
def append_javascript_pack_tag(*names, defer: true)
if @javascript_pack_tag_loaded
raise "You can only call append_javascript_pack_tag before javascript_pack_tag helper. " \
"Please refer to https://github.com/shakacode/shakapacker/blob/master/README.md#view-helper-append_javascript_pack_tag-and-append_stylesheet_pack_tag for the usage guide"
end
hash_key = defer ? :deferred : :non_deferred
javascript_pack_tag_queue[hash_key] |= names
# prevent rendering Array#to_s representation when used with <%= … %> syntax
nil
end
private
def javascript_pack_tag_queue
@javascript_pack_tag_queue ||= {
deferred: [],
non_deferred: []
}
end
def sources_from_manifest_entrypoints(names, type:)
names.map { |name| current_webpacker_instance.manifest.lookup_pack_with_chunks!(name.to_s, type: type) }.flatten.uniq
end
def available_sources_from_manifest_entrypoints(names, type:)
names.map { |name| current_webpacker_instance.manifest.lookup_pack_with_chunks(name.to_s, type: type) }.flatten.compact.uniq
end
def resolve_path_to_image(name, **options)
path = name.starts_with?("static/") ? name : "static/#{name}"
path_to_asset(current_webpacker_instance.manifest.lookup!(path), options)
rescue
path_to_asset(current_webpacker_instance.manifest.lookup!(name), options)
end
end