Se você é como eu e tem necessidade de testar toda e qualquer nova tecnologia, entende “beta” como “estável o suficiente para mim”, usa mais plugins do que programas propriamente ditos e estava assistindo à Build desse ano, é muito provável que você tenha decidido testar o Visual Studio 2015RC e todas as novas fantásticas features do .NET 5, é muito provável também que você tenha ficado feliz em ver que o WebEssentials já foi atualizado para o 2015RC, ai você instalou e… Pera ai, por que o Less não compila? Por que o Bundle não gera? Por que os scripts não minificam? A resposta é simples: Esses recursos foram removidos do WebEssentials!
Pode ser um choque nos primeiros 15 minutos, a praticidade do WebEssentials nesse ponto era realmente impressionante, mas depois que a gente se acalma começa a perceber que faz algum sentido.
A Microsoft vem investindo pesado no Azure e nos recursos de Build no server, inclusive recentemente lançou uma versão do Visual Studio para Mac e Linux baseada nesse recurso.
O WebEssentials trabalhava basicamente client-side no Visual Studio e só conseguia compilar o front-end quem tivesse ele na máquina, alem de terem que manter o compilador de less, sass, coffee etc, sendo que já existem outras ferramentas para isso.
Ta, legal, mas e ai? Como eu fico?
Existem algumas ferramentas para substituir essas funções e o WebEssentials agora te ajuda a configura-las.
Minha escolha pessoal foi o Grunt, mas muita gente prefere o Gulp, ambos são compiladores de script bastante poderosos e existe ainda no pacote o Bower, que é gerenciador de pacotes de script. Assim como o NuGet se encarrega de manter os pacotes de código do seu backend atualizados (como Entity Framework, Ajax Toolkit etc) o Bower é mais focado no seu front (Angular, Bootstrap, Jquery etc).
Eu sei que as libs que eu dei de exemplo existem no NuGet também, mas em geral, a recomendação é usar o Nuget para Assemblies .Net e o Bower para CSS e JS.
Tanto o Bower quanto o Grunt são baseados em Node.js, então antes de utiliza-los você vai precisar instalar o Node.js.
Assumo que você já instalou o WebEssentials.
(você provavelmente vai precisar de um reboot para o comando npm ser reconhecido).
Clique no projeto com o botão direito, e em Add haverá uma nova opção: “Grunt and Bower to Project”.
Serão criados três arquivos com uma estruta JSON, o bower.json, gruntfile.js e package.json. No momento não nos preocuparemos com o Bower, queremos apenas substituir o WE.
No arquivo Package você precisa adicionar os pacotes com as extensões que usará dentro de “devDependencies”. O intelisense funciona para os pacotes e ajuda bastante, alguns podem não ter um nome muito obvio, nada que um rápido google não resolva, mas segue o meu exemplo com o que estou usando nesse momento enquanto configuro um projeto que estou migrando para o 2015RC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "name": "package", "version": "1.0.0", "private": true, "devDependencies": { "grunt": "0.4.5", // grunt "grunt-contrib-less": "^1.0.1", // Compilador Less "grunt-contrib-cssmin": "^0.12.3", // Minificador CSS "grunt-contrib-copy": "^0.8.0", //Copia de arquivo "grunt-contrib-clean": "^0.6.0", //Exclusão de arquivos "grunt-contrib-uglify": "^0.9.1", // Minificação de JS "grunt-contrib-watch": "^0.6.1" // monitora arquivos para build automático } } |
(se copiar remova os comentários, o npm não aceita comentários)
Criado o arquivo de configuração de pacotes agora é preciso instala-los, abra um prompt e navegue até a pasta do seu projeto, com o comando npm install (nome do pacote) é possivel instalar cada pacote.
1 2 3 4 5 6 7 |
npm install grunt npm install grunt-contrib-less npm install grunt-contrib-cssmin npm install grunt-contrib-copy npm install grunt-contrib-clean npm install grunt-contrib-uglify npm install grunt-contrib-watch |
Pronto para uso
Com as libs instaladas agora é “só” configurar o Build.
O arquivo gruntfile.js é criado com a configuração do bower, que eu vou apagar do meu exemplo por não estar utilizando, você vai entender a estrutura de cada job utilizando as libs, mas em geral os jobs tem mais ou menos esse formato:
1 2 3 4 5 6 7 8 9 |
modulo: { nomedojob:{ opcoes: { ... }, arquivos:{ 'destino':'origem' } } } |
a grande vantagem de escrever esse arquivo de configuração, na minha opnião, é poder ter mais de um perfil de Build, é possível ter um perfil de produção e outro de testes no qual não se minifica os arquivos para facilitar o debug.
segue, para fins ilustrativos o arquivo de configuração que eu estou usando agora, pode não ter a sintaxe ideal ou talvez de pra otimizar, mas está quebrando o meu galho.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
module.exports = function (grunt) { grunt.initConfig({ less: { development: { files: { "App_Themes/Cadastro/css/cadastro.css": "App_Themes/Cadastro/less/Cadastro.less", "App_Themes/Evento/css/evento.css": "App_Themes/Evento/less/Evento.less", "App_Themes/Home/css/home.css": "App_Themes/Home/less/Home.less", "App_Themes/Principal/css/componentes.css": "App_Themes/Principal/less/Componentes.less", "App_Themes/Principal/css/goinout.css": "App_Themes/Principal/less/Goinout.less", "App_Themes/TermosDeUso/css/style.css": "App_Themes/TermosDeUso/less/style.less", } } }, copy: { main: { files: [ { expand: true, flatten: true, dest: "App_Themes/Cadastro/css/", src: ["App_Themes/Cadastro/unprocessed/*"], filter: 'isFile' }, { expand: true, flatten: true, dest: "App_Themes/Evento/css/", src: ["App_Themes/Evento/unprocessed/*"], filter: 'isFile' }, { expand: true, flatten: true, dest: "App_Themes/Home/css/", src: ["App_Themes/Home/unprocessed/*"], filter: 'isFile' }, { expand: true, flatten: true, dest: "App_Themes/Principal/css/", src: ["App_Themes/Principal/unprocessed/*"], filter: 'isFile' }, { expand: true, flatten: true, dest: "App_Themes/TermosDeUso/css/", src: ["App_Themes/TermosDeUso/unprocessed/*"], filter: 'isFile' } ] } }, cssmin: { target: { files: { 'App_Themes/Cadastro/css/cadastro.min.css': ['App_Themes/Cadastro/css/*.css'], 'App_Themes/Evento/css/evento.min.css': ['App_Themes/Evento/css/*.css'], 'App_Themes/Home/css/home.min.css': ['App_Themes/Home/css/*.css'], 'App_Themes/Principal/css/goinout.min.css': ['App_Themes/Principal/css/*.css'], 'App_Themes/TermosDeUso/css/style.min.css': ['App_Themes/TermosDeUso/css/*.css'] } } }, clean: { pre: { src: ["App_Themes/**/css/*", "Script/minified/*"] }, pos: { src: ["App_Themes/**/css/*", "!App_Themes/**/css/*.min.css"] } }, uglify: { all: { files: { 'Scripts/minified/facebook.js': ['Scripts/Facebook.js'], 'Scripts/minified/home.js': ['Scripts/Home.js'], 'Scripts/minified/mapstyle.js': ['Scripts/mapstyle.js'], 'Scripts/minified/eventolink.js': ['Scripts/EventoLink.js'], 'Scripts/minified/addfriends.js': ['Scripts/AddFriends.js'], } } }, watch: { files: ["App_Themes/**/less/*.less", "Scripts/*.js"], tasks: ["default"] } }); grunt.loadNpmTasks("grunt-contrib-less"); grunt.loadNpmTasks("grunt-contrib-copy"); grunt.loadNpmTasks("grunt-contrib-clean"); grunt.loadNpmTasks("grunt-contrib-cssmin"); grunt.loadNpmTasks("grunt-contrib-watch"); grunt.loadNpmTasks("grunt-contrib-uglify"); grunt.registerTask("default", ["clean:pre","less","copy","cssmin","uglify","clean:pos"]); }; |
Repare que eu descrevo as tasks, importo as libs com o grunt.loadNpmTasks(”); e depois defino um alias “default” para uma sequencia de tasks.
Repare também que eu tenho dois jobs de clean no alias default, e escolho qual usar com “:”.
Ao abrir o Task Runner Explorer podemos ver e executar as tasks:
Ta brincando né? como eu automatizo isso?
Há duas formas de automatização aqui, a mais simples está no próprio Task Runner, clicando com o botão direito você vai perceber que existe a opção de criar Binds para a abertura do projeto, pré compilação, pós compilação e clean:
A outra está na task Watch, que monitora a edição de arquivos, atribuindo um Bind de “Project Open” ela será executada ao abrir o projeto e ficará monitorando a lista de arquivos que você especificou para cada tasks, o que gera um comportamento muito parecido com o que o WebEssentials fazia, você pode especificar que os arquivos .less acionarão o compilador de less ou os .js o minificador de JavaScript, eu prefiro executar todo o processo sempre que algo muda:
1 2 3 4 |
watch: { files: ["App_Themes/**/less/*.less", "Scripts/*.js"], tasks: ["default"] } |
TL;DR;
O compilador do WebEssentials foi aposentado em favor de ferramentas melhores mais flexíveis, depois de configurado uma vez, o comportamento do projeto é bem parecido, mas em todo início de projeto você vai perder uns minutos configurando o processo de compilação do seu front-end, não é tão prático quanto era mas é o padrão que está sendo adotado pelo Visual Studio.