vite-static-shared 0.5.0

Shared code for vite-static
Documentation
//! Vite manifest parsing module.
//!
//! This module contains all required functions to parse manifest and read chunks, so it's sort of
//! "internal", that you probably shouldn't use.
//!
//! **To parse and automatically implement traits, use `Manifest` derive!**

use std::{borrow::Cow, fs::File, io::Read as _, path::Path};

use anyhow::Context as _;
use anyhow::Result;
use base64::{Engine as _, prelude::BASE64_STANDARD};

use crate::{ManifestChunk, ViteManifest};

/// Parses Vite manifest and returns it.
///
/// Takes Vite Project's dist folder and returns [`ViteManifest`].
///
/// ```rust
/// parse_manifest(concat!(env!("CARGO_MANIFEST_DIR"), "/examples/vite-project/dist"))
/// ```
///
/// # Errors
///
/// - `failed to open vite manifest` - cannot open `.vite/manifest.json`
/// - `failed to parse vite manifest` - invalid `.vite/manifest.json`
pub fn parse_manifest(vite_dist: &Path) -> anyhow::Result<ViteManifest<'static>> {
    let file = File::open(vite_dist.join(".vite").join("manifest.json"))
        .context("failed to open vite manifest")?;

    let mut manifest: ViteManifest =
        serde_json::from_reader(file).context("failed to parse vite manifest")?;

    // for whatever reason, "cssCodeSplit" doesn't add splitted CSS files into vite manifest as chunks/assets,
    // to fix it, we are manually add all referenced CSS files into manifest
    for chunk in manifest.clone().values() {
        for css in chunk.css.as_ref() {
            manifest.insert(
                css.clone(),
                ManifestChunk {
                    key: css.clone(),
                    file: css.clone(),
                    ..Default::default()
                },
            );
        }
    }

    for (key, chunk) in &mut manifest {
        chunk.key.clone_from(key);
    }

    Ok(manifest)
}

/// Reads contents of chunk, computes hash and guesses `mime_type`, and writes into [`ManifestChunk`].
///
/// ```rust
/// let SomeManifestChunk = ManifestChunk { ... };
/// read_manifest_chunk("/some/path/to/vite-project/dist", &mut SomeManifestChunk)?;
/// ```
///
/// # Errors
///
/// - `failed to open vite chunk at "..."`
/// - `failed to read vite chunk at "..."`
pub fn read_manifest_chunk(vite_dist: &Path, chunk: &mut ManifestChunk) -> Result<()> {
    let mut file = File::open(vite_dist.join(chunk.file.as_ref())).with_context(|| {
        format!(
            r#"failed to open vite chunk at "{path}""#,
            path = chunk.file
        )
    })?;

    let mut contents = Vec::new();

    file.read_to_end(&mut contents).with_context(|| {
        format!(
            r#"failed to read vite chunk at "{path}""#,
            path = chunk.file
        )
    })?;

    chunk.hash = Cow::Owned({
        let hash = blake3::hash(&contents);
        BASE64_STANDARD.encode(hash.as_bytes())
    });

    chunk.contents = Cow::Owned(contents);

    chunk.mime_type = mime_guess::from_path(chunk.file.as_ref())
        .first_or_octet_stream()
        .to_string()
        .into();

    Ok(())
}