Cómo migrar Puppeteer a TypeScript

Somos grandes fanáticos de TypeScript en el equipo de Herramientas para desarrolladores, tanto que el código nuevo en Herramientas para desarrolladores se está creando en él y estamos en medio de una gran migración de toda la base de código para que TypeScript verifique los tipos. Puedes obtener más información sobre esa migración en nuestra charla en la Chrome Dev Summit 2020. Por lo tanto, también tenía mucho sentido migrar la base de código de Puppeteer a TypeScript.

Planifica la migración

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

También tuvimos la suerte de que Puppeteer tuviera un conjunto sólido de pruebas de unidades que cubrieran todas sus funciones. Esto significaba que podíamos tener la certeza de que no estábamos dañando el código a medida que realizábamos la migración, pero también que no estábamos introduciendo cambios en nuestra API. El objetivo de la migración era completarla sin que los usuarios de Puppeteer se dieran cuenta de que habíamos migrado, y las pruebas fueron una parte fundamental de esa estrategia. Si no hubiéramos tenido una buena cobertura de pruebas, la 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 la totalidad de la base de código son especialmente riesgosos. Cuando se realizan cambios mecánicos, es fácil omitir un paso y, en varias ocasiones, las pruebas detectaron un problema que había pasado desapercibido tanto para el implementador como para el revisor.

En lo que sí invertimos tiempo desde el principio fue en la configuración de la integración continua (CI). Notamos que las ejecuciones de CI en las solicitudes de extracción eran inestables y, a menudo, fallaban. Esto sucedía con tanta frecuencia que nos habíamos acostumbrado a ignorar nuestra CI y a combinar las solicitudes de extracción de todos modos, suponiendo que la falla era un problema aislado en la CI y no un problema en Puppeteer.

Después de un mantenimiento general y de dedicar tiempo a corregir algunas fallas de pruebas habituales, logramos que pasaran de forma mucho más constante, lo que nos permitió prestar atención a la CI y saber que una falla indicaba un problema real. Este trabajo no es glamoroso y es frustrante ver ejecuciones interminables de CI, pero era fundamental que nuestro paquete de pruebas se ejecutara de manera confiable dada la cantidad de solicitudes de extracción que la migración le enviaba.

Elige y sube un archivo

En este punto, teníamos nuestra migración lista para comenzar y un servidor de CI sólido lleno de pruebas para respaldarnos. En lugar de analizar cualquier archivo arbitrario, elegimos un archivo pequeño para migrar de forma intencional. 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 a empezar.

Además, revisar los archivos uno por uno (y con lanzamientos regulares de Puppeteer, de modo que todos los cambios no se incluyeran en la misma versión de npm) mantuvo el riesgo bajo. Elegimos DeviceDescriptors.js como el primer archivo, ya que era uno de los archivos más sencillos de la base de código. Puede parecer un poco decepcionante hacer todo este trabajo de preparación y lograr un cambio tan pequeño, pero el objetivo no es hacer cambios enormes de inmediato, sino proceder con precaución y de forma metódica, archivo por archivo. El tiempo que se dedica a validar el enfoque definitivamente ahorra tiempo más adelante en la migración cuando se llega a esos archivos más complicados.

Demuestra el patrón y repite el ejercicio.

Afortunadamente, 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 excelente manera de agrupar todas las solicitudes de extracción, y nos pareció útil para hacer un seguimiento del progreso.

Migrarlo y mejorarlo más adelante

Para cada 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. Corrige los problemas.
  4. Crea la solicitud de extracción.

La mayor parte del trabajo en estas solicitudes de extracción iniciales consistió en extraer interfaces de TypeScript para las estructuras de datos existentes. En el caso de la primera solicitud de extracción que migró DeviceDescriptors.js, de la que hablamos 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 existe desde hace algunos años y creció con el tiempo, hay áreas de oportunidad para refactorizar el código y mejorar la situación. Especialmente con la migración a TypeScript, vimos lugares en los que una ligera reestructuración del código nos permitiría apoyarnos más en el compilador y obtener una mejor seguridad de tipos.

Aunque parezca contradictorio, es muy importante que no realices 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 causar interrupciones en el software y en tus usuarios. Al mantener los cambios iniciales al mínimo, mantuvimos ese riesgo bajo. Una vez que el archivo se combinó y migró a TypeScript, pudimos realizar cambios de seguimiento para mejorar el código y aprovechar el sistema de tipos. Asegúrate de establecer límites estrictos para tu migración y trata de mantenerte dentro de ellos.

Migración de las pruebas para probar nuestras definiciones de tipos

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 significaba que una de las cosas que no probaron fueron nuestras definiciones de tipos. Uno de los objetivos a largo plazo del proyecto (en el que seguimos trabajando) es lanzar 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.

Al migrar 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 abarcan toda nuestra funcionalidad, sino que también actúan como un control de calidad de nuestro TypeScript.

Ya nos beneficiamos enormemente de TypeScript como ingenieros que trabajan en la base de código de Puppeteer. Junto con nuestro entorno de CI mejorado, nos permitió aumentar la productividad cuando trabajamos en Puppeteer y hacer que TypeScript detecte errores que, de otro modo, se habrían incluido en una versión de npm. Nos complace enviar definiciones de TypeScript de alta calidad para que todos los desarrolladores que usan Puppeteer también puedan beneficiarse 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 vista previa te brindan acceso a las funciones más recientes de las Herramientas para desarrolladores, te permiten probar las APIs de vanguardia de la plataforma web y te ayudan a encontrar problemas en tu sitio antes que los usuarios.

Comunícate con el equipo de Herramientas para desarrolladores de Chrome

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