Durante meses, el convertidor tuvo un error que no podía ver: cada sombra de caja que producía se descartaba. No se renderizaba mal: se desechaba. La conversión tenía éxito, la importación tenía éxito, los elementos llegaban y las sombras simplemente no estaban.
Ninguna excepción. Ninguna advertencia en la consola. Ninguna solicitud fallida. Nada en un registro. Si no hubieras construido la página tú mismo, no sabrías que faltaba algo; simplemente pensarías que la conversión era mediocre.
Vale la pena escribir esto porque la forma del problema es general. Elementor es solo el caso específico. El caso general es: escribes en un sistema que ignora claves que no reconoce, y una errata o una suposición obsoleta se convierte en una pérdida de datos permanente y silenciosa.
Por qué es invisible
Elementor almacena cada elemento como JSON: un tipo de elemento más un objeto settings. Al renderizar, recorre sus controles registrados y le pide al objeto settings el valor de cada control. Una clave que no corresponde a un control registrado nunca se consulta. No se rechaza. Simplemente nunca se lee.
Ese es un diseño razonable: los datos antiguos sobreviven a las actualizaciones del plugin y las claves desconocidas de una versión más nueva no rompen una más antigua. También explica por qué un nombre de clave incorrecto produce exactamente cero retroalimentación. Con una API que valida, una clave incorrecta es un error en el momento de la escritura, detectado por la siguiente ejecución de pruebas. Con una API que ignora, es silencio, descubierto por un usuario meses después, si acaso.
Por qué no puedes simplemente buscar los nombres
La respuesta obvia es comprobar los nombres de los controles con la documentación. El problema es que muchos de ellos no están escritos en ninguna parte: se generan.
Elementor tiene controles de grupo: grupos reutilizables como tipografía, borde, fondo, sombra de caja. Se adjunta uno a un widget con un nombre y los identificadores de control individuales se componen en tiempo de ejecución: el nombre del grupo, un guion bajo y luego el nombre de cada campo. Así, un grupo de tipografía registrado como title produce el control title_font_size. No hay ningún archivo en el que aparezca esa cadena. No puedes buscarla con grep. Tienes que conocer la regla y aplicarla.
Esta es exactamente la situación en la que la memoria, las respuestas de los foros y las viejas entradas de blog son peores que inútiles: son confiadamente específicas, con frecuencia erróneas, y la API no te corregirá.
El error: una sombra es un control, no cinco
El convertidor había estado escribiendo siete claves para una sombra: una para activarla, y luego color, horizontal, vertical, desenfoque, expansión y posición como valores separados. Parecía cuidadoso. Estaba casi completamente equivocado.
El grupo de sombra de caja registra exactamente dos campos. La sombra en sí es un control compuesto cuyo valor es un único objeto que contiene horizontal, vertical, desenfoque, expansión y color juntos; el segundo campo es la posición contorno/inset. Añade el conmutador del popover y el conjunto real de claves es de tres nombres, todos con doble prefijo porque el grupo se llama a su vez box_shadow.
De las siete claves que se estaban escribiendo, cinco no existían. La sexta tenía el nombre correcto, pero se le estaba dando la cadena "yes" en lugar del objeto que espera. La séptima estaba cerca, pero le faltaba un prefijo.
Un detalle que ninguna cantidad de conjeturas habría producido: el valor para “Outline” es un único espacio, no una cadena vacía. Está en el array de opciones y en ningún otro lugar.
El segundo error: el nombre correcto que aun así no hacía nada
La misma revisión sacó a la luz flex_grow y flex_shrink, escritos en elementos con un ancho explícito para impedir que un padre flex los estire o comprima. Otra vez equivocado: esos controles viven en un grupo registrado con un nombre diferente, así que necesitaban un prefijo.
Pero corregir el prefijo aun así no habría funcionado, y esta es la parte que vale la pena recordar. Ambos campos llevan una condición: solo se aplican cuando el control de tamaño del grupo está configurado en “custom”. Las condiciones controlan la generación de CSS, no solo la visibilidad en el panel. Un nombre de clave correcto es necesario pero no suficiente.
La solución real resultó ser más simple que cualquiera de los dos intentos, y estaba en el código fuente todo el tiempo: el control de tamaño tiene una opción “none” que significa exactamente “no crecer y no encoger”: un control incondicional en lugar de tres condicionales. Leer el código fuente no solo corrigió el error, sino que produjo una implementación mejor que la que se pretendía.
Auditando el resto
Dos errores encontrados mediante lectura. Había otras noventa y tantas claves que nadie había leído. Así que el siguiente paso fue mecánico: analizar el código fuente oficial del plugin, construir el conjunto de referencia de nombres de control para cada widget y compararlo con cada clave que el convertidor puede emitir.
El script de auditoría dio cuatro respuestas equivocadas antes de dar una correcta. Cada fallo se generaliza:
- El nombre a menudo está en la línea siguiente. Los registros de control con frecuencia ponen el nombre en la línea posterior a la llamada. Un patrón de una sola línea no los detectaba, y cada widget parecía emitir cinco claves inválidas: toda la pestaña Advanced. Un comprobador que marca un control estándar como desconocido no está informando de un error; está informando de su propio punto ciego.
- Dos formas de hacer lo mismo. Un grupo construye sus campos bajo el nombre de una variable; otro usa una diferente. Hacer coincidir solo el primero descartaba silenciosamente grupos enteros, incluido, con cierta ironía, justo el grupo que se estaba investigando.
- Los arrays de argumentos se anidan. Detener la coincidencia en el primer corchete de cierre truncaba en el primer array anidado y omitía el registro por completo. Así fue como varias claves de diseño perfectamente válidas aparecieron primero como «inválidas».
- Una ventana de captura amplia consume. Corregir el punto anterior capturando una ventana grande significaba que cada coincidencia se tragaba cualquier registro dentro de ella. El recuento de controles conocidos de un widget cayó de 281 a 60, y la comparación seguía informando éxito.
Y una más, específica de esta base de código: el archivo del widget Button contiene cero registros de control. Todos viven en un trait. La primera ejecución informó que Button no tenía ningún control.
Observa la dirección de esos errores. Tres produjeron falsas alarmas: molestas, pero autocorregibles: investigas y descubres que la herramienta está equivocada. Uno produjo un falso aprobado, que es el tipo peligroso. Menos nombres válidos conocidos significa menos problemas reportados; el modo de fallo es silencioso y tranquilizador.
La comprobación que hace confiables a las demás
Lo que lleva a la única idea que vale la pena conservar de todo el ejercicio.
Un comprobador que informa «todo limpio» no vale nada hasta que hayas demostrado que es capaz de informar «no limpio».
Así que la auditoría termina con un control negativo: alimenta de nuevo los cinco nombres de clave que se sabe que son incorrectos y hace fallar toda la ejecución si se acepta alguno de ellos. Si el extractor se rompe más tarde —un patrón ampliado, un archivo omitido, una caché defectuosa—, esa aserción se pone en rojo antes de que la línea de «todo pasó» tenga oportunidad de mentir.
Junto con él van otros dos anclajes: una lista de nombres de control que con certeza deben existir tiene que encontrarse completa, y el recuento total nunca debe reducirse silenciosamente. Ese segundo habría detectado el colapso de 281 a 60.
Hacer que se mantenga corregido
Estado final una vez que la comparación volvió limpia: 205 claves comprobadas en nueve tipos de widgets, ninguna desconocida. Luego, un trabajo de integración continua para que siga así: convierte una página que contiene una sombra y comprueba en la salida que la clave compuesta está presente y que ninguno de los antiguos nombres separados ha vuelto.
No es una prueba unitaria de una función pura. Es una aserción sobre el artefacto que realmente se envía, que comprueba el error exacto que ya ocurrió una vez.
La versión general
Si escribes en cualquier cosa que ignore claves desconocidas —un constructor de páginas, una tabla de metadatos de un CMS, una carga útil de analítica, un blob de banderas de funcionalidad, la mitad de los formatos de configuración que existen—, entonces:
- Deriva el conjunto válido de claves de la fuente de la verdad, no de la memoria ni de la documentación. La documentación describe la intención; los nombres generados solo existen en tiempo de ejecución.
- Compara mecánicamente lo que emites contra él. Noventa claves no se pueden revisar a simple vista, y las incorrectas se ven exactamente igual que las correctas.
- Desconfía primero de tu comprobador. Cuando una ejecución marca algo de lo que estás seguro, la herramienta está equivocada mucho más a menudo que el código.
- Añade un control negativo. Demuestra que el comprobador puede fallar, o sus aprobados no significan nada.
- Comprueba el artefacto enviado en CI, porque esta clase de error regresa en el momento en que alguien refactoriza el código que emite.
Las sombras funcionan ahora. El resultado más útil fue notar que «sin error» se había tratado como evidencia de corrección durante meses, en una API estructuralmente incapaz de producirlo.