Skip to content

Instantly share code, notes, and snippets.

@SamMolokanov
Last active January 26, 2024 23:57
Show Gist options
  • Save SamMolokanov/efc441b44345c24662cf1063dd6ed950 to your computer and use it in GitHub Desktop.
Save SamMolokanov/efc441b44345c24662cf1063dd6ed950 to your computer and use it in GitHub Desktop.
Custom sprockets compressor as proxy skips minified assets

Proposal

Many modern Rails application use third-party assets like JavaScript and CSS libraries or frameworks. In most cases these assets are minified and compressed by tools outside of the Rails asset pipeline. For example popular gem React on Rails uses WebPack to build JavaScript bundles. When we include these files to assets pipeline it goes to minify again. We can safely avoid double minification and reduce assets compilation time. Meanwhile it is still good to add a digest and gzip compression.

Example

Savings are strongly depend on how many minified assets are used in an application, some numbers for a random application:

# standard setup

$ time RAILS_ENV=production rake assets:precompile

real    2m44.868s
user    2m25.503s
sys    0m17.170s
# avoid double minification 

$ time RAILS_ENV=production rake assets:precompile

real    2m2.841s
user    1m53.312s
sys    0m10.562s
require File.expand_path("../../lib/skippable_compressor", __FILE__)
module MyApp
class Application < Rails::Application
# Register and use SkippableCompressor for css and javascript files
Sprockets.register_compressor "text/css", :skippable_compressor, SkippableCompressor
Sprockets.register_compressor "application/javascript", :skippable_compressor, SkippableCompressor
config.assets.css_compressor = :skippable_compressor
config.assets.js_compressor = :skippable_compressor
# include minified bundles to pipeline
config.assets.precompile += %w[
generated/app1-bundle.min.js
generated/app2-bundle.min.js
generated/app3-bundle.min.js
]
end
end
# lib/skippable_compressor.rb
# Compressor class, skips files based on filename pattern
# Uses default compressors for CSS and JavaScript files, if assets are not minified
#
# Skips compression for filename 'jquery.min.js' but not for 'jquery.js'
# Skips compression for filename 'bootstrap.min.css' but not for 'bootstrap.css'
#
class SkippableCompressor
FILENAME_PATTERNS = {
"application/javascript" => /\.min\.js/,
"text/css" => /\.min\.css/,
}.freeze
def self.default_css_compressor
@default_css_compressor ||= Sprockets::SassCompressor.instance
end
def self.default_js_compressor
@default_js_compressor ||= Sprockets::UglifierCompressor.instance
end
def self.default_compressors
@default_compressors ||= {
"application/javascript" => default_js_compressor,
"text/css" => default_css_compressor,
}
end
def self.call(input)
content_type = input[:content_type]
if input[:filename] =~ FILENAME_PATTERNS[content_type]
input[:data]
else
default_compressors[content_type].call(input)
end
end
end
// Example for WebPack config
// add min suffix for generated files with React applications
config.output = {
filename: '[name]-bundle.min.js',
path: '../app/assets/javascripts/generated',
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment