Grunt: Automatizando tarefas de build

Você já parou para pensar em quantas tarefas repetidas você executa na hora de fazer build/deploy do seu projeto de Frontend?
Vamos pensar em algumas mais comuns:
- concatenar arquivos css/js;
- minificar css/js;
- processar arquivos sass/less;
- minificar imagens;
- minificar html;
- fazer upload dos arquivos para o servidor;
Agora tente pensar em quanto tempo você gasta diariamente na realização dessas tarefas. Por alto, podemos dizer que perde-se tempo suficiente para até 5 xícaras de café na copa 😀
Mas então você deve se perguntar, como agilizar esse processo e ganhar essas preciosas 5 doses diárias de café a mais? Eu te respondo, com o Grunt!.
O que é Grunt?

O Grunt é uma ferramenta de linha de comando criada pelo Ben Alman, e é basicamente um executor tarefas. Sabe todas as tarefas repetitivas listadas agora a pouco, além de todas as outras que você realiza mas não foram listadas? Você pode deixar que o Grunt execute elas para você.
Por que usar um executor de tarefas?
Em uma palavra: automação. Quanto menos trabalho você tem executando tarefas repetitivas como minificação, compilação, teste de unidade, validação, etc, mais fácil se torna o seu trabalho.
Depois de configurado, um executor de tarefas pode realizar boa parte do trabalho para você com basicamente nenhum esforço.
Por que usar o Grunt?
O ecossistema do Grunt é enorme e continua crescendo diariamente. Com literalmente centenas de plugins disponíveis, você pode usar o Grunt para automatizar praticamente qualquer coisa com o mínimo de esforço.
Se ninguém tiver desenvolvido o que você precisa, criar e publicar o seu próprio plugin do Grunt é extremamente fácil. Aqui você encontra mais detalhes de como criar o seu plugin para o Grunt.
Quem usa Grunt?
Atualmente o Grunt é utilizado em uma grande quantidade de projetos Web pelo mundo afora. Projetos como jQuery e Bootstrap utilizam o Grunt para automatizar o build. A imagem abaixo demonstra algumas empresas/projetos que utilizam o Grunt.

Enfim, atualmente considero o Grunt como uma ferramenta indispensável para qualquer projeto de Frontend Web. Se você ainda não está usando, está perdendo tempo (literalmente)!
Mais detalhes sobre a ferramenta podem ser encontrados na página oficial do Grunt.
Grunt: Primeiros Passos

Então, vou usar o Grunt, do que eu preciso?
O primeiro passo é entender como o Grunt funciona. O Grunt é implementado em Javascript e roda no Node.js, então antes de instalar o Grunt você vai precisar ter o node e o npm instalados. Caso você não tenha, seguem alguns links de como instalar o node e npm no Windows e como instalar o node e npm no Ubuntu.
Instalação do Grunt
Tendo instalado o node e o npm na sua máquina, o único trabalho para instalar o Grunt é rodar o seguinte comando:
npm install -g grunt-cli
Isto vai habilitar o comando grunt no terminal.
Como você já deve ter notado, instalamos o grunt-cli e não o grunt, a função do grunt-cli é executar o grunt configurado para o projeto. Dessa forma é possível ter vários projetos, cada um rodando uma versão diferente do grunt sem problemas.
Configurando o projeto para utilizar o Grunt
Para um projeto utilizar o Grunt, são necessários pelo menos dois arquivos, o package.json e o Gruntfile.js.
package.json
O arquivo package.json é utilizado pelo npm para armazenar os meta-dados do seu projeto. Informações como nome do projeto, autor, url, licença, repositório e dependências, são todas armazenadas neste arquivo.
Então é por isso que o Grunt precisa dele, ao instalar um plugin do grunt no projeto, esse plugin é registrado como dependência no arquivo package.json.
O arquivo package.json deve estar no diretório raiz da sua aplicação. Você pode criar um através do comando npm init.
Ao rodar o comando npm init ele vai solicitar os dados do seu projeto, nem todos são obrigatórios. Executei o comando aqui, preenchi algumas informações e gerei o seguinte arquivo package.json:
{
"name": "projeto",
"version": "1.0.0",
"description": "Meu Projeto",
"main": "package.json",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Andrew Cavalcante Pacifico",
"license": "Apache"
}
package.jsonPara instalar o Grunt e os plugins dele no seu projeto, utilize sempre o comando npm install <dependência> --save-dev
, dessa forma a dependência é automaticamente adicionada à sessão devDependencies do arquivo package.json.
A importância de adicionar a dependência à sessão devDependencies do package.json, é que caso você precise configurar o projeto novamente em uma outra máquina, basta rodar o comando npm install, e todas as dependências do projeto (incluindo o Grunt e seus plugins) serão automaticamente instaladas.
Então vamos instalar o Grunt localmente em nosso projeto. Para isso basta executar o seguinte comando:
npm install grunt --save-dev
Um diretório chamado node-modules deve ter sido adicionado ao seu projeto, este diretório conterá todas as dependências do seu projeto instaladas via npm.
Este diretório deve conter um outro diretório chamado grunt, o que indica que o Grunt foi instalado localmente no seu projeto.
Se você der uma olhada no arquivo package.json verá que ele conterá agora a seguinte sessão:
"devDependencies": {
"grunt": "~0.4.5"
}
Isso significa que o grunt foi adicionado como dependência do projeto.
Caso você esteja utilizando algum software de controle de versão para o seu projeto, é importante ressaltar que apenas o arquivo package.json precisa ser versionado, o diretório node_modules deve ser deixado de fora, uma vez que ele é gerado automaticamente.
Gruntfile.js
O Gruntfile.js é o arquivo onde você vai definir e as tarefas e configurar os plugins do Grunt. Ele é um arquivo Javascript que deve ser colocado na raiz do seu projeto junto com o package.json, e assim como ele, também deve ser versionado.
O Gruntfile é formado basicamente pela configuração do projeto/plugins, pelo carregamento dos plugins do Grunt, definição das tarefas, e uma função que engloba tudo isso. Abaixo temos um exemplo de um Gruntfile básico.
module.exports = function(grunt) {
// configuração dos plugins
grunt.initConfig({
});
// carregamento dos plugins
grunt.loadNpmTasks('nome-do-plugin');
// definição das tarefas
grunt.registerTask('default', []);
};
gruntfile.jsNo código acima é possível perceber as três seções principais mencionadas.
Toda a configuração dos plugins e tarefas do Grunt deve ser feita através de um objeto passado como parâmetro para a função grunt.initConfig()
.
Todos os plugins instalados devem ser carregados através da chamada da função grunt.loadNpmTasks()
.
E por fim, você pode definir as suas tarefas através da função grunt.registerTask()
. Cada plugin instalado automaticamente cria uma tarefa com o seu nome, mas se o objetivo é automatizar, não faz sentido termos que chamar todas as tarefas uma por uma, então você pode definir uma tarefa default que é executada sempre que o comando grunt for executado, e pode configurar esta tarefa para executar todas as outras.
Plugins do Grunt

Agora que você já sabe como instalar e configurar o Grunt, só o que falta é começar instalar os plugins. Como eu mencionei no início do artigo, o Grunt já tem plugin pra praticamente tudo, então vamos configurar alguns dos mais comuns para o nosso projeto.
Concat
Geralmente quando trabalhamos em um projeto mais complexo, o ideal é modularizar o nosso código Javascript, e dividir em diferentes arquivos para facilitar a manutenção. Porém para melhorar a performance do projeto, quanto menos arquivos carregados melhor. Então como proceder?
Neste caso podemos utilizar o plugin grunt-contrib-concat, que como o próprio nome já diz, é utilizado para concatenar arquivos. Dessa forma é possível possuir diversos arquivos com Javascript, e configurar o Grunt para juntar todos esses arquivos em um só.
Para instalar o plugin, basta executar o comando abaixo:
npm install grunt-contrib-concat --save-dev
Depois disso, vamos criar o nosso Gruntfile.js, com o seguinte conteúdo:
module.exports = function(grunt) {
// configuração dos plugins
grunt.initConfig({
concat: {
dist: {
src: [ 'js/src/functions.js', 'js/src/core.js' ],
dest: 'js/dist/projeto.js',
}
}
});
// carregamento dos plugins
grunt.loadNpmTasks('grunt-contrib-concat');
// definição das tarefas
grunt.registerTask('default', ['concat']);
};
No código acima carregamos o plugin grunt-contrib-concat, definimos que a tarefa default deverá executar a tarefa referente ao plugin, e definimos as configurações do plugin.
Nas configurações do plugin o que fizemos foi definir como source os arquivos functions.js e core.js ambos localizados no diretório js/src, e definimos como destino um único arquivo chamado projeto.js que deverá ser gerado no diretório js/dist (que é criado caso não exista).
Portanto, ao executar o comando grunt os arquivos functions.js e core.js serão automaticamente concatenados em um novo arquivo chamado projeto.js.
Mágica não é? 😀
O plugin concat possui uma série de outras configurações possíveis, mais informações é só consultar a documentação do plugin.
Uglify
Uma das práticas mais conhecidas de quem trabalha com frontend é a de minificar arquivos Javascript e CSS, dessa forma é possível diminuir o tempo de carregamento da página melhorando a performance e a experiência do usuário.
Durante algum tempo, o que eu fazia em meus projetos era procurar algum site de minificação como o http://jscompress.com/, copiar e colar o meu código lá, pegar o código minificado e salvar em um arquivo .min.js. E quando eu alterava alguma coisa no arquivo? Exato, tinha que fazer tudo isso de novo.
O grunt-contrib-uglify é um plugin para o Grunt que faz exatamente isso pra você. Nele você pode especificar uma lista de arquivos js de entrada e um arquivo js de saída que terá o conteúdo dos arquivos de entrada, e já minificados.
Sabendo disso, então qual a necessidade do concat? Bem, geralmente em ambiente de desenvolvimento nós não queremos os arquivos minificados, então o ideal é configurar o grunt para executar o concat em desenvolvimento, e o uglify em uma task de build/deploy.
Então vamos lá, para instalar o plugin basta rodar o comando abaixo:
npm install grunt-contrib-uglify --save-dev
Após a instalação vamos adicionar a seguinte configuração ao nosso Gruntfile no objeto passado para a função grunt.initConfig
:
uglify: {
dist: {
files: {
'js/dist/projeto.js': [
'js/src/functions.js',
'js/src/core.js'
]
}
}
}
Então adicionamos a linha para carregar o plugin:
grunt.loadNpmTasks('grunt-contrib-uglify');
E vamos definir uma nova task chamada deploy, que irá executar o uglify.
grunt.registerTask('deploy', ['uglify']);
Dessa maneira, ao executar o comando grunt no terminal, os seus arquivos Javascript serão concatenados em um único arquivo chamado projeto.js, e ao executar o comando grunt deploy eles serão concatenados e minificados.
CSSMin
Beleza, falei que é essencial minificar Javascript e CSS, e mostrei o Uglify que minifica o Javascript. Mas e o CSS?
Pra minificar o CSS tem esse cara aí, CSSMin.
A instalação do plugin segue o padrão dos plugins anteriores:
npm install grunt-contrib-cssmin --save-dev
A configuração do plugin no Gruntfile vai ficar assim:
cssmin: {
dist: {
files: {
'css/dist/projeto.css': [
'css/src/components.css',
'css/src/core.css'
]
}
}
}
Então adicionamos o carregamento do plugin no Gruntfile:
grunt.loadNpmTasks('grunt-contrib-cssmin');
E alteramos a definição da task de deploy para executar além do uglify, o cssmin:
grunt.registerTask('deploy', ['uglify', 'cssmin']);
Nessa configuração do exemplo, assumimos que o nosso projeto possui dois arquivos css, um contendo as definições dos componentes utilizados na página, e outro com as configurações mais genéricas. No build nós vamos juntar esses arquivos e minifica-los em um único arquivo chamado projeto.css.
A mesma estratégia do Javascript pode ser utilizada também aqui, usar o concat em ambiente de desenvolvimento, e o cssmin para deploy.
Watch
O ser humano é sempre acha um problema em tudo 😀 , digo isso pois quando eu comecei a usar o Grunt, mesmo com todas as vantagens, eu reclamei do fato de precisar ir até o terminal e rodar o comando grunt todas as vezes que fizesse alguma alteração no meu projeto.
Se você estiver achando que vai precisar fazer a mesma coisa, adianto logo que não.

O Grunt possui um plugin chamado Watch responsável por ficar observando os seus arquivos e aguardando que alguma alteração aconteça, e quando acontecer, executar uma determinada tarefa.
Então, o primeiro passo como sempre, instalar o plugin:
npm install grunt-contrib-watch --save-dev
E vamos adicionar a configuração do plugin, que vai ser a seguinte:
watch: {
scripts: {
files: [
'js/src/functions.js',
'js/src/core.js'
],
tasks : [ 'concat' ]
}
}
Nessa configuração, nós definimos para o Watch ficar observando alterações nos nossos arquivos Javascript, e caso algum deles seja alterado, automaticamente a task concat será executada.
Adicionamos o carregamento do plugin:
grunt.loadNpmTasks('grunt-contrib-watch');
E dessa vez não vamos adicionar a task do plugin a nenhuma das tasks que nós definimos, pois uma vez que o watch gera uma task que fica escutando alterações, ele ocupa o terminal, então deve ser executado sozinho.
Portanto, após a instalação, carregamento e configuração do watch no nosso Gruntfile, só o que precisamos fazer é rodar o comando grunt watch
. Ao fazer isso, o terminal deve exibir algo mais ou menos assim:

Isso indica que o grunt watch está executando e aguardando alguma alteração nos arquivos configurados para serem observados.
Ao realizar uma alteração em um dos arquivos, o que deve acontecer é mais ou menos isso:

Percebam o que aconteceu. O grunt watch detectou uma alteração no arquivo core.js então executou a task concat conforme foi definido na configuração.
Outros Plugins
Nós demos uma olhada por alto em alguns dos plugins mais básicos, e mais comuns. Deixo claro que todos eles têm ainda uma porção de outras configurações possíveis, portanto vale a pena dar uma olhada na documentação detalhada de cada um e adapta-los para a sua necessidade.
Mas agora que você já conhece o Grunt, o próximo passo é buscar outros plugins que atendam às suas necessidades, como eu falei, tem plugin pra praticamente tudo. Abaixo listo alguns dos plugins que eu utilizo nos meus projetos:
- ImageMin: Compressão de imagens;
- Copy: Copia arquivos e diretórios;
- Exec: Executa comandos shell;
- HTMLMin: Minificação de HTML;
- Replace: Alteração de trechos de um arquivo seguindo um padrão;
- Sass: Compila arquivos do SASS para Css;
- Rsync: Utiliza o rsync para sincronizar diretórios. Excelente como alternativa ao FTP para fazer deploy de aplicações.
Enfim, existem centenas de outros plugins disponíveis, a página oficial do Grunt oferece uma lista de plugins disponíveis, é só ir lá e dar uma olhada.
Conclusão

Então pessoal, com isso já dá pra ter uma boa noção de como usar o Grunt nos seus projetos. Lembrando que o Gruntfile é um arquivo javascript padrão, logo você pode programar lá dentro conforme as suas necessidades, sem problema algum.
Caso alguém queira dar uma olhada no “projeto” exemplo completo, é só conferir aqui: https://github.com/ProgramAi/tutorial-grunt.
Vale mencionar que o Grunt não é a única ferramenta de automação de build disponível no mercado, uma outra ferramenta que tem muitos adeptos é o Gulp. Tem diversos artigos na Web comparando os dois. Vale a pena dar uma avaliada também.
Então, por enquanto é isso. Dúvidas, sugestões, opiniões, por favor mandem nos comentários.
Não esqueçam de acompanhar o blog nas redes sociais.
Valeu e até a próxima.
Brax!

Bacharel em Ciência da Computação pela Universidade Federal do Amazonas, MBA em Gerenciamento de Projetos pela Fundação Getúlio Vargas.
Atualmente trabalha como Gerente de Engenharia na Meliuz.
Com 20 anos de experiência na área, já trabalhou com um pouco de tudo, C, C++, Java, C#, Delphi, Node.js, AWS, PHP, Python, React, Angular, jQuery, e mais um monte de coisa que nem existe mais 😀