Cómo migrar Puppeteer a TypeScript

En el equipo de DevTools, somos grandes fans de TypeScript, tanto que el código nuevo de DevTools se crea en él y estamos en medio de una gran migración de toda la base de código para que TypeScript realice la verificación de tipos. Puedes obtener más información sobre esa migración en nuestra charla en la Chrome Dev Summit 2020. Por lo tanto, tenía mucho sentido considerar migrar la base de código de Puppeteer a TypeScript.

Planifica la migración

Cuando planificamos la migración, queríamos avanzar en pequeños pasos. Esto mantiene baja la sobrecarga de la migración (solo trabajas en una pequeña parte del código en cualquier momento) y también reduce el riesgo. Si algo sale mal con uno de los pasos, puedes revertirlo fácilmente. Puppeteer tiene muchos usuarios, y una versión dañada causaría problemas para muchos de ellos, por lo que era fundamental que reduzcamos al mínimo el riesgo de que se produzcan cambios que generen errores.

También tuvimos la suerte de que Puppeteer cuente con un conjunto sólido de pruebas de unidades que cubren toda su funcionalidad. Esto significaba que podíamos estar seguros de que no estábamos dañando el código durante la migración, pero también de que no estábamos introduciendo cambios en nuestra API. El objetivo de la migración era completarla sin que ningún usuario de Puppeteer se diera cuenta de que habíamos migrado, y las pruebas fueron una parte vital de esa estrategia. Si no hubiéramos tenido una buena cobertura de pruebas, lo habríamos agregado antes de continuar con la migración.

Realizar cualquier cambio de código sin pruebas es riesgoso, pero los cambios en los que se modifican archivos completos o toda la base de código son especialmente peligrosos. Cuando se realizan cambios mecánicos, es fácil pasar por alto un paso y, en varias ocasiones, las pruebas detectaron un problema que pasó desapercibido para el implementador y el revisor.

Una cosa en la que invertimos tiempo de antemano fue nuestra configuración de integración continua (IC). Notamos que las ejecuciones de CI en solicitudes de extracción eran inestables y, a menudo, fallaban. Esto sucedía con tanta frecuencia que nos acostumbramos a ignorar nuestro CI y a combinar las solicitudes de extracción de todos modos, suponiendo que la falla era un problema único en CI en lugar de un problema en Puppeteer.

Después de realizar un mantenimiento general y dedicar tiempo a corregir algunos errores de prueba habituales, logramos que el estado de aprobación fuera mucho más coherente, lo que nos permitió escuchar a CI y saber que una falla indicaba un problema real. Este trabajo no es glamoroso y es frustrante ver ejecuciones de CI interminables, pero era vital que nuestro paquete de pruebas se ejecutara de forma confiable dada la cantidad de solicitudes de extracción que le arrojaba la migración.

Elige y publica un archivo

En este punto, teníamos nuestra migración lista para comenzar y un servidor de CI sólido lleno de pruebas para protegernos. En lugar de analizar cualquier archivo arbitrario, seleccionamos un archivo pequeño para migrar. Este es un ejercicio útil porque te permite validar el proceso planificado que estás a punto de emprender. Si funciona en este archivo, tu enfoque es válido. De lo contrario, puedes volver al punto de partida.

Además, ir archivo por archivo (y con los lanzamientos regulares de Puppeteer, por lo que todos los cambios no se enviaron en la misma versión de npm), mantuvo el riesgo bajo. Elegimos DeviceDescriptors.js como el primer archivo, porque era uno de los más sencillos de la base de código. Puede parecer un poco abrumante hacer todo este trabajo de preparación y realizar un cambio tan pequeño, pero el objetivo no es hacer grandes cambios de inmediato, sino proceder con cautela y metódicamente archivo por archivo. El tiempo que dedicas a validar el enfoque definitivamente te ahorra tiempo más adelante en la migración cuando te encuentras con esos archivos más complicados.

Prueba el patrón y repite

Por suerte, el cambio a DeviceDescriptors.js se incorporó correctamente a la base de código, y el plan funcionó como esperábamos. En este punto, ya puedes ponerte manos a la obra, que es exactamente lo que hicimos. Usar una etiqueta de GitHub es una muy buena forma de agrupar todas las solicitudes de extracción y nos pareció útil hacer un seguimiento del progreso.

Realizar la migración y mejorarla más tarde

Para cualquier archivo JavaScript individual, nuestro proceso fue el siguiente:

  1. Cambia el nombre del archivo de .js a .ts.
  2. Ejecuta el compilador de TypeScript.
  3. Soluciona los problemas.
  4. Crea la solicitud de extracción.

La mayor parte del trabajo en estas solicitudes de extracción iniciales fue extraer interfaces de TypeScript para estructuras de datos existentes. En el caso de la primera solicitud de extracción que migró DeviceDescriptors.js que analizamos anteriormente, el código pasó de lo siguiente:

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

Y se convirtió en lo siguiente:

interface Device {
  name: string,
  
}

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

module.exports = devices;

Como parte de este proceso, revisamos cada línea de la base de código en busca de problemas. Al igual que con cualquier base de código que haya existido durante algunos años y haya crecido con el tiempo, hay áreas de oportunidad para refactorizar el código y mejorar la situación. En particular, con el cambio a TypeScript, vimos lugares en los que una ligera reestructuración del código nos permitiría depender más del compilador y obtener una mejor seguridad de tipos.

Aunque parezca contra intuitivo, es muy importante no realizar estos cambios de inmediato. El objetivo de la migración es llevar la base de código a TypeScript y, en todo momento durante una migración grande, debes pensar en el riesgo de provocar fallas en el software y en tus usuarios. Manteniendo los cambios iniciales mínimos, mantuvimos ese riesgo bajo. Una vez que se unificó el archivo y se migró a TypeScript, pudimos realizar cambios posteriores para mejorar el código y aprovechar el sistema de tipos. Asegúrate de establecer límites estrictos para la migración y mantenerte dentro de ellos.

Migra las pruebas para probar nuestras definiciones de tipo

Una vez que migramos todo el código fuente a TypeScript, pudimos enfocarnos en nuestras pruebas. Nuestras pruebas tenían una gran cobertura, pero todas estaban escritas en JavaScript. Esto significa que una cosa que no probaron fueron nuestras definiciones de tipos. Uno de los objetivos a largo plazo del proyecto (en el que aún estamos trabajando) es enviar definiciones de tipos de alta calidad listas para usar con Puppeteer, pero no teníamos ninguna verificación en nuestra base de código sobre nuestras definiciones de tipos.

Cuando migramos las pruebas a TypeScript (siguiendo el mismo proceso, archivo por archivo), encontramos problemas con nuestro TypeScript que, de otro modo, los usuarios habrían tenido que encontrar por nosotros. Ahora nuestras pruebas no solo cubren todas nuestras funciones, sino que también actúan como un control de calidad de nuestro TypeScript.

Como ingenieros que trabajan en la base de código de Puppeteer, ya nos beneficiamos enormemente de TypeScript. Junto con nuestro entorno de CI mucho mejorado, nos permitió ser más productivos cuando trabajamos en Puppeteer y hacer que TypeScript detecte errores que, de otro modo, habrían llegado a una versión de npm. Nos complace enviar definiciones de TypeScript de alta calidad para que todos los desarrolladores que usan Puppeteer también se beneficien de este trabajo.

Descarga los canales de vista previa

Considera usar Chrome Canary, Dev o Beta como tu navegador de desarrollo predeterminado. Estos canales de versión preliminar te brindan acceso a las funciones más recientes de DevTools, te permiten probar las APIs de plataformas web de vanguardia y te ayudan a encontrar problemas en tu sitio antes que tus usuarios.

Comunícate con el equipo de Chrome DevTools

Usa las siguientes opciones para hablar sobre las funciones nuevas, las actualizaciones o cualquier otro tema relacionado con DevTools.