Skip to main content

vite_static_shared/
parsing.rs

1//! Vite manifest parsing module.
2//!
3//! This module contains all required functions to parse manifest and read chunks, so it's sort of
4//! "internal", that you probably shouldn't use.
5//!
6//! **To parse and automatically implement traits, use `Manifest` derive!**
7
8use std::{borrow::Cow, fs::File, io::Read as _, path::Path};
9
10use anyhow::Context as _;
11use anyhow::Result;
12use base64::{Engine as _, prelude::BASE64_STANDARD};
13
14use crate::{ManifestChunk, ViteManifest};
15
16/// Parses Vite manifest and returns it.
17///
18/// Takes Vite Project's dist folder and returns [`ViteManifest`].
19///
20/// ```rust
21/// parse_manifest(concat!(env!("CARGO_MANIFEST_DIR"), "/examples/vite-project/dist"))
22/// ```
23///
24/// # Errors
25///
26/// - `failed to open vite manifest` - cannot open `.vite/manifest.json`
27/// - `failed to parse vite manifest` - invalid `.vite/manifest.json`
28pub fn parse_manifest(vite_dist: &Path) -> anyhow::Result<ViteManifest<'static>> {
29    let file = File::open(vite_dist.join(".vite").join("manifest.json"))
30        .context("failed to open vite manifest")?;
31
32    let mut manifest: ViteManifest =
33        serde_json::from_reader(file).context("failed to parse vite manifest")?;
34
35    // for whatever reason, "cssCodeSplit" doesn't add splitted CSS files into vite manifest as chunks/assets,
36    // to fix it, we are manually add all referenced CSS files into manifest
37    for chunk in manifest.clone().values() {
38        for css in chunk.css.as_ref() {
39            manifest.insert(
40                css.clone(),
41                ManifestChunk {
42                    key: css.clone(),
43                    file: css.clone(),
44                    ..Default::default()
45                },
46            );
47        }
48    }
49
50    for (key, chunk) in &mut manifest {
51        chunk.key.clone_from(key);
52    }
53
54    Ok(manifest)
55}
56
57/// Reads contents of chunk, computes hash and guesses `mime_type`, and writes into [`ManifestChunk`].
58///
59/// ```rust
60/// let SomeManifestChunk = ManifestChunk { ... };
61/// read_manifest_chunk("/some/path/to/vite-project/dist", &mut SomeManifestChunk)?;
62/// ```
63///
64/// # Errors
65///
66/// - `failed to open vite chunk at "..."`
67/// - `failed to read vite chunk at "..."`
68pub fn read_manifest_chunk(vite_dist: &Path, chunk: &mut ManifestChunk) -> Result<()> {
69    let mut file = File::open(vite_dist.join(chunk.file.as_ref())).with_context(|| {
70        format!(
71            r#"failed to open vite chunk at "{path}""#,
72            path = chunk.file
73        )
74    })?;
75
76    let mut contents = Vec::new();
77
78    file.read_to_end(&mut contents).with_context(|| {
79        format!(
80            r#"failed to read vite chunk at "{path}""#,
81            path = chunk.file
82        )
83    })?;
84
85    chunk.hash = Cow::Owned({
86        let hash = blake3::hash(&contents);
87        BASE64_STANDARD.encode(hash.as_bytes())
88    });
89
90    chunk.contents = Cow::Owned(contents);
91
92    chunk.mime_type = mime_guess::from_path(chunk.file.as_ref())
93        .first_or_octet_stream()
94        .to_string()
95        .into();
96
97    Ok(())
98}