Desenvolvimento

13 jan, 2016

NodeJS com TypeScript e Task Runners – Visual Studio Code

Publicidade

Introdução

Este artigo tem por objetivo abordar uma visão geral sobre automatização de tarefas para linguagem Typescript, com o uso do editor Visual Studio Code.

Muito se fala do desenvolvimento de aplicações utilizando o MEAN Stack (Mongodb, ExpressJS, AngularJS e NodeJS), que possuem como linguagem padrão o JavaScript. Isso nos permite trabalhar com uma linguagem unificada, tanto back, quanto front end. Falaremos hoje como tornar mais prático o desenvolvimento de aplicações TypeScript (definido nas próximas seções), trabalhando com tarefas, compiladores e toda configuração de ambiente.

Para isso, hoje trabalharemos com o VS Code, editor de texto da Microsoft, explorando suas ferramentas de automatização de tarefas, compilação e desenvolvimento.

TypeScript

“Any browser. Any host. Any OS. Open Source.” – typescriptlang.org

É uma linguagem open source criada pela Microsoft, que tem por objetivo criar aplicações JavaScript escaláveis, com uma sintaxe mais agradável e extensão de funcionalidades da linguagem. Basicamente o código escrito em TypeScript é transpilado (processo onde uma linguagem é transformada em outra em um nível similar de abstração –  Steve Fenton) para a linguagem JavaScript.

De acordo com o livro Pro TypeScript Application-Scale Javascript Development (Fenton, Steve), ela é um superset de features para o JS, onde resolve diversos problemas recorrentes na linguagem, como herança de prototipagem, gerenciamento de módulos, gerenciamento de escopos e, claro, tipagem estática. Um dos benefícios da utilização, sem dúvida, é a utilização de orientação a objetos, encapsulamento e, uma das que mais gosto, a conversão de boa parte das features para versões anteriores do ECMAScript (especificação internacional que o JavaScript segue), podendo escrever, por exemplo, uma aplicação TypeScript seguindo a especificação ECMAScript 6 e definir que o código gerado será em ECMAScript 3 Standard.

Preparando o ambiente

Para a execução dos exemplos abaixo, é necessário o download dos seguintes itens:

  1. NodeJS  – faça o download e instale-o.
  2. Git / GitBash (para usuários Windows) – Instale e execute o Git Bash para os exemplos das próximas seções. Caso tenha outro terminal que execute comandos Linux, este item é opcional.

Basicamente, criaremos uma aplicação explorando o poder dos task runners com NodeJS. Vamos ao terminal (ou git bash para usuários Windows) e à instalação global do TSD (TypeScript Definition Manager) e TypeScript Compiler nos seguintes comandos:

npm install –g tsd
npm install –g typescript

Quando trabalhamos com TypeScript, além dos tradicionais node_modules (módulos do NodeJs) escritos em arquivos javascript, precisamos dos módulos específicos escritos em TypeScript – encontramos em DefinitelyTyped Oficial Repository diversos módulos prontos para utilização em nossa aplicação.  Repare que, ao fazer a instalação do TSD, ele automaticamente adicionou diversos módulos adicionais, que são as dependências que a aplicação precisa para ser executada. Para fins de teste, execute o comando seguinte para testar se está tudo correto até agora:

node –-version
tsd –-version
tsc –-version

Imagem 1 – Versões de módulos da aplicação.

Feito isso, estamos com o ambiente pronto para o desenvolvimento de nossa aplicação.

Estrutura do projeto

Em sua pasta de preferência, vamos criar uma aplicação NodeJS utilizando os comandos a seguir:

npm init

Em seguida, devemos informar o nome da aplicação, versão, descrição e entry point (esse entry point será o arquivo que fará o “start” da aplicação, podemos deixar assim por enquanto, alteraremos esse arquivo mais tarde) etc. O único item obrigatório nesse ponto será o nome da aplicação, vamos definir como “typescript-app-hello” (lembrando que todos os caracteres precisam ser em letras minúsculas). Com isso, geramos o arquivo de configuração de pacotes da nossa aplicação; esse arquivo informa todas as informações da nossa aplicação, e dependências que ela terá.

Precisaremos de um arquivo de configuração que informará ao compilador TypeScript as definições necessárias para executar a aplicação a partir do editor (VSCode). Criaremos também uma pasta nomeada src, onde ficarão nossos arquivos TypeScript, inserindo em seguida nosso arquivo index.ts.

.
├── package.json
├── src
│   └── index.ts
└── tsconfig.json

Listagem 1 – Estrutura de pastas e arquivos iniciais

Primeiros passos com TypeScript

Voltando ao terminal, vamos criar um arquivo de configuração do TSD, onde será informado quais arquivos .ts serão utilizados como módulos de nossa aplicação. Vamos ao seguinte comando:

tsd init

Imagem 2 – Inicialização do TypeScript Definition na aplicação.

No Visual Studio Code, clique em File > Open e selecione a pasta de seu projeto. O arquivo gerado em typings/tsd.d.ts serve de armazenamento de todas as referências para os módulos de definição TypeScript adicionados daqui para frente.

De volta ao terminal, vamos instalar o NodeJs em definição TypeScript. É importante estar na raiz do projeto para que as definições não sejam instaladas em lugares indesejados.

tsd query node –-action install –-save

Imagem 3 – Instalação do módulo NodeJS para TypeScript

Analisando novamente nosso arquivo typings/tsd.d.ts, automaticamente a referência para o node.d.ts foi adicionado e incluído no projeto, isso por conta do comando –-save.

Imagem 4 – Arquivo atualizado com a referência do arquivo de configuração no VSCode

Adicionaremos em tsconfig.json a configuração necessária para o compilador. Para esse primeiro momento, precisamos das seguintes configurações em compilerOptions:

  • Target: Especificação ECMAScript que o código gerado irá seguir.
  • Module: Runtime do NodeJS, podendo ser AMD ou CommonJS.
  • OutDir: Diretório de saída dos arquivos compilados.
  • Watch: Esta opção habilita a recompilação de nossos arquivos de forma automática; a cada alteração em arquivos TypeScript, os arquivos JavaScript serão gerados com as novas alterações. O arquivo de configurações JSON de tsconfig deve seguir o seguinte código:
{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "outDir": "lib/",
        "watch": true
    }
}

Listagem 2 – Arquivo de configuração tsconfig.json

Esse arquivo será visto e utilizado pela configuração do Visual Studio Code. Crie dentro de src, uma pasta chamada app e insira um arquivo nomeado Hello.ts. A estrutura ficará da seguinte forma:

 .
├── package.json
├── src
│   ├── app
│   │   └── Hello.ts
│   └── index.ts
├── tsconfig.json
├── tsd.json
└── typings
    ├── node
    │   └── node.d.ts
    └── tsd.d.ts

Listagem 3 – Estrutura de pastas e arquivos

No arquivo Hello.ts, adicionaremos o seguinte código:

//aqui definimos que será uma classe TypeScript
class Hello {
    
    //criamos um método que recebe dois parâmetros:
    //-> name, um nome para nosso aplicativo
    hello (name:string): string {
        var mensagem: string = ("Hello World !!!!!  Nome: %s", name);
        return  mensagem;
    }
}
//definimos que nossa classe não pode ser herdada
//similar ao sealed do C#
//utilizamos assim, apenas por boa prática
Object.seal(Hello);
//tornamos nossa classe visível a
//todos da aplicação
export = Hello;

Listagem 4 – Classe Hello.ts

Observe a tipagem estática de nosso projeto. Diferentemente de arquivos JavaScript, nosso arquivo não permitirá alguns dos recorrentes “bugs do milênio” que ocorrem. Exemplificando: o JS permite somas entre strings e números, isso geralmente retorna resultados muitas vezes imprevisíveis, seja concatenando strings ou somando valores. No exemplo, definimos que nosso parâmetro será obrigatoriamente uma string, prevendo que caso tentarmos inserir um valor do tipo number, ocorrerá um erro de compilação. Isso nos permite um maior controle sobre os resultados, mitigando os possíveis imprevistos.

Imagem 5 – Erro de compilação entre strings e números.

Em index.ts, importaremos a referência de nossa classe, para assim utilizá-la e então chamar nosso método, seguindo o código:

//para conseguir trabalhar com typescript,
//a classe principal precisa do módulo nodeJS
//onde a referencia encontra-se na pasta typings
//adicionamos a referencia ao nosso projeto
///< reference path="typings/tsd.d.ts"
//importamos a classe Hello
import Hello = require('./app/Hello');
var helloClass = new Hello();
//criamos uma variavel do tipo string, em seguida passamos
//o retorno do valor de nosso método
var helloMensagem: string = helloClass.hello('app hello world - Erick Wendel');
//exibimos o retorno na tela.
console.log(helloMensagem);

Listagem 4 – Classe App.ts

Em seguida, vamos compilar nossa aplicação via terminal. Com os seguintes comandos:

tsc src/*.ts --module commonjs --target es5 --outDir lib

Com esse comando, informamos ao compilador TypeScript (TSC) onde estão nossos arquivos, qual módulo e especificação alvo (similar ao nosso tsconfig.json) e o diretório de saída dos novos arquivos; caso ele não exista, o próprio TSC irá criar para nós, não ativaremos nosso watcher neste momento. Para fins de conhecimento, caso decidamos ativá-lo, devemos ao final do comando adicionar o parâmetro “–watch”.

Com a execução desse comando, em seguida, executar também o comando para rodar nossa classe compilada.

node lib/index.js

Imagem 6 – compilando e executando a aplicação.

Visual Studio Code – Task Runners

O Visual Studio Code, em sua atual versão 0.10.6, possui recursos para automatizar os processos de compilação de nosso TypeScript e tarefas em geral, por padrão definidas em um arquivo de configuração do editor.

Pressionando as teclas Command + Shift + P (OS X) ou Ctrl + Shift + P (Windows), digite “> task”.

Imagem 7 – Pesquisando configurações de Task Runners.

Escolhendo a opção “Configure Task Runner”, o próprio Visual Studio Code nos devolve algumas configurações pré-estabelecidas para compilar todo nosso projeto TypeScript. Precisamos apenas remover os argumentos dessa configuração inicial, em “args”, definindo um array vazio, para utilizar as configurações do nosso tsconfig.json.

Imagem 8 – Arquivo de configurações de tarefas e remoção de argumentos.

Em uma breve explicação, o VSCode facilita a compilação de nossos arquivos, mas “por baixo dos panos” ele utiliza o mesmo comando processo utilizado via terminal anteriormente, TSC. O benefício de configurar no editor é que não precisamos voltar a todo momento ao terminal e executar os comandos. Outro detalhe é que, em nosso arquivo tsconfig.json, definimos que ele será “observado”, com a configuração watch. Isso indica que a cada mudança no código TypeScript, ele automaticamente recompilará e fará toda atualização dos arquivos gerados na saída.

Pressionando Command + Shift + B (OS X) / Ctrl + Shift + b (Windows), o editor executará todas as tarefas definidas no arquivo. Para visualizar a saída dessas informações, pressione Command + Shift + U (OS X) ou Ctrl + Shift + U (Windows).

Imagem 9 – Output de Tasks do editor.

Ao atualizar um arquivo TypeScript qualquer e salvar o arquivo, voltando aos Outputs do VSCode, ele recompilará e mostrará a saída nos logs.

Imagem 10 – Watch de Arquivos.

Gulp Task Runner no VS Code

Nos exemplos anteriores, podemos ver o quão fácil e rápido é trabalhar com JavaScript, TypeScript e Task Runners rodando internamente do editor, agora utilizaremos um famoso Task Runner chamado Gulp. Instalaremos globalmente, com o comando -g, e localmente, adicionando a dependência no arquivo de pacotes. Precisaremos desses pacotes nos dois locais, pois utilizaremos de formas diferentes nos exemplos.

No terminal, execute os seguintes comandos:

npm install -g gulp 
npm install gulp –-save-dev
npm install gulp-typescript-compiler –-save-dev

Em seguida, criaremos um arquivo na raiz do projeto, nomeado “gulpfile.js”. Esse arquivo será responsável pelas tarefas daqui para frente, o editor apenas dará o start nesse arquivo. No arquivo gulpfile, teremos o seguinte código:

//importamos o modulo gulp
var gulp = require('gulp');
//importamos o modulo para compilacao de arquivos typescript
var tsc  = require('gulp-typescript-compiler');
//todo arquivo gulpfile deve conter uma tarefa
//nomeada default, esta tarefa será responsável
//por chamar as demais tarefas
gulp.task('default', ['compile']);
//criando a tarefa para compilar os
//arquivos typescript, a partir do
//modulo nodejs gulp typescript compiler
gulp.task('compile', function() {
  
  return gulp.
        //informamos que a pasta de nossos
        //arquivos typescript estarão em src
        //e esta função procurará por todos arquivos
        //desta extensão
        src('src/**/*.ts')
        
        //em seguida, executamos a funçao
        //do gulp typescript compiler,
        //passando informacoes similares
        //ao nosso tsconfig
        .pipe(tsc({
            module: 'commonjs',
            target: 'ES5',
            logErrors: true
        }))
        
        //incluimos a pasta para a saida de
        //arquivos transpilados
        .pipe(gulp.dest('lib'));
});

Listagem 5 – Tarefas de compilação do GulpJS.

A vantagem da compilação de arquivos a partir do gulp é que não precisamos mais de arquivos adicionais de configuração, como o tsconfig – ele é conhecido por seu poder em diversas outras tarefas, concatenação, ofuscação e até remoção de arquivos dinamicamente.

Adicionamos uma tarefa de Watch, para que, como no arquivo de configuração anterior, os arquivos sejam recompilados a cada mudança. Nosso arquivo gulpfile.js completo ficará da seguinte forma:

//importamos o modulo gulp
var gulp = require('gulp');
//importamos o modulo para compilacao de arquivos typescript
var tsc  = require('gulp-typescript-compiler');
//todo arquivo gulpfile deve conter uma tarefa
//nomeada default, esta tarefa será responsável
//por chamar as demais tarefas
gulp.task('default', ['compile', 'watch']);
//criando a tarefa para compilar os
//arquivos typescript, a partir do
//modulo nodejs gulp typescript compiler
gulp.task('compile', function() {
  
  return gulp.
        //informamos que a pasta de nossos
        //arquivos typescript estarão em src
        //e esta função procurará por todos arquivos
        //desta extensão
        src('src/**/*.ts')
        
        //em seguida, executamos a funçao
        //do gulp typescript compiler,
        //passando informacoes similares
        //ao nosso tsconfig
        .pipe(tsc({
            module: 'commonjs',
            target: 'ES5',
            logErrors: true
        }))
        
        //incluimos a pasta para a saida de
        //arquivos transpilados
        .pipe(gulp.dest('lib'));
});
//funçao responsável pela observacao de alteracoes em
//arquivos de nossa pasta src.
//a cada alteração, automaticamente, serão recompilados
gulp.task('watch', function () {
    return gulp.watch('src/**/*.*', ['compile']);
});

Listagem 6 – Código completo do GulpJS com Watcher e compiller.

Não se esqueça de adicionar a tarefa do watcher na tarefa default para ser executada. De volta ao terminal raiz do projeto, executaremos o comando “gulp”. Nesse momento, como não foi definido um nome especifico de tarefa, ele procurará uma tarefa default a ser executada. Caso queira executar uma tarefa especifica, digite o nome da tarefa a frente do comando. Ex.: “gulp compile”. No exemplo abaixo, veremos como nossas tarefas se comportam via terminal.

Imagem 11 – Gulp Task Runner via terminal.

No arquivo de configuração de tarefas do VSCode, vamos configurar nosso novo Task Runner, substituindo o código anterior. Precisamos nesse ponto definir qual será o comando a ser executado pelo editor e qual a tarefa que ele deve procurar.

{
   "version": "0.1.0",
   //alteramos o comando para gulp (aquele mesmo comando)
   //executado em terminal
   "command": "gulp",
   // The command is a shell script
   "isShellCommand": true,
 
   //adicionamos o array de tarefas a serem executadas
   //após o comando ser executado
 
   "tasks": [{
       "taskName": "default",
       "args": [],
       "isBuildCommand": true,
       "showOutput": "silent",
       "problemMatcher": ["$tsc"]
   }]
}

Listagem 7 – Nova configuração de tarefas do editor.

Após a execução das tarefas (Command + Shift + B / Ctrl + Shift + B), analisaremos a saída das mensagens do Output do VSCode.

Imagem 12 – Execução de tarefas Gulp via VSCode.

Conclusão

Vimos, ao longo deste artigo, uma parte de todo o poder que o JavaScript nos fornece, seja com editores, Task Runners, seja com linguagens intermediárias, ele simplesmente não deixa a desejar. O TypeScript é uma linguagem muito poderosa, e abordaremos mais a fundo num próximo artigo todo ciclo de vida de uma aplicação TypeScript, features etc.

Referências

Download dos Exemplos

TechNet Gallery