
Olá pessoal. Recentemente, deparei-me com a tarefa de configurar pacotes npm privados. Tudo parecia muito interessante e promissor, até que não havia muito o que fazer lá. Tudo teria terminado aqui, mas surgiu a segunda tarefa - escrever um repositório de demonstração para o pacote npm, que poderia ser usado, clonado e baseado nele rapidamente, criar algo útil e no mesmo estilo.
O resultado foi um projeto com formatação personalizada, estilo de código, testes para cada pool, limites de cobertura de código, relatório de cobertura de código e documentação automática. Além de publicação conveniente em npm. Detalhes sobre a configuração - sob o corte.
Exigências
Primeiro, descobri o que já temos:
- Novos projetos são escritos em TypeScript
- Além de novos projetos, há vários projetos em JavaScript puro
- Existem requisitos para escrever testes e os resultados devem ser enviados para análise
Então ele estimou sua lista de desejos - já que há tempo e desejo, por que não ficar com calma? O que eu quero mais:
- Eu quero um estilo de formatação uniforme
- Quero um estilo TypeScript unificado
- Quero documentação, mas não quero escrevê-la
- Em geral, eu quero automatizar tudo ao máximo. O que fyr-fyr-fyr e em produção
Como resultado, os requisitos tomaram forma no seguinte:
- O módulo deve ser TypeScript e testado usando TsLint
- O módulo deve ser usado em TypeScript e em JavaScript
- Os testes devem ser configurados no git hook, a cobertura mínima de código também deve ser configurada, as estatísticas devem ser
- A formatação deve ser configurada
- A documentação deve ser criada a partir do código.
- A publicação deve ser conveniente e consistente.
- Tudo isso pode ser automatizado
Parece ser bom, você precisa tentar.
Gestos preliminares
Criamos (clonamos) o repositório, inicializamos o package.json, configuramos o TypeScript localmente. Em geral, definimos todas as dependências localmente, porque tudo irá para o servidor. Não se esqueça de corrigir as dependências da versão .
git init npm init npm i -D typescript ./node_modules/.bin/tsc --init
Imediatamente no local, você precisa ajustar o tsconfig.json por conta própria - defina target, libs, include / exclude, outDir e outras opções.
Para manter a formatação uniforme, peguei duas ferramentas. O primeiro é o arquivo .editorconfig. Ele é entendido por todos os principais IDEs (WebStorm, VSCode, Visual Studio etc.), não requer instalação de nada supérfluo e funciona com um grande número de tipos de arquivos - js, ts, md e assim por diante.
#root = true [*] indent_style = space end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true max_line_length = 100 indent_size = 4 [*.md] trim_trailing_whitespace = false
Agora, o autoformato se comportará mais ou menos o mesmo com meus colegas.
A segunda ferramenta é mais bonita . Este é um pacote npm que verifica e, se possível, corrige automaticamente sua formatação de texto. Instale-o localmente e adicione o primeiro comando ao package.json
npm i -D prettier
package.json
"prettier": "prettier --config .prettierrc.json --write src*.ts"
O Prettier não possui um comando init, portanto você precisa configurá- lo manualmente. Crie .prettierrc.json na raiz do projeto com algo parecido com este conteúdo controverso (se houver, a postagem não é sobre quais aspas são melhores, mas você pode tentar)
.prettierrc.json
{ "tabWidth": 4, "useTabs": false, "semi": true, "singleQuote": true, "trailingComma": "es5", "arrowParens": "always" }
Agora crie a pasta src, crie index.ts com algum conteúdo condicional e tente executar mais bonita. Se ele não gostar da sua formatação, ele a corrigirá automaticamente. Muito confortável Se você não quiser se lembrar disso apenas durante uma confirmação / envio, poderá configurá-lo para executar automaticamente ou instalar uma extensão para o estúdio.
Estilo do código
Com o estilo do código, tudo também não é complicado. Para JavaScript, existe eslint ; para TypeScript, existe tslint . Colocamos tslint e criamos tsconfig.js para armazenar as configurações
npm i -D tslint ./node_modules/.bin/tslint --init
package.json
"lint": "tslint -c tslint.json 'src/**/*.ts' 'tests/**/*.spec.ts'"
Você pode escrever suas próprias regras ou pode usar regras existentes usando o parâmetro extend. Aqui , por exemplo, uma configuração do airbnb.
Desenvolvedores Tslint estão brincando module.exports = { extends: "./tslint-base.json", rules: { "no-excessive-commenting": [true, {maxComments: Math.random() * 10}] } };
Bem, isso não é bonito?
Há um ponto importante: tslint e mais bonitos se cruzam na funcionalidade (por exemplo, no comprimento de uma string ou vírgulas "penduradas"). Portanto, se você usar os dois, precisará monitorar a conformidade ou abandonar algo.
E, no entanto, para aqueles que desejam verificar nem todos os arquivos, mas apenas em etapas - existe um pacote em etapas
Testes
O que precisamos dos testes acima de tudo? Em primeiro lugar, para que iniciem automaticamente, em segundo lugar, controle de cobertura, em terceiro lugar algum relatório, de preferência no formato lcov (se houver, lcov é bem compreendido por diferentes analisadores - do SonarQube ao CodeCov). Lidaremos com a automação um pouco mais tarde, enquanto configuramos os próprios testes.
Colocamos karma , jasmim e todo o kit corporal correspondente
npm i -D karma karma-jasmine jasmine karma-typescript karma-chrome-launcher @types/jasmine ./node_modules/.bin/karma init
Modificamos um pouco o karma.conf.js e configuramos imediatamente o trabalho com cobertura
karma.conf.js karmaTypescriptConfig : { include: ["./src/**/*.ts", "./tests/**/*.spec.ts"], tsconfig: "./tsconfig.json", reports: { "html": "coverage", "lcovonly": { directory: './coverage', filename: '../lcov.dat' } }, coverageOptions: { threshold: { global: { statements: 60, branches: 60, functions: 60, lines: 60, excludes: [] }, file: { statements: 60, branches: 60, functions: 60, lines: 60, excludes: [], overrides: {} } } }, }
E, é claro, não esqueça de adicionar um novo comando ao package.json
package.json
"test": "karma start"
Se você estiver usando ou planejando usar o IC, é melhor colocar o HeadlessChrome :
npm i -D puppeteer
E pré-configure o Karma (correção do Chrome no ChromeHeadless) e mais alguma coisa. As edições podem ser visualizadas no repositório de demonstração .
Execute os testes, verifique se tudo funciona. Ao mesmo tempo, verifique o relatório de cobertura (está na pasta de cobertura) e remova-o do controle de versão, pois não é necessário no repositório.
Relatório:

Estilo de confirmação
As confirmações também podem ser unificadas (e, ao mesmo tempo, levar alguém a ficar incandescente, se você exagerar, é melhor sem fanatismo). Por isso, assumi o compromisso . Ele funciona na forma de um diálogo, suporta o formato de registro de alterações convencional (você pode criar um registro de alterações a partir de suas confirmações) e existe um plug- in VsCode para ele
npm i -D commitizen npm i -D cz-conventional-changelog
cz-convencional-changelog é um adaptador responsável por perguntas e, como resultado, pelo formato de suas confirmações
Adicione um novo comando à seção de scripts
"commit":"git-cz"
E uma nova seção package.json - config for commitizen
"config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" } }
A caixa de diálogo com o commitizen fica assim:

A documentação
Agora para a documentação. Teremos dois tipos de documentação - do código e do commit. Para documentação do código, usei typedoc (análogo ao esdoc, mas para TypeScript). É muito simples de configurar e trabalhar. O principal é não se esqueça de remover os resultados de seu trabalho do controle de versão.
npm i typedoc -D
e atualize o package.json
package.json
"doc": "typedoc --out docs/src/ --readme ./README.md"
O sinalizador --readme forçará o leia-me a ser incluído na página principal da documentação, o que é conveniente.
O segundo tipo de documentação é o changelog, e o pacote changelog-cli convencional nos ajudará com isso.
npm i -D conventional-changelog-cli
package.json
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
Do angular, há apenas formatação e nada mais. Agora, para atualizar o changelog, basta executar o npm run changelog. O principal é escrever cuidadosamente as confirmações. Bem, sempre escrevemos confirmações perfeitas, portanto isso não deve ser um problema.
Construir
Como nosso pacote também deve funcionar para JavaScript, precisamos transformar o TypeScript em JavaScript. Além disso, seria bom fazer um pacote compactado, apenas por precaução. Para isso, precisamos de uglifyjs e um pequeno pacote de ajustes.json
npm i -D uglifyjs
package.json
"clean":"rmdir dist /S /Q", "build": "tsc --p ./ --sourceMap false", "bundle": "uglifyjs ./dist/*.js --compress --mangle --output ./dist/index.min.js"
A propósito, se você deseja controlar o tamanho do seu projeto, existem mais dois pacotes interessantes
Eles também podem ser integrados no processo de confirmação / envio / publicação para permanecer no tamanho aceitável do pacote configurável. Muito, muito útil.
Automação
Bem, já tomamos as etapas básicas, agora tudo precisa ser automatizado; caso contrário, será francamente inconveniente para o trabalho.
Para isso, precisamos de mais um pacote - husky . Ele reescreve o git hooks e chama os comandos associados do package.json. Por exemplo, quando você confirma, o pré-comprometimento funciona, o push-push e assim por diante. Se o script retornar um erro, a confirmação falhará.
npm i -D husky
package.json
"precommit":"npm run prettier", "prepush": "call npm run lint && call npm run test"
Há uma nuance importante aqui, o uso da sintaxe de chamada não é multiplataforma e não decola em sistemas unix. Portanto, se você quiser fazer tudo honestamente, também precisará instalar o pacote npm-run-all , mas o mesmo será feito em várias plataformas.
Postagem
Bem, aqui chegamos à publicação do nosso pacote (embora vazio). Vamos pensar o que queremos da publicação?
- Teste tudo de novo
- Coletar artefatos de construção
- Coletar documentação
- Aumentar versão
- Gatilho de tags
- Enviar para npm
As mãos fazem tudo - triste. Ou você esquece alguma coisa ou precisa escrever uma lista de verificação. Também é necessário automatizar. Você pode colocar outro pacote - solte . E você pode usar os ganchos nativos do próprio npm - preversão, versão, pós-versão, por exemplo:
"preversion": "npm run test", "version": "call npm run clean && call npm run build && npm run bundle && call npm run doc && call npm run changelog && git add . && git commit -m 'changelogupdate'
Resta especificar para package.json o que incluir no pacote, o ponto de entrada e o caminho para nossos tipos (não esqueça de especificar o sinalizador --declaration em tsconfig.json para obter os arquivos d.ts)
package.json
"main": "./dist/index.min.js", "types": "./dist/index.d.ts", "files": [ "dist/", "src/", "tests/" ]
Bem, isso parece ser tudo. Agora apenas faça
npm version ... npm publish
E tudo o mais será feito automaticamente.
Bônus
Como bônus, existe um repositório de demonstração que suporta tudo isso + CI usando travis-ci. Deixe-me lembrá-lo de que o HeadlessChrome está configurado lá; portanto, se isso for importante para você, recomendo que você procure lá.
Agradecimentos
Muito obrigado a Alexei Volkov por seu relatório sobre o JsFest, que se tornou a base deste artigo.
Graças ao max7z , indestrutível , justboris por esclarecer que os caminhos para as dependências locais podem ser omitidos.
E algumas estatísticas
- Tamanho dos nós de módulo: 444 MB (NTFS)
- Número de dependências do primeiro nível: 17
- Total de pacotes usados: 643
Links úteis