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: falseno hay plantilla de PDF que buscar, ypdf: sídetiene el build. - La plantilla es la de la página con
.pdfantes de la extensión: conplantilla: menu,plantillas/menu.pdf.twig. Recibe las mismas variables que la de la página (sitio,datosypagina, §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 conpagina.pdf, que es su dirección o queda vacío; tambiénpagina.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.ymlcomún de la página (§15.7). - Lo que admite. CSS 2.1 con algo de CSS3, y nada de
flexni de rejilla. La hoja es A4 salvo que el CSS diga otra cosa con@page { size: … }. El CSS se declara concss('…')y{{ css() }}en la plantilla del PDF y en sus parciales, como en una página (§9.1), pero sincss_fichero()(que en una plantilla de PDF queda vacío, sin aviso); el campocssdel front matter no cuenta, porque es el de la web. Las direcciones que empiezan por/se leen depublico/; 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) ymonospace(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
fechade 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 eltitulode 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 pararandom()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
domymbstring. Es una dependencia opcional: un sitio sinpdf: síno la necesita, y conpdf: síy sin ella el build se detiene y dice cómo instalarla. Elehundu.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 envendor/ni en el proyecto fuera desalida/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/ypublico/, y de cada página su HTML (y su PDF), lo que usó y los cuerpos convertidos. Pesa del orden desalida/más lo leído y se carga en una fracción de segundo (las medidas, encompilacion.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 elmemory_limitde PHP, no se intenta:compilarhace 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 unErrorDeProyectoque 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. Sinmemory_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.
avanzarhace 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 unLoteque dice si está completo concompleto(). 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 elLoteestá completosalida/mezcla páginas nuevas y viejas: es lo contrario de la garantía de la compilación completa, que ante un error dejasalida/como estaba, y por eso nada debe publicarse mientrascompleto()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
avanzarque llega mientras otra está avanzando no espera ni estropea nada: devuelve el estado, conocupado(). Una decompilarespera a que la otra acabe, hasta un minuto, y entonces lanza unErrorDeProyectoen vez de quedarse esperando. - El primer arranque (sin memoria) es una compilación completa: con
avanzarse reparte en las llamadas que haga falta.