Como migrar o Puppeteer para o TypeScript

Somos grandes fãs do TypeScript na equipe do DevTools, tanto que um novo código nele está sendo criado e estamos no meio de uma grande migração de toda a base de código para uma verificação de tipo pelo TypeScript. Saiba mais sobre essa migração na nossa palestra na Conferência de Desenvolvedores do Chrome 2020. Por isso, fez sentido também analisar a migração da base de código do Puppeteer para o TypeScript.

Como planejar a migração

Ao planejar o processo da migração, queríamos progredir em pequenas etapas. Isso reduz a sobrecarga da migração, ou seja, você trabalha apenas em uma pequena parte do código a qualquer momento, além de reduzir o risco. Se algo der errado com uma das etapas, você pode reverter facilmente. O Puppeteer tem muitos usuários, e uma versão corrompida causava problemas para muitos deles. Por isso, era fundamental minimizar o risco de corromper as alterações.

Também tivemos a sorte de que a Puppeteer tenha um conjunto robusto de testes de unidade que abrange toda a funcionalidade dele. Assim, tínhamos certeza de que não estávamos quebrando o código durante a migração, mas também de que não faríamos mudanças na API. O objetivo da migração era concluí-la sem que nenhum usuário do Puppeteer percebesse que havia migrado, e os testes eram parte vital dessa estratégia. Se não tivéssemos uma boa cobertura de teste, teríamos adicionado isso antes de continuar com a migração.

Realizar qualquer mudança de código sem testes é arriscado, mas mudar em arquivos inteiros ou em toda a base de código é especialmente arriscado. Ao fazer mudanças mecânicas, é fácil perder uma etapa e, em várias ocasiões, os testes detectaram um problema que passou do implementador e do revisor.

Algo que investimos tempo foi a configuração de integração contínua (CI). Percebemos que as execuções de CI em solicitações de envio eram instáveis e geralmente falhavam. Isso acontecia com tanta frequência que nos acostumamos a ignorar nossa CI e mesclar as solicitações de envio de qualquer maneira, supondo que a falha fosse um problema único na CI, e não um problema no Puppeteer.

Depois de alguma manutenção geral e tempo dedicado para corrigir alguns flocos de teste regulares, nós os colocamos em um estado de aprovação muito mais consistente, o que nos permite detectar a CI e saber que uma falha indica um problema real. Esse trabalho não é glamoroso, e é frustrante assistir a intermináveis execuções de CI, mas era fundamental que nosso conjunto de testes fosse executado de maneira confiável, considerando o número de solicitações de envio que a migração gerava.

Escolher e enviar um arquivo

Nesse ponto, tínhamos nossa migração pronta e um servidor de CI robusto cheio de testes para nos ajudar. Em vez de nos aprofundar em qualquer arquivo arbitrário, escolhemos intencionalmente um pequeno arquivo para migrar. Esse é um exercício útil porque permite validar o processo planejado que você está prestes a realizar. Se funcionar neste arquivo, sua abordagem é válida. Se não funcionar, você pode voltar para a prancheta.

Além disso, o uso arquivo por arquivo (e com versões regulares do Puppeteer para que as mudanças não fossem enviadas na mesma versão npm) manteve o risco baixo. Escolhemos DeviceDescriptors.js como o primeiro arquivo, porque ele era um dos arquivos mais simples na base de código. Pode parecer um pouco desanimador fazer todo esse trabalho de preparação e realizar uma mudança tão pequena, mas o objetivo não é fazer grandes mudanças imediatamente, mas prosseguir com cautela e metodicamente um arquivo por vez. O tempo gasto na validação da abordagem certamente economiza tempo depois na migração, quando você acessa esses arquivos mais complicados.

Provar o padrão e repetir

Felizmente, a mudança para DeviceDescriptors.js entrou na base de código, e o plano funcionou como esperávamos. Agora, você está pronto para seguir em frente e seguir em frente, que é exatamente o que fizemos. Usar um rótulo do GitHub é uma ótima maneira de agrupar todas as solicitações de envio, e achamos que isso é útil para acompanhar o progresso.

Migrar e melhorar depois

Para qualquer arquivo JavaScript individual, nosso processo foi:

  1. Renomeie o arquivo de .js como .ts.
  2. Execute o compilador TypeScript.
  3. Corrija todos os problemas.
  4. Crie a solicitação de envio.

A maior parte do trabalho nessas solicitações de envio iniciais foi extrair interfaces TypeScript para estruturas de dados existentes. No caso da primeira solicitação de envio que migrou o DeviceDescriptors.js, conforme discutido anteriormente, o código foi:

module.exports = [
  { 
    name: 'Pixel 4',
    … // Other fields omitted to save space
  }, 
  …
]

Ele se tornou:

interface Device {
  name: string,
  …
}

const devices: Device[] = [{name: 'Pixel 4', …}, …]

module.exports = devices;

Como parte desse processo, trabalhamos em todas as linhas da base de código para verificar se há problemas. Como acontece com qualquer base de código que existe há alguns anos e que cresceu ao longo do tempo, há áreas de oportunidade para refatorar o código e melhorar a situação. Especialmente com a mudança para o TypeScript, notamos momentos em que uma ligeira reestruturação do código nos permitiria confiar mais no compilador e melhorar a segurança de tipo.

Não é muito intuitivo, mas é muito importante resistir a fazer essas mudanças imediatamente. O objetivo da migração é colocar a base de código no TypeScript. Durante uma grande migração, pense nos riscos de causar falhas no software e nos usuários. Ao manter as mudanças iniciais mínimas, mantivemos esse risco baixo. Depois que o arquivo foi mesclado e migrado para o TypeScript, poderíamos fazer alterações de acompanhamento para melhorar o código a fim de aproveitar o sistema de tipos. Defina limites rígidos para a migração e tente não excedê-los.

Como migrar os testes para testar nossas definições de tipo

Depois de migrar todo o código-fonte para o TypeScript, poderíamos mudar nosso foco para os testes. Nossos testes tiveram uma ótima cobertura, mas foram todos programados em JavaScript. Isso significa que uma coisa que eles não testaram foram nossas definições de tipo. Uma das metas de longo prazo do projeto (em que ainda estamos trabalhando) é enviar definições de tipo de alta qualidade prontas para uso com o Puppeteer, mas não tínhamos nenhuma verificação em nossa base de código sobre nossas definições de tipo.

Ao migrar os testes para o TypeScript (seguindo o mesmo processo, arquivo por arquivo), encontramos problemas no TypeScript que seriam encontrados pelos usuários. Agora, nossos testes não só abrangem todas as funcionalidades, mas também uma verificação de qualidade do TypeScript.

Já nos beneficiamos muito do TypeScript como engenheiros que trabalham na base de código da Puppeteer. Junto com nosso ambiente de CI bastante aprimorado, ele nos permitiu aumentar a produtividade ao trabalhar no Puppeteer e fazer com que o TypeScript detecte bugs que de outra forma teriam se tornado um lançamento npm. Estamos empolgados com o envio de definições de TypeScript de alta qualidade para permitir que todos os desenvolvedores que usam o Puppeteer também se beneficiem desse trabalho.

Fazer o download dos canais de visualização

Use o Chrome Canary, Dev ou Beta como navegador de desenvolvimento padrão. Esses canais de pré-visualização dão acesso aos recursos mais recentes do DevTools, testam as APIs de plataforma da Web modernas e encontram problemas no seu site antes que os usuários o encontrem.

Entrar em contato com a equipe do Chrome DevTools

Use as opções abaixo para discutir os novos recursos e mudanças na postagem ou qualquer outro assunto relacionado ao DevTools.

  • Envie uma sugestão ou feedback em crbug.com.
  • Informe um problema do DevTools em Mais opções   Mais   > Ajuda > Informar problemas no DevTools.
  • Publique no Twitter em @ChromeDevTools.
  • Deixe comentários nos vídeos do YouTube sobre o que há de novo ou nos vídeos do YouTube de dicas sobre o DevTools.