use std::{borrow::Cow, env, path::Path};
use vite_static_shared::{
ManifestChunk, ViteManifest,
parsing::{parse_manifest, read_manifest_chunk},
};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Ident, LitByteStr};
use crate::parse::ManifestSettings;
fn str_to_tokens(data: &str) -> TokenStream {
quote! { Cow::Borrowed(#data) }
}
fn vec_to_tokens(data: &[Cow<'_, str>]) -> TokenStream {
let items = data.iter().map(|i| str_to_tokens(i));
quote! { Cow::Borrowed(&[ #(#items),* ]) }
}
fn option_to_tokens(data: &Option<Cow<'_, str>>) -> TokenStream {
if let Some(data) = data {
let data = str_to_tokens(data);
quote! { Some(#data) }
} else {
quote! { None }
}
}
fn construct_chunk(chunk: &ManifestChunk<'_>) -> TokenStream {
let key = str_to_tokens(&chunk.key);
let contents = LitByteStr::new(&chunk.contents, Span::call_site());
let hash = str_to_tokens(&chunk.hash);
let mime_type = str_to_tokens(&chunk.mime_type);
let src = option_to_tokens(&chunk.src);
let file = str_to_tokens(&chunk.file);
let css = vec_to_tokens(&chunk.css);
let assets = vec_to_tokens(&chunk.assets);
let is_entry = &chunk.is_entry;
let name = option_to_tokens(&chunk.name);
let is_dynamic_entry = &chunk.is_dynamic_entry;
let imports = vec_to_tokens(&chunk.imports);
let dynamic_imports = vec_to_tokens(&chunk.dynamic_imports);
quote! {
::vite_static::ManifestChunk::<'static> {
key: #key,
contents: Cow::Borrowed(#contents),
hash: #hash,
mime_type: #mime_type,
src: #src,
file: #file,
css: #css,
assets: #assets,
is_entry: #is_entry,
name: #name,
is_dynamic_entry: #is_dynamic_entry,
imports: #imports,
dynamic_imports: #dynamic_imports,
}
}
}
fn get_manifest(dir: &str) -> anyhow::Result<ViteManifest<'static>> {
let vite_dist = if dir.starts_with('/') {
Path::new(dir).to_path_buf()
} else {
let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string());
let current_dir = Path::new(&cargo_manifest_dir);
current_dir.join(dir)
};
let mut manifest = parse_manifest(&vite_dist)?;
for chunk in manifest.values_mut() {
read_manifest_chunk(&vite_dist, chunk)?;
}
Ok(manifest)
}
fn generate_manifest_data(manifest: &ViteManifest<'_>) -> (Vec<TokenStream>, Vec<TokenStream>) {
let mut manifest_items = manifest.iter().collect::<Vec<_>>();
let key_output_pairs = {
manifest_items.sort_unstable_by_key(|&(key, _)| key);
manifest_items
.iter()
.map(|&(key, chunk)| {
let output = &chunk.file;
quote! { (#key, #output) }
})
.collect()
};
let chunks = {
manifest_items.sort_unstable_by_key(|&(_, chunk)| &chunk.file);
manifest_items
.iter()
.map(|&(_, chunk)| {
let output = &chunk.file;
let chunk = construct_chunk(chunk);
quote! { (#output, &#chunk) }
})
.collect()
};
(key_output_pairs, chunks)
}
pub fn generate(ident: &Ident, settings: &ManifestSettings) -> syn::Result<TokenStream> {
let manifest = get_manifest(&settings.vite_dist.0)
.map_err(|err| syn::Error::new(settings.vite_dist.1, format!("{err:#}")))?;
let (key_output_pairs, chunks) = generate_manifest_data(&manifest);
let key_output_pairs_ident = format_ident!("{}_KEY_OUTPUT_PAIRS", ident);
let chunks_ident = format_ident!("{}_CHUNKS", ident);
Ok(quote! {
use ::std::borrow::Cow;
#[allow(non_upper_case_globals)]
const #key_output_pairs_ident: &[(&'static str, &'static str)] = &[ #(#key_output_pairs),* ];
#[allow(non_upper_case_globals)]
const #chunks_ident: &[(&'static str, &'static ::vite_static::ManifestChunk<'static>)] = &[ #(#chunks),* ];
impl ::vite_static::StaticManifest for #ident {
fn static_key_output_pairs(&self) -> &[(&'static str, &'static str)] { #key_output_pairs_ident }
fn static_chunks(&self) -> &[(&'static str, &'static ::vite_static::ManifestChunk<'static>)] { #chunks_ident }
}
})
}