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:

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.