task 'assets:precompile:before' do require 'uglifier' unless %w{profile production}.include? Rails.env raise "rake assets:precompile should only be run in RAILS_ENV=production, you are risking unminified assets" end # Ensure we ALWAYS do a clean build # We use many .erbs that get out of date quickly, especially with plugins puts "Purging temp files" `rm -fr #{Rails.root}/tmp/cache` if Rails.configuration.assets.js_compressor == :uglifier && !`which uglifyjs`.empty? && !ENV['SKIP_NODE_UGLIFY'] $node_uglify = true end puts "Bundling assets" # in the past we applied a patch that removed asset postfixes, but it is terrible practice # leaving very complicated build issues # https://github.com/rails/sprockets-rails/issues/49 require 'sprockets' require 'digest/sha1' # Needed for proper source maps with a CDN load "#{Rails.root}/lib/global_path.rb" include GlobalPath if $node_uglify # monkey patch asset pipeline not to gzip, compress: false is broken class ::Sprockets::Asset # Save asset to disk. def write_to(filename, options = {}) # Gzip contents if filename has '.gz' return if File.extname(filename) == '.gz' begin FileUtils.mkdir_p File.dirname(filename) File.open("#{filename}+", 'wb') do |f| f.write to_s end # Atomic write FileUtils.mv("#{filename}+", filename) # Set mtime correctly File.utime(mtime, mtime, filename) nil ensure # Ensure tmp file gets cleaned up FileUtils.rm("#{filename}+") if File.exist?("#{filename}+") end end end module ::Sprockets class UglifierCompressor def evaluate(context, locals, &block) # monkey patch cause we do this later, no idea how to cleanly disable data end end end end end task 'assets:precompile:css' => 'environment' do if ENV["DONT_PRECOMPILE_CSS"] == "1" STDERR.puts "Skipping CSS precompilation, ensure CSS lives in a shared directory across hosts" else STDERR.puts "Start compiling CSS: #{Time.zone.now}" RailsMultisite::ConnectionManagement.each_connection do |db| # Heroku precompiles assets before db migration, so tables may not exist. # css will get precompiled during first request instead in that case. if ActiveRecord::Base.connection.table_exists?(ColorScheme.table_name) STDERR.puts "Compiling css for #{db}" [:desktop, :mobile, :desktop_rtl, :mobile_rtl].each do |target| STDERR.puts "target: #{target} #{DiscourseStylesheets.compile(target)}" end end end STDERR.puts "Done compiling CSS: #{Time.zone.now}" end end def assets_path "#{Rails.root}/public/assets" end def compress_node(from,to) to_path = "#{assets_path}/#{to}" source_map_root = (d=File.dirname(from)) == "." ? "/assets" : "/assets/#{d}" source_map_url = cdn_path "/assets/#{to}.map" cmd = "uglifyjs '#{assets_path}/#{from}' -p relative -c -m -o '#{to_path}' --source-map-root '#{source_map_root}' --source-map '#{assets_path}/#{to}.map' --source-map-url '#{source_map_url}'" STDERR.puts cmd result = `#{cmd} 2>&1` unless $?.success? STDERR.puts result exit 1 end result end def compress_ruby(from,to) data = File.read("#{assets_path}/#{from}") uglified, map = Uglifier.new(comments: :none, screw_ie8: true, source_filename: File.basename(from), output_filename: File.basename(to) ) .compile_with_map(data) dest = "#{assets_path}/#{to}" File.write(dest, uglified << "\n//# sourceMappingURL=#{cdn_path "/assets/#{to}.map"}") File.write(dest + ".map", map) end def gzip(path) STDERR.puts "gzip #{path}" STDERR.puts `gzip -f -c -7 #{path} > #{path}.gz` end def compress(from,to) if @has_uglifyjs ||= !`which uglifyjs`.empty? compress_node(from,to) else compress_ruby(from,to) end end task 'assets:precompile' => 'assets:precompile:before' do # Run after assets:precompile Rake::Task["assets:precompile:css"].invoke if $node_uglify puts "Compressing Javascript and Generating Source Maps" manifest = Sprockets::Manifest.new(assets_path) to_skip = Rails.configuration.assets.skip_minification || [] manifest.files .select{|k,v| k =~ /\.js$/} .each do |file, info| path = "#{assets_path}/#{file}" _file = (d = File.dirname(file)) == "." ? "_#{file}" : "#{d}/_#{File.basename(file)}" _path = "#{assets_path}/#{_file}" if File.exists?(_path) STDERR.puts "Skipping: #{file} already compressed" else STDERR.puts "Compressing: #{file}" # We can specify some files to never minify unless (ENV["DONT_MINIFY"] == "1") || to_skip.include?(info['logical_path']) FileUtils.mv(path, _path) compress(_file,file) end info["size"] = File.size(path) info["mtime"] = File.mtime(path).iso8601 gzip(path) end end # protected manifest.send :save end end