Skip to content

Commit ecad350

Browse files
committed
feat(cli): hotreload support for frontend static files, closes tauri-apps#2173
1 parent 1d7171a commit ecad350

File tree

13 files changed

+788
-19
lines changed

13 files changed

+788
-19
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"cli": "minor"
3+
---
4+
5+
Hot-reload the frontend when `tauri.conf.json > build > devPath` points to a directory.

.changes/utils-mimetype.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-utils": "patch"
3+
---
4+
5+
Add `mime_type` module

core/tauri-runtime/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ tauri-utils = { version = "1.1.1", path = "../tauri-utils" }
3030
uuid = { version = "1", features = [ "v4" ] }
3131
http = "0.2.4"
3232
http-range = "0.1.4"
33-
infer = "0.7"
3433
raw-window-handle = "0.5"
3534
rand = "0.8"
3635

core/tauri-runtime/src/http/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ mod request;
88
mod response;
99

1010
pub use self::{
11-
mime_type::MimeType,
1211
request::{Request, RequestParts},
1312
response::{Builder as ResponseBuilder, Response, ResponseParts},
1413
};
1514

15+
pub use tauri_utils::mime_type::MimeType;
16+
1617
// re-expose default http types
1718
pub use http::{header, method, status, uri::InvalidUri, version, Uri};
1819

core/tauri-utils/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ glob = { version = "0.3.0", optional = true }
3535
walkdir = { version = "2", optional = true }
3636
memchr = "2.4"
3737
semver = "1"
38+
infer = "0.7"
3839

3940
[target."cfg(target_os = \"linux\")".dependencies]
4041
heck = "0.4"

core/tauri-utils/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub mod assets;
1414
pub mod config;
1515
pub mod html;
1616
pub mod io;
17+
pub mod mime_type;
1718
pub mod platform;
1819
/// Prepare application resources and sidecars.
1920
#[cfg(feature = "resources")]

core/tauri-utils/src/mime_type.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2019-2022 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
#![allow(missing_docs)]
6+
7+
use std::fmt;
8+
9+
const MIMETYPE_PLAIN: &str = "text/plain";
10+
11+
/// [Web Compatible MimeTypes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#important_mime_types_for_web_developers)
12+
pub enum MimeType {
13+
Css,
14+
Csv,
15+
Html,
16+
Ico,
17+
Js,
18+
Json,
19+
Jsonld,
20+
OctetStream,
21+
Rtf,
22+
Svg,
23+
Mp4,
24+
}
25+
26+
impl std::fmt::Display for MimeType {
27+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28+
let mime = match self {
29+
MimeType::Css => "text/css",
30+
MimeType::Csv => "text/csv",
31+
MimeType::Html => "text/html",
32+
MimeType::Ico => "image/vnd.microsoft.icon",
33+
MimeType::Js => "text/javascript",
34+
MimeType::Json => "application/json",
35+
MimeType::Jsonld => "application/ld+json",
36+
MimeType::OctetStream => "application/octet-stream",
37+
MimeType::Rtf => "application/rtf",
38+
MimeType::Svg => "image/svg+xml",
39+
MimeType::Mp4 => "video/mp4",
40+
};
41+
write!(f, "{}", mime)
42+
}
43+
}
44+
45+
impl MimeType {
46+
/// parse a URI suffix to convert text/plain mimeType to their actual web compatible mimeType.
47+
pub fn parse_from_uri(uri: &str) -> MimeType {
48+
let suffix = uri.split('.').last();
49+
match suffix {
50+
Some("bin") => Self::OctetStream,
51+
Some("css") => Self::Css,
52+
Some("csv") => Self::Csv,
53+
Some("html") => Self::Html,
54+
Some("ico") => Self::Ico,
55+
Some("js") => Self::Js,
56+
Some("json") => Self::Json,
57+
Some("jsonld") => Self::Jsonld,
58+
Some("mjs") => Self::Js,
59+
Some("rtf") => Self::Rtf,
60+
Some("svg") => Self::Svg,
61+
Some("mp4") => Self::Mp4,
62+
// Assume HTML when a TLD is found for eg. `wry:://tauri.app` | `wry://hello.com`
63+
Some(_) => Self::Html,
64+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
65+
// using octet stream according to this:
66+
None => Self::OctetStream,
67+
}
68+
}
69+
70+
/// infer mimetype from content (or) URI if needed.
71+
pub fn parse(content: &[u8], uri: &str) -> String {
72+
let mime = if uri.ends_with(".svg") {
73+
// when reading svg, we can't use `infer`
74+
None
75+
} else {
76+
infer::get(content).map(|info| info.mime_type())
77+
};
78+
79+
match mime {
80+
Some(mime) if mime == MIMETYPE_PLAIN => Self::parse_from_uri(uri).to_string(),
81+
None => Self::parse_from_uri(uri).to_string(),
82+
Some(mime) => mime.to_string(),
83+
}
84+
}
85+
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use super::*;
90+
91+
#[test]
92+
fn should_parse_mimetype_from_uri() {
93+
let css = MimeType::parse_from_uri(
94+
"https://unpkg.com/browse/[email protected]/dist/css/bootstrap-grid.css",
95+
)
96+
.to_string();
97+
assert_eq!(css, "text/css".to_string());
98+
99+
let csv: String = MimeType::parse_from_uri("https://example.com/random.csv").to_string();
100+
assert_eq!(csv, "text/csv".to_string());
101+
102+
let ico: String =
103+
MimeType::parse_from_uri("https://icons.duckduckgo.com/ip3/microsoft.com.ico").to_string();
104+
assert_eq!(ico, String::from("image/vnd.microsoft.icon"));
105+
106+
let html: String = MimeType::parse_from_uri("https://tauri.app/index.html").to_string();
107+
assert_eq!(html, String::from("text/html"));
108+
109+
let js: String =
110+
MimeType::parse_from_uri("https://unpkg.com/[email protected]/umd/react.production.min.js")
111+
.to_string();
112+
assert_eq!(js, "text/javascript".to_string());
113+
114+
let json: String =
115+
MimeType::parse_from_uri("https://unpkg.com/browse/[email protected]/build-info.json").to_string();
116+
assert_eq!(json, String::from("application/json"));
117+
118+
let jsonld: String = MimeType::parse_from_uri("https:/example.com/hello.jsonld").to_string();
119+
assert_eq!(jsonld, String::from("application/ld+json"));
120+
121+
let mjs: String = MimeType::parse_from_uri("https://example.com/bundled.mjs").to_string();
122+
assert_eq!(mjs, String::from("text/javascript"));
123+
124+
let rtf: String = MimeType::parse_from_uri("https://example.com/document.rtf").to_string();
125+
assert_eq!(rtf, String::from("application/rtf"));
126+
127+
let svg: String = MimeType::parse_from_uri("https://example.com/picture.svg").to_string();
128+
assert_eq!(svg, String::from("image/svg+xml"));
129+
130+
let mp4: String = MimeType::parse_from_uri("https://example.com/video.mp4").to_string();
131+
assert_eq!(mp4, String::from("video/mp4"));
132+
133+
let custom_scheme = MimeType::parse_from_uri("wry://tauri.app").to_string();
134+
assert_eq!(custom_scheme, String::from("text/html"));
135+
}
136+
}

0 commit comments

Comments
 (0)