Ir al contenido
Código en GitHub (se abre en una pestaña nueva)

Documentación · Referencia · El formato de un proyecto

10. Salida

Lo que sale a salida/: el sitemap, el feed, los PDF y la compilación por lotes e incremental.

Versión 0.16

En salida/ van las páginas, lo que se copia de publico/ y de contenido/, el PDF de las páginas que lo piden (§10.3) y los ficheros que genera el motor: sitemap.xml; feed.xml, si el sitio lo pide (uno por idioma en un sitio en varios, §15.10); y, si una página los declara y usa css_fichero() o js_fichero(), los paquetes css/<huella>.css y js/<huella>.js (§9.1). Nada más: los añadidos van como ficheros normales dentro de contenido/ o de publico/. El manifiesto del despliegue (.ehundu.json) se escribe en el destino, no en salida/: lleva su propia versión ("ehundu": 1), y un Ehundu 1.x lee el que dejó cualquier versión anterior, también el de un despliegue de la 0.x. Lo que hay en .ehundu/ (memoria.bin, lote.json y sus cerrojos) no se promete: es del motor y se puede tirar (§1). Una página con url: /404.html se escribe como 404.html, igual que cualquier otra.

Si dos cosas van al mismo fichero de salida (dos páginas, una página y un fichero, un fichero de publico/ y otro de contenido/), el build se detiene con un error que nombra las dos. Las mayúsculas no cuentan.

Si el proyecto tiene su propio /sitemap.xml o /feed.xml, como página o como fichero, gana el del proyecto y el motor no genera el suyo.

Una compilación completa deja salida/ igual que el sitio construido: borra lo que sobra de compilaciones anteriores, para que no se despliegue, y escribe o copia solo lo que ha cambiado, para que lo que no cambia conserve su fecha. Solo toca lo que hay dentro de salida/, y solo después de haber construido todo sin errores: si una plantilla falla, salida/ se queda como estaba.

La previsualización compila de forma incremental: rehace solo las páginas a las que afecta cada cambio. El resultado es siempre el mismo que el de una compilación completa; cómo se consigue está en docs/compilacion.md.

ehundu desplegar compila y publica salida/ en el destino de sitio.yml (§2): sube lo nuevo o cambiado y borra solo lo que subió Ehundu y ya no se genera. Los detalles están en docs/despliegue.md.

10.1 sitemap.xml

Lleva las páginas HTML de la colección todo (las publicadas, con URL y sin listada: no; §6), en el orden de coleccion('todo') (§6.1; en un sitio en varios idiomas se intercalan por fecha), salvo /404.html; en un sitio en varios idiomas, las de todos ellos, sin la 404.html de ninguno (§15.10). Una página es HTML si su URL acaba en / o en .html: robots.txt o un XML no entran. Cada una lleva su dirección completa, la url del sitio más la de la página, y <lastmod> con su fecha (AAAA-MM-DD) en la zona horaria del sitio, solo si la tiene.

10.2 feed.xml

Se genera en formato Atom si sitio.yml tiene una sección feed:

feed:
  coleccion: blog     # de dónde salen las entradas; obligatorio
  limite: 20          # cuántas, las más recientes; 20 si no se indica
  titulo: Novedades   # el nombre del sitio si no se indica

Cada entrada lleva el título, la dirección completa, la fecha, la descripcion como resumen si la tiene, y el contenido completo (el cuerpo de la página, pagina.contenido, no la página con su plantilla), con el href y el src que empiezan por / pasados a direcciones completas (también las de cada srcset, §2), porque un lector de feeds no sabe de qué sitio vienen; solo se reescriben los atributos entre comillas dobles. Las entradas van de la más reciente a la más antigua y, a igual fecha, por ruta. Una página de la colección sin fecha no entra, y se avisa: Atom exige una fecha en cada entrada. Los fragmentos (url: false) no entran. Si ninguna página lleva la etiqueta de coleccion, se avisa, porque seguramente está mal escrita, y el feed sale vacío.

Un lector de feeds identifica cada entrada por su <id>, que es la dirección completa de la entrada, y el del feed es la dirección de la raíz de su idioma; no cambian con el tiempo. El autor, <author><name>, es el nombre del sitio. El feed lleva además su propio <link rel="self"> y el xml:lang de su idioma. No hay una clave para otro autor; añadirla más adelante es crecer (decisión 138).

La fecha del feed es la de su entrada más reciente. Un feed sin entradas lleva siempre el 1 de enero de 1970, en la zona horaria del sitio: Atom exige una fecha también ahí, y con una fija el feed no cambia de una compilación a otra, ni se vuelve a escribir ni a subir mientras siga vacío.

En un sitio en varios idiomas hay un feed por idioma (§15.10).

10.3 PDF

Una página con pdf: sí sale además como PDF. Es una salida más de esa misma página, con su propia plantilla, y no una conversión de su HTML: un PDF se maqueta distinto de una web, y las dos comparten los datos, no el marcado. Se pide página a página, y no para todo el sitio, porque cada PDF cuesta unas décimas de segundo.

  • La página. Con plantilla: false no hay plantilla de PDF que buscar, y pdf: sí detiene el build.
  • La plantilla es la de la página con .pdf antes de la extensión: con plantilla: menu, plantillas/menu.pdf.twig. Recibe las mismas variables que la de la página (sitio, datos y pagina, §7.2) y escribe un documento HTML completo. Si no existe, el build se detiene, como con la de la página (§7.1).
  • La dirección es la de la página con la barra final cambiada por .pdf: /menu-del-dia/ da /menu-del-dia.pdf, y la portada, /index.pdf. Si la URL acaba en .html, esa extensión pasa a .pdf. Una página cuya URL no acaba en / ni en .html, o que es un fragmento (url: false), no puede tener PDF: se avisa y se ignora el campo. Si el fichero coincide con otro de la salida, el build se detiene (§10). Las plantillas lo enlazan con pagina.pdf, que es su dirección o queda vacío; también pagina.traducciones.eu.pdf.
  • Cada idioma da su PDF, en su idioma, con la plantilla y los datos de su página. En un sitio en varios idiomas, pdf: sí se escribe una vez, en el .yml común de la página (§15.7).
  • Lo que admite. CSS 2.1 con algo de CSS3, y nada de flex ni de rejilla. La hoja es A4 salvo que el CSS diga otra cosa con @page { size: … }. El CSS se declara con css('…') y {{ css() }} en la plantilla del PDF y en sus parciales, como en una página (§9.1), pero sin css_fichero() (que en una plantilla de PDF queda vacío, sin aviso); el campo css del front matter no cuenta, porque es el de la web. Las direcciones que empiezan por / se leen de publico/; nada remoto se carga, y lo que dompdf no entiende (una propiedad de CSS, una imagen que no encuentra) sale como aviso, con la plantilla como fichero. Las imágenes pueden ser SVG, JPEG o PNG: los SVG no necesitan ninguna extensión de PHP, y las PNG con transparencia, GD. Las tipografías son las tres básicas de un PDF, sans-serif (Helvetica), serif (Times) y monospace (Courier), que cubren el juego de caracteres de Windows-1252 (castellano, euskera, inglés y alemán); no se pueden usar otras.
  • Reproducible. El PDF lleva como fecha de creación y de modificación la fecha de la página, a medianoche en la zona horaria del sitio, o el 1 de enero de 1970 si no la tiene (como el feed vacío, §10.2); su título es el titulo de la página, y su identificador sale de su contenido. La misma página, con la misma versión de la biblioteca, da los mismos bytes: si no, cada compilación cambiaría todos los PDF y el despliegue volvería a subirlos. Vale lo de §7.3 para random() y la fecha del momento.
  • Si falla. Si un PDF no se puede generar, el build se detiene con el error y la plantilla, por lo mismo que con un front matter roto (§4): seguir sin él haría que el despliegue borrara el que estaba publicado.
  • La biblioteca es dompdf, que es PHP puro, sin binarios, y necesita las extensiones dom y mbstring. Es una dependencia opcional: un sitio sin pdf: sí no la necesita, y con pdf: sí y sin ella el build se detiene y dice cómo instalarla. El ehundu.phar (el ejecutable de un solo fichero que acompaña a cada versión) no la trae: para generar PDF hay que usar el motor con Composer. Su caché de tipografías va a la carpeta temporal del sistema, no junto a su código: el motor no escribe en vendor/ ni en el proyecto fuera de salida/ y .ehundu/.

En la previsualización, un PDF se rehace cuando cambia algo de lo que usó su plantilla o de lo que lee de publico/, igual que su página (compilacion.md). El sitemap no lleva PDF (§10.1).

10.4 Compilación por lotes (no se promete)

El motor trae una compilación por lotes (CompiladorPorLotes), que reparte una compilación completa en llamadas con un presupuesto de tiempo y guarda por dónde va en .ehundu/lote.json (§1). No se promete (decisión 128): CompiladorPorLotes y CompiladorPorLotesEnProceso son @internal y pueden cambiar o desaparecer en cualquier versión. Lo que se promete para repartir una compilación en llamadas con un presupuesto de tiempo es avanzar de la compilación incremental (§10.5). De este apartado, del formato solo vale que .ehundu/ es del motor (§1) y que lote.json es un nombre que el motor puede usar en él. Cómo funciona se cuenta en compilacion.md.

El despliegue (despliegue.md) también se reparte con un presupuesto de tiempo, y eso sí se promete (despliegue.md).

10.5 Compilación incremental entre procesos

La compilación incremental (compilacion.md) recuerda la última compilación y rehace solo lo que un cambio afecta. La previsualización la tiene dentro de su proceso; CompiladorIncrementalEnProceso (biblioteca.md) la ofrece a quien incruste el motor, guardando lo que recuerda en .ehundu/memoria.bin (§1) para que la aproveche una compilación de otro proceso: guardar un cambio en un sitio de mil páginas rehace unas pocas. La consola no la necesita y no la ofrece.

  • El mismo resultado. Como toda compilación incremental: los mismos ficheros y los mismos avisos que una compilación completa del mismo proyecto. Si no se puede asegurar, se rehace todo.
  • Qué se guarda. Lo que la incremental ya recuerda, tal cual: lo leído del proyecto, las huellas de plantillas/, parciales/ y publico/, y de cada página su HTML (y su PDF), lo que usó y los cuerpos convertidos. Pesa del orden de salida/ más lo leído y se carga en una fracción de segundo (las medidas, en compilacion.md). No lleva nada que no esté en el proyecto.
  • Cuándo no vale. Si falta, está dañada o a medio escribir, es de otra versión de la memoria o del motor, o es de otro proyecto (otra ruta), se ignora y se hace una compilación completa. Nunca es un error. Cambiar de versión del motor, de alguna de sus dependencias o de PHP, o tocar el código del motor, hace que la primera compilación sea completa: lo que puede cambiar el HTML que sale no se mezcla con lo que se recordó con otra cosa.
  • Cuando no cabe en la memoria de PHP. Cargar la memoria guardada y rehacer lo que cambie pide, de la memoria de PHP, del orden de tres veces lo que mide el fichero más 16 MB (las medidas, en compilacion.md; la cifra no se promete). Si eso no cabe en el memory_limit de PHP, no se intenta: compilar hace una compilación completa sin leer ni tocar la memoria y lo dice en un aviso, con cuánto ocupa lo guardado, cuánto pide y a cuánto subir el límite; avanzar, que sin la memoria no puede repartir el trabajo, falla con un ErrorDeProyecto que dice lo mismo. Es la única vez que una memoria que existe y es válida impide hacer lo que se pide, y en cuanto el límite da para ella vuelve a servir. Sin memory_limit (-1) no se comprueba.
  • Cuándo se guarda. Solo cuando una llamada sale bien. Un error del proyecto deja la memoria y salida/ como estaban, y el siguiente cambio vuelve a aprovecharla.
  • Por presupuesto. avanzar hace lo mismo con un presupuesto de segundos: rehace páginas hasta agotarlo, siempre al menos una (la que ha empezado se termina, así que una llamada puede tardar más que el presupuesto), guarda lo hecho y devuelve un Lote que dice si está completo con completo(). La llamada siguiente sigue: lo que ya está hecho se aprovecha por la misma regla que cualquier otro cambio, y a una memoria con la mitad de las páginas solo le faltan páginas, así que no hace falta ningún estado aparte, y si el proyecto cambia entre dos llamadas solo se rehace lo que el cambio afecta, no todo. Cada página se escribe al hacerla, así que hasta que el Lote está completo salida/ mezcla páginas nuevas y viejas: es lo contrario de la garantía de la compilación completa, que ante un error deja salida/ como estaba, y por eso nada debe publicarse mientras completo() sea falso. Completo, salida/ y los avisos son los de una compilación completa, también si se ha cortado y retomado tantas veces como se quiera.
  • Una llamada a la vez. Una llamada de avanzar que llega mientras otra está avanzando no espera ni estropea nada: devuelve el estado, con ocupado(). Una de compilar espera a que la otra acabe, hasta un minuto, y entonces lanza un ErrorDeProyecto en vez de quedarse esperando.
  • El primer arranque (sin memoria) es una compilación completa: con avanzar se reparte en las llamadas que haga falta.

Ver este documento en GitHub (se abre en una pestaña nueva)