Slide 1

Slide 1 text

JAVASCRIPT NO TERMINAL fernahh.com.br $_

Slide 2

Slide 2 text

@fernahh fernahh.com.br

Slide 3

Slide 3 text

contaazul.com

Slide 4

Slide 4 text

Os dois porques 1. Por que CLI?

Slide 5

Slide 5 text

Agilidade Tarefas podem ser executadas de forma mais ágil, sem contar que é mais rápido do que abrir um programa.

Slide 6

Slide 6 text

Versatilidade Programas que podem ser usados por humanos ou robôs.

Slide 7

Slide 7 text

Os dois porques 2. Por que JavaScript?

Slide 8

Slide 8 text

WTF?!!!

Slide 9

Slide 9 text

O principal responsável pela universalização do JavaScript. Nos dá módulos e ferramentas que facilitam o desenvolvimento de programas maduros.

Slide 10

Slide 10 text

NPM

Slide 11

Slide 11 text

NPM ● + 250 mil pacotes

Slide 12

Slide 12 text

NPM ● + 250 mil pacotes ● Facilita a instalação

Slide 13

Slide 13 text

NPM ● + 250 mil pacotes ● Facilita a instalação ● Gerenciamento de versão

Slide 14

Slide 14 text

JAVASCRIPT É FODA! Está em todos os lugares e, segundo o Stack Overflow, é a linguagem mais popular do mundo.

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Ken Thompson

Slide 17

Slide 17 text

Ken Thompson ● Criador da codificação UTF-8

Slide 18

Slide 18 text

Ken Thompson ● Criador da codificação UTF-8 ● Co-criador do Go

Slide 19

Slide 19 text

Ken Thompson ● Criador da codificação UTF-8 ● Co-criador do Go ● Primeiro a criar a Filosofia Unix

Slide 20

Slide 20 text

Filosofia Unix Conjunto de normas e abordagens para o desenvolvimento de software.

Slide 21

Slide 21 text

Regra da Composição Projete programas que podem ser usados com outros programas.

Slide 22

Slide 22 text

$ ls

Slide 23

Slide 23 text

$ ls auto Documents Personal Code Downloads Pictures Desktop Music Public

Slide 24

Slide 24 text

$ ls | grep Do

Slide 25

Slide 25 text

$ ls | grep Do Documents Downloads

Slide 26

Slide 26 text

process Objeto global do Node.js que possui atributos e funções que podem ser usados para ouvir, ler ou escrever algo.

Slide 27

Slide 27 text

process.argv Retorna um array de argumentos, os quais retornam as entradas do usuário.

Slide 28

Slide 28 text

#!/usr/bin/env node console.log(process.argv)

Slide 29

Slide 29 text

#!/usr/bin/env node console.log(process.argv)

Slide 30

Slide 30 text

#!/usr/bin/env node console.log(process.argv)

Slide 31

Slide 31 text

$ node ./index.js Foo Bar

Slide 32

Slide 32 text

$ node ./index.js Foo Bar [ '/home//bin/node', '/home//index.js', 'Foo', 'Bar' ]

Slide 33

Slide 33 text

#!/usr/bin/env node const argv = require('minimist')(process.argv.slice(2)) console.log(argv)

Slide 34

Slide 34 text

#!/usr/bin/env node const argv = require('minimist')(process.argv.slice(2)) console.log(argv)

Slide 35

Slide 35 text

#!/usr/bin/env node const argv = require('minimist')(process.argv.slice(2)) console.log(argv)

Slide 36

Slide 36 text

$ node ./index.js Foo Bar --verbose

Slide 37

Slide 37 text

$ node ./index.js Foo Bar --verbose { _: [ 'Foo', 'Bar' ], verbose: true }

Slide 38

Slide 38 text

process.stdin, process.stdout Devem ser usados, respectivamente, para ler e escrever. Dessa forma o usuário poderá usar outros programas ao mesmo tempo.

Slide 39

Slide 39 text

#!/usr/bin/env node const logger = str => process.argv.write(`${str} \n`) logger('RSJS')

Slide 40

Slide 40 text

#!/usr/bin/env node const logger = str => process.argv.write(`${str} \n`) logger('RSJS')

Slide 41

Slide 41 text

#!/usr/bin/env node const logger = str => process.argv.write(`${str} \n`) logger('RSJS')

Slide 42

Slide 42 text

$ node ./index.js

Slide 43

Slide 43 text

$ node ./index.js RSJS

Slide 44

Slide 44 text

process.exit() Serve para sinalizar se a execução deu certo ou errado.

Slide 45

Slide 45 text

#!/usr/bin/env node if (error) process.exit(1)

Slide 46

Slide 46 text

#!/usr/bin/env node if (error) process.exit(1)

Slide 47

Slide 47 text

Regra da Simplicidade Projete para a simplicidade; adicione complexidade somente onde é necessário.

Slide 48

Slide 48 text

package.json É muito mais que um manifesto da sua aplicação. Pode ser usado para automatizar tarefas e facilitar a instalação de seu programa.

Slide 49

Slide 49 text

{ "name": "RSJS", "version": "0.1.0", "description": "A Simple CLI Example", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "bin": { "rsjs": "./index.js" }, "dependecies": { "minimist": "^1.2.0" } }

Slide 50

Slide 50 text

{ "name": "RSJS", "version": "0.1.0", "description": "A Simple CLI Example", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "bin": { "rsjs": "./index.js" }, "dependecies": { "minimist": "^1.2.0" } }

Slide 51

Slide 51 text

$ npm test

Slide 52

Slide 52 text

$ npm test Error: no test specified npm ERR! Test failed. See above for more details.

Slide 53

Slide 53 text

{ "name": "RSJS", "version": "0.1.0", "description": "A Simple CLI Example", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "bin": { "rsjs": "./index.js" }, "dependecies": { "minimist": "^1.2.0" } }

Slide 54

Slide 54 text

$ npm install -g

Slide 55

Slide 55 text

$ npm install -g $ rsjs

Slide 56

Slide 56 text

$ npm install -g $ rsjs RSJS

Slide 57

Slide 57 text

$ which rsjs

Slide 58

Slide 58 text

$ which rsjs /home//bin/rsjs

Slide 59

Slide 59 text

{ "name": "RSJS", "version": "0.1.0", "description": "A Simple CLI Example", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "bin": { "rsjs": "./index.js", "foo": "./foobar.js" }, "dependecies": { "minimist": "^1.2.0" } }

Slide 60

Slide 60 text

Evite decorações Headers e decorações sempre devem ser evitados, pois eles dificultam o parse de seu output.

Slide 61

Slide 61 text

Regra da Modularidade Escreva partes simples, conectadas por interfaces simples.

Slide 62

Slide 62 text

Convenção CommonJS Segundo a convenção CommonJS, executáveis devem ficar em um diretório bin.

Slide 63

Slide 63 text

├── package.json ├── index.js └── bin └── rsjs #!/usr/bin/env node 'use strict' const logger = require('../index.js') const argvs = require('minimist')(process.argv.slice(2)) logger(argvs._)

Slide 64

Slide 64 text

├── package.json ├── index.js └── bin └── rsjs #!/usr/bin/env node 'use strict' const logger = require('../index.js') const argvs = require('minimist')(process.argv.slice(2)) logger(argvs._)

Slide 65

Slide 65 text

├── package.json ├── index.js └── bin └── rsjs #!/usr/bin/env node 'use strict' const logger = require('../index.js') const argvs = require('minimist')(process.argv.slice(2)) logger(argvs._)

Slide 66

Slide 66 text

├── package.json ├── index.js └── bin └── rsjs 'use strict' const logger = str => process.stdout.write(`${str} \n`) module.exports = logger

Slide 67

Slide 67 text

CLI.js e API privada Uma boa prática é isolar a lógica de negócio em uma “API” privada.

Slide 68

Slide 68 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = str => process.stdout.write(`${str} \n`) module.exports = logger

Slide 69

Slide 69 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = str => process.stdout.write(`${str} \n`) module.exports = logger

Slide 70

Slide 70 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const run = argvs => logger(argvs) exports.run = argvs => run(argvs)

Slide 71

Slide 71 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const run = argvs => logger(argvs) exports.run = argvs => run(argvs)

Slide 72

Slide 72 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const run = argvs => logger(argvs) exports.run = argvs => run(argvs)

Slide 73

Slide 73 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const run = argvs => logger(argvs) exports.run = argvs => run(argvs)

Slide 74

Slide 74 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js #!/usr/bin/env node 'use strict' const cli = require('../cli.js') const argvs = require('minimist')(process.argv.slice(2)) cli.run(argvs._)

Slide 75

Slide 75 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js #!/usr/bin/env node 'use strict' const cli = require('../cli.js') const argvs = require('minimist')(process.argv.slice(2)) cli.run(argvs._)

Slide 76

Slide 76 text

├── cli.js ├── package.json ├── bin ├── rsjs └── src └── logger.js #!/usr/bin/env node 'use strict' const cli = require('../cli.js') const argvs = require('minimist')(process.argv.slice(2)) cli.run(argvs._)

Slide 77

Slide 77 text

Regra do Reparo Quando é inevitável falhar, falhe ruidosamente e o mais cedo possível.

Slide 78

Slide 78 text

Testes automatizados Com testes, temos a garantia que erros sejam capturados antes de chegar aos usuários de um programa.

Slide 79

Slide 79 text

Mito sobre testes “A melhor forma de garantir uma funcionalidade é testar comandos no shell.”

Slide 80

Slide 80 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── cli.test.js const rsjs = require('../bin/rsjs') const test = require('ava') test('should return the entry as log', t => { const result = shell.exec('rsjs 2016', { silent: true }) t.is(result.output, '2016') })

Slide 81

Slide 81 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── cli.test.js const rsjs = require('../bin/rsjs') const test = require('ava') test('should return the entry as log', t => { const result = shell.exec('rsjs 2016', { silent: true }) t.is(result.output, '2016') })

Slide 82

Slide 82 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── cli.test.js const rsjs = require('../bin/rsjs') const test = require('ava') test('should return the entry as log', t => { const result = shell.exec('rsjs 2016', { silent: true }) t.is(result.output, '2016') })

Slide 83

Slide 83 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── cli.test.js const rsjs = require('../bin/rsjs') const test = require('ava') test('should return the entry as log', t => { const result = shell.exec('rsjs 2016', { silent: true }) t.is(result.output, '2016') })

Slide 84

Slide 84 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── logger.test.js const rsjs = require('../bin/rsjs') const test = require('ava') test('should return the entry as log', t => { const result = shell.exec('rsjs 2016', { silent: true }) t.is(result.output, '2016') })

Slide 85

Slide 85 text

Command shell anti-pattern Escrever testes automatizados que dependam de comandos shell para comparar resultados é lento e difícil de manter.

Slide 86

Slide 86 text

Teste o que é necessário Não faz sentido testarmos um executável, por exemplo, quando ele funciona apenas como um proxy.

Slide 87

Slide 87 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── logger.test.js const logger = require('../src/logger.js') const test = require('ava') let output = '' const entry = 'RSJS' const write = process.stdout.write test.beforeEach(t => process.stdout.write = str => output += str) test.afterEach(t => process.stdout.write = write) test('should return the entry as log', t => { logger(entry) t.is(output, entry) })

Slide 88

Slide 88 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── logger.test.js const logger = require('../src/logger.js') const test = require('ava') let output = '' const entry = 'RSJS' const write = process.stdout.write test.beforeEach(t => process.stdout.write = str => output += str) test.afterEach(t => process.stdout.write = write) test('should return the entry as log', t => { logger(entry) t.is(output, entry) })

Slide 89

Slide 89 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── logger.test.js const logger = require('../src/logger.js') const test = require('ava') let output = '' const entry = 'RSJS' const write = process.stdout.write test.beforeEach(t => process.stdout.write = str => output += str) test.afterEach(t => process.stdout.write = write) test('should return the entry as log', t => { logger(entry) t.is(output, entry) })

Slide 90

Slide 90 text

├── cli.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── logger.test.js const logger = require('../src/logger.js') const test = require('ava') let output = '' const entry = 'RSJS' const write = process.stdout.write test.beforeEach(t => process.stdout.write = str => output += str) test.afterEach(t => process.stdout.write = write) test('should return the entry as log', t => { logger(entry) t.is(output, entry) })

Slide 91

Slide 91 text

Regra da Clareza A clareza é melhor que a inteligência.

Slide 92

Slide 92 text

Good patterns Output === API

Slide 93

Slide 93 text

$ node rsjs

Slide 94

Slide 94 text

$ node rsjs RSJS2016 23 04 POA RSJS2015 16 05 POA RSJS2014 17 05 POA RSJS2013 23 03 POA

Slide 95

Slide 95 text

$ node rsjs | grep 05

Slide 96

Slide 96 text

$ node rsjs | grep 05 RSJS2015 16 05 POA RSJS2014 17 05 POA

Slide 97

Slide 97 text

Good patterns Deve funcionar sem o usuário

Slide 98

Slide 98 text

ex: git push --force

Slide 99

Slide 99 text

Good patterns Disponibilize flags padrões ao usuário

Slide 100

Slide 100 text

--no-color Existem terminais que não suportam cores. Seu output deve ser extremamente legível mesmo sem suporte.

Slide 101

Slide 101 text

$ git diff

Slide 102

Slide 102 text

$ git diff -test('test2', t => { - const result = shell.exec('rsjs 2016', { silent: true }) - - t.is(result.output, '2016') + t.not('foo', entry) })

Slide 103

Slide 103 text

$ git diff --no-color

Slide 104

Slide 104 text

$ git diff --no-color -test('test2', t => { - const result = shell.exec('rsjs 2016', { silent: true }) - - t.is(result.output, '2016') + t.not('foo', entry) })

Slide 105

Slide 105 text

--help O problema com artefatos de ajuda (e documentações), é que são facilmente esquecidos de serem atualizados.

Slide 106

Slide 106 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── cli.test.js 'use strict' const help = ` RSJS - O evento mais gaudério de JavaScript Usage $ rsjs Example $ RSJS '2016'` modules.exports = help

Slide 107

Slide 107 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs ├── src ├── logger.js └── test └── cli.test.js 'use strict' const help = ` RSJS - O evento mais gaudério de JavaScript Usage $ rsjs Example $ RSJS '2016'` modules.exports = help

Slide 108

Slide 108 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const help = require('./help.js') const run = argvs => { if (argvs.help) { process.stdout.write(help) return } logger(argvs._) } exports.run = argvs => run(argvs)

Slide 109

Slide 109 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const help = require('./help.js') const run = argvs => { if (argvs.help) { process.stdout.write(help) return } logger(argvs._) } exports.run = argvs => run(argvs)

Slide 110

Slide 110 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const help = require('./help.js') const run = argvs => { if (argvs.help) { process.stdout.write(help) return } logger(argvs._) } exports.run = argvs => run(argvs)

Slide 111

Slide 111 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs ├── doc ├── help.txt └── src └── logger.js RSJS - O evento mais gaudério de JavaScript Usage $ rsjs Example $ rsjs '2016'

Slide 112

Slide 112 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs ├── doc ├── help.txt └── src └── logger.js 'use strict' const help = require('fs').readFileSync('./doc/help.txt') exports.run = help

Slide 113

Slide 113 text

--version O output da versão de seu programa deve sempre espelhar o que está no package.json.

Slide 114

Slide 114 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const help = require('./help.js') const pkg = require('./package.json') const run = argvs => { if (argvs.help) { process.stdout.write(help) return } if (argvs.version) { process.stdout.write(`${pkg.name} v${pkg.version}`) return } logger(argvs._) } exports.run = argvs => run(argvs)

Slide 115

Slide 115 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs └── src └── logger.js 'use strict' const logger = require('./src/logger.js') const help = require('./help.js') const pkg = require('./package.json') const run = argvs => { if (argvs.help) { process.stdout.write(help) return } if (argvs.version) { process.stdout.write(`${pkg.name} v${pkg.version}`) return } logger(argvs._) } exports.run = argvs => run(argvs)

Slide 116

Slide 116 text

--verbose Por padrão, nossos programas devem ser silenciosos, porém, é importante dar mais informações ao usuário quando ele precisar.

Slide 117

Slide 117 text

Good patterns Ao invés de emitir logs, emita eventos

Slide 118

Slide 118 text

EventEmitter Classe do Node.js que é exposta pelo módulo events. Através dela que são implementados listeners e emitters de eventos.

Slide 119

Slide 119 text

Regra da Diversidade Desconfie de todas as alegações sobre a existência de um “modo correto” de fazer as coisas.

Slide 120

Slide 120 text

Seja independente de SO Nossos programas podem funcionar, sem muito esforço, tanto no universo Unix como Windows.

Slide 121

Slide 121 text

Good patterns Evite usar paths “hard- code”

Slide 122

Slide 122 text

path Módulo do Node.js que auxilia na manipulação de caminhos de arquivos e diretórios.

Slide 123

Slide 123 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs ├── doc ├── help.txt └── src └── logger.js 'use strict' const path = require('path').join const read = require('fs').readFileSync const help = read(path(__dirname, './doc/help.txt')) exports.run = help

Slide 124

Slide 124 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs ├── doc ├── help.txt └── src └── logger.js 'use strict' const path = require('path').join const read = require('fs').readFileSync const help = read(path(__dirname, './doc/help.txt')) exports.run = help

Slide 125

Slide 125 text

├── cli.js ├── help.js ├── package.json ├── bin ├── rsjs ├── doc ├── help.txt └── src └── logger.js 'use strict' const path = require('path').join const read = require('fs').readFileSync const help = read(path(__dirname, './doc/help.txt')) exports.run = help

Slide 126

Slide 126 text

Regra da Extensibilidade Projete para o futuro, pois ele chegará antes do que você imagina.

Slide 127

Slide 127 text

O futuro?

Slide 128

Slide 128 text

No content

Slide 129

Slide 129 text

obrigado! @fernahh fernahh.com.br