Compactando Javascript e CSS antes de fazer um commit no Git
Leia em 2 minutos
O Git tem alguns hooks que permitem fazer coisas bem interessantes. Eu, malandro que sou, escrevi um para compactar arquivos Javascript e CSS utilizando YUI Compressor, desenvolvido pelo Yahoo!.
Em seu repositório Git, crie o arquivo ".git/hooks/pre-commit" com o conteúdo abaixo:
#!/bin/bash
cd "$0/../../.."
rake git:precommit
Execute o comando chmod a+x .git/hooks/pre-commit
. Se você não fizer isso, o Git não irá executar este hook. Ele irá executar a tarefa Rake git:precommit
.
Acesse a Yahoo! Developer Network e baixe o YUI Compressor. É um arquivo "jar", o que significa que você precisa ter Java 1.4 ou superior instalado. Alternativamente, você pode executar os comandos abaixo à partir da raíz de seu projeto Ruby on Rails.
wget http://www.julienlecomte.net/yuicompressor/yuicompressor-2.4.2.zip -O yuicompressor.zip
unzip yuicompressor.zip
mkdir tools
cp yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar tools/yuicompressor.jar
rm -rf yuicompressor-2.4.2/
Agora, podemos fazer todo o trabalho sujo utilizando código Ruby. Crie o arquivo lib/tasks/dev.rake
e adicione o código abaixo:
require "config/environment"
def run_compressor(type, files, except=[])
output_refs = {:css => "stylesheets", :js => "javascripts"}
output_dir = "public/#{output_refs[type]}"
files.each do |input|
output_name = File.basename(input)
output_name.gsub!(/\.(js|css)$/, "-min#{File.extname(output_name)}")
output = "#{output_dir}/#{output_name}"
system "java -jar tools/yuicompressor.jar --type=#{type} #{input} > #{output}"
original = file_size(input)
compressed = file_size(output)
puts " - #{File.basename(input)} [#{original}] => #{output_name} [#{compressed}]"
end
end
def file_size(file)
ActionController::Base.helpers.number_to_human_size(File.size(file))
end
namespace :git do
desc "Run before a Git commit"
task :precommit do
Rake::Task['compress:css'].invoke
Rake::Task['compress:javascript'].invoke
end
end
namespace :compress do
desc "Compress all javascript files using YUI Compressor"
task :javascript do
puts "\nCompressing javascript files"
files = Dir['public/javascripts/*.js'].reject do |f|
f =~ /-min\.js$/ || f =~ /\/jquery\.js/
end
run_compressor(:js, files)
end
desc "Compress all stylesheet files using YUI Compressor"
task :css do
puts "\nCompressing stylesheet files"
files = Dir['public/stylesheets/*.css'].reject do |f|
f =~ /-min\.css$/
end
run_compressor(:css, files)
end
end
É um código bastante simples. Ele simplesmente pega todos os arquivos Javascript e CSS que não possuem "-min" no nome e aplica a compactação. O arquivo "jquery.js" não é compactado pois sempre utilizado a versão reduzida.
Como saber se está funcionando? Faça um commit! Se os arquivos compactados foram gerados, tudo saiu como esperado. Você receberá uma saída como esta:
Compressing stylesheet files
- application.css [15.1 KB] => application-min.css [12.3 KB]
Compressing javascript files
- application.js [4.6 KB] => application-min.js [3.1 KB]
- facebox.js [9.2 KB] => facebox-min.js [4.9 KB]
- jquery.form.js [22.3 KB] => jquery.form-min.js [8.2 KB]
- rails.js [1 KB] => rails-min.js [466 Bytes]
Eu estou utilizando dois helpers que exibem a versão original dos arquivos caso eu esteja no ambiente de desenvolvimento, tornando a tarefa de depurar erros de Javascript e CSS mais simples.
module ApplicationHelper
def compressed_stylesheets(*files)
files.collect! do |f|
f.gsub!(/\.css$/, '')
"#{f}-min.css"
end unless Rails.env == "development"
stylesheet_link_tag *files
end
def compressed_javascripts(*files)
files.collect! do |f|
f.gsub!(/\.js$/, '')
"#{f}-min.js"
end unless Rails.env == "development"
javascript_include_tag *files
end
end
Para utilizá-los, basta passar o nome dos arquivos compactados.
<%= compressed_stylesheets 'application' %>
<%= compressed_javascripts 'rails', 'facebox', 'jquery.form.js', 'application' %>
Importante!
Tenha sempre em mente que se seu Javascript for ruim — por ruim quero dizer nas coxas, mal-feito, gambiarra, tosco, nojento, um código que nem seu pior inimigo deveria ter acesso — a compactação irá, provavelmente, gerar erros de Javascript (sintaxe inválida).
Para garantir nada irá quebrar, siga os passos explicados na página do JSLint; eu já sigo há um bom tempo e nunca tive nenhum problema em versões compactadas de meus códigos! Veja os erros mais comuns:
- não finalizar linhas de código com ponto-e-vírgula
- não utilize
eval
; as funçõessetInterval
esetTimeout
também devem ser evitadas se o argumento for uma string. Utilize algo comosetInterval(function(){ /* do something */ }, 1000)
. - evite operadores de incremento (++) e decremento (--); prefira algo como
i += 1
ei -= 1
Para ver muitas outras dicas, leia a documentação do JSLint.
Infelizmente, não é possível garantir que bibliotecas de terceiros irão funcionar em 100% dos casos. Leia o código antes de aplicar a compactação, já que você pode estar lidando com algo bastante ruim.