tuono_lib 0.19.7

Superfast React fullstack framework
Documentation
use crate::config::GLOBAL_CONFIG;
use crate::manifest::MANIFEST;
use crate::mode::{GLOBAL_MODE, Mode};
use erased_serde::Serialize;
use serde::Serialize as SerdeSerialize;
use tuono_internal::config::ServerConfig;

use crate::request::{Location, Request};

#[derive(SerdeSerialize)]
/// This is the payload sent to the client for hydration
pub struct Payload<'a> {
    location: Location,
    data: &'a dyn Serialize,
    mode: Mode,
    #[serde(rename(serialize = "jsBundles"))]
    js_bundles: Option<Vec<String>>,
    #[serde(rename(serialize = "cssBundles"))]
    css_bundles: Option<Vec<String>>,
    #[serde(rename(serialize = "devServerConfig"))]
    dev_server_config: Option<&'a ServerConfig>,
}

impl<'a> Payload<'a> {
    pub fn new(req: &'a Request, data: &'a dyn Serialize) -> Payload<'a> {
        let config = GLOBAL_CONFIG
            .get()
            .expect("Failed to load the current config");

        let mode = *GLOBAL_MODE.get().expect("Failed to load the current mode");

        let dev_server_config = if mode == Mode::Dev {
            Some(&config.server)
        } else {
            None
        };

        Payload {
            location: req.location(),
            data,
            mode,
            js_bundles: None,
            css_bundles: None,
            dev_server_config,
        }
    }

    pub fn client_payload(&mut self) -> Result<String, serde_json::Error> {
        if self.mode == Mode::Prod {
            self.add_bundle_sources();
        }
        serde_json::to_string(&self)
    }

    fn add_bundle_sources(&mut self) {
        let manifest = MANIFEST.get().expect("Manifest not loaded");
        let bundles = manifest.get_bundle_from_pathname(self.location.pathname());
        self.js_bundles = Some(bundles.js_files);
        self.css_bundles = Some(bundles.css_files);
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use crate::manifest::ViteManifest;
    use axum::http::Uri;

    const MANIFEST_EXAMPLE: &str = r#"{
        "../src/routes/index.tsx": {
            "file": "assets/index-D-yFyCZo.js",
            "name": "index",
            "src": "../src/routes/index.tsx",
            "isDynamicEntry": true,
            "imports": [
                "client-main.tsx"
            ],
            "css": [
                "assets/index-CynfArjF.css"
            ]
        },
        "../src/routes/pokemons/[pokemon].tsx": {
            "file": "assets/_pokemon_-DlFInatQ.js",
            "name": "_pokemon_",
            "src": "../src/routes/pokemons/[pokemon].tsx",
            "isDynamicEntry": true,
            "imports": [
                "client-main.tsx"
            ],
            "css": [
                "assets/_pokemon_-BcJZaQaO.css"
            ]
        },
        "../src/routes/pokemons/__layout.tsx": {
            "file": "assets/__layout-BFnT3M7X.js",
            "name": "__layout",
            "src": "../src/routes/pokemons/__layout.tsx",
            "isDynamicEntry": true,
            "imports": [
                "client-main.tsx"
            ],
            "css": [
                "assets/__layout-CXGGqNw5.css"
            ]
        },
        "client-main.tsx": {
            "file": "assets/client-main-B9g1NVV7.js",
            "name": "client-main",
            "src": "client-main.tsx",
            "isEntry": true,
            "dynamicImports": [
                "../src/routes/pokemons/__layout.tsx",
                "../src/routes/index.tsx",
                "../src/routes/pokemons/[pokemon].tsx"
            ],  
            "css": [
                "assets/client-main-BS7N-NIa.css"
            ]
        } 
    }"#;

    fn prepare_payload(uri: Option<&str>, mode: Mode) -> Payload {
        let manifest_mock = serde_json::from_str::<ViteManifest>(MANIFEST_EXAMPLE)
            .expect("Failed to parse the manifest example");
        MANIFEST.get_or_init(|| manifest_mock.into());

        let uri = uri
            .unwrap_or("http://localhost:3000/")
            .parse::<Uri>()
            .unwrap();

        let location = Location::from(uri);

        Payload {
            location,
            data: &None::<Option<()>>,
            mode,
            js_bundles: None,
            css_bundles: None,
            dev_server_config: None,
        }
    }

    #[test]
    fn should_load_the_bundles_on_mode_prod() {
        let mut payload = prepare_payload(None, Mode::Prod);

        let _ = payload.client_payload();
        assert_eq!(
            payload.js_bundles,
            Some(vec![
                "assets/index-D-yFyCZo.js".to_string(),
                "assets/client-main-B9g1NVV7.js".to_string()
            ])
        );
        assert_eq!(
            payload.css_bundles,
            Some(vec![
                "assets/index-CynfArjF.css".to_string(),
                "assets/client-main-BS7N-NIa.css".to_string()
            ])
        );
    }

    #[test]
    fn should_not_load_the_bundles_on_mode_dev() {
        let mut payload = prepare_payload(None, Mode::Dev);
        let _ = payload.client_payload();
        assert!(payload.js_bundles.is_none());
        assert!(payload.css_bundles.is_none());
    }
}