Vivendo sem o Grunt

No meu primeiro contato com o Grunt, ele não me convenceu. Qual era a necessidade de um task runner se eu já tinha o Makefile? O mesmo valia para o build de estáticos... Frameworks como o Django já possuíam um pipeline de concatenação e minificação, não sendo necessário que um processo externo interferisse em algo que (até então) funcionava muito bem.

Foi ao trabalhar com Single Page Applications que o Grunt me conquistou. Coisas que iam da otimização de imagens a deploy para ambientes passaram a ser responsabilidade da ferramenta, e a partir desse momento eu a carreguei para todo projeto que participei.

Mas o Grunt não é "bala de prata". Se para determinados problemas ele funciona muito bem, para outros ele representa um "peso" questionável na sua stack. Aumentando tempo de desenvolvimento (alguém aí já sofreu com o grunt-contrib-watch?), build e setup do seu ambiente.

É baseado nesse contexto que faço coro com alguns developers espalhados por aí: Talvez o seu projeto não precise do Grunt.

O que há de errado com o ele?

Nada!

Grunt, Gulp, Brocolli, Brunch, etc. são ferramentas super bacanas que cumprem com louvor o seu objetivo. Só que assim como o jQuery, para determinados casos elas podem ser "too much".

Assim como há uma frente defendendo o uso de microlibs ao invés de fat frameworks, há uma frente defendendo o uso do NPM como ferramenta de build. E por mais que possa parecer "mimimi", alguns argumentos fazem certo sentido. Como por exemplo, o do Keith Cirkel no "Why we should stop using Grunt & Gulp":

None of these build tools work without plugins. Just found an awesome new tool which will revolutionise your build process? Great! Now just wait for someone to write a wrapper for Grunt/Gulp/Brocolli, or write it yourself. Rather than just learning the command line for the tool you want to use, you now have to learn its programatic API, plus the API for your build tool.

Flamewars e "discursos de ódio" à parte, a pergunta que fica é: Será que é tão difícil assim montar um pipeline de build sem o uso de Grunt e Gulp?

"Nem todo mundo gosta de Javalis (br.ign.com)"
Nem todo mundo gosta de Javalis (br.ign.com)

Ter uma ferramenta a menos na stack do projeto pode tornar-ser um diferencial ao reduzir atrito e curva de aprendizado. Mas tirar o Grunt da jogada não significa necessariamente ter menos complexidade no seu projeto... Apenas significa fazer a mesma coisa com uma dependência a menos.

NPM como ferramenta de build

O Livereload é uma biblioteca escrita em Node que levanta um servidor que monitora alterações no seu projeto e promove um refresh no navegador web.

Podemos executá-lo através da linha de comando:

$ livereload meuprojeto/static/css

É possível usar o package.json como um "centralizador" de operações, assim como fazemos com o nosso Makefile:

$ npm run livereload

Para tanto, no arquivo package.json, precisamos adicionar a chave scripts com as instruções de execução para o comando livereload:

// package.json

...
"scripts": {
    "livereload": "livereload meuprojeto/static/css"
}
...

Talvez haja a necessidade da execução de um comando mais complexo. Para isso, podemos utilizar um script Node como ajuda:

// build/livereload.js

var livereload = require("livereload");

var PATH_CSS = "meuprojeto/build/static/css";
var PATH_JS = "meuprojeto/build/static/js";

var server = livereload.createServer();
server.watch([PATH_CSS, PATH_JS]);

E o nosso arquivo de configuração ficaria assim:

// package.json

...
"scripts": {
    "livereload": "node buid/livereload"
}
...

Vale lembrar que o npm já possui alguns comandos padrões, que não necessitam da instrução run. Por exemplo, temos a execução de testes através do comando npm test.

Um exemplo mais complexo

O exemplo acima é simples e questionável. Vamos partir de uma necessidade mais palpável e complexa: Quero compilar componentes escritos em React e ES6.

Para ilustrar, usaremos as bibliotecas React, Babelify e UglifyJS:

$ npm install react --save
$ npm install babelify babel-preset-react babel-preset-es2015 uglify-js --save-dev

Podemos criar dois comandos diferentes na nossa chave scripts, um para transpiling de Javascript e outro para minificação:

// package.json

"compile-js": "browserify meuprojeto/js/script.js -o meuprojeto/build/static/js/bundle.js -t [ babelify --presets [ es2015 react ] ]",

"minify-js": "uglifyjs meuprojeto/build/static/js/bundle.js -o meuprojeto/build/static/js/bundle.min.js"

Grande demais? É possível isolar esses comandos em scripts, como no exemplo do livereload:

// package.json

"compile-js": "node build/compile-js",
"minify-js": "node build/minify-js"

Para tornar mais simples a execução, vamos criar uma task genérica de build:

// package.json

"compile-js": "node build/compile-js",
"minify-js": "node build/minify-js",
"build": "npm run compile-js && npm run minify-js"

Com uma ajudinha da lib Watch, podemos incrementar ainda mais o nosso processo:

// package.json

...
"scripts": {
    "compile-js": "node build/compile-js",
    "minify-js": "node build/minify-js",
    "build": "npm run compile-js && npm run minify-js",
    "watch": "watch 'npm run build' meuprojeto/static/js"
}
...

Pronto! Agora temos os comandos npm run build e npm run watch que nos ajudarão na demanda de "transpilar" e minificar componentes escritos em React.

Não foi tããããão difícil assim... Certo? Bastou abrir a documentação de cada ferramenta e perder alguns minutinhos lendo.

"Mas você teve que escrever mais linhas que escreveria utilizando um plugin do Grunt."

Possivelmente. Bem como é possível que eu nunca mais vá mexer nessas linhas escritas, uma vez que o processo já esteja montado e operacional.

Considerações finais

O mérito dos exemplos acima não está necessariamente na utilização do npm como ferramenta de build, mas sim nos incríveis pacotes que a comunidade Javascript tem construído para agilizar a construção de aplicações.

Ferramentas como o Grunt, em seu tempo, fizeram uma revolução no que tange o desenvolvimento de aplicações web (e não há dúvida que ainda o fazem). Mas com o advento do webpack, Browserify e PostCSS, o seu uso passou de essencial para opcional.

Até a próxima.

Referências