Skip to main content

esbuild_metafile/
http_preloader.rs

1use std::sync::Arc;
2
3use actix_utils::future::Ready;
4use actix_utils::future::ok;
5use actix_web::Error;
6use actix_web::FromRequest;
7use actix_web::HttpRequest;
8use actix_web::dev;
9use dashmap::DashSet;
10
11use super::EsbuildMetaFile;
12use super::asset::Asset;
13use super::instance::get_esbuild_metafile;
14use super::preloadable_asset::PreloadableAsset;
15
16pub struct HttpPreloader {
17    pub includes: DashSet<Asset>,
18    pub preloads: DashSet<PreloadableAsset>,
19
20    esbuild_metafile: Arc<EsbuildMetaFile>,
21}
22
23impl HttpPreloader {
24    pub fn new(esbuild_metafile: Arc<EsbuildMetaFile>) -> Self {
25        Self {
26            esbuild_metafile,
27            includes: DashSet::new(),
28            preloads: DashSet::new(),
29        }
30    }
31
32    pub fn register_input(&self, input_path: &str) -> Option<()> {
33        match self.esbuild_metafile.find_outputs_for_input(input_path) {
34            None => None,
35            Some(output_paths) => {
36                for output_path in output_paths {
37                    if self.includes.insert(Asset::from_path(output_path.clone())) {
38                        for preload in self.esbuild_metafile.get_preloads(&output_path) {
39                            self.preloads.insert(PreloadableAsset::from_path(preload));
40                        }
41                    }
42                }
43
44                Some(())
45            }
46        }
47    }
48
49    pub fn register_preload(&self, preload_path: &str) -> Option<()> {
50        match self.esbuild_metafile.find_outputs_for_input(preload_path) {
51            None => None,
52            Some(output_paths) => {
53                for output_path in output_paths {
54                    self.preloads
55                        .insert(PreloadableAsset::from_path(output_path.clone()));
56
57                    for preload in self.esbuild_metafile.get_preloads(&output_path) {
58                        self.preloads.insert(PreloadableAsset::from_path(preload));
59                    }
60                }
61
62                Some(())
63            }
64        }
65    }
66}
67
68impl FromRequest for HttpPreloader {
69    type Error = Error;
70    type Future = Ready<Result<Self, Self::Error>>;
71
72    fn from_request(_: &HttpRequest, _: &mut dev::Payload) -> Self::Future {
73        ok(HttpPreloader::new(get_esbuild_metafile()))
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use anyhow::Result;
80
81    use super::*;
82    use crate::test::get_metafile_basic;
83
84    #[test]
85    fn test_unique_includes_and_preloads() -> Result<()> {
86        let metafile = get_metafile_basic()?;
87        let preloader = HttpPreloader::new(metafile);
88
89        preloader.register_input("src/main.ts");
90        preloader.register_input("src/main.ts");
91
92        let includes = &preloader.includes;
93
94        assert_eq!(includes.len(), 2);
95        assert!(includes.contains(&Asset::from_path("dist/main.js".to_string())));
96        assert!(includes.contains(&Asset::from_path("dist/main.css".to_string())));
97
98        let preloads = &preloader.preloads;
99
100        assert_eq!(preloads.len(), 3);
101        assert!(preloads.contains(&PreloadableAsset::from_path("dist/chunk1.js".to_string())));
102        assert!(preloads.contains(&PreloadableAsset::from_path("dist/chunk2.js".to_string())));
103        assert!(preloads.contains(&PreloadableAsset::from_path("dist/style1.css".to_string())));
104
105        Ok(())
106    }
107
108    #[test]
109    fn test_css_preloads_uniqueness() -> Result<()> {
110        let metafile = get_metafile_basic()?;
111        let preloader = HttpPreloader::new(metafile);
112
113        preloader.register_input("src/style.css");
114
115        let preloads = preloader.preloads;
116
117        assert_eq!(preloads.len(), 1);
118        assert!(preloads.contains(&PreloadableAsset::from_path("dist/style1.css".to_string())));
119
120        Ok(())
121    }
122}