<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es">
  <title>Novedades Ehundu</title>
  <link href="https://ehundu.com/feed.xml" rel="self"/>
  <link href="https://ehundu.com/"/>
  <updated>2026-10-17T00:00:00+02:00</updated>
  <id>https://ehundu.com/</id>
  <author><name>Ehundu</name></author>
  <entry>
    <title>Ehundu 0.15.0</title>
    <link href="https://ehundu.com/novedades/0-15-0/"/>
    <updated>2026-10-17T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-15-0/</id>
    <summary>El contrato, repasado antes de congelarlo</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;El contrato, repasado antes de congelarlo.&lt;/strong&gt; La 0.15 no trae funciones nuevas: es la versión en la que se ha leído el contrato entero contra el código, para que lo que la 1.0 vaya a prometer sea cierto. Salen de ahí las decisiones 128 a 140 de &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.15.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;. Lo que el motor hacía y solo estaba en el código queda escrito (las reglas exactas de los atajos, qué es un enlace a otro dominio, el orden de los textos, la tabla de letras del filtro &lt;code&gt;slug&lt;/code&gt;, lo que el motor escribe en &lt;code&gt;salida/&lt;/code&gt;), y lo que se tomaba a ciegas, por ser peligroso o ambiguo, ahora se avisa o detiene el build. Compilados con la 0.14.0 y con esta, los veintitrés sitios de prueba dan los mismos ficheros, uno a uno, sin avisos nuevos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lo que ahora detiene el build&lt;/strong&gt; (con el fichero y la línea, en el error). Un &lt;code&gt;formato&lt;/code&gt; que no es un entero mayor que cero (&lt;code&gt;&amp;quot;2&amp;quot;&lt;/code&gt; entre comillas, &lt;code&gt;0&lt;/code&gt;): antes se avisaba y se tomaba por 1, y podía hacer pasar por bueno un proyecto de otro formato. Una &lt;code&gt;url&lt;/code&gt; de &lt;code&gt;sitio.yml&lt;/code&gt; con ruta o con parámetros (&lt;code&gt;https://ejemplo.com/sub/&lt;/code&gt;): el motor la aceptaba y dejaba el sitio a medias, y la &lt;code&gt;url&lt;/code&gt; es la del dominio, con o sin la barra final (decisiones 130 y 136). Un punto en el nombre de una página, de un fichero de &lt;code&gt;datos/&lt;/code&gt; o de un &lt;code&gt;_datos.yml&lt;/code&gt; cuando lo que va tras el último punto no tiene forma de código de idioma (&lt;code&gt;guia.v2.md&lt;/code&gt;, &lt;code&gt;2024.10.01-titulo.md&lt;/code&gt;): antes se saltaba el fichero con un aviso y el siguiente despliegue borraba la página del servidor (decisión 135).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lo que ahora avisa.&lt;/strong&gt; Una fecha (&lt;code&gt;fecha&lt;/code&gt;, &lt;code&gt;publicar&lt;/code&gt;) escrita con hora o con zona avisa y se toma solo el día, desde su medianoche en la zona del sitio: una fecha es un día (decisión 129). Los cinco campos que el motor escribe siempre en &lt;code&gt;pagina&lt;/code&gt; (&lt;code&gt;ruta&lt;/code&gt;, &lt;code&gt;contenido&lt;/code&gt;, &lt;code&gt;markdown&lt;/code&gt;, &lt;code&gt;encabezados&lt;/code&gt; y &lt;code&gt;traducciones&lt;/code&gt;) avisan si el front matter los usa, y el campo se ignora, como ya hacía &lt;code&gt;idioma&lt;/code&gt;; antes los tapaban en silencio (decisión 133). &lt;code&gt;componentes&lt;/code&gt;, reservado desde hace tiempo, avisa si un sitio lo usa como campo propio, aunque sigue llegando a la plantilla (decisión 138).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lo que cambia en la salida.&lt;/strong&gt; Una imagen enlazada a otro dominio lleva &lt;code&gt;target=&amp;quot;_blank&amp;quot;&lt;/code&gt; y &lt;code&gt;rel&lt;/code&gt;, como cualquier enlace escrito en Markdown: la figura de una imagen sola enlazada y el atajo &lt;code&gt;imagen&lt;/code&gt; con &lt;code&gt;enlace&lt;/code&gt;. La plantilla &lt;code&gt;imagen&lt;/code&gt; recibe dos variables nuevas, &lt;code&gt;externo&lt;/code&gt; y &lt;code&gt;rel&lt;/code&gt;; una plantilla &lt;code&gt;imagen&lt;/code&gt; del sitio que no las use queda como la escribió (decisión 131). La carpeta &lt;code&gt;publico/&lt;/code&gt; deja de copiar lo que dejan el sistema y Git (&lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;.gitkeep&lt;/code&gt;, &lt;code&gt;Thumbs.db&lt;/code&gt;, &lt;code&gt;desktop.ini&lt;/code&gt; y cualquier &lt;code&gt;.git&lt;/code&gt;), y &lt;code&gt;contenido/&lt;/code&gt; ignora &lt;code&gt;Thumbs.db&lt;/code&gt; y &lt;code&gt;desktop.ini&lt;/code&gt;; &lt;code&gt;.htaccess&lt;/code&gt; y &lt;code&gt;.well-known/&lt;/code&gt; se siguen copiando (decisión 138). Ninguno de los sitios de prueba cambia.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La compilación incremental persistida, más segura.&lt;/strong&gt; La huella que decide si la memoria de &lt;code&gt;.ehundu/&lt;/code&gt; sirve para este motor mira ahora el contenido de los ficheros del motor y las versiones de sus dependencias instaladas (y la versión menor de PHP), no solo la fecha de los ficheros: un &lt;code&gt;composer update&lt;/code&gt; que cambia Twig o CommonMark ya no reaprovecha páginas hechas con la versión anterior. Cada huella de un fichero anota cuándo se tomó, para que un fichero modificado mientras se compilaba no se dé por visto. La espera al cerrojo de &lt;code&gt;.ehundu/&lt;/code&gt; tiene un tope de un minuto, y un presupuesto de tiempo enorme en &lt;code&gt;avanzar&lt;/code&gt; o en el despliegue ya no acaba tras la primera tarea. Ordenar por texto calcula la clave de cada página una vez, en vez de en cada comparación. Al actualizar, la primera compilación es completa (la memoria de la 0.14 no vale para esta): ocurre una vez.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lo que la 1.0 promete y lo que no.&lt;/strong&gt; La compilación por lotes (&lt;code&gt;CompiladorPorLotes&lt;/code&gt; y su estado en &lt;code&gt;.ehundu/lote.json&lt;/code&gt;) pasa a &lt;code&gt;@internal&lt;/code&gt;: lo que se promete para repartir una compilación en llamadas es &lt;code&gt;CompiladorIncrementalEnProceso::avanzar&lt;/code&gt;, que devuelve el mismo &lt;code&gt;Lote&lt;/code&gt;. Si la usabas, sigue funcionando; solo deja de estar prometida (decisión 128). &lt;code&gt;docs/biblioteca.md&lt;/code&gt; precisa los parámetros por nombre de &lt;code&gt;Desplegador::desplegar&lt;/code&gt; y lo que hay dentro de &lt;code&gt;Pagina::$campos&lt;/code&gt; y de &lt;code&gt;Lectura::$datos&lt;/code&gt;. El contrato promete el mismo HTML con la misma versión del motor y de sus dependencias, y no entre versiones: lo que cambie al actualizar se anotará aquí (decisión 137).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Para quien incruste la biblioteca.&lt;/strong&gt; &lt;code&gt;Formato::leer&lt;/code&gt; ya no recibe los avisos: lanza &lt;code&gt;ErrorDeProyecto&lt;/code&gt; si el valor no vale. &lt;code&gt;Informe::$rehechas&lt;/code&gt; vale siempre un número (en una compilación completa, el de páginas), no &lt;code&gt;null&lt;/code&gt;. Para un despliegue propio, &lt;code&gt;Configuracion::abrir&lt;/code&gt; abre el destino que dice la configuración, y &lt;code&gt;PlanDeDespliegue::deSalida&lt;/code&gt; y &lt;code&gt;PlanDeDespliegue::ficheros&lt;/code&gt; trazan o leen los ficheros de &lt;code&gt;salida/&lt;/code&gt; sin repetir el código del despliegue. Y &lt;code&gt;Manifiesto::leer&lt;/code&gt; rechaza las rutas que salen del sitio (&lt;code&gt;..&lt;/code&gt;, una barra inicial, una letra de unidad): el manifiesto vive en el destino, y quien pudiera escribirlo allí no debe poder hacer que Ehundu borre otra cosa. Con &lt;code&gt;--todo&lt;/code&gt; se rehace.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Documentación.&lt;/strong&gt; El registro de decisiones pasa al final de &lt;code&gt;formato.md&lt;/code&gt;, como apéndice A, para que quien lee el contrato encuentre la norma antes que su historia (las secciones no se renumeran). El esquema de los editores tiene su documento, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.15.0/docs/esquema.md&quot;&gt;&lt;code&gt;docs/esquema.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, que separa lo que es formato de cómo se comporta un editor. La documentación seguirá en español, como el formato: no se promete en inglés. Y &lt;code&gt;ehundu --version&lt;/code&gt; (o &lt;code&gt;-V&lt;/code&gt;) dice qué versión es.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.15.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.16.0</title>
    <link href="https://ehundu.com/novedades/0-16-0/"/>
    <updated>2026-10-17T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-16-0/</id>
    <summary>La relectura final del contrato</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;La relectura final del contrato.&lt;/strong&gt; La 0.16 no trae funciones nuevas: es la versión en la que tres lectores independientes comprobaron el contrato, línea a línea, contra el código (unas 250 afirmaciones, de las que unas 200 eran ciertas tal cual). Salen de ahí las decisiones 141 a 148 de &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.16.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;: unos noventa retoques del texto, que deja escrito lo que el motor hacía sin decirlo (los ficheros de texto son UTF-8, un &lt;code&gt;[nombre]&lt;/code&gt; registrado es siempre una llamada, qué es &lt;code&gt;pagina.ruta&lt;/code&gt; en una página generada, cómo comparan &lt;code&gt;donde&lt;/code&gt; y &lt;code&gt;orden&lt;/code&gt;, qué cuenta como vacío en el esquema…), la regla 7 de nombres, y unos pocos arreglos del código para que cumpla lo que el contrato ya decía. Compilados con la 0.15.0 y con esta, los veintitrés sitios de prueba dan los mismos ficheros, uno a uno, sin avisos nuevos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lo que ahora detiene el build.&lt;/strong&gt; Un nombre con la extensión de un fichero que se quiere generar (&lt;code&gt;robots.txt.twig&lt;/code&gt;, &lt;code&gt;sitemap.xml.twig&lt;/code&gt;, &lt;code&gt;llms.txt.md&lt;/code&gt;, &lt;code&gt;tabla.csv.yml&lt;/code&gt;): antes se saltaba con un aviso y un código de salida 0, y el siguiente despliegue borraba el &lt;code&gt;robots.txt&lt;/code&gt; publicado. El error dice cómo hacerlo: una página &lt;code&gt;robots.twig&lt;/code&gt; con &lt;code&gt;url: /robots.txt&lt;/code&gt; (decisión 143). Un código de idioma que el sitio no declara (&lt;code&gt;contacto.fr.md&lt;/code&gt;) sigue siendo un aviso.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lo que ahora avisa.&lt;/strong&gt; Un atajo registrado con los atributos mal escritos (&lt;code&gt;[imagen fichero=a.png]&lt;/code&gt;, sin comillas), que se quedaba en la página como texto sin que nadie lo notara. Un &lt;code&gt;feed.coleccion&lt;/code&gt; que ninguna página usa, que daba un feed vacío con la fecha de 1970. Una página con la extensión en mayúsculas o &lt;code&gt;.markdown&lt;/code&gt; (&lt;code&gt;LEEME.MD&lt;/code&gt;), que se copia tal cual (decisión 147).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lo que cambia en la salida.&lt;/strong&gt; Un día sin hora y sin comillas en un campo propio, en &lt;code&gt;datos/&lt;/code&gt; o en &lt;code&gt;sitio.yml&lt;/code&gt; es ese día en la zona del sitio: con &lt;code&gt;zonaHoraria: America/Mexico_City&lt;/code&gt;, &lt;code&gt;|fecha&lt;/code&gt; escribía el 18 de marzo para el 19 (decisión 144). Los atajos &lt;code&gt;imagen&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt; y &lt;code&gt;archivo&lt;/code&gt; ya no leen un &lt;code&gt;fichero&lt;/code&gt; de fuera de &lt;code&gt;publico/&lt;/code&gt;: avisan y no insertan nada. &lt;code&gt;svg()&lt;/code&gt; quita la declaración XML y el DOCTYPE aunque un comentario los separe. Un enlace a &lt;code&gt;https://EJEMPLO.com&lt;/code&gt; ya es del sitio aunque el dominio vaya en mayúsculas. &lt;code&gt;slugify&lt;/code&gt; vale también en el patrón de &lt;code&gt;agrupar&lt;/code&gt; y de &lt;code&gt;paginar&lt;/code&gt;, y &lt;code&gt;paginar&lt;/code&gt; avisa de una &lt;code&gt;url&lt;/code&gt; sin barra final aunque todo quepa en un trozo. Una &lt;code&gt;url&lt;/code&gt; de solo espacios es una en blanco. Ninguno de los sitios de prueba cambia.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La memoria de la incremental, por registros.&lt;/strong&gt; &lt;code&gt;.ehundu/memoria.bin&lt;/code&gt; se guarda y se lee registro a registro, sin construir una cadena entera de la memoria, que en un sitio grande eran cien megas de más (decisión 141). En el sitio más pesado de los de prueba (1.204 páginas, 97 MB de memoria guardada) el pico de memoria de PHP baja de 332 a 170 MB en la primera compilación, de 352 a 190 al cambiar una página y de 416 a 252 al cambiar una plantilla; en dos sitios de 27 y 29 MB, de 102-134 a 50-82 MB. Y si la memoria guardada no cabe en el &lt;code&gt;memory_limit&lt;/code&gt; de PHP (tres veces lo que mide el fichero más 16 MB), &lt;code&gt;compilar&lt;/code&gt; compila entero sin tocarla y avisa a cuánto subirlo, y &lt;code&gt;avanzar&lt;/code&gt;, que sin ella no puede repartir el trabajo, falla con ese mismo texto en vez del error de PHP. &lt;code&gt;docs/compilacion.md&lt;/code&gt; lleva las medidas y lo que cabe con 128, 256 y 512 MB.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Para quien use la biblioteca.&lt;/strong&gt; &lt;code&gt;paginacion.cantidad&lt;/code&gt; se llama &lt;code&gt;paginacion.entradas&lt;/code&gt;, sin alias, porque ningún sitio usa &lt;code&gt;paginar&lt;/code&gt; todavía y el nombre no decía de qué (decisión 142; &lt;code&gt;total&lt;/code&gt; sigue siendo el de páginas). &lt;code&gt;Desplegador::desplegar&lt;/code&gt; con &lt;code&gt;$todo&lt;/code&gt; ignora &lt;code&gt;$segundos&lt;/code&gt;: repartido, no terminaba nunca. &lt;code&gt;Version::ACTUAL&lt;/code&gt; se promete. &lt;code&gt;Lote::progreso()&lt;/code&gt; de un lote &lt;code&gt;ocupado()&lt;/code&gt; vale 0. La carpeta de destino de un despliegue &lt;code&gt;carpeta&lt;/code&gt; se crea al subir el primer fichero y no al abrirla, así que &lt;code&gt;--simular&lt;/code&gt; no deja nada (si tu código contaba con que abrir la creara, créala tú). Un volcado de &lt;code&gt;Configuracion&lt;/code&gt; tampoco enseña dónde está la clave privada, y el &lt;code&gt;sitemap.xml&lt;/code&gt; y el &lt;code&gt;feed.xml&lt;/code&gt; de cada idioma se suben los últimos. &lt;code&gt;docs/biblioteca.md&lt;/code&gt; gana lo que se promete y no estaba escrito: las firmas de &lt;code&gt;Destino&lt;/code&gt;, &lt;code&gt;ErrorDeProyecto&lt;/code&gt;, &lt;code&gt;trazar&lt;/code&gt;, &lt;code&gt;anotar&lt;/code&gt; y &lt;code&gt;quitar&lt;/code&gt;, la semántica de &lt;code&gt;Proyecto&lt;/code&gt; y de &lt;code&gt;Pagina::$campos&lt;/code&gt;, los códigos de salida de la consola y el formato del manifiesto del despliegue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Al actualizar.&lt;/strong&gt; La primera compilación de cada sitio que tenga memoria guardada es completa, porque cambia cómo se guarda (una vez). El HTML de los sitios no cambia, así que no hay que volver a publicarlos. Si usabas &lt;code&gt;pagina.paginacion.cantidad&lt;/code&gt; en una plantilla, cámbialo por &lt;code&gt;entradas&lt;/code&gt;: Twig no avisa de un nombre que no existe.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.16.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.11.0</title>
    <link href="https://ehundu.com/novedades/0-11-0/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-11-0/</id>
    <summary>Las imágenes con varios anchos salen con su srcset</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Las imágenes con varios anchos salen con su &lt;code&gt;srcset&lt;/code&gt;.&lt;/strong&gt; Ehundu sigue sin procesar imágenes, pero ahora lee las variantes que otra herramienta (un editor al subir la foto, un programa aparte) deja en &lt;code&gt;publico/&lt;/code&gt;. Con la sección &lt;code&gt;imagenes&lt;/code&gt; de &lt;code&gt;sitio.yml&lt;/code&gt; (&lt;code&gt;anchos: [480, 960, 1600]&lt;/code&gt;, &lt;code&gt;formatos: [avif, webp]&lt;/code&gt; y un &lt;code&gt;sizes&lt;/code&gt; si hace falta), la función &lt;code&gt;variantes(&apos;img/foto&apos;)&lt;/code&gt; da &lt;code&gt;src&lt;/code&gt;, &lt;code&gt;ancho&lt;/code&gt;, &lt;code&gt;alto&lt;/code&gt;, &lt;code&gt;srcset&lt;/code&gt; y &lt;code&gt;fuentes&lt;/code&gt; para escribir el &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; o el &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;, y el atajo &lt;code&gt;[imagen …]&lt;/code&gt; y las figuras del Markdown lo hacen solos. El &lt;code&gt;srcset&lt;/code&gt; lleva el ancho real de cada fichero, porque quien genera las variantes no amplía una foto estrecha; una imagen sin variantes sale como antes, sin aviso; cada fichero que se busca, exista o no, es una dependencia de la página en la compilación incremental, y el feed pasa a direcciones completas las de cada &lt;code&gt;srcset&lt;/code&gt; (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.11.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §2, §7.3 y §8.2, decisión 120).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Y una lista larga se parte en páginas con &lt;code&gt;paginar&lt;/code&gt;.&lt;/strong&gt; La propia página es la primera, en su dirección, y el motor genera las demás: &lt;code&gt;paginar: {coleccion: blog, porPagina: 10, orden: fecha desc}&lt;/code&gt;. Cada una ve las entradas de su trozo en &lt;code&gt;pagina.paginas&lt;/code&gt; y dónde está en &lt;code&gt;pagina.paginacion&lt;/code&gt; (&lt;code&gt;actual&lt;/code&gt;, &lt;code&gt;total&lt;/code&gt;, &lt;code&gt;anterior&lt;/code&gt;, &lt;code&gt;siguiente&lt;/code&gt;, &lt;code&gt;enlaces&lt;/code&gt;…). Por defecto salen en &lt;code&gt;/blog/pagina/2/&lt;/code&gt;, y &lt;code&gt;paginar.url&lt;/code&gt; con &lt;code&gt;{{ numero }}&lt;/code&gt; cambia la forma para quien migra y no quiere perder direcciones. Una entrada nueva que desborde la última abre la siguiente sola. Las páginas que genera no entran en colecciones ni en el sitemap. Partir cada grupo de &lt;code&gt;agrupar&lt;/code&gt; queda reservado (§6.4, decisión 121).&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.10 compilan igual: no cambia nada que no use &lt;code&gt;imagenes&lt;/code&gt; ni &lt;code&gt;paginar&lt;/code&gt;. Compilados desde cero con la 0.10.0 y con esta, los veintitrés sitios de prueba (PDF incluidos) dan los mismos ficheros, uno a uno.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.11.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.11.1</title>
    <link href="https://ehundu.com/novedades/0-11-1/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-11-1/</id>
    <summary>Actualiza symfony/yaml a la 8.1.9, que corrige el CVE-2026-109413</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Actualiza &lt;code&gt;symfony/yaml&lt;/code&gt; a la 8.1.9, que corrige el CVE-2026-109413.&lt;/strong&gt; La recursión sin límite del lector de YAML en línea permite que un solo documento tumbe el proceso. El &lt;code&gt;ehundu.phar&lt;/code&gt; de la 0.11.0 llevaba dentro la 8.0.15: el &lt;code&gt;composer.json&lt;/code&gt; de Ehundu calculaba su &lt;code&gt;composer.lock&lt;/code&gt; como si el PHP fuera el 8.4.0, y la 8.1 pide el 8.4.1 o superior. Ahora el &lt;code&gt;platform&lt;/code&gt; es 8.4.1 y el &lt;code&gt;.phar&lt;/code&gt; lleva la 8.1.9.&lt;/p&gt;
&lt;p&gt;No cambia nada del motor ni del formato: compilados desde cero con la 0.11.0 y con esta, los veintitrés sitios de prueba (PDF incluidos) dan los mismos ficheros, uno a uno. El mínimo sigue siendo PHP 8.4. Quien instala Ehundu con Composer y PHP 8.4.1 o superior ya recibía la 8.1.x; si tu proyecto tiene un &lt;code&gt;composer.lock&lt;/code&gt; con la 8.0, &lt;code&gt;composer update symfony/yaml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.11.1&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.12.0</title>
    <link href="https://ehundu.com/novedades/0-12-0/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-12-0/</id>
    <summary>Un sitio grande se compila y se sube en varias llamadas</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Un sitio grande se compila y se sube en varias llamadas.&lt;/strong&gt; Un proceso web tiene un límite de tiempo, y compilar de golpe un sitio de mil páginas no cabe. La interfaz nueva &lt;code&gt;CompiladorPorLotes&lt;/code&gt; (con &lt;code&gt;CompiladorPorLotesEnProceso&lt;/code&gt; y &lt;code&gt;Lote&lt;/code&gt; como resultado) hace una parte del trabajo por llamada, con un presupuesto de segundos que da quien llama: &lt;code&gt;iniciar($proyecto)&lt;/code&gt; empieza, &lt;code&gt;avanzar($proyecto, 20.0)&lt;/code&gt; hace tareas hasta agotar el presupuesto, y siempre al menos una, y cuando &lt;code&gt;completo()&lt;/code&gt; el &lt;code&gt;informe()&lt;/code&gt; es el de una compilación entera y &lt;code&gt;salida/&lt;/code&gt; queda igual que si se hubiera compilado de una vez. Por dónde va se guarda en &lt;code&gt;.ehundu/lote.json&lt;/code&gt;, dentro del proyecto, con su propio &lt;code&gt;.gitignore&lt;/code&gt; para que no ensucie un repositorio; si el proyecto cambia a mitad, el lote empieza de nuevo y lo dice. La subida también se reparte: &lt;code&gt;Desplegador::desplegar()&lt;/code&gt; acepta un presupuesto (&lt;code&gt;$segundos&lt;/code&gt;) y una compilación ya hecha (&lt;code&gt;$compilado&lt;/code&gt;), y &lt;code&gt;InformeDeDespliegue&lt;/code&gt; dice si terminó (&lt;code&gt;$completo&lt;/code&gt;) y cuántos ficheros quedan (&lt;code&gt;$pendientes&lt;/code&gt;). Cada página se escribe al hacerla, así que &lt;code&gt;salida/&lt;/code&gt; mezcla páginas nuevas y viejas hasta el último lote. La orden &lt;code&gt;ehundu&lt;/code&gt; no cambia (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.12.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §10.4 y decisiones 124 y 125; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.12.0/docs/compilacion.md&quot;&gt;&lt;code&gt;docs/compilacion.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Un proyecto declara para qué versión del formato está escrito: &lt;code&gt;formato: 1&lt;/code&gt;.&lt;/strong&gt; En &lt;code&gt;sitio.yml&lt;/code&gt; (§2), y &lt;code&gt;&amp;quot;formato&amp;quot;: 1&lt;/code&gt; en el &lt;code&gt;esquema.json&lt;/code&gt; de un editor (§11). Sin la clave, el formato es el 1 y no se avisa, así que ningún proyecto de la 0.11 tiene que cambiar; un valor que no es un entero mayor que cero se avisa y se toma como 1; y uno mayor que el que entiende el motor detiene el build con un error claro, en lugar de compilar a ciegas un formato que no conoce. Es solo la versión mayor, porque dentro de una el formato únicamente crece (decisión 122; clase &lt;code&gt;Ehundu\Formato&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Y por fin está escrito qué promete la biblioteca.&lt;/strong&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.12.0/docs/biblioteca.md&quot;&gt;&lt;code&gt;docs/biblioteca.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt; lista la orden de consola y las clases PHP que se mantendrán estables dentro de una versión mayor: compilar, leer un proyecto, los avisos y los errores, &lt;code&gt;Slug&lt;/code&gt; y el despliegue. Las demás clases llevan &lt;code&gt;@internal&lt;/code&gt; y pueden cambiar en cualquier versión menor. Lo que va a dejar de valer avisa durante, al menos, una versión menor antes de quitarse (decisión 123). La 1.0 congelará el formato, la orden y esa lista.&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.11 compilan igual: no cambia nada que no use &lt;code&gt;formato&lt;/code&gt; ni los lotes. Compilados desde cero con la 0.11.1 y con esta, los veintitrés sitios de prueba (PDF incluidos) dan los mismos ficheros, uno a uno, y compilados por lotes dan los mismos que de golpe.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.12.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.13.0</title>
    <link href="https://ehundu.com/novedades/0-13-0/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-13-0/</id>
    <summary>La compilación incremental sale del proceso</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;La compilación incremental sale del proceso.&lt;/strong&gt; Hasta ahora solo &lt;code&gt;ehundu servir&lt;/code&gt; compilaba de forma incremental, porque lo que recuerda una compilación no sobrevivía al proceso que la hizo; quien incrustaba el motor en un proceso web, como un panel de edición, tenía que compilar el sitio entero en cada guardado. La clase nueva &lt;code&gt;CompiladorIncrementalEnProceso&lt;/code&gt; (&lt;code&gt;compilar&lt;/code&gt;, &lt;code&gt;avanzar&lt;/code&gt; y &lt;code&gt;olvidar&lt;/code&gt;, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.13.0/docs/biblioteca.md&quot;&gt;&lt;code&gt;docs/biblioteca.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;) guarda lo que recuerda en &lt;code&gt;.ehundu/memoria.bin&lt;/code&gt;, dentro del proyecto, y la siguiente compilación, aunque sea de otro proceso, lo aprovecha: en el sitio más grande de los de prueba, 1.204 páginas, cambiar una página rehace una y tarda unos 3 s, frente a los 35-45 s de una compilación completa. El resultado es siempre el de una compilación completa: los mismos ficheros y los mismos avisos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Y se puede repartir en llamadas.&lt;/strong&gt; &lt;code&gt;avanzar($proyecto, $segundos)&lt;/code&gt; rehace páginas hasta agotar el presupuesto y guarda lo hecho; la llamada siguiente sigue. No hace falta un estado aparte, como en la compilación por lotes: una memoria con la mitad de las páginas es una memoria a la que le faltan páginas, y el plan de la incremental rehace las que faltan y aprovecha las que valen, también si el proyecto cambia entre dos llamadas. El &lt;code&gt;Informe&lt;/code&gt; de la llamada que termina cuenta la compilación entera, no solo su parte. Los lotes de la 0.12 siguen como estaban, para quien quiera repartir una compilación completa sin guardar nada del sitio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La memoria se ignora sin error si no vale.&lt;/strong&gt; Si falta, está dañada o cortada, la escribió otra versión de la memoria o del motor (lleva una huella del tamaño y la fecha de su código, así que actualizar el motor hace que la primera compilación sea entera), o es de otro proyecto, se hace una compilación completa. Solo se guarda cuando una llamada sale bien, y no se vuelve a escribir si no se ha rehecho ninguna página. Una llamada que llega mientras otra está compilando el mismo proyecto no la estropea: &lt;code&gt;avanzar&lt;/code&gt; devuelve &lt;code&gt;ocupado()&lt;/code&gt; y &lt;code&gt;compilar&lt;/code&gt; espera. La memoria pesa del orden de &lt;code&gt;salida/&lt;/code&gt; más lo leído (97 MB en el sitio de 1.204 páginas) y el proceso que la carga necesita unos 350 MB en ese sitio; &lt;code&gt;.ehundu/&lt;/code&gt; lleva su propio &lt;code&gt;.gitignore&lt;/code&gt;, y se puede borrar cuando se quiera (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.13.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §10.5, decisión 126; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.13.0/docs/compilacion.md&quot;&gt;&lt;code&gt;docs/compilacion.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.12 compilan igual: no cambia nada que no use la clase nueva, y la orden &lt;code&gt;ehundu&lt;/code&gt; no la usa. Compilados desde cero con la 0.12.0 y con esta, los veintitrés sitios de prueba (PDF incluidos) dan los mismos ficheros, uno a uno; y compilados con la incremental persistida, repartida en llamadas y después sin cambios, dan lo mismo que de golpe.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.13.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.14.0</title>
    <link href="https://ehundu.com/novedades/0-14-0/"/>
    <updated>2026-10-16T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-14-0/</id>
    <summary>Las páginas llevan una marca de Ehundu</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Las páginas llevan una marca de Ehundu.&lt;/strong&gt; El motor añade &lt;code&gt;&amp;lt;meta name=&amp;quot;generator&amp;quot; content=&amp;quot;Ehundu&amp;quot;&amp;gt;&lt;/code&gt; al &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; de cada página HTML que escribe, para que se reconozcan los sitios hechos con él, como hacen otros generadores. La etiqueta va justo antes del primer &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt;, en su propia línea y con la sangría de esa línea más dos espacios (pegada a él, si el HTML está minificado). Se quita con una clave de &lt;code&gt;sitio.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;generador: no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Sin versión, a propósito.&lt;/strong&gt; El contenido es siempre &lt;code&gt;Ehundu&lt;/code&gt;, sin el número de versión: con él, cada actualización del motor cambiaría todas las páginas de todos los sitios y obligaría a volver a subirlas, y quien mira el código de una web no tendría por qué saber qué versión exacta la hizo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lo que no cambia.&lt;/strong&gt; Una página que ya trae su propia etiqueta &lt;code&gt;generator&lt;/code&gt; en la plantilla se queda como está: manda la plantilla. Tampoco cambia lo que no tiene &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt; (un fragmento, un &lt;code&gt;.txt&lt;/code&gt;, un XML), ni el PDF de una página, ni el &lt;code&gt;sitemap.xml&lt;/code&gt; ni el &lt;code&gt;feed.xml&lt;/code&gt;. &lt;code&gt;sí&lt;/code&gt; o no poner la clave es lo mismo; cualquier valor que no sea &lt;code&gt;sí&lt;/code&gt; ni &lt;code&gt;no&lt;/code&gt; avisa y se toma como &lt;code&gt;sí&lt;/code&gt;. Cambiar la clave rehace todas las páginas, como cualquier otro cambio de &lt;code&gt;sitio.yml&lt;/code&gt; (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.14.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §2, decisión 127).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Al actualizar.&lt;/strong&gt; Es la primera vez que el motor toca el HTML que sale de las plantillas, y por eso cada página de cada sitio gana una línea al compilar con la 0.14: hay que volver a publicar los sitios una vez. No volverá a pasar de una versión a otra, porque la etiqueta no cambia con ella. Compilados con la 0.13.0 y con esta, los veintitrés sitios de prueba dan los mismos ficheros, uno a uno, salvo esa línea.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.14.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.10.0</title>
    <link href="https://ehundu.com/novedades/0-10-0/"/>
    <updated>2026-10-11T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-10-0/</id>
    <summary>El texto con formato de un campo se convierte a HTML con el filtro markdown</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;El texto con formato de un campo se convierte a HTML con el filtro &lt;code&gt;markdown&lt;/code&gt;.&lt;/strong&gt; Hasta ahora solo el cuerpo de una página se podía convertir de Markdown: un titular con una palabra en cursiva, o un resumen con una negrita, no tenía forma de llegar a la plantilla como HTML. Con &lt;code&gt;titular: &amp;quot;Una cocina que *viaja*&amp;quot;&lt;/code&gt;, &lt;code&gt;{{ pagina.titular|markdown }}&lt;/code&gt; da &lt;code&gt;Una cocina que &amp;lt;em&amp;gt;viaja&amp;lt;/em&amp;gt;&lt;/code&gt;. Sin argumento es de línea: el Markdown sin bloques, sin &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, con énfasis, negrita, tachado, código, enlaces, enlaces automáticos y HTML escrito a mano (&lt;code&gt;&amp;lt;mark&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt;); un salto de línea con una barra invertida o dos espacios al final de la línea sale como &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.10.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §7.3, decisión 113).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Y de bloque, con &lt;code&gt;markdown(&apos;bloque&apos;)&lt;/code&gt;.&lt;/strong&gt; Para un texto con varios párrafos dentro de un bloque que pone la plantilla: se convierte como el cuerpo, con párrafos, listas, citas y encabezados. Sigue sin atajos, y no hace figuras de las imágenes, ni convierte tablas ni notas al pie, ni da &lt;code&gt;id&lt;/code&gt; a los encabezados: eso es del cuerpo (decisión 114).&lt;/p&gt;
&lt;p&gt;En los dos modos, los enlaces a otros dominios se tratan como en el cuerpo (&lt;code&gt;target&lt;/code&gt;, &lt;code&gt;rel&lt;/code&gt; y el aviso de &lt;code&gt;enlacesExternos&lt;/code&gt;), el resultado no se escapa de nuevo, un valor vacío no da nada, un número se toma como texto, y una lista, un mapa o un sí o un no avisan. Un modo que no existe avisa y se usa el de línea.&lt;/p&gt;
&lt;p&gt;Salió de rehacer un sitio con páginas de aterrizaje que se editan desde un panel: el esquema (§11) ya ofrecía el tipo &lt;code&gt;markdown&lt;/code&gt; para cualquier campo, pero el motor no tenía cómo pintarlo.&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.9 compilan igual: son filtros nuevos y no cambian nada que no los use. Compilados desde cero con la 0.9.0 y con esta, los dieciséis sitios de prueba que compilan con la 0.9 dan los mismos ficheros, uno a uno.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.10.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.5.0</title>
    <link href="https://ehundu.com/novedades/0-5-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-5-0/</id>
    <summary>Una página por cada valor</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Una página por cada valor.&lt;/strong&gt; Una página con &lt;code&gt;agrupar&lt;/code&gt; es una plantilla de páginas: el motor la repite por cada valor de un campo de las páginas de una colección, que es lo que hace falta para tener una página por etiqueta sin escribirla a mano, en un blog donde la siguiente entrada puede traer una etiqueta nueva. Agrupa por &lt;code&gt;slug&lt;/code&gt;, así que &lt;code&gt;Cultura pop&lt;/code&gt; y &lt;code&gt;cultura-pop&lt;/code&gt; son un grupo; da a la plantilla &lt;code&gt;grupo&lt;/code&gt; (la variante más usada) y &lt;code&gt;paginas&lt;/code&gt; (las del grupo, con la forma de las que da &lt;code&gt;coleccion()&lt;/code&gt;); no genera página para un grupo sin páginas publicadas; y con &lt;code&gt;etiquetas: [temas]&lt;/code&gt; las páginas generadas forman una colección que sirve de índice. No ejecuta código del sitio, no es paginación (que sigue fuera de la v1) y la compilación incremental sabe de ella: una etiqueta nueva es una página nueva, y editar el cuerpo de una entrada no rehace las páginas de sus etiquetas (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.5.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §6.3, decisión 105).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Publicar en su fecha.&lt;/strong&gt; Una &lt;code&gt;fecha&lt;/code&gt; futura sigue sin ocultar nada, como la de un evento en una agenda. Para un blog que se escribe por adelantado, &lt;code&gt;publicarEnSuFecha: sí&lt;/code&gt; en el &lt;code&gt;_datos.yml&lt;/code&gt; de su carpeta hace que una página no se publique antes de su &lt;code&gt;fecha&lt;/code&gt;: es lo mismo que escribir &lt;code&gt;publicar&lt;/code&gt; con esa fecha, sin repetirla en cada fichero. Una subcarpeta lo quita con &lt;code&gt;no&lt;/code&gt;, un &lt;code&gt;publicar&lt;/code&gt; explícito manda sobre él y &lt;code&gt;borrador&lt;/code&gt; sobre los dos. Una página retenida no genera fichero ni sale en colecciones, sitemap, feed ni traducciones, y sale cuando el sitio se vuelve a compilar después de su día (§4, decisión 106).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;pagina.markdown&lt;/code&gt;.&lt;/strong&gt; El Markdown de una página tal como está escrito, sin el front matter y sin convertir, para un &lt;code&gt;llms-full.txt&lt;/code&gt; que reúna el texto de varios artículos. También lo tienen las páginas que da &lt;code&gt;coleccion()&lt;/code&gt;. Quien lo lee depende del fichero de esa página (§7.2, decisión 104).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El formato, para los editores.&lt;/strong&gt; El esquema (§11) describe ahora el orden manual de un tipo, las páginas fijas (&lt;code&gt;unico&lt;/code&gt;), el menú lateral en secciones (&lt;code&gt;grupo&lt;/code&gt;, &lt;code&gt;rotulo&lt;/code&gt;, &lt;code&gt;excluir&lt;/code&gt;), los borradores y la programación de páginas desde el editor (decisiones 99 a 103). El motor no lee ninguno de estos atributos: son cosas de los editores.&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.4 compilan igual, fichero a fichero. Lo único que cambia es que &lt;code&gt;agrupar&lt;/code&gt; y &lt;code&gt;publicarEnSuFecha&lt;/code&gt; son ahora campos reservados del front matter, y que &lt;code&gt;grupo&lt;/code&gt; y &lt;code&gt;paginas&lt;/code&gt; solo existen en las páginas que genera &lt;code&gt;agrupar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.5.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.6.0</title>
    <link href="https://ehundu.com/novedades/0-6-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-6-0/</id>
    <summary>Las páginas de una carpeta, sin la carpeta en la URL</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Las páginas de una carpeta, sin la carpeta en la URL.&lt;/strong&gt; El patrón de &lt;code&gt;url&lt;/code&gt; tiene ahora &lt;code&gt;{{ nombreDeFichero }}&lt;/code&gt;: el nombre del fichero de la página, sin carpeta, sin extensión y sin el código de idioma. Con &lt;code&gt;url: &amp;quot;/{{ nombreDeFichero }}/&amp;quot;&lt;/code&gt; en el &lt;code&gt;_datos.yml&lt;/code&gt; de &lt;code&gt;contenido/posts/&lt;/code&gt;, el fichero &lt;code&gt;mi-entrada.md&lt;/code&gt; sale como &lt;code&gt;/mi-entrada/&lt;/code&gt; y no como &lt;code&gt;/posts/mi-entrada/&lt;/code&gt;, y una entrada nueva no tiene que escribir nada. Admite &lt;code&gt;|slug&lt;/code&gt;, en una página con &lt;code&gt;agrupar&lt;/code&gt; es el nombre del fichero que genera las páginas, y manda sobre un campo que se llame igual. Es lo que hacía falta para migrar un blog cuyas direcciones nunca llevaron la carpeta (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.6.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §5.1, decisión 107).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El aviso de los enlaces que abren otra pestaña.&lt;/strong&gt; Un enlace a otro sitio escrito en Markdown ya salía con &lt;code&gt;target=&amp;quot;_blank&amp;quot;&lt;/code&gt; y &lt;code&gt;rel=&amp;quot;noopener&amp;quot;&lt;/code&gt;. La nueva sección &lt;code&gt;enlacesExternos&lt;/code&gt; de &lt;code&gt;sitio.yml&lt;/code&gt; añade a esos enlaces &lt;code&gt;noreferrer&lt;/code&gt; (&lt;code&gt;noreferrer: sí&lt;/code&gt;) y un texto oculto para los lectores de pantalla, que avisa de que se abre otra pestaña (&lt;code&gt;aviso: (abre en ventana nueva)&lt;/code&gt;). En un sitio con varios idiomas, el aviso lleva un texto por idioma, con su código como clave, y cada página usa el suyo. La clase del texto es &lt;code&gt;visually-hidden&lt;/code&gt; o la que diga &lt;code&gt;clase&lt;/code&gt;: el motor no trae CSS, así que el sitio define la suya. Un enlace que solo envuelve una imagen, que es una figura, no lo lleva, y el HTML escrito a mano y los enlaces de las plantillas no se tocan. Sin la sección, los enlaces salen como antes (§2 y §8, decisión 108).&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.5 compilan igual, fichero a fichero. Lo único que cambia es que &lt;code&gt;nombreDeFichero&lt;/code&gt; vale ahora en los patrones de &lt;code&gt;url&lt;/code&gt;, donde antes no existía, y que &lt;code&gt;enlacesExternos&lt;/code&gt; es una clave de &lt;code&gt;sitio.yml&lt;/code&gt; con significado.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.6.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.7.0</title>
    <link href="https://ehundu.com/novedades/0-7-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-7-0/</id>
    <summary>Un id para los encabezados del Markdown, y un índice con ellos</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Un &lt;code&gt;id&lt;/code&gt; para los encabezados del Markdown, y un índice con ellos.&lt;/strong&gt; La nueva sección &lt;code&gt;encabezados&lt;/code&gt; de &lt;code&gt;sitio.yml&lt;/code&gt; da un &lt;code&gt;id&lt;/code&gt; a los encabezados del Markdown de los niveles que se indiquen (&lt;code&gt;niveles: [2]&lt;/code&gt;, y por defecto del 2 al 6): el &lt;code&gt;slug&lt;/code&gt; de su texto, con &lt;code&gt;-2&lt;/code&gt;, &lt;code&gt;-3&lt;/code&gt;… si se repite en la página, y &lt;code&gt;seccion&lt;/code&gt; si no deja nada. Con &lt;code&gt;enlace: ¶&lt;/code&gt; cada uno termina en un enlace permanente a su apartado, con la &lt;code&gt;clase&lt;/code&gt; que defina el sitio y una &lt;code&gt;etiqueta&lt;/code&gt; para lectores de pantalla (&lt;code&gt;Enlace a «{texto}»&lt;/code&gt;, con un texto por idioma en un sitio con varios). Las plantillas leen la lista en &lt;code&gt;pagina.encabezados&lt;/code&gt;, con &lt;code&gt;nivel&lt;/code&gt;, &lt;code&gt;texto&lt;/code&gt; e &lt;code&gt;id&lt;/code&gt; de cada uno, para construir un índice; también la tienen las páginas de una colección. Actúa al convertir el Markdown y no recorre después el HTML ya generado: no toca los encabezados de un atajo que envuelve contenido, ni los del HTML escrito a mano, ni los que ponen las plantillas. Sin la sección, el Markdown sale como antes (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.7.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §2, §7.2 y §8, decisión 109).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El &lt;code&gt;slug&lt;/code&gt; conoce &lt;code&gt;ș&lt;/code&gt; y &lt;code&gt;ț&lt;/code&gt;.&lt;/strong&gt; La tabla tenía &lt;code&gt;ş&lt;/code&gt; y &lt;code&gt;ţ&lt;/code&gt;, con cedilla, pero no las letras rumanas con coma debajo, así que &lt;code&gt;Ceaușescu&lt;/code&gt; daba &lt;code&gt;ceau-escu&lt;/code&gt;. Ahora da &lt;code&gt;ceausescu&lt;/code&gt; (§5.2).&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.6 compilan igual, fichero a fichero. Lo único que cambia es que &lt;code&gt;encabezados&lt;/code&gt; es una clave de &lt;code&gt;sitio.yml&lt;/code&gt; con significado, que &lt;code&gt;encabezados&lt;/code&gt; es ahora una variable de página que tapa un campo del front matter del mismo nombre, y las direcciones o ids de los textos con &lt;code&gt;ș&lt;/code&gt; o &lt;code&gt;ț&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.7.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.8.0</title>
    <link href="https://ehundu.com/novedades/0-8-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-8-0/</id>
    <summary>Los textos se ordenan sin contar ¿ ni ¡</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Los textos se ordenan sin contar &lt;code&gt;¿&lt;/code&gt; ni &lt;code&gt;¡&lt;/code&gt;.&lt;/strong&gt; Al ordenar con &lt;code&gt;orden()&lt;/code&gt;, los signos de apertura de pregunta y de exclamación ya no cuentan: «¿Cómo llegar?» se ordena como «Cómo llegar?», entre las «c», y no al principio de la lista, que es donde lo mandaba el orden de los caracteres. Dos textos que solo se distinguen por esos signos empatan y quedan en el orden que tenían. El resto de la puntuación cuenta como hasta ahora, y las mayúsculas, las tildes y la &lt;code&gt;ñ&lt;/code&gt; siguen igual (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.8.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §6.2, decisión 110).&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.7 compilan igual, fichero a fichero, salvo los que ordenen por un texto que lleve esos signos: en ellos solo cambia la posición de esas páginas en la lista.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.8.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.9.0</title>
    <link href="https://ehundu.com/novedades/0-9-0/"/>
    <updated>2026-10-10T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-9-0/</id>
    <summary>El CSS y el JavaScript de una página se pueden enlazar como un fichero</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;El CSS y el JavaScript de una página se pueden enlazar como un fichero.&lt;/strong&gt; &lt;code&gt;css_fichero()&lt;/code&gt; y &lt;code&gt;js_fichero()&lt;/code&gt; hacen con lo que la página ha declarado lo mismo que &lt;code&gt;css()&lt;/code&gt; y &lt;code&gt;js()&lt;/code&gt;, pero en lugar de incrustarlo escriben la etiqueta que enlaza un fichero con ese mismo contenido: &lt;code&gt;&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/css/3fa9c1d2ab.css&amp;quot;&amp;gt;&lt;/code&gt; o &lt;code&gt;&amp;lt;script src=&amp;quot;/js/7c01e4b9d3.js&amp;quot; defer&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;. El motor escribe el fichero en &lt;code&gt;salida/css/&lt;/code&gt; o &lt;code&gt;salida/js/&lt;/code&gt; con la huella de su contenido en el nombre (los 10 primeros caracteres del SHA-256), así que las páginas que declaran lo mismo lo comparten y el navegador puede guardarlo un año sin riesgo de servir uno viejo tras un cambio. Es para el CSS largo, que incrustado se repite en cada página. &lt;code&gt;css()&lt;/code&gt; y &lt;code&gt;js()&lt;/code&gt; no cambian, y el motor sigue sin minificar ni procesar nada (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.9.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §9.1, decisión 111).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;version(ruta)&lt;/code&gt;&lt;/strong&gt; da la dirección de un fichero de &lt;code&gt;publico/&lt;/code&gt; con la huella de su contenido, &lt;code&gt;/css/estilo.css?v=3fa9c1d2ab&lt;/code&gt;, para enlazar con una caducidad larga las tipografías, el JavaScript suelto y lo demás que no pasa por &lt;code&gt;css()&lt;/code&gt; (§7.3, decisión 112).&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.8 compilan igual, fichero a fichero. Solo cambia el texto del aviso que sale cuando una página declara CSS y ninguna plantilla lo escribe, que ahora nombra las dos formas.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.9.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.3.2</title>
    <link href="https://ehundu.com/novedades/0-3-2/"/>
    <updated>2026-09-29T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-3-2/</id>
    <summary>Las fechas, también en alemán</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Las fechas, también en alemán.&lt;/strong&gt; &lt;code&gt;fecha()&lt;/code&gt; escribe los meses y los días en alemán en las páginas en ese idioma, con mayúscula, como se escriben, y «18. März 2025» por defecto; &lt;code&gt;fecha(&apos;l j F Y&apos;)&lt;/code&gt; da «Dienstag 18 März 2025». Hasta ahora salían en inglés, y un sitio con &lt;code&gt;idioma: de&lt;/code&gt; avisaba de ello en cada compilación (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.2/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §15.9, decisión 89).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Una fecha imposible dentro de una lista de varias líneas ya no detiene la compilación.&lt;/strong&gt; Desde la 0.3.1, una fecha sin comillas que no existe, como &lt;code&gt;2026-13-01&lt;/code&gt;, se lee como el texto que es. Faltaba un caso: dentro de una lista o un mapa entre corchetes que ocupa varias líneas, la lectura del YAML entero seguía fallando, con un error que señalaba el corchete de cierre («Cerca de «]»»). Ahora se lee como texto también ahí, y la misma fecha escrita dentro de un texto no se toca (§4, decisión 88).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Un feed sin entradas ya no cambia en cada compilación.&lt;/strong&gt; Su fecha (&lt;code&gt;updated&lt;/code&gt;) era el momento de la compilación, así que un feed vacío salía distinto cada vez: se volvía a escribir en &lt;code&gt;salida/&lt;/code&gt; y se volvía a subir en cada despliegue, y la previsualización podía dar un feed distinto del de una compilación completa. Ahora lleva siempre el 1 de enero de 1970 en la zona horaria del sitio. Con entradas no cambia nada: lleva la fecha de la más reciente (§10.2, decisión 90).&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.3 compilan igual, fichero a fichero, salvo en dos cosas: en las páginas en alemán, las fechas que escribe &lt;code&gt;fecha()&lt;/code&gt; salen en alemán y no en inglés, y un feed sin entradas lleva la fecha de 1970.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.3.2&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.4.0</title>
    <link href="https://ehundu.com/novedades/0-4-0/"/>
    <updated>2026-09-29T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-4-0/</id>
    <summary>El PDF de una página</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;El PDF de una página.&lt;/strong&gt; Una página con &lt;code&gt;pdf: sí&lt;/code&gt; sale también como PDF, además de como HTML. Lo maqueta su propia plantilla, &lt;code&gt;&amp;lt;plantilla&amp;gt;.pdf.twig&lt;/code&gt;, con los mismos datos que la de la web pero con HTML y CSS sencillos (el PDF no admite &lt;code&gt;flex&lt;/code&gt; ni rejilla). Sale en la dirección de la página con &lt;code&gt;.pdf&lt;/code&gt; en lugar de la barra final, y las plantillas lo enlazan con &lt;code&gt;pagina.pdf&lt;/code&gt;. Es una salida más de la compilación, declarada por la página, y no un procesador sobre el HTML de otra (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.4.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §10.3, decisiones 96 y 97).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El PDF lo genera dompdf, que es opcional.&lt;/strong&gt; Es PHP puro, sin programas externos ni GD, y se instala con &lt;code&gt;composer require dompdf/dompdf&lt;/code&gt; (hace falta la extensión &lt;code&gt;dom&lt;/code&gt;). El motor solo lo pide si una página declara &lt;code&gt;pdf: sí&lt;/code&gt;; si falta, la compilación se detiene con un error que lo dice. El &lt;code&gt;ehundu.phar&lt;/code&gt; no lo trae: para generar PDF hay que usar el motor por Composer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El mismo proyecto da siempre los mismos bytes.&lt;/strong&gt; El PDF lleva la fecha de la página (o el 1 de enero de 1970 si no tiene), un identificador sacado de su contenido y la caché de tipografías en la carpeta temporal del sistema, así que no escribe fuera de &lt;code&gt;salida/&lt;/code&gt; y solo lee de &lt;code&gt;publico/&lt;/code&gt;. Un PDF que no cambia no se vuelve a subir en el despliegue, y la compilación incremental lo tiene en cuenta: lo que dompdf lee de &lt;code&gt;publico/&lt;/code&gt; cuenta como dependencia de la página.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Los ficheros PHP detienen la compilación.&lt;/strong&gt; Un fichero PHP en &lt;code&gt;publico/&lt;/code&gt; o en &lt;code&gt;contenido/&lt;/code&gt; (&lt;code&gt;.php&lt;/code&gt;, &lt;code&gt;.php3&lt;/code&gt; a &lt;code&gt;.php8&lt;/code&gt;, &lt;code&gt;.pht&lt;/code&gt;, &lt;code&gt;.phtml&lt;/code&gt;, &lt;code&gt;.phar&lt;/code&gt;, y también &lt;code&gt;foto.php.jpg&lt;/code&gt;, que algunos servidores ejecutan) ya no se copia: el error dice cuál es. Un sitio estático no tiene por qué llevarlos, y en uno infectado son lo primero que se cuela (§9, decisión 98). &lt;strong&gt;Es lo único que puede impedir compilar un sitio de la 0.3.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El formato, para los editores.&lt;/strong&gt; El esquema (§11) admite una lista dentro de una lista de campos simples, campos &lt;code&gt;traducible&lt;/code&gt; con un texto por idioma, el tipo &lt;code&gt;seleccion-multiple&lt;/code&gt; con las opciones en &lt;code&gt;datos/&lt;/code&gt;, &lt;code&gt;recuerda&lt;/code&gt; (lo que un editor guarda de las filas de una lista) y ficheros de &lt;code&gt;datos/&lt;/code&gt; editables desde el esquema (§11.1 a §11.5, decisiones 91 a 95). El motor no lee ninguno de estos atributos: son cosas de los editores, y el motor solo lee lo que ellos escriben en el proyecto.&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.3 compilan igual, fichero a fichero, salvo que lleven ficheros PHP en &lt;code&gt;publico/&lt;/code&gt; o &lt;code&gt;contenido/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.4.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.3.1</title>
    <link href="https://ehundu.com/novedades/0-3-1/"/>
    <updated>2026-09-28T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-3-1/</id>
    <summary>Una fecha imposible sin comillas ya no detiene la compilación</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Una fecha imposible sin comillas ya no detiene la compilación.&lt;/strong&gt; &lt;code&gt;fecha: 2026-13-01&lt;/code&gt; o &lt;code&gt;publicar: 2026-01-32&lt;/code&gt;, sin comillas, hacían fallar la lectura del YAML entero («El YAML no es válido»). Ahora avisan con fichero y línea y el campo cuenta como si no estuviera, igual que ya pasaba con &lt;code&gt;fecha: 2025-02-31&lt;/code&gt;, en el front matter y en &lt;code&gt;_datos.yml&lt;/code&gt;. En un campo que no es reservado, esa fecha llega a la plantilla como el texto que es, sin aviso (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.1/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §4, decisión 88).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El despliegue por FTP ya no se atasca en ciertos tamaños de fichero.&lt;/strong&gt; Algunos servidores FTPS cortan siempre la subida de algunos tamaños exactos de fichero, sea cual sea su contenido, y repetirla no servía de nada. Ahora, como último recurso, un fichero que no sube entero se sube en dos partes, la segunda añadida al final (APPE), y se comprueba que llega con su tamaño; queda dicho en un aviso (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.1/docs/despliegue.md&quot;&gt;&lt;code&gt;docs/despliegue.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El esquema para un editor, en el formato.&lt;/strong&gt; &lt;code&gt;esquema.json&lt;/code&gt; (§11) describe ya lo que necesita un editor de verdad: carpetas con comodín y en lista, campos con ayuda y con rol, el tipo &lt;code&gt;enlace&lt;/code&gt;, grupos y listas de un nivel, el nombre de fichero de las páginas nuevas y el cuerpo con su rótulo (decisiones 82 a 87). El motor no lo lee; es para los programas que lo incrustan.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La marca, aparte de la licencia.&lt;/strong&gt; La licencia MIT cubre el código y la documentación, pero no el logotipo ni las ilustraciones de &lt;code&gt;marca/&lt;/code&gt; (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.1/marca/README.md&quot;&gt;&lt;code&gt;marca/README.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;); las ilustraciones tampoco van en el paquete de Composer. Ehundu es un proyecto de Paigar.&lt;/p&gt;
&lt;p&gt;Los sitios de la 0.3 compilan igual, fichero a fichero, sin cambiar nada.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.3.1&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.2.0</title>
    <link href="https://ehundu.com/novedades/0-2-0/"/>
    <updated>2026-09-27T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-2-0/</id>
    <summary>Orden y donde también filtran por la dirección y la ruta de la página</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;&lt;code&gt;orden&lt;/code&gt; y &lt;code&gt;donde&lt;/code&gt; ven también &lt;code&gt;url&lt;/code&gt; y &lt;code&gt;ruta&lt;/code&gt;&lt;/strong&gt;, con los mismos valores que las plantillas: &lt;code&gt;donde(&apos;url&apos;, &apos;/contacto/&apos;)&lt;/code&gt; encuentra una página aunque su URL salga de la ruta del fichero y no esté escrita en el front matter. &lt;code&gt;contenido&lt;/code&gt; no cuenta, para no convertir el cuerpo de todas las páginas de la lista (decisión 71 de &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.2.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El despliegue guarda en el manifiesto la &lt;code&gt;url&lt;/code&gt; del sitio&lt;/strong&gt; y se detiene antes de tocar nada, también al simular, si el destino es de otro sitio: con varios sitios en el mismo servidor, una &lt;code&gt;ruta&lt;/code&gt; equivocada borraría lo que subió el otro. &lt;code&gt;--todo&lt;/code&gt; confirma que es el mismo sitio con otra dirección. Un manifiesto de la 0.1, sin &lt;code&gt;url&lt;/code&gt;, se acepta y la gana en el siguiente despliegue (decisión 70; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.2.0/docs/despliegue.md&quot;&gt;&lt;code&gt;docs/despliegue.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.2.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.3.0</title>
    <link href="https://ehundu.com/novedades/0-3-0/"/>
    <updated>2026-09-27T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-3-0/</id>
    <summary>Sitios en varios idiomas</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Sitios en varios idiomas&lt;/strong&gt;, con un fichero por idioma en el mismo árbol: &lt;code&gt;contacto.md&lt;/code&gt; y &lt;code&gt;contacto.eu.md&lt;/code&gt; son la misma página en castellano y en euskera, sin declararlo en ningún sitio. Los idiomas se declaran en &lt;code&gt;idiomas&lt;/code&gt; de &lt;code&gt;sitio.yml&lt;/code&gt;, y el primero es el predeterminado. Un sitio en un solo idioma no escribe nada de esto y compila igual que con la 0.2 (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.0/docs/formato.md&quot;&gt;&lt;code&gt;docs/formato.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, §15, decisiones 72 a 81).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cada idioma con su prefijo en la URL&lt;/strong&gt;, que Ehundu pone también a la URL escrita en &lt;code&gt;url&lt;/code&gt; y a la de un patrón: &lt;code&gt;url: /instalazioak/&lt;/code&gt; en &lt;code&gt;instalaciones.eu.md&lt;/code&gt; da &lt;code&gt;/eu/instalazioak/&lt;/code&gt;, y el patrón de un &lt;code&gt;_datos.yml&lt;/code&gt; vale para todos los idiomas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Las traducciones, en las plantillas&lt;/strong&gt;: &lt;code&gt;pagina.idioma&lt;/code&gt;, &lt;code&gt;pagina.traducciones&lt;/code&gt; (las versiones publicadas de la página, ella incluida) y &lt;code&gt;sitio.idiomas&lt;/code&gt;, con lo que una plantilla escribe los &lt;code&gt;hreflang&lt;/code&gt; y un selector de idioma que nunca enlaza a una página que no existe. Traducir solo una parte del sitio es lo normal: no hay respaldo automático ni avisos por las traducciones que faltan.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Colecciones y datos de cada idioma.&lt;/strong&gt; &lt;code&gt;coleccion()&lt;/code&gt; da las páginas del idioma de la página que la pide, o de otro con &lt;code&gt;coleccion(&apos;blog&apos;, &apos;eu&apos;)&lt;/code&gt; o &lt;code&gt;&apos;todos&apos;&lt;/code&gt;. &lt;code&gt;datos/menus.eu.yml&lt;/code&gt; se mezcla con &lt;code&gt;datos/menus.yml&lt;/code&gt; en las páginas en euskera, &lt;code&gt;_datos.eu.yml&lt;/code&gt; va encima del &lt;code&gt;_datos.yml&lt;/code&gt; de su carpeta, y lo que no se traduce de una página puede ir en un &lt;code&gt;.yml&lt;/code&gt; con su nombre, común a todas sus versiones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;fecha()&lt;/code&gt; en el idioma de la página&lt;/strong&gt;, con castellano, euskera («2025eko martxoaren 18a») e inglés; también en los sitios de un solo idioma que no están en castellano.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Un sitemap con todos los idiomas y un feed por idioma&lt;/strong&gt; (&lt;code&gt;/feed.xml&lt;/code&gt;, &lt;code&gt;/eu/feed.xml&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;La compilación incremental de &lt;code&gt;ehundu servir&lt;/code&gt; sabe qué traducciones mira cada página y sigue dando siempre lo mismo que una compilación completa (&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/blob/v0.3.0/docs/compilacion.md&quot;&gt;&lt;code&gt;docs/compilacion.md&lt;/code&gt;&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cambios para los sitios de la 0.2:&lt;/strong&gt; &lt;code&gt;sitio.idioma&lt;/code&gt; vale &lt;code&gt;es&lt;/code&gt; si &lt;code&gt;sitio.yml&lt;/code&gt; no lo dice, y el feed lleva siempre &lt;code&gt;xml:lang&lt;/code&gt;; un &lt;code&gt;.yml&lt;/code&gt; dentro de &lt;code&gt;contenido/&lt;/code&gt; con el nombre de una página deja de copiarse a &lt;code&gt;salida/&lt;/code&gt;, porque son sus campos comunes; &lt;code&gt;idioma&lt;/code&gt; pasa a ser un campo reservado; y dos ficheros de la misma página en el mismo idioma (&lt;code&gt;contacto.md&lt;/code&gt; y &lt;code&gt;contacto.twig&lt;/code&gt;) detienen la compilación.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.3.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ehundu 0.1.0</title>
    <link href="https://ehundu.com/novedades/0-1-0/"/>
    <updated>2026-09-26T00:00:00+02:00</updated>
    <id>https://ehundu.com/novedades/0-1-0/</id>
    <summary>Primera versión utilizable de Ehundu</summary>
    <content type="html">&lt;p&gt;Primera versión utilizable de Ehundu: compila sitios de Markdown y Twig, los previsualiza con compilación incremental y los publica en una carpeta, por FTP, por SFTP o en un almacenamiento compatible con S3.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ehundu/ehundu/releases/tag/v0.1.0&quot;&gt;Esta versión en GitHub&lt;span class=&quot;oculto&quot;&gt; (se abre en una pestaña nueva)&lt;/span&gt;&lt;/a&gt;, con el &lt;code&gt;ehundu.phar&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
</feed>
