esbuild_metafile/
http_preloader.rs1use 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}