# frozen_string_literal: true require 'base64' require 'action_view/helpers' require 'sewing_kit/webpack/manifest' module SewingKit module Webpack module Helper include ActionView::Helpers class UnknownJavaScriptAssetError < StandardError end def sewing_kit_asset_paths(entrypoint_name, extension: 'js') return '' unless entrypoint_name.present? paths = SewingKit::Webpack::Manifest.asset_paths(entrypoint_name) return '' unless paths && paths[extension] entry_paths = paths[extension] entry_paths.unshift('/webpack/assets/dll/vendor.js') if serve_development_assets? && extension == 'js' entry_paths end def sewing_kit_link_tag(*paths) options = paths.extract_options! tags = paths.uniq.map do |path| next '' if path == '' create_asset_tag(:link, path, options) end safe_join(tags, "\n") end def sewing_kit_script_tag(*paths) options = paths.extract_options! tags = paths.uniq.map do |path| next '' if path == '' create_asset_tag(:script, path, options) end safe_join(tags, "\n") end private def create_asset_tag(tag_type, path, tag_options) raise ArgumentError, "Invalid tag type: #{tag_type}" unless [:script, :link].include? tag_type options = tag_options.clone if tag_options[:integrity] file_hash = extract_hash(path) options[:integrity] = maybe_integrity(file_hash) end case tag_type when :script content_tag(:script, '', options.reverse_merge(src: path)) when :link tag(:link, options.reverse_merge(href: path, rel: 'stylesheet')) end end def maybe_integrity(file_hash) return nil unless file_hash encode_hash(file_hash) end def encode_hash(hash) # sewing-kit's default config places a sha256 hash in the filename. Browsers expect integrity # to be the Base64 encoded version of the binary hash prefixed with the hashing algorithm binary_hash = Array(hash).pack('H*') "sha256-#{Base64.strict_encode64(binary_hash)}" end def extract_hash(path) # Consumes sewing-kit's [name]-[chunkhash].[ext] format return unless path =~ /.*-[A-Za-z0-9]{64}\.(js|css)/ path.rpartition('-').last.split('.').first end def serve_development_assets? return false if ENV['SK_SIMULATE_PRODUCTION'] == '1' Rails.env.development? end end end end