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 #

#1 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

#2 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! ;)

#3 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 :)

#4 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.

#5 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.

jQuery: Dominando o framework

Você quer aprender a usar jQuery de verdade? Então chegou a hora! Neste workshop você verá como funciona este framework de JavaScript, entendendo todos os aspectos que fazem do jQuery uma das melhores ferramentas para desenvolvimento de interfaces.

Saiba mais Fechar

Conheça também o HOWTO