pagetop/helpers/pagetop-build/README.md
Manuel Cillero f27790c3a2 (build): Introduce nuevas funciones de build
Añade `compile_scss()`, `copy_dir()`, `copy_file()`,
`copy_file_replacing()` y `minify_js()` para preparar activos en
`build.rs`. Adopta el patrón `assets/ -> static/`: los archivos
estáticos se mueven a `assets/` y `static/` se añade a `.gitignore`. Los
`build.rs` de *pagetop* y *pagetop-htmx* se actualizan con el nuevo
patrón.  La documentación del módulo se reescribe para reflejar los
nuevos cambios.
2026-06-14 21:18:54 +02:00

6.4 KiB

PageTop Build

Genera o prepara archivos estáticos para servirlos o incluirlos en un proyecto PageTop.

Doc API Crates.io Descargas Licencia

Sobre PageTop

PageTop es un entorno de desarrollo que reivindica la esencia de la web clásica para crear soluciones web SSR (renderizadas en el servidor) modulares, extensibles y configurables, basadas en HTML, CSS y JavaScript.

Guía rápida

La convención recomendada para extensiones, temas o aplicaciones basadas en PageTop es separar los archivos fuente de los generados siguiendo el patrón assets/ -> static/:

  • assets/ - archivos versionados en el repositorio, por ejemplo archivos SCSS, JavaScript de terceros, fuentes, etc. Todo lo que hay aquí se sube al repositorio y será la fuente para generar el directorio final static/.
  • static/ - archivos generados en tiempo de compilación a partir de assets/. Se añade a .gitignore y nunca se sube al repositorio.
  • build.rs - orquesta la transformación: genera static/ desde assets/ para servirlos o incluirlos en el proyecto.

Durante el desarrollo, static/ existe en disco y los archivos se sirven desde ahí. En producción, el directorio no existe y los recursos salen del binario. La macro serve_static_files! gestiona esta dualidad de forma transparente.

Funciones de transformación

Estas funciones se usan en el build.rs de cada proyecto para generar static/ a partir de assets/. Todas crean el directorio padre del destino si no existe y devuelven io::Result<()> para poder propagarse con ? en caso de error.

  • compile_scss() - compila un archivo SCSS a CSS minificado.
  • copy_dir() - copia recursivamente un directorio completo. Útil para copiar todos los archivos de assets/ o de un subdirectorio a static/ sin transformación.
  • copy_file() - copia un archivo al destino.
  • copy_file_replacing() - copia un archivo aplicando una lista de sustituciones de texto en su contenido; útil para actualizar referencias internas (p.ej. sourceMappingURL) al renombrar archivos.
  • minify_js() - minifica un archivo JavaScript.

Incluir los archivos estáticos en el proyecto

Una vez generado static/, usaremos StaticFilesBundle para incluir su contenido en el binario. Se pueden crear tantos paquetes de recursos como sea necesario, siempre que tengan nombres distintos:

use pagetop_build::StaticFilesBundle;

fn main() -> std::io::Result<()> {
    StaticFilesBundle::from_dir("./static/css", None)
        .with_name("app_css")
        .build()?;
    StaticFilesBundle::from_dir("./static/fonts", None)
        .with_name("app_fonts")
        .build()
}

Si es necesario excluir algunos archivos del paquete de recursos (p. ej. los archivos .map que no son necesarios en producción), se puede pasar una función de filtro:

use pagetop_build::StaticFilesBundle;
use std::path::Path;

fn main() -> std::io::Result<()> {
    StaticFilesBundle::from_dir("./static/js", Some(only_js))
        .with_name("app_js")
        .build()
}

fn only_js(path: &Path) -> bool {
    path.extension().map_or(false, |ext| ext == "js")
}

Cada paquete de recursos genera un archivo .rs en OUT_DIR. No es necesario acceder a él directamente: el nombre asignado con .with_name() se usa como identificador en serve_static_files! para configurar la ruta del servicio:

use pagetop::prelude::*;

pub struct MyExtension;

impl Extension for MyExtension {
    fn configure_router(&self, mut router: Router) -> Router {
        serve_static_files!(router, ["./static/css", app_css] => "/public/css");
        router
    }
}

Ejemplo completo

use pagetop_build::StaticFilesBundle;
use pagetop_build::{compile_scss, copy_file, copy_file_replacing, minify_js};
use std::path::Path;

fn main() -> std::io::Result<()> {
    // Regenera `static/` desde cero sólo si hay cambios en `assets/`.
    println!("cargo:rerun-if-changed=assets");
    let _ = std::fs::remove_dir_all("static");

    // Genera `static/` a partir de `assets/`.
    compile_scss("assets/main.scss", "static/css/main.min.css")?;
    copy_file("assets/fonts/icon.woff2", "static/fonts/icon.woff2")?;
    copy_file_replacing(
        "assets/lib.min.js",
        "static/js/app.min.js",
        &[("lib.min.js.map", "app.min.js.map")],
    )?;
    minify_js("assets/shell.js", "static/js/shell.min.js")?;

    // Prepara los paquetes de recursos para incluir en el proyecto.
    StaticFilesBundle::from_dir("./static/css", None).with_name("app_css").build()?;
    StaticFilesBundle::from_dir("./static/js", Some(only_js)).with_name("app_js").build()?;
    StaticFilesBundle::from_dir("./static/fonts", None).with_name("app_fonts").build()
}

// Los `.map` no se incluyen, se servirán desde disco durante el desarrollo.
fn only_js(path: &Path) -> bool {
    path.extension().map_or(false, |ext| ext == "js")
}

Advertencia

PageTop es un proyecto personal para aprender Rust y conocer su ecosistema. Su API está sujeta a cambios frecuentes. No se recomienda su uso en producción, al menos hasta que se libere la versión 1.0.0.

Licencia

El código está disponible bajo una doble licencia:

Puedes elegir la licencia que prefieras. Este enfoque de doble licencia es el estándar de facto en el ecosistema Rust.