- 1. Prefácio
- 2. Resumo do projeto
- 3. Objetivos de aprendizagem
- 4. Considerações gerais
- 5. Critérios de aceitação mínimos do projeto
- 6. Entregáveis
- 7. Hacker edition
- 8. Guias, dicas e leituras complementares
- 9. Checklist
- 10. Dividindo o problema - babies steps
Markdown é uma linguagem de marcação
muito popular entre os programadores. É usada em muitas plataformas que
manipulam texto (GitHub, fórum, blogs e etc) e é muito comum encontrar arquivos
com este formato em qualquer repositório (começando pelo tradicional
README.md
).
Os arquivos Markdown
normalmente contém links que podem estar
quebrados, ou que já não são válidos, prejudicando muito o valor da
informação que está ali.
Uma comunidade open source nos propôs criar uma ferramenta, usando
Node.js, que leia e analise arquivos no formato
Markdown
, para verificar os arquivos que contenham links e mostrar algumas
estatísticas.
Neste projeto, será criado uma ferramenta de linha de comando (CLI) assim como a sua própria biblioteca (library) em Javascript.
Desta vez, vamos ficar um pouco longe do navegador para construir um programa
executado com Node.js. Iremos aprender sobre processos
(process.env
, process.argv
, ...),como interagir com sistemas de arquivos,
como fazer consultas de rede, etc.
Node.js é um ambiente de execução para JavaScript construído com o motor de JavaScript V8 do Chrome. Ele vai nos permitir executar o JavaScript no nosso sistema operacional, seja no seu computador ou em um servidor, o que nos abre portas para poder interagir com sistemas, arquivos, redes e etc.
Desenvolver sua própria biblioteca é uma experiência fundamental para qualquer desenvolvedora, pois te obriga a pensar na interface (API) dos seus módulos e como ela será usada por outras desenvolvedoras. Você deve levar em conta as peculiaridades da linguagem, convenções e boas práticas.
Reflita e depois enumere os objetivos que quer alcançar e aplique no seu projeto. Pense nisso para decidir sua estratégia de trabalho.
-
Diferenciar entre tipos de dados primitivos e não primitivos
-
Arrays (arranjos)
-
Objetos (key, value)
-
Uso de condicionais (if-else, switch, operador ternário, lógica booleana)
-
Funções (params, args, return)
-
Recursão
-
Módulos de CommonJS
-
Diferença entre expressões (expressions) e declarações (statements)
-
Callbacks
-
Promessas
-
Testes unitários (unit tests)
-
Testes assíncronos
-
Uso de mocks e espiões
-
Teste de compatibilidade em vários ambientes de execução
-
Uso de linter (ESLINT)
-
Uso de identificadores descritivos (Nomenclatura e Semântica)
-
Instalar e usar módulos com npm
-
Configuração do package.json
-
Configuração do npm-scripts
-
process (env, argv, stdin-stdout-stderr, exit-code)
-
File system (fs, path)
-
Git: Instalação e configuração
-
Git: Controle de versão com git (init, clone, add, commit, status, push, pull, remote)
-
Git: Integração de mudanças entre ramos (branch, checkout, fetch, merge, reset, rebase, tag)
-
GitHub: Criação de contas e repositórios, configuração de chave SSH
-
GitHub: Colaboração pelo Github (branches | forks | pull requests | code review | tags)
-
GitHub: Organização pelo Github (projects | issues | labels | milestones | releases)
-
Consulta ou solicitação (request) e resposta (response).
-
Códigos de status de HTTP
-
Este projeto deve ser feito individualmente.
-
O intervalo de tempo estimado para concluir o projeto é de 4 a 5 Sprints.
-
A biblioteca e script executável (ferramenta de linha de comando - CLI) devem ser implementados em JavaScript para serem executadas com Node.JS. É permitido usar bibliotecas externas.
-
O seu módulo deve ser instalável via
npm install <github-user>/md-links
. O módulo deve incluir um executável que pode ser chamado tanto por linha de comando quanto importado comrequire
para ser usado em seu código. -
Os testes unitários devem cobrir no mínimo 70% dos statements, functions, lines e branches. Recomendamos que explore o Jest para as suas provas unitárias.
-
Neste projeto não é permitido utilizar
async/await
. -
Para este projeto, sugerimos que você não use a versão síncrona da função de leitura de arquivo,
readFileSync
, e tente resolver esse desafio de forma assíncrona. -
Para este projeto é opcional o uso de ES modules
(import/export)
. Caso você decida utilizá-lo deverá criar um script debuild
nopackage.json
para que seja transformado emrequires
emodule.exports
com ajuda do Babel. -
Para diminuir a complexidade de seu algoritmo recursivo, recomendamos usar a versão síncrona da função de leitura do diretórios,
readdirSync
.
Para começar este projeto você deverá fazer um fork e clonar este repositório.
Antes de começar o código, é necessário criar um plano de ação. Ele deve estar
detalhado no README.md
do seu repositório e em uma série de issues e
milestones para priorizar e organizar o trabalho, e para fazer um
acompanhamento do seu progresso.
Dentro de cada milestone serão criados e atribuidos as issues que considerar necessários.
README.md
com descrição do módulo, instruções de instalação e uso, documentação da API e exemplos. Tudo que for relevante para qualquer desenvolvedora saber como utilizar a sua biblioteca sem inconvenientes.index.js
: este arquivo deve exportar a funçãomdLinks
.package.json
deve possuir o nome, versão, descrição, autor, licença, dependências e scripts (pretest, test e etc)..editorconfig
com a configuração para o editor de texto. Este arquivo não deve ser alterado..eslintrc
com a configuração para o linter. Este arquivo contém uma configuração básica para ESLint, se quiser colocar regras adicionais como Airbnb, você deverá modificar este arquivo..gitignore
para ignorar onode_modules
e outras pastas que não devem ser incluídas no controle de versão (git
).test/md-links.spec.js
deve conter os testes unitários para a funçãomdLinks()
. A sua implementação deve rodar estes testes.
O módulo deve poder ser importado em outros scripts Node.js e deve oferecer a seguinte interface:
path
: Rota absoluta ou relativa ao arquivo ou diretório. Se a rota passada é relativa, deve resolver como sendo relativa ao diretório onde foi chamada - current working directoryoptions
: Um objeto com a seguinte propriedade:validate
: Um booleano que determina se deseja validar os links encontrados.stats
: Booleano que determina se deseja obter um output com informações estatísticas gerais.
A função deve retornar uma promessa (Promise
) que
resolve um array (Array
) e
objetos(Object
), onde cada objeto representa um link, contendo as seguintes
propriedades:
Com validate:false
:
href
: URL encontrada.text
: Texto que irá aparecer dentro de um link (<a>
).file
: Rota do arquivo onde foi encontrado o link.
Com validate:true
:
href
: URL encontrada.text
: Texto que aparecía dentro del link (<a>
).file
: Ruta del archivo donde se encontró el link.status
: Código de resposta HTTP.ok
: Mensagemfail
em caso de falha ouok
em caso de sucesso.
const mdLinks = require("md-links");
mdLinks("./some/example.md")
.then(links => {
// => [{ href, text, file }, ...]
})
.catch(console.error);
mdLinks("./some/example.md", { validate: true })
.then(links => {
// => [{ href, text, file, status, ok }, ...]
})
.catch(console.error);
mdLinks("./some/dir")
.then(links => {
// => [{ href, text, file }, ...]
})
.catch(console.error);
O executável da nossa aplicação deve poder ser executado da seguinte maneira, através do terminal:
md-links <path-to-file> [options]
Por exemplo:
$ md-links ./some/example.md
./some/example.md http://algo.com/2/3/ Link de algo
./some/example.md https://outra-coisa-.net/algum-doc.html algum doc
./some/example.md http://google.com/ Google
O comportamento padrão não deve validar se as URLs respondem ok ou não, somente deve identificar o arquivo Markdown (a partir da rota que recebeu como argumento), analisar o arquivo Markdown e imprimir os links que vão sendo encontrados, junto com a rota do arquivo onde aparece e o texto encontrado dentro do link (truncado 50 caracteres).
Se passamos a opção --validate
, o módulo deve fazer uma requisição HTTP para
verificar se o link funciona ou não. Se o link resultar em um redirecionamento a
uma URL que responde ok, então consideraremos o link como ok.
Por exemplo:
$ md-links ./some/example.md --validate
./some/example.md http://algo.com/2/3/ ok 200 Link de algo
./some/example.md https://outra-coisa-.net/algum-doc.html fail 404 algum doc
./some/example.md http://google.com/ ok 301 Google
Vemos que o output neste caso inclui a palavra ok
e fail
depois da URL,
assim como o status da resposta recebida à requisição HTTP feita pela URL.
Se passamos a opção --stats
o output (saída) será um texto com estatísticas
básicas sobre os links.
$ md-links ./some/example.md --stats
Total: 3
Unique: 3
Também podemos combinar --stats
e --validate
para obter estatísticas que
necessitem dos resultados da validação.
$ md-links ./some/example.md --stats --validate
Total: 3
Unique: 3
Broken: 1
O módulo deve ser instalável via npm install <github-user>/md-links
. Este
módulo deve incluir um executável que pode ser chamado tanto por linha de
comando, como também possa ser importado com require
para usá-lo no seu código.
As seções chamadas Hacker Edition são opcionais. É para caso você tenha terminado todos os requisitos anteriores e ainda tenha tempo disponível, e pode assim aprofundar e/ou exercitar mais sobre os objetivos de aprendizagem deste projeto.
- Poder adicionar uma propriedade
line
a cada objetolink
indicando em que linha do arquivo está o link. - Poder agregar mais estatísticas.
- Integração contínua com Travis ou Circle CI.
Para que o módulo seja instalável pelo GitHub você tem que:
- Deixar o seu repo público
- Ter um
package.json
válido
Com o comando npm install <githubname>/<reponame>
podemos instalar diretamente
pelo GitHub. Ver docs oficiais dp npm install
aqui
Por exemplo, o
course-parser
que é
usado para o currículo não está publicado nos registros públicos do NPM, com
isso temos que instalar diretamente pelo GitHub com o commando npm install Laboratoria/course-parser
.
A implementação deste projeto tem várias partes: ler do sistema de arquivos, receber argumentos através da linha de comando, analisar um teste, fazer consultas HTTP, etc. Tudo isso pode ser feito de muitas formas, tanto com bibliotecas quanto com JS puro.
Por exemplo, o parse (análise) do Markdown para extrair os links poderia ser criado das seguintes maneiras (todas são válidas):
- Usando um módulo como markdown-it, que nos devolve um array de tokes que utilizamos para identificar os links.
- Seguindo outro caminho, poderíamos usar expressões regulares
(
RegExp
). - Também poderíamos usar uma combinação de vários módulos (poderia ser válido transformar o markdown em um HTML usando o marked e depois extrair os links com uma biblioteca de DOM como JSDOM o Cheerio).
- Usando um custom renderer de marked
(
new marked.Renderer()
).
Não hesite em consultar as suas companheiras e mentores se tiver dúvidas a respeito destas decisões. Não existe uma única maneira certa 😉
- Sobre Node.js - Documentação oficial
- Node.js file system - Documentação oficial
- Node.js http.get - Documentação oficial
- Node.js - Wikipedia
- What exactly is Node.js? - freeCodeCamp
- Node.js – O que é, como funciona e quais as vantagens
- O que é npm
- JavaScript assíncrono: callbacks, promises e async functions
- NPM
- Publicar package
- Criando um módulo Node.js
- Ler um arquivo
- Ler um diretório
- Path
- Criando sua CLI com Node.js
- Poder instalar via
npm install --global <github-user>/md-links
- Um board com o backlog das implementações da sua biblioteca
- Documentação técnica da sua biblioteca
- Guia de uso e instalação da biblioteca
- O módulo exporta uma função com a interface (API) esperada
- Implementa suporte para arquivo individual
- Implementa suporte para diretórios
- Implementa
options.validate
- Possuir o executável
md-links
no path (configurado nopackage.json
) - Executar sem erros e ter o resultado esperado
- Implementar
--validate
- Implementar
--stats
- Os testes unitários devem cobrir no mínimo 70% dos statements, functions, lines e branches.
- Rodar os testes e linter (
npm test
).
Uma das habilidades que esperamos que vocˆw possa desenvolver durante o bootcamp é o de definir "mini-projetos/babies steps" que a aproxime passo-a-passo da solução do "grande projeto". É o mesmo que começar fazendo as bordas de um quebra-cabeça sem necessariamente saber como se encaixará no final.
Estas são algumas sugestões:
Este projeto é diferente dos que você tem trabalhado até agora. Como não há uma interface web, tudo será desenvolvido em seu editor e consola/terminal.
Por isso, para visualizar melhor o que você terá que fazer
para planejar suas tarefas e objetivos, é aconselhável fazer um
fluxograma
.
Se você nunca fez um fluxograma, confira este recurso.
Uma alternativa ao fluxograma pode ser pseudocódigo
.
Neste projeto recomendamos o uso do Github Projects, ferramenta de planejamento e organização do GitHub
Por meio de issues e milestones pode-se organizar e planificar tarefas e objetivos concretos.
Levando em consideração os entregáveis do projeto,
9. Checklist e os passos que foram definidos em seu
fluxograma
, crie o seu planejamento em GitHub Projects.
Desta vez você estará trabalhando em NodeJS, certifique-se de saber para que serve e suas considerações.
Em particular, é preciso decidir antecipadamente se usará
ES Modules
, ou seja usar import/export, ou se utilizará
CommonJS Modules
, ou seja require/module.exports.
Certifique-se de ter esta decisão clara desde o início para que você não encontre problemas mais tarde.
Como primeiro desafio, você pode tentar ler um único arquivo com
um caminho fixo e imprimir seu conteúdo no console com um console.log
.
A biblioteca nativa FS
(FileSystem) será útil para você.
Lembrar: Para este projeto, sugerimos que você não use
a versão síncrona da função de leitura de arquivo,
readFileSync
, e tente resolver esse desafio de forma assíncrona.
Já sabendo ler um arquivo, aventure-se em saber qual é a sua extensão.
Lembre-se, as extensões são aquelas letras no final do nome de um arquivo, por exemplo: .js, .txt, .doc etc.
A biblioteca FS
também pode ser útil aqui.
Este projeto consiste em buscar arquivos, mas para isso, você deve primeiro ser capaz de vê-los.
Tenta imprimir para console a lista de arquivos em uma pasta.
A biblioteca FS
também será útil aqui.
Lembrar: Para diminuir a complexidade de seu algoritmo
recursivo, recomendamos usar a versão síncrona da função
de leitura do diretórios, readdirSync
.
Para acessar pastas e arquivos, será necessário indicar onde eles estão localizados em seu computador, sendo chamadas de rotas.
Use a biblioteca nativa path
para unir dois segmentos de caminho,
Por exemplo, se quisermos juntar:
- /home/Laboratório/
- ./teste
O resultado seria: /home/Lab/test
Este projeto pode ser resolvido com recursão.
Por que?
Porque não sabemos quantas pastas e arquivos teremos que passar antes de terminar.
Se você receber um caminho de pasta, não saberá com antecedência se há mais pastas dentro ou mais arquivos.
Portanto, certifique-se de entender o que o recursão e veja alguns exemplos.
O valor de retorno da nossa biblioteca é uma Promise
,
não um Array
.
Tente ler sobre promessas e criando uma por conta própria usando new Promise()
É importante que você saiba o que é um callback porque serão usadas nas promessas.