use std::collections::HashMap;
use crate::resource::Resource;
#[allow(dead_code)]
#[derive(serde::Deserialize)]
#[serde(transparent)]
pub(crate) struct Manifest(HashMap<String, Chunk>);
#[allow(dead_code)]
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Chunk {
#[serde(default)]
pub src: Option<String>,
pub file: String,
#[serde(default)]
pub css: Vec<String>,
#[serde(default)]
pub assets: Vec<String>,
#[serde(default)]
pub is_entry: bool,
#[serde(default)]
pub is_dynamic_entry: bool,
#[serde(default)]
pub imports: Vec<String>,
#[serde(default)]
pub dynamic_imports: Vec<String>,
}
impl<'a> Manifest {
pub fn resolve_resources(&'a self, entrypoint: &'a str) -> Vec<Resource<'a>> {
let Some(chunk) = self.0.get(entrypoint) else {
return vec![];
};
if !chunk.is_entry {
return vec![];
}
let mut resources: Vec<Resource<'a>> = vec![];
self.resolve_imports(&mut resources, entrypoint, chunk);
resources.sort();
resources
}
fn resolve_imports(
&'a self,
resources: &mut Vec<Resource<'a>>,
key: &'a str,
chunk: &'a Chunk,
) {
for css in chunk.css.iter() {
resources.push(Resource::Stylesheet(css));
}
for import in chunk.imports.iter() {
let Some(chunk) = self.0.get(import) else {
continue;
};
self.resolve_imports(resources, import, chunk);
}
if !chunk.is_entry {
resources.push(Resource::PreloadModule(&chunk.file));
return;
}
if key.ends_with(".css") {
resources.push(Resource::Stylesheet(&chunk.file));
} else if key.ends_with(".js") || key.ends_with(".jsx") || key.ends_with(".ts") || key.ends_with(".tsx") {
resources.push(Resource::Module(&chunk.file));
}
}
}
#[cfg(test)]
mod test {
use super::{Manifest, Resource};
const SAMPLE_MANIFEST: &str = include_str!("../test/sample_manifest.json");
#[test]
fn can_deserialize_sample_manifest() {
let result = serde_json::from_str::<Manifest>(SAMPLE_MANIFEST);
assert!(result.is_ok());
}
#[test]
fn can_resolve_entrypoints() {
let manifest = serde_json::from_str::<Manifest>(SAMPLE_MANIFEST)
.expect("sample manifest should be deserializable");
let resources = manifest.resolve_resources("views/foo.js");
let expected = vec![
Resource::Stylesheet("assets/foo-5UjPuW-k.css"),
Resource::Stylesheet("assets/shared-ChJ_j-JJ.css"),
Resource::Module("assets/foo-BRBmoGS9.js"),
Resource::PreloadModule("assets/shared-B7PI925R.js"),
];
assert_eq!(resources, expected);
}
}