Skip to main content

vite_static_shared/
lib.rs

1#![warn(clippy::pedantic)]
2
3use std::{borrow::Cow, collections::HashMap};
4
5use serde::{Deserialize, Serialize};
6
7pub mod parsing;
8
9/// Type of `.vite/manifest.json`.
10pub type ViteManifest<'a> = HashMap<Cow<'a, str>, ManifestChunk<'a>>;
11
12/// Vite manifest chunk.
13///
14/// [See Vite documentation](https://vite.dev/guide/backend-integration)
15#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, Hash)]
16#[serde(rename_all = "camelCase")]
17pub struct ManifestChunk<'a> {
18    /// The key of this chunk in manifest.
19    ///
20    /// This field is added by `vite-static` and skipped during (de)serialization.
21    #[serde(skip)]
22    pub key: Cow<'a, str>,
23
24    /// The contents of manifest chunk.
25    ///
26    /// This field is added by `vite-static` and skipped during (de)serialization.
27    #[serde(skip)]
28    pub contents: Cow<'a, [u8]>,
29
30    /// Hash of this chunk. Derive macro uses [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) as hashing algorithm.
31    ///
32    /// This field is added by `vite-static` and skipped during (de)serialization.
33    #[serde(skip)]
34    pub hash: Cow<'a, str>,
35
36    /// Mime type of this chunk.
37    ///
38    /// This field is added by `vite-static` and skipped during (de)serialization.
39    #[serde(skip)]
40    pub mime_type: Cow<'a, str>,
41
42    /// The input file name of this chunk/asset if known.
43    pub src: Option<Cow<'a, str>>,
44
45    /// The output file name of this chunk/asset.
46    pub file: Cow<'a, str>,
47
48    /// The list of CSS files imported by this chunk.
49    #[serde(default)]
50    pub css: Cow<'a, [Cow<'a, str>]>,
51
52    /// The list of asset files imported by this chunk, excluding CSS files.
53    #[serde(default)]
54    pub assets: Cow<'a, [Cow<'a, str>]>,
55
56    /// Whether this chunk or asset is an entry point
57    #[serde(default)]
58    pub is_entry: bool,
59
60    /// The name of this chunk/asset if known.
61    pub name: Option<Cow<'a, str>>,
62
63    /// Whether this chunk is a dynamic entry point
64    ///
65    /// This field is only present in JS chunks.
66    #[serde(default)]
67    pub is_dynamic_entry: bool,
68
69    /// The list of statically imported chunks by this chunk
70    ///
71    /// The values are the keys of the manifest. This field is only present in JS chunks.
72    #[serde(default)]
73    pub imports: Cow<'a, [Cow<'a, str>]>,
74
75    /// The list of dynamically imported chunks by this chunk
76    ///
77    /// The values are the keys of the manifest. This field is only present in JS chunks.
78    #[serde(default)]
79    pub dynamic_imports: Cow<'a, [Cow<'a, str>]>,
80}
81
82/// Trait, that returns boxed [`Manifest`].
83///
84/// This trait is required for manifests and usually by integrations. **It is automatically implemented by derive macro**.
85///
86/// ```
87/// impl Boxed<'static> for MyViteManifest {
88///     fn boxed(&self) -> Box<dyn Manifest<'static>> {
89///         Box::new(MyViteManifest)
90///     }
91/// }
92///
93/// SomeCoolAndAwesomeIntegration::new(MyViteManifest.boxed()) // easy and simple!
94/// ```
95pub trait Boxed<'a> {
96    fn boxed(&self) -> Box<dyn Manifest<'a>>;
97}
98
99/// Trait for static/embedded manifests.
100///
101/// This trait is sort of internal, because it's suitable **only for embedded manifests**, so use [`Manifest`] instead!
102///
103/// This trait automatically implements [`Manifest`].
104pub trait StaticManifest: Boxed<'static> {
105    /// Get array of `(key, output_filename)` pairs.
106    ///
107    /// Array must be sorted by `key`
108    fn static_key_output_pairs(&self) -> &[(&'static str, &'static str)];
109
110    /// Get [`ManifestChunk`]s as `(output_filename, chunk)` pairs.
111    ///
112    /// Array must be sorted by `output_filename`
113    fn static_chunks(&self) -> &[(&'static str, &'static ManifestChunk<'static>)];
114}
115
116/// Trait, that helps query chunks in Vite manifest.
117///
118/// See `Manifest` derive and [`ManifestChunk`].
119pub trait Manifest<'a>: Boxed<'a> {
120    /// Get `output_filename` from manifest key.
121    ///
122    /// ```rust
123    /// MyViteManifest.resolve_output("src/main.tsx") // returns "main.HASH.js"
124    /// ```
125    fn resolve_output(&self, key: &str) -> Option<Cow<'a, str>>;
126
127    /// Iterator over manifest keys (input filenames, e.g. "src/main.tsx").
128    ///
129    /// ```rust
130    /// MyViteManifest.iter_keys()
131    /// // returns iterator over keys (e.g. "src/main.tsx", "src/component.tsx", ...)
132    /// ```
133    fn iter_keys(&self) -> Box<dyn Iterator<Item = &str> + '_>;
134
135    /// Iterator over manifest outputs (output filenames, e.g. "main.hashhh.js").
136    ///
137    /// ```rust
138    /// MyViteManifest.iter_outputs()
139    /// // returns iterator over output files (e.g. "main.HASH.js", "chunks/_shared.HASH.js", ...)
140    /// ```
141    fn iter_outputs(&self) -> Box<dyn Iterator<Item = &str> + '_>;
142
143    /// Get [`ManifestChunk`] from output filename.
144    ///
145    /// ```rust
146    /// dbg!(MyViteManifest.chunk("main.HASH.js"))
147    /// ```
148    fn chunk(&self, output: &str) -> Option<Cow<'a, ManifestChunk<'a>>>;
149
150    /// Get [`ManifestChunk`] from manifest key.
151    ///
152    /// ```rust
153    /// dbg!(MyViteManifest.chunk_by_key("src/main.tsx"))
154    /// ```
155    fn chunk_by_key(&self, key: &str) -> Option<Cow<'a, ManifestChunk<'a>>> {
156        match self.resolve_output(key) {
157            Some(output) => self.chunk(&output),
158            None => None,
159        }
160    }
161}
162
163impl<M: StaticManifest> Manifest<'static> for M {
164    fn resolve_output(&self, key: &str) -> Option<Cow<'static, str>> {
165        let pairs = self.static_key_output_pairs();
166
167        match pairs.binary_search_by_key(&key, |(this_key, _)| this_key) {
168            Ok(index) => Some(Cow::Borrowed(pairs[index].1)),
169            Err(_) => None,
170        }
171    }
172
173    fn iter_keys(&self) -> Box<dyn Iterator<Item = &str> + '_> {
174        Box::new(self.static_key_output_pairs().iter().map(|&(key, _)| key))
175    }
176
177    fn iter_outputs(&self) -> Box<dyn Iterator<Item = &str> + '_> {
178        Box::new(
179            self.static_key_output_pairs()
180                .iter()
181                .map(|&(_, output)| output),
182        )
183    }
184
185    fn chunk(&self, output: &str) -> Option<Cow<'static, ManifestChunk<'static>>> {
186        let chunks = self.static_chunks();
187
188        match chunks.binary_search_by_key(&output, |(this_output, _)| this_output) {
189            Ok(index) => Some(Cow::Borrowed(chunks[index].1)),
190            Err(_) => None,
191        }
192    }
193}