Conhecendo Grunt: O Task Runner em JavaScript

1 de agosto de 2022
Ronaldo B.

Neste artigo iremos falar sobre o Grunt. Ele é um Task Runner, que em português livre significa “Executador de Tarefas”. Ele foi criado em 2012 e hoje possui cerca de 423.000 downloads semanais no npm. Mas, como ele nos ajuda? Ele nos auxilia em uma questão muito importante, a automatização.

O que é automatizar?

A palavra automatizar significa basicamente converter algum serviço em uma tarefa automática. Ou seja, permitir que um serviço repetitivo se torne mais simples de realizar, de maneira mecânica. Isso nos ajuda a desempenhar o serviço de maneira mais ágil e otimizada.

Automatização é algo que o ser humano está acostumado a criar. Pense apenas em dois exemplos: 1) pense em como se percebeu com o tempo a necessidade de criar portões de garagem automáticos, para que não precisássemos mais ficar descendo de nossos carros para abri-los e fechá-los ou 2) pense em como foi necessário criar uma parafusadeira elétrica, para que o serviço que antes levava alguns minutos, pudesse ser realizado em poucos segundos. Esses dois exemplos são bem diferentes um do outro, mas ambos têm em comum a automatização.

Quando trabalhamos com o Desenvolvimento Web não é diferente. Nós realizamos muitas tarefas que podem aos poucos se tornar repetitivas. Veja alguns exemplos: 1) sabemos que é muito interessante ter um arquivo CSS ou JS que contenha todos os códigos desenvolvidos nessas linguagens. Isso ajuda a deixar nosso arquivo HTML menor. 2) Também é recomendado que retiremos todos os espaços e quebras de linhas de nossos arquivos externos, ou seja, minificar os arquivos. Isso é importante para que nosso site fique mais leve. Essas duas tarefas são necessárias, mas ao mesmo tempo são repetitivas. Não seria bom se pudéssemos automatizá-las? Com o Grunt isso é possível!

Como instalar o Grunt?

Ele foi construído completamente em JavaScript e é executado no Terminal. É necessário o Node para instalá-lo (usando o npm) e para executá-lo também. Se você não possui o Node, poderá conferir como instalá-lo em um de nossos vídeos no YouTube, clicando aqui.

Após instalar o Node, vamos instalar o Grunt de fato em nosso computador. Para isso, abra um Terminal e execute o seguinte comando:

npm install grunt-cli -g

O Node irá usar a nossa Internet para realizar o download do Grunt CLI e instalá-lo de maneira global em nosso sistema. Assim, poderemos utilizar seus recursos em qualquer projeto que desejarmos.

Iremos criar um pequeno projeto para que possamos testar o Grunt. Esse projeto irá conter uma pasta chamada assets, que irá armazenar os nossos arquivo CSS e JavaScript. Cada arquivo CSS irá conter estilos específicos para uma seção de nosso site. E cada arquivo JavaScript irá conter uma função útil para nosso sistema. Também possuíremos uma pasta chamada dist, que irá conter os arquivos de terceiros. A estrutura de pastas ficará assim:

Estrutura de Pastas do Projeto sobre Grunt

O Grunt irá trabalhar bastante com o Node, principalmente baixando dependências. Assim, será necessário iniciar um projeto Node neste diretório. Para isso, vamos executar o seguinte comando:

npm init -y

Adicionando o Grunt ao Projeto

Para adicionar o Grunt ao nosso projeto, vamos realizar o download dele. Talvez nos perguntemos: “Mas já não instalamos o Grunt agora há pouco de maneira global?” Na realidade, aquele era o Grunt CLI, responsável por realizar a chamada dos comandos do Grunt pela linha de comando. Contudo, o Grunt de fato, que nos dará todos os recursos de automatização, será instalado agora. Faremos isso com o seguinte comando:

npm install grunt

O próximo passo é criar o arquivo Gruntfile.js. Iremos adicionar nele todos os códigos relacionados com as funções do Grunt. Como estamos trabalhando com Node.js, iremos exportar um módulo neste arquivo. A sintaxe básica dele será a seguinte:

module.exports = grunt => {

};

Lembrando que nós ensinamos tudo sobre módulos nativos e módulos de terceiros em nosso Curso Completo de Node.js, onde desenvolvemos projetos incríveis usando o Node.

Vamos agora criar a nossa primeira automatização com o Grunt. Lembra que falamos sobre a boa prática de juntar todos os arquivos CSS e JS em um só? O Grunt possui um módulo que faz isso, o grunt-contrib-concat. Para usá-lo, é necessário instalá-lo usando o npm. Vamos executar o seguinte no Terminal:

npm install grunt-contrib-concat

Depois, em nosso arquivo Gruntfile.js, iremos implementá-lo em nosso código usando o método loadNpmTasks(), próprio do Grunt. O código ficará assim:

module.exports = grunt => {

	grunt.loadNpmTasks("grunt-contrib-concat");

};

Para iniciar a configuração do Grunt, iremos usar o método initConfig(). Ele espera um objeto com as configurações. A primeira que iremos realizar é a do concat. O código ficará assim:

grunt.initConfig({
	concat: {

	}
});

 A chave “concat” é um nome padrão, que vem do módulo do concat. Dentro dessa chave iremos informar os arquivos CSS e JS que queremos juntar, com o seguinte código:

grunt.initConfig({
	concat: {
		css: {
			src: [
				'./assets/css/header.css',
				'./assets/css/section.css',
				'./assets/css/footer.css'
			],
			dest: './dist/all.css'
		},
		js: {
			src: [
				'./assets/js/site.js',
				'./assets/js/utils.js'
			],
			dest: './dist/all.js'
		}
	}
});

Explicando: nós separamos o que o Grunt irá executar entre CSS e JS, com duas chaves do objeto. Dentro de cada um desses objetos nós temos duas opções:

1 - src. Ela define quais serão os arquivos que iremos juntar. Ela espera um array de caminhos para os arquivos.

2 - dest. Define qual será o arquivo final ou arquivo de destino onde todos os códigos dos arquivos acima serão reunidos. Ele espera uma string com o caminho para esse arquivo. Algo muito interessante é que se esse arquivo não existir, ele irá criá-lo para nós.

Vamos testar esse módulo. Vamos voltar em nosso Terminal e executar grunt concat. Será retornado o seguinte:

Retorno do comando em nosso Terminal

A tarefa foi executada corretamente, sem erros. Se formos até a pasta dist, encontraremos os arquivos all.css e all.js, que estarão assim:

Conteúdo do arquivo all.css
Conteúdo do arquivo all.js

Algo interessante é que como separamos esse módulo em dois namespaces, podemos executar simplesmente grunt concat:css e apenas os arquivos CSS serão afetados. Podemos fazer o mesmo para os arquivos JS.

Esses estilos e funções estavam em arquivos separados, mas conseguimos juntar seus conteúdos por meio do Grunt. O módulo concat é bem útil, não concorda?

Vamos aprender mais um módulo, relacionado à minificação dos arquivos. Vamos começar com o CSS. Há um módulo chamado grunt-contrib-cssmin. Para implementá-lo em nosso projeto, vamos seguir os mesmos passos que realizamos com o concat.

Primeiro vamos realizar o download dele usando o npm, com o seguinte comando:

npm install grunt-contrib-cssmin

Vamos realizar a requisição dele em nosso código com a seguinte linha de código:

grunt.loadNpmTasks("grunt-contrib-cssmin");

Por fim, vamos adicioná-lo à nossa configuração do Grunt. O nome padrão de sua chave é cssmin. Ele ficará assim em nosso código:

cssmin: {
	styles: {
		files: {
			'./dist/all.min.css': ['./dist/all.css']
		}
	}
}

Explicando: as chaves styles e files são necessárias para que o cssmin seja corretamente executado. Dentro da opção files nós informamos primeiro o caminho destino em que o arquivo minificado será criado e depois o arquivo original, que sofrerá a minificação.

Para testar se está tudo funcionando, vamos executar o seguinte em nosso Terminal:

grunt cssmin

Será retornada a seguinte mensagem no Terminal:

O retorno da execução do comando em nosso Terminal

A execução foi um sucesso. Nesse momento um arquivo all.min.css já deve estar criado em nosso diretório dist. Esse será o conteúdo dele:

Conteúdo do arquivo all.min.css

Incrível! Nosso arquivo que já estava com seus estilos todos unidos em um mesmo local, agora também está minificado com a ajuda do Grunt. Contudo, perceba que se alterarmos algo no arquivo CSS original e desejarmos repetir o processo, vamos precisar executar os dois comandos novamente. Não seria melhor se pudéssemos executar o comando apenas uma vez e ele realizasse tudo para nós? Isso é possível com o Grunt, através do método registerTask(). Esse método registra uma tarefa personalizada, é como um “ouvinte de evento” do Grunt. Precisamos criar esse método fora da variável da função de configuração. A sintaxe dele ficará assim:

grunt.registerTask('styles', function() {

	grunt.task.run(['concat:css', 'cssmin']);
	
});

Explicação: o primeiro parâmetro é o nome da tarefa personalizada. Podemos escolher o nome que desejarmos. O segundo parâmetro é uma função e dentro dela nós realizamos a chamada do método nativo do grunt chamado run(). Ele é como um trigger do Grunt, e dispara a chamada de um ou mais módulos. No caso, realizamos a chamada dos dois módulos relacionados com o CSS.

Para testar se isso está funcionando, vamos adicionar mais um estilo CSS ao arquivo footer.css. Iremos adicionar o estilo text-decoration: none, apenas como exemplo. Agora, basta executar em nosso Terminal grunt styles, que é a tarefa que criamos. Quando executamos isso no Terminal, vemos o seguinte retorno:

Retorno da execução do Grunt em nosso Terminal

Note que as duas tarefas foram executadas. Agora, se formos em nossos arquivos all.css e all.min.css veremos o seguinte:

Conteúdo do arquivo all.css
Conteúdo do arquivo all.min.css

Os dois arquivos foram alterados com o uso da tarefa personalizada do Grunt.

O próximo módulo que iremos conhecer será um módulo para a minificação de arquivos JavaScript. Seu nome é uglify. Iremos repetir o processo de instalação, já estamos ficando craques nisso :).

Primeiro iremos executar em nosso Terminal:

npm install grunt-contrib-uglify

Vamos adicionar esse módulo ao projeto com o método loadNpmTasks():

grunt.loadNpmTasks("grunt-contrib-uglify");

E agora vamos criar sua configuração no método initConfig():

uglify: {
	functions: {
		files: {
			'./dist/all.min.js': ['./dist/all.js']
		}
	}
}

Perceba que a estrutura é bem similar ao cssmin. Apenas mudamos a chave “styles” por “functions”. O que acha de praticarmos a criação de tarefas personalizadas usando o registerTask()? Vamos unir tudo relacionado ao JavaScript usando a tarefa “scripts”, que iremos criar agora:

grunt.registerTask('scripts', () => {

	grunt.task.run(['concat:js', 'uglify']);
	
	grunt.log.ok('Tudo certo!');
	
});

A sintaxe é bem similar à última tarefa personalizada que criamos. Apenas uma pequena diferença: veja que usamos a propriedade .log.ok() nesse exemplo. Essa propriedade do Grunt nos permite escrever mensagens de feedback no Terminal, informando se tudo ocorreu bem, ou não. O método ok() é para sucesso, mas existem outros métodos:

1 - warn() para informar um alerta.

2 - error() para informar um erro.

3 - writeln() para escrever alguma mensagem em linha.

4 - header() e subhead() que definem um cabeçalho para a mensagem.

Voltando aos testes, vamos executar grunt scripts em nosso Terminal. Esse será o resultado:

Retorno do comando em nosso Terminal

Veja que a mensagem de sucesso foi retornada em verde, como programamos acima e também nos foi informado que as duas tarefas foram executadas com sucesso. Se formos ao arquivo all.min.js, veremos o seguinte:

Conteúdo do arquivo all.min.js

Nosso código foi corretamente minificado. Se realizarmos alguma alteração em nossos arquivos JS originais, basta executar novamente esse comando no Terminal e a compilação ocorrerá novamente.

Aprendemos módulos muito interessantes, mas iremos aprender mais um que nos ajuda bastante. Imagine que estamos usando o Grunt em nosso desenvolvimento e alteramos o arquivo CSS. Vamos precisar executar no Terminal a tarefa que minimiza o CSS. Se alteramos o JS faremos a mesma coisa. Entretanto, essas alterações são muito recorrentes, o que pode deixar a tarefa de executar os comandos no Terminal um pouco repetitiva. Não seria mais interessante ter algum módulo que executasse automaticamente a chamada dessas tarefas? É para isso que existe o módulo watch. Ele é como um “vigia” que fica atento às alterações em nosso código. Quando salvamos um arquivo, ele executa no Terminal todas as tarefas que já programamos.

Vamos implementá-lo à nossa aplicação, repetindo o processo que já fizemos algumas vezes. Primeiro, vamos executar npm install grunt-contrib-watch em nosso Terminal. Vamos trazer esse módulo para nossa aplicação com a seguinte linha de código:

grunt.loadNpmTasks("grunt-contrib-watch");

E, para configurar sua atuação em nosso método initConfig(), iremos usar o seguinte código:

watch: {
	css: {
		files: ['./assets/css/*.css'],
		tasks: ['concat:css', 'cssmin']
	},
	js: {
		files: './assets/js/*.js',
		tasks: ['concat:js', 'uglify']
	}
}

Iremos dividir o módulo novamente entre arquivos CSS e JS. A propriedade files espera um array com os arquivos que ele precisa vigiar, ou seja, ficar atento às mudanças. Perceba que tanto no CSS quanto no JavaScript nós usamos o * (asterisco), para que o watch fique vigiando todos os arquivos com as extensões .css e .js. E a propriedade tasks espera um array de tarefas que devem ser executadas quando um arquivo daquela extensão for salvo. Nós basicamente inserimos as tarefas que já havíamos criado nas tarefas personalizadas.

Para testar o Grunt, vamos executar grunt watch em nosso Terminal. Veremos o seguinte resultado:

Retorno de nosso módulo no Terminal

Veja que ele retorna uma mensagem de “Esperando”. Esse módulo é diferente pois ele não encerra seu processo, mas fica o tempo inteiro com o Terminal sendo executado, esperando mudanças. Se alterarmos qualquer arquivo agora e o salvarmos, veremos o seguinte no Terminal:

Retorno de nosso módulo no Terminal

O Grunt consegue perceber qual arquivo foi alterado, no caso o header.css, quais tarefas foram executadas e até mesmo a data em que isso ocorreu. Sensacional, não é mesmo?

Nós podemos fazer apenas mais uma mudança. O método registerTask() pode criar tarefas personalizadas, mas ele também possui tarefas padrão. Uma dessas tarefas se chama “default”, que define o que ocorrerá se executarmos apenas grunt no Terminal. Vamos criar essa tarefa com o seguinte código:

grunt.registerTask('default', ['watch']);

Isso mostra que podemos informar para o método registerTask() apenas um array de tarefas se desejarmos, ao invés de uma função

Agora, se executarmos apenas grunt no Terminal, veremos o seguinte:

Retorno da chamada do módulo em nosso Terminal

Agora a tarefa watch é disparada por padrão e podemos deixar essa tarefa sendo executada enquanto estivermos programando nossos projetos.

Aprendemos alguns módulos do Grunt neste artigo, mas existem muitos outros. Você pode encontrar uma lista completa dos outros módulos ou plugins clicando aqui.

Você também pode encontrar os códigos desenvolvidos neste artigo em nosso GitHub, clicando aqui.

Nesse artigo aprendemos como o Grunt pode nos ajudar a automatizar tarefas repetitivas em nossos projetos e vimos como ele realmente é prático. Contudo, existe uma outra ferramenta que tem esse mesmo objetivo e que se tornou até mesmo mais conhecida que o Grunt: estamos falando do Gulp. Nós iremos falar mais sobre esse automatizador no próximo artigo: Gulp: Automatizando tarefas sem Dificuldade, que já sai amanhã, te espero lá! :)

Hcode: Utilizamos cookies para a personalização de anúncios e experiências de navegação dentro de nosso site. Ao continuar navegando, você concorda com as nossas Política de Privacidade.