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}