vite_rust/config.rs
1use std::env;
2
3use crate::utils::check_heart_beat;
4
5#[derive(Debug, PartialEq, Eq, Clone)]
6pub enum ViteMode {
7 Development,
8 Manifest,
9}
10
11impl ViteMode {
12 pub(crate) async fn discover(
13 use_hb: bool,
14 use_dev_server: bool,
15 host: &str,
16 hb_retries: u8,
17 ) -> ViteMode {
18 if !use_hb {
19 return ViteMode::Development;
20 }
21
22 if !use_dev_server {
23 return ViteMode::Manifest;
24 }
25
26 if is_production() {
27 return ViteMode::Manifest;
28 }
29
30 let dev_server_is_ok = check_heart_beat(host, None, hb_retries).await;
31
32 match dev_server_is_ok {
33 true => ViteMode::Development,
34 false => ViteMode::Manifest,
35 }
36 }
37}
38
39fn is_production() -> bool {
40 env::vars().any(|(k, v)| {
41 if [
42 "RUST_ENV",
43 "NODE_ENV",
44 "APP_ENV",
45 "__TEST_APP_ENV",
46 "LOCO_ENV",
47 "RAILS_ENV",
48 ]
49 .contains(&k.as_str())
50 {
51 return v == "production";
52 }
53
54 false
55 })
56}
57
58#[derive(Debug, PartialEq, Eq, Clone)]
59pub struct ViteConfig<'a> {
60 /// The `path/to/manifest.json` file.
61 /// Currently, Vite won't resolve relative paths, so please consider
62 /// your current working directory as the root of it and start the path
63 /// with a root node directory directly.
64 ///
65 /// **Optionally and experimentally**, you can use the [`resolve_path`]
66 /// method for the manifest file resolution. However, this method might
67 /// come to fail at some point, and will also panic in the many situations
68 /// described on its documentation.
69 ///
70 /// # Example
71 /// ```plaintext
72 /// your_project/
73 /// |-- public/
74 /// | |-- dist/
75 /// | | |-- manifest.json
76 /// |-- src/
77 /// | |-- main.rs // <-- you're here!
78 /// ```
79 ///
80 /// ```ignore
81 ///
82 /// use vite_rust::{ViteConfig, utils::resolve_path};
83 ///
84 /// let config = ViteConfig {
85 /// manifest_path: Some("public/dist/manifest.json"),
86 /// // or
87 /// manifest_path: Some(resolve_path(file!(), "../public/dist/manifest.json")),
88 /// // ...
89 /// };
90 /// ```
91 pub manifest_path: Option<&'a str>,
92 /// Defines which entrypoints Vite will use to generate the html `script`,
93 /// `link` and `stylesheet` tags.
94 ///
95 /// If `None` is provided, Vite will scan the manifest for files with
96 /// `isEntry: true` property and consider them the entrypoints.
97 pub entrypoints: Option<Vec<&'a str>>,
98 /// If `None` is provided, Vite will discover which one to use considering:
99 /// - any of `RUST_ENV`, `NODE_ENV` or `APP_ENV` environment variables exists
100 /// and is set to `true`;
101 /// - Dev-server is running;
102 /// - Heart beat check is enabled.
103 ///
104 /// By setting this option, the discovering phase will be skipped.
105 /// Refer to the crate's `README.md` file to understand the way it decides which mode to pick.
106 pub force_mode: Option<ViteMode>,
107 /// Whether Vite should ping your vite dev-server to check if its running.
108 /// If false, `ViteMode` will be set to `Development` if not forced by the configuration.
109 pub use_heart_beat_check: bool,
110 /// How many times heartbeat checker should try before fallbacking.
111 pub heart_beat_retries_limit: Option<u8>,
112 /// Whether dev server should be considered or not.
113 ///
114 /// If false, `force_mode` should be either `Manifest` or `None`,
115 /// otherwise, undefined behavior might occur.
116 pub enable_dev_server: bool,
117 /// The host in which your vite dev-server is running.
118 /// Normally, it would be `"http://localhost:5173"`.
119 ///
120 /// Please, do not forget the protocol (http, https)!
121 pub server_host: Option<&'a str>,
122 /// Prefix assets path with the given `str`.
123 pub prefix: Option<&'a str>,
124 /// Add a custom domain to prefix every asset URL with.
125 pub app_url: Option<&'a str>,
126}
127
128impl<'a> ViteConfig<'a> {
129 /// Creates a new `ViteConfig` instance with `manifest_path` and `entrypoints` fields set.
130 pub fn new(manifest_path: &'a str, entrypoints: Vec<&'a str>) -> Self {
131 ViteConfig::default()
132 .set_manifest_path(manifest_path)
133 .set_entrypoints(entrypoints)
134 }
135
136 pub fn set_manifest_path(mut self, manifest_path: &'a str) -> Self {
137 self.manifest_path = Some(manifest_path);
138 self
139 }
140
141 pub fn set_entrypoints(mut self, entrypoints: Vec<&'a str>) -> Self {
142 self.entrypoints = Some(entrypoints);
143 self
144 }
145
146 pub fn set_force_mode(mut self, mode: ViteMode) -> Self {
147 self.force_mode = Some(mode);
148 self
149 }
150 pub fn set_server_host(mut self, server_host: &'a str) -> Self {
151 self.server_host = Some(server_host);
152 self
153 }
154
155 pub fn set_heart_beat_retries_limit(mut self, limit: u8) -> Self {
156 self.heart_beat_retries_limit = Some(limit);
157 self
158 }
159
160 pub fn without_heart_beat_check(mut self) -> Self {
161 self.use_heart_beat_check = false;
162 self
163 }
164
165 pub fn without_dev_server(mut self) -> Self {
166 self.enable_dev_server = false;
167 self
168 }
169
170 pub fn set_prefix(mut self, prefix: &'a str) -> Self {
171 self.prefix = Some(prefix);
172 self
173 }
174
175 pub fn set_app_url(mut self, app_url: &'a str) -> Self {
176 self.app_url = Some(app_url);
177 self
178 }
179}
180
181impl Default for ViteConfig<'_> {
182 /// Create a `ViteConfig` instance.
183 ///
184 /// You can create your config by directly instantiating the struct, or
185 /// by using some default options. Note that you **must set the manifest
186 /// path**.
187 ///
188 /// # Example
189 /// ```rust
190 /// use vite_rust::ViteConfig;
191 ///
192 /// let manual_config = ViteConfig {
193 /// manifest_path: Some("path/to/manifest.json"),
194 /// entrypoints: None, // Vite can discover them by itself
195 /// force_mode: None, // Vite can discover it too
196 /// use_heart_beat_check: true,
197 /// enable_dev_server: true,
198 /// server_host: Some("http://localhost:5173"),
199 /// heart_beat_retries_limit: Some(5),
200 /// prefix: None,
201 /// app_url: None,
202 /// };
203 ///
204 /// let with_defaults_config = ViteConfig::default().set_manifest_path("path/to/manifest.json");
205 ///
206 /// assert_eq!(manual_config, with_defaults_config);
207 /// ```
208 fn default() -> Self {
209 Self {
210 enable_dev_server: true,
211 entrypoints: None,
212 manifest_path: None,
213 force_mode: None,
214 server_host: Some("http://localhost:5173"),
215 use_heart_beat_check: true,
216 heart_beat_retries_limit: Some(5),
217 prefix: None,
218 app_url: None,
219 }
220 }
221}
222
223#[cfg(test)]
224mod test {
225 use std::env;
226
227 use crate::{config::is_production, ViteMode};
228
229 #[tokio::test]
230 async fn test_discover() {
231 let host = "http://localhost:3000";
232 let hb_retries = 1;
233
234 assert_eq!(
235 ViteMode::Development,
236 ViteMode::discover(false, true, host, hb_retries).await
237 );
238
239 assert_eq!(
240 ViteMode::Manifest,
241 ViteMode::discover(true, false, host, hb_retries).await
242 );
243 }
244
245 #[test]
246 fn test_is_production() {
247 env::set_var("__TEST_APP_ENV", "production");
248 assert!(is_production());
249
250 env::remove_var("__TEST_APP_ENV");
251 assert!(!is_production());
252 }
253}