Porté JustHTML de Python a JavaScript con Codex CLI y GPT-5.2 en 4.5 horas
Escribí sobre JustHTML ayer—el proyecto de Emil Stenström para construir un nuevo analizador HTML5 compatible con estándares en código Python puro usando agentes de codificación ejecutándose contra la biblioteca de pruebas completa html5lib-tests. Anoche, por pura curiosidad, decidí intentar portar JustHTML de Python a JavaScript con la menor cantidad de esfuerzo posible, usando Codex CLI y GPT-5.2. Funcionó más allá de mis expectativas.
TL;DR #
Construí simonw/justjshtml, una biblioteca de análisis HTML5 sin dependencias en JavaScript que pasa 9,200 pruebas de la suite html5lib-tests e imita el diseño de API de la biblioteca JustHTML de Emil.
Tomó dos indicaciones iniciales y algunos seguimientos pequeños. GPT-5.2 ejecutándose en Codex CLI se ejecutó sin interrupciones durante varias horas, consumió 1,464,295 tokens de entrada, 97,122,176 tokens de entrada en caché y 625,563 tokens de salida y terminó produciendo 9,000 líneas de JavaScript completamente probado en 43 commits.
Tiempo transcurrido desde la idea del proyecto hasta la biblioteca terminada: aproximadamente 4 horas, durante las cuales también compré y decoré un árbol de Navidad con la familia y vi la última película de Knives Out.
Algo de contexto #
Una de las contribuciones más importantes de la especificación HTML5 hace diez años fue la forma en que especificó precisamente cómo se debe analizar el HTML inválido. El mundo está lleno de documentos inválidos y tener una especificación que los cubra significa que los navegadores pueden tratarlos de la misma manera—no hay más “comportamiento indefinido” de qué preocuparse al construir software de análisis.
¡Sin sorpresa, esas reglas de análisis inválidas son bastante complejas! El libro en línea gratuito Idiosyncrasies of the HTML parser de Simon Pieters es una excelente inmersión profunda en este tema, en particular Capítulo 3. El analizador HTML.
El proyecto Python html5lib inició el repositorio html5lib-tests con un conjunto de pruebas independientes de la implementación. Desde entonces se han convertido en el estándar de oro para pruebas de interoperabilidad de analizadores HTML5, y son utilizadas por proyectos como Servo que las utilizó para ayudar a construir html5ever, un “analizador HTML5 de grado de navegador de alto rendimiento” escrito en Rust.
El proyecto JustHTML de Emil Stenström es una implementación pura en Python de un analizador HTML5 que pasa la suite completa html5lib-tests. Emil pasó un par de meses trabajando en esto como un proyecto secundario, eligiendo deliberadamente un problema con una suite de pruebas completa existente para ver hasta dónde podía llegar con agentes de codificación.
En un momento, hizo que los agentes lo reescribieran basándose en una inspección cercana de la biblioteca html5ever de Rust. No sé cuánto de esto fue traducción directa versus inspiración (aquí está el comentario de Emil sobre eso)—su proyecto tiene 1,215 commits en total, por lo que parece haber incluido una gran cantidad de iteración, no solo un puerto directo.
Mi proyecto es un puerto directo. Instruí a Codex CLI para construir una versión JavaScript del código Python de Emil.
El proceso en detalle #
Comencé con un poco de mise en place. Cloné dos repositorios y creé un tercer directorio vacío para el nuevo proyecto:
cd ~/dev git clone GitHub - EmilStenstrom/justhtml: A pure Python HTML5 parser that just works. No C extensions to compile. No system dependencies to install. No complex API to learn. git clone GitHub - html5lib/html5lib-tests: Testsuite data for html5lib, including the de-facto standard HTML parsing tests. mkdir justjshtml cd justjshtml
Luego inicié Codex CLI para GPT-5.2 así:
codex --yolo -m gpt-5.2
Esa bandera --yolo es un atajo para --dangerously-bypass-approvals-and-sandbox, que es tan peligrosa como suena.
Mi primer indicador le dijo a Codex que inspeccionara el código existente y lo usara para construir una especificación para la nueva biblioteca JavaScript:
We are going to create a JavaScript port of ~/dev/justhtml - an HTML parsing library that passes the full ~/dev/html5lib-tests test suite. It is going to have a similar API to the Python library but in JavaScript. It will have no dependencies other than raw JavaScript, hence it will work great in the browser and node.js and other environments. Start by reading ~/dev/justhtml and designing the user-facing API for the new library - create a spec.md containing your plan.
Revisé la especificación, que incluía un conjunto de hitos propuestos, y le dije que agregara otro:
Add an early step to the roadmap that involves an initial version that parses a simple example document that is valid and returns the right results. Then add and commit the spec.md file.
Aquí está el archivo spec.md resultante. Mi solicitud para esa versión inicial se convirtió en “Milestone 0.5” que se veía así:
Milestone 0.5 — End-to-end smoke parse (single valid document)
- Implement the smallest end-to-end slice so the public API is real early:
new JustHTML("<html><head></head><body><p>Hello</p></body></html>")returns a tree with the expected tag structure and text nodes.doc.toText()returns"Hello"anddoc.errorsis empty for this valid input.- Add
scripts/smoke.js(no deps) that runs the example and asserts the expected structure/output.- Gate:
node scripts/smoke.jspasses.
Luego le dije:
Implement Milestone 0.5
Y se fue. El código resultante parecía funcionar, así que dije:
Configure GitHub Actions test.yml to run that on every commit, then commit and push
Ejecuté git init y creé un repositorio privado de GitHub para este proyecto en este punto, y configuré el directorio local para hacer push a ese remoto.
Aquí está ese push inicial. Luego le dije:
OK do the rest, commit and push often
¡Y eso fue casi todo!
Configuré mi laptop para que no se durmiera y la dejé a su suerte mientras nos íbamos a comprar un árbol de Navidad. El “commit and push often” significaba que podía monitorear su progreso en mi teléfono actualizando el registro de commits en GitHub.
Estaba ejecutando esto contra mi cuenta ChatGPT Plus de $20/mes, que tiene una ventana de asignación de tokens de cinco horas para Codex CLI. Eso se agotó a las 6:35pm y Codex se pausó, así que esperé hasta el punto de reinicio a las 7:14pm y escribí:
Keep going
A las 9:30pm se declaró a sí mismo terminado con el siguiente mensaje de resumen:
• What's Done
- html5lib-tests now run in CI and pass: tokenizer 6810/6810, tree 1770/1782
(12 skipped: #script-on), serializer 230/230, encoding 82/83 (1 skipped:
scripted) (.github/workflows/test.yml:1).
- Public API expanded: stream() (src/stream.js:1), CSS selectors query()/
matches() (src/selector.js:1), and toMarkdown() (src/markdown.js:1), all
exported via src/index.js:1.
- Added no-deps unit scripts for selectors/stream/markdown (scripts/run-
selector-tests.js:1, scripts/run-stream-tests.js:1, scripts/run-markdown-
tests.js:1) and wired them into CI.
- Fixed TreeBuilder so <br> no longer closes <p> (s
```rc/treebuilder.js:647).\n```\n\nComo toque final, le pedí que añadiera una interfaz de playground para poder probar la nueva biblioteca en mi navegador. Le indiqué:\n\n\u003e `Añade un playground.html en la carpeta de nivel superior que cargue los módulos ES necesarios desde ./src/... e implemente exactamente la misma funcionalidad que se ve en https://tools.simonwillison.net/justhtml pero usando la biblioteca de JavaScript en lugar de Pyodide`\n\nObtuvo mi [página de playground JustHTML existente](https://tools.simonwillison.net/justhtml) ([descrita aquí](https://simonwillison.net/2025/Dec/14/justhtml/#first-impressions-of-justhtml)) usando `curl` y construyó un nuevo archivo `playground.html` que cargaba el nuevo código JavaScript en su lugar. Esto funcionó *perfectamente*.\n\nHabilité GitHub Pages para mi repositorio aún privado, lo que significaba que podía acceder al nuevo playground en esta URL:\n\nhttps://simonw.github.io/justjshtml/playground.html\n\n\" \"- \\[Acerca de\\](/about)\" \"- \\[Contacto\\](/contact)\".]( https://static.simonwillison.net/static/2025/justjshtml-playground.jpg)\n\nAhora solo necesitaba documentación:\n\n\u003e `Añade un README completo con instrucciones de uso completas incluyendo atribución más cómo se construyó más cómo usarlo en HTML más cómo usarlo en Node.js`\n\nPuedes [leer el resultado aquí](https://github.com/simonw/justjshtml/blob/f3a33fdb29bf97846fd017185edc8cf82783032e/README.md).\n\nAhora estamos en ocho indicaciones en total, ejecutándose durante poco más de cuatro horas y he decorado para Navidad y he visto [Wake Up Dead Man](https://en.wikipedia.org/wiki/Wake_Up_Dead_Man) en Netflix.\n\nSegún Codex CLI:\n\n\u003e `Uso de tokens: total=2,089,858 entrada=1,464,295 (+ 97,122,176 en caché) salida=625,563 (razonamiento 437,010)`\n\nMi [calculadora de llm-prices.com](https://www.llm-prices.com/#it=2089858\u0026cit=97122176\u0026ot=625563\u0026sel=gpt-5.2) estima que sería $29.41 si pagara esos tokens a precios de API, pero estaban incluidos en mi suscripción ChatGPT Plus de $20/mes, así que el costo adicional real para mí fue cero.\n\n#### ¿Qué podemos aprender de esto? [#](https://simonwillison.net/2025/Dec/15/porting-justhtml/#what-can-we-learn-from-this-)\n\nComparto este proyecto porque creo que demuestra un montón de cosas interesantes sobre el estado de los LLM en diciembre de 2025.\n\n* Los LLM de frontera realmente pueden realizar tareas complejas de varias horas con cientos de llamadas a herramientas y supervisión mínima. Usé GPT-5.2 para esto, pero no tengo razón para creer que Claude Opus 4.5 o Gemini 3 Pro no podrían lograr lo mismo—la única razón por la que no lo he intentado es que no quiero quemar otras 4 horas de tiempo y varios millones de tokens en más ejecuciones.\n* Si puedes reducir un problema a un conjunto de pruebas robusto, puedes soltar un bucle de agente de codificación sobre él con un alto grado de confianza de que eventualmente tendrá éxito. Llamé a esto [diseñar el bucle agéntico](https://simonwillison.net/2025/Sep/30/designing-agentic-loops/) hace unos meses. Creo que es la habilidad clave para desbloquear el potencial de los LLM para tareas complejas.\n* Portar bibliotecas de código abierto completas de un idioma a otro a través de un agente de codificación funciona extremadamente bien.\n* El código es tan barato que es prácticamente gratis. El código que *funciona* continúa teniendo un costo, pero ese costo se ha desplomado ahora que los agentes de codificación pueden verificar su trabajo mientras avanzan.\n* Ni siquiera hemos *comenzado* a desempacar la etiqueta y la ética alrededor de este estilo de desarrollo. ¿Es responsable y apropiado producir un puerto directo de una biblioteca como esta en pocas horas mientras ves una película? ¿Qué se necesitaría para que el código construido de esta manera sea confiable en producción?\n\nTerminaré con algunas preguntas abiertas:\n\n* ¿Representa esta biblioteca una violación de derechos de autor de la biblioteca Rust o de la Python?\n* Incluso si esto es legal, ¿es ético construir una biblioteca de esta manera?\n* ¿Daña este formato de desarrollo al ecosistema de código abierto?\n* ¿Puedo incluso afirmar derechos de autor sobre esto, dado cuánto del trabajo fue producido por el LLM?\n* ¿Es responsable publicar bibliotecas de software construidas de esta manera?\n* ¿Cuánto mejor sería esta biblioteca si un equipo de expertos la elaborara a mano durante varios meses?