@@ -42,6 +42,7 @@ sections up to the index to be used with the `get_section` Tera function | |||
- Add `transparent` sections, for when you want to separate content by sections but want to group them at a higher level (think a `posts` folder with years | |||
that want to use pagination on the index). | |||
- Add `page_template` to section front-matter for when you want to specify the template to use for every page under it | |||
- Improves to `zola serve`: now handles files/directories renaming | |||
## 0.4.2 (2018-09-03) | |||
@@ -86,7 +86,7 @@ dependencies = [ | |||
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -506,7 +506,7 @@ dependencies = [ | |||
"rust-stemmers 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"strum 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"strum_macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
@@ -1140,7 +1140,7 @@ dependencies = [ | |||
"phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -1385,7 +1385,7 @@ dependencies = [ | |||
[[package]] | |||
name = "onig" | |||
version = "4.2.0" | |||
version = "4.2.1" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -1751,7 +1751,7 @@ dependencies = [ | |||
"mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -1907,7 +1907,7 @@ dependencies = [ | |||
[[package]] | |||
name = "serde_json" | |||
version = "1.0.32" | |||
version = "1.0.33" | |||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||
dependencies = [ | |||
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -2119,12 +2119,12 @@ dependencies = [ | |||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"onig 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"onig 4.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"plist 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"regex-syntax 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"walkdir 2.2.6 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
@@ -2156,7 +2156,7 @@ dependencies = [ | |||
"library 0.1.0", | |||
"pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"reqwest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"tera 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -2187,7 +2187,7 @@ dependencies = [ | |||
"pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -2946,7 +2946,7 @@ dependencies = [ | |||
"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" | |||
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" | |||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" | |||
"checksum onig 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f0d9dac9b8a5211103d75f3ce8d0c799e6f4f3f22608eeaefb4419786e4b258" | |||
"checksum onig 4.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3febe8cb22362af9e662c9c35e4d8a675de50b1b119823aa556892ac967fb776" | |||
"checksum onig_sys 69.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "78c04019a39ebac42dfd8c7822af0a009043720845a812ddbb95e403298b0183" | |||
"checksum openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "5e1309181cdcbdb51bc3b6bedb33dfac2a83b3d585033d3f6d9e22e8c1928613" | |||
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" | |||
@@ -3001,7 +3001,7 @@ dependencies = [ | |||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" | |||
"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef" | |||
"checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c" | |||
"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce" | |||
"checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" | |||
"checksum serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aaed41d9fb1e2f587201b863356590c90c1157495d811430a0c0325fe8169650" | |||
"checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" | |||
"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" | |||
@@ -277,8 +277,8 @@ impl Library { | |||
.collect() | |||
} | |||
pub fn find_parent_section(&self, path: &Path) -> Option<&Section> { | |||
let page_key = self.paths_to_pages[path]; | |||
pub fn find_parent_section<P: AsRef<Path>>(&self, path: P) -> Option<&Section> { | |||
let page_key = self.paths_to_pages[path.as_ref()]; | |||
for s in self.sections.values() { | |||
if s.pages.contains(&page_key) { | |||
return Some(s); | |||
@@ -289,16 +289,16 @@ impl Library { | |||
} | |||
/// Only used in tests | |||
pub fn get_section_key(&self, path: &PathBuf) -> Option<&Key> { | |||
self.paths_to_sections.get(path) | |||
pub fn get_section_key<P: AsRef<Path>>(&self, path: P) -> Option<&Key> { | |||
self.paths_to_sections.get(path.as_ref()) | |||
} | |||
pub fn get_section(&self, path: &PathBuf) -> Option<&Section> { | |||
self.sections.get(self.paths_to_sections.get(path).cloned().unwrap_or_default()) | |||
pub fn get_section<P: AsRef<Path>>(&self, path: P) -> Option<&Section> { | |||
self.sections.get(self.paths_to_sections.get(path.as_ref()).cloned().unwrap_or_default()) | |||
} | |||
pub fn get_section_mut(&mut self, path: &PathBuf) -> Option<&mut Section> { | |||
self.sections.get_mut(self.paths_to_sections.get(path).cloned().unwrap_or_default()) | |||
pub fn get_section_mut<P: AsRef<Path>>(&mut self, path: P) -> Option<&mut Section> { | |||
self.sections.get_mut(self.paths_to_sections.get(path.as_ref()).cloned().unwrap_or_default()) | |||
} | |||
pub fn get_section_by_key(&self, key: Key) -> &Section { | |||
@@ -313,8 +313,8 @@ impl Library { | |||
&self.get_section_by_key(key).file.relative | |||
} | |||
pub fn get_page(&self, path: &PathBuf) -> Option<&Page> { | |||
self.pages.get(self.paths_to_pages.get(path).cloned().unwrap_or_default()) | |||
pub fn get_page<P: AsRef<Path>>(&self, path: P) -> Option<&Page> { | |||
self.pages.get(self.paths_to_pages.get(path.as_ref()).cloned().unwrap_or_default()) | |||
} | |||
pub fn get_page_by_key(&self, key: Key) -> &Page { | |||
@@ -325,16 +325,16 @@ impl Library { | |||
self.pages.get_mut(key).unwrap() | |||
} | |||
pub fn remove_section(&mut self, path: &PathBuf) -> Option<Section> { | |||
if let Some(k) = self.paths_to_sections.remove(path) { | |||
pub fn remove_section<P: AsRef<Path>>(&mut self, path: P) -> Option<Section> { | |||
if let Some(k) = self.paths_to_sections.remove(path.as_ref()) { | |||
self.sections.remove(k) | |||
} else { | |||
None | |||
} | |||
} | |||
pub fn remove_page(&mut self, path: &PathBuf) -> Option<Page> { | |||
if let Some(k) = self.paths_to_pages.remove(path) { | |||
pub fn remove_page<P: AsRef<Path>>(&mut self, path: P) -> Option<Page> { | |||
if let Some(k) = self.paths_to_pages.remove(path.as_ref()) { | |||
self.pages.remove(k) | |||
} else { | |||
None | |||
@@ -342,12 +342,12 @@ impl Library { | |||
} | |||
/// Used in rebuild, to check if we know it already | |||
pub fn contains_section(&self, path: &PathBuf) -> bool { | |||
self.paths_to_sections.contains_key(path) | |||
pub fn contains_section<P: AsRef<Path>>(&self, path: P) -> bool { | |||
self.paths_to_sections.contains_key(path.as_ref()) | |||
} | |||
/// Used in rebuild, to check if we know it already | |||
pub fn contains_page(&self, path: &PathBuf) -> bool { | |||
self.paths_to_pages.contains_key(path) | |||
pub fn contains_page<P: AsRef<Path>>(&self, path: P) -> bool { | |||
self.paths_to_pages.contains_key(path.as_ref()) | |||
} | |||
} |
@@ -240,7 +240,53 @@ fn handle_page_editing(site: &mut Site, path: &Path) -> Result<()> { | |||
} | |||
} | |||
/// What happens when a section or a page is changed | |||
/// What happens when we rename a file/folder in the content directory. | |||
/// Note that this is only called for folders when it isn't empty | |||
pub fn after_content_rename(site: &mut Site, old: &Path, new: &Path) -> Result<()> { | |||
let new_path = if new.is_dir() { | |||
if new.join("_index.md").exists() { | |||
// This is a section keep the dir folder to differentiate from renaming _index.md | |||
// which doesn't do the same thing | |||
new.to_path_buf() | |||
} else if new.join("index.md").exists() { | |||
new.join("index.md") | |||
} else { | |||
bail!("Got unexpected folder {:?} while handling renaming that was not expected", new); | |||
} | |||
} else { | |||
new.to_path_buf() | |||
}; | |||
// A section folder has been renamed: just reload the whole site and rebuild it as we | |||
// do not really know what needs to be rendered | |||
if new_path.is_dir() { | |||
site.load()?; | |||
return site.build(); | |||
} | |||
// Renaming a file to _index.md, let the section editing do something and hope for the best | |||
if new_path.file_name().unwrap() == "_index.md" { | |||
// We aren't entirely sure where the original thing was so just try to delete whatever was | |||
// at the old path | |||
site.library.remove_page(&old.to_path_buf()); | |||
site.library.remove_section(&old.to_path_buf()); | |||
return handle_section_editing(site, &new_path); | |||
} | |||
// If it is a page, just delete what was there before and | |||
// fake it's a new page | |||
let old_path = if new_path.file_name().unwrap() == "index.md" { | |||
old.join("index.md") | |||
} else { | |||
old.to_path_buf() | |||
}; | |||
site.library.remove_page(&old_path); | |||
return handle_page_editing(site, &new_path); | |||
} | |||
/// What happens when a section or a page is created/edited | |||
pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> { | |||
let is_section = path.file_name().unwrap() == "_index.md"; | |||
let is_md = path.extension().unwrap() == "md"; | |||
@@ -4,14 +4,14 @@ extern crate site; | |||
extern crate tempfile; | |||
use std::env; | |||
use std::fs::{remove_dir_all, File}; | |||
use std::fs::{self, File}; | |||
use std::io::prelude::*; | |||
use fs_extra::dir; | |||
use site::Site; | |||
use tempfile::tempdir; | |||
use rebuild::after_content_change; | |||
use rebuild::{after_content_change, after_content_rename}; | |||
// Loads the test_site in a tempdir and build it there | |||
// Returns (site_path_in_tempdir, site) | |||
@@ -25,10 +25,6 @@ macro_rules! load_and_build_site { | |||
dir::copy(&path, &$tmp_dir, &options).unwrap(); | |||
let site_path = $tmp_dir.path().join("test_site"); | |||
// delete useless sections for those tests | |||
remove_dir_all(site_path.join("content").join("paginated")).unwrap(); | |||
remove_dir_all(site_path.join("content").join("posts")).unwrap(); | |||
let mut site = Site::new(&site_path, "config.toml").unwrap(); | |||
site.load().unwrap(); | |||
let public = &site_path.join("public"); | |||
@@ -67,6 +63,22 @@ macro_rules! file_contains { | |||
}}; | |||
} | |||
/// Rename a file or a folder to the new given name | |||
macro_rules! rename { | |||
($site_path: expr, $path: expr, $new_name: expr) => {{ | |||
let mut t = $site_path.clone(); | |||
for c in $path.split('/') { | |||
t.push(c); | |||
} | |||
let mut new_path = t.parent().unwrap().to_path_buf(); | |||
new_path.push($new_name); | |||
fs::rename(&t, &new_path).unwrap(); | |||
println!("Renamed {:?} to {:?}", t, new_path); | |||
(t, new_path) | |||
}}; | |||
} | |||
#[test] | |||
fn can_rebuild_after_simple_change_to_page_content() { | |||
let tmp_dir = tempdir().expect("create temp dir"); | |||
@@ -135,3 +147,87 @@ template = "rebuild.html" | |||
"<h1>first</h1><h1>second</h1>" | |||
)); | |||
} | |||
#[test] | |||
fn can_rebuild_after_transparent_change() { | |||
let tmp_dir = tempdir().expect("create temp dir"); | |||
let (site_path, mut site) = load_and_build_site!(tmp_dir); | |||
let file_path = edit_file!( | |||
site_path, | |||
"content/posts/2018/_index.md", | |||
br#" | |||
+++ | |||
transparent = false | |||
render = false | |||
+++ | |||
"# | |||
); | |||
// Also remove pagination from posts section so we check whether the transparent page title | |||
// is there or not without dealing with pagination | |||
edit_file!( | |||
site_path, | |||
"content/posts/_index.md", | |||
br#" | |||
+++ | |||
template = "section.html" | |||
insert_anchor_links = "left" | |||
+++ | |||
"# | |||
); | |||
let res = after_content_change(&mut site, &file_path); | |||
assert!(res.is_ok()); | |||
assert!(!file_contains!( | |||
site_path, | |||
"public/posts/index.html", | |||
"A transparent page" | |||
)); | |||
} | |||
#[test] | |||
fn can_rebuild_after_renaming_page() { | |||
let tmp_dir = tempdir().expect("create temp dir"); | |||
let (site_path, mut site) = load_and_build_site!(tmp_dir); | |||
let (old_path, new_path) = rename!(site_path, "content/posts/simple.md", "hard.md"); | |||
let res = after_content_rename(&mut site, &old_path, &new_path); | |||
println!("{:?}", res); | |||
assert!(res.is_ok()); | |||
assert!(file_contains!( | |||
site_path, | |||
"public/posts/hard/index.html", | |||
"A simple page" | |||
)); | |||
} | |||
// https://github.com/Keats/gutenberg/issues/385 | |||
#[test] | |||
fn can_rebuild_after_renaming_colocated_asset_folder() { | |||
let tmp_dir = tempdir().expect("create temp dir"); | |||
let (site_path, mut site) = load_and_build_site!(tmp_dir); | |||
let (old_path, new_path) = rename!(site_path, "content/posts/with-assets", "with-assets-updated"); | |||
assert!(file_contains!(site_path, "content/posts/with-assets-updated/index.md", "Hello")); | |||
let res = after_content_rename(&mut site, &old_path, &new_path); | |||
println!("{:?}", res); | |||
assert!(res.is_ok()); | |||
assert!(file_contains!( | |||
site_path, | |||
"public/posts/with-assets-updated/index.html", | |||
"Hello world" | |||
)); | |||
} | |||
// https://github.com/Keats/gutenberg/issues/385 | |||
#[test] | |||
fn can_rebuild_after_renaming_section_folder() { | |||
let tmp_dir = tempdir().expect("create temp dir"); | |||
let (site_path, mut site) = load_and_build_site!(tmp_dir); | |||
let (old_path, new_path) = rename!(site_path, "content/posts", "new-posts"); | |||
assert!(file_contains!(site_path, "content/new-posts/simple.md", "simple")); | |||
let res = after_content_rename(&mut site, &old_path, &new_path); | |||
assert!(res.is_ok()); | |||
assert!(file_contains!(site_path, "public/new-posts/simple/index.html", "simple")); | |||
} |
@@ -22,7 +22,7 @@ | |||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
use std::env; | |||
use std::fs::{remove_dir_all, File}; | |||
use std::fs::{remove_dir_all, File, read_dir}; | |||
use std::io::{self, Read}; | |||
use std::path::{Path, PathBuf, MAIN_SEPARATOR}; | |||
use std::sync::mpsc::channel; | |||
@@ -225,7 +225,7 @@ pub fn serve( | |||
.bind(&address) | |||
.expect("Can't start the webserver") | |||
.shutdown_timeout(20); | |||
println!("Web server is available at http://{}", &address); | |||
println!("Web server is available at http://{}\n", &address); | |||
s.run(); | |||
}); | |||
// The websocket for livereload | |||
@@ -286,12 +286,81 @@ pub fn serve( | |||
use notify::DebouncedEvent::*; | |||
let reload_templates = |site: &mut Site, path: &Path| { | |||
let msg = if path.is_dir() { | |||
format!("-> Directory in `templates` folder changed {}", path.display()) | |||
} else { | |||
format!("-> Template changed {}", path.display()) | |||
}; | |||
console::info(&msg); | |||
if let Some(ref broadcaster) = broadcaster { | |||
// Force refresh | |||
rebuild_done_handling( | |||
broadcaster, | |||
rebuild::after_template_change(site, &path), | |||
"/x.js", | |||
); | |||
} | |||
}; | |||
let reload_sass = |site: &Site, path: &Path, partial_path: &Path| { | |||
let msg = if path.is_dir() { | |||
format!("-> Directory in `sass` folder changed {}", path.display()) | |||
} else { | |||
format!("-> Sass file changed {}", path.display()) | |||
}; | |||
console::info(&msg); | |||
if let Some(ref broadcaster) = broadcaster { | |||
rebuild_done_handling( | |||
&broadcaster, | |||
site.compile_sass(&site.base_path), | |||
&partial_path.to_string_lossy(), | |||
); | |||
} | |||
}; | |||
let copy_static = |site: &Site, path: &Path, partial_path: &Path| { | |||
// Do nothing if the file/dir was deleted | |||
if !path.exists() { | |||
return; | |||
} | |||
let msg = if path.is_dir() { | |||
format!("-> Directory in `static` folder changed {}", path.display()) | |||
} else { | |||
format!("-> Static file changed {}", path.display()) | |||
}; | |||
console::info(&msg); | |||
if let Some(ref broadcaster) = broadcaster { | |||
if path.is_dir() { | |||
rebuild_done_handling( | |||
broadcaster, | |||
site.copy_static_directories(), | |||
&path.to_string_lossy(), | |||
); | |||
} else { | |||
rebuild_done_handling( | |||
broadcaster, | |||
copy_file(&path, &site.output_path, &site.static_path), | |||
&partial_path.to_string_lossy(), | |||
); | |||
} | |||
} | |||
}; | |||
loop { | |||
match rx.recv() { | |||
Ok(event) => { | |||
match event { | |||
Create(path) | Write(path) | Remove(path) | Rename(_, path) => { | |||
if is_temp_file(&path) || path.is_dir() { | |||
Rename(old_path, path) => { | |||
if path.is_file() && is_temp_file(&path) { | |||
continue; | |||
} | |||
let (change_kind, partial_path) = detect_change_kind(&pwd, &path); | |||
// We only care about changes in non-empty folders | |||
if path.is_dir() && is_folder_empty(&path) { | |||
continue; | |||
} | |||
@@ -299,55 +368,65 @@ pub fn serve( | |||
"Change detected @ {}", | |||
Local::now().format("%Y-%m-%d %H:%M:%S").to_string() | |||
); | |||
let start = Instant::now(); | |||
match detect_change_kind(&pwd, &path) { | |||
(ChangeKind::Content, _) => { | |||
console::info(&format!("-> Content changed {}", path.display())); | |||
match change_kind { | |||
ChangeKind::Content => { | |||
console::info(&format!("-> Content renamed {}", path.display())); | |||
if let Some(ref broadcaster) = broadcaster { | |||
// Force refresh | |||
rebuild_done_handling( | |||
broadcaster, | |||
rebuild::after_content_change(&mut site, &path), | |||
rebuild::after_content_rename(&mut site, &old_path, &path), | |||
"/x.js", | |||
); | |||
} | |||
} | |||
(ChangeKind::Templates, _) => { | |||
console::info(&format!("-> Template changed {}", path.display())); | |||
ChangeKind::Templates => reload_templates(&mut site, &path), | |||
ChangeKind::StaticFiles => copy_static(&site, &path, &partial_path), | |||
ChangeKind::Sass => reload_sass(&site, &path, &partial_path), | |||
ChangeKind::Config => { | |||
console::info("-> Config changed. The whole site will be reloaded. The browser needs to be refreshed to make the changes visible."); | |||
site = create_new_site( | |||
interface, | |||
port, | |||
output_dir, | |||
base_url, | |||
config_file, | |||
) | |||
.unwrap() | |||
.0; | |||
} | |||
} | |||
console::report_elapsed_time(start); | |||
} | |||
Create(path) | Write(path) | Remove(path) => { | |||
if is_temp_file(&path) || path.is_dir() { | |||
continue; | |||
} | |||
println!( | |||
"Change detected @ {}", | |||
Local::now().format("%Y-%m-%d %H:%M:%S").to_string() | |||
); | |||
let start = Instant::now(); | |||
match detect_change_kind(&pwd, &path) { | |||
(ChangeKind::Content, _) => { | |||
console::info(&format!("-> Content changed {}", path.display())); | |||
if let Some(ref broadcaster) = broadcaster { | |||
// Force refresh | |||
rebuild_done_handling( | |||
broadcaster, | |||
rebuild::after_template_change(&mut site, &path), | |||
rebuild::after_content_change(&mut site, &path), | |||
"/x.js", | |||
); | |||
} | |||
} | |||
(ChangeKind::StaticFiles, p) => { | |||
if path.is_file() { | |||
console::info(&format!( | |||
"-> Static file changes detected {}", | |||
path.display() | |||
)); | |||
if let Some(ref broadcaster) = broadcaster { | |||
rebuild_done_handling( | |||
broadcaster, | |||
copy_file(&path, &site.output_path, &site.static_path), | |||
&p.to_string_lossy(), | |||
); | |||
} | |||
} | |||
} | |||
(ChangeKind::Sass, p) => { | |||
console::info(&format!("-> Sass file changed {}", path.display())); | |||
if let Some(ref broadcaster) = broadcaster { | |||
rebuild_done_handling( | |||
&broadcaster, | |||
site.compile_sass(&site.base_path), | |||
&p.to_string_lossy(), | |||
); | |||
} | |||
} | |||
(ChangeKind::Templates, _) => reload_templates(&mut site, &path), | |||
(ChangeKind::StaticFiles, p) => copy_static(&site, &path, &p), | |||
(ChangeKind::Sass, p) => reload_sass(&site, &path, &p), | |||
(ChangeKind::Config, _) => { | |||
console::info("-> Config changed. The whole site will be reloaded. The browser needs to be refreshed to make the changes visible."); | |||
site = create_new_site( | |||
@@ -421,6 +500,18 @@ fn detect_change_kind(pwd: &Path, path: &Path) -> (ChangeKind, PathBuf) { | |||
(change_kind, partial_path) | |||
} | |||
/// Check if the directory at path contains any file | |||
fn is_folder_empty(dir: &Path) -> bool { | |||
// Can panic if we don't have the rights I guess? | |||
for _ in read_dir(dir).expect("Failed to read a directory to see if it was empty") { | |||
// If we get there, that means we have a file | |||
return false; | |||
} | |||
true | |||
} | |||
#[cfg(test)] | |||
mod tests { | |||
use std::path::{Path, PathBuf}; | |||