Todas las notas
Ingeniería6 de agosto de 20263 min

El editor que se comía mis <

Este es un bug que se sentía como gaslighting: como si algo te hiciera dudar de lo que veías con tus propios ojos. Abrías el editor de plantillas de SendDock, cambiabas a la pestaña de Código, y escribías un signo de menor que — un < solo. En el instante en que lo escribías, se convertía en &lt;. Escribías <div> y lo veías deshacerse en &lt;div&gt; un carácter a la vez, como si algo estuviera transcribiendo tu HTML a entidades en tiempo real. Que, resultó, era exactamente lo que pasaba.

Dos editores, un solo valor

El editor de plantillas tiene dos pestañas sobre el mismo contenido: una pestaña de Código respaldada por CodeMirror, y una pestaña Visual respaldada por GrapesJS, el constructor drag-and-drop. Ambas están enlazadas al mismo v-model — el HTML de la plantilla. Escribes en una, la otra lo refleja. Ese valor compartido es todo el punto, y también todo el problema.

La pestaña Visual estaba oculta con v-show. Y v-show no desmonta — solo pone display: none. Así que mientras escribías tranquilo en la pestaña de Código, la instancia de GrapesJS en la pestaña Visual invisible seguía montada, seguía viva, seguía observando.

El loop de retroalimentación

Esta es la secuencia, por cada tecla:

  • Escribes < en CodeMirror. El v-model compartido se vuelve el string <.
  • GrapesJS, observando ese modelo, dispara su watcher y llama a setComponents() con el valor a medio escribir.
  • < por sí solo no es HTML válido. GrapesJS hace algo razonable para un parser al que le pasan basura: trata al < solo como contenido de texto. Y el contenido de texto, serializado de forma segura de vuelta a HTML, es &lt;.
  • GrapesJS reemite &lt; de vuelta al v-model compartido.
  • CodeMirror, enlazado a ese mismo modelo, ahora muestra &lt;.

Cada tecla daba una vuelta completa por un parser de HTML oculto que "amablemente" escapaba todo lo que aún no fuera markup válido. No estabas escribiendo en un editor. Estabas escribiendo en dos editores jugando al teléfono roto, y uno de ellos no hablaba HTML a medio terminar.

El arreglo es una letra

v-show se volvió v-if.

Con v-if, la instancia de GrapesJS de la pestaña Visual ni siquiera está montada a menos que estés realmente en esa pestaña. Sin componente oculto, sin watcher, sin vuelta completa, sin escapado. La pestaña de Código vuelve a ser solo un editor de código.

Ese es el arreglo entero. Una letra — que es lo molesto y lo instructivo del asunto. El bug no estaba en el código que se veía roto (CodeMirror hacía su trabajo a la perfección) sino en un componente dos pestañas más allá que todos habían archivado mentalmente como "no está en pantalla, no está corriendo". v-show violaba esa suposición en silencio.

La lección

"Oculto" no es "ido". v-show, opacity: 0, visibility: hidden, una ruta fuera de pantalla mantenida viva por velocidad — todas dejan al componente corriendo, con sus watchers, timers y suscripciones plenamente vivos. Si dos componentes comparten una fuente de verdad y uno de ellos transforma esa verdad al cambiar, no importa que sea invisible. El código invisible igual corre. Y cuando corre sobre un valor que otro componente está editando activamente, obtienes un bug que te responde escribiendo.

Cuando algo en pantalla se comporta de forma imposible, el culpable suele ser algo fuera de pantalla que nunca dejó de trabajar.

Notas relacionadas