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ções setInterval e setTimeout também devem ser evitadas se o argumento for uma string. Utilize algo como setInterval(function(){ /* do something */ }, 1000).
  • evite operadores de incremento (++) e decremento (--); prefira algo como i += 1 e i -= 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.

Comentários #

# Tapajós disse:
20 Nov 08, 09:04AM

Falae Nando, beleza?

Lá na Improve It eu bolei um esquema de compactação de JS e CSS um pouco diferente. A idéia é compactar os arquivos apenas em produção, quando o servidor sobre. Eu modifiquei a funcionalidade do Rails de unificar tudo em um único arquivo e compactei.

Já pensou nisso?

[]'s

# Nando Vieira disse:
20 Nov 08, 10:18AM

Eu tinha pensando nisso sim... inclusive era a idéia inicial... mas achei melhor seguir por esse caminho porque tenho mais flexibilidade para fazer o que eu quiser, precisar! ;)

# Diego Carrion disse:
20 Nov 08, 01:13PM

Me parece que os operadores ++ e -- nao deveriam ocacionar problemas sempre e quando a linha na que se encontram terminase com punto e virgula (;). Pessoalmente acho mais elegante escrever algo do tipo count++ que count += 1, questao de gostos :)

# Leonardo A. Souza disse:
20 Nov 08, 05:27PM

Eu já uso uma solução parecida a algum tempo... realmente é uma mão na roda.

Mas me diz uma coisa, qual o problema com ++ e --? Eu nunca escrevi um código sequer com "i+=1" e nunca tive problemas.

# Walter Cruz disse:
20 Nov 08, 11:37PM

Como sugestão, vc poderia integrar o javascript lint. Dê uma olhada em http://www.javascriptlint.com/docs/index.htm . Ele é a engine de javascript do firefox extraída pra 1 executável, o jsl, que informa os erros que você teria no console do firefox.

Deixe um comentário





Não é aceito código HTML: adicione-o no pastie.org ou paste.milk-it.net e poste apenas o link.

Se este é seu primeiro comentário, ele terá que ser aprovado antes de ser exibido.